> Can you share your patches, please? > > I would also be very interested in your specific setup and if you had to > tweak > e.g. xorg configuration.
This patch should do it. It's not exactly what I use, but it compiles, and appears to have the same behaviour. The only difference (related to this) is that I setup some other stuff to support multiple users in a single config so I don't have to have multiple dwm's executables. About xorg.conf, I only changed it to name my devices. That's it. However I use another program I made to solve the problem in which changing vt's (usually the ctrl+alt+f1 and similar) attaches all slave devices to "Virtual Core" so no input is generated on other master devices. Basically it detects changes in the input hierarchy and does the necessary readjustments. It's a bit more complex than it could be, but this way we make the clean changes we need. In principle you compile two executables with different configs and do something like: DISPLAY=:0.0 dwm-screen0 & DISPLAY=:0.1 dwm-screen1 & I tried to maintain the coding style and philosophy Hope it's helpful :) -- Raimundo Martins <raimundoomart...@gmail.com>
diff --git a/config.def.h b/config.def.h index 875885b..58fa776 100644 --- a/config.def.h +++ b/config.def.h @@ -1,5 +1,7 @@ /* See LICENSE file for copyright and license details. */ +static char devicename[] = "Virtual Core"; + /* appearance */ static const char font[] = "-*-terminus-medium-r-*-*-16-*-*-*-*-*-*-*"; static const char normbordercolor[] = "#444444"; diff --git a/config.mk b/config.mk index bc3d80e..3db15fe 100644 --- a/config.mk +++ b/config.mk @@ -16,7 +16,7 @@ XINERAMAFLAGS = -DXINERAMA # includes and libs INCS = -I${X11INC} -LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} +LIBS = -L${X11LIB} -lX11 -lXi ${XINERAMALIBS} # flags CPPFLAGS = -D_BSD_SOURCE -D_POSIX_C_SOURCE=2 -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS} diff --git a/dwm.c b/dwm.c index 1bbb4b3..11d23c0 100644 --- a/dwm.c +++ b/dwm.c @@ -31,8 +31,10 @@ #include <sys/types.h> #include <sys/wait.h> #include <X11/cursorfont.h> +#include <X11/extensions/XInput2.h> #include <X11/keysym.h> #include <X11/Xatom.h> +#include <X11/XF86keysym.h> #include <X11/Xlib.h> #include <X11/Xproto.h> #include <X11/Xutil.h> @@ -169,13 +171,15 @@ static void focus(Client *c); static void focusin(XEvent *e); static void focusmon(const Arg *arg); static void focusstack(const Arg *arg); +static void genericevent(XEvent *e); +static XIDeviceInfo *getdevicebyname(XIDeviceInfo *devs, int ndev, const char *name); +static void getdevices(const char *user); static Bool getrootptr(int *x, int *y); static long getstate(Window w); static Bool gettextprop(Window w, Atom atom, char *text, unsigned int size); static void grabbuttons(Client *c, Bool focused); static void grabkeys(void); static void incnmaster(const Arg *arg); -static void keypress(XEvent *e); static void killclient(const Arg *arg); static void manage(Window w, XWindowAttributes *wa); static void mappingnotify(XEvent *e); @@ -250,7 +254,7 @@ static void (*handler[LASTEvent]) (XEvent *) = { [EnterNotify] = enternotify, [Expose] = expose, [FocusIn] = focusin, - [KeyPress] = keypress, + [GenericEvent] = genericevent, [MappingNotify] = mappingnotify, [MapRequest] = maprequest, [MotionNotify] = motionnotify, @@ -266,6 +270,14 @@ static Drw *drw; static Fnt *fnt; static Monitor *mons, *selmon; static Window root; +static int xi2opcode; +static unsigned char ptrmask[XIMaskLen(XI_LASTEVENT)]; +static unsigned char kbdmask[XIMaskLen(XI_LASTEVENT)]; +static unsigned char hcmask[XIMaskLen(XI_HierarchyChanged)]; +static XIEventMask ptrevm = { -1, LENGTH(ptrmask), ptrmask }; +static XIEventMask kbdevm = { -1, LENGTH(kbdmask), kbdmask }; +static XIEventMask hcevm = { XIAllDevices, sizeof(hcmask), hcmask }; +static XIGrabModifiers anymodifier[] = { { XIAnyModifier, 0 } }; /* configuration, allows nested code to access above variables */ #include "config.h" @@ -287,7 +299,6 @@ applyrules(Client *c) { XGetClassHint(dpy, c->win, &ch); class = ch.res_class ? ch.res_class : broken; instance = ch.res_name ? ch.res_name : broken; - for(i = 0; i < LENGTH(rules); i++) { r = &rules[i]; if((!r->title || strstr(c->name, r->title)) @@ -468,7 +479,7 @@ cleanup(void) { for(m = mons; m; m = m->next) while(m->stack) unmanage(m->stack, False); - XUngrabKey(dpy, AnyKey, AnyModifier, root); + XIUngrabKeycode(dpy, kbdevm.deviceid, XIAnyKeycode, root, LENGTH(anymodifier), anymodifier); while(mons) cleanupmon(mons); drw_cur_free(drw, cursor[CurNormal]); @@ -483,7 +494,7 @@ cleanup(void) { drw_clr_free(scheme[SchemeSel].fg); drw_free(drw); XSync(dpy, False); - XSetInputFocus(dpy, PointerRoot, RevertToPointerRoot, CurrentTime); + XISetFocus(dpy, kbdevm.deviceid, PointerRoot, CurrentTime); XDeleteProperty(dpy, root, netatom[NetActiveWindow]); } @@ -796,7 +807,7 @@ focus(Client *c) { setfocus(c); } else { - XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); + XISetFocus(dpy, kbdevm.deviceid, PointerRoot, CurrentTime); XDeleteProperty(dpy, root, netatom[NetActiveWindow]); } selmon->sel = c; @@ -851,6 +862,51 @@ focusstack(const Arg *arg) { } } +void +genericevent(XEvent *e) { + /* I discovered that XSendEvent can send us events that as not GenericEvent */ + if(e->type != GenericEvent) + return; + if(e->xcookie.extension == xi2opcode) + { + unsigned int i; + + if(!XGetEventData(dpy, &e->xcookie)) { + fputs("warning: failed to retrieve cookie data of a XI2 event\n", stderr); + XFreeEventData(dpy, &e->xcookie); + return; + } + switch(e->xcookie.evtype) { + case XI_KeyPress: { + KeySym keysym; + XIDeviceEvent *ev = (XIDeviceEvent *)e->xcookie.data; + + if(ev->deviceid != kbdevm.deviceid) + break; + keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->detail, 0); + for(i = 0; keys[i].func; i++) + if(keysym == keys[i].keysym && CLEANMASK(keys[i].mod) == CLEANMASK(ev->mods.effective)) + keys[i].func(&(keys[i].arg)); + } + break; + case XI_HierarchyChanged: { + XIHierarchyEvent *ev = (XIHierarchyEvent *)e->xcookie.data; + + if(ev->flags & XIMasterRemoved) { + for(i = 0; i < ev->num_info; i++) { + if(ev->info[i].flags && ev->info[i].deviceid == kbdevm.deviceid) + /* our keyboard device has been removed! let's recreate it or we'll never get focus again */ + getdevices(devicename); + } + break; + } + } + break; + } + XFreeEventData(dpy, &e->xcookie); + } +} + Atom getatomprop(Client *c, Atom prop) { int di; @@ -866,6 +922,55 @@ getatomprop(Client *c, Atom prop) { return atom; } +XIDeviceInfo* +getdevicebyname(XIDeviceInfo * const devs, const int ndev, const char *name) { + unsigned int i, j; + const char *devname; + XIDeviceInfo *result = NULL; + + for (i = 0; i < ndev; i++) { + if(devs[i].use != XIMasterPointer) + continue; + devname = devs[i].name; + for(j = 0; name[j] && devname[j] && name[j] == devname[j]; j++); + if(name[j] || !devname[j] || strcmp(&devname[j], " pointer")) + continue; + result = &devs[i]; + break; + } + return result; +} + +void +getdevices(const char *name) { + int ndevices; + XIDeviceInfo *devs, *ptr; + const XIAddMasterInfo addm = {.type = XIAddMaster, .name = devicename, .send_core = True, .enable = True }; + + devs = XIQueryDevice(dpy, XIAllMasterDevices, &ndevices); + if(!devs) + die("fatal: can't get list of input devices\n"); + ptr = getdevicebyname(devs, ndevices, name); + if(!ptr) { + if(XIChangeHierarchy(dpy, (XIAnyHierarchyChangeInfo *)&addm, 1) == Success) { + XIFreeDeviceInfo(devs); + devs = XIQueryDevice(dpy, XIAllMasterDevices, &ndevices); + if(!devs) + die("fatal: can't get list of input devices\n"); + ptr = getdevicebyname(devs, ndevices, name); + } + } + if(ptr) { + ptrevm.deviceid = ptr->deviceid; + kbdevm.deviceid = ptr->attachment; + } + else { + ptrevm.deviceid = 2; /* Virtual core pointer */ + kbdevm.deviceid = 3; /* Virtual core keyboard */ + } + XIFreeDeviceInfo(devs); + XISelectEvents(dpy, root, &kbdevm, 1); +} Bool getrootptr(int *x, int *y) { int di; @@ -943,16 +1048,16 @@ void grabkeys(void) { updatenumlockmask(); { - unsigned int i, j; - unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask|LockMask }; + unsigned int i; KeyCode code; - XUngrabKey(dpy, AnyKey, AnyModifier, root); - for(i = 0; i < LENGTH(keys); i++) - if((code = XKeysymToKeycode(dpy, keys[i].keysym))) - for(j = 0; j < LENGTH(modifiers); j++) - XGrabKey(dpy, code, keys[i].mod | modifiers[j], root, - True, GrabModeAsync, GrabModeAsync); + XIUngrabKeycode(dpy, kbdevm.deviceid, XIAnyKeycode, root, LENGTH(anymodifier), anymodifier); + for(i = 0; keys[i].func; i++) + if((code = XKeysymToKeycode(dpy, keys[i].keysym))) { + XIGrabModifiers modifiers[] = { { keys[i].mod, 0 }, { keys[i].mod|LockMask, 0 }, + { keys[i].mod|numlockmask, 0 }, { keys[i].mod|numlockmask|LockMask, 0 } }; + XIGrabKeycode(dpy, kbdevm.deviceid, code, root, XIGrabModeAsync, XIGrabModeAsync, True, &kbdevm, LENGTH(modifiers), modifiers); + } } } @@ -974,21 +1079,6 @@ isuniquegeom(XineramaScreenInfo *unique, size_t n, XineramaScreenInfo *info) { #endif /* XINERAMA */ void -keypress(XEvent *e) { - unsigned int i; - KeySym keysym; - XKeyEvent *ev; - - ev = &e->xkey; - keysym = XKeycodeToKeysym(dpy, (KeyCode)ev->keycode, 0); - for(i = 0; i < LENGTH(keys); i++) - if(keysym == keys[i].keysym - && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) - && keys[i].func) - keys[i].func(&(keys[i].arg)); -} - -void killclient(const Arg *arg) { if(!selmon->sel) return; @@ -1045,6 +1135,7 @@ manage(Window w, XWindowAttributes *wa) { updatewindowtype(c); updatesizehints(c); updatewmhints(c); + XISetClientPointer(dpy, w, ptrevm.deviceid); XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask); grabbuttons(c, False); if(!c->isfloating) @@ -1424,7 +1515,7 @@ sendevent(Client *c, Atom proto) { void setfocus(Client *c) { if(!c->neverfocus) { - XSetInputFocus(dpy, c->win, RevertToPointerRoot, CurrentTime); + XISetFocus(dpy, kbdevm.deviceid, c->win, CurrentTime); XChangeProperty(dpy, root, netatom[NetActiveWindow], XA_WINDOW, 32, PropModeReplace, (unsigned char *) &(c->win), 1); @@ -1489,8 +1580,21 @@ setmfact(const Arg *arg) { void setup(void) { + int event, error, major = 2, minor = 1; XSetWindowAttributes wa; + if (!XQueryExtension(dpy, "XInputExtension", &xi2opcode, &event, &error)) + die("X Input extension not available.\n"); + if (XIQueryVersion(dpy, &major, &minor) == BadRequest) + die("XI2 not available. Server supports %d.%d\n", major, minor); + + /* setup some vars */ + memset(kbdmask, 0, sizeof(kbdmask)); + memset(hcmask, 0, sizeof(kbdmask)); + XISetMask(kbdmask, XI_KeyPress); + XISetMask(hcmask, XI_HierarchyChanged); + getdevices(devicename); + /* clean up any zombies immediately */ sigchld(0); @@ -1541,8 +1645,11 @@ setup(void) { |EnterWindowMask|LeaveWindowMask|StructureNotifyMask|PropertyChangeMask; XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); XSelectInput(dpy, root, wa.event_mask); + XISelectEvents(dpy, root, &hcevm, 1); grabkeys(); focus(NULL); + /* move pointer device to our screen */ + XIWarpPointer(dpy, ptrevm.deviceid, None, root, 0, 0, 0, 0, sw/2, sh/2); } void @@ -1678,7 +1785,7 @@ unfocus(Client *c, Bool setfocus) { grabbuttons(c, False); XSetWindowBorder(dpy, c->win, scheme[SchemeNorm].border->rgb); if(setfocus) { - XSetInputFocus(dpy, root, RevertToPointerRoot, CurrentTime); + XISetFocus(dpy, kbdevm.deviceid, root, CurrentTime); XDeleteProperty(dpy, root, netatom[NetActiveWindow]); } } @@ -1696,7 +1803,7 @@ unmanage(Client *c, Bool destroyed) { XGrabServer(dpy); XSetErrorHandler(xerrordummy); XConfigureWindow(dpy, c->win, CWBorderWidth, &wc); /* restore border */ - XUngrabButton(dpy, AnyButton, AnyModifier, c->win); + XIUngrabButton(dpy, ptrevm.deviceid, XIAnyButton, c->win, LENGTH(anymodifier), anymodifier); setclientstate(c, WithdrawnState); XSync(dpy, False); XSetErrorHandler(xerror);
#include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <X11/extensions/XInput2.h> #include <X11/Xlib.h> #define LENGTH(item) (sizeof(item)/sizeof(*item)) typedef struct { char *name; const char * display; /* only screens may change for now. xserver must be the same for all */ const char * const * ptrs; const char * const * kbds; } MasterDevice; static void attachslaves(const XIDeviceInfo *devs, int ndev, int master, const char * const *slaves); static void die(const char *errstr, ...); static const XIDeviceInfo *getdevicebyname(const XIDeviceInfo *devs, int ndev, int use, const char * name); static void hierarchychanged(const XIHierarchyEvent * const ev); static void inittree(void); static int strcatcmp(const char *lhs, const char *rhs1, const char *rhs2); static const MasterDevice mdevs[] = { { "raimundo", ":0.0", (const char *[]){ "MousePS2", NULL }, (const char *[]){ "KeyboardPS2", NULL } }, { "carla", ":0.1", (const char *[]){ "MouseGIGABYTE", "KeyboardGIGABYTEMultimedia", NULL }, (const char *[]){ "KeyboardGIGABYTE", NULL } }, }; static int xi2opcode; static unsigned char hcmask[XIMaskLen(XI_HierarchyChanged)]; static Display *dpy; static Window root; static XIEventMask hcevm = { XIAllDevices, sizeof(hcmask), hcmask }; void attachslaves(const XIDeviceInfo * const devs, const int ndev, const int master, const char * const * const slaves) { int i; const XIDeviceInfo *slave; XIAttachSlaveInfo atts = { XIAttachSlave, 0, master }; for(i = 0; slaves[i]; i++) { if(!(slave = getdevicebyname(devs, ndev, XIFloatingSlave, slaves[i])) || slave->attachment == master) continue; atts.deviceid = slave->deviceid; XIChangeHierarchy(dpy, (XIAnyHierarchyChangeInfo *)&atts, 1); } } void die(const char *errstr, ...) { va_list ap; va_start(ap, errstr); vfprintf(stderr, errstr, ap); va_end(ap); exit(EXIT_FAILURE); } const XIDeviceInfo *getdevicebyname(const XIDeviceInfo * const devs, const int ndev, const int use, const char * const name) { int i; const char *cmp; switch(use) { case XIMasterPointer: cmp = " pointer"; break; case XIMasterKeyboard: cmp = " keyboard"; break; default: cmp = ""; break; } for(i = ndev-1; i >= 0; i--) if(!strcatcmp(devs[i].name, name, cmp)) return &devs[i]; return NULL; } void hierarchychanged(const XIHierarchyEvent * const ev) { int i, j, k, did, ndev; XIDeviceInfo *changed, * const devs = XIQueryDevice(dpy, XIAllDevices, &ndev); if(!devs) return; for(i = 0; i < ev->num_info; i++) { if(!ev->info[i].flags) continue; did = ev->info[i].deviceid; for(changed = NULL, j = 0; j < ndev; j++) if(devs[i].deviceid == did) { changed = &devs[i]; break; } if(!changed) continue; if(ev->info[i].flags & XIMasterAdded) { switch(changed->use) { case XIMasterPointer: for(j = 0; j < LENGTH(mdevs); j++) { if(strcatcmp(changed->name, mdevs[j].name, " pointer")) continue; attachslaves(devs, ndev, did, mdevs[j].ptrs); break; } break; case XIMasterKeyboard: for(j = 0; j < LENGTH(mdevs); j++) { if(strcatcmp(changed->name, mdevs[j].name, " keyboard")) continue; attachslaves(devs, ndev, did, mdevs[j].kbds); break; } break; } } if((ev->info[i].flags & XIDeviceEnabled) && (changed->use & (XISlavePointer|XISlaveKeyboard|XIFloatingSlave))) { XIAttachSlaveInfo atts = { XIAttachSlave, did, 0 }; const XIDeviceInfo *master = NULL; for(k = 0; k < LENGTH(mdevs); k++) { for(j = 0; mdevs[k].ptrs[j]; j++) if(!strcmp(mdevs[k].ptrs[j], changed->name)) { master = getdevicebyname(devs, ndev, XIMasterPointer, mdevs[k].name); goto breakcycle; } for(j = 0; mdevs[k].kbds[j]; j++) if(!strcmp(mdevs[k].kbds[j], changed->name)) { master = getdevicebyname(devs, ndev, XIMasterKeyboard, mdevs[k].name); goto breakcycle; } } breakcycle: if(master) { atts.new_master = master->deviceid; XIChangeHierarchy(dpy, (XIAnyHierarchyChangeInfo *)&atts, 1); } } } XIFreeDeviceInfo(devs); } void inittree(void) { int i, ndev; const XIDeviceInfo *master; XIDeviceInfo * const devs = XIQueryDevice(dpy, XIAllDevices, &ndev); if(!devs) return; for(i = 0; i < LENGTH(mdevs); i++) { if(!(master = getdevicebyname(devs, ndev, XIMasterPointer, mdevs[i].name)) continue; attachslaves(devs, ndev, master->deviceid, mdevs[i].ptrs); attachslaves(devs, ndev, master->attachment, mdevs[i].kbds); } XIFreeDeviceInfo(devs); } int strcatcmp(const char *lhs, const char *const rhs1, const char *const rhs2) { int i; for(i = 0; lhs[i] && rhs1[i] && lhs[i] == rhs1[i]; i++); if(rhs1[i]) return -1; lhs = &lhs[i]; for(i = 0; lhs[i] && rhs2[i] && lhs[i] == rhs2[i]; i++); if(!lhs[i] && !rhs2[i]) return 0; else return 1; } int main(int argc, char **argv) { XEvent ev; int screen, event, error, major = 2, minor = 1; if(!(dpy = XOpenDisplay(NULL))) die("inputmanager: cannot open display\n"); if (!XQueryExtension(dpy, "XInputExtension", &xi2opcode, &event, &error)) die("inputmanager: XInputExtension is not available\n"); if(XIQueryVersion(dpy, &major, &minor) == BadRequest) die("inputmanager: XInput2 2.1 is not available. Server supports up to %d.%d\n", major, minor); root = DefaultRootWindow(dpy); inittree(); memset(hcmask, 0, sizeof(hcmask)); XISetMask(hcmask, XI_HierarchyChanged); XISelectEvents(dpy, root, &hcevm, 1); screen = DefaultScreen(dpy); XIWarpPointer(dpy, 2, 0, root, 0, 0, 0, 0, DisplayWidth(dpy, screen), DisplayHeight(dpy, screen)); XSync(dpy, False); while(1) { XNextEvent(dpy, &ev); if(ev.type != GenericEvent || ev.xcookie.extension != xi2opcode || ev.xcookie.evtype != XI_HierarchyChanged || !XGetEventData(dpy, &ev.xcookie)) continue; hierarchychanged((XIHierarchyEvent *)ev.xcookie.data); XFreeEventData(dpy, &ev.xcookie); } XCloseDisplay(dpy); return 0; }