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 */

Reply via email to