From 1fed2e4e7d8300717df59e31ea7c69e8930d3ec2 Mon Sep 17 00:00:00 2001 From: th3r00t Date: Fri, 19 Sep 2025 11:59:54 -0400 Subject: [PATCH] Working on dwlb --- flake.nix | 3 + home/.local/usr/bin/dwlb.sh | 7 +- modules/dwl/patches/ipc.patch | 597 ++++++++++++++++++ modules/dwl/protocols/dwl-ipc-unstable-v2.xml | 181 ++++++ modules/dwl/source/Makefile | 14 +- modules/dwl/source/config.def.h | 3 + modules/dwl/source/config.def.h.orig | 130 +++- modules/dwl/source/dwl.c | 257 +++++++- modules/dwl/source/dwl.c.orig | 157 ++++- .../source/protocols/dwl-ipc-unstable-v2.xml | 181 ++++++ 10 files changed, 1456 insertions(+), 74 deletions(-) create mode 100644 modules/dwl/patches/ipc.patch create mode 100644 modules/dwl/protocols/dwl-ipc-unstable-v2.xml create mode 100644 modules/dwl/source/protocols/dwl-ipc-unstable-v2.xml diff --git a/flake.nix b/flake.nix index 79fb5c7..0d0415d 100644 --- a/flake.nix +++ b/flake.nix @@ -53,6 +53,9 @@ postPatch = '' # Copy the current config.def.h to config.h cp config.def.h config.h + # Ensure protocols directory exists and has the right files + mkdir -p protocols + cp -r ${./modules/dwl/source/protocols}/* protocols/ || true ''; buildInputs = old.buildInputs or[] ++ [ final.wlroots ]; nativeBuildInputs = old.nativeBuildInputs or[] ++ [ final.pkg-config final.wayland-scanner final.wayland-protocols ]; diff --git a/home/.local/usr/bin/dwlb.sh b/home/.local/usr/bin/dwlb.sh index 64176df..6abfb73 100755 --- a/home/.local/usr/bin/dwlb.sh +++ b/home/.local/usr/bin/dwlb.sh @@ -1,8 +1,3 @@ #!/usr/bin/env bash -for output in $(wlr-randr | grep '^[^ ]' | cut -d' ' -f1); do - dwlb -middle-bg-color '#16181a' -active-fg-color '#ffffff' \ - -active-bg-color '#5ea1ff' -inactive-fg-color '#ffffff' \ - -inactive-bg-color '#3c4048' -urgent-fg-color '#ffffff' \ - -urgent-bg-color '#ff6e5e' -font 'FiraCode Nerd Font Mono:size=10' & -done +dwlb -ipc -middle-bg-color '#16181a' -active-fg-color '#ffffff' -active-bg-color '#5ea1ff' -inactive-fg-color '#ffffff' -inactive-bg-color '#3c4048' -urgent-fg-color '#ffffff' -urgent-bg-color '#ff6e5e' -font 'FiraCode Nerd Font Mono:size=10' & diff --git a/modules/dwl/patches/ipc.patch b/modules/dwl/patches/ipc.patch new file mode 100644 index 0000000..9f989e7 --- /dev/null +++ b/modules/dwl/patches/ipc.patch @@ -0,0 +1,597 @@ +From 6c6d655b68770ce82a24fde9b58c4d97b672553a Mon Sep 17 00:00:00 2001 +From: choc +Date: Mon, 23 Oct 2023 10:35:17 +0800 +Subject: [PATCH] implement dwl-ipc-unstable-v2 + https://codeberg.org/dwl/dwl-patches/wiki/ipc + +--- + Makefile | 14 +- + config.def.h | 1 + + dwl.c | 257 ++++++++++++++++++++++++++---- + protocols/dwl-ipc-unstable-v2.xml | 181 +++++++++++++++++++++ + 4 files changed, 419 insertions(+), 34 deletions(-) + create mode 100644 protocols/dwl-ipc-unstable-v2.xml + +diff --git a/Makefile b/Makefile +index 8db7409..a79a080 100644 +--- a/Makefile ++++ b/Makefile +@@ -17,12 +17,14 @@ DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(WLR_INCS) $(DWLCPPFLAGS) $(DWLDEV + LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` $(WLR_LIBS) -lm $(LIBS) + + all: dwl +-dwl: dwl.o util.o +- $(CC) dwl.o util.o $(DWLCFLAGS) $(LDFLAGS) $(LDLIBS) -o $@ ++dwl: dwl.o util.o dwl-ipc-unstable-v2-protocol.o ++ $(CC) dwl.o util.o dwl-ipc-unstable-v2-protocol.o $(DWLCFLAGS) $(LDFLAGS) $(LDLIBS) -o $@ + dwl.o: dwl.c client.h config.h config.mk cursor-shape-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 ++ wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h \ ++ dwl-ipc-unstable-v2-protocol.h + util.o: util.c util.h ++dwl-ipc-unstable-v2-protocol.o: dwl-ipc-unstable-v2-protocol.c dwl-ipc-unstable-v2-protocol.h + + # wayland-scanner is a tool which generates C headers and rigging for Wayland + # protocols, which are specified in XML. wlroots requires you to rig these up +@@ -45,6 +47,12 @@ wlr-output-power-management-unstable-v1-protocol.h: + xdg-shell-protocol.h: + $(WAYLAND_SCANNER) server-header \ + $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@ ++dwl-ipc-unstable-v2-protocol.h: ++ $(WAYLAND_SCANNER) server-header \ ++ protocols/dwl-ipc-unstable-v2.xml $@ ++dwl-ipc-unstable-v2-protocol.c: ++ $(WAYLAND_SCANNER) private-code \ ++ protocols/dwl-ipc-unstable-v2.xml $@ + + config.h: + cp config.def.h $@ +diff --git a/config.def.h b/config.def.h +index 22d2171..1593033 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -127,6 +127,7 @@ static const Key keys[] = { + /* modifier key function argument */ + { MODKEY, XKB_KEY_p, spawn, {.v = menucmd} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return, spawn, {.v = termcmd} }, ++ { MODKEY, XKB_KEY_b, togglebar, {0} }, + { MODKEY, XKB_KEY_j, focusstack, {.i = +1} }, + { MODKEY, XKB_KEY_k, focusstack, {.i = -1} }, + { MODKEY, XKB_KEY_i, incnmaster, {.i = +1} }, +diff --git a/dwl.c b/dwl.c +index 8a587d1..7a4949b 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -68,6 +68,7 @@ + #include + #endif + ++#include "dwl-ipc-unstable-v2-protocol.h" + #include "util.h" + + /* macros */ +@@ -144,6 +145,12 @@ typedef struct { + uint32_t resize; /* configure serial of a pending resize */ + } Client; + ++typedef struct { ++ struct wl_list link; ++ struct wl_resource *resource; ++ Monitor *mon; ++} DwlIpcOutput; ++ + typedef struct { + uint32_t mod; + xkb_keysym_t keysym; +@@ -189,6 +196,7 @@ typedef struct { + + struct Monitor { + struct wl_list link; ++ struct wl_list dwl_ipc_outputs; + struct wlr_output *wlr_output; + struct wlr_scene_output *scene_output; + struct wlr_scene_rect *fullscreen_bg; /* See createmon() for info */ +@@ -286,6 +294,17 @@ static void destroysessionlock(struct wl_listener *listener, void *data); + static void destroysessionmgr(struct wl_listener *listener, void *data); + static void destroykeyboardgroup(struct wl_listener *listener, void *data); + static Monitor *dirtomon(enum wlr_direction dir); ++static void dwl_ipc_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id); ++static void dwl_ipc_manager_destroy(struct wl_resource *resource); ++static void dwl_ipc_manager_get_output(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *output); ++static void dwl_ipc_manager_release(struct wl_client *client, struct wl_resource *resource); ++static void dwl_ipc_output_destroy(struct wl_resource *resource); ++static void dwl_ipc_output_printstatus(Monitor *monitor); ++static void dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output); ++static void dwl_ipc_output_set_client_tags(struct wl_client *client, struct wl_resource *resource, uint32_t and_tags, uint32_t xor_tags); ++static void dwl_ipc_output_set_layout(struct wl_client *client, struct wl_resource *resource, uint32_t index); ++static void dwl_ipc_output_set_tags(struct wl_client *client, struct wl_resource *resource, uint32_t tagmask, uint32_t toggle_tagset); ++static void dwl_ipc_output_release(struct wl_client *client, struct wl_resource *resource); + static void focusclient(Client *c, int lift); + static void focusmon(const Arg *arg); + static void focusstack(const Arg *arg); +@@ -338,6 +357,7 @@ static void startdrag(struct wl_listener *listener, void *data); + static void tag(const Arg *arg); + static void tagmon(const Arg *arg); + static void tile(Monitor *m); ++static void togglebar(const Arg *arg); + static void togglefloating(const Arg *arg); + static void togglefullscreen(const Arg *arg); + static void toggletag(const Arg *arg); +@@ -411,6 +431,9 @@ static struct wlr_box sgeom; + static struct wl_list mons; + static Monitor *selmon; + ++static struct zdwl_ipc_manager_v2_interface dwl_manager_implementation = {.release = dwl_ipc_manager_release, .get_output = dwl_ipc_manager_get_output}; ++static struct zdwl_ipc_output_v2_interface dwl_output_implementation = {.release = dwl_ipc_output_release, .set_tags = dwl_ipc_output_set_tags, .set_layout = dwl_ipc_output_set_layout, .set_client_tags = dwl_ipc_output_set_client_tags}; ++ + #ifdef XWAYLAND + static void activatex11(struct wl_listener *listener, void *data); + static void associatex11(struct wl_listener *listener, void *data); +@@ -703,6 +726,10 @@ cleanupmon(struct wl_listener *listener, void *data) + LayerSurface *l, *tmp; + size_t i; + ++ DwlIpcOutput *ipc_output, *ipc_output_tmp; ++ wl_list_for_each_safe(ipc_output, ipc_output_tmp, &m->dwl_ipc_outputs, link) ++ wl_resource_destroy(ipc_output->resource); ++ + /* m->layers[i] are intentionally not unlinked */ + for (i = 0; i < LENGTH(m->layers); i++) { + wl_list_for_each_safe(l, tmp, &m->layers[i], link) +@@ -983,6 +1010,8 @@ createmon(struct wl_listener *listener, void *data) + m = wlr_output->data = ecalloc(1, sizeof(*m)); + m->wlr_output = wlr_output; + ++ wl_list_init(&m->dwl_ipc_outputs); ++ + for (i = 0; i < LENGTH(m->layers); i++) + wl_list_init(&m->layers[i]); + +@@ -1334,6 +1363,192 @@ dirtomon(enum wlr_direction dir) + return selmon; + } + ++void ++dwl_ipc_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) ++{ ++ struct wl_resource *manager_resource = wl_resource_create(client, &zdwl_ipc_manager_v2_interface, version, id); ++ if (!manager_resource) { ++ wl_client_post_no_memory(client); ++ return; ++ } ++ wl_resource_set_implementation(manager_resource, &dwl_manager_implementation, NULL, dwl_ipc_manager_destroy); ++ ++ zdwl_ipc_manager_v2_send_tags(manager_resource, TAGCOUNT); ++ ++ for (unsigned int i = 0; i < LENGTH(layouts); i++) ++ zdwl_ipc_manager_v2_send_layout(manager_resource, layouts[i].symbol); ++} ++ ++void ++dwl_ipc_manager_destroy(struct wl_resource *resource) ++{ ++ /* No state to destroy */ ++} ++ ++void ++dwl_ipc_manager_get_output(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *output) ++{ ++ DwlIpcOutput *ipc_output; ++ Monitor *monitor = wlr_output_from_resource(output)->data; ++ struct wl_resource *output_resource = wl_resource_create(client, &zdwl_ipc_output_v2_interface, wl_resource_get_version(resource), id); ++ if (!output_resource) ++ return; ++ ++ ipc_output = ecalloc(1, sizeof(*ipc_output)); ++ ipc_output->resource = output_resource; ++ ipc_output->mon = monitor; ++ wl_resource_set_implementation(output_resource, &dwl_output_implementation, ipc_output, dwl_ipc_output_destroy); ++ wl_list_insert(&monitor->dwl_ipc_outputs, &ipc_output->link); ++ dwl_ipc_output_printstatus_to(ipc_output); ++} ++ ++void ++dwl_ipc_manager_release(struct wl_client *client, struct wl_resource *resource) ++{ ++ wl_resource_destroy(resource); ++} ++ ++static void ++dwl_ipc_output_destroy(struct wl_resource *resource) ++{ ++ DwlIpcOutput *ipc_output = wl_resource_get_user_data(resource); ++ wl_list_remove(&ipc_output->link); ++ free(ipc_output); ++} ++ ++void ++dwl_ipc_output_printstatus(Monitor *monitor) ++{ ++ DwlIpcOutput *ipc_output; ++ wl_list_for_each(ipc_output, &monitor->dwl_ipc_outputs, link) ++ dwl_ipc_output_printstatus_to(ipc_output); ++} ++ ++void ++dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output) ++{ ++ Monitor *monitor = ipc_output->mon; ++ Client *c, *focused; ++ int tagmask, state, numclients, focused_client, tag; ++ const char *title, *appid; ++ focused = focustop(monitor); ++ zdwl_ipc_output_v2_send_active(ipc_output->resource, monitor == selmon); ++ ++ for (tag = 0 ; tag < TAGCOUNT; tag++) { ++ numclients = state = focused_client = 0; ++ tagmask = 1 << tag; ++ if ((tagmask & monitor->tagset[monitor->seltags]) != 0) ++ state |= ZDWL_IPC_OUTPUT_V2_TAG_STATE_ACTIVE; ++ ++ wl_list_for_each(c, &clients, link) { ++ if (c->mon != monitor) ++ continue; ++ if (!(c->tags & tagmask)) ++ continue; ++ if (c == focused) ++ focused_client = 1; ++ if (c->isurgent) ++ state |= ZDWL_IPC_OUTPUT_V2_TAG_STATE_URGENT; ++ ++ numclients++; ++ } ++ zdwl_ipc_output_v2_send_tag(ipc_output->resource, tag, state, numclients, focused_client); ++ } ++ title = focused ? client_get_title(focused) : ""; ++ appid = focused ? client_get_appid(focused) : ""; ++ ++ zdwl_ipc_output_v2_send_layout(ipc_output->resource, monitor->lt[monitor->sellt] - layouts); ++ zdwl_ipc_output_v2_send_title(ipc_output->resource, title); ++ zdwl_ipc_output_v2_send_appid(ipc_output->resource, appid); ++ zdwl_ipc_output_v2_send_layout_symbol(ipc_output->resource, monitor->ltsymbol); ++ if (wl_resource_get_version(ipc_output->resource) >= ZDWL_IPC_OUTPUT_V2_FULLSCREEN_SINCE_VERSION) { ++ zdwl_ipc_output_v2_send_fullscreen(ipc_output->resource, focused ? focused->isfullscreen : 0); ++ } ++ if (wl_resource_get_version(ipc_output->resource) >= ZDWL_IPC_OUTPUT_V2_FLOATING_SINCE_VERSION) { ++ zdwl_ipc_output_v2_send_floating(ipc_output->resource, focused ? focused->isfloating : 0); ++ } ++ zdwl_ipc_output_v2_send_frame(ipc_output->resource); ++} ++ ++void ++dwl_ipc_output_set_client_tags(struct wl_client *client, struct wl_resource *resource, uint32_t and_tags, uint32_t xor_tags) ++{ ++ DwlIpcOutput *ipc_output; ++ Monitor *monitor; ++ Client *selected_client; ++ unsigned int newtags = 0; ++ ++ ipc_output = wl_resource_get_user_data(resource); ++ if (!ipc_output) ++ return; ++ ++ monitor = ipc_output->mon; ++ selected_client = focustop(monitor); ++ if (!selected_client) ++ return; ++ ++ newtags = (selected_client->tags & and_tags) ^ xor_tags; ++ if (!newtags) ++ return; ++ ++ selected_client->tags = newtags; ++ if (selmon == monitor) ++ focusclient(focustop(monitor), 1); ++ arrange(selmon); ++ printstatus(); ++} ++ ++void ++dwl_ipc_output_set_layout(struct wl_client *client, struct wl_resource *resource, uint32_t index) ++{ ++ DwlIpcOutput *ipc_output; ++ Monitor *monitor; ++ ++ ipc_output = wl_resource_get_user_data(resource); ++ if (!ipc_output) ++ return; ++ ++ monitor = ipc_output->mon; ++ if (index >= LENGTH(layouts)) ++ return; ++ if (index != monitor->lt[monitor->sellt] - layouts) ++ monitor->sellt ^= 1; ++ ++ monitor->lt[monitor->sellt] = &layouts[index]; ++ arrange(monitor); ++ printstatus(); ++} ++ ++void ++dwl_ipc_output_set_tags(struct wl_client *client, struct wl_resource *resource, uint32_t tagmask, uint32_t toggle_tagset) ++{ ++ DwlIpcOutput *ipc_output; ++ Monitor *monitor; ++ unsigned int newtags = tagmask & TAGMASK; ++ ++ ipc_output = wl_resource_get_user_data(resource); ++ if (!ipc_output) ++ return; ++ monitor = ipc_output->mon; ++ ++ if (!newtags || newtags == monitor->tagset[monitor->seltags]) ++ return; ++ if (toggle_tagset) ++ monitor->seltags ^= 1; ++ ++ monitor->tagset[monitor->seltags] = newtags; ++ if (selmon == monitor) ++ focusclient(focustop(monitor), 1); ++ arrange(monitor); ++ printstatus(); ++} ++ ++void ++dwl_ipc_output_release(struct wl_client *client, struct wl_resource *resource) ++{ ++ wl_resource_destroy(resource); ++} ++ + void + focusclient(Client *c, int lift) + { +@@ -2033,38 +2248,9 @@ void + printstatus(void) + { + Monitor *m = NULL; +- Client *c; +- uint32_t occ, urg, sel; + +- wl_list_for_each(m, &mons, link) { +- occ = urg = 0; +- wl_list_for_each(c, &clients, link) { +- if (c->mon != m) +- continue; +- occ |= c->tags; +- if (c->isurgent) +- urg |= c->tags; +- } +- if ((c = focustop(m))) { +- printf("%s title %s\n", m->wlr_output->name, client_get_title(c)); +- printf("%s appid %s\n", m->wlr_output->name, client_get_appid(c)); +- printf("%s fullscreen %d\n", m->wlr_output->name, c->isfullscreen); +- printf("%s floating %d\n", m->wlr_output->name, c->isfloating); +- sel = c->tags; +- } else { +- printf("%s title \n", m->wlr_output->name); +- printf("%s appid \n", m->wlr_output->name); +- printf("%s fullscreen \n", m->wlr_output->name); +- printf("%s floating \n", m->wlr_output->name); +- sel = 0; +- } +- +- printf("%s selmon %u\n", m->wlr_output->name, m == selmon); +- printf("%s tags %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32"\n", +- m->wlr_output->name, occ, m->tagset[m->seltags], sel, urg); +- printf("%s layout %s\n", m->wlr_output->name, m->ltsymbol); +- } +- fflush(stdout); ++ wl_list_for_each(m, &mons, link) ++ dwl_ipc_output_printstatus(m); + } + + void +@@ -2584,6 +2770,8 @@ setup(void) + LISTEN_STATIC(&output_mgr->events.apply, outputmgrapply); + LISTEN_STATIC(&output_mgr->events.test, outputmgrtest); + ++ wl_global_create(dpy, &zdwl_ipc_manager_v2_interface, 2, NULL, dwl_ipc_manager_bind); ++ + /* Make sure XWayland clients don't connect to the parent X server, + * e.g when running in the x11 backend or the wayland backend and the + * compositor has Xwayland support */ +@@ -2681,6 +2869,13 @@ tile(Monitor *m) + } + } + ++void ++togglebar(const Arg *arg) { ++ DwlIpcOutput *ipc_output; ++ wl_list_for_each(ipc_output, &selmon->dwl_ipc_outputs, link) ++ zdwl_ipc_output_v2_send_toggle_visibility(ipc_output->resource); ++} ++ + void + togglefloating(const Arg *arg) + { +diff --git a/protocols/dwl-ipc-unstable-v2.xml b/protocols/dwl-ipc-unstable-v2.xml +new file mode 100644 +index 0000000..0a6e7e5 +--- /dev/null ++++ b/protocols/dwl-ipc-unstable-v2.xml +@@ -0,0 +1,181 @@ ++ ++ ++ ++ ++ This protocol allows clients to update and get updates from dwl. ++ ++ Warning! The protocol described in this file is experimental and ++ backward incompatible changes may be made. Backward compatible ++ changes may be added together with the corresponding interface ++ version bump. ++ Backward incompatible changes are done by bumping the version ++ number in the protocol and interface names and resetting the ++ interface version. Once the protocol is to be declared stable, ++ the 'z' prefix and the version number in the protocol and ++ interface names are removed and the interface version number is ++ reset. ++ ++ ++ ++ ++ This interface is exposed as a global in wl_registry. ++ ++ Clients can use this interface to get a dwl_ipc_output. ++ After binding the client will recieve the dwl_ipc_manager.tags and dwl_ipc_manager.layout events. ++ The dwl_ipc_manager.tags and dwl_ipc_manager.layout events expose tags and layouts to the client. ++ ++ ++ ++ ++ Indicates that the client will not the dwl_ipc_manager object anymore. ++ Objects created through this instance are not affected. ++ ++ ++ ++ ++ ++ Get a dwl_ipc_outout for the specified wl_output. ++ ++ ++ ++ ++ ++ ++ ++ This event is sent after binding. ++ A roundtrip after binding guarantees the client recieved all tags. ++ ++ ++ ++ ++ ++ ++ This event is sent after binding. ++ A roundtrip after binding guarantees the client recieved all layouts. ++ ++ ++ ++ ++ ++ ++ ++ Observe and control a dwl output. ++ ++ Events are double-buffered: ++ Clients should cache events and redraw when a dwl_ipc_output.frame event is sent. ++ ++ Request are not double-buffered: ++ The compositor will update immediately upon request. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ Indicates to that the client no longer needs this dwl_ipc_output. ++ ++ ++ ++ ++ ++ Indicates the client should hide or show themselves. ++ If the client is visible then hide, if hidden then show. ++ ++ ++ ++ ++ ++ Indicates if the output is active. Zero is invalid, nonzero is valid. ++ ++ ++ ++ ++ ++ ++ Indicates that a tag has been updated. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ Indicates a new layout is selected. ++ ++ ++ ++ ++ ++ ++ Indicates the title has changed. ++ ++ ++ ++ ++ ++ ++ Indicates the appid has changed. ++ ++ ++ ++ ++ ++ ++ Indicates the layout has changed. Since layout symbols are dynamic. ++ As opposed to the zdwl_ipc_manager.layout event, this should take precendence when displaying. ++ You can ignore the zdwl_ipc_output.layout event. ++ ++ ++ ++ ++ ++ ++ Indicates that a sequence of status updates have finished and the client should redraw. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ The tags are updated as follows: ++ new_tags = (current_tags AND and_tags) XOR xor_tags ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ Indicates if the selected client on this output is fullscreen. ++ ++ ++ ++ ++ ++ ++ Indicates if the selected client on this output is floating. ++ ++ ++ ++ ++ +-- +2.43.0 + diff --git a/modules/dwl/protocols/dwl-ipc-unstable-v2.xml b/modules/dwl/protocols/dwl-ipc-unstable-v2.xml new file mode 100644 index 0000000..0a6e7e5 --- /dev/null +++ b/modules/dwl/protocols/dwl-ipc-unstable-v2.xml @@ -0,0 +1,181 @@ + + + + + This protocol allows clients to update and get updates from dwl. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible + changes may be added together with the corresponding interface + version bump. + Backward incompatible changes are done by bumping the version + number in the protocol and interface names and resetting the + interface version. Once the protocol is to be declared stable, + the 'z' prefix and the version number in the protocol and + interface names are removed and the interface version number is + reset. + + + + + This interface is exposed as a global in wl_registry. + + Clients can use this interface to get a dwl_ipc_output. + After binding the client will recieve the dwl_ipc_manager.tags and dwl_ipc_manager.layout events. + The dwl_ipc_manager.tags and dwl_ipc_manager.layout events expose tags and layouts to the client. + + + + + Indicates that the client will not the dwl_ipc_manager object anymore. + Objects created through this instance are not affected. + + + + + + Get a dwl_ipc_outout for the specified wl_output. + + + + + + + + This event is sent after binding. + A roundtrip after binding guarantees the client recieved all tags. + + + + + + + This event is sent after binding. + A roundtrip after binding guarantees the client recieved all layouts. + + + + + + + + Observe and control a dwl output. + + Events are double-buffered: + Clients should cache events and redraw when a dwl_ipc_output.frame event is sent. + + Request are not double-buffered: + The compositor will update immediately upon request. + + + + + + + + + + + Indicates to that the client no longer needs this dwl_ipc_output. + + + + + + Indicates the client should hide or show themselves. + If the client is visible then hide, if hidden then show. + + + + + + Indicates if the output is active. Zero is invalid, nonzero is valid. + + + + + + + Indicates that a tag has been updated. + + + + + + + + + + Indicates a new layout is selected. + + + + + + + Indicates the title has changed. + + + + + + + Indicates the appid has changed. + + + + + + + Indicates the layout has changed. Since layout symbols are dynamic. + As opposed to the zdwl_ipc_manager.layout event, this should take precendence when displaying. + You can ignore the zdwl_ipc_output.layout event. + + + + + + + Indicates that a sequence of status updates have finished and the client should redraw. + + + + + + + + + + + + The tags are updated as follows: + new_tags = (current_tags AND and_tags) XOR xor_tags + + + + + + + + + + + + + + Indicates if the selected client on this output is fullscreen. + + + + + + + Indicates if the selected client on this output is floating. + + + + + diff --git a/modules/dwl/source/Makefile b/modules/dwl/source/Makefile index 578194f..84b2a64 100644 --- a/modules/dwl/source/Makefile +++ b/modules/dwl/source/Makefile @@ -17,12 +17,14 @@ DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(WLR_INCS) $(DWLCPPFLAGS) $(DWLDEV LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` $(WLR_LIBS) -lm $(LIBS) all: dwl -dwl: dwl.o util.o - $(CC) dwl.o util.o $(DWLCFLAGS) $(LDFLAGS) $(LDLIBS) -o $@ +dwl: dwl.o util.o dwl-ipc-unstable-v2-protocol.o + $(CC) dwl.o util.o dwl-ipc-unstable-v2-protocol.o $(DWLCFLAGS) $(LDFLAGS) $(LDLIBS) -o $@ dwl.o: dwl.c client.h config.h config.mk cursor-shape-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 + wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h \ + dwl-ipc-unstable-v2-protocol.h util.o: util.c util.h +dwl-ipc-unstable-v2-protocol.o: dwl-ipc-unstable-v2-protocol.c dwl-ipc-unstable-v2-protocol.h # wayland-scanner is a tool which generates C headers and rigging for Wayland # protocols, which are specified in XML. wlroots requires you to rig these up @@ -45,6 +47,12 @@ wlr-output-power-management-unstable-v1-protocol.h: xdg-shell-protocol.h: $(WAYLAND_SCANNER) server-header \ $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@ +dwl-ipc-unstable-v2-protocol.h: + $(WAYLAND_SCANNER) server-header \ + protocols/dwl-ipc-unstable-v2.xml $@ +dwl-ipc-unstable-v2-protocol.c: + $(WAYLAND_SCANNER) private-code \ + protocols/dwl-ipc-unstable-v2.xml $@ config.h: cp config.def.h $@ diff --git a/modules/dwl/source/config.def.h b/modules/dwl/source/config.def.h index 9f9e239..60fdba9 100644 --- a/modules/dwl/source/config.def.h +++ b/modules/dwl/source/config.def.h @@ -206,6 +206,9 @@ static const Key keys[] = { { 0, XKB_KEY_XF86MonBrightnessUp, spawn, {.v = brightnessup} }, { 0, XKB_KEY_XF86MonBrightnessDown, spawn, {.v = brightnessdown} }, + /* Bar toggle */ + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_B, togglebar, {0} }, + /* Original dwl keybindings */ { MODKEY, XKB_KEY_j, focusstack, {.i = +1} }, { MODKEY, XKB_KEY_k, focusstack, {.i = -1} }, diff --git a/modules/dwl/source/config.def.h.orig b/modules/dwl/source/config.def.h.orig index 243ff07..9f9e239 100644 --- a/modules/dwl/source/config.def.h.orig +++ b/modules/dwl/source/config.def.h.orig @@ -6,13 +6,20 @@ /* appearance */ static const int sloppyfocus = 1; /* focus follows mouse */ static const int bypass_surface_visibility = 0; /* 1 means idle inhibitors will disable idle tracking even if it's surface isn't visible */ +static const int smartgaps = 0; /* 1 means no outer gap when there is only one window */ +static const int monoclegaps = 0; /* 1 means outer gaps in monocle layout */ static const unsigned int borderpx = 1; /* border pixel of windows */ -static const float rootcolor[] = COLOR(0x222222ff); -static const float bordercolor[] = COLOR(0x444444ff); -static const float focuscolor[] = COLOR(0x005577ff); -static const float urgentcolor[] = COLOR(0xff0000ff); +static const unsigned int gappih = 10; /* horiz inner gap between windows */ +static const unsigned int gappiv = 10; /* vert inner gap between windows */ +static const unsigned int gappoh = 10; /* horiz outer gap between windows and screen edge */ +static const unsigned int gappov = 10; /* vert outer gap between windows and screen edge */ +static const float rootcolor[] = COLOR(0x16181aff); /* cyberdream background */ +static const float bordercolor[] = COLOR(0x3c4048ff); /* cyberdream inactive/dim */ +static const float focuscolor[] = COLOR(0x5ea1ffff); /* cyberdream blue */ +static const float urgentcolor[] = COLOR(0xff6e5eff); /* cyberdream red */ /* This conforms to the xdg-protocol. Set the alpha to zero to restore the old behavior */ -static const float fullscreen_bg[] = {0.0f, 0.0f, 0.0f, 1.0f}; /* You can also use glsl colors */ +static const float fullscreen_bg[] = {0.09f, 0.09f, 0.10f, 1.0f}; /* cyberdream background */ +static const float default_opacity = 0.75; /* tagging - TAGCOUNT must be no greater than 31 */ #define TAGCOUNT (9) @@ -22,7 +29,10 @@ static int log_level = WLR_ERROR; /* Autostart */ static const char *const autostart[] = { - "wbg", "/path/to/your/image", NULL, + "sh", "-c", "~/.local/usr/bin/dwlb.sh", NULL, + "swaybg", "-i", "/etc/nixos/home/Wallpapers/Star_Wars_Linux-1250471.jpg", "-m", "fill", NULL, + "dunst", NULL, + "sh", "-c", "~/.local/usr/bin/dwlb-status.sh &", NULL, NULL /* terminate */ }; @@ -57,11 +67,10 @@ static const Layout layouts[] = { /* 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 }, - */ - /* defaults */ - { NULL, 0.55f, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 }, + /* HiDPI laptop monitor with 2x scaling */ + { "eDP-1", 0.55f, 1, 2, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 }, + /* defaults for other monitors */ + { NULL, 0.55f, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 }, }; /* keyboard */ @@ -70,7 +79,7 @@ static const struct xkb_rule_names xkb_rules = { /* example: .options = "ctrl:nocaps", */ - .options = NULL, + .options = "caps:escape", }; static const int repeat_rate = 25; @@ -80,10 +89,10 @@ static const int repeat_delay = 600; static const int tap_to_click = 1; static const int tap_and_drag = 1; static const int drag_lock = 1; -static const int natural_scrolling = 0; +static const int natural_scrolling = 1; static const int disable_while_typing = 1; static const int left_handed = 0; -static const int middle_button_emulation = 0; +static const int middle_button_emulation = 1; /* You can choose between: LIBINPUT_CONFIG_SCROLL_NO_SCROLL LIBINPUT_CONFIG_SCROLL_2FG @@ -120,7 +129,7 @@ LIBINPUT_CONFIG_TAP_MAP_LMR -- 1/2/3 finger tap maps to left/middle/right static const enum libinput_config_tap_button_map button_map = LIBINPUT_CONFIG_TAP_MAP_LRM; /* If you want to use the windows key for MODKEY, use WLR_MODIFIER_LOGO */ -#define MODKEY WLR_MODIFIER_ALT +#define MODKEY WLR_MODIFIER_LOGO #define TAGKEYS(KEY,SKEY,TAG) \ { MODKEY, KEY, view, {.ui = 1 << TAG} }, \ @@ -132,14 +141,72 @@ static const enum libinput_config_tap_button_map button_map = LIBINPUT_CONFIG_TA #define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } /* commands */ -static const char *termcmd[] = { "foot", NULL }; -static const char *menucmd[] = { "wmenu-run", NULL }; +static const char *termcmd[] = { "kitty", NULL }; +static const char *termtmuxcmd[] = { "kitty", "-e", "tmuxinator", "start", "Dashboard", NULL }; +static const char *menucmd[] = { "rofi", "-show", "drun", NULL }; +static const char *browsercmd[] = { "qutebrowser", NULL }; +static const char *filecmd[] = { "kitty", "-T", "ranger", "-e", "ranger", NULL }; +static const char *editorcmd[] = { "kitty", "-T", "Editor", "-e", "tmuxinator", "start", "Development", NULL }; +static const char *syscmd[] = { "kitty", "-T", "system-monitor", "-e", "htop", NULL }; +static const char *notescmd[] = { "kitty", "-T", "capture", "-e", "pytui", "-n", NULL }; +static const char *manpagescmd[] = { "kitty", "-T", "capture", "-e", "pytui", "-m", NULL }; + +/* Audio control commands */ +static const char *volup[] = { "wpctl", "set-volume", "-l", "1", "@DEFAULT_AUDIO_SINK@", "5%+", NULL }; +static const char *voldown[] = { "wpctl", "set-volume", "@DEFAULT_AUDIO_SINK@", "5%-", NULL }; +static const char *volmute[] = { "wpctl", "set-mute", "@DEFAULT_AUDIO_SINK@", "toggle", NULL }; +static const char *micmute[] = { "wpctl", "set-mute", "@DEFAULT_AUDIO_SOURCE@", "toggle", NULL }; + +/* Media control commands */ +static const char *playpause[] = { "playerctl", "play-pause", NULL }; +static const char *nexttrack[] = { "playerctl", "next", NULL }; +static const char *prevtrack[] = { "playerctl", "previous", NULL }; + +/* Brightness control commands */ +static const char *brightnessup[] = { "brightnessctl", "-e4", "-n2", "set", "5%+", NULL }; +static const char *brightnessdown[] = { "brightnessctl", "-e4", "-n2", "set", "5%-", NULL }; + +/* Monitor switching commands */ +static const char *laptop_only[] = { "wlr-randr", "--output", "eDP-1", "--on", "--output", "DP-1", "--off", NULL }; +static const char *external_only[] = { "wlr-randr", "--output", "eDP-1", "--off", "--output", "DP-1", "--on", "--mode", "2560x1080@74.990997", NULL }; +static const char *dual_monitor[] = { "wlr-randr", "--output", "eDP-1", "--on", "--output", "DP-1", "--on", "--pos", "0,-1080", "--mode", "2560x1080@74.990997", NULL }; static const Key keys[] = { /* 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} }, + /* Application launching - based on Hyprland config */ + { MODKEY, XKB_KEY_r, spawn, {.v = menucmd} }, + { MODKEY, XKB_KEY_Return, spawn, {.v = termcmd} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return, spawn, {.v = termtmuxcmd} }, + { MODKEY, XKB_KEY_b, spawn, {.v = browsercmd} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_E, spawn, {.v = filecmd} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_D, spawn, {.v = editorcmd} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_M, spawn, {.v = syscmd} }, + { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_n, spawn, {.v = notescmd} }, + { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_m, spawn, {.v = manpagescmd} }, + + /* Monitor switching */ + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_F1, spawn, {.v = laptop_only} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_F2, spawn, {.v = external_only} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_F3, spawn, {.v = dual_monitor} }, + + /* Audio controls */ + { 0, XKB_KEY_XF86AudioRaiseVolume, spawn, {.v = volup} }, + { 0, XKB_KEY_XF86AudioLowerVolume, spawn, {.v = voldown} }, + { 0, XKB_KEY_XF86AudioMute, spawn, {.v = volmute} }, + { 0, XKB_KEY_XF86AudioMicMute, spawn, {.v = micmute} }, + + /* Media controls */ + { 0, XKB_KEY_XF86AudioPlay, spawn, {.v = playpause} }, + { 0, XKB_KEY_XF86AudioPause, spawn, {.v = playpause} }, + { 0, XKB_KEY_XF86AudioNext, spawn, {.v = nexttrack} }, + { 0, XKB_KEY_XF86AudioPrev, spawn, {.v = prevtrack} }, + + /* Brightness controls */ + { 0, XKB_KEY_XF86MonBrightnessUp, spawn, {.v = brightnessup} }, + { 0, XKB_KEY_XF86MonBrightnessDown, spawn, {.v = brightnessdown} }, + + /* Original dwl keybindings */ { MODKEY, XKB_KEY_j, focusstack, {.i = +1} }, { MODKEY, XKB_KEY_k, focusstack, {.i = -1} }, { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_h, focusdir, {.ui = 0} }, @@ -150,19 +217,36 @@ static const Key keys[] = { { MODKEY, XKB_KEY_d, incnmaster, {.i = -1} }, { MODKEY, XKB_KEY_h, setmfact, {.f = -0.05f} }, { MODKEY, XKB_KEY_l, setmfact, {.f = +0.05f} }, - { MODKEY, XKB_KEY_o, setopacity, {.f = +0.1f} }, - { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_O, setopacity, {.f = -0.1f} }, + { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_h, incgaps, {.i = +1 } }, + { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_l, incgaps, {.i = -1 } }, + { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_SHIFT, XKB_KEY_H, incogaps, {.i = +1 } }, + { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_SHIFT, XKB_KEY_L, incogaps, {.i = -1 } }, + { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_CTRL, XKB_KEY_h, incigaps, {.i = +1 } }, + { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_CTRL, XKB_KEY_l, incigaps, {.i = -1 } }, + { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_0, togglegaps, {0} }, + { MODKEY|WLR_MODIFIER_LOGO|WLR_MODIFIER_SHIFT, XKB_KEY_parenright,defaultgaps, {0} }, + { MODKEY, XKB_KEY_bracketleft, incihgaps, {.i = +1 } }, + { MODKEY, XKB_KEY_bracketright, incihgaps, {.i = -1 } }, + { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_bracketleft, incivgaps, {.i = +1 } }, + { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_bracketright, incivgaps, {.i = -1 } }, + { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_bracketleft, incohgaps, {.i = +1 } }, + { MODKEY|WLR_MODIFIER_LOGO, XKB_KEY_bracketright, incohgaps, {.i = -1 } }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_braceleft, incovgaps, {.i = +1 } }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_braceright, incovgaps, {.i = -1 } }, + { MODKEY, XKB_KEY_u, setopacity, {.f = +0.1f} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_U, setopacity, {.f = -0.1f} }, { MODKEY, XKB_KEY_Return, zoom, {0} }, { MODKEY, XKB_KEY_Tab, view, {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]} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_T, setlayout, {.v = &layouts[2]} }, { MODKEY, XKB_KEY_space, setlayout, {0} }, { MODKEY, XKB_KEY_o, menu, {.v = &menus[0]} }, { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_O, menu, {.v = &menus[1]} }, + { MODKEY, XKB_KEY_v, togglefloating, {0} }, { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} }, - { MODKEY, XKB_KEY_e, togglefullscreen, {0} }, + { MODKEY, XKB_KEY_y, togglefullscreen, {0} }, { MODKEY, XKB_KEY_0, view, {.ui = ~0} }, { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} }, { MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} }, diff --git a/modules/dwl/source/dwl.c b/modules/dwl/source/dwl.c index a535d8a..bd1b386 100644 --- a/modules/dwl/source/dwl.c +++ b/modules/dwl/source/dwl.c @@ -69,6 +69,7 @@ #include #endif +#include "dwl-ipc-unstable-v2-protocol.h" #include "util.h" /* macros */ @@ -143,6 +144,12 @@ typedef struct { uint32_t resize; /* configure serial of a pending resize */ } Client; +typedef struct { + struct wl_list link; + struct wl_resource *resource; + Monitor *mon; +} DwlIpcOutput; + typedef struct { uint32_t mod; xkb_keysym_t keysym; @@ -187,6 +194,7 @@ typedef struct { struct Monitor { struct wl_list link; + struct wl_list dwl_ipc_outputs; struct wlr_output *wlr_output; struct wlr_scene_output *scene_output; struct wlr_scene_rect *fullscreen_bg; /* See createmon() for info */ @@ -297,6 +305,17 @@ static void destroypointerconstraint(struct wl_listener *listener, void *data); static void destroysessionlock(struct wl_listener *listener, void *data); static void destroykeyboardgroup(struct wl_listener *listener, void *data); static Monitor *dirtomon(enum wlr_direction dir); +static void dwl_ipc_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id); +static void dwl_ipc_manager_destroy(struct wl_resource *resource); +static void dwl_ipc_manager_get_output(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *output); +static void dwl_ipc_manager_release(struct wl_client *client, struct wl_resource *resource); +static void dwl_ipc_output_destroy(struct wl_resource *resource); +static void dwl_ipc_output_printstatus(Monitor *monitor); +static void dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output); +static void dwl_ipc_output_set_client_tags(struct wl_client *client, struct wl_resource *resource, uint32_t and_tags, uint32_t xor_tags); +static void dwl_ipc_output_set_layout(struct wl_client *client, struct wl_resource *resource, uint32_t index); +static void dwl_ipc_output_set_tags(struct wl_client *client, struct wl_resource *resource, uint32_t tagmask, uint32_t toggle_tagset); +static void dwl_ipc_output_release(struct wl_client *client, struct wl_resource *resource); static void focusclient(Client *c, int lift); static void focusmon(const Arg *arg); static void focusstack(const Arg *arg); @@ -366,6 +385,7 @@ static void startdrag(struct wl_listener *listener, void *data); static void tag(const Arg *arg); static void tagmon(const Arg *arg); static void tile(Monitor *m); +static void togglebar(const Arg *arg); static void togglefloating(const Arg *arg); static void togglefullscreen(const Arg *arg); static void togglegaps(const Arg *arg); @@ -475,6 +495,9 @@ static int menu_fd; static pid_t menu_pid; static struct wl_event_source *menu_source; +static struct zdwl_ipc_manager_v2_interface dwl_manager_implementation = {.release = dwl_ipc_manager_release, .get_output = dwl_ipc_manager_get_output}; +static struct zdwl_ipc_output_v2_interface dwl_output_implementation = {.release = dwl_ipc_output_release, .set_tags = dwl_ipc_output_set_tags, .set_layout = dwl_ipc_output_set_layout, .set_client_tags = dwl_ipc_output_set_client_tags}; + #ifdef XWAYLAND static void activatex11(struct wl_listener *listener, void *data); static void associatex11(struct wl_listener *listener, void *data); @@ -807,6 +830,10 @@ cleanupmon(struct wl_listener *listener, void *data) LayerSurface *l, *tmp; size_t i; + DwlIpcOutput *ipc_output, *ipc_output_tmp; + wl_list_for_each_safe(ipc_output, ipc_output_tmp, &m->dwl_ipc_outputs, link) + wl_resource_destroy(ipc_output->resource); + /* m->layers[i] are intentionally not unlinked */ for (i = 0; i < LENGTH(m->layers); i++) { wl_list_for_each_safe(l, tmp, &m->layers[i], link) @@ -1132,6 +1159,8 @@ createmon(struct wl_listener *listener, void *data) m = wlr_output->data = ecalloc(1, sizeof(*m)); m->wlr_output = wlr_output; + wl_list_init(&m->dwl_ipc_outputs); + for (i = 0; i < LENGTH(m->layers); i++) wl_list_init(&m->layers[i]); @@ -1491,6 +1520,192 @@ dirtomon(enum wlr_direction dir) return selmon; } +void +dwl_ipc_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) +{ + struct wl_resource *manager_resource = wl_resource_create(client, &zdwl_ipc_manager_v2_interface, version, id); + if (!manager_resource) { + wl_client_post_no_memory(client); + return; + } + wl_resource_set_implementation(manager_resource, &dwl_manager_implementation, NULL, dwl_ipc_manager_destroy); + + zdwl_ipc_manager_v2_send_tags(manager_resource, TAGCOUNT); + + for (unsigned int i = 0; i < LENGTH(layouts); i++) + zdwl_ipc_manager_v2_send_layout(manager_resource, layouts[i].symbol); +} + +void +dwl_ipc_manager_destroy(struct wl_resource *resource) +{ + /* No state to destroy */ +} + +void +dwl_ipc_manager_get_output(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *output) +{ + DwlIpcOutput *ipc_output; + Monitor *monitor = wlr_output_from_resource(output)->data; + struct wl_resource *output_resource = wl_resource_create(client, &zdwl_ipc_output_v2_interface, wl_resource_get_version(resource), id); + if (!output_resource) + return; + + ipc_output = ecalloc(1, sizeof(*ipc_output)); + ipc_output->resource = output_resource; + ipc_output->mon = monitor; + wl_resource_set_implementation(output_resource, &dwl_output_implementation, ipc_output, dwl_ipc_output_destroy); + wl_list_insert(&monitor->dwl_ipc_outputs, &ipc_output->link); + dwl_ipc_output_printstatus_to(ipc_output); +} + +void +dwl_ipc_manager_release(struct wl_client *client, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +static void +dwl_ipc_output_destroy(struct wl_resource *resource) +{ + DwlIpcOutput *ipc_output = wl_resource_get_user_data(resource); + wl_list_remove(&ipc_output->link); + free(ipc_output); +} + +void +dwl_ipc_output_printstatus(Monitor *monitor) +{ + DwlIpcOutput *ipc_output; + wl_list_for_each(ipc_output, &monitor->dwl_ipc_outputs, link) + dwl_ipc_output_printstatus_to(ipc_output); +} + +void +dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output) +{ + Monitor *monitor = ipc_output->mon; + Client *c, *focused; + int tagmask, state, numclients, focused_client, tag; + const char *title, *appid; + focused = focustop(monitor); + zdwl_ipc_output_v2_send_active(ipc_output->resource, monitor == selmon); + + for (tag = 0 ; tag < TAGCOUNT; tag++) { + numclients = state = focused_client = 0; + tagmask = 1 << tag; + if ((tagmask & monitor->tagset[monitor->seltags]) != 0) + state |= ZDWL_IPC_OUTPUT_V2_TAG_STATE_ACTIVE; + + wl_list_for_each(c, &clients, link) { + if (c->mon != monitor) + continue; + if (!(c->tags & tagmask)) + continue; + if (c == focused) + focused_client = 1; + if (c->isurgent) + state |= ZDWL_IPC_OUTPUT_V2_TAG_STATE_URGENT; + + numclients++; + } + zdwl_ipc_output_v2_send_tag(ipc_output->resource, tag, state, numclients, focused_client); + } + title = focused ? client_get_title(focused) : ""; + appid = focused ? client_get_appid(focused) : ""; + + zdwl_ipc_output_v2_send_layout(ipc_output->resource, monitor->lt[monitor->sellt] - layouts); + zdwl_ipc_output_v2_send_title(ipc_output->resource, title); + zdwl_ipc_output_v2_send_appid(ipc_output->resource, appid); + zdwl_ipc_output_v2_send_layout_symbol(ipc_output->resource, monitor->ltsymbol); + if (wl_resource_get_version(ipc_output->resource) >= ZDWL_IPC_OUTPUT_V2_FULLSCREEN_SINCE_VERSION) { + zdwl_ipc_output_v2_send_fullscreen(ipc_output->resource, focused ? focused->isfullscreen : 0); + } + if (wl_resource_get_version(ipc_output->resource) >= ZDWL_IPC_OUTPUT_V2_FLOATING_SINCE_VERSION) { + zdwl_ipc_output_v2_send_floating(ipc_output->resource, focused ? focused->isfloating : 0); + } + zdwl_ipc_output_v2_send_frame(ipc_output->resource); +} + +void +dwl_ipc_output_set_client_tags(struct wl_client *client, struct wl_resource *resource, uint32_t and_tags, uint32_t xor_tags) +{ + DwlIpcOutput *ipc_output; + Monitor *monitor; + Client *selected_client; + unsigned int newtags = 0; + + ipc_output = wl_resource_get_user_data(resource); + if (!ipc_output) + return; + + monitor = ipc_output->mon; + selected_client = focustop(monitor); + if (!selected_client) + return; + + newtags = (selected_client->tags & and_tags) ^ xor_tags; + if (!newtags) + return; + + selected_client->tags = newtags; + if (selmon == monitor) + focusclient(focustop(monitor), 1); + arrange(selmon); + printstatus(); +} + +void +dwl_ipc_output_set_layout(struct wl_client *client, struct wl_resource *resource, uint32_t index) +{ + DwlIpcOutput *ipc_output; + Monitor *monitor; + + ipc_output = wl_resource_get_user_data(resource); + if (!ipc_output) + return; + + monitor = ipc_output->mon; + if (index >= LENGTH(layouts)) + return; + if (index != monitor->lt[monitor->sellt] - layouts) + monitor->sellt ^= 1; + + monitor->lt[monitor->sellt] = &layouts[index]; + arrange(monitor); + printstatus(); +} + +void +dwl_ipc_output_set_tags(struct wl_client *client, struct wl_resource *resource, uint32_t tagmask, uint32_t toggle_tagset) +{ + DwlIpcOutput *ipc_output; + Monitor *monitor; + unsigned int newtags = tagmask & TAGMASK; + + ipc_output = wl_resource_get_user_data(resource); + if (!ipc_output) + return; + monitor = ipc_output->mon; + + if (!newtags || newtags == monitor->tagset[monitor->seltags]) + return; + if (toggle_tagset) + monitor->seltags ^= 1; + + monitor->tagset[monitor->seltags] = newtags; + if (selmon == monitor) + focusclient(focustop(monitor), 1); + arrange(monitor); + printstatus(); +} + +void +dwl_ipc_output_release(struct wl_client *client, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + void focusclient(Client *c, int lift) { @@ -2458,38 +2673,9 @@ void printstatus(void) { Monitor *m = NULL; - Client *c; - uint32_t occ, urg, sel; - wl_list_for_each(m, &mons, link) { - occ = urg = 0; - wl_list_for_each(c, &clients, link) { - if (c->mon != m) - continue; - occ |= c->tags; - if (c->isurgent) - urg |= c->tags; - } - if ((c = focustop(m))) { - printf("%s title %s\n", m->wlr_output->name, client_get_title(c)); - printf("%s appid %s\n", m->wlr_output->name, client_get_appid(c)); - printf("%s fullscreen %d\n", m->wlr_output->name, c->isfullscreen); - printf("%s floating %d\n", m->wlr_output->name, c->isfloating); - sel = c->tags; - } else { - printf("%s title \n", m->wlr_output->name); - printf("%s appid \n", m->wlr_output->name); - printf("%s fullscreen \n", m->wlr_output->name); - printf("%s floating \n", m->wlr_output->name); - sel = 0; - } - - printf("%s selmon %u\n", m->wlr_output->name, m == selmon); - printf("%s tags %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32"\n", - m->wlr_output->name, occ, m->tagset[m->seltags], sel, urg); - printf("%s layout %s\n", m->wlr_output->name, m->ltsymbol); - } - fflush(stdout); + wl_list_for_each(m, &mons, link) + dwl_ipc_output_printstatus(m); } void @@ -3053,6 +3239,8 @@ setup(void) wl_signal_add(&output_mgr->events.apply, &output_mgr_apply); wl_signal_add(&output_mgr->events.test, &output_mgr_test); + wl_global_create(dpy, &zdwl_ipc_manager_v2_interface, 2, NULL, dwl_ipc_manager_bind); + /* Make sure XWayland clients don't connect to the parent X server, * e.g when running in the x11 backend or the wayland backend and the * compositor has Xwayland support */ @@ -3159,6 +3347,13 @@ tile(Monitor *m) } } +void +togglebar(const Arg *arg) { + DwlIpcOutput *ipc_output; + wl_list_for_each(ipc_output, &selmon->dwl_ipc_outputs, link) + zdwl_ipc_output_v2_send_toggle_visibility(ipc_output->resource); +} + void togglefloating(const Arg *arg) { diff --git a/modules/dwl/source/dwl.c.orig b/modules/dwl/source/dwl.c.orig index 1c945d8..a535d8a 100644 --- a/modules/dwl/source/dwl.c.orig +++ b/modules/dwl/source/dwl.c.orig @@ -199,6 +199,10 @@ struct Monitor { struct wlr_box w; /* window area, layout-relative */ struct wl_list layers[4]; /* LayerSurface.link */ const Layout *lt[2]; + int gappih; /* horizontal gap between windows */ + int gappiv; /* vertical gap between windows */ + int gappoh; /* horizontal outer gaps */ + int gappov; /* vertical outer gaps */ unsigned int seltags; unsigned int sellt; uint32_t tagset[2]; @@ -281,6 +285,7 @@ static void createpopup(struct wl_listener *listener, void *data); static void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint); static void cursorframe(struct wl_listener *listener, void *data); static void cursorwarptohint(void); +static void defaultgaps(const Arg *arg); static void destroydecoration(struct wl_listener *listener, void *data); static void destroydragicon(struct wl_listener *listener, void *data); static void destroyidleinhibitor(struct wl_listener *listener, void *data); @@ -301,6 +306,13 @@ static void fullscreennotify(struct wl_listener *listener, void *data); static void gpureset(struct wl_listener *listener, void *data); static void handlesig(int signo); static void incnmaster(const Arg *arg); +static void incgaps(const Arg *arg); +static void incigaps(const Arg *arg); +static void incihgaps(const Arg *arg); +static void incivgaps(const Arg *arg); +static void incogaps(const Arg *arg); +static void incohgaps(const Arg *arg); +static void incovgaps(const Arg *arg); static void inputdevice(struct wl_listener *listener, void *data); static int keybinding(uint32_t mods, xkb_keysym_t sym); static void keypress(struct wl_listener *listener, void *data); @@ -341,6 +353,7 @@ 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 setgaps(int oh, int ov, int ih, int iv); static void setlayout(const Arg *arg); static void setmfact(const Arg *arg); static void setmon(Client *c, Monitor *m, uint32_t newtags); @@ -355,6 +368,7 @@ static void tagmon(const Arg *arg); static void tile(Monitor *m); static void togglefloating(const Arg *arg); static void togglefullscreen(const Arg *arg); +static void togglegaps(const Arg *arg); static void toggletag(const Arg *arg); static void toggleview(const Arg *arg); static void unlocksession(struct wl_listener *listener, void *data); @@ -424,6 +438,7 @@ static struct wlr_output_layout *output_layout; static struct wlr_box sgeom; static struct wl_list mons; static Monitor *selmon; +static int enablegaps = 1; /* enables gaps, used by togglegaps */ /* global event handlers */ static struct wl_listener cursor_axis = {.notify = axisnotify}; @@ -526,6 +541,8 @@ applyrules(Client *c) } } } + if (c->scene_surface) + wlr_scene_node_for_each_buffer(&c->scene_surface->node, scenebuffersetopacity, c); c->isfloating |= client_is_float_type(c); setmon(c, mon, newtags); @@ -1118,6 +1135,11 @@ createmon(struct wl_listener *listener, void *data) for (i = 0; i < LENGTH(m->layers); i++) wl_list_init(&m->layers[i]); + m->gappih = gappih; + m->gappiv = gappiv; + m->gappoh = gappoh; + m->gappov = gappov; + wlr_output_state_init(&state); /* Initialize monitor state using configured rules */ m->tagset[0] = m->tagset[1] = 1; @@ -1301,6 +1323,12 @@ cursorwarptohint(void) } } +void +defaultgaps(const Arg *arg) +{ + setgaps(gappoh, gappov, gappih, gappiv); +} + void destroydecoration(struct wl_listener *listener, void *data) { @@ -1697,6 +1725,83 @@ incnmaster(const Arg *arg) arrange(selmon); } +void +incgaps(const Arg *arg) +{ + setgaps( + selmon->gappoh + arg->i, + selmon->gappov + arg->i, + selmon->gappih + arg->i, + selmon->gappiv + arg->i + ); +} + +void +incigaps(const Arg *arg) +{ + setgaps( + selmon->gappoh, + selmon->gappov, + selmon->gappih + arg->i, + selmon->gappiv + arg->i + ); +} + +void +incihgaps(const Arg *arg) +{ + setgaps( + selmon->gappoh, + selmon->gappov, + selmon->gappih + arg->i, + selmon->gappiv + ); +} + +void +incivgaps(const Arg *arg) +{ + setgaps( + selmon->gappoh, + selmon->gappov, + selmon->gappih, + selmon->gappiv + arg->i + ); +} + +void +incogaps(const Arg *arg) +{ + setgaps( + selmon->gappoh + arg->i, + selmon->gappov + arg->i, + selmon->gappih, + selmon->gappiv + ); +} + +void +incohgaps(const Arg *arg) +{ + setgaps( + selmon->gappoh + arg->i, + selmon->gappov, + selmon->gappih, + selmon->gappiv + ); +} + +void +incovgaps(const Arg *arg) +{ + setgaps( + selmon->gappoh, + selmon->gappov + arg->i, + selmon->gappih, + selmon->gappiv + ); +} + void inputdevice(struct wl_listener *listener, void *data) { @@ -2089,8 +2194,12 @@ monocle(Monitor *m) wl_list_for_each(c, &clients, link) { if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) continue; - resize(c, m->w, 0); n++; + if (!monoclegaps) + resize(c, m->w, 0); + else + resize(c, (struct wlr_box){.x = m->w.x + gappoh, .y = m->w.y + gappov, + .width = m->w.width - 2 * gappoh, .height = m->w.height - 2 * gappov}, 0); } if (n) snprintf(m->ltsymbol, LENGTH(m->ltsymbol), "[%d]", n); @@ -2639,6 +2748,16 @@ setfullscreen(Client *c, int fullscreen) printstatus(); } +void +setgaps(int oh, int ov, int ih, int iv) +{ + selmon->gappoh = MAX(oh, 0); + selmon->gappov = MAX(ov, 0); + selmon->gappih = MAX(ih, 0); + selmon->gappiv = MAX(iv, 0); + arrange(selmon); +} + void setlayout(const Arg *arg) { @@ -3000,7 +3119,7 @@ tagmon(const Arg *arg) void tile(Monitor *m) { - unsigned int mw, my, ty; + unsigned int mw, my, ty, h, r, oe = enablegaps, ie = enablegaps; int i, n = 0; Client *c; @@ -3010,22 +3129,31 @@ tile(Monitor *m) if (n == 0) return; + if (smartgaps == n) { + oe = 0; // outer gaps disabled + } + if (n > m->nmaster) - mw = m->nmaster ? (int)roundf(m->w.width * m->mfact) : 0; + mw = m->nmaster ? (int)roundf((m->w.width + m->gappiv*ie) * m->mfact) : 0; else - mw = m->w.width; - i = my = ty = 0; + mw = m->w.width - 2*m->gappov*oe + m->gappiv*ie; + i = 0; + my = ty = m->gappoh*oe; wl_list_for_each(c, &clients, link) { if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) continue; if (i < m->nmaster) { - resize(c, (struct wlr_box){.x = m->w.x, .y = m->w.y + my, .width = mw, - .height = (m->w.height - my) / (MIN(n, m->nmaster) - i)}, 0); - my += c->geom.height; + r = MIN(n, m->nmaster) - i; + h = (m->w.height - my - m->gappoh*oe - m->gappih*ie * (r - 1)) / r; + resize(c, (struct wlr_box){.x = m->w.x + m->gappov*oe, .y = m->w.y + my, + .width = mw - m->gappiv*ie, .height = h}, 0); + my += c->geom.height + m->gappih*ie; } else { - resize(c, (struct wlr_box){.x = m->w.x + mw, .y = m->w.y + ty, - .width = m->w.width - mw, .height = (m->w.height - ty) / (n - i)}, 0); - ty += c->geom.height; + r = n - i; + h = (m->w.height - ty - m->gappoh*oe - m->gappih*ie * (r - 1)) / r; + resize(c, (struct wlr_box){.x = m->w.x + mw + m->gappov*oe, .y = m->w.y + ty, + .width = m->w.width - mw - 2*m->gappov*oe, .height = h}, 0); + ty += c->geom.height + m->gappih*ie; } i++; } @@ -3048,6 +3176,13 @@ togglefullscreen(const Arg *arg) setfullscreen(sel, !sel->isfullscreen); } +void +togglegaps(const Arg *arg) +{ + enablegaps = !enablegaps; + arrange(selmon); +} + void toggletag(const Arg *arg) { diff --git a/modules/dwl/source/protocols/dwl-ipc-unstable-v2.xml b/modules/dwl/source/protocols/dwl-ipc-unstable-v2.xml new file mode 100644 index 0000000..0a6e7e5 --- /dev/null +++ b/modules/dwl/source/protocols/dwl-ipc-unstable-v2.xml @@ -0,0 +1,181 @@ + + + + + This protocol allows clients to update and get updates from dwl. + + Warning! The protocol described in this file is experimental and + backward incompatible changes may be made. Backward compatible + changes may be added together with the corresponding interface + version bump. + Backward incompatible changes are done by bumping the version + number in the protocol and interface names and resetting the + interface version. Once the protocol is to be declared stable, + the 'z' prefix and the version number in the protocol and + interface names are removed and the interface version number is + reset. + + + + + This interface is exposed as a global in wl_registry. + + Clients can use this interface to get a dwl_ipc_output. + After binding the client will recieve the dwl_ipc_manager.tags and dwl_ipc_manager.layout events. + The dwl_ipc_manager.tags and dwl_ipc_manager.layout events expose tags and layouts to the client. + + + + + Indicates that the client will not the dwl_ipc_manager object anymore. + Objects created through this instance are not affected. + + + + + + Get a dwl_ipc_outout for the specified wl_output. + + + + + + + + This event is sent after binding. + A roundtrip after binding guarantees the client recieved all tags. + + + + + + + This event is sent after binding. + A roundtrip after binding guarantees the client recieved all layouts. + + + + + + + + Observe and control a dwl output. + + Events are double-buffered: + Clients should cache events and redraw when a dwl_ipc_output.frame event is sent. + + Request are not double-buffered: + The compositor will update immediately upon request. + + + + + + + + + + + Indicates to that the client no longer needs this dwl_ipc_output. + + + + + + Indicates the client should hide or show themselves. + If the client is visible then hide, if hidden then show. + + + + + + Indicates if the output is active. Zero is invalid, nonzero is valid. + + + + + + + Indicates that a tag has been updated. + + + + + + + + + + Indicates a new layout is selected. + + + + + + + Indicates the title has changed. + + + + + + + Indicates the appid has changed. + + + + + + + Indicates the layout has changed. Since layout symbols are dynamic. + As opposed to the zdwl_ipc_manager.layout event, this should take precendence when displaying. + You can ignore the zdwl_ipc_output.layout event. + + + + + + + Indicates that a sequence of status updates have finished and the client should redraw. + + + + + + + + + + + + The tags are updated as follows: + new_tags = (current_tags AND and_tags) XOR xor_tags + + + + + + + + + + + + + + Indicates if the selected client on this output is fullscreen. + + + + + + + Indicates if the selected client on this output is floating. + + + + +