/* SDL - Simple DirectMedia Layer Copyright (C) 1997-2006 Sam Lantinga This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Sam Lantinga slouken@libsdl.org */ #include "SDL_config.h" /* Handle the event stream, converting console events into SDL events */ #include #include #include #include #include #include #include /* For parsing /proc */ #include #include #include #include #include #include "SDL_mutex.h" #include "../SDL_sysvideo.h" #include "../../events/SDL_sysevents.h" #include "../../events/SDL_events_c.h" #include "SDL_gsvideo.h" #include "SDL_gsevents_c.h" #include "SDL_gskeys.h" #ifndef GPM_NODE_FIFO #define GPM_NODE_FIFO "/dev/gpmdata" #endif /* The translation tables from a console scancode to a SDL keysym */ #define NUM_VGAKEYMAPS (1<= 0) && (saved_kbd_mode >= 0)); } int GS_EnterGraphicsMode(_THIS) { struct termios keyboard_termios; /* Set medium-raw keyboard mode */ if ( (keyboard_fd >= 0) && !GS_InGraphicsMode(this) ) { /* Switch to the correct virtual terminal */ if ( current_vt > 0 ) { struct vt_stat vtstate; if ( ioctl(keyboard_fd, VT_GETSTATE, &vtstate) == 0 ) { saved_vt = vtstate.v_active; } if ( ioctl(keyboard_fd, VT_ACTIVATE, current_vt) == 0 ) { ioctl(keyboard_fd, VT_WAITACTIVE, current_vt); } } /* Set the terminal input mode */ if ( tcgetattr(keyboard_fd, &saved_kbd_termios) < 0 ) { SDL_SetError("Unable to get terminal attributes"); if ( keyboard_fd > 0 ) { close(keyboard_fd); } keyboard_fd = -1; return(-1); } if ( ioctl(keyboard_fd, KDGKBMODE, &saved_kbd_mode) < 0 ) { SDL_SetError("Unable to get current keyboard mode"); if ( keyboard_fd > 0 ) { close(keyboard_fd); } keyboard_fd = -1; return(-1); } keyboard_termios = saved_kbd_termios; keyboard_termios.c_lflag &= ~(ICANON | ECHO | ISIG); keyboard_termios.c_iflag &= ~(ISTRIP | IGNCR | ICRNL | INLCR | IXOFF | IXON); keyboard_termios.c_cc[VMIN] = 0; keyboard_termios.c_cc[VTIME] = 0; if (tcsetattr(keyboard_fd, TCSAFLUSH, &keyboard_termios) < 0) { GS_CloseKeyboard(this); SDL_SetError("Unable to set terminal attributes"); return(-1); } /* This will fail if we aren't root or this isn't our tty */ if ( ioctl(keyboard_fd, KDSKBMODE, K_MEDIUMRAW) < 0 ) { GS_CloseKeyboard(this); SDL_SetError("Unable to set keyboard in raw mode"); return(-1); } if ( ioctl(keyboard_fd, KDSETMODE, KD_GRAPHICS) < 0 ) { GS_CloseKeyboard(this); SDL_SetError("Unable to set keyboard in graphics mode"); return(-1); } } return(keyboard_fd); } void GS_LeaveGraphicsMode(_THIS) { if ( GS_InGraphicsMode(this) ) { ioctl(keyboard_fd, KDSETMODE, KD_TEXT); ioctl(keyboard_fd, KDSKBMODE, saved_kbd_mode); tcsetattr(keyboard_fd, TCSAFLUSH, &saved_kbd_termios); saved_kbd_mode = -1; /* Head back over to the original virtual terminal */ if ( saved_vt > 0 ) { ioctl(keyboard_fd, VT_ACTIVATE, saved_vt); } } } void GS_CloseKeyboard(_THIS) { if ( keyboard_fd >= 0 ) { GS_LeaveGraphicsMode(this); if ( keyboard_fd > 0 ) { close(keyboard_fd); } } keyboard_fd = -1; } int GS_OpenKeyboard(_THIS) { /* Open only if not already opened */ if ( keyboard_fd < 0 ) { char *tty0[] = { "/dev/tty0", "/dev/vc/0", NULL }; char *vcs[] = { "/dev/vc/%d", "/dev/tty%d", NULL }; int i, tty0_fd; /* Try to query for a free virtual terminal */ tty0_fd = -1; for ( i=0; tty0[i] && (tty0_fd < 0); ++i ) { tty0_fd = open(tty0[i], O_WRONLY, 0); } if ( tty0_fd < 0 ) { tty0_fd = dup(0); /* Maybe stdin is a VT? */ } ioctl(tty0_fd, VT_OPENQRY, ¤t_vt); close(tty0_fd); if ( (geteuid() == 0) && (current_vt > 0) ) { for ( i=0; vcs[i] && (keyboard_fd < 0); ++i ) { char vtpath[12]; SDL_snprintf(vtpath, SDL_arraysize(vtpath), vcs[i], current_vt); keyboard_fd = open(vtpath, O_RDWR, 0); #ifdef DEBUG_KEYBOARD fprintf(stderr, "vtpath = %s, fd = %d\n", vtpath, keyboard_fd); #endif /* DEBUG_KEYBOARD */ /* This needs to be our controlling tty so that the kernel ioctl() calls work */ if ( keyboard_fd >= 0 ) { tty0_fd = open("/dev/tty", O_RDWR, 0); if ( tty0_fd >= 0 ) { ioctl(tty0_fd, TIOCNOTTY, 0); close(tty0_fd); } } } } if ( keyboard_fd < 0 ) { /* Last resort, maybe our tty is a usable VT */ current_vt = 0; keyboard_fd = open("/dev/tty", O_RDWR); } #ifdef DEBUG_KEYBOARD fprintf(stderr, "Current VT: %d\n", current_vt); #endif saved_kbd_mode = -1; /* Make sure that our input is a console terminal */ { int dummy; if ( ioctl(keyboard_fd, KDGKBMODE, &dummy) < 0 ) { close(keyboard_fd); keyboard_fd = -1; SDL_SetError("Unable to open a console terminal"); } } /* Set up keymap */ GS_vgainitkeymaps(keyboard_fd); } return(keyboard_fd); } static enum { MOUSE_NONE = -1, MOUSE_GPM, /* Note: GPM uses the MSC protocol */ MOUSE_PS2, MOUSE_IMPS2, MOUSE_MS, MOUSE_BM, NUM_MOUSE_DRVS } mouse_drv = MOUSE_NONE; void GS_CloseMouse(_THIS) { if ( mouse_fd > 0 ) { close(mouse_fd); } mouse_fd = -1; } /* Returns processes listed in /proc with the desired name */ static int find_pid(DIR *proc, const char *wanted_name) { struct dirent *entry; int pid; /* First scan proc for the gpm process */ pid = 0; while ( (pid == 0) && ((entry=readdir(proc)) != NULL) ) { if ( isdigit(entry->d_name[0]) ) { FILE *status; char path[PATH_MAX]; char name[PATH_MAX]; SDL_snprintf(path, SDL_arraysize(path), "/proc/%s/status", entry->d_name); status=fopen(path, "r"); if ( status ) { name[0] = '\0'; fscanf(status, "Name: %s", name); if ( SDL_strcmp(name, wanted_name) == 0 ) { pid = atoi(entry->d_name); } fclose(status); } } } return pid; } /* Returns true if /dev/gpmdata is being written to by gpm */ static int gpm_available(void) { int available; DIR *proc; int pid; int cmdline, len, arglen; char path[PATH_MAX]; char args[PATH_MAX], *arg; /* Don't bother looking if the fifo isn't there */ if ( access(GPM_NODE_FIFO, F_OK) < 0 ) { return(0); } available = 0; proc = opendir("/proc"); if ( proc ) { while ( (pid=find_pid(proc, "gpm")) > 0 ) { SDL_snprintf(path, SDL_arraysize(path), "/proc/%d/cmdline", pid); cmdline = open(path, O_RDONLY, 0); if ( cmdline >= 0 ) { len = read(cmdline, args, sizeof(args)); arg = args; while ( len > 0 ) { if ( SDL_strcmp(arg, "-R") == 0 ) { available = 1; } arglen = SDL_strlen(arg)+1; len -= arglen; arg += arglen; } close(cmdline); } } closedir(proc); } return available; } /* rcg06112001 Set up IMPS/2 mode, if possible. This gives * us access to the mousewheel, etc. Returns zero if * writes to device failed, but you still need to query the * device to see which mode it's actually in. */ static int set_imps2_mode(int fd) { /* If you wanted to control the mouse mode (and we do :) ) ... Set IMPS/2 protocol: {0xf3,200,0xf3,100,0xf3,80} Reset mouse device: {0xFF} */ Uint8 set_imps2[] = {0xf3, 200, 0xf3, 100, 0xf3, 80}; Uint8 reset = 0xff; fd_set fdset; struct timeval tv; int retval = 0; if ( write(fd, &set_imps2, sizeof(set_imps2)) == sizeof(set_imps2) ) { if (write(fd, &reset, sizeof (reset)) == sizeof (reset) ) { retval = 1; } } /* Get rid of any chatter from the above */ FD_ZERO(&fdset); FD_SET(fd, &fdset); tv.tv_sec = 0; tv.tv_usec = 0; while ( select(fd+1, &fdset, 0, 0, &tv) > 0 ) { char temp[32]; read(fd, temp, sizeof(temp)); } return retval; } /* Returns true if the mouse uses the IMPS/2 protocol */ static int detect_imps2(int fd) { int imps2; imps2 = 0; if ( SDL_getenv("SDL_MOUSEDEV_IMPS2") ) { imps2 = 1; } if ( ! imps2 ) { Uint8 query_ps2 = 0xF2; fd_set fdset; struct timeval tv; /* Get rid of any mouse motion noise */ FD_ZERO(&fdset); FD_SET(fd, &fdset); tv.tv_sec = 0; tv.tv_usec = 0; while ( select(fd+1, &fdset, 0, 0, &tv) > 0 ) { char temp[32]; read(fd, temp, sizeof(temp)); } /* Query for the type of mouse protocol */ if ( write(fd, &query_ps2, sizeof (query_ps2)) == sizeof (query_ps2)) { Uint8 ch = 0; /* Get the mouse protocol response */ do { FD_ZERO(&fdset); FD_SET(fd, &fdset); tv.tv_sec = 1; tv.tv_usec = 0; if ( select(fd+1, &fdset, 0, 0, &tv) < 1 ) { break; } } while ( (read(fd, &ch, sizeof (ch)) == sizeof (ch)) && ((ch == 0xFA) || (ch == 0xAA)) ); /* Experimental values (Logitech wheelmouse) */ #ifdef DEBUG_MOUSE fprintf(stderr, "Last mouse mode: 0x%x\n", ch); #endif if ( ch == 3 ) { imps2 = 1; } } } return imps2; } int GS_OpenMouse(_THIS) { int i; const char *mousedev; const char *mousedrv; mousedrv = SDL_getenv("SDL_MOUSEDRV"); mousedev = SDL_getenv("SDL_MOUSEDEV"); mouse_fd = -1; /* STD MICE */ if ( mousedev == NULL ) { /* FIXME someday... allow multiple mice in this driver */ char *ps2mice[] = { "/dev/input/mice", "/dev/usbmouse", "/dev/psaux", NULL }; /* First try to use GPM in repeater mode */ if ( mouse_fd < 0 ) { if ( gpm_available() ) { mouse_fd = open(GPM_NODE_FIFO, O_RDONLY, 0); if ( mouse_fd >= 0 ) { #ifdef DEBUG_MOUSE fprintf(stderr, "Using GPM mouse\n"); #endif mouse_drv = MOUSE_GPM; } } } /* Now try to use a modern PS/2 mouse */ for ( i=0; (mouse_fd < 0) && ps2mice[i]; ++i ) { mouse_fd = open(ps2mice[i], O_RDWR, 0); if (mouse_fd < 0) { mouse_fd = open(ps2mice[i], O_RDONLY, 0); } if (mouse_fd >= 0) { /* rcg06112001 Attempt to set IMPS/2 mode */ if ( i == 0 ) { set_imps2_mode(mouse_fd); } if (detect_imps2(mouse_fd)) { #ifdef DEBUG_MOUSE fprintf(stderr, "Using IMPS2 mouse\n"); #endif mouse_drv = MOUSE_IMPS2; } else { mouse_drv = MOUSE_PS2; #ifdef DEBUG_MOUSE fprintf(stderr, "Using PS2 mouse\n"); #endif } } } /* Next try to use a PPC ADB port mouse */ if ( mouse_fd < 0 ) { mouse_fd = open("/dev/adbmouse", O_RDONLY, 0); if ( mouse_fd >= 0 ) { #ifdef DEBUG_MOUSE fprintf(stderr, "Using ADB mouse\n"); #endif mouse_drv = MOUSE_BM; } } } /* Default to a serial Microsoft mouse */ if ( mouse_fd < 0 ) { if ( mousedev == NULL ) { mousedev = "/dev/mouse"; } mouse_fd = open(mousedev, O_RDONLY, 0); if ( mouse_fd >= 0 ) { struct termios mouse_termios; /* Set the sampling speed to 1200 baud */ tcgetattr(mouse_fd, &mouse_termios); mouse_termios.c_iflag = IGNBRK | IGNPAR; mouse_termios.c_oflag = 0; mouse_termios.c_lflag = 0; mouse_termios.c_line = 0; mouse_termios.c_cc[VTIME] = 0; mouse_termios.c_cc[VMIN] = 1; mouse_termios.c_cflag = CREAD | CLOCAL | HUPCL; mouse_termios.c_cflag |= CS8; mouse_termios.c_cflag |= B1200; tcsetattr(mouse_fd, TCSAFLUSH, &mouse_termios); #ifdef DEBUG_MOUSE fprintf(stderr, "Using Microsoft mouse on %s\n", mousedev); #endif mouse_drv = MOUSE_MS; } } if ( mouse_fd < 0 ) { mouse_drv = MOUSE_NONE; } return(mouse_fd); } static int posted = 0; void GS_vgamousecallback(int button, int dx, int dy) { int button_1, button_3; int button_state; int state_changed; int i; Uint8 state; if ( dx || dy ) { posted += SDL_PrivateMouseMotion(0, 1, dx, dy); } /* Swap button 1 and 3 */ button_1 = (button & 0x04) >> 2; button_3 = (button & 0x01) << 2; button &= ~0x05; button |= (button_1|button_3); /* See what changed */ button_state = SDL_GetMouseState(NULL, NULL); state_changed = button_state ^ button; for ( i=0; i<8; ++i ) { if ( state_changed & (1<> 1 | /*Middle*/ (mousebuf[i] & 0x02) >> 1 | /*Right*/ (mousebuf[i] & 0x01) << 2; /*Left*/ dx = (mousebuf[i] & 0x10) ? mousebuf[i+1] - 256 : mousebuf[i+1]; dy = (mousebuf[i] & 0x20) ? -(mousebuf[i+2] - 256) : -mousebuf[i+2]; break; case MOUSE_IMPS2: /* Get current mouse state */ button = (mousebuf[i] & 0x04) >> 1 | /*Middle*/ (mousebuf[i] & 0x02) >> 1 | /*Right*/ (mousebuf[i] & 0x01) << 2 | /*Left*/ (mousebuf[i] & 0x40) >> 3 | /* 4 */ (mousebuf[i] & 0x80) >> 3; /* 5 */ dx = (mousebuf[i] & 0x10) ? mousebuf[i+1] - 256 : mousebuf[i+1]; dy = (mousebuf[i] & 0x20) ? -(mousebuf[i+2] - 256) : -mousebuf[i+2]; switch (mousebuf[i+3]&0x0F) { case 0x0E: /* DX = +1 */ case 0x02: /* DX = -1 */ break; case 0x0F: /* DY = +1 (map button 4) */ FB_vgamousecallback(button | (1<<3), 1, 0, 0); break; case 0x01: /* DY = -1 (map button 5) */ FB_vgamousecallback(button | (1<<4), 1, 0, 0); break; } break; case MOUSE_MS: /* Microsoft protocol has 0x40 in high byte */ if ( (mousebuf[i] & 0x40) != 0x40 ) { /* Go to next byte */ i -= (packetsize-1); continue; } /* Get current mouse state */ button = ((mousebuf[i] & 0x20) >> 3) | ((mousebuf[i] & 0x10) >> 4); dx = (signed char)(((mousebuf[i] & 0x03) << 6) | (mousebuf[i + 1] & 0x3F)); dy = (signed char)(((mousebuf[i] & 0x0C) << 4) | (mousebuf[i + 2] & 0x3F)); break; case MOUSE_BM: /* BusMouse protocol has 0xF8 in high byte */ if ( (mousebuf[i] & 0xF8) != 0x80 ) { /* Go to next byte */ i -= (packetsize-1); continue; } /* Get current mouse state */ button = (~mousebuf[i]) & 0x07; dx = (signed char)mousebuf[i+1]; dy = -(signed char)mousebuf[i+2]; break; case NUM_MOUSE_DRVS: /* Uh oh.. */ dx = 0; dy = 0; break; } GS_vgamousecallback(button, dx, dy); } if ( i < nread ) { SDL_memcpy(mousebuf, &mousebuf[i], (nread-i)); start = (nread-i); } else { start = 0; } return; } static void handle_keyboard(_THIS) { unsigned char keybuf[BUFSIZ]; int i, nread; int pressed; int scancode; SDL_keysym keysym; nread = read(keyboard_fd, keybuf, BUFSIZ); for ( i=0; i= 0 ) { FD_SET(keyboard_fd, &fdset); if ( max_fd < keyboard_fd ) { max_fd = keyboard_fd; } } if ( mouse_fd >= 0 ) { FD_SET(mouse_fd, &fdset); if ( max_fd < mouse_fd ) { max_fd = mouse_fd; } } if ( select(max_fd+1, &fdset, NULL, NULL, &zero) > 0 ) { if ( keyboard_fd >= 0 ) { if ( FD_ISSET(keyboard_fd, &fdset) ) { handle_keyboard(this); } } if ( mouse_fd >= 0 ) { if ( FD_ISSET(mouse_fd, &fdset) ) { handle_mouse(this); } } } } while ( posted ); } void GS_InitOSKeymap(_THIS) { int i; /* Initialize the Linux key translation table */ /* First get the ascii keys and others not well handled */ for (i=0; iscancode = scancode; keysym->sym = keymap[scancode]; keysym->mod = KMOD_NONE; /* If UNICODE is on, get the UNICODE value for the key */ keysym->unicode = 0; if ( SDL_TranslateUNICODE ) { int map; SDLMod modstate; modstate = SDL_GetModState(); map = 0; if ( modstate & KMOD_SHIFT ) { map |= (1<unicode=KVAL(vga_keymap[map][scancode]); } } else { keysym->unicode = KVAL(vga_keymap[map][scancode]); } } return(keysym); }