diff --git a/src/LunarWM.c b/src/LunarWM.c index 914e3cb..8e2f551 100644 --- a/src/LunarWM.c +++ b/src/LunarWM.c @@ -63,6 +63,7 @@ void toplevel_commit_notify(struct wl_listener *l, void *) LunarWM_Toplevel_update(tl); } +static void focus_fallback(LunarWM *wm); void toplevel_destroy_notify(struct wl_listener *l, void *) { auto *tl = wl_container_of(l, (LunarWM_Toplevel *)(NULL), destroy); @@ -73,10 +74,66 @@ void toplevel_destroy_notify(struct wl_listener *l, void *) break; } } + + focus_fallback(tl->server); + LunarWM_Toplevel_destroy(tl); free(tl); } +static void toplevel_map_notify(struct wl_listener *l, void *data) +{ + (void)data; + LunarWM_Toplevel *tl = wl_container_of(l, (LunarWM_Toplevel *)0, map); + if (!tl || !tl->surface) + return; + + if (tl->is_xwayland && tl->u.xwl) { + if (tl->u.xwl->override_redirect + && !wlr_xwayland_surface_override_redirect_wants_focus(tl->u.xwl)) { + return; + } + } + + LunarWM_Toplevel_focus(tl); + + LunarWM *wm = tl->server; + for (size_t i = 0; i < vector_size(wm->wayland.v_toplevels); ++i) { + if (wm->wayland.v_toplevels[i] == tl) { + wm->wayland.current_focus = (int)i; + break; + } + } +} + +static void toplevel_unmap_notify(struct wl_listener *l, void *data) +{ + (void)data; + LunarWM_Toplevel *tl = wl_container_of(l, tl, unmap); + if (!tl || !tl->surface) + return; + + // detach per-surface listeners/resources + if (tl->map.link.prev || tl->map.link.next) + wl_list_remove(&tl->map.link); + if (tl->unmap.link.prev || tl->unmap.link.next) + wl_list_remove(&tl->unmap.link); + if (tl->commit.link.prev || tl->commit.link.next) + wl_list_remove(&tl->commit.link); + + if (tl->locked_buffer) { + wlr_buffer_unlock(tl->locked_buffer); + tl->locked_buffer = NULL; + } + tl->texture = NULL; + tl->gles_texture = NULL; + tl->rl_texture = (Texture) { 0 }; + tl->surface = NULL; + + // If the thing that unmapped was focused, pick a fallback + focus_fallback(tl->server); +} + bool LunarWM_Toplevel_init_xdg( LunarWM_Toplevel *tl, LunarWM *wm, struct wlr_xdg_toplevel *xdg) { @@ -94,6 +151,12 @@ bool LunarWM_Toplevel_init_xdg( tl->destroy.notify = toplevel_destroy_notify; wl_signal_add(&tl->surface->events.destroy, &tl->destroy); + tl->map.notify = toplevel_map_notify; + wl_signal_add(&tl->u.xdg->base->surface->events.map, &tl->map); + + tl->unmap.notify = toplevel_unmap_notify; + wl_signal_add(&tl->u.xdg->base->surface->events.unmap, &tl->unmap); + return true; } @@ -105,7 +168,6 @@ bool LunarWM_Toplevel_init_xwayland( tl->is_xwayland = true; tl->u.xwl = xwl; tl->surface = xwl->surface; - assert(tl->surface); tl->commit.notify = toplevel_commit_notify; @@ -114,16 +176,29 @@ bool LunarWM_Toplevel_init_xwayland( tl->destroy.notify = toplevel_destroy_notify; wl_signal_add(&tl->surface->events.destroy, &tl->destroy); + tl->map.notify = toplevel_map_notify; + wl_signal_add(&xwl->surface->events.map, &tl->map); + + tl->unmap.notify = toplevel_unmap_notify; + wl_signal_add(&xwl->surface->events.unmap, &tl->unmap); + return true; } bool LunarWM_Toplevel_destroy(LunarWM_Toplevel *this) { - wl_list_remove(&this->commit.link); - wl_list_remove(&this->destroy.link); - if (this->locked_buffer != nullptr) { + if (!this) + return false; + if (this->map.link.prev || this->map.link.next) + wl_list_remove(&this->map.link); + if (this->unmap.link.prev || this->unmap.link.next) + wl_list_remove(&this->unmap.link); + if (this->commit.link.prev || this->commit.link.next) + wl_list_remove(&this->commit.link); + if (this->destroy.link.prev || this->destroy.link.next) + wl_list_remove(&this->destroy.link); + if (this->locked_buffer) wlr_buffer_unlock(this->locked_buffer); - } return true; } @@ -210,6 +285,7 @@ struct XwlHooks { struct wl_listener req_configure; struct wl_listener req_maximize; struct wl_listener req_fullscreen; + struct wl_listener req_activate; struct wl_listener set_geometry; }; @@ -290,6 +366,10 @@ static void xwl_unmap_toplevel(LunarWM_Toplevel *tl) if (!tl) return; + if (tl->map.link.prev || tl->map.link.next) + wl_list_remove(&tl->map.link); + if (tl->unmap.link.prev || tl->unmap.link.next) + wl_list_remove(&tl->unmap.link); if (tl->commit.link.prev || tl->commit.link.next) wl_list_remove(&tl->commit.link); if (tl->destroy.link.prev || tl->destroy.link.next) @@ -319,6 +399,8 @@ static void xwl_on_dissociate(struct wl_listener *ll, void *data) break; } } + + focus_fallback(wm); } static void xwl_on_request_configure(struct wl_listener *ll, void *data) @@ -357,6 +439,18 @@ static void xwl_on_request_fullscreen(struct wl_listener *ll, void *data) wlr_xwayland_surface_set_fullscreen(xh->xwl, true); } +static void xwl_on_request_activate(struct wl_listener *ll, void *data) +{ + struct XwlHooks *h = wl_container_of(ll, h, req_activate); + for (size_t i = 0; i < vector_size(h->wm->wayland.v_toplevels); ++i) { + LunarWM_Toplevel *tl = h->wm->wayland.v_toplevels[i]; + if (tl->is_xwayland && tl->u.xwl == h->xwl) { + LunarWM_Toplevel_focus(tl); + break; + } + } +} + static void xwl_on_set_geometry(struct wl_listener *ll, void *data) { (void)ll; @@ -375,6 +469,8 @@ static void xwl_on_destroy(struct wl_listener *ll, void *data) wl_list_remove(&xh->req_maximize.link); if (xh->req_fullscreen.link.prev) wl_list_remove(&xh->req_fullscreen.link); + if (xh->req_activate.link.prev) + wl_list_remove(&xh->req_activate.link); if (xh->set_geometry.link.prev) wl_list_remove(&xh->set_geometry.link); if (xh->associate.link.prev) @@ -393,9 +489,60 @@ static void xwl_on_destroy(struct wl_listener *ll, void *data) } } + focus_fallback(wm); + free(xh); } +static bool xwl_wants_focus(struct wlr_xwayland_surface *x) +{ + if (!x) + return false; + if (!x->surface) + return false; + if (x->override_redirect + && !wlr_xwayland_surface_override_redirect_wants_focus(x)) { + return false; + } + if (x->withdrawn || x->minimized) + return false; + return true; +} + +static void focus_fallback(LunarWM *wm) +{ + if (!wm || !wm->wayland.seat) + return; + + for (ssize_t i = (ssize_t)vector_size(wm->wayland.v_toplevels) - 1; i >= 0; + --i) { + LunarWM_Toplevel *cand = wm->wayland.v_toplevels[i]; + if (!cand || !cand->surface) + continue; + + if (cand->is_xwayland) { + if (!xwl_wants_focus(cand->u.xwl)) + continue; + } + LunarWM_Toplevel_focus(cand); + wm->wayland.current_focus = (int)i; + return; + } + + struct wlr_seat *seat = wm->wayland.seat; + struct wlr_surface *prev = seat->keyboard_state.focused_surface; + if (prev) { + struct wlr_xdg_toplevel *pt; + struct wlr_xwayland_surface *px; + if ((pt = wlr_xdg_toplevel_try_from_wlr_surface(prev))) + wlr_xdg_toplevel_set_activated(pt, false); + if ((px = wlr_xwayland_surface_try_from_wlr_surface(prev))) + wlr_xwayland_surface_activate(px, false); + } + wlr_seat_keyboard_clear_focus(seat); + wm->wayland.current_focus = -1; +} + static void xwayland_new_surface_notify(struct wl_listener *l, void *data) { LunarWM *wm = wl_container_of(l, wm, wayland.xwayland_new_surface); @@ -430,6 +577,9 @@ static void xwayland_new_surface_notify(struct wl_listener *l, void *data) h->req_fullscreen.notify = xwl_on_request_fullscreen; wl_signal_add(&xwl->events.request_fullscreen, &h->req_fullscreen); + h->req_activate.notify = xwl_on_request_activate; + wl_signal_add(&xwl->events.request_activate, &h->req_activate); + h->set_geometry.notify = xwl_on_set_geometry; wl_signal_add(&xwl->events.set_geometry, &h->set_geometry); @@ -2560,6 +2710,12 @@ static bool render_layer(LunarWM *this, LunarWM_RenderLayerInfo *info, float dt) info->layers[info->layers_count++] = (XrCompositionLayerBaseHeader *)&info->layer_projection; + if (this->renderer.first_frame) { + l_recenter(this->cman->L); + lua_pop(this->cman->L, 1); + this->renderer.first_frame = false; + } + return true; } @@ -2568,6 +2724,8 @@ void LunarWM_run(LunarWM *this) assert(this); assert(this->initialized); + this->renderer.first_frame = true; + if (!wlr_backend_start(this->wayland.backend)) { wlr_log(WLR_ERROR, "Failed to start backend"); return; diff --git a/src/LunarWM.h b/src/LunarWM.h index 76d284d..d85814d 100644 --- a/src/LunarWM.h +++ b/src/LunarWM.h @@ -75,6 +75,8 @@ typedef struct { struct wl_listener commit; struct wl_listener destroy; + struct wl_listener map, unmap; + union { struct wlr_xdg_toplevel *xdg; struct wlr_xwayland_surface *xwl; @@ -206,6 +208,8 @@ typedef struct LunarWM { Shader linear_srgb; Skybox skybox; + + bool first_frame; } renderer; ConfigManager *cman;