X-Git-Url: https://jasonwoof.com/gitweb/?a=blobdiff_plain;f=scrotwm.c;h=ae5fd0df916c0b404aa9050a980d3450b4c51dea;hb=0aa96eda7c9a049f7b61929a99dcfd78f6ada624;hp=d8f2e618e39689c23c7176cc997905c1476fe9f8;hpb=601c699a893fdf8c95619b3bcc270004c7524f6f;p=spectrwm.git diff --git a/scrotwm.c b/scrotwm.c index d8f2e61..ae5fd0d 100644 --- a/scrotwm.c +++ b/scrotwm.c @@ -2,6 +2,7 @@ /* * Copyright (c) 2009 Marco Peereboom * Copyright (c) 2009 Ryan McBride + * Copyright (c) 2009 Darrin Chandler * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -50,7 +51,7 @@ static const char *cvstag = "$scrotwm$"; -#define SWM_VERSION "0.8" +#define SWM_VERSION "0.9.5" #include #include @@ -105,6 +106,8 @@ static const char *cvstag = "$scrotwm$"; #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 u_int32_t swm_debug = 0 | SWM_D_MISC @@ -116,6 +119,8 @@ u_int32_t swm_debug = 0 | SWM_D_MOUSE | SWM_D_PROP | SWM_D_CLASS + | SWM_D_KEY + | SWM_D_QUIRK ; #else #define DPRINTF(x...) @@ -128,10 +133,14 @@ u_int32_t swm_debug = 0 #define BUTTONMASK (ButtonPressMask|ButtonReleaseMask) #define MOUSEMASK (BUTTONMASK|PointerMotionMask) #define SWM_PROPLEN (16) +#define SWM_FUNCNAME_LEN (32) +#define SWM_KEYS_LEN (255) +#define SWM_QUIRK_LEN (32) #define X(r) (r)->g.x #define Y(r) (r)->g.y #define WIDTH(r) (r)->g.w #define HEIGHT(r) (r)->g.h +#define SWM_MAX_FONT_STEPS (3) #ifndef SWM_LIB #define SWM_LIB "/usr/X11R6/lib/swmhack.so" @@ -139,6 +148,8 @@ u_int32_t swm_debug = 0 char **start_argv; Atom astate; +Atom aprot; +Atom adelete; int (*xerrorxlib)(Display *, XErrorEvent *); int other_wm; int running = 1; @@ -151,11 +162,13 @@ Display *display; int cycle_empty = 0; int cycle_visible = 0; +int term_width = 0; +int font_adjusted = 0; /* dialog windows */ double dialog_ratio = .6; /* status bar */ -#define SWM_BAR_MAX (128) +#define SWM_BAR_MAX (256) char *bar_argv[] = { NULL, NULL }; int bar_pipe[2]; char bar_ext[SWM_BAR_MAX]; @@ -168,13 +181,14 @@ int bar_extra = 1; int bar_extra_running = 0; int bar_verbose = 1; int bar_height = 0; +int clock_enabled = 1; pid_t bar_pid; GC bar_gc; XGCValues bar_gcv; int bar_fidx = 0; XFontStruct *bar_fs; char *bar_fonts[] = { - "-*-terminus-*-*-*-*-*-*-*-*-*-*-*-*", + "-*-terminus-medium-*-*-*-*-*-*-*-*-*-*-*", "-*-times-medium-r-*-*-*-*-*-*-*-*-*-*", NULL }; @@ -222,6 +236,10 @@ struct ws_win { int floating; int transient; int manual; + int font_size_boundary[SWM_MAX_FONT_STEPS]; + int font_steps; + int last_inc; + int can_delete; unsigned long quirks; struct workspace *ws; /* always valid */ struct swm_screen *s; /* always valid, never changes */ @@ -231,6 +249,62 @@ struct ws_win { }; TAILQ_HEAD(ws_win_list, ws_win); +/* user/key callable function IDs */ +enum keyfuncid { + kf_cycle_layout, + kf_stack_reset, + kf_master_shrink, + kf_master_grow, + kf_master_add, + kf_master_del, + kf_stack_inc, + kf_stack_dec, + kf_swap_main, + kf_focus_next, + kf_focus_prev, + kf_swap_next, + kf_swap_prev, + kf_spawn_term, + kf_spawn_menu, + kf_quit, + kf_restart, + kf_focus_main, + kf_ws_1, + kf_ws_2, + kf_ws_3, + kf_ws_4, + kf_ws_5, + kf_ws_6, + kf_ws_7, + kf_ws_8, + kf_ws_9, + kf_ws_10, + kf_ws_next, + kf_ws_prev, + kf_screen_next, + kf_screen_prev, + kf_mvws_1, + kf_mvws_2, + kf_mvws_3, + kf_mvws_4, + kf_mvws_5, + kf_mvws_6, + kf_mvws_7, + kf_mvws_8, + kf_mvws_9, + kf_mvws_10, + kf_bar_toggle, + kf_wind_kill, + kf_wind_del, + kf_screenshot_all, + kf_screenshot_wind, + kf_float_toggle, + kf_version, + kf_spawn_lock, + kf_spawn_initscr, + kf_invalid +}; + /* layout handlers */ void stack(void); void vertical_config(struct workspace *, int); @@ -240,6 +314,16 @@ void horizontal_stack(struct workspace *, struct swm_geometry *); 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); struct layout { void (*l_stack)(struct workspace *, struct swm_geometry *); @@ -261,6 +345,7 @@ struct workspace { int restack; /* restack on switch */ struct layout *cur_layout; /* current layout handlers */ struct ws_win *focus; /* may be NULL */ + struct ws_win *focus_prev; /* may be NULL */ struct swm_region *r; /* may be NULL */ struct ws_win_list winlist; /* list of windows in ws */ @@ -317,33 +402,30 @@ union arg { #define SWM_ARG_ID_CYCLEWS_DOWN (13) #define SWM_ARG_ID_CYCLESC_UP (14) #define SWM_ARG_ID_CYCLESC_DOWN (15) -#define SWM_ARG_ID_COLINC (16) -#define SWM_ARG_ID_COLDEC (17) -#define SWM_ARG_ID_ROWINC (16) -#define SWM_ARG_ID_ROWDEL (17) +#define SWM_ARG_ID_STACKINC (16) +#define SWM_ARG_ID_STACKDEC (17) #define SWM_ARG_ID_SS_ALL (0) #define SWM_ARG_ID_SS_WINDOW (1) #define SWM_ARG_ID_DONTCENTER (0) #define SWM_ARG_ID_CENTER (1) +#define SWM_ARG_ID_KILLWINDOW (0) +#define SWM_ARG_ID_DELETEWINDOW (1) char **argv; }; /* quirks */ struct quirk { - char *class; - char *name; + char class[SWM_QUIRK_LEN]; + char name[SWM_QUIRK_LEN]; unsigned long quirk; #define SWM_Q_FLOAT (1<<0) /* float this window */ #define SWM_Q_TRANSSZ (1<<1) /* transiend window size too small */ #define SWM_Q_ANYWHERE (1<<2) /* don't position this window */ -} quirks[] = { - { "MPlayer", "xv", SWM_Q_FLOAT }, - { "OpenOffice.org 2.4", "VCLSalFrame", SWM_Q_FLOAT }, - { "OpenOffice.org 3.0", "VCLSalFrame", SWM_Q_FLOAT }, - { "Firefox-bin", "firefox-bin", SWM_Q_TRANSSZ}, - { "Gimp", "gimp", SWM_Q_FLOAT | SWM_Q_ANYWHERE}, - { NULL, NULL, 0}, +#define SWM_Q_XTERM_FONTADJ (1<<3) /* adjust xterm fonts when resizing */ +#define SWM_Q_FULLSCREEN (1<<4) /* remove border */ }; +int quirks_size = 0, quirks_length = 0; +struct quirk *quirks = NULL; /* events */ void expose(XEvent *); @@ -354,6 +436,7 @@ void configurenotify(XEvent *); void destroynotify(XEvent *); void enternotify(XEvent *); void focusin(XEvent *); +void focusout(XEvent *); void mappingnotify(XEvent *); void maprequest(XEvent *); void propertynotify(XEvent *); @@ -369,6 +452,7 @@ void (*handler[LASTEvent])(XEvent *) = { [DestroyNotify] = destroynotify, [EnterNotify] = enternotify, [FocusIn] = focusin, + [FocusOut] = focusout, [MappingNotify] = mappingnotify, [MapRequest] = maprequest, [PropertyNotify] = propertynotify, @@ -418,8 +502,6 @@ setscreencolor(char *val, int i, int c) i, ScreenCount(display)); } -void new_region(struct swm_screen *, int, int, int, int); - void custom_region(char *val) { @@ -483,6 +565,12 @@ conf_load(char *filename) 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); @@ -526,12 +614,17 @@ conf_load(char *filename) 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 (!varmatch(var, "color_focus", &i)) + 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); @@ -553,6 +646,29 @@ conf_load(char *filename) 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); @@ -563,13 +679,23 @@ conf_load(char *filename) case 's': if (!strncmp(var, "spawn_term", strlen("spawn_term"))) asprintf(&spawn_term[0], "%s", val); - if (!strncmp(var, "screenshot_enabled", + else if (!strncmp(var, "screenshot_enabled", strlen("screenshot_enabled"))) ss_enabled = atoi(val); - if (!strncmp(var, "screenshot_app", + 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; } @@ -633,7 +759,6 @@ bar_update(void) if (bar_enabled == 0) return; - if (bar_extra && bar_extra_running) { /* ignore short reads; it'll correct itself */ while ((b = fgetln(stdin, &len)) != NULL) @@ -649,14 +774,18 @@ bar_update(void) } else strlcpy(bar_ext, "", sizeof bar_ext); - time(&tmt); - localtime_r(&tmt, &tm); - strftime(s, sizeof s, "%a %b %d %R %Z %Y", &tm); + if (clock_enabled == 0) + strlcpy(s, "", sizeof s); + else { + time(&tmt); + 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, "%s %d:%d %s %s", - s, x++, r->ws->idx + 1, bar_ext, bar_vertext); + snprintf(loc, sizeof loc, "%d:%d %s%s %s", + x++, r->ws->idx + 1, s, bar_ext, bar_vertext); bar_print(r, loc); } } @@ -678,26 +807,23 @@ bar_toggle(struct swm_region *r, union arg *args) DNPRINTF(SWM_D_MISC, "bar_toggle\n"); - if (bar_enabled) { + if (bar_enabled) for (i = 0; i < sc; i++) TAILQ_FOREACH(tmpr, &screens[i].rl, entry) XUnmapWindow(display, tmpr->bar_window); - } else { + else for (i = 0; i < sc; i++) TAILQ_FOREACH(tmpr, &screens[i].rl, entry) XMapRaised(display, tmpr->bar_window); - } + bar_enabled = !bar_enabled; - XSync(display, False); for (i = 0; i < sc; i++) for (j = 0; j < SWM_WS_MAX; j++) screens[i].ws[j].restack = 1; stack(); /* must be after stack */ - for (i = 0; i < sc; i++) - TAILQ_FOREACH(tmpr, &screens[i].rl, entry) - bar_update(); + bar_update(); } void @@ -786,13 +912,29 @@ 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(); } void +client_msg(struct ws_win *win, Atom a) +{ + XClientMessageEvent cm; + + bzero(&cm, sizeof cm); + cm.type = ClientMessage; + cm.window = win->id; + cm.message_type = aprot; + cm.format = 32; + cm.data.l[0] = a; + cm.data.l[1] = CurrentTime; + XSendEvent(display, win->id, False, 0L, (XEvent *)&cm); +} + +void config_win(struct ws_win *win) { XConfigureEvent ce; @@ -851,6 +993,34 @@ unmap_all(void) } void +fake_keypress(struct ws_win *win, int keysym, int modifiers) +{ + XKeyEvent event; + + event.display = display; /* Ignored, but what the hell */ + event.window = win->id; + event.root = win->s->root; + event.subwindow = None; + event.time = CurrentTime; + event.x = win->g.x; + event.y = win->g.y; + event.x_root = 1; + event.y_root = 1; + event.same_screen = True; + event.keycode = XKeysymToKeycode(display, keysym); + event.state = modifiers; + + event.type = KeyPress; + XSendEvent(event.display, event.window, True, + KeyPressMask, (XEvent *)&event); + + event.type = KeyRelease; + XSendEvent(event.display, event.window, True, + KeyPressMask, (XEvent *)&event); + +} + +void restart(struct swm_region *r, union arg *args) { DNPRINTF(SWM_D_MISC, "restart: %s\n", start_argv[0]); @@ -952,6 +1122,16 @@ spawn(struct swm_region *r, union arg *args) } void +spawnterm(struct swm_region *r, union arg *args) +{ + DNPRINTF(SWM_D_MISC, "spawnterm\n"); + + if (term_width) + setenv("_SWM_XTERM_FONTADJ", "", 1); + spawn(r, args); +} + +void spawnmenu(struct swm_region *r, union arg *args) { DNPRINTF(SWM_D_MISC, "spawnmenu\n"); @@ -966,6 +1146,28 @@ spawnmenu(struct swm_region *r, union arg *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; + + 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 unfocus_all(void) { struct ws_win *win; @@ -975,16 +1177,8 @@ unfocus_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) { - if (win->ws->r == NULL) - continue; - grabbuttons(win, 0); - XSetWindowBorder(display, win->id, - win->ws->r->s->c[SWM_S_COLOR_UNFOCUS].color); - win->got_focus = 0; - win->ws->focus = NULL; - cur_focus = NULL; - } + TAILQ_FOREACH(win, &screens[i].ws[j].winlist, entry) + unfocus_win(win); } void @@ -995,7 +1189,14 @@ focus_win(struct ws_win *win) if (win == NULL) return; - unfocus_all(); + 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); + } win->ws->focus = win; if (win->ws->r != NULL) { cur_focus = win; @@ -1050,6 +1251,8 @@ switchws(struct swm_region *r, union arg *args) 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(); @@ -1124,7 +1327,7 @@ cyclescr(struct swm_region *r, union arg *args) void swapwin(struct swm_region *r, union arg *args) { - struct ws_win *target; + struct ws_win *target, *source; struct ws_win_list *wl; @@ -1134,40 +1337,48 @@ swapwin(struct swm_region *r, union arg *args) if (cur_focus == NULL) return; - wl = &cur_focus->ws->winlist; + source = cur_focus; + wl = &source->ws->winlist; switch (args->id) { case SWM_ARG_ID_SWAPPREV: - target = TAILQ_PREV(cur_focus, ws_win_list, entry); + target = TAILQ_PREV(source, ws_win_list, entry); TAILQ_REMOVE(wl, cur_focus, entry); if (target == NULL) - TAILQ_INSERT_TAIL(wl, cur_focus, entry); + TAILQ_INSERT_TAIL(wl, source, entry); else - TAILQ_INSERT_BEFORE(target, cur_focus, entry); + TAILQ_INSERT_BEFORE(target, source, entry); break; case SWM_ARG_ID_SWAPNEXT: - target = TAILQ_NEXT(cur_focus, entry); - TAILQ_REMOVE(wl, cur_focus, entry); + target = TAILQ_NEXT(source, entry); + TAILQ_REMOVE(wl, source, entry); if (target == NULL) - TAILQ_INSERT_HEAD(wl, cur_focus, entry); + TAILQ_INSERT_HEAD(wl, source, entry); else - TAILQ_INSERT_AFTER(wl, target, cur_focus, entry); + TAILQ_INSERT_AFTER(wl, target, source, entry); break; case SWM_ARG_ID_SWAPMAIN: target = TAILQ_FIRST(wl); - if (target == cur_focus) - return; + if (target == source) { + if (source->ws->focus_prev != NULL && + source->ws->focus_prev != target) + + source = source->ws->focus_prev; + else + return; + } + source->ws->focus_prev = target; TAILQ_REMOVE(wl, target, entry); - TAILQ_INSERT_BEFORE(cur_focus, target, entry); - TAILQ_REMOVE(wl, cur_focus, entry); - TAILQ_INSERT_HEAD(wl, cur_focus, entry); + TAILQ_INSERT_BEFORE(source, target, entry); + TAILQ_REMOVE(wl, source, entry); + TAILQ_INSERT_HEAD(wl, source, entry); break; default: DNPRINTF(SWM_D_MOVE, "invalid id: %d\n", args->id); return; } - ignore_enter = 2; + ignore_enter = 1; stack(); } @@ -1200,6 +1411,10 @@ focus(struct swm_region *r, union arg *args) case SWM_ARG_ID_FOCUSMAIN: winfocus = TAILQ_FIRST(wl); + if (winfocus == cur_focus) + winfocus = cur_focus->ws->focus_prev; + if (winfocus == NULL) + return; break; default: @@ -1270,6 +1485,8 @@ stack(void) { r->ws->cur_layout->l_stack(r->ws, &g); } } + if (font_adjusted) + font_adjusted--; XSync(display, False); } @@ -1281,7 +1498,11 @@ stack_floater(struct ws_win *win, struct swm_region *r) bzero(&wc, sizeof wc); mask = CWX | CWY | CWBorderWidth | CWWidth | CWHeight; - wc.border_width = 1; + if ((win->quirks & SWM_Q_FULLSCREEN) && (win->g.w == WIDTH(r)) && + (win->g.h == HEIGHT(r))) + wc.border_width = 0; + else + wc.border_width = 1; if (win->transient && (win->quirks & SWM_Q_TRANSSZ)) { win->g.w = (double)WIDTH(r) * dialog_ratio; win->g.h = (double)HEIGHT(r) * dialog_ratio; @@ -1302,6 +1523,35 @@ stack_floater(struct ws_win *win, struct swm_region *r) XConfigureWindow(display, win->id, mask, &wc); } +/* + * Send keystrokes to terminal to decrease/increase the font size as the + * window size changes. + */ +void +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) { + win->font_size_boundary[win->font_steps] = + (win->sh.width_inc * term_width) + win->sh.base_width; + win->font_steps++; + font_adjusted++; + 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]) { + win->font_steps--; + font_adjusted++; + win->last_inc = win->sh.width_inc; + fake_keypress(win, XK_KP_Add, ShiftMask); + } +} + #define SWAPXY(g) do { \ int tmp; \ tmp = (g)->y; (g)->y = (g)->x; (g)->x = tmp; \ @@ -1313,10 +1563,11 @@ 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, w_inc, h_inc, w_base, h_base, stacks; - int hrh, extra, h_slice, last_h = 0; + 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", @@ -1329,7 +1580,13 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip) ws->focus = TAILQ_FIRST(&ws->winlist); winfocus = cur_focus ? cur_focus : ws->focus; - win = TAILQ_FIRST(&ws->winlist); + TAILQ_FOREACH(win, &ws->winlist, entry) + if (win->transient == 0 && win->floating == 0) + break; + + if (win == NULL) + goto notiles; + if (rot) { w_inc = win->sh.width_inc; w_base = win->sh.base_width; @@ -1348,6 +1605,8 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip) if (stacks > winno - mwin) stacks = winno - mwin; + if (stacks < 1) + stacks = 1; h_slice = r_g.h / SWM_H_SLICE; if (mwin && winno > mwin) { @@ -1361,26 +1620,17 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip) /* adjust for window's requested size increment */ remain = (win_g.w - w_base) % w_inc; missing = w_inc - remain; - - if (missing <= extra || j == 0) { - extra -= missing; - win_g.w += missing; - } else { - win_g.w -= remain; - extra += remain; - } + win_g.w -= remain; + extra += remain; } msize = win_g.w; if (flip) win_g.x += r_g.w - msize; } else { - if (stacks > 1) { - colno = split = (winno - mwin) / stacks; - } else { - split = 0; - colno = winno; - } + msize = -2; + colno = split = winno / stacks; + win_g.w = ((r_g.w - (stacks * 2) + 2) / stacks); } hrh = r_g.h / colno; extra = r_g.h - (colno * hrh); @@ -1403,11 +1653,10 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip) win_g.x = r_g.x; else win_g.x += win_g.w + 2; - win_g.w = (((r_g.w - (msize + 2)) - - ((stacks - 1) * 2)) / stacks); + win_g.w = (r_g.w - msize - (stacks * 2)) / stacks; if (s == 1) - win_g.w += (((r_g.w - (msize + 2)) - - ((stacks - 1) * 2)) % stacks); + win_g.w += (r_g.w - msize - (stacks * 2)) % + stacks; s--; j = 0; } @@ -1453,6 +1702,7 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip) win->g.w = wc.width = win_g.w; win->g.h = wc.height = win_g.h; } + adjust_font(win); mask = CWX | CWY | CWWidth | CWHeight | CWBorderWidth; XConfigureWindow(display, win->id, mask, &wc); XMapRaised(display, win->id); @@ -1462,6 +1712,7 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip) j++; } + notiles: /* now, stack all the floaters and transients */ TAILQ_FOREACH(win, &ws->winlist, entry) { if (win->transient == 0 && win->floating == 0) @@ -1501,10 +1752,11 @@ vertical_config(struct workspace *ws, int id) case SWM_ARG_ID_MASTERDEL: if (ws->l_state.vertical_mwin > 0) ws->l_state.vertical_mwin--; - case SWM_ARG_ID_COLINC: + break; + case SWM_ARG_ID_STACKINC: ws->l_state.vertical_stacks++; break; - case SWM_ARG_ID_COLDEC: + case SWM_ARG_ID_STACKDEC: if (ws->l_state.vertical_stacks > 1) ws->l_state.vertical_stacks--; break; @@ -1548,12 +1800,13 @@ horizontal_config(struct workspace *ws, int id) if (ws->l_state.horizontal_mwin > 0) ws->l_state.horizontal_mwin--; break; - case SWM_ARG_ID_COLINC: + case SWM_ARG_ID_STACKINC: ws->l_state.horizontal_stacks++; break; - case SWM_ARG_ID_COLDEC: + case SWM_ARG_ID_STACKDEC: if (ws->l_state.horizontal_stacks > 1) ws->l_state.horizontal_stacks--; + break; default: return; } @@ -1665,9 +1918,16 @@ send_to_ws(struct swm_region *r, union arg *args) void wkill(struct swm_region *r, union arg *args) { - DNPRINTF(SWM_D_MISC, "wkill\n"); - if(r->ws->focus != NULL) + DNPRINTF(SWM_D_MISC, "wkill %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); } void @@ -1708,68 +1968,6 @@ floating_toggle(struct swm_region *r, union arg *args) focus_win(win); } -/* key definitions */ -struct key { - unsigned int mod; - KeySym keysym; - void (*func)(struct swm_region *r, union arg *); - union arg args; -} keys[] = { - /* modifier key function argument */ - { MODKEY, XK_space, cycle_layout, {0} }, - { MODKEY | ShiftMask, XK_space, stack_config, {.id = SWM_ARG_ID_STACKRESET} }, - { MODKEY, XK_h, stack_config, {.id = SWM_ARG_ID_MASTERSHRINK} }, - { MODKEY, XK_l, stack_config, {.id = SWM_ARG_ID_MASTERGROW} }, - { MODKEY, XK_comma, stack_config, {.id = SWM_ARG_ID_MASTERADD} }, - { MODKEY, XK_period, stack_config, {.id = SWM_ARG_ID_MASTERDEL} }, - { MODKEY | ShiftMask, XK_comma, stack_config, {.id = SWM_ARG_ID_COLINC} }, - { MODKEY | ShiftMask, XK_period, stack_config, {.id = SWM_ARG_ID_COLDEC} }, - { MODKEY, XK_Return, swapwin, {.id = SWM_ARG_ID_SWAPMAIN} }, - { MODKEY, XK_j, focus, {.id = SWM_ARG_ID_FOCUSNEXT} }, - { MODKEY, XK_k, focus, {.id = SWM_ARG_ID_FOCUSPREV} }, - { MODKEY | ShiftMask, XK_j, swapwin, {.id = SWM_ARG_ID_SWAPNEXT} }, - { MODKEY | ShiftMask, XK_k, swapwin, {.id = SWM_ARG_ID_SWAPPREV} }, - { MODKEY | ShiftMask, XK_Return, spawn, {.argv = spawn_term} }, - { MODKEY, XK_p, spawnmenu, {.argv = spawn_menu} }, - { MODKEY | ShiftMask, XK_q, quit, {0} }, - { MODKEY, XK_q, restart, {0} }, - { MODKEY, XK_m, focus, {.id = SWM_ARG_ID_FOCUSMAIN} }, - { MODKEY, XK_1, switchws, {.id = 0} }, - { MODKEY, XK_2, switchws, {.id = 1} }, - { MODKEY, XK_3, switchws, {.id = 2} }, - { MODKEY, XK_4, switchws, {.id = 3} }, - { MODKEY, XK_5, switchws, {.id = 4} }, - { MODKEY, XK_6, switchws, {.id = 5} }, - { MODKEY, XK_7, switchws, {.id = 6} }, - { MODKEY, XK_8, switchws, {.id = 7} }, - { MODKEY, XK_9, switchws, {.id = 8} }, - { MODKEY, XK_0, switchws, {.id = 9} }, - { MODKEY, XK_Right, cyclews, {.id = SWM_ARG_ID_CYCLEWS_UP} }, - { MODKEY, XK_Left, cyclews, {.id = SWM_ARG_ID_CYCLEWS_DOWN} }, - { MODKEY | ShiftMask, XK_Right, cyclescr, {.id = SWM_ARG_ID_CYCLESC_UP} }, - { MODKEY | ShiftMask, XK_Left, cyclescr, {.id = SWM_ARG_ID_CYCLESC_DOWN} }, - { MODKEY | ShiftMask, XK_1, send_to_ws, {.id = 0} }, - { MODKEY | ShiftMask, XK_2, send_to_ws, {.id = 1} }, - { MODKEY | ShiftMask, XK_3, send_to_ws, {.id = 2} }, - { MODKEY | ShiftMask, XK_4, send_to_ws, {.id = 3} }, - { MODKEY | ShiftMask, XK_5, send_to_ws, {.id = 4} }, - { MODKEY | ShiftMask, XK_6, send_to_ws, {.id = 5} }, - { MODKEY | ShiftMask, XK_7, send_to_ws, {.id = 6} }, - { MODKEY | ShiftMask, XK_8, send_to_ws, {.id = 7} }, - { MODKEY | ShiftMask, XK_9, send_to_ws, {.id = 8} }, - { MODKEY | ShiftMask, XK_0, send_to_ws, {.id = 9} }, - { MODKEY, XK_b, bar_toggle, {0} }, - { MODKEY, XK_Tab, focus, {.id = SWM_ARG_ID_FOCUSNEXT} }, - { MODKEY | ShiftMask, XK_Tab, focus, {.id = SWM_ARG_ID_FOCUSPREV} }, - { MODKEY | ShiftMask, XK_x, wkill, {0} }, - { MODKEY, XK_s, screenshot, {.id = SWM_ARG_ID_SS_ALL} }, - { MODKEY | ShiftMask, XK_s, screenshot, {.id = SWM_ARG_ID_SS_WINDOW} }, - { MODKEY, XK_t, floating_toggle,{0} }, - { MODKEY | ShiftMask, XK_v, version, {0} }, - { MODKEY | ShiftMask, XK_Delete, spawn, {.argv = spawn_lock} }, - { MODKEY | ShiftMask, XK_i, spawn, {.argv = spawn_initscr} }, -}; - void resize_window(struct ws_win *win, int center) { @@ -1822,10 +2020,10 @@ resize(struct ws_win *win, union arg *args) handler[ev.type](&ev); break; case MotionNotify: - if (ev.xmotion.x < 0) - ev.xmotion.x = 0; - if (ev.xmotion.y < 0) - ev.xmotion.y = 0; + if (ev.xmotion.x <= 1) + ev.xmotion.x = 1; + if (ev.xmotion.y <= 1) + ev.xmotion.y = 1; win->g.w = ev.xmotion.x; win->g.h = ev.xmotion.y; @@ -1925,6 +2123,73 @@ move(struct ws_win *win, union arg *args) while (XCheckMaskEvent(display, EnterWindowMask, &ev)); } +/* key definitions */ +struct keyfunc { + char name[SWM_FUNCNAME_LEN]; + void (*func)(struct swm_region *r, union arg *); + union arg args; +} keyfuncs[kf_invalid] = { + /* name function argument */ + { "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} }, + { "master_del", stack_config, {.id = SWM_ARG_ID_MASTERDEL} }, + { "stack_inc", stack_config, {.id = SWM_ARG_ID_STACKINC} }, + { "stack_dec", stack_config, {.id = SWM_ARG_ID_STACKDEC} }, + { "swap_main", swapwin, {.id = SWM_ARG_ID_SWAPMAIN} }, + { "focus_next", focus, {.id = SWM_ARG_ID_FOCUSNEXT} }, + { "focus_prev", focus, {.id = SWM_ARG_ID_FOCUSPREV} }, + { "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} }, + { "quit", quit, {0} }, + { "restart", restart, {0} }, + { "focus_main", focus, {.id = SWM_ARG_ID_FOCUSMAIN} }, + { "ws_1", switchws, {.id = 0} }, + { "ws_2", switchws, {.id = 1} }, + { "ws_3", switchws, {.id = 2} }, + { "ws_4", switchws, {.id = 3} }, + { "ws_5", switchws, {.id = 4} }, + { "ws_6", switchws, {.id = 5} }, + { "ws_7", switchws, {.id = 6} }, + { "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} }, + { "mvws_1", send_to_ws, {.id = 0} }, + { "mvws_2", send_to_ws, {.id = 1} }, + { "mvws_3", send_to_ws, {.id = 2} }, + { "mvws_4", send_to_ws, {.id = 3} }, + { "mvws_5", send_to_ws, {.id = 4} }, + { "mvws_6", send_to_ws, {.id = 5} }, + { "mvws_7", send_to_ws, {.id = 6} }, + { "mvws_8", send_to_ws, {.id = 7} }, + { "mvws_9", send_to_ws, {.id = 8} }, + { "mvws_10", send_to_ws, {.id = 9} }, + { "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} }, + { "float_toggle", floating_toggle,{0} }, + { "version", version, {0} }, + { "spawn_lock", spawn, {.argv = spawn_lock} }, + { "spawn_initscr", spawn, {.argv = spawn_initscr} }, +}; +struct key { + unsigned int mod; + KeySym keysym; + enum keyfuncid funcid; +}; +int keys_size = 0, keys_length = 0; +struct key *keys = NULL; + /* mouse */ enum { client_click, root_click }; struct button { @@ -1941,6 +2206,223 @@ struct button { }; void +update_modkey(unsigned int mod) +{ + int i; + + for (i = 0; i < keys_length; i++) + if (keys[i].mod & ShiftMask) + keys[i].mod = mod | ShiftMask; + else + keys[i].mod = mod; + + for (i = 0; i < LENGTH(buttons); i++) + if (buttons[i].mask & ShiftMask) + buttons[i].mask = mod | ShiftMask; + else + buttons[i].mask = mod; +} + +#define SWM_MODNAME_SIZE 32 +#define SWM_KEY_WS "\n+ \t" +int +parsekeys(char *keystr, unsigned int currmod, unsigned int *mod, KeySym *ks) +{ + char *cp, *name; + KeySym uks; + if (mod == NULL || ks == NULL) + return (0); + cp = keystr; + *mod = 0; + while ((name = strsep(&cp, SWM_KEY_WS)) != NULL) { + if (cp) + cp += (long)strspn(cp, SWM_KEY_WS); + if (strncasecmp(name, "MOD", SWM_MODNAME_SIZE) == 0) + *mod |= currmod; + else if (!strncasecmp(name, "Mod1", SWM_MODNAME_SIZE)) + *mod |= Mod1Mask; + else if (!strncasecmp(name, "Mod2", SWM_MODNAME_SIZE)) + *mod += Mod2Mask; + else if (!strncmp(name, "Mod3", SWM_MODNAME_SIZE)) + *mod |= Mod3Mask; + else if (!strncmp(name, "Mod4", SWM_MODNAME_SIZE)) + *mod |= Mod4Mask; + else if (strncasecmp(name, "SHIFT", SWM_MODNAME_SIZE) == 0) + *mod |= ShiftMask; + else { + *ks = XStringToKeysym(name); + XConvertCase(*ks, ks, &uks); + if (ks == NoSymbol) { + DNPRINTF(SWM_D_KEY, + "parsekeys: invalid key %s\n", + name); + return (0); + } + } + } + return (1); +} +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); +} +void +setkeybinding(unsigned int mod, KeySym ks, enum keyfuncid kfid) +{ + int i, j; + /* 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); + j = keys_length - 1; + if (i < j) + keys[i] = keys[j]; + keys_length--; + return; + } else { + /* found: replace */ + DNPRINTF(SWM_D_KEY, + "setkeybinding: replace #%d %s\n", + i, keyfuncs[keys[i].funcid].name); + keys[i].mod = mod; + keys[i].keysym = ks; + keys[i].funcid = kfid; + return; + } + } + } + if (kfid == kf_invalid) { + fprintf(stderr, + "error: setkeybinding: cannot find mod/key combination"); + return; + } + /* not found: add */ + if (keys_size == 0 || keys == NULL) { + keys_size = 4; + DNPRINTF(SWM_D_KEY, "setkeybinding: init list %d\n", keys_size); + keys = malloc((size_t)keys_size * sizeof(struct key)); + if (!keys) { + fprintf(stderr, "malloc failed\n"); + perror(" failed"); + quit(NULL, NULL); + } + } else if (keys_length == keys_size) { + keys_size *= 2; + DNPRINTF(SWM_D_KEY, "setkeybinding: grow list %d\n", keys_size); + keys = realloc(keys, (size_t)keys_size * sizeof(struct key)); + if (!keys) { + fprintf(stderr, "realloc failed\n"); + perror(" failed"); + quit(NULL, NULL); + } + } + if (keys_length < keys_size) { + DNPRINTF(SWM_D_KEY, "setkeybinding: add %d\n", keys_length); + j = keys_length++; + keys[j].mod = mod; + keys[j].keysym = ks; + keys[j].funcid = kfid; + } else { + fprintf(stderr, "keys array problem?\n"); + if (!keys) { + fprintf(stderr, "keys array problem\n"); + quit(NULL, NULL); + } + } +} +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); +} +void updatenumlockmask(void) { unsigned int i, j; @@ -1973,7 +2455,7 @@ grabkeys(void) if (TAILQ_EMPTY(&screens[k].rl)) continue; XUngrabKey(display, AnyKey, AnyModifier, screens[k].root); - for (i = 0; i < LENGTH(keys); i++) { + for (i = 0; i < keys_length; i++) { if ((code = XKeysymToKeycode(display, keys[i].keysym))) for (j = 0; j < LENGTH(modifiers); j++) XGrabKey(display, code, @@ -2023,12 +2505,14 @@ keypress(XEvent *e) DNPRINTF(SWM_D_EVENT, "keypress: window: %lu\n", ev->window); keysym = XKeycodeToKeysym(display, (KeyCode)ev->keycode, 0); - for (i = 0; i < LENGTH(keys); i++) + for (i = 0; i < keys_length; i++) if (keysym == keys[i].keysym && CLEANMASK(keys[i].mod) == CLEANMASK(ev->state) - && keys[i].func) - keys[i].func(root_to_region(ev->root), - &(keys[i].args)); + && keyfuncs[keys[i].funcid].func) + keyfuncs[keys[i].funcid].func( + root_to_region(ev->root), + &(keyfuncs[keys[i].funcid].args) + ); } void @@ -2067,15 +2551,186 @@ set_win_state(struct ws_win *win, long state) (unsigned char *)data, 2); } +const char *quirkname[] = { + "NONE", /* config string for "no value" */ + "FLOAT", + "TRANSSZ", + "ANYWHERE", + "XTERM_FONTADJ", + "FULLSCREEN", +}; + +#define SWM_Q_WS "\n| \t" +int +parsequirks(char *qstr, unsigned long *quirk) +{ + char *cp, *name; + int i; + if (quirk == NULL) + return (0); + cp = qstr; + *quirk = 0; + while ((name = strsep(&cp, SWM_Q_WS)) != NULL) { + if (cp) + cp += (long)strspn(cp, SWM_Q_WS); + for (i = 0; i < LENGTH(quirkname); i++) { + if (!strncasecmp(name, quirkname[i], SWM_QUIRK_LEN)) { + DNPRINTF(SWM_D_QUIRK, "parsequirks: %s\n", name); + if (i == 0) { + *quirk = 0; + return (1); + } + *quirk |= 1 << (i-1); + break; + } + } + if (i >= LENGTH(quirkname)) { + DNPRINTF(SWM_D_QUIRK, + "parsequirks: invalid quirk [%s]\n", name); + return (0); + } + } + 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); +} +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) && + !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); + 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); + strlcpy(quirks[i].class, class, + sizeof quirks->class); + strlcpy(quirks[i].name, name, + sizeof quirks->name); + quirks[i].quirk = quirk; + return; + } + } + } + if (!quirk) { + fprintf(stderr, + "error: setquirk: cannot find class/name combination"); + return; + } + /* not found: add */ + if (quirks_size == 0 || quirks == NULL) { + quirks_size = 4; + DNPRINTF(SWM_D_QUIRK, "setquirk: init list %d\n", quirks_size); + quirks = malloc((size_t)quirks_size * sizeof(struct quirk)); + if (!quirks) { + fprintf(stderr, "setquirk: malloc failed\n"); + perror(" failed"); + quit(NULL, NULL); + } + } else if (quirks_length == quirks_size) { + quirks_size *= 2; + DNPRINTF(SWM_D_QUIRK, "setquirk: grow list %d\n", quirks_size); + quirks = realloc(quirks, (size_t)quirks_size * sizeof(struct quirk)); + if (!quirks) { + fprintf(stderr, "setquirk: realloc failed\n"); + perror(" failed"); + quit(NULL, NULL); + } + } + if (quirks_length < quirks_size) { + DNPRINTF(SWM_D_QUIRK, "setquirk: add %d\n", quirks_length); + j = quirks_length++; + strlcpy(quirks[j].class, class, sizeof quirks->class); + strlcpy(quirks[j].name, name, sizeof quirks->name); + quirks[j].quirk = quirk; + } else { + fprintf(stderr, "quirks array problem?\n"); + if (!quirks) { + fprintf(stderr, "quirks array problem!\n"); + quit(NULL, NULL); + } + } +} + +void +setup_quirks(void) +{ + setquirk("MPlayer", "xv", SWM_Q_FLOAT | SWM_Q_FULLSCREEN); + setquirk("OpenOffice.org 2.4", "VCLSalFrame", SWM_Q_FLOAT); + setquirk("OpenOffice.org 3.0", "VCLSalFrame", SWM_Q_FLOAT); + setquirk("Firefox-bin", "firefox-bin", SWM_Q_TRANSSZ); + setquirk("Firefox", "Dialog", SWM_Q_FLOAT); + setquirk("Gimp", "gimp", SWM_Q_FLOAT | SWM_Q_ANYWHERE); + setquirk("XTerm", "xterm", SWM_Q_XTERM_FONTADJ); + setquirk("xine", "Xine Window", SWM_Q_FLOAT | SWM_Q_ANYWHERE); + setquirk("Xitk", "Xitk Combo", SWM_Q_FLOAT | SWM_Q_ANYWHERE); + setquirk("xine", "xine Panel", SWM_Q_FLOAT | SWM_Q_ANYWHERE); + setquirk("Xitk", "Xine Window", SWM_Q_FLOAT | SWM_Q_ANYWHERE); + setquirk("xine", "xine Video Fullscreen Window", SWM_Q_FULLSCREEN | SWM_Q_FLOAT); + setquirk("pcb", "pcb", SWM_Q_FLOAT); +} + struct ws_win * manage_window(Window id) { Window trans; struct workspace *ws; struct ws_win *win; - int format, i, ws_idx; + int format, i, ws_idx, n; unsigned long nitems, bytes; Atom ws_idx_atom = 0, type; + Atom *prot = NULL, *pp; unsigned char ws_idx_str[SWM_PROPLEN], *prop = NULL; struct swm_region *r; long mask; @@ -2101,6 +2756,14 @@ manage_window(Window id) 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++) + if (*pp == adelete) + win->can_delete = 1; + if (prot) + XFree(prot); + } /* * Figure out where to put the window. If it was previously assigned to @@ -2143,8 +2806,7 @@ 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); - for (i = 0; quirks[i].class != NULL && quirks[i].name != NULL && - quirks[i].quirk != 0; i++){ + 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", @@ -2174,6 +2836,14 @@ manage_window(Window id) XConfigureWindow(display, win->id, mask, &wc); } + /* Reset font sizes (the bruteforce way; no default keybinding). */ + if (win->quirks & SWM_Q_XTERM_FONTADJ) { + for (i = 0; i < SWM_MAX_FONT_STEPS; i++) + fake_keypress(win, XK_KP_Subtract, ShiftMask); + for (i = 0; i < SWM_MAX_FONT_STEPS; i++) + fake_keypress(win, XK_KP_Add, ShiftMask); + } + XSelectInput(display, id, EnterWindowMask | FocusChangeMask | PropertyChangeMask | StructureNotifyMask); @@ -2211,9 +2881,11 @@ unmanage_window(struct ws_win *win) ws->focus = TAILQ_FIRST(&ws->winlist); if (ws->focus == NULL || ws->focus == win) { ws->focus = NULL; - unfocus_all(); + unfocus_win(win); } else focus_win(ws->focus); + if (ws->focus_prev == win) + ws->focus_prev = NULL; TAILQ_REMOVE(&win->ws->winlist, win, entry); set_win_state(win, WithdrawnState); @@ -2262,13 +2934,13 @@ configurerequest(XEvent *e) if (win->ws->r != NULL) { /* this seems to be full screen */ if (win->g.w >= WIDTH(win->ws->r)) { - win->g.x = -1; + win->g.x = 0; win->g.w = WIDTH(win->ws->r); ev->value_mask |= CWX | CWWidth; } if (win->g.h >= HEIGHT(win->ws->r)) { /* kill border */ - win->g.y = -1; + win->g.y = 0; win->g.h = HEIGHT(win->ws->r); ev->value_mask |= CWY | CWHeight; } @@ -2286,8 +2958,21 @@ configurerequest(XEvent *e) void configurenotify(XEvent *e) { + struct ws_win *win; + long mask; + DNPRINTF(SWM_D_EVENT, "configurenotify: window: %lu\n", e->xconfigure.window); + + XMapWindow(display, e->xconfigure.window); + win = find_window(e->xconfigure.window); + if (win) { + XGetWMNormalHints(display, win->id, &win->sh, &mask); + adjust_font(win); + XMapWindow(display, win->id); + if (font_adjusted) + stack(); + } } void @@ -2329,6 +3014,29 @@ focusin(XEvent *e) } 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; + + /* 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); + } + } +} + +void mappingnotify(XEvent *e) { XMappingEvent *ev = &e->xmapping; @@ -2462,13 +3170,12 @@ getstate(Window w) unsigned long n, extra; Atom real; - astate = XInternAtom(display, "WM_STATE", False); 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 = *p; + result = *((long *)p); XFree(p); return (result); } @@ -2540,7 +3247,6 @@ new_region(struct swm_screen *s, int x, int y, int w, int h) r->ws = ws; ws->r = r; TAILQ_INSERT_TAIL(&s->rl, r, entry); - bar_setup(r); } void @@ -2627,6 +3333,11 @@ screenchange(XEvent *e) { 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) + bar_setup(r); stack(); } @@ -2726,14 +3437,35 @@ setup_screens(void) } } +void +workaround(void) +{ + int i; + Atom netwmcheck, netwmname, utf8_string; + Window root; + + /* work around sun jdk bugs, code from wmname */ + netwmcheck = XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", False); + netwmname = XInternAtom(display, "_NET_WM_NAME", False); + utf8_string = XInternAtom(display, "UTF8_STRING", False); + for (i = 0; i < ScreenCount(display); i++) { + root = screens[i].root; + XChangeProperty(display, root, netwmcheck, XA_WINDOW, 32, + PropModeReplace, (unsigned char *)&root, 1); + XChangeProperty(display, root, netwmname, utf8_string, 8, + PropModeReplace, "LG3D", strlen("LG3D")); + } +} + int main(int argc, char *argv[]) { struct passwd *pwd; + struct swm_region *r; char conf[PATH_MAX], *cfile = NULL; struct stat sb; XEvent e; - int xfd; + int xfd, i; fd_set rd; start_argv = argv; @@ -2749,6 +3481,8 @@ main(int argc, char *argv[]) errx(1, "other wm running"); astate = XInternAtom(display, "WM_STATE", False); + aprot = XInternAtom(display, "WM_PROTOCOLS", False); + adelete = XInternAtom(display, "WM_DELETE_WINDOW", False); /* look for local and global conf file */ pwd = getpwuid(getuid()); @@ -2756,6 +3490,8 @@ main(int argc, char *argv[]) errx(1, "invalid user %d", getuid()); setup_screens(); + setup_keys(); + setup_quirks(); snprintf(conf, sizeof conf, "%s/.%s", pwd->pw_dir, SWM_CONF_FILE); if (stat(conf, &sb) != -1) { @@ -2770,9 +3506,14 @@ main(int argc, char *argv[]) } if (cfile) conf_load(cfile); - bar_refresh(); - /* ws[0].focus = TAILQ_FIRST(&ws[0].winlist); */ + /* setup all bars */ + for (i = 0; i < ScreenCount(display); i++) + TAILQ_FOREACH(r, &screens[i].rl, entry) + bar_setup(r); + + /* set some values to work around bad programs */ + workaround(); grabkeys(); stack();