From 3121181a18c6d084d08283f7cb65ac6d89b53b97 Mon Sep 17 00:00:00 2001 From: Yauheni Kaliuta Date: Sun, 25 Nov 2018 18:42:44 +0200 Subject: [PATCH 1/7] main,win: factor out wm guessing and their options set code Move g.option_wm set including the guessing code to win.c to keep wm related code in one place. Move also g.option_max_reclevel assignemnt. Signed-off-by: Yauheni Kaliuta --- src/alttab.c | 41 ++++------------------------------------- src/alttab.h | 2 ++ src/win.c | 47 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+), 37 deletions(-) diff --git a/src/alttab.c b/src/alttab.c index 504a769..902097e 100644 --- a/src/alttab.c +++ b/src/alttab.c @@ -95,10 +95,6 @@ static int use_args_and_xrm(int *argc, char **argv) char *rm; char *empty = ""; int uo; - Atom nwm_prop, atype; - unsigned char *nwm; - int form; - unsigned long remain, len; XrmOptionDescRec xrmTable[] = { {"-w", "*windowmanager", XrmoptionSepArg, NULL}, {"-d", "*desktops", XrmoptionSepArg, NULL}, @@ -169,43 +165,20 @@ static int use_args_and_xrm(int *argc, char **argv) switch (xresource_load_int(&db, XRMAPPNAME, "windowmanager", &wmindex)) { case 1: - if (wmindex >= WM_MIN && wmindex <= WM_MAX) { - g.option_wm = wmindex; - goto wmDone; - } else { + if (wmindex < WM_MIN || wmindex > WM_MAX) { die(inv, "windowmanager argument range"); } break; case 0: msg(0, "no WM index or unknown, guessing\n"); + wmindex = WM_GUESS; break; case -1: die(inv, "windowmanager argument"); break; } -// EWMH? - if (ewmh_detectFeatures(&(g.ewmh))) { - msg(0, "EWMH-compatible WM detected: %s\n", g.ewmh.wmname); - g.option_wm = WM_EWMH; - goto wmDone; - } -// ratpoison? - nwm_prop = XInternAtom(dpy, "_NET_WM_NAME", false); - if (XGetWindowProperty(dpy, root, nwm_prop, 0, MAXNAMESZ, false, - AnyPropertyType, &atype, &form, &len, &remain, - &nwm) == Success && nwm) { - msg(0, "_NET_WM_NAME root property present: %s\n", nwm); - if (strstr((char *)nwm, "ratpoison") != NULL) { - g.option_wm = WM_RATPOISON; - XFree(nwm); - goto wmDone; - } - XFree(nwm); - } - msg(0, "unknown WM, using WM_TWM\n"); - g.option_wm = WM_TWM; - wmDone: - msg(0, "WM: %d\n", g.option_wm); + + initWin(wmindex); switch (xresource_load_int(&db, XRMAPPNAME, "desktops", &dsindex)) { case 1: @@ -430,12 +403,6 @@ static int use_args_and_xrm(int *argc, char **argv) g.option_font = DEFFONT + 4; } -// max recursion for searching windows -// -1 is "everything" -// in raw X this returns too much windows, "1" is probably sufficient -// no need for an option - g.option_max_reclevel = (g.option_wm == WM_NO) ? 1 : -1; - return 1; } diff --git a/src/alttab.h b/src/alttab.h index a09c821..2e29fae 100644 --- a/src/alttab.h +++ b/src/alttab.h @@ -133,6 +133,7 @@ typedef struct { #define WM_RATPOISON 2 #define WM_TWM 3 #define WM_MAX 3 +#define WM_GUESS -1 int option_wm; #define DESK_MIN 0 #define DESK_CURRENT 0 @@ -200,6 +201,7 @@ Window getUiwin(); void shutdownGUI(void); // windows +void initWin(int wmindex); int startupWintasks(); int addIconFromProperty(WindowInfo * wi); int addIconFromHints(WindowInfo * wi); diff --git a/src/win.c b/src/win.c index 8f35bfa..2557650 100644 --- a/src/win.c +++ b/src/win.c @@ -139,6 +139,53 @@ void addToSortlist(Window w, bool to_head, bool move) } } +// +// initializes g.option_wm +// wmindex is the already validated option +void initWin(int wmindex) +{ + unsigned long remain, len; + Atom nwm_prop, atype; + int form; + unsigned char *nwm; + + if (wmindex != WM_GUESS) { + g.option_wm = wmindex; + goto out; + } + +// EWMH? + if (ewmh_detectFeatures(&(g.ewmh))) { + msg(0, "EWMH-compatible WM detected: %s\n", g.ewmh.wmname); + g.option_wm = WM_EWMH; + goto out; + } +// ratpoison? + nwm_prop = XInternAtom(dpy, "_NET_WM_NAME", false); + if (XGetWindowProperty(dpy, root, nwm_prop, 0, MAXNAMESZ, false, + AnyPropertyType, &atype, &form, &len, &remain, + &nwm) == Success && nwm) { + msg(0, "_NET_WM_NAME root property present: %s\n", nwm); + if (strstr((char *)nwm, "ratpoison") != NULL) { + g.option_wm = WM_RATPOISON; + XFree(nwm); + goto out; + } + XFree(nwm); + } + msg(0, "unknown WM, using WM_TWM\n"); + g.option_wm = WM_TWM; + +out: + msg(0, "WM: %d\n", g.option_wm); + +// max recursion for searching windows +// -1 is "everything" +// in raw X this returns too much windows, "1" is probably sufficient +// no need for an option + g.option_max_reclevel = (g.option_wm == WM_NO) ? 1 : -1; +} + // // early initialization // once per execution From 2e08a09de8291d89f4bf5a066a911b592f6f493f Mon Sep 17 00:00:00 2001 From: Yauheni Kaliuta Date: Sun, 25 Nov 2018 21:16:52 +0200 Subject: [PATCH 2/7] win, WMs: access WM specific code with methods Customize WM functionality via methods of struct WmOps. Copy WM specific code from win.c to the corresponding WM files (ewmh, rp, x) and replace it with methods calls. Provide inline wrappers for default values for the cases when WM does not require custom functionality. Signed-off-by: Yauheni Kaliuta --- src/alttab.h | 15 +++++ src/ewmh.c | 59 +++++++++++++++++++ src/rp.c | 46 +++++++++++++++ src/win.c | 163 ++++++++++++++++++++++----------------------------- src/x.c | 22 +++++++ 5 files changed, 213 insertions(+), 92 deletions(-) diff --git a/src/alttab.h b/src/alttab.h index 2e29fae..313390a 100644 --- a/src/alttab.h +++ b/src/alttab.h @@ -107,6 +107,21 @@ typedef struct PermanentWindowInfo { struct PermanentWindowInfo *next, *prev; } PermanentWindowInfo; +struct WmOps { + bool (*probe)(void); + int (*startup)(void); + int (*winlist)(Window win, int reclevel); + int (*setFocus)(int idx); + Window (*getActiveWindow)(void); + bool (*skipWindowInTaskbar)(Window w); + bool (*skipFocusChangeEvent)(void); +}; + +extern struct WmOps WmNoOps; +extern struct WmOps WmEwmhOps; +extern struct WmOps WmRatpoisonOps; +extern struct WmOps WmTwmOps; + /* typedef struct SwitchMoment { Window prev; diff --git a/src/ewmh.c b/src/ewmh.c index 6dffe13..bcfee90 100644 --- a/src/ewmh.c +++ b/src/ewmh.c @@ -338,3 +338,62 @@ bool ewmh_skipWindowInTaskbar(Window w) free(state); return ret; } + +static bool ewmhProbe(void) +{ + return ewmh_detectFeatures(&g.ewmh); +} + +static int ewmhStartup(void) +{ + long rootevmask = 0; + + // root: watching for _NET_ACTIVE_WINDOW + g.naw = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", true); + rootevmask |= PropertyChangeMask; + + // warning: this overwrites any previous value. + // note: x_setCommonPropertiesForAnyWindow does the similar thing + // for any window other than root and uiwin + XSelectInput(dpy, root, rootevmask); + + return 1; +} + +static int ewmhWinlist(Window win, int rec) +{ + return ewmh_initWinlist(); +} + +static int ewmhSetFocus(int winNdx) +{ + int r; + XWindowAttributes att; + + r = ewmh_setFocus(winNdx, 0); + // XSetInputFocus stuff. + // skippy-xd does it and notes that "order is important". + // fixes #28. + // it must be protected by testing IsViewable in the same way + // as in x.c, or BadMatch happens after switching desktops. + XGetWindowAttributes(dpy, g.winlist[winNdx].id, &att); + if (att.map_state == IsViewable) + XSetInputFocus(dpy, g.winlist[winNdx].id, RevertToParent, + CurrentTime); + return r; +} + +static bool ewmhSkipFocusChangeEvent(void) +{ + return true; +} + +struct WmOps WmEwmhOps = { + .probe = ewmhProbe, + .startup = ewmhStartup, + .winlist = ewmhWinlist, + .setFocus = ewmhSetFocus, + .getActiveWindow = ewmh_getActiveWindow, + .skipWindowInTaskbar = ewmh_skipWindowInTaskbar, + .skipFocusChangeEvent = ewmhSkipFocusChangeEvent, +}; diff --git a/src/rp.c b/src/rp.c index 1a0cfa9..7353517 100644 --- a/src/rp.c +++ b/src/rp.c @@ -30,6 +30,8 @@ along with alttab. If not, see . #include "alttab.h" #include "util.h" extern Globals g; +extern Display *dpy; +extern Window root; // PRIVATE @@ -236,3 +238,47 @@ int rp_setFocus(int winNdx) return 1; } + +static bool ratpoisonProbe(void) +{ + unsigned long remain, len; + Atom nwm_prop, atype; + int form; + unsigned char *nwm; + bool ret = false; + + nwm_prop = XInternAtom(dpy, "_NET_WM_NAME", false); + if (!XGetWindowProperty(dpy, root, nwm_prop, 0, MAXNAMESZ, false, + AnyPropertyType, &atype, &form, &len, &remain, + &nwm) == Success && nwm) + return false; + + msg(0, "_NET_WM_NAME root property present: %s\n", nwm); + if (strstr((char *)nwm, "ratpoison") != NULL) + ret = true; + + XFree(nwm); + return ret; +} + +static int ratpoisonStartup(void) +{ + return rp_startupWintasks(); +} + +static int ratpoisonWinlist(Window win, int rec) +{ + return rp_initWinlist(); +} + +static int ratpoisonSetFocus(int idx) +{ + return rp_setFocus(idx); +} + +struct WmOps WmRatpoisonOps = { + .probe = ratpoisonProbe, + .startup = ratpoisonStartup, + .winlist = ratpoisonWinlist, + .setFocus = ratpoisonSetFocus, +}; diff --git a/src/win.c b/src/win.c index 2557650..dde7158 100644 --- a/src/win.c +++ b/src/win.c @@ -38,6 +38,62 @@ extern Window root; // PRIVATE +static struct WmOps *WmOpsVariants[] = { + [WM_NO] = &WmNoOps, + [WM_EWMH] = &WmEwmhOps, + [WM_RATPOISON] = &WmRatpoisonOps, + [WM_TWM] = &WmTwmOps, +}; + +static struct WmOps *WmOps; + +static inline bool wmProbe(int wm) +{ + return (WmOpsVariants[wm]->probe != NULL) && (WmOpsVariants[wm]->probe()); +} + +static inline int wmStartup(void) +{ + if (WmOps->startup == NULL) + return 1; + return WmOps->startup(); +} + +static inline int wmInitWinlist(Window win, int reclevel) +{ + if (WmOps->winlist == NULL) + return 0; + return WmOps->winlist(win, reclevel); +} + +static inline int wmSetFocus(int idx) +{ + if (WmOps->setFocus == NULL) + return 0; + return WmOps->setFocus(idx); +} + +static inline Window wmGetActiveWindow(void) +{ + if (WmOps->getActiveWindow == NULL) + return 0; + return WmOps->getActiveWindow(); +} + +static inline bool wmSkipWindowInTaskbar(Window w) +{ + if (WmOps->skipWindowInTaskbar == NULL) + return false; + return WmOps->skipWindowInTaskbar(w); +} + +static inline bool wmSkipFocusChangeEvent(void) +{ + if (WmOps->skipFocusChangeEvent == NULL) + return false; + return WmOps->skipFocusChangeEvent(); +} + // // helper for windows' qsort // CAUSES O(log(N)) OR EVEN WORSE! reintroduce winlist[]->order instead? @@ -144,40 +200,28 @@ void addToSortlist(Window w, bool to_head, bool move) // wmindex is the already validated option void initWin(int wmindex) { - unsigned long remain, len; - Atom nwm_prop, atype; - int form; - unsigned char *nwm; - if (wmindex != WM_GUESS) { g.option_wm = wmindex; goto out; } -// EWMH? - if (ewmh_detectFeatures(&(g.ewmh))) { + if (wmProbe(WM_EWMH)) { msg(0, "EWMH-compatible WM detected: %s\n", g.ewmh.wmname); g.option_wm = WM_EWMH; goto out; } -// ratpoison? - nwm_prop = XInternAtom(dpy, "_NET_WM_NAME", false); - if (XGetWindowProperty(dpy, root, nwm_prop, 0, MAXNAMESZ, false, - AnyPropertyType, &atype, &form, &len, &remain, - &nwm) == Success && nwm) { - msg(0, "_NET_WM_NAME root property present: %s\n", nwm); - if (strstr((char *)nwm, "ratpoison") != NULL) { - g.option_wm = WM_RATPOISON; - XFree(nwm); - goto out; - } - XFree(nwm); + + if (wmProbe(WM_RATPOISON)) { + g.option_wm = WM_RATPOISON; + goto out; } + msg(0, "unknown WM, using WM_TWM\n"); g.option_wm = WM_TWM; out: msg(0, "WM: %d\n", g.option_wm); + WmOps = WmOpsVariants[g.option_wm]; // max recursion for searching windows // -1 is "everything" @@ -192,37 +236,13 @@ void initWin(int wmindex) // int startupWintasks() { - long rootevmask = 0; - g.sortlist = NULL; // utlist head must be initialized to NULL g.ic = NULL; // uthash too if (g.option_iconSrc != ISRC_RAM) { initIconHash(&(g.ic)); } - // root: watching for _NET_ACTIVE_WINDOW - if (g.option_wm == WM_EWMH) { - g.naw = XInternAtom(dpy, "_NET_ACTIVE_WINDOW", true); - rootevmask |= PropertyChangeMask; - } - // warning: this overwrites any previous value. - // note: x_setCommonPropertiesForAnyWindow does the similar thing - // for any window other than root and uiwin - if (rootevmask != 0) { - XSelectInput(dpy, root, rootevmask); - } - switch (g.option_wm) { - case WM_NO: - return 1; - case WM_RATPOISON: - return rp_startupWintasks(); - case WM_EWMH: - return 1; - case WM_TWM: - return 1; - default: - return 0; - } + return wmStartup(); } // @@ -555,23 +575,8 @@ int initWinlist(void) msg(1, "before initWinlist\n"); print_sortlist(); } - switch (g.option_wm) { - case WM_NO: - r = x_initWindowsInfoRecursive(root, 0); // note: direction/current window index aren't used - break; - case WM_RATPOISON: - r = rp_initWinlist(); - break; - case WM_EWMH: - r = ewmh_initWinlist(); - break; - case WM_TWM: - r = x_initWindowsInfoRecursive(root, 0); - break; - default: - r = 0; - break; - } + + r = wmInitWinlist(root, 0); if (!r) __initWinlist(); @@ -618,34 +623,8 @@ void freeWinlist() int setFocus(int winNdx) { int r; - switch (g.option_wm) { - case WM_NO: - r = ewmh_setFocus(winNdx, 0); // for WM which isn't identified as EWMH compatible but accepts setting focus (dwm) - x_setFocus(winNdx); - break; - case WM_RATPOISON: - r = rp_setFocus(winNdx); - break; - case WM_EWMH: - r = ewmh_setFocus(winNdx, 0); - // XSetInputFocus stuff. - // skippy-xd does it and notes that "order is important". - // fixes #28. - // it must be protected by testing IsViewable in the same way - // as in x.c, or BadMatch happens after switching desktops. - XWindowAttributes att; - XGetWindowAttributes(dpy, g.winlist[winNdx].id, &att); - if (att.map_state == IsViewable) - XSetInputFocus(dpy, g.winlist[winNdx].id, RevertToParent, - CurrentTime); - break; - case WM_TWM: - r = ewmh_setFocus(winNdx, 0); - x_setFocus(winNdx); - break; - default: - return 0; - } + + r = wmSetFocus(winNdx); // pull to head addToSortlist(g.winlist[winNdx].id, true, true); return r; @@ -672,7 +651,7 @@ void winPropChangeEvent(XPropertyEvent e) if (e.atom != g.naw) return; // don't check for wm==EWMH, because _NET_ACTIVE_WINDOW changed for sure - aw = ewmh_getActiveWindow(); + aw = wmGetActiveWindow(); // can't get active window if (!aw) return; @@ -683,7 +662,7 @@ void winPropChangeEvent(XPropertyEvent e) if (g.sortlist != NULL && aw == g.sortlist->id) return; // is window hidden in WM? - if (ewmh_skipWindowInTaskbar(aw)) + if (wmSkipWindowInTaskbar(aw)) return; /* // the i3 sortlist bug is not here, see ewmh.c init_winlist instead @@ -792,7 +771,7 @@ void winFocusChangeEvent(XFocusChangeEvent e) // in non-EWMH only // probably should also maintain _NET_ACTIVE_WINDOW // support flag in EwmhFeatures - if (g.option_wm == WM_EWMH) + if (wmSkipFocusChangeEvent()) return; // focusIn only if (e.type != FocusIn) diff --git a/src/x.c b/src/x.c index e59c6e6..44dc5b9 100644 --- a/src/x.c +++ b/src/x.c @@ -171,3 +171,25 @@ void x_setCommonPropertiesForAnyWindow(Window win) if (evmask != 0) XSelectInput(dpy, win, evmask); } + +static int xSetFocus(int idx) +{ + int r; + + // for WM which isn't identified as EWMH compatible + // but accepts setting focus (dwm) + r = ewmh_setFocus(idx, 0); + x_setFocus(idx); + + return r; +} + +struct WmOps WmNoOps = { + .winlist = x_initWindowsInfoRecursive, + .setFocus = xSetFocus, +}; + +struct WmOps WmTwmOps = { + .winlist = x_initWindowsInfoRecursive, + .setFocus = xSetFocus, +}; From 6281ed031ac3e164c8254bc1dabda4b763ca0038 Mon Sep 17 00:00:00 2001 From: Yauheni Kaliuta Date: Sun, 25 Nov 2018 21:39:31 +0200 Subject: [PATCH 3/7] WMs: make most of the old API private After introducing WM abstraction, most of the old API is used only inside of the WMs' modules, so make them static (and move to the PRIVATE part). There are still cross dependencies to solve, like ewmh_setFocus() from x.c. Signed-off-by: Yauheni Kaliuta --- src/alttab.h | 11 --------- src/ewmh.c | 63 +++++++++++++++++++++++++++------------------------- src/rp.c | 10 ++++----- src/x.c | 32 +++++++++++++------------- 4 files changed, 54 insertions(+), 62 deletions(-) diff --git a/src/alttab.h b/src/alttab.h index 313390a..2fab0a8 100644 --- a/src/alttab.h +++ b/src/alttab.h @@ -226,11 +226,6 @@ int addWindowInfo(Window win, int reclevel, int wm_id, unsigned long desktop, int initWinlist(void); void freeWinlist(); int setFocus(int winNdx); -int rp_startupWintasks(); -int x_initWindowsInfoRecursive(Window win, int reclevel); -int rp_initWinlist(); -int x_setFocus(int wndx); -int rp_setFocus(int winNdx); int execAndReadStdout(char *exe, char *args[], char *buf, int bufsize); int pulloutWindowToTop(int winNdx); void winPropChangeEvent(XPropertyEvent e); @@ -243,13 +238,7 @@ void addToSortlist(Window w, bool to_head, bool move); void shutdownWin(void); /* EWHM */ -bool ewmh_detectFeatures(EwmhFeatures * e); -Window ewmh_getActiveWindow(); -int ewmh_initWinlist(); int ewmh_setFocus(int winNdx, Window fwin); // fwin used if non-zero -unsigned long ewmh_getCurrentDesktop(); -unsigned long ewmh_getDesktopOfWindow(Window w); -bool ewmh_skipWindowInTaskbar(Window w); /* RANDR */ bool randrAvailable(); diff --git a/src/ewmh.c b/src/ewmh.c index bcfee90..ef65aff 100644 --- a/src/ewmh.c +++ b/src/ewmh.c @@ -36,6 +36,9 @@ extern int scr; extern Window root; // PRIVATE +static unsigned long ewmh_getCurrentDesktop(); +static unsigned long ewmh_getDesktopOfWindow(Window w); +static bool ewmh_skipWindowInTaskbar(Window w); // // returns ptr to EWMH client list and client_list_size @@ -116,13 +119,11 @@ static int ewmh_switch_window(unsigned long window) return ewmh_send_wm_evt(window, "_NET_ACTIVE_WINDOW", edata); } -// PUBLIC - // // initialize EwmhFeatures // return true if usable at all // -bool ewmh_detectFeatures(EwmhFeatures * e) +static bool ewmh_detectFeatures(EwmhFeatures * e) { Window *chld_win; char *r; @@ -178,7 +179,7 @@ bool ewmh_detectFeatures(EwmhFeatures * e) // // active window or 0 // -Window ewmh_getActiveWindow() +static Window ewmh_getActiveWindow() { Window w = (Window) 0; char *awp; @@ -196,7 +197,7 @@ Window ewmh_getActiveWindow() // initialize winlist, correcting sortlist // return 1 if ok // -int ewmh_initWinlist() +static int ewmh_initWinlist() { Window *client_list; unsigned long client_list_size; @@ -254,28 +255,6 @@ int ewmh_initWinlist() return 1; } -// -// focus window in EWMH WM -// fwin used if non-zero, winNdx otherwise -// -int ewmh_setFocus(int winNdx, Window fwin) -{ - Window win = (fwin != 0) ? fwin : g.winlist[winNdx].id; - msg(1, "ewmh_setFocus win 0x%lx\n", win); - if (fwin == 0 && g.option_desktop != DESK_CURRENT) { - unsigned long wdesk = g.winlist[winNdx].desktop; - unsigned long cdesk = ewmh_getCurrentDesktop(); - msg(1, "ewmh_setFocus fwin 0x%lx opt %d wdesk %lu cdesk %lu\n", - fwin, g.option_desktop, wdesk, cdesk); - if (cdesk != wdesk && wdesk != DESKTOP_UNKNOWN) { - ewmh_switch_desktop(wdesk); - } - } - ewmh_switch_window(win); - XMapRaised(dpy, win); - return 1; -} - static unsigned long ewmh_getDesktopFromProp(Window w, char *prop1, char *prop2) { unsigned long *d; @@ -295,7 +274,7 @@ static unsigned long ewmh_getDesktopFromProp(Window w, char *prop1, char *prop2) // // get current desktop in EWMH WM // -unsigned long ewmh_getCurrentDesktop() +static unsigned long ewmh_getCurrentDesktop() { return ewmh_getDesktopFromProp(root, "_NET_CURRENT_DESKTOP", "_WIN_WORKSPACE"); } @@ -303,7 +282,7 @@ unsigned long ewmh_getCurrentDesktop() // // get desktop of window w in EWMH WM // -unsigned long ewmh_getDesktopOfWindow(Window w) +static unsigned long ewmh_getDesktopOfWindow(Window w) { return ewmh_getDesktopFromProp(w, "_NET_WM_DESKTOP", "_WIN_WORKSPACE"); } @@ -311,7 +290,7 @@ unsigned long ewmh_getDesktopOfWindow(Window w) // // does window have _NET_WM_STATE_SKIP_TASKBAR // -bool ewmh_skipWindowInTaskbar(Window w) +static bool ewmh_skipWindowInTaskbar(Window w) { Atom *state; long unsigned int state_propsize; @@ -388,6 +367,30 @@ static bool ewmhSkipFocusChangeEvent(void) return true; } +// PUBLIC + +// +// focus window in EWMH WM +// fwin used if non-zero, winNdx otherwise +// +int ewmh_setFocus(int winNdx, Window fwin) +{ + Window win = (fwin != 0) ? fwin : g.winlist[winNdx].id; + msg(1, "ewmh_setFocus win 0x%lx\n", win); + if (fwin == 0 && g.option_desktop != DESK_CURRENT) { + unsigned long wdesk = g.winlist[winNdx].desktop; + unsigned long cdesk = ewmh_getCurrentDesktop(); + msg(1, "ewmh_setFocus fwin 0x%lx opt %d wdesk %lu cdesk %lu\n", + fwin, g.option_desktop, wdesk, cdesk); + if (cdesk != wdesk && wdesk != DESKTOP_UNKNOWN) { + ewmh_switch_desktop(wdesk); + } + } + ewmh_switch_window(win); + XMapRaised(dpy, win); + return 1; +} + struct WmOps WmEwmhOps = { .probe = ewmhProbe, .startup = ewmhStartup, diff --git a/src/rp.c b/src/rp.c index 7353517..b193ded 100644 --- a/src/rp.c +++ b/src/rp.c @@ -101,12 +101,10 @@ static int rp_add_windows_in_group(int current_group, int window_group) return 1; } -// PUBLIC - // // early initialization in ratpoison // -int rp_startupWintasks() +static int rp_startupWintasks() { // search for ratpoison executable for later execv @@ -160,7 +158,7 @@ int rp_startupWintasks() // // initialize winlist/update sortlist from ratpoison output // -int rp_initWinlist() +static int rp_initWinlist() { #define fallback { msg(-1, "using current rp group\n") ; \ return rp_add_windows_in_group(DESKTOP_UNKNOWN, DESKTOP_UNKNOWN); } @@ -219,7 +217,7 @@ int rp_initWinlist() // // focus window in ratpoison // -int rp_setFocus(int winNdx) +static int rp_setFocus(int winNdx) { char selarg[64]; char *args[] = { "ratpoison", "-c", selarg, NULL }; @@ -276,6 +274,8 @@ static int ratpoisonSetFocus(int idx) return rp_setFocus(idx); } +// PUBLIC + struct WmOps WmRatpoisonOps = { .probe = ratpoisonProbe, .startup = ratpoisonStartup, diff --git a/src/x.c b/src/x.c index 44dc5b9..fed936e 100644 --- a/src/x.c +++ b/src/x.c @@ -57,13 +57,11 @@ Window x_get_leader(Window win) return leader; } -// PUBLIC - // // set winlist,maxNdx recursively using raw Xlib // first call should be (win=root, reclevel=0) // -int x_initWindowsInfoRecursive(Window win, int reclevel) +static int x_initWindowsInfoRecursive(Window win, int reclevel) { Window root, parent; @@ -126,7 +124,7 @@ int x_initWindowsInfoRecursive(Window win, int reclevel) // // set window focus in raw X // -int x_setFocus(int wndx) +static int x_setFocus(int wndx) { Window w = g.winlist[wndx].id; @@ -149,6 +147,20 @@ int x_setFocus(int wndx) return 1; } +static int xSetFocus(int idx) +{ + int r; + + // for WM which isn't identified as EWMH compatible + // but accepts setting focus (dwm) + r = ewmh_setFocus(idx, 0); + x_setFocus(idx); + + return r; +} + +// PUBLIC + // // this is where alttab is supposed to set properties or // register interest in event for ANY foreign window encountered. @@ -172,18 +184,6 @@ void x_setCommonPropertiesForAnyWindow(Window win) XSelectInput(dpy, win, evmask); } -static int xSetFocus(int idx) -{ - int r; - - // for WM which isn't identified as EWMH compatible - // but accepts setting focus (dwm) - r = ewmh_setFocus(idx, 0); - x_setFocus(idx); - - return r; -} - struct WmOps WmNoOps = { .winlist = x_initWindowsInfoRecursive, .setFocus = xSetFocus, From 554af8220970a87d7b67792ba0f35f4defecbe0d Mon Sep 17 00:00:00 2001 From: Yauheni Kaliuta Date: Sun, 25 Nov 2018 21:55:59 +0200 Subject: [PATCH 4/7] win, x: move x_setCommonPropertiesForAnyWindow() to win win.c handles exacly common things, where customization is done under struct WmOps, so the function more suits there. If there is a need to move X related stuff to a separate module, then it makes sense to look at that independently of WM_NO and WM_TWM code and move it to more self-descriptive file. Signed-off-by: Yauheni Kaliuta --- src/alttab.h | 1 - src/win.c | 25 ++++++++++++++++++++++++- src/x.c | 23 ----------------------- 3 files changed, 24 insertions(+), 25 deletions(-) diff --git a/src/alttab.h b/src/alttab.h index 2fab0a8..581c12f 100644 --- a/src/alttab.h +++ b/src/alttab.h @@ -233,7 +233,6 @@ void winDestroyEvent(XDestroyWindowEvent e); void winFocusChangeEvent(XFocusChangeEvent e); bool common_skipWindow(Window w, unsigned long current_desktop, unsigned long window_desktop); -void x_setCommonPropertiesForAnyWindow(Window win); void addToSortlist(Window w, bool to_head, bool move); void shutdownWin(void); diff --git a/src/win.c b/src/win.c index dde7158..dd1ddd1 100644 --- a/src/win.c +++ b/src/win.c @@ -155,6 +155,29 @@ static void print_winlist() } } +// +// this is where alttab is supposed to set properties or +// register interest in event for ANY foreign window encountered. +// warning: this is called only on addition to sortlist. +// +static void winSetCommonPropertiesForAnyWindow(Window win) +{ + long evmask = 0; + // root and our window are treated elsewhere + if (win == root || win == getUiwin()) + return; + // for delete notification + evmask |= StructureNotifyMask; + // for focusIn notification + if (g.option_wm != WM_EWMH) { + msg(0, "using direct focus tracking for 0x%lx\n", win); + evmask |= FocusChangeMask; + } + // warning: this overwrites previous value + if (evmask != 0) + XSelectInput(dpy, win, evmask); +} + // PUBLIC // @@ -191,7 +214,7 @@ void addToSortlist(Window w, bool to_head, bool move) if (add && !was) { // new window // register interest in events - x_setCommonPropertiesForAnyWindow(w); + winSetCommonPropertiesForAnyWindow(w); } } diff --git a/src/x.c b/src/x.c index fed936e..91f4dac 100644 --- a/src/x.c +++ b/src/x.c @@ -161,29 +161,6 @@ static int xSetFocus(int idx) // PUBLIC -// -// this is where alttab is supposed to set properties or -// register interest in event for ANY foreign window encountered. -// warning: this is called only on addition to sortlist. -// -void x_setCommonPropertiesForAnyWindow(Window win) -{ - long evmask = 0; - // root and our window are treated elsewhere - if (win == root || win == getUiwin()) - return; - // for delete notification - evmask |= StructureNotifyMask; - // for focusIn notification - if (g.option_wm != WM_EWMH) { - msg(0, "using direct focus tracking for 0x%lx\n", win); - evmask |= FocusChangeMask; - } - // warning: this overwrites previous value - if (evmask != 0) - XSelectInput(dpy, win, evmask); -} - struct WmOps WmNoOps = { .winlist = x_initWindowsInfoRecursive, .setFocus = xSetFocus, From 3a8d59d8b471aa3c9c0ac5ce88d59133defdc253 Mon Sep 17 00:00:00 2001 From: Yauheni Kaliuta Date: Sun, 25 Nov 2018 22:10:33 +0200 Subject: [PATCH 5/7] global, x: make max_reclevel local for x The global option max_reclevel used only for raw X recursive windows iterating, so make it local for it. Signed-off-by: Yauheni Kaliuta --- src/alttab.h | 1 - src/win.c | 6 ------ src/x.c | 22 +++++++++++++++++++++- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/src/alttab.h b/src/alttab.h index 581c12f..f25a8f5 100644 --- a/src/alttab.h +++ b/src/alttab.h @@ -141,7 +141,6 @@ typedef struct { * unlike g.winlist, survives uiHide */ PermanentWindowInfo *sortlist; // option_* are initialized from command line arguments or X resources or defaults - int option_max_reclevel; // max reclevel. -1 is "everything" #define WM_MIN 0 #define WM_NO 0 #define WM_EWMH 1 diff --git a/src/win.c b/src/win.c index dd1ddd1..c6a36b8 100644 --- a/src/win.c +++ b/src/win.c @@ -245,12 +245,6 @@ void initWin(int wmindex) out: msg(0, "WM: %d\n", g.option_wm); WmOps = WmOpsVariants[g.option_wm]; - -// max recursion for searching windows -// -1 is "everything" -// in raw X this returns too much windows, "1" is probably sufficient -// no need for an option - g.option_max_reclevel = (g.option_wm == WM_NO) ? 1 : -1; } // diff --git a/src/x.c b/src/x.c index 91f4dac..384146d 100644 --- a/src/x.c +++ b/src/x.c @@ -33,6 +33,12 @@ extern Window root; // PRIVATE +// max recursion for searching windows +// -1 is "everything" +// in raw X this returns too much windows, "1" is probably sufficient +// no need for an option +static int max_reclevel; + // // get window group leader // to be used later. rewritten, not tested. @@ -103,7 +109,7 @@ static int x_initWindowsInfoRecursive(Window win, int reclevel) addWindowInfo(win, reclevel, 0, DESKTOP_UNKNOWN, winname); } // skip children if max recursion level reached - if (g.option_max_reclevel != -1 && reclevel >= g.option_max_reclevel) + if (max_reclevel != -1 && reclevel >= max_reclevel) return 1; // recursion @@ -159,14 +165,28 @@ static int xSetFocus(int idx) return r; } +static int xStartup(void) +{ + max_reclevel = 1; + return 1; +} + +static int twmStartup(void) +{ + max_reclevel = -1; + return 1; +} + // PUBLIC struct WmOps WmNoOps = { + .startup = xStartup, .winlist = x_initWindowsInfoRecursive, .setFocus = xSetFocus, }; struct WmOps WmTwmOps = { + .startup = twmStartup, .winlist = x_initWindowsInfoRecursive, .setFocus = xSetFocus, }; From 7df87e8a6a8f9eb3b88d47304e50b0a0a54b9221 Mon Sep 17 00:00:00 2001 From: Yauheni Kaliuta Date: Sun, 25 Nov 2018 22:21:16 +0200 Subject: [PATCH 6/7] global, WMs: get rid of global_wm option After introducing abstract interface, there is no more need to make branches based on the global_wm option value, so no need in the option. The patch requires one more hook to struct WmOps. Signed-off-by: Yauheni Kaliuta --- src/alttab.h | 17 +++++++++-------- src/ewmh.c | 7 +++++++ src/win.c | 27 ++++++++++++++++----------- src/x.c | 13 ++++++++----- 4 files changed, 40 insertions(+), 24 deletions(-) diff --git a/src/alttab.h b/src/alttab.h index f25a8f5..aa3b5d1 100644 --- a/src/alttab.h +++ b/src/alttab.h @@ -58,6 +58,14 @@ along with alttab. If not, see . #define DEFPREVKEYKS XK_VoidSymbol #define DEFNEXTKEYKS XK_VoidSymbol +#define WM_MIN 0 +#define WM_NO 0 +#define WM_EWMH 1 +#define WM_RATPOISON 2 +#define WM_TWM 3 +#define WM_MAX 3 +#define WM_GUESS -1 + #include "icon.h" #ifndef COMTYPES @@ -115,6 +123,7 @@ struct WmOps { Window (*getActiveWindow)(void); bool (*skipWindowInTaskbar)(Window w); bool (*skipFocusChangeEvent)(void); + long (*eventMask)(Window w); }; extern struct WmOps WmNoOps; @@ -141,14 +150,6 @@ typedef struct { * unlike g.winlist, survives uiHide */ PermanentWindowInfo *sortlist; // option_* are initialized from command line arguments or X resources or defaults -#define WM_MIN 0 -#define WM_NO 0 -#define WM_EWMH 1 -#define WM_RATPOISON 2 -#define WM_TWM 3 -#define WM_MAX 3 -#define WM_GUESS -1 - int option_wm; #define DESK_MIN 0 #define DESK_CURRENT 0 #define DESK_ALL 1 diff --git a/src/ewmh.c b/src/ewmh.c index ef65aff..3c1bd48 100644 --- a/src/ewmh.c +++ b/src/ewmh.c @@ -367,6 +367,12 @@ static bool ewmhSkipFocusChangeEvent(void) return true; } +static long ewmhEventMask(Window w) +{ + msg(0, "using direct focus tracking for 0x%lx\n", w); + return FocusChangeMask; +} + // PUBLIC // @@ -399,4 +405,5 @@ struct WmOps WmEwmhOps = { .getActiveWindow = ewmh_getActiveWindow, .skipWindowInTaskbar = ewmh_skipWindowInTaskbar, .skipFocusChangeEvent = ewmhSkipFocusChangeEvent, + .eventMask = ewmhEventMask, }; diff --git a/src/win.c b/src/win.c index c6a36b8..7356db9 100644 --- a/src/win.c +++ b/src/win.c @@ -94,6 +94,13 @@ static inline bool wmSkipFocusChangeEvent(void) return WmOps->skipFocusChangeEvent(); } +static inline long wmEventMask(Window w) +{ + if (WmOps->eventMask == NULL) + return 0; + return WmOps->eventMask(w); +} + // // helper for windows' qsort // CAUSES O(log(N)) OR EVEN WORSE! reintroduce winlist[]->order instead? @@ -169,10 +176,7 @@ static void winSetCommonPropertiesForAnyWindow(Window win) // for delete notification evmask |= StructureNotifyMask; // for focusIn notification - if (g.option_wm != WM_EWMH) { - msg(0, "using direct focus tracking for 0x%lx\n", win); - evmask |= FocusChangeMask; - } + evmask |= wmEventMask(win); // warning: this overwrites previous value if (evmask != 0) XSelectInput(dpy, win, evmask); @@ -219,32 +223,33 @@ void addToSortlist(Window w, bool to_head, bool move) } // -// initializes g.option_wm // wmindex is the already validated option void initWin(int wmindex) { + int wm; + if (wmindex != WM_GUESS) { - g.option_wm = wmindex; + wm = wmindex; goto out; } if (wmProbe(WM_EWMH)) { msg(0, "EWMH-compatible WM detected: %s\n", g.ewmh.wmname); - g.option_wm = WM_EWMH; + wm = WM_EWMH; goto out; } if (wmProbe(WM_RATPOISON)) { - g.option_wm = WM_RATPOISON; + wm = WM_RATPOISON; goto out; } msg(0, "unknown WM, using WM_TWM\n"); - g.option_wm = WM_TWM; + wm = WM_TWM; out: - msg(0, "WM: %d\n", g.option_wm); - WmOps = WmOpsVariants[g.option_wm]; + msg(0, "WM: %d\n", wm); + WmOps = WmOpsVariants[wm]; } // diff --git a/src/x.c b/src/x.c index 384146d..28a9ed9 100644 --- a/src/x.c +++ b/src/x.c @@ -38,6 +38,7 @@ extern Window root; // in raw X this returns too much windows, "1" is probably sufficient // no need for an option static int max_reclevel; +static int wm; // // get window group leader @@ -92,18 +93,18 @@ static int x_initWindowsInfoRecursive(Window win, int reclevel) // caveat: in rp, skips anything except of visible window // probably add an option for this in WMs too? wa.map_state = 0; - if (g.option_wm != WM_TWM) + if (wm != WM_TWM) XGetWindowAttributes(dpy, win, &wa); // in twm-like, add only windows with a name winname = NULL; - if (g.option_wm == WM_TWM) { + if (wm == WM_TWM) { winname = get_x_property(win, XA_STRING, "WM_NAME", NULL); } // insert detailed window data in window list - if ((g.option_wm == WM_TWM || wa.map_state == IsViewable) - && reclevel != 0 && (g.option_wm != WM_TWM || winname != NULL) -// && (g.option_wm != WM_TWM || leader == win) + if ((wm == WM_TWM || wa.map_state == IsViewable) + && reclevel != 0 && (wm != WM_TWM || winname != NULL) +// && (wm != WM_TWM || leader == win) && !common_skipWindow(win, DESKTOP_UNKNOWN, DESKTOP_UNKNOWN) ) { addWindowInfo(win, reclevel, 0, DESKTOP_UNKNOWN, winname); @@ -167,12 +168,14 @@ static int xSetFocus(int idx) static int xStartup(void) { + wm = WM_NO; max_reclevel = 1; return 1; } static int twmStartup(void) { + wm = WM_TWM; max_reclevel = -1; return 1; } From dcdd7bab36b1e2707dd76a22e16ea6367b8d19b2 Mon Sep 17 00:00:00 2001 From: Yauheni Kaliuta Date: Mon, 26 Nov 2018 06:32:06 +0200 Subject: [PATCH 7/7] WMs: change .winlist() signature to (void) Actually, the parameters were needed for the first recursion call, so make it other way round -- wrap the first call of x/twm method with the initial arguments, but main call without any. Signed-off-by: Yauheni Kaliuta --- src/alttab.h | 2 +- src/ewmh.c | 7 +------ src/rp.c | 2 +- src/win.c | 6 +++--- src/x.c | 9 +++++++-- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/alttab.h b/src/alttab.h index aa3b5d1..06139a4 100644 --- a/src/alttab.h +++ b/src/alttab.h @@ -118,7 +118,7 @@ typedef struct PermanentWindowInfo { struct WmOps { bool (*probe)(void); int (*startup)(void); - int (*winlist)(Window win, int reclevel); + int (*winlist)(void); int (*setFocus)(int idx); Window (*getActiveWindow)(void); bool (*skipWindowInTaskbar)(Window w); diff --git a/src/ewmh.c b/src/ewmh.c index 3c1bd48..4c036eb 100644 --- a/src/ewmh.c +++ b/src/ewmh.c @@ -339,11 +339,6 @@ static int ewmhStartup(void) return 1; } -static int ewmhWinlist(Window win, int rec) -{ - return ewmh_initWinlist(); -} - static int ewmhSetFocus(int winNdx) { int r; @@ -400,7 +395,7 @@ int ewmh_setFocus(int winNdx, Window fwin) struct WmOps WmEwmhOps = { .probe = ewmhProbe, .startup = ewmhStartup, - .winlist = ewmhWinlist, + .winlist = ewmh_initWinlist, .setFocus = ewmhSetFocus, .getActiveWindow = ewmh_getActiveWindow, .skipWindowInTaskbar = ewmh_skipWindowInTaskbar, diff --git a/src/rp.c b/src/rp.c index b193ded..f7ebc52 100644 --- a/src/rp.c +++ b/src/rp.c @@ -264,7 +264,7 @@ static int ratpoisonStartup(void) return rp_startupWintasks(); } -static int ratpoisonWinlist(Window win, int rec) +static int ratpoisonWinlist(void) { return rp_initWinlist(); } diff --git a/src/win.c b/src/win.c index 7356db9..4afb92c 100644 --- a/src/win.c +++ b/src/win.c @@ -59,11 +59,11 @@ static inline int wmStartup(void) return WmOps->startup(); } -static inline int wmInitWinlist(Window win, int reclevel) +static inline int wmInitWinlist(void) { if (WmOps->winlist == NULL) return 0; - return WmOps->winlist(win, reclevel); + return WmOps->winlist(); } static inline int wmSetFocus(int idx) @@ -598,7 +598,7 @@ int initWinlist(void) print_sortlist(); } - r = wmInitWinlist(root, 0); + r = wmInitWinlist(); if (!r) __initWinlist(); diff --git a/src/x.c b/src/x.c index 28a9ed9..6e0fe66 100644 --- a/src/x.c +++ b/src/x.c @@ -180,16 +180,21 @@ static int twmStartup(void) return 1; } +static int xWinlist(void) +{ + return x_initWindowsInfoRecursive(root, 0); +} + // PUBLIC struct WmOps WmNoOps = { .startup = xStartup, - .winlist = x_initWindowsInfoRecursive, + .winlist = xWinlist, .setFocus = xSetFocus, }; struct WmOps WmTwmOps = { .startup = twmStartup, - .winlist = x_initWindowsInfoRecursive, + .winlist = xWinlist, .setFocus = xSetFocus, };