X-Git-Url: https://jasonwoof.com/gitweb/?a=blobdiff_plain;f=scrotwm.c;h=658696b1a93cae32099d889c8dfb4cd6ba57311f;hb=1bee50dce7f24e8746d0ff0113a1061aa3068ffc;hp=965340353b54a52d3414003e1279b1cd2be9cfd0;hpb=3403d0e9a98d3f739df54c6c4a74d0a578a581ed;p=spectrwm.git diff --git a/scrotwm.c b/scrotwm.c index 9653403..658696b 100644 --- a/scrotwm.c +++ b/scrotwm.c @@ -1,10 +1,11 @@ -/* $scrotwm$ */ /* - * Copyright (c) 2009-2010-2011 Marco Peereboom - * Copyright (c) 2009-2010-2011 Ryan McBride + * Copyright (c) 2009-2012 Marco Peereboom + * Copyright (c) 2009-2011 Ryan McBride * Copyright (c) 2009 Darrin Chandler * Copyright (c) 2009 Pierre-Yves Ritschard + * Copyright (c) 2010 Tuukka Kataja * Copyright (c) 2011 Jason L. Wright + * Copyright (c) 2011-2012 Reginald Kennedy * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -51,10 +52,6 @@ * DEALINGS IN THE SOFTWARE. */ -static const char *cvstag = "$scrotwm$"; - -#define SWM_VERSION "0.9.30" - #include #include #include @@ -85,11 +82,20 @@ static const char *cvstag = "$scrotwm$"; #include #include #include +#include #ifdef __OSX__ #include #endif +#include "version.h" + +#ifdef SCROTWM_BUILDSTR +static const char *buildstr = SCROTWM_BUILDSTR; +#else +static const char *buildstr = SCROTWM_VERSION; +#endif + #if RANDR_MAJOR < 1 # error XRandR versions less than 1.0 are not supported #endif @@ -100,7 +106,7 @@ static const char *cvstag = "$scrotwm$"; #endif #endif -/* #define SWM_DEBUG */ +/*#define SWM_DEBUG*/ #ifdef SWM_DEBUG #define DPRINTF(x...) do { if (swm_debug) fprintf(stderr, x); } while (0) #define DNPRINTF(n,x...) do { if (swm_debug & n) fprintf(stderr, x); } while (0) @@ -118,6 +124,7 @@ static const char *cvstag = "$scrotwm$"; #define SWM_D_SPAWN 0x0800 #define SWM_D_EVENTQ 0x1000 #define SWM_D_CONF 0x2000 +#define SWM_D_BAR 0x4000 u_int32_t swm_debug = 0 | SWM_D_MISC @@ -134,6 +141,7 @@ u_int32_t swm_debug = 0 | SWM_D_SPAWN | SWM_D_EVENTQ | SWM_D_CONF + | SWM_D_BAR ; #else #define DPRINTF(x...) @@ -153,13 +161,26 @@ u_int32_t swm_debug = 0 #define Y(r) (r)->g.y #define WIDTH(r) (r)->g.w #define HEIGHT(r) (r)->g.h +#define SH_MIN(w) (w)->sh_mask & PMinSize +#define SH_MIN_W(w) (w)->sh.min_width +#define SH_MIN_H(w) (w)->sh.min_height +#define SH_MAX(w) (w)->sh_mask & PMaxSize +#define SH_MAX_W(w) (w)->sh.max_width +#define SH_MAX_H(w) (w)->sh.max_height +#define SH_INC(w) (w)->sh_mask & PResizeInc +#define SH_INC_W(w) (w)->sh.width_inc +#define SH_INC_H(w) (w)->sh.height_inc #define SWM_MAX_FONT_STEPS (3) -#define WINID(w) (w ? w->id : 0) +#define WINID(w) ((w) ? (w)->id : 0) +#define YESNO(x) ((x) ? "yes" : "no") #define SWM_FOCUS_DEFAULT (0) #define SWM_FOCUS_SYNERGY (1) #define SWM_FOCUS_FOLLOW (2) +#define SWM_CONF_DEFAULT (0) +#define SWM_CONF_KEYMAPPING (1) + #ifndef SWM_LIB #define SWM_LIB "/usr/local/lib/libswmhack.so" #endif @@ -170,6 +191,7 @@ Atom aprot; Atom adelete; Atom takefocus; Atom a_wmname; +Atom a_netwmname; Atom a_utf8_string; Atom a_string; Atom a_swm_iconic; @@ -197,14 +219,50 @@ int select_list_pipe[2]; int select_resp_pipe[2]; pid_t searchpid; volatile sig_atomic_t search_resp; +int search_resp_action; + +struct search_window { + TAILQ_ENTRY(search_window) entry; + int idx; + struct ws_win *win; + GC gc; + Window indicator; +}; +TAILQ_HEAD(search_winlist, search_window); + +struct search_winlist search_wl; + +/* search actions */ +enum { + SWM_SEARCH_NONE, + SWM_SEARCH_UNICONIFY, + SWM_SEARCH_NAME_WORKSPACE, + SWM_SEARCH_SEARCH_WORKSPACE, + SWM_SEARCH_SEARCH_WINDOW +}; /* dialog windows */ -double dialog_ratio = .6; +double dialog_ratio = 0.6; /* status bar */ -#define SWM_BAR_MAX (256) +#define SWM_BAR_MAX (256) +#define SWM_BAR_JUSTIFY_LEFT (0) +#define SWM_BAR_JUSTIFY_CENTER (1) +#define SWM_BAR_JUSTIFY_RIGHT (2) +#define SWM_BAR_OFFSET (4) +#define SWM_BAR_FONTS "-*-terminus-medium-*-*-*-*-*-*-*-*-*-*-*," \ + "-*-profont-*-*-*-*-*-*-*-*-*-*-*-*," \ + "-*-times-medium-r-*-*-*-*-*-*-*-*-*-*," \ + "-misc-fixed-medium-r-*-*-*-*-*-*-*-*-*-*" + +#ifdef X_HAVE_UTF8_STRING +#define DRAWSTRING(x...) Xutf8DrawString(x) +#else +#define DRAWSTRING(x...) XmbDrawString(x) +#endif + char *bar_argv[] = { NULL, NULL }; int bar_pipe[2]; -char bar_ext[SWM_BAR_MAX]; +unsigned char bar_ext[SWM_BAR_MAX]; char bar_vertext[SWM_BAR_MAX]; int bar_version = 0; sig_atomic_t bar_alarm = 0; @@ -216,8 +274,10 @@ int bar_extra = 1; int bar_extra_running = 0; int bar_verbose = 1; int bar_height = 0; +int bar_justify = SWM_BAR_JUSTIFY_LEFT; int stack_enabled = 1; int clock_enabled = 1; +int urgent_enabled = 0; char *clock_format = NULL; int title_name_enabled = 0; int title_class_enabled = 0; @@ -225,13 +285,13 @@ int window_name_enabled = 0; int focus_mode = SWM_FOCUS_DEFAULT; int disable_border = 0; int border_width = 1; +int verbose_layout = 0; pid_t bar_pid; -GC bar_gc; -XGCValues bar_gcv; -int bar_fidx = 0; -XFontStruct *bar_fs; -char *bar_fonts[] = { NULL, NULL, NULL, NULL };/* XXX Make fully dynamic */ -char *spawn_term[] = { NULL, NULL }; /* XXX Make fully dynamic */ +XFontSet bar_fs; +XFontSetExtents *bar_fs_extents; +char *bar_fonts; +char *spawn_term[] = { NULL, NULL }; /* XXX fully dynamic */ +struct passwd *pwd; #define SWM_MENU_FN (2) #define SWM_MENU_NB (4) @@ -269,8 +329,8 @@ struct ws_win { struct swm_geometry g; /* current geometry */ struct swm_geometry g_float; /* geometry when floating */ struct swm_geometry rg_float; /* region geom when floating */ - int g_floatvalid; /* flag: geometry in g_float is valid */ - int floatmaxed; /* flag: floater was maxed in max_stack */ + int g_floatvalid; /* g_float geometry validity */ + int floatmaxed; /* whether maxed by max_stack */ int floating; int manual; int iconic; @@ -286,10 +346,21 @@ struct ws_win { struct swm_screen *s; /* always valid, never changes */ XWindowAttributes wa; XSizeHints sh; + long sh_mask; XClassHint ch; + XWMHints *hints; }; TAILQ_HEAD(ws_win_list, ws_win); +/* pid goo */ +struct pid_e { + TAILQ_ENTRY(pid_e) entry; + long pid; + int ws; +}; +TAILQ_HEAD(pid_list, pid_e); +struct pid_list pidlist = TAILQ_HEAD_INITIALIZER(pidlist); + /* layout handlers */ void stack(void); void vertical_config(struct workspace *, int); @@ -297,6 +368,8 @@ void vertical_stack(struct workspace *, struct swm_geometry *); void horizontal_config(struct workspace *, int); void horizontal_stack(struct workspace *, struct swm_geometry *); void max_stack(struct workspace *, struct swm_geometry *); +void plain_stacker(struct workspace *); +void fancy_stacker(struct workspace *); struct ws_win *find_window(Window); @@ -305,24 +378,28 @@ void new_region(struct swm_screen *, int, int, int, int); void unmanage_window(struct ws_win *); long getstate(Window); +int conf_load(char *, int); + struct layout { void (*l_stack)(struct workspace *, struct swm_geometry *); void (*l_config)(struct workspace *, int); u_int32_t flags; #define SWM_L_FOCUSPREV (1<<0) #define SWM_L_MAPONFOCUS (1<<1) - char *name; + void (*l_string)(struct workspace *); } layouts[] = { /* stack, configure */ - { vertical_stack, vertical_config, 0, "[|]" }, - { horizontal_stack, horizontal_config, 0, "[-]" }, + { vertical_stack, vertical_config, 0, plain_stacker }, + { horizontal_stack, horizontal_config, 0, plain_stacker }, { max_stack, NULL, - SWM_L_MAPONFOCUS | SWM_L_FOCUSPREV, "[ ]"}, - { NULL, NULL, 0, NULL }, + SWM_L_MAPONFOCUS | SWM_L_FOCUSPREV, plain_stacker }, + { NULL, NULL, 0, NULL }, }; -/* position of max_stack mode in the layouts array */ -#define SWM_MAX_STACK 2 +/* position of max_stack mode in the layouts array, index into layouts! */ +#define SWM_V_STACK (0) +#define SWM_H_STACK (1) +#define SWM_MAX_STACK (2) #define SWM_H_SLICE (32) #define SWM_V_SLICE (32) @@ -330,6 +407,8 @@ struct layout { /* define work spaces */ struct workspace { int idx; /* workspace index */ + char *name; /* workspace name */ + int always_raise; /* raise windows on focus */ struct layout *cur_layout; /* current layout handlers */ struct ws_win *focus; /* may be NULL */ struct ws_win *focus_prev; /* may be NULL */ @@ -337,15 +416,18 @@ struct workspace { struct swm_region *old_r; /* may be NULL */ struct ws_win_list winlist; /* list of windows in ws */ struct ws_win_list unmanagedlist; /* list of dead windows in ws */ + char stacker[10]; /* display stacker and layout */ /* stacker state */ struct { int horizontal_msize; int horizontal_mwin; int horizontal_stacks; + int horizontal_flip; int vertical_msize; int vertical_mwin; int vertical_stacks; + int vertical_flip; } l_state; }; @@ -366,6 +448,8 @@ struct swm_screen { unsigned long color; char *name; } c[SWM_S_COLOR_MAX]; + + GC bar_gc; }; struct swm_screen *screens; int num_screens; @@ -385,12 +469,15 @@ union arg { #define SWM_ARG_ID_MASTERGROW (21) #define SWM_ARG_ID_MASTERADD (22) #define SWM_ARG_ID_MASTERDEL (23) +#define SWM_ARG_ID_FLIPLAYOUT (24) #define SWM_ARG_ID_STACKRESET (30) #define SWM_ARG_ID_STACKINIT (31) #define SWM_ARG_ID_CYCLEWS_UP (40) #define SWM_ARG_ID_CYCLEWS_DOWN (41) #define SWM_ARG_ID_CYCLESC_UP (42) #define SWM_ARG_ID_CYCLESC_DOWN (43) +#define SWM_ARG_ID_CYCLEWS_UP_ALL (44) +#define SWM_ARG_ID_CYCLEWS_DOWN_ALL (45) #define SWM_ARG_ID_STACKINC (50) #define SWM_ARG_ID_STACKDEC (51) #define SWM_ARG_ID_SS_ALL (60) @@ -399,15 +486,23 @@ union arg { #define SWM_ARG_ID_CENTER (71) #define SWM_ARG_ID_KILLWINDOW (80) #define SWM_ARG_ID_DELETEWINDOW (81) +#define SWM_ARG_ID_WIDTHGROW (90) +#define SWM_ARG_ID_WIDTHSHRINK (91) +#define SWM_ARG_ID_HEIGHTGROW (92) +#define SWM_ARG_ID_HEIGHTSHRINK (93) +#define SWM_ARG_ID_MOVEUP (100) +#define SWM_ARG_ID_MOVEDOWN (101) +#define SWM_ARG_ID_MOVELEFT (102) +#define SWM_ARG_ID_MOVERIGHT (103) char **argv; }; void focus(struct swm_region *, union arg *); -void focus_magic(struct ws_win *, int); -#define SWM_F_GENERIC (0) -#define SWM_F_TRANSIENT (1) +void focus_magic(struct ws_win *); + /* quirks */ struct quirk { + TAILQ_ENTRY(quirk) entry; char *class; char *name; unsigned long quirk; @@ -416,9 +511,10 @@ struct quirk { #define SWM_Q_ANYWHERE (1<<2) /* don't position this window */ #define SWM_Q_XTERM_FONTADJ (1<<3) /* adjust xterm fonts when resizing */ #define SWM_Q_FULLSCREEN (1<<4) /* remove border */ +#define SWM_Q_FOCUSPREV (1<<5) /* focus on caller */ }; -int quirks_size = 0, quirks_length = 0; -struct quirk *quirks = NULL; +TAILQ_HEAD(quirk_list, quirk); +struct quirk_list quirks = TAILQ_HEAD_INITIALIZER(quirks); /* * Supported EWMH hints should be added to @@ -467,22 +563,25 @@ struct ewmh_hint { {"_NET_WM_ACTION_CLOSE", None}, }; -void store_float_geom(struct ws_win *win, struct swm_region *r); -int floating_toggle_win(struct ws_win *win); -void spawn_select(struct swm_region *, union arg *, char *, int *); +void store_float_geom(struct ws_win *, struct swm_region *); +int floating_toggle_win(struct ws_win *); +void spawn_select(struct swm_region *, union arg *, char *, int *); +unsigned char *get_win_name(Window); int -get_property(Window id, Atom atom, long count, Atom type, - unsigned long *n, unsigned char **data) +get_property(Window id, Atom atom, long count, Atom type, unsigned long *nitems, + unsigned long *nbytes, unsigned char **data) { int format, status; - unsigned long tmp, extra; - unsigned long *nitems; + unsigned long *nbytes_ret, *nitems_ret; + unsigned long nbytes_tmp, nitems_tmp; Atom real; - nitems = n != NULL ? n : &tmp; + nbytes_ret = nbytes != NULL ? nbytes : &nbytes_tmp; + nitems_ret = nitems != NULL ? nitems : &nitems_tmp; + status = XGetWindowProperty(display, id, atom, 0L, count, False, type, - &real, &format, nitems, &extra, data); + &real, &format, nitems_ret, nbytes_ret, data); if (status != Success) return False; @@ -555,7 +654,8 @@ setup_ewmh(void) /* Report supported atoms */ XDeleteProperty(display, screens[i].root, sup_list); for (j = 0; j < LENGTH(ewmh); j++) - XChangeProperty(display, screens[i].root, sup_list, XA_ATOM, 32, + XChangeProperty(display, screens[i].root, + sup_list, XA_ATOM, 32, PropModeAppend, (unsigned char *)&ewmh[j].atom,1); } } @@ -575,7 +675,7 @@ teardown_ewmh(void) for (i = 0; i < ScreenCount(display); i++) { /* Get the support check window and destroy it */ success = get_property(screens[i].root, sup_check, 1, XA_WINDOW, - &n, &data); + &n, NULL, &data); if (success) { id = data[0]; @@ -592,12 +692,11 @@ void ewmh_autoquirk(struct ws_win *win) { int success, i; - unsigned long *data = NULL; - unsigned long n; + unsigned long *data = NULL, n; Atom type; success = get_property(win->id, ewmh[_NET_WM_WINDOW_TYPE].atom, (~0L), - XA_ATOM, &n, (unsigned char **)&data); + XA_ATOM, &n, NULL, (void *)&data); if (!success) { XFree(data); @@ -645,25 +744,22 @@ ewmh_set_win_fullscreen(struct ws_win *win, int fs) if (!win->floating) return 0; - DNPRINTF(SWM_D_MISC, "ewmh_set_win_fullscreen: win 0x%lx fs: %d\n", - win->id, fs); + DNPRINTF(SWM_D_MISC, "ewmh_set_win_fullscreen: window: 0x%lx, " + "fullscreen %s\n", win->id, YESNO(fs)); rg = win->ws->r->g; if (fs) { store_float_geom(win, win->ws->r); - win->g.x = rg.x; - win->g.y = rg.y; - win->g.w = rg.w; - win->g.h = rg.h; - } else { + win->g = rg; + } else { if (win->g_floatvalid) { /* refloat at last floating relative position */ - win->g.x = win->g_float.x - win->rg_float.x + rg.x; - win->g.y = win->g_float.y - win->rg_float.y + rg.y; - win->g.w = win->g_float.w; - win->g.h = win->g_float.h; + X(win) = win->g_float.x - win->rg_float.x + rg.x; + Y(win) = win->g_float.y - win->rg_float.y + rg.y; + WIDTH(win) = win->g_float.w; + HEIGHT(win) = win->g_float.h; } } @@ -741,7 +837,8 @@ ewmh_update_win_state(struct ws_win *win, long state, long action) win->manual = (win->ewmh_flags & SWM_F_MANUAL) != 0; if (state == ewmh[_NET_WM_STATE_FULLSCREEN].atom) if (changed) - if (!ewmh_set_win_fullscreen(win, win->ewmh_flags & EWMH_F_FULLSCREEN)) + if (!ewmh_set_win_fullscreen(win, + win->ewmh_flags & EWMH_F_FULLSCREEN)) win->ewmh_flags = orig_flags; /* revert */ XDeleteProperty(display, win->id, ewmh[_NET_WM_STATE].atom); @@ -784,8 +881,8 @@ ewmh_get_win_state(struct ws_win *win) if (win->manual) win->ewmh_flags |= SWM_F_MANUAL; - success = get_property(win->id, ewmh[_NET_WM_STATE].atom, (~0L), XA_ATOM, - &n, (unsigned char **)&states); + success = get_property(win->id, ewmh[_NET_WM_STATE].atom, + (~0L), XA_ATOM, &n, NULL, (void *)&states); if (!success) return; @@ -798,8 +895,8 @@ ewmh_get_win_state(struct ws_win *win) /* events */ #ifdef SWM_DEBUG -void -dumpevent(XEvent *e) +char * +geteventname(XEvent *e) { char *name = NULL; @@ -903,16 +1000,27 @@ dumpevent(XEvent *e) case MappingNotify: name = "MappingNotify"; break; + default: + name = "Unknown"; } - if (name) - DNPRINTF(SWM_D_EVENTQ ,"window: %lu event: %s (%d), %d " - "remaining\n", - e->xany.window, name, e->type, QLength(display)); - else - DNPRINTF(SWM_D_EVENTQ, "window: %lu unknown event %d, %d " - "remaining\n", - e->xany.window, e->type, QLength(display)); + return name; +} + +char * +xrandr_geteventname(XEvent *e) +{ + char *name = NULL; + + switch(e->type - xrandr_eventbase) { + case RRScreenChangeNotify: + name = "RRScreenChangeNotify"; + break; + default: + name = "Unknown"; + } + + return name; } void @@ -923,35 +1031,36 @@ dumpwins(struct swm_region *r, union arg *args) XWindowAttributes wa; if (r->ws == NULL) { - fprintf(stderr, "invalid workspace\n"); + warnx("dumpwins: invalid workspace"); return; } - fprintf(stderr, "=== managed window list ws %02d ===\n", r->ws->idx); + warnx("=== managed window list ws %02d ===", r->ws->idx); TAILQ_FOREACH(win, &r->ws->winlist, entry) { state = getstate(win->id); if (!XGetWindowAttributes(display, win->id, &wa)) - fprintf(stderr, "window: %lu failed " - "XGetWindowAttributes\n", win->id); - fprintf(stderr, "window: %lu map_state: %d state: %d\n", - win->id, wa.map_state, state); + warnx("window: 0x%lx, failed XGetWindowAttributes", + win->id); + warnx("window: 0x%lx, map_state: %d, state: %d, " + "transient: 0x%lx", win->id, wa.map_state, state, + win->transient); } - fprintf(stderr, "===== unmanaged window list =====\n"); + warnx("===== unmanaged window list ====="); TAILQ_FOREACH(win, &r->ws->unmanagedlist, entry) { state = getstate(win->id); if (!XGetWindowAttributes(display, win->id, &wa)) - fprintf(stderr, "window: %lu failed " - "XGetWindowAttributes\n", win->id); - fprintf(stderr, "window: %lu map_state: %d state: %d\n", - win->id, wa.map_state, state); + warnx("window: 0x%lx, failed XGetWindowAttributes", + win->id); + warnx("window: 0x%lx, map_state: %d, state: %d, " + "transient: 0x%lx", win->id, wa.map_state, state, + win->transient); } - fprintf(stderr, "=================================\n"); + warnx("================================="); } #else -#define dumpevent(e) void dumpwins(struct swm_region *r, union arg *args) { @@ -1041,6 +1150,24 @@ sighdlr(int sig) errno = saved_errno; } +struct pid_e * +find_pid(long pid) +{ + struct pid_e *p = NULL; + + DNPRINTF(SWM_D_MISC, "find_pid: %lu\n", pid); + + if (pid == 0) + return (NULL); + + TAILQ_FOREACH(p, &pidlist, entry) { + if (p->pid == pid) + return (p); + } + + return (NULL); +} + unsigned long name_to_color(char *colorname) { @@ -1061,7 +1188,7 @@ name_to_color(char *colorname) if (status) result = screen_def.pixel; else - fprintf(stderr, "color '%s' not found.\n", colorname); + warnx("color '%s' not found", colorname); return (result); } @@ -1073,41 +1200,67 @@ setscreencolor(char *val, int i, int c) screens[i - 1].c[c].color = name_to_color(val); free(screens[i - 1].c[c].name); if ((screens[i - 1].c[c].name = strdup(val)) == NULL) - errx(1, "strdup"); + err(1, "strdup"); } else if (i == -1) { for (i = 0; i < ScreenCount(display); i++) { screens[i].c[c].color = name_to_color(val); free(screens[i].c[c].name); if ((screens[i].c[c].name = strdup(val)) == NULL) - errx(1, "strdup"); + err(1, "strdup"); } } else - errx(1, "invalid screen index: %d out of bounds (maximum %d)\n", + errx(1, "invalid screen index: %d out of bounds (maximum %d)", i, ScreenCount(display)); } void +fancy_stacker(struct workspace *ws) +{ + strlcpy(ws->stacker, "[ ]", sizeof ws->stacker); + if (ws->cur_layout->l_stack == vertical_stack) + snprintf(ws->stacker, sizeof ws->stacker, + ws->l_state.vertical_flip ? "[%d>%d]" : "[%d|%d]", + ws->l_state.vertical_mwin, ws->l_state.vertical_stacks); + if (ws->cur_layout->l_stack == horizontal_stack) + snprintf(ws->stacker, sizeof ws->stacker, + ws->l_state.horizontal_flip ? "[%dv%d]" : "[%d-%d]", + ws->l_state.horizontal_mwin, ws->l_state.horizontal_stacks); +} + +void +plain_stacker(struct workspace *ws) +{ + strlcpy(ws->stacker, "[ ]", sizeof ws->stacker); + if (ws->cur_layout->l_stack == vertical_stack) + strlcpy(ws->stacker, ws->l_state.vertical_flip ? "[>]" : "[|]", + sizeof ws->stacker); + if (ws->cur_layout->l_stack == horizontal_stack) + strlcpy(ws->stacker, ws->l_state.horizontal_flip ? "[v]" : "[-]", + sizeof ws->stacker); +} + +void custom_region(char *val) { unsigned int sidx, x, y, w, h; if (sscanf(val, "screen[%u]:%ux%u+%u+%u", &sidx, &w, &h, &x, &y) != 5) errx(1, "invalid custom region, " - "should be 'screen[]:x++\n"); + "should be 'screen[]:x++"); if (sidx < 1 || sidx > ScreenCount(display)) - errx(1, "invalid screen index: %d out of bounds (maximum %d)\n", + errx(1, "invalid screen index: %d out of bounds (maximum %d)", sidx, ScreenCount(display)); sidx--; if (w < 1 || h < 1) - errx(1, "region %ux%u+%u+%u too small\n", w, h, x, y); + errx(1, "region %ux%u+%u+%u too small", w, h, x, y); - if (x < 0 || x > DisplayWidth(display, sidx) || - y < 0 || y > DisplayHeight(display, sidx) || + if (x > DisplayWidth(display, sidx) || + y > DisplayHeight(display, sidx) || w + x > DisplayWidth(display, sidx) || h + y > DisplayHeight(display, sidx)) { - fprintf(stderr, "ignoring region %ux%u+%u+%u - not within screen boundaries " - "(%ux%u)\n", w, h, x, y, + warnx("ignoring region %ux%u+%u+%u - not within screen " + "boundaries (%ux%u)", w, h, x, y, DisplayWidth(display, sidx), DisplayHeight(display, sidx)); return; } @@ -1130,10 +1283,33 @@ socket_setnonblock(int fd) void bar_print(struct swm_region *r, char *s) { + int x = 0; + size_t len; + XRectangle ibox, lbox; + XClearWindow(display, r->bar_window); - XSetForeground(display, bar_gc, r->s->c[SWM_S_COLOR_BAR_FONT].color); - XDrawString(display, r->bar_window, bar_gc, 4, bar_fs->ascent, s, - strlen(s)); + + len = strlen(s); + XmbTextExtents(bar_fs, s, len, &ibox, &lbox); + + switch (bar_justify) { + case SWM_BAR_JUSTIFY_LEFT: + x = SWM_BAR_OFFSET; + break; + case SWM_BAR_JUSTIFY_CENTER: + x = (WIDTH(r) - lbox.width) / 2; + break; + case SWM_BAR_JUSTIFY_RIGHT: + x = WIDTH(r) - lbox.width - SWM_BAR_OFFSET; + break; + } + + if (x < SWM_BAR_OFFSET) + x = SWM_BAR_OFFSET; + + DRAWSTRING(display, r->bar_window, bar_fs, r->s->bar_gc, + x, (bar_fs_extents->max_logical_extent.height - lbox.height) / 2 - + lbox.y, s, len); } void @@ -1147,7 +1323,7 @@ bar_extra_stop(void) kill(bar_pid, SIGTERM); bar_pid = 0; } - strlcpy(bar_ext, "", sizeof bar_ext); + strlcpy((char *)bar_ext, "", sizeof bar_ext); bar_extra = 0; } @@ -1176,27 +1352,71 @@ bar_class_name(char *s, ssize_t sz, struct ws_win *cur_focus) strlcat(s, " ", sz); } out: - if (xch) + if (xch) { + XFree(xch->res_name); + XFree(xch->res_class); XFree(xch); + } } void bar_window_name(char *s, ssize_t sz, struct ws_win *cur_focus) { - char *title; + unsigned char *title; if (window_name_enabled && cur_focus != NULL) { - XFetchName(display, cur_focus->id, &title); - if (title) { + title = get_win_name(cur_focus->id); + if (title != NULL) { + DNPRINTF(SWM_D_BAR, "bar_window_name: title: %s\n", + title); + if (cur_focus->floating) strlcat(s, "(f) ", sz); - strlcat(s, title, sz); + strlcat(s, (char *)title, sz); strlcat(s, " ", sz); XFree(title); } } } +int urgent[SWM_WS_MAX]; +void +bar_urgent(char *s, ssize_t sz) +{ + XWMHints *wmh = NULL; + struct ws_win *win; + int i, j; + char b[8]; + + if (urgent_enabled == 0) + return; + + for (i = 0; i < SWM_WS_MAX; i++) + urgent[i] = 0; + + for (i = 0; i < ScreenCount(display); i++) + for (j = 0; j < SWM_WS_MAX; j++) + TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry) { + wmh = XGetWMHints(display, win->id); + if (wmh == NULL) + continue; + + if (wmh->flags & XUrgencyHint) + urgent[j] = 1; + XFree(wmh); + } + + strlcat(s, "* ", sz); + for (i = 0; i < SWM_WS_MAX; i++) { + if (urgent[i]) + snprintf(b, sizeof b, "%d ", i + 1); + else + snprintf(b, sizeof b, "- "); + strlcat(s, b, sz); + } + strlcat(s, "* ", sz); +} + void bar_update(void) { @@ -1205,11 +1425,11 @@ bar_update(void) struct swm_region *r; int i, x; size_t len; + char ws[SWM_BAR_MAX]; char s[SWM_BAR_MAX]; - char cn[SWM_BAR_MAX]; + unsigned char cn[SWM_BAR_MAX]; char loc[SWM_BAR_MAX]; - char *b; - char *stack = ""; + char *b, *stack = ""; if (bar_enabled == 0) return; @@ -1218,40 +1438,46 @@ bar_update(void) while ((b = fgetln(stdin, &len)) != NULL) if (b && b[len - 1] == '\n') { b[len - 1] = '\0'; - strlcpy(bar_ext, b, sizeof bar_ext); + strlcpy((char *)bar_ext, b, sizeof bar_ext); } if (b == NULL && errno != EAGAIN) { - fprintf(stderr, "bar_extra failed: errno: %d %s\n", - errno, strerror(errno)); + warn("bar_update: bar_extra failed"); bar_extra_stop(); } } else - strlcpy(bar_ext, "", sizeof bar_ext); + strlcpy((char *)bar_ext, "", sizeof bar_ext); if (clock_enabled == 0) strlcpy(s, "", sizeof s); else { time(&tmt); localtime_r(&tmt, &tm); - strftime(s, sizeof s, clock_format, &tm); + len = strftime(s, sizeof s, clock_format, &tm); + s[len] = '\0'; strlcat(s, " ", sizeof s); } for (i = 0; i < ScreenCount(display); i++) { x = 1; TAILQ_FOREACH(r, &screens[i].rl, entry) { - strlcpy(cn, "", sizeof cn); + strlcpy((char *)cn, "", sizeof cn); + strlcpy(ws, "", sizeof ws); if (r && r->ws) { - bar_class_name(cn, sizeof cn, r->ws->focus); - bar_window_name(cn, sizeof cn, r->ws->focus); + bar_urgent((char *)cn, sizeof cn); + bar_class_name((char *)cn, sizeof cn, + r->ws->focus); + bar_window_name((char *)cn, sizeof cn, + r->ws->focus); + if (r->ws->name) + snprintf(ws, sizeof ws, "<%s>", + r->ws->name); } - if (stack_enabled) - stack = r->ws->cur_layout->name; + stack = r->ws->stacker; - snprintf(loc, sizeof loc, "%d:%d %s %s%s %s %s", - x++, r->ws->idx + 1, stack, s, cn, bar_ext, - bar_vertext); + snprintf(loc, sizeof loc, "%d:%d %s %s %s%s %s " + "%s", x++, r->ws->idx + 1, stack, ws, s, cn, + bar_ext, bar_vertext); bar_print(r, loc); } } @@ -1270,7 +1496,7 @@ bar_toggle(struct swm_region *r, union arg *args) struct swm_region *tmpr; int i, sc = ScreenCount(display); - DNPRINTF(SWM_D_MISC, "bar_toggle\n"); + DNPRINTF(SWM_D_BAR, "bar_toggle\n"); if (bar_enabled) for (i = 0; i < sc; i++) @@ -1304,9 +1530,9 @@ bar_refresh(void) socket_setnonblock(bar_pipe[0]); socket_setnonblock(bar_pipe[1]); /* XXX hmmm, really? */ if (dup2(bar_pipe[0], 0) == -1) - errx(1, "dup2"); + err(1, "dup2"); if (dup2(bar_pipe[1], 1) == -1) - errx(1, "dup2"); + err(1, "dup2"); if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) err(1, "could not disable SIGPIPE"); switch (bar_pid = fork()) { @@ -1340,39 +1566,60 @@ bar_refresh(void) void bar_setup(struct swm_region *r) { + char *default_string; + char **missing_charsets; + int num_missing_charsets = 0; int i, x, y; if (bar_fs) { - XFreeFont(display, bar_fs); + XFreeFontSet(display, bar_fs); bar_fs = NULL; } - for (i = 0; bar_fonts[i] != NULL; i++) { - bar_fs = XLoadQueryFont(display, bar_fonts[i]); - if (bar_fs) { - bar_fidx = i; - break; - } + + DNPRINTF(SWM_D_BAR, "bar_setup: loading bar_fonts: %s\n", bar_fonts); + + bar_fs = XCreateFontSet(display, bar_fonts, &missing_charsets, + &num_missing_charsets, &default_string); + + if (num_missing_charsets > 0) { + warnx("Unable to load charset(s):"); + + for (i = 0; i < num_missing_charsets; ++i) + warnx("%s", missing_charsets[i]); + + XFreeStringList(missing_charsets); + + if (strcmp(default_string, "")) + warnx("Glyphs from those sets will be replaced " + "by '%s'.", default_string); + else + warnx("Glyphs from those sets won't be drawn."); } - if (bar_fonts[i] == NULL) - errx(1, "couldn't load font"); + if (bar_fs == NULL) - errx(1, "couldn't create font structure"); + errx(1, "Error creating font set structure."); + + bar_fs_extents = XExtentsOfFontSet(bar_fs); + + bar_height = bar_fs_extents->max_logical_extent.height + + 2 * bar_border_width; + + if (bar_height < 1) + bar_height = 1; - bar_height = bar_fs->ascent + bar_fs->descent + 1 + 2 * bar_border_width; x = X(r); y = bar_at_bottom ? (Y(r) + HEIGHT(r) - bar_height) : Y(r); r->bar_window = XCreateSimpleWindow(display, - r->s->root, x, y, WIDTH(r) - 2 * bar_border_width, bar_height - 2 * bar_border_width, + r->s->root, x, y, WIDTH(r) - 2 * bar_border_width, + bar_height - 2 * bar_border_width, bar_border_width, r->s->c[SWM_S_COLOR_BAR_BORDER].color, r->s->c[SWM_S_COLOR_BAR].color); - bar_gc = XCreateGC(display, r->bar_window, 0, &bar_gcv); - XSetFont(display, bar_gc, bar_fs->fid); XSelectInput(display, r->bar_window, VisibilityChangeMask); if (bar_enabled) XMapRaised(display, r->bar_window); - DNPRINTF(SWM_D_MISC, "bar_setup: bar_window %lu\n", r->bar_window); + DNPRINTF(SWM_D_BAR, "bar_setup: bar_window: 0x%lx\n", r->bar_window); if (signal(SIGALRM, bar_signal) == SIG_ERR) err(1, "could not install bar_signal"); @@ -1380,11 +1627,23 @@ bar_setup(struct swm_region *r) } void +drain_enter_notify(void) +{ + int i = 0; + XEvent cne; + + while (XCheckMaskEvent(display, EnterWindowMask, &cne)) + i++; + + DNPRINTF(SWM_D_MISC, "drain_enter_notify: drained: %d\n", i); +} + +void set_win_state(struct ws_win *win, long state) { long data[] = {state, None}; - DNPRINTF(SWM_D_EVENT, "set_win_state: window: %lu\n", win->id); + DNPRINTF(SWM_D_EVENT, "set_win_state: window: 0x%lx\n", win->id); if (win == NULL) return; @@ -1400,7 +1659,7 @@ getstate(Window w) unsigned char *p = NULL; unsigned long n; - if (!get_property(w, astate, 2L, astate, &n, &p)) + if (!get_property(w, astate, 2L, astate, &n, NULL, &p)) return (-1); if (n != 0) result = *((long *)p); @@ -1413,8 +1672,8 @@ version(struct swm_region *r, union arg *args) { bar_version = !bar_version; if (bar_version) - snprintf(bar_vertext, sizeof bar_vertext, "Version: %s CVS: %s", - SWM_VERSION, cvstag); + snprintf(bar_vertext, sizeof bar_vertext, + "Version: %s Build: %s", SCROTWM_VERSION, buildstr); else strlcpy(bar_vertext, "", sizeof bar_vertext); bar_update(); @@ -1438,6 +1697,7 @@ client_msg(struct ws_win *win, Atom a) XSendEvent(display, win->id, False, 0L, (XEvent *)&cm); } +/* synthetic response to a ConfigureRequest when not making a change */ void config_win(struct ws_win *win, XConfigureRequestEvent *ev) { @@ -1446,37 +1706,74 @@ config_win(struct ws_win *win, XConfigureRequestEvent *ev) if (win == NULL) return; - if (ev == NULL) { - DNPRINTF(SWM_D_MISC, "config_win: win %lu x %d y %d w %d h %d\n", - win->id, win->g.x, win->g.y, win->g.w, win->g.h); + /* send notification of unchanged state. */ + ce.type = ConfigureNotify; + ce.x = X(win); + ce.y = Y(win); + ce.width = WIDTH(win); + ce.height = HEIGHT(win); + ce.override_redirect = False; - ce.type = ConfigureNotify; + if (ev == NULL) { + /* EWMH */ ce.display = display; ce.event = win->id; ce.window = win->id; - ce.x = win->g.x; - ce.y = win->g.y; - ce.width = win->g.w; - ce.height = win->g.h; ce.border_width = border_width; ce.above = None; - ce.override_redirect = False; } else { - DNPRINTF(SWM_D_MISC, "config_win: ev win %lu x %d y %d w %d h %d\n", - ev->window, ev->x, ev->y, ev->width, ev->height); - ce.type = ConfigureNotify; + /* normal */ ce.display = ev->display; ce.event = ev->window; ce.window = ev->window; - ce.x = ev->x; - ce.y = ev->y; - ce.width = ev->width; - ce.height = ev->height; + + /* make response appear more WM_SIZE_HINTS-compliant */ + if (win->sh_mask) + DNPRINTF(SWM_D_MISC, "config_win: hints: window: 0x%lx," + " sh_mask: %ld, min: %d x %d, max: %d x %d, inc: " + "%d x %d\n", win->id, win->sh_mask, SH_MIN_W(win), + SH_MIN_H(win), SH_MAX_W(win), SH_MAX_H(win), + SH_INC_W(win), SH_INC_H(win)); + + /* min size */ + if (SH_MIN(win)) { + /* the hint may be set... to 0! */ + if (SH_MIN_W(win) > 0 && ce.width < SH_MIN_W(win)) + ce.width = SH_MIN_W(win); + if (SH_MIN_H(win) > 0 && ce.height < SH_MIN_H(win)) + ce.height = SH_MIN_H(win); + } + + /* max size */ + if (SH_MAX(win)) { + /* may also be advertized as 0 */ + if (SH_MAX_W(win) > 0 && ce.width > SH_MAX_W(win)) + ce.width = SH_MAX_W(win); + if (SH_MAX_H(win) > 0 && ce.height > SH_MAX_H(win)) + ce.height = SH_MAX_H(win); + } + + /* resize increment. */ + if (SH_INC(win)) { + if (SH_INC_W(win) > 1 && ce.width > SH_INC_W(win)) + ce.width -= (ce.width - SH_MIN_W(win)) % + SH_INC_W(win); + if (SH_INC_H(win) > 1 && ce.height > SH_INC_H(win)) + ce.height -= (ce.height - SH_MIN_H(win)) % + SH_INC_H(win); + } + + /* adjust x and y for requested border_width. */ + ce.x += border_width - ev->border_width; + ce.y += border_width - ev->border_width; ce.border_width = ev->border_width; ce.above = ev->above; - ce.override_redirect = False; } + DNPRINTF(SWM_D_MISC, "config_win: ewmh: %s, window: 0x%lx, (x,y) w x h: " + "(%d,%d) %d x %d, border: %d\n", YESNO(ev == NULL), win->id, ce.x, + ce.y, ce.width, ce.height, ce.border_width); + XSendEvent(display, win->id, False, StructureNotifyMask, (XEvent *)&ce); } @@ -1549,8 +1846,8 @@ fake_keypress(struct ws_win *win, int keysym, int modifiers) event.root = win->s->root; event.subwindow = None; event.time = CurrentTime; - event.x = win->g.x; - event.y = win->g.y; + event.x = X(win); + event.y = Y(win); event.x_root = 1; event.y_root = 1; event.same_screen = True; @@ -1575,15 +1872,14 @@ restart(struct swm_region *r, union arg *args) /* disable alarm because the following code may not be interrupted */ alarm(0); if (signal(SIGALRM, SIG_IGN) == SIG_ERR) - errx(1, "can't disable alarm"); + err(1, "can't disable alarm"); bar_extra_stop(); bar_extra = 1; unmap_all(); XCloseDisplay(display); execvp(start_argv[0], start_argv); - fprintf(stderr, "execvp failed\n"); - perror(" failed"); + warn("execvp failed"); quit(NULL, NULL); } @@ -1664,7 +1960,7 @@ find_window(Window id) } void -spawn(struct swm_region *r, union arg *args, int close_fd) +spawn(int ws_idx, union arg *args, int close_fd) { int fd; char *ret = NULL; @@ -1676,8 +1972,8 @@ spawn(struct swm_region *r, union arg *args, int close_fd) setenv("LD_PRELOAD", SWM_LIB, 1); - if (asprintf(&ret, "%d", r->ws->idx) == -1) { - perror("_SWM_WS"); + if (asprintf(&ret, "%d", ws_idx) == -1) { + warn("spawn: asprintf SWM_WS"); _exit(1); } setenv("_SWM_WS", ret, 1); @@ -1685,7 +1981,7 @@ spawn(struct swm_region *r, union arg *args, int close_fd) ret = NULL; if (asprintf(&ret, "%d", getpid()) == -1) { - perror("_SWM_PID"); + warn("spawn: asprintf _SWM_PID"); _exit(1); } setenv("_SWM_PID", ret, 1); @@ -1693,7 +1989,7 @@ spawn(struct swm_region *r, union arg *args, int close_fd) ret = NULL; if (setsid() == -1) { - perror("setsid"); + warn("spawn: setsid"); _exit(1); } @@ -1704,7 +2000,7 @@ spawn(struct swm_region *r, union arg *args, int close_fd) * leave stderr open to record errors */ if ((fd = open(_PATH_DEVNULL, O_RDWR, 0)) == -1) { - perror("open"); + warn("spawn: open"); _exit(1); } dup2(fd, STDIN_FILENO); @@ -1715,7 +2011,7 @@ spawn(struct swm_region *r, union arg *args, int close_fd) execvp(args->argv[0], args->argv); - perror("execvp"); + warn("spawn: execvp"); _exit(1); } @@ -1724,10 +2020,11 @@ spawnterm(struct swm_region *r, union arg *args) { DNPRINTF(SWM_D_MISC, "spawnterm\n"); - if (term_width) - setenv("_SWM_XTERM_FONTADJ", "", 1); - if (fork() == 0) - spawn(r, args, 1); + if (fork() == 0) { + if (term_width) + setenv("_SWM_XTERM_FONTADJ", "", 1); + spawn(r->ws->idx, args, 1); + } } void @@ -1757,12 +2054,12 @@ validate_win(struct ws_win *testwin) struct ws_win *win; struct workspace *ws; struct swm_region *r; - int i, x, foundit = 0; + int i, x; if (testwin == NULL) - return(0); + return (0); - for (i = 0, foundit = 0; i < ScreenCount(display); i++) + for (i = 0; i < ScreenCount(display); i++) TAILQ_FOREACH(r, &screens[i].rl, entry) for (x = 0; x < SWM_WS_MAX; x++) { ws = &r->s->ws[x]; @@ -1778,10 +2075,10 @@ validate_ws(struct workspace *testws) { struct swm_region *r; struct workspace *ws; - int foundit, i, x; + int i, x; /* validate all ws */ - for (i = 0, foundit = 0; i < ScreenCount(display); i++) + for (i = 0; i < ScreenCount(display); i++) TAILQ_FOREACH(r, &screens[i].rl, entry) for (x = 0; x < SWM_WS_MAX; x++) { ws = &r->s->ws[x]; @@ -1797,7 +2094,7 @@ unfocus_win(struct ws_win *win) XEvent cne; Window none = None; - DNPRINTF(SWM_D_FOCUS, "unfocus_win: id: %lu\n", WINID(win)); + DNPRINTF(SWM_D_FOCUS, "unfocus_win: window: 0x%lx\n", WINID(win)); if (win == NULL) return; @@ -1865,7 +2162,7 @@ focus_win(struct ws_win *win) struct ws_win *cfw = NULL; - DNPRINTF(SWM_D_FOCUS, "focus_win: id: %lu\n", win ? win->id : 0); + DNPRINTF(SWM_D_FOCUS, "focus_win: window: 0x%lx\n", WINID(win)); if (win == NULL) return; @@ -1888,6 +2185,13 @@ focus_win(struct ws_win *win) XGetInputFocus(display, &cur_focus, &rr); if ((cfw = find_window(cur_focus)) != NULL) unfocus_win(cfw); + else { + /* use larger hammer since the window was killed somehow */ + TAILQ_FOREACH(cfw, &win->ws->winlist, entry) + if (cfw->ws && cfw->ws->r && cfw->ws->r->s) + XSetWindowBorder(display, cfw->id, + cfw->ws->r->s->c[SWM_S_COLOR_UNFOCUS].color); + } win->ws->focus = win; @@ -1902,7 +2206,8 @@ focus_win(struct ws_win *win) grabbuttons(win, 1); XSetWindowBorder(display, win->id, win->ws->r->s->c[SWM_S_COLOR_FOCUS].color); - if (win->ws->cur_layout->flags & SWM_L_MAPONFOCUS) + if (win->ws->cur_layout->flags & SWM_L_MAPONFOCUS || + win->ws->always_raise) XMapRaised(display, win->id); XChangeProperty(display, win->s->root, @@ -1910,7 +2215,7 @@ focus_win(struct ws_win *win) PropModeReplace, (unsigned char *)&win->id,1); } - if (window_name_enabled) + if (window_name_enabled || title_class_enabled || title_name_enabled) bar_update(); } @@ -1930,9 +2235,8 @@ switchws(struct swm_region *r, union arg *args) old_ws = this_r->ws; new_ws = &this_r->s->ws[wsid]; - DNPRINTF(SWM_D_WS, "switchws screen[%d]:%dx%d+%d+%d: " - "%d -> %d\n", r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), - old_ws->idx, wsid); + DNPRINTF(SWM_D_WS, "switchws: screen[%d]:%dx%d+%d+%d: %d -> %d\n", + r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), old_ws->idx, wsid); if (new_ws == NULL || old_ws == NULL) return; @@ -1954,15 +2258,22 @@ switchws(struct swm_region *r, union arg *args) this_r->ws = new_ws; new_ws->r = this_r; + /* this is needed so that we can click on a window after a restart */ + unfocus_all(); + stack(); a.id = SWM_ARG_ID_FOCUSCUR; focus(new_ws->r, &a); + bar_update(); /* unmap old windows */ if (unmap_old) TAILQ_FOREACH(win, &old_ws->winlist, entry) unmap_window(win); + + if (focus_mode == SWM_FOCUS_DEFAULT) + drain_enter_notify(); } void @@ -1970,20 +2281,26 @@ cyclews(struct swm_region *r, union arg *args) { union arg a; struct swm_screen *s = r->s; + int cycle_all = 0; - DNPRINTF(SWM_D_WS, "cyclews id %d " - "in screen[%d]:%dx%d+%d+%d ws %d\n", args->id, - r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx); + DNPRINTF(SWM_D_WS, "cyclews: id: %d, screen[%d]:%dx%d+%d+%d, ws: %d\n", + args->id, r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx); a.id = r->ws->idx; do { switch (args->id) { + case SWM_ARG_ID_CYCLEWS_UP_ALL: + cycle_all = 1; + /* FALLTHROUGH */ case SWM_ARG_ID_CYCLEWS_UP: if (a.id < SWM_WS_MAX - 1) a.id++; else a.id = 0; break; + case SWM_ARG_ID_CYCLEWS_DOWN_ALL: + cycle_all = 1; + /* FALLTHROUGH */ case SWM_ARG_ID_CYCLEWS_DOWN: if (a.id > 0) a.id--; @@ -1994,7 +2311,8 @@ cyclews(struct swm_region *r, union arg *args) return; }; - if (cycle_empty == 0 && TAILQ_EMPTY(&s->ws[a.id].winlist)) + if (!cycle_all && + (cycle_empty == 0 && TAILQ_EMPTY(&s->ws[a.id].winlist))) continue; if (cycle_visible == 0 && s->ws[a.id].r != NULL) continue; @@ -2008,9 +2326,8 @@ priorws(struct swm_region *r, union arg *args) { union arg a; - DNPRINTF(SWM_D_WS, "priorws id %d " - "in screen[%d]:%dx%d+%d+%d ws %d\n", args->id, - r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx); + DNPRINTF(SWM_D_WS, "priorws: id: %d, screen[%d]:%dx%d+%d+%d, ws: %d\n", + args->id, r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx); if (r->ws_prior == NULL) return; @@ -2049,8 +2366,8 @@ cyclescr(struct swm_region *r, union arg *args) return; /* move mouse to region */ - x = rr->g.x + 1; - y = rr->g.y + 1 + (bar_enabled ? bar_height : 0); + x = X(rr) + 1; + y = Y(rr) + 1 + (bar_enabled ? bar_height : 0); XWarpPointer(display, None, rr->s[i].root, 0, 0, 0, 0, x, y); a.id = SWM_ARG_ID_FOCUSCUR; @@ -2058,13 +2375,36 @@ cyclescr(struct swm_region *r, union arg *args) if (rr->ws->focus) { /* move to focus window */ - x = rr->ws->focus->g.x + 1; - y = rr->ws->focus->g.y + 1; + x = X(rr->ws->focus) + 1; + y = Y(rr->ws->focus) + 1; XWarpPointer(display, None, rr->s[i].root, 0, 0, 0, 0, x, y); } } void +sort_windows(struct ws_win_list *wl) +{ + struct ws_win *win, *parent, *nxt; + + if (wl == NULL) + return; + + for (win = TAILQ_FIRST(wl); win != TAILQ_END(wl); win = nxt) { + nxt = TAILQ_NEXT(win, entry); + if (win->transient) { + parent = find_window(win->transient); + if (parent == NULL) { + warnx("not possible bug"); + continue; + } + TAILQ_REMOVE(wl, win, entry); + TAILQ_INSERT_AFTER(wl, parent, win, entry); + } + } + +} + +void swapwin(struct swm_region *r, union arg *args) { struct ws_win *target, *source; @@ -2072,9 +2412,8 @@ swapwin(struct swm_region *r, union arg *args) struct ws_win_list *wl; - DNPRINTF(SWM_D_WS, "swapwin id %d " - "in screen %d region %dx%d+%d+%d ws %d\n", args->id, - r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx); + DNPRINTF(SWM_D_WS, "swapwin: id: %d, screen[%d]:%dx%d+%d+%d, ws: %d\n", + args->id, r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx); cur_focus = r->ws->focus; if (cur_focus == NULL) @@ -2085,8 +2424,12 @@ swapwin(struct swm_region *r, union arg *args) switch (args->id) { case SWM_ARG_ID_SWAPPREV: + if (source->transient) + source = find_window(source->transient); target = TAILQ_PREV(source, ws_win_list, entry); - TAILQ_REMOVE(wl, cur_focus, entry); + if (target && target->transient) + target = find_window(target->transient); + TAILQ_REMOVE(wl, source, entry); if (target == NULL) TAILQ_INSERT_TAIL(wl, source, entry); else @@ -2094,6 +2437,9 @@ swapwin(struct swm_region *r, union arg *args) break; case SWM_ARG_ID_SWAPNEXT: target = TAILQ_NEXT(source, entry); + /* move the parent and let the sort handle the move */ + if (source->transient) + source = find_window(source->transient); TAILQ_REMOVE(wl, source, entry); if (target == NULL) TAILQ_INSERT_HEAD(wl, source, entry); @@ -2109,7 +2455,7 @@ swapwin(struct swm_region *r, union arg *args) source = source->ws->focus_prev; else return; - } + } if (target == NULL || source == NULL) return; source->ws->focus_prev = target; @@ -2123,10 +2469,12 @@ swapwin(struct swm_region *r, union arg *args) TAILQ_INSERT_TAIL(wl, source, entry); break; default: - DNPRINTF(SWM_D_MOVE, "invalid id: %d\n", args->id); + DNPRINTF(SWM_D_MOVE, "swapwin: invalid id: %d\n", args->id); return; } + sort_windows(wl); + stack(); } @@ -2138,7 +2486,7 @@ focus_prev(struct ws_win *win) struct ws_win_list *wl = NULL; struct workspace *ws = NULL; - DNPRINTF(SWM_D_FOCUS, "focus_prev: id %lu\n", WINID(win)); + DNPRINTF(SWM_D_FOCUS, "focus_prev: window: 0x%lx\n", WINID(win)); if (!(win && win->ws)) return; @@ -2166,7 +2514,8 @@ focus_prev(struct ws_win *win) } /* if in max_stack try harder */ - if (ws->cur_layout->flags & SWM_L_FOCUSPREV) { + if ((win->quirks & SWM_Q_FOCUSPREV) || + (ws->cur_layout->flags & SWM_L_FOCUSPREV)) { if (cur_focus != ws->focus_prev) winfocus = ws->focus_prev; else if (cur_focus != ws->focus) @@ -2184,10 +2533,15 @@ focus_prev(struct ws_win *win) if (winfocus == NULL || winfocus == win) winfocus = TAILQ_NEXT(cur_focus, entry); done: - if (winfocus == winlostfocus || winfocus == NULL) + if (winfocus == winlostfocus || winfocus == NULL) { + /* update the bar so that title/class/name will be cleared. */ + if (window_name_enabled || title_name_enabled || + title_class_enabled) + bar_update(); return; + } - focus_magic(winfocus, SWM_F_GENERIC); + focus_magic(winfocus); } void @@ -2197,11 +2551,12 @@ focus(struct swm_region *r, union arg *args) struct ws_win *cur_focus = NULL; struct ws_win_list *wl = NULL; struct workspace *ws = NULL; + int all_iconics; if (!(r && r->ws)) return; - DNPRINTF(SWM_D_FOCUS, "focus: id %d\n", args->id); + DNPRINTF(SWM_D_FOCUS, "focus: id: %d\n", args->id); /* treat FOCUS_CUR special */ if (args->id == SWM_ARG_ID_FOCUSCUR) { @@ -2214,7 +2569,7 @@ focus(struct swm_region *r, union arg *args) if (winfocus->iconic == 0) break; - focus_magic(winfocus, SWM_F_GENERIC); + focus_magic(winfocus); return; } @@ -2222,6 +2577,17 @@ focus(struct swm_region *r, union arg *args) return; ws = r->ws; wl = &ws->winlist; + if (TAILQ_EMPTY(wl)) + return; + /* make sure there is at least one uniconified window */ + all_iconics = 1; + TAILQ_FOREACH(winfocus, wl, entry) + if (winfocus->iconic == 0) { + all_iconics = 0; + break; + } + if (all_iconics) + return; winlostfocus = cur_focus; @@ -2231,17 +2597,22 @@ focus(struct swm_region *r, union arg *args) if (head == NULL) head = TAILQ_LAST(wl, ws_win_list); winfocus = head; - for (;;) { - if (winfocus == NULL) - break; - if (!winfocus->iconic) - break; - winfocus = TAILQ_PREV(winfocus, ws_win_list, entry); - if (winfocus == NULL) - winfocus = TAILQ_LAST(wl, ws_win_list); - if (winfocus == head) { - winfocus = NULL; - break; + if (WINID(winfocus) == cur_focus->transient) { + head = TAILQ_PREV(winfocus, ws_win_list, entry); + if (head == NULL) + head = TAILQ_LAST(wl, ws_win_list); + winfocus = head; + } + + /* skip iconics */ + if (winfocus && winfocus->iconic) { + while (winfocus != cur_focus) { + if (winfocus == NULL) + winfocus = TAILQ_LAST(wl, ws_win_list); + if (winfocus->iconic == 0) + break; + winfocus = TAILQ_PREV(winfocus, ws_win_list, + entry); } } break; @@ -2251,17 +2622,15 @@ focus(struct swm_region *r, union arg *args) if (head == NULL) head = TAILQ_FIRST(wl); winfocus = head; - for (;;) { - if (winfocus == NULL) - break; - if (!winfocus->iconic) - break; - winfocus = TAILQ_NEXT(winfocus, entry); - if (winfocus == NULL) - winfocus = TAILQ_FIRST(wl); - if (winfocus == head) { - winfocus = NULL; - break; + + /* skip iconics */ + if (winfocus && winfocus->iconic) { + while (winfocus != cur_focus) { + if (winfocus == NULL) + winfocus = TAILQ_FIRST(wl); + if (winfocus->iconic == 0) + break; + winfocus = TAILQ_NEXT(winfocus, entry); } } break; @@ -2275,29 +2644,33 @@ focus(struct swm_region *r, union arg *args) default: return; } + if (winfocus == winlostfocus || winfocus == NULL) { + /* update the bar so that title/class/name will be cleared. */ + if (window_name_enabled || title_name_enabled || + title_class_enabled) + bar_update(); - if (winfocus == winlostfocus || winfocus == NULL) return; + } - focus_magic(winfocus, SWM_F_GENERIC); + focus_magic(winfocus); } void cycle_layout(struct swm_region *r, union arg *args) { struct workspace *ws = r->ws; - struct ws_win *winfocus; union arg a; DNPRINTF(SWM_D_EVENT, "cycle_layout: workspace: %d\n", ws->idx); - winfocus = ws->focus; - ws->cur_layout++; if (ws->cur_layout->l_stack == NULL) ws->cur_layout = &layouts[0]; stack(); + if (focus_mode == SWM_FOCUS_DEFAULT) + drain_enter_notify(); a.id = SWM_ARG_ID_FOCUSCUR; focus(r, &a); bar_update(); @@ -2308,29 +2681,35 @@ stack_config(struct swm_region *r, union arg *args) { struct workspace *ws = r->ws; - DNPRINTF(SWM_D_STACK, "stack_config for workspace %d (id %d\n", + DNPRINTF(SWM_D_STACK, "stack_config: id: %d workspace: %d\n", args->id, ws->idx); if (ws->cur_layout->l_config != NULL) ws->cur_layout->l_config(ws, args->id); - if (args->id != SWM_ARG_ID_STACKINIT); + if (args->id != SWM_ARG_ID_STACKINIT) stack(); + bar_update(); } void stack(void) { struct swm_geometry g; struct swm_region *r; - int i, j; + int i; +#ifdef SWM_DEBUG + int j; +#endif - DNPRINTF(SWM_D_STACK, "stack\n"); + DNPRINTF(SWM_D_STACK, "stack: begin\n"); for (i = 0; i < ScreenCount(display); i++) { +#ifdef SWM_DEBUG j = 0; +#endif TAILQ_FOREACH(r, &screens[i].rl, entry) { - DNPRINTF(SWM_D_STACK, "stacking workspace %d " - "(screen %d, region %d)\n", r->ws->idx, i, j++); + DNPRINTF(SWM_D_STACK, "stack: workspace: %d " + "(screen: %d, region: %d)\n", r->ws->idx, i, j++); /* start with screen geometry, adjust for bar */ g = r->g; @@ -2342,26 +2721,26 @@ stack(void) { g.h -= bar_height; } r->ws->cur_layout->l_stack(r->ws, &g); + r->ws->cur_layout->l_string(r->ws); /* save r so we can track region changes */ r->ws->old_r = r; } } if (font_adjusted) font_adjusted--; + + if (focus_mode == SWM_FOCUS_DEFAULT) + drain_enter_notify(); + + DNPRINTF(SWM_D_STACK, "stack: end\n"); } void store_float_geom(struct ws_win *win, struct swm_region *r) { /* retain window geom and region geom */ - win->g_float.x = win->g.x; - win->g_float.y = win->g.y; - win->g_float.w = win->g.w; - win->g_float.h = win->g.h; - win->rg_float.x = r->g.x; - win->rg_float.y = r->g.y; - win->rg_float.w = r->g.w; - win->rg_float.h = r->g.h; + win->g_float = win->g; + win->rg_float = r->g; win->g_floatvalid = 1; } @@ -2381,30 +2760,29 @@ stack_floater(struct ws_win *win, struct swm_region *r) * to allow windows to change their size (e.g. mplayer fs) only retrieve * geom on ws switches or return from max mode */ - if (win->floatmaxed || (r != r->ws->old_r && win->g_floatvalid && !(win->ewmh_flags & EWMH_F_FULLSCREEN))) { /* * use stored g and rg to set relative position and size * as in old region or before max stack mode */ - win->g.x = win->g_float.x - win->rg_float.x + r->g.x; - win->g.y = win->g_float.y - win->rg_float.y + r->g.y; - win->g.w = win->g_float.w; - win->g.h = win->g_float.h; + X(win) = win->g_float.x - win->rg_float.x + X(r); + Y(win) = win->g_float.y - win->rg_float.y + Y(r); + WIDTH(win) = win->g_float.w; + HEIGHT(win) = win->g_float.h; win->g_floatvalid = 0; } win->floatmaxed = 0; - if ((win->quirks & SWM_Q_FULLSCREEN) && (win->g.w >= WIDTH(r)) && - (win->g.h >= HEIGHT(r))) + if ((win->quirks & SWM_Q_FULLSCREEN) && (WIDTH(win) >= WIDTH(r)) && + (HEIGHT(win) >= HEIGHT(r))) wc.border_width = 0; else wc.border_width = border_width; if (win->transient && (win->quirks & SWM_Q_TRANSSZ)) { - win->g.w = (double)WIDTH(r) * dialog_ratio; - win->g.h = (double)HEIGHT(r) * dialog_ratio; + WIDTH(win) = (double)WIDTH(r) * dialog_ratio; + HEIGHT(win) = (double)HEIGHT(r) * dialog_ratio; } if (!win->manual) { @@ -2412,27 +2790,27 @@ stack_floater(struct ws_win *win, struct swm_region *r) * floaters and transients are auto-centred unless moved * or resized */ - win->g.x = r->g.x + (WIDTH(r) - win->g.w) / 2 - border_width; - win->g.y = r->g.y + (HEIGHT(r) - win->g.h) / 2 - border_width; + X(win) = X(r) + (WIDTH(r) - WIDTH(win)) / 2 - wc.border_width; + Y(win) = Y(r) + (HEIGHT(r) - HEIGHT(win)) / 2 - wc.border_width; } /* win can be outside r if new r smaller than old r */ /* Ensure top left corner inside r (move probs otherwise) */ - if (win->g.x < r->g.x - border_width) - win->g.x = r->g.x - border_width; - if (win->g.x > r->g.x + r->g.w - 1) - win->g.x = (win->g.w > r->g.w) ? r->g.x : - (r->g.x + r->g.w - win->g.w - 2 * border_width); - if (win->g.y < r->g.y - border_width) - win->g.y = r->g.y - border_width; - if (win->g.y > r->g.y + r->g.h - 1) - win->g.y = (win->g.h > r->g.h) ? r->g.y : - (r->g.y + r->g.h - win->g.h - 2 * border_width); - - wc.x = win->g.x; - wc.y = win->g.y; - wc.width = win->g.w; - wc.height = win->g.h; + if (X(win) < X(r) - wc.border_width) + X(win) = X(r) - wc.border_width; + if (X(win) > X(r) + WIDTH(r) - 1) + X(win) = (WIDTH(win) > WIDTH(r)) ? X(r) : + (X(r) + WIDTH(r) - WIDTH(win) - 2 * wc.border_width); + if (Y(win) < Y(r) - wc.border_width) + Y(win) = Y(r) - wc.border_width; + if (Y(win) > Y(r) + HEIGHT(r) - 1) + Y(win) = (HEIGHT(win) > HEIGHT(r)) ? Y(r) : + (Y(r) + HEIGHT(r) - HEIGHT(win) - 2 * wc.border_width); + + wc.x = X(win); + wc.y = Y(win); + wc.width = WIDTH(win); + wc.height = HEIGHT(win); /* * Retain floater and transient geometry for correct positioning @@ -2441,8 +2819,8 @@ stack_floater(struct ws_win *win, struct swm_region *r) if (!(win->ewmh_flags & EWMH_F_FULLSCREEN)) store_float_geom(win, r); - DNPRINTF(SWM_D_MISC, "stack_floater: win %lu x %d y %d w %d h %d\n", - win->id, wc.x, wc.y, wc.width, wc.height); + DNPRINTF(SWM_D_MISC, "stack_floater: window: %lu, (x,y) w x h: (%d,%d) " + "%d x %d\n", win->id, wc.x, wc.y, wc.width, wc.height); XConfigureWindow(display, win->id, mask, &wc); } @@ -2459,7 +2837,7 @@ adjust_font(struct ws_win *win) return; if (win->sh.width_inc && win->last_inc != win->sh.width_inc && - win->g.w / win->sh.width_inc < term_width && + WIDTH(win) / win->sh.width_inc < term_width && win->font_steps < SWM_MAX_FONT_STEPS) { win->font_size_boundary[win->font_steps] = (win->sh.width_inc * term_width) + win->sh.base_width; @@ -2468,7 +2846,7 @@ adjust_font(struct ws_win *win) win->last_inc = win->sh.width_inc; fake_keypress(win, XK_KP_Subtract, ShiftMask); } else if (win->font_steps && win->last_inc != win->sh.width_inc && - win->g.w > win->font_size_boundary[win->font_steps - 1]) { + WIDTH(win) > win->font_size_boundary[win->font_steps - 1]) { win->font_steps--; font_adjusted++; win->last_inc = win->sh.width_inc; @@ -2495,15 +2873,16 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip) int remain, missing, v_slice, reconfigure; unsigned int mask; - DNPRINTF(SWM_D_STACK, "stack_master: workspace: %d\n rot=%s flip=%s", - ws->idx, rot ? "yes" : "no", flip ? "yes" : "no"); + DNPRINTF(SWM_D_STACK, "stack_master: workspace: %d, rot: %s, " + "flip: %s\n", ws->idx, YESNO(rot), YESNO(flip)); winno = count_win(ws, 0); if (winno == 0 && count_win(ws, 1) == 0) return; TAILQ_FOREACH(win, &ws->winlist, entry) - if (win->transient == 0 && win->floating == 0 && win->iconic == 0) + if (win->transient == 0 && win->floating == 0 + && win->iconic == 0) break; if (win == NULL) @@ -2552,7 +2931,8 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip) } else { msize = -2; colno = split = winno / stacks; - win_g.w = ((r_g.w - (stacks * 2 * border_width) + 2 * border_width) / stacks); + win_g.w = ((r_g.w - (stacks * 2 * border_width) + + 2 * border_width) / stacks); } hrh = r_g.h / colno; extra = r_g.h - (colno * hrh); @@ -2582,10 +2962,11 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip) win_g.x = r_g.x; else win_g.x += win_g.w + 2 * border_width; - win_g.w = (r_g.w - msize - (stacks * 2 * border_width)) / stacks; + win_g.w = (r_g.w - msize - + (stacks * 2 * border_width)) / stacks; if (s == 1) - win_g.w += (r_g.w - msize - (stacks * 2 * border_width)) % - stacks; + win_g.w += (r_g.w - msize - + (stacks * 2 * border_width)) % stacks; s--; j = 0; } @@ -2627,22 +3008,22 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip) wc.border_width = border_width; reconfigure = 0; if (rot) { - if (win->g.x != win_g.y || win->g.y != win_g.x || - win->g.w != win_g.h || win->g.h != win_g.w) { + if (X(win) != win_g.y || Y(win) != win_g.x || + WIDTH(win) != win_g.h || HEIGHT(win) != win_g.w) { reconfigure = 1; - win->g.x = wc.x = win_g.y; - win->g.y = wc.y = win_g.x; - win->g.w = wc.width = win_g.h; - win->g.h = wc.height = win_g.w; + X(win) = wc.x = win_g.y; + Y(win) = wc.y = win_g.x; + WIDTH(win) = wc.width = win_g.h; + HEIGHT(win) = wc.height = win_g.w; } } else { - if (win->g.x != win_g.x || win->g.y != win_g.y || - win->g.w != win_g.w || win->g.h != win_g.h) { + if (X(win) != win_g.x || Y(win) != win_g.y || + WIDTH(win) != win_g.w || HEIGHT(win) != win_g.h) { reconfigure = 1; - win->g.x = wc.x = win_g.x; - win->g.y = wc.y = win_g.y; - win->g.w = wc.width = win_g.w; - win->g.h = wc.height = win_g.h; + X(win) = wc.x = win_g.x; + Y(win) = wc.y = win_g.y; + WIDTH(win) = wc.width = win_g.w; + HEIGHT(win) = wc.height = win_g.h; } } if (reconfigure) { @@ -2660,14 +3041,13 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip) j++; } - notiles: +notiles: /* now, stack all the floaters and transients */ TAILQ_FOREACH(win, &ws->winlist, entry) { if (win->transient == 0 && win->floating == 0) continue; - if (win->iconic == 0) + if (win->iconic == 1) continue; - if (win->ewmh_flags & EWMH_F_FULLSCREEN) { fs_win = win; continue; @@ -2686,7 +3066,8 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip) void vertical_config(struct workspace *ws, int id) { - DNPRINTF(SWM_D_STACK, "vertical_resize: workspace: %d\n", ws->idx); + DNPRINTF(SWM_D_STACK, "vertical_config: id: %d, workspace: %d\n", + id, ws->idx); switch (id) { case SWM_ARG_ID_STACKRESET: @@ -2717,6 +3098,9 @@ vertical_config(struct workspace *ws, int id) if (ws->l_state.vertical_stacks > 1) ws->l_state.vertical_stacks--; break; + case SWM_ARG_ID_FLIPLAYOUT: + ws->l_state.vertical_flip = !ws->l_state.vertical_flip; + break; default: return; } @@ -2727,7 +3111,7 @@ vertical_stack(struct workspace *ws, struct swm_geometry *g) { DNPRINTF(SWM_D_STACK, "vertical_stack: workspace: %d\n", ws->idx); - stack_master(ws, g, 0, 0); + stack_master(ws, g, 0, ws->l_state.vertical_flip); } void @@ -2764,6 +3148,9 @@ horizontal_config(struct workspace *ws, int id) if (ws->l_state.horizontal_stacks > 1) ws->l_state.horizontal_stacks--; break; + case SWM_ARG_ID_FLIPLAYOUT: + ws->l_state.horizontal_flip = !ws->l_state.horizontal_flip; + break; default: return; } @@ -2774,7 +3161,7 @@ horizontal_stack(struct workspace *ws, struct swm_geometry *g) { DNPRINTF(SWM_D_STACK, "horizontal_stack: workspace: %d\n", ws->idx); - stack_master(ws, g, 1, 0); + stack_master(ws, g, 1, ws->l_state.horizontal_flip); } /* fullscreen view */ @@ -2813,19 +3200,20 @@ max_stack(struct workspace *ws, struct swm_geometry *g) } /* only reconfigure if necessary */ - if (win->g.x != gg.x || win->g.y != gg.y || win->g.w != gg.w || - win->g.h != gg.h) { + if (X(win) != gg.x || Y(win) != gg.y || WIDTH(win) != gg.w || + HEIGHT(win) != gg.h) { bzero(&wc, sizeof wc); - win->g.x = wc.x = gg.x; - win->g.y = wc.y = gg.y; + X(win) = wc.x = gg.x; + Y(win) = wc.y = gg.y; if (bar_enabled){ wc.border_width = border_width; - win->g.w = wc.width = gg.w; - win->g.h = wc.height = gg.h; + WIDTH(win) = wc.width = gg.w; + HEIGHT(win) = wc.height = gg.h; } else { wc.border_width = 0; - win->g.w = wc.width = gg.w + 2 * border_width; - win->g.h = wc.height = gg.h + 2 * border_width; + WIDTH(win) = wc.width = gg.w + 2 * border_width; + HEIGHT(win) = wc.height = gg.h + + 2 * border_width; } mask = CWX | CWY | CWWidth | CWHeight | CWBorderWidth; XConfigureWindow(display, win->id, mask, &wc); @@ -2841,7 +3229,7 @@ max_stack(struct workspace *ws, struct swm_geometry *g) if (parent) XMapRaised(display, parent->id); stack_floater(wintrans, ws->r); - focus_magic(wintrans, SWM_F_TRANSIENT); + focus_magic(wintrans); } } @@ -2849,13 +3237,13 @@ void send_to_ws(struct swm_region *r, union arg *args) { int wsid = args->id; - struct ws_win *win = win; + struct ws_win *win = NULL, *parent; struct workspace *ws, *nws; Atom ws_idx_atom = 0; unsigned char ws_idx_str[SWM_PROPLEN]; union arg a; - if (r && r->ws) + if (r && r->ws && r->ws->focus) win = r->ws->focus; else return; @@ -2864,23 +3252,35 @@ send_to_ws(struct swm_region *r, union arg *args) if (win->ws->idx == wsid) return; - DNPRINTF(SWM_D_MOVE, "send_to_ws: win: %lu\n", win->id); + DNPRINTF(SWM_D_MOVE, "send_to_ws: window: 0x%lx\n", win->id); ws = win->ws; nws = &win->s->ws[wsid]; a.id = SWM_ARG_ID_FOCUSPREV; focus(r, &a); + if (win->transient) { + parent = find_window(win->transient); + if (parent) { + unmap_window(parent); + TAILQ_REMOVE(&ws->winlist, parent, entry); + TAILQ_INSERT_TAIL(&nws->winlist, parent, entry); + parent->ws = nws; + } + } unmap_window(win); TAILQ_REMOVE(&ws->winlist, win, entry); TAILQ_INSERT_TAIL(&nws->winlist, win, entry); + if (TAILQ_EMPTY(&ws->winlist)) + r->ws->focus = NULL; win->ws = nws; /* Try to update the window's workspace property */ ws_idx_atom = XInternAtom(display, "_SWM_WS", False); if (ws_idx_atom && - snprintf(ws_idx_str, SWM_PROPLEN, "%d", nws->idx) < SWM_PROPLEN) { - DNPRINTF(SWM_D_PROP, "setting property _SWM_WS to %s\n", + snprintf((char *)ws_idx_str, SWM_PROPLEN, "%d", nws->idx) < + SWM_PROPLEN) { + DNPRINTF(SWM_D_PROP, "send_to_ws: set property: _SWM_WS: %s\n", ws_idx_str); XChangeProperty(display, win->id, ws_idx_atom, XA_STRING, 8, PropModeReplace, ws_idx_str, SWM_PROPLEN); @@ -2890,6 +3290,26 @@ send_to_ws(struct swm_region *r, union arg *args) } void +pressbutton(struct swm_region *r, union arg *args) +{ + XTestFakeButtonEvent(display, args->id, True, CurrentTime); + XTestFakeButtonEvent(display, args->id, False, CurrentTime); +} + +void +raise_toggle(struct swm_region *r, union arg *args) +{ + if (r && r->ws == NULL) + return; + + r->ws->always_raise = !r->ws->always_raise; + + /* bring floaters back to top */ + if (r->ws->always_raise == 0) + stack(); +} + +void iconify(struct swm_region *r, union arg *args) { union arg a; @@ -2899,38 +3319,36 @@ iconify(struct swm_region *r, union arg *args) unmap_window(r->ws->focus); update_iconic(r->ws->focus, 1); stack(); + if (focus_mode == SWM_FOCUS_DEFAULT) + drain_enter_notify(); r->ws->focus = NULL; a.id = SWM_ARG_ID_FOCUSCUR; focus(r, &a); } unsigned char * -get_win_name(Display *dpy, Window win, Atom wname, Atom stype, - unsigned long *slen) +get_win_name(Window win) { - int status, retfmt; - unsigned long nitems, nbytes, nextra; unsigned char *prop = NULL; - Atom rettype; - - status = XGetWindowProperty(dpy, win, wname, 0L, 0L, False, stype, - &rettype, &retfmt, &nitems, &nbytes, &prop); - if (status != Success) - return (NULL); - XFree(prop); + unsigned long nbytes, nitems; - status = XGetWindowProperty(dpy, win, wname, 0L, nbytes, False, - stype, &rettype, &retfmt, &nitems, &nextra, &prop); - if (status != Success) { + /* try _NET_WM_NAME first */ + if (get_property(win, a_netwmname, 0L, a_utf8_string, NULL, &nbytes, + &prop)) { XFree(prop); - return (NULL); + if (get_property(win, a_netwmname, nbytes, a_utf8_string, + &nitems, NULL, &prop)) + return (prop); } - if (rettype != stype) { - XFree(prop); + + /* fallback to WM_NAME */ + if (!get_property(win, a_wmname, 0L, a_string, NULL, &nbytes, &prop)) return (NULL); - } - *slen = nitems; - return (prop); + XFree(prop); + if (get_property(win, a_wmname, nbytes, a_string, &nitems, NULL, &prop)) + return (prop); + + return (NULL); } void @@ -2938,9 +3356,8 @@ uniconify(struct swm_region *r, union arg *args) { struct ws_win *win; FILE *lfile; - char *name; + unsigned char *name; int count = 0; - unsigned long len; DNPRINTF(SWM_D_MISC, "uniconify\n"); @@ -2959,8 +3376,9 @@ uniconify(struct swm_region *r, union arg *args) return; search_r = r; + search_resp_action = SWM_SEARCH_UNICONIFY; - spawn_select(r, args, "uniconify", &searchpid); + spawn_select(r, args, "search", &searchpid); if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL) return; @@ -2971,8 +3389,7 @@ uniconify(struct swm_region *r, union arg *args) if (win->iconic == 0) continue; - name = get_win_name(display, win->id, a_wmname, a_string, - &len); + name = get_win_name(win->id); if (name == NULL) continue; fprintf(lfile, "%s.%lu\n", name, win->id); @@ -2982,39 +3399,154 @@ uniconify(struct swm_region *r, union arg *args) fclose(lfile); } -#define MAX_RESP_LEN 1024 +void +name_workspace(struct swm_region *r, union arg *args) +{ + FILE *lfile; + + DNPRINTF(SWM_D_MISC, "name_workspace\n"); + + if (r == NULL) + return; + + search_r = r; + search_resp_action = SWM_SEARCH_NAME_WORKSPACE; + + spawn_select(r, args, "name_workspace", &searchpid); + + if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL) + return; + + fprintf(lfile, "%s", ""); + fclose(lfile); +} void -search_do_resp(void) +search_workspace(struct swm_region *r, union arg *args) { - ssize_t rbytes; - struct ws_win *win; - char *name, *resp, *s; - unsigned long len; + int i; + struct workspace *ws; + FILE *lfile; - DNPRINTF(SWM_D_MISC, "search_do_resp:\n"); + DNPRINTF(SWM_D_MISC, "search_workspace\n"); - search_resp = 0; - searchpid = 0; + if (r == NULL) + return; - if ((resp = calloc(1, MAX_RESP_LEN + 1)) == NULL) { - fprintf(stderr, "search: calloc\n"); + search_r = r; + search_resp_action = SWM_SEARCH_SEARCH_WORKSPACE; + + spawn_select(r, args, "search", &searchpid); + + if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL) return; + + for (i = 0; i < SWM_WS_MAX; i++) { + ws = &r->s->ws[i]; + if (ws == NULL) + continue; + fprintf(lfile, "%d%s%s\n", ws->idx + 1, + (ws->name ? ":" : ""), (ws->name ? ws->name : "")); } - rbytes = read(select_resp_pipe[0], resp, MAX_RESP_LEN); - if (rbytes <= 0) { - fprintf(stderr, "search: read error: %s\n", strerror(errno)); - goto done; + fclose(lfile); +} + +void +search_win_cleanup(void) +{ + struct search_window *sw = NULL; + + while ((sw = TAILQ_FIRST(&search_wl)) != NULL) { + XDestroyWindow(display, sw->indicator); + XFreeGC(display, sw->gc); + TAILQ_REMOVE(&search_wl, sw, entry); + free(sw); } - resp[rbytes] = '\0'; - len = strlen(resp); +} + +void +search_win(struct swm_region *r, union arg *args) +{ + struct ws_win *win = NULL; + struct search_window *sw = NULL; + Window w; + XGCValues gcv; + int i; + char s[8]; + FILE *lfile; + size_t len; + XRectangle ibox, lbox; + + DNPRINTF(SWM_D_MISC, "search_win\n"); + + search_r = r; + search_resp_action = SWM_SEARCH_SEARCH_WINDOW; + + spawn_select(r, args, "search", &searchpid); + + if ((lfile = fdopen(select_list_pipe[1], "w")) == NULL) + return; + + TAILQ_INIT(&search_wl); + + i = 1; + TAILQ_FOREACH(win, &r->ws->winlist, entry) { + if (win->iconic == 1) + continue; + + sw = calloc(1, sizeof(struct search_window)); + if (sw == NULL) { + warn("search_win: calloc"); + fclose(lfile); + search_win_cleanup(); + return; + } + sw->idx = i; + sw->win = win; + + snprintf(s, sizeof s, "%d", i); + len = strlen(s); + + XmbTextExtents(bar_fs, s, len, &ibox, &lbox); + + w = XCreateSimpleWindow(display, + win->id, 0, 0,lbox.width + 4, + bar_fs_extents->max_logical_extent.height, 1, + r->s->c[SWM_S_COLOR_UNFOCUS].color, + r->s->c[SWM_S_COLOR_FOCUS].color); + + sw->indicator = w; + TAILQ_INSERT_TAIL(&search_wl, sw, entry); + + sw->gc = XCreateGC(display, w, 0, &gcv); + XMapRaised(display, w); + XSetForeground(display, sw->gc, r->s->c[SWM_S_COLOR_BAR].color); + + DRAWSTRING(display, w, bar_fs, sw->gc, 2, + (bar_fs_extents->max_logical_extent.height - + lbox.height) / 2 - lbox.y, s, len); + + fprintf(lfile, "%d\n", i); + i++; + } + + fclose(lfile); +} + +void +search_resp_uniconify(char *resp, unsigned long len) +{ + unsigned char *name; + struct ws_win *win; + char *s; + + DNPRINTF(SWM_D_MISC, "search_resp_uniconify: resp: %s\n", resp); - DNPRINTF(SWM_D_MISC, "search_do_resp: resp %s\n", resp); TAILQ_FOREACH(win, &search_r->ws->winlist, entry) { if (win->iconic == 0) continue; - name = get_win_name(display, win->id, a_wmname, a_string, &len); + name = get_win_name(win->id); if (name == NULL) continue; if (asprintf(&s, "%s.%lu", name, win->id) == -1) { @@ -3030,14 +3562,159 @@ search_do_resp(void) } free(s); } +} + +void +search_resp_name_workspace(char *resp, unsigned long len) +{ + struct workspace *ws; + + DNPRINTF(SWM_D_MISC, "search_resp_name_workspace: resp: %s\n", resp); + + if (search_r->ws == NULL) + return; + ws = search_r->ws; + + if (ws->name) { + free(search_r->ws->name); + search_r->ws->name = NULL; + } + + if (len > 1) { + ws->name = strdup(resp); + if (ws->name == NULL) { + DNPRINTF(SWM_D_MISC, "search_resp_name_workspace: " + "strdup: %s", strerror(errno)); + return; + } + } +} + +void +search_resp_search_workspace(char *resp, unsigned long len) +{ + char *p, *q; + int ws_idx; + const char *errstr; + union arg a; + + DNPRINTF(SWM_D_MISC, "search_resp_search_workspace: resp: %s\n", resp); + + q = strdup(resp); + if (!q) { + DNPRINTF(SWM_D_MISC, "search_resp_search_workspace: strdup: %s", + strerror(errno)); + return; + } + p = strchr(q, ':'); + if (p != NULL) + *p = '\0'; + ws_idx = strtonum(q, 1, SWM_WS_MAX, &errstr); + if (errstr) { + DNPRINTF(SWM_D_MISC, "workspace idx is %s: %s", + errstr, q); + free(q); + return; + } + free(q); + a.id = ws_idx - 1; + switchws(search_r, &a); +} + +void +search_resp_search_window(char *resp, unsigned long len) +{ + char *s; + int idx; + const char *errstr; + struct search_window *sw; + + DNPRINTF(SWM_D_MISC, "search_resp_search_window: resp: %s\n", resp); + + s = strdup(resp); + if (!s) { + DNPRINTF(SWM_D_MISC, "search_resp_search_window: strdup: %s", + strerror(errno)); + return; + } + + idx = strtonum(s, 1, INT_MAX, &errstr); + if (errstr) { + DNPRINTF(SWM_D_MISC, "window idx is %s: %s", + errstr, s); + free(s); + return; + } + free(s); + + TAILQ_FOREACH(sw, &search_wl, entry) + if (idx == sw->idx) { + focus_win(sw->win); + break; + } +} + +#define MAX_RESP_LEN 1024 + +void +search_do_resp(void) +{ + ssize_t rbytes; + char *resp; + unsigned long len; + + DNPRINTF(SWM_D_MISC, "search_do_resp:\n"); + + search_resp = 0; + searchpid = 0; + + if ((resp = calloc(1, MAX_RESP_LEN + 1)) == NULL) { + warn("search: calloc"); + goto done; + } + + rbytes = read(select_resp_pipe[0], resp, MAX_RESP_LEN); + if (rbytes <= 0) { + warn("search: read error"); + goto done; + } + resp[rbytes] = '\0'; + + /* XXX: + * Older versions of dmenu (Atleast pre 4.4.1) do not send a + * newline, so work around that by sanitizing the resp now. + */ + resp[strcspn(resp, "\n")] = '\0'; + len = strlen(resp); + + switch (search_resp_action) { + case SWM_SEARCH_UNICONIFY: + search_resp_uniconify(resp, len); + break; + case SWM_SEARCH_NAME_WORKSPACE: + search_resp_name_workspace(resp, len); + break; + case SWM_SEARCH_SEARCH_WORKSPACE: + search_resp_search_workspace(resp, len); + break; + case SWM_SEARCH_SEARCH_WINDOW: + search_resp_search_window(resp, len); + break; + } + done: + if (search_resp_action == SWM_SEARCH_SEARCH_WINDOW) + search_win_cleanup(); + + search_resp_action = SWM_SEARCH_NONE; + close(select_resp_pipe[0]); free(resp); } void wkill(struct swm_region *r, union arg *args) { - DNPRINTF(SWM_D_MISC, "wkill %d\n", args->id); + DNPRINTF(SWM_D_MISC, "wkill: id: %d\n", args->id); if (r->ws->focus == NULL) return; @@ -3076,10 +3753,10 @@ floating_toggle_win(struct ws_win *win) } else { if (win->g_floatvalid) { /* refloat at last floating relative position */ - win->g.x = win->g_float.x - win->rg_float.x + r->g.x; - win->g.y = win->g_float.y - win->rg_float.y + r->g.y; - win->g.w = win->g_float.w; - win->g.h = win->g_float.h; + X(win) = win->g_float.x - win->rg_float.x + X(r); + Y(win) = win->g_float.y - win->rg_float.y + Y(r); + WIDTH(win) = win->g_float.w; + HEIGHT(win) = win->g_float.h; } win->floating = 1; } @@ -3102,6 +3779,8 @@ floating_toggle(struct swm_region *r, union arg *args) _NET_WM_STATE_TOGGLE); stack(); + if (focus_mode == SWM_FOCUS_DEFAULT) + drain_enter_notify(); if (win == win->ws->focus) { a.id = SWM_ARG_ID_FOCUSCUR; @@ -3110,42 +3789,87 @@ floating_toggle(struct swm_region *r, union arg *args) } void -resize_window(struct ws_win *win, int center) +constrain_window(struct ws_win *win, struct swm_region *r, int resizable) +{ + if (X(win) + WIDTH(win) > X(r) + WIDTH(r) - border_width) { + if (resizable) + WIDTH(win) = X(r) + WIDTH(r) - X(win) - border_width; + else + X(win) = X(r) + WIDTH(r) - WIDTH(win) - border_width; + } + + if (X(win) < X(r) - border_width) { + if (resizable) + WIDTH(win) -= X(r) - X(win) - border_width; + + X(win) = X(r) - border_width; + } + + if (Y(win) + HEIGHT(win) > Y(r) + HEIGHT(r) - border_width) { + if (resizable) + HEIGHT(win) = Y(r) + HEIGHT(r) - Y(win) - border_width; + else + Y(win) = Y(r) + HEIGHT(r) - HEIGHT(win) - border_width; + } + + if (Y(win) < Y(r) - border_width) { + if (resizable) + HEIGHT(win) -= Y(r) - Y(win) - border_width; + + Y(win) = Y(r) - border_width; + } + + if (WIDTH(win) < 1) + WIDTH(win) = 1; + if (HEIGHT(win) < 1) + HEIGHT(win) = 1; +} + +void +update_window(struct ws_win *win) { unsigned int mask; XWindowChanges wc; - struct swm_region *r; - r = root_to_region(win->wa.root); bzero(&wc, sizeof wc); - mask = CWBorderWidth | CWWidth | CWHeight; + mask = CWBorderWidth | CWWidth | CWHeight | CWX | CWY; wc.border_width = border_width; - wc.width = win->g.w; - wc.height = win->g.h; - if (center == SWM_ARG_ID_CENTER) { - wc.x = (WIDTH(r) - win->g.w) / 2 - border_width; - wc.y = (HEIGHT(r) - win->g.h) / 2 - border_width; - mask |= CWX | CWY; - } + wc.x = X(win); + wc.y = Y(win); + wc.width = WIDTH(win); + wc.height = HEIGHT(win); - DNPRINTF(SWM_D_STACK, "resize_window: win %lu x %d y %d w %d h %d\n", - win->id, wc.x, wc.y, wc.width, wc.height); + DNPRINTF(SWM_D_MISC, "update_window: window: 0x%lx, (x,y) w x h: " + "(%d,%d) %d x %d\n", win->id, wc.x, wc.y, wc.width, wc.height); XConfigureWindow(display, win->id, mask, &wc); } +#define SWM_RESIZE_STEPS (50) + void resize(struct ws_win *win, union arg *args) { XEvent ev; Time time = 0; - struct swm_region *r = win->ws->r; - int relx, rely; - union arg a; + struct swm_region *r = NULL; + int resize_step = 0; + Window rr, cr; + int x, y, wx, wy; + unsigned int mask; + struct swm_geometry g; + int top = 0, left = 0; + int dx, dy; + Cursor cursor; + unsigned int shape; /* cursor style */ + if (win == NULL) + return; + r = win->ws->r; - DNPRINTF(SWM_D_MOUSE, "resize: win %lu floating %d trans %lu\n", - win->id, win->floating, win->transient); + DNPRINTF(SWM_D_MOUSE, "resize: window: 0x%lx, floating: %s, " + "transient: 0x%lx\n", win->id, YESNO(win->floating), + win->transient); if (!(win->transient != 0 || win->floating != 0)) return; @@ -3157,104 +3881,177 @@ resize(struct ws_win *win, union arg *args) win->manual = 1; ewmh_update_win_state(win, ewmh[_SWM_WM_STATE_MANUAL].atom, _NET_WM_STATE_ADD); - /* raise the window = move to last in window list */ - a.id = SWM_ARG_ID_MOVELAST; - swapwin(r, &a); + stack(); - if (XGrabPointer(display, win->id, False, MOUSEMASK, GrabModeAsync, - GrabModeAsync, None, None /* cursor */, CurrentTime) != GrabSuccess) + switch (args->id) { + case SWM_ARG_ID_WIDTHSHRINK: + WIDTH(win) -= SWM_RESIZE_STEPS; + resize_step = 1; + break; + case SWM_ARG_ID_WIDTHGROW: + WIDTH(win) += SWM_RESIZE_STEPS; + resize_step = 1; + break; + case SWM_ARG_ID_HEIGHTSHRINK: + HEIGHT(win) -= SWM_RESIZE_STEPS; + resize_step = 1; + break; + case SWM_ARG_ID_HEIGHTGROW: + HEIGHT(win) += SWM_RESIZE_STEPS; + resize_step = 1; + break; + default: + break; + } + if (resize_step) { + constrain_window(win, r, 1); + update_window(win); + store_float_geom(win,r); return; + } - /* place pointer at bottom left corner or nearest point inside r */ - if ( win->g.x + win->g.w < r->g.x + r->g.w - 1) - relx = win->g.w - 1; - else - relx = r->g.x + r->g.w - win->g.x - 1; + if (focus_mode == SWM_FOCUS_DEFAULT) + drain_enter_notify(); - if ( win->g.y + win->g.h < r->g.y + r->g.h - 1) - rely = win->g.h - 1; + /* get cursor offset from window root */ + if (!XQueryPointer(display, win->id, &rr, &cr, &x, &y, &wx, &wy, &mask)) + return; + + g = win->g; + + if (wx < WIDTH(win) / 2) + left = 1; + + if (wy < HEIGHT(win) / 2) + top = 1; + + if (args->id == SWM_ARG_ID_CENTER) + shape = XC_sizing; + else if (top) + shape = (left) ? XC_top_left_corner : XC_top_right_corner; else - rely = r->g.y + r->g.h - win->g.y - 1; + shape = (left) ? XC_bottom_left_corner : XC_bottom_right_corner; + + cursor = XCreateFontCursor(display, shape); + + if (XGrabPointer(display, win->id, False, MOUSEMASK, GrabModeAsync, + GrabModeAsync, None, cursor, CurrentTime) != GrabSuccess) { + XFreeCursor(display, cursor); + return; + } - XWarpPointer(display, None, win->id, 0, 0, 0, 0, relx, rely); do { XMaskEvent(display, MOUSEMASK | ExposureMask | SubstructureRedirectMask, &ev); - switch(ev.type) { + switch (ev.type) { case ConfigureRequest: case Expose: case MapRequest: handler[ev.type](&ev); break; case MotionNotify: - /* do not allow resize outside of region */ - if ( ev.xmotion.x_root < r->g.x || - ev.xmotion.x_root > r->g.x + r->g.w - 1 || - ev.xmotion.y_root < r->g.y || - ev.xmotion.y_root > r->g.y + r->g.h - 1) - continue; + /* cursor offset/delta from start of the operation */ + dx = ev.xmotion.x_root - x; + dy = ev.xmotion.y_root - y; + + /* vertical */ + if (top) + dy = -dy; + + if (args->id == SWM_ARG_ID_CENTER) { + if (g.h / 2 + dy < 1) + dy = 1 - g.h / 2; + + Y(win) = g.y - dy; + HEIGHT(win) = g.h + 2 * dy; + } else { + if (g.h + dy < 1) + dy = 1 - g.h; + + if (top) + Y(win) = g.y - dy; + + HEIGHT(win) = g.h + dy; + } - if (ev.xmotion.x <= 1) - ev.xmotion.x = 1; - if (ev.xmotion.y <= 1) - ev.xmotion.y = 1; - win->g.w = ev.xmotion.x + 1; - win->g.h = ev.xmotion.y + 1; + /* horizontal */ + if (left) + dx = -dx; + + if (args->id == SWM_ARG_ID_CENTER) { + if (g.w / 2 + dx < 1) + dx = 1 - g.w / 2; + + X(win) = g.x - dx; + WIDTH(win) = g.w + 2 * dx; + } else { + if (g.w + dx < 1) + dx = 1 - g.w; + + if (left) + X(win) = g.x - dx; + + WIDTH(win) = g.w + dx; + } + + constrain_window(win, r, 1); /* not free, don't sync more than 120 times / second */ if ((ev.xmotion.time - time) > (1000 / 120) ) { time = ev.xmotion.time; XSync(display, False); - resize_window(win, args->id); + update_window(win); } break; } } while (ev.type != ButtonRelease); if (time) { XSync(display, False); - resize_window(win, args->id); + update_window(win); } store_float_geom(win,r); - XWarpPointer(display, None, win->id, 0, 0, 0, 0, win->g.w - 1, - win->g.h - 1); XUngrabPointer(display, CurrentTime); + XFreeCursor(display, cursor); /* drain events */ - while (XCheckMaskEvent(display, EnterWindowMask, &ev)); + drain_enter_notify(); } void -move_window(struct ws_win *win) +resize_step(struct swm_region *r, union arg *args) { - unsigned int mask; - XWindowChanges wc; - struct swm_region *r; + struct ws_win *win = NULL; - r = root_to_region(win->wa.root); - bzero(&wc, sizeof wc); - mask = CWX | CWY; - wc.x = win->g.x; - wc.y = win->g.y; - wc.border_width = border_width; - - DNPRINTF(SWM_D_STACK, "move_window: win %lu x %d y %d w %d h %d\n", - win->id, wc.x, wc.y, wc.width, wc.height); + if (r && r->ws && r->ws->focus) + win = r->ws->focus; + else + return; - XConfigureWindow(display, win->id, mask, &wc); + resize(win, args); } +#define SWM_MOVE_STEPS (50) + void move(struct ws_win *win, union arg *args) { XEvent ev; Time time = 0; - struct swm_region *r = win->ws->r; - union arg a; + int move_step = 0; + struct swm_region *r = NULL; - DNPRINTF(SWM_D_MOUSE, "move: win %lu floating %d trans %lu\n", - win->id, win->floating, win->transient); + Window rr, cr; + int x, y, wx, wy; + unsigned int mask; + + if (win == NULL) + return; + r = win->ws->r; + + DNPRINTF(SWM_D_MOUSE, "move: window: 0x%lx, floating: %s, transient: " + "0x%lx\n", win->id, YESNO(win->floating), win->transient); /* in max_stack mode should only move transients */ if (win->ws->cur_layout == &layouts[SWM_MAX_STACK] && !win->transient) @@ -3262,66 +4059,108 @@ move(struct ws_win *win, union arg *args) win->manual = 1; if (win->floating == 0 && !win->transient) { - win->floating = 1; + store_float_geom(win,r); ewmh_update_win_state(win, ewmh[_NET_WM_STATE_ABOVE].atom, _NET_WM_STATE_ADD); } ewmh_update_win_state(win, ewmh[_SWM_WM_STATE_MANUAL].atom, _NET_WM_STATE_ADD); - /* raise the window = move to last in window list */ - a.id = SWM_ARG_ID_MOVELAST; - swapwin(r, &a); stack(); + move_step = 0; + switch (args->id) { + case SWM_ARG_ID_MOVELEFT: + X(win) -= (SWM_MOVE_STEPS - border_width); + move_step = 1; + break; + case SWM_ARG_ID_MOVERIGHT: + X(win) += (SWM_MOVE_STEPS - border_width); + move_step = 1; + break; + case SWM_ARG_ID_MOVEUP: + Y(win) -= (SWM_MOVE_STEPS - border_width); + move_step = 1; + break; + case SWM_ARG_ID_MOVEDOWN: + Y(win) += (SWM_MOVE_STEPS - border_width); + move_step = 1; + break; + default: + break; + } + if (move_step) { + constrain_window(win, r, 0); + update_window(win); + store_float_geom(win, r); + return; + } + if (XGrabPointer(display, win->id, False, MOUSEMASK, GrabModeAsync, - GrabModeAsync, None, None /* cursor */, CurrentTime) != GrabSuccess) + GrabModeAsync, None, XCreateFontCursor(display, XC_fleur), + CurrentTime) != GrabSuccess) return; - XWarpPointer(display, None, win->id, 0, 0, 0, 0, 0, 0); + + /* get cursor offset from window root */ + if (!XQueryPointer(display, win->id, &rr, &cr, &x, &y, &wx, &wy, &mask)) + return; + do { XMaskEvent(display, MOUSEMASK | ExposureMask | SubstructureRedirectMask, &ev); - switch(ev.type) { + switch (ev.type) { case ConfigureRequest: case Expose: case MapRequest: handler[ev.type](&ev); break; case MotionNotify: - /* don't allow to move window origin out of region */ - if ( ev.xmotion.x_root < r->g.x || - ev.xmotion.x_root > r->g.x + r->g.w - 1 || - ev.xmotion.y_root < r->g.y || - ev.xmotion.y_root > r->g.y + r->g.h - 1) - continue; + X(win) = ev.xmotion.x_root - wx - border_width; + Y(win) = ev.xmotion.y_root - wy - border_width; - win->g.x = ev.xmotion.x_root - border_width; - win->g.y = ev.xmotion.y_root - border_width; + constrain_window(win, r, 0); /* not free, don't sync more than 120 times / second */ if ((ev.xmotion.time - time) > (1000 / 120) ) { time = ev.xmotion.time; XSync(display, False); - move_window(win); + update_window(win); } break; } } while (ev.type != ButtonRelease); if (time) { XSync(display, False); - move_window(win); + update_window(win); } store_float_geom(win,r); - XWarpPointer(display, None, win->id, 0, 0, 0, 0, 0, 0); XUngrabPointer(display, CurrentTime); /* drain events */ - while (XCheckMaskEvent(display, EnterWindowMask, &ev)); + drain_enter_notify(); } +void +move_step(struct swm_region *r, union arg *args) +{ + struct ws_win *win = NULL; + + if (r && r->ws && r->ws->focus) + win = r->ws->focus; + else + return; + + if (!(win->transient != 0 || win->floating != 0)) + return; + + move(win, args); +} + + /* user/key callable function IDs */ enum keyfuncid { kf_cycle_layout, + kf_flip_layout, kf_stack_reset, kf_master_shrink, kf_master_grow, @@ -3351,6 +4190,8 @@ enum keyfuncid { kf_ws_10, kf_ws_next, kf_ws_prev, + kf_ws_next_all, + kf_ws_prev_all, kf_ws_prior, kf_screen_next, kf_screen_prev, @@ -3376,6 +4217,19 @@ enum keyfuncid { kf_spawn_custom, kf_iconify, kf_uniconify, + kf_raise_toggle, + kf_button2, + kf_width_shrink, + kf_width_grow, + kf_height_shrink, + kf_height_grow, + kf_move_left, + kf_move_right, + kf_move_up, + kf_move_down, + kf_name_workspace, + kf_search_workspace, + kf_search_win, kf_dumpwins, /* MUST BE LAST */ kf_invalid }; @@ -3398,6 +4252,7 @@ struct keyfunc { } keyfuncs[kf_invalid + 1] = { /* name function argument */ { "cycle_layout", cycle_layout, {0} }, + { "flip_layout", stack_config, {.id = SWM_ARG_ID_FLIPLAYOUT} }, { "stack_reset", stack_config, {.id = SWM_ARG_ID_STACKRESET} }, { "master_shrink", stack_config, {.id = SWM_ARG_ID_MASTERSHRINK} }, { "master_grow", stack_config, {.id = SWM_ARG_ID_MASTERGROW} }, @@ -3427,6 +4282,8 @@ struct keyfunc { { "ws_10", switchws, {.id = 9} }, { "ws_next", cyclews, {.id = SWM_ARG_ID_CYCLEWS_UP} }, { "ws_prev", cyclews, {.id = SWM_ARG_ID_CYCLEWS_DOWN} }, + { "ws_next_all", cyclews, {.id = SWM_ARG_ID_CYCLEWS_UP_ALL} }, + { "ws_prev_all", cyclews, {.id = SWM_ARG_ID_CYCLEWS_DOWN_ALL} }, { "ws_prior", priorws, {0} }, { "screen_next", cyclescr, {.id = SWM_ARG_ID_CYCLESC_UP} }, { "screen_prev", cyclescr, {.id = SWM_ARG_ID_CYCLESC_DOWN} }, @@ -3452,17 +4309,31 @@ struct keyfunc { { "spawn_custom", dummykeyfunc, {0} }, { "iconify", iconify, {0} }, { "uniconify", uniconify, {0} }, + { "raise_toggle", raise_toggle, {0} }, + { "button2", pressbutton, {2} }, + { "width_shrink", resize_step, {.id = SWM_ARG_ID_WIDTHSHRINK} }, + { "width_grow", resize_step, {.id = SWM_ARG_ID_WIDTHGROW} }, + { "height_shrink", resize_step, {.id = SWM_ARG_ID_HEIGHTSHRINK} }, + { "height_grow", resize_step, {.id = SWM_ARG_ID_HEIGHTGROW} }, + { "move_left", move_step, {.id = SWM_ARG_ID_MOVELEFT} }, + { "move_right", move_step, {.id = SWM_ARG_ID_MOVERIGHT} }, + { "move_up", move_step, {.id = SWM_ARG_ID_MOVEUP} }, + { "move_down", move_step, {.id = SWM_ARG_ID_MOVEDOWN} }, + { "name_workspace", name_workspace, {0} }, + { "search_workspace", search_workspace, {0} }, + { "search_win", search_win, {0} }, { "dumpwins", dumpwins, {0} }, /* MUST BE LAST */ { "invalid key func", NULL, {0} }, }; struct key { + TAILQ_ENTRY(key) entry; unsigned int mod; KeySym keysym; enum keyfuncid funcid; char *spawn_name; }; -int keys_size = 0, keys_length = 0; -struct key *keys = NULL; +TAILQ_HEAD(key_list, key); +struct key_list keys = TAILQ_HEAD_INITIALIZER(keys); /* mouse */ enum { client_click, root_click }; @@ -3483,13 +4354,14 @@ void update_modkey(unsigned int mod) { int i; + struct key *kp; mod_key = mod; - for (i = 0; i < keys_length; i++) - if (keys[i].mod & ShiftMask) - keys[i].mod = mod | ShiftMask; + TAILQ_FOREACH(kp, &keys, entry) + if (kp->mod & ShiftMask) + kp->mod = mod | ShiftMask; else - keys[i].mod = mod; + kp->mod = mod; for (i = 0; i < LENGTH(buttons); i++) if (buttons[i].mask & ShiftMask) @@ -3500,13 +4372,13 @@ update_modkey(unsigned int mod) /* spawn */ struct spawn_prog { + TAILQ_ENTRY(spawn_prog) entry; char *name; int argc; char **argv; }; - -int spawns_size = 0, spawns_length = 0; -struct spawn_prog *spawns = NULL; +TAILQ_HEAD(spawn_list, spawn_prog); +struct spawn_list spawns = TAILQ_HEAD_INITIALIZER(spawns); int spawn_expand(struct swm_region *r, union arg *args, char *spawn_name, @@ -3516,16 +4388,15 @@ spawn_expand(struct swm_region *r, union arg *args, char *spawn_name, int i; char *ap, **real_args; - DNPRINTF(SWM_D_SPAWN, "spawn_expand %s\n", spawn_name); + DNPRINTF(SWM_D_SPAWN, "spawn_expand: %s\n", spawn_name); /* find program */ - for (i = 0; i < spawns_length; i++) { - if (!strcasecmp(spawn_name, spawns[i].name)) - prog = &spawns[i]; + TAILQ_FOREACH(prog, &spawns, entry) { + if (!strcasecmp(spawn_name, prog->name)) + break; } if (prog == NULL) { - fprintf(stderr, "spawn_custom: program %s not found\n", - spawn_name); + warnx("spawn_custom: program %s not found", spawn_name); return (-1); } @@ -3536,7 +4407,7 @@ spawn_expand(struct swm_region *r, union arg *args, char *spawn_name, /* expand spawn_args into real_args */ for (i = 0; i < prog->argc; i++) { ap = prog->argv[i]; - DNPRINTF(SWM_D_SPAWN, "spawn_custom: raw arg = %s\n", ap); + DNPRINTF(SWM_D_SPAWN, "spawn_custom: raw arg: %s\n", ap); if (!strcasecmp(ap, "$bar_border")) { if ((real_args[i] = strdup(r->s->c[SWM_S_COLOR_BAR_BORDER].name)) @@ -3548,7 +4419,7 @@ spawn_expand(struct swm_region *r, union arg *args, char *spawn_name, == NULL) err(1, "spawn_custom bar color"); } else if (!strcasecmp(ap, "$bar_font")) { - if ((real_args[i] = strdup(bar_fonts[bar_fidx])) + if ((real_args[i] = strdup(bar_fonts)) == NULL) err(1, "spawn_custom bar fonts"); } else if (!strcasecmp(ap, "$bar_font_color")) { @@ -3571,17 +4442,15 @@ spawn_expand(struct swm_region *r, union arg *args, char *spawn_name, if ((real_args[i] = strdup(ap)) == NULL) err(1, "spawn_custom strdup(ap)"); } - DNPRINTF(SWM_D_SPAWN, "spawn_custom: cooked arg = %s\n", + DNPRINTF(SWM_D_SPAWN, "spawn_custom: cooked arg: %s\n", real_args[i]); } #ifdef SWM_DEBUG - if ((swm_debug & SWM_D_SPAWN) != 0) { - fprintf(stderr, "spawn_custom: result = "); - for (i = 0; i < prog->argc; i++) - fprintf(stderr, "\"%s\" ", real_args[i]); - fprintf(stderr, "\n"); - } + DNPRINTF(SWM_D_SPAWN, "spawn_custom: result: "); + for (i = 0; i < prog->argc; i++) + DNPRINTF(SWM_D_SPAWN, "\"%s\" ", real_args[i]); + DNPRINTF(SWM_D_SPAWN, "\n"); #endif *ret_args = real_args; return (prog->argc); @@ -3598,7 +4467,7 @@ spawn_custom(struct swm_region *r, union arg *args, char *spawn_name) return; a.argv = real_args; if (fork() == 0) - spawn(r, &a, 1); + spawn(r->ws->idx, &a, 1); for (i = 0; i < spawn_argc; i++) free(real_args[i]); @@ -3629,12 +4498,12 @@ spawn_select(struct swm_region *r, union arg *args, char *spawn_name, int *pid) break; case 0: /* child */ if (dup2(select_list_pipe[0], 0) == -1) - errx(1, "dup2"); + err(1, "dup2"); if (dup2(select_resp_pipe[1], 1) == -1) - errx(1, "dup2"); + err(1, "dup2"); close(select_list_pipe[1]); close(select_resp_pipe[0]); - spawn(r, &a, 0); + spawn(r->ws->idx, &a, 0); break; default: /* parent */ close(select_list_pipe[0]); @@ -3648,122 +4517,102 @@ spawn_select(struct swm_region *r, union arg *args, char *spawn_name, int *pid) } void -setspawn(struct spawn_prog *prog) +spawn_insert(char *name, char *args) { - int i, j; + char *arg, *cp, *ptr; + struct spawn_prog *sp; - if (prog == NULL || prog->name == NULL) - return; + DNPRINTF(SWM_D_SPAWN, "spawn_insert: %s\n", name); - /* find existing */ - for (i = 0; i < spawns_length; i++) { - if (!strcmp(spawns[i].name, prog->name)) { - /* found */ - if (prog->argv == NULL) { - /* delete */ - DNPRINTF(SWM_D_SPAWN, - "setspawn: delete #%d %s\n", - i, spawns[i].name); - free(spawns[i].name); - for (j = 0; j < spawns[i].argc; j++) - free(spawns[i].argv[j]); - free(spawns[i].argv); - j = spawns_length - 1; - if (i < j) - spawns[i] = spawns[j]; - spawns_length--; - free(prog->name); - } else { - /* replace */ - DNPRINTF(SWM_D_SPAWN, - "setspawn: replace #%d %s\n", - i, spawns[i].name); - free(spawns[i].name); - for (j = 0; j < spawns[i].argc; j++) - free(spawns[i].argv[j]); - free(spawns[i].argv); - spawns[i] = *prog; - } - /* found case handled */ - free(prog); - return; - } + if ((sp = calloc(1, sizeof *sp)) == NULL) + err(1, "spawn_insert: malloc"); + if ((sp->name = strdup(name)) == NULL) + err(1, "spawn_insert: strdup"); + + /* convert the arguments to an argument list */ + if ((ptr = cp = strdup(args)) == NULL) + err(1, "spawn_insert: strdup"); + while ((arg = strsep(&ptr, " \t")) != NULL) { + /* empty field; skip it */ + if (*arg == '\0') + continue; + + sp->argc++; + if ((sp->argv = realloc(sp->argv, sp->argc * + sizeof *sp->argv)) == NULL) + err(1, "spawn_insert: realloc"); + if ((sp->argv[sp->argc - 1] = strdup(arg)) == NULL) + err(1, "spawn_insert: strdup"); } + free(cp); + + TAILQ_INSERT_TAIL(&spawns, sp, entry); + DNPRINTF(SWM_D_SPAWN, "spawn_insert: leave\n"); +} + +void +spawn_remove(struct spawn_prog *sp) +{ + int i; + + DNPRINTF(SWM_D_SPAWN, "spawn_remove: %s\n", sp->name); - if (prog->argv == NULL) { - fprintf(stderr, - "error: setspawn: cannot find program %s", prog->name); - free(prog); + TAILQ_REMOVE(&spawns, sp, entry); + for (i = 0; i < sp->argc; i++) + free(sp->argv[i]); + free(sp->argv); + free(sp->name); + free(sp); + + DNPRINTF(SWM_D_SPAWN, "spawn_remove: leave\n"); +} + +void +spawn_replace(struct spawn_prog *sp, char *name, char *args) +{ + DNPRINTF(SWM_D_SPAWN, "spawn_replace: %s [%s]\n", sp->name, name); + + spawn_remove(sp); + spawn_insert(name, args); + + DNPRINTF(SWM_D_SPAWN, "spawn_replace: leave\n"); +} + +void +setspawn(char *name, char *args) +{ + struct spawn_prog *sp; + + DNPRINTF(SWM_D_SPAWN, "setspawn: %s\n", name); + + if (name == NULL) return; - } - /* not found: add */ - if (spawns_size == 0 || spawns == NULL) { - spawns_size = 4; - DNPRINTF(SWM_D_SPAWN, "setspawn: init list %d\n", spawns_size); - spawns = malloc((size_t)spawns_size * - sizeof(struct spawn_prog)); - if (spawns == NULL) { - fprintf(stderr, "setspawn: malloc failed\n"); - perror(" failed"); - quit(NULL, NULL); - } - } else if (spawns_length == spawns_size) { - spawns_size *= 2; - DNPRINTF(SWM_D_SPAWN, "setspawn: grow list %d\n", spawns_size); - spawns = realloc(spawns, (size_t)spawns_size * - sizeof(struct spawn_prog)); - if (spawns == NULL) { - fprintf(stderr, "setspawn: realloc failed\n"); - perror(" failed"); - quit(NULL, NULL); + TAILQ_FOREACH(sp, &spawns, entry) { + if (!strcmp(sp->name, name)) { + if (*args == '\0') + spawn_remove(sp); + else + spawn_replace(sp, name, args); + DNPRINTF(SWM_D_SPAWN, "setspawn: leave\n"); + return; } } - - if (spawns_length < spawns_size) { - DNPRINTF(SWM_D_SPAWN, "setspawn: add #%d %s\n", - spawns_length, prog->name); - i = spawns_length++; - spawns[i] = *prog; - } else { - fprintf(stderr, "spawns array problem?\n"); - if (spawns == NULL) { - fprintf(stderr, "spawns array is NULL!\n"); - quit(NULL, NULL); - } + if (*args == '\0') { + warnx("error: setspawn: cannot find program: %s", name); + return; } - free(prog); + + spawn_insert(name, args); + DNPRINTF(SWM_D_SPAWN, "setspawn: leave\n"); } int setconfspawn(char *selector, char *value, int flags) { - struct spawn_prog *prog; - char *vp, *cp, *word; - DNPRINTF(SWM_D_SPAWN, "setconfspawn: [%s] [%s]\n", selector, value); - if ((prog = calloc(1, sizeof *prog)) == NULL) - err(1, "setconfspawn: calloc prog"); - prog->name = strdup(selector); - if (prog->name == NULL) - err(1, "setconfspawn prog->name"); - if ((cp = vp = strdup(value)) == NULL) - err(1, "setconfspawn: strdup(value) "); - while ((word = strsep(&cp, " \t")) != NULL) { - DNPRINTF(SWM_D_SPAWN, "setconfspawn: arg [%s]\n", word); - if (cp) - cp += (long)strspn(cp, " \t"); - if (strlen(word) > 0) { - prog->argc++; - prog->argv = realloc(prog->argv, - prog->argc * sizeof(char *)); - if ((prog->argv[prog->argc - 1] = strdup(word)) == NULL) - err(1, "setconfspawn: strdup"); - } - } - free(vp); - setspawn(prog); + setspawn(selector, value); DNPRINTF(SWM_D_SPAWN, "setconfspawn: done\n"); return (0); @@ -3783,13 +4632,20 @@ setup_spawn(void) " -nf $bar_font_color" " -sb $bar_border" " -sf $bar_color", 0); - setconfspawn("uniconify", "dmenu" + setconfspawn("search", "dmenu" " -i" " -fn $bar_font" " -nb $bar_color" " -nf $bar_font_color" " -sb $bar_border" " -sf $bar_color", 0); + setconfspawn("name_workspace", "dmenu" + " -p Workspace" + " -fn $bar_font" + " -nb $bar_color" + " -nf $bar_font_color" + " -sb $bar_border" + " -sf $bar_color", 0); } /* key bindings */ @@ -3855,83 +4711,76 @@ strdupsafe(char *str) } void -setkeybinding(unsigned int mod, KeySym ks, enum keyfuncid kfid, char *spawn_name) +key_insert(unsigned int mod, KeySym ks, enum keyfuncid kfid, char *spawn_name) { - int i, j; + struct key *kp; + + DNPRINTF(SWM_D_KEY, "key_insert: enter %s [%s]\n", + keyfuncs[kfid].name, spawn_name); + + if ((kp = malloc(sizeof *kp)) == NULL) + err(1, "key_insert: malloc"); + + kp->mod = mod; + kp->keysym = ks; + kp->funcid = kfid; + kp->spawn_name = strdupsafe(spawn_name); + TAILQ_INSERT_TAIL(&keys, kp, entry); + + DNPRINTF(SWM_D_KEY, "key_insert: leave\n"); +} + +void +key_remove(struct key *kp) +{ + DNPRINTF(SWM_D_KEY, "key_remove: %s\n", keyfuncs[kp->funcid].name); + + TAILQ_REMOVE(&keys, kp, entry); + free(kp->spawn_name); + free(kp); + + DNPRINTF(SWM_D_KEY, "key_remove: leave\n"); +} + +void +key_replace(struct key *kp, unsigned int mod, KeySym ks, enum keyfuncid kfid, + char *spawn_name) +{ + DNPRINTF(SWM_D_KEY, "key_replace: %s [%s]\n", keyfuncs[kp->funcid].name, + spawn_name); + + key_remove(kp); + key_insert(mod, ks, kfid, spawn_name); + + DNPRINTF(SWM_D_KEY, "key_replace: leave\n"); +} + +void +setkeybinding(unsigned int mod, KeySym ks, enum keyfuncid kfid, + char *spawn_name) +{ + struct key *kp; + DNPRINTF(SWM_D_KEY, "setkeybinding: enter %s [%s]\n", keyfuncs[kfid].name, spawn_name); - /* find existing */ - for (i = 0; i < keys_length; i++) { - if (keys[i].mod == mod && keys[i].keysym == ks) { - if (kfid == kf_invalid) { - /* found: delete */ - DNPRINTF(SWM_D_KEY, - "setkeybinding: delete #%d %s\n", - i, keyfuncs[keys[i].funcid].name); - free(keys[i].spawn_name); - j = keys_length - 1; - if (i < j) - keys[i] = keys[j]; - keys_length--; - DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n"); - return; - } else { - /* found: replace */ - DNPRINTF(SWM_D_KEY, - "setkeybinding: replace #%d %s %s\n", - i, keyfuncs[keys[i].funcid].name, - spawn_name); - free(keys[i].spawn_name); - keys[i].mod = mod; - keys[i].keysym = ks; - keys[i].funcid = kfid; - keys[i].spawn_name = strdupsafe(spawn_name); - DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n"); - return; - } + + TAILQ_FOREACH(kp, &keys, entry) { + if (kp->mod == mod && kp->keysym == ks) { + if (kfid == kf_invalid) + key_remove(kp); + else + key_replace(kp, mod, ks, kfid, spawn_name); + DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n"); + return; } } if (kfid == kf_invalid) { - fprintf(stderr, - "error: setkeybinding: cannot find mod/key combination"); + warnx("error: setkeybinding: cannot find mod/key combination"); DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n"); return; } - /* not found: add */ - if (keys_size == 0 || keys == NULL) { - keys_size = 4; - DNPRINTF(SWM_D_KEY, "setkeybinding: init list %d\n", keys_size); - keys = malloc((size_t)keys_size * sizeof(struct key)); - if (!keys) { - fprintf(stderr, "malloc failed\n"); - perror(" failed"); - quit(NULL, NULL); - } - } else if (keys_length == keys_size) { - keys_size *= 2; - DNPRINTF(SWM_D_KEY, "setkeybinding: grow list %d\n", keys_size); - keys = realloc(keys, (size_t)keys_size * sizeof(struct key)); - if (!keys) { - fprintf(stderr, "realloc failed\n"); - perror(" failed"); - quit(NULL, NULL); - } - } - if (keys_length < keys_size) { - j = keys_length++; - DNPRINTF(SWM_D_KEY, "setkeybinding: add #%d %s %s\n", - j, keyfuncs[kfid].name, spawn_name); - keys[j].mod = mod; - keys[j].keysym = ks; - keys[j].funcid = kfid; - keys[j].spawn_name = strdupsafe(spawn_name); - } else { - fprintf(stderr, "keys array problem?\n"); - if (!keys) { - fprintf(stderr, "keys array problem\n"); - quit(NULL, NULL); - } - } + + key_insert(mod, ks, kfid, spawn_name); DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n"); } @@ -3941,7 +4790,7 @@ setconfbinding(char *selector, char *value, int flags) enum keyfuncid kfid; unsigned int mod; KeySym ks; - int i; + struct spawn_prog *sp; DNPRINTF(SWM_D_KEY, "setconfbinding: enter\n"); if (selector == NULL) { DNPRINTF(SWM_D_KEY, "setconfbinding: unbind %s\n", value); @@ -3966,13 +4815,13 @@ setconfbinding(char *selector, char *value, int flags) } } /* search by custom spawn name */ - for (i = 0; i < spawns_length; i++) { - if (strcasecmp(selector, spawns[i].name) == 0) { + TAILQ_FOREACH(sp, &spawns, entry) { + if (strcasecmp(selector, sp->name) == 0) { DNPRINTF(SWM_D_KEY, "setconfbinding: %s: match\n", selector); if (parsekeys(value, mod_key, &mod, &ks) == 0) { setkeybinding(mod, ks, kf_spawn_custom, - spawns[i].name); + sp->name); return (0); } else return (1); @@ -3986,6 +4835,7 @@ void setup_keys(void) { setkeybinding(MODKEY, XK_space, kf_cycle_layout,NULL); + setkeybinding(MODKEY|ShiftMask, XK_backslash, kf_flip_layout, NULL); setkeybinding(MODKEY|ShiftMask, XK_space, kf_stack_reset, NULL); setkeybinding(MODKEY, XK_h, kf_master_shrink,NULL); setkeybinding(MODKEY, XK_l, kf_master_grow, NULL); @@ -3999,7 +4849,7 @@ setup_keys(void) setkeybinding(MODKEY|ShiftMask, XK_j, kf_swap_next, NULL); setkeybinding(MODKEY|ShiftMask, XK_k, kf_swap_prev, NULL); setkeybinding(MODKEY|ShiftMask, XK_Return, kf_spawn_term, NULL); - setkeybinding(MODKEY, XK_p, kf_spawn_custom, "menu"); + setkeybinding(MODKEY, XK_p, kf_spawn_custom,"menu"); setkeybinding(MODKEY|ShiftMask, XK_q, kf_quit, NULL); setkeybinding(MODKEY, XK_q, kf_restart, NULL); setkeybinding(MODKEY, XK_m, kf_focus_main, NULL); @@ -4015,6 +4865,8 @@ setup_keys(void) setkeybinding(MODKEY, XK_0, kf_ws_10, NULL); setkeybinding(MODKEY, XK_Right, kf_ws_next, NULL); setkeybinding(MODKEY, XK_Left, kf_ws_prev, NULL); + setkeybinding(MODKEY, XK_Up, kf_ws_next_all, NULL); + setkeybinding(MODKEY, XK_Down, kf_ws_prev_all, NULL); setkeybinding(MODKEY, XK_a, kf_ws_prior, NULL); setkeybinding(MODKEY|ShiftMask, XK_Right, kf_screen_next, NULL); setkeybinding(MODKEY|ShiftMask, XK_Left, kf_screen_prev, NULL); @@ -4033,20 +4885,66 @@ setup_keys(void) setkeybinding(MODKEY|ShiftMask, XK_Tab, kf_focus_prev, NULL); setkeybinding(MODKEY|ShiftMask, XK_x, kf_wind_kill, NULL); setkeybinding(MODKEY, XK_x, kf_wind_del, NULL); - setkeybinding(MODKEY, XK_s, kf_spawn_custom, "screenshot_all"); - setkeybinding(MODKEY|ShiftMask, XK_s, kf_spawn_custom, "screenshot_wind"); + setkeybinding(MODKEY, XK_s, kf_spawn_custom,"screenshot_all"); + setkeybinding(MODKEY|ShiftMask, XK_s, kf_spawn_custom,"screenshot_wind"); setkeybinding(MODKEY, XK_t, kf_float_toggle,NULL); setkeybinding(MODKEY|ShiftMask, XK_v, kf_version, NULL); - setkeybinding(MODKEY|ShiftMask, XK_Delete, kf_spawn_custom, "lock"); - setkeybinding(MODKEY|ShiftMask, XK_i, kf_spawn_custom, "initscr"); + setkeybinding(MODKEY|ShiftMask, XK_Delete, kf_spawn_custom,"lock"); + setkeybinding(MODKEY|ShiftMask, XK_i, kf_spawn_custom,"initscr"); setkeybinding(MODKEY, XK_w, kf_iconify, NULL); setkeybinding(MODKEY|ShiftMask, XK_w, kf_uniconify, NULL); + setkeybinding(MODKEY|ShiftMask, XK_r, kf_raise_toggle,NULL); + setkeybinding(MODKEY, XK_v, kf_button2, NULL); + setkeybinding(MODKEY, XK_equal, kf_width_grow, NULL); + setkeybinding(MODKEY, XK_minus, kf_width_shrink,NULL); + setkeybinding(MODKEY|ShiftMask, XK_equal, kf_height_grow, NULL); + setkeybinding(MODKEY|ShiftMask, XK_minus, kf_height_shrink,NULL); + setkeybinding(MODKEY, XK_bracketleft, kf_move_left, NULL); + setkeybinding(MODKEY, XK_bracketright,kf_move_right, NULL); + setkeybinding(MODKEY|ShiftMask, XK_bracketleft, kf_move_up, NULL); + setkeybinding(MODKEY|ShiftMask, XK_bracketright,kf_move_down, NULL); + setkeybinding(MODKEY|ShiftMask, XK_slash, kf_name_workspace,NULL); + setkeybinding(MODKEY, XK_slash, kf_search_workspace,NULL); + setkeybinding(MODKEY, XK_f, kf_search_win, NULL); #ifdef SWM_DEBUG setkeybinding(MODKEY|ShiftMask, XK_d, kf_dumpwins, NULL); #endif } void +clear_keys(void) +{ + struct key *kp_loop, *kp_next; + + kp_loop = TAILQ_FIRST(&keys); + while (kp_loop != NULL) { + kp_next = TAILQ_NEXT(kp_loop, entry); + key_remove(kp_loop); + kp_loop = kp_next; + } +} + +int +setkeymapping(char *selector, char *value, int flags) +{ + char keymapping_file[PATH_MAX]; + DNPRINTF(SWM_D_KEY, "setkeymapping: enter\n"); + if (value[0] == '~') + snprintf(keymapping_file, sizeof keymapping_file, "%s/%s", + pwd->pw_dir, &value[1]); + else + strlcpy(keymapping_file, value, sizeof keymapping_file); + clear_keys(); + /* load new key bindings; if it fails, revert to default bindings */ + if (conf_load(keymapping_file, SWM_CONF_KEYMAPPING)) { + clear_keys(); + setup_keys(); + } + DNPRINTF(SWM_D_KEY, "setkeymapping: leave\n"); + return (0); +} + +void updatenumlockmask(void) { unsigned int i, j; @@ -4058,7 +4956,7 @@ updatenumlockmask(void) for (i = 0; i < 8; i++) for (j = 0; j < modmap->max_keypermod; j++) if (modmap->modifiermap[i * modmap->max_keypermod + j] - == XKeysymToKeycode(display, XK_Num_Lock)) + == XKeysymToKeycode(display, XK_Num_Lock)) numlockmask = (1 << i); XFreeModifiermap(modmap); @@ -4067,10 +4965,11 @@ updatenumlockmask(void) void grabkeys(void) { - unsigned int i, j, k; + unsigned int j, k; KeyCode code; unsigned int modifiers[] = { 0, LockMask, numlockmask, numlockmask | LockMask }; + struct key *kp; DNPRINTF(SWM_D_MISC, "grabkeys\n"); updatenumlockmask(); @@ -4079,11 +4978,11 @@ grabkeys(void) if (TAILQ_EMPTY(&screens[k].rl)) continue; XUngrabKey(display, AnyKey, AnyModifier, screens[k].root); - for (i = 0; i < keys_length; i++) { - if ((code = XKeysymToKeycode(display, keys[i].keysym))) + TAILQ_FOREACH(kp, &keys, entry) { + if ((code = XKeysymToKeycode(display, kp->keysym))) for (j = 0; j < LENGTH(modifiers); j++) XGrabKey(display, code, - keys[i].mod | modifiers[j], + kp->mod | modifiers[j], screens[k].root, True, GrabModeAsync, GrabModeAsync); } @@ -4120,6 +5019,7 @@ const char *quirkname[] = { "ANYWHERE", "XTERM_FONTADJ", "FULLSCREEN", + "FOCUSPREV", }; /* SWM_Q_WS: retain '|' for back compat for now (2009-08-11) */ @@ -4140,7 +5040,8 @@ parsequirks(char *qstr, unsigned long *quirk) cp += (long)strspn(cp, SWM_Q_WS); for (i = 0; i < LENGTH(quirkname); i++) { if (!strncasecmp(name, quirkname[i], SWM_QUIRK_LEN)) { - DNPRINTF(SWM_D_QUIRK, "parsequirks: %s\n", name); + DNPRINTF(SWM_D_QUIRK, + "parsequirks: %s\n", name); if (i == 0) { *quirk = 0; return (0); @@ -4159,78 +5060,78 @@ parsequirks(char *qstr, unsigned long *quirk) } void -setquirk(const char *class, const char *name, const int quirk) +quirk_insert(const char *class, const char *name, unsigned long quirk) { - int i, j; + struct quirk *qp; - /* find existing */ - for (i = 0; i < quirks_length; i++) { - if (!strcmp(quirks[i].class, class) && - !strcmp(quirks[i].name, name)) { - if (!quirk) { - /* found: delete */ - DNPRINTF(SWM_D_QUIRK, - "setquirk: delete #%d %s:%s\n", - i, quirks[i].class, quirks[i].name); - free(quirks[i].class); - free(quirks[i].name); - j = quirks_length - 1; - if (i < j) - quirks[i] = quirks[j]; - quirks_length--; - return; - } else { - /* found: replace */ - DNPRINTF(SWM_D_QUIRK, - "setquirk: replace #%d %s:%s\n", - i, quirks[i].class, quirks[i].name); - free(quirks[i].class); - free(quirks[i].name); - quirks[i].class = strdup(class); - quirks[i].name = strdup(name); - quirks[i].quirk = quirk; - return; - } + DNPRINTF(SWM_D_QUIRK, "quirk_insert: %s:%s [%lu]\n", class, name, + quirk); + + if ((qp = malloc(sizeof *qp)) == NULL) + err(1, "quirk_insert: malloc"); + if ((qp->class = strdup(class)) == NULL) + err(1, "quirk_insert: strdup"); + if ((qp->name = strdup(name)) == NULL) + err(1, "quirk_insert: strdup"); + + qp->quirk = quirk; + TAILQ_INSERT_TAIL(&quirks, qp, entry); + + DNPRINTF(SWM_D_QUIRK, "quirk_insert: leave\n"); +} + +void +quirk_remove(struct quirk *qp) +{ + DNPRINTF(SWM_D_QUIRK, "quirk_remove: %s:%s [%lu]\n", qp->class, + qp->name, qp->quirk); + + TAILQ_REMOVE(&quirks, qp, entry); + free(qp->class); + free(qp->name); + free(qp); + + DNPRINTF(SWM_D_QUIRK, "quirk_remove: leave\n"); +} + +void +quirk_replace(struct quirk *qp, const char *class, const char *name, + unsigned long quirk) +{ + DNPRINTF(SWM_D_QUIRK, "quirk_replace: %s:%s [%lu]\n", qp->class, + qp->name, qp->quirk); + + quirk_remove(qp); + quirk_insert(class, name, quirk); + + DNPRINTF(SWM_D_QUIRK, "quirk_replace: leave\n"); +} + +void +setquirk(const char *class, const char *name, unsigned long quirk) +{ + struct quirk *qp; + + DNPRINTF(SWM_D_QUIRK, "setquirk: enter %s:%s [%lu]\n", class, name, + quirk); + + TAILQ_FOREACH(qp, &quirks, entry) { + if (!strcmp(qp->class, class) && !strcmp(qp->name, name)) { + if (!quirk) + quirk_remove(qp); + else + quirk_replace(qp, class, name, quirk); + DNPRINTF(SWM_D_QUIRK, "setquirk: leave\n"); + return; } } if (!quirk) { - fprintf(stderr, - "error: setquirk: cannot find class/name combination"); + warnx("error: setquirk: cannot find class/name combination"); return; } - /* not found: add */ - if (quirks_size == 0 || quirks == NULL) { - quirks_size = 4; - DNPRINTF(SWM_D_QUIRK, "setquirk: init list %d\n", quirks_size); - quirks = malloc((size_t)quirks_size * sizeof(struct quirk)); - if (!quirks) { - fprintf(stderr, "setquirk: malloc failed\n"); - perror(" failed"); - quit(NULL, NULL); - } - } else if (quirks_length == quirks_size) { - quirks_size *= 2; - DNPRINTF(SWM_D_QUIRK, "setquirk: grow list %d\n", quirks_size); - quirks = realloc(quirks, (size_t)quirks_size * sizeof(struct quirk)); - if (!quirks) { - fprintf(stderr, "setquirk: realloc failed\n"); - perror(" failed"); - quit(NULL, NULL); - } - } - if (quirks_length < quirks_size) { - DNPRINTF(SWM_D_QUIRK, "setquirk: add %d\n", quirks_length); - j = quirks_length++; - quirks[j].class = strdup(class); - quirks[j].name = strdup(name); - quirks[j].quirk = quirk; - } else { - fprintf(stderr, "quirks array problem?\n"); - if (!quirks) { - fprintf(stderr, "quirks array problem!\n"); - quit(NULL, NULL); - } - } + + quirk_insert(class, name, quirk); + DNPRINTF(SWM_D_QUIRK, "setquirk: leave\n"); } int @@ -4254,7 +5155,7 @@ setconfquirk(char *selector, char *value, int flags) void setup_quirks(void) { - setquirk("MPlayer", "xv", SWM_Q_FLOAT | SWM_Q_FULLSCREEN); + setquirk("MPlayer", "xv", SWM_Q_FLOAT | SWM_Q_FULLSCREEN | SWM_Q_FOCUSPREV); setquirk("OpenOffice.org 3.2", "VCLSalFrame", SWM_Q_FLOAT); setquirk("Firefox-bin", "firefox-bin", SWM_Q_TRANSSZ); setquirk("Firefox", "Dialog", SWM_Q_FLOAT); @@ -4266,23 +5167,29 @@ setup_quirks(void) setquirk("Xitk", "Xine Window", SWM_Q_FLOAT | SWM_Q_ANYWHERE); setquirk("xine", "xine Video Fullscreen Window", SWM_Q_FULLSCREEN | SWM_Q_FLOAT); setquirk("pcb", "pcb", SWM_Q_FLOAT); + setquirk("SDL_App", "SDL_App", SWM_Q_FLOAT | SWM_Q_FULLSCREEN); } /* conf file stuff */ #define SWM_CONF_FILE "scrotwm.conf" -enum { SWM_S_BAR_DELAY, SWM_S_BAR_ENABLED, SWM_S_BAR_BORDER_WIDTH, SWM_S_STACK_ENABLED, - SWM_S_CLOCK_ENABLED, SWM_S_CLOCK_FORMAT, SWM_S_CYCLE_EMPTY, - SWM_S_CYCLE_VISIBLE, SWM_S_SS_ENABLED, SWM_S_TERM_WIDTH, - SWM_S_TITLE_CLASS_ENABLED, SWM_S_TITLE_NAME_ENABLED, SWM_S_WINDOW_NAME_ENABLED, - SWM_S_FOCUS_MODE, SWM_S_DISABLE_BORDER, SWM_S_BORDER_WIDTH, SWM_S_BAR_FONT, - SWM_S_BAR_ACTION, SWM_S_SPAWN_TERM, SWM_S_SS_APP, SWM_S_DIALOG_RATIO, - SWM_S_BAR_AT_BOTTOM +enum { SWM_S_BAR_DELAY, SWM_S_BAR_ENABLED, SWM_S_BAR_BORDER_WIDTH, + SWM_S_STACK_ENABLED, SWM_S_CLOCK_ENABLED, SWM_S_CLOCK_FORMAT, + SWM_S_CYCLE_EMPTY, SWM_S_CYCLE_VISIBLE, SWM_S_SS_ENABLED, + SWM_S_TERM_WIDTH, SWM_S_TITLE_CLASS_ENABLED, + SWM_S_TITLE_NAME_ENABLED, SWM_S_WINDOW_NAME_ENABLED, SWM_S_URGENT_ENABLED, + SWM_S_FOCUS_MODE, SWM_S_DISABLE_BORDER, SWM_S_BORDER_WIDTH, + SWM_S_BAR_FONT, SWM_S_BAR_ACTION, SWM_S_SPAWN_TERM, + SWM_S_SS_APP, SWM_S_DIALOG_RATIO, SWM_S_BAR_AT_BOTTOM, + SWM_S_VERBOSE_LAYOUT, SWM_S_BAR_JUSTIFY }; int setconfvalue(char *selector, char *value, int flags) { + int i; + char *b; + switch (flags) { case SWM_S_BAR_DELAY: bar_delay = atoi(value); @@ -4296,6 +5203,16 @@ setconfvalue(char *selector, char *value, int flags) case SWM_S_BAR_AT_BOTTOM: bar_at_bottom = atoi(value); break; + case SWM_S_BAR_JUSTIFY: + if (!strcmp(value, "left")) + bar_justify = SWM_BAR_JUSTIFY_LEFT; + else if (!strcmp(value, "center")) + bar_justify = SWM_BAR_JUSTIFY_CENTER; + else if (!strcmp(value, "right")) + bar_justify = SWM_BAR_JUSTIFY_RIGHT; + else + errx(1, "invalid bar_justify"); + break; case SWM_S_STACK_ENABLED: stack_enabled = atoi(value); break; @@ -4330,6 +5247,9 @@ setconfvalue(char *selector, char *value, int flags) case SWM_S_TITLE_NAME_ENABLED: title_name_enabled = atoi(value); break; + case SWM_S_URGENT_ENABLED: + urgent_enabled = atoi(value); + break; case SWM_S_FOCUS_MODE: if (!strcmp(value, "default")) focus_mode = SWM_FOCUS_DEFAULT; @@ -4338,7 +5258,7 @@ setconfvalue(char *selector, char *value, int flags) else if (!strcmp(value, "synergy")) focus_mode = SWM_FOCUS_SYNERGY; else - err(1, "focus_mode"); + errx(1, "focus_mode"); break; case SWM_S_DISABLE_BORDER: disable_border = atoi(value); @@ -4347,9 +5267,12 @@ setconfvalue(char *selector, char *value, int flags) border_width = atoi(value); break; case SWM_S_BAR_FONT: - free(bar_fonts[0]); - if ((bar_fonts[0] = strdup(value)) == NULL) - err(1, "setconfvalue: bar_font"); + b = bar_fonts; + if (asprintf(&bar_fonts, "%s,%s", value, bar_fonts) == -1) + err(1, "setconfvalue: asprintf: failed to allocate " + "memory for bar_fonts."); + + free(b); break; case SWM_S_BAR_ACTION: free(bar_argv[0]); @@ -4368,6 +5291,15 @@ setconfvalue(char *selector, char *value, int flags) if (dialog_ratio > 1.0 || dialog_ratio <= .3) dialog_ratio = .6; break; + case SWM_S_VERBOSE_LAYOUT: + verbose_layout = atoi(value); + for (i = 0; layouts[i].l_stack != NULL; i++) { + if (verbose_layout) + layouts[i].l_string = fancy_stacker; + else + layouts[i].l_string = plain_stacker; + } + break; default: return (1); } @@ -4404,22 +5336,155 @@ setconfregion(char *selector, char *value, int flags) return (0); } +int +setautorun(char *selector, char *value, int flags) +{ + int ws_id; + char s[1024]; + char *ap, *sp = s; + union arg a; + int argc = 0; + long pid; + struct pid_e *p; + + if (getenv("SWM_STARTED")) + return (0); + + bzero(s, sizeof s); + if (sscanf(value, "ws[%d]:%1023c", &ws_id, s) != 2) + errx(1, "invalid autorun entry, should be 'ws[]:command'"); + ws_id--; + if (ws_id < 0 || ws_id >= SWM_WS_MAX) + errx(1, "autorun: invalid workspace %d", ws_id + 1); + + /* + * This is a little intricate + * + * If the pid already exists we simply reuse it because it means it was + * used before AND not claimed by manage_window. We get away with + * altering it in the parent after INSERT because this can not be a race + */ + a.argv = NULL; + while ((ap = strsep(&sp, " \t")) != NULL) { + if (*ap == '\0') + continue; + DNPRINTF(SWM_D_SPAWN, "setautorun: arg [%s]\n", ap); + argc++; + if ((a.argv = realloc(a.argv, argc * sizeof(char *))) == NULL) + err(1, "setautorun: realloc"); + a.argv[argc - 1] = ap; + } + + if ((a.argv = realloc(a.argv, (argc + 1) * sizeof(char *))) == NULL) + err(1, "setautorun: realloc"); + a.argv[argc] = NULL; + + if ((pid = fork()) == 0) { + spawn(ws_id, &a, 1); + /* NOTREACHED */ + _exit(1); + } + free(a.argv); + + /* parent */ + p = find_pid(pid); + if (p == NULL) { + p = calloc(1, sizeof *p); + if (p == NULL) + return (1); + TAILQ_INSERT_TAIL(&pidlist, p, entry); + } + + p->pid = pid; + p->ws = ws_id; + + return (0); +} + +int +setlayout(char *selector, char *value, int flags) +{ + int ws_id, i, x, mg, ma, si, raise; + int st = SWM_V_STACK; + char s[1024]; + struct workspace *ws; + + if (getenv("SWM_STARTED")) + return (0); + + bzero(s, sizeof s); + if (sscanf(value, "ws[%d]:%d:%d:%d:%d:%1023c", + &ws_id, &mg, &ma, &si, &raise, s) != 6) + errx(1, "invalid layout entry, should be 'ws[]:" + "::::" + "'"); + ws_id--; + if (ws_id < 0 || ws_id >= SWM_WS_MAX) + errx(1, "layout: invalid workspace %d", ws_id + 1); + + if (!strcasecmp(s, "vertical")) + st = SWM_V_STACK; + else if (!strcasecmp(s, "horizontal")) + st = SWM_H_STACK; + else if (!strcasecmp(s, "fullscreen")) + st = SWM_MAX_STACK; + else + errx(1, "invalid layout entry, should be 'ws[]:" + "::::" + "'"); + + for (i = 0; i < ScreenCount(display); i++) { + ws = (struct workspace *)&screens[i].ws; + ws[ws_id].cur_layout = &layouts[st]; + + ws[ws_id].always_raise = raise; + if (st == SWM_MAX_STACK) + continue; + + /* master grow */ + for (x = 0; x < abs(mg); x++) { + ws[ws_id].cur_layout->l_config(&ws[ws_id], + mg >= 0 ? SWM_ARG_ID_MASTERGROW : + SWM_ARG_ID_MASTERSHRINK); + stack(); + } + /* master add */ + for (x = 0; x < abs(ma); x++) { + ws[ws_id].cur_layout->l_config(&ws[ws_id], + ma >= 0 ? SWM_ARG_ID_MASTERADD : + SWM_ARG_ID_MASTERDEL); + stack(); + } + /* stack inc */ + for (x = 0; x < abs(si); x++) { + ws[ws_id].cur_layout->l_config(&ws[ws_id], + si >= 0 ? SWM_ARG_ID_STACKINC : + SWM_ARG_ID_STACKDEC); + stack(); + } + } + + return (0); +} + /* config options */ struct config_option { char *optname; - int (*func)(char*, char*, int); - int funcflags; + int (*func)(char*, char*, int); + int funcflags; }; struct config_option configopt[] = { { "bar_enabled", setconfvalue, SWM_S_BAR_ENABLED }, { "bar_at_bottom", setconfvalue, SWM_S_BAR_AT_BOTTOM }, { "bar_border", setconfcolor, SWM_S_COLOR_BAR_BORDER }, - { "bar_border_width", setconfvalue, SWM_S_BAR_BORDER_WIDTH }, + { "bar_border_width", setconfvalue, SWM_S_BAR_BORDER_WIDTH }, { "bar_color", setconfcolor, SWM_S_COLOR_BAR }, { "bar_font_color", setconfcolor, SWM_S_COLOR_BAR_FONT }, { "bar_font", setconfvalue, SWM_S_BAR_FONT }, { "bar_action", setconfvalue, SWM_S_BAR_ACTION }, { "bar_delay", setconfvalue, SWM_S_BAR_DELAY }, + { "bar_justify", setconfvalue, SWM_S_BAR_JUSTIFY }, + { "keyboard_mapping", setkeymapping, 0 }, { "bind", setconfbinding, 0 }, { "stack_enabled", setconfvalue, SWM_S_STACK_ENABLED }, { "clock_enabled", setconfvalue, SWM_S_CLOCK_ENABLED }, @@ -4429,6 +5494,7 @@ struct config_option configopt[] = { { "cycle_empty", setconfvalue, SWM_S_CYCLE_EMPTY }, { "cycle_visible", setconfvalue, SWM_S_CYCLE_VISIBLE }, { "dialog_ratio", setconfvalue, SWM_S_DIALOG_RATIO }, + { "verbose_layout", setconfvalue, SWM_S_VERBOSE_LAYOUT }, { "modkey", setconfmodkey, 0 }, { "program", setconfspawn, 0 }, { "quirk", setconfquirk, 0 }, @@ -4437,17 +5503,20 @@ struct config_option configopt[] = { { "screenshot_enabled", setconfvalue, SWM_S_SS_ENABLED }, { "screenshot_app", setconfvalue, SWM_S_SS_APP }, { "window_name_enabled", setconfvalue, SWM_S_WINDOW_NAME_ENABLED }, + { "urgent_enabled", setconfvalue, SWM_S_URGENT_ENABLED }, { "term_width", setconfvalue, SWM_S_TERM_WIDTH }, { "title_class_enabled", setconfvalue, SWM_S_TITLE_CLASS_ENABLED }, { "title_name_enabled", setconfvalue, SWM_S_TITLE_NAME_ENABLED }, - { "focus_mode", setconfvalue, SWM_S_FOCUS_MODE }, - { "disable_border", setconfvalue, SWM_S_DISABLE_BORDER }, - { "border_width", setconfvalue, SWM_S_BORDER_WIDTH }, + { "focus_mode", setconfvalue, SWM_S_FOCUS_MODE }, + { "disable_border", setconfvalue, SWM_S_DISABLE_BORDER }, + { "border_width", setconfvalue, SWM_S_BORDER_WIDTH }, + { "autorun", setautorun, 0 }, + { "layout", setlayout, 0 }, }; int -conf_load(char *filename) +conf_load(char *filename, int keymapping) { FILE *config; char *line, *cp, *optsub, *optval; @@ -4455,14 +5524,14 @@ conf_load(char *filename) int wordlen, i, optind; struct config_option *opt; - DNPRINTF(SWM_D_CONF, "conf_load begin\n"); + DNPRINTF(SWM_D_CONF, "conf_load: begin\n"); if (filename == NULL) { - fprintf(stderr, "conf_load: no filename\n"); + warnx("conf_load: no filename"); return (1); } if ((config = fopen(filename, "r")) == NULL) { - warn("conf_load: fopen"); + warn("conf_load: fopen: %s", filename); return (1); } @@ -4486,7 +5555,7 @@ conf_load(char *filename) if (wordlen == 0) { warnx("%s: line %zd: no option found", filename, lineno); - return (1); + goto out; } optind = -1; for (i = 0; i < LENGTH(configopt); i++) { @@ -4500,7 +5569,12 @@ conf_load(char *filename) if (optind == -1) { warnx("%s: line %zd: unknown option %.*s", filename, lineno, wordlen, cp); - return (1); + goto out; + } + if (keymapping && strcmp(opt->optname, "bind")) { + warnx("%s: line %zd: invalid option %.*s", + filename, lineno, wordlen, cp); + goto out; } cp += wordlen; cp += strspn(cp, " \t\n"); /* eat whitespace */ @@ -4513,9 +5587,16 @@ conf_load(char *filename) if (wordlen == 0) { warnx("%s: line %zd: syntax error", filename, lineno); - return (1); + goto out; + } + + if (asprintf(&optsub, "%.*s", wordlen, cp) == + -1) { + warnx("%s: line %zd: unable to allocate" + "memory for selector", filename, + lineno); + goto out; } - asprintf(&optsub, "%.*s", wordlen, cp); } cp += wordlen; cp += strspn(cp, "] \t\n"); /* eat trailing */ @@ -4525,31 +5606,119 @@ conf_load(char *filename) optval = strdup(cp); /* call function to deal with it all */ if (configopt[optind].func(optsub, optval, - configopt[optind].funcflags) != 0) { - fprintf(stderr, "%s line %zd: %s\n", - filename, lineno, line); + configopt[optind].funcflags) != 0) errx(1, "%s: line %zd: invalid data for %s", filename, lineno, configopt[optind].optname); - } free(optval); free(optsub); free(line); } fclose(config); - DNPRINTF(SWM_D_CONF, "conf_load end\n"); + DNPRINTF(SWM_D_CONF, "conf_load: end\n"); return (0); + +out: + free(line); + fclose(config); + DNPRINTF(SWM_D_CONF, "conf_load: end with error.\n"); + + return (1); } void -set_child_transient(struct ws_win *win) +set_child_transient(struct ws_win *win, Window *trans) { - struct ws_win *parent; + struct ws_win *parent, *w; + XWMHints *wmh = NULL; + struct swm_region *r; + struct workspace *ws; parent = find_window(win->transient); if (parent) parent->child_trans = win; + else { + DNPRINTF(SWM_D_MISC, "set_child_transient: parent doesn't exist" + " for 0x%lx trans 0x%lx\n", win->id, win->transient); + + if (win->hints == NULL) { + warnx("no hints for 0x%lx", win->id); + return; + } + + r = root_to_region(win->wa.root); + ws = r->ws; + /* parent doen't exist in our window list */ + TAILQ_FOREACH(w, &ws->winlist, entry) { + if (wmh) + XFree(wmh); + + if ((wmh = XGetWMHints(display, w->id)) == NULL) { + warnx("can't get hints for 0x%lx", w->id); + continue; + } + + if (win->hints->window_group != wmh->window_group) + continue; + + w->child_trans = win; + win->transient = w->id; + *trans = w->id; + DNPRINTF(SWM_D_MISC, "set_child_transient: asjusting " + "transient to 0x%lx\n", win->transient); + break; + } + } + + if (wmh) + XFree(wmh); +} + +long +window_get_pid(Window win) +{ + Atom actual_type_return; + int actual_format_return = 0; + unsigned long nitems_return = 0; + unsigned long bytes_after_return = 0; + long *pid = NULL; + long ret = 0; + const char *errstr; + unsigned char *prop = NULL; + + if (XGetWindowProperty(display, win, + XInternAtom(display, "_NET_WM_PID", False), 0, 1, False, + XA_CARDINAL, &actual_type_return, &actual_format_return, + &nitems_return, &bytes_after_return, + (unsigned char**)(void*)&pid) != Success) + goto tryharder; + if (actual_type_return != XA_CARDINAL) + goto tryharder; + if (pid == NULL) + goto tryharder; + + ret = *pid; + XFree(pid); + + return (ret); + +tryharder: + if (XGetWindowProperty(display, win, + XInternAtom(display, "_SWM_PID", False), 0, SWM_PROPLEN, False, + XA_STRING, &actual_type_return, &actual_format_return, + &nitems_return, &bytes_after_return, &prop) != Success) + return (0); + if (actual_type_return != XA_STRING) + return (0); + if (prop == NULL) + return (0); + + ret = strtonum((const char *)prop, 0, UINT_MAX, &errstr); + /* ignore error because strtonum returns 0 anyway */ + XFree(prop); + + return (ret); } struct ws_win * @@ -4567,40 +5736,51 @@ manage_window(Window id) long mask; const char *errstr; XWindowChanges wc; + struct pid_e *p; + struct quirk *qp; if ((win = find_window(id)) != NULL) return (win); /* already being managed */ /* see if we are on the unmanaged list */ if ((win = find_unmanaged_window(id)) != NULL) { - DNPRINTF(SWM_D_MISC, "manage previously unmanaged window " - "%lu\n", win->id); + DNPRINTF(SWM_D_MISC, "manage_window: previously unmanaged " + "window: 0x%lx\n", win->id); TAILQ_REMOVE(&win->ws->unmanagedlist, win, entry); - TAILQ_INSERT_TAIL(&win->ws->winlist, win, entry); - if (win->transient) - set_child_transient(win); + if (win->transient) { + set_child_transient(win, &trans); + } if (trans && (ww = find_window(trans))) + TAILQ_INSERT_AFTER(&win->ws->winlist, ww, win, entry); + else + TAILQ_INSERT_TAIL(&win->ws->winlist, win, entry); ewmh_update_actions(win); return (win); } if ((win = calloc(1, sizeof(struct ws_win))) == NULL) - errx(1, "calloc: failed to allocate memory for new window"); + err(1, "manage_window: calloc: failed to allocate memory for " + "new window"); win->id = id; + /* see if we need to override the workspace */ + p = find_pid(window_get_pid(id)); + /* Get all the window data in one shot */ ws_idx_atom = XInternAtom(display, "_SWM_WS", False); - if (ws_idx_atom) + if (ws_idx_atom) { XGetWindowProperty(display, id, ws_idx_atom, 0, SWM_PROPLEN, False, XA_STRING, &type, &format, &nitems, &bytes, &prop); + } XGetWindowAttributes(display, id, &win->wa); - XGetWMNormalHints(display, id, &win->sh, &mask); + XGetWMNormalHints(display, id, &win->sh, &win->sh_mask); + win->hints = XGetWMHints(display, id); XGetTransientForHint(display, id, &trans); if (trans) { win->transient = trans; - set_child_transient(win); - DNPRINTF(SWM_D_MISC, "manage_window: win %lu transient %lu\n", - win->id, win->transient); + set_child_transient(win, &trans); + DNPRINTF(SWM_D_MISC, "manage_window: window: 0x%lx, " + "transient: 0x%lx\n", win->id, win->transient); } /* get supported protocols */ @@ -4623,11 +5803,16 @@ manage_window(Window id) * transient, * put it in the same workspace */ r = root_to_region(win->wa.root); - if (prop && win->transient == 0) { - DNPRINTF(SWM_D_PROP, "got property _SWM_WS=%s\n", prop); - ws_idx = strtonum(prop, 0, 9, &errstr); + if (p) { + ws = &r->s->ws[p->ws]; + TAILQ_REMOVE(&pidlist, p, entry); + free(p); + p = NULL; + } else if (prop && win->transient == 0) { + DNPRINTF(SWM_D_PROP, "manage_window: get _SWM_WS: %s\n", prop); + ws_idx = strtonum((const char *)prop, 0, 9, &errstr); if (errstr) { - DNPRINTF(SWM_D_EVENT, "window idx is %s: %s", + DNPRINTF(SWM_D_EVENT, "manage_window: window: #%s: %s", errstr, prop); } ws = &r->s->ws[ws_idx]; @@ -4641,8 +5826,8 @@ manage_window(Window id) if (ww->ws->r) r = ww->ws->r; else - fprintf(stderr, - "fix this bug mcbride\n"); + warnx("manage_window: fix this " + "bug mcbride"); border_me = 1; } } @@ -4651,20 +5836,24 @@ manage_window(Window id) win->id = id; win->ws = ws; win->s = r->s; /* this never changes */ - TAILQ_INSERT_TAIL(&ws->winlist, win, entry); + if (trans && (ww = find_window(trans))) + TAILQ_INSERT_AFTER(&ws->winlist, ww, win, entry); + else + TAILQ_INSERT_TAIL(&ws->winlist, win, entry); - win->g.w = win->wa.width; - win->g.h = win->wa.height; - win->g.x = win->wa.x; - win->g.y = win->wa.y; + WIDTH(win) = win->wa.width; + HEIGHT(win) = win->wa.height; + X(win) = win->wa.x; + Y(win) = win->wa.y; win->g_floatvalid = 0; win->floatmaxed = 0; win->ewmh_flags = 0; /* Set window properties so we can remember this after reincarnation */ if (ws_idx_atom && prop == NULL && - snprintf(ws_idx_str, SWM_PROPLEN, "%d", ws->idx) < SWM_PROPLEN) { - DNPRINTF(SWM_D_PROP, "setting property _SWM_WS to %s\n", + snprintf((char *)ws_idx_str, SWM_PROPLEN, "%d", ws->idx) < + SWM_PROPLEN) { + DNPRINTF(SWM_D_PROP, "manage_window: set _SWM_WS: %s\n", ws_idx_str); XChangeProperty(display, win->id, ws_idx_atom, XA_STRING, 8, PropModeReplace, ws_idx_str, SWM_PROPLEN); @@ -4675,7 +5864,7 @@ manage_window(Window id) ewmh_autoquirk(win); if (XGetClassHint(display, win->id, &win->ch)) { - DNPRINTF(SWM_D_CLASS, "class: %s name: %s\n", + DNPRINTF(SWM_D_CLASS, "manage_window: class: %s, name: %s\n", win->ch.res_class, win->ch.res_name); /* java is retarded so treat it special */ @@ -4684,16 +5873,17 @@ manage_window(Window id) border_me = 1; } - for (i = 0; i < quirks_length; i++){ - if (!strcmp(win->ch.res_class, quirks[i].class) && - !strcmp(win->ch.res_name, quirks[i].name)) { - DNPRINTF(SWM_D_CLASS, "found: %s name: %s\n", - win->ch.res_class, win->ch.res_name); - if (quirks[i].quirk & SWM_Q_FLOAT) { + TAILQ_FOREACH(qp, &quirks, entry) { + if (!strcmp(win->ch.res_class, qp->class) && + !strcmp(win->ch.res_name, qp->name)) { + DNPRINTF(SWM_D_CLASS, "manage_window: found: " + "class: %s, name: %s\n", win->ch.res_class, + win->ch.res_name); + if (qp->quirk & SWM_Q_FLOAT) { win->floating = 1; border_me = 1; } - win->quirks = quirks[i].quirk; + win->quirks = qp->quirk; } } } @@ -4703,12 +5893,12 @@ manage_window(Window id) win->manual = 1; /* don't center the quirky windows */ bzero(&wc, sizeof wc); mask = 0; - if (bar_enabled && win->g.y < bar_height) { - win->g.y = wc.y = bar_height; + if (bar_enabled && Y(win) < bar_height) { + Y(win) = wc.y = bar_height; mask |= CWY; } - if (win->g.w + win->g.x > WIDTH(r)) { - win->g.x = wc.x = WIDTH(r) - win->g.w - 2; + if (WIDTH(win) + X(win) > WIDTH(r)) { + X(win) = wc.x = WIDTH(r) - WIDTH(win) - 2; mask |= CWX; } border_me = 1; @@ -4736,10 +5926,6 @@ manage_window(Window id) XSelectInput(display, id, EnterWindowMask | FocusChangeMask | PropertyChangeMask | StructureNotifyMask); - if (win->iconic) - set_win_state(win, IconicState); - else - set_win_state(win, NormalState); /* floaters need to be mapped if they are in the current workspace */ if ((win->floating || win->transient) && (ws->idx == r->ws->idx)) @@ -4751,7 +5937,7 @@ manage_window(Window id) void free_window(struct ws_win *win) { - DNPRINTF(SWM_D_MISC, "free_window: %lu\n", win->id); + DNPRINTF(SWM_D_MISC, "free_window: window: 0x%lx\n", win->id); if (win == NULL) return; @@ -4782,7 +5968,7 @@ unmanage_window(struct ws_win *win) if (win == NULL) return; - DNPRINTF(SWM_D_MISC, "unmanage_window: %lu\n", win->id); + DNPRINTF(SWM_D_MISC, "unmanage_window: window: 0x%lx\n", win->id); if (win->transient) { parent = find_window(win->transient); @@ -4793,8 +5979,12 @@ unmanage_window(struct ws_win *win) /* focus on root just in case */ XSetInputFocus(display, PointerRoot, PointerRoot, CurrentTime); - if (!win->floating) - focus_prev(win); + focus_prev(win); + + if (win->hints) { + XFree(win->hints); + win->hints = NULL; + } TAILQ_REMOVE(&win->ws->winlist, win, entry); TAILQ_INSERT_TAIL(&win->ws->unmanagedlist, win, entry); @@ -4803,14 +5993,14 @@ unmanage_window(struct ws_win *win) } void -focus_magic(struct ws_win *win, int do_trans) +focus_magic(struct ws_win *win) { - DNPRINTF(SWM_D_FOCUS, "focus_magic: %lu %d\n", WINID(win), do_trans); + DNPRINTF(SWM_D_FOCUS, "focus_magic: window: 0x%lx\n", WINID(win)); if (win == NULL) return; - if (do_trans == SWM_F_TRANSIENT && win->child_trans) { + if (win->child_trans) { /* win = parent & has a transient so focus on that */ if (win->java) { focus_win(win->child_trans); @@ -4840,33 +6030,31 @@ focus_magic(struct ws_win *win, int do_trans) void expose(XEvent *e) { - DNPRINTF(SWM_D_EVENT, "expose: window: %lu\n", e->xexpose.window); + DNPRINTF(SWM_D_EVENT, "expose: window: 0x%lx\n", e->xexpose.window); } void keypress(XEvent *e) { - unsigned int i; KeySym keysym; XKeyEvent *ev = &e->xkey; - - DNPRINTF(SWM_D_EVENT, "keypress: window: %lu\n", ev->window); + struct key *kp; keysym = XKeycodeToKeysym(display, (KeyCode)ev->keycode, 0); - for (i = 0; i < keys_length; i++) - if (keysym == keys[i].keysym - && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) - && keyfuncs[keys[i].funcid].func) { - if (keys[i].funcid == kf_spawn_custom) + TAILQ_FOREACH(kp, &keys, entry) + if (keysym == kp->keysym + && CLEANMASK(kp->mod) == CLEANMASK(ev->state) + && keyfuncs[kp->funcid].func) { + if (kp->funcid == kf_spawn_custom) spawn_custom( root_to_region(ev->root), - &(keyfuncs[keys[i].funcid].args), - keys[i].spawn_name + &(keyfuncs[kp->funcid].args), + kp->spawn_name ); else - keyfuncs[keys[i].funcid].func( + keyfuncs[kp->funcid].func( root_to_region(ev->root), - &(keyfuncs[keys[i].funcid].args) + &(keyfuncs[kp->funcid].args) ); } } @@ -4878,13 +6066,10 @@ buttonpress(XEvent *e) int i, action; XButtonPressedEvent *ev = &e->xbutton; - DNPRINTF(SWM_D_EVENT, "buttonpress: window: %lu\n", ev->window); - - action = root_click; if ((win = find_window(ev->window)) == NULL) return; - focus_magic(win, SWM_F_TRANSIENT); + focus_magic(win); action = client_click; for (i = 0; i < LENGTH(buttons); i++) @@ -4906,9 +6091,10 @@ configurerequest(XEvent *e) if ((win = find_unmanaged_window(ev->window)) == NULL) new = 1; + DNPRINTF(SWM_D_EVENT, "configurerequest: window: 0x%lx, new: %s\n", + ev->window, YESNO(new)); + if (new) { - DNPRINTF(SWM_D_EVENT, "configurerequest: new window: %lu\n", - ev->window); bzero(&wc, sizeof wc); wc.x = ev->x; wc.y = ev->y; @@ -4919,8 +6105,6 @@ configurerequest(XEvent *e) wc.stack_mode = ev->detail; XConfigureWindow(display, ev->window, ev->value_mask, &wc); } else { - DNPRINTF(SWM_D_EVENT, "configurerequest: change window: %lu\n", - ev->window); config_win(win, ev); } } @@ -4929,17 +6113,18 @@ void configurenotify(XEvent *e) { struct ws_win *win; - long mask; - DNPRINTF(SWM_D_EVENT, "configurenotify: window: %lu\n", + DNPRINTF(SWM_D_EVENT, "configurenotify: window: 0x%lx\n", e->xconfigure.window); win = find_window(e->xconfigure.window); if (win) { - XGetWMNormalHints(display, win->id, &win->sh, &mask); + XGetWMNormalHints(display, win->id, &win->sh, &win->sh_mask); adjust_font(win); if (font_adjusted) stack(); + if (focus_mode == SWM_FOCUS_DEFAULT) + drain_enter_notify(); } } @@ -4949,7 +6134,7 @@ destroynotify(XEvent *e) struct ws_win *win; XDestroyWindowEvent *ev = &e->xdestroywindow; - DNPRINTF(SWM_D_EVENT, "destroynotify: window %lu\n", ev->window); + DNPRINTF(SWM_D_EVENT, "destroynotify: window: 0x%lx\n", ev->window); if ((win = find_window(ev->window)) == NULL) { if ((win = find_unmanaged_window(ev->window)) == NULL) @@ -4963,6 +6148,8 @@ destroynotify(XEvent *e) unmanage_window(win); stack(); + if (focus_mode == SWM_FOCUS_DEFAULT) + drain_enter_notify(); free_window(win); } @@ -4977,18 +6164,19 @@ enternotify(XEvent *e) Window focus_return; int revert_to_return; #endif - DNPRINTF(SWM_D_FOCUS, "enternotify: window: %lu mode %d detail %d root " - "%lu subwindow %lu same_screen %d focus %d state %d\n", - ev->window, ev->mode, ev->detail, ev->root, ev->subwindow, - ev->same_screen, ev->focus, ev->state); + DNPRINTF(SWM_D_FOCUS, "enternotify: window: 0x%lx, mode: %d, detail: " + "%d, root: 0x%lx, subwindow: 0x%lx, same_screen: %s, focus: %s, " + "state: %d\n", ev->window, ev->mode, ev->detail, ev->root, + ev->subwindow, YESNO(ev->same_screen), YESNO(ev->focus), ev->state); + + if (ev->mode != NotifyNormal) { + DNPRINTF(SWM_D_EVENT, "skip enternotify: generated by " + "cursor grab.\n"); + return; + } switch (focus_mode) { case SWM_FOCUS_DEFAULT: - if (QLength(display)) { - DNPRINTF(SWM_D_EVENT, "ignore enternotify %d\n", - QLength(display)); - return; - } break; case SWM_FOCUS_FOLLOW: break; @@ -5065,7 +6253,7 @@ enternotify(XEvent *e) } if ((win = find_window(ev->window)) == NULL) { - DNPRINTF(SWM_D_EVENT, "ignoring enternotify: win == NULL\n"); + DNPRINTF(SWM_D_EVENT, "skip enternotify: window is NULL\n"); return; } @@ -5079,7 +6267,7 @@ enternotify(XEvent *e) return; } - focus_magic(win, SWM_F_TRANSIENT); + focus_magic(win); } /* lets us use one switch statement for arbitrary mode/detail combinations */ @@ -5093,8 +6281,8 @@ focusevent(XEvent *e) u_int32_t mode_detail; XFocusChangeEvent *ev = &e->xfocus; - DNPRINTF(SWM_D_EVENT, "focusevent: %s window: %lu mode %d detail %d\n", - ev->type == FocusIn ? "entering" : "leaving", + DNPRINTF(SWM_D_EVENT, "focusevent: %s window: 0x%lx mode: %d " + "detail: %d\n", ev->type == FocusIn ? "entering" : "leaving", ev->window, ev->mode, ev->detail); if (last_focus_event == ev->type) { @@ -5122,7 +6310,7 @@ focusevent(XEvent *e) SWM_S_COLOR_FOCUS : SWM_S_COLOR_UNFOCUS].color); break; default: - fprintf(stderr, "ignoring focusevent\n"); + warnx("ignoring focusevent"); DNPRINTF(SWM_D_FOCUS, "ignoring focusevent\n"); break; } @@ -5135,7 +6323,7 @@ mapnotify(XEvent *e) struct ws_win *win; XMapEvent *ev = &e->xmap; - DNPRINTF(SWM_D_EVENT, "mapnotify: window: %lu\n", ev->window); + DNPRINTF(SWM_D_EVENT, "mapnotify: window: 0x%lx\n", ev->window); win = manage_window(ev->window); if (win) @@ -5160,7 +6348,7 @@ maprequest(XEvent *e) XWindowAttributes wa; XMapRequestEvent *ev = &e->xmaprequest; - DNPRINTF(SWM_D_EVENT, "maprequest: window: %lu\n", + DNPRINTF(SWM_D_EVENT, "maprequest: window: 0x%lx\n", e->xmaprequest.window); if (!XGetWindowAttributes(display, ev->window, &wa)) @@ -5177,7 +6365,7 @@ maprequest(XEvent *e) /* make new win focused */ r = root_to_region(win->wa.root); if (win->ws == r->ws) - focus_magic(win, SWM_F_GENERIC); + focus_magic(win); } void @@ -5185,9 +6373,13 @@ propertynotify(XEvent *e) { struct ws_win *win; XPropertyEvent *ev = &e->xproperty; - - DNPRINTF(SWM_D_EVENT, "propertynotify: window: %lu\n", - ev->window); +#ifdef SWM_DEBUG + char *name; + name = XGetAtomName(display, ev->atom); + DNPRINTF(SWM_D_EVENT, "propertynotify: window: 0x%lx, atom: %s\n", + ev->window, name); + XFree(name); +#endif win = find_window(ev->window); if (win == NULL) @@ -5202,19 +6394,24 @@ propertynotify(XEvent *e) } switch (ev->atom) { - case XA_WM_NORMAL_HINTS: #if 0 + case XA_WM_NORMAL_HINTS: long mask; XGetWMNormalHints(display, win->id, &win->sh, &mask); - fprintf(stderr, "normal hints: flag 0x%x\n", win->sh.flags); + warnx("normal hints: flag 0x%x", win->sh.flags); if (win->sh.flags & PMinSize) { - win->g.w = win->sh.min_width; - win->g.h = win->sh.min_height; - fprintf(stderr, "min %d %d\n", win->g.w, win->g.h); + WIDTH(win) = win->sh.min_width; + HEIGHT(win) = win->sh.min_height; + warnx("min %d %d", WIDTH(win), HEIGHT(win)); } XMoveResizeWindow(display, win->id, - win->g.x, win->g.y, win->g.w, win->g.h); + X(win), Y(win), WIDTH(win), HEIGHT(win)); #endif + case XA_WM_CLASS: + if (title_name_enabled || title_class_enabled) + bar_update(); + break; + case XA_WM_NAME: if (window_name_enabled) bar_update(); break; @@ -5228,7 +6425,7 @@ unmapnotify(XEvent *e) { struct ws_win *win; - DNPRINTF(SWM_D_EVENT, "unmapnotify: window: %lu\n", e->xunmap.window); + DNPRINTF(SWM_D_EVENT, "unmapnotify: window: 0x%lx\n", e->xunmap.window); /* determine if we need to help unmanage this window */ win = find_window(e->xunmap.window); @@ -5238,7 +6435,25 @@ unmapnotify(XEvent *e) if (getstate(e->xunmap.window) == NormalState) { unmanage_window(win); stack(); + + /* giant hack for apps that don't destroy transient windows */ + /* eat a bunch of events to prevent remanaging the window */ + XEvent cne; + while (XCheckWindowEvent(display, e->xunmap.window, + EnterWindowMask, &cne)) + ; + while (XCheckWindowEvent(display, e->xunmap.window, + StructureNotifyMask, &cne)) + ; + while (XCheckWindowEvent(display, e->xunmap.window, + SubstructureNotifyMask, &cne)) + ; + /* resend unmap because we ated it */ + XUnmapWindow(display, e->xunmap.window); } + + if (focus_mode == SWM_FOCUS_DEFAULT) + drain_enter_notify(); } void @@ -5247,7 +6462,7 @@ visibilitynotify(XEvent *e) int i; struct swm_region *r; - DNPRINTF(SWM_D_EVENT, "visibilitynotify: window: %lu\n", + DNPRINTF(SWM_D_EVENT, "visibilitynotify: window: 0x%lx\n", e->xvisibility.window); if (e->xvisibility.state == VisibilityUnobscured) for (i = 0; i < ScreenCount(display); i++) @@ -5268,42 +6483,47 @@ clientmessage(XEvent *e) if (win == NULL) return; - DNPRINTF(SWM_D_EVENT, "clientmessage: window: 0x%lx type: %ld \n", + DNPRINTF(SWM_D_EVENT, "clientmessage: window: 0x%lx, type: %ld\n", ev->window, ev->message_type); if (ev->message_type == ewmh[_NET_ACTIVE_WINDOW].atom) { - DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_ACTIVE_WINDOW \n"); + DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_ACTIVE_WINDOW\n"); focus_win(win); } if (ev->message_type == ewmh[_NET_CLOSE_WINDOW].atom) { - DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_CLOSE_WINDOW \n"); + DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_CLOSE_WINDOW\n"); if (win->can_delete) client_msg(win, adelete); else XKillClient(display, win->id); } if (ev->message_type == ewmh[_NET_MOVERESIZE_WINDOW].atom) { - DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_MOVERESIZE_WINDOW \n"); + DNPRINTF(SWM_D_EVENT, + "clientmessage: _NET_MOVERESIZE_WINDOW\n"); if (win->floating) { if (ev->data.l[0] & (1<<8)) /* x */ - win->g.x = ev->data.l[1]; + X(win) = ev->data.l[1]; if (ev->data.l[0] & (1<<9)) /* y */ - win->g.y = ev->data.l[2]; + Y(win) = ev->data.l[2]; if (ev->data.l[0] & (1<<10)) /* width */ - win->g.w = ev->data.l[3]; + WIDTH(win) = ev->data.l[3]; if (ev->data.l[0] & (1<<11)) /* height */ - win->g.h = ev->data.l[4]; + HEIGHT(win) = ev->data.l[4]; + + update_window(win); } else { /* TODO: Change stack sizes */ + /* notify no change was made. */ + config_win(win, NULL); } - config_win(win, NULL); } if (ev->message_type == ewmh[_NET_WM_STATE].atom) { - DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_WM_STATE \n"); + DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_WM_STATE\n"); ewmh_update_win_state(win, ev->data.l[1], ev->data.l[0]); if (ev->data.l[2]) - ewmh_update_win_state(win, ev->data.l[2], ev->data.l[0]); + ewmh_update_win_state(win, ev->data.l[2], + ev->data.l[0]); stack(); } @@ -5319,7 +6539,7 @@ xerror_start(Display *d, XErrorEvent *ee) int xerror(Display *d, XErrorEvent *ee) { - /* fprintf(stderr, "error: %p %p\n", display, ee); */ + /* warnx("error: %p %p", display, ee); */ return (-1); } @@ -5389,7 +6609,8 @@ new_region(struct swm_screen *s, int x, int y, int w, int h) ws = r->ws; } else if ((r = calloc(1, sizeof(struct swm_region))) == NULL) - errx(1, "calloc: failed to allocate memory for screen"); + err(1, "new_region: calloc: failed to allocate memory " + "for screen"); /* if we don't have a workspace already, find one */ if (ws == NULL) { @@ -5401,7 +6622,7 @@ new_region(struct swm_screen *s, int x, int y, int w, int h) } if (ws == NULL) - errx(1, "no free workspaces\n"); + errx(1, "new_region: no free workspaces"); X(r) = x; Y(r) = y; @@ -5428,7 +6649,7 @@ scan_xrandr(int i) if (i >= ScreenCount(display)) - errx(1, "invalid screen"); + errx(1, "scan_xrandr: invalid screen"); /* remove any old regions */ while ((r = TAILQ_FIRST(&screens[i].rl)) != NULL) { @@ -5480,7 +6701,7 @@ screenchange(XEvent *e) { struct swm_region *r; int i; - DNPRINTF(SWM_D_EVENT, "screenchange: %lu\n", xe->root); + DNPRINTF(SWM_D_EVENT, "screenchange: root: 0x%lx\n", xe->root); if (!XRRUpdateConfiguration(e)) return; @@ -5490,7 +6711,7 @@ screenchange(XEvent *e) { if (screens[i].root == xe->root) break; if (i >= ScreenCount(display)) - errx(1, "screenchange: screen not found\n"); + errx(1, "screenchange: screen not found"); /* brute force for now, just re-enumerate the regions */ scan_xrandr(i); @@ -5500,6 +6721,8 @@ screenchange(XEvent *e) { TAILQ_FOREACH(r, &screens[i].rl, entry) bar_setup(r); stack(); + if (focus_mode == SWM_FOCUS_DEFAULT) + drain_enter_notify(); } void @@ -5553,13 +6776,12 @@ setup_screens(void) int i, j, k; int errorbase, major, minor; struct workspace *ws; - int ws_idx_atom; + XGCValues gcv; if ((screens = calloc(ScreenCount(display), sizeof(struct swm_screen))) == NULL) - errx(1, "calloc: screens"); - - ws_idx_atom = XInternAtom(display, "_SWM_WS", False); + err(1, "setup_screens: calloc: failed to allocate memory for " + "screens"); /* initial Xrandr setup */ xrandr_support = XRRQueryExtension(display, @@ -5570,7 +6792,7 @@ setup_screens(void) /* map physical screens */ for (i = 0; i < ScreenCount(display); i++) { - DNPRINTF(SWM_D_WS, "setup_screens: init screen %d\n", i); + DNPRINTF(SWM_D_WS, "setup_screens: init screen: %d\n", i); screens[i].idx = i; TAILQ_INIT(&screens[i].rl); TAILQ_INIT(&screens[i].orl); @@ -5583,6 +6805,13 @@ setup_screens(void) setscreencolor("black", i + 1, SWM_S_COLOR_BAR); setscreencolor("rgb:a0/a0/a0", i + 1, SWM_S_COLOR_BAR_FONT); + /* create graphics context on screen with default font color */ + screens[i].bar_gc = XCreateGC(display, screens[i].root, 0, + &gcv); + + XSetForeground(display, screens[i].bar_gc, + screens[i].c[SWM_S_COLOR_BAR_FONT].color); + /* set default cursor */ XDefineCursor(display, screens[i].root, XCreateFontCursor(display, XC_left_ptr)); @@ -5592,6 +6821,7 @@ setup_screens(void) for (j = 0; j < SWM_WS_MAX; j++) { ws = &screens[i].ws[j]; ws->idx = j; + ws->name = NULL; ws->focus = NULL; ws->r = NULL; ws->old_r = NULL; @@ -5603,6 +6833,7 @@ setup_screens(void) layouts[k].l_config(ws, SWM_ARG_ID_STACKINIT); ws->cur_layout = &layouts[0]; + ws->cur_layout->l_string(ws); } scan_xrandr(i); @@ -5616,19 +6847,14 @@ setup_screens(void) void setup_globals(void) { - if ((bar_fonts[0] = strdup("-*-terminus-medium-*-*-*-*-*-*-*-*-*-*-*")) - == NULL) - err(1, "setup_globals: strdup"); - if ((bar_fonts[1] = strdup("-*-times-medium-r-*-*-*-*-*-*-*-*-*-*")) - == NULL) - err(1, "setup_globals: strdup"); - if ((bar_fonts[2] = strdup("-misc-fixed-medium-r-*-*-*-*-*-*-*-*-*-*")) - == NULL) - err(1, "setup_globals: strdup"); + if ((bar_fonts = strdup(SWM_BAR_FONTS)) == NULL) + err(1, "setup_globals: strdup: failed to allocate memory."); + if ((spawn_term[0] = strdup("xterm")) == NULL) - err(1, "setup_globals: strdup"); + err(1, "setup_globals: strdup: failed to allocate memory."); + if ((clock_format = strdup("%a %b %d %R %Z %Y")) == NULL) - errx(1, "strdup"); + err(1, "setup_globals: strdup: failed to allocate memory."); } void @@ -5660,7 +6886,6 @@ workaround(void) int main(int argc, char *argv[]) { - struct passwd *pwd; struct swm_region *r, *rr; struct ws_win *winfocus = NULL; struct timeval tv; @@ -5673,11 +6898,14 @@ main(int argc, char *argv[]) struct sigaction sact; start_argv = argv; - fprintf(stderr, "Welcome to scrotwm V%s cvs tag: %s\n", - SWM_VERSION, cvstag); - if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) + warnx("Welcome to scrotwm V%s Build: %s", SCROTWM_VERSION, buildstr); + if (!setlocale(LC_CTYPE, "") || !setlocale(LC_TIME, "") || + !XSupportsLocale()) warnx("no locale support"); + if (!X_HAVE_UTF8_STRING) + warnx("no UTF-8 support"); + if (!(display = XOpenDisplay(0))) errx(1, "can not open display"); @@ -5703,6 +6931,7 @@ main(int argc, char *argv[]) adelete = XInternAtom(display, "WM_DELETE_WINDOW", False); takefocus = XInternAtom(display, "WM_TAKE_FOCUS", False); a_wmname = XInternAtom(display, "WM_NAME", False); + a_netwmname = XInternAtom(display, "_NET_WM_NAME", False); a_utf8_string = XInternAtom(display, "UTF8_STRING", False); a_string = XInternAtom(display, "STRING", False); a_swm_iconic = XInternAtom(display, "_SWM_ICONIC", False); @@ -5710,10 +6939,10 @@ main(int argc, char *argv[]) /* look for local and global conf file */ pwd = getpwuid(getuid()); if (pwd == NULL) - errx(1, "invalid user %d", getuid()); + errx(1, "invalid user: %d", getuid()); - setup_screens(); setup_globals(); + setup_screens(); setup_keys(); setup_quirks(); setup_spawn(); @@ -5730,16 +6959,22 @@ main(int argc, char *argv[]) if (S_ISREG(sb.st_mode)) cfile = conf; } - if (cfile) - conf_load(cfile); + + /* load conf (if any) and refresh font color in bar graphics contexts */ + if (cfile && conf_load(cfile, SWM_CONF_DEFAULT) == 0) + for (i = 0; i < ScreenCount(display); ++i) + XSetForeground(display, screens[i].bar_gc, + screens[i].c[SWM_S_COLOR_BAR_FONT].color); setup_ewmh(); /* set some values to work around bad programs */ workaround(); - /* grab existing windows (before we build the bars) */ grab_windows(); + if (getenv("SWM_STARTED") == NULL) + setenv("SWM_STARTED", "YES", 1); + /* setup all bars */ for (i = 0; i < ScreenCount(display); i++) TAILQ_FOREACH(r, &screens[i].rl, entry) { @@ -5752,6 +6987,8 @@ main(int argc, char *argv[]) grabkeys(); stack(); + if (focus_mode == SWM_FOCUS_DEFAULT) + drain_enter_notify(); xfd = ConnectionNumber(display); while (running) { @@ -5760,22 +6997,24 @@ main(int argc, char *argv[]) if (running == 0) goto done; if (e.type < LASTEvent) { - dumpevent(&e); + DNPRINTF(SWM_D_EVENTQ ,"XEvent: handled: %s, " + "window: 0x%lx, type: %s (%d), %d remaining" + "\n", YESNO(handler[e.type]), + e.xany.window, geteventname(&e), + e.type, QLength(display)); + if (handler[e.type]) handler[e.type](&e); - else - DNPRINTF(SWM_D_EVENT, - "win: %lu unknown event: %d\n", - e.xany.window, e.type); } else { + DNPRINTF(SWM_D_EVENTQ, "XRandr Event: window: " + "0x%lx, type: %s (%d)\n", e.xany.window, + xrandr_geteventname(&e), e.type); + switch (e.type - xrandr_eventbase) { case RRScreenChangeNotify: screenchange(&e); break; default: - DNPRINTF(SWM_D_EVENT, - "win: %lu unknown xrandr event: " - "%d\n", e.xany.window, e.type); break; } } @@ -5792,8 +7031,8 @@ main(int argc, char *argv[]) /* move pointer to first screen if multi screen */ if (ScreenCount(display) > 1 || outputs > 1) XWarpPointer(display, None, rr->s[0].root, - 0, 0, 0, 0, rr->g.x, - rr->g.y + (bar_enabled ? bar_height : 0)); + 0, 0, 0, 0, X(rr), + Y(rr) + (bar_enabled ? bar_height : 0)); a.id = SWM_ARG_ID_FOCUSCUR; focus(rr, &a); @@ -5822,7 +7061,12 @@ main(int argc, char *argv[]) done: teardown_ewmh(); bar_extra_stop(); - XFreeGC(display, bar_gc); + + for (i = 0; i < ScreenCount(display); ++i) + if (screens[i].bar_gc != NULL) + XFreeGC(display, screens[i].bar_gc); + + XFreeFontSet(display, bar_fs); XCloseDisplay(display); return (0);