From 19b5d47a9e712b980749b7b9e1c562701d5afafc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Tue, 6 Dec 2022 13:31:48 -0600 Subject: [PATCH 1/5] fix popups appearing outside the monitor and a potentially crash --- dwl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 3d71d19..1dae796 100644 --- a/dwl.c +++ b/dwl.c @@ -914,7 +914,7 @@ createnotify(struct wl_listener *listener, void *data) return; xdg_surface->surface->data = wlr_scene_xdg_surface_create( xdg_surface->popup->parent->data, xdg_surface); - if ((!l || !l->mon) || (!c || !c->mon)) + if ((l && !l->mon) || (c && !c->mon)) return; box = type == LayerShell ? l->mon->m : c->mon->w; box.x -= (type == LayerShell ? l->geom.x : c->geom.x); From 9136b6247d186790926bdee32d0a17d668ff8924 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Tue, 6 Dec 2022 13:34:35 -0600 Subject: [PATCH 2/5] return early if couldn't find the popup's parent --- dwl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index 1dae796..b26743c 100644 --- a/dwl.c +++ b/dwl.c @@ -910,7 +910,7 @@ createnotify(struct wl_listener *listener, void *data) if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { struct wlr_box box; int type = toplevel_from_wlr_surface(xdg_surface->surface, &c, &l); - if (!xdg_surface->popup->parent) + if (!xdg_surface->popup->parent || type < 0) return; xdg_surface->surface->data = wlr_scene_xdg_surface_create( xdg_surface->popup->parent->data, xdg_surface); From 4a32293548667e68cd9a103e22368b8db1754deb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Tue, 6 Dec 2022 13:56:28 -0600 Subject: [PATCH 3/5] Revert "fix flickering when resizing/spawning windows" This reverts commit 017bb7d7521f68d37bfe656c10f45edbcc92dd61. Bug: https://github.com/djpohly/dwl/issues/349 --- client.h | 15 --------------- dwl.c | 29 +++++++++++++++++++++++------ 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/client.h b/client.h index 77cde58..dbf018f 100644 --- a/client.h +++ b/client.h @@ -240,21 +240,6 @@ client_is_mapped(Client *c) return c->surface.xdg->mapped; } -static inline int -client_is_rendered_on_mon(Client *c, Monitor *m) -{ - /* This is needed for when you don't want to check formal assignment, - * but rather actual displaying of the pixels. - * Usually VISIBLEON suffices and is also faster. */ - struct wlr_surface_output *s; - if (!c->scene->node.enabled) - return 0; - wl_list_for_each(s, &client_surface(c)->current_outputs, link) - if (s->output == m->wlr_output) - return 1; - return 0; -} - static inline int client_is_unmanaged(Client *c) { diff --git a/dwl.c b/dwl.c index b26743c..a1f579a 100644 --- a/dwl.c +++ b/dwl.c @@ -182,6 +182,7 @@ struct Monitor { unsigned int tagset[2]; double mfact; int nmaster; + int un_map; /* If a map/unmap happened on this monitor, then this should be true */ }; typedef struct { @@ -1383,6 +1384,8 @@ mapnotify(struct wl_listener *listener, void *data) } printstatus(); + c->mon->un_map = 1; + unset_fullscreen: m = c->mon ? c->mon : xytomon(c->geom.x, c->geom.y); wl_list_for_each(w, &clients, link) @@ -1683,19 +1686,30 @@ rendermon(struct wl_listener *listener, void *data) * generally at the output's refresh rate (e.g. 60Hz). */ Monitor *m = wl_container_of(listener, m, frame); Client *c; + int skip = 0; struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + /* Render if no XDG clients have an outstanding resize and are visible on * this monitor. */ - wl_list_for_each(c, &clients, link) - if (client_is_rendered_on_mon(c, m) && (!c->isfloating && c->resize)) - goto skip; - if (!wlr_scene_output_commit(m->scene_output)) + /* Checking m->un_map for every client is not optimal but works */ + wl_list_for_each(c, &clients, link) { + if ((c->resize && m->un_map) || (c->type == XDGShell + && (c->surface.xdg->pending.geometry.width != + c->surface.xdg->current.geometry.width + || c->surface.xdg->pending.geometry.height != + c->surface.xdg->current.geometry.height))) { + /* Lie */ + wlr_surface_send_frame_done(client_surface(c), &now); + skip = 1; + } + } + if (!skip && !wlr_scene_output_commit(m->scene_output)) return; -skip: /* Let clients know a frame has been rendered */ - clock_gettime(CLOCK_MONOTONIC, &now); wlr_scene_output_send_frame_done(m->scene_output, &now); + m->un_map = 0; } void @@ -2260,6 +2274,9 @@ unmapnotify(struct wl_listener *listener, void *data) grabc = NULL; } + if (c->mon) + c->mon->un_map = 1; + if (client_is_unmanaged(c)) { if (c == exclusive_focus) exclusive_focus = NULL; From d42a977b5ba8ae09753c7f6fcf4e2044bac7d92e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Tue, 6 Dec 2022 13:56:46 -0600 Subject: [PATCH 4/5] Revert "remove unneeded changes in commitnotify()" This reverts commit 30c24a53ad2aaa842bc3b028ba0b98e3362dad7c. Bug: https://github.com/djpohly/dwl/issues/349 --- dwl.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/dwl.c b/dwl.c index a1f579a..d1f39d5 100644 --- a/dwl.c +++ b/dwl.c @@ -730,9 +730,14 @@ commitnotify(struct wl_listener *listener, void *data) struct wlr_box box = {0}; client_get_geometry(c, &box); + if (c->mon && !wlr_box_empty(&box) && (box.width != c->geom.width - 2 * c->bw + || box.height != c->geom.height - 2 * c->bw)) + arrange(c->mon); /* mark a pending resize as completed */ - if (c->resize && (c->resize <= c->surface.xdg->current.configure_serial)) + if (c->resize && (c->resize <= c->surface.xdg->current.configure_serial + || (c->surface.xdg->current.geometry.width == c->surface.xdg->pending.geometry.width + && c->surface.xdg->current.geometry.height == c->surface.xdg->pending.geometry.height))) c->resize = 0; } From 79b051f2425af8a1e91a61fba145beb00f522973 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= Date: Sun, 13 Feb 2022 11:56:57 -0600 Subject: [PATCH 5/5] implement ext-session-lock-v1 --- dwl.c | 175 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 167 insertions(+), 8 deletions(-) diff --git a/dwl.c b/dwl.c index d1f39d5..9ee3c76 100644 --- a/dwl.c +++ b/dwl.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -73,7 +74,7 @@ /* enums */ enum { CurNormal, CurPressed, CurMove, CurResize }; /* cursor */ enum { XDGShell, LayerShell, X11Managed, X11Unmanaged }; /* client types */ -enum { LyrBg, LyrBottom, LyrTop, LyrOverlay, LyrTile, LyrFloat, LyrFS, LyrDragIcon, NUM_LAYERS }; /* scene layers */ +enum { LyrBg, LyrBottom, LyrTop, LyrOverlay, LyrTile, LyrFloat, LyrFS, LyrDragIcon, LyrBlock, NUM_LAYERS }; /* scene layers */ #ifdef XWAYLAND enum { NetWMWindowTypeDialog, NetWMWindowTypeSplash, NetWMWindowTypeToolbar, NetWMWindowTypeUtility, NetLast }; /* EWMH atoms */ @@ -173,6 +174,8 @@ struct Monitor { struct wlr_scene_rect *fullscreen_bg; /* See createmon() for info */ struct wl_listener frame; struct wl_listener destroy; + struct wl_listener destroy_lock_surface; + struct wlr_session_lock_surface_v1 *lock_surface; struct wlr_box m; /* monitor area, layout-relative */ struct wlr_box w; /* window area, layout-relative */ struct wl_list layers[4]; /* LayerSurface::link */ @@ -202,6 +205,15 @@ typedef struct { int monitor; } Rule; +typedef struct { + struct wlr_scene_tree *scene; + + struct wlr_session_lock_v1 *lock; + struct wl_listener new_surface; + struct wl_listener unlock; + struct wl_listener destroy; +} SessionLock; + /* function declarations */ static void applybounds(Client *c, struct wlr_box *bbox); static void applyrules(Client *c); @@ -222,6 +234,7 @@ static void commitnotify(struct wl_listener *listener, void *data); static void createidleinhibitor(struct wl_listener *listener, void *data); static void createkeyboard(struct wlr_keyboard *keyboard); static void createlayersurface(struct wl_listener *listener, void *data); +static void createlocksurface(struct wl_listener *listener, void *data); static void createmon(struct wl_listener *listener, void *data); static void createnotify(struct wl_listener *listener, void *data); static void createpointer(struct wlr_pointer *pointer); @@ -229,7 +242,11 @@ static void cursorframe(struct wl_listener *listener, void *data); static void destroydragicon(struct wl_listener *listener, void *data); static void destroyidleinhibitor(struct wl_listener *listener, void *data); static void destroylayersurfacenotify(struct wl_listener *listener, void *data); +static void destroylock(SessionLock *lock, int unlocked); +static void destroylocksurface(struct wl_listener *listener, void *data); static void destroynotify(struct wl_listener *listener, void *data); +static void destroysessionlock(struct wl_listener *listener, void *data); +static void destroysessionmgr(struct wl_listener *listener, void *data); static Monitor *dirtomon(enum wlr_direction dir); static void focusclient(Client *c, int lift); static void focusmon(const Arg *arg); @@ -242,6 +259,7 @@ static int keybinding(uint32_t mods, xkb_keysym_t sym); static void keypress(struct wl_listener *listener, void *data); static void keypressmod(struct wl_listener *listener, void *data); static void killclient(const Arg *arg); +static void locksession(struct wl_listener *listener, void *data); static void maplayersurfacenotify(struct wl_listener *listener, void *data); static void mapnotify(struct wl_listener *listener, void *data); static void maximizenotify(struct wl_listener *listener, void *data); @@ -281,6 +299,7 @@ static void togglefloating(const Arg *arg); static void togglefullscreen(const Arg *arg); static void toggletag(const Arg *arg); static void toggleview(const Arg *arg); +static void unlocksession(struct wl_listener *listener, void *data); static void unmaplayersurfacenotify(struct wl_listener *listener, void *data); static void unmapnotify(struct wl_listener *listener, void *data); static void updatemons(struct wl_listener *listener, void *data); @@ -297,6 +316,7 @@ static void zoom(const Arg *arg); static const char broken[] = "broken"; static const char *cursor_image = "left_ptr"; static pid_t child_pid = -1; +static int locked; static void *exclusive_focus; static struct wl_display *dpy; static struct wlr_backend *backend; @@ -321,6 +341,10 @@ static struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard_mgr; static struct wlr_cursor *cursor; static struct wlr_xcursor_manager *cursor_mgr; +static struct wlr_session_lock_manager_v1 *session_lock_mgr; +static struct wlr_scene_rect *locked_bg; +static struct wlr_session_lock_v1 *cur_lock; + static struct wlr_seat *seat; static struct wl_list keyboards; static unsigned int cursor_mode; @@ -355,6 +379,8 @@ static struct wl_listener request_set_psel = {.notify = setpsel}; static struct wl_listener request_set_sel = {.notify = setsel}; static struct wl_listener request_start_drag = {.notify = requeststartdrag}; static struct wl_listener start_drag = {.notify = startdrag}; +static struct wl_listener session_lock_create_lock = {.notify = locksession}; +static struct wl_listener session_lock_mgr_destroy = {.notify = destroysessionmgr}; #ifdef XWAYLAND static void activatex11(struct wl_listener *listener, void *data); @@ -504,8 +530,8 @@ arrangelayers(Monitor *m) for (i = 0; i < LENGTH(layers_above_shell); i++) { wl_list_for_each_reverse(layersurface, &m->layers[layers_above_shell[i]], link) { - if (layersurface->layer_surface->current.keyboard_interactive && - layersurface->mapped) { + if (!locked && layersurface->layer_surface->current.keyboard_interactive + && layersurface->mapped) { /* Deactivate the focused client. */ focusclient(NULL, 0); exclusive_focus = layersurface; @@ -544,6 +570,10 @@ buttonpress(struct wl_listener *listener, void *data) switch (event->state) { case WLR_BUTTON_PRESSED: + cursor_mode = CurPressed; + if (locked) + break; + /* Change focus if the button was _pressed_ over a client */ xytonode(cursor->x, cursor->y, NULL, &c, NULL, NULL, NULL); if (c && (!client_is_unmanaged(c) || client_wants_focus(c))) @@ -558,11 +588,10 @@ buttonpress(struct wl_listener *listener, void *data) return; } } - cursor_mode = CurPressed; break; case WLR_BUTTON_RELEASED: /* If you released any buttons, we exit interactive move/resize mode. */ - if (cursor_mode != CurNormal && cursor_mode != CurPressed) { + if (!locked && cursor_mode != CurNormal && cursor_mode != CurPressed) { cursor_mode = CurNormal; /* Clear the pointer focus, this way if the cursor is over a surface * we will send an enter event after which the client will provide us @@ -828,6 +857,25 @@ createlayersurface(struct wl_listener *listener, void *data) wlr_layer_surface->current = old_state; } +void +createlocksurface(struct wl_listener *listener, void *data) +{ + SessionLock *lock = wl_container_of(listener, lock, new_surface); + struct wlr_session_lock_surface_v1 *lock_surface = data; + Monitor *m = lock_surface->output->data; + struct wlr_scene_tree *scene_tree = lock_surface->surface->data = + wlr_scene_subsurface_tree_create(lock->scene, lock_surface->surface); + m->lock_surface = lock_surface; + + wlr_scene_node_set_position(&scene_tree->node, m->m.x, m->m.y); + wlr_session_lock_surface_v1_configure(lock_surface, m->m.width, m->m.height); + + LISTEN(&lock_surface->events.destroy, &m->destroy_lock_surface, destroylocksurface); + + if (m == selmon) + client_notify_enter(lock_surface->surface, wlr_seat_get_keyboard(seat)); +} + void createmon(struct wl_listener *listener, void *data) { @@ -1032,6 +1080,49 @@ destroylayersurfacenotify(struct wl_listener *listener, void *data) free(layersurface); } +void +destroylock(SessionLock *lock, int unlock) +{ + wlr_seat_keyboard_notify_clear_focus(seat); + if ((locked = !unlock)) + goto destroy; + + wlr_scene_node_set_enabled(&locked_bg->node, 0); + + focusclient(focustop(selmon), 0); + motionnotify(0); + +destroy: + wl_list_remove(&lock->new_surface.link); + wl_list_remove(&lock->unlock.link); + wl_list_remove(&lock->destroy.link); + + wlr_scene_node_destroy(&lock->scene->node); + cur_lock = NULL; + free(lock); +} + +void +destroylocksurface(struct wl_listener *listener, void *data) +{ + Monitor *m = wl_container_of(listener, m, destroy_lock_surface); + struct wlr_session_lock_surface_v1 *surface, *lock_surface = m->lock_surface; + + m->lock_surface = NULL; + wl_list_remove(&m->destroy_lock_surface.link); + + if (lock_surface->surface == seat->keyboard_state.focused_surface) { + if (locked && cur_lock && !wl_list_empty(&cur_lock->surfaces)) { + surface = wl_container_of(cur_lock->surfaces.next, surface, link); + client_notify_enter(surface->surface, wlr_seat_get_keyboard(seat)); + } else if (!locked) { + focusclient(focustop(selmon), 1); + } else { + wlr_seat_keyboard_clear_focus(seat); + } + } +} + void destroynotify(struct wl_listener *listener, void *data) { @@ -1052,6 +1143,20 @@ destroynotify(struct wl_listener *listener, void *data) free(c); } +void +destroysessionlock(struct wl_listener *listener, void *data) +{ + SessionLock *lock = wl_container_of(listener, lock, destroy); + destroylock(lock, 0); +} + +void +destroysessionmgr(struct wl_listener *listener, void *data) +{ + wl_list_remove(&session_lock_create_lock.link); + wl_list_remove(&session_lock_mgr_destroy.link); +} + Monitor * dirtomon(enum wlr_direction dir) { @@ -1074,6 +1179,9 @@ focusclient(Client *c, int lift) struct wlr_surface *old = seat->keyboard_state.focused_surface; int i; + if (locked) + return; + /* Raise client in stacking order if requested */ if (c && lift) wlr_scene_node_raise_to_top(&c->scene->node); @@ -1276,7 +1384,7 @@ keypress(struct wl_listener *listener, void *data) /* On _press_ if there is no active screen locker, * attempt to process a compositor keybinding. */ - if (!input_inhibit_mgr->active_inhibitor + if (!locked && !input_inhibit_mgr->active_inhibitor && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) for (i = 0; i < nsyms; i++) handled = keybinding(mods, syms[i]) || handled; @@ -1315,6 +1423,31 @@ killclient(const Arg *arg) client_send_close(sel); } +void +locksession(struct wl_listener *listener, void *data) +{ + struct wlr_session_lock_v1 *session_lock = data; + SessionLock *lock; + wlr_scene_node_set_enabled(&locked_bg->node, 1); + if (cur_lock) { + wlr_session_lock_v1_destroy(session_lock); + return; + } + lock = ecalloc(1, sizeof(*lock)); + focusclient(NULL, 0); + + lock->scene = wlr_scene_tree_create(layers[LyrBlock]); + cur_lock = lock->lock = session_lock; + locked = 1; + session_lock->data = lock; + + LISTEN(&session_lock->events.new_surface, &lock->new_surface, createlocksurface); + LISTEN(&session_lock->events.destroy, &lock->destroy, destroysessionlock); + LISTEN(&session_lock->events.unlock, &lock->unlock, unlocksession); + + wlr_session_lock_v1_send_locked(session_lock); +} + void maplayersurfacenotify(struct wl_listener *listener, void *data) { @@ -1985,6 +2118,7 @@ setup(void) layers[LyrTop] = wlr_scene_tree_create(&scene->tree); layers[LyrOverlay] = wlr_scene_tree_create(&scene->tree); layers[LyrDragIcon] = wlr_scene_tree_create(&scene->tree); + layers[LyrBlock] = wlr_scene_tree_create(&scene->tree); /* Create a renderer with the default implementation */ if (!(drw = wlr_renderer_autocreate(backend))) @@ -2049,6 +2183,12 @@ setup(void) wl_signal_add(&xdg_shell->events.new_surface, &new_xdg_surface); input_inhibit_mgr = wlr_input_inhibit_manager_create(dpy); + session_lock_mgr = wlr_session_lock_manager_v1_create(dpy); + wl_signal_add(&session_lock_mgr->events.new_lock, &session_lock_create_lock); + wl_signal_add(&session_lock_mgr->events.destroy, &session_lock_mgr_destroy); + locked_bg = wlr_scene_rect_create(layers[LyrBlock], sgeom.width, sgeom.height, + (float [4]){0.1, 0.1, 0.1, 1.0}); + wlr_scene_node_set_enabled(&locked_bg->node, 0); /* Use decoration protocols to negotiate server-side decorations */ wlr_server_decoration_manager_set_default_mode( @@ -2251,6 +2391,13 @@ toggleview(const Arg *arg) printstatus(); } +void +unlocksession(struct wl_listener *listener, void *data) +{ + SessionLock *lock = wl_container_of(listener, lock, unlock); + destroylock(lock, 1); +} + void unmaplayersurfacenotify(struct wl_listener *listener, void *data) { @@ -2334,6 +2481,7 @@ updatemons(struct wl_listener *listener, void *data) wlr_output_layout_add_auto(output_layout, m->wlr_output); /* Now that we update the output layout we can get its box */ wlr_output_layout_get_box(output_layout, NULL, &sgeom); + wlr_scene_rect_set_size(locked_bg, sgeom.width, sgeom.height); wl_list_for_each(m, &mons, link) { if (!m->wlr_output->enabled) continue; @@ -2351,16 +2499,27 @@ updatemons(struct wl_listener *listener, void *data) wlr_scene_node_set_position(&m->fullscreen_bg->node, m->m.x, m->m.y); wlr_scene_rect_set_size(m->fullscreen_bg, m->m.width, m->m.height); + if (m->lock_surface) { + struct wlr_scene_tree *scene_tree = m->lock_surface->surface->data; + wlr_scene_node_set_position(&scene_tree->node, m->m.x, m->m.y); + wlr_session_lock_surface_v1_configure(m->lock_surface, m->m.width, + m->m.height); + } + config_head->state.enabled = 1; config_head->state.mode = m->wlr_output->current_mode; config_head->state.x = m->m.x; config_head->state.y = m->m.y; } - if (selmon && selmon->wlr_output->enabled) + if (selmon && selmon->wlr_output->enabled) { wl_list_for_each(c, &clients, link) if (!c->mon && client_is_mapped(c)) setmon(c, selmon, c->tags); + if (selmon->lock_surface) + client_notify_enter(selmon->lock_surface->surface, + wlr_seat_get_keyboard(seat)); + } wlr_output_manager_v1_set_configuration(output_mgr, config); } @@ -2421,7 +2580,7 @@ xytonode(double x, double y, struct wlr_surface **psurface, Client *c = NULL; LayerSurface *l = NULL; const int *layer; - int focus_order[] = { LyrOverlay, LyrTop, LyrFS, LyrFloat, LyrTile, LyrBottom, LyrBg }; + int focus_order[] = { LyrBlock, LyrOverlay, LyrTop, LyrFS, LyrFloat, LyrTile, LyrBottom, LyrBg }; for (layer = focus_order; layer < END(focus_order); layer++) { if ((node = wlr_scene_node_at(&layers[*layer]->node, x, y, nx, ny))) {