wayland: Phase 57 — icccm_set/get/free_command store WM_COMMAND in view

Add wm_command/wm_command_n fields to wl_toplevel_view.
wl_icccm_set_command stores a deep copy of argv in the view struct;
wl_icccm_get_command returns a NULL-terminated heap copy (caller frees
each string plus the array via wl_icccm_free_command).
handle_toplevel_destroy frees the stored command on cleanup.

Update getCommandForWindow in misc.c: when dpy is NULL (Wayland path),
call wm_backend->icccm_get_command instead of returning NULL immediately.
This allows fixLeaderProperties and session.c to retrieve the command
string for Wayland-managed windows.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Levi Neely 2026-03-28 07:34:12 +01:00
parent e727bdbad0
commit 2c6e24dfb0
2 changed files with 70 additions and 4 deletions

View File

@ -39,6 +39,8 @@
* window_move records WM-assigned x,y in the view struct. * window_move records WM-assigned x,y in the view struct.
* Phase 56: prop_set/get_wm_state persist WM state (workspace, miniaturized, * Phase 56: prop_set/get_wm_state persist WM state (workspace, miniaturized,
* shaded, etc.) in the view struct so wWindowGetSavedState works correctly. * shaded, etc.) in the view struct so wWindowGetSavedState works correctly.
* Phase 57: icccm_set/get/free_command store WM_COMMAND in the view struct;
* misc.c getCommandForWindow uses the vtable on the Wayland path.
*/ */
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
@ -92,6 +94,8 @@ struct wl_toplevel_view {
int x, y; /* Phase 55: WM-assigned position */ int x, y; /* Phase 55: WM-assigned position */
long *wm_state; /* Phase 56: saved WM state or NULL */ long *wm_state; /* Phase 56: saved WM state or NULL */
int wm_state_n; /* Phase 56: element count */ int wm_state_n; /* Phase 56: element count */
char **wm_command; /* Phase 57: WM_COMMAND argv or NULL */
int wm_command_n; /* Phase 57: argument count */
struct wl_list link; /* wl_state.toplevel_list */ struct wl_list link; /* wl_state.toplevel_list */
}; };
@ -281,6 +285,12 @@ handle_toplevel_destroy(struct wl_listener *listener, void *data)
wl_list_remove(&view->link); wl_list_remove(&view->link);
wl_state.managed_surface_count--; wl_state.managed_surface_count--;
if (view->wm_state) wfree(view->wm_state); /* Phase 56 */ if (view->wm_state) wfree(view->wm_state); /* Phase 56 */
if (view->wm_command) { /* Phase 57 */
int ci;
for (ci = 0; ci < view->wm_command_n; ci++)
wfree(view->wm_command[ci]);
wfree(view->wm_command);
}
wfree(view); wfree(view);
} }
@ -1064,9 +1074,50 @@ wl_coords_translate(WNativeWindow src, WNativeWindow dst,
static void wl_icccm_set_wm_hints(WNativeWindow win, const WWMHints *hints) { (void)win; (void)hints; } static void wl_icccm_set_wm_hints(WNativeWindow win, const WWMHints *hints) { (void)win; (void)hints; }
static void wl_icccm_set_class_hint(WNativeWindow win, const char *wm_class, const char *wm_instance) { (void)win; (void)wm_class; (void)wm_instance; } static void wl_icccm_set_class_hint(WNativeWindow win, const char *wm_class, const char *wm_instance) { (void)win; (void)wm_class; (void)wm_instance; }
static void wl_icccm_set_class_hint_if_absent(WNativeWindow win, const char *wm_class, const char *wm_instance) { (void)win; (void)wm_class; (void)wm_instance; } static void wl_icccm_set_class_hint_if_absent(WNativeWindow win, const char *wm_class, const char *wm_instance) { (void)win; (void)wm_class; (void)wm_instance; }
static void wl_icccm_set_command(WNativeWindow win, char **argv, int argc) { (void)win; (void)argv; (void)argc; } static void
static int wl_icccm_get_command(WNativeWindow win, char ***argv_out, int *argc_out) { (void)win; *argv_out = NULL; *argc_out = 0; return 0; } wl_icccm_set_command(WNativeWindow win, char **argv, int argc)
static void wl_icccm_free_command(char **argv) { (void)argv; } {
/* Phase 57: store WM_COMMAND in the view struct. */
struct wl_toplevel_view *v = wl_find_view_by_id(win);
int i;
if (!v || !argv || argc <= 0) return;
/* free previous command if any */
if (v->wm_command) {
for (i = 0; i < v->wm_command_n; i++) wfree(v->wm_command[i]);
wfree(v->wm_command);
}
v->wm_command = wmalloc((size_t)argc * sizeof(char *));
for (i = 0; i < argc; i++)
v->wm_command[i] = wstrdup(argv[i] ? argv[i] : "");
v->wm_command_n = argc;
}
static int
wl_icccm_get_command(WNativeWindow win, char ***argv_out, int *argc_out)
{
/* Phase 57: return a NULL-terminated copy of the stored WM_COMMAND.
* Caller frees with wl_icccm_free_command. */
struct wl_toplevel_view *v = wl_find_view_by_id(win);
int i;
if (!v || !v->wm_command) { *argv_out = NULL; *argc_out = 0; return 0; }
/* Allocate n+1 entries (NULL-terminated for free_command iteration). */
*argv_out = wmalloc((size_t)(v->wm_command_n + 1) * sizeof(char *));
for (i = 0; i < v->wm_command_n; i++)
(*argv_out)[i] = wstrdup(v->wm_command[i]);
(*argv_out)[v->wm_command_n] = NULL;
*argc_out = v->wm_command_n;
return 1;
}
static void
wl_icccm_free_command(char **argv)
{
/* Phase 57: free NULL-terminated argv returned by wl_icccm_get_command. */
int i;
if (!argv) return;
for (i = 0; argv[i] != NULL; i++) wfree(argv[i]);
wfree(argv);
}
/* ------------------------------------------------------------------ */ /* ------------------------------------------------------------------ */
/* Fake leader */ /* Fake leader */

View File

@ -1072,7 +1072,22 @@ static char *getCommandForWindow(Window win, int elements)
char **argv, *command = NULL; char **argv, *command = NULL;
int argc; int argc;
if (!dpy) return NULL; /* Wayland: no X11 WM_COMMAND property */ if (!dpy) {
/* Wayland: read WM_COMMAND from the vtable-backed store. */
if (!wm_backend->icccm_get_command((WNativeWindow)win, &argv, &argc))
return NULL;
if (argc > 0 && argv != NULL) {
if (elements == 0)
elements = argc;
command = wtokenjoin(argv, WMIN(argc, elements));
if (command[0] == 0) {
wfree(command);
command = NULL;
}
}
wm_backend->icccm_free_command(argv);
return command;
}
if (XGetCommand(dpy, win, &argv, &argc)) { if (XGetCommand(dpy, win, &argv, &argc)) {
if (argc > 0 && argv != NULL) { if (argc > 0 && argv != NULL) {