diff --git a/config.def.h b/config.def.h index 875885b..780ff6f 100644 --- a/config.def.h +++ b/config.def.h @@ -21,9 +21,9 @@ static const Rule rules[] = { * WM_CLASS(STRING) = instance, class * WM_NAME(STRING) = title */ - /* class instance title tags mask isfloating monitor */ - { "Gimp", NULL, NULL, 0, True, -1 }, - { "Firefox", NULL, NULL, 1 << 8, False, -1 }, + /* class instance title tags mask isfloating monitor xkb_layout */ + { "Gimp", NULL, NULL, 0, True, -1, 0 }, + { "Firefox", NULL, NULL, 1 << 8, False, -1, -1 }, }; /* layout(s) */ @@ -31,6 +31,13 @@ static const float mfact = 0.55; /* factor of master area size [0.05..0.95] static const int nmaster = 1; /* number of clients in master area */ static const Bool resizehints = True; /* True means respect size hints in tiled resizals */ +/* xkb frontend */ +static const Bool showxkb = True; /* False means no xkb layout text */ +static const char *xkb_layouts [] = { + "en", + "ru", +}; + static const Layout layouts[] = { /* symbol arrange function */ { "[]=", tile }, /* first entry is default */ diff --git a/dwm.c b/dwm.c index 1bbb4b3..ec39eef 100644 --- a/dwm.c +++ b/dwm.c @@ -36,6 +36,7 @@ #include #include #include +#include #ifdef XINERAMA #include #endif /* XINERAMA */ @@ -83,6 +84,7 @@ typedef struct { typedef struct Monitor Monitor; typedef struct Client Client; +typedef struct XkbInfo XkbInfo; struct Client { char name[256]; float mina, maxa; @@ -96,6 +98,13 @@ struct Client { Client *snext; Monitor *mon; Window win; + XkbInfo *xkb; +}; +struct XkbInfo { + XkbInfo *next; + XkbInfo *prev; + int group; + Window w; }; typedef struct { @@ -138,6 +147,7 @@ typedef struct { unsigned int tags; Bool isfloating; int monitor; + int xkb_layout; } Rule; /* function declarations */ @@ -157,6 +167,7 @@ static void configure(Client *c); static void configurenotify(XEvent *e); static void configurerequest(XEvent *e); static Monitor *createmon(void); +static XkbInfo *createxkb(Window w); static void destroynotify(XEvent *e); static void detach(Client *c); static void detachstack(Client *c); @@ -165,6 +176,7 @@ static void drawbar(Monitor *m); static void drawbars(void); static void enternotify(XEvent *e); static void expose(XEvent *e); +static XkbInfo *findxkb(Window w); static void focus(Client *c); static void focusin(XEvent *e); static void focusmon(const Arg *arg); @@ -231,6 +243,7 @@ static Monitor *wintomon(Window w); static int xerror(Display *dpy, XErrorEvent *ee); static int xerrordummy(Display *dpy, XErrorEvent *ee); static int xerrorstart(Display *dpy, XErrorEvent *ee); +static void xkbeventnotify(XEvent *e); static void zoom(const Arg *arg); /* variables */ @@ -241,6 +254,7 @@ static int sw, sh; /* X display screen geometry width, height */ static int bh, blw = 0; /* bar geometry */ static int (*xerrorxlib)(Display *, XErrorEvent *); static unsigned int numlockmask = 0; +static int xkbEventType = 0; static void (*handler[LASTEvent]) (XEvent *) = { [ButtonPress] = buttonpress, [ClientMessage] = clientmessage, @@ -266,6 +280,8 @@ static Drw *drw; static Fnt *fnt; static Monitor *mons, *selmon; static Window root; +static XkbInfo xkbGlobal; +static XkbInfo *xkbSaved = NULL; /* configuration, allows nested code to access above variables */ #include "config.h" @@ -299,6 +315,9 @@ applyrules(Client *c) { for(m = mons; m && m->num != r->monitor; m = m->next); if(m) c->mon = m; + if(r->xkb_layout > -1 ) { + c->xkb->group = r->xkb_layout; + } } } if(ch.res_class) @@ -644,6 +663,25 @@ createmon(void) { strncpy(m->ltsymbol, layouts[0].symbol, sizeof m->ltsymbol); return m; } +static XkbInfo * +createxkb(Window w){ + XkbInfo *xkb; + + xkb = malloc(sizeof *xkb); + if (xkb == NULL) { + die("fatal: could not malloc() %u bytes\n", sizeof *xkb); + } + xkb->group = xkbGlobal.group; + xkb->w = w; + xkb->next = xkbSaved; + if (xkbSaved != NULL) { + xkbSaved->prev = xkb; + } + xkb->prev = NULL; + xkbSaved = xkb; + + return xkb; +} void destroynotify(XEvent *e) { @@ -693,6 +731,7 @@ dirtomon(int dir) { void drawbar(Monitor *m) { int x, xx, w; + int ww = 0; unsigned int i, occ = 0, urg = 0; Client *c; @@ -718,14 +757,23 @@ drawbar(Monitor *m) { if(m == selmon) { /* status is only drawn on selected monitor */ w = TEXTW(stext); x = m->ww - w; + if (showxkb) { + ww = TEXTW(xkb_layouts[xkbGlobal.group]); + x -= ww; + } if(x < xx) { x = xx; w = m->ww - xx; } drw_text(drw, x, 0, w, bh, stext, 0); + if (showxkb) { + drw_setscheme(drw, &scheme[SchemeNorm]); + drw_text(drw, x+w, 0, ww, bh, xkb_layouts[xkbGlobal.group], 0); + } } else x = m->ww; + if((w = x - xx) > bh) { x = xx; if(m->sel) { @@ -777,6 +825,18 @@ expose(XEvent *e) { drawbar(m); } +XkbInfo * +findxkb(Window w) +{ + XkbInfo *xkb; + for (xkb = xkbSaved; xkb != NULL; xkb=xkb->next) { + if (xkb->w == w) { + return xkb; + } + } + return NULL; +} + void focus(Client *c) { if(!c || !ISVISIBLE(c)) @@ -1008,11 +1068,20 @@ manage(Window w, XWindowAttributes *wa) { Client *c, *t = NULL; Window trans = None; XWindowChanges wc; + XkbInfo *xkb; if(!(c = calloc(1, sizeof(Client)))) die("fatal: could not malloc() %u bytes\n", sizeof(Client)); c->win = w; updatetitle(c); + + /* Setting current xkb state must be before applyrules */ + xkb = findxkb(c->win); + if (xkb == NULL) { + xkb = createxkb(c->win); + } + c->xkb = xkb; + if(XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { c->mon = t->mon; c->tags = t->tags; @@ -1344,8 +1413,14 @@ run(void) { /* main event loop */ XSync(dpy, False); while(running && !XNextEvent(dpy, &ev)) + { + if(ev.type == xkbEventType) { + xkbeventnotify(&ev); + continue; + } if(handler[ev.type]) handler[ev.type](&ev); /* call handler */ + } } void @@ -1428,6 +1503,7 @@ setfocus(Client *c) { XChangeProperty(dpy, root, netatom[NetActiveWindow], XA_WINDOW, 32, PropModeReplace, (unsigned char *) &(c->win), 1); + XkbLockGroup(dpy, XkbUseCoreKbd, c->xkb->group); } sendevent(c, wmatom[WMTakeFocus]); } @@ -1490,6 +1566,7 @@ setmfact(const Arg *arg) { void setup(void) { XSetWindowAttributes wa; + XkbStateRec xkbstate; /* clean up any zombies immediately */ sigchld(0); @@ -1541,6 +1618,16 @@ setup(void) { |EnterWindowMask|LeaveWindowMask|StructureNotifyMask|PropertyChangeMask; XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); XSelectInput(dpy, root, wa.event_mask); + + /* get xkb extension info, events and current state */ + if (!XkbQueryExtension(dpy, NULL, &xkbEventType, NULL, NULL, NULL)) { + fputs("warning: can not query xkb extension\n", stderr); + } + XkbSelectEventDetails(dpy, XkbUseCoreKbd, XkbStateNotify, + XkbAllStateComponentsMask, XkbGroupStateMask); + XkbGetState(dpy, XkbUseCoreKbd, &xkbstate); + xkbGlobal.group = xkbstate.locked_group; + grabkeys(); focus(NULL); } @@ -1687,6 +1774,7 @@ void unmanage(Client *c, Bool destroyed) { Monitor *m = c->mon; XWindowChanges wc; + XkbInfo *xkb; /* The server grab construct avoids race conditions. */ detach(c); @@ -1702,6 +1790,18 @@ unmanage(Client *c, Bool destroyed) { XSetErrorHandler(xerror); XUngrabServer(dpy); } + else { + xkb = findxkb(c->win); + if (xkb != NULL) { + if (xkb->prev) { + xkb->prev->next = xkb->next; + } + if (xkb->next) { + xkb->next->prev = xkb->prev; + } + free(xkb); + } + } free(c); focus(NULL); updateclientlist(); @@ -2030,6 +2130,23 @@ xerrorstart(Display *dpy, XErrorEvent *ee) { return -1; } +void xkbeventnotify(XEvent *e) +{ + XkbEvent *ev; + + ev = (XkbEvent *) e; + switch (ev->any.xkb_type) { + case XkbStateNotify: + xkbGlobal.group = ev->state.locked_group; + if (selmon != NULL && selmon->sel != NULL) { + selmon->sel->xkb->group = xkbGlobal.group; + } + if (showxkb) { + drawbars(); + } + break; + } +} void zoom(const Arg *arg) { Client *c = selmon->sel;