wayland: Phase 46 — client_close vtable; title updates; protocol guards
Add client_close vtable slot to WMBackend: - x11: XKillClient + XFlush - wl: wlr_xdg_toplevel_send_close (politely asks client to close) - wClientKill: use wm_backend->client_close() for Wayland path Guard wClientSendProtocol with if (!dpy) return so it is safe to call on Wayland windows (protocols.DELETE_WINDOW is always 0 for Wayland). Wire XDG toplevel set_title signal: add title_listener to wl_toplevel_view; handle_toplevel_title updates wwin->wm_class / wm_instance when the client changes its title, and calls wWindowUpdateName (which no-ops on frameless windows but posts WMNChangedName when a frame is present). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
69cee6fb15
commit
fc0e233865
|
|
@ -811,6 +811,12 @@ typedef struct WMBackend {
|
|||
*/
|
||||
void (*client_remove_from_save_set)(WNativeWindow win);
|
||||
|
||||
/*
|
||||
* Request a client window to close itself.
|
||||
* On X11: XKillClient (forceful); on Wayland: xdg_toplevel_send_close.
|
||||
*/
|
||||
void (*client_close)(WNativeWindow win);
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* Fake group-leader window (Phase 13) */
|
||||
/* ------------------------------------------------------------------ */
|
||||
|
|
|
|||
|
|
@ -27,6 +27,9 @@
|
|||
* so clients receive size changes from the WM.
|
||||
* Phase 45: pointer_query returns 1 (always on-screen); wWindowFocus/Unfocus
|
||||
* guarded on frame!=NULL so wSetFocusTo works without a frame window.
|
||||
* Phase 46: client_close vtable slot (wlr_xdg_toplevel_send_close);
|
||||
* title_listener updates wwin->wm_instance/wm_class on set_title signal;
|
||||
* wClientSendProtocol/wClientKill guarded for Wayland.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
|
|
@ -75,6 +78,7 @@ struct wl_toplevel_view {
|
|||
struct wl_listener map_listener;
|
||||
struct wl_listener unmap_listener;
|
||||
struct wl_listener destroy_listener;
|
||||
struct wl_listener title_listener; /* Phase 46: xdg_toplevel set_title */
|
||||
WNativeWindow id; /* synthetic handle, >= 0x10000000 */
|
||||
struct wl_list link; /* wl_state.toplevel_list */
|
||||
};
|
||||
|
|
@ -159,6 +163,34 @@ wl_find_view_by_id(WNativeWindow id)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Phase 46: XDG toplevel title changed — update wwin->wm_instance.
|
||||
*/
|
||||
static void
|
||||
handle_toplevel_title(struct wl_listener *listener, void *data)
|
||||
{
|
||||
struct wl_toplevel_view *view = VIEW_FROM_LISTENER(listener, title_listener);
|
||||
(void)data;
|
||||
const char *new_title = view->xdg_surface->toplevel->title;
|
||||
|
||||
WObjDescriptor *desc = wm_backend->context_find(view->id, WM_CTX_CLIENT_WIN);
|
||||
if (!desc || desc->parent_type != WCLASS_WINDOW)
|
||||
return;
|
||||
WWindow *wwin = (WWindow *)desc->parent;
|
||||
|
||||
if (wwin->wm_instance)
|
||||
wfree(wwin->wm_instance);
|
||||
wwin->wm_instance = new_title ? strdup(new_title) : NULL;
|
||||
|
||||
if (wwin->wm_class)
|
||||
wfree(wwin->wm_class);
|
||||
const char *app_id = (view->xdg_surface->toplevel->app_id)
|
||||
? view->xdg_surface->toplevel->app_id : "wayland-client";
|
||||
wwin->wm_class = strdup(app_id);
|
||||
|
||||
wWindowUpdateName(wwin, new_title);
|
||||
}
|
||||
|
||||
/*
|
||||
* Phase 38: toplevel map — surface is ready to be managed.
|
||||
*/
|
||||
|
|
@ -226,6 +258,7 @@ handle_toplevel_destroy(struct wl_listener *listener, void *data)
|
|||
wl_list_remove(&view->map_listener.link);
|
||||
wl_list_remove(&view->unmap_listener.link);
|
||||
wl_list_remove(&view->destroy_listener.link);
|
||||
wl_list_remove(&view->title_listener.link);
|
||||
wl_list_remove(&view->link);
|
||||
wl_state.managed_surface_count--;
|
||||
wfree(view);
|
||||
|
|
@ -254,10 +287,12 @@ handle_xdg_new_surface(struct wl_listener *listener, void *data)
|
|||
view->map_listener.notify = handle_toplevel_map;
|
||||
view->unmap_listener.notify = handle_toplevel_unmap;
|
||||
view->destroy_listener.notify = handle_toplevel_destroy;
|
||||
view->title_listener.notify = handle_toplevel_title;
|
||||
|
||||
wl_signal_add(&xdg_surface->events.map, &view->map_listener);
|
||||
wl_signal_add(&xdg_surface->events.unmap, &view->unmap_listener);
|
||||
wl_signal_add(&xdg_surface->events.destroy, &view->destroy_listener);
|
||||
wl_signal_add(&xdg_surface->events.map, &view->map_listener);
|
||||
wl_signal_add(&xdg_surface->events.unmap, &view->unmap_listener);
|
||||
wl_signal_add(&xdg_surface->events.destroy, &view->destroy_listener);
|
||||
wl_signal_add(&xdg_surface->toplevel->events.set_title, &view->title_listener);
|
||||
|
||||
wl_list_insert(&wl_state.toplevel_list, &view->link);
|
||||
wl_state.managed_surface_count++;
|
||||
|
|
@ -864,6 +899,15 @@ wl_client_synth_configure(WNativeWindow client_win,
|
|||
static void wl_client_add_to_save_set(WNativeWindow win) { (void)win; }
|
||||
static void wl_client_remove_from_save_set(WNativeWindow win) { (void)win; }
|
||||
|
||||
static void
|
||||
wl_client_close(WNativeWindow win)
|
||||
{
|
||||
/* Phase 46: ask the XDG toplevel to close itself. */
|
||||
struct wl_toplevel_view *v = wl_find_view_by_id(win);
|
||||
if (v && v->xdg_surface)
|
||||
wlr_xdg_toplevel_send_close(v->xdg_surface);
|
||||
}
|
||||
|
||||
static void wl_client_query_shaped(WNativeWindow win, int *shaped_out)
|
||||
{ (void)win; *shaped_out = 0; }
|
||||
|
||||
|
|
@ -1101,6 +1145,7 @@ const WMBackend wl_backend = {
|
|||
.client_synth_configure = wl_client_synth_configure,
|
||||
.client_add_to_save_set = wl_client_add_to_save_set,
|
||||
.client_remove_from_save_set = wl_client_remove_from_save_set,
|
||||
.client_close = wl_client_close,
|
||||
.client_query_shaped = wl_client_query_shaped,
|
||||
.client_get_gnustep_attrs = wl_client_get_gnustep_attrs,
|
||||
.client_get_pid = wl_client_get_pid,
|
||||
|
|
|
|||
|
|
@ -963,6 +963,13 @@ x11_client_remove_from_save_set(WNativeWindow win)
|
|||
XRemoveFromSaveSet(dpy, WNW_TO_XID(win));
|
||||
}
|
||||
|
||||
static void
|
||||
x11_client_close(WNativeWindow win)
|
||||
{
|
||||
XKillClient(dpy, WNW_TO_XID(win));
|
||||
XFlush(dpy);
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/* Fake group-leader window (Phase 13) */
|
||||
/* -------------------------------------------------------------------- */
|
||||
|
|
@ -2458,6 +2465,7 @@ const WMBackend x11_backend = {
|
|||
.client_reparent = x11_client_reparent,
|
||||
.client_add_to_save_set = x11_client_add_to_save_set,
|
||||
.client_remove_from_save_set = x11_client_remove_from_save_set,
|
||||
.client_close = x11_client_close,
|
||||
.fake_leader_create = x11_fake_leader_create,
|
||||
.fake_leader_destroy = x11_fake_leader_destroy,
|
||||
.icccm_set_class_hint = x11_icccm_set_class_hint,
|
||||
|
|
|
|||
|
|
@ -250,6 +250,9 @@ void wClientSendProtocol(WWindow * wwin, Atom protocol, Time time)
|
|||
{
|
||||
XEvent event;
|
||||
|
||||
if (!dpy)
|
||||
return; /* Wayland: no X11 ClientMessage */
|
||||
|
||||
event.xclient.type = ClientMessage;
|
||||
event.xclient.message_type = w_global.atom.wm.protocols;
|
||||
event.xclient.format = 32;
|
||||
|
|
@ -265,6 +268,11 @@ void wClientSendProtocol(WWindow * wwin, Atom protocol, Time time)
|
|||
|
||||
void wClientKill(WWindow * wwin)
|
||||
{
|
||||
if (!dpy) {
|
||||
/* Wayland: request close via vtable */
|
||||
wm_backend->client_close(wwin->client_win);
|
||||
return;
|
||||
}
|
||||
XKillClient(dpy, wwin->client_win);
|
||||
|
||||
XFlush(dpy);
|
||||
|
|
|
|||
Loading…
Reference in New Issue