diff --git a/config.def.h b/config.def.h index 1ac028c..da77ebf 100644 --- a/config.def.h +++ b/config.def.h @@ -483,6 +483,9 @@ static const BarRule barrules[] = { #elif BAR_STATUS_PATCH { 'A', 0, BAR_ALIGN_RIGHT, width_status, draw_status, click_status, "status" }, #endif // BAR_STATUS2D_PATCH | BAR_STATUSCMD_PATCH + #if XKB_PATCH + { 0, 0, BAR_ALIGN_RIGHT, width_xkb, draw_xkb, click_xkb, "xkb" }, + #endif // XKB_PATCH #if BAR_FLEXWINTITLE_PATCH { -1, 0, BAR_ALIGN_NONE, width_flexwintitle, draw_flexwintitle, click_flexwintitle, "flexwintitle" }, #elif BAR_TABGROUPS_PATCH @@ -660,6 +663,14 @@ static const Layout layouts[] = { }; #endif // FLEXTILE_DELUXE_LAYOUT +#if XKB_PATCH +/* xkb frontend */ +static const char *xkb_layouts[] = { + "en", + "ru", +}; +#endif // XKB_PATCH + /* key definitions */ #define MODKEY Mod1Mask #if COMBO_PATCH && SWAPTAGS_PATCH && TAGOTHERMONITOR_PATCH diff --git a/dwm.c b/dwm.c index 31061f6..4084ed6 100644 --- a/dwm.c +++ b/dwm.c @@ -55,6 +55,10 @@ #include #endif // BAR_PANGO_PATCH +#if XKB_PATCH +#include +#endif // XKB_PATCH + #if SPAWNCMD_PATCH #include #include @@ -217,6 +221,9 @@ enum { ClkWinTitle, ClkClientWin, ClkRootWin, + #if XKB_PATCH + ClkXKB, + #endif // XKB_PATCH ClkLast }; /* clicks */ @@ -303,6 +310,16 @@ typedef struct { const Arg arg; } Button; +#if XKB_PATCH +typedef struct XkbInfo XkbInfo; +struct XkbInfo { + XkbInfo *next; + XkbInfo *prev; + int group; + Window w; +}; +#endif // XKB_PATCH + typedef struct Client Client; struct Client { char name[256]; @@ -367,6 +384,9 @@ struct Client { #if IPC_PATCH ClientState prevstate; #endif // IPC_PATCH + #if XKB_PATCH + XkbInfo *xkb; + #endif // XKB_PATCH }; typedef struct { @@ -496,9 +516,16 @@ typedef struct { const char *floatpos; #endif // FLOATPOS_PATCH int monitor; + #if XKB_PATCH + int xkb_layout; + #endif // XKB_PATCH } Rule; +#if XKB_PATCH +#define RULE(...) { .monitor = -1, .xkb_layout = -1, ##__VA_ARGS__ }, +#else #define RULE(...) { .monitor = -1, ##__VA_ARGS__ }, +#endif // XKB_PATCH /* Cross patch compatibility rule macro helper macros */ #define FLOATING , .isfloating = 1 @@ -688,6 +715,9 @@ static char rawestext[1024]; #endif // BAR_STATUS2D_PATCH | BAR_STATUSCMD_PATCH #endif // BAR_EXTRASTATUS_PATCH +#if XKB_PATCH +static int xkbEventType = 0; +#endif // XKB_PATCH static int screen; static int sw, sh; /* X display screen geometry width, height */ static int bh; /* bar geometry */ @@ -877,6 +907,10 @@ applyrules(Client *c) } } #endif // SWITCHTAG_PATCH + #if XKB_PATCH + if (r->xkb_layout > -1) + c->xkb->group = r->xkb_layout; + #endif // XKB_PATCH #if ONLY_ONE_RULE_MATCH_PATCH break; #endif // ONLY_ONE_RULE_MATCH_PATCH @@ -2246,6 +2280,13 @@ manage(Window w, XWindowAttributes *wa) c->cfact = 1.0; #endif // CFACTS_PATCH updatetitle(c); + + #if XKB_PATCH + /* Setting current xkb state must be before applyrules */ + if (!(c->xkb = findxkb(c->win))) + c->xkb = createxkb(c->win); + #endif // XKB_PATCH + if (XGetTransientForHint(dpy, w, &trans) && (t = wintoclient(trans))) { c->mon = t->mon; c->tags = t->tags; @@ -3018,9 +3059,20 @@ run(void) XEvent ev; /* main event loop */ XSync(dpy, False); - while (running && !XNextEvent(dpy, &ev)) + while (running && !XNextEvent(dpy, &ev)) { + + #if XKB_PATCH + /* Unfortunately the xkbEventType is not constant hence it can't be part of the + * normal event handler below */ + if (ev.type == xkbEventType) { + xkbeventnotify(&ev); + continue; + } + #endif // XKB_PATCH + if (handler[ev.type]) handler[ev.type](&ev); /* call handler */ + } } #endif // IPC_PATCH @@ -3198,6 +3250,9 @@ setfocus(Client *c) XChangeProperty(dpy, root, netatom[NetActiveWindow], XA_WINDOW, 32, PropModeReplace, (unsigned char *) &(c->win), 1); + #if XKB_PATCH + XkbLockGroup(dpy, XkbUseCoreKbd, c->xkb->group); + #endif // XKB_PATCH } #if BAR_SYSTRAY_PATCH sendevent(c->win, wmatom[WMTakeFocus], NoEventMask, wmatom[WMTakeFocus], CurrentTime, 0, 0, 0); @@ -3381,6 +3436,9 @@ setup(void) { int i; XSetWindowAttributes wa; + #if XKB_PATCH + XkbStateRec xkbstate; + #endif // XKB_PATCH Atom utf8string; /* clean up any zombies immediately */ @@ -3553,6 +3611,17 @@ setup(void) |LeaveWindowMask|StructureNotifyMask|PropertyChangeMask; XChangeWindowAttributes(dpy, root, CWEventMask|CWCursor, &wa); XSelectInput(dpy, root, wa.event_mask); + + #if XKB_PATCH + /* 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; + #endif // XKB_PATCH + grabkeys(); focus(NULL); #if IPC_PATCH @@ -4034,6 +4103,9 @@ unmanage(Client *c, int destroyed) unsigned int switchtag = c->switchtag; #endif // SWITCHTAG_PATCH XWindowChanges wc; + #if XKB_PATCH + XkbInfo *xkb; + #endif // XKB_PATCH #if SWALLOW_PATCH if (c->swallowing) { @@ -4064,6 +4136,19 @@ unmanage(Client *c, int destroyed) XSetErrorHandler(xerror); XUngrabServer(dpy); } + #if XKB_PATCH + 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); + } + } + #endif // XKB_PATCH + free(c); #if SWALLOW_PATCH if (s) diff --git a/patch/include.c b/patch/include.c index 9e93ded..e4f121d 100644 --- a/patch/include.c +++ b/patch/include.c @@ -298,6 +298,9 @@ #if ZOOMSWAP_PATCH #include "zoomswap.c" #endif +#if XKB_PATCH +#include "xkb.c" +#endif #if XRDB_PATCH && !BAR_VTCOLORS_PATCH #include "xrdb.c" #endif diff --git a/patch/include.h b/patch/include.h index 5cf6e70..9df14fc 100644 --- a/patch/include.h +++ b/patch/include.h @@ -294,6 +294,9 @@ #if ZOOMSWAP_PATCH #include "zoomswap.h" #endif +#if XKB_PATCH +#include "xkb.h" +#endif #if XRDB_PATCH && !BAR_VTCOLORS_PATCH #include "xrdb.h" #endif diff --git a/patch/ipc.c b/patch/ipc.c index e863afc..ef0e577 100644 --- a/patch/ipc.c +++ b/patch/ipc.c @@ -9,6 +9,15 @@ handlexevent(struct epoll_event *ev) XEvent ev; while (running && XPending(dpy)) { XNextEvent(dpy, &ev); + #if XKB_PATCH + /* Unfortunately the xkbEventType is not constant hence it can't be part of the + * normal event handler below */ + if (ev.type == xkbEventType) { + xkbeventnotify(&ev); + continue; + } + #endif // XKB_PATCH + if (handler[ev.type]) { handler[ev.type](&ev); /* call handler */ ipc_send_events(mons, &lastselmon, selmon); diff --git a/patch/xkb.c b/patch/xkb.c new file mode 100644 index 0000000..0ce2cec --- /dev/null +++ b/patch/xkb.c @@ -0,0 +1,67 @@ +static XkbInfo xkbGlobal; +static XkbInfo *xkbSaved = NULL; + +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; +} + +XkbInfo * +findxkb(Window w) +{ + XkbInfo *xkb; + for (xkb = xkbSaved; xkb != NULL; xkb = xkb->next) + if (xkb->w == w) + return xkb; + return NULL; +} + +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; + drawbars(); + break; + } +} + +/* xkb bar module */ +int +width_xkb(Bar *bar, BarArg *a) +{ + return TEXTW(xkb_layouts[xkbGlobal.group]); +} + +int +draw_xkb(Bar *bar, BarArg *a) +{ + drw_text(drw, a->x, a->y, a->w, a->h, lrpad / 2, xkb_layouts[xkbGlobal.group], 0, False); + return 1; +} + +int +click_xkb(Bar *bar, Arg *arg, BarArg *a) +{ + return ClkXKB; +} diff --git a/patch/xkb.h b/patch/xkb.h new file mode 100644 index 0000000..abe7c2c --- /dev/null +++ b/patch/xkb.h @@ -0,0 +1,7 @@ +static XkbInfo *createxkb(Window w); +static XkbInfo *findxkb(Window w); +static void xkbeventnotify(XEvent *e); + +static int width_xkb(Bar *bar, BarArg *a); +static int draw_xkb(Bar *bar, BarArg *a); +static int click_xkb(Bar *bar, Arg *arg, BarArg *a); \ No newline at end of file diff --git a/patches.def.h b/patches.def.h index 472d535..441d179 100644 --- a/patches.def.h +++ b/patches.def.h @@ -1105,6 +1105,13 @@ */ #define WINVIEW_PATCH 0 +/* Remember keyboard layout per client. + * It is recommended that you configure xkb before using this patch as described in + * https://www.x.org/archive/X11R7.5/doc/input/XKB-Config.html + * https://dwm.suckless.org/patches/xkb/ + */ +#define XKB_PATCH 0 + /* Allows dwm to read colors from xrdb (.Xresources) during runtime. Compatible with * the float border color, awesomebar, urgentborder and titlecolor patches. * https://dwm.suckless.org/patches/xrdb/