(linux-only) keyboard driver Medved
[email protected] (Michal Maruška)
| Newsgroups | gmane.comp.xfree86.devel |
|---|---|
| Message-ID | <[email protected]> |
hello fellow _xfree86_ hackers!
This is a second email in a series aimed at getting code into the X server
needed to use a more sophisticated way of typing --- exploiting the time
information: key semantics changes according to the duration of the key press.
I am providing the code to be distributed under the xfree86 version 1.1 licence.
Linux provides an alternative way to read keyboard input:
/dev/input/eventXXX device, provided by the "evdev" driver/module.
This source provides 2 new features:
1/ every event has a timestamp, which is more precise that calling (inside X
server) GetTimeInMillis
Playing with the time of key events is the core point of my "rewriting
plugin", so i had to make several changes to all parts of X server which
handle key events.
2/ distinct hardware devices (keyboard) are reported in distinct devices/files
/dev/input/event0 /dev/input/event1 ...
The driver, called Medved, wraps/represents the set of evdev devices (keyboards)
as 1 X keyboard. I.e. X clients see 1 _core_ keyboard --- no need to play
with XInput:
The driver is quite unusual, b/c it handles more file descriptors, and therefore
it cannot use a higher level
xf86AddInputDriver and xf86Wakeup
but i have to use lower level RegisterBlockAndWakeupHandlers.
and look directly at the select mask to see which of managed files/devices has input ready.
While coding this driver, i realized the cause of several bugs in other
drivers.
I had previously communicated in
http://www.mail-archive.com/devel%40xfree86.org/msg06818.html
the conflict between "current state" of the 2 halves of key handling code,
"separated" by xf86EventQueue.
In that message, i talked about "down" bit-array, now i add, that keyc->state is
impossible to use in the lower half.
This (incorrect use of upper half's state in the lower half, which runs ahead in
time) explain 2 bugs:
1/ XF86Config options:
Option "AllowDeactivateGrabs" "yes" # C-M kp-/ steal the grab
Option "AllowClosedownGrabs" "yes" # C-M kp-* kill the grabbing client
simply didn't work (in situations i needed them)
situation:
an application stopped accepting key events (i.e. holds a synchro grab &
doesn't call XAllowEvent) -> therefore keys accumulate in syncEvents
(dix/events.c) w/o effecting the XKB state
XKB state is not "meta & control modifiers"
now, you want to steal the grab, so you press 3 keys Contol Meta kp-/
-> the first 2 events enter the queue, and the keyboard driver
when processing the 3rd (kp-/) will look at the XKB state which is not
"meta & control modifiers", hence it will not trigger any special function.
Same applies to C-M-Fn to switch VTs (during such frozen situation).
2/ C-M-Fn occasionally leaked the Fn key into applications.
When the Fn was processed before the the upper part picked C M from
xf86EventQueue and changed the XKB state. This is possible when X server
reads all 3 key events at once.
Solution is not very flexible, though. I just look if the fixed keys (say Alt_L,
Ctrl_L) are down.
(Since i play a lot with the time,) medved does not use HW autorepeat. This will
be explained in the next email.
More hw keyboards are used in the OR mode: a key is down, if and only if it is down
on any of the hw keyboards.
Hotplug is handled by polling, sorry.
I had to pull some functionality from the linux specific part of the "kbd"
driver (lnx_kbd.c) into a shared part.
I enclose these preparative changes as a patch.
The driver itself is at http://maruska.dyndns.org/comp/packages/medved.tar.gz
If you build xfree86 server with this patch & driver, you should notice that:
- auto-repeat does not work
- X mixes its monotonic time (GetTimeInMillis) with the timestamp coming from
evdev devices (gettimeofday) ... with various consequences (keyboard grabbing
stops working, iirc)
I don't bother providing fix for this, because in the next messages i will
provide
- patch for linux kernel & GetTimeInMillis:
preversion http://maruska.dyndns.org/wiki/kernel.html
- and an alternative method of generating auto-repeat events: at last
a reasonable auto-repeat, which i found lacking 2 years ago, when i started
fixing (hacking around) the key-event processing in X server.
medved.patch
(text/x-patch, 8.6 KB)
--- /linux/13/x/xfree86/xc/programs/Xserver/hw/xfree86/common/xf86Events.c 2005-05-31 04:46:39.000000000 +0200
+++ hw/xfree86/common/xf86Events.c 2005-06-02 19:14:34.000000000 +0200
@@ -228,6 +228,11 @@
}
+/* mmc: called from Dispatch !!! (to serve the events collected in signal handlers?)
+ * but more importantly from WakeupHandler (after drivers have a go on reading from their FDs)!
+ * But, that means that they will not be in chronological order! if i have 2 FDs for a keyboard.
+ * Unless either the driver handles both of them & takes care, or ... we sort them later. (todo)
+ */
/*
* ProcessInputEvents --
@@ -1643,6 +1648,6 @@
xf86PostKbdEvent(keycode);
xf86UnblockSIGIO(blocked);
}
- }
+}
}
#endif /* WSCONS_SUPPORT */
--- /linux/13/x/xfree86/xc/programs/Xserver/hw/xfree86/common/xf86Init.c 2005-03-07 18:32:40.000000000 +0100
+++ hw/xfree86/common/xf86Init.c 2005-04-10 16:51:21.000000000 +0200
@@ -604,6 +604,7 @@
xfree(driverlist);
}
+ /* mmc: unconditionally?? */
/* Setup the builtin input drivers */
xf86AddInputDriver(&xf86KEYBOARD, NULL, 0);
/* Load all input driver modules specified in the config file. */
--- /linux/13/x/xfree86/xc/programs/Xserver/hw/xfree86/common/xf86KbdLnx.c 2004-02-14 00:58:37.000000000 +0100
+++ hw/xfree86/common/xf86KbdLnx.c 2005-04-13 00:06:44.000000000 +0200
@@ -126,8 +126,8 @@
* compute the modifier map
*/
for (i = 0; i < MAP_LENGTH; i++)
- pModMap[i] = NoSymbol; /* make sure it is restored */
-
+ pModMap[i] = NoSymbol; /* make sure it is restored */ /*mmc: why not simply 0? NoSymbol is keysym,
+ * but here we set CARD8*/
for (k = map, i = MIN_KEYCODE;
i < (NUM_KEYCODES + MIN_KEYCODE);
i++, k += 4)
--- /linux/13/x/xfree86/xc/programs/Xserver/hw/xfree86/common/xf86Xinput.c 2004-02-14 00:58:39.000000000 +0100
+++ hw/xfree86/common/xf86Xinput.c 2005-06-02 19:11:08.000000000 +0200
@@ -1349,12 +1349,14 @@
va_end(var);
}
+/* new entry point for drivers which provide the event's timestamp ("medved") */
void
-xf86PostKeyboardEvent(DeviceIntPtr device,
+xf86PostKeyboardTimedEvent(DeviceIntPtr device,
unsigned int key_code,
- int is_down)
+ int is_down,
+ CARD32 time)
{
- xEvent xE[2];
+ xEvent xE[1]; /* why 2 ?? */
deviceKeyButtonPointer *xev = (deviceKeyButtonPointer*) xE;
if (xf86IsCoreKeyboard(device)) {
@@ -1363,7 +1365,7 @@
xev->type = is_down ? DeviceKeyPress : DeviceKeyRelease;
}
xev->detail = key_code;
- xf86Info.lastEventTime = xev->time = GetTimeInMillis();
+ xf86Info.lastEventTime = xev->time = time;
#ifdef XFreeXDGA
/* if(!DGAStealKeyEvent(xf86EventQueue.pEnqueueScreen->myNum, xE)) */
@@ -1371,6 +1373,15 @@
ENQUEUE(xE);
}
+/* mmc: called from the "kbd" driver. PostKbdEvent() from the builtin driver instead */
+void
+xf86PostKeyboardEvent(DeviceIntPtr device,
+ unsigned int key_code,
+ int is_down)
+{
+ xf86PostKeyboardTimedEvent(device, key_code, is_down, GetTimeInMillis());
+}
+
/*
* Motion history management.
*/
--- /linux/13/x/xfree86/xc/programs/Xserver/hw/xfree86/common/xf86Xinput.h 2004-02-14 00:58:39.000000000 +0100
+++ hw/xfree86/common/xf86Xinput.h 2005-04-10 17:36:43.000000000 +0200
@@ -209,6 +209,10 @@
...);
void xf86PostKeyboardEvent(DeviceIntPtr device, unsigned int key_code,
int is_down);
+
+void xf86PostKeyboardTimedEvent(DeviceIntPtr device, unsigned int key_code,
+ int is_down, CARD32 time);
+
void xf86MotionHistoryAllocate(LocalDevicePtr local);
int xf86GetMotionEvents(DeviceIntPtr dev, xTimecoord *buff,
unsigned long start, unsigned long stop,
--- /linux/13/x/xfree86/xc/programs/Xserver/hw/xfree86/loader/xf86sym.c 2005-02-26 19:31:48.000000000 +0100
+++ hw/xfree86/loader/xf86sym.c 2005-06-02 13:49:50.000000000 +0200
@@ -339,6 +339,15 @@
SYMFUNC(xf86EnableAGP)
SYMFUNC(xf86SoundKbdBell)
SYMFUNC(xf86GARTCloseScreen)
+#ifdef __linux__
+ /* mmc: export some more functions for the keyboard driver. They are all linux specific! */
+ /* fixme: i don't know if linux or __linux__ is correct! */
+ SYMFUNC(put_console_into_raw)
+ SYMFUNC(restore_console)
+ SYMFUNC(select_console)
+ SYMFUNC(console_beep)
+#endif /* __linux__ */
+
#ifdef XINPUT
/* XISB routines (Merged from Metrolink tree) */
SYMFUNC(XisbNew)
@@ -561,6 +570,8 @@
SYMFUNC(xf86RemoveEnabledDevice)
SYMFUNC(xf86InterceptSignals)
SYMFUNC(xf86EnableVTSwitch)
+ /* (mmc:) keyboard driver might know better than xf86CommonSpecialKey, when to take action */
+ SYMFUNC(xf86ProcessActionEvent)
/* xf86Helper.c */
SYMFUNC(xf86AddDriver)
@@ -847,6 +858,7 @@
SYMFUNC(xf86PostButtonEvent)
SYMFUNC(xf86PostKeyEvent)
SYMFUNC(xf86PostKeyboardEvent)
+ SYMFUNC(xf86PostKeyboardTimedEvent) /* new entry point */
SYMFUNC(xf86GetMotionEvents)
SYMFUNC(xf86MotionHistoryAllocate)
SYMFUNC(xf86FirstLocalDevice)
--- /linux/13/x/xfree86/xc/programs/Xserver/hw/xfree86/os-support/linux/lnx_kbd.c 2004-11-10 05:28:38.000000000 +0100
+++ hw/xfree86/os-support/linux/lnx_kbd.c 2005-06-02 19:28:34.000000000 +0200
@@ -305,11 +305,6 @@
#endif /* KBD_DIRECTHW */
}
-typedef struct {
- int kbdtrans;
- struct termios kbdtty;
-} LnxKbdPrivRec, *LnxKbdPrivPtr;
-
static int
KbdInit(InputInfoPtr pInfo, int what)
{
@@ -317,7 +312,7 @@
LnxKbdPrivPtr priv = (LnxKbdPrivPtr) pKbd->private;
if (pKbd->isConsole) {
- ioctl (pInfo->fd, KDGKBMODE, &(priv->kbdtrans));
+ ioctl (pInfo->fd, KDGKBMODE, &(priv->kbdtrans)); /* gets current keyboard mode */
tcgetattr (pInfo->fd, &(priv->kbdtty));
}
if (!pKbd->CustomKeycodes) {
@@ -338,7 +333,7 @@
if (pKbd->CustomKeycodes)
ioctl(pInfo->fd, KDSKBMODE, K_MEDIUMRAW);
else
- ioctl(pInfo->fd, KDSKBMODE, K_RAW);
+ ioctl(pInfo->fd, KDSKBMODE, K_RAW); /* mmc: i need this to avoid M-Right changing the VT? */
nTty = priv->kbdtty;
nTty.c_iflag = (IGNPAR | IGNBRK) & (~PARMRK) & (~ISTRIP);
@@ -353,6 +348,48 @@
}
return Success;
}
+/* extracted functionality from SoundBell (see above)!*/
+void
+console_beep(int loudness, int pitch, int duration)
+{
+ if (loudness && pitch)
+ {
+ ioctl(xf86Info.consoleFd, KDMKTONE,
+ ((1193190 / pitch) & 0xffff) |
+ (((unsigned long)duration *
+ loudness / 50) << 16));
+ }
+}
+
+
+
+void
+put_console_into_raw(LnxKbdPrivPtr save)
+{
+ int fd = xf86Info.consoleFd;
+
+ ioctl (fd, KDGKBMODE, &(save->kbdtrans)); /* mmc: gets current keyboard mode */
+ tcgetattr (fd, &(save->kbdtty));
+
+ ioctl(fd, KDSKBMODE, K_RAW);
+}
+
+void
+restore_console(LnxKbdPrivPtr save)
+{
+ int fd = xf86Info.consoleFd;
+
+ ioctl(fd, KDSKBMODE, save->kbdtrans);
+ tcsetattr(fd, TCSANOW, &(save->kbdtty));
+}
+
+
+void
+select_console(int console)
+{
+ int fd = xf86Info.consoleFd;
+ ioctl(fd, VT_ACTIVATE, console);
+}
static int
KbdOff(InputInfoPtr pInfo, int what)
--- /linux/13/x/xfree86/xc/programs/Xserver/hw/xfree86/os-support/linux/lnx_KbdMap.c 2002-10-11 03:40:35.000000000 +0200
+++ hw/xfree86/os-support/linux/lnx_KbdMap.c 2005-06-02 19:18:01.000000000 +0200
@@ -49,7 +49,7 @@
* compute the modifier map
*/
for (i = 0; i < MAP_LENGTH; i++)
- pModMap[i] = NoSymbol; /* make sure it is restored */
+ pModMap[i] = NoSymbol; /* make sure it is restored mmc: i really prefer 0L: this is a bitmask, not a keysym */
for (k = map, i = MIN_KEYCODE;
i < (NUM_KEYCODES + MIN_KEYCODE);
--- /linux/13/x/xfree86/xc/programs/Xserver/hw/xfree86/os-support/xf86OSKbd.h 2003-11-03 06:11:51.000000000 +0100
+++ hw/xfree86/os-support/xf86OSKbd.h 2005-06-02 19:30:01.000000000 +0200
@@ -114,6 +114,23 @@
Bool xf86OSKbdPreInit(InputInfoPtr pInfo);
+
+#ifdef __linux__
+/* mmc: see my comment in ../loader/xf86sym.c */
+/* taken from lnx_kbd.c this should be common for medved and kbd(on linux), but for now it's not. */
+
+#include <termios.h>
+typedef struct {
+ int kbdtrans;
+ struct termios kbdtty;
+} LnxKbdPrivRec, *LnxKbdPrivPtr;
+
+extern void put_console_into_raw(LnxKbdPrivPtr);
+extern void restore_console (LnxKbdPrivPtr);
+extern void select_console(int console);
+extern void console_beep(int loudness, int pitch, int duration);
+#endif
+
/* Adjust this when the kbd interface changes. */
/*