С обработкой Esc связана ещё одна особенность ncurses. Поскольку Esc может быть началом последовательности для функциональной клавиши, ncurses при нажатии Esc запустит односекундный таймер. В случае, если остаток получен в течение этого времени, возвратится код функциональной клавиши. Но если была нажата только Esc, ваша программа будет ждать ровно секунду, прежде чем возвратит код этой клавиши. Поэтому часто выход из многих ncurses-приложений рекомендуется производить не просто по Esc, а по двойному Esc. В этом случае ncurses прерывает чтение, возвращая код Esc, и выход из программы происходит без задержки.
примеры:
1)
shterm1> ./your_script 2>errlog.txt
shterm2> tail -f errlog.txt
2)
function my_error_handler($errno, $errstr, $errfile, $errline) {
return dialog(
array(
'message' => 'ERROR #'.$errno.': '.$errstr.'(FILE:'.$errfile.',LINE:'.$errline.')',
'buttons' => array('OK')
)
);
}
set_error_handler('my_error_handler');
пример реализации функции dialog см. ниже
#!/usr/local/bin/php -q
<?
define('XCURSES_KEY_LF', 13);
define('XCURSES_KEY_CR', 10);
define('XCURSES_KEY_ESC', 27);
###############################################################################################
function dialog($params) {
###############################################################################################
if (empty($params) || !is_array($params) ) {
trigger_error('params must be non-empty array');
return NULL;
}
$message = isset($params['message']) ? $params['message'] : '';
$buttons = (!empty($params['buttons']) && is_array($params['buttons'])) ?
$params['buttons'] : array('OK');
$n_buttons = count($buttons);
for ($i=0;$i<$n_buttons;$i++) {
$buttons[$i] = ' '.$buttons[$i].' ';
}
$parent_rows = (isset($params['rows']) && ($params['rows']>0)) ?
(int)$params['rows'] : 25;
$parent_cols = (isset($params['cols']) && ($params['cols']>0)) ?
(int)$params['cols'] : 80;
if (empty($message) || empty($buttons) || $parent_rows<=0 || $parent_cols<=0) {
trigger_error('wrong params');
return NULL;
}
$message_lines = split("\n",$message);
$message_width = 0;
$n_message_lines = count($message_lines);
for ($i=0; $i<$n_message_lines; $i++) {
$message_width = max(strlen($message_lines[$i]),$message_width);
}
$buttons_delim = ' ';
$buttons_delim_len = strlen($buttons_delim);
$buttons_len = strlen(implode($buttons_delim, $buttons));
$width = 4 + max($buttons_len + 2*$buttons_delim_len,$message_width);
$height = 4 + $n_message_lines;
$dlg_y = ($parent_rows > $height) ?
(($parent_rows - $height) >> 1) : 1;
$dlg_x = ($parent_cols > $width) ?
(($parent_cols - $width) >> 1) : 1;
$window = ncurses_newwin($height, $width, $dlg_y, $dlg_x);
if (empty($window)) {
trigger_error('unable to create window');
return NULL;
}
ncurses_wborder($window, 0,0, 0,0, 0,0, 0,0);
$i_x = 0;
$i_y = 0;
for ($i = 0; $i<$n_message_lines; $i++) {
$i_y = 1 + $i;
$i_x = 1 + (($width - 2 - strlen($message_lines[$i])) >> 1);
ncurses_mvwaddstr($window, $i_y, $i_x, rtrim($message_lines[$i]));
}
$buttons_data = array();
$buttons_shift_x = 1 + (($width - 1 - $buttons_len) >> 1);
$buttons_shift_y = 2 + $n_message_lines;
$i_title = '';
$i_x = $buttons_shift_x;
for ($i = 0; $i < $n_buttons; $i++) {
$i_title = $buttons[$i];
$buttons_data[] = array(
'x' => $i_x,
's' => $i_title
);
if (0 == $i) ncurses_wattron($window, NCURSES_A_REVERSE);
ncurses_mvwaddstr($window, $buttons_shift_y, $i_x, $i_title);
if (0 == $i) ncurses_wattroff($window, NCURSES_A_REVERSE);
$i_x += strlen($i_title) + $buttons_delim_len;
}
ncurses_wrefresh($window);
ncurses_keypad($window,TRUE);
ncurses_curs_set(0);
ncurses_noecho();
$result = -1;
$do_loop = 1;
$move = 0;
$current = 0;
while ($do_loop) {
$key = ncurses_wgetch($window);
$move = 0;
switch ($key) {
case NCURSES_KEY_LEFT:
if ($current > 0) $move = -1;
break;
case NCURSES_KEY_RIGHT:
if ($current < $n_buttons-1) $move = 1;
break;
case XCURSES_KEY_LF:
case XCURSES_KEY_CR:
$result = $current;
$do_loop = 0;
break;
case XCURSES_KEY_ESC:
$do_loop = 0;
break;
}
if (0 == $do_loop) {
ncurses_flushinp();
} elseif ($move) {
ncurses_mvwaddstr($window, $buttons_shift_y,
$buttons_data[$current]['x'], $buttons_data[$current]['s']);
$current += $move;
ncurses_wattron($window, NCURSES_A_REVERSE);
ncurses_mvwaddstr($window, $buttons_shift_y,
$buttons_data[$current]['x'], $buttons_data[$current]['s']);
ncurses_wattroff($window, NCURSES_A_REVERSE);
ncurses_wrefresh($window);
}
}
ncurses_delwin($window);
return $result;
}
###################################################################################################
function my_error_handler($errno, $errstr, $errfile, $errline) {
###################################################################################################
global $errors;
$errors[] = 'ERROR #'.$errno.': '.$errstr.' ('.$errfile.', line '.$errline.')';
return TRUE;
}
###################################################################################################
# main
###################################################################################################
if (!posix_isatty(STDOUT)) {
trigger_error('wrong terminal');
exit;
}
ncurses_init();
error_reporting(E_ALL);
$errors = array();
set_error_handler('my_error_handler');
$main_window = ncurses_newwin(0, 0, 0, 0);
ncurses_getmaxyx($main_window, $rows, $cols);
ncurses_keypad($main_window,TRUE);
$message = < < < EOM
Mr. Treehorn draws a lot of water in this town. You don't draw shit, Lebowski.
Now we got a nice, quiet little beach community here, and I aim to keep it
nice and quiet. So let me make something plain. I don't like
you sucking around, bothering our citizens, Lebowski.
I don't like your jerk-off name. I don't like your jerk-off face.
I don't like your jerk-off behavior, and I don't like you, jerk-off.
Do I make myself clear?
EOM;
$result = dialog(
array (
'message' => $message,
'rows' => -1,
'cols' => -1,
'buttons' => array ('Yes','No','I\'m sorry, I wasn\'t listening')
)
);
ncurses_end();
if (!empty($errors)) {
print join("\n",$errors)."\n";
}
?>
#!/usr/local/bin/php -q
<?
define('XCURSES_KEY_LF', 13);
define('XCURSES_KEY_CR', 10);
define('XCURSES_KEY_ESC', 27);
if (!posix_isatty(STDOUT)) {
trigger_error('wrong terminal');
exit;
}
ncurses_init();
$quotes = <<<EOT
----------------------------------------------
Usage:
ESC - exit
any other key - browse windows
----------------------------------------------
We will encourage you to develop
the three great virtues of a programmer:
laziness, impatience, and hubris (Larry Wall)
A good programmer is someone who looks
both ways before crossing a one-way
street (Doug Linder)
Managing senior programmers is like
herding cats (Dave Platt)
EOT;
$lines = explode("\n",$quotes);
$n_lines = count($lines);
$window_width = 0;
for ($i=0; $i<$n_lines; $i++) {
$window_width = max($window_width,strlen($lines[$i]));
}
$window_width += 4;
$x_coords = array(10,14,18);
$y_coords = array(10,12,8);
for ($i=0; $i<3; $i++) {
$windows[$i] = ncurses_newwin(4+$n_lines, $window_width, $y_coords[$i], $x_coords[$i]);
ncurses_wborder($windows[$i], 0,0, 0,0, 0,0, 0,0);
ncurses_wattron($windows[$i], NCURSES_A_REVERSE);
ncurses_mvwaddstr($windows[$i], 0, 2, ' window #'.$i.' ');
ncurses_wattroff($windows[$i], NCURSES_A_REVERSE);
for ($j=0; $j<$n_lines; $j++) {
ncurses_mvwaddstr($windows[$i], 2+$j, 2, $lines[$j]);
}
ncurses_wrefresh($windows[$i]);
$panels[$i] = ncurses_new_panel($windows[$i]);
}
ncurses_update_panels();
ncurses_curs_set(0);
ncurses_noecho();
$i = 0;
$k = NULL;
while(XCURSES_KEY_ESC != $k) {
$k = ncurses_getch();
ncurses_top_panel($panels[$i%3]);
ncurses_update_panels();
ncurses_doupdate();
$i++;
}
ncurses_end();
?>
#!/usr/local/bin/php -q
<?
define('XCURSES_KEY_LF', 13);
define('XCURSES_KEY_CR', 10);
define('XCURSES_KEY_ESC', 27);
define('XCURSES_KEY_PRINTABLE_MIN', 32);
define('XCURSES_KEY_PRINTABLE_MAX', 127);
###############################################################################################
function dlg_input($params = array()) {
###############################################################################################
$title = isset($params['title']) ? $params['title'] : NULL;
$max_length = isset($params['max_len']) ? (int)$params['max_len'] : 10;
$dlg_rows = isset($params['dlg_cols']) ? (int)$params['dlg_cols'] : 3;
$dlg_cols = isset($params['dlg_cols']) ? (int)$params['dlg_cols'] : 40;
$parent_cols = isset($params['cols']) ? (int)$params['cols'] : NULL;
$parent_rows = isset($params['rows']) ? (int)$params['rows'] : NULL;
$dlg_x = (int)(($parent_cols - $dlg_cols)/2);
if($dlg_x<0) $dlg_x = 0;
$dlg_y = (int)(($parent_rows - $dlg_rows)/2);
if($dlg_y<0) $dlg_y = 0;
if ($max_length<=0 || $dlg_rows<=0 || $dlg_cols<=0) {
trigger_error('wrong params');
return NULL;
}
$dlg_window = ncurses_newwin($dlg_rows, $dlg_cols, $dlg_y, $dlg_x);
if (empty($dlg_window)) {
return NULL;
}
ncurses_wborder($dlg_window, 0, 0, 0, 0, 0, 0, 0, 0);
if ($title) {
ncurses_wattron($dlg_window, NCURSES_A_REVERSE);
ncurses_mvwaddstr($dlg_window, 0, 2,' '.$title.' ');
ncurses_wattroff($dlg_window, NCURSES_A_REVERSE);
}
ncurses_curs_set(1);
ncurses_wmove($dlg_window, 2, 2);
ncurses_wrefresh($dlg_window);
$do_getch = 1;
$input_val = '';
$input_char = '';
$input_len = 0;
$cursor_x = 2;
$cursor_y = 1;
ncurses_wmove($dlg_window, $cursor_y, $cursor_x);
ncurses_noecho();
ncurses_keypad($dlg_window,TRUE);
while($do_getch){
$key_code = ncurses_wgetch($dlg_window);
if (($key_code == XCURSES_KEY_CR) || ($key_code == XCURSES_KEY_LF)) {
$do_getch = 0;
} elseif ($key_code == NCURSES_KEY_BACKSPACE) {
if($input_len>0) {
$input_len--;
$input_val = substr($input_val,0,$input_len);
$cursor_x--;
ncurses_mvwaddstr($dlg_window, $cursor_y, $cursor_x,' ');
ncurses_wmove($dlg_window, $cursor_y, $cursor_x);
}
} elseif ($key_code < XCURSES_KEY_PRINTABLE_MIN || $key_code > XCURSES_KEY_PRINTABLE_MAX) {
continue;
} elseif($input_len<$max_length) {
$input_val .= $input_char = chr($key_code);
$input_len++;
$cursor_x++;
ncurses_waddstr($dlg_window, $input_char);
}
}
ncurses_delwin($dlg_window);
return $input_val;
}
###################################################################################################
# main
###################################################################################################
if (!posix_isatty(STDOUT)) {
trigger_error('wrong terminal');
exit;
}
error_reporting(E_ALL);
ncurses_init();
$main_window = ncurses_newwin(0, 0, 0, 0);
ncurses_getmaxyx($main_window, $rows, $cols);
$input = dlg_input(
array(
'title' => 'sample input',
'rows' => $rows,
'cols' => $cols
)
);
ncurses_end();
echo 'Input was: '.$input."\n";
?>
#!/usr/local/bin/php -q
<?
define('XCURSES_KEY_LF', 13);
define('XCURSES_KEY_CR', 10);
define('XCURSES_KEY_ESC', 27);
###################################################################################################
function menu_select($params) {
###################################################################################################
if(!is_array($params) || empty($params)) {
trigger_error('wrong params');
return NULL;
}
$menu = isset($params['items']) ? $params['items'] : NULL;
$rows = isset($params['rows']) ? (int)$params['rows'] : 0;
$cols = isset($params['cols']) ? (int)$params['cols'] : 0;
$selected = isset($params['selected']) ? (int)$params['selected'] : 0;
$centered = empty($params['centered']) ? 0 : 1;
$y_menu = isset($params['y']) ? (int)$params['y'] : 0;
$x_menu = isset($params['x']) ? (int)$params['x'] : 0;
if(!is_array($menu) || empty($menu) || $rows<=0 || $cols<=0 || $y_menu<0 || $x_menu<0) {
trigger_error('wrong params');
return NULL;
}
$keys = array_keys($menu);
$values = array();
$current = 0;
$width = 0;
$height = count($menu) + 2;
foreach ($menu as $value) {
$width = max($width, strlen($value));
}
$i = 0;
foreach ($menu as $k => $v) {
$values[$i] = ' '.$v.str_repeat(' ',1 + $width - strlen($v));
if ($k == $selected) $current = $i;
$i++;
}
$width += 4;
if ($centered) {
$y_menu = ($rows - $height) >> 1;
$x_menu = ($cols - $width) >> 1;
}
$window = ncurses_newwin($height, $width, $y_menu, $x_menu);
if (empty($window)) {
trigger_error('unable to create window');
return NULL;
}
ncurses_wborder($window, 0,0, 0,0, 0,0, 0,0);
for ($a = 0; $a < count($values); $a++) {
if ($a == $current) ncurses_wattron($window, NCURSES_A_REVERSE);
ncurses_mvwaddstr($window, 1 + $a, 1, $values[$a]);
if ($a == $current) ncurses_wattroff($window, NCURSES_A_REVERSE);
}
ncurses_wrefresh($window);
ncurses_keypad($window,TRUE);
ncurses_curs_set(0);
do {
$key = ncurses_wgetch($window);
$move = 0;
switch ($key) {
case NCURSES_KEY_UP :
if ($current > 0) $move = -1;
break;
case NCURSES_KEY_DOWN :
if ($current < count($values) - 1) $move = 1;
break;
case XCURSES_KEY_LF:
case XCURSES_KEY_CR :
$result = $keys[$current];
break;
case XCURSES_KEY_ESC :
ncurses_flushinp();
$result = '';
break;
}
if ($move) {
ncurses_mvwaddstr($window, 1 + $current, 1, $values[$current]);
$current += $move;
ncurses_wattron($window, NCURSES_A_REVERSE);
ncurses_mvwaddstr($window, 1 + $current, 1, $values[$current]);
ncurses_wattroff($window, NCURSES_A_REVERSE);
ncurses_wrefresh($window);
}
} while (!isset($result));
ncurses_delwin($window);
return $result;
}
###################################################################################################
# main
###################################################################################################
if (!posix_isatty(STDOUT)) {
trigger_error('wrong terminal');
exit;
}
ncurses_init();
error_reporting(E_ALL);
$main_window = ncurses_newwin(0, 0, 0, 0);
ncurses_getmaxyx($main_window, $rows, $cols);
$result = menu_select(
array (
'items' => array (
'dude' => 'The Dude',
'walter' => 'Walter Sobchak',
'donny' => 'Donny',
'maude' => 'Maude Lebowski',
'jesus' => 'Jesus Quintana',
'smokey' => 'Smokey'
),
'rows' => $rows,
'cols' => $cols,
'selected' => 3,
'centered' => 1
)
);
ncurses_end();
echo 'Result is:'.$result."\n";
?>
#!/usr/local/bin/php -q
<?
define('XCURSES_KEY_LF', 13);
define('XCURSES_KEY_CR', 10);
define('XCURSES_KEY_ESC', 27);
define('XCURSES_KEY_SPACE', 32);
###############################################################################################
function menu_check_list($params) {
###############################################################################################
if(!is_array($params) || empty($params)) {
trigger_error('wrong_params');
return NULL;
}
$menu = isset($params['items']) ? $params['items'] : NULL;
$rows = isset($params['rows']) ? (int)$params['rows'] : 0;
$cols = isset($params['cols']) ? (int)$params['cols'] : 0;
$centered = empty($params['centered']) ? 0 : 1;
$y_menu = isset($params['y']) ? (int)$params['y'] : 0;
$x_menu = isset($params['x']) ? (int)$params['x'] : 0;
if(!is_array($menu) || empty($menu) || $rows<=0 || $cols<=0 || $y_menu<0 || $x_menu<0) {
trigger_error('wrong params');
return NULL;
}
$keys = array_keys($menu);
$n_menu = count($keys);
$items = array();
$checked = array();
$current = 0;
$width = 0;
$height = $n_menu + 2;
$i = 0;
$k = NULL;
$i_checked = NULL;
for($i=0; $i<$n_menu; $i++) {
$k = $keys[$i];
$i_checked = (isset($menu[$k][1]) && $menu[$k][1] == 1) ? 1 : 0;
$items[$i] = ' ['. ($i_checked ? '*' : ' ').'] '.$menu[$k][0];
$width = max($width, strlen($items[$i]));
$checked[$i] = $i_checked;
}
for ($i=0; $i<$n_menu; $i++) {
$items[$i] = $items[$i].str_repeat(' ', 2 + $width - strlen($items[$i]));
}
$width += 4;
if ($centered) {
$r = ($rows - $height) >> 1;
$c = ($cols - $width) >> 1;
}
$window = ncurses_newwin($height, $width, $r, $c);
if (empty($window)) {
trigger_error('unable to create window');
return NULL;
}
ncurses_wborder($window, 0,0, 0,0, 0,0, 0,0);
$n_items = count($items);
for ($i = 0; $i < $n_items; $i++) {
if ($i == $current) ncurses_wattron($window, NCURSES_A_REVERSE);
ncurses_mvwaddstr($window, 1 + $i, 1, $items[$i]);
if ($i == $current) ncurses_wattroff($window, NCURSES_A_REVERSE);
}
ncurses_wrefresh($window);
ncurses_keypad($window,TRUE);
ncurses_noecho();
ncurses_curs_set(0);
$do_loop = 1;
$save_result = 0;
while($do_loop) {
$key = ncurses_wgetch($window);
$move = 0;
switch ($key) {
case NCURSES_KEY_UP :
if ($current > 0) $move = -1;
break;
case NCURSES_KEY_DOWN :
if ($current < $n_menu - 1) $move = 1;
break;
case XCURSES_KEY_LF:
case XCURSES_KEY_CR:
$do_loop = 0;
$save_result = 1;
break;
case XCURSES_KEY_SPACE:
if ($checked[$current]) {
$checked[$current] = 0;
$items[$current] = ' [ ] '.substr($items[$current],5);
} else {
$checked[$current] = 1;
$items[$current] = ' [*] '.substr($items[$current],5);
}
ncurses_wattron($window, NCURSES_A_REVERSE);
ncurses_mvwaddstr($window, 1 + $current, 1, $items[$current]);
ncurses_wattroff($window, NCURSES_A_REVERSE);
ncurses_wrefresh($window);
break;
case XCURSES_KEY_ESC :
ncurses_flushinp();
$do_loop = 0;
break;
}
if ($move) {
ncurses_mvwaddstr($window, 1 + $current, 1, $items[$current]);
$current += $move;
ncurses_wattron($window, NCURSES_A_REVERSE);
ncurses_mvwaddstr($window, 1 + $current, 1, $items[$current]);
ncurses_wattroff($window, NCURSES_A_REVERSE);
ncurses_wrefresh($window);
}
}
ncurses_delwin($window);
$result = NULL;
if ($save_result) {
for ($i=0; $i<$n_menu; $i++) {
$result[$keys[$i]] = $checked[$i];
}
}
return $result;
}
###################################################################################################
# main
###################################################################################################
if (!posix_isatty(STDOUT)) {
trigger_error('wrong terminal');
exit;
}
ncurses_init();
error_reporting(E_ALL);
$main_window = ncurses_newwin(0, 0, 0, 0);
ncurses_getmaxyx($main_window, $rows, $cols);
$result = menu_check_list(
array (
'items' => array (
'dude' => array ('The Dude', 1),
'walter' => array ('Walter Sobchak', 0),
'donny' => array ('Donny', 1),
'maude' => array ('Maude Lebowski', 1),
'jesus' => array ('Jesus Quintana', 0),
'smokey' => array ('Smokey', 0)
),
'rows' => $rows,
'cols' => $cols,
'selected' => 3,
'centered' => 1
)
);
ncurses_end();
echo 'Result is:'.var_export($result,TRUE)."\n";
?>
Очень хороший обзор возможностей ncurses содержится в ncurses-HOWTO и Writing Programs with NCURSES.
Исходный код ncurses-расширения php может также послужить неплохим источником информации, а разработчикам, знакомым с perl, может быть полезным изучить исходники некоторых Curses модулей из CPAN, например, Curses::UI, Curses::Application, Curses::Forms, Curses::Widgets.
wbr, fisher