/* $XdotOrg: xc/programs/Xserver/hw/xfree86/os-support/usl/usl_xqueue.c,v 1.3 2005/11/10 02:41:20 jkj Exp $ */ /* * Copyright 2005 by Kean Johnston * Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany * Copyright 1993-1999 by The XFree86 Project, Inc. * * 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 the copyright holders not be used in * advertising or publicity pertaining to distribution of the software without * specific, written prior permission. The copyright holders make no * representations about the suitability of this software for any purpose. * It is provided "as is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE COPYRIGHT HOLDERS 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. * */ /* $XConsortium$ */ #include "X.h" #include "compiler.h" #include "xf86.h" #include "xf86Priv.h" #include "xf86_OSlib.h" #include "xf86Xinput.h" #include "xf86OSmouse.h" #include "xf86OSKbd.h" #include "usl_xqueue.h" #ifdef XKB #include "inputstr.h" #include #include #include extern Bool noXkbExtension; #endif #include "xf86Xinput.h" #include "mipointer.h" #if !defined(XQ_WHEEL) # define XQ_WHEEL 4 #endif /* * Implementation notes * * This code is based on a mixture of the original XFree86 sysv/xqueue.c * and information gathered from the SCO X server code (no actual code * was used, just the principles). * * The XFree86 XQUEUE code went to some considerable lengths to implement * what it calls "asynchronous XQUEUE". This involved creating a pipe, * and writing to that pipe each time an XQUEUE signal is received. The * one end of that pipe was then added to the list of selectable file * descriptors with AddEnabledDevice(). I completely fail to see the need * for this, and this code does not implement that mechanism. The server * will be interrupted anyway by the XQUEUE driver, so whether we pull the * events off the queue at the time we receive the signal or whether we * write to a pipe and then have the main select() loop stop and call us, * it makes no difference I can fathom. * * The code also differs from the original XFree86 code in that it maintains * local variables for the number of devices initialized. The original code * stored that information in the private data pointer of the mouse structure, * but this same code is used for both the keyboard and the mouse, so that * was changed. * * Part of the difficulty in dealing with XQUEUE is that it is a single * interface to two devices. The recent changes in XFree86/Xorg try to * treat the mouse and keyboard as discrete devices, and the code is * structured in such a way that they should be able to be independently * opened and closed. But we can't do that with XQUEUE, so we have to * centralize XQUEUE access here in this module. */ static xqEventQueue *xqQaddr = NULL; static int xqSigEnable = 1; static int xqEnableCount = 0; static struct kd_quemode xqMode; /* * These two pointers are set when the keyboard/mouse handler procs * are called to turn them on or off. This is so that we can call the * correct PostEvent for the device. */ static InputInfoPtr xqMouse = NULL; static InputInfoPtr xqKeyboard = NULL; static void XqSignalHandler (int signo); /* * Private functions */ static void XqReset (void) { if (xqEnableCount > 0) { xqQaddr->xq_head = xqQaddr->xq_tail; xqQaddr->xq_sigenable = xqSigEnable; } } #ifdef NOTNEEDED static void XqLock (void) { xqSigEnable = 0; if (xqEnableCount > 0) { xqQaddr->xq_sigenable = xqSigEnable; } } static void XqUnlock (void) { xqSigEnable = 1; if (xqEnableCount > 0) { xqQaddr->xq_sigenable = xqSigEnable; } } #endif /* NOTNEEDED */ /* * Since this code is shared between two devices, we need to keep track * of how many times we've been enabled or disabled. For example, if the * keyboard has been turned off, but the mouse hasn't, then we do not * want the whole queue off. Only when both devices are turned off do we * actually disable Xqueue mode. When either device is turned on, we * enable it. */ static int XqEnable (InputInfoPtr pInfo) { struct sigaction xqsig; static int msefd = -1; if (msefd == -1) { msefd = open ("/dev/mouse", O_RDONLY | O_NONBLOCK); #if 0 msefd = open ("/dev/mouse", O_RDONLY | O_NONBLOCK | O_NOCTTY); if (msefd < 0) { /* * Try giving it a controlling tty */ msefd = open (ttyname(xf86Info.consoleFd), O_RDWR | O_NONBLOCK); if (msefd >= 0) close (msefd); msefd = open ("/dev/mouse", O_RDONLY | O_NONBLOCK | O_NOCTTY); if (msefd < 0) sleep(2); } #endif } if (msefd < 0) { if (xf86GetAllowMouseOpenFail()) { ErrorF("%s: cannot open /dev/mouse (%s)\n", ttyname(xf86Info.consoleFd), strerror(errno)); } else { sleep(5); FatalError ("%s: cannot open /dev/mouse (%s)\n", ttyname(xf86Info.consoleFd), strerror(errno)); } } if (xqEnableCount++ == 0) { xqMode.qaddr = 0; ioctl (xf86Info.consoleFd, KDQUEMODE, NULL); /* * Note: We need to make sure the signal is armed before we enable * XQUEUE mode, so that if we get events immediately after the ioctl * we dont have an unhandled signal coming to the Xserver. * Also note that we use sigaction, so that we do not have to re-arm * the signal every time it is delivered, which just slows things * down (setting a signal is a fairly expensive operation). */ xqsig.sa_handler = XqSignalHandler; sigfillset (&xqsig.sa_mask); xqsig.sa_flags = 0; sigaction (SIGUSR2, &xqsig, NULL); /* * This is a fairly large queue size. Since we are reacting to events * asynchronously, its best for performance if we deal with as many * events as possible, and high resolution mice generate a lot of * events. */ xqMode.qsize = 64; xqMode.signo = SIGUSR2; xqMode.qaddr = 0; if (ioctl (xf86Info.consoleFd, KDQUEMODE, &xqMode) < 0) { xf86Msg (X_ERROR, "%s: could not set XQUEUE mode (%s)", pInfo->name, strerror(errno)); xqEnableCount--; xqsig.sa_handler = SIG_DFL; sigfillset (&xqsig.sa_mask); xqsig.sa_flags = 0; sigaction (SIGUSR2, &xqsig, NULL); return !Success; } /* * We're in business. The workstation is now in XQUEUE mode. */ xqQaddr = (xqEventQueue *)xqMode.qaddr; xqQaddr->xq_sigenable = 0; /* LOCK */ nap(500); XqReset(); } return Success; } static int XqDisable (InputInfoPtr pInfo) { struct sigaction xqsig; if (xqEnableCount-- == 1) { xqQaddr->xq_sigenable = 0; /* LOCK */ if (ioctl (xf86Info.consoleFd, KDQUEMODE, NULL) < 0) { xf86Msg (X_ERROR, "%s: could not unset XQUEUE mode (%s)", pInfo->name, strerror(errno)); xqEnableCount++; return !Success; } xqsig.sa_handler = SIG_DFL; sigfillset (&xqsig.sa_mask); xqsig.sa_flags = 0; sigaction (SIGUSR2, &xqsig, NULL); } return Success; } /* * XQUEUE signal handler. This is what goes through the list of events * we've already received and dispatches them to either the keyboard or * mouse event poster. */ static void XqSignalHandler (int signo) { xqEvent *xqEvents = xqQaddr->xq_events; int xqHead = xqQaddr->xq_head; xEvent xE; MouseDevPtr pMse = NULL; KbdDevPtr pKbd = NULL; signed char dx, dy; if (xqMouse) pMse = (MouseDevPtr)xqMouse->private; if (xqKeyboard) pKbd = (KbdDevPtr)xqKeyboard->private; while (xqHead != xqQaddr->xq_tail) { switch (xqEvents[xqHead].xq_type) { case XQ_MOTION: dx = (signed char)xqEvents[xqHead].xq_x; dy = (signed char)xqEvents[xqHead].xq_y; if (pMse) pMse->PostEvent(xqMouse, ~(xqEvents[xqHead].xq_code) & 0x07, (int)dx, (int)dy, 0, 0); break; case XQ_BUTTON: if (pMse) pMse->PostEvent(xqMouse, ~(xqEvents[xqHead].xq_code) & 0x07, 0, 0, 0, 0); break; case XQ_WHEEL: if (pMse) { int wbut = pMse->lastButtons, dz; if (xqEvents[xqHead].xq_code == 1) dz = 1; else dz = -1; pMse->PostEvent(xqMouse, wbut, 0, 0, dz, 0); } break; case XQ_KEY: if (pKbd) pKbd->PostEvent(xqKeyboard, xqEvents[xqHead].xq_code & 0x7f, xqEvents[xqHead].xq_code & 0x80 ? FALSE : TRUE); break; default: xf86Msg(X_WARNING, "XQUEUE: unknown event type %d\n", xqEvents[xqHead].xq_type); break; } xqHead++; if (xqHead == xqQaddr->xq_size) xqHead = 0; xf86Info.inputPending = TRUE; } XqReset(); } /* * Public functions */ int XqMseOnOff (InputInfoPtr pInfo, int on) { if (on) { if (xqMouse) { if (xqMouse != pInfo) xf86Msg(X_WARNING, "XqMseOnOff: mouse pointer structure changed!\n"); xqMouse = pInfo; } else { xqMouse = pInfo; return XqEnable(pInfo); } } else { xqMouse = NULL; return XqDisable(pInfo); } return Success; } int XqKbdOnOff (InputInfoPtr pInfo, int on) { if (on) { if (xqKeyboard) { if (xqKeyboard != pInfo) xf86Msg(X_WARNING, "XqKbdOnOff: keyboard pointer structure changed!\n"); xqKeyboard = pInfo; } else { xqKeyboard = pInfo; return XqEnable(pInfo); } } else { xqKeyboard = NULL; return XqDisable(pInfo); } return Success; }