Hi,
I had problems with pushing my patches to the site. Maybe I need to push
them to master?
Anyway, here some updates of my patches:
1. handling mouse input in bar with taggrid correctly;
2. new Rule field for default language of application.
Unfortunately, Rule functionality doesn't working with dmenu, but it
could be very useful for it. Does someone has an idea how to handle such
problems?
On 02/14/2014 09:07 PM, Yury Shvedov wrote:
Hi,
I'm newbie in dwm, so I felt the luck of desktops grid like in other
Window Managers like XFCE or OpenBox. And I couldn't find any solution
for multiple rows of tags, so I decided to make it by myself.
In addition I got assumed to xxkb's functionality, but it have some
problems with dwm, described here:
http://lists.suckless.org/dwm/0808/6471.html
So I decided to implement this functionality directly in dwm. While
implementation I had some problems with applications like guake which
have hide-restore behaviour. Unfortunately, dwm removes information
about any unmapping client so I had to store list of xkb info separately.
I lived with this two patches for a week and they seems to work nice,
but I think they need to be checked.
P.S. xkb patch have small conflict with systemtray patch which easy to
solve manually.
P.P.S Sorry for my English.
--
Kind regards
Yury Shvedov
diff --git a/config.def.h b/config.def.h
index 875885b..d6670dc 100644
--- a/config.def.h
+++ b/config.def.h
@@ -16,6 +16,22 @@ static const Bool topbar = True; /* False means bottom bar */
/* tagging */
static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
+/* grid of tags */
+#define DRAWCLASSICTAGS 1 << 0
+#define DRAWTAGGRID 1 << 1
+
+#define SWITCHTAG_UP 1 << 0
+#define SWITCHTAG_DOWN 1 << 1
+#define SWITCHTAG_LEFT 1 << 2
+#define SWITCHTAG_RIGHT 1 << 3
+#define SWITCHTAG_TOGGLETAG 1 << 4
+#define SWITCHTAG_TAG 1 << 5
+#define SWITCHTAG_VIEW 1 << 6
+#define SWITCHTAG_TOGGLEVIEW 1 << 7
+
+static const unsigned int drawtagmask = DRAWTAGGRID; /* | DRAWCLASSICTAGS to show classic row of tags */
+static const int tagrows = 3;
+
static const Rule rules[] = {
/* xprop(1):
* WM_CLASS(STRING) = instance, class
@@ -89,6 +105,16 @@ static Key keys[] = {
TAGKEYS( XK_8, 7)
TAGKEYS( XK_9, 8)
{ MODKEY|ShiftMask, XK_q, quit, {0} },
+
+ { MODKEY|ControlMask, XK_Up, switchtag, { .ui = SWITCHTAG_UP | SWITCHTAG_VIEW } },
+ { MODKEY|ControlMask, XK_Down, switchtag, { .ui = SWITCHTAG_DOWN | SWITCHTAG_VIEW } },
+ { MODKEY|ControlMask, XK_Right, switchtag, { .ui = SWITCHTAG_RIGHT | SWITCHTAG_VIEW } },
+ { MODKEY|ControlMask, XK_Left, switchtag, { .ui = SWITCHTAG_LEFT | SWITCHTAG_VIEW } },
+
+ { MODKEY|Mod4Mask, XK_Up, switchtag, { .ui = SWITCHTAG_UP | SWITCHTAG_TAG | SWITCHTAG_VIEW } },
+ { MODKEY|Mod4Mask, XK_Down, switchtag, { .ui = SWITCHTAG_DOWN | SWITCHTAG_TAG | SWITCHTAG_VIEW } },
+ { MODKEY|Mod4Mask, XK_Right, switchtag, { .ui = SWITCHTAG_RIGHT | SWITCHTAG_TAG | SWITCHTAG_VIEW } },
+ { MODKEY|Mod4Mask, XK_Left, switchtag, { .ui = SWITCHTAG_LEFT | SWITCHTAG_TAG | SWITCHTAG_VIEW } },
};
/* button definitions */
diff --git a/dwm.c b/dwm.c
index 1bbb4b3..d1b8799 100644
--- a/dwm.c
+++ b/dwm.c
@@ -163,6 +163,7 @@ static void detachstack(Client *c);
static Monitor *dirtomon(int dir);
static void drawbar(Monitor *m);
static void drawbars(void);
+static void drawtaggrid(Monitor *m, int *x_pos, unsigned int occ);
static void enternotify(XEvent *e);
static void expose(XEvent *e);
static void focus(Client *c);
@@ -205,6 +206,7 @@ static void setup(void);
static void showhide(Client *c);
static void sigchld(int unused);
static void spawn(const Arg *arg);
+static void switchtag(const Arg *arg);
static void tag(const Arg *arg);
static void tagmon(const Arg *arg);
static void tile(Monitor *);
@@ -409,11 +411,13 @@ attachstack(Client *c) {
void
buttonpress(XEvent *e) {
unsigned int i, x, click;
+ unsigned int columns;
Arg arg = {0};
Client *c;
Monitor *m;
XButtonPressedEvent *ev = &e->xbutton;
+ columns = LENGTH(tags) / tagrows + ((LENGTH(tags) % tagrows > 0) ? 1 : 0);
click = ClkRootWin;
/* focus monitor if necessary */
if((m = wintomon(ev->window)) && m != selmon) {
@@ -423,14 +427,24 @@ buttonpress(XEvent *e) {
}
if(ev->window == selmon->barwin) {
i = x = 0;
+ if (drawtagmask & DRAWCLASSICTAGS)
do
x += TEXTW(tags[i]);
while(ev->x >= x && ++i < LENGTH(tags));
- if(i < LENGTH(tags)) {
+ if(i < LENGTH(tags) && (drawtagmask & DRAWCLASSICTAGS)) {
click = ClkTagBar;
arg.ui = 1 << i;
}
- else if(ev->x < x + blw)
+ else if(ev->x < x + columns * bh / tagrows && (drawtagmask & DRAWTAGGRID)) {
+ click = ClkTagBar;
+ i = (ev->x - x) / (bh / tagrows);
+ i = i + columns * (ev->y / (bh / tagrows));
+ if (i >= LENGTH(tags)) {
+ i = LENGTH(tags) - 1;
+ }
+ arg.ui = 1 << i;
+ }
+ else if(ev->x < x + blw + columns * bh / tagrows)
click = ClkLtSymbol;
else if(ev->x > selmon->ww - TEXTW(stext))
click = ClkStatusText;
@@ -702,6 +716,7 @@ drawbar(Monitor *m) {
urg |= c->tags;
}
x = 0;
+ if (drawtagmask & DRAWCLASSICTAGS)
for(i = 0; i < LENGTH(tags); i++) {
w = TEXTW(tags[i]);
drw_setscheme(drw, m->tagset[m->seltags] & 1 << i ? &scheme[SchemeSel] : &scheme[SchemeNorm]);
@@ -710,6 +725,9 @@ drawbar(Monitor *m) {
occ & 1 << i, urg & 1 << i);
x += w;
}
+ if (drawtagmask & DRAWTAGGRID) {
+ drawtaggrid(m,&x,occ);
+ }
w = blw = TEXTW(m->ltsymbol);
drw_setscheme(drw, &scheme[SchemeNorm]);
drw_text(drw, x, 0, w, bh, m->ltsymbol, 0);
@@ -748,6 +766,47 @@ drawbars(void) {
for(m = mons; m; m = m->next)
drawbar(m);
}
+void drawtaggrid(Monitor *m, int *x_pos, unsigned int occ)
+{
+ unsigned int x, y, h, max_x, columns;
+ int invert, i,j, k;
+
+ h = bh / tagrows;
+ x = max_x = *x_pos;
+ y = 0;
+ columns = LENGTH(tags) / tagrows + ((LENGTH(tags) % tagrows > 0) ? 1 : 0);
+
+ /* Firstly we will fill the borders of squares */
+ XSetForeground(drw->dpy, drw->gc, scheme[SchemeNorm].border->rgb);
+ XFillRectangle(dpy, drw->drawable, drw->gc, x, y, h*columns + 1, bh);
+
+ /* We will draw LENGTH(tags) squares in tagraws raws. */
+ for(j = 0, i= 0; j < tagrows; j++) {
+ x = *x_pos;
+ for (k = 0; k < columns && i < LENGTH(tags); k++, i++) {
+ invert = m->tagset[m->seltags] & 1 << i ? 0 : 1;
+
+ /* Select active color for current square */
+ XSetForeground(drw->dpy, drw->gc, !invert ? scheme[SchemeSel].bg->rgb :
+ scheme[SchemeNorm].fg->rgb);
+ XFillRectangle(dpy, drw->drawable, drw->gc, x+1, y+1, h-1, h-1);
+
+ /* Mark square if tag has client */
+ if (occ & 1 << i) {
+ XSetForeground(drw->dpy, drw->gc, !invert ? scheme[SchemeSel].fg->rgb :
+ scheme[SchemeNorm].bg->rgb);
+ XFillRectangle(dpy, drw->drawable, drw->gc, x + 1, y + 1,
+ h / 2, h / 2);
+ }
+ x += h;
+ if (x > max_x) {
+ max_x = x;
+ }
+ }
+ y += h;
+ }
+ *x_pos = max_x + 1;
+}
void
enternotify(XEvent *e) {
@@ -1582,6 +1641,81 @@ spawn(const Arg *arg) {
exit(EXIT_SUCCESS);
}
}
+void switchtag(const Arg *arg)
+{
+ unsigned int columns;
+ unsigned int new_tagset = 0;
+ unsigned int pos, i;
+ int col, row;
+ Arg new_arg;
+
+ columns = LENGTH(tags) / tagrows + ((LENGTH(tags) % tagrows > 0) ? 1 : 0);
+
+ for (i = 0; i < LENGTH(tags); ++i) {
+ if (!(selmon->tagset[selmon->seltags] & 1 << i)) {
+ continue;
+ }
+ pos = i;
+ row = pos / columns;
+ col = pos % columns;
+ if (arg->ui & SWITCHTAG_UP) { /* UP */
+ row --;
+ if (row < 0) {
+ row = tagrows - 1;
+ }
+ do {
+ pos = row * columns + col;
+ row --;
+ } while (pos >= LENGTH(tags));
+ }
+ if (arg->ui & SWITCHTAG_DOWN) { /* DOWN */
+ row ++;
+ if (row >= tagrows) {
+ row = 0;
+ }
+ pos = row * columns + col;
+ if (pos >= LENGTH(tags)) {
+ row = 0;
+ }
+ pos = row * columns + col;
+ }
+ if (arg->ui & SWITCHTAG_LEFT) { /* LEFT */
+ col --;
+ if (col < 0) {
+ col = columns - 1;
+ }
+ do {
+ pos = row * columns + col;
+ col --;
+ } while (pos >= LENGTH(tags));
+ }
+ if (arg->ui & SWITCHTAG_RIGHT) { /* RIGHT */
+ col ++;
+ if (col >= columns) {
+ col = 0;
+ }
+ pos = row * columns + col;
+ if (pos >= LENGTH(tags)) {
+ col = 0;
+ pos = row * columns + col;
+ }
+ }
+ new_tagset |= 1 << pos;
+ }
+ new_arg.ui = new_tagset;
+ if (arg->ui & SWITCHTAG_TOGGLETAG) {
+ toggletag(&new_arg);
+ }
+ if (arg->ui & SWITCHTAG_TAG) {
+ tag(&new_arg);
+ }
+ if (arg->ui & SWITCHTAG_VIEW) {
+ view (&new_arg);
+ }
+ if (arg->ui & SWITCHTAG_TOGGLEVIEW) {
+ toggleview (&new_arg);
+ }
+}
void
tag(const Arg *arg) {
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 <X11/Xlib.h>
#include <X11/Xproto.h>
#include <X11/Xutil.h>
+#include <X11/XKBlib.h>
#ifdef XINERAMA
#include <X11/extensions/Xinerama.h>
#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;