/* * * Copyright © 1999 Keith Packard * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Keith Packard not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. Keith Packard makes no * representations about the suitability of this software for any purpose. It * is provided "as is" without express or implied warranty. * * KEITH PACKARD DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL KEITH PACKARD BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. */ #ifdef HAVE_CONFIG_H #include #endif #include "kdrive.h" #include "inputstr.h" #define XK_PUBLISHING #include #if HAVE_X11_XF86KEYSYM_H #include #endif #include "kkeymap.h" #include #include static DeviceIntPtr pKdKeyboard, pKdPointer; #define MAX_MOUSE_DRIVERS 4 static const KdMouseFuncs *kdMouseFuncs[MAX_MOUSE_DRIVERS]; static int kdNMouseFuncs; static const KdKeyboardFuncs *kdKeyboardFuncs; static int kdBellPitch; static int kdBellDuration; static int kdLeds; static Bool kdInputEnabled; static Bool kdOffScreen; static unsigned long kdOffScreenTime; static KdMouseMatrix kdMouseMatrix = { {{1, 0, 0}, {0, 1, 0}} }; static int kdMouseButtonCount; int kdMinScanCode; int kdMaxScanCode; static int kdMinKeyCode; static int kdMaxKeyCode; static const int kdKeymapWidth = KD_MAX_WIDTH; KeySym kdKeymap[KD_MAX_LENGTH * KD_MAX_WIDTH]; static CARD8 kdModMap[MAP_LENGTH]; static KeySymsRec kdKeySyms; void KdResetInputMachine(void); #define KD_KEY_COUNT 248 CARD8 kdKeyState[KD_KEY_COUNT / 8]; #define IsKeyDown(key) ((kdKeyState[(key) >> 3] >> ((key) & 7)) & 1) #define KD_MAX_INPUT_FDS 8 typedef struct _kdInputFd { int type; int fd; void (*read) (int fd, void *closure); int (*enable) (int fd, void *closure); void (*disable) (int fd, void *closure); void *closure; } KdInputFd; static KdInputFd kdInputFds[KD_MAX_INPUT_FDS]; static int kdNumInputFds; static int kdInputTypeSequence; static void KdSigio(int sig) { int i; for (i = 0; i < kdNumInputFds; i++) (*kdInputFds[i].read) (kdInputFds[i].fd, kdInputFds[i].closure); } static void KdBlockSigio(void) { sigset_t set; sigemptyset(&set); sigaddset(&set, SIGIO); sigprocmask(SIG_BLOCK, &set, 0); } static void KdUnblockSigio(void) { sigset_t set; sigemptyset(&set); sigaddset(&set, SIGIO); sigprocmask(SIG_UNBLOCK, &set, 0); } #undef VERIFY_SIGIO #ifdef VERIFY_SIGIO void KdAssertSigioBlocked(char *where) { sigset_t set, old; sigemptyset(&set); sigprocmask(SIG_BLOCK, &set, &old); if (!sigismember(&old, SIGIO)) ErrorF("SIGIO not blocked at %s\n", where); } #else #define KdAssertSigioBlocked(s) #endif static int kdnFds; #ifdef FNONBLOCK #define NOBLOCK FNONBLOCK #else #define NOBLOCK FNDELAY #endif static void KdNonBlockFd(int fd) { int flags; flags = fcntl(fd, F_GETFL); flags |= FASYNC | NOBLOCK; fcntl(fd, F_SETFL, flags); } static void KdAddFd(int fd) { struct sigaction act; sigset_t set; kdnFds++; fcntl(fd, F_SETOWN, getpid()); KdNonBlockFd(fd); AddEnabledDevice(fd); memset(&act, '\0', sizeof act); act.sa_handler = KdSigio; sigemptyset(&act.sa_mask); sigaddset(&act.sa_mask, SIGIO); sigaddset(&act.sa_mask, SIGALRM); sigaddset(&act.sa_mask, SIGVTALRM); sigaction(SIGIO, &act, 0); sigemptyset(&set); sigprocmask(SIG_SETMASK, &set, 0); } static void KdRemoveFd(int fd) { struct sigaction act; int flags; kdnFds--; RemoveEnabledDevice(fd); flags = fcntl(fd, F_GETFL); flags &= ~(FASYNC | NOBLOCK); fcntl(fd, F_SETFL, flags); if (kdnFds == 0) { memset(&act, '\0', sizeof act); act.sa_handler = SIG_IGN; sigemptyset(&act.sa_mask); sigaction(SIGIO, &act, 0); } } int KdAllocInputType(void) { return ++kdInputTypeSequence; } Bool KdRegisterFd(int type, int fd, void (*read) (int fd, void *closure), void *closure) { if (kdNumInputFds == KD_MAX_INPUT_FDS) return FALSE; kdInputFds[kdNumInputFds].type = type; kdInputFds[kdNumInputFds].fd = fd; kdInputFds[kdNumInputFds].read = read; kdInputFds[kdNumInputFds].enable = 0; kdInputFds[kdNumInputFds].disable = 0; kdInputFds[kdNumInputFds].closure = closure; ++kdNumInputFds; if (kdInputEnabled) KdAddFd(fd); return TRUE; } void KdRegisterFdEnableDisable(int fd, int (*enable) (int fd, void *closure), void (*disable) (int fd, void *closure)) { int i; for (i = 0; i < kdNumInputFds; i++) if (kdInputFds[i].fd == fd) { kdInputFds[i].enable = enable; kdInputFds[i].disable = disable; break; } } void KdUnregisterFds(int type, Bool do_close) { int i, j; for (i = 0; i < kdNumInputFds;) { if (kdInputFds[i].type == type) { if (kdInputEnabled) KdRemoveFd(kdInputFds[i].fd); if (do_close) close(kdInputFds[i].fd); --kdNumInputFds; for (j = i; j < kdNumInputFds; j++) kdInputFds[j] = kdInputFds[j + 1]; } else i++; } } void KdDisableInput(void) { int i; KdBlockSigio(); for (i = 0; i < kdNumInputFds; i++) { KdRemoveFd(kdInputFds[i].fd); if (kdInputFds[i].disable) (*kdInputFds[i].disable) (kdInputFds[i].fd, kdInputFds[i].closure); } kdInputEnabled = FALSE; } void KdEnableInput(void) { xEvent xE; int i; kdInputEnabled = TRUE; for (i = 0; i < kdNumInputFds; i++) { KdNonBlockFd(kdInputFds[i].fd); if (kdInputFds[i].enable) kdInputFds[i].fd = (*kdInputFds[i].enable) (kdInputFds[i].fd, kdInputFds[i].closure); KdAddFd(kdInputFds[i].fd); } /* reset screen saver */ xE.u.keyButtonPointer.time = GetTimeInMillis(); NoticeEventTime(&xE); KdUnblockSigio(); } static int KdMouseProc(DeviceIntPtr pDevice, int onoff) { BYTE map[KD_MAX_BUTTON]; DevicePtr pDev = (DevicePtr) pDevice; int i; if (!pDev) return BadImplementation; switch (onoff) { case DEVICE_INIT: for (i = 1; i <= kdMouseButtonCount; i++) map[i] = i; InitPointerDeviceStruct(pDev, map, kdMouseButtonCount, miPointerGetMotionEvents, (PtrCtrlProcPtr) NoopDDA, miPointerGetMotionBufferSize()); break; case DEVICE_ON: pDev->on = TRUE; pKdPointer = pDevice; for (i = 0; i < kdNMouseFuncs; i++) (*kdMouseFuncs[i]->Init) (); break; case DEVICE_OFF: case DEVICE_CLOSE: if (pDev->on) { pDev->on = FALSE; pKdPointer = 0; for (i = 0; i < kdNMouseFuncs; i++) (*kdMouseFuncs[i]->Fini) (); } break; } return Success; } Bool LegalModifier(unsigned int key, DevicePtr pDev) { return TRUE; } static void KdBell(int volume, DeviceIntPtr pDev, pointer ctrl, int something) { if (kdInputEnabled) (*kdKeyboardFuncs->Bell) (volume, kdBellPitch, kdBellDuration); } static void KdSetLeds(void) { if (kdInputEnabled) (*kdKeyboardFuncs->Leds) (kdLeds); } void KdSetLed(int led, Bool on) { NoteLedState(pKdKeyboard, led, on); kdLeds = pKdKeyboard->kbdfeed->ctrl.leds; KdSetLeds(); } void KdSetMouseMatrix(KdMouseMatrix * matrix) { kdMouseMatrix = *matrix; } void KdComputeMouseMatrix(KdMouseMatrix * m, Rotation randr, int width, int height) { int x_dir = 1, y_dir = 1; int i, j; int size[2]; size[0] = width; size[1] = height; if (randr & RR_Reflect_X) x_dir = -1; if (randr & RR_Reflect_Y) y_dir = -1; switch (randr & (RR_Rotate_All)) { case RR_Rotate_0: m->matrix[0][0] = x_dir; m->matrix[0][1] = 0; m->matrix[1][0] = 0; m->matrix[1][1] = y_dir; break; case RR_Rotate_90: m->matrix[0][0] = 0; m->matrix[0][1] = -x_dir; m->matrix[1][0] = y_dir; m->matrix[1][1] = 0; break; case RR_Rotate_180: m->matrix[0][0] = -x_dir; m->matrix[0][1] = 0; m->matrix[1][0] = 0; m->matrix[1][1] = -y_dir; break; case RR_Rotate_270: m->matrix[0][0] = 0; m->matrix[0][1] = x_dir; m->matrix[1][0] = -y_dir; m->matrix[1][1] = 0; break; } for (i = 0; i < 2; i++) { m->matrix[i][2] = 0; for (j = 0; j < 2; j++) if (m->matrix[i][j] < 0) m->matrix[i][2] = size[j] - 1; } } static void KdKbdCtrl(DeviceIntPtr pDevice, KeybdCtrl * ctrl) { kdLeds = ctrl->leds; kdBellPitch = ctrl->bell_pitch; kdBellDuration = ctrl->bell_duration; KdSetLeds(); } static int KdKeybdProc(DeviceIntPtr pDevice, int onoff) { Bool ret; DevicePtr pDev = (DevicePtr) pDevice; if (!pDev) return BadImplementation; switch (onoff) { case DEVICE_INIT: if (pDev != LookupKeyboardDevice()) { return !Success; } ret = InitKeyboardDeviceStruct(pDev, &kdKeySyms, kdModMap, KdBell, KdKbdCtrl); if (!ret) return BadImplementation; break; case DEVICE_ON: pDev->on = TRUE; pKdKeyboard = pDevice; if (kdKeyboardFuncs) (*kdKeyboardFuncs->Init) (); break; case DEVICE_OFF: case DEVICE_CLOSE: pKdKeyboard = 0; if (pDev->on) { pDev->on = FALSE; if (kdKeyboardFuncs) (*kdKeyboardFuncs->Fini) (); } break; } return Success; } extern KeybdCtrl defaultKeyboardControl; static void KdInitAutoRepeats(void) { int key_code; unsigned char mask; int i; unsigned char *repeats; repeats = defaultKeyboardControl.autoRepeats; memset(repeats, '\0', 32); for (key_code = KD_MIN_KEYCODE; key_code <= KD_MAX_KEYCODE; key_code++) { if (!kdModMap[key_code]) { i = key_code >> 3; mask = 1 << (key_code & 7); repeats[i] |= mask; } } } static const KdKeySymModsRec kdKeySymMods[] = { {XK_Control_L, ControlMask}, {XK_Control_R, ControlMask}, {XK_Shift_L, ShiftMask}, {XK_Shift_R, ShiftMask}, {XK_Caps_Lock, LockMask}, {XK_Shift_Lock, LockMask}, {XK_Alt_L, Mod1Mask}, {XK_Alt_R, Mod1Mask}, {XK_Meta_L, Mod1Mask}, {XK_Meta_R, Mod1Mask}, {XK_Num_Lock, Mod2Mask}, {XK_Super_L, Mod3Mask}, {XK_Super_R, Mod3Mask}, {XK_Hyper_L, Mod3Mask}, {XK_Hyper_R, Mod3Mask}, {XK_Mode_switch, Mod4Mask}, }; #define NUM_SYM_MODS (sizeof(kdKeySymMods) / sizeof(kdKeySymMods[0])) static void KdInitModMap(void) { int key_code; int row; int width; KeySym *syms; int i; width = kdKeySyms.mapWidth; for (key_code = kdMinKeyCode; key_code <= kdMaxKeyCode; key_code++) { kdModMap[key_code] = 0; syms = kdKeymap + (key_code - kdMinKeyCode) * width; for (row = 0; row < width; row++, syms++) { for (i = 0; i < NUM_SYM_MODS; i++) { if (*syms == kdKeySymMods[i].modsym) kdModMap[key_code] |= kdKeySymMods[i].modbit; } } } } static void KdAddMouseDriver(const KdMouseFuncs * const pMouseFuncs) { if (kdNMouseFuncs < MAX_MOUSE_DRIVERS) kdMouseFuncs[kdNMouseFuncs++] = pMouseFuncs; } void KdInitInput(const KdMouseFuncs * const pMouseFuncs, const KdKeyboardFuncs * const pKeyboardFuncs) { DeviceIntPtr pKeyboard, pPointer; KdMouseInfo *mi; if (!kdMouseInfo) KdParseMouse(0); kdMouseButtonCount = 0; for (mi = kdMouseInfo; mi; mi = mi->next) { if (mi->nbutton > kdMouseButtonCount) kdMouseButtonCount = mi->nbutton; } kdNMouseFuncs = 0; KdAddMouseDriver(pMouseFuncs); kdKeyboardFuncs = pKeyboardFuncs; memset(kdKeyState, '\0', sizeof(kdKeyState)); if (kdKeyboardFuncs) (*kdKeyboardFuncs->Load) (); kdMinKeyCode = kdMinScanCode + KD_KEY_OFFSET; kdMaxKeyCode = kdMaxScanCode + KD_KEY_OFFSET; kdKeySyms.map = kdKeymap; kdKeySyms.minKeyCode = kdMinKeyCode; kdKeySyms.maxKeyCode = kdMaxKeyCode; kdKeySyms.mapWidth = kdKeymapWidth; kdLeds = 0; kdBellPitch = 1000; kdBellDuration = 200; kdInputEnabled = TRUE; KdInitModMap(); KdInitAutoRepeats(); KdResetInputMachine(); pPointer = AddInputDevice(KdMouseProc, TRUE); pKeyboard = AddInputDevice(KdKeybdProc, TRUE); RegisterPointerDevice(pPointer); RegisterKeyboardDevice(pKeyboard); miRegisterPointerDevice(screenInfo.screens[0], pPointer); mieqInit(&pKeyboard->public, &pPointer->public); } /* * Middle button emulation state machine * * Possible transitions: * Button 1 press v1 * Button 1 release ^1 * Button 2 press v2 * Button 2 release ^2 * Button 3 press v3 * Button 3 release ^3 * Button other press vo * Button other release ^o * Mouse motion <> * Keyboard event k * timeout ... * outside box <-> * * States: * start * button_1_pend * button_1_down * button_2_down * button_3_pend * button_3_down * synthetic_2_down_13 * synthetic_2_down_3 * synthetic_2_down_1 * * Transition diagram * * start * v1 -> (hold) (settimeout) button_1_pend * ^1 -> (deliver) start * v2 -> (deliver) button_2_down * ^2 -> (deliever) start * v3 -> (hold) (settimeout) button_3_pend * ^3 -> (deliver) start * vo -> (deliver) start * ^o -> (deliver) start * <> -> (deliver) start * k -> (deliver) start * * button_1_pend (button 1 is down, timeout pending) * ^1 -> (release) (deliver) start * v2 -> (release) (deliver) button_1_down * ^2 -> (release) (deliver) button_1_down * v3 -> (cleartimeout) (generate v2) synthetic_2_down_13 * ^3 -> (release) (deliver) button_1_down * vo -> (release) (deliver) button_1_down * ^o -> (release) (deliver) button_1_down * <-> -> (release) (deliver) button_1_down * <> -> (deliver) button_1_pend * k -> (release) (deliver) button_1_down * ... -> (release) button_1_down * * button_1_down (button 1 is down) * ^1 -> (deliver) start * v2 -> (deliver) button_1_down * ^2 -> (deliver) button_1_down * v3 -> (deliver) button_1_down * ^3 -> (deliver) button_1_down * vo -> (deliver) button_1_down * ^o -> (deliver) button_1_down * <> -> (deliver) button_1_down * k -> (deliver) button_1_down * * button_2_down (button 2 is down) * v1 -> (deliver) button_2_down * ^1 -> (deliver) button_2_down * ^2 -> (deliver) start * v3 -> (deliver) button_2_down * ^3 -> (deliver) button_2_down * vo -> (deliver) button_2_down * ^o -> (deliver) button_2_down * <> -> (deliver) button_2_down * k -> (deliver) button_2_down * * button_3_pend (button 3 is down, timeout pending) * v1 -> (generate v2) synthetic_2_down * ^1 -> (release) (deliver) button_3_down * v2 -> (release) (deliver) button_3_down * ^2 -> (release) (deliver) button_3_down * ^3 -> (release) (deliver) start * vo -> (release) (deliver) button_3_down * ^o -> (release) (deliver) button_3_down * <-> -> (release) (deliver) button_3_down * <> -> (deliver) button_3_pend * k -> (release) (deliver) button_3_down * ... -> (release) button_3_down * * button_3_down (button 3 is down) * v1 -> (deliver) button_3_down * ^1 -> (deliver) button_3_down * v2 -> (deliver) button_3_down * ^2 -> (deliver) button_3_down * ^3 -> (deliver) start * vo -> (deliver) button_3_down * ^o -> (deliver) button_3_down * <> -> (deliver) button_3_down * k -> (deliver) button_3_down * * synthetic_2_down_13 (button 1 and 3 are down) * ^1 -> (generate ^2) synthetic_2_down_3 * v2 -> synthetic_2_down_13 * ^2 -> synthetic_2_down_13 * ^3 -> (generate ^2) synthetic_2_down_1 * vo -> (deliver) synthetic_2_down_13 * ^o -> (deliver) synthetic_2_down_13 * <> -> (deliver) synthetic_2_down_13 * k -> (deliver) synthetic_2_down_13 * * synthetic_2_down_3 (button 3 is down) * v1 -> (deliver) synthetic_2_down_3 * ^1 -> (deliver) synthetic_2_down_3 * v2 -> synthetic_2_down_3 * ^2 -> synthetic_2_down_3 * ^3 -> start * vo -> (deliver) synthetic_2_down_3 * ^o -> (deliver) synthetic_2_down_3 * <> -> (deliver) synthetic_2_down_3 * k -> (deliver) synthetic_2_down_3 * * synthetic_2_down_1 (button 1 is down) * ^1 -> start * v2 -> synthetic_2_down_1 * ^2 -> synthetic_2_down_1 * v3 -> (deliver) synthetic_2_down_1 * ^3 -> (deliver) synthetic_2_down_1 * vo -> (deliver) synthetic_2_down_1 * ^o -> (deliver) synthetic_2_down_1 * <> -> (deliver) synthetic_2_down_1 * k -> (deliver) synthetic_2_down_1 */ typedef enum _inputClass { down_1, up_1, down_2, up_2, down_3, up_3, down_o, up_o, motion, outside_box, keyboard, timeout, num_input_class } KdInputClass; typedef enum _inputAction { noop, hold, setto, deliver, release, clearto, gen_down_2, gen_up_2 } KdInputAction; #define MAX_ACTIONS 2 typedef struct _inputTransition { KdInputAction actions[MAX_ACTIONS]; KdMouseState nextState; } KdInputTransition; static const KdInputTransition kdInputMachine[num_input_states][num_input_class] = { /* start */ { {{hold, setto}, button_1_pend}, /* v1 */ {{deliver, noop}, start}, /* ^1 */ {{deliver, noop}, button_2_down}, /* v2 */ {{deliver, noop}, start}, /* ^2 */ {{hold, setto}, button_3_pend}, /* v3 */ {{deliver, noop}, start}, /* ^3 */ {{deliver, noop}, start}, /* vo */ {{deliver, noop}, start}, /* ^o */ {{deliver, noop}, start}, /* <> */ {{deliver, noop}, start}, /* <-> */ {{noop, noop}, start}, /* k */ {{noop, noop}, start}, /* ... */ }, /* button_1_pend */ { {{noop, noop}, button_1_pend}, /* v1 */ {{release, deliver}, start}, /* ^1 */ {{release, deliver}, button_1_down}, /* v2 */ {{release, deliver}, button_1_down}, /* ^2 */ {{clearto, gen_down_2}, synth_2_down_13}, /* v3 */ {{release, deliver}, button_1_down}, /* ^3 */ {{release, deliver}, button_1_down}, /* vo */ {{release, deliver}, button_1_down}, /* ^o */ {{deliver, noop}, button_1_pend}, /* <> */ {{release, deliver}, button_1_down}, /* <-> */ {{noop, noop}, button_1_down}, /* k */ {{release, noop}, button_1_down}, /* ... */ }, /* button_1_down */ { {{noop, noop}, button_1_down}, /* v1 */ {{deliver, noop}, start}, /* ^1 */ {{deliver, noop}, button_1_down}, /* v2 */ {{deliver, noop}, button_1_down}, /* ^2 */ {{deliver, noop}, button_1_down}, /* v3 */ {{deliver, noop}, button_1_down}, /* ^3 */ {{deliver, noop}, button_1_down}, /* vo */ {{deliver, noop}, button_1_down}, /* ^o */ {{deliver, noop}, button_1_down}, /* <> */ {{deliver, noop}, button_1_down}, /* <-> */ {{noop, noop}, button_1_down}, /* k */ {{noop, noop}, button_1_down}, /* ... */ }, /* button_2_down */ { {{deliver, noop}, button_2_down}, /* v1 */ {{deliver, noop}, button_2_down}, /* ^1 */ {{noop, noop}, button_2_down}, /* v2 */ {{deliver, noop}, start}, /* ^2 */ {{deliver, noop}, button_2_down}, /* v3 */ {{deliver, noop}, button_2_down}, /* ^3 */ {{deliver, noop}, button_2_down}, /* vo */ {{deliver, noop}, button_2_down}, /* ^o */ {{deliver, noop}, button_2_down}, /* <> */ {{deliver, noop}, button_2_down}, /* <-> */ {{noop, noop}, button_2_down}, /* k */ {{noop, noop}, button_2_down}, /* ... */ }, /* button_3_pend */ { {{clearto, gen_down_2}, synth_2_down_13}, /* v1 */ {{release, deliver}, button_3_down}, /* ^1 */ {{release, deliver}, button_3_down}, /* v2 */ {{release, deliver}, button_3_down}, /* ^2 */ {{release, deliver}, button_3_down}, /* v3 */ {{release, deliver}, start}, /* ^3 */ {{release, deliver}, button_3_down}, /* vo */ {{release, deliver}, button_3_down}, /* ^o */ {{deliver, noop}, button_3_pend}, /* <> */ {{release, deliver}, button_3_down}, /* <-> */ {{release, noop}, button_3_down}, /* k */ {{release, noop}, button_3_down}, /* ... */ }, /* button_3_down */ { {{deliver, noop}, button_3_down}, /* v1 */ {{deliver, noop}, button_3_down}, /* ^1 */ {{deliver, noop}, button_3_down}, /* v2 */ {{deliver, noop}, button_3_down}, /* ^2 */ {{noop, noop}, button_3_down}, /* v3 */ {{deliver, noop}, start}, /* ^3 */ {{deliver, noop}, button_3_down}, /* vo */ {{deliver, noop}, button_3_down}, /* ^o */ {{deliver, noop}, button_3_down}, /* <> */ {{deliver, noop}, button_3_down}, /* <-> */ {{noop, noop}, button_3_down}, /* k */ {{noop, noop}, button_3_down}, /* ... */ }, /* synthetic_2_down_13 */ { {{noop, noop}, synth_2_down_13}, /* v1 */ {{gen_up_2, noop}, synth_2_down_3}, /* ^1 */ {{noop, noop}, synth_2_down_13}, /* v2 */ {{noop, noop}, synth_2_down_13}, /* ^2 */ {{noop, noop}, synth_2_down_13}, /* v3 */ {{gen_up_2, noop}, synth_2_down_1}, /* ^3 */ {{deliver, noop}, synth_2_down_13}, /* vo */ {{deliver, noop}, synth_2_down_13}, /* ^o */ {{deliver, noop}, synth_2_down_13}, /* <> */ {{deliver, noop}, synth_2_down_13}, /* <-> */ {{noop, noop}, synth_2_down_13}, /* k */ {{noop, noop}, synth_2_down_13}, /* ... */ }, /* synthetic_2_down_3 */ { {{deliver, noop}, synth_2_down_3}, /* v1 */ {{deliver, noop}, synth_2_down_3}, /* ^1 */ {{deliver, noop}, synth_2_down_3}, /* v2 */ {{deliver, noop}, synth_2_down_3}, /* ^2 */ {{noop, noop}, synth_2_down_3}, /* v3 */ {{noop, noop}, start}, /* ^3 */ {{deliver, noop}, synth_2_down_3}, /* vo */ {{deliver, noop}, synth_2_down_3}, /* ^o */ {{deliver, noop}, synth_2_down_3}, /* <> */ {{deliver, noop}, synth_2_down_3}, /* <-> */ {{noop, noop}, synth_2_down_3}, /* k */ {{noop, noop}, synth_2_down_3}, /* ... */ }, /* synthetic_2_down_1 */ { {{noop, noop}, synth_2_down_1}, /* v1 */ {{noop, noop}, start}, /* ^1 */ {{deliver, noop}, synth_2_down_1}, /* v2 */ {{deliver, noop}, synth_2_down_1}, /* ^2 */ {{deliver, noop}, synth_2_down_1}, /* v3 */ {{deliver, noop}, synth_2_down_1}, /* ^3 */ {{deliver, noop}, synth_2_down_1}, /* vo */ {{deliver, noop}, synth_2_down_1}, /* ^o */ {{deliver, noop}, synth_2_down_1}, /* <> */ {{deliver, noop}, synth_2_down_1}, /* <-> */ {{noop, noop}, synth_2_down_1}, /* k */ {{noop, noop}, synth_2_down_1}, /* ... */ }, }; #define EMULATION_WINDOW 10 #define EMULATION_TIMEOUT 100 #define EventX(e) ((e)->u.keyButtonPointer.rootX) #define EventY(e) ((e)->u.keyButtonPointer.rootY) static int KdInsideEmulationWindow(KdMouseInfo * mi, xEvent * ev) { if (ev->u.keyButtonPointer.pad1) { mi->emulationDx += EventX(ev); mi->emulationDy += EventY(ev); } else { mi->emulationDx = EventX(&mi->heldEvent) - EventX(ev); mi->emulationDy = EventY(&mi->heldEvent) - EventY(ev); } return (abs(mi->emulationDx) < EMULATION_WINDOW && abs(mi->emulationDy) < EMULATION_WINDOW); } static KdInputClass KdClassifyInput(KdMouseInfo * mi, xEvent * ev) { switch (ev->u.u.type) { case ButtonPress: switch (ev->u.u.detail) { case 1: return down_1; case 2: return down_2; case 3: return down_3; default: return down_o; } break; case ButtonRelease: switch (ev->u.u.detail) { case 1: return up_1; case 2: return up_2; case 3: return up_3; default: return up_o; } break; case MotionNotify: if (mi->eventHeld && !KdInsideEmulationWindow(mi, ev)) return outside_box; else return motion; default: return keyboard; } return keyboard; } #ifndef NDEBUG char *kdStateNames[] = { "start", "button_1_pend", "button_1_down", "button_2_down", "button_3_pend", "button_3_down", "synth_2_down_13", "synth_2_down_3", "synthetic_2_down_1", "num_input_states" }; char *kdClassNames[] = { "down_1", "up_1", "down_2", "up_2", "down_3", "up_3", "motion", "ouside_box", "keyboard", "timeout", "num_input_class" }; char *kdActionNames[] = { "noop", "hold", "setto", "deliver", "release", "clearto", "gen_down_2", "gen_up_2", }; #endif static void KdQueueEvent(xEvent * ev) { KdAssertSigioBlocked("KdQueueEvent"); if (ev->u.u.type == MotionNotify) { if (ev->u.keyButtonPointer.pad1) { ev->u.keyButtonPointer.pad1 = 0; miPointerDeltaCursor(ev->u.keyButtonPointer.rootX, ev->u.keyButtonPointer.rootY, ev->u.keyButtonPointer.time); } else { miPointerAbsoluteCursor(ev->u.keyButtonPointer.rootX, ev->u.keyButtonPointer.rootY, ev->u.keyButtonPointer.time); } } else { mieqEnqueue(ev); } } static void KdRunMouseMachine(KdMouseInfo * mi, KdInputClass c, xEvent * ev) { const KdInputTransition *t; int a; t = &kdInputMachine[mi->mouseState][c]; for (a = 0; a < MAX_ACTIONS; a++) { switch (t->actions[a]) { case noop: break; case hold: mi->eventHeld = TRUE; mi->emulationDx = 0; mi->emulationDy = 0; mi->heldEvent = *ev; break; case setto: mi->emulationTimeout = GetTimeInMillis() + EMULATION_TIMEOUT; mi->timeoutPending = TRUE; break; case deliver: KdQueueEvent(ev); break; case release: mi->eventHeld = FALSE; mi->timeoutPending = FALSE; KdQueueEvent(&mi->heldEvent); break; case clearto: mi->timeoutPending = FALSE; break; case gen_down_2: ev->u.u.detail = 2; mi->eventHeld = FALSE; KdQueueEvent(ev); break; case gen_up_2: ev->u.u.detail = 2; KdQueueEvent(ev); break; } } mi->mouseState = t->nextState; } void KdResetInputMachine(void) { KdMouseInfo *mi; for (mi = kdMouseInfo; mi; mi = mi->next) { mi->mouseState = start; mi->eventHeld = FALSE; } } static void KdHandleMouseEvent(KdMouseInfo * mi, xEvent * ev) { if (mi->emulateMiddleButton) KdRunMouseMachine(mi, KdClassifyInput(mi, ev), ev); else KdQueueEvent(ev); } static void KdReceiveTimeout(KdMouseInfo * mi) { KdRunMouseMachine(mi, timeout, 0); } #define KILL_SEQUENCE ((1L << KK_CONTROL)|(1L << KK_ALT)|(1L << KK_F8)|(1L << KK_F10)) #define SPECIAL_SEQUENCE ((1L << KK_CONTROL) | (1L << KK_ALT)) #define SETKILLKEY(b) (KdSpecialKeys |= (1L << (b))) #define CLEARKILLKEY(b) (KdSpecialKeys &= ~(1L << (b))) #define KEYMAP (pKdKeyboard->key->curKeySyms) #define KEYCOL1(k) (KEYMAP.map[((k)-kdMinKeyCode)*KEYMAP.mapWidth]) /* * kdCheckTermination * * This function checks for the key sequence that terminates the server. When * detected, it sets the dispatchException flag and returns. The key sequence * is: * Control-Alt * It's assumed that the server will be waken up by the caller when this * function returns. */ extern int nClients; static void KdCheckSpecialKeys(xEvent * xE) { KeySym sym = KEYCOL1(xE->u.u.detail); if (!pKdKeyboard) return; /* * Ignore key releases */ if (xE->u.u.type == KeyRelease) return; /* * Check for control/alt pressed */ if ((pKdKeyboard->key->state & (ControlMask | Mod1Mask)) != (ControlMask | Mod1Mask)) return; /* * Let OS function see keysym first */ if (kdOsFuncs->SpecialKey) if ((*kdOsFuncs->SpecialKey) (sym)) return; /* * Now check for backspace or delete; these signal the * X server to terminate */ switch (sym) { case XK_BackSpace: case XK_Delete: case XK_KP_Delete: /* * Set the dispatch exception flag so the server will terminate the * next time through the dispatch loop. */ if (kdDontZap == FALSE) dispatchException |= DE_TERMINATE; break; } } /* * kdEnqueueKeyboardEvent * * This function converts hardware keyboard event information into an X event * and enqueues it using MI. It wakes up the server before returning so that * the event will be processed normally. * */ static void KdHandleKeyboardEvent(xEvent * ev) { int key = ev->u.u.detail; int byte; CARD8 bit; KdMouseInfo *mi; byte = key >> 3; bit = 1 << (key & 7); switch (ev->u.u.type) { case KeyPress: kdKeyState[byte] |= bit; break; case KeyRelease: kdKeyState[byte] &= ~bit; break; } for (mi = kdMouseInfo; mi; mi = mi->next) KdRunMouseMachine(mi, keyboard, 0); KdQueueEvent(ev); } void KdReleaseAllKeys(void) { xEvent xE; int key; KdBlockSigio(); for (key = 0; key < KD_KEY_COUNT; key++) if (IsKeyDown(key)) { xE.u.keyButtonPointer.time = GetTimeInMillis(); xE.u.u.type = KeyRelease; xE.u.u.detail = key; KdHandleKeyboardEvent(&xE); } KdUnblockSigio(); } static void KdCheckLock(void) { KeyClassPtr keyc = pKdKeyboard->key; Bool isSet, shouldBeSet; if (kdKeyboardFuncs->LockLed) { isSet = (kdLeds & (1 << (kdKeyboardFuncs->LockLed - 1))) != 0; shouldBeSet = (keyc->state & LockMask) != 0; if (isSet != shouldBeSet) { KdSetLed(kdKeyboardFuncs->LockLed, shouldBeSet); } } } #define KEY_KP_0 /* 0 Insert 0x52 */ 82 #define KEY_KP_1 /* 1 End 0x4f */ 79 #define KEY_KP_2 /* 2 Down 0x50 */ 80 #define KEY_KP_3 /* 3 PgDown 0x51 */ 81 #define KEY_KP_4 /* 4 Left 0x4b */ 75 #define KEY_KP_5 /* 5 0x4c */ 76 #define KEY_KP_6 /* 6 Right 0x4d */ 77 #define KEY_KP_7 /* 7 Home 0x47 */ 71 #define KEY_KP_8 /* 8 Up 0x48 */ 72 #define KEY_KP_9 /* 9 PgUp 0x49 */ 73 #define KEY_KP_Decimal /* . (Decimal) Delete 0x53 */ 83 #define KEY_Insert /* Insert 0x62 */ 0x6e #define KEY_Delete /* Delete 0x63 */ 0x6f #define KEY_Home /* Home 0x59 */ 0x66 #define KEY_Up /* Up 0x5a */ 0x67 #define KEY_PgUp /* PgUp 0x5b */ 0x68 #define KEY_Left /* Left 0x5c */ 0x69 #define KEY_Right /* Right 0x5e */ 0x6a #define KEY_End /* End 0x5f */ 0x6b #define KEY_Down /* Down 0x60 */ 0x6c #define KEY_PgDown /* PgDown 0x61 */ 0x6d static unsigned char remap(const unsigned char scan) { // The numpad buttons need to be remapped, as they have the same scan keys switch (scan) { case KEY_KP_0: return KEY_Insert; case KEY_KP_1: return KEY_End; case KEY_KP_2: return KEY_Down; case KEY_KP_3: return KEY_PgDown; case KEY_KP_4: return KEY_Left; case KEY_KP_6: return KEY_Right; case KEY_KP_7: return KEY_Home; case KEY_KP_8: return KEY_Up; case KEY_KP_9: return KEY_PgUp; case KEY_KP_Decimal: return KEY_Delete; } return scan; } void KdEnqueueKeyboardEvent(unsigned char scan_code, unsigned char is_up) { unsigned char key_code; static unsigned int locks = 0; xEvent xE; KeyClassPtr keyc; if (!pKdKeyboard) return; keyc = pKdKeyboard->key; xE.u.keyButtonPointer.time = GetTimeInMillis(); if (!(locks & Mod2Mask)) scan_code = remap(scan_code); if (kdMinScanCode <= scan_code && scan_code <= kdMaxScanCode) { key_code = scan_code + KD_MIN_KEYCODE - kdMinScanCode; /* * Set up this event -- the type may be modified below */ if (is_up) xE.u.u.type = KeyRelease; else xE.u.u.type = KeyPress; xE.u.u.detail = key_code; // Handle toggling keys if (xE.u.u.type == KeyPress) { switch (KEYCOL1(key_code)) { case XK_Num_Lock: locks ^= Mod2Mask; break; case XK_Shift_Lock: case XK_Caps_Lock: locks ^= LockMask; break; } } keyc->state |= locks; /* * Check pressed keys which are already down */ if (IsKeyDown(key_code) && xE.u.u.type == KeyPress) { KeybdCtrl *ctrl = &pKdKeyboard->kbdfeed->ctrl; /* * Check auto repeat */ if (!ctrl->autoRepeat || keyc->modifierMap[key_code] || !(ctrl-> autoRepeats[key_code >> 3] & (1 << (key_code & 7)))) { return; } /* * X delivers press/release even for autorepeat */ xE.u.u.type = KeyRelease; KdHandleKeyboardEvent(&xE); xE.u.u.type = KeyPress; } /* * Check released keys which are already up */ else if (!IsKeyDown(key_code) && xE.u.u.type == KeyRelease) { return; } KdCheckSpecialKeys(&xE); KdHandleKeyboardEvent(&xE); } } #define SetButton(mi, b, v, s) \ {\ xE.u.u.detail = mi->map[b]; \ xE.u.u.type = v; \ KdHandleMouseEvent (mi, &xE); \ } #define Press(mi, b) SetButton(mi, b, ButtonPress, "Down") #define Release(mi, b) SetButton(mi, b, ButtonRelease, "Up") /* * kdEnqueueMouseEvent * * This function converts hardware mouse event information into X event * information. A mouse movement event is passed off to MI to generate * a MotionNotify event, if appropriate. Button events are created and * passed off to MI for enqueueing. */ static void KdMouseAccelerate(DeviceIntPtr device, int *dx, int *dy) { PtrCtrl *pCtrl = &device->ptrfeed->ctrl; double speed = sqrt(*dx * *dx + *dy * *dy); double accel; #ifdef QUADRATIC_ACCELERATION double m; /* * Ok, so we want it moving num/den times faster at threshold*2 * * accel = m *threshold + b * 1 = m * 0 + b -> b = 1 * * num/den = m * (threshold * 2) + 1 * * num / den - 1 = m * threshold * 2 * (num / den - 1) / threshold * 2 = m */ m = (((double)pCtrl->num / (double)pCtrl->den - 1.0) / ((double)pCtrl->threshold * 2.0)); accel = m * speed + 1; #else accel = 1.0; if (speed > pCtrl->threshold) accel = (double)pCtrl->num / pCtrl->den; #endif *dx = accel * *dx; *dy = accel * *dy; } void KdEnqueueMouseEvent(KdMouseInfo * mi, unsigned long flags, int rx, int ry) { CARD32 ms; xEvent xE; unsigned char buttons; int x, y; int (*matrix)[3] = kdMouseMatrix.matrix; unsigned long button; int n; if (!pKdPointer) return; ms = GetTimeInMillis(); if (flags & KD_MOUSE_DELTA) { if (mi->transformCoordinates) { x = matrix[0][0] * rx + matrix[0][1] * ry; y = matrix[1][0] * rx + matrix[1][1] * ry; } else { x = rx; y = ry; } KdMouseAccelerate(pKdPointer, &x, &y); xE.u.keyButtonPointer.pad1 = 1; } else { if (mi->transformCoordinates) { x = matrix[0][0] * rx + matrix[0][1] * ry + matrix[0][2]; y = matrix[1][0] * rx + matrix[1][1] * ry + matrix[1][2]; } else { x = rx; y = ry; } xE.u.keyButtonPointer.pad1 = 0; } xE.u.keyButtonPointer.time = ms; xE.u.keyButtonPointer.rootX = x; xE.u.keyButtonPointer.rootY = y; xE.u.u.type = MotionNotify; xE.u.u.detail = 0; KdHandleMouseEvent(mi, &xE); buttons = flags; for (button = KD_BUTTON_1, n = 0; button <= KD_BUTTON_5; button <<= 1, n++) { if ((mi->buttonState & button) ^ (buttons & button)) { if (buttons & button) { Press(mi, n); } else { Release(mi, n); } } } mi->buttonState = buttons; } void KdEnqueueMotionEvent(KdMouseInfo * mi, int x, int y) { xEvent xE; CARD32 ms; ms = GetTimeInMillis(); xE.u.u.type = MotionNotify; xE.u.keyButtonPointer.time = ms; xE.u.keyButtonPointer.rootX = x; xE.u.keyButtonPointer.rootY = y; KdHandleMouseEvent(mi, &xE); } void KdBlockHandler(int screen, pointer blockData, pointer timeout, pointer readmask) { KdMouseInfo *mi; int myTimeout = 0; for (mi = kdMouseInfo; mi; mi = mi->next) { if (mi->timeoutPending) { int ms; ms = mi->emulationTimeout - GetTimeInMillis(); if (ms < 1) ms = 1; if (ms < myTimeout || myTimeout == 0) myTimeout = ms; } } /* if we need to poll for events, do that */ if (kdOsFuncs->pollEvents) { (*kdOsFuncs->pollEvents) (); myTimeout = 20; } if (myTimeout > 0) AdjustWaitForDelay(timeout, myTimeout); } void KdWakeupHandler(int screen, pointer data, unsigned long lresult, pointer readmask) { int result = (int)lresult; fd_set *pReadmask = (fd_set *) readmask; int i; KdMouseInfo *mi; if (kdInputEnabled && result > 0) { for (i = 0; i < kdNumInputFds; i++) if (FD_ISSET(kdInputFds[i].fd, pReadmask)) { KdBlockSigio(); (*kdInputFds[i].read) (kdInputFds[i].fd, kdInputFds[i].closure); KdUnblockSigio(); } } for (mi = kdMouseInfo; mi; mi = mi->next) { if (mi->timeoutPending) { if ((long)(GetTimeInMillis() - mi->emulationTimeout) >= 0) { mi->timeoutPending = FALSE; KdBlockSigio(); KdReceiveTimeout(mi); KdUnblockSigio(); } } } if (kdSwitchPending) KdProcessSwitch(); } #define KdScreenOrigin(pScreen) (&(KdGetScreenPriv(pScreen)->screen->origin)) static Bool KdCursorOffScreen(ScreenPtr * ppScreen, int *x, int *y) { ScreenPtr pScreen = *ppScreen; ScreenPtr pNewScreen; int n; int dx, dy; int best_x, best_y; int n_best_x, n_best_y; CARD32 ms; if (kdDisableZaphod || screenInfo.numScreens <= 1) return FALSE; if (0 <= *x && *x < pScreen->width && 0 <= *y && *y < pScreen->height) return FALSE; ms = GetTimeInMillis(); if (kdOffScreen && (int)(ms - kdOffScreenTime) < 1000) return FALSE; kdOffScreen = TRUE; kdOffScreenTime = ms; n_best_x = -1; best_x = 32767; n_best_y = -1; best_y = 32767; for (n = 0; n < screenInfo.numScreens; n++) { pNewScreen = screenInfo.screens[n]; if (pNewScreen == pScreen) continue; dx = KdScreenOrigin(pNewScreen)->x - KdScreenOrigin(pScreen)->x; dy = KdScreenOrigin(pNewScreen)->y - KdScreenOrigin(pScreen)->y; if (*x < 0) { if (dx <= 0 && -dx < best_x) { best_x = -dx; n_best_x = n; } } else if (*x >= pScreen->width) { if (dx >= 0 && dx < best_x) { best_x = dx; n_best_x = n; } } if (*y < 0) { if (dy <= 0 && -dy < best_y) { best_y = -dy; n_best_y = n; } } else if (*y >= pScreen->height) { if (dy >= 0 && dy < best_y) { best_y = dy; n_best_y = n; } } } if (best_y < best_x) n_best_x = n_best_y; if (n_best_x == -1) return FALSE; pNewScreen = screenInfo.screens[n_best_x]; if (*x < 0) *x += pNewScreen->width; if (*y < 0) *y += pNewScreen->height; if (*x >= pScreen->width) *x -= pScreen->width; if (*y >= pScreen->height) *y -= pScreen->height; *ppScreen = pNewScreen; return TRUE; } static void KdCrossScreen(ScreenPtr pScreen, Bool entering) { if (entering) KdEnableScreen(pScreen); else KdDisableScreen(pScreen); } static int KdCurScreen; /* current event screen */ static void KdWarpCursor(ScreenPtr pScreen, int x, int y) { KdBlockSigio(); KdCurScreen = pScreen->myNum; miPointerWarpCursor(pScreen, x, y); KdUnblockSigio(); } miPointerScreenFuncRec kdPointerScreenFuncs = { KdCursorOffScreen, KdCrossScreen, KdWarpCursor }; void ProcessInputEvents() { mieqProcessInputEvents(); miPointerUpdate(); if (kdSwitchPending) KdProcessSwitch(); KdCheckLock(); }