diff --git a/Makefile b/Makefile index 5320e42..6cde460 100644 --- a/Makefile +++ b/Makefile @@ -5,7 +5,7 @@ include config.mk # flags for compiling DWLCPPFLAGS = -I. -DWLR_USE_UNSTABLE -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XWAYLAND) -DWLDEVCFLAGS = -pedantic -Wall -Wextra -Wdeclaration-after-statement -Wno-unused-parameter -Wno-sign-compare -Wshadow -Wunused-macros\ +DWLDEVCFLAGS = -g -pedantic -Wall -Wextra -Wdeclaration-after-statement -Wno-unused-parameter -Wno-sign-compare -Wshadow -Wunused-macros\ -Werror=strict-prototypes -Werror=implicit -Werror=return-type -Werror=incompatible-pointer-types # CFLAGS / LDFLAGS @@ -16,7 +16,7 @@ LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` $(LIBS) all: dwl dwl: dwl.o util.o $(CC) dwl.o util.o $(LDLIBS) $(LDFLAGS) $(DWLCFLAGS) -o $@ -dwl.o: dwl.c config.mk config.h client.h xdg-shell-protocol.h wlr-layer-shell-unstable-v1-protocol.h +dwl.o: dwl.c config.mk config.h client.h cursor-shape-v1-protocol.h xdg-shell-protocol.h wlr-layer-shell-unstable-v1-protocol.h util.o: util.c util.h # wayland-scanner is a tool which generates C headers and rigging for Wayland @@ -31,6 +31,9 @@ xdg-shell-protocol.h: wlr-layer-shell-unstable-v1-protocol.h: $(WAYLAND_SCANNER) server-header \ protocols/wlr-layer-shell-unstable-v1.xml $@ +cursor-shape-v1-protocol.h: + $(WAYLAND_SCANNER) server-header \ + $(WAYLAND_PROTOCOLS)/staging/cursor-shape/cursor-shape-v1.xml $@ config.h: cp config.def.h $@ diff --git a/client.h b/client.h index cf1ed85..71c7d76 100644 --- a/client.h +++ b/client.h @@ -11,34 +11,8 @@ client_is_x11(Client *c) { #ifdef XWAYLAND return c->type == X11Managed || c->type == X11Unmanaged; -#else +#endif return 0; -#endif -} - -static inline void -client_get_size_hints(Client *c, struct wlr_box *max, struct wlr_box *min) -{ - struct wlr_xdg_toplevel *toplevel; - struct wlr_xdg_toplevel_state *state; -#ifdef XWAYLAND - if (client_is_x11(c)) { - xcb_size_hints_t *size_hints = c->surface.xwayland->size_hints; - if (size_hints) { - max->width = size_hints->max_width; - max->height = size_hints->max_height; - min->width = size_hints->min_width; - min->height = size_hints->min_height; - } - return; - } -#endif - toplevel = c->surface.xdg->toplevel; - state = &toplevel->current; - max->width = state->max_width; - max->height = state->max_height; - min->width = state->min_width; - min->height = state->min_height; } static inline struct wlr_surface * @@ -54,7 +28,7 @@ client_surface(Client *c) static inline int toplevel_from_wlr_surface(struct wlr_surface *s, Client **pc, LayerSurface **pl) { - struct wlr_xdg_surface *xdg_surface; + struct wlr_xdg_surface *xdg_surface, *tmp_xdg_surface; struct wlr_surface *root_surface; struct wlr_layer_surface_v1 *layer_surface; Client *c = NULL; @@ -65,44 +39,44 @@ toplevel_from_wlr_surface(struct wlr_surface *s, Client **pc, LayerSurface **pl) #endif if (!s) - return type; + return -1; 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))) { + if ((xsurface = wlr_xwayland_surface_try_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))) { + if ((layer_surface = wlr_layer_surface_v1_try_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: + xdg_surface = wlr_xdg_surface_try_from_wlr_surface(root_surface); + while (xdg_surface) { + tmp_xdg_surface = NULL; + switch (xdg_surface->role) { + case WLR_XDG_SURFACE_ROLE_POPUP: + if (!xdg_surface->popup || !xdg_surface->popup->parent) return -1; - } + + tmp_xdg_surface = wlr_xdg_surface_try_from_wlr_surface(xdg_surface->popup->parent); + + if (!tmp_xdg_surface) + return toplevel_from_wlr_surface(xdg_surface->popup->parent, pc, pl); + + xdg_surface = tmp_xdg_surface; + break; + case WLR_XDG_SURFACE_ROLE_TOPLEVEL: + c = xdg_surface->data; + type = c->type; + goto end; + case WLR_XDG_SURFACE_ROLE_NONE: + return -1; } } @@ -118,19 +92,16 @@ end: static inline void client_activate_surface(struct wlr_surface *s, int activated) { - struct wlr_xdg_surface *surface; + struct wlr_xdg_toplevel *toplevel; #ifdef XWAYLAND struct wlr_xwayland_surface *xsurface; - if (wlr_surface_is_xwayland_surface(s) - && (xsurface = wlr_xwayland_surface_from_wlr_surface(s))) { + if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(s))) { wlr_xwayland_surface_activate(xsurface, activated); return; } #endif - if (wlr_surface_is_xdg_surface(s) - && (surface = wlr_xdg_surface_from_wlr_surface(s)) - && surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL) - wlr_xdg_toplevel_set_activated(surface->toplevel, activated); + if ((toplevel = wlr_xdg_toplevel_try_from_wlr_surface(s))) + wlr_xdg_toplevel_set_activated(toplevel, activated); } static inline uint32_t @@ -140,23 +111,16 @@ client_set_bounds(Client *c, int32_t width, int32_t height) if (client_is_x11(c)) return 0; #endif - if (c->surface.xdg->client->shell->version >= - XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION && width >= 0 && height >= 0) + if (wl_resource_get_version(c->surface.xdg->toplevel->resource) >= + XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION && width >= 0 && height >= 0 + && (c->bounds.width != width || c->bounds.height != height)) { + c->bounds.width = width; + c->bounds.height = height; return wlr_xdg_toplevel_set_bounds(c->surface.xdg->toplevel, width, height); + } return 0; } -static inline void -client_for_each_surface(Client *c, wlr_surface_iterator_func_t fn, void *data) -{ - wlr_surface_for_each_surface(client_surface(c), fn, data); -#ifdef XWAYLAND - if (client_is_x11(c)) - return; -#endif - wlr_xdg_surface_for_each_popup_surface(c->surface.xdg, fn, data); -} - static inline const char * client_get_appid(Client *c) { @@ -167,6 +131,27 @@ client_get_appid(Client *c) return c->surface.xdg->toplevel->app_id; } +static inline void +client_get_clip(Client *c, struct wlr_box *clip) +{ + struct wlr_box xdg_geom = {0}; + *clip = (struct wlr_box){ + .x = 0, + .y = 0, + .width = c->geom.width - c->bw, + .height = c->geom.height - c->bw, + }; + +#ifdef XWAYLAND + if (client_is_x11(c)) + return; +#endif + + wlr_xdg_surface_get_geometry(c->surface.xdg, &xdg_geom); + clip->x = xdg_geom.x; + clip->y = xdg_geom.y; +} + static inline void client_get_geometry(Client *c, struct wlr_box *geom) { @@ -192,7 +177,6 @@ client_get_parent(Client *c) #endif if (c->surface.xdg->toplevel->parent) toplevel_from_wlr_surface(c->surface.xdg->toplevel->parent->base->surface, &p, NULL); - return p; } @@ -209,35 +193,35 @@ client_get_title(Client *c) static inline int client_is_float_type(Client *c) { - struct wlr_box min = {0}, max = {0}; - client_get_size_hints(c, &max, &min); + struct wlr_xdg_toplevel *toplevel; + struct wlr_xdg_toplevel_state state; #ifdef XWAYLAND if (client_is_x11(c)) { struct wlr_xwayland_surface *surface = c->surface.xwayland; + xcb_size_hints_t *size_hints = surface->size_hints; + size_t i; if (surface->modal) return 1; - for (size_t i = 0; i < surface->window_type_len; i++) + for (i = 0; i < surface->window_type_len; i++) if (surface->window_type[i] == netatom[NetWMWindowTypeDialog] || surface->window_type[i] == netatom[NetWMWindowTypeSplash] || surface->window_type[i] == netatom[NetWMWindowTypeToolbar] || surface->window_type[i] == netatom[NetWMWindowTypeUtility]) return 1; + + return size_hints && size_hints->min_width > 0 && size_hints->min_height > 0 + && (size_hints->max_width == size_hints->min_width + || size_hints->max_height == size_hints->min_height); } #endif - return ((min.width > 0 || min.height > 0 || max.width > 0 || max.height > 0) - && (min.width == max.width || min.height == max.height)); -} -static inline int -client_is_mapped(Client *c) -{ -#ifdef XWAYLAND - if (client_is_x11(c)) - return c->surface.xwayland->mapped; -#endif - return c->surface.xdg->mapped; + toplevel = c->surface.xdg->toplevel; + state = toplevel->current; + return toplevel->parent || (state.min_width != 0 && state.min_height != 0 + && (state.min_width == state.max_width + || state.min_height == state.max_height)); } static inline int @@ -247,7 +231,8 @@ client_is_rendered_on_mon(Client *c, Monitor *m) * but rather actual displaying of the pixels. * Usually VISIBLEON suffices and is also faster. */ struct wlr_surface_output *s; - if (!c->scene->node.enabled) + int unused_lx, unused_ly; + if (!wlr_scene_node_coords(&c->scene->node, &unused_lx, &unused_ly)) return 0; wl_list_for_each(s, &client_surface(c)->current_outputs, link) if (s->output == m->wlr_output) @@ -366,18 +351,25 @@ client_set_tiled(Client *c, uint32_t edges) if (client_is_x11(c)) return; #endif - wlr_xdg_toplevel_set_tiled(c->surface.xdg->toplevel, edges); + if (wl_resource_get_version(c->surface.xdg->resource) + >= XDG_TOPLEVEL_STATE_TILED_RIGHT_SINCE_VERSION) { + wlr_xdg_toplevel_set_tiled(c->surface.xdg->toplevel, edges); + } else { + wlr_xdg_toplevel_set_maximized(c->surface.xdg->toplevel, edges != WLR_EDGE_NONE); + } } -static inline struct wlr_surface * -client_surface_at(Client *c, double cx, double cy, double *sx, double *sy) +static inline void +client_set_suspended(Client *c, int suspended) { #ifdef XWAYLAND - if (client_is_x11(c)) - return wlr_surface_surface_at(c->surface.xwayland->surface, - cx, cy, sx, sy); + if (client_is_x11(c)) { + wlr_xwayland_surface_set_withdrawn(c->surface.xwayland, suspended); + return; + } #endif - return wlr_xdg_surface_surface_at(c->surface.xdg, cx, cy, sx, sy); + + wlr_xdg_toplevel_set_suspended(c->surface.xdg->toplevel, suspended); } static inline int diff --git a/dwl.c b/dwl.c index b78ed02..ef27a1d 100644 --- a/dwl.c +++ b/dwl.c @@ -3,7 +3,6 @@ */ #include #include -#include #include #include #include @@ -18,15 +17,17 @@ #include #include #include +#include #include #include +#include +#include #include +#include #include -#include #include #include #include -#include #include #include #include @@ -69,7 +70,7 @@ #define END(A) ((A) + LENGTH(A)) #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) +#define LISTEN_STATIC(E, H) do { static struct wl_listener _l = {.notify = (H)}; wl_signal_add((E), &_l); } while (0) /* enums */ enum { CurNormal, CurPressed, CurMove, CurResize }; /* cursor */ @@ -117,8 +118,11 @@ typedef struct { struct wl_listener set_title; struct wl_listener fullscreen; struct wlr_box prev; /* layout-relative, includes border */ + struct wlr_box bounds; #ifdef XWAYLAND struct wl_listener activate; + struct wl_listener associate; + struct wl_listener dissociate; struct wl_listener configure; struct wl_listener set_hints; #endif @@ -179,6 +183,7 @@ struct Monitor { struct wlr_scene_rect *fullscreen_bg; /* See createmon() for info */ struct wl_listener frame; struct wl_listener destroy; + struct wl_listener request_state; struct wl_listener destroy_lock_surface; struct wlr_session_lock_surface_v1 *lock_surface; struct wlr_box m; /* monitor area, layout-relative */ @@ -189,6 +194,7 @@ struct Monitor { unsigned int sellt; uint32_t tagset[2]; double mfact; + int gamma_lut_changed; int nmaster; char ltsymbol[16]; }; @@ -286,11 +292,14 @@ static void printstatus(void); static void quit(const Arg *arg); static void rendermon(struct wl_listener *listener, void *data); static void requeststartdrag(struct wl_listener *listener, void *data); +static void requestmonstate(struct wl_listener *listener, void *data); static void resize(Client *c, struct wlr_box geo, int interact); static void run(char *startup_cmd); static void setcursor(struct wl_listener *listener, void *data); +static void setcursorshape(struct wl_listener *listener, void *data); static void setfloating(Client *c, int floating); static void setfullscreen(Client *c, int fullscreen); +static void setgamma(struct wl_listener *listener, void *data); static void setlayout(const Arg *arg); static void setmfact(const Arg *arg); static void setmon(Client *c, Monitor *m, uint32_t newtags); @@ -321,7 +330,6 @@ static void zoom(const Arg *arg); /* variables */ 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; @@ -335,19 +343,20 @@ static const int layermap[] = { LyrBg, LyrBottom, LyrTop, LyrOverlay }; static struct wlr_renderer *drw; static struct wlr_allocator *alloc; static struct wlr_compositor *compositor; +static struct wlr_session *session; 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; static struct wlr_idle_notifier_v1 *idle_notifier; static struct wlr_idle_inhibit_manager_v1 *idle_inhibit_mgr; -static struct wlr_input_inhibit_manager *input_inhibit_mgr; static struct wlr_layer_shell_v1 *layer_shell; static struct wlr_output_manager_v1 *output_mgr; +static struct wlr_gamma_control_manager_v1 *gamma_control_mgr; static struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard_mgr; +static struct wlr_cursor_shape_manager_v1 *cursor_shape_mgr; static struct wlr_cursor *cursor; static struct wlr_xcursor_manager *cursor_mgr; @@ -355,6 +364,7 @@ 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 wl_listener lock_listener = {.notify = locksession}; static struct wlr_seat *seat; static struct wl_list keyboards; @@ -367,42 +377,15 @@ static struct wlr_box sgeom; static struct wl_list mons; static Monitor *selmon; -/* global event handlers */ -static struct wl_listener cursor_axis = {.notify = axisnotify}; -static struct wl_listener cursor_button = {.notify = buttonpress}; -static struct wl_listener cursor_frame = {.notify = cursorframe}; -static struct wl_listener cursor_motion = {.notify = motionrelative}; -static struct wl_listener cursor_motion_absolute = {.notify = motionabsolute}; -static struct wl_listener drag_icon_destroy = {.notify = destroydragicon}; -static struct wl_listener idle_inhibitor_create = {.notify = createidleinhibitor}; -static struct wl_listener idle_inhibitor_destroy = {.notify = destroyidleinhibitor}; -static struct wl_listener layout_change = {.notify = updatemons}; -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}; -static struct wl_listener request_activate = {.notify = urgent}; -static struct wl_listener request_cursor = {.notify = setcursor}; -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); +static void associatex11(struct wl_listener *listener, void *data); static void configurex11(struct wl_listener *listener, void *data); static void createnotifyx11(struct wl_listener *listener, void *data); +static void dissociatex11(struct wl_listener *listener, void *data); static xcb_atom_t getatom(xcb_connection_t *xc, const char *name); static void sethints(struct wl_listener *listener, void *data); 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}; static struct wlr_xwayland *xwayland; static xcb_atom_t netatom[NetLast]; #endif @@ -417,19 +400,9 @@ static xcb_atom_t netatom[NetLast]; void applybounds(Client *c, struct wlr_box *bbox) { - if (!c->isfullscreen) { - struct wlr_box min = {0}, max = {0}; - client_get_size_hints(c, &max, &min); - /* 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 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 */ - c->geom.height = MIN(max.height + (2 * c->bw), c->geom.height); - } + /* set minimum possible */ + c->geom.width = MAX(1, c->geom.width); + c->geom.height = MAX(1, c->geom.height); if (c->geom.x >= bbox->x + bbox->width) c->geom.x = bbox->x + bbox->width - c->geom.width; @@ -475,9 +448,12 @@ void arrange(Monitor *m) { Client *c; - wl_list_for_each(c, &clients, link) - if (c->mon == m) + wl_list_for_each(c, &clients, link) { + if (c->mon == m) { wlr_scene_node_set_enabled(&c->scene->node, VISIBLEON(c, m)); + client_set_suspended(c, !VISIBLEON(c, m)); + } + } wlr_scene_node_set_enabled(&m->fullscreen_bg->node, (c = focustop(m)) && c->isfullscreen); @@ -559,7 +535,7 @@ axisnotify(struct wl_listener *listener, void *data) /* This event is forwarded by the cursor when a pointer emits an axis event, * for example when you move the scroll wheel. */ struct wlr_pointer_axis_event *event = data; - IDLE_NOTIFY_ACTIVITY; + wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); /* TODO: allow usage of scroll whell for mousebindings, it can be implemented * checking the event's orientation and the delta of the event */ /* Notify the client with pointer focus of the axis event. */ @@ -577,7 +553,7 @@ buttonpress(struct wl_listener *listener, void *data) Client *c; const Button *b; - IDLE_NOTIFY_ACTIVITY; + wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); switch (event->state) { case WLR_BUTTON_PRESSED: @@ -602,13 +578,10 @@ buttonpress(struct wl_listener *listener, void *data) break; case WLR_BUTTON_RELEASED: /* If you released any buttons, we exit interactive move/resize mode. */ + /* TODO should reset to the pointer focus's current setcursor */ if (!locked && cursor_mode != CurNormal && cursor_mode != CurPressed) { + wlr_cursor_set_xcursor(cursor, cursor_mgr, "default"); 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 - * a cursor surface */ - wlr_seat_pointer_clear_focus(seat); - motionnotify(0); /* Drop the window off on its new monitor */ selmon = xytomon(cursor->x, cursor->y); setmon(grabc, selmon, 0); @@ -627,7 +600,7 @@ buttonpress(struct wl_listener *listener, void *data) void chvt(const Arg *arg) { - wlr_session_change_vt(wlr_backend_get_session(backend), arg->ui); + wlr_session_change_vt(session, arg->ui); } void @@ -645,7 +618,6 @@ checkidleinhibitor(struct wlr_surface *exclude) } } - wlr_idle_set_enabled(idle, NULL, !inhibited); wlr_idle_notifier_v1_set_inhibited(idle_notifier, inhibited); } @@ -654,21 +626,19 @@ cleanup(void) { #ifdef XWAYLAND wlr_xwayland_destroy(xwayland); + xwayland = NULL; #endif wl_display_destroy_clients(dpy); if (child_pid > 0) { kill(child_pid, SIGTERM); waitpid(child_pid, NULL, 0); } - wlr_backend_destroy(backend); - wlr_scene_node_destroy(&scene->tree.node); - wlr_renderer_destroy(drw); - wlr_allocator_destroy(alloc); wlr_xcursor_manager_destroy(cursor_mgr); - wlr_cursor_destroy(cursor); wlr_output_layout_destroy(output_layout); - wlr_seat_destroy(seat); wl_display_destroy(dpy); + /* Destroy after the wayland display (when the monitors are already destroyed) + to avoid destroying them with an invalid scene output. */ + wlr_scene_node_destroy(&scene->tree.node); } void @@ -757,9 +727,9 @@ commitlayersurfacenotify(struct wl_listener *listener, void *data) wlr_scene_node_reparent(&layersurface->popups->node, layers[LyrTop]); if (wlr_layer_surface->current.committed == 0 - && layersurface->mapped == wlr_layer_surface->mapped) + && layersurface->mapped == wlr_layer_surface->surface->mapped) return; - layersurface->mapped = wlr_layer_surface->mapped; + layersurface->mapped = wlr_layer_surface->surface->mapped; arrangelayers(layersurface->mon); } @@ -769,6 +739,9 @@ commitnotify(struct wl_listener *listener, void *data) { Client *c = wl_container_of(listener, c, commit); + if (client_surface(c)->mapped) + resize(c, c->geom, (c->isfloating && !c->isfullscreen)); + /* mark a pending resize as completed */ if (c->resize && c->resize <= c->surface.xdg->current.configure_serial) c->resize = 0; @@ -785,7 +758,7 @@ void createidleinhibitor(struct wl_listener *listener, void *data) { struct wlr_idle_inhibitor_v1 *idle_inhibitor = data; - wl_signal_add(&idle_inhibitor->events.destroy, &idle_inhibitor_destroy); + LISTEN_STATIC(&idle_inhibitor->events.destroy, destroyidleinhibitor); checkidleinhibitor(NULL); } @@ -840,21 +813,19 @@ createlayersurface(struct wl_listener *listener, void *data) return; } - layersurface = ecalloc(1, sizeof(LayerSurface)); + layersurface = wlr_layer_surface->data = ecalloc(1, sizeof(LayerSurface)); layersurface->type = LayerShell; LISTEN(&wlr_layer_surface->surface->events.commit, &layersurface->surface_commit, commitlayersurfacenotify); LISTEN(&wlr_layer_surface->events.destroy, &layersurface->destroy, destroylayersurfacenotify); - LISTEN(&wlr_layer_surface->events.map, &layersurface->map, + LISTEN(&wlr_layer_surface->surface->events.map, &layersurface->map, maplayersurfacenotify); - LISTEN(&wlr_layer_surface->events.unmap, &layersurface->unmap, + LISTEN(&wlr_layer_surface->surface->events.unmap, &layersurface->unmap, unmaplayersurfacenotify); layersurface->layer_surface = wlr_layer_surface; layersurface->mon = wlr_layer_surface->output->data; - wlr_layer_surface->data = layersurface; - layersurface->scene_layer = wlr_scene_layer_surface_v1_create(l, wlr_layer_surface); layersurface->scene = layersurface->scene_layer->tree; layersurface->popups = wlr_layer_surface->surface->data = wlr_scene_tree_create(l); @@ -915,7 +886,6 @@ createmon(struct wl_listener *listener, void *data) m->mfact = r->mfact; m->nmaster = r->nmaster; wlr_output_set_scale(wlr_output, r->scale); - 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; @@ -933,16 +903,12 @@ createmon(struct wl_listener *listener, void *data) /* Set up event listeners */ LISTEN(&wlr_output->events.frame, &m->frame, rendermon); LISTEN(&wlr_output->events.destroy, &m->destroy, cleanupmon); + LISTEN(&wlr_output->events.request_state, &m->request_state, requestmonstate); wlr_output_enable(wlr_output, 1); if (!wlr_output_commit(wlr_output)) return; - /* Try to enable adaptive sync, note that not all monitors support it. - * wlr_output_commit() will deactivate it in case it cannot be enabled */ - wlr_output_enable_adaptive_sync(wlr_output, 1); - wlr_output_commit(wlr_output); - wl_list_insert(&mons, &m->link); printstatus(); @@ -1006,8 +972,12 @@ createnotify(struct wl_listener *listener, void *data) c->surface.xdg = xdg_surface; c->bw = borderpx; - LISTEN(&xdg_surface->events.map, &c->map, mapnotify); - LISTEN(&xdg_surface->events.unmap, &c->unmap, unmapnotify); + wlr_xdg_toplevel_set_wm_capabilities(xdg_surface->toplevel, + WLR_XDG_TOPLEVEL_WM_CAPABILITIES_FULLSCREEN); + + LISTEN(&xdg_surface->surface->events.commit, &c->commit, commitnotify); + LISTEN(&xdg_surface->surface->events.map, &c->map, mapnotify); + LISTEN(&xdg_surface->surface->events.unmap, &c->unmap, unmapnotify); LISTEN(&xdg_surface->events.destroy, &c->destroy, destroynotify); LISTEN(&xdg_surface->toplevel->events.set_title, &c->set_title, updatetitle); LISTEN(&xdg_surface->toplevel->events.request_fullscreen, &c->fullscreen, @@ -1074,8 +1044,6 @@ cursorframe(struct wl_listener *listener, void *data) void 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(focustop(selmon), 1); motionnotify(0); @@ -1150,20 +1118,25 @@ destroylocksurface(struct wl_listener *listener, void *data) void destroynotify(struct wl_listener *listener, void *data) { - /* Called when the surface is destroyed and should never be shown again. */ + /* Called when the xdg_toplevel is destroyed. */ Client *c = wl_container_of(listener, c, destroy); - wl_list_remove(&c->map.link); - wl_list_remove(&c->unmap.link); wl_list_remove(&c->destroy.link); wl_list_remove(&c->set_title.link); wl_list_remove(&c->fullscreen.link); #ifdef XWAYLAND if (c->type != XDGShell) { - wl_list_remove(&c->configure.link); - wl_list_remove(&c->set_hints.link); wl_list_remove(&c->activate.link); - } + wl_list_remove(&c->associate.link); + wl_list_remove(&c->configure.link); + wl_list_remove(&c->dissociate.link); + wl_list_remove(&c->set_hints.link); + } else #endif + { + wl_list_remove(&c->commit.link); + wl_list_remove(&c->map.link); + wl_list_remove(&c->unmap.link); + } free(c); } @@ -1177,8 +1150,8 @@ destroysessionlock(struct wl_listener *listener, void *data) void destroysessionmgr(struct wl_listener *listener, void *data) { - wl_list_remove(&session_lock_create_lock.link); - wl_list_remove(&session_lock_mgr_destroy.link); + wl_list_remove(&lock_listener.link); + wl_list_remove(&listener->link); } Monitor * @@ -1427,12 +1400,11 @@ keypress(struct wl_listener *listener, void *data) int handled = 0; uint32_t mods = wlr_keyboard_get_modifiers(kb->wlr_keyboard); - IDLE_NOTIFY_ACTIVITY; + wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); /* On _press_ if there is no active screen locker, * attempt to process a compositor keybinding. */ - if (!locked && !input_inhibit_mgr->active_inhibitor - && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) + if (!locked && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) for (i = 0; i < nsyms; i++) handled = keybinding(mods, syms[i]) || handled; @@ -1509,13 +1481,12 @@ locksession(struct wl_listener *listener, void *data) wlr_session_lock_v1_destroy(session_lock); return; } - lock = ecalloc(1, sizeof(*lock)); + lock = session_lock->data = 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); @@ -1540,17 +1511,11 @@ mapnotify(struct wl_listener *listener, void *data) int i; /* Create scene tree for this client and its border */ - c->scene = wlr_scene_tree_create(layers[LyrTile]); + c->scene = client_surface(c)->data = 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)); - if (client_surface(c)) { - client_surface(c)->data = c->scene; - /* Ideally we should do this in createnotify{,x11} but at that moment - * wlr_xwayland_surface doesn't have wlr_surface yet. */ - LISTEN(&client_surface(c)->events.commit, &c->commit, commitnotify); - } c->scene->node.data = c->scene_surface->node.data = c; /* Handle unmanaged clients first so we can return prior create borders */ @@ -1610,9 +1575,14 @@ maximizenotify(struct wl_listener *listener, void *data) * typically because the user clicked on the maximize button on * client-side decorations. dwl doesn't support maximization, but * to conform to xdg-shell protocol we still must send a configure. + * Since xdg-shell protocol v5 we should ignore request of unsupported + * capabilities, just schedule a empty configure when the client uses <5 + * protocol version * wlr_xdg_surface_schedule_configure() is used to send an empty reply. */ Client *c = wl_container_of(listener, c, maximize); - wlr_xdg_surface_schedule_configure(c->surface.xdg); + if (wl_resource_get_version(c->surface.xdg->resource) + < XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION) + wlr_xdg_surface_schedule_configure(c->surface.xdg); } void @@ -1658,7 +1628,7 @@ motionnotify(uint32_t time) /* time is 0 in internal calls meant to restore pointer focus. */ if (time) { - IDLE_NOTIFY_ACTIVITY; + wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); /* Update selmon (even while dragging a window) */ if (sloppyfocus) @@ -1696,8 +1666,8 @@ motionnotify(uint32_t time) /* If there's no client surface under the cursor, set the cursor image to a * default. This is what makes the cursor image appear when you move it * off of a client or over its border. */ - if (!surface && !seat->drag && (!cursor_image || strcmp(cursor_image, "left_ptr"))) - wlr_xcursor_manager_set_cursor_image(cursor_mgr, (cursor_image = "left_ptr"), cursor); + if (!surface && !seat->drag) + wlr_cursor_set_xcursor(cursor, cursor_mgr, "default"); pointerfocus(c, surface, sx, sy, time); } @@ -1732,7 +1702,7 @@ moveresize(const Arg *arg) case CurMove: grabcx = cursor->x - grabc->geom.x; grabcy = cursor->y - grabc->geom.y; - wlr_xcursor_manager_set_cursor_image(cursor_mgr, (cursor_image = "fleur"), cursor); + wlr_cursor_set_xcursor(cursor, cursor_mgr, "fleur"); break; case CurResize: /* Doesn't work for X11 output - the next absolute motion event @@ -1740,8 +1710,7 @@ moveresize(const Arg *arg) wlr_cursor_warp_closest(cursor, NULL, grabc->geom.x + grabc->geom.width, grabc->geom.y + grabc->geom.height); - wlr_xcursor_manager_set_cursor_image(cursor_mgr, - (cursor_image = "bottom_right_corner"), cursor); + wlr_cursor_set_xcursor(cursor, cursor_mgr, "se-resize"); break; } } @@ -1783,7 +1752,7 @@ outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int test) /* Don't move monitors if position wouldn't change, this to avoid * wlroots marking the output as manually configured */ if (m->m.x != config_head->state.x || m->m.y != config_head->state.y) - wlr_output_layout_move(output_layout, wlr_output, + wlr_output_layout_add(output_layout, wlr_output, config_head->state.x, config_head->state.y); wlr_output_set_transform(wlr_output, config_head->state.transform); wlr_output_set_scale(wlr_output, config_head->state.scale); @@ -1842,6 +1811,7 @@ pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, * wlroots makes this a no-op if surface is already focused */ wlr_seat_pointer_notify_enter(seat, surface, sx, sy); wlr_seat_pointer_notify_motion(seat, time, sx, sy); + } void @@ -1898,6 +1868,8 @@ 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; + struct wlr_output_state pending = {0}; + struct wlr_gamma_control_v1 *gamma_control; struct timespec now; /* Render if no XDG clients have an outstanding resize and are visible on @@ -1905,12 +1877,38 @@ rendermon(struct wl_listener *listener, void *data) wl_list_for_each(c, &clients, link) if (c->resize && !c->isfloating && client_is_rendered_on_mon(c, m) && !client_is_stopped(c)) goto skip; - wlr_scene_output_commit(m->scene_output); + + /* + * HACK: The "correct" way to set the gamma is to commit it together with + * the rest of the state in one go, but to do that we would need to rewrite + * wlr_scene_output_commit() in order to add the gamma to the pending + * state before committing, instead try to commit the gamma in one frame, + * and commit the rest of the state in the next one (or in the same frame if + * the gamma can not be committed). + */ + if (m->gamma_lut_changed) { + gamma_control = wlr_gamma_control_manager_v1_get_control(gamma_control_mgr, m->wlr_output); + m->gamma_lut_changed = 0; + + if (!wlr_gamma_control_v1_apply(gamma_control, &pending)) + goto commit; + + if (!wlr_output_test_state(m->wlr_output, &pending)) { + wlr_gamma_control_v1_send_failed_and_destroy(gamma_control); + goto commit; + } + wlr_output_commit_state(m->wlr_output, &pending); + wlr_output_schedule_frame(m->wlr_output); + } else { +commit: + wlr_scene_output_commit(m->scene_output, NULL); + } skip: /* Let clients know a frame has been rendered */ clock_gettime(CLOCK_MONOTONIC, &now); wlr_scene_output_send_frame_done(m->scene_output, &now); + wlr_output_state_finish(&pending); } void @@ -1925,10 +1923,19 @@ requeststartdrag(struct wl_listener *listener, void *data) wlr_data_source_destroy(event->drag->source); } +void +requestmonstate(struct wl_listener *listener, void *data) +{ + struct wlr_output_event_request_state *event = data; + wlr_output_commit_state(event->output, event->state); + updatemons(NULL, NULL); +} + void resize(Client *c, struct wlr_box geo, int interact) { struct wlr_box *bbox = interact ? &sgeom : &c->mon->w; + struct wlr_box clip; client_set_bounds(c, geo.width, geo.height); c->geom = geo; applybounds(c, bbox); @@ -1947,6 +1954,8 @@ resize(Client *c, struct wlr_box geo, int interact) /* 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); + client_get_clip(c, &clip); + wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip); } void @@ -1992,7 +2001,7 @@ run(char *startup_cmd) * initialized, as the image/coordinates are not transformed for the * monitor when displayed here */ wlr_cursor_warp_closest(cursor, NULL, cursor->x, cursor->y); - wlr_xcursor_manager_set_cursor_image(cursor_mgr, cursor_image, cursor); + wlr_cursor_set_xcursor(cursor, cursor_mgr, "default"); /* Run the Wayland event loop. This does not return until you exit the * compositor. Starting the backend rigged up all of the necessary event @@ -2011,7 +2020,6 @@ setcursor(struct wl_listener *listener, void *data) * event, which will result in the client requesting set the cursor surface */ if (cursor_mode != CurNormal && cursor_mode != CurPressed) return; - cursor_image = NULL; /* This can be sent by any client, so we check to make sure this one is * actually has pointer focus first. If so, we can tell the cursor to * use the provided surface as the cursor image. It will set the @@ -2022,6 +2030,20 @@ setcursor(struct wl_listener *listener, void *data) event->hotspot_x, event->hotspot_y); } +void +setcursorshape(struct wl_listener *listener, void *data) +{ + struct wlr_cursor_shape_manager_v1_request_set_shape_event *event = data; + if (cursor_mode != CurNormal && cursor_mode != CurPressed) + return; + /* This can be sent by any client, so we check to make sure this one is + * actually has pointer focus first. If so, we can tell the cursor to + * use the provided cursor shape. */ + if (event->seat_client == seat->pointer_state.focused_client) + wlr_cursor_set_xcursor(cursor, cursor_mgr, + wlr_cursor_shape_v1_name(event->shape)); +} + void setfloating(Client *c, int floating) { @@ -2057,6 +2079,15 @@ setfullscreen(Client *c, int fullscreen) printstatus(); } +void +setgamma(struct wl_listener *listener, void *data) +{ + struct wlr_gamma_control_manager_v1_set_gamma_event *event = data; + Monitor *m = event->output->data; + m->gamma_lut_changed = 1; + wlr_output_schedule_frame(m->wlr_output); +} + void setlayout(const Arg *arg) { @@ -2150,12 +2181,8 @@ setup(void) /* 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 - * if an X11 server is running. The NULL argument here optionally allows you - * to pass in a custom renderer if wlr_renderer doesn't meet your needs. The - * backend uses the renderer, for example, to fall back to software cursors - * if the backend does not support hardware cursors (some older GPUs - * don't). */ - if (!(backend = wlr_backend_autocreate(dpy))) + * if an X11 server is running. */ + if (!(backend = wlr_backend_autocreate(dpy, &session))) die("couldn't create backend"); /* Initialize the scene graph used to lay out windows */ @@ -2165,12 +2192,30 @@ setup(void) drag_icon = wlr_scene_tree_create(&scene->tree); wlr_scene_node_place_below(&drag_icon->node, &layers[LyrBlock]->node); - /* Create a renderer with the default implementation */ + /* Autocreates a renderer, either Pixman, GLES2 or Vulkan for us. The user + * can also specify a renderer using the WLR_RENDERER env var. + * The renderer is responsible for defining the various pixel formats it + * supports for shared memory, this configures that for clients. */ if (!(drw = wlr_renderer_autocreate(backend))) die("couldn't create renderer"); - wlr_renderer_init_wl_display(drw, dpy); - /* Create a default allocator */ + /* Create shm, drm and linux_dmabuf interfaces by ourselves. + * The simplest way is call: + * wlr_renderer_init_wl_display(drw); + * but we need to create manually the linux_dmabuf interface to integrate it + * with wlr_scene. */ + wlr_renderer_init_wl_shm(drw, dpy); + + if (wlr_renderer_get_dmabuf_texture_formats(drw)) { + wlr_drm_create(dpy, drw); + wlr_scene_set_linux_dmabuf_v1(scene, + wlr_linux_dmabuf_v1_create_with_renderer(dpy, 4, drw)); + } + + /* Autocreates an allocator for us. + * The allocator is the bridge between the renderer and the backend. It + * handles the buffer creation, allowing wlroots to render onto the + * screen */ if (!(alloc = wlr_allocator_autocreate(backend, drw))) die("couldn't create allocator"); @@ -2180,33 +2225,36 @@ setup(void) * to dig your fingers in and play with their behavior if you want. Note that * the clients cannot set the selection directly without compositor approval, * see the setsel() function. */ - compositor = wlr_compositor_create(dpy, drw); + compositor = wlr_compositor_create(dpy, 6, drw); + wlr_subcompositor_create(dpy); + wlr_data_device_manager_create(dpy); wlr_export_dmabuf_manager_v1_create(dpy); wlr_screencopy_manager_v1_create(dpy); wlr_data_control_manager_v1_create(dpy); - wlr_data_device_manager_create(dpy); - wlr_gamma_control_manager_v1_create(dpy); wlr_primary_selection_v1_device_manager_create(dpy); wlr_viewporter_create(dpy); wlr_single_pixel_buffer_manager_v1_create(dpy); - wlr_subcompositor_create(dpy); + wlr_fractional_scale_manager_v1_create(dpy, 1); /* Initializes the interface used to implement urgency hints */ activation = wlr_xdg_activation_v1_create(dpy); - wl_signal_add(&activation->events.request_activate, &request_activate); + LISTEN_STATIC(&activation->events.request_activate, urgent); + + gamma_control_mgr = wlr_gamma_control_manager_v1_create(dpy); + LISTEN_STATIC(&gamma_control_mgr->events.set_gamma, setgamma); /* Creates an output layout, which a wlroots utility for working with an * arrangement of screens in a physical layout. */ output_layout = wlr_output_layout_create(); - wl_signal_add(&output_layout->events.change, &layout_change); + LISTEN_STATIC(&output_layout->events.change, updatemons); wlr_xdg_output_manager_v1_create(dpy, output_layout); /* Configure a listener to be notified when new outputs are available on the * backend. */ wl_list_init(&mons); - wl_signal_add(&backend->events.new_output, &new_output); + LISTEN_STATIC(&backend->events.new_output, createmon); - /* Set up our client lists and the xdg-shell. The xdg-shell is a + /* Set up our client lists, the xdg-shell and the layer-shell. The xdg-shell is a * Wayland protocol which is used for application windows. For more * detail on shells, refer to the article: * @@ -2215,22 +2263,20 @@ setup(void) wl_list_init(&clients); wl_list_init(&fstack); - idle = wlr_idle_create(dpy); + xdg_shell = wlr_xdg_shell_create(dpy, 6); + LISTEN_STATIC(&xdg_shell->events.new_surface, createnotify); + + layer_shell = wlr_layer_shell_v1_create(dpy, 3); + LISTEN_STATIC(&layer_shell->events.new_surface, createlayersurface); + idle_notifier = wlr_idle_notifier_v1_create(dpy); idle_inhibit_mgr = wlr_idle_inhibit_v1_create(dpy); - wl_signal_add(&idle_inhibit_mgr->events.new_inhibitor, &idle_inhibitor_create); + LISTEN_STATIC(&idle_inhibit_mgr->events.new_inhibitor, createidleinhibitor); - layer_shell = wlr_layer_shell_v1_create(dpy); - wl_signal_add(&layer_shell->events.new_surface, &new_layer_shell_surface); - - xdg_shell = wlr_xdg_shell_create(dpy, 4); - 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); + wl_signal_add(&session_lock_mgr->events.new_lock, &lock_listener); + LISTEN_STATIC(&session_lock_mgr->events.destroy, destroysessionmgr); 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); @@ -2240,7 +2286,7 @@ setup(void) wlr_server_decoration_manager_create(dpy), WLR_SERVER_DECORATION_MANAGER_MODE_SERVER); xdg_decoration_mgr = wlr_xdg_decoration_manager_v1_create(dpy); - wl_signal_add(&xdg_decoration_mgr->events.new_toplevel_decoration, &new_xdg_decoration); + LISTEN_STATIC(&xdg_decoration_mgr->events.new_toplevel_decoration, createdecoration); /* * Creates a cursor, which is a wlroots utility for tracking the cursor @@ -2261,18 +2307,19 @@ setup(void) * when the pointer moves. However, we can attach input devices to it, and * it will generate aggregate events for all of them. In these events, we * can choose how we want to process them, forwarding them to clients and - * moving the cursor around. More detail on this process is described in my - * input handling blog post: - * + * moving the cursor around. More detail on this process is described in * https://drewdevault.com/2018/07/17/Input-handling-in-wlroots.html * * And more comments are sprinkled throughout the notify functions above. */ - wl_signal_add(&cursor->events.motion, &cursor_motion); - wl_signal_add(&cursor->events.motion_absolute, &cursor_motion_absolute); - wl_signal_add(&cursor->events.button, &cursor_button); - wl_signal_add(&cursor->events.axis, &cursor_axis); - wl_signal_add(&cursor->events.frame, &cursor_frame); + LISTEN_STATIC(&cursor->events.motion, motionrelative); + LISTEN_STATIC(&cursor->events.motion_absolute, motionabsolute); + LISTEN_STATIC(&cursor->events.button, buttonpress); + LISTEN_STATIC(&cursor->events.axis, axisnotify); + LISTEN_STATIC(&cursor->events.frame, cursorframe); + + cursor_shape_mgr = wlr_cursor_shape_manager_v1_create(dpy, 1); + LISTEN_STATIC(&cursor_shape_mgr->events.request_set_shape, setcursorshape); /* * Configures a seat, which is a single "seat" at which a user sits and @@ -2281,20 +2328,19 @@ setup(void) * let us know when new input devices are available on the backend. */ wl_list_init(&keyboards); - wl_signal_add(&backend->events.new_input, &new_input); + LISTEN_STATIC(&backend->events.new_input, inputdevice); virtual_keyboard_mgr = wlr_virtual_keyboard_manager_v1_create(dpy); - wl_signal_add(&virtual_keyboard_mgr->events.new_virtual_keyboard, - &new_virtual_keyboard); + LISTEN_STATIC(&virtual_keyboard_mgr->events.new_virtual_keyboard, virtualkeyboard); seat = wlr_seat_create(dpy, "seat0"); - wl_signal_add(&seat->events.request_set_cursor, &request_cursor); - wl_signal_add(&seat->events.request_set_selection, &request_set_sel); - wl_signal_add(&seat->events.request_set_primary_selection, &request_set_psel); - wl_signal_add(&seat->events.request_start_drag, &request_start_drag); - wl_signal_add(&seat->events.start_drag, &start_drag); + LISTEN_STATIC(&seat->events.request_set_cursor, setcursor); + LISTEN_STATIC(&seat->events.request_set_selection, setsel); + LISTEN_STATIC(&seat->events.request_set_primary_selection, setpsel); + LISTEN_STATIC(&seat->events.request_start_drag, requeststartdrag); + LISTEN_STATIC(&seat->events.start_drag, startdrag); output_mgr = wlr_output_manager_v1_create(dpy); - wl_signal_add(&output_mgr->events.apply, &output_mgr_apply); - wl_signal_add(&output_mgr->events.test, &output_mgr_test); + LISTEN_STATIC(&output_mgr->events.apply, outputmgrapply); + LISTEN_STATIC(&output_mgr->events.test, outputmgrtest); wlr_scene_set_presentation(scene, wlr_presentation_create(dpy, backend)); @@ -2305,8 +2351,8 @@ setup(void) */ xwayland = wlr_xwayland_create(dpy, compositor, 1); if (xwayland) { - wl_signal_add(&xwayland->events.ready, &xwayland_ready); - wl_signal_add(&xwayland->events.new_surface, &new_xwayland_surface); + LISTEN_STATIC(&xwayland->events.ready, xwaylandready); + LISTEN_STATIC(&xwayland->events.new_surface, createnotifyx11); setenv("DISPLAY", xwayland->display_name, 1); } else { @@ -2333,8 +2379,8 @@ startdrag(struct wl_listener *listener, void *data) if (!drag->icon) return; - drag->icon->data = &wlr_scene_subsurface_tree_create(drag_icon, drag->icon->surface)->node; - wl_signal_add(&drag->icon->events.destroy, &drag_icon_destroy); + drag->icon->data = &wlr_scene_drag_icon_create(drag_icon, drag->icon)->node; + LISTEN_STATIC(&drag->icon->events.destroy, destroydragicon); } void @@ -2485,7 +2531,6 @@ unmapnotify(struct wl_listener *listener, void *data) wl_list_remove(&c->flink); } - wl_list_remove(&c->commit.link); wlr_scene_node_destroy(&c->scene->node); printstatus(); motionnotify(0); @@ -2524,7 +2569,6 @@ 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); @@ -2538,8 +2582,8 @@ updatemons(struct wl_listener *listener, void *data) config_head = wlr_output_configuration_head_v1_create(config, m->wlr_output); /* Get the effective monitor geometry to use for surfaces */ - 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_output_layout_get_box(output_layout, m->wlr_output, &m->m); + m->w = m->m; 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); @@ -2556,7 +2600,11 @@ updatemons(struct wl_listener *listener, void *data) arrangelayers(m); /* Don't move clients to the left output when plugging monitors */ arrange(m); + /* make sure fullscreen clients have the right size */ + if ((c = focustop(m)) && c->isfullscreen) + resize(c, m->m, 0); + m->gamma_lut_changed = 1; config_head->state.enabled = 1; config_head->state.mode = m->wlr_output->current_mode; config_head->state.x = m->m.x; @@ -2565,7 +2613,7 @@ updatemons(struct wl_listener *listener, void *data) if (selmon && selmon->wlr_output->enabled) { wl_list_for_each(c, &clients, link) - if (!c->mon && client_is_mapped(c)) + if (!c->mon && client_surface(c)->mapped) setmon(c, selmon, c->tags); focusclient(focustop(selmon), 1); if (selmon->lock_surface) { @@ -2575,6 +2623,13 @@ updatemons(struct wl_listener *listener, void *data) } } + /* FIXME: figure out why the cursor image is at 0,0 after turning all + * the monitors on. + * Move the cursor image where it used to be. It does not generate a + * wl_pointer.motion event for the clients, it's only the image what it's + * at the wrong position after all. */ + wlr_cursor_move(cursor, NULL, 0, 0); + wlr_output_manager_v1_set_configuration(output_mgr, config); } @@ -2595,7 +2650,7 @@ urgent(struct wl_listener *listener, void *data) if (!c || c == focustop(selmon)) return; - if (client_is_mapped(c)) + if (client_surface(c)->mapped) client_set_border_color(c, urgentcolor); c->isurgent = 1; printstatus(); @@ -2643,7 +2698,7 @@ xytonode(double x, double y, struct wlr_surface **psurface, continue; if (node->type == WLR_SCENE_NODE_BUFFER) - surface = wlr_scene_surface_from_buffer( + surface = wlr_scene_surface_try_from_buffer( wlr_scene_buffer_from_node(node))->surface; /* Walk the tree to find a node that knows the client */ for (pnode = node; pnode && !c; pnode = &pnode->parent->node) @@ -2702,6 +2757,15 @@ activatex11(struct wl_listener *listener, void *data) wlr_xwayland_surface_activate(c->surface.xwayland, 1); } +void +associatex11(struct wl_listener *listener, void *data) +{ + Client *c = wl_container_of(listener, c, associate); + + LISTEN(&client_surface(c)->events.map, &c->map, mapnotify); + LISTEN(&client_surface(c)->events.unmap, &c->unmap, unmapnotify); +} + void configurex11(struct wl_listener *listener, void *data) { @@ -2729,8 +2793,8 @@ createnotifyx11(struct wl_listener *listener, void *data) c->bw = borderpx; /* Listen to the various events it can emit */ - LISTEN(&xsurface->events.map, &c->map, mapnotify); - LISTEN(&xsurface->events.unmap, &c->unmap, unmapnotify); + LISTEN(&xsurface->events.associate, &c->associate, associatex11); + LISTEN(&xsurface->events.dissociate, &c->dissociate, dissociatex11); LISTEN(&xsurface->events.request_activate, &c->activate, activatex11); LISTEN(&xsurface->events.request_configure, &c->configure, configurex11); LISTEN(&xsurface->events.set_hints, &c->set_hints, sethints); @@ -2739,6 +2803,14 @@ createnotifyx11(struct wl_listener *listener, void *data) LISTEN(&xsurface->events.request_fullscreen, &c->fullscreen, fullscreennotify); } +void +dissociatex11(struct wl_listener *listener, void *data) +{ + Client *c = wl_container_of(listener, c, dissociate); + wl_list_remove(&c->map.link); + wl_list_remove(&c->unmap.link); +} + xcb_atom_t getatom(xcb_connection_t *xc, const char *name) { @@ -2756,12 +2828,13 @@ void sethints(struct wl_listener *listener, void *data) { Client *c = wl_container_of(listener, c, set_hints); + struct wlr_surface *surface = client_surface(c); if (c == focustop(selmon)) return; c->isurgent = xcb_icccm_wm_hints_get_urgency(c->surface.xwayland->hints); - if (c->isurgent && client_is_mapped(c)) + if (c->isurgent && surface && surface->mapped) client_set_border_color(c, urgentcolor); printstatus(); @@ -2789,7 +2862,7 @@ xwaylandready(struct wl_listener *listener, void *data) wlr_xwayland_set_seat(xwayland, seat); /* Set the default XWayland cursor to match the rest of dwl. */ - if ((xcursor = wlr_xcursor_manager_get_xcursor(cursor_mgr, "left_ptr", 1))) + if ((xcursor = wlr_xcursor_manager_get_xcursor(cursor_mgr, "default", 1))) wlr_xwayland_set_cursor(xwayland, xcursor->images[0]->buffer, xcursor->images[0]->width * 4, xcursor->images[0]->width, xcursor->images[0]->height,