X-Git-Url: https://jasonwoof.com/gitweb/?a=blobdiff_plain;f=scrotwm.c;h=52927256bc94e257c85a4810abee9c771b8c4852;hb=db031f1cc5b0329becb5ffd12eeadc429dc08495;hp=cae3cacefaca46c1acac6cea6fbab31cd2f39126;hpb=ce1a2d182eac1d8163206f52a814c4054f98c5a2;p=spectrwm.git diff --git a/scrotwm.c b/scrotwm.c index cae3cac..5292725 100644 --- a/scrotwm.c +++ b/scrotwm.c @@ -3,6 +3,7 @@ * Copyright (c) 2009 Marco Peereboom * Copyright (c) 2009 Ryan McBride * Copyright (c) 2009 Darrin Chandler + * Copyright (c) 2009 Pierre-Yves Ritschard * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -19,7 +20,7 @@ /* * Much code and ideas taken from dwm under the following license: * MIT/X Consortium License - * + * * 2006-2008 Anselm R Garbe * 2006-2007 Sander van Dijk * 2006-2007 Jukka Salmi @@ -29,17 +30,17 @@ * 2007-2008 Enno Gottox Boland * 2007-2008 Peter Hartlich * 2008 Martin Hurton - * + * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: - * + * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. - * + * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL @@ -51,7 +52,7 @@ static const char *cvstag = "$scrotwm$"; -#define SWM_VERSION "0.9.2" +#define SWM_VERSION "1.0" #include #include @@ -95,8 +96,8 @@ static const char *cvstag = "$scrotwm$"; /* #define SWM_DEBUG */ #ifdef SWM_DEBUG -#define DPRINTF(x...) do { if (swm_debug) fprintf(stderr, x); } while(0) -#define DNPRINTF(n,x...) do { if (swm_debug & n) fprintf(stderr, x); } while(0) +#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 @@ -108,6 +109,9 @@ static const char *cvstag = "$scrotwm$"; #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 u_int32_t swm_debug = 0 | SWM_D_MISC @@ -121,6 +125,9 @@ u_int32_t swm_debug = 0 | SWM_D_CLASS | SWM_D_KEY | SWM_D_QUIRK + | SWM_D_SPAWN + | SWM_D_EVENTQ + | SWM_D_CONF ; #else #define DPRINTF(x...) @@ -135,7 +142,7 @@ u_int32_t swm_debug = 0 #define SWM_PROPLEN (16) #define SWM_FUNCNAME_LEN (32) #define SWM_KEYS_LEN (255) -#define SWM_QUIRK_LEN (32) +#define SWM_QUIRK_LEN (64) #define X(r) (r)->g.x #define Y(r) (r)->g.y #define WIDTH(r) (r)->g.w @@ -143,16 +150,18 @@ u_int32_t swm_debug = 0 #define SWM_MAX_FONT_STEPS (3) #ifndef SWM_LIB -#define SWM_LIB "/usr/X11R6/lib/swmhack.so" +#define SWM_LIB "/usr/local/lib/libswmhack.so" #endif char **start_argv; Atom astate; Atom aprot; Atom adelete; +Atom takefocus; +volatile sig_atomic_t running = 1; +int outputs = 0; int (*xerrorxlib)(Display *, XErrorEvent *); int other_wm; -int running = 1; int ss_enabled = 0; int xrandr_support; int xrandr_eventbase; @@ -164,6 +173,7 @@ int cycle_empty = 0; int cycle_visible = 0; int term_width = 0; int font_adjusted = 0; +unsigned int mod_key = MODKEY; /* dialog windows */ double dialog_ratio = .6; @@ -181,25 +191,17 @@ int bar_extra = 1; int bar_extra_running = 0; int bar_verbose = 1; int bar_height = 0; +int stack_enabled = 1; int clock_enabled = 1; +int title_name_enabled = 0; +int title_class_enabled = 0; pid_t bar_pid; GC bar_gc; XGCValues bar_gcv; int bar_fidx = 0; XFontStruct *bar_fs; -char *bar_fonts[] = { - "-*-terminus-medium-*-*-*-*-*-*-*-*-*-*-*", - "-*-times-medium-r-*-*-*-*-*-*-*-*-*-*", - NULL -}; - -/* terminal + args */ -char *spawn_term[] = { "xterm", NULL }; -char *spawn_screenshot[] = { "screenshot.sh", NULL, NULL }; -char *spawn_lock[] = { "xlock", NULL }; -char *spawn_initscr[] = { "initscreen.sh", NULL }; -char *spawn_menu[] = { "dmenu_run", "-fn", NULL, "-nb", NULL, - "-nf", NULL, "-sb", NULL, "-sf", NULL, NULL }; +char *bar_fonts[] = { NULL, NULL, NULL, NULL };/* XXX Make fully dynamic */ +char *spawn_term[] = { NULL, NULL }; /* XXX Make fully dynamic */ #define SWM_MENU_FN (2) #define SWM_MENU_NB (4) @@ -222,24 +224,26 @@ struct workspace; struct swm_region { TAILQ_ENTRY(swm_region) entry; struct swm_geometry g; - struct workspace *ws; /* current workspace on this region */ + struct workspace *ws; /* current workspace on this region */ struct swm_screen *s; /* screen idx */ Window bar_window; -}; +}; TAILQ_HEAD(swm_region_list, swm_region); struct ws_win { TAILQ_ENTRY(ws_win) entry; Window id; + Window transient; + struct ws_win *child_trans; /* transient child window */ struct swm_geometry g; - int got_focus; int floating; - int transient; int manual; int font_size_boundary[SWM_MAX_FONT_STEPS]; int font_steps; int last_inc; int can_delete; + int take_focus; + int java; unsigned long quirks; struct workspace *ws; /* always valid */ struct swm_screen *s; /* always valid, never changes */ @@ -302,6 +306,7 @@ enum keyfuncid { kf_version, kf_spawn_lock, kf_spawn_initscr, + kf_spawn_custom, kf_invalid }; @@ -315,25 +320,22 @@ void max_stack(struct workspace *, struct swm_geometry *); void grabbuttons(struct ws_win *, int); void new_region(struct swm_screen *, int, int, int, int); -void update_modkey(unsigned int); -/* user functions / key binding */ -int bindmatch(const char *var, const char *name, unsigned int currmod, char *keystr, - enum keyfuncid *kfid, unsigned int *mod, KeySym *ks); -void setkeybinding(unsigned int mod, KeySym ks, enum keyfuncid kfid); -/* quirks */ -int quirkmatch(const char *var, const char *name, char *qstr, - char *qclass, char *qname, unsigned long *qquirk); -void setquirk(const char *class, const char *name, const int quirk); +void unmanage_window(struct ws_win *); 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; } layouts[] = { /* stack, configure */ - { vertical_stack, vertical_config}, - { horizontal_stack, horizontal_config}, - { max_stack, NULL}, - { NULL, NULL}, + { vertical_stack, vertical_config, 0, "[|]" }, + { horizontal_stack, horizontal_config, 0, "[-]" }, + { max_stack, NULL, + SWM_L_FOCUSPREV | SWM_L_MAPONFOCUS, "[ ]"}, + { NULL, NULL, 0}, }; #define SWM_H_SLICE (32) @@ -347,6 +349,7 @@ struct workspace { struct ws_win *focus; /* may be NULL */ struct ws_win *focus_prev; /* may be NULL */ struct swm_region *r; /* may be NULL */ + struct swm_region *old_r; /* may be NULL */ struct ws_win_list winlist; /* list of windows in ws */ /* stacker state */ @@ -381,8 +384,6 @@ struct swm_screen { struct swm_screen *screens; int num_screens; -struct ws_win *cur_focus = NULL; - /* args to functions */ union arg { int id; @@ -415,8 +416,8 @@ union arg { /* quirks */ struct quirk { - char class[SWM_QUIRK_LEN]; - char name[SWM_QUIRK_LEN]; + char *class; + char *name; unsigned long quirk; #define SWM_Q_FLOAT (1<<0) /* float this window */ #define SWM_Q_TRANSSZ (1<<1) /* transiend window size too small */ @@ -428,6 +429,127 @@ int quirks_size = 0, quirks_length = 0; struct quirk *quirks = NULL; /* events */ +#ifdef SWM_DEBUG +void +dumpevent(XEvent *e) +{ + char *name = NULL; + + switch (e->type) { + case KeyPress: + name = "KeyPress"; + break; + case KeyRelease: + name = "KeyRelease"; + break; + case ButtonPress: + name = "ButtonPress"; + break; + case ButtonRelease: + name = "ButtonRelease"; + break; + case MotionNotify: + name = "MotionNotify"; + break; + case EnterNotify: + name = "EnterNotify"; + break; + case LeaveNotify: + name = "LeaveNotify"; + break; + case FocusIn: + name = "FocusIn"; + break; + case FocusOut: + name = "FocusOut"; + break; + case KeymapNotify: + name = "KeymapNotify"; + break; + case Expose: + name = "Expose"; + break; + case GraphicsExpose: + name = "GraphicsExpose"; + break; + case NoExpose: + name = "NoExpose"; + break; + case VisibilityNotify: + name = "VisibilityNotify"; + break; + case CreateNotify: + name = "CreateNotify"; + break; + case DestroyNotify: + name = "DestroyNotify"; + break; + case UnmapNotify: + name = "UnmapNotify"; + break; + case MapNotify: + name = "MapNotify"; + break; + case MapRequest: + name = "MapRequest"; + break; + case ReparentNotify: + name = "ReparentNotify"; + break; + case ConfigureNotify: + name = "ConfigureNotify"; + break; + case ConfigureRequest: + name = "ConfigureRequest"; + break; + case GravityNotify: + name = "GravityNotify"; + break; + case ResizeRequest: + name = "ResizeRequest"; + break; + case CirculateNotify: + name = "CirculateNotify"; + break; + case CirculateRequest: + name = "CirculateRequest"; + break; + case PropertyNotify: + name = "PropertyNotify"; + break; + case SelectionClear: + name = "SelectionClear"; + break; + case SelectionRequest: + name = "SelectionRequest"; + break; + case SelectionNotify: + name = "SelectionNotify"; + break; + case ColormapNotify: + name = "ColormapNotify"; + break; + case ClientMessage: + name = "ClientMessage"; + break; + case MappingNotify: + name = "MappingNotify"; + break; + } + + 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)); +} +#else +#define dumpevent(e) +#endif /* SWM_DEBUG */ + void expose(XEvent *); void keypress(XEvent *); void buttonpress(XEvent *); @@ -437,6 +559,7 @@ void destroynotify(XEvent *); void enternotify(XEvent *); void focusin(XEvent *); void focusout(XEvent *); +void mapnotify(XEvent *); void mappingnotify(XEvent *); void maprequest(XEvent *); void propertynotify(XEvent *); @@ -453,6 +576,7 @@ void (*handler[LASTEvent])(XEvent *) = { [EnterNotify] = enternotify, [FocusIn] = focusin, [FocusOut] = focusout, + [MapNotify] = mapnotify, [MappingNotify] = mappingnotify, [MapRequest] = maprequest, [PropertyNotify] = propertynotify, @@ -460,6 +584,40 @@ void (*handler[LASTEvent])(XEvent *) = { [VisibilityNotify] = visibilitynotify, }; +void +sighdlr(int sig) +{ + pid_t pid; + + switch (sig) { + case SIGCHLD: + while ((pid = waitpid(WAIT_ANY, NULL, WNOHANG)) != -1) { + DNPRINTF(SWM_D_MISC, "reaping: %d\n", pid); + if (pid <= 0) + break; + } + break; + case SIGINT: + case SIGTERM: + case SIGHUP: + case SIGQUIT: + running = 0; + break; + } +} + +void +installsignal(int sig, char *name) +{ + struct sigaction sa; + + sa.sa_handler = sighdlr; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + if (sigaction(sig, &sa, NULL) == -1) + err(1, "could not install %s handler", name); +} + unsigned long name_to_color(char *colorname) { @@ -490,13 +648,16 @@ setscreencolor(char *val, int i, int c) { if (i > 0 && i <= ScreenCount(display)) { 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"); } else if (i == -1) { - for (i = 0; i < ScreenCount(display); i++) + for (i = 0; i < ScreenCount(display); i++) { screens[i].c[c].color = name_to_color(val); - if ((screens[i - 1].c[c].name = strdup(val)) == NULL) + free(screens[i].c[c].name); + if ((screens[i].c[c].name = strdup(val)) == NULL) errx(1, "strdup"); + } } else errx(1, "invalid screen index: %d out of bounds (maximum %d)\n", i, ScreenCount(display)); @@ -525,188 +686,8 @@ custom_region(char *val) errx(1, "region %ux%u+%u+%u not within screen boundaries " "(%ux%u)\n", w, h, x, y, DisplayWidth(display, sidx), DisplayHeight(display, sidx)); - - new_region(&screens[sidx], x, y, w, h); -} - -int -varmatch(char *var, char *name, int *index) -{ - char *p, buf[5]; - int i; - - i = strncmp(var, name, 255); - if (index == NULL) - return (i); - - *index = -1; - if (i <= 0) - return (i); - p = var + strlen(name); - if (*p++ != '[') - return (i); - bzero(buf, sizeof buf); - i = 0; - while (isdigit(*p) && i < sizeof buf) - buf[i++] = *p++; - if (i == 0 || i >= sizeof buf || *p != ']') - return (1); - *index = strtonum(buf, 0, 99, NULL); - return (0); -} - -/* conf file stuff */ -#define SWM_CONF_WS "\n= \t" -#define SWM_CONF_FILE "scrotwm.conf" -int -conf_load(char *filename) -{ - FILE *config; - char *line, *cp, *var, *val; - size_t len, lineno = 0; - int i, sc; - unsigned int modkey = MODKEY, modmask; - KeySym ks; - enum keyfuncid kfid; - char class[SWM_QUIRK_LEN]; - char name[SWM_QUIRK_LEN]; - unsigned long quirk; - - DNPRINTF(SWM_D_MISC, "conf_load: filename %s\n", filename); - - if (filename == NULL) - return (1); - - if ((config = fopen(filename, "r")) == NULL) - return (1); - - for (sc = ScreenCount(display);;) { - if ((line = fparseln(config, &len, &lineno, NULL, 0)) == NULL) - if (feof(config)) - break; - cp = line; - cp += (long)strspn(cp, SWM_CONF_WS); - if (cp[0] == '\0') { - /* empty line */ - free(line); - continue; - } - if ((var = strsep(&cp, SWM_CONF_WS)) == NULL || cp == NULL) - break; - cp += (long)strspn(cp, SWM_CONF_WS); - if ((val = strsep(&cp, SWM_CONF_WS)) == NULL) - break; - - DNPRINTF(SWM_D_MISC, "conf_load: %s=%s\n",var ,val); - switch (var[0]) { - case 'b': - if (!strncmp(var, "bar_enabled", strlen("bar_enabled"))) - bar_enabled = atoi(val); - else if (!varmatch(var, "bar_border", &i)) - setscreencolor(val, i, SWM_S_COLOR_BAR_BORDER); - else if (!varmatch(var, "bar_color", &i)) - setscreencolor(val, i, SWM_S_COLOR_BAR); - else if (!varmatch(var, "bar_font_color", &i)) - setscreencolor(val, i, SWM_S_COLOR_BAR_FONT); - else if (!strncmp(var, "bar_font", strlen("bar_font"))) - asprintf(&bar_fonts[0], "%s", val); - else if (!strncmp(var, "bar_action", strlen("bar_action"))) - asprintf(&bar_argv[0], "%s", val); - else if (!strncmp(var, "bar_delay", strlen("bar_delay"))) - bar_delay = atoi(val); - else if (!bindmatch(var, "bind", modkey, val, - &kfid, &modmask, &ks)) - setkeybinding(modmask, ks, kfid); - else - goto bad; - break; - - case 'c': - if (!strncmp(var, "clock_enabled", strlen("clock_enabled"))) - clock_enabled = atoi(val); - else if (!varmatch(var, "color_focus", &i)) - setscreencolor(val, i, SWM_S_COLOR_FOCUS); - else if (!varmatch(var, "color_unfocus", &i)) - setscreencolor(val, i, SWM_S_COLOR_UNFOCUS); - else if (!strncmp(var, "cycle_empty", strlen("cycle_empty"))) - cycle_visible = atoi(val); - else if (!strncmp(var, "cycle_visible", strlen("cycle_visible"))) - cycle_visible = atoi(val); - else - goto bad; - break; - - case 'd': - if (!strncmp(var, "dialog_ratio", - strlen("dialog_ratio"))) { - dialog_ratio = atof(val); - if (dialog_ratio > 1.0 || dialog_ratio <= .3) - dialog_ratio = .6; - } else - goto bad; - break; - - case 'm': - if (!strncmp(var, "modkey", strlen("modkey"))) { - modkey = MODKEY; - if (!strncmp(val, "Mod2", strlen("Mod2"))) - modkey = Mod2Mask; - else if (!strncmp(val, "Mod3", strlen("Mod3"))) - modkey = Mod3Mask; - else if (!strncmp(val, "Mod4", strlen("Mod4"))) - modkey = Mod4Mask; - else - modkey = Mod1Mask; - update_modkey(modkey); - } else - goto bad; - break; - - case 'q': - if (!quirkmatch(var, "quirk", val, class, name, &quirk)) - setquirk(class, name, quirk); - else - goto bad; - break; - - case 'r': - if (!strncmp(var, "region", strlen("region"))) - custom_region(val); - else - goto bad; - break; - - case 's': - if (!strncmp(var, "spawn_term", strlen("spawn_term"))) - asprintf(&spawn_term[0], "%s", val); - else if (!strncmp(var, "screenshot_enabled", - strlen("screenshot_enabled"))) - ss_enabled = atoi(val); - else if (!strncmp(var, "screenshot_app", - strlen("screenshot_app"))) - asprintf(&spawn_screenshot[0], "%s", val); - else - goto bad; - break; - - case 't': - if (!strncmp(var, "term_width", strlen("term_width"))) - term_width = atoi(val); - else - goto bad; - break; - - default: - goto bad; - } - free(line); - } - - fclose(config); - return (0); -bad: - errx(1, "invalid conf file entry: %s=%s", var, val); + new_region(&screens[sidx], x, y, w, h); } void @@ -746,6 +727,35 @@ bar_extra_stop(void) } void +bar_class_name(char *s, ssize_t sz, struct ws_win *cur_focus) +{ + int do_class, do_name; + Status status; + XClassHint *xch = NULL; + + if ((title_name_enabled == 1 || title_class_enabled == 1) && + cur_focus != NULL) { + if ((xch = XAllocClassHint()) == NULL) + goto out; + status = XGetClassHint(display, cur_focus->id, xch); + if (status == BadWindow || status == BadAlloc) + goto out; + do_class = (title_class_enabled && xch->res_class != NULL); + do_name = (title_name_enabled && xch->res_name != NULL); + if (do_class) + strlcat(s, xch->res_class, sz); + if (do_class && do_name) + strlcat(s, ":", sz); + if (do_name) + strlcat(s, xch->res_name, sz); + strlcat(s, " ", sz); + } +out: + if (xch) + XFree(xch); +} + +void bar_update(void) { time_t tmt; @@ -756,6 +766,7 @@ bar_update(void) char s[SWM_BAR_MAX]; char loc[SWM_BAR_MAX]; char *b; + char *stack = ""; if (bar_enabled == 0) return; @@ -781,15 +792,22 @@ bar_update(void) localtime_r(&tmt, &tm); strftime(s, sizeof s, "%a %b %d %R %Z %Y ", &tm); } + for (i = 0; i < ScreenCount(display); i++) { x = 1; TAILQ_FOREACH(r, &screens[i].rl, entry) { - snprintf(loc, sizeof loc, "%d:%d %s%s %s", - x++, r->ws->idx + 1, s, bar_ext, bar_vertext); + if (r && r->ws) + bar_class_name(s, sizeof s, r->ws->focus); + + if (stack_enabled) + stack = r->ws->cur_layout->name; + + snprintf(loc, sizeof loc, "%d:%d %s %s %s %s", + x++, r->ws->idx + 1, stack, s, bar_ext, + bar_vertext); bar_print(r, loc); } } - XSync(display, False); alarm(bar_delay); } @@ -891,7 +909,7 @@ bar_setup(struct swm_region *r) errx(1, "couldn't load font"); bar_height = bar_fs->ascent + bar_fs->descent + 3; - r->bar_window = XCreateSimpleWindow(display, + r->bar_window = XCreateSimpleWindow(display, r->s->root, X(r), Y(r), WIDTH(r) - 2, bar_height - 2, 1, r->s->c[SWM_S_COLOR_BAR_BORDER].color, r->s->c[SWM_S_COLOR_BAR].color); @@ -907,12 +925,68 @@ bar_setup(struct swm_region *r) bar_refresh(); } +Bool +set_win_notify_cb(Display *d, XEvent *e, char *arg) +{ + struct ws_win *win = (struct ws_win *)arg; + + if (win && win->id == e->xany.window && e->xany.type == PropertyNotify) + return (True); + return (False); +} + +void +set_win_state(struct ws_win *win, long state) +{ + long data[] = {state, None}; + XEvent ev; + XWindowAttributes wa; + + DNPRINTF(SWM_D_EVENT, "set_win_state: window: %lu\n", win->id); + + if (win == NULL) + return; + /* make sure we drain everything */ + XSync(display, True); + + /* make sure we still exist too */ + if (XGetWindowAttributes(display, win->id, &wa) == BadWindow) + return; + + XChangeProperty(display, win->id, astate, astate, 32, PropModeReplace, + (unsigned char *)data, 2); + + /* wait for completion of XChangeProperty */ + while (XCheckIfEvent(display, &ev, set_win_notify_cb, (char *)win)) + ; +} + +long +getstate(Window w) +{ + int format, status; + long result = -1; + unsigned char *p = NULL; + unsigned long n, extra; + Atom real; + + status = XGetWindowProperty(display, w, astate, 0L, 2L, False, astate, + &real, &format, &n, &extra, (unsigned char **)&p); + if (status != Success) + return (-1); + if (n != 0) + result = *((long *)p); + XFree(p); + return (result); +} + void version(struct swm_region *r, union arg *args) { bar_version = !bar_version; if (bar_version) - strlcpy(bar_vertext, cvstag, sizeof bar_vertext); + snprintf(bar_vertext, sizeof bar_vertext, "Version: %s CVS: %s", + SWM_VERSION, cvstag); else strlcpy(bar_vertext, "", sizeof bar_vertext); bar_update(); @@ -959,6 +1033,21 @@ count_win(struct workspace *ws, int count_transient) { struct ws_win *win; int count = 0; + int state; + + /* + * Under stress conditions windows sometimes do not get removed from + * the managed list quickly enough. Use a very large hammer to get rid + * of them. A smaller hammer would be nice. + */ + TAILQ_FOREACH(win, &ws->winlist, entry) { + state = getstate(win->id); + if (state == -1) { + DNPRINTF(SWM_D_MISC, "count_win:removing: %lu\n", + win->id); + unmanage_window(win); + } + } TAILQ_FOREACH(win, &ws->winlist, entry) { if (count_transient == 0 && win->floating) @@ -979,6 +1068,44 @@ quit(struct swm_region *r, union arg *args) running = 0; } +Bool +unmap_window_cb(Display *d, XEvent *e, char *arg) +{ + struct ws_win *win = (struct ws_win *)arg; + + if (win && win->id == e->xany.window && e->xany.type == UnmapNotify) + return (True); + return (False); +} + +void +unmap_window(struct ws_win *win) +{ + XEvent ev; + XWindowAttributes wa; + + if (win == NULL) + return; + + /* make sure we still exist too */ + if (XGetWindowAttributes(display, win->id, &wa) == BadWindow) + return; + + /* don't unmap again */ + if (wa.map_state == IsUnmapped && getstate(win->id) == IconicState) + return; + + /* java shits itself when windows are set to iconic state */ + if (win->java == 0) + set_win_state(win, IconicState); + + XUnmapWindow(display, win->id); + + /* make sure we wait for XUnmapWindow completion */ + while (XCheckIfEvent(display, &ev, unmap_window_cb, (char *)win)) + ; +} + void unmap_all(void) { @@ -988,7 +1115,7 @@ unmap_all(void) for (i = 0; i < ScreenCount(display); i++) for (j = 0; j < SWM_WS_MAX; j++) TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry) - XUnmapWindow(display, win->id); + unmap_window(win); } void @@ -1051,7 +1178,7 @@ root_to_region(Window root) if (screens[i].root == root) break; - if (XQueryPointer(display, screens[i].root, + if (XQueryPointer(display, screens[i].root, &rr, &cr, &x, &y, &wx, &wy, &mask) != False) { /* choose a region based on pointer location */ TAILQ_FOREACH(r, &screens[i].rl, entry) @@ -1095,7 +1222,7 @@ spawn(struct swm_region *r, union arg *args) if (fork() == 0) { if (display) close(ConnectionNumber(display)); - setenv("LD_PRELOAD", SWM_LIB, 1); + setenv("LD_PRELOAD", SWM_LIB, 1); if (asprintf(&ret, "%d", r->ws->idx)) { setenv("_SWM_WS", ret, 1); free(ret); @@ -1117,7 +1244,6 @@ spawn(struct swm_region *r, union arg *args) } exit(0); } - wait(0); } void @@ -1131,39 +1257,22 @@ spawnterm(struct swm_region *r, union arg *args) } void -spawnmenu(struct swm_region *r, union arg *args) -{ - DNPRINTF(SWM_D_MISC, "spawnmenu\n"); - - spawn_menu[SWM_MENU_FN] = bar_fonts[bar_fidx]; - spawn_menu[SWM_MENU_NB] = r->s->c[SWM_S_COLOR_BAR].name; - spawn_menu[SWM_MENU_NF] = r->s->c[SWM_S_COLOR_BAR_FONT].name; - spawn_menu[SWM_MENU_SB] = r->s->c[SWM_S_COLOR_BAR_BORDER].name; - spawn_menu[SWM_MENU_SF] = r->s->c[SWM_S_COLOR_BAR].name; - - spawn(r, args); -} - -void unfocus_win(struct ws_win *win) { if (win == NULL) return; - if (win->ws->focus != win && win->ws->focus != NULL) - win->ws->focus_prev = win->ws->focus; - if (win->ws->r == NULL) return; + if (win->ws->focus == win) { + win->ws->focus = NULL; + win->ws->focus_prev = win; + } + grabbuttons(win, 0); XSetWindowBorder(display, win->id, win->ws->r->s->c[SWM_S_COLOR_UNFOCUS].color); - win->got_focus = 0; - if (win->ws->focus == win) - win->ws->focus = NULL; - if (cur_focus == win) - cur_focus = NULL; } void @@ -1188,25 +1297,19 @@ focus_win(struct ws_win *win) if (win == NULL) return; - if (cur_focus) - unfocus_win(cur_focus); - if (win->ws->focus) { - /* probably shouldn't happen due to the previous unfocus_win */ - DNPRINTF(SWM_D_FOCUS, "unfocusing win->ws->focus: %lu\n", - win->ws->focus->id); - unfocus_win(win->ws->focus); - } + /* use big hammer to make sure it works under all use cases */ + unfocus_all(); win->ws->focus = win; + if (win->ws->r != NULL) { - cur_focus = win; - if (!win->got_focus) { - XSetWindowBorder(display, win->id, - win->ws->r->s->c[SWM_S_COLOR_FOCUS].color); - grabbuttons(win, 1); - } - win->got_focus = 1; - XSetInputFocus(display, win->id, - RevertToPointerRoot, CurrentTime); + XSetWindowBorder(display, win->id, + win->ws->r->s->c[SWM_S_COLOR_FOCUS].color); + grabbuttons(win, 1); + if (win->ws->cur_layout->flags & SWM_L_MAPONFOCUS) + XMapRaised(display, win->id); + if (win->java == 0) + XSetInputFocus(display, win->id, + RevertToPointerRoot, CurrentTime); } } @@ -1215,9 +1318,12 @@ switchws(struct swm_region *r, union arg *args) { int wsid = args->id; struct swm_region *this_r, *other_r; - struct ws_win *win; + struct ws_win *win, *winfocus = NULL, *parent = NULL; struct workspace *new_ws, *old_ws; + if (!(r && r->s)) + return; + this_r = r; old_ws = this_r->ws; new_ws = &this_r->s->ws[wsid]; @@ -1226,21 +1332,37 @@ switchws(struct swm_region *r, union arg *args) "%d -> %d\n", r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), old_ws->idx, wsid); - if (new_ws == old_ws) + if (new_ws == NULL || old_ws == NULL || new_ws == old_ws) return; + /* get focus window */ + if (new_ws->focus) + winfocus = new_ws->focus; + else if (new_ws->focus_prev) + winfocus = new_ws->focus_prev; + else + winfocus = TAILQ_FIRST(&new_ws->winlist); + other_r = new_ws->r; - if (!other_r) { + if (other_r == NULL) { /* if the other workspace is hidden, switch windows */ - /* map new window first to prevent ugly blinking */ + if (old_ws->r != NULL) + old_ws->old_r = old_ws->r; old_ws->r = NULL; old_ws->restack = 1; - TAILQ_FOREACH(win, &new_ws->winlist, entry) - XMapRaised(display, win->id); + /* + * Map new windows first if they were here before + * to minimize ugly blinking. + */ + if (new_ws->old_r == this_r) + TAILQ_FOREACH(win, &new_ws->winlist, entry) + if (!(win->ws->cur_layout->flags & + SWM_L_MAPONFOCUS)) + XMapRaised(display, win->id); TAILQ_FOREACH(win, &old_ws->winlist, entry) - XUnmapWindow(display, win->id); + unmap_window(win); } else { other_r->ws = old_ws; old_ws->r = other_r; @@ -1249,12 +1371,18 @@ switchws(struct swm_region *r, union arg *args) new_ws->r = this_r; ignore_enter = 1; - /* set focus */ - if (new_ws->focus == NULL) - new_ws->focus = TAILQ_FIRST(&new_ws->winlist); - if (new_ws->focus) - focus_win(new_ws->focus); stack(); + if (winfocus) { + /* make sure we see the parent window */ + if (winfocus->transient) { + parent = find_window(winfocus->transient); + if (parent) + focus_win(parent); + } + + focus_win(winfocus); + } + ignore_enter = 0; bar_update(); } @@ -1299,8 +1427,14 @@ cyclews(struct swm_region *r, union arg *args) void cyclescr(struct swm_region *r, union arg *args) { - struct swm_region *rr; - int i; + struct swm_region *rr = NULL; + struct workspace *ws = NULL; + struct ws_win *winfocus = NULL; + int i, x, y; + + /* do nothing if we don't have more than one screen */ + if (!(ScreenCount(display) > 1 || outputs > 1)) + return; i = r->s->idx; switch (args->id) { @@ -1317,22 +1451,43 @@ cyclescr(struct swm_region *r, union arg *args) default: return; }; + if (rr == NULL) + return; + + ws = rr->ws; + winfocus = ws->focus; + if (winfocus == NULL) + winfocus = ws->focus_prev; + if (winfocus) { + /* use window coordinates */ + x = winfocus->g.x + 1; + y = winfocus->g.y + 1; + } else { + /* use region coordinates */ + x = rr->g.x + 1; + y = rr->g.y + 1 + bar_enabled ? bar_height : 0; + } + unfocus_all(); XSetInputFocus(display, PointerRoot, RevertToPointerRoot, CurrentTime); - XWarpPointer(display, None, rr->s[i].root, 0, 0, 0, 0, rr->g.x, - rr->g.y + bar_enabled ? bar_height : 0); + XWarpPointer(display, None, rr->s[i].root, 0, 0, 0, 0, x, y); + + focus_win(winfocus); } void swapwin(struct swm_region *r, union arg *args) { struct ws_win *target, *source; + struct ws_win *cur_focus; 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); + + cur_focus = r->ws->focus; if (cur_focus == NULL) return; @@ -1348,7 +1503,7 @@ swapwin(struct swm_region *r, union arg *args) else TAILQ_INSERT_BEFORE(target, source, entry); break; - case SWM_ARG_ID_SWAPNEXT: + case SWM_ARG_ID_SWAPNEXT: target = TAILQ_NEXT(source, entry); TAILQ_REMOVE(wl, source, entry); if (target == NULL) @@ -1360,8 +1515,8 @@ swapwin(struct swm_region *r, union arg *args) target = TAILQ_FIRST(wl); if (target == source) { if (source->ws->focus_prev != NULL && - source->ws->focus_prev != target) - + source->ws->focus_prev != target) + source = source->ws->focus_prev; else return; @@ -1386,8 +1541,11 @@ focus(struct swm_region *r, union arg *args) { struct ws_win *winfocus, *winlostfocus; struct ws_win_list *wl; + struct ws_win *cur_focus; DNPRINTF(SWM_D_FOCUS, "focus: id %d\n", args->id); + + cur_focus = r->ws->focus; if (cur_focus == NULL) return; @@ -1423,23 +1581,34 @@ focus(struct swm_region *r, union arg *args) if (winfocus == winlostfocus || winfocus == NULL) return; - XMapRaised(display, winfocus->id); focus_win(winfocus); - XSync(display, False); } void cycle_layout(struct swm_region *r, union arg *args) { struct workspace *ws = r->ws; + struct ws_win *winfocus, *parent = NULL; 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]; + ignore_enter = 1; stack(); + /* make sure we see the parent window */ + if (winfocus) { + if (winfocus->transient) + parent = find_window(winfocus->transient); + if (parent) + focus_win(parent); + focus_win(winfocus); + } + ignore_enter = 0; } void @@ -1478,7 +1647,7 @@ stack(void) { if (bar_enabled) { g.y += bar_height; g.h -= bar_height; - } + } r->ws->restack = 0; r->ws->cur_layout->l_stack(r->ws, &g); @@ -1486,7 +1655,6 @@ stack(void) { } if (font_adjusted) font_adjusted--; - XSync(display, False); } void @@ -1495,6 +1663,9 @@ stack_floater(struct ws_win *win, struct swm_region *r) unsigned int mask; XWindowChanges wc; + if (win == NULL) + return; + bzero(&wc, sizeof wc); mask = CWX | CWY | CWBorderWidth | CWWidth | CWHeight; if ((win->quirks & SWM_Q_FULLSCREEN) && (win->g.w == WIDTH(r)) && @@ -1516,6 +1687,10 @@ stack_floater(struct ws_win *win, struct swm_region *r) wc.y = (HEIGHT(r) - win->g.h) / 2; } + /* adjust for region */ + wc.x += r->g.x; + wc.y += r->g.y; + DNPRINTF(SWM_D_STACK, "stack_floater: win %lu x %d y %d w %d h %d\n", win->id, wc.x, wc.y, wc.width, wc.height); @@ -1532,7 +1707,7 @@ adjust_font(struct ws_win *win) if (!(win->quirks & SWM_Q_XTERM_FONTADJ) || win->floating || win->transient) return; - + if (win->sh.width_inc && win->last_inc != win->sh.width_inc && win->g.w / win->sh.width_inc < term_width && win->font_steps < SWM_MAX_FONT_STEPS) { @@ -1561,24 +1736,21 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip) { XWindowChanges wc; struct swm_geometry win_g, r_g = *g; - struct ws_win *win, *winfocus; - int i, j, s, stacks; + struct ws_win *win; + int i, j, s, stacks; int w_inc = 1, h_inc, w_base = 1, h_base; int hrh, extra = 0, h_slice, last_h = 0; int split, colno, winno, mwin, msize, mscale; - int remain, missing, v_slice;; + int remain, missing, v_slice; unsigned int mask; DNPRINTF(SWM_D_STACK, "stack_master: workspace: %d\n rot=%s flip=%s", ws->idx, rot ? "yes" : "no", flip ? "yes" : "no"); - if ((winno = count_win(ws, 0)) == 0) + winno = count_win(ws, 0); + if (winno == 0 && count_win(ws, 1) == 0) return; - if (ws->focus == NULL) - ws->focus = TAILQ_FIRST(&ws->winlist); - winfocus = cur_focus ? cur_focus : ws->focus; - TAILQ_FOREACH(win, &ws->winlist, entry) if (win->transient == 0 && win->floating == 0) break; @@ -1624,7 +1796,7 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip) } msize = win_g.w; - if (flip) + if (flip) win_g.x += r_g.w - msize; } else { msize = -2; @@ -1664,7 +1836,7 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip) h_inc = win->sh.width_inc; h_base = win->sh.base_width; } else { - h_inc = win->sh.height_inc; + h_inc = win->sh.height_inc; h_base = win->sh.base_height; } if (j == colno - 1) { @@ -1682,7 +1854,7 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip) extra += remain; } } - + if (j == 0) win_g.y = r_g.y; else @@ -1720,9 +1892,6 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip) stack_floater(win, ws->r); XMapRaised(display, win->id); } - - if (winfocus) - focus_win(winfocus); /* has to be done outside of the loop */ } void @@ -1821,29 +1990,26 @@ horizontal_stack(struct workspace *ws, struct swm_geometry *g) /* fullscreen view */ void -max_stack(struct workspace *ws, struct swm_geometry *g) { +max_stack(struct workspace *ws, struct swm_geometry *g) +{ XWindowChanges wc; struct swm_geometry gg = *g; - struct ws_win *win, *winfocus; + struct ws_win *win, *wintrans = NULL; unsigned int mask; + int winno; DNPRINTF(SWM_D_STACK, "max_stack: workspace: %d\n", ws->idx); - if (count_win(ws, 0) == 0) + if (ws == NULL) return; - if (ws->focus == NULL) - ws->focus = TAILQ_FIRST(&ws->winlist); - winfocus = cur_focus ? cur_focus : ws->focus; + winno = count_win(ws, 0); + if (winno == 0 && count_win(ws, 1) == 0) + return; TAILQ_FOREACH(win, &ws->winlist, entry) { - if (win->transient != 0 || win->floating != 0) { - if (win == ws->focus) { - /* XXX maximize? */ - stack_floater(win, ws->r); - XMapRaised(display, win->id); - } else - XUnmapWindow(display, win->id); + if (win->transient != 0) { + wintrans = win; } else { bzero(&wc, sizeof wc); wc.border_width = 1; @@ -1854,26 +2020,33 @@ max_stack(struct workspace *ws, struct swm_geometry *g) { mask = CWX | CWY | CWWidth | CWHeight | CWBorderWidth; XConfigureWindow(display, win->id, mask, &wc); - if (win == ws->focus) { - XMapRaised(display, win->id); - } else - XUnmapWindow(display, win->id); + /* unmap only if we don't have multi screen */ + if (win != ws->focus) + if (!(ScreenCount(display) > 1 || outputs > 1)) + unmap_window(win); } } - if (winfocus) - focus_win(winfocus); /* has to be done outside of the loop */ + /* put the last transient on top */ + if (wintrans) { + stack_floater(wintrans, ws->r); + focus_win(wintrans); /* override */ + } } void send_to_ws(struct swm_region *r, union arg *args) { int wsid = args->id; - struct ws_win *win = cur_focus; + struct ws_win *win = win, *winfocus = NULL; struct workspace *ws, *nws; Atom ws_idx_atom = 0; unsigned char ws_idx_str[SWM_PROPLEN]; + if (r && r->ws) + win = r->ws->focus; + else + return; if (win == NULL) return; @@ -1882,17 +2055,22 @@ send_to_ws(struct swm_region *r, union arg *args) ws = win->ws; nws = &win->s->ws[wsid]; - XUnmapWindow(display, win->id); - /* find a window to focus */ - ws->focus = TAILQ_PREV(win, ws_win_list, entry); - if (ws->focus == NULL) - ws->focus = TAILQ_FIRST(&ws->winlist); - if (ws->focus == win) - ws->focus = NULL; + winfocus = TAILQ_PREV(win, ws_win_list, entry); + if (TAILQ_FIRST(&ws->winlist) == win) + winfocus = TAILQ_NEXT(win, entry); + else { + winfocus = TAILQ_PREV(win, ws_win_list, entry); + if (winfocus == NULL) + winfocus = TAILQ_LAST(&ws->winlist, ws_win_list); + } + /* out of windows in ws so focus on nws instead if we multi screen */ + if (winfocus == NULL) + if (ScreenCount(display) > 1 || outputs > 1) + winfocus = win; + unmap_window(win); TAILQ_REMOVE(&ws->winlist, win, entry); - TAILQ_INSERT_TAIL(&nws->winlist, win, entry); win->ws = nws; @@ -1912,6 +2090,8 @@ send_to_ws(struct swm_region *r, union arg *args) nws->restack = 1; stack(); + if (winfocus) + focus_win(winfocus); } void @@ -1930,33 +2110,9 @@ wkill(struct swm_region *r, union arg *args) } void -screenshot(struct swm_region *r, union arg *args) -{ - union arg a; - - DNPRINTF(SWM_D_MISC, "screenshot\n"); - - if (ss_enabled == 0) - return; - - switch (args->id) { - case SWM_ARG_ID_SS_ALL: - spawn_screenshot[1] = "full"; - break; - case SWM_ARG_ID_SS_WINDOW: - spawn_screenshot[1] = "window"; - break; - default: - return; - } - a.argv = spawn_screenshot; - spawn(r, &a); -} - -void floating_toggle(struct swm_region *r, union arg *args) { - struct ws_win *win = cur_focus; + struct ws_win *win = r->ws->focus; if (win == NULL) return; @@ -2123,14 +2279,24 @@ move(struct ws_win *win, union arg *args) } /* key definitions */ +void +dummykeyfunc(struct swm_region *r, union arg *args) +{ +}; + +void +legacyfunc(struct swm_region *r, union arg *args) +{ +}; + struct keyfunc { char name[SWM_FUNCNAME_LEN]; void (*func)(struct swm_region *r, union arg *); union arg args; -} keyfuncs[kf_invalid] = { +} keyfuncs[kf_invalid + 1] = { /* name function argument */ - { "cycle_layout", cycle_layout, {0} }, - { "stack_reset", stack_config, {.id = SWM_ARG_ID_STACKRESET} }, + { "cycle_layout", cycle_layout, {0} }, + { "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} }, { "master_add", stack_config, {.id = SWM_ARG_ID_MASTERADD} }, @@ -2143,7 +2309,7 @@ struct keyfunc { { "swap_next", swapwin, {.id = SWM_ARG_ID_SWAPNEXT} }, { "swap_prev", swapwin, {.id = SWM_ARG_ID_SWAPPREV} }, { "spawn_term", spawnterm, {.argv = spawn_term} }, - { "spawn_menu", spawnmenu, {.argv = spawn_menu} }, + { "spawn_menu", legacyfunc, {0} }, { "quit", quit, {0} }, { "restart", restart, {0} }, { "focus_main", focus, {.id = SWM_ARG_ID_FOCUSMAIN} }, @@ -2157,10 +2323,10 @@ struct keyfunc { { "ws_8", switchws, {.id = 7} }, { "ws_9", switchws, {.id = 8} }, { "ws_10", switchws, {.id = 9} }, - { "ws_next", cyclews, {.id = SWM_ARG_ID_CYCLEWS_UP} }, - { "ws_prev", cyclews, {.id = SWM_ARG_ID_CYCLEWS_DOWN} }, - { "screen_next", cyclescr, {.id = SWM_ARG_ID_CYCLESC_UP} }, - { "screen_prev", cyclescr, {.id = SWM_ARG_ID_CYCLESC_DOWN} }, + { "ws_next", cyclews, {.id = SWM_ARG_ID_CYCLEWS_UP} }, + { "ws_prev", cyclews, {.id = SWM_ARG_ID_CYCLEWS_DOWN} }, + { "screen_next", cyclescr, {.id = SWM_ARG_ID_CYCLESC_UP} }, + { "screen_prev", cyclescr, {.id = SWM_ARG_ID_CYCLESC_DOWN} }, { "mvws_1", send_to_ws, {.id = 0} }, { "mvws_2", send_to_ws, {.id = 1} }, { "mvws_3", send_to_ws, {.id = 2} }, @@ -2174,17 +2340,20 @@ struct keyfunc { { "bar_toggle", bar_toggle, {0} }, { "wind_kill", wkill, {.id = SWM_ARG_ID_KILLWINDOW} }, { "wind_del", wkill, {.id = SWM_ARG_ID_DELETEWINDOW} }, - { "screenshot_all", screenshot, {.id = SWM_ARG_ID_SS_ALL} }, - { "screenshot_wind", screenshot, {.id = SWM_ARG_ID_SS_WINDOW} }, + { "screenshot_all", legacyfunc, {0} }, + { "screenshot_wind", legacyfunc, {0} }, { "float_toggle", floating_toggle,{0} }, { "version", version, {0} }, - { "spawn_lock", spawn, {.argv = spawn_lock} }, - { "spawn_initscr", spawn, {.argv = spawn_initscr} }, + { "spawn_lock", legacyfunc, {0} }, + { "spawn_initscr", legacyfunc, {0} }, + { "spawn_custom", dummykeyfunc, {0} }, + { "invalid key func", NULL, {0} }, }; struct key { unsigned int mod; KeySym keysym; enum keyfuncid funcid; + char *spawn_name; }; int keys_size = 0, keys_length = 0; struct key *keys = NULL; @@ -2198,10 +2367,10 @@ struct button { void (*func)(struct ws_win *, union arg *); union arg args; } buttons[] = { - /* action key mouse button func args */ - { client_click, MODKEY, Button3, resize, {.id = SWM_ARG_ID_DONTCENTER} }, - { client_click, MODKEY | ShiftMask, Button3, resize, {.id = SWM_ARG_ID_CENTER} }, - { client_click, MODKEY, Button1, move, {0} }, + /* action key mouse button func args */ + { client_click, MODKEY, Button3, resize, {.id = SWM_ARG_ID_DONTCENTER} }, + { client_click, MODKEY | ShiftMask, Button3, resize, {.id = SWM_ARG_ID_CENTER} }, + { client_click, MODKEY, Button1, move, {0} }, }; void @@ -2209,6 +2378,7 @@ update_modkey(unsigned int mod) { int i; + mod_key = mod; for (i = 0; i < keys_length; i++) if (keys[i].mod & ShiftMask) keys[i].mod = mod | ShiftMask; @@ -2222,6 +2392,238 @@ update_modkey(unsigned int mod) buttons[i].mask = mod; } +/* spawn */ +struct spawn_prog { + char *name; + int argc; + char **argv; +}; + +int spawns_size = 0, spawns_length = 0; +struct spawn_prog *spawns = NULL; + +void +spawn_custom(struct swm_region *r, union arg *args, char *spawn_name) +{ + union arg a; + struct spawn_prog *prog = NULL; + int i; + char *ap, **real_args; + + DNPRINTF(SWM_D_SPAWN, "spawn_custom %s\n", spawn_name); + + /* find program */ + for (i = 0; i < spawns_length; i++) { + if (!strcasecmp(spawn_name, spawns[i].name)) + prog = &spawns[i]; + } + if (prog == NULL) { + fprintf(stderr, "spawn_custom: program %s not found\n", + spawn_name); + return; + } + + /* make room for expanded args */ + if ((real_args = calloc(prog->argc + 1, sizeof(char *))) == NULL) + err(1, "spawn_custom: calloc real_args"); + + /* 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); + if (!strcasecmp(ap, "$bar_border")) { + if ((real_args[i] = + strdup(r->s->c[SWM_S_COLOR_BAR_BORDER].name)) + == NULL) + err(1, "spawn_custom border color"); + } else if (!strcasecmp(ap, "$bar_color")) { + if ((real_args[i] = + strdup(r->s->c[SWM_S_COLOR_BAR].name)) + == NULL) + err(1, "spawn_custom bar color"); + } else if (!strcasecmp(ap, "$bar_font")) { + if ((real_args[i] = strdup(bar_fonts[bar_fidx])) + == NULL) + err(1, "spawn_custom bar fonts"); + } else if (!strcasecmp(ap, "$bar_font_color")) { + if ((real_args[i] = + strdup(r->s->c[SWM_S_COLOR_BAR_FONT].name)) + == NULL) + err(1, "spawn_custom color font"); + } else if (!strcasecmp(ap, "$color_focus")) { + if ((real_args[i] = + strdup(r->s->c[SWM_S_COLOR_FOCUS].name)) + == NULL) + err(1, "spawn_custom color focus"); + } else if (!strcasecmp(ap, "$color_unfocus")) { + if ((real_args[i] = + strdup(r->s->c[SWM_S_COLOR_UNFOCUS].name)) + == NULL) + err(1, "spawn_custom color unfocus"); + } else { + /* no match --> copy as is */ + if ((real_args[i] = strdup(ap)) == NULL) + err(1, "spawn_custom strdup(ap)"); + } + 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"); + } +#endif + + a.argv = real_args; + spawn(r, &a); + for (i = 0; i < prog->argc; i++) + free(real_args[i]); + free(real_args); +} + +void +setspawn(struct spawn_prog *prog) +{ + int i, j; + + if (prog == NULL || prog->name == NULL) + return; + + /* 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 (prog->argv == NULL) { + fprintf(stderr, + "error: setspawn: cannot find program %s", prog->name); + free(prog); + 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); + } + } + + 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); + } + } + free(prog); +} + +int +setconfspawn(char *selector, char *value, int flags) +{ + struct spawn_prog *prog; + char *vp, *cp, *word; + + DNPRINTF(SWM_D_SPAWN, "setconfspawn: [%s] [%s]\n", selector, value); + if ((prog = calloc(1, sizeof *prog)) == NULL) + err(1, "setconfspawn: calloc prog"); + prog->name = strdup(selector); + if (prog->name == NULL) + err(1, "setconfspawn prog->name"); + if ((cp = vp = strdup(value)) == NULL) + err(1, "setconfspawn: strdup(value) "); + while ((word = strsep(&cp, " \t")) != NULL) { + DNPRINTF(SWM_D_SPAWN, "setconfspawn: arg [%s]\n", word); + if (cp) + cp += (long)strspn(cp, " \t"); + if (strlen(word) > 0) { + prog->argc++; + prog->argv = realloc(prog->argv, + prog->argc * sizeof(char *)); + if ((prog->argv[prog->argc - 1] = strdup(word)) == NULL) + err(1, "setconfspawn: strdup"); + } + } + free(vp); + + setspawn(prog); + + DNPRINTF(SWM_D_SPAWN, "setconfspawn: done\n"); + return (0); +} + +void +setup_spawn(void) +{ + setconfspawn("term", "xterm", 0); + setconfspawn("screenshot_all", "screenshot.sh full", 0); + setconfspawn("screenshot_wind", "screenshot.sh window", 0); + setconfspawn("lock", "xlock", 0); + setconfspawn("initscr", "initscreen.sh", 0); + setconfspawn("menu", "dmenu_run" + " -fn $bar_font" + " -nb $bar_color" + " -nf $bar_font_color" + " -sb $bar_border" + " -sf $bar_color", 0); +} + +/* key bindings */ #define SWM_MODNAME_SIZE 32 #define SWM_KEY_WS "\n+ \t" int @@ -2229,11 +2631,20 @@ parsekeys(char *keystr, unsigned int currmod, unsigned int *mod, KeySym *ks) { char *cp, *name; KeySym uks; - if (mod == NULL || ks == NULL) - return (0); + DNPRINTF(SWM_D_KEY, "parsekeys: enter [%s]\n", keystr); + if (mod == NULL || ks == NULL) { + DNPRINTF(SWM_D_KEY, "parsekeys: no mod or key vars\n"); + return (1); + } + if (keystr == NULL || strlen(keystr) == 0) { + DNPRINTF(SWM_D_KEY, "parsekeys: no keystr\n"); + return (1); + } cp = keystr; + *ks = NoSymbol; *mod = 0; while ((name = strsep(&cp, SWM_KEY_WS)) != NULL) { + DNPRINTF(SWM_D_KEY, "parsekeys: key [%s]\n", name); if (cp) cp += (long)strspn(cp, SWM_KEY_WS); if (strncasecmp(name, "MOD", SWM_MODNAME_SIZE) == 0) @@ -2248,6 +2659,8 @@ parsekeys(char *keystr, unsigned int currmod, unsigned int *mod, KeySym *ks) *mod |= Mod4Mask; else if (strncasecmp(name, "SHIFT", SWM_MODNAME_SIZE) == 0) *mod |= ShiftMask; + else if (strncasecmp(name, "CONTROL", SWM_MODNAME_SIZE) == 0) + *mod |= ControlMask; else { *ks = XStringToKeysym(name); XConvertCase(*ks, ks, &uks); @@ -2255,51 +2668,29 @@ parsekeys(char *keystr, unsigned int currmod, unsigned int *mod, KeySym *ks) DNPRINTF(SWM_D_KEY, "parsekeys: invalid key %s\n", name); - return (0); + return (1); } } } - return (1); + DNPRINTF(SWM_D_KEY, "parsekeys: leave ok\n"); + return (0); } -int -bindmatch(const char *var, const char *name, unsigned int currmod, char *keystr, - enum keyfuncid *kfid, unsigned int *mod, KeySym *ks) -{ - char *p; - int i; - char funcname[SWM_FUNCNAME_LEN]; - i = strncmp(var, name, 255); - if (kfid == NULL || mod == NULL || ks == NULL) - return (i); - *kfid = kf_invalid; - *mod = 0; - *ks = NoSymbol; - bzero(funcname, LENGTH(funcname)); - if (i <= 0) - return (i); - p = (char *)var + strlen(name); - if (*p++ != '[') - return (i); - i = 0; - while (isgraph(*p) && *p != ']' && i < LENGTH(funcname)) - funcname[i++] = *p++; - if (i >= LENGTH(funcname) || *p != ']') - return (1); - if (i == 0) - return (!parsekeys(keystr, currmod, mod, ks)); - for (*kfid = 0; *kfid < kf_invalid; (*kfid)++) { - if (strncasecmp(funcname, keyfuncs[*kfid].name, - SWM_FUNCNAME_LEN) == 0) { - return (!parsekeys(keystr, currmod, mod, ks)); - } - } - return (1); +char * +strdupsafe(char *str) +{ + if (str == NULL) + return (NULL); + else + return (strdup(str)); } + void -setkeybinding(unsigned int mod, KeySym ks, enum keyfuncid kfid) +setkeybinding(unsigned int mod, KeySym ks, enum keyfuncid kfid, char *spawn_name) { int i, j; + 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) { @@ -2308,19 +2699,25 @@ setkeybinding(unsigned int mod, KeySym ks, enum keyfuncid kfid) 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\n", - i, keyfuncs[keys[i].funcid].name); + "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; } } @@ -2328,6 +2725,7 @@ setkeybinding(unsigned int mod, KeySym ks, enum keyfuncid kfid) if (kfid == kf_invalid) { fprintf(stderr, "error: setkeybinding: cannot find mod/key combination"); + DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n"); return; } /* not found: add */ @@ -2351,11 +2749,13 @@ setkeybinding(unsigned int mod, KeySym ks, enum keyfuncid kfid) } } if (keys_length < keys_size) { - DNPRINTF(SWM_D_KEY, "setkeybinding: add %d\n", keys_length); 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) { @@ -2363,64 +2763,114 @@ setkeybinding(unsigned int mod, KeySym ks, enum keyfuncid kfid) quit(NULL, NULL); } } + DNPRINTF(SWM_D_KEY, "setkeybinding: leave\n"); +} + +int +setconfbinding(char *selector, char *value, int flags) +{ + enum keyfuncid kfid; + unsigned int mod; + KeySym ks; + int i; + DNPRINTF(SWM_D_KEY, "setconfbinding: enter\n"); + if (selector == NULL) { + DNPRINTF(SWM_D_KEY, "setconfbinding: unbind %s\n", value); + if (parsekeys(value, mod_key, &mod, &ks) == 0) { + kfid = kf_invalid; + setkeybinding(mod, ks, kfid, NULL); + return (0); + } else + return (1); + } + /* search by key function name */ + for (kfid = 0; kfid < kf_invalid; (kfid)++) { + if (strncasecmp(selector, keyfuncs[kfid].name, + SWM_FUNCNAME_LEN) == 0) { + DNPRINTF(SWM_D_KEY, "setconfbinding: %s: match\n", + selector); + if (parsekeys(value, mod_key, &mod, &ks) == 0) { + setkeybinding(mod, ks, kfid, NULL); + return (0); + } else + return (1); + } + } + /* search by custom spawn name */ + for (i = 0; i < spawns_length; i++) { + if (strcasecmp(selector, spawns[i].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); + return (0); + } else + return (1); + } + } + DNPRINTF(SWM_D_KEY, "setconfbinding: no match\n"); + return (1); } + void setup_keys(void) { - setkeybinding(MODKEY, XK_space, kf_cycle_layout); - setkeybinding(MODKEY|ShiftMask, XK_space, kf_stack_reset); - setkeybinding(MODKEY, XK_h, kf_master_shrink); - setkeybinding(MODKEY, XK_l, kf_master_grow); - setkeybinding(MODKEY, XK_comma, kf_master_add); - setkeybinding(MODKEY, XK_period, kf_master_del); - setkeybinding(MODKEY|ShiftMask, XK_comma, kf_stack_inc); - setkeybinding(MODKEY|ShiftMask, XK_period, kf_stack_dec); - setkeybinding(MODKEY, XK_Return, kf_swap_main); - setkeybinding(MODKEY, XK_j, kf_focus_next); - setkeybinding(MODKEY, XK_k, kf_focus_prev); - setkeybinding(MODKEY|ShiftMask, XK_j, kf_swap_next); - setkeybinding(MODKEY|ShiftMask, XK_k, kf_swap_prev); - setkeybinding(MODKEY|ShiftMask, XK_Return, kf_spawn_term); - setkeybinding(MODKEY, XK_p, kf_spawn_menu); - setkeybinding(MODKEY|ShiftMask, XK_q, kf_quit); - setkeybinding(MODKEY, XK_q, kf_restart); - setkeybinding(MODKEY, XK_m, kf_focus_main); - setkeybinding(MODKEY, XK_1, kf_ws_1); - setkeybinding(MODKEY, XK_2, kf_ws_2); - setkeybinding(MODKEY, XK_3, kf_ws_3); - setkeybinding(MODKEY, XK_4, kf_ws_4); - setkeybinding(MODKEY, XK_5, kf_ws_5); - setkeybinding(MODKEY, XK_6, kf_ws_6); - setkeybinding(MODKEY, XK_7, kf_ws_7); - setkeybinding(MODKEY, XK_8, kf_ws_8); - setkeybinding(MODKEY, XK_9, kf_ws_9); - setkeybinding(MODKEY, XK_0, kf_ws_10); - setkeybinding(MODKEY, XK_Right, kf_ws_next); - setkeybinding(MODKEY, XK_Left, kf_ws_prev); - setkeybinding(MODKEY|ShiftMask, XK_Right, kf_screen_next); - setkeybinding(MODKEY|ShiftMask, XK_Left, kf_screen_prev); - setkeybinding(MODKEY|ShiftMask, XK_1, kf_mvws_1); - setkeybinding(MODKEY|ShiftMask, XK_2, kf_mvws_2); - setkeybinding(MODKEY|ShiftMask, XK_3, kf_mvws_3); - setkeybinding(MODKEY|ShiftMask, XK_4, kf_mvws_4); - setkeybinding(MODKEY|ShiftMask, XK_5, kf_mvws_5); - setkeybinding(MODKEY|ShiftMask, XK_6, kf_mvws_6); - setkeybinding(MODKEY|ShiftMask, XK_7, kf_mvws_7); - setkeybinding(MODKEY|ShiftMask, XK_8, kf_mvws_8); - setkeybinding(MODKEY|ShiftMask, XK_9, kf_mvws_9); - setkeybinding(MODKEY|ShiftMask, XK_0, kf_mvws_10); - setkeybinding(MODKEY, XK_b, kf_bar_toggle); - setkeybinding(MODKEY, XK_Tab, kf_focus_next); - setkeybinding(MODKEY|ShiftMask, XK_Tab, kf_focus_prev); - setkeybinding(MODKEY|ShiftMask, XK_x, kf_wind_kill); - setkeybinding(MODKEY, XK_x, kf_wind_del); - setkeybinding(MODKEY, XK_s, kf_screenshot_all); - setkeybinding(MODKEY|ShiftMask, XK_s, kf_screenshot_wind); - setkeybinding(MODKEY, XK_t, kf_float_toggle); - setkeybinding(MODKEY|ShiftMask, XK_v, kf_version); - setkeybinding(MODKEY|ShiftMask, XK_Delete, kf_spawn_lock); - setkeybinding(MODKEY|ShiftMask, XK_i, kf_spawn_initscr); + setkeybinding(MODKEY, XK_space, kf_cycle_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); + setkeybinding(MODKEY, XK_comma, kf_master_add, NULL); + setkeybinding(MODKEY, XK_period, kf_master_del, NULL); + setkeybinding(MODKEY|ShiftMask, XK_comma, kf_stack_inc, NULL); + setkeybinding(MODKEY|ShiftMask, XK_period, kf_stack_dec, NULL); + setkeybinding(MODKEY, XK_Return, kf_swap_main, NULL); + setkeybinding(MODKEY, XK_j, kf_focus_next, NULL); + setkeybinding(MODKEY, XK_k, kf_focus_prev, NULL); + 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|ShiftMask, XK_q, kf_quit, NULL); + setkeybinding(MODKEY, XK_q, kf_restart, NULL); + setkeybinding(MODKEY, XK_m, kf_focus_main, NULL); + setkeybinding(MODKEY, XK_1, kf_ws_1, NULL); + setkeybinding(MODKEY, XK_2, kf_ws_2, NULL); + setkeybinding(MODKEY, XK_3, kf_ws_3, NULL); + setkeybinding(MODKEY, XK_4, kf_ws_4, NULL); + setkeybinding(MODKEY, XK_5, kf_ws_5, NULL); + setkeybinding(MODKEY, XK_6, kf_ws_6, NULL); + setkeybinding(MODKEY, XK_7, kf_ws_7, NULL); + setkeybinding(MODKEY, XK_8, kf_ws_8, NULL); + setkeybinding(MODKEY, XK_9, kf_ws_9, NULL); + 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|ShiftMask, XK_Right, kf_screen_next, NULL); + setkeybinding(MODKEY|ShiftMask, XK_Left, kf_screen_prev, NULL); + setkeybinding(MODKEY|ShiftMask, XK_1, kf_mvws_1, NULL); + setkeybinding(MODKEY|ShiftMask, XK_2, kf_mvws_2, NULL); + setkeybinding(MODKEY|ShiftMask, XK_3, kf_mvws_3, NULL); + setkeybinding(MODKEY|ShiftMask, XK_4, kf_mvws_4, NULL); + setkeybinding(MODKEY|ShiftMask, XK_5, kf_mvws_5, NULL); + setkeybinding(MODKEY|ShiftMask, XK_6, kf_mvws_6, NULL); + setkeybinding(MODKEY|ShiftMask, XK_7, kf_mvws_7, NULL); + setkeybinding(MODKEY|ShiftMask, XK_8, kf_mvws_8, NULL); + setkeybinding(MODKEY|ShiftMask, XK_9, kf_mvws_9, NULL); + setkeybinding(MODKEY|ShiftMask, XK_0, kf_mvws_10, NULL); + setkeybinding(MODKEY, XK_b, kf_bar_toggle, NULL); + setkeybinding(MODKEY, XK_Tab, kf_focus_next, NULL); + 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_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"); } + void updatenumlockmask(void) { @@ -2488,68 +2938,6 @@ grabbuttons(struct ws_win *win, int focused) BUTTONMASK, GrabModeAsync, GrabModeSync, None, None); } -void -expose(XEvent *e) -{ - DNPRINTF(SWM_D_EVENT, "expose: window: %lu\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); - - 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) - keyfuncs[keys[i].funcid].func( - root_to_region(ev->root), - &(keyfuncs[keys[i].funcid].args) - ); -} - -void -buttonpress(XEvent *e) -{ - XButtonPressedEvent *ev = &e->xbutton; - - struct ws_win *win; - int i, action; - - DNPRINTF(SWM_D_EVENT, "buttonpress: window: %lu\n", ev->window); - - action = root_click; - if ((win = find_window(ev->window)) == NULL) - return; - else { - focus_win(win); - action = client_click; - } - - for (i = 0; i < LENGTH(buttons); i++) - if (action == buttons[i].action && buttons[i].func && - buttons[i].button == ev->button && - CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) - buttons[i].func(win, &buttons[i].args); -} - -void -set_win_state(struct ws_win *win, long state) -{ - long data[] = {state, None}; - - DNPRINTF(SWM_D_EVENT, "set_win_state: window: %lu\n", win->id); - - XChangeProperty(display, win->id, astate, astate, 32, PropModeReplace, - (unsigned char *)data, 2); -} - const char *quirkname[] = { "NONE", /* config string for "no value" */ "FLOAT", @@ -2559,14 +2947,17 @@ const char *quirkname[] = { "FULLSCREEN", }; -#define SWM_Q_WS "\n| \t" +/* SWM_Q_WS: retain '|' for back compat for now (2009-08-11) */ +#define SWM_Q_WS "\n|+ \t" int parsequirks(char *qstr, unsigned long *quirk) { char *cp, *name; int i; + if (quirk == NULL) - return (0); + return (1); + cp = qstr; *quirk = 0; while ((name = strsep(&cp, SWM_Q_WS)) != NULL) { @@ -2577,7 +2968,7 @@ parsequirks(char *qstr, unsigned long *quirk) DNPRINTF(SWM_D_QUIRK, "parsequirks: %s\n", name); if (i == 0) { *quirk = 0; - return (1); + return (0); } *quirk |= 1 << (i-1); break; @@ -2586,54 +2977,17 @@ parsequirks(char *qstr, unsigned long *quirk) if (i >= LENGTH(quirkname)) { DNPRINTF(SWM_D_QUIRK, "parsequirks: invalid quirk [%s]\n", name); - return (0); + return (1); } } - return (1); -} -int -quirkmatch(const char *var, const char *name, char *qstr, char *qclass, - char *qname, unsigned long *qquirk) -{ - char *p; - int i; - char classname[SWM_QUIRK_LEN*2+1]; - DNPRINTF(SWM_D_QUIRK, "quirkmatch: in [%s]\n", var); - i = strncmp(var, name, 255); - if (qclass == NULL || qname == NULL || qquirk == NULL) - return (i); - *qquirk = 0; - *qclass = '\0'; - *qname = '\0'; - bzero(classname, LENGTH(classname)); - if (i <= 0) - return (i); - p = (char *)var + strlen(name); - if (*p++ != '[') - return (i); - i = 0; - while (isgraph(*p) && *p != ']' && i < LENGTH(classname)) - classname[i++] = *p++; - if (i >= LENGTH(classname) || *p != ']') - return (1); - if ((p = strchr(classname, ':')) == NULL || p-classname >= SWM_QUIRK_LEN) - return (1); - strlcpy(qclass, classname, p-classname+1); - strlcpy(qname, ++p, SWM_QUIRK_LEN); - for (p = qclass; *p && p-qclass < SWM_QUIRK_LEN; p++) - if (*p == '_') - *p = ' '; - for (p = qname; *p && p-qname < SWM_QUIRK_LEN; p++) - if (*p == '_') - *p = ' '; - i = (!parsequirks(qstr, qquirk)); - DNPRINTF(SWM_D_QUIRK, "quirkmatch: [%s][%s] %d\n", qclass, qname, i); - return (i); + return (0); } + void setquirk(const char *class, const char *name, const int quirk) { int i, j; + /* find existing */ for (i = 0; i < quirks_length; i++) { if (!strcmp(quirks[i].class, class) && @@ -2643,6 +2997,8 @@ setquirk(const char *class, const char *name, const int quirk) 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]; @@ -2653,10 +3009,10 @@ setquirk(const char *class, const char *name, const int quirk) DNPRINTF(SWM_D_QUIRK, "setquirk: replace #%d %s:%s\n", i, quirks[i].class, quirks[i].name); - strlcpy(quirks[i].class, class, - sizeof quirks->class); - strlcpy(quirks[i].name, name, - sizeof quirks->name); + free(quirks[i].class); + free(quirks[i].name); + quirks[i].class = strdup(class); + quirks[i].name = strdup(name); quirks[i].quirk = quirk; return; } @@ -2690,8 +3046,8 @@ setquirk(const char *class, const char *name, const int quirk) if (quirks_length < quirks_size) { DNPRINTF(SWM_D_QUIRK, "setquirk: add %d\n", quirks_length); j = quirks_length++; - strlcpy(quirks[j].class, class, sizeof quirks->class); - strlcpy(quirks[j].name, name, sizeof quirks->name); + quirks[j].class = strdup(class); + quirks[j].name = strdup(name); quirks[j].quirk = quirk; } else { fprintf(stderr, "quirks array problem?\n"); @@ -2702,6 +3058,24 @@ setquirk(const char *class, const char *name, const int quirk) } } +int +setconfquirk(char *selector, char *value, int flags) +{ + char *cp, *class, *name; + int retval; + unsigned long quirks; + if (selector == NULL) + return (0); + if ((cp = strchr(selector, ':')) == NULL) + return (0); + *cp = '\0'; + class = selector; + name = cp + 1; + if ((retval = parsequirks(value, &quirks)) == 0) + setquirk(class, name, quirks); + return (retval); +} + void setup_quirks(void) { @@ -2720,13 +3094,245 @@ setup_quirks(void) setquirk("pcb", "pcb", SWM_Q_FLOAT); } -struct ws_win * -manage_window(Window id) +/* conf file stuff */ +#define SWM_CONF_FILE "scrotwm.conf" + +enum { SWM_S_BAR_DELAY, SWM_S_BAR_ENABLED, SWM_S_STACK_ENABLED, + SWM_S_CLOCK_ENABLED, 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_BAR_FONT, SWM_S_BAR_ACTION, + SWM_S_SPAWN_TERM, SWM_S_SS_APP, SWM_S_DIALOG_RATIO }; + +int +setconfvalue(char *selector, char *value, int flags) { - Window trans; - struct workspace *ws; - struct ws_win *win; - int format, i, ws_idx, n; + switch (flags) { + case SWM_S_BAR_DELAY: + bar_delay = atoi(value); + break; + case SWM_S_BAR_ENABLED: + bar_enabled = atoi(value); + break; + case SWM_S_STACK_ENABLED: + stack_enabled = atoi(value); + break; + case SWM_S_CLOCK_ENABLED: + clock_enabled = atoi(value); + break; + case SWM_S_CYCLE_EMPTY: + cycle_empty = atoi(value); + break; + case SWM_S_CYCLE_VISIBLE: + cycle_visible = atoi(value); + break; + case SWM_S_SS_ENABLED: + ss_enabled = atoi(value); + break; + case SWM_S_TERM_WIDTH: + term_width = atoi(value); + break; + case SWM_S_TITLE_CLASS_ENABLED: + title_class_enabled = atoi(value); + break; + case SWM_S_TITLE_NAME_ENABLED: + title_name_enabled = atoi(value); + break; + case SWM_S_BAR_FONT: + free(bar_fonts[0]); + if ((bar_fonts[0] = strdup(value)) == NULL) + err(1, "setconfvalue: bar_font"); + break; + case SWM_S_BAR_ACTION: + free(bar_argv[0]); + if ((bar_argv[0] = strdup(value)) == NULL) + err(1, "setconfvalue: bar_action"); + break; + case SWM_S_SPAWN_TERM: + free(spawn_term[0]); + if ((spawn_term[0] = strdup(value)) == NULL) + err(1, "setconfvalue: spawn_term"); + break; + case SWM_S_SS_APP: + break; + case SWM_S_DIALOG_RATIO: + dialog_ratio = atof(value); + if (dialog_ratio > 1.0 || dialog_ratio <= .3) + dialog_ratio = .6; + break; + default: + return (1); + } + return (0); +} + +int +setconfmodkey(char *selector, char *value, int flags) +{ + if (!strncasecmp(value, "Mod1", strlen("Mod1"))) + update_modkey(Mod1Mask); + else if (!strncasecmp(value, "Mod2", strlen("Mod2"))) + update_modkey(Mod2Mask); + else if (!strncasecmp(value, "Mod3", strlen("Mod3"))) + update_modkey(Mod3Mask); + else if (!strncasecmp(value, "Mod4", strlen("Mod4"))) + update_modkey(Mod4Mask); + else + return (1); + return (0); +} + +int +setconfcolor(char *selector, char *value, int flags) +{ + setscreencolor(value, ((selector == NULL)?-1:atoi(selector)), flags); + return (0); +} + +int +setconfregion(char *selector, char *value, int flags) +{ + custom_region(value); + return (0); +} + +/* config options */ +struct config_option { + char *optname; + int (*func)(char*, char*, int); + int funcflags; +}; +struct config_option configopt[] = { + { "bar_enabled", setconfvalue, SWM_S_BAR_ENABLED }, + { "bar_border", setconfcolor, SWM_S_COLOR_BAR_BORDER }, + { "bar_color", setconfcolor, SWM_S_COLOR_BAR }, + { "bar_font_color", setconfcolor, SWM_S_COLOR_BAR_FONT }, + { "bar_font", setconfvalue, SWM_S_BAR_FONT }, + { "bar_action", setconfvalue, SWM_S_BAR_ACTION }, + { "bar_delay", setconfvalue, SWM_S_BAR_DELAY }, + { "bind", setconfbinding, 0 }, + { "stack_enabled", setconfvalue, SWM_S_STACK_ENABLED }, + { "clock_enabled", setconfvalue, SWM_S_CLOCK_ENABLED }, + { "color_focus", setconfcolor, SWM_S_COLOR_FOCUS }, + { "color_unfocus", setconfcolor, SWM_S_COLOR_UNFOCUS }, + { "cycle_empty", setconfvalue, SWM_S_CYCLE_EMPTY }, + { "cycle_visible", setconfvalue, SWM_S_CYCLE_VISIBLE }, + { "dialog_ratio", setconfvalue, SWM_S_DIALOG_RATIO }, + { "modkey", setconfmodkey, 0 }, + { "program", setconfspawn, 0 }, + { "quirk", setconfquirk, 0 }, + { "region", setconfregion, 0 }, + { "spawn_term", setconfvalue, SWM_S_SPAWN_TERM }, + { "screenshot_enabled", setconfvalue, SWM_S_SS_ENABLED }, + { "screenshot_app", setconfvalue, SWM_S_SS_APP }, + { "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 } +}; + + +int +conf_load(char *filename) +{ + FILE *config; + char *line, *cp, *optsub, *optval; + size_t linelen, lineno = 0; + int wordlen, i, optind; + struct config_option *opt; + + DNPRINTF(SWM_D_CONF, "conf_load begin\n"); + + if (filename == NULL) { + fprintf(stderr, "conf_load: no filename\n"); + return (1); + } + if ((config = fopen(filename, "r")) == NULL) { + warn("conf_load: fopen"); + return (1); + } + + while (!feof(config)) { + if ((line = fparseln(config, &linelen, &lineno, NULL, 0)) + == NULL) { + if (ferror(config)) + err(1, "%s", filename); + else + continue; + } + cp = line; + cp += strspn(cp, " \t\n"); /* eat whitespace */ + if (cp[0] == '\0') { + /* empty line */ + free(line); + continue; + } + /* get config option */ + wordlen = strcspn(cp, "=[ \t\n"); + if (wordlen == 0) { + warnx("%s: line %zd: no option found", + filename, lineno); + return (1); + } + optind = -1; + for (i = 0; i < LENGTH(configopt); i++) { + opt = &configopt[i]; + if (!strncasecmp(cp, opt->optname, wordlen) && + strlen(opt->optname) == wordlen) { + optind = i; + break; + } + } + if (optind == -1) { + warnx("%s: line %zd: unknown option %.*s", + filename, lineno, wordlen, cp); + return (1); + } + cp += wordlen; + cp += strspn(cp, " \t\n"); /* eat whitespace */ + /* get [selector] if any */ + optsub = NULL; + if (*cp == '[') { + cp++; + wordlen = strcspn(cp, "]"); + if (*cp != ']') { + if (wordlen == 0) { + warnx("%s: line %zd: syntax error", + filename, lineno); + return (1); + } + asprintf(&optsub, "%.*s", wordlen, cp); + } + cp += wordlen; + cp += strspn(cp, "] \t\n"); /* eat trailing */ + } + cp += strspn(cp, "= \t\n"); /* eat trailing */ + /* get RHS value */ + 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); + 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"); + + return (0); +} + +struct ws_win * +manage_window(Window id) +{ + Window trans = 0; + struct workspace *ws; + struct ws_win *win, *ww, *parent; + int format, i, ws_idx, n, border_me = 0; unsigned long nitems, bytes; Atom ws_idx_atom = 0, type; Atom *prot = NULL, *pp; @@ -2748,18 +3354,24 @@ manage_window(Window id) XGetWindowProperty(display, id, ws_idx_atom, 0, SWM_PROPLEN, False, XA_STRING, &type, &format, &nitems, &bytes, &prop); XGetWindowAttributes(display, id, &win->wa); + XGetWMNormalHints(display, id, &win->sh, &mask); XGetTransientForHint(display, id, &trans); - XGetWMNormalHints(display, id, &win->sh, &mask); /* XXX function? */ if (trans) { win->transient = trans; + parent = find_window(win->transient); + if (parent) + parent->child_trans = win; DNPRINTF(SWM_D_MISC, "manage_window: win %u transient %u\n", (unsigned)win->id, win->transient); } /* get supported protocols */ if (XGetWMProtocols(display, id, &prot, &n)) { - for (i = 0, pp = prot; i < n; i++, pp++) + for (i = 0, pp = prot; i < n; i++, pp++) { + if (*pp == takefocus) + win->take_focus = 1; if (*pp == adelete) win->can_delete = 1; + } if (prot) XFree(prot); } @@ -2778,8 +3390,21 @@ manage_window(Window id) errstr, prop); } ws = &r->s->ws[ws_idx]; - } else + } else { ws = r->ws; + /* this should launch transients in the same ws as parent */ + if (id && trans) + if ((ww = find_window(trans)) != NULL) + if (ws->r) { + ws = ww->ws; + if (ww->ws->r) + r = ww->ws->r; + else + fprintf(stderr, + "fix this bug mcbride\n"); + border_me = 1; + } + } /* set up the window layout */ win->id = id; @@ -2805,6 +3430,11 @@ manage_window(Window id) if (XGetClassHint(display, win->id, &win->ch)) { DNPRINTF(SWM_D_CLASS, "class: %s name: %s\n", win->ch.res_class, win->ch.res_name); + + /* java is retarded so treat it special */ + if (strstr(win->ch.res_name, "sun-awt")) + win->java = 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)) { @@ -2827,12 +3457,10 @@ manage_window(Window id) mask |= CWY; } if (win->g.w + win->g.x > WIDTH(r)) { - win->g.x = wc.x = WIDTH(win->ws->r) - win->g.w - 2; + win->g.x = wc.x = WIDTH(r) - win->g.w - 2; mask |= CWX; } - wc.border_width = 1; - mask |= CWBorderWidth; - XConfigureWindow(display, win->id, mask, &wc); + border_me = 1; } /* Reset font sizes (the bruteforce way; no default keybinding). */ @@ -2843,18 +3471,23 @@ manage_window(Window id) fake_keypress(win, XK_KP_Add, ShiftMask); } + /* border me */ + if (border_me) { + bzero(&wc, sizeof wc); + wc.border_width = 1; + mask = CWBorderWidth; + XConfigureWindow(display, win->id, mask, &wc); + } + XSelectInput(display, id, EnterWindowMask | FocusChangeMask | PropertyChangeMask | StructureNotifyMask); set_win_state(win, NormalState); /* floaters need to be mapped if they are in the current workspace */ - if (win->floating && (ws->idx == r->ws->idx)) + if ((win->floating || win->transient) && (ws->idx == r->ws->idx)) XMapRaised(display, win->id); - /* make new win focused */ - focus_win(win); - return (win); } @@ -2862,32 +3495,24 @@ void unmanage_window(struct ws_win *win) { struct workspace *ws; + struct ws_win *parent; if (win == NULL) return; DNPRINTF(SWM_D_MISC, "unmanage_window: %lu\n", win->id); - /* don't unmanage if we are switching workspaces */ - ws = win->ws; - if (ws->restack) - return; + /* needed for restart wm */ + set_win_state(win, WithdrawnState); - /* find a window to focus */ - if (ws->focus == win) - ws->focus = TAILQ_PREV(win, ws_win_list, entry); - if (ws->focus == NULL) - ws->focus = TAILQ_FIRST(&ws->winlist); - if (ws->focus == NULL || ws->focus == win) { - ws->focus = NULL; - unfocus_win(win); - } else - focus_win(ws->focus); - if (ws->focus_prev == win) - ws->focus_prev = NULL; + if (win->transient) { + parent = find_window(win->transient); + if (parent) + parent->child_trans = NULL; + } + ws = win->ws; TAILQ_REMOVE(&win->ws->winlist, win, entry); - set_win_state(win, WithdrawnState); if (win->ch.res_class) XFree(win->ch.res_class); if (win->ch.res_name) @@ -2896,6 +3521,86 @@ unmanage_window(struct ws_win *win) } void +focus_magic(struct ws_win *win) +{ + if (win->child_trans) { + /* win = parent & has a transient so focus on that */ + if (win->java) { + focus_win(win->child_trans); + if (win->child_trans->take_focus) + client_msg(win, takefocus); + } else { + focus_win(win->child_trans); + if (win->child_trans->take_focus) + client_msg(win->child_trans, takefocus); + } + } else { + /* regular focus */ + focus_win(win); + if (win->take_focus) + client_msg(win, takefocus); + } +} + +void +expose(XEvent *e) +{ + DNPRINTF(SWM_D_EVENT, "expose: window: %lu\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); + + 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) + ); + } +} + +void +buttonpress(XEvent *e) +{ + XButtonPressedEvent *ev = &e->xbutton; + + struct ws_win *win; + int i, action; + + DNPRINTF(SWM_D_EVENT, "buttonpress: window: %lu\n", ev->window); + + action = root_click; + if ((win = find_window(ev->window)) == NULL) + return; + + focus_magic(win); + action = client_click; + + for (i = 0; i < LENGTH(buttons); i++) + if (action == buttons[i].action && buttons[i].func && + buttons[i].button == ev->button && + CLEANMASK(buttons[i].mask) == CLEANMASK(ev->state)) + buttons[i].func(win, &buttons[i].args); +} + +void configurerequest(XEvent *e) { XConfigureRequestEvent *ev = &e->xconfigurerequest; @@ -2977,14 +3682,50 @@ configurenotify(XEvent *e) void destroynotify(XEvent *e) { - struct ws_win *win; + struct ws_win *win, *winfocus = NULL; + struct workspace *ws; + struct ws_win_list *wl; XDestroyWindowEvent *ev = &e->xdestroywindow; DNPRINTF(SWM_D_EVENT, "destroynotify: window %lu\n", ev->window); if ((win = find_window(ev->window)) != NULL) { + /* find a window to focus */ + ws = win->ws; + wl = &ws->winlist; + + /* if we are transient give focus to parent */ + if (win->transient) + winfocus = find_window(win->transient); + else if (ws->focus == win) { + /* if in max_stack try harder */ + if (ws->cur_layout->flags & SWM_L_FOCUSPREV) { + if (win != ws->focus_prev) + winfocus = ws->focus_prev; + else if (win != ws->focus) + winfocus = ws->focus; + } + + /* fallback and normal handling */ + if (winfocus == NULL) { + if (TAILQ_FIRST(wl) == win) + winfocus = TAILQ_NEXT(win, entry); + else { + winfocus = TAILQ_PREV(ws->focus, + ws_win_list, entry); + if (winfocus == NULL) + winfocus = TAILQ_LAST(wl, + ws_win_list); + } + } + } unmanage_window(win); + + ignore_enter = 1; stack(); + if (winfocus) + focus_win(winfocus); + ignore_enter = 0; } } @@ -2998,12 +3739,20 @@ enternotify(XEvent *e) if (ignore_enter) { /* eat event(r) to prevent autofocus */ - ignore_enter--; + ignore_enter = 0; return; } + /* + * happens when a window is created or destroyed and the border + * crosses the mouse pointer + */ + if (QLength(display)) + return; - if ((win = find_window(ev->window)) != NULL) - focus_win(win); + if ((win = find_window(ev->window)) == NULL) + return; + + focus_magic(win); } void @@ -3016,23 +3765,19 @@ void focusout(XEvent *e) { DNPRINTF(SWM_D_EVENT, "focusout: window: %lu\n", e->xfocus.window); +} - if (cur_focus && cur_focus->ws->r && - cur_focus->id == e->xfocus.window) { - struct swm_screen *s = cur_focus->ws->r->s; - Window rr, cr; - int x, y, wx, wy; - unsigned int mask; +void +mapnotify(XEvent *e) +{ + struct ws_win *win; + XMapEvent *ev = &e->xmap; - /* Try to detect synergy hiding the cursor. */ - if (XQueryPointer(display, cur_focus->id, - &rr, &cr, &x, &y, &wx, &wy, &mask) != False && - cr == 0 && !mask && - x == DisplayWidth(display, s->idx)/2 && - y == DisplayHeight(display, s->idx)/2) { - unfocus_win(cur_focus); - } - } + DNPRINTF(SWM_D_EVENT, "mapnotify: window: %lu\n", ev->window); + + win = find_window(ev->window); + if (win) + set_win_state(win, NormalState); } void @@ -3040,8 +3785,6 @@ mappingnotify(XEvent *e) { XMappingEvent *ev = &e->xmapping; - DNPRINTF(SWM_D_EVENT, "mappingnotify: window: %lu\n", ev->window); - XRefreshKeyboardMapping(ev); if (ev->request == MappingKeyboard) grabkeys(); @@ -3050,6 +3793,9 @@ mappingnotify(XEvent *e) void maprequest(XEvent *e) { + struct ws_win *win; + struct swm_region *r; + XMapRequestEvent *ev = &e->xmaprequest; XWindowAttributes wa; @@ -3060,9 +3806,19 @@ maprequest(XEvent *e) return; if (wa.override_redirect) return; - manage_window(e->xmaprequest.window); + win = manage_window(e->xmaprequest.window); + if (win == NULL) + return; /* can't happen */ + + ignore_enter = 1; stack(); + ignore_enter = 0; + + /* make new win focused */ + r = root_to_region(win->wa.root); + if (win->ws == r->ws) + focus_win(win); } void @@ -3103,14 +3859,58 @@ propertynotify(XEvent *e) void unmapnotify(XEvent *e) { - XDestroyWindowEvent *ev = &e->xdestroywindow; - struct ws_win *win; + struct ws_win *win, *winfocus = NULL; + struct workspace *ws; DNPRINTF(SWM_D_EVENT, "unmapnotify: window: %lu\n", e->xunmap.window); - if ((win = find_window(ev->window)) != NULL) - if (win->transient) - unmanage_window(win); + /* determine if we need to help unmanage this window */ + win = find_window(e->xunmap.window); + if (win == NULL) + return; + + /* java can not deal with this heuristic */ + if (win->java) + return; + + if (getstate(e->xunmap.window) == NormalState) { + /* + * this window does not have a destroy event but but it is no + * longer visible due to the app unmapping it so unmanage it + */ + ws = win->ws; + /* if we are max_stack try harder to focus on something */ + if (ws->cur_layout->flags & SWM_L_FOCUSPREV) { + if (win->transient) + winfocus = find_window(win->transient); + else if (win != ws->focus_prev) + winfocus = ws->focus_prev; + else if (win != ws->focus) + winfocus = ws->focus; + } + + /* normal and fallback if haven't found anything to focus on */ + if (winfocus == NULL) { + winfocus = TAILQ_PREV(win, ws_win_list, entry); + if (TAILQ_FIRST(&ws->winlist) == win) + winfocus = TAILQ_NEXT(win, entry); + else { + if (ws->focus) + winfocus = TAILQ_PREV(ws->focus, + ws_win_list, entry); + if (winfocus == NULL) + winfocus = TAILQ_LAST(&ws->winlist, + ws_win_list); + } + } + + /* trash window and refocus */ + unmanage_window(win); + ignore_enter = 1; + stack(); + focus_win(winfocus); + ignore_enter = 0; + } } void @@ -3122,7 +3922,7 @@ visibilitynotify(XEvent *e) DNPRINTF(SWM_D_EVENT, "visibilitynotify: window: %lu\n", e->xvisibility.window); if (e->xvisibility.state == VisibilityUnobscured) - for (i = 0; i < ScreenCount(display); i++) + for (i = 0; i < ScreenCount(display); i++) TAILQ_FOREACH(r, &screens[i].rl, entry) if (e->xvisibility.window == r->bar_window) bar_update(); @@ -3160,25 +3960,6 @@ active_wm(void) return (0); } -long -getstate(Window w) -{ - int format, status; - long result = -1; - unsigned char *p = NULL; - unsigned long n, extra; - Atom real; - - status = XGetWindowProperty(display, w, astate, 0L, 2L, False, astate, - &real, &format, &n, &extra, (unsigned char **)&p); - if (status != Success) - return (-1); - if (n != 0) - result = *((long *)p); - XFree(p); - return (result); -} - void new_region(struct swm_screen *s, int x, int y, int w, int h) { @@ -3265,7 +4046,7 @@ scan_xrandr(int i) /* remove any old regions */ while ((r = TAILQ_FIRST(&screens[i].rl)) != NULL) { - r->ws->r = NULL; + r->ws->old_r = r->ws->r = NULL; XDestroyWindow(display, r->bar_window); TAILQ_REMOVE(&screens[i].rl, r, entry); TAILQ_INSERT_TAIL(&screens[i].orl, r, entry); @@ -3273,24 +4054,26 @@ scan_xrandr(int i) /* map virtual screens onto physical screens */ #ifdef SWM_XRR_HAS_CRTC + outputs = 0; if (xrandr_support) { sr = XRRGetScreenResources(display, screens[i].root); if (sr == NULL) new_region(&screens[i], 0, 0, DisplayWidth(display, i), - DisplayHeight(display, i)); - else + DisplayHeight(display, i)); + else ncrtc = sr->ncrtc; for (c = 0, ci = NULL; c < ncrtc; c++) { ci = XRRGetCrtcInfo(display, sr, sr->crtcs[c]); if (ci->noutput == 0) continue; + outputs++; if (ci != NULL && ci->mode == None) new_region(&screens[i], 0, 0, DisplayWidth(display, i), - DisplayHeight(display, i)); + DisplayHeight(display, i)); else new_region(&screens[i], ci->x, ci->y, ci->width, ci->height); @@ -3302,7 +4085,7 @@ scan_xrandr(int i) #endif /* SWM_XRR_HAS_CRTC */ { new_region(&screens[i], 0, 0, DisplayWidth(display, i), - DisplayHeight(display, i)); + DisplayHeight(display, i)); } } @@ -3310,7 +4093,6 @@ void screenchange(XEvent *e) { XRRScreenChangeNotifyEvent *xe = (XRRScreenChangeNotifyEvent *)e; struct swm_region *r; - struct ws_win *win; int i; DNPRINTF(SWM_D_EVENT, "screenchange: %lu\n", xe->root); @@ -3328,11 +4110,6 @@ screenchange(XEvent *e) { /* brute force for now, just re-enumerate the regions */ scan_xrandr(i); - /* hide any windows that went away */ - TAILQ_FOREACH(r, &screens[i].rl, entry) - TAILQ_FOREACH(win, &r->ws->winlist, entry) - XUnmapWindow(display, win->id); - /* add bars to all regions */ for (i = 0; i < ScreenCount(display); i++) TAILQ_FOREACH(r, &screens[i].rl, entry) @@ -3350,7 +4127,7 @@ setup_screens(void) int errorbase, major, minor; struct workspace *ws; int ws_idx_atom; - + long state, manage; if ((screens = calloc(ScreenCount(display), sizeof(struct swm_screen))) == NULL) @@ -3363,7 +4140,7 @@ setup_screens(void) &xrandr_eventbase, &errorbase); if (xrandr_support) if (XRRQueryVersion(display, &major, &minor) && major < 1) - xrandr_support = 0; + xrandr_support = 0; /* map physical screens */ for (i = 0; i < ScreenCount(display); i++) { @@ -3388,6 +4165,7 @@ setup_screens(void) ws->restack = 1; ws->focus = NULL; ws->r = NULL; + ws->old_r = NULL; TAILQ_INIT(&ws->winlist); for (k = 0; layouts[k].l_stack != NULL; k++) @@ -3409,24 +4187,26 @@ setup_screens(void) /* attach windows to a region */ /* normal windows */ for (j = 0; j < no; j++) { - XGetWindowAttributes(display, wins[j], &wa); if (!XGetWindowAttributes(display, wins[j], &wa) || wa.override_redirect || XGetTransientForHint(display, wins[j], &d1)) continue; - if (wa.map_state == IsViewable || - getstate(wins[j]) == NormalState) + state = getstate(wins[j]); + manage = state == NormalState || state == IconicState; + if (wa.map_state == IsViewable || manage) manage_window(wins[j]); } /* transient windows */ for (j = 0; j < no; j++) { - if (!XGetWindowAttributes(display, wins[j], &wa)) + if (!XGetWindowAttributes(display, wins[j], &wa) || + wa.override_redirect) continue; + state = getstate(wins[j]); + manage = state == NormalState || state == IconicState; if (XGetTransientForHint(display, wins[j], &d1) && - (wa.map_state == IsViewable || getstate(wins[j]) == - NormalState)) + manage) manage_window(wins[j]); } if (wins) { @@ -3437,6 +4217,22 @@ 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 ((spawn_term[0] = strdup("xterm")) == NULL) + err(1, "setup_globals: strdup"); +} + +void workaround(void) { int i; @@ -3460,7 +4256,8 @@ int main(int argc, char *argv[]) { struct passwd *pwd; - struct swm_region *r; + struct swm_region *r, *rr; + struct ws_win *winfocus = NULL; char conf[PATH_MAX], *cfile = NULL; struct stat sb; XEvent e; @@ -3479,9 +4276,17 @@ main(int argc, char *argv[]) if (active_wm()) errx(1, "other wm running"); + /* handle some signale */ + installsignal(SIGINT, "INT"); + installsignal(SIGHUP, "HUP"); + installsignal(SIGQUIT, "QUIT"); + installsignal(SIGTERM, "TERM"); + installsignal(SIGCHLD, "CHLD"); + astate = XInternAtom(display, "WM_STATE", False); aprot = XInternAtom(display, "WM_PROTOCOLS", False); adelete = XInternAtom(display, "WM_DELETE_WINDOW", False); + takefocus = XInternAtom(display, "WM_TAKE_FOCUS", False); /* look for local and global conf file */ pwd = getpwuid(getuid()); @@ -3489,8 +4294,10 @@ main(int argc, char *argv[]) errx(1, "invalid user %d", getuid()); setup_screens(); + setup_globals(); setup_keys(); setup_quirks(); + setup_spawn(); snprintf(conf, sizeof conf, "%s/.%s", pwd->pw_dir, SWM_CONF_FILE); if (stat(conf, &sb) != -1) { @@ -3508,8 +4315,11 @@ main(int argc, char *argv[]) /* setup all bars */ for (i = 0; i < ScreenCount(display); i++) - TAILQ_FOREACH(r, &screens[i].rl, entry) + TAILQ_FOREACH(r, &screens[i].rl, entry) { + if (winfocus == NULL) + winfocus = TAILQ_FIRST(&r->ws->winlist); bar_setup(r); + } /* set some values to work around bad programs */ workaround(); @@ -3519,18 +4329,12 @@ main(int argc, char *argv[]) xfd = ConnectionNumber(display); while (running) { - FD_ZERO(&rd); - FD_SET(xfd, &rd); - if (select(xfd + 1, &rd, NULL, NULL, NULL) == -1) - if (errno != EINTR) - errx(1, "select failed"); - if (bar_alarm) { - bar_alarm = 0; - bar_update(); - } while (XPending(display)) { XNextEvent(display, &e); + if (running == 0) + goto done; if (e.type < LASTEvent) { + dumpevent(&e); if (handler[e.type]) handler[e.type](&e); else @@ -3550,7 +4354,35 @@ main(int argc, char *argv[]) } } } + + /* if we are being restarted go focus on first window */ + if (winfocus) { + rr = TAILQ_FIRST(&screens[0].rl); + /* 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); + + focus_win(winfocus); + winfocus = NULL; + continue; + } + + FD_ZERO(&rd); + FD_SET(xfd, &rd); + if (select(xfd + 1, &rd, NULL, NULL, NULL) == -1) + if (errno != EINTR) + DNPRINTF(SWM_D_MISC, "select failed"); + if (running == 0) + goto done; + if (bar_alarm) { + bar_alarm = 0; + bar_update(); + } } +done: + bar_extra_stop(); XCloseDisplay(display);