diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 6b60803..cd9bd8d 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -14,9 +14,4 @@ wlroots version: diff --git a/client.h b/client.h index 4dc9e1a..5a45edc 100644 --- a/client.h +++ b/client.h @@ -16,40 +16,6 @@ client_is_x11(Client *c) #endif } -static inline Client * -client_from_wlr_surface(struct wlr_surface *s) -{ - struct wlr_xdg_surface *surface; - -#ifdef XWAYLAND - struct wlr_xwayland_surface *xsurface; - if (s && wlr_surface_is_xwayland_surface(s) - && (xsurface = wlr_xwayland_surface_from_wlr_surface(s))) - return xsurface->data; -#endif - if (s && wlr_surface_is_xdg_surface(s) - && (surface = wlr_xdg_surface_from_wlr_surface(s)) - && surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) - return surface->data; - - if (s && wlr_surface_is_subsurface(s)) - return client_from_wlr_surface(wlr_surface_get_root_surface(s)); - return NULL; -} - -static inline Client * -client_get_parent(Client *c) -{ -#ifdef XWAYLAND - if (client_is_x11(c) && c->surface.xwayland->parent) - return client_from_wlr_surface(c->surface.xwayland->parent->surface); -#endif - if (c->surface.xdg->toplevel->parent) - return client_from_wlr_surface(c->surface.xdg->toplevel->parent->base->surface); - - return NULL; -} - static inline void client_get_size_hints(Client *c, struct wlr_box *max, struct wlr_box *min) { @@ -85,6 +51,69 @@ client_surface(Client *c) return c->surface.xdg->surface; } +static inline int +toplevel_from_wlr_surface(struct wlr_surface *s, Client **pc, LayerSurface **pl) +{ + struct wlr_xdg_surface *xdg_surface; + struct wlr_surface *root_surface; + struct wlr_layer_surface_v1 *layer_surface; + Client *c = NULL; + LayerSurface *l = NULL; + int type = -1; +#ifdef XWAYLAND + struct wlr_xwayland_surface *xsurface; +#endif + + if (!s) + return type; + root_surface = wlr_surface_get_root_surface(s); + +#ifdef XWAYLAND + if (wlr_surface_is_xwayland_surface(root_surface) + && (xsurface = wlr_xwayland_surface_from_wlr_surface(root_surface))) { + c = xsurface->data; + type = c->type; + goto end; + } +#endif + + if (wlr_surface_is_layer_surface(root_surface) + && (layer_surface = wlr_layer_surface_v1_from_wlr_surface(root_surface))) { + l = layer_surface->data; + type = LayerShell; + goto end; + } + + if (wlr_surface_is_xdg_surface(root_surface) + && (xdg_surface = wlr_xdg_surface_from_wlr_surface(root_surface))) { + while (1) { + switch (xdg_surface->role) { + case WLR_XDG_SURFACE_ROLE_POPUP: + if (!xdg_surface->popup->parent) + return -1; + else if (!wlr_surface_is_xdg_surface(xdg_surface->popup->parent)) + return toplevel_from_wlr_surface(xdg_surface->popup->parent, pc, pl); + + xdg_surface = wlr_xdg_surface_from_wlr_surface(xdg_surface->popup->parent); + break; + case WLR_XDG_SURFACE_ROLE_TOPLEVEL: + c = xdg_surface->data; + type = c->type; + goto end; + case WLR_XDG_SURFACE_ROLE_NONE: + return -1; + } + } + } + +end: + if (pl) + *pl = l; + if (pc) + *pc = c; + return type; +} + /* The others */ static inline void client_activate_surface(struct wlr_surface *s, int activated) @@ -112,7 +141,7 @@ client_set_bounds(Client *c, int32_t width, int32_t height) return 0; #endif if (c->surface.xdg->client->shell->version >= - XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION) + XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION && width >= 0 && height >= 0) return wlr_xdg_toplevel_set_bounds(c->surface.xdg->toplevel, width, height); return 0; } @@ -153,6 +182,20 @@ client_get_geometry(Client *c, struct wlr_box *geom) wlr_xdg_surface_get_geometry(c->surface.xdg, geom); } +static inline Client * +client_get_parent(Client *c) +{ + Client *p = NULL; +#ifdef XWAYLAND + if (client_is_x11(c) && c->surface.xwayland->parent) + toplevel_from_wlr_surface(c->surface.xwayland->parent->surface, &p, NULL); +#endif + if (c->surface.xdg->toplevel->parent) + toplevel_from_wlr_surface(c->surface.xdg->toplevel->parent->base->surface, &p, NULL); + + return p; +} + static inline const char * client_get_title(Client *c) { @@ -212,6 +255,32 @@ client_is_rendered_on_mon(Client *c, Monitor *m) return 0; } +static inline int +client_is_stopped(Client *c) +{ + int pid; + siginfo_t in = {0}; +#ifdef XWAYLAND + if (client_is_x11(c)) + return 0; +#endif + + wl_client_get_credentials(c->surface.xdg->client->client, &pid, NULL, NULL); + if (waitid(P_PID, pid, &in, WNOHANG|WCONTINUED|WSTOPPED|WNOWAIT) < 0) { + /* This process is not our child process, while is very unluckely that + * it is stopped, in order to do not skip frames assume that it is. */ + if (errno == ECHILD) + return 1; + } else if (in.si_pid) { + if (in.si_code == CLD_STOPPED || in.si_code == CLD_TRAPPED) + return 1; + if (in.si_code == CLD_CONTINUED) + return 0; + } + + return 0; +} + static inline int client_is_unmanaged(Client *c) { @@ -276,6 +345,9 @@ client_set_size(Client *c, uint32_t width, uint32_t height) return 0; } #endif + if (width == c->surface.xdg->toplevel->current.width + && height ==c->surface.xdg->toplevel->current.height) + return 0; return wlr_xdg_toplevel_set_size(c->surface.xdg->toplevel, width, height); } @@ -320,43 +392,3 @@ client_wants_fullscreen(Client *c) #endif return c->surface.xdg->toplevel->requested.fullscreen; } - -static inline void * -toplevel_from_popup(struct wlr_xdg_popup *popup) -{ - struct wlr_xdg_surface *surface = popup->base; - - while (1) { - switch (surface->role) { - case WLR_XDG_SURFACE_ROLE_POPUP: - if (!surface->popup->parent) - return NULL; - else if (wlr_surface_is_layer_surface(surface->popup->parent)) - return wlr_layer_surface_v1_from_wlr_surface(surface->popup->parent)->data; - else if (!wlr_surface_is_xdg_surface(surface->popup->parent)) - return NULL; - - surface = wlr_xdg_surface_from_wlr_surface(surface->popup->parent); - break; - case WLR_XDG_SURFACE_ROLE_TOPLEVEL: - return surface->data; - case WLR_XDG_SURFACE_ROLE_NONE: - return NULL; - } - } -} - -static inline void * -toplevel_from_wlr_layer_surface(struct wlr_surface *s) -{ - Client *c; - struct wlr_layer_surface_v1 *wlr_layer_surface; - - if ((c = client_from_wlr_surface(s))) - return c; - else if (s && wlr_surface_is_layer_surface(s) - && (wlr_layer_surface = wlr_layer_surface_v1_from_wlr_surface(s))) - return wlr_layer_surface->data; - - return NULL; -} diff --git a/config.def.h b/config.def.h index a4f7c13..447ba00 100644 --- a/config.def.h +++ b/config.def.h @@ -2,14 +2,13 @@ static const int sloppyfocus = 1; /* focus follows mouse */ static const int bypass_surface_visibility = 0; /* 1 means idle inhibitors will disable idle tracking even if it's surface isn't visible */ static const unsigned int borderpx = 1; /* border pixel of windows */ -static const float rootcolor[] = {0.3, 0.3, 0.3, 1.0}; static const float bordercolor[] = {0.5, 0.5, 0.5, 1.0}; static const float focuscolor[] = {1.0, 0.0, 0.0, 1.0}; /* To conform the xdg-protocol, set the alpha to zero to restore the old behavior */ static const float fullscreen_bg[] = {0.1, 0.1, 0.1, 1.0}; -/* tagging */ -static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; +/* tagging - tagcount must be no greater than 31 */ +static const int tagcount = 9; static const Rule rules[] = { /* app_id title tags mask isfloating monitor */ @@ -29,12 +28,12 @@ static const Layout layouts[] = { /* monitors */ static const MonitorRule monrules[] = { - /* name mfact nmaster scale layout rotate/reflect */ + /* name mfact nmaster scale layout rotate/reflect x y */ /* example of a HiDPI laptop monitor: - { "eDP-1", 0.5, 1, 2, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL }, + { "eDP-1", 0.5, 1, 2, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 }, */ /* defaults */ - { NULL, 0.55, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL }, + { NULL, 0.55, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 }, }; /* keyboard */ @@ -66,9 +65,9 @@ LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN static const enum libinput_config_scroll_method scroll_method = LIBINPUT_CONFIG_SCROLL_2FG; /* You can choose between: -LIBINPUT_CONFIG_CLICK_METHOD_NONE -LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS -LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER +LIBINPUT_CONFIG_CLICK_METHOD_NONE +LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS +LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER */ static const enum libinput_config_click_method click_method = LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS; diff --git a/config.mk b/config.mk index c2dd026..f50156f 100644 --- a/config.mk +++ b/config.mk @@ -1,5 +1,5 @@ -_VERSION = 0.4-rc1 -VERSION = `git describe --long --tags --dirty 2>/dev/null || echo $(_VERSION)` +_VERSION = 0.4 +VERSION = `git describe --tags --dirty 2>/dev/null || echo $(_VERSION)` PKG_CONFIG = pkg-config diff --git a/dwl.c b/dwl.c index 49b87c1..df8f32d 100644 --- a/dwl.c +++ b/dwl.c @@ -40,6 +40,7 @@ #include #include #include +#include #include #include #include @@ -66,14 +67,14 @@ #define VISIBLEON(C, M) ((M) && (C)->mon == (M) && ((C)->tags & (M)->tagset[(M)->seltags])) #define LENGTH(X) (sizeof X / sizeof X[0]) #define END(A) ((A) + LENGTH(A)) -#define TAGMASK ((1 << LENGTH(tags)) - 1) +#define TAGMASK ((1u << tagcount) - 1) #define LISTEN(E, L, H) wl_signal_add((E), ((L)->notify = (H), (L))) #define IDLE_NOTIFY_ACTIVITY wlr_idle_notify_activity(idle, seat), wlr_idle_notifier_v1_notify_activity(idle_notifier, seat) /* 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 */ @@ -81,7 +82,7 @@ enum { NetWMWindowTypeDialog, NetWMWindowTypeSplash, NetWMWindowTypeToolbar, typedef union { int i; - unsigned int ui; + uint32_t ui; float f; const void *v; } Arg; @@ -97,7 +98,7 @@ typedef struct Monitor Monitor; typedef struct { /* Must keep these three elements in this order */ unsigned int type; /* XDGShell or X11* */ - struct wlr_box geom; /* layout-relative, includes border */ + struct wlr_box geom; /* layout-relative, includes border */ Monitor *mon; struct wlr_scene_tree *scene; struct wlr_scene_rect *border[4]; /* top, bottom, left, right */ @@ -115,14 +116,14 @@ typedef struct { struct wl_listener destroy; struct wl_listener set_title; struct wl_listener fullscreen; - struct wlr_box prev; /* layout-relative, includes border */ + struct wlr_box prev; /* layout-relative, includes border */ #ifdef XWAYLAND struct wl_listener activate; struct wl_listener configure; struct wl_listener set_hints; #endif unsigned int bw; - unsigned int tags; + uint32_t tags; int isfloating, isurgent, isfullscreen; uint32_t resize; /* configure serial of a pending resize */ } Client; @@ -138,6 +139,11 @@ typedef struct { struct wl_list link; struct wlr_keyboard *wlr_keyboard; + int nsyms; + const xkb_keysym_t *keysyms; /* invalid if nsyms == 0 */ + uint32_t mods; /* invalid if nsyms == 0 */ + struct wl_event_source *key_repeat_source; + struct wl_listener modifiers; struct wl_listener key; struct wl_listener destroy; @@ -173,15 +179,18 @@ struct Monitor { struct wlr_scene_rect *fullscreen_bg; /* See createmon() for info */ struct wl_listener frame; struct wl_listener destroy; - struct wlr_box m; /* monitor area, layout-relative */ - struct wlr_box w; /* window area, layout-relative */ + 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 */ const Layout *lt[2]; unsigned int seltags; unsigned int sellt; - unsigned int tagset[2]; + uint32_t tagset[2]; double mfact; int nmaster; + char ltsymbol[16]; }; typedef struct { @@ -191,16 +200,26 @@ typedef struct { float scale; const Layout *lt; enum wl_output_transform rr; + int x, y; } MonitorRule; typedef struct { const char *id; const char *title; - unsigned int tags; + uint32_t tags; int isfloating; 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); @@ -218,9 +237,11 @@ static void cleanupmon(struct wl_listener *listener, void *data); static void closemon(Monitor *m); static void commitlayersurfacenotify(struct wl_listener *listener, void *data); static void commitnotify(struct wl_listener *listener, void *data); +static void createdecoration(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); @@ -228,7 +249,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); @@ -240,7 +265,9 @@ static void inputdevice(struct wl_listener *listener, void *data); 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 int keyrepeat(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); @@ -261,16 +288,16 @@ static void rendermon(struct wl_listener *listener, void *data); static void requeststartdrag(struct wl_listener *listener, void *data); static void resize(Client *c, struct wlr_box geo, int interact); static void run(char *startup_cmd); -static Client *selclient(void); static void setcursor(struct wl_listener *listener, void *data); static void setfloating(Client *c, int floating); static void setfullscreen(Client *c, int fullscreen); static void setlayout(const Arg *arg); static void setmfact(const Arg *arg); -static void setmon(Client *c, Monitor *m, unsigned int newtags); +static void setmon(Client *c, Monitor *m, uint32_t newtags); static void setpsel(struct wl_listener *listener, void *data); static void setsel(struct wl_listener *listener, void *data); static void setup(void); +static void sigchld(int unused); static void spawn(const Arg *arg); static void startdrag(struct wl_listener *listener, void *data); static void tag(const Arg *arg); @@ -280,6 +307,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); @@ -296,6 +324,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; @@ -307,6 +336,7 @@ static struct wlr_compositor *compositor; static struct wlr_xdg_shell *xdg_shell; static struct wlr_xdg_activation_v1 *activation; +static struct wlr_xdg_decoration_manager_v1 *xdg_decoration_mgr; static struct wl_list clients; /* tiling order */ static struct wl_list fstack; /* focus order */ static struct wlr_idle *idle; @@ -320,6 +350,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; @@ -345,6 +379,7 @@ static struct wl_listener new_input = {.notify = inputdevice}; static struct wl_listener new_virtual_keyboard = {.notify = virtualkeyboard}; static struct wl_listener new_output = {.notify = createmon}; static struct wl_listener new_xdg_surface = {.notify = createnotify}; +static struct wl_listener new_xdg_decoration = {.notify = createdecoration}; static struct wl_listener new_layer_shell_surface = {.notify = createlayersurface}; static struct wl_listener output_mgr_apply = {.notify = outputmgrapply}; static struct wl_listener output_mgr_test = {.notify = outputmgrtest}; @@ -354,6 +389,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); @@ -361,7 +398,6 @@ static void configurex11(struct wl_listener *listener, void *data); static void createnotifyx11(struct wl_listener *listener, void *data); static Atom getatom(xcb_connection_t *xc, const char *name); static void sethints(struct wl_listener *listener, void *data); -static void sigchld(int unused); static void xwaylandready(struct wl_listener *listener, void *data); static struct wl_listener new_xwayland_surface = {.notify = createnotifyx11}; static struct wl_listener xwayland_ready = {.notify = xwaylandready}; @@ -377,9 +413,6 @@ static Atom netatom[NetLast]; /* attempt to encapsulate suck into one file */ #include "client.h" -/* compile-time check if all tags fit into an unsigned int bit array. */ -struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; - /* function implementations */ void applybounds(Client *c, struct wlr_box *bbox) @@ -390,8 +423,8 @@ applybounds(Client *c, struct wlr_box *bbox) /* try to set size hints */ c->geom.width = MAX(min.width + (2 * (int)c->bw), c->geom.width); c->geom.height = MAX(min.height + (2 * (int)c->bw), c->geom.height); - /* Some clients set them max size to INT_MAX, which does not violates - * the protocol but its innecesary, they can set them max size to zero. */ + /* Some clients set their max size to INT_MAX, which does not violate the + * protocol but it's unnecesary, as they can set their max size to zero. */ if (max.width > 0 && !(2 * c->bw > INT_MAX - max.width)) /* Checks for overflow */ c->geom.width = MIN(max.width + (2 * c->bw), c->geom.width); if (max.height > 0 && !(2 * c->bw > INT_MAX - max.height)) /* Checks for overflow */ @@ -413,7 +446,7 @@ applyrules(Client *c) { /* rule matching */ const char *appid, *title; - unsigned int i, newtags = 0; + uint32_t i, newtags = 0; const Rule *r; Monitor *mon = selmon, *m; @@ -449,9 +482,12 @@ arrange(Monitor *m) wlr_scene_node_set_enabled(&m->fullscreen_bg->node, (c = focustop(m)) && c->isfullscreen); - if (m && m->lt[m->sellt]->arrange) + strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, LENGTH(m->ltsymbol)); + + if (m->lt[m->sellt]->arrange) m->lt[m->sellt]->arrange(m); motionnotify(0); + checkidleinhibitor(NULL); } void @@ -505,8 +541,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; @@ -545,6 +581,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))) @@ -559,11 +599,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 @@ -594,13 +633,13 @@ chvt(const Arg *arg) void checkidleinhibitor(struct wlr_surface *exclude) { - int inhibited = 0; + int inhibited = 0, unused_lx, unused_ly; struct wlr_idle_inhibitor_v1 *inhibitor; wl_list_for_each(inhibitor, &idle_inhibit_mgr->inhibitors, link) { struct wlr_surface *surface = wlr_surface_get_root_surface(inhibitor->surface); struct wlr_scene_tree *tree = surface->data; - if (bypass_surface_visibility || (exclude != surface - && tree->node.enabled)) { + if (exclude != surface && (bypass_surface_visibility || (!tree + || wlr_scene_node_coords(&tree->node, &unused_lx, &unused_ly)))) { inhibited = 1; break; } @@ -636,6 +675,7 @@ cleanupkeyboard(struct wl_listener *listener, void *data) { Keyboard *kb = wl_container_of(listener, kb, destroy); + wl_event_source_remove(kb->key_repeat_source); wl_list_remove(&kb->link); wl_list_remove(&kb->modifiers.link); wl_list_remove(&kb->key.link); @@ -731,12 +771,22 @@ 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)) + c->isfloating ? resize(c, c->geom, 1) : 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->resize = 0; } +void +createdecoration(struct wl_listener *listener, void *data) +{ + struct wlr_xdg_toplevel_decoration_v1 *dec = data; + wlr_xdg_toplevel_decoration_v1_set_mode(dec, WLR_XDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); +} + void createidleinhibitor(struct wl_listener *listener, void *data) { @@ -771,6 +821,9 @@ createkeyboard(struct wlr_keyboard *keyboard) wlr_seat_set_keyboard(seat, keyboard); + kb->key_repeat_source = wl_event_loop_add_timer( + wl_display_get_event_loop(dpy), keyrepeat, kb); + /* And add the keyboard to our list of keyboards */ wl_list_insert(&keyboards, &kb->link); } @@ -785,8 +838,10 @@ createlayersurface(struct wl_listener *listener, void *data) if (!wlr_layer_surface->output) wlr_layer_surface->output = selmon ? selmon->wlr_output : NULL; - if (!wlr_layer_surface->output) + if (!wlr_layer_surface->output) { wlr_layer_surface_v1_destroy(wlr_layer_surface); + return; + } layersurface = ecalloc(1, sizeof(LayerSurface)); layersurface->type = LayerShell; @@ -824,6 +879,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) { @@ -849,6 +923,8 @@ createmon(struct wl_listener *listener, void *data) wlr_xcursor_manager_load(cursor_mgr, r->scale); m->lt[0] = m->lt[1] = r->lt; wlr_output_set_transform(wlr_output, r->rr); + m->m.x = r->x; + m->m.y = r->y; break; } } @@ -894,7 +970,11 @@ createmon(struct wl_listener *listener, void *data) * output (such as DPI, scale factor, manufacturer, etc). */ m->scene_output = wlr_scene_output_create(scene, wlr_output); - wlr_output_layout_add_auto(output_layout, wlr_output); + if (m->m.x < 0 || m->m.y < 0) + wlr_output_layout_add_auto(output_layout, wlr_output); + else + wlr_output_layout_add(output_layout, wlr_output, m->m.x, m->m.y); + strncpy(m->ltsymbol, m->lt[m->sellt]->symbol, LENGTH(m->ltsymbol)); } void @@ -906,22 +986,21 @@ createnotify(struct wl_listener *listener, void *data) * If you want to do something tricky with popups you should check if * its parent is wlr_xdg_shell or wlr_layer_shell */ struct wlr_xdg_surface *xdg_surface = data; - Client *c; + Client *c = NULL; + LayerSurface *l = NULL; if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_POPUP) { struct wlr_box box; - LayerSurface *l = toplevel_from_popup(xdg_surface->popup); - if (!xdg_surface->popup->parent) + int type = toplevel_from_wlr_surface(xdg_surface->surface, &c, &l); + if (!xdg_surface->popup->parent || type < 0) return; xdg_surface->surface->data = wlr_scene_xdg_surface_create( xdg_surface->popup->parent->data, xdg_surface); - /* Probably the check of `l` is useless, the only thing that can be NULL - * is its monitor */ - if (!l || !l->mon) + if ((l && !l->mon) || (c && !c->mon)) return; - box = l->type == LayerShell ? l->mon->m : l->mon->w; - box.x -= l->geom.x; - box.y -= l->geom.y; + box = type == LayerShell ? l->mon->m : c->mon->w; + box.x -= (type == LayerShell ? l->geom.x : c->geom.x); + box.y -= (type == LayerShell ? l->geom.y : c->geom.y); wlr_xdg_popup_unconstrain_from_box(xdg_surface->popup, &box); return; } else if (xdg_surface->role == WLR_XDG_SURFACE_ROLE_NONE) @@ -946,7 +1025,7 @@ void createpointer(struct wlr_pointer *pointer) { if (wlr_input_device_is_libinput(&pointer->base)) { - struct libinput_device *libinput_device = (struct libinput_device*) + struct libinput_device *libinput_device = (struct libinput_device*) wlr_libinput_get_device_handle(&pointer->base); if (libinput_device_config_tap_get_finger_count(libinput_device)) { @@ -970,7 +1049,7 @@ createpointer(struct wlr_pointer *pointer) if (libinput_device_config_scroll_get_methods(libinput_device) != LIBINPUT_CONFIG_SCROLL_NO_SCROLL) libinput_device_config_scroll_set_method (libinput_device, scroll_method); - + if (libinput_device_config_click_get_methods(libinput_device) != LIBINPUT_CONFIG_CLICK_METHOD_NONE) libinput_device_config_click_set_method (libinput_device, click_method); @@ -1003,7 +1082,7 @@ destroydragicon(struct wl_listener *listener, void *data) struct wlr_drag_icon *icon = data; wlr_scene_node_destroy(icon->data); /* Focus enter isn't sent during drag, so refocus the focused node. */ - focusclient(selclient(), 1); + focusclient(focustop(selmon), 1); motionnotify(0); } @@ -1029,6 +1108,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) { @@ -1049,16 +1171,30 @@ 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) { struct wlr_output *next; - if (wlr_output_layout_get(output_layout, selmon->wlr_output) - && (next = wlr_output_layout_adjacent_output(output_layout, + if (!wlr_output_layout_get(output_layout, selmon->wlr_output)) + return selmon; + if ((next = wlr_output_layout_adjacent_output(output_layout, dir, selmon->wlr_output, selmon->m.x, selmon->m.y))) return next->data; - if (wlr_output_layout_get(output_layout, selmon->wlr_output) - && (next = wlr_output_layout_farthest_output(output_layout, + if ((next = wlr_output_layout_farthest_output(output_layout, dir ^ (WLR_DIRECTION_LEFT|WLR_DIRECTION_RIGHT), selmon->wlr_output, selmon->m.x, selmon->m.y))) return next->data; @@ -1069,7 +1205,12 @@ void focusclient(Client *c, int lift) { struct wlr_surface *old = seat->keyboard_state.focused_surface; - int i; + int i, unused_lx, unused_ly, old_client_type; + Client *old_c = NULL; + LayerSurface *old_l = NULL; + + if (locked) + return; /* Raise client in stacking order if requested */ if (c && lift) @@ -1078,6 +1219,12 @@ focusclient(Client *c, int lift) if (c && client_surface(c) == old) return; + if ((old_client_type = toplevel_from_wlr_surface(old, &old_c, &old_l)) == XDGShell) { + struct wlr_xdg_popup *popup, *tmp; + wl_list_for_each_safe(popup, tmp, &old_c->surface.xdg->popups, link) + wlr_xdg_popup_destroy(popup); + } + /* Put the new client atop the focus stack and select its monitor */ if (c && !client_is_unmanaged(c)) { wl_list_remove(&c->flink); @@ -1098,29 +1245,22 @@ focusclient(Client *c, int lift) /* If an overlay is focused, don't focus or activate the client, * but only update its position in fstack to render its border with focuscolor * and focus it after the overlay is closed. */ - Client *w = client_from_wlr_surface(old); - if (wlr_surface_is_layer_surface(old)) { - struct wlr_layer_surface_v1 *wlr_layer_surface = - wlr_layer_surface_v1_from_wlr_surface(old); - - if (wlr_layer_surface && ((LayerSurface *)wlr_layer_surface->data)->mapped - && (wlr_layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_TOP - || wlr_layer_surface->current.layer == ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY)) - return; - } else if (w && w == exclusive_focus && client_wants_focus(w)) { + if (old_client_type == LayerShell && wlr_scene_node_coords( + &old_l->scene->node, &unused_lx, &unused_ly) + && old_l->layer_surface->current.layer >= ZWLR_LAYER_SHELL_V1_LAYER_TOP) { + return; + } else if (old_c && old_c == exclusive_focus && client_wants_focus(old_c)) { return; /* Don't deactivate old client if the new one wants focus, as this causes issues with winecfg * and probably other clients */ - } else if (w && !client_is_unmanaged(w) && (!c || !client_wants_focus(c))) { + } else if (old_c && !client_is_unmanaged(old_c) && (!c || !client_wants_focus(c))) { for (i = 0; i < 4; i++) - wlr_scene_rect_set_color(w->border[i], bordercolor); + wlr_scene_rect_set_color(old_c->border[i], bordercolor); client_activate_surface(old, 0); } } - printstatus(); - checkidleinhibitor(NULL); if (!c) { /* With no client, all we have left is to clear focus */ @@ -1153,22 +1293,22 @@ void focusstack(const Arg *arg) { /* Focus the next or previous client (in tiling order) on selmon */ - Client *c, *sel = selclient(); + Client *c, *sel = focustop(selmon); if (!sel || sel->isfullscreen) return; if (arg->i > 0) { wl_list_for_each(c, &sel->link, link) { if (&c->link == &clients) - continue; /* wrap past the sentinel node */ + continue; /* wrap past the sentinel node */ if (VISIBLEON(c, selmon)) - break; /* found it */ + break; /* found it */ } } else { wl_list_for_each_reverse(c, &sel->link, link) { if (&c->link == &clients) - continue; /* wrap past the sentinel node */ + continue; /* wrap past the sentinel node */ if (VISIBLEON(c, selmon)) - break; /* found it */ + break; /* found it */ } } /* If only one client is visible on selmon, then c == sel */ @@ -1276,11 +1416,22 @@ 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; + if (handled && kb->wlr_keyboard->repeat_info.delay > 0) { + kb->mods = mods; + kb->keysyms = syms; + kb->nsyms = nsyms; + wl_event_source_timer_update(kb->key_repeat_source, + kb->wlr_keyboard->repeat_info.delay); + } else { + kb->nsyms = 0; + wl_event_source_timer_update(kb->key_repeat_source, 0); + } + if (!handled) { /* Pass unhandled keycodes along to the client. */ wlr_seat_set_keyboard(seat, kb->wlr_keyboard); @@ -1307,14 +1458,55 @@ keypressmod(struct wl_listener *listener, void *data) &kb->wlr_keyboard->modifiers); } +int +keyrepeat(void *data) +{ + Keyboard *kb = data; + int i; + if (kb->nsyms && kb->wlr_keyboard->repeat_info.rate > 0) { + wl_event_source_timer_update(kb->key_repeat_source, + 1000 / kb->wlr_keyboard->repeat_info.rate); + + for (i = 0; i < kb->nsyms; i++) + keybinding(kb->mods, kb->keysyms[i]); + } + + return 0; +} + void killclient(const Arg *arg) { - Client *sel = selclient(); + Client *sel = focustop(selmon); if (sel) 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) { @@ -1333,6 +1525,7 @@ mapnotify(struct wl_listener *listener, void *data) /* Create scene tree for this client and its border */ c->scene = wlr_scene_tree_create(layers[LyrTile]); + wlr_scene_node_set_enabled(&c->scene->node, c->type != XDGShell); c->scene_surface = c->type == XDGShell ? wlr_scene_xdg_surface_create(c->scene, c->surface.xdg) : wlr_scene_subsurface_tree_create(c->scene, client_surface(c)); @@ -1344,7 +1537,6 @@ mapnotify(struct wl_listener *listener, void *data) } c->scene->node.data = c->scene_surface->node.data = c; -#ifdef XWAYLAND /* Handle unmanaged clients first so we can return prior create borders */ if (client_is_unmanaged(c)) { client_get_geometry(c, &c->geom); @@ -1358,12 +1550,10 @@ mapnotify(struct wl_listener *listener, void *data) } goto unset_fullscreen; } -#endif for (i = 0; i < 4; i++) { c->border[i] = wlr_scene_rect_create(c->scene, 0, 0, bordercolor); c->border[i]->node.data = c; - wlr_scene_rect_set_color(c->border[i], bordercolor); } /* Initialize client geometry with room for border */ @@ -1393,7 +1583,7 @@ mapnotify(struct wl_listener *listener, void *data) unset_fullscreen: m = c->mon ? c->mon : xytomon(c->geom.x, c->geom.y); wl_list_for_each(w, &clients, link) - if (w != c && w->isfullscreen && VISIBLEON(w, m)) + if (w != c && w->isfullscreen && m == w->mon && (w->tags & c->tags)) setfullscreen(w, 0); } @@ -1413,12 +1603,16 @@ void monocle(Monitor *m) { Client *c; + int n = 0; wl_list_for_each(c, &clients, link) { if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) continue; resize(c, m->w, 0); + n++; } + if (n) + snprintf(m->ltsymbol, LENGTH(m->ltsymbol), "[%d]", n); if ((c = focustop(m))) wlr_scene_node_raise_to_top(&c->scene->node); } @@ -1441,8 +1635,9 @@ void motionnotify(uint32_t time) { double sx = 0, sy = 0; - Client *c = NULL; - LayerSurface *l; + Client *c = NULL, *w = NULL; + LayerSurface *l = NULL; + int type; struct wlr_surface *surface = NULL; struct wlr_drag_icon *icon; @@ -1475,11 +1670,12 @@ motionnotify(uint32_t time) xytonode(cursor->x, cursor->y, &surface, &c, NULL, &sx, &sy); if (cursor_mode == CurPressed && !seat->drag) { - if ((l = toplevel_from_wlr_layer_surface( - seat->pointer_state.focused_surface))) { + if ((type = toplevel_from_wlr_surface( + seat->pointer_state.focused_surface, &w, &l)) >= 0) { + c = w; surface = seat->pointer_state.focused_surface; - sx = cursor->x - l->geom.x; - sy = cursor->y - l->geom.y; + sx = cursor->x - (type == LayerShell ? l->geom.x : w->geom.x); + sy = cursor->y - (type == LayerShell ? l->geom.y : w->geom.y); } } @@ -1548,7 +1744,7 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test) { /* * Called when a client such as wlr-randr requests a change in output - * configuration. This is only one way that the layout can be changed, + * configuration. This is only one way that the layout can be changed, * so any Monitor information should be updated by updatemons() after an * output_layout.change event, not here. */ @@ -1639,7 +1835,8 @@ printstatus(void) { Monitor *m = NULL; Client *c; - unsigned int occ, urg, sel; + uint32_t occ, urg, sel; + const char *appid, *title; wl_list_for_each(m, &mons, link) { occ = urg = 0; @@ -1651,12 +1848,16 @@ printstatus(void) urg |= c->tags; } if ((c = focustop(m))) { - printf("%s title %s\n", m->wlr_output->name, client_get_title(c)); + title = client_get_title(c); + appid = client_get_appid(c); + printf("%s title %s\n", m->wlr_output->name, title ? title : broken); + printf("%s appid %s\n", m->wlr_output->name, appid ? appid : broken); printf("%s fullscreen %u\n", m->wlr_output->name, c->isfullscreen); printf("%s floating %u\n", m->wlr_output->name, c->isfloating); sel = c->tags; } else { printf("%s title \n", m->wlr_output->name); + printf("%s appid \n", m->wlr_output->name); printf("%s fullscreen \n", m->wlr_output->name); printf("%s floating \n", m->wlr_output->name); sel = 0; @@ -1665,8 +1866,9 @@ printstatus(void) printf("%s selmon %u\n", m->wlr_output->name, m == selmon); printf("%s tags %u %u %u %u\n", m->wlr_output->name, occ, m->tagset[m->seltags], sel, urg); - printf("%s layout %s\n", m->wlr_output->name, m->lt[m->sellt]->symbol); + printf("%s layout %s\n", m->wlr_output->name, m->ltsymbol); } + fflush(stdout); } void @@ -1693,10 +1895,10 @@ rendermon(struct wl_listener *listener, void *data) /* 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)) + if (c->resize && !c->isfloating && client_is_rendered_on_mon(c, m) && !client_is_stopped(c)) goto skip; - if (!wlr_scene_output_commit(m->scene_output)) - return; + wlr_scene_output_commit(m->scene_output); + skip: /* Let clients know a frame has been rendered */ clock_gettime(CLOCK_MONOTONIC, &now); @@ -1734,7 +1936,7 @@ resize(Client *c, struct wlr_box geo, int interact) wlr_scene_node_set_position(&c->border[2]->node, 0, c->bw); wlr_scene_node_set_position(&c->border[3]->node, c->geom.width - c->bw, c->bw); - /* wlroots makes this a no-op if size hasn't changed */ + /* this is a no-op if size hasn't changed */ c->resize = client_set_size(c, c->geom.width - 2 * c->bw, c->geom.height - 2 * c->bw); } @@ -1744,6 +1946,8 @@ run(char *startup_cmd) { /* Add a Unix socket to the Wayland display. */ const char *socket = wl_display_add_socket_auto(dpy); + struct sigaction sa = {.sa_flags = SA_RESTART, .sa_handler = SIG_IGN}; + sigemptyset(&sa.sa_mask); if (!socket) die("startup: display_add_socket_auto"); setenv("WAYLAND_DISPLAY", socket, 1); @@ -1772,7 +1976,7 @@ run(char *startup_cmd) close(piperw[0]); } /* If nobody is reading the status output, don't terminate */ - signal(SIGPIPE, SIG_IGN); + sigaction(SIGPIPE, &sa, NULL); printstatus(); /* At this point the outputs are initialized, choose initial selmon based on @@ -1780,7 +1984,7 @@ run(char *startup_cmd) selmon = xytomon(cursor->x, cursor->y); /* TODO hack to get cursor to display in its initial location (100, 100) - * instead of (0, 0) and then jumping. still may not be fully + * instead of (0, 0) and then jumping. still may not be fully * initialized, as the image/coordinates are not transformed for the * monitor when displayed here */ wlr_cursor_warp_closest(cursor, NULL, cursor->x, cursor->y); @@ -1793,15 +1997,6 @@ run(char *startup_cmd) wl_display_run(dpy); } -Client * -selclient(void) -{ - Client *c = wl_container_of(fstack.next, c, flink); - if (wl_list_empty(&fstack) || !VISIBLEON(c, selmon)) - return NULL; - return c; -} - void setcursor(struct wl_listener *listener, void *data) { @@ -1864,7 +2059,7 @@ setlayout(const Arg *arg) selmon->sellt ^= 1; if (arg && arg->v) selmon->lt[selmon->sellt] = (Layout *)arg->v; - /* TODO change layout symbol? */ + strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, LENGTH(selmon->ltsymbol)); arrange(selmon); printstatus(); } @@ -1885,7 +2080,7 @@ setmfact(const Arg *arg) } void -setmon(Client *c, Monitor *m, unsigned int newtags) +setmon(Client *c, Monitor *m, uint32_t newtags) { Monitor *oldmon = c->mon; @@ -1934,22 +2129,19 @@ setsel(struct wl_listener *listener, void *data) void setup(void) { - /* Force line-buffered stdout */ - setvbuf(stdout, NULL, _IOLBF, 0); + /* Set up signal handlers */ + struct sigaction sa = {.sa_flags = SA_RESTART, .sa_handler = sigchld}; + sigemptyset(&sa.sa_mask); + sigaction(SIGCHLD, &sa, NULL); + + sa.sa_handler = quitsignal; + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); /* The Wayland display is managed by libwayland. It handles accepting * clients from the Unix socket, manging Wayland globals, and so on. */ dpy = wl_display_create(); - /* Set up signal handlers */ -#ifdef XWAYLAND - sigchld(0); -#else - signal(SIGCHLD, SIG_IGN); -#endif - signal(SIGINT, quitsignal); - signal(SIGTERM, quitsignal); - /* The backend is a wlroots feature which abstracts the underlying input and * output hardware. The autocreate option will choose the most suitable * backend based on the current environment, such as opening an X11 window @@ -1971,6 +2163,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))) @@ -2035,12 +2228,19 @@ 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( wlr_server_decoration_manager_create(dpy), WLR_SERVER_DECORATION_MANAGER_MODE_SERVER); - wlr_xdg_decoration_manager_v1_create(dpy); + xdg_decoration_mgr = wlr_xdg_decoration_manager_v1_create(dpy); + wl_signal_add(&xdg_decoration_mgr->events.new_toplevel_decoration, &new_xdg_decoration); /* * Creates a cursor, which is a wlroots utility for tracking the cursor @@ -2054,6 +2254,7 @@ setup(void) * images are available at all scale factors on the screen (necessary for * HiDPI support). Scaled cursors will be loaded with each output. */ cursor_mgr = wlr_xcursor_manager_create(NULL, 24); + setenv("XCURSOR_SIZE", "24", 1); /* * wlr_cursor *only* displays an image on screen. It does not move around @@ -2114,6 +2315,28 @@ setup(void) #endif } +void +sigchld(int unused) +{ +#ifdef XWAYLAND + siginfo_t in; + /* We should be able to remove this function in favor of a simple + * struct sigaction sa = {.sa_handler = SIG_IGN}; + * sigaction(SIGCHLD, &sa, NULL); + * but the Xwayland implementation in wlroots currently prevents us from + * setting our own disposition for SIGCHLD. + */ + /* WNOWAIT leaves the child in a waitable state, in case this is the + * XWayland process + */ + while (!waitid(P_ALL, 0, &in, WEXITED|WNOHANG|WNOWAIT) && in.si_pid + && (!xwayland || in.si_pid != xwayland->server->pid)) + waitpid(in.si_pid, NULL, 0); +#else + while (waitpid(-1, NULL, WNOHANG) > 0); +#endif +} + void spawn(const Arg *arg) { @@ -2141,7 +2364,7 @@ startdrag(struct wl_listener *listener, void *data) void tag(const Arg *arg) { - Client *sel = selclient(); + Client *sel = focustop(selmon); if (sel && arg->ui & TAGMASK) { sel->tags = arg->ui & TAGMASK; focusclient(focustop(selmon), 1); @@ -2153,7 +2376,7 @@ tag(const Arg *arg) void tagmon(const Arg *arg) { - Client *sel = selclient(); + Client *sel = focustop(selmon); if (sel) setmon(sel, dirtomon(arg->i), 0); } @@ -2194,7 +2417,7 @@ tile(Monitor *m) void togglefloating(const Arg *arg) { - Client *sel = selclient(); + Client *sel = focustop(selmon); /* return if fullscreen */ if (sel && !sel->isfullscreen) setfloating(sel, !sel->isfloating); @@ -2203,7 +2426,7 @@ togglefloating(const Arg *arg) void togglefullscreen(const Arg *arg) { - Client *sel = selclient(); + Client *sel = focustop(selmon); if (sel) setfullscreen(sel, !sel->isfullscreen); } @@ -2211,8 +2434,8 @@ togglefullscreen(const Arg *arg) void toggletag(const Arg *arg) { - unsigned int newtags; - Client *sel = selclient(); + uint32_t newtags; + Client *sel = focustop(selmon); if (!sel) return; newtags = sel->tags ^ (arg->ui & TAGMASK); @@ -2227,7 +2450,7 @@ toggletag(const Arg *arg) void toggleview(const Arg *arg) { - unsigned int newtagset = selmon ? selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK) : 0; + uint32_t newtagset = selmon ? selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK) : 0; if (newtagset) { selmon->tagset[selmon->seltags] = newtagset; @@ -2237,6 +2460,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) { @@ -2251,7 +2481,7 @@ unmaplayersurfacenotify(struct wl_listener *listener, void *data) arrangelayers(layersurface->mon); if (layersurface->layer_surface->surface == seat->keyboard_state.focused_surface) - focusclient(selclient(), 1); + focusclient(focustop(selmon), 1); motionnotify(0); } @@ -2269,7 +2499,7 @@ unmapnotify(struct wl_listener *listener, void *data) if (c == exclusive_focus) exclusive_focus = NULL; if (client_surface(c) == seat->keyboard_state.focused_surface) - focusclient(selclient(), 1); + focusclient(focustop(selmon), 1); } else { wl_list_remove(&c->link); setmon(c, NULL, 0); @@ -2287,7 +2517,7 @@ updatemons(struct wl_listener *listener, void *data) { /* * Called whenever the output layout changes: adding or removing a - * monitor, changing an output's mode or position, etc. This is where + * monitor, changing an output's mode or position, etc. This is where * the change officially happens and we update geometry, window * positions, focus, and the stored configuration in wlroots' * output-manager implementation. @@ -2315,8 +2545,14 @@ updatemons(struct wl_listener *listener, void *data) if (m->wlr_output->enabled && !wlr_output_layout_get(output_layout, m->wlr_output)) 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); + + /* Make sure the clients are hidden when dwl is locked */ + wlr_scene_node_set_position(&locked_bg->node, sgeom.x, sgeom.y); + wlr_scene_rect_set_size(locked_bg, sgeom.width, sgeom.height); + wl_list_for_each(m, &mons, link) { if (!m->wlr_output->enabled) continue; @@ -2326,24 +2562,39 @@ updatemons(struct wl_listener *listener, void *data) wlr_output_layout_get_box(output_layout, m->wlr_output, &(m->m)); wlr_output_layout_get_box(output_layout, m->wlr_output, &(m->w)); wlr_scene_output_set_position(m->scene_output, m->m.x, m->m.y); + + 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); + } + /* Calculate the effective monitor geometry to use for clients */ arrangelayers(m); /* Don't move clients to the left output when plugging monitors */ arrange(m); - 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); - 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); + focusclient(focustop(selmon), 1); + if (selmon->lock_surface) { + client_notify_enter(selmon->lock_surface->surface, + wlr_seat_get_keyboard(seat)); + client_activate_surface(selmon->lock_surface->surface, 1); + } + } wlr_output_manager_v1_set_configuration(output_mgr, config); } @@ -2360,8 +2611,9 @@ void urgent(struct wl_listener *listener, void *data) { struct wlr_xdg_activation_v1_request_activate_event *event = data; - Client *c = client_from_wlr_surface(event->surface); - if (c && c != selclient()) { + Client *c = NULL; + toplevel_from_wlr_surface(event->surface, &c, NULL); + if (c && c != focustop(selmon)) { c->isurgent = 1; printstatus(); } @@ -2403,7 +2655,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))) { @@ -2431,7 +2683,7 @@ xytonode(double x, double y, struct wlr_surface **psurface, void zoom(const Arg *arg) { - Client *c, *sel = selclient(); + Client *c, *sel = focustop(selmon); if (!sel || !selmon || !selmon->lt[selmon->sellt]->arrange || sel->isfloating) return; @@ -2525,31 +2777,12 @@ void sethints(struct wl_listener *listener, void *data) { Client *c = wl_container_of(listener, c, set_hints); - if (c != selclient()) { + if (c != focustop(selmon)) { c->isurgent = xcb_icccm_wm_hints_get_urgency(c->surface.xwayland->hints); printstatus(); } } -void -sigchld(int unused) -{ - siginfo_t in; - /* We should be able to remove this function in favor of a simple - * signal(SIGCHLD, SIG_IGN); - * but the Xwayland implementation in wlroots currently prevents us from - * setting our own disposition for SIGCHLD. - */ - if (signal(SIGCHLD, sigchld) == SIG_ERR) - die("can't install SIGCHLD handler:"); - /* WNOWAIT leaves the child in a waitable state, in case this is the - * XWayland process - */ - while (!waitid(P_ALL, 0, &in, WEXITED|WNOHANG|WNOWAIT) && in.si_pid - && (!xwayland || in.si_pid != xwayland->server->pid)) - waitpid(in.si_pid, NULL, 0); -} - void xwaylandready(struct wl_listener *listener, void *data) { @@ -2561,7 +2794,7 @@ xwaylandready(struct wl_listener *listener, void *data) return; } - /* Collect atoms we are interested in. If getatom returns 0, we will + /* Collect atoms we are interested in. If getatom returns 0, we will * not detect that window type. */ netatom[NetWMWindowTypeDialog] = getatom(xc, "_NET_WM_WINDOW_TYPE_DIALOG"); netatom[NetWMWindowTypeSplash] = getatom(xc, "_NET_WM_WINDOW_TYPE_SPLASH"); diff --git a/push.c b/push.c index 26ed462..323c317 100644 --- a/push.c +++ b/push.c @@ -24,7 +24,7 @@ prevtiled(Client *sel) { static void pushup(const Arg *arg) { - Client *sel = selclient(); + Client *sel = focustop(selmon); Client *c; if(!sel || sel->isfloating) @@ -44,7 +44,7 @@ pushup(const Arg *arg) { static void pushdown(const Arg *arg) { - Client *sel = selclient(); + Client *sel = focustop(selmon); Client *c; if(!sel || sel->isfloating)