7 Commits

Author SHA1 Message Date
Zuki Air 61e198d7a1 fix crashes with windows changing their title after unmapnotify is called 2025-08-05 20:35:15 +01:00
Zuki Air 9859268ca3 switch to wlroots-0.20 in config.mk 2025-08-05 16:48:36 +01:00
Zuki Air f43772a20d update foreign_toplevel_state on title change
this commit means that in the xdg-desktop-portal-wlr picker it should
show up to date title names rather then the one the client spawned with
2025-08-05 14:30:38 +01:00
Zuki Air 8b27e5c859 switch to using union for *image_capture_tree and *image_capture_scene_surface in screencapture
only image_capture_tree is needed for screensharing single wayland
windows and only image_capture_scene_surface is needed for screensharing
single xwayland windows. Making them into a union makes sense for
reducing memory usage.
2025-08-05 14:09:11 +01:00
Zuki Air 32126eb87b fix xwayland screenshare 2025-08-05 13:52:58 +01:00
Zuki Air 4087f8a3c8 add wlr_ext_foreign_toplevel allowing screenshare of single windows. 2025-08-04 18:41:56 +01:00
Leonardo Hernández Hernández db2cf6624e add support for ext-image-copy-capture-v1 and ext-image-capture-source-v1 (wlroots!4545)
References: https://gitlab.freedesktop.org/wlroots/wlroots/-/merge_requests/4545
2025-08-04 16:35:49 +01:00
5 changed files with 146 additions and 83 deletions
+4
View File
@@ -20,6 +20,7 @@ all: dwl
dwl: dwl.o util.o
$(CC) dwl.o util.o $(DWLCFLAGS) $(LDFLAGS) $(LDLIBS) -o $@
dwl.o: dwl.c client.h config.h config.mk cursor-shape-v1-protocol.h \
ext-image-copy-capture-v1-protocol.h \
pointer-constraints-unstable-v1-protocol.h wlr-layer-shell-unstable-v1-protocol.h \
wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h
util.o: util.c util.h
@@ -33,6 +34,9 @@ WAYLAND_PROTOCOLS = `$(PKG_CONFIG) --variable=pkgdatadir wayland-protocols`
cursor-shape-v1-protocol.h:
$(WAYLAND_SCANNER) enum-header \
$(WAYLAND_PROTOCOLS)/staging/cursor-shape/cursor-shape-v1.xml $@
ext-image-copy-capture-v1-protocol.h:
$(WAYLAND_SCANNER) enum-header \
$(WAYLAND_PROTOCOLS)/staging/ext-image-copy-capture/ext-image-copy-capture-v1.xml $@
pointer-constraints-unstable-v1-protocol.h:
$(WAYLAND_SCANNER) enum-header \
$(WAYLAND_PROTOCOLS)/unstable/pointer-constraints/pointer-constraints-unstable-v1.xml $@
+16 -31
View File
@@ -1,13 +1,12 @@
# dwl - dwm for Wayland
Join us on our [Discord server]
Or Matrix: [#dwl-official:matrix.org]
Or on our IRC channel: [#dwl on Libera Chat]
Join us on our IRC channel: [#dwl on Libera Chat]
Or on the community-maintained [Discord server].
dwl is a compact, hackable compositor for [Wayland] based on [wlroots]. It is
intended to fill the same space in the Wayland world that [dwm] does in X11,
intended to fill the same space in the Wayland world that dwm does in X11,
primarily in terms of functionality, and secondarily in terms of
philosophy. Like [dwm], dwl is:
philosophy. Like dwm, dwl is:
- Easy to understand, hack on, and extend with patches
- One C source file (or a very small number) configurable via `config.h`
@@ -24,13 +23,11 @@ with each release on the [release] page
### Development branch [main]
Active development progresses on the `main` branch. The `main` branch is built
against the latest release of [wlroots]. PRs should target this branch unless they
depend on functionality that is not in the current release of `wlroots`.
### Preview branch [wlroots-next]
The `wlroots-next` branch is built against the git version of [wlroots], which
is unstable and changes frequently. PRs requiring functionality from the git
version of `wlroots` should target this branch.
against a late (and often changing) git commit of wlroots. While the adventurous
are welcome to use `main`, it is a rocky road. Using `main` requires that the
user be willing to chase git commits of wlroots. Testing development pull
requests may involve merging unmerged pull requests in [wlroots]' git repository
and/or git commits of wayland.
### Building dwl
dwl has the following dependencies:
@@ -58,11 +55,11 @@ To enable XWayland, you should uncomment its flags in `config.mk`.
## Configuration
All configuration is done by editing `config.h` and recompiling, in the same
manner as [dwm]. There is no way to separately restart the window manager in
manner as dwm. There is no way to separately restart the window manager in
Wayland without restarting the entire display server, so any changes will take
effect the next time dwl is executed.
As in the [dwm] community, we encourage users to share patches they have
As in the dwm community, we encourage users to share patches they have
created. Check out the [dwl-patches] repository!
## Running dwl
@@ -121,26 +118,17 @@ script with the line
To get a list of status bars that work with dwl consult our [wiki].
### (Known) Java nonreparenting WM issue
Certain IDEs don't display correctly unless an environmental variable for Java AWT
indicates that the WM is nonreparenting.
For some Java AWT-based IDEs, such as Xilinx Vivado and Microchip MPLAB X, the
following environment variable needs to be set before running the IDE or dwl:
export _JAVA_AWT_WM_NONREPARENTING=1
## Replacements for X applications
You can find a [list of useful resources on our wiki].
## Background
dwl is not meant to provide every feature under the sun. Instead, like [dwm], it
dwl is not meant to provide every feature under the sun. Instead, like dwm, it
sticks to features which are necessary, simple, and straightforward to implement
given the base on which it is built. Implemented default features are:
- Any features provided by [dwm]/Xlib: simple window borders, tags, keybindings,
- Any features provided by dwm/Xlib: simple window borders, tags, keybindings,
client rules, mouse move/resize. Providing a built-in status bar is an
exception to this goal, to avoid dependencies on font rendering and/or drawing
libraries when an external bar could work well.
@@ -157,10 +145,10 @@ given the base on which it is built. Implemented default features are:
- Layer shell popups (used by Waybar)
- Damage tracking provided by scenegraph API
Given the Wayland architecture, dwl has to implement features from [dwm] **and**
Given the Wayland architecture, dwl has to implement features from dwm **and**
the xorg-server. Because of this, it is impossible to maintain the original
project goal of 2000 SLOC and have a reasonably complete compositor with
features comparable to [dwm]. However, this does not mean that the code will grow
features comparable to dwm. However, this does not mean that the code will grow
indiscriminately. We will try to keep the code as small as possible.
Features under consideration (possibly as patches) are:
@@ -184,7 +172,7 @@ developers. This was made possible in many cases by looking at how sway
accomplished something, then trying to do the same in as suckless a way as
possible.
Many thanks to suckless.org and the [dwm] developers and community for the
Many thanks to suckless.org and the dwm developers and community for the
inspiration, and to the various contributors to the project, including:
- **Devin J. Pohly for creating and nurturing the fledgling project**
@@ -195,7 +183,6 @@ inspiration, and to the various contributors to the project, including:
[wlroots]: https://gitlab.freedesktop.org/wlroots
[dwm]: https://dwm.suckless.org/
[`systemd --user`]: https://wiki.archlinux.org/title/Systemd/User
[#dwl on Libera Chat]: https://web.libera.chat/?channels=#dwl
[0.7-rc1]: https://codeberg.org/dwl/dwl/releases/tag/v0.7-rc1
@@ -205,7 +192,6 @@ inspiration, and to the various contributors to the project, including:
[dwl-patches]: https://codeberg.org/dwl/dwl-patches
[list of useful resources on our wiki]: https://codeberg.org/dwl/dwl/wiki/Home#migrating-from-x
[main]: https://codeberg.org/dwl/dwl/src/branch/main
[wlroots-next]: https://codeberg.org/dwl/dwl/src/branch/wlroots-next
[release]: https://codeberg.org/dwl/dwl/releases
[runit]: http://smarden.org/runit/faq.html#userservices
[s6]: https://skarnet.org/software/s6/
@@ -213,4 +199,3 @@ inspiration, and to the various contributors to the project, including:
[wiki]: https://codeberg.org/dwl/dwl/wiki/Home#compatible-status-bars
[Discord server]: https://discord.gg/jJxZnrGPWN
[Wayland]: https://wayland.freedesktop.org/
[#dwl-official:matrix.org]: https://matrix.to/#/#dwl-official:matrix.org
+14 -10
View File
@@ -20,11 +20,12 @@ static const float fullscreen_bg[] = {0.0f, 0.0f, 0.0f, 1.0f}; /* You ca
/* logging */
static int log_level = WLR_ERROR;
/* NOTE: ALWAYS keep a rule declared even if you don't use rules (e.g leave at least one example) */
static const Rule rules[] = {
/* app_id title tags mask isfloating monitor */
/* examples: */
{ "Gimp_EXAMPLE", NULL, 0, 1, -1 }, /* Start on currently visible tags floating, not tiled */
{ "firefox_EXAMPLE", NULL, 1 << 8, 0, -1 }, /* Start on ONLY tag "9" */
/* default/example rule: can be changed but cannot be eliminated; at least one rule must exist */
};
/* layout(s) */
@@ -37,14 +38,17 @@ static const Layout layouts[] = {
/* monitors */
/* (x=-1, y=-1) is reserved as an "autoconfigure" monitor position indicator
* WARNING: negative values other than (-1, -1) cause problems with Xwayland clients due to
* https://gitlab.freedesktop.org/xorg/xserver/-/issues/899 */
* WARNING: negative values other than (-1, -1) cause problems with Xwayland clients
* https://gitlab.freedesktop.org/xorg/xserver/-/issues/899
*/
/* NOTE: ALWAYS add a fallback rule, even if you are completely sure it won't be used */
static const MonitorRule monrules[] = {
/* name mfact nmaster scale layout rotate/reflect x y
* example of a HiDPI laptop monitor:
{ "eDP-1", 0.5f, 1, 2, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 }, */
/* name mfact nmaster scale layout rotate/reflect x y */
/* example of a HiDPI laptop monitor:
{ "eDP-1", 0.5f, 1, 2, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 },
*/
/* defaults */
{ NULL, 0.55f, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 },
/* default monitor rule: can be changed but cannot be eliminated; at least one monitor rule must exist */
};
/* keyboard */
@@ -119,7 +123,7 @@ static const char *termcmd[] = { "foot", NULL };
static const char *menucmd[] = { "wmenu-run", NULL };
static const Key keys[] = {
/* Note that Shift changes certain key codes: 2 -> at, etc. */
/* Note that Shift changes certain key codes: c -> C, 2 -> at, etc. */
/* modifier key function argument */
{ MODKEY, XKB_KEY_p, spawn, {.v = menucmd} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return, spawn, {.v = termcmd} },
@@ -131,7 +135,7 @@ static const Key keys[] = {
{ MODKEY, XKB_KEY_l, setmfact, {.f = +0.05f} },
{ MODKEY, XKB_KEY_Return, zoom, {0} },
{ MODKEY, XKB_KEY_Tab, view, {0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_c, killclient, {0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_C, killclient, {0} },
{ MODKEY, XKB_KEY_t, setlayout, {.v = &layouts[0]} },
{ MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} },
{ MODKEY, XKB_KEY_m, setlayout, {.v = &layouts[2]} },
@@ -153,7 +157,7 @@ static const Key keys[] = {
TAGKEYS( XKB_KEY_7, XKB_KEY_ampersand, 6),
TAGKEYS( XKB_KEY_8, XKB_KEY_asterisk, 7),
TAGKEYS( XKB_KEY_9, XKB_KEY_parenleft, 8),
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_q, quit, {0} },
{ MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Q, quit, {0} },
/* Ctrl-Alt-Backspace and Ctrl-Alt-Fx used to be handled by X server */
{ WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_Terminate_Server, quit, {0} },
+2 -2
View File
@@ -8,8 +8,8 @@ PREFIX = /usr/local
MANDIR = $(PREFIX)/share/man
DATADIR = $(PREFIX)/share
WLR_INCS = `$(PKG_CONFIG) --cflags wlroots-0.19`
WLR_LIBS = `$(PKG_CONFIG) --libs wlroots-0.19`
WLR_INCS = `$(PKG_CONFIG) --cflags wlroots-0.20`
WLR_LIBS = `$(PKG_CONFIG) --libs wlroots-0.20`
# Allow using an alternative wlroots installation
# This has to have all the includes required by wlroots, e.g:
+77 -7
View File
@@ -24,7 +24,8 @@
#include <wlr/types/wlr_data_device.h>
#include <wlr/types/wlr_drm.h>
#include <wlr/types/wlr_export_dmabuf_v1.h>
#include <wlr/types/wlr_ext_data_control_v1.h>
#include <wlr/types/wlr_ext_image_capture_source_v1.h>
#include <wlr/types/wlr_ext_image_copy_capture_v1.h>
#include <wlr/types/wlr_fractional_scale_v1.h>
#include <wlr/types/wlr_gamma_control_v1.h>
#include <wlr/types/wlr_idle_inhibit_v1.h>
@@ -60,6 +61,8 @@
#include <wlr/types/wlr_xdg_decoration_v1.h>
#include <wlr/types/wlr_xdg_output_v1.h>
#include <wlr/types/wlr_xdg_shell.h>
#include <wlr/types/wlr_ext_image_capture_source_v1.h>
#include <wlr/types/wlr_ext_foreign_toplevel_list_v1.h>
#include <wlr/util/log.h>
#include <wlr/util/region.h>
#include <xkbcommon/xkbcommon.h>
@@ -121,6 +124,13 @@ typedef struct {
} surface;
struct wlr_xdg_toplevel_decoration_v1 *decoration;
struct wl_listener commit;
struct wlr_scene *image_capture_scene;
struct wlr_ext_image_capture_source_v1 *image_capture_source;
struct wlr_ext_foreign_toplevel_handle_v1 *ext_foreign_toplevel;
union {
struct wlr_scene_tree *image_capture_tree;
struct wlr_scene_surface *image_capture_scene_surface;
} capture;
struct wl_listener map;
struct wl_listener maximize;
struct wl_listener unmap;
@@ -372,6 +382,8 @@ static struct wlr_session *session;
static struct wlr_xdg_shell *xdg_shell;
static struct wlr_xdg_activation_v1 *activation;
static struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1 *ext_foreign_toplevel_image_capture_source_manager_v1;
static struct wl_listener new_foreign_toplevel_capture_request;
static struct wlr_xdg_decoration_manager_v1 *xdg_decoration_mgr;
static struct wl_list clients; /* tiling order */
static struct wl_list fstack; /* focus order */
@@ -383,6 +395,7 @@ static struct wlr_virtual_keyboard_manager_v1 *virtual_keyboard_mgr;
static struct wlr_virtual_pointer_manager_v1 *virtual_pointer_mgr;
static struct wlr_cursor_shape_manager_v1 *cursor_shape_mgr;
static struct wlr_output_power_manager_v1 *power_mgr;
static struct wlr_ext_foreign_toplevel_list_v1 *foreign_toplevel_list;
static struct wlr_pointer_constraints_v1 *pointer_constraints;
static struct wlr_relative_pointer_manager_v1 *relative_pointer_mgr;
@@ -586,7 +599,7 @@ arrangelayers(Monitor *m)
arrange(m);
}
/* Arrange non-exclusive surfaces from top->bottom */
/* Arrange non-exlusive surfaces from top->bottom */
for (i = 3; i >= 0; i--)
arrangelayer(m, &m->layers[i], &usable_area, 0);
@@ -783,6 +796,7 @@ cleanuplisteners(void)
wl_list_remove(&request_start_drag.link);
wl_list_remove(&start_drag.link);
wl_list_remove(&new_session_lock.link);
wl_list_remove(&new_foreign_toplevel_capture_request.link);
#ifdef XWAYLAND
wl_list_remove(&new_xwayland_surface.link);
wl_list_remove(&xwayland_ready.link);
@@ -1124,6 +1138,7 @@ createnotify(struct wl_listener *listener, void *data)
struct wlr_xdg_toplevel *toplevel = data;
Client *c = NULL;
/* Allocate a Client for this surface */
c = toplevel->base->data = ecalloc(1, sizeof(*c));
c->surface.xdg = toplevel->base;
@@ -1617,8 +1632,7 @@ keybinding(uint32_t mods, xkb_keysym_t sym)
const Key *k;
for (k = keys; k < END(keys); k++) {
if (CLEANMASK(mods) == CLEANMASK(k->mod)
&& xkb_keysym_to_lower(sym) == xkb_keysym_to_lower(k->keysym)
&& k->func) {
&& sym == k->keysym && k->func) {
k->func(&k->arg);
return 1;
}
@@ -1744,6 +1758,12 @@ mapnotify(struct wl_listener *listener, void *data)
Monitor *m;
int i;
struct wlr_ext_foreign_toplevel_handle_v1_state foreign_toplevel_state = {
.app_id = client_get_appid(c),
.title = client_get_title(c),
};
/* Create scene tree for this client and its border */
c->scene = client_surface(c)->data = wlr_scene_tree_create(layers[LyrTile]);
/* Enabled later by a call to arrange() */
@@ -1752,6 +1772,18 @@ mapnotify(struct wl_listener *listener, void *data)
? wlr_scene_xdg_surface_create(c->scene, c->surface.xdg)
: wlr_scene_subsurface_tree_create(c->scene, client_surface(c));
c->scene->node.data = c->scene_surface->node.data = c;
printf("image capture tree\n");
c->image_capture_scene = wlr_scene_create();
c->ext_foreign_toplevel = wlr_ext_foreign_toplevel_handle_v1_create(foreign_toplevel_list,&foreign_toplevel_state);
c->ext_foreign_toplevel->data = c;
if (c->type == XDGShell) {
c->capture.image_capture_tree = wlr_scene_xdg_surface_create(&c->image_capture_scene->tree, c->surface.xdg);
#ifdef XWAYLAND
} else { /* xwayland */
c->capture.image_capture_scene_surface = wlr_scene_surface_create(&c->image_capture_scene->tree, c->surface.xwayland->surface);
#endif
}
client_get_geometry(c, &c->geom);
@@ -2134,7 +2166,7 @@ powermgrsetmode(struct wl_listener *listener, void *data)
if (!m)
return;
m->gamma_lut_changed = 1; /* Reapply gamma LUT when re-enabling the output */
m->gamma_lut_changed = 1; /* Reapply gamma LUT when re-enabling the ouput */
wlr_output_state_set_enabled(&state, event->mode);
wlr_output_commit_state(m->wlr_output, &state);
@@ -2442,6 +2474,20 @@ setsel(struct wl_listener *listener, void *data)
struct wlr_seat_request_set_selection_event *event = data;
wlr_seat_set_selection(seat, event->source, event->serial);
}
static void handle_new_foreign_toplevel_capture_request(struct wl_listener *listener, void *data) {
struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request *request = data;
Client *view = request->toplevel_handle->data;
if (view->image_capture_source == NULL) {
view->image_capture_source = wlr_ext_image_capture_source_v1_create_with_scene_node(
&view->image_capture_scene->tree.node, event_loop, alloc, drw);
if (view->image_capture_source == NULL) {
return;
}
}
wlr_ext_foreign_toplevel_image_capture_source_manager_v1_request_accept(request, view->image_capture_source);
}
void
setup(void)
@@ -2456,7 +2502,7 @@ setup(void)
wlr_log_init(log_level, NULL);
/* The Wayland display is managed by libwayland. It handles accepting
* clients from the Unix socket, managing Wayland globals, and so on. */
* clients from the Unix socket, manging Wayland globals, and so on. */
dpy = wl_display_create();
event_loop = wl_display_get_event_loop(dpy);
@@ -2518,8 +2564,9 @@ setup(void)
wlr_data_device_manager_create(dpy);
wlr_export_dmabuf_manager_v1_create(dpy);
wlr_screencopy_manager_v1_create(dpy);
wlr_ext_image_copy_capture_manager_v1_create(dpy, 1);
wlr_ext_output_image_capture_source_manager_v1_create(dpy, 1);
wlr_data_control_manager_v1_create(dpy);
wlr_ext_data_control_manager_v1_create(dpy, 1);
wlr_primary_selection_v1_device_manager_create(dpy);
wlr_viewporter_create(dpy);
wlr_single_pixel_buffer_manager_v1_create(dpy);
@@ -2575,6 +2622,11 @@ setup(void)
(float [4]){0.1f, 0.1f, 0.1f, 1.0f});
wlr_scene_node_set_enabled(&locked_bg->node, 0);
foreign_toplevel_list = wlr_ext_foreign_toplevel_list_v1_create(dpy,1);
ext_foreign_toplevel_image_capture_source_manager_v1 = wlr_ext_foreign_toplevel_image_capture_source_manager_v1_create(dpy, 1);
new_foreign_toplevel_capture_request.notify = handle_new_foreign_toplevel_capture_request;
wl_signal_add(&ext_foreign_toplevel_image_capture_source_manager_v1->events.new_request,&new_foreign_toplevel_capture_request);
/* Use decoration protocols to negotiate server-side decorations */
wlr_server_decoration_manager_set_default_mode(
wlr_server_decoration_manager_create(dpy),
@@ -2832,7 +2884,18 @@ unmapnotify(struct wl_listener *listener, void *data)
setmon(c, NULL, 0);
wl_list_remove(&c->flink);
}
if (c->ext_foreign_toplevel != NULL) {
wlr_ext_foreign_toplevel_handle_v1_destroy(c->ext_foreign_toplevel);
c->ext_foreign_toplevel = NULL;
}
#ifdef XWAYLAND
if (c->type != XDGShell && c->capture.image_capture_scene_surface) {
wlr_scene_node_destroy(&c->capture.image_capture_scene_surface->buffer->node);
c->capture.image_capture_scene_surface = NULL;
}
#endif
wlr_scene_node_destroy(&c->image_capture_scene->tree.node);
wlr_scene_node_destroy(&c->scene->node);
printstatus();
motionnotify(0, NULL, 0, 0, 0, 0);
@@ -2950,6 +3013,13 @@ updatetitle(struct wl_listener *listener, void *data)
Client *c = wl_container_of(listener, c, set_title);
if (c == focustop(c->mon))
printstatus();
if (c->ext_foreign_toplevel != NULL) {
struct wlr_ext_foreign_toplevel_handle_v1_state foreign_toplevel_state = {
.app_id = client_get_appid(c),
.title = client_get_title(c),
};
wlr_ext_foreign_toplevel_handle_v1_update_state(c->ext_foreign_toplevel,&foreign_toplevel_state);
}
}
void