From d902477efdfb15466da6644fb1a944c20a935c6c Mon Sep 17 00:00:00 2001 From: David Maciejak Date: Mon, 20 Feb 2023 20:02:28 +0800 Subject: [PATCH] Set missing WM_COMMAND using pid given from libXRes For apps which are not setting the window WM_COMMAND property like those old apps using Motif toolkit (I am thinking of NEdit for example) it's bringing some issues in windowmaker which is relying on it for a few interactions. Especially, *an app without WM_COMMAND will not be saved during the workspace state (so session restore is not working for them) *when added to the dock, the settings parameters are empty and need to be filled *cannot autostart from the dock (even if the settings are manually filled and saved) *right click on the app titlebar, and choosing Launch has no effect Most of the time, those apps are also not setting the X11_NET_WM_PID property. With the pid we could have a chance to find the running program. To link a window to a pid, there is the X11 Resource extension library (libXRes). After checking, gnome and xfce are also using the same method to handle such issues. The patch is checking if the libXRes is present on the system (but it's not mandatory to compile). Then, it adds a layer on top of wNETWMGetPidForWindow to not only check the window property but if necessary to get the underlying pid from libXRes if available. That's solving the points mentioned above. --- configure.ac | 12 ++++++++++++ m4/wm_xext_check.m4 | 27 +++++++++++++++++++++++++++ src/main.c | 20 +++++++++++++++----- src/window.c | 40 +++++++++++++++++++++++++++++++++++++++- 4 files changed, 93 insertions(+), 6 deletions(-) diff --git a/configure.ac b/configure.ac index 211689d2..25a83628 100644 --- a/configure.ac +++ b/configure.ac @@ -580,6 +580,18 @@ AS_IF([test "x$enable_wmreplace" = "xyes"], [define to support ICCCM protocol for window manager replacement]) supported_xext="$supported_xext WMReplace"]) +dnl XRes support +dnl ============== +m4_divert_push([INIT_PREPARE])dnl +AC_ARG_ENABLE([res], + [AS_HELP_STRING([--disable-res], [disable resource window extension support])], + [AS_CASE(["$enableval"], + [yes|no], [], + [AC_MSG_ERROR([bad value $enableval for --enable-res]) ]) ], + [enable_res=auto]) +m4_divert_pop([INIT_PREPARE])dnl + +WM_XEXT_CHECK_XRES dnl XShape support dnl ============== diff --git a/m4/wm_xext_check.m4 b/m4/wm_xext_check.m4 index 9503af2c..751d8314 100644 --- a/m4/wm_xext_check.m4 +++ b/m4/wm_xext_check.m4 @@ -16,6 +16,33 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +# WM_XEXT_CHECK_XRES +# -------------------- +# +# Check for the X Resource Window extension +# The check depends on variable 'enable_xshape' being either: +# yes - detect, fail if not found +# no - do not detect, disable support +# auto - detect, disable if not found +# +# When found, append appropriate stuff in XLIBS, and append info to +# the variable 'supported_xext' +# When not found, append info to variable 'unsupported' +AC_DEFUN_ONCE([WM_XEXT_CHECK_XRES], +[WM_LIB_CHECK([XRes], [-lXRes], [XResQueryClientIds], [$XLIBS], + [wm_save_CFLAGS="$CFLAGS" + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([dnl +@%:@include +], [dnl + + XResQueryClientIds(NULL, 0, NULL, NULL, NULL);])] + [], + [AC_MSG_ERROR([found $CACHEVAR but cannot compile using XRes header])]) + CFLAGS="$wm_save_CFLAGS"], + [supported_xext], [XLIBS], [enable_res], [-])dnl +]) dnl AC_DEFUN + + # WM_XEXT_CHECK_XSHAPE # -------------------- # diff --git a/src/main.c b/src/main.c index bf5b1a0a..998bbcc5 100644 --- a/src/main.c +++ b/src/main.c @@ -56,6 +56,7 @@ #include "dialog.h" #include "main.h" #include "monitor.h" +#include "misc.h" #include @@ -354,11 +355,19 @@ Bool RelaunchWindow(WWindow *wwin) char **argv; int argc; + char *command = NULL; - if (! XGetCommand(dpy, wwin->client_win, &argv, &argc) || argc == 0 || argv == NULL) { + command = GetCommandForWindow(wwin->client_win); + if (!command) { +#ifdef USE_XRES + werror("cannot relaunch the application because no associated process found"); +#else werror("cannot relaunch the application because no WM_COMMAND property is set"); +#endif return False; - } + } else + wtokensplit(command, &argv, &argc); + pid_t pid = fork(); @@ -384,18 +393,19 @@ Bool RelaunchWindow(WWindow *wwin) } else if (pid < 0) { werror("cannot fork a new process"); - XFreeStringList(argv); + wfree(argv); + wfree(command); return False; } else { _tuple *data = wmalloc(sizeof(_tuple)); data->scr = wwin->screen_ptr; - data->command = wtokenjoin(argv, argc); + data->command = command; /* not actually a shell command */ wAddDeathHandler(pid, shellCommandHandler, data); - XFreeStringList(argv); + wfree(argv); } return True; diff --git a/src/window.c b/src/window.c index 59721b39..ef85f3d4 100644 --- a/src/window.c +++ b/src/window.c @@ -29,6 +29,9 @@ #ifdef KEEP_XKB_LOCK_STATUS #include #endif /* KEEP_XKB_LOCK_STATUS */ +#ifdef USE_XRES +#include +#endif #include #include #include @@ -475,6 +478,41 @@ Bool wWindowObscuresWindow(WWindow *wwin, WWindow *obscured) return True; } +/* Get the corresponding Process Identification Number of the active window */ +static pid_t getWindowPid(Window win) +{ + pid_t pid = -1; + + pid = wNETWMGetPidForWindow(win); +#ifdef USE_XRES + if (pid > 0) + return pid; + else { + XResClientIdSpec spec; + int status; + long i, num_ids = 0; + XResClientIdValue *client_ids = NULL; + + spec.client = win; + spec.mask = XRES_CLIENT_ID_PID_MASK; + + status = XResQueryClientIds(dpy, 1, &spec, &num_ids, &client_ids); + + if (status != Success) + return -1; + + for (i = 0; i < num_ids; i++) { + if (client_ids[i].spec.mask == XRES_CLIENT_ID_PID_MASK) { + pid = XResGetClientPid(&client_ids[i]); + break; + } + } + XResClientIdsDestroy(num_ids, client_ids); + } +#endif + return pid; +} + static void fixLeaderProperties(WWindow *wwin) { XClassHint *classHint; @@ -487,7 +525,7 @@ static void fixLeaderProperties(WWindow *wwin) classHint = XAllocClassHint(); clientHints = XGetWMHints(dpy, wwin->client_win); - pid = wNETWMGetPidForWindow(wwin->client_win); + pid = getWindowPid(wwin->client_win); if (pid > 0) haveCommand = GetCommandForPid(pid, &argv, &argc); else