From 25d4ae74f1dbddf99b147df0887d6810d34e36dd Mon Sep 17 00:00:00 2001 From: wm4 Date: Thu, 19 Dec 2013 21:31:27 +0100 Subject: Rename getch2....c/h to terminal....c/h "getch2" really tells nothing about what the heck this code does. It'd be even worse when moving the rest of terminal handling code there. --- osdep/getch2-win.c | 194 ----------------- osdep/getch2.c | 578 -------------------------------------------------- osdep/getch2.h | 63 ------ osdep/terminal-unix.c | 578 ++++++++++++++++++++++++++++++++++++++++++++++++++ osdep/terminal-win.c | 194 +++++++++++++++++ osdep/terminal.h | 63 ++++++ 6 files changed, 835 insertions(+), 835 deletions(-) delete mode 100644 osdep/getch2-win.c delete mode 100644 osdep/getch2.c delete mode 100644 osdep/getch2.h create mode 100644 osdep/terminal-unix.c create mode 100644 osdep/terminal-win.c create mode 100644 osdep/terminal.h (limited to 'osdep') diff --git a/osdep/getch2-win.c b/osdep/getch2-win.c deleted file mode 100644 index 166084d425..0000000000 --- a/osdep/getch2-win.c +++ /dev/null @@ -1,194 +0,0 @@ -/* Windows TermIO - * - * copyright (C) 2003 Sascha Sommer - * - * This file is part of MPlayer. - * - * MPlayer is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * MPlayer is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with MPlayer; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -// See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/WinUI/WindowsUserInterface/UserInput/VirtualKeyCodes.asp -// for additional virtual keycodes - - -#include "config.h" -#include -#include -#include -#include -#include "input/keycodes.h" -#include "input/input.h" -#include "getch2.h" - -int mp_input_slave_cmd_func(int fd, char *dest, int size) -{ - DWORD retval; - HANDLE in = GetStdHandle(STD_INPUT_HANDLE); - if (PeekNamedPipe(in, NULL, size, &retval, NULL, NULL)) { - if (size > retval) - size = retval; - } else { - if (WaitForSingleObject(in, 0)) - size = 0; - } - if (!size) - return MP_INPUT_NOTHING; - ReadFile(in, dest, size, &retval, NULL); - if (retval) - return retval; - return MP_INPUT_NOTHING; -} - -int screen_width = 80; -int screen_height = 24; -char *erase_to_end_of_line = NULL; - -void get_screen_size(void) -{ - CONSOLE_SCREEN_BUFFER_INFO cinfo; - if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cinfo)) { - screen_width = cinfo.dwMaximumWindowSize.X; - screen_height = cinfo.dwMaximumWindowSize.Y; - } -} - -int load_termcap(char *termtype) -{ - return 0; -} - -static HANDLE in; -static int getch2_status = 0; - -static int getch2_internal(void) -{ - INPUT_RECORD eventbuffer[128]; - DWORD retval; - int i = 0; - if (!getch2_status) { - // supports e.g. MinGW xterm, unfortunately keys are only received after - // enter was pressed. - uint8_t c; - if (!PeekNamedPipe(in, NULL, 1, &retval, NULL, NULL) || !retval) - return -1; - ReadFile(in, &c, 1, &retval, NULL); - return retval == 1 ? c : -1; - } - /*check if there are input events*/ - if (!GetNumberOfConsoleInputEvents(in, &retval)) { - printf("getch2: can't get number of input events: %i\n", - (int)GetLastError()); - return -1; - } - if (retval <= 0) - return -1; - - /*read all events*/ - if (!ReadConsoleInput(in, eventbuffer, 128, &retval)) { - printf("getch: can't read input events\n"); - return -1; - } - - /*filter out keyevents*/ - for (i = 0; i < retval; i++) { - switch (eventbuffer[i].EventType) { - case KEY_EVENT: - /*only a pressed key is interresting for us*/ - if (eventbuffer[i].Event.KeyEvent.bKeyDown == TRUE) { - /*check for special keys*/ - switch (eventbuffer[i].Event.KeyEvent.wVirtualKeyCode) { - case VK_HOME: - return MP_KEY_HOME; - case VK_END: - return MP_KEY_END; - case VK_DELETE: - return MP_KEY_DEL; - case VK_INSERT: - return MP_KEY_INS; - case VK_BACK: - return MP_KEY_BS; - case VK_PRIOR: - return MP_KEY_PGUP; - case VK_NEXT: - return MP_KEY_PGDWN; - case VK_RETURN: - return MP_KEY_ENTER; - case VK_ESCAPE: - return MP_KEY_ESC; - case VK_LEFT: - return MP_KEY_LEFT; - case VK_UP: - return MP_KEY_UP; - case VK_RIGHT: - return MP_KEY_RIGHT; - case VK_DOWN: - return MP_KEY_DOWN; - case VK_SHIFT: - continue; - } - /*check for function keys*/ - if (0x87 >= eventbuffer[i].Event.KeyEvent.wVirtualKeyCode && - eventbuffer[i].Event.KeyEvent.wVirtualKeyCode >= 0x70) - return MP_KEY_F + 1 + - eventbuffer[i].Event.KeyEvent.wVirtualKeyCode - 0x70; - - /*only characters should be remaining*/ - //printf("getch2: YOU PRESSED \"%c\" \n",eventbuffer[i].Event.KeyEvent.uChar.AsciiChar); - return eventbuffer[i].Event.KeyEvent.uChar.AsciiChar; - } - break; - - case MOUSE_EVENT: - case WINDOW_BUFFER_SIZE_EVENT: - case FOCUS_EVENT: - case MENU_EVENT: - default: - //printf("getch2: unsupported event type"); - break; - } - } - return -1; -} - -bool getch2(struct input_ctx *ctx) -{ - int r = getch2_internal(); - if (r >= 0) - mp_input_put_key(ctx, r); - return true; -} - -void getch2_poll(void) -{ -} - -void getch2_enable(void) -{ - DWORD retval; - in = GetStdHandle(STD_INPUT_HANDLE); - if (!GetNumberOfConsoleInputEvents(in, &retval)) { - printf("getch2: %i can't get number of input events " - "[disabling console input]\n", (int)GetLastError()); - getch2_status = 0; - } else - getch2_status = 1; -} - -void getch2_disable(void) -{ - if (!getch2_status) - return; // already disabled / never enabled - getch2_status = 0; -} diff --git a/osdep/getch2.c b/osdep/getch2.c deleted file mode 100644 index 054b9bf63f..0000000000 --- a/osdep/getch2.c +++ /dev/null @@ -1,578 +0,0 @@ -/* - * GyS-TermIO v2.0 (for GySmail v3) - * a very small replacement of ncurses library - * - * copyright (C) 1999 A'rpi/ESP-team - * - * This file is part of MPlayer. - * - * MPlayer is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * MPlayer is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with MPlayer; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include - -#if HAVE_TERMIOS -#if HAVE_TERMIOS_H -#include -#endif -#if HAVE_SYS_TERMIOS_H -#include -#endif -#endif - -#include - -#include "common/common.h" -#include "bstr/bstr.h" -#include "input/input.h" -#include "input/keycodes.h" -#include "getch2.h" - -#if HAVE_TERMIOS -static volatile struct termios tio_orig; -static volatile int tio_orig_set; -#endif - -int screen_width = 80; -int screen_height = 24; -char * erase_to_end_of_line = NULL; - -typedef struct { - char *cap; - int len; - int code; - char chars[8]; -} keycode_st; - -typedef struct { - keycode_st *map; - int len; - int cap; -} keycode_map; - -static keycode_map getch2_keys; - -#if HAVE_TERMINFO || HAVE_TERMCAP - -static char *term_rmkx = NULL; -static char *term_smkx = NULL; - -#if HAVE_TERMINFO -#include -#endif -#include - -#endif - -static keycode_st *keys_push(char *p, int code) { - if (getch2_keys.len == getch2_keys.cap) { - getch2_keys.cap *= 2; - if (getch2_keys.cap == 0) - getch2_keys.cap = 32; - - getch2_keys.map = realloc(getch2_keys.map, sizeof(keycode_st) * getch2_keys.cap); - } - - keycode_st *st = &getch2_keys.map[getch2_keys.len++]; - st->cap = NULL; - st->len = strlen(p); - st->code = code; - strncpy(st->chars, p, 8); - - return st; -} - -static int keys_count_matches(char *buf, int buflen) { - int count = 0; - if (buflen < 0) - buflen = strlen(buf); - - for (int i = 0; i < getch2_keys.len; i++) { - keycode_st *st = &getch2_keys.map[i]; - int len = MPMIN(buflen, st->len); - - if (memcmp(buf, st->chars, len) == 0) - count++; - } - return count; -} - -static keycode_st *keys_search(char *buf, int buflen) { - if (buflen < 0) - buflen = strlen(buf); - - for (int i = 0; i < getch2_keys.len; i++) { - keycode_st *st = &getch2_keys.map[i]; - - if (buflen >= st->len && memcmp(buf, st->chars, st->len) == 0) - return st; - } - return NULL; -} - -/* pushes only if there is no duplicate. - important as we only consider keys if the matches are unique. */ -static keycode_st* keys_push_once(char *p, int code) { - keycode_st *st = keys_search(p, -1); - if (!st) - return keys_push(p, code); - return st; -} - -#if HAVE_TERMINFO || HAVE_TERMCAP - -typedef struct { - char *buf; - char *pos; - int cap; -} buf_st; -static buf_st termcap_buf; - -static void ensure_cap(buf_st *buf, int cap) { - if (buf->pos - buf->buf < cap) { - ptrdiff_t diff = buf->pos - buf->buf; - buf->cap += cap; - buf->buf = realloc(buf->buf, buf->cap); - buf->pos = buf->buf + diff; - } -} - -static char *termcap_get(char *id) { - ensure_cap(&termcap_buf, 1024); - return tgetstr(id, &termcap_buf.pos); -} - -typedef struct { - char *id; - int code; -} cap_key_pair; - -#if 0 -#include -#include - -static void debug_keycode(keycode_st *st) { - if (!st) - return; - - char buf[128]; /* worst case should be 70 bytes */ - unsigned char *b = &buf[0]; - unsigned char *p = &st->chars[0]; - - if (st->cap) - b += sprintf(b, "%s: ", st->cap); - - for(; *p; p++) { - if (*p == 27) - b += sprintf(b, "\\e"); - else if (*p < 27) - b += sprintf(b, "^%c", '@' + *p); - else if (!isgraph(*p)) - b += sprintf(b, "\\x%02x", (unsigned int)*p); - else - b += sprintf(b, "%c", *p); - } - fprintf(stderr, "%s\n", buf); -} -#endif - -static void termcap_add(cap_key_pair pair) { - char *p = termcap_get(pair.id); - if (p) { - keycode_st *st = keys_push_once(p, pair.code); - if (st) - st->cap = pair.id; - /* debug_keycode(st); */ - } -} - -static void termcap_add_extra_f_keys(void) { - char capbuf[3]; - for (int i = 11; i < 0x20; i++) { - unsigned char c; - if (i < 20) { /* 1-9 */ - c = '0' + (i - 10); - } else { /* A-Z */ - c = 'A' + (i - 20); - } - - sprintf(&capbuf[0], "F%c", c); - - char *p = termcap_get(capbuf); - if (p) - keys_push_once(p, MP_KEY_F+i); - else - break; /* unlikely that the database has further keys */ - } -} - -#endif - -int load_termcap(char *termtype){ -#if HAVE_TERMINFO || HAVE_TERMCAP - -#if HAVE_TERMINFO - use_env(TRUE); - int ret; - if (setupterm(termtype, 1, &ret) != OK) { - /* try again, with with "ansi" terminal if it was unset before */ - if (!termtype) - termtype = getenv("TERM"); - if (!termtype || *termtype == '\0') - termtype = "ansi"; - - if (setupterm(termtype, 1, &ret) != OK) { - if (ret < 0) { - printf("Could not access the 'terminfo' data base.\n"); - return 0; - } else { - printf("Couldn't use terminal `%s' for input.\n", termtype); - return 0; - } - } - } -#else - static char term_buffer[2048]; - if (!termtype) termtype = getenv("TERM"); - if (!termtype) termtype = "ansi"; - int success = tgetent(term_buffer, termtype); - if (success < 0) { - printf("Could not access the 'termcap' data base.\n"); - return 0; - } else if (success == 0) { - printf("Terminal type `%s' is not defined.\n", termtype); - return 0; - } -#endif - ensure_cap(&termcap_buf, 2048); - - static char term_buf[64]; - char *buf_ptr = &term_buf[0]; - - erase_to_end_of_line = tgetstr("ce", &buf_ptr); - - screen_width = tgetnum("co"); - screen_height = tgetnum("li"); - if (screen_width < 1 || screen_width > 255) - screen_width = 80; - if (screen_height < 1 || screen_height > 255) - screen_height = 24; - - term_smkx = tgetstr("ks", &buf_ptr); - term_rmkx = tgetstr("ke", &buf_ptr); - - cap_key_pair keys[] = { - {"kP", MP_KEY_PGUP}, {"kN", MP_KEY_PGDWN}, {"kh", MP_KEY_HOME}, {"kH", MP_KEY_END}, - {"kI", MP_KEY_INS}, {"kD", MP_KEY_DEL}, /* on PC keyboards */ {"@7", MP_KEY_END}, - - {"kl", MP_KEY_LEFT}, {"kd", MP_KEY_DOWN}, {"ku", MP_KEY_UP}, {"kr", MP_KEY_RIGHT}, - - {"do", MP_KEY_ENTER}, - {"kb", MP_KEY_BS}, - - {"k1", MP_KEY_F+1}, {"k2", MP_KEY_F+2}, {"k3", MP_KEY_F+3}, - {"k4", MP_KEY_F+4}, {"k5", MP_KEY_F+5}, {"k6", MP_KEY_F+6}, - {"k7", MP_KEY_F+7}, {"k8", MP_KEY_F+8}, {"k9", MP_KEY_F+9}, - {"k;", MP_KEY_F+10}, {"k0", MP_KEY_F+0}, - - /* K2 is the keypad center */ - {"K2", MP_KEY_KP5}, - - /* EOL */ - {NULL}, - }; - for (int i = 0; keys[i].id; i++) { - termcap_add(keys[i]); - } - termcap_add_extra_f_keys(); -#endif - - /* special cases (hardcoded, no need for HAVE_TERMCAP) */ - - /* it's important to use keys_push_once as we can't have duplicates */ - - /* many terminals, for emacs compatibility, use 0x7f instead of ^H - when typing backspace, even when the 'kb' cap says otherwise. */ - keys_push_once("\177", MP_KEY_BS); - - /* mintty always sends these when using the numpad arrows, - even in application mode, for telling them from regular arrows. */ - keys_push_once("\033[A", MP_KEY_UP); - keys_push_once("\033[B", MP_KEY_DOWN); - keys_push_once("\033[C", MP_KEY_RIGHT); - keys_push_once("\033[D", MP_KEY_LEFT); - - /* mintty uses this instead of the "K2" cap for keypad center */ - keys_push_once("\033OE", MP_KEY_KP5); - - return getch2_keys.len; -} - -void get_screen_size(void) { - struct winsize ws; - if (ioctl(0, TIOCGWINSZ, &ws) < 0 || !ws.ws_row || !ws.ws_col) - return; - - screen_width = ws.ws_col; - screen_height = ws.ws_row; -} - -#define BUF_LEN 256 - -static unsigned char getch2_buf[BUF_LEN]; -static int getch2_len = 0; -static int getch2_pos = 0; -static enum { - STATE_INITIAL, - STATE_UTF8, -} state = STATE_INITIAL; -static int utf8_len = 0; - -static void walk_buf(unsigned int count) { - if (!(count < BUF_LEN && count <= getch2_len)) - abort(); - - memmove(&getch2_buf[0], &getch2_buf[count], getch2_len - count); - getch2_len -= count; - getch2_pos -= count; - if (getch2_pos < 0) - getch2_pos = 0; -} - -bool getch2(struct input_ctx *input_ctx) -{ - int retval = read(0, &getch2_buf[getch2_pos], BUF_LEN - getch2_len - getch2_pos); - /* Return false on EOF to stop running select() on the FD, as it'd - * trigger all the time. Note that it's possible to get temporary - * EOF on terminal if the user presses ctrl-d, but that shouldn't - * happen if the terminal state change done in getch2_enable() - * works. - */ - if (retval == 0) - return false; - if (retval == -1) - return errno != EBADF && errno != EINVAL; - getch2_len += retval; - - while (getch2_pos < getch2_len) { - unsigned char c = getch2_buf[getch2_pos++]; - - switch (state) { - case STATE_INITIAL: { - int match_count = keys_count_matches(&getch2_buf[0], getch2_len); - if (match_count == 1) { - keycode_st *st = keys_search(&getch2_buf[0], getch2_len); - - if (st) { - mp_input_put_key(input_ctx, st->code); - walk_buf(st->len); - } /* else this is still a partial (but unique) match */ - - continue; - } else if (match_count > 1) { - continue; /* need more bytes to disambiguate */ - } else { - /* backtrack, send as UTF-8 */ - getch2_pos = 1; - c = getch2_buf[0]; - } - utf8_len = bstr_parse_utf8_code_length(c); - - if (utf8_len > 1) { - state = STATE_UTF8; - } else if (utf8_len == 1) { - switch (c) { - case 0x1b: /* ESC that's not part of escape sequence */ - /* only if ESC was typed twice, otherwise ignore it */ - if (getch2_len > 1 && getch2_buf[1] == 0x1b) { - walk_buf(1); /* eat the second ESC */ - mp_input_put_key(input_ctx, MP_KEY_ESC); - } - break; - default: - mp_input_put_key(input_ctx, c); - } - walk_buf(1); - } else - walk_buf(getch2_pos); - - break; - } - case STATE_UTF8: { - if (getch2_pos < utf8_len) /* need more bytes */ - continue; - - struct bstr s = {getch2_buf, utf8_len}; - int unicode = bstr_decode_utf8(s, NULL); - - if (unicode > 0) { - mp_input_put_key(input_ctx, unicode); - } - walk_buf(utf8_len); - state = STATE_INITIAL; - continue; - } - } - } - - return true; -} - -static volatile int getch2_active = 0; -static volatile int getch2_enabled = 0; - -static void do_activate_getch2(void) -{ - if (getch2_active) - return; - -#if HAVE_TERMINFO || HAVE_TERMCAP - if (term_smkx) - tputs(term_smkx, 1, putchar); -#endif - -#if HAVE_TERMIOS - struct termios tio_new; - tcgetattr(0,&tio_new); - - if (!tio_orig_set) { - tio_orig = tio_new; - tio_orig_set = 1; - } - - tio_new.c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */ - tio_new.c_cc[VMIN] = 1; - tio_new.c_cc[VTIME] = 0; - tcsetattr(0,TCSANOW,&tio_new); -#endif - - getch2_active = 1; -} - -static void do_deactivate_getch2(void) -{ - if (!getch2_active) - return; - -#if HAVE_TERMINFO || HAVE_TERMCAP - if (term_rmkx) - tputs(term_rmkx, 1, putchar); -#endif - -#if HAVE_TERMIOS - if (tio_orig_set) { - // once set, it will never be set again - // so we can cast away volatile here - tcsetattr(0, TCSANOW, (const struct termios *) &tio_orig); - } -#endif - - getch2_active = 0; -} - -// sigaction wrapper -static int setsigaction(int signo, void (*handler) (int), - int flags, bool do_mask) -{ - struct sigaction sa; - sa.sa_handler = handler; - - if(do_mask) - sigfillset(&sa.sa_mask); - else - sigemptyset(&sa.sa_mask); - - sa.sa_flags = flags; - return sigaction(signo, &sa, NULL); -} - -void getch2_poll(void){ - if (!getch2_enabled) - return; - - // check if stdin is in the foreground process group - int newstatus = (tcgetpgrp(0) == getpgrp()); - - // and activate getch2 if it is, deactivate otherwise - if (newstatus) - do_activate_getch2(); - else - do_deactivate_getch2(); -} - -static void stop_sighandler(int signum) -{ - do_deactivate_getch2(); - - // note: for this signal, we use SA_RESETHAND but do NOT mask signals - // so this will invoke the default handler - raise(SIGTSTP); -} - -static void continue_sighandler(int signum) -{ - // SA_RESETHAND has reset SIGTSTP, so we need to restore it here - setsigaction(SIGTSTP, stop_sighandler, SA_RESETHAND, false); - - getch2_poll(); -} - -static void quit_request_sighandler(int signum) -{ - do_deactivate_getch2(); - - async_quit_request = 1; -} - -void getch2_enable(void){ - if (getch2_enabled) - return; - - // handlers to fix terminal settings - setsigaction(SIGCONT, continue_sighandler, 0, true); - setsigaction(SIGTSTP, stop_sighandler, SA_RESETHAND, false); - setsigaction(SIGINT, quit_request_sighandler, SA_RESETHAND, false); - setsigaction(SIGQUIT, quit_request_sighandler, SA_RESETHAND, false); - setsigaction(SIGTERM, quit_request_sighandler, SA_RESETHAND, false); - setsigaction(SIGTTIN, SIG_IGN, 0, true); - - do_activate_getch2(); - - getch2_enabled = 1; -} - -void getch2_disable(void){ - if (!getch2_enabled) - return; - - // restore signals - setsigaction(SIGCONT, SIG_DFL, 0, false); - setsigaction(SIGTSTP, SIG_DFL, 0, false); - setsigaction(SIGINT, SIG_DFL, 0, false); - setsigaction(SIGQUIT, SIG_DFL, 0, false); - setsigaction(SIGTERM, SIG_DFL, 0, false); - setsigaction(SIGTTIN, SIG_DFL, 0, false); - - do_deactivate_getch2(); - - getch2_enabled = 0; -} diff --git a/osdep/getch2.h b/osdep/getch2.h deleted file mode 100644 index 76cedd9928..0000000000 --- a/osdep/getch2.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * GyS-TermIO v2.0 (for GySmail v3) - * a very small replacement of ncurses library - * - * copyright (C) 1999 A'rpi/ESP-team - * - * This file is part of MPlayer. - * - * MPlayer is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * MPlayer is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with MPlayer; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -#ifndef MPLAYER_GETCH2_H -#define MPLAYER_GETCH2_H - -#include - -/* Screen size. Initialized by load_termcap() and get_screen_size() */ -extern int screen_width; -extern int screen_height; - -/* Termcap code to erase to end of line */ -extern char * erase_to_end_of_line; - -/* Get screen-size using IOCTL call. */ -void get_screen_size(void); - -/* Load key definitions from the TERMCAP database. 'termtype' can be NULL */ -int load_termcap(char *termtype); - -/* Initialize getch2 */ -void getch2_enable(void); -void getch2_disable(void); - -/* Enable and disable STDIN line-buffering */ -void getch2_poll(void); - -/* Read a character or a special key code (see keycodes.h) */ -struct input_ctx; -bool getch2(struct input_ctx *ictx); - -#if defined(__MINGW32__) -// slave cmd function for Windows -int mp_input_slave_cmd_func(int fd,char* dest,int size); -#define USE_FD0_CMD_SELECT 0 -#define MP_INPUT_SLAVE_CMD_FUNC mp_input_slave_cmd_func -#else -#define USE_FD0_CMD_SELECT 1 -#define MP_INPUT_SLAVE_CMD_FUNC NULL -#endif - -#endif /* MPLAYER_GETCH2_H */ diff --git a/osdep/terminal-unix.c b/osdep/terminal-unix.c new file mode 100644 index 0000000000..583ab162eb --- /dev/null +++ b/osdep/terminal-unix.c @@ -0,0 +1,578 @@ +/* + * GyS-TermIO v2.0 (for GySmail v3) + * a very small replacement of ncurses library + * + * copyright (C) 1999 A'rpi/ESP-team + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +#if HAVE_TERMIOS +#if HAVE_TERMIOS_H +#include +#endif +#if HAVE_SYS_TERMIOS_H +#include +#endif +#endif + +#include + +#include "common/common.h" +#include "bstr/bstr.h" +#include "input/input.h" +#include "input/keycodes.h" +#include "terminal.h" + +#if HAVE_TERMIOS +static volatile struct termios tio_orig; +static volatile int tio_orig_set; +#endif + +int screen_width = 80; +int screen_height = 24; +char * erase_to_end_of_line = NULL; + +typedef struct { + char *cap; + int len; + int code; + char chars[8]; +} keycode_st; + +typedef struct { + keycode_st *map; + int len; + int cap; +} keycode_map; + +static keycode_map getch2_keys; + +#if HAVE_TERMINFO || HAVE_TERMCAP + +static char *term_rmkx = NULL; +static char *term_smkx = NULL; + +#if HAVE_TERMINFO +#include +#endif +#include + +#endif + +static keycode_st *keys_push(char *p, int code) { + if (getch2_keys.len == getch2_keys.cap) { + getch2_keys.cap *= 2; + if (getch2_keys.cap == 0) + getch2_keys.cap = 32; + + getch2_keys.map = realloc(getch2_keys.map, sizeof(keycode_st) * getch2_keys.cap); + } + + keycode_st *st = &getch2_keys.map[getch2_keys.len++]; + st->cap = NULL; + st->len = strlen(p); + st->code = code; + strncpy(st->chars, p, 8); + + return st; +} + +static int keys_count_matches(char *buf, int buflen) { + int count = 0; + if (buflen < 0) + buflen = strlen(buf); + + for (int i = 0; i < getch2_keys.len; i++) { + keycode_st *st = &getch2_keys.map[i]; + int len = MPMIN(buflen, st->len); + + if (memcmp(buf, st->chars, len) == 0) + count++; + } + return count; +} + +static keycode_st *keys_search(char *buf, int buflen) { + if (buflen < 0) + buflen = strlen(buf); + + for (int i = 0; i < getch2_keys.len; i++) { + keycode_st *st = &getch2_keys.map[i]; + + if (buflen >= st->len && memcmp(buf, st->chars, st->len) == 0) + return st; + } + return NULL; +} + +/* pushes only if there is no duplicate. + important as we only consider keys if the matches are unique. */ +static keycode_st* keys_push_once(char *p, int code) { + keycode_st *st = keys_search(p, -1); + if (!st) + return keys_push(p, code); + return st; +} + +#if HAVE_TERMINFO || HAVE_TERMCAP + +typedef struct { + char *buf; + char *pos; + int cap; +} buf_st; +static buf_st termcap_buf; + +static void ensure_cap(buf_st *buf, int cap) { + if (buf->pos - buf->buf < cap) { + ptrdiff_t diff = buf->pos - buf->buf; + buf->cap += cap; + buf->buf = realloc(buf->buf, buf->cap); + buf->pos = buf->buf + diff; + } +} + +static char *termcap_get(char *id) { + ensure_cap(&termcap_buf, 1024); + return tgetstr(id, &termcap_buf.pos); +} + +typedef struct { + char *id; + int code; +} cap_key_pair; + +#if 0 +#include +#include + +static void debug_keycode(keycode_st *st) { + if (!st) + return; + + char buf[128]; /* worst case should be 70 bytes */ + unsigned char *b = &buf[0]; + unsigned char *p = &st->chars[0]; + + if (st->cap) + b += sprintf(b, "%s: ", st->cap); + + for(; *p; p++) { + if (*p == 27) + b += sprintf(b, "\\e"); + else if (*p < 27) + b += sprintf(b, "^%c", '@' + *p); + else if (!isgraph(*p)) + b += sprintf(b, "\\x%02x", (unsigned int)*p); + else + b += sprintf(b, "%c", *p); + } + fprintf(stderr, "%s\n", buf); +} +#endif + +static void termcap_add(cap_key_pair pair) { + char *p = termcap_get(pair.id); + if (p) { + keycode_st *st = keys_push_once(p, pair.code); + if (st) + st->cap = pair.id; + /* debug_keycode(st); */ + } +} + +static void termcap_add_extra_f_keys(void) { + char capbuf[3]; + for (int i = 11; i < 0x20; i++) { + unsigned char c; + if (i < 20) { /* 1-9 */ + c = '0' + (i - 10); + } else { /* A-Z */ + c = 'A' + (i - 20); + } + + sprintf(&capbuf[0], "F%c", c); + + char *p = termcap_get(capbuf); + if (p) + keys_push_once(p, MP_KEY_F+i); + else + break; /* unlikely that the database has further keys */ + } +} + +#endif + +int load_termcap(char *termtype){ +#if HAVE_TERMINFO || HAVE_TERMCAP + +#if HAVE_TERMINFO + use_env(TRUE); + int ret; + if (setupterm(termtype, 1, &ret) != OK) { + /* try again, with with "ansi" terminal if it was unset before */ + if (!termtype) + termtype = getenv("TERM"); + if (!termtype || *termtype == '\0') + termtype = "ansi"; + + if (setupterm(termtype, 1, &ret) != OK) { + if (ret < 0) { + printf("Could not access the 'terminfo' data base.\n"); + return 0; + } else { + printf("Couldn't use terminal `%s' for input.\n", termtype); + return 0; + } + } + } +#else + static char term_buffer[2048]; + if (!termtype) termtype = getenv("TERM"); + if (!termtype) termtype = "ansi"; + int success = tgetent(term_buffer, termtype); + if (success < 0) { + printf("Could not access the 'termcap' data base.\n"); + return 0; + } else if (success == 0) { + printf("Terminal type `%s' is not defined.\n", termtype); + return 0; + } +#endif + ensure_cap(&termcap_buf, 2048); + + static char term_buf[64]; + char *buf_ptr = &term_buf[0]; + + erase_to_end_of_line = tgetstr("ce", &buf_ptr); + + screen_width = tgetnum("co"); + screen_height = tgetnum("li"); + if (screen_width < 1 || screen_width > 255) + screen_width = 80; + if (screen_height < 1 || screen_height > 255) + screen_height = 24; + + term_smkx = tgetstr("ks", &buf_ptr); + term_rmkx = tgetstr("ke", &buf_ptr); + + cap_key_pair keys[] = { + {"kP", MP_KEY_PGUP}, {"kN", MP_KEY_PGDWN}, {"kh", MP_KEY_HOME}, {"kH", MP_KEY_END}, + {"kI", MP_KEY_INS}, {"kD", MP_KEY_DEL}, /* on PC keyboards */ {"@7", MP_KEY_END}, + + {"kl", MP_KEY_LEFT}, {"kd", MP_KEY_DOWN}, {"ku", MP_KEY_UP}, {"kr", MP_KEY_RIGHT}, + + {"do", MP_KEY_ENTER}, + {"kb", MP_KEY_BS}, + + {"k1", MP_KEY_F+1}, {"k2", MP_KEY_F+2}, {"k3", MP_KEY_F+3}, + {"k4", MP_KEY_F+4}, {"k5", MP_KEY_F+5}, {"k6", MP_KEY_F+6}, + {"k7", MP_KEY_F+7}, {"k8", MP_KEY_F+8}, {"k9", MP_KEY_F+9}, + {"k;", MP_KEY_F+10}, {"k0", MP_KEY_F+0}, + + /* K2 is the keypad center */ + {"K2", MP_KEY_KP5}, + + /* EOL */ + {NULL}, + }; + for (int i = 0; keys[i].id; i++) { + termcap_add(keys[i]); + } + termcap_add_extra_f_keys(); +#endif + + /* special cases (hardcoded, no need for HAVE_TERMCAP) */ + + /* it's important to use keys_push_once as we can't have duplicates */ + + /* many terminals, for emacs compatibility, use 0x7f instead of ^H + when typing backspace, even when the 'kb' cap says otherwise. */ + keys_push_once("\177", MP_KEY_BS); + + /* mintty always sends these when using the numpad arrows, + even in application mode, for telling them from regular arrows. */ + keys_push_once("\033[A", MP_KEY_UP); + keys_push_once("\033[B", MP_KEY_DOWN); + keys_push_once("\033[C", MP_KEY_RIGHT); + keys_push_once("\033[D", MP_KEY_LEFT); + + /* mintty uses this instead of the "K2" cap for keypad center */ + keys_push_once("\033OE", MP_KEY_KP5); + + return getch2_keys.len; +} + +void get_screen_size(void) { + struct winsize ws; + if (ioctl(0, TIOCGWINSZ, &ws) < 0 || !ws.ws_row || !ws.ws_col) + return; + + screen_width = ws.ws_col; + screen_height = ws.ws_row; +} + +#define BUF_LEN 256 + +static unsigned char getch2_buf[BUF_LEN]; +static int getch2_len = 0; +static int getch2_pos = 0; +static enum { + STATE_INITIAL, + STATE_UTF8, +} state = STATE_INITIAL; +static int utf8_len = 0; + +static void walk_buf(unsigned int count) { + if (!(count < BUF_LEN && count <= getch2_len)) + abort(); + + memmove(&getch2_buf[0], &getch2_buf[count], getch2_len - count); + getch2_len -= count; + getch2_pos -= count; + if (getch2_pos < 0) + getch2_pos = 0; +} + +bool getch2(struct input_ctx *input_ctx) +{ + int retval = read(0, &getch2_buf[getch2_pos], BUF_LEN - getch2_len - getch2_pos); + /* Return false on EOF to stop running select() on the FD, as it'd + * trigger all the time. Note that it's possible to get temporary + * EOF on terminal if the user presses ctrl-d, but that shouldn't + * happen if the terminal state change done in getch2_enable() + * works. + */ + if (retval == 0) + return false; + if (retval == -1) + return errno != EBADF && errno != EINVAL; + getch2_len += retval; + + while (getch2_pos < getch2_len) { + unsigned char c = getch2_buf[getch2_pos++]; + + switch (state) { + case STATE_INITIAL: { + int match_count = keys_count_matches(&getch2_buf[0], getch2_len); + if (match_count == 1) { + keycode_st *st = keys_search(&getch2_buf[0], getch2_len); + + if (st) { + mp_input_put_key(input_ctx, st->code); + walk_buf(st->len); + } /* else this is still a partial (but unique) match */ + + continue; + } else if (match_count > 1) { + continue; /* need more bytes to disambiguate */ + } else { + /* backtrack, send as UTF-8 */ + getch2_pos = 1; + c = getch2_buf[0]; + } + utf8_len = bstr_parse_utf8_code_length(c); + + if (utf8_len > 1) { + state = STATE_UTF8; + } else if (utf8_len == 1) { + switch (c) { + case 0x1b: /* ESC that's not part of escape sequence */ + /* only if ESC was typed twice, otherwise ignore it */ + if (getch2_len > 1 && getch2_buf[1] == 0x1b) { + walk_buf(1); /* eat the second ESC */ + mp_input_put_key(input_ctx, MP_KEY_ESC); + } + break; + default: + mp_input_put_key(input_ctx, c); + } + walk_buf(1); + } else + walk_buf(getch2_pos); + + break; + } + case STATE_UTF8: { + if (getch2_pos < utf8_len) /* need more bytes */ + continue; + + struct bstr s = {getch2_buf, utf8_len}; + int unicode = bstr_decode_utf8(s, NULL); + + if (unicode > 0) { + mp_input_put_key(input_ctx, unicode); + } + walk_buf(utf8_len); + state = STATE_INITIAL; + continue; + } + } + } + + return true; +} + +static volatile int getch2_active = 0; +static volatile int getch2_enabled = 0; + +static void do_activate_getch2(void) +{ + if (getch2_active) + return; + +#if HAVE_TERMINFO || HAVE_TERMCAP + if (term_smkx) + tputs(term_smkx, 1, putchar); +#endif + +#if HAVE_TERMIOS + struct termios tio_new; + tcgetattr(0,&tio_new); + + if (!tio_orig_set) { + tio_orig = tio_new; + tio_orig_set = 1; + } + + tio_new.c_lflag &= ~(ICANON|ECHO); /* Clear ICANON and ECHO. */ + tio_new.c_cc[VMIN] = 1; + tio_new.c_cc[VTIME] = 0; + tcsetattr(0,TCSANOW,&tio_new); +#endif + + getch2_active = 1; +} + +static void do_deactivate_getch2(void) +{ + if (!getch2_active) + return; + +#if HAVE_TERMINFO || HAVE_TERMCAP + if (term_rmkx) + tputs(term_rmkx, 1, putchar); +#endif + +#if HAVE_TERMIOS + if (tio_orig_set) { + // once set, it will never be set again + // so we can cast away volatile here + tcsetattr(0, TCSANOW, (const struct termios *) &tio_orig); + } +#endif + + getch2_active = 0; +} + +// sigaction wrapper +static int setsigaction(int signo, void (*handler) (int), + int flags, bool do_mask) +{ + struct sigaction sa; + sa.sa_handler = handler; + + if(do_mask) + sigfillset(&sa.sa_mask); + else + sigemptyset(&sa.sa_mask); + + sa.sa_flags = flags; + return sigaction(signo, &sa, NULL); +} + +void getch2_poll(void){ + if (!getch2_enabled) + return; + + // check if stdin is in the foreground process group + int newstatus = (tcgetpgrp(0) == getpgrp()); + + // and activate getch2 if it is, deactivate otherwise + if (newstatus) + do_activate_getch2(); + else + do_deactivate_getch2(); +} + +static void stop_sighandler(int signum) +{ + do_deactivate_getch2(); + + // note: for this signal, we use SA_RESETHAND but do NOT mask signals + // so this will invoke the default handler + raise(SIGTSTP); +} + +static void continue_sighandler(int signum) +{ + // SA_RESETHAND has reset SIGTSTP, so we need to restore it here + setsigaction(SIGTSTP, stop_sighandler, SA_RESETHAND, false); + + getch2_poll(); +} + +static void quit_request_sighandler(int signum) +{ + do_deactivate_getch2(); + + async_quit_request = 1; +} + +void getch2_enable(void){ + if (getch2_enabled) + return; + + // handlers to fix terminal settings + setsigaction(SIGCONT, continue_sighandler, 0, true); + setsigaction(SIGTSTP, stop_sighandler, SA_RESETHAND, false); + setsigaction(SIGINT, quit_request_sighandler, SA_RESETHAND, false); + setsigaction(SIGQUIT, quit_request_sighandler, SA_RESETHAND, false); + setsigaction(SIGTERM, quit_request_sighandler, SA_RESETHAND, false); + setsigaction(SIGTTIN, SIG_IGN, 0, true); + + do_activate_getch2(); + + getch2_enabled = 1; +} + +void getch2_disable(void){ + if (!getch2_enabled) + return; + + // restore signals + setsigaction(SIGCONT, SIG_DFL, 0, false); + setsigaction(SIGTSTP, SIG_DFL, 0, false); + setsigaction(SIGINT, SIG_DFL, 0, false); + setsigaction(SIGQUIT, SIG_DFL, 0, false); + setsigaction(SIGTERM, SIG_DFL, 0, false); + setsigaction(SIGTTIN, SIG_DFL, 0, false); + + do_deactivate_getch2(); + + getch2_enabled = 0; +} diff --git a/osdep/terminal-win.c b/osdep/terminal-win.c new file mode 100644 index 0000000000..a56d1a409c --- /dev/null +++ b/osdep/terminal-win.c @@ -0,0 +1,194 @@ +/* Windows TermIO + * + * copyright (C) 2003 Sascha Sommer + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +// See http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/WinUI/WindowsUserInterface/UserInput/VirtualKeyCodes.asp +// for additional virtual keycodes + + +#include "config.h" +#include +#include +#include +#include +#include "input/keycodes.h" +#include "input/input.h" +#include "terminal.h" + +int mp_input_slave_cmd_func(int fd, char *dest, int size) +{ + DWORD retval; + HANDLE in = GetStdHandle(STD_INPUT_HANDLE); + if (PeekNamedPipe(in, NULL, size, &retval, NULL, NULL)) { + if (size > retval) + size = retval; + } else { + if (WaitForSingleObject(in, 0)) + size = 0; + } + if (!size) + return MP_INPUT_NOTHING; + ReadFile(in, dest, size, &retval, NULL); + if (retval) + return retval; + return MP_INPUT_NOTHING; +} + +int screen_width = 80; +int screen_height = 24; +char *erase_to_end_of_line = NULL; + +void get_screen_size(void) +{ + CONSOLE_SCREEN_BUFFER_INFO cinfo; + if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cinfo)) { + screen_width = cinfo.dwMaximumWindowSize.X; + screen_height = cinfo.dwMaximumWindowSize.Y; + } +} + +int load_termcap(char *termtype) +{ + return 0; +} + +static HANDLE in; +static int getch2_status = 0; + +static int getch2_internal(void) +{ + INPUT_RECORD eventbuffer[128]; + DWORD retval; + int i = 0; + if (!getch2_status) { + // supports e.g. MinGW xterm, unfortunately keys are only received after + // enter was pressed. + uint8_t c; + if (!PeekNamedPipe(in, NULL, 1, &retval, NULL, NULL) || !retval) + return -1; + ReadFile(in, &c, 1, &retval, NULL); + return retval == 1 ? c : -1; + } + /*check if there are input events*/ + if (!GetNumberOfConsoleInputEvents(in, &retval)) { + printf("getch2: can't get number of input events: %i\n", + (int)GetLastError()); + return -1; + } + if (retval <= 0) + return -1; + + /*read all events*/ + if (!ReadConsoleInput(in, eventbuffer, 128, &retval)) { + printf("getch: can't read input events\n"); + return -1; + } + + /*filter out keyevents*/ + for (i = 0; i < retval; i++) { + switch (eventbuffer[i].EventType) { + case KEY_EVENT: + /*only a pressed key is interresting for us*/ + if (eventbuffer[i].Event.KeyEvent.bKeyDown == TRUE) { + /*check for special keys*/ + switch (eventbuffer[i].Event.KeyEvent.wVirtualKeyCode) { + case VK_HOME: + return MP_KEY_HOME; + case VK_END: + return MP_KEY_END; + case VK_DELETE: + return MP_KEY_DEL; + case VK_INSERT: + return MP_KEY_INS; + case VK_BACK: + return MP_KEY_BS; + case VK_PRIOR: + return MP_KEY_PGUP; + case VK_NEXT: + return MP_KEY_PGDWN; + case VK_RETURN: + return MP_KEY_ENTER; + case VK_ESCAPE: + return MP_KEY_ESC; + case VK_LEFT: + return MP_KEY_LEFT; + case VK_UP: + return MP_KEY_UP; + case VK_RIGHT: + return MP_KEY_RIGHT; + case VK_DOWN: + return MP_KEY_DOWN; + case VK_SHIFT: + continue; + } + /*check for function keys*/ + if (0x87 >= eventbuffer[i].Event.KeyEvent.wVirtualKeyCode && + eventbuffer[i].Event.KeyEvent.wVirtualKeyCode >= 0x70) + return MP_KEY_F + 1 + + eventbuffer[i].Event.KeyEvent.wVirtualKeyCode - 0x70; + + /*only characters should be remaining*/ + //printf("getch2: YOU PRESSED \"%c\" \n",eventbuffer[i].Event.KeyEvent.uChar.AsciiChar); + return eventbuffer[i].Event.KeyEvent.uChar.AsciiChar; + } + break; + + case MOUSE_EVENT: + case WINDOW_BUFFER_SIZE_EVENT: + case FOCUS_EVENT: + case MENU_EVENT: + default: + //printf("getch2: unsupported event type"); + break; + } + } + return -1; +} + +bool getch2(struct input_ctx *ctx) +{ + int r = getch2_internal(); + if (r >= 0) + mp_input_put_key(ctx, r); + return true; +} + +void getch2_poll(void) +{ +} + +void getch2_enable(void) +{ + DWORD retval; + in = GetStdHandle(STD_INPUT_HANDLE); + if (!GetNumberOfConsoleInputEvents(in, &retval)) { + printf("getch2: %i can't get number of input events " + "[disabling console input]\n", (int)GetLastError()); + getch2_status = 0; + } else + getch2_status = 1; +} + +void getch2_disable(void) +{ + if (!getch2_status) + return; // already disabled / never enabled + getch2_status = 0; +} diff --git a/osdep/terminal.h b/osdep/terminal.h new file mode 100644 index 0000000000..76cedd9928 --- /dev/null +++ b/osdep/terminal.h @@ -0,0 +1,63 @@ +/* + * GyS-TermIO v2.0 (for GySmail v3) + * a very small replacement of ncurses library + * + * copyright (C) 1999 A'rpi/ESP-team + * + * This file is part of MPlayer. + * + * MPlayer is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * MPlayer is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with MPlayer; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef MPLAYER_GETCH2_H +#define MPLAYER_GETCH2_H + +#include + +/* Screen size. Initialized by load_termcap() and get_screen_size() */ +extern int screen_width; +extern int screen_height; + +/* Termcap code to erase to end of line */ +extern char * erase_to_end_of_line; + +/* Get screen-size using IOCTL call. */ +void get_screen_size(void); + +/* Load key definitions from the TERMCAP database. 'termtype' can be NULL */ +int load_termcap(char *termtype); + +/* Initialize getch2 */ +void getch2_enable(void); +void getch2_disable(void); + +/* Enable and disable STDIN line-buffering */ +void getch2_poll(void); + +/* Read a character or a special key code (see keycodes.h) */ +struct input_ctx; +bool getch2(struct input_ctx *ictx); + +#if defined(__MINGW32__) +// slave cmd function for Windows +int mp_input_slave_cmd_func(int fd,char* dest,int size); +#define USE_FD0_CMD_SELECT 0 +#define MP_INPUT_SLAVE_CMD_FUNC mp_input_slave_cmd_func +#else +#define USE_FD0_CMD_SELECT 1 +#define MP_INPUT_SLAVE_CMD_FUNC NULL +#endif + +#endif /* MPLAYER_GETCH2_H */ -- cgit v1.2.3