Hello,
here's part 2 in my attempts to porting larswm features I like to dwm.
Attached are two (independent) patches against dwm-4.0 sources, adding
convenience functions:
- the "place" patch adds a `place()` function that can be used to
instantly move a floating window to a "cardinal point" of the
window area.
There's a bug with this: the moved window will at times loose
focus. I could not identify the culprit, as the bug seems to
manifest itself at random... Any clue will be appreciated :-)
- the "switchto" patch adds `focusnamed()`/`zoomnamed()` functions
that will focus (resp. zoom) a client window selected with a
"class:instance:title" regex (like the one used in the tags
rules). Repeatedly calling the same function will cycle through
all matching windows. If no window matches the given regex, a
command is spawned to create it.
For instance, the key assignment::
{ MODKEY|ControlMask|ShiftMask, XK_Return, zoomnamed,
"XTerm:xterm:.*:xterm" },
will zoom the XTerm window which comes last in the window list, or
spawn a new `xterm` if no XTerm window is currently open.
(I understand that one can already do this with tag+spawn, but I
find the one-key solution more comfortable.)
Ciao,
Riccardo
=== added file 'place.c'
--- place.c 1970-01-01 00:00:00 +0000
+++ place.c 2007-05-08 20:49:13 +0000
@@ -0,0 +1,54 @@
+/* © 2006-2007 Riccardo Murri <[EMAIL PROTECTED]>
+ * See LICENSE file for license details. */
+#include "dwm.h"
+#include <string.h>
+
+
+typedef enum {Top=0, Middle=1, Bottom=2} vertical_placement_t;
+typedef enum {Left=0, Center=1, Right=2} horizontal_placement_t;
+
+static void
+_place(const vertical_placement_t vpos, const horizontal_placement_t hpos)
+{
+ unsigned int nx, ny;
+
+ if(NULL == sel || !sel->isfloating)
+ return;
+
+ /* FIXME: should we take window gravity into account here? */
+ nx = wax + hpos*(waw - sel->w)/2;
+ ny = way + vpos*(wah - sel->h)/2;
+
+ XMoveWindow(dpy, sel->win, nx, ny);
+}
+
+
+/** True if strings are case-insensitive equal. */
+#define streq(a,b) (0 == strcasecmp(a,b))
+
+void
+place(const char* arg)
+{
+ if(NULL == arg)
+ /* by default, center window in window area */
+ _place(Middle, Center);
+ else if(streq(arg, "n"))
+ _place(Top, Center);
+ else if(streq(arg, "ne"))
+ _place(Top, Right);
+ else if(streq(arg, "e"))
+ _place(Middle, Right);
+ else if(streq(arg, "se"))
+ _place(Bottom, Right);
+ else if(streq(arg, "s"))
+ _place(Bottom, Center);
+ else if(streq(arg, "sw"))
+ _place(Bottom, Left);
+ else if(streq(arg, "w"))
+ _place(Middle, Left);
+ else if(streq(arg, "nw"))
+ _place(Top, Left);
+ else
+ /* by default, center window in window area */
+ _place(Middle, Center);
+}
=== modified file 'Makefile'
--- Makefile 2007-05-08 20:42:49 +0000
+++ Makefile 2007-05-08 20:49:13 +0000
@@ -3,7 +3,7 @@
include config.mk
-SRC = client.c draw.c event.c layout.c main.c tag.c util.c
+SRC = client.c draw.c event.c layout.c main.c place.c tag.c util.c
OBJ = ${SRC:.c=.o}
all: options dwm
=== modified file 'config.default.h'
--- config.default.h 2007-05-08 20:42:49 +0000
+++ config.default.h 2007-05-08 20:49:13 +0000
@@ -55,6 +55,15 @@
{ MODKEY, XK_Return, zoom, NULL }, \
{ MODKEY|ShiftMask, XK_space, togglefloating, NULL }, \
{ MODKEY|ShiftMask, XK_c, killclient, NULL }, \
+ { MODKEY|ControlMask, XK_KP_1, place, "sw" }, \
+ { MODKEY|ControlMask, XK_KP_2, place, "s" }, \
+ { MODKEY|ControlMask, XK_KP_3, place, "se" }, \
+ { MODKEY|ControlMask, XK_KP_4, place, "w" }, \
+ { MODKEY|ControlMask, XK_KP_5, place, NULL }, \
+ { MODKEY|ControlMask, XK_KP_6, place, "e" }, \
+ { MODKEY|ControlMask, XK_KP_7, place, "nw" }, \
+ { MODKEY|ControlMask, XK_KP_8, place, "n" }, \
+ { MODKEY|ControlMask, XK_KP_9, place, "ne" }, \
{ MODKEY, XK_0, view, NULL }, \
{ MODKEY, XK_1, view, "0" }, \
{ MODKEY, XK_2, view, "1" }, \
=== modified file 'dwm.h'
--- dwm.h 2007-05-08 20:42:49 +0000
+++ dwm.h 2007-05-08 20:49:13 +0000
@@ -133,6 +133,9 @@
void quit(const char *arg); /* quit dwm nicely */
int xerror(Display *dsply, XErrorEvent *ee); /* dwm's X error handler */
+/* place.c */
+void place(const char* arg); /* place sel window at cardinal point of screen */
+
/* tag.c */
void compileregs(void); /* initialize regexps of rules defined in config.h */
Bool isvisible(Client *c); /* returns True if client is visible */
=== added file 'switchto.c'
--- switchto.c 1970-01-01 00:00:00 +0000
+++ switchto.c 2007-05-08 21:11:04 +0000
@@ -0,0 +1,106 @@
+/* Author: Riccardo Murri <[EMAIL PROTECTED]>
+ * See LICENSE file for license details. */
+#include "dwm.h"
+#include <regex.h>
+#include <stdio.h>
+#include <string.h>
+#include <X11/Xutil.h>
+
+
+static Bool
+matches(Client *c, regex_t *re)
+{
+ XClassHint ch = { 0 };
+ unsigned int len;
+ char *s;
+ Bool result;
+
+ XGetClassHint(dpy, c->win, &ch);
+ len = (4 + (ch.res_class? strlen(ch.res_class) : 0)
+ + (ch.res_name? strlen(ch.res_name) : 0)
+ + strlen(c->name));
+ s = emallocz(len);
+ snprintf(s, len, "%s:%s:%s",
+ ch.res_class ? ch.res_class : "",
+ ch.res_name ? ch.res_name : "",
+ c->name);
+ result = (REG_NOMATCH != regexec(re, s, 1, NULL, 0));
+ free(s);
+ return result;
+}
+
+
+/** Focus client matching given rule, and optionally raise or zoom it.
+
+Scan list of clients (but the currently-focused one) and focus the
+first one that matches the given rule. If no client, matches the
+rule, spawn a new one.
+
[EMAIL PROTECTED] rule A string in the form "Class:instance:title-regex:command",
+ just like
[EMAIL PROTECTED] do_raise If True, map and raise client window.
[EMAIL PROTECTED] do_zoom If True, call dwm "zoom" after focusing client.
+*/
+static void
+_switchto (const char *arg, Bool do_raise, Bool do_zoom)
+{
+ char *cmd, *rule;
+ unsigned int colons;
+ Client *match = NULL;
+ regex_t re;
+
+ /* advance until right after third occurrence of ':' in `rule` */
+ for (cmd = rule, colons=0; colons<3 && *cmd!='\0'; cmd++)
+ if (':' == *cmd)
+ colons++;
+
+ /* copy rule part */
+ rule = emallocz(cmd - arg);
+ strncpy(rule, arg, cmd-arg-1);
+
+ /* compile rule to regex */
+ if(regcomp(&re, rule, REG_EXTENDED|REG_NOSUB))
+ return;
+
+ if(clients) {
+ Client *c, *last;
+ /* find last client in list */
+ for(c = clients; c; c = c->next)
+ last = c;
+
+ /* find matching window searching from end of list backwards,
+ or we'll just switch between the first two matching clients */
+ for(c = last; c && !match; c = c->prev) {
+ if (sel != c && matches(c, &re))
+ match = c;
+ }
+ }
+ if(NULL == match && sel && matches(sel, &re))
+ return; /* currently-selected window is the only match */
+
+ if(match) {
+ focus(match);
+ if(do_raise)
+ XMapRaised(dpy, match->win);
+ else
+ XMapWindow(dpy, match->win);
+ if (do_zoom)
+ zoom(NULL);
+ }
+ else {
+ spawn (cmd);
+ /* FIXME: should take extra steps to focus/zoom this one? */
+ }
+}
+
+void
+focusnamed(const char* rule)
+{
+ _switchto(rule, True, False);
+}
+
+void
+zoomnamed(const char* rule)
+{
+ _switchto(rule, True, True);
+}
=== modified file 'Makefile'
--- Makefile 2007-05-04 21:02:42 +0000
+++ Makefile 2007-05-08 21:11:27 +0000
@@ -3,7 +3,7 @@
include config.mk
-SRC = client.c draw.c event.c layout.c main.c tag.c util.c
+SRC = client.c draw.c event.c layout.c main.c switchto.c tag.c util.c
OBJ = ${SRC:.c=.o}
all: options dwm
=== modified file 'config.default.h'
--- config.default.h 2007-05-04 21:02:42 +0000
+++ config.default.h 2007-05-08 21:13:26 +0000
@@ -44,6 +44,7 @@
/* modifier key function argument */ \
{ MODKEY|ShiftMask, XK_Return, spawn, "exec xterm" }, \
{ MODKEY, XK_p, spawn, "exe=`dmenu_path | dmenu` && exec $exe" }, \
+ { MODKEY|ControlMask|ShiftMask, XK_Return, zoomnamed, "URxvt:xterm:.*:urxvtc" }, \
{ MODKEY, XK_space, setlayout, NULL }, \
{ MODKEY, XK_h, incmasterw, "-32" }, \
{ MODKEY, XK_l, incmasterw, "32" }, \
=== modified file 'dwm.h'
--- dwm.h 2007-05-04 21:02:42 +0000
+++ dwm.h 2007-05-08 21:10:31 +0000
@@ -133,6 +133,13 @@
void quit(const char *arg); /* quit dwm nicely */
int xerror(Display *dsply, XErrorEvent *ee); /* dwm's X error handler */
+/* switchto.c */
+
+void focusnamed(const char* arg); /* focus+raise window identified by class:instance:title regex,
+ or spawn a new instance if none is found. */
+void zoomnamed(const char* arg); /* zoom window identified by class:instance:title regex,
+ or spawn a new instance if none is found. */
+
/* tag.c */
void compileregs(void); /* initialize regexps of rules defined in config.h */
Bool isvisible(Client *c); /* returns True if client is visible */