Merge branch 'wlroots-next'

This commit is contained in:
Leonardo Hernández Hernández 2023-11-25 00:04:19 -06:00
commit 7ac76219df
Failed to generate hash of commit
3 changed files with 341 additions and 273 deletions

View file

@ -5,7 +5,7 @@ include config.mk
# flags for compiling # flags for compiling
DWLCPPFLAGS = -I. -DWLR_USE_UNSTABLE -D_POSIX_C_SOURCE=200809L -DVERSION=\"$(VERSION)\" $(XWAYLAND) 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 -Werror=strict-prototypes -Werror=implicit -Werror=return-type -Werror=incompatible-pointer-types
# CFLAGS / LDFLAGS # CFLAGS / LDFLAGS
@ -16,7 +16,7 @@ LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` $(LIBS)
all: dwl all: dwl
dwl: dwl.o util.o dwl: dwl.o util.o
$(CC) dwl.o util.o $(LDLIBS) $(LDFLAGS) $(DWLCFLAGS) -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 util.o: util.c util.h
# wayland-scanner is a tool which generates C headers and rigging for Wayland # 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: wlr-layer-shell-unstable-v1-protocol.h:
$(WAYLAND_SCANNER) server-header \ $(WAYLAND_SCANNER) server-header \
protocols/wlr-layer-shell-unstable-v1.xml $@ 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: config.h:
cp config.def.h $@ cp config.def.h $@

180
client.h
View file

@ -11,34 +11,8 @@ client_is_x11(Client *c)
{ {
#ifdef XWAYLAND #ifdef XWAYLAND
return c->type == X11Managed || c->type == X11Unmanaged; return c->type == X11Managed || c->type == X11Unmanaged;
#else #endif
return 0; 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 * static inline struct wlr_surface *
@ -54,7 +28,7 @@ client_surface(Client *c)
static inline int static inline int
toplevel_from_wlr_surface(struct wlr_surface *s, Client **pc, LayerSurface **pl) 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_surface *root_surface;
struct wlr_layer_surface_v1 *layer_surface; struct wlr_layer_surface_v1 *layer_surface;
Client *c = NULL; Client *c = NULL;
@ -65,44 +39,44 @@ toplevel_from_wlr_surface(struct wlr_surface *s, Client **pc, LayerSurface **pl)
#endif #endif
if (!s) if (!s)
return type; return -1;
root_surface = wlr_surface_get_root_surface(s); root_surface = wlr_surface_get_root_surface(s);
#ifdef XWAYLAND #ifdef XWAYLAND
if (wlr_surface_is_xwayland_surface(root_surface) if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(root_surface))) {
&& (xsurface = wlr_xwayland_surface_from_wlr_surface(root_surface))) {
c = xsurface->data; c = xsurface->data;
type = c->type; type = c->type;
goto end; goto end;
} }
#endif #endif
if (wlr_surface_is_layer_surface(root_surface) if ((layer_surface = wlr_layer_surface_v1_try_from_wlr_surface(root_surface))) {
&& (layer_surface = wlr_layer_surface_v1_from_wlr_surface(root_surface))) {
l = layer_surface->data; l = layer_surface->data;
type = LayerShell; type = LayerShell;
goto end; goto end;
} }
if (wlr_surface_is_xdg_surface(root_surface) xdg_surface = wlr_xdg_surface_try_from_wlr_surface(root_surface);
&& (xdg_surface = wlr_xdg_surface_from_wlr_surface(root_surface))) { while (xdg_surface) {
while (1) { tmp_xdg_surface = NULL;
switch (xdg_surface->role) { switch (xdg_surface->role) {
case WLR_XDG_SURFACE_ROLE_POPUP: case WLR_XDG_SURFACE_ROLE_POPUP:
if (!xdg_surface->popup->parent) if (!xdg_surface->popup || !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; 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 static inline void
client_activate_surface(struct wlr_surface *s, int activated) client_activate_surface(struct wlr_surface *s, int activated)
{ {
struct wlr_xdg_surface *surface; struct wlr_xdg_toplevel *toplevel;
#ifdef XWAYLAND #ifdef XWAYLAND
struct wlr_xwayland_surface *xsurface; struct wlr_xwayland_surface *xsurface;
if (wlr_surface_is_xwayland_surface(s) if ((xsurface = wlr_xwayland_surface_try_from_wlr_surface(s))) {
&& (xsurface = wlr_xwayland_surface_from_wlr_surface(s))) {
wlr_xwayland_surface_activate(xsurface, activated); wlr_xwayland_surface_activate(xsurface, activated);
return; return;
} }
#endif #endif
if (wlr_surface_is_xdg_surface(s) if ((toplevel = wlr_xdg_toplevel_try_from_wlr_surface(s)))
&& (surface = wlr_xdg_surface_from_wlr_surface(s)) wlr_xdg_toplevel_set_activated(toplevel, activated);
&& surface->role == WLR_XDG_SURFACE_ROLE_TOPLEVEL)
wlr_xdg_toplevel_set_activated(surface->toplevel, activated);
} }
static inline uint32_t static inline uint32_t
@ -140,23 +111,16 @@ client_set_bounds(Client *c, int32_t width, int32_t height)
if (client_is_x11(c)) if (client_is_x11(c))
return 0; return 0;
#endif #endif
if (c->surface.xdg->client->shell->version >= if (wl_resource_get_version(c->surface.xdg->toplevel->resource) >=
XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION && width >= 0 && height >= 0) 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 wlr_xdg_toplevel_set_bounds(c->surface.xdg->toplevel, width, height);
}
return 0; 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 * static inline const char *
client_get_appid(Client *c) client_get_appid(Client *c)
{ {
@ -167,6 +131,27 @@ client_get_appid(Client *c)
return c->surface.xdg->toplevel->app_id; 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 static inline void
client_get_geometry(Client *c, struct wlr_box *geom) client_get_geometry(Client *c, struct wlr_box *geom)
{ {
@ -192,7 +177,6 @@ client_get_parent(Client *c)
#endif #endif
if (c->surface.xdg->toplevel->parent) if (c->surface.xdg->toplevel->parent)
toplevel_from_wlr_surface(c->surface.xdg->toplevel->parent->base->surface, &p, NULL); toplevel_from_wlr_surface(c->surface.xdg->toplevel->parent->base->surface, &p, NULL);
return p; return p;
} }
@ -209,35 +193,35 @@ client_get_title(Client *c)
static inline int static inline int
client_is_float_type(Client *c) client_is_float_type(Client *c)
{ {
struct wlr_box min = {0}, max = {0}; struct wlr_xdg_toplevel *toplevel;
client_get_size_hints(c, &max, &min); struct wlr_xdg_toplevel_state state;
#ifdef XWAYLAND #ifdef XWAYLAND
if (client_is_x11(c)) { if (client_is_x11(c)) {
struct wlr_xwayland_surface *surface = c->surface.xwayland; struct wlr_xwayland_surface *surface = c->surface.xwayland;
xcb_size_hints_t *size_hints = surface->size_hints;
size_t i;
if (surface->modal) if (surface->modal)
return 1; 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] if (surface->window_type[i] == netatom[NetWMWindowTypeDialog]
|| surface->window_type[i] == netatom[NetWMWindowTypeSplash] || surface->window_type[i] == netatom[NetWMWindowTypeSplash]
|| surface->window_type[i] == netatom[NetWMWindowTypeToolbar] || surface->window_type[i] == netatom[NetWMWindowTypeToolbar]
|| surface->window_type[i] == netatom[NetWMWindowTypeUtility]) || surface->window_type[i] == netatom[NetWMWindowTypeUtility])
return 1; 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 #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 toplevel = c->surface.xdg->toplevel;
client_is_mapped(Client *c) state = toplevel->current;
{ return toplevel->parent || (state.min_width != 0 && state.min_height != 0
#ifdef XWAYLAND && (state.min_width == state.max_width
if (client_is_x11(c)) || state.min_height == state.max_height));
return c->surface.xwayland->mapped;
#endif
return c->surface.xdg->mapped;
} }
static inline int static inline int
@ -247,7 +231,8 @@ client_is_rendered_on_mon(Client *c, Monitor *m)
* but rather actual displaying of the pixels. * but rather actual displaying of the pixels.
* Usually VISIBLEON suffices and is also faster. */ * Usually VISIBLEON suffices and is also faster. */
struct wlr_surface_output *s; 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; return 0;
wl_list_for_each(s, &client_surface(c)->current_outputs, link) wl_list_for_each(s, &client_surface(c)->current_outputs, link)
if (s->output == m->wlr_output) if (s->output == m->wlr_output)
@ -366,18 +351,25 @@ client_set_tiled(Client *c, uint32_t edges)
if (client_is_x11(c)) if (client_is_x11(c))
return; return;
#endif #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 * static inline void
client_surface_at(Client *c, double cx, double cy, double *sx, double *sy) client_set_suspended(Client *c, int suspended)
{ {
#ifdef XWAYLAND #ifdef XWAYLAND
if (client_is_x11(c)) if (client_is_x11(c)) {
return wlr_surface_surface_at(c->surface.xwayland->surface, wlr_xwayland_surface_set_withdrawn(c->surface.xwayland, suspended);
cx, cy, sx, sy); return;
}
#endif #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 static inline int

427
dwl.c
View file

@ -3,7 +3,6 @@
*/ */
#include <getopt.h> #include <getopt.h>
#include <libinput.h> #include <libinput.h>
#include <limits.h>
#include <linux/input-event-codes.h> #include <linux/input-event-codes.h>
#include <signal.h> #include <signal.h>
#include <stdio.h> #include <stdio.h>
@ -18,15 +17,17 @@
#include <wlr/render/wlr_renderer.h> #include <wlr/render/wlr_renderer.h>
#include <wlr/types/wlr_compositor.h> #include <wlr/types/wlr_compositor.h>
#include <wlr/types/wlr_cursor.h> #include <wlr/types/wlr_cursor.h>
#include <wlr/types/wlr_cursor_shape_v1.h>
#include <wlr/types/wlr_data_control_v1.h> #include <wlr/types/wlr_data_control_v1.h>
#include <wlr/types/wlr_data_device.h> #include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_drm.h>
#include <wlr/types/wlr_linux_dmabuf_v1.h>
#include <wlr/types/wlr_export_dmabuf_v1.h> #include <wlr/types/wlr_export_dmabuf_v1.h>
#include <wlr/types/wlr_fractional_scale_v1.h>
#include <wlr/types/wlr_gamma_control_v1.h> #include <wlr/types/wlr_gamma_control_v1.h>
#include <wlr/types/wlr_idle.h>
#include <wlr/types/wlr_idle_inhibit_v1.h> #include <wlr/types/wlr_idle_inhibit_v1.h>
#include <wlr/types/wlr_idle_notify_v1.h> #include <wlr/types/wlr_idle_notify_v1.h>
#include <wlr/types/wlr_input_device.h> #include <wlr/types/wlr_input_device.h>
#include <wlr/types/wlr_input_inhibitor.h>
#include <wlr/types/wlr_keyboard.h> #include <wlr/types/wlr_keyboard.h>
#include <wlr/types/wlr_layer_shell_v1.h> #include <wlr/types/wlr_layer_shell_v1.h>
#include <wlr/types/wlr_output.h> #include <wlr/types/wlr_output.h>
@ -69,7 +70,7 @@
#define END(A) ((A) + LENGTH(A)) #define END(A) ((A) + LENGTH(A))
#define TAGMASK ((1u << TAGCOUNT) - 1) #define TAGMASK ((1u << TAGCOUNT) - 1)
#define LISTEN(E, L, H) wl_signal_add((E), ((L)->notify = (H), (L))) #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 */ /* enums */
enum { CurNormal, CurPressed, CurMove, CurResize }; /* cursor */ enum { CurNormal, CurPressed, CurMove, CurResize }; /* cursor */
@ -117,8 +118,11 @@ typedef struct {
struct wl_listener set_title; struct wl_listener set_title;
struct wl_listener fullscreen; struct wl_listener fullscreen;
struct wlr_box prev; /* layout-relative, includes border */ struct wlr_box prev; /* layout-relative, includes border */
struct wlr_box bounds;
#ifdef XWAYLAND #ifdef XWAYLAND
struct wl_listener activate; struct wl_listener activate;
struct wl_listener associate;
struct wl_listener dissociate;
struct wl_listener configure; struct wl_listener configure;
struct wl_listener set_hints; struct wl_listener set_hints;
#endif #endif
@ -179,6 +183,7 @@ struct Monitor {
struct wlr_scene_rect *fullscreen_bg; /* See createmon() for info */ struct wlr_scene_rect *fullscreen_bg; /* See createmon() for info */
struct wl_listener frame; struct wl_listener frame;
struct wl_listener destroy; struct wl_listener destroy;
struct wl_listener request_state;
struct wl_listener destroy_lock_surface; struct wl_listener destroy_lock_surface;
struct wlr_session_lock_surface_v1 *lock_surface; struct wlr_session_lock_surface_v1 *lock_surface;
struct wlr_box m; /* monitor area, layout-relative */ struct wlr_box m; /* monitor area, layout-relative */
@ -189,6 +194,7 @@ struct Monitor {
unsigned int sellt; unsigned int sellt;
uint32_t tagset[2]; uint32_t tagset[2];
double mfact; double mfact;
int gamma_lut_changed;
int nmaster; int nmaster;
char ltsymbol[16]; char ltsymbol[16];
}; };
@ -286,11 +292,14 @@ static void printstatus(void);
static void quit(const Arg *arg); static void quit(const Arg *arg);
static void rendermon(struct wl_listener *listener, void *data); static void rendermon(struct wl_listener *listener, void *data);
static void requeststartdrag(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 resize(Client *c, struct wlr_box geo, int interact);
static void run(char *startup_cmd); static void run(char *startup_cmd);
static void setcursor(struct wl_listener *listener, void *data); 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 setfloating(Client *c, int floating);
static void setfullscreen(Client *c, int fullscreen); static void setfullscreen(Client *c, int fullscreen);
static void setgamma(struct wl_listener *listener, void *data);
static void setlayout(const Arg *arg); static void setlayout(const Arg *arg);
static void setmfact(const Arg *arg); static void setmfact(const Arg *arg);
static void setmon(Client *c, Monitor *m, uint32_t newtags); static void setmon(Client *c, Monitor *m, uint32_t newtags);
@ -321,7 +330,6 @@ static void zoom(const Arg *arg);
/* variables */ /* variables */
static const char broken[] = "broken"; static const char broken[] = "broken";
static const char *cursor_image = "left_ptr";
static pid_t child_pid = -1; static pid_t child_pid = -1;
static int locked; static int locked;
static void *exclusive_focus; static void *exclusive_focus;
@ -335,19 +343,20 @@ static const int layermap[] = { LyrBg, LyrBottom, LyrTop, LyrOverlay };
static struct wlr_renderer *drw; static struct wlr_renderer *drw;
static struct wlr_allocator *alloc; static struct wlr_allocator *alloc;
static struct wlr_compositor *compositor; static struct wlr_compositor *compositor;
static struct wlr_session *session;
static struct wlr_xdg_shell *xdg_shell; static struct wlr_xdg_shell *xdg_shell;
static struct wlr_xdg_activation_v1 *activation; static struct wlr_xdg_activation_v1 *activation;
static struct wlr_xdg_decoration_manager_v1 *xdg_decoration_mgr; static struct wlr_xdg_decoration_manager_v1 *xdg_decoration_mgr;
static struct wl_list clients; /* tiling order */ static struct wl_list clients; /* tiling order */
static struct wl_list fstack; /* focus 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_notifier_v1 *idle_notifier;
static struct wlr_idle_inhibit_manager_v1 *idle_inhibit_mgr; 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_layer_shell_v1 *layer_shell;
static struct wlr_output_manager_v1 *output_mgr; 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_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_cursor *cursor;
static struct wlr_xcursor_manager *cursor_mgr; 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_session_lock_manager_v1 *session_lock_mgr;
static struct wlr_scene_rect *locked_bg; static struct wlr_scene_rect *locked_bg;
static struct wlr_session_lock_v1 *cur_lock; static struct wlr_session_lock_v1 *cur_lock;
static struct wl_listener lock_listener = {.notify = locksession};
static struct wlr_seat *seat; static struct wlr_seat *seat;
static struct wl_list keyboards; static struct wl_list keyboards;
@ -367,42 +377,15 @@ static struct wlr_box sgeom;
static struct wl_list mons; static struct wl_list mons;
static Monitor *selmon; 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 #ifdef XWAYLAND
static void activatex11(struct wl_listener *listener, void *data); 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 configurex11(struct wl_listener *listener, void *data);
static void createnotifyx11(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 xcb_atom_t getatom(xcb_connection_t *xc, const char *name);
static void sethints(struct wl_listener *listener, void *data); static void sethints(struct wl_listener *listener, void *data);
static void xwaylandready(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 struct wlr_xwayland *xwayland;
static xcb_atom_t netatom[NetLast]; static xcb_atom_t netatom[NetLast];
#endif #endif
@ -417,19 +400,9 @@ static xcb_atom_t netatom[NetLast];
void void
applybounds(Client *c, struct wlr_box *bbox) applybounds(Client *c, struct wlr_box *bbox)
{ {
if (!c->isfullscreen) { /* set minimum possible */
struct wlr_box min = {0}, max = {0}; c->geom.width = MAX(1, c->geom.width);
client_get_size_hints(c, &max, &min); c->geom.height = MAX(1, c->geom.height);
/* 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);
}
if (c->geom.x >= bbox->x + bbox->width) if (c->geom.x >= bbox->x + bbox->width)
c->geom.x = bbox->x + bbox->width - c->geom.width; c->geom.x = bbox->x + bbox->width - c->geom.width;
@ -475,9 +448,12 @@ void
arrange(Monitor *m) arrange(Monitor *m)
{ {
Client *c; Client *c;
wl_list_for_each(c, &clients, link) wl_list_for_each(c, &clients, link) {
if (c->mon == m) if (c->mon == m) {
wlr_scene_node_set_enabled(&c->scene->node, VISIBLEON(c, 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, wlr_scene_node_set_enabled(&m->fullscreen_bg->node,
(c = focustop(m)) && c->isfullscreen); (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, /* This event is forwarded by the cursor when a pointer emits an axis event,
* for example when you move the scroll wheel. */ * for example when you move the scroll wheel. */
struct wlr_pointer_axis_event *event = data; 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 /* TODO: allow usage of scroll whell for mousebindings, it can be implemented
* checking the event's orientation and the delta of the event */ * checking the event's orientation and the delta of the event */
/* Notify the client with pointer focus of the axis event. */ /* Notify the client with pointer focus of the axis event. */
@ -577,7 +553,7 @@ buttonpress(struct wl_listener *listener, void *data)
Client *c; Client *c;
const Button *b; const Button *b;
IDLE_NOTIFY_ACTIVITY; wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
switch (event->state) { switch (event->state) {
case WLR_BUTTON_PRESSED: case WLR_BUTTON_PRESSED:
@ -602,13 +578,10 @@ buttonpress(struct wl_listener *listener, void *data)
break; break;
case WLR_BUTTON_RELEASED: case WLR_BUTTON_RELEASED:
/* If you released any buttons, we exit interactive move/resize mode. */ /* 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) { if (!locked && cursor_mode != CurNormal && cursor_mode != CurPressed) {
wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
cursor_mode = CurNormal; 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 */ /* Drop the window off on its new monitor */
selmon = xytomon(cursor->x, cursor->y); selmon = xytomon(cursor->x, cursor->y);
setmon(grabc, selmon, 0); setmon(grabc, selmon, 0);
@ -627,7 +600,7 @@ buttonpress(struct wl_listener *listener, void *data)
void void
chvt(const Arg *arg) chvt(const Arg *arg)
{ {
wlr_session_change_vt(wlr_backend_get_session(backend), arg->ui); wlr_session_change_vt(session, arg->ui);
} }
void 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); wlr_idle_notifier_v1_set_inhibited(idle_notifier, inhibited);
} }
@ -654,21 +626,19 @@ cleanup(void)
{ {
#ifdef XWAYLAND #ifdef XWAYLAND
wlr_xwayland_destroy(xwayland); wlr_xwayland_destroy(xwayland);
xwayland = NULL;
#endif #endif
wl_display_destroy_clients(dpy); wl_display_destroy_clients(dpy);
if (child_pid > 0) { if (child_pid > 0) {
kill(child_pid, SIGTERM); kill(child_pid, SIGTERM);
waitpid(child_pid, NULL, 0); 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_xcursor_manager_destroy(cursor_mgr);
wlr_cursor_destroy(cursor);
wlr_output_layout_destroy(output_layout); wlr_output_layout_destroy(output_layout);
wlr_seat_destroy(seat);
wl_display_destroy(dpy); 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 void
@ -757,9 +727,9 @@ commitlayersurfacenotify(struct wl_listener *listener, void *data)
wlr_scene_node_reparent(&layersurface->popups->node, layers[LyrTop]); wlr_scene_node_reparent(&layersurface->popups->node, layers[LyrTop]);
if (wlr_layer_surface->current.committed == 0 if (wlr_layer_surface->current.committed == 0
&& layersurface->mapped == wlr_layer_surface->mapped) && layersurface->mapped == wlr_layer_surface->surface->mapped)
return; return;
layersurface->mapped = wlr_layer_surface->mapped; layersurface->mapped = wlr_layer_surface->surface->mapped;
arrangelayers(layersurface->mon); arrangelayers(layersurface->mon);
} }
@ -769,6 +739,9 @@ commitnotify(struct wl_listener *listener, void *data)
{ {
Client *c = wl_container_of(listener, c, commit); 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 */ /* 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; c->resize = 0;
@ -785,7 +758,7 @@ void
createidleinhibitor(struct wl_listener *listener, void *data) createidleinhibitor(struct wl_listener *listener, void *data)
{ {
struct wlr_idle_inhibitor_v1 *idle_inhibitor = 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); checkidleinhibitor(NULL);
} }
@ -840,21 +813,19 @@ createlayersurface(struct wl_listener *listener, void *data)
return; return;
} }
layersurface = ecalloc(1, sizeof(LayerSurface)); layersurface = wlr_layer_surface->data = ecalloc(1, sizeof(LayerSurface));
layersurface->type = LayerShell; layersurface->type = LayerShell;
LISTEN(&wlr_layer_surface->surface->events.commit, LISTEN(&wlr_layer_surface->surface->events.commit,
&layersurface->surface_commit, commitlayersurfacenotify); &layersurface->surface_commit, commitlayersurfacenotify);
LISTEN(&wlr_layer_surface->events.destroy, &layersurface->destroy, LISTEN(&wlr_layer_surface->events.destroy, &layersurface->destroy,
destroylayersurfacenotify); destroylayersurfacenotify);
LISTEN(&wlr_layer_surface->events.map, &layersurface->map, LISTEN(&wlr_layer_surface->surface->events.map, &layersurface->map,
maplayersurfacenotify); maplayersurfacenotify);
LISTEN(&wlr_layer_surface->events.unmap, &layersurface->unmap, LISTEN(&wlr_layer_surface->surface->events.unmap, &layersurface->unmap,
unmaplayersurfacenotify); unmaplayersurfacenotify);
layersurface->layer_surface = wlr_layer_surface; layersurface->layer_surface = wlr_layer_surface;
layersurface->mon = wlr_layer_surface->output->data; 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_layer = wlr_scene_layer_surface_v1_create(l, wlr_layer_surface);
layersurface->scene = layersurface->scene_layer->tree; layersurface->scene = layersurface->scene_layer->tree;
layersurface->popups = wlr_layer_surface->surface->data = wlr_scene_tree_create(l); 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->mfact = r->mfact;
m->nmaster = r->nmaster; m->nmaster = r->nmaster;
wlr_output_set_scale(wlr_output, r->scale); wlr_output_set_scale(wlr_output, r->scale);
wlr_xcursor_manager_load(cursor_mgr, r->scale);
m->lt[0] = m->lt[1] = r->lt; m->lt[0] = m->lt[1] = r->lt;
wlr_output_set_transform(wlr_output, r->rr); wlr_output_set_transform(wlr_output, r->rr);
m->m.x = r->x; m->m.x = r->x;
@ -933,16 +903,12 @@ createmon(struct wl_listener *listener, void *data)
/* Set up event listeners */ /* Set up event listeners */
LISTEN(&wlr_output->events.frame, &m->frame, rendermon); LISTEN(&wlr_output->events.frame, &m->frame, rendermon);
LISTEN(&wlr_output->events.destroy, &m->destroy, cleanupmon); LISTEN(&wlr_output->events.destroy, &m->destroy, cleanupmon);
LISTEN(&wlr_output->events.request_state, &m->request_state, requestmonstate);
wlr_output_enable(wlr_output, 1); wlr_output_enable(wlr_output, 1);
if (!wlr_output_commit(wlr_output)) if (!wlr_output_commit(wlr_output))
return; 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); wl_list_insert(&mons, &m->link);
printstatus(); printstatus();
@ -1006,8 +972,12 @@ createnotify(struct wl_listener *listener, void *data)
c->surface.xdg = xdg_surface; c->surface.xdg = xdg_surface;
c->bw = borderpx; c->bw = borderpx;
LISTEN(&xdg_surface->events.map, &c->map, mapnotify); wlr_xdg_toplevel_set_wm_capabilities(xdg_surface->toplevel,
LISTEN(&xdg_surface->events.unmap, &c->unmap, unmapnotify); 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->events.destroy, &c->destroy, destroynotify);
LISTEN(&xdg_surface->toplevel->events.set_title, &c->set_title, updatetitle); LISTEN(&xdg_surface->toplevel->events.set_title, &c->set_title, updatetitle);
LISTEN(&xdg_surface->toplevel->events.request_fullscreen, &c->fullscreen, LISTEN(&xdg_surface->toplevel->events.request_fullscreen, &c->fullscreen,
@ -1074,8 +1044,6 @@ cursorframe(struct wl_listener *listener, void *data)
void void
destroydragicon(struct wl_listener *listener, void *data) 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. */ /* Focus enter isn't sent during drag, so refocus the focused node. */
focusclient(focustop(selmon), 1); focusclient(focustop(selmon), 1);
motionnotify(0); motionnotify(0);
@ -1150,20 +1118,25 @@ destroylocksurface(struct wl_listener *listener, void *data)
void void
destroynotify(struct wl_listener *listener, void *data) 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); 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->destroy.link);
wl_list_remove(&c->set_title.link); wl_list_remove(&c->set_title.link);
wl_list_remove(&c->fullscreen.link); wl_list_remove(&c->fullscreen.link);
#ifdef XWAYLAND #ifdef XWAYLAND
if (c->type != XDGShell) { 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->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 #endif
{
wl_list_remove(&c->commit.link);
wl_list_remove(&c->map.link);
wl_list_remove(&c->unmap.link);
}
free(c); free(c);
} }
@ -1177,8 +1150,8 @@ destroysessionlock(struct wl_listener *listener, void *data)
void void
destroysessionmgr(struct wl_listener *listener, void *data) destroysessionmgr(struct wl_listener *listener, void *data)
{ {
wl_list_remove(&session_lock_create_lock.link); wl_list_remove(&lock_listener.link);
wl_list_remove(&session_lock_mgr_destroy.link); wl_list_remove(&listener->link);
} }
Monitor * Monitor *
@ -1427,12 +1400,11 @@ keypress(struct wl_listener *listener, void *data)
int handled = 0; int handled = 0;
uint32_t mods = wlr_keyboard_get_modifiers(kb->wlr_keyboard); 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, /* On _press_ if there is no active screen locker,
* attempt to process a compositor keybinding. */ * attempt to process a compositor keybinding. */
if (!locked && !input_inhibit_mgr->active_inhibitor if (!locked && event->state == WL_KEYBOARD_KEY_STATE_PRESSED)
&& event->state == WL_KEYBOARD_KEY_STATE_PRESSED)
for (i = 0; i < nsyms; i++) for (i = 0; i < nsyms; i++)
handled = keybinding(mods, syms[i]) || handled; handled = keybinding(mods, syms[i]) || handled;
@ -1509,13 +1481,12 @@ locksession(struct wl_listener *listener, void *data)
wlr_session_lock_v1_destroy(session_lock); wlr_session_lock_v1_destroy(session_lock);
return; return;
} }
lock = ecalloc(1, sizeof(*lock)); lock = session_lock->data = ecalloc(1, sizeof(*lock));
focusclient(NULL, 0); focusclient(NULL, 0);
lock->scene = wlr_scene_tree_create(layers[LyrBlock]); lock->scene = wlr_scene_tree_create(layers[LyrBlock]);
cur_lock = lock->lock = session_lock; cur_lock = lock->lock = session_lock;
locked = 1; locked = 1;
session_lock->data = lock;
LISTEN(&session_lock->events.new_surface, &lock->new_surface, createlocksurface); LISTEN(&session_lock->events.new_surface, &lock->new_surface, createlocksurface);
LISTEN(&session_lock->events.destroy, &lock->destroy, destroysessionlock); LISTEN(&session_lock->events.destroy, &lock->destroy, destroysessionlock);
@ -1540,17 +1511,11 @@ mapnotify(struct wl_listener *listener, void *data)
int i; int i;
/* Create scene tree for this client and its border */ /* 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); wlr_scene_node_set_enabled(&c->scene->node, c->type != XDGShell);
c->scene_surface = c->type == XDGShell c->scene_surface = c->type == XDGShell
? wlr_scene_xdg_surface_create(c->scene, c->surface.xdg) ? wlr_scene_xdg_surface_create(c->scene, c->surface.xdg)
: wlr_scene_subsurface_tree_create(c->scene, client_surface(c)); : 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; c->scene->node.data = c->scene_surface->node.data = c;
/* Handle unmanaged clients first so we can return prior create borders */ /* 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 * typically because the user clicked on the maximize button on
* client-side decorations. dwl doesn't support maximization, but * client-side decorations. dwl doesn't support maximization, but
* to conform to xdg-shell protocol we still must send a configure. * 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. */ * wlr_xdg_surface_schedule_configure() is used to send an empty reply. */
Client *c = wl_container_of(listener, c, maximize); 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 void
@ -1658,7 +1628,7 @@ motionnotify(uint32_t time)
/* time is 0 in internal calls meant to restore pointer focus. */ /* time is 0 in internal calls meant to restore pointer focus. */
if (time) { if (time) {
IDLE_NOTIFY_ACTIVITY; wlr_idle_notifier_v1_notify_activity(idle_notifier, seat);
/* Update selmon (even while dragging a window) */ /* Update selmon (even while dragging a window) */
if (sloppyfocus) 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 /* 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 * default. This is what makes the cursor image appear when you move it
* off of a client or over its border. */ * off of a client or over its border. */
if (!surface && !seat->drag && (!cursor_image || strcmp(cursor_image, "left_ptr"))) if (!surface && !seat->drag)
wlr_xcursor_manager_set_cursor_image(cursor_mgr, (cursor_image = "left_ptr"), cursor); wlr_cursor_set_xcursor(cursor, cursor_mgr, "default");
pointerfocus(c, surface, sx, sy, time); pointerfocus(c, surface, sx, sy, time);
} }
@ -1732,7 +1702,7 @@ moveresize(const Arg *arg)
case CurMove: case CurMove:
grabcx = cursor->x - grabc->geom.x; grabcx = cursor->x - grabc->geom.x;
grabcy = cursor->y - grabc->geom.y; 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; break;
case CurResize: case CurResize:
/* Doesn't work for X11 output - the next absolute motion event /* 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, wlr_cursor_warp_closest(cursor, NULL,
grabc->geom.x + grabc->geom.width, grabc->geom.x + grabc->geom.width,
grabc->geom.y + grabc->geom.height); grabc->geom.y + grabc->geom.height);
wlr_xcursor_manager_set_cursor_image(cursor_mgr, wlr_cursor_set_xcursor(cursor, cursor_mgr, "se-resize");
(cursor_image = "bottom_right_corner"), cursor);
break; 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 /* Don't move monitors if position wouldn't change, this to avoid
* wlroots marking the output as manually configured */ * wlroots marking the output as manually configured */
if (m->m.x != config_head->state.x || m->m.y != config_head->state.y) 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); config_head->state.x, config_head->state.y);
wlr_output_set_transform(wlr_output, config_head->state.transform); wlr_output_set_transform(wlr_output, config_head->state.transform);
wlr_output_set_scale(wlr_output, config_head->state.scale); 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 */ * wlroots makes this a no-op if surface is already focused */
wlr_seat_pointer_notify_enter(seat, surface, sx, sy); wlr_seat_pointer_notify_enter(seat, surface, sx, sy);
wlr_seat_pointer_notify_motion(seat, time, sx, sy); wlr_seat_pointer_notify_motion(seat, time, sx, sy);
} }
void void
@ -1898,6 +1868,8 @@ rendermon(struct wl_listener *listener, void *data)
* generally at the output's refresh rate (e.g. 60Hz). */ * generally at the output's refresh rate (e.g. 60Hz). */
Monitor *m = wl_container_of(listener, m, frame); Monitor *m = wl_container_of(listener, m, frame);
Client *c; Client *c;
struct wlr_output_state pending = {0};
struct wlr_gamma_control_v1 *gamma_control;
struct timespec now; struct timespec now;
/* Render if no XDG clients have an outstanding resize and are visible on /* 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) wl_list_for_each(c, &clients, link)
if (c->resize && !c->isfloating && client_is_rendered_on_mon(c, m) && !client_is_stopped(c)) if (c->resize && !c->isfloating && client_is_rendered_on_mon(c, m) && !client_is_stopped(c))
goto skip; 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: skip:
/* Let clients know a frame has been rendered */ /* Let clients know a frame has been rendered */
clock_gettime(CLOCK_MONOTONIC, &now); clock_gettime(CLOCK_MONOTONIC, &now);
wlr_scene_output_send_frame_done(m->scene_output, &now); wlr_scene_output_send_frame_done(m->scene_output, &now);
wlr_output_state_finish(&pending);
} }
void void
@ -1925,10 +1923,19 @@ requeststartdrag(struct wl_listener *listener, void *data)
wlr_data_source_destroy(event->drag->source); 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 void
resize(Client *c, struct wlr_box geo, int interact) resize(Client *c, struct wlr_box geo, int interact)
{ {
struct wlr_box *bbox = interact ? &sgeom : &c->mon->w; struct wlr_box *bbox = interact ? &sgeom : &c->mon->w;
struct wlr_box clip;
client_set_bounds(c, geo.width, geo.height); client_set_bounds(c, geo.width, geo.height);
c->geom = geo; c->geom = geo;
applybounds(c, bbox); 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 */ /* this is a no-op if size hasn't changed */
c->resize = client_set_size(c, c->geom.width - 2 * c->bw, c->resize = client_set_size(c, c->geom.width - 2 * c->bw,
c->geom.height - 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 void
@ -1992,7 +2001,7 @@ run(char *startup_cmd)
* initialized, as the image/coordinates are not transformed for the * initialized, as the image/coordinates are not transformed for the
* monitor when displayed here */ * monitor when displayed here */
wlr_cursor_warp_closest(cursor, NULL, cursor->x, cursor->y); 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 /* Run the Wayland event loop. This does not return until you exit the
* compositor. Starting the backend rigged up all of the necessary event * 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 */ * event, which will result in the client requesting set the cursor surface */
if (cursor_mode != CurNormal && cursor_mode != CurPressed) if (cursor_mode != CurNormal && cursor_mode != CurPressed)
return; return;
cursor_image = NULL;
/* This can be sent by any client, so we check to make sure this one is /* 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 * 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 * 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); 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 void
setfloating(Client *c, int floating) setfloating(Client *c, int floating)
{ {
@ -2057,6 +2079,15 @@ setfullscreen(Client *c, int fullscreen)
printstatus(); 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 void
setlayout(const Arg *arg) setlayout(const Arg *arg)
{ {
@ -2150,12 +2181,8 @@ setup(void)
/* The backend is a wlroots feature which abstracts the underlying input and /* The backend is a wlroots feature which abstracts the underlying input and
* output hardware. The autocreate option will choose the most suitable * output hardware. The autocreate option will choose the most suitable
* backend based on the current environment, such as opening an X11 window * 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 * if an X11 server is running. */
* to pass in a custom renderer if wlr_renderer doesn't meet your needs. The if (!(backend = wlr_backend_autocreate(dpy, &session)))
* 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)))
die("couldn't create backend"); die("couldn't create backend");
/* Initialize the scene graph used to lay out windows */ /* Initialize the scene graph used to lay out windows */
@ -2165,12 +2192,30 @@ setup(void)
drag_icon = wlr_scene_tree_create(&scene->tree); drag_icon = wlr_scene_tree_create(&scene->tree);
wlr_scene_node_place_below(&drag_icon->node, &layers[LyrBlock]->node); 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))) if (!(drw = wlr_renderer_autocreate(backend)))
die("couldn't create renderer"); 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))) if (!(alloc = wlr_allocator_autocreate(backend, drw)))
die("couldn't create allocator"); 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 * 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, * the clients cannot set the selection directly without compositor approval,
* see the setsel() function. */ * 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_export_dmabuf_manager_v1_create(dpy);
wlr_screencopy_manager_v1_create(dpy); wlr_screencopy_manager_v1_create(dpy);
wlr_data_control_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_primary_selection_v1_device_manager_create(dpy);
wlr_viewporter_create(dpy); wlr_viewporter_create(dpy);
wlr_single_pixel_buffer_manager_v1_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 */ /* Initializes the interface used to implement urgency hints */
activation = wlr_xdg_activation_v1_create(dpy); 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 /* Creates an output layout, which a wlroots utility for working with an
* arrangement of screens in a physical layout. */ * arrangement of screens in a physical layout. */
output_layout = wlr_output_layout_create(); 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); wlr_xdg_output_manager_v1_create(dpy, output_layout);
/* Configure a listener to be notified when new outputs are available on the /* Configure a listener to be notified when new outputs are available on the
* backend. */ * backend. */
wl_list_init(&mons); 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 * Wayland protocol which is used for application windows. For more
* detail on shells, refer to the article: * detail on shells, refer to the article:
* *
@ -2215,22 +2263,20 @@ setup(void)
wl_list_init(&clients); wl_list_init(&clients);
wl_list_init(&fstack); 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_notifier = wlr_idle_notifier_v1_create(dpy);
idle_inhibit_mgr = wlr_idle_inhibit_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); 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.new_lock, &lock_listener);
wl_signal_add(&session_lock_mgr->events.destroy, &session_lock_mgr_destroy); LISTEN_STATIC(&session_lock_mgr->events.destroy, destroysessionmgr);
locked_bg = wlr_scene_rect_create(layers[LyrBlock], sgeom.width, sgeom.height, locked_bg = wlr_scene_rect_create(layers[LyrBlock], sgeom.width, sgeom.height,
(float [4]){0.1, 0.1, 0.1, 1.0}); (float [4]){0.1, 0.1, 0.1, 1.0});
wlr_scene_node_set_enabled(&locked_bg->node, 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_create(dpy),
WLR_SERVER_DECORATION_MANAGER_MODE_SERVER); WLR_SERVER_DECORATION_MANAGER_MODE_SERVER);
xdg_decoration_mgr = 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); LISTEN_STATIC(&xdg_decoration_mgr->events.new_toplevel_decoration, createdecoration);
/* /*
* Creates a cursor, which is a wlroots utility for tracking the cursor * 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 * 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 * 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 * 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 * moving the cursor around. More detail on this process is described in
* input handling blog post:
*
* https://drewdevault.com/2018/07/17/Input-handling-in-wlroots.html * https://drewdevault.com/2018/07/17/Input-handling-in-wlroots.html
* *
* And more comments are sprinkled throughout the notify functions above. * And more comments are sprinkled throughout the notify functions above.
*/ */
wl_signal_add(&cursor->events.motion, &cursor_motion); LISTEN_STATIC(&cursor->events.motion, motionrelative);
wl_signal_add(&cursor->events.motion_absolute, &cursor_motion_absolute); LISTEN_STATIC(&cursor->events.motion_absolute, motionabsolute);
wl_signal_add(&cursor->events.button, &cursor_button); LISTEN_STATIC(&cursor->events.button, buttonpress);
wl_signal_add(&cursor->events.axis, &cursor_axis); LISTEN_STATIC(&cursor->events.axis, axisnotify);
wl_signal_add(&cursor->events.frame, &cursor_frame); 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 * 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. * let us know when new input devices are available on the backend.
*/ */
wl_list_init(&keyboards); 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); virtual_keyboard_mgr = wlr_virtual_keyboard_manager_v1_create(dpy);
wl_signal_add(&virtual_keyboard_mgr->events.new_virtual_keyboard, LISTEN_STATIC(&virtual_keyboard_mgr->events.new_virtual_keyboard, virtualkeyboard);
&new_virtual_keyboard);
seat = wlr_seat_create(dpy, "seat0"); seat = wlr_seat_create(dpy, "seat0");
wl_signal_add(&seat->events.request_set_cursor, &request_cursor); LISTEN_STATIC(&seat->events.request_set_cursor, setcursor);
wl_signal_add(&seat->events.request_set_selection, &request_set_sel); LISTEN_STATIC(&seat->events.request_set_selection, setsel);
wl_signal_add(&seat->events.request_set_primary_selection, &request_set_psel); LISTEN_STATIC(&seat->events.request_set_primary_selection, setpsel);
wl_signal_add(&seat->events.request_start_drag, &request_start_drag); LISTEN_STATIC(&seat->events.request_start_drag, requeststartdrag);
wl_signal_add(&seat->events.start_drag, &start_drag); LISTEN_STATIC(&seat->events.start_drag, startdrag);
output_mgr = wlr_output_manager_v1_create(dpy); output_mgr = wlr_output_manager_v1_create(dpy);
wl_signal_add(&output_mgr->events.apply, &output_mgr_apply); LISTEN_STATIC(&output_mgr->events.apply, outputmgrapply);
wl_signal_add(&output_mgr->events.test, &output_mgr_test); LISTEN_STATIC(&output_mgr->events.test, outputmgrtest);
wlr_scene_set_presentation(scene, wlr_presentation_create(dpy, backend)); wlr_scene_set_presentation(scene, wlr_presentation_create(dpy, backend));
@ -2305,8 +2351,8 @@ setup(void)
*/ */
xwayland = wlr_xwayland_create(dpy, compositor, 1); xwayland = wlr_xwayland_create(dpy, compositor, 1);
if (xwayland) { if (xwayland) {
wl_signal_add(&xwayland->events.ready, &xwayland_ready); LISTEN_STATIC(&xwayland->events.ready, xwaylandready);
wl_signal_add(&xwayland->events.new_surface, &new_xwayland_surface); LISTEN_STATIC(&xwayland->events.new_surface, createnotifyx11);
setenv("DISPLAY", xwayland->display_name, 1); setenv("DISPLAY", xwayland->display_name, 1);
} else { } else {
@ -2333,8 +2379,8 @@ startdrag(struct wl_listener *listener, void *data)
if (!drag->icon) if (!drag->icon)
return; return;
drag->icon->data = &wlr_scene_subsurface_tree_create(drag_icon, drag->icon->surface)->node; drag->icon->data = &wlr_scene_drag_icon_create(drag_icon, drag->icon)->node;
wl_signal_add(&drag->icon->events.destroy, &drag_icon_destroy); LISTEN_STATIC(&drag->icon->events.destroy, destroydragicon);
} }
void void
@ -2485,7 +2531,6 @@ unmapnotify(struct wl_listener *listener, void *data)
wl_list_remove(&c->flink); wl_list_remove(&c->flink);
} }
wl_list_remove(&c->commit.link);
wlr_scene_node_destroy(&c->scene->node); wlr_scene_node_destroy(&c->scene->node);
printstatus(); printstatus();
motionnotify(0); motionnotify(0);
@ -2524,7 +2569,6 @@ updatemons(struct wl_listener *listener, void *data)
if (m->wlr_output->enabled if (m->wlr_output->enabled
&& !wlr_output_layout_get(output_layout, m->wlr_output)) && !wlr_output_layout_get(output_layout, m->wlr_output))
wlr_output_layout_add_auto(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 */ /* Now that we update the output layout we can get its box */
wlr_output_layout_get_box(output_layout, NULL, &sgeom); 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); config_head = wlr_output_configuration_head_v1_create(config, m->wlr_output);
/* Get the effective monitor geometry to use for surfaces */ /* 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->m);
wlr_output_layout_get_box(output_layout, m->wlr_output, &(m->w)); m->w = m->m;
wlr_scene_output_set_position(m->scene_output, m->m.x, m->m.y); 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_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); arrangelayers(m);
/* Don't move clients to the left output when plugging monitors */ /* Don't move clients to the left output when plugging monitors */
arrange(m); 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.enabled = 1;
config_head->state.mode = m->wlr_output->current_mode; config_head->state.mode = m->wlr_output->current_mode;
config_head->state.x = m->m.x; config_head->state.x = m->m.x;
@ -2565,7 +2613,7 @@ updatemons(struct wl_listener *listener, void *data)
if (selmon && selmon->wlr_output->enabled) { if (selmon && selmon->wlr_output->enabled) {
wl_list_for_each(c, &clients, link) 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); setmon(c, selmon, c->tags);
focusclient(focustop(selmon), 1); focusclient(focustop(selmon), 1);
if (selmon->lock_surface) { 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); 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)) if (!c || c == focustop(selmon))
return; return;
if (client_is_mapped(c)) if (client_surface(c)->mapped)
client_set_border_color(c, urgentcolor); client_set_border_color(c, urgentcolor);
c->isurgent = 1; c->isurgent = 1;
printstatus(); printstatus();
@ -2643,7 +2698,7 @@ xytonode(double x, double y, struct wlr_surface **psurface,
continue; continue;
if (node->type == WLR_SCENE_NODE_BUFFER) 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; wlr_scene_buffer_from_node(node))->surface;
/* Walk the tree to find a node that knows the client */ /* Walk the tree to find a node that knows the client */
for (pnode = node; pnode && !c; pnode = &pnode->parent->node) 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); 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 void
configurex11(struct wl_listener *listener, void *data) configurex11(struct wl_listener *listener, void *data)
{ {
@ -2729,8 +2793,8 @@ createnotifyx11(struct wl_listener *listener, void *data)
c->bw = borderpx; c->bw = borderpx;
/* Listen to the various events it can emit */ /* Listen to the various events it can emit */
LISTEN(&xsurface->events.map, &c->map, mapnotify); LISTEN(&xsurface->events.associate, &c->associate, associatex11);
LISTEN(&xsurface->events.unmap, &c->unmap, unmapnotify); LISTEN(&xsurface->events.dissociate, &c->dissociate, dissociatex11);
LISTEN(&xsurface->events.request_activate, &c->activate, activatex11); LISTEN(&xsurface->events.request_activate, &c->activate, activatex11);
LISTEN(&xsurface->events.request_configure, &c->configure, configurex11); LISTEN(&xsurface->events.request_configure, &c->configure, configurex11);
LISTEN(&xsurface->events.set_hints, &c->set_hints, sethints); 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); 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 xcb_atom_t
getatom(xcb_connection_t *xc, const char *name) getatom(xcb_connection_t *xc, const char *name)
{ {
@ -2756,12 +2828,13 @@ void
sethints(struct wl_listener *listener, void *data) sethints(struct wl_listener *listener, void *data)
{ {
Client *c = wl_container_of(listener, c, set_hints); Client *c = wl_container_of(listener, c, set_hints);
struct wlr_surface *surface = client_surface(c);
if (c == focustop(selmon)) if (c == focustop(selmon))
return; return;
c->isurgent = xcb_icccm_wm_hints_get_urgency(c->surface.xwayland->hints); 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); client_set_border_color(c, urgentcolor);
printstatus(); printstatus();
@ -2789,7 +2862,7 @@ xwaylandready(struct wl_listener *listener, void *data)
wlr_xwayland_set_seat(xwayland, seat); wlr_xwayland_set_seat(xwayland, seat);
/* Set the default XWayland cursor to match the rest of dwl. */ /* 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, wlr_xwayland_set_cursor(xwayland,
xcursor->images[0]->buffer, xcursor->images[0]->width * 4, xcursor->images[0]->buffer, xcursor->images[0]->width * 4,
xcursor->images[0]->width, xcursor->images[0]->height, xcursor->images[0]->width, xcursor->images[0]->height,