X-Git-Url: https://jasonwoof.com/gitweb/?a=blobdiff_plain;f=scrotwm.c;h=a0d33b799640f141224c7eab340b21165d755a24;hb=e218053b3044cbc21b8c2485e0a1584ef344881f;hp=0558a4cd94985f4060ac2c5c00073cea05d185e6;hpb=838dae60f10a285d103e3ad26ef9fccaeadae159;p=spectrwm.git diff --git a/scrotwm.c b/scrotwm.c index 0558a4c..a0d33b7 100644 --- a/scrotwm.c +++ b/scrotwm.c @@ -52,7 +52,7 @@ static const char *cvstag = "$scrotwm$"; -#define SWM_VERSION "0.9.25" +#define SWM_VERSION "0.9.27" #include #include @@ -258,6 +258,7 @@ struct ws_win { int floatmaxed; /* flag: floater was maxed in max_stack */ int floating; int manual; + unsigned int ewmh_flags; int font_size_boundary[SWM_MAX_FONT_STEPS]; int font_steps; int last_inc; @@ -403,6 +404,335 @@ struct quirk { int quirks_size = 0, quirks_length = 0; struct quirk *quirks = NULL; +/* + * Supported EWMH hints should be added to + * both the enum and the ewmh array + */ +enum { _NET_ACTIVE_WINDOW, _NET_MOVERESIZE_WINDOW, _NET_CLOSE_WINDOW, + _NET_WM_WINDOW_TYPE, _NET_WM_WINDOW_TYPE_DOCK, + _NET_WM_WINDOW_TYPE_TOOLBAR, _NET_WM_WINDOW_TYPE_UTILITY, + _NET_WM_WINDOW_TYPE_SPLASH, _NET_WM_WINDOW_TYPE_DIALOG, + _NET_WM_WINDOW_TYPE_NORMAL, _NET_WM_STATE, + _NET_WM_STATE_MAXIMIZED_HORZ, _NET_WM_STATE_MAXIMIZED_VERT, + _NET_WM_STATE_SKIP_TASKBAR, _NET_WM_STATE_SKIP_PAGER, + _NET_WM_STATE_HIDDEN, _NET_WM_STATE_ABOVE, _SWM_WM_STATE_MANUAL, + _NET_WM_STATE_FULLSCREEN, _NET_WM_ALLOWED_ACTIONS, _NET_WM_ACTION_MOVE, + _NET_WM_ACTION_RESIZE, _NET_WM_ACTION_FULLSCREEN, _NET_WM_ACTION_CLOSE, + SWM_EWMH_HINT_MAX }; + +struct ewmh_hint { + char *name; + Atom atom; +} ewmh[SWM_EWMH_HINT_MAX] = { + /* must be in same order as in the enum */ + {"_NET_ACTIVE_WINDOW", None}, + {"_NET_MOVERESIZE_WINDOW", None}, + {"_NET_CLOSE_WINDOW", None}, + {"_NET_WM_WINDOW_TYPE", None}, + {"_NET_WM_WINDOW_TYPE_DOCK", None}, + {"_NET_WM_WINDOW_TYPE_TOOLBAR", None}, + {"_NET_WM_WINDOW_TYPE_UTILITY", None}, + {"_NET_WM_WINDOW_TYPE_SPLASH", None}, + {"_NET_WM_WINDOW_TYPE_DIALOG", None}, + {"_NET_WM_WINDOW_TYPE_NORMAL", None}, + {"_NET_WM_STATE", None}, + {"_NET_WM_STATE_MAXIMIZED_HORZ", None}, + {"_NET_WM_STATE_MAXIMIZED_VERT", None}, + {"_NET_WM_STATE_SKIP_TASKBAR", None}, + {"_NET_WM_STATE_SKIP_PAGER", None}, + {"_NET_WM_STATE_HIDDEN", None}, + {"_NET_WM_STATE_ABOVE", None}, + {"_SWM_WM_STATE_MANUAL", None}, + {"_NET_WM_STATE_FULLSCREEN", None}, + {"_NET_WM_ALLOWED_ACTIONS", None}, + {"_NET_WM_ACTION_MOVE", None}, + {"_NET_WM_ACTION_RESIZE", None}, + {"_NET_WM_ACTION_FULLSCREEN", None}, + {"_NET_WM_ACTION_CLOSE", None}, +}; + +void store_float_geom(struct ws_win *win, struct swm_region *r); +int floating_toggle_win(struct ws_win *win); + +int +get_property(Window id, Atom atom, long count, Atom type, + unsigned long *n, unsigned char **data) +{ + int format, status; + unsigned long tmp, extra; + unsigned long *nitems; + Atom real; + + nitems = n != NULL ? n : &tmp; + status = XGetWindowProperty(display, id, atom, 0L, count, False, type, + &real, &format, nitems, &extra, data); + + if (status != Success) + return False; + if (real != type) + return False; + + return True; +} + +void +setup_ewmh(void) +{ + int i,j; + Atom sup_list; + + sup_list = XInternAtom(display, "_NET_SUPPORTED", False); + + for (i = 0; i < LENGTH(ewmh); i++) + ewmh[i].atom = XInternAtom(display, ewmh[i].name, False); + + for (i = 0; i < ScreenCount(display); i++) { + /* Support check window will be created by workaround(). */ + + /* Report supported atoms */ + XDeleteProperty(display, screens[i].root, sup_list); + for (j = 0; j < LENGTH(ewmh); j++) + XChangeProperty(display, screens[i].root, sup_list, XA_ATOM, 32, + PropModeAppend, (unsigned char *)&ewmh[j].atom,1); + } +} + +void +teardown_ewmh(void) +{ + int i, success; + unsigned char *data = NULL; + unsigned long n; + Atom sup_check, sup_list; + Window id; + + sup_check = XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", False); + sup_list = XInternAtom(display, "_NET_SUPPORTED", False); + + for (i = 0; i < ScreenCount(display); i++) { + /* Get the support check window and destroy it */ + success = get_property(screens[i].root, sup_check, 1, XA_WINDOW, + &n, &data); + + if (success) { + id = data[0]; + XDestroyWindow(display, id); + XDeleteProperty(display, screens[i].root, sup_check); + XDeleteProperty(display, screens[i].root, sup_list); + } + + XFree(data); + } +} + +void +ewmh_autoquirk(struct ws_win *win) +{ + int success, i; + unsigned long *data = NULL; + unsigned long n; + Atom type; + + success = get_property(win->id, ewmh[_NET_WM_WINDOW_TYPE].atom, (~0L), + XA_ATOM, &n, (unsigned char **)&data); + + if (!success) { + XFree(data); + return; + } + + for (i = 0; i < n; i++) { + type = data[i]; + if (type == ewmh[_NET_WM_WINDOW_TYPE_NORMAL].atom) + break; + if (type == ewmh[_NET_WM_WINDOW_TYPE_DOCK].atom || + type == ewmh[_NET_WM_WINDOW_TYPE_TOOLBAR].atom || + type == ewmh[_NET_WM_WINDOW_TYPE_UTILITY].atom) { + win->floating = 1; + win->quirks = SWM_Q_FLOAT | SWM_Q_ANYWHERE; + break; + } + if (type == ewmh[_NET_WM_WINDOW_TYPE_SPLASH].atom || + type == ewmh[_NET_WM_WINDOW_TYPE_DIALOG].atom) { + win->floating = 1; + win->quirks = SWM_Q_FLOAT; + break; + } + } + + XFree(data); +} + +#define SWM_EWMH_ACTION_COUNT_MAX (6) +#define EWMH_F_FULLSCREEN (1<<0) +#define EWMH_F_ABOVE (1<<1) +#define EWMH_F_HIDDEN (1<<2) +#define EWMH_F_SKIP_PAGER (1<<3) +#define EWMH_F_SKIP_TASKBAR (1<<4) +#define SWM_F_MANUAL (1<<5) + +int +ewmh_set_win_fullscreen(struct ws_win *win, int fs) +{ + struct swm_geometry rg; + + if (!win->ws->r) + return 0; + + if (!win->floating) + return 0; + + DNPRINTF(SWM_D_MISC, "ewmh_set_win_fullscreen: win 0x%lx fs: %d\n", + win->id, fs); + + rg = win->ws->r->g; + + if (fs) { + store_float_geom(win, win->ws->r); + + win->g.x = rg.x; + win->g.y = rg.y; + win->g.w = rg.w; + win->g.h = rg.h; + } else { + if (win->g_floatvalid) { + /* refloat at last floating relative position */ + win->g.x = win->g_float.x - win->rg_float.x + rg.x; + win->g.y = win->g_float.y - win->rg_float.y + rg.y; + win->g.w = win->g_float.w; + win->g.h = win->g_float.h; + } + } + + return 1; +} + +void +ewmh_update_actions(struct ws_win *win) +{ + Atom actions[SWM_EWMH_ACTION_COUNT_MAX]; + int n = 0; + + if (win == NULL) + return; + + actions[n++] = ewmh[_NET_WM_ACTION_CLOSE].atom; + + if (win->floating) { + actions[n++] = ewmh[_NET_WM_ACTION_MOVE].atom; + actions[n++] = ewmh[_NET_WM_ACTION_RESIZE].atom; + } + + XChangeProperty(display, win->id, ewmh[_NET_WM_ALLOWED_ACTIONS].atom, + XA_ATOM, 32, PropModeReplace, (unsigned char *)actions, n); +} + +#define _NET_WM_STATE_REMOVE 0 /* remove/unset property */ +#define _NET_WM_STATE_ADD 1 /* add/set property */ +#define _NET_WM_STATE_TOGGLE 2 /* toggle property */ + +void +ewmh_update_win_state(struct ws_win *win, long state, long action) +{ + unsigned int mask = 0; + unsigned int changed = 0; + unsigned int orig_flags; + + if (win == NULL) + return; + + if (state == ewmh[_NET_WM_STATE_FULLSCREEN].atom) + mask = EWMH_F_FULLSCREEN; + if (state == ewmh[_NET_WM_STATE_ABOVE].atom) + mask = EWMH_F_ABOVE; + if (state == ewmh[_SWM_WM_STATE_MANUAL].atom) + mask = SWM_F_MANUAL; + if (state == ewmh[_NET_WM_STATE_SKIP_PAGER].atom) + mask = EWMH_F_SKIP_PAGER; + if (state == ewmh[_NET_WM_STATE_SKIP_TASKBAR].atom) + mask = EWMH_F_SKIP_TASKBAR; + + + orig_flags = win->ewmh_flags; + + switch (action) { + case _NET_WM_STATE_REMOVE: + win->ewmh_flags &= ~mask; + break; + case _NET_WM_STATE_ADD: + win->ewmh_flags |= mask; + break; + case _NET_WM_STATE_TOGGLE: + win->ewmh_flags ^= mask; + break; + } + + changed = (win->ewmh_flags & mask) ^ (orig_flags & mask) ? 1 : 0; + + if (state == ewmh[_NET_WM_STATE_ABOVE].atom) + if (changed) + if (!floating_toggle_win(win)) + win->ewmh_flags = orig_flags; /* revert */ + if (state == ewmh[_SWM_WM_STATE_MANUAL].atom) + if (changed) + win->manual = (win->ewmh_flags & SWM_F_MANUAL) != 0; + if (state == ewmh[_NET_WM_STATE_FULLSCREEN].atom) + if (changed) + if (!ewmh_set_win_fullscreen(win, win->ewmh_flags & EWMH_F_FULLSCREEN)) + win->ewmh_flags = orig_flags; /* revert */ + + XDeleteProperty(display, win->id, ewmh[_NET_WM_STATE].atom); + + if (win->ewmh_flags & EWMH_F_FULLSCREEN) + XChangeProperty(display, win->id, ewmh[_NET_WM_STATE].atom, + XA_ATOM, 32, PropModeAppend, + (unsigned char *)&ewmh[_NET_WM_STATE_FULLSCREEN].atom, 1); + if (win->ewmh_flags & EWMH_F_SKIP_PAGER) + XChangeProperty(display, win->id, ewmh[_NET_WM_STATE].atom, + XA_ATOM, 32, PropModeAppend, + (unsigned char *)&ewmh[_NET_WM_STATE_SKIP_PAGER].atom, 1); + if (win->ewmh_flags & EWMH_F_SKIP_TASKBAR) + XChangeProperty(display, win->id, ewmh[_NET_WM_STATE].atom, + XA_ATOM, 32, PropModeAppend, + (unsigned char *)&ewmh[_NET_WM_STATE_SKIP_TASKBAR].atom, 1); + if (win->ewmh_flags & EWMH_F_ABOVE) + XChangeProperty(display, win->id, ewmh[_NET_WM_STATE].atom, + XA_ATOM, 32, PropModeAppend, + (unsigned char *)&ewmh[_NET_WM_STATE_ABOVE].atom, 1); + if (win->ewmh_flags & SWM_F_MANUAL) + XChangeProperty(display, win->id, ewmh[_NET_WM_STATE].atom, + XA_ATOM, 32, PropModeAppend, + (unsigned char *)&ewmh[_SWM_WM_STATE_MANUAL].atom, 1); +} + +void +ewmh_get_win_state(struct ws_win *win) +{ + int success, i; + unsigned long n; + Atom *states; + + if (win == NULL) + return; + + win->ewmh_flags = 0; + if (win->floating) + win->ewmh_flags |= EWMH_F_ABOVE; + if (win->manual) + win->ewmh_flags |= SWM_F_MANUAL; + + success = get_property(win->id, ewmh[_NET_WM_STATE].atom, (~0L), XA_ATOM, + &n, (unsigned char **)&states); + + if (!success) + return; + + for (i = 0; i < n; i++) + ewmh_update_win_state(win, states[i], _NET_WM_STATE_ADD); + + XFree(states); +} + /* events */ #ifdef SWM_DEBUG void @@ -579,6 +909,7 @@ void maprequest(XEvent *); void propertynotify(XEvent *); void unmapnotify(XEvent *); void visibilitynotify(XEvent *); +void clientmessage(XEvent *); void (*handler[LASTEvent])(XEvent *) = { [Expose] = expose, @@ -596,6 +927,7 @@ void (*handler[LASTEvent])(XEvent *) = { [PropertyNotify] = propertynotify, [UnmapNotify] = unmapnotify, [VisibilityNotify] = visibilitynotify, + [ClientMessage] = clientmessage, }; void @@ -708,10 +1040,12 @@ custom_region(char *val) if (x < 0 || x > DisplayWidth(display, sidx) || y < 0 || y > DisplayHeight(display, sidx) || w + x > DisplayWidth(display, sidx) || - h + y > DisplayHeight(display, sidx)) - errx(1, "region %ux%u+%u+%u not within screen boundaries " + h + y > DisplayHeight(display, sidx)) { + fprintf(stderr, "ignoring region %ux%u+%u+%u - not within screen boundaries " "(%ux%u)\n", w, h, x, y, DisplayWidth(display, sidx), DisplayHeight(display, sidx)); + return; + } new_region(&screens[sidx], x, y, w, h); } @@ -978,15 +1312,11 @@ set_win_state(struct ws_win *win, long state) long getstate(Window w) { - int format, status; long result = -1; unsigned char *p = NULL; - unsigned long n, extra; - Atom real; + unsigned long n; - status = XGetWindowProperty(display, w, astate, 0L, 2L, False, astate, - &real, &format, &n, &extra, (unsigned char **)&p); - if (status != Success) + if (!get_property(w, astate, 2L, astate, &n, &p)) return (-1); if (n != 0) result = *((long *)p); @@ -1109,9 +1439,8 @@ unmap_window(struct ws_win *win) set_win_state(win, IconicState); XUnmapWindow(display, win->id); - if (win->ws->r) - XSetWindowBorder(display, win->id, - win->ws->r->s->c[SWM_S_COLOR_UNFOCUS].color); + XSetWindowBorder(display, win->id, + win->s->c[SWM_S_COLOR_UNFOCUS].color); } void @@ -1224,7 +1553,8 @@ find_window(Window id) { struct ws_win *win; Window wrr, wpr, *wcr = NULL; - int i, j, nc; + int i, j; + unsigned int nc; for (i = 0; i < ScreenCount(display); i++) for (j = 0; j < SWM_WS_MAX; j++) @@ -1383,6 +1713,7 @@ void unfocus_win(struct ws_win *win) { XEvent cne; + Window none = None; DNPRINTF(SWM_D_FOCUS, "unfocus_win: id: %lu\n", WINID(win)); @@ -1392,7 +1723,7 @@ unfocus_win(struct ws_win *win) return; if (validate_ws(win->ws)) - abort(); + abort(); /* XXX replace with return at some point */ if (win->ws->r == NULL) return; @@ -1424,6 +1755,9 @@ unfocus_win(struct ws_win *win) XSetWindowBorder(display, win->id, win->ws->r->s->c[SWM_S_COLOR_UNFOCUS].color); + XChangeProperty(display, win->s->root, + ewmh[_NET_ACTIVE_WINDOW].atom, XA_WINDOW, 32, + PropModeReplace, (unsigned char *)&none,1); } void @@ -1457,7 +1791,8 @@ focus_win(struct ws_win *win) return; if (validate_ws(win->ws)) - abort(); + abort(); /* XXX replace with return at some point */ + if (validate_win(win)) { kill_refs(win); return; @@ -1487,6 +1822,10 @@ focus_win(struct ws_win *win) win->ws->r->s->c[SWM_S_COLOR_FOCUS].color); if (win->ws->cur_layout->flags & SWM_L_MAPONFOCUS) XMapRaised(display, win->id); + + XChangeProperty(display, win->s->root, + ewmh[_NET_ACTIVE_WINDOW].atom, XA_WINDOW, 32, + PropModeReplace, (unsigned char *)&win->id,1); } } @@ -1583,14 +1922,14 @@ void priorws(struct swm_region *r, union arg *args) { union arg a; - + DNPRINTF(SWM_D_WS, "priorws id %d " "in screen[%d]:%dx%d+%d+%d ws %d\n", args->id, r->s->idx, WIDTH(r), HEIGHT(r), X(r), Y(r), r->ws->idx); - + if (r->ws_prior == NULL) return; - + a.id = r->ws_prior->idx; switchws(r, &a); } @@ -1743,10 +2082,10 @@ focus_prev(struct ws_win *win) /* if in max_stack try harder */ if (ws->cur_layout->flags & SWM_L_FOCUSPREV) { - if (cur_focus != ws->focus_prev) - winfocus = ws->focus_prev; - else if (cur_focus != ws->focus) - winfocus = ws->focus; + if (cur_focus != ws->focus_prev) + winfocus = ws->focus_prev; + else if (cur_focus != ws->focus) + winfocus = ws->focus; else winfocus = TAILQ_PREV(win, ws_win_list, entry); if (winfocus) @@ -1800,7 +2139,7 @@ focus(struct swm_region *r, union arg *args) winlostfocus = cur_focus; switch (args->id) { - case SWM_ARG_ID_FOCUSPREV: + case SWM_ARG_ID_FOCUSPREV: winfocus = TAILQ_PREV(cur_focus, ws_win_list, entry); if (winfocus == NULL) winfocus = TAILQ_LAST(wl, ws_win_list); @@ -1928,7 +2267,8 @@ stack_floater(struct ws_win *win, struct swm_region *r) * geom on ws switches or return from max mode */ - if (win->floatmaxed || (r != r->ws->old_r && win->g_floatvalid) ) { + if (win->floatmaxed || (r != r->ws->old_r && win->g_floatvalid + && !(win->ewmh_flags & EWMH_F_FULLSCREEN))) { /* * use stored g and rg to set relative position and size * as in old region or before max stack mode @@ -1957,8 +2297,8 @@ stack_floater(struct ws_win *win, struct swm_region *r) * floaters and transients are auto-centred unless moved * or resized */ - win->g.x = r->g.x + (WIDTH(r) - win->g.w) / 2; - win->g.y = r->g.y + (HEIGHT(r) - win->g.h) / 2; + win->g.x = r->g.x + (WIDTH(r) - win->g.w) / 2; + win->g.y = r->g.y + (HEIGHT(r) - win->g.h) / 2; } /* win can be outside r if new r smaller than old r */ @@ -1983,7 +2323,8 @@ stack_floater(struct ws_win *win, struct swm_region *r) * Retain floater and transient geometry for correct positioning * when ws changes region */ - store_float_geom(win, r); + if (!(win->ewmh_flags & EWMH_F_FULLSCREEN)) + store_float_geom(win, r); DNPRINTF(SWM_D_MISC, "stack_floater: win %lu x %d y %d w %d h %d\n", win->id, wc.x, wc.y, wc.width, wc.height); @@ -2032,7 +2373,7 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip) XWindowChanges wc; XWindowAttributes wa; struct swm_geometry win_g, r_g = *g; - struct ws_win *win; + struct ws_win *win, *fs_win = 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; @@ -2109,6 +2450,11 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip) if (win->transient != 0 || win->floating != 0) continue; + if (win->ewmh_flags & EWMH_F_FULLSCREEN) { + fs_win = win; + continue; + } + if (split && i == split) { colno = (winno - mwin) / stacks; if (s <= (winno - mwin) % stacks) @@ -2205,9 +2551,19 @@ stack_master(struct workspace *ws, struct swm_geometry *g, int rot, int flip) if (win->transient == 0 && win->floating == 0) continue; + if (win->ewmh_flags & EWMH_F_FULLSCREEN) { + fs_win = win; + continue; + } + stack_floater(win, ws->r); XMapRaised(display, win->id); } + + if (fs_win) { + stack_floater(fs_win, ws->r); + XMapRaised(display, fs_win->id); + } } void @@ -2413,6 +2769,7 @@ send_to_ws(struct swm_region *r, union arg *args) XChangeProperty(display, win->id, ws_idx_atom, XA_STRING, 8, PropModeReplace, ws_idx_str, SWM_PROPLEN); } + stack(); } @@ -2421,7 +2778,7 @@ wkill(struct swm_region *r, union arg *args) { DNPRINTF(SWM_D_MISC, "wkill %d\n", args->id); - if(r->ws->focus == NULL) + if (r->ws->focus == NULL) return; if (args->id == SWM_ARG_ID_KILLWINDOW) @@ -2431,18 +2788,23 @@ wkill(struct swm_region *r, union arg *args) client_msg(r->ws->focus, adelete); } -void -floating_toggle(struct swm_region *r, union arg *args) + +int +floating_toggle_win(struct ws_win *win) { - struct ws_win *win = r->ws->focus; - union arg a; + struct swm_region *r; if (win == NULL) - return; + return 0; + + if (!win->ws->r) + return 0; + + r = win->ws->r; /* reject floating toggles in max stack mode */ - if (r->ws->cur_layout == &layouts[SWM_MAX_STACK]) - return; + if (win->ws->cur_layout == &layouts[SWM_MAX_STACK]) + return 0; if (win->floating) { if (!win->floatmaxed) { @@ -2452,18 +2814,38 @@ floating_toggle(struct swm_region *r, union arg *args) win->floating = 0; } else { if (win->g_floatvalid) { - /* refloat at last floating relative position */ - win->g.x = win->g_float.x - win->rg_float.x + r->g.x; - win->g.y = win->g_float.y - win->rg_float.y + r->g.y; - win->g.w = win->g_float.w; - win->g.h = win->g_float.h; - } + /* refloat at last floating relative position */ + win->g.x = win->g_float.x - win->rg_float.x + r->g.x; + win->g.y = win->g_float.y - win->rg_float.y + r->g.y; + win->g.w = win->g_float.w; + win->g.h = win->g_float.h; + } win->floating = 1; } + ewmh_update_actions(win); + + return 1; +} + +void +floating_toggle(struct swm_region *r, union arg *args) +{ + struct ws_win *win = r->ws->focus; + union arg a; + + if (win == NULL) + return; + + ewmh_update_win_state(win, ewmh[_NET_WM_STATE_ABOVE].atom, + _NET_WM_STATE_TOGGLE); + stack(); - a.id = SWM_ARG_ID_FOCUSCUR; - focus(win->ws->r, &a); + + if (win == win->ws->focus) { + a.id = SWM_ARG_ID_FOCUSCUR; + focus(win->ws->r, &a); + } } void @@ -2513,6 +2895,8 @@ resize(struct ws_win *win, union arg *args) return; win->manual = 1; + ewmh_update_win_state(win, ewmh[_SWM_WM_STATE_MANUAL].atom, + _NET_WM_STATE_ADD); /* raise the window = move to last in window list */ a.id = SWM_ARG_ID_MOVELAST; swapwin(r, &a); @@ -2619,7 +3003,11 @@ move(struct ws_win *win, union arg *args) win->manual = 1; if (win->floating == 0 && !win->transient) { win->floating = 1; + ewmh_update_win_state(win, ewmh[_NET_WM_STATE_ABOVE].atom, + _NET_WM_STATE_ADD); } + ewmh_update_win_state(win, ewmh[_SWM_WM_STATE_MANUAL].atom, + _NET_WM_STATE_ADD); /* raise the window = move to last in window list */ a.id = SWM_ARG_ID_MOVELAST; @@ -3382,7 +3770,7 @@ grabbuttons(struct ws_win *win, int focused) updatenumlockmask(); XUngrabButton(display, AnyButton, AnyModifier, win->id); - if(focused) { + if (focused) { for (i = 0; i < LENGTH(buttons); i++) if (buttons[i].action == client_click) for (j = 0; j < LENGTH(modifiers); j++) @@ -3850,6 +4238,7 @@ manage_window(Window id) TAILQ_INSERT_TAIL(&win->ws->winlist, win, entry); if (win->transient) set_child_transient(win); + ewmh_update_actions(win); return (win); } @@ -3870,6 +4259,7 @@ manage_window(Window id) DNPRINTF(SWM_D_MISC, "manage_window: win %lu transient %lu\n", win->id, win->transient); } + /* get supported protocols */ if (XGetWMProtocols(display, id, &prot, &n)) { for (i = 0, pp = prot; i < n; i++, pp++) { @@ -3924,6 +4314,7 @@ manage_window(Window id) win->g.y = win->wa.y; win->g_floatvalid = 0; win->floatmaxed = 0; + win->ewmh_flags = 0; /* Set window properties so we can remember this after reincarnation */ if (ws_idx_atom && prop == NULL && @@ -3933,7 +4324,10 @@ manage_window(Window id) XChangeProperty(display, win->id, ws_idx_atom, XA_STRING, 8, PropModeReplace, ws_idx_str, SWM_PROPLEN); } - XFree(prop); + if (prop) + XFree(prop); + + ewmh_autoquirk(win); if (XGetClassHint(display, win->id, &win->ch)) { DNPRINTF(SWM_D_CLASS, "class: %s name: %s\n", @@ -3964,7 +4358,7 @@ manage_window(Window id) win->manual = 1; /* don't center the quirky windows */ bzero(&wc, sizeof wc); mask = 0; - if (win->g.y < bar_height) { + if (bar_enabled && win->g.y < bar_height) { win->g.y = wc.y = bar_height; mask |= CWY; } @@ -3983,6 +4377,10 @@ manage_window(Window id) fake_keypress(win, XK_KP_Add, ShiftMask); } + ewmh_get_win_state(win); + ewmh_update_actions(win); + ewmh_update_win_state(win, None, _NET_WM_STATE_REMOVE); + /* border me */ if (border_me) { bzero(&wc, sizeof wc); @@ -4046,7 +4444,9 @@ unmanage_window(struct ws_win *win) parent->child_trans = NULL; } - /* work around for mplayer going full screen */ + /* focus on root just in case */ + XSetInputFocus(display, PointerRoot, PointerRoot, CurrentTime); + if (!win->floating) focus_prev(win); @@ -4071,9 +4471,17 @@ focus_magic(struct ws_win *win, int do_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); + /* make sure transient hasn't dissapeared */ + if (validate_win(win->child_trans) == 0) { + focus_win(win->child_trans); + if (win->child_trans->take_focus) + client_msg(win->child_trans, takefocus); + } else { + win->child_trans = NULL; + focus_win(win); + if (win->take_focus) + client_msg(win, takefocus); + } } } else { /* regular focus */ @@ -4504,6 +4912,59 @@ visibilitynotify(XEvent *e) bar_update(); } +void +clientmessage(XEvent *e) +{ + XClientMessageEvent *ev; + struct ws_win *win; + + ev = &e->xclient; + + win = find_window(ev->window); + if (win == NULL) + return; + + DNPRINTF(SWM_D_EVENT, "clientmessage: window: 0x%lx type: %ld \n", + ev->window, ev->message_type); + + if (ev->message_type == ewmh[_NET_ACTIVE_WINDOW].atom) { + DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_ACTIVE_WINDOW \n"); + focus_win(win); + } + if (ev->message_type == ewmh[_NET_CLOSE_WINDOW].atom) { + DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_CLOSE_WINDOW \n"); + if (win->can_delete) + client_msg(win, adelete); + else + XKillClient(display, win->id); + } + if (ev->message_type == ewmh[_NET_MOVERESIZE_WINDOW].atom) { + DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_MOVERESIZE_WINDOW \n"); + if (win->floating) { + if (ev->data.l[0] & (1<<8)) /* x */ + win->g.x = ev->data.l[1]; + if (ev->data.l[0] & (1<<9)) /* y */ + win->g.y = ev->data.l[2]; + if (ev->data.l[0] & (1<<10)) /* width */ + win->g.w = ev->data.l[3]; + if (ev->data.l[0] & (1<<11)) /* height */ + win->g.h = ev->data.l[4]; + } + else { + /* TODO: Change stack sizes */ + } + config_win(win); + } + if (ev->message_type == ewmh[_NET_WM_STATE].atom) { + DNPRINTF(SWM_D_EVENT, "clientmessage: _NET_WM_STATE \n"); + ewmh_update_win_state(win, ev->data.l[1], ev->data.l[0]); + if (ev->data.l[2]) + ewmh_update_win_state(win, ev->data.l[2], ev->data.l[0]); + + stack(); + } +} + int xerror_start(Display *d, XErrorEvent *ee) { @@ -4698,16 +5159,57 @@ screenchange(XEvent *e) { } void -setup_screens(void) +grab_windows(void) { Window d1, d2, *wins = NULL; XWindowAttributes wa; unsigned int no; - int i, j, k; + int i, j; + long state, manage; + + for (i = 0; i < ScreenCount(display); i++) { + if (!XQueryTree(display, screens[i].root, &d1, &d2, &wins, &no)) + continue; + + /* attach windows to a region */ + /* normal windows */ + for (j = 0; j < no; j++) { + if (!XGetWindowAttributes(display, wins[j], &wa) || + wa.override_redirect || + XGetTransientForHint(display, wins[j], &d1)) + continue; + + state = getstate(wins[j]); + manage = 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) || + wa.override_redirect) + continue; + + state = getstate(wins[j]); + manage = state == IconicState; + if (XGetTransientForHint(display, wins[j], &d1) && + manage) + manage_window(wins[j]); + } + if (wins) { + XFree(wins); + wins = NULL; + } + } +} + +void +setup_screens(void) +{ + int i, j, k; int errorbase, major, minor; struct workspace *ws; int ws_idx_atom; - long state, manage; if ((screens = calloc(ScreenCount(display), sizeof(struct swm_screen))) == NULL) @@ -4758,45 +5260,12 @@ setup_screens(void) SWM_ARG_ID_STACKINIT); ws->cur_layout = &layouts[0]; } - /* grab existing windows (before we build the bars)*/ - if (!XQueryTree(display, screens[i].root, &d1, &d2, &wins, &no)) - continue; scan_xrandr(i); if (xrandr_support) XRRSelectInput(display, screens[i].root, RRScreenChangeNotifyMask); - - /* attach windows to a region */ - /* normal windows */ - for (j = 0; j < no; j++) { - if (!XGetWindowAttributes(display, wins[j], &wa) || - wa.override_redirect || - XGetTransientForHint(display, wins[j], &d1)) - continue; - - state = getstate(wins[j]); - manage = 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) || - wa.override_redirect) - continue; - - state = getstate(wins[j]); - manage = state == IconicState; - if (XGetTransientForHint(display, wins[j], &d1) && - manage) - manage_window(wins[j]); - } - if (wins) { - XFree(wins); - wins = NULL; - } } } @@ -4823,7 +5292,7 @@ workaround(void) { int i; Atom netwmcheck, netwmname, utf8_string; - Window root; + Window root, win; /* work around sun jdk bugs, code from wmname */ netwmcheck = XInternAtom(display, "_NET_SUPPORTING_WM_CHECK", False); @@ -4831,10 +5300,16 @@ workaround(void) utf8_string = XInternAtom(display, "UTF8_STRING", False); for (i = 0; i < ScreenCount(display); i++) { root = screens[i].root; + win = XCreateSimpleWindow(display,root, 0, 0, 1, 1, 0, + screens[i].c[SWM_S_COLOR_UNFOCUS].color, + screens[i].c[SWM_S_COLOR_UNFOCUS].color); + XChangeProperty(display, root, netwmcheck, XA_WINDOW, 32, - PropModeReplace, (unsigned char *)&root, 1); - XChangeProperty(display, root, netwmname, utf8_string, 8, - PropModeReplace, "LG3D", strlen("LG3D")); + PropModeReplace, (unsigned char *)&win,1); + XChangeProperty(display, win, netwmcheck, XA_WINDOW, 32, + PropModeReplace, (unsigned char *)&win,1); + XChangeProperty(display, win, netwmname, utf8_string, 8, + PropModeReplace, (unsigned char*)"LG3D", strlen("LG3D")); } } @@ -4895,6 +5370,7 @@ main(int argc, char *argv[]) setup_quirks(); setup_spawn(); + /* load config */ snprintf(conf, sizeof conf, "%s/.%s", pwd->pw_dir, SWM_CONF_FILE); if (stat(conf, &sb) != -1) { if (S_ISREG(sb.st_mode)) @@ -4909,6 +5385,13 @@ main(int argc, char *argv[]) if (cfile) conf_load(cfile); + setup_ewmh(); + /* set some values to work around bad programs */ + workaround(); + + /* grab existing windows (before we build the bars) */ + grab_windows(); + /* setup all bars */ for (i = 0; i < ScreenCount(display); i++) TAILQ_FOREACH(r, &screens[i].rl, entry) { @@ -4917,9 +5400,6 @@ main(int argc, char *argv[]) bar_setup(r); } - /* set some values to work around bad programs */ - workaround(); - unfocus_all(); grabkeys(); @@ -4990,6 +5470,7 @@ main(int argc, char *argv[]) } } done: + teardown_ewmh(); bar_extra_stop(); XFreeGC(display, bar_gc); XCloseDisplay(display);