X-Git-Url: https://code.delx.au/spectrwm/blobdiff_plain/2a8d7bafe32519fac1ca81604a625199f21b3ad4..99a8c34790d8706e43faefa02c12a577da911a1c:/scrotwm.c diff --git a/scrotwm.c b/scrotwm.c index a1f1c0a..cd8b68a 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,11 +52,6 @@ * DEALINGS IN THE SOFTWARE. */ -static const char *cvstag = - "$scrotwm$"; - -#define SWM_VERSION "0.9.32" - #include #include #include @@ -78,6 +74,15 @@ static const char *cvstag = #include #include #include +#if defined(__linux__) +#include "tree.h" +#elif defined(__OpenBSD__) +#include +#elif defined(__FreeBSD__) +#include +#else +#include +#endif #include #include @@ -86,11 +91,20 @@ static const char *cvstag = #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 @@ -105,20 +119,21 @@ static const char *cvstag = #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) -#define SWM_D_MISC 0x0001 -#define SWM_D_EVENT 0x0002 -#define SWM_D_WS 0x0004 -#define SWM_D_FOCUS 0x0008 -#define SWM_D_MOVE 0x0010 -#define SWM_D_STACK 0x0020 -#define SWM_D_MOUSE 0x0040 -#define SWM_D_PROP 0x0080 -#define SWM_D_CLASS 0x0100 +#define SWM_D_MISC 0x0001 +#define SWM_D_EVENT 0x0002 +#define SWM_D_WS 0x0004 +#define SWM_D_FOCUS 0x0008 +#define SWM_D_MOVE 0x0010 +#define SWM_D_STACK 0x0020 +#define SWM_D_MOUSE 0x0040 +#define SWM_D_PROP 0x0080 +#define SWM_D_CLASS 0x0100 #define SWM_D_KEY 0x0200 #define SWM_D_QUIRK 0x0400 #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 @@ -135,6 +150,7 @@ u_int32_t swm_debug = 0 | SWM_D_SPAWN | SWM_D_EVENTQ | SWM_D_CONF + | SWM_D_BAR ; #else #define DPRINTF(x...) @@ -154,13 +170,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 @@ -171,6 +200,7 @@ Atom aprot; Atom adelete; Atom takefocus; Atom a_wmname; +Atom a_netwmname; Atom a_utf8_string; Atom a_string; Atom a_swm_iconic; @@ -198,14 +228,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; @@ -217,8 +283,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; @@ -226,13 +294,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) @@ -270,8 +338,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; @@ -287,6 +355,7 @@ struct ws_win { struct swm_screen *s; /* always valid, never changes */ XWindowAttributes wa; XSizeHints sh; + long sh_mask; XClassHint ch; XWMHints *hints; }; @@ -308,6 +377,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); @@ -316,19 +387,21 @@ 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, "[ ]" }, + SWM_L_MAPONFOCUS | SWM_L_FOCUSPREV, plain_stacker }, { NULL, NULL, 0, NULL }, }; @@ -343,6 +416,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 */ @@ -350,15 +425,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; }; @@ -379,6 +457,8 @@ struct swm_screen { unsigned long color; char *name; } c[SWM_S_COLOR_MAX]; + + GC bar_gc; }; struct swm_screen *screens; int num_screens; @@ -398,12 +478,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) @@ -412,6 +495,14 @@ 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; }; @@ -420,6 +511,7 @@ void focus_magic(struct ws_win *); /* quirks */ struct quirk { + TAILQ_ENTRY(quirk) entry; char *class; char *name; unsigned long quirk; @@ -430,8 +522,8 @@ struct quirk { #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 @@ -480,22 +572,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; @@ -589,7 +684,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]; @@ -606,12 +701,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); @@ -659,25 +753,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; + 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; } } @@ -800,7 +891,7 @@ ewmh_get_win_state(struct ws_win *win) win->ewmh_flags |= SWM_F_MANUAL; success = get_property(win->id, ewmh[_NET_WM_STATE].atom, - (~0L), XA_ATOM, &n, (unsigned char **)&states); + (~0L), XA_ATOM, &n, NULL, (void *)&states); if (!success) return; @@ -813,8 +904,8 @@ ewmh_get_win_state(struct ws_win *win) /* events */ #ifdef SWM_DEBUG -void -dumpevent(XEvent *e) +char * +geteventname(XEvent *e) { char *name = NULL; @@ -918,16 +1009,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 @@ -938,37 +1040,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 " - "transient: %lu\n", - win->id, wa.map_state, state, win->transient); + 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 " - "transient: %lu\n", - win->id, wa.map_state, state, win->transient); + 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) { @@ -1096,7 +1197,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); } @@ -1108,19 +1209,45 @@ 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) { @@ -1128,22 +1255,21 @@ custom_region(char *val) 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; } @@ -1166,10 +1292,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 @@ -1183,7 +1332,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; } @@ -1212,27 +1361,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) { @@ -1241,11 +1434,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; @@ -1254,46 +1447,59 @@ 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); } } alarm(bar_delay); } +void +bar_check_opts(void) +{ + if (title_class_enabled || title_name_enabled || window_name_enabled) + bar_update(); +} + void bar_signal(int sig) { @@ -1306,7 +1512,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++) @@ -1340,9 +1546,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()) { @@ -1376,27 +1582,48 @@ 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_height = bar_fs->ascent + bar_fs->descent + 1 + + 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; + x = X(r); y = bar_at_bottom ? (Y(r) + HEIGHT(r) - bar_height) : Y(r); @@ -1405,12 +1632,10 @@ bar_setup(struct swm_region *r) 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"); @@ -1426,7 +1651,7 @@ drain_enter_notify(void) while (XCheckMaskEvent(display, EnterWindowMask, &cne)) i++; - DNPRINTF(SWM_D_MISC, "drain_enter_notify: drained %d\n", i); + DNPRINTF(SWM_D_MISC, "drain_enter_notify: drained: %d\n", i); } void @@ -1434,7 +1659,7 @@ 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; @@ -1450,7 +1675,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); @@ -1463,8 +1688,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(); @@ -1488,6 +1713,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) { @@ -1496,39 +1722,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); } @@ -1601,8 +1862,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; @@ -1627,15 +1888,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); } @@ -1729,7 +1989,7 @@ spawn(int ws_idx, union arg *args, int close_fd) setenv("LD_PRELOAD", SWM_LIB, 1); if (asprintf(&ret, "%d", ws_idx) == -1) { - perror("_SWM_WS"); + warn("spawn: asprintf SWM_WS"); _exit(1); } setenv("_SWM_WS", ret, 1); @@ -1737,7 +1997,7 @@ spawn(int ws_idx, 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); @@ -1745,7 +2005,7 @@ spawn(int ws_idx, union arg *args, int close_fd) ret = NULL; if (setsid() == -1) { - perror("setsid"); + warn("spawn: setsid"); _exit(1); } @@ -1756,7 +2016,7 @@ spawn(int ws_idx, 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); @@ -1767,7 +2027,7 @@ spawn(int ws_idx, union arg *args, int close_fd) execvp(args->argv[0], args->argv); - perror("execvp"); + warn("spawn: execvp"); _exit(1); } @@ -1810,12 +2070,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); - 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]; @@ -1831,10 +2091,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]; @@ -1850,7 +2110,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; @@ -1918,7 +2178,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; @@ -1959,11 +2219,11 @@ focus_win(struct ws_win *win) if (win->java == 0) XSetInputFocus(display, win->id, RevertToParent, CurrentTime); - XMapRaised(display, win->id); 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, @@ -1971,8 +2231,7 @@ focus_win(struct ws_win *win) PropModeReplace, (unsigned char *)&win->id,1); } - if (window_name_enabled) - bar_update(); + bar_check_opts(); } void @@ -1991,9 +2250,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; @@ -2038,20 +2296,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--; @@ -2062,7 +2326,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; @@ -2076,9 +2341,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; @@ -2117,8 +2381,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; @@ -2126,8 +2390,8 @@ 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); } } @@ -2145,7 +2409,7 @@ sort_windows(struct ws_win_list *wl) if (win->transient) { parent = find_window(win->transient); if (parent == NULL) { - fprintf(stderr, "not possible bug\n"); + warnx("not possible bug"); continue; } TAILQ_REMOVE(wl, win, entry); @@ -2163,9 +2427,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) @@ -2221,7 +2484,7 @@ 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; } @@ -2233,12 +2496,12 @@ swapwin(struct swm_region *r, union arg *args) void focus_prev(struct ws_win *win) { - struct ws_win *winfocus = NULL, *winlostfocus = NULL; + struct ws_win *winfocus = NULL; struct ws_win *cur_focus = NULL; 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; @@ -2246,7 +2509,6 @@ focus_prev(struct ws_win *win) ws = win->ws; wl = &ws->winlist; cur_focus = ws->focus; - winlostfocus = cur_focus; /* pickle, just focus on whatever */ if (cur_focus == NULL) { @@ -2284,25 +2546,24 @@ focus_prev(struct ws_win *win) winfocus = TAILQ_LAST(wl, ws_win_list); if (winfocus == NULL || winfocus == win) winfocus = TAILQ_NEXT(cur_focus, entry); -done: - if (winfocus == winlostfocus || winfocus == NULL) - return; +done: focus_magic(winfocus); } void focus(struct swm_region *r, union arg *args) { - struct ws_win *winfocus = NULL, *winlostfocus = NULL, *head; + struct ws_win *winfocus = NULL, *head; 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) { @@ -2323,8 +2584,17 @@ focus(struct swm_region *r, union arg *args) return; ws = r->ws; wl = &ws->winlist; - - winlostfocus = cur_focus; + 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; switch (args->id) { case SWM_ARG_ID_FOCUSPREV: @@ -2341,9 +2611,14 @@ focus(struct swm_region *r, union arg *args) /* skip iconics */ if (winfocus && winfocus->iconic) { - TAILQ_FOREACH_REVERSE(winfocus, wl, ws_win_list, entry) + 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; @@ -2355,9 +2630,13 @@ focus(struct swm_region *r, union arg *args) /* skip iconics */ if (winfocus && winfocus->iconic) { - TAILQ_FOREACH(winfocus, wl, entry) + while (winfocus != cur_focus) { + if (winfocus == NULL) + winfocus = TAILQ_FIRST(wl); if (winfocus->iconic == 0) break; + winfocus = TAILQ_NEXT(winfocus, entry); + } } break; @@ -2370,8 +2649,6 @@ focus(struct swm_region *r, union arg *args) default: return; } - if (winfocus == winlostfocus || winfocus == NULL) - return; focus_magic(winfocus); } @@ -2380,13 +2657,10 @@ 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]; @@ -2404,7 +2678,7 @@ 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) @@ -2412,21 +2686,27 @@ stack_config(struct swm_region *r, union arg *args) 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; @@ -2438,6 +2718,7 @@ 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; } @@ -2447,20 +2728,16 @@ stack(void) { 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; } @@ -2480,30 +2757,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) { @@ -2511,29 +2787,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 - wc.border_width; - win->g.y = r->g.y + (HEIGHT(r) - win->g.h) / - 2 - wc.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 - wc.border_width) - win->g.x = r->g.x - wc.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 * wc.border_width); - if (win->g.y < r->g.y - wc.border_width) - win->g.y = r->g.y - wc.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 * wc.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 @@ -2542,8 +2816,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); } @@ -2560,7 +2834,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; @@ -2569,7 +2843,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; @@ -2596,8 +2870,8 @@ 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) @@ -2731,22 +3005,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) { @@ -2789,7 +3063,8 @@ notiles: 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: @@ -2820,6 +3095,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; } @@ -2830,7 +3108,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 @@ -2867,6 +3145,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; } @@ -2877,7 +3158,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 */ @@ -2916,19 +3197,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); @@ -2958,7 +3240,7 @@ send_to_ws(struct swm_region *r, union arg *args) 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; @@ -2967,7 +3249,7 @@ 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]; @@ -2986,21 +3268,44 @@ send_to_ws(struct swm_region *r, union arg *args) 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); + PropModeReplace, ws_idx_str, strlen((char *)ws_idx_str)); } stack(); } +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) { @@ -3019,32 +3324,28 @@ iconify(struct swm_region *r, union arg *args) } 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 @@ -3052,9 +3353,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"); @@ -3073,8 +3373,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; @@ -3085,8 +3386,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); @@ -3096,39 +3396,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) { @@ -3144,23 +3559,168 @@ search_do_resp(void) } free(s); } -done: - free(resp); } void -wkill(struct swm_region *r, union arg *args) +search_resp_name_workspace(char *resp, unsigned long len) { - DNPRINTF(SWM_D_MISC, "wkill %d\n", args->id); + struct workspace *ws; - if (r->ws->focus == NULL) + DNPRINTF(SWM_D_MISC, "search_resp_name_workspace: resp: %s\n", resp); + + if (search_r->ws == NULL) return; + ws = search_r->ws; - if (args->id == SWM_ARG_ID_KILLWINDOW) - XKillClient(display, r->ws->focus->id); - else - if (r->ws->focus->can_delete) - client_msg(r->ws->focus, adelete); + 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: id: %d\n", args->id); + + if (r->ws->focus == NULL) + return; + + if (args->id == SWM_ARG_ID_KILLWINDOW) + XKillClient(display, r->ws->focus->id); + else + if (r->ws->focus->can_delete) + client_msg(r->ws->focus, adelete); } @@ -3190,10 +3750,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; } @@ -3226,41 +3786,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; + 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; @@ -3274,25 +3880,64 @@ resize(struct ws_win *win, union arg *args) _NET_WM_STATE_ADD); stack(); + + 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; + } + if (focus_mode == SWM_FOCUS_DEFAULT) drain_enter_notify(); - if (XGrabPointer(display, win->id, False, MOUSEMASK, GrabModeAsync, - GrabModeAsync, None, None /* cursor */, CurrentTime) != GrabSuccess) - return; + /* get cursor offset from window root */ + if (!XQueryPointer(display, win->id, &rr, &cr, &x, &y, &wx, &wy, &mask)) + 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; + g = win->g; - if ( win->g.y + win->g.h < r->g.y + r->g.h - 1) - rely = win->g.h - 1; + 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); @@ -3303,72 +3948,107 @@ resize(struct ws_win *win, union arg *args) 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 (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; + 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; + } + + /* 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 */ 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; + 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) @@ -3376,6 +4056,7 @@ move(struct ws_win *win, union arg *args) win->manual = 1; if (win->floating == 0 && !win->transient) { + store_float_geom(win,r); ewmh_update_win_state(win, ewmh[_NET_WM_STATE_ABOVE].atom, _NET_WM_STATE_ADD); } @@ -3384,10 +4065,43 @@ move(struct ws_win *win, union arg *args) 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); @@ -3398,40 +4112,52 @@ move(struct ws_win *win, union arg *args) 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 */ 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, @@ -3461,6 +4187,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, @@ -3486,6 +4214,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 }; @@ -3508,6 +4249,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} }, @@ -3537,6 +4279,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} }, @@ -3562,17 +4306,49 @@ 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 { + RB_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; +RB_HEAD(key_list, key); + +int +key_cmp(struct key *kp1, struct key *kp2) +{ + if (kp1->keysym < kp2->keysym) + return (-1); + if (kp1->keysym > kp2->keysym) + return (1); + + if (kp1->mod < kp2->mod) + return (-1); + if (kp1->mod > kp2->mod) + return (1); + + return (0); +} + +RB_GENERATE_STATIC(key_list, key, entry, key_cmp); +struct key_list keys; /* mouse */ enum { client_click, root_click }; @@ -3593,13 +4369,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; + RB_FOREACH(kp, key_list, &keys) + 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) @@ -3610,13 +4387,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, @@ -3626,16 +4403,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); } @@ -3646,7 +4422,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)) @@ -3658,7 +4434,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")) { @@ -3681,17 +4457,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); @@ -3739,9 +4513,9 @@ 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->ws->idx, &a, 0); @@ -3758,123 +4532,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"); +} - if (prog->argv == NULL) { - fprintf(stderr, - "error: setspawn: cannot find program %s", prog->name); - free(prog); +void +spawn_remove(struct spawn_prog *sp) +{ + int i; + + DNPRINTF(SWM_D_SPAWN, "spawn_remove: %s\n", sp->name); + + 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++; - if ((prog->argv = realloc(prog->argv, - prog->argc * sizeof(char *))) == NULL) - err(1, "setconfspawn: realloc"); - 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); @@ -3894,13 +4647,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 */ @@ -3965,85 +4725,86 @@ strdupsafe(char *str) return (strdup(str)); } +void +key_insert(unsigned int mod, KeySym ks, enum keyfuncid kfid, char *spawn_name) +{ + 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); + RB_INSERT(key_list, &keys, kp); + + DNPRINTF(SWM_D_KEY, "key_insert: leave\n"); +} + +struct key * +key_lookup(unsigned int mod, KeySym ks) +{ + struct key kp; + + kp.keysym = ks; + kp.mod = mod; + + return (RB_FIND(key_list, &keys, &kp)); +} + +void +key_remove(struct key *kp) +{ + DNPRINTF(SWM_D_KEY, "key_remove: %s\n", keyfuncs[kp->funcid].name); + + RB_REMOVE(key_list, &keys, kp); + 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) { - int i, j; + 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; - } - } + + if ((kp = key_lookup(mod, ks)) != NULL) { + 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 == NULL) { - 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 == NULL) { - 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 == NULL) { - fprintf(stderr, "keys array problem\n"); - quit(NULL, NULL); - } - } + + key_insert(mod, ks, kfid, spawn_name); DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n"); } @@ -4053,7 +4814,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); @@ -4078,13 +4839,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); @@ -4098,6 +4859,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); @@ -4111,7 +4873,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); @@ -4127,6 +4889,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); @@ -4145,19 +4909,63 @@ 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; + + while (RB_EMPTY(&keys) == 0) { + kp = RB_ROOT(&keys); + key_remove(kp); + } +} + +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) { @@ -4179,10 +4987,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(); @@ -4191,11 +5000,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))) + RB_FOREACH(kp, key_list, &keys) { + 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); } @@ -4273,79 +5082,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 == NULL) { - 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 == NULL) { - 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 == NULL) { - fprintf(stderr, "quirks array problem!\n"); - quit(NULL, NULL); - } - } + + quirk_insert(class, name, quirk); + DNPRINTF(SWM_D_QUIRK, "setquirk: leave\n"); } int @@ -4391,15 +5199,19 @@ 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_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_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); @@ -4413,6 +5225,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; @@ -4447,6 +5269,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; @@ -4455,7 +5280,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); @@ -4464,9 +5289,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]); @@ -4485,6 +5313,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); } @@ -4537,10 +5374,10 @@ setautorun(char *selector, char *value, int flags) bzero(s, sizeof s); if (sscanf(value, "ws[%d]:%1023c", &ws_id, s) != 2) - errx(1, "invalid autorun entry, should be 'ws[]:command'\n"); + 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\n", ws_id + 1); + errx(1, "autorun: invalid workspace %d", ws_id + 1); /* * This is a little intricate @@ -4589,7 +5426,8 @@ setautorun(char *selector, char *value, int flags) int setlayout(char *selector, char *value, int flags) { - int ws_id, st, i; + int ws_id, i, x, mg, ma, si, raise; + int st = SWM_V_STACK; char s[1024]; struct workspace *ws; @@ -4597,11 +5435,14 @@ setlayout(char *selector, char *value, int flags) return (0); bzero(s, sizeof s); - if (sscanf(value, "ws[%d]:%1023c", &ws_id, s) != 2) - errx(1, "invalid layout entry, should be 'ws[]:'\n"); + 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\n", ws_id + 1); + errx(1, "layout: invalid workspace %d", ws_id + 1); if (!strcasecmp(s, "vertical")) st = SWM_V_STACK; @@ -4610,11 +5451,39 @@ setlayout(char *selector, char *value, int flags) else if (!strcasecmp(s, "fullscreen")) st = SWM_MAX_STACK; else - errx(1, "invalid layout entry, should be 'ws[]:'\n"); + 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); @@ -4636,6 +5505,8 @@ struct config_option configopt[] = { { "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 }, @@ -4645,6 +5516,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 }, @@ -4653,6 +5525,7 @@ 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 }, @@ -4665,7 +5538,7 @@ struct config_option configopt[] = { int -conf_load(char *filename) +conf_load(char *filename, int keymapping) { FILE *config; char *line, *cp, *optsub, *optval; @@ -4673,14 +5546,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); } @@ -4704,7 +5577,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++) { @@ -4718,7 +5591,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 */ @@ -4731,9 +5609,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 */ @@ -4743,21 +5628,25 @@ 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 @@ -4773,10 +5662,10 @@ set_child_transient(struct ws_win *win, Window *trans) parent->child_trans = win; else { DNPRINTF(SWM_D_MISC, "set_child_transient: parent doesn't exist" - " for %lu trans %lu\n", win->id, win->transient); + " for 0x%lx trans 0x%lx\n", win->id, win->transient); if (win->hints == NULL) { - fprintf(stderr, "no hints for %lu\n", win->id); + warnx("no hints for 0x%lx", win->id); return; } @@ -4788,8 +5677,7 @@ set_child_transient(struct ws_win *win, Window *trans) XFree(wmh); if ((wmh = XGetWMHints(display, w->id)) == NULL) { - fprintf(stderr, "can't get hints for %lu\n", - w->id); + warnx("can't get hints for 0x%lx", w->id); continue; } @@ -4800,7 +5688,7 @@ set_child_transient(struct ws_win *win, Window *trans) win->transient = w->id; *trans = w->id; DNPRINTF(SWM_D_MISC, "set_child_transient: asjusting " - "transient to %lu\n", win->transient); + "transient to 0x%lx\n", win->transient); break; } } @@ -4848,7 +5736,7 @@ tryharder: if (prop == NULL) return (0); - ret = strtonum(prop, 0, UINT_MAX, &errstr); + ret = strtonum((const char *)prop, 0, UINT_MAX, &errstr); /* ignore error because strtonum returns 0 anyway */ XFree(prop); @@ -4871,14 +5759,15 @@ manage_window(Window id) 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); if (win->transient) { set_child_transient(win, &trans); @@ -4891,7 +5780,8 @@ manage_window(Window id) } 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; @@ -4905,14 +5795,14 @@ manage_window(Window id) 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, &trans); - DNPRINTF(SWM_D_MISC, "manage_window: win %lu transient %lu\n", - win->id, win->transient); + DNPRINTF(SWM_D_MISC, "manage_window: window: 0x%lx, " + "transient: 0x%lx\n", win->id, win->transient); } /* get supported protocols */ @@ -4941,10 +5831,10 @@ manage_window(Window id) free(p); p = NULL; } else if (prop && win->transient == 0) { - DNPRINTF(SWM_D_PROP, "got property _SWM_WS=%s\n", prop); - ws_idx = strtonum(prop, 0, 9, &errstr); + 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]; @@ -4958,8 +5848,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; } } @@ -4973,21 +5863,22 @@ manage_window(Window id) 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); + PropModeReplace, ws_idx_str, strlen((char *)ws_idx_str)); } if (prop) XFree(prop); @@ -4995,7 +5886,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 */ @@ -5004,16 +5895,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; } } } @@ -5023,12 +5915,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; @@ -5067,7 +5959,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; @@ -5098,7 +5990,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); @@ -5111,6 +6003,11 @@ unmanage_window(struct ws_win *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); @@ -5120,10 +6017,13 @@ unmanage_window(struct ws_win *win) void focus_magic(struct ws_win *win) { - DNPRINTF(SWM_D_FOCUS, "focus_magic: %lu\n", WINID(win)); + DNPRINTF(SWM_D_FOCUS, "focus_magic: window: 0x%lx\n", WINID(win)); - if (win == NULL) + if (win == NULL) { + /* if there are no windows clear the status-bar */ + bar_check_opts(); return; + } if (win->child_trans) { /* win = parent & has a transient so focus on that */ @@ -5132,7 +6032,7 @@ focus_magic(struct ws_win *win) if (win->child_trans->take_focus) client_msg(win, takefocus); } else { - /* make sure transient hasn't dissapeared */ + /* make sure transient hasn't disappeared */ if (validate_win(win->child_trans) == 0) { focus_win(win->child_trans); if (win->child_trans->take_focus) @@ -5155,35 +6055,28 @@ focus_magic(struct ws_win *win) 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; + struct swm_region *r; 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) - spawn_custom( - root_to_region(ev->root), - &(keyfuncs[keys[i].funcid].args), - keys[i].spawn_name - ); - else - keyfuncs[keys[i].funcid].func( - root_to_region(ev->root), - &(keyfuncs[keys[i].funcid].args) - ); - } + if ((kp = key_lookup(CLEANMASK(ev->state), keysym)) == NULL) + return; + if (keyfuncs[kp->funcid].func == NULL) + return; + + r = root_to_region(ev->root); + if (kp->funcid == kf_spawn_custom) + spawn_custom(r, &(keyfuncs[kp->funcid].args), kp->spawn_name); + else + keyfuncs[kp->funcid].func(r, &(keyfuncs[kp->funcid].args)); } void @@ -5193,9 +6086,6 @@ 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; @@ -5221,9 +6111,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; @@ -5234,8 +6125,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); } } @@ -5244,14 +6133,13 @@ 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(); @@ -5266,7 +6154,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) @@ -5296,10 +6184,16 @@ 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: @@ -5379,7 +6273,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; } @@ -5407,8 +6301,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) { @@ -5436,7 +6330,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; } @@ -5449,7 +6343,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) @@ -5474,7 +6368,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)) @@ -5499,9 +6393,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) @@ -5516,19 +6414,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; @@ -5542,7 +6445,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); @@ -5579,7 +6482,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++) @@ -5600,15 +6503,15 @@ 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 @@ -5616,24 +6519,27 @@ clientmessage(XEvent *e) } if (ev->message_type == ewmh[_NET_MOVERESIZE_WINDOW].atom) { DNPRINTF(SWM_D_EVENT, - "clientmessage: _NET_MOVERESIZE_WINDOW \n"); + "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], @@ -5653,7 +6559,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); } @@ -5723,7 +6629,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) { @@ -5735,7 +6642,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; @@ -5762,7 +6669,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) { @@ -5814,7 +6721,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; @@ -5824,7 +6731,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); @@ -5889,10 +6796,12 @@ setup_screens(void) int i, j, k; int errorbase, major, minor; struct workspace *ws; + XGCValues gcv; if ((screens = calloc(ScreenCount(display), sizeof(struct swm_screen))) == NULL) - errx(1, "calloc: screens"); + err(1, "setup_screens: calloc: failed to allocate memory for " + "screens"); /* initial Xrandr setup */ xrandr_support = XRRQueryExtension(display, @@ -5903,7 +6812,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); @@ -5916,6 +6825,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)); @@ -5925,6 +6841,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; @@ -5936,6 +6853,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); @@ -5949,19 +6867,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 @@ -5993,7 +6906,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; @@ -6006,11 +6918,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"); @@ -6036,6 +6951,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); @@ -6043,10 +6959,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(); @@ -6063,13 +6979,16 @@ 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(); @@ -6098,22 +7017,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; } } @@ -6130,8 +7051,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); @@ -6160,7 +7081,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);