diff --git a/.gitignore b/.gitignore index 0dde90e..5c28541 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,3 @@ dwl *-protocol.c *-protocol.h .ccls-cache -config.h diff --git a/Makefile b/Makefile index 578194f..84b2a64 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 95c2afa..0149e9a 100644 --- a/config.def.h +++ b/config.def.h @@ -113,7 +113,9 @@ static const enum libinput_config_tap_button_map button_map = LIBINPUT_CONFIG_TA { MODKEY, KEY, view, {.ui = 1 << TAG} }, \ { MODKEY|WLR_MODIFIER_CTRL, KEY, toggleview, {.ui = 1 << TAG} }, \ { MODKEY|WLR_MODIFIER_SHIFT, SKEY, tag, {.ui = 1 << TAG} }, \ - { MODKEY|WLR_MODIFIER_CTRL|WLR_MODIFIER_SHIFT,SKEY,toggletag, {.ui = 1 << TAG} } + { MODKEY|WLR_MODIFIER_CTRL|WLR_MODIFIER_SHIFT,SKEY,toggletag, {.ui = 1 << TAG} }, \ + { WLR_MODIFIER_ALT, KEY, focusnthmon, {.ui = TAG} }, \ + { WLR_MODIFIER_ALT|WLR_MODIFIER_SHIFT, SKEY, tagnthmon, {.ui = TAG} } /* helper for spawning shell commands in the pre dwm-5.0 fashion */ #define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } @@ -127,6 +129,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 12f441e..f5515cb 100644 --- a/dwl.c +++ b/dwl.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -282,8 +283,10 @@ 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 Monitor *numtomon(int num); static void focusclient(Client *c, int lift); static void focusmon(const Arg *arg); +static void focusnthmon(const Arg *arg); static void focusstack(const Arg *arg); static Client *focustop(Monitor *m); static void fullscreennotify(struct wl_listener *listener, void *data); @@ -333,6 +336,7 @@ static void spawn(const Arg *arg); static void startdrag(struct wl_listener *listener, void *data); static void tag(const Arg *arg); static void tagmon(const Arg *arg); +static void tagnthmon(const Arg *arg); static void tile(Monitor *m); static void togglefloating(const Arg *arg); static void togglefullscreen(const Arg *arg); @@ -347,6 +351,7 @@ static void urgent(struct wl_listener *listener, void *data); static void view(const Arg *arg); static void virtualkeyboard(struct wl_listener *listener, void *data); static void virtualpointer(struct wl_listener *listener, void *data); +static void warpcursor(const Client *c); static Monitor *xytomon(double x, double y); static void xytonode(double x, double y, struct wlr_surface **psurface, Client **pc, LayerSurface **pl, double *nx, double *ny); @@ -541,6 +546,7 @@ arrange(Monitor *m) m->lt[m->sellt]->arrange(m); motionnotify(0, NULL, 0, 0, 0, 0); checkidleinhibitor(NULL); + warpcursor(focustop(selmon)); } void @@ -1399,6 +1405,25 @@ dirtomon(enum wlr_direction dir) return selmon; } +Monitor * +numtomon(int num) +{ + Monitor *m = NULL; + int found = 0; + int i = 0; + + wl_list_for_each(m, &mons, link) { + if (!m->wlr_output->enabled) + i--; + else if (i == num) { + found = true; + break; + } + i++; + } + return found ? m : NULL; +} + void focusclient(Client *c, int lift) { @@ -1410,6 +1435,10 @@ focusclient(Client *c, int lift) if (locked) return; + /* Warp cursor to center of client if it is outside */ + if (lift) + warpcursor(c); + /* Raise client in stacking order if requested */ if (c && lift) wlr_scene_node_raise_to_top(&c->scene->node); @@ -1485,6 +1514,16 @@ focusmon(const Arg *arg) focusclient(focustop(selmon), 1); } +void +focusnthmon(const Arg *arg) +{ + Monitor *m = numtomon(arg->i); + if (!m || m == selmon) + return; + selmon = m; + focusclient(focustop(selmon), 1); +} + void focusstack(const Arg *arg) { @@ -2708,6 +2747,19 @@ tagmon(const Arg *arg) setmon(sel, dirtomon(arg->i), 0); } +void +tagnthmon(const Arg *arg) +{ + Client *sel = focustop(selmon); + Monitor *m = numtomon(arg->i); + if (!m || !sel) + return; + setmon(sel, m, 0); + + arrange(selmon); + arrange(m); +} + void tile(Monitor *m) { @@ -3003,6 +3055,27 @@ virtualpointer(struct wl_listener *listener, void *data) wlr_cursor_map_input_to_output(cursor, device, event->suggested_output); } +void +warpcursor(const Client *c) { + if (cursor_mode != CurNormal) { + return; + } + if (!c && selmon) { + wlr_cursor_warp_closest(cursor, + NULL, + selmon->w.x + selmon->w.width / 2.0 , + selmon->w.y + selmon->w.height / 2.0); + } + else if ( c && (cursor->x < c->geom.x || + cursor->x > c->geom.x + c->geom.width || + cursor->y < c->geom.y || + cursor->y > c->geom.y + c->geom.height)) + wlr_cursor_warp_closest(cursor, + NULL, + c->geom.x + c->geom.width / 2.0, + c->geom.y + c->geom.height / 2.0); +} + Monitor * xytomon(double x, double y) { diff --git a/patches/accessnthmon.patch b/patches/accessnthmon.patch new file mode 100644 index 0000000..f69f57d --- /dev/null +++ b/patches/accessnthmon.patch @@ -0,0 +1,122 @@ +From 5f531bfb1387ded7b8817faf7df760d3b998742b Mon Sep 17 00:00:00 2001 +From: Rutherther +Date: Sat, 27 Apr 2024 21:25:16 +0200 +Subject: [PATCH] feat: access nth monitor + +--- + config.def.h | 4 +++- + dwl.c | 46 ++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 49 insertions(+), 1 deletion(-) + +diff --git a/config.def.h b/config.def.h +index 8847e58..4709c5d 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -108,7 +108,9 @@ static const enum libinput_config_tap_button_map button_map = LIBINPUT_CONFIG_TA + { MODKEY, KEY, view, {.ui = 1 << TAG} }, \ + { MODKEY|WLR_MODIFIER_CTRL, KEY, toggleview, {.ui = 1 << TAG} }, \ + { MODKEY|WLR_MODIFIER_SHIFT, SKEY, tag, {.ui = 1 << TAG} }, \ +- { MODKEY|WLR_MODIFIER_CTRL|WLR_MODIFIER_SHIFT,SKEY,toggletag, {.ui = 1 << TAG} } ++ { MODKEY|WLR_MODIFIER_CTRL|WLR_MODIFIER_SHIFT,SKEY,toggletag, {.ui = 1 << TAG} }, \ ++ { WLR_MODIFIER_ALT, KEY, focusnthmon, {.ui = TAG} }, \ ++ { WLR_MODIFIER_ALT|WLR_MODIFIER_SHIFT, SKEY, tagnthmon, {.ui = TAG} } + + /* helper for spawning shell commands in the pre dwm-5.0 fashion */ + #define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } +diff --git a/dwl.c b/dwl.c +index bf763df..1d42caf 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -11,6 +11,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -278,8 +279,10 @@ static void destroypointerconstraint(struct wl_listener *listener, void *data); + static void destroysessionlock(struct wl_listener *listener, void *data); + static void destroysessionmgr(struct wl_listener *listener, void *data); + static Monitor *dirtomon(enum wlr_direction dir); ++static Monitor *numtomon(int num); + static void focusclient(Client *c, int lift); + static void focusmon(const Arg *arg); ++static void focusnthmon(const Arg *arg); + static void focusstack(const Arg *arg); + static Client *focustop(Monitor *m); + static void fullscreennotify(struct wl_listener *listener, void *data); +@@ -329,6 +332,7 @@ static void spawn(const Arg *arg); + static void startdrag(struct wl_listener *listener, void *data); + static void tag(const Arg *arg); + static void tagmon(const Arg *arg); ++static void tagnthmon(const Arg *arg); + static void tile(Monitor *m); + static void togglefloating(const Arg *arg); + static void togglefullscreen(const Arg *arg); +@@ -1233,6 +1237,25 @@ dirtomon(enum wlr_direction dir) + return selmon; + } + ++Monitor * ++numtomon(int num) ++{ ++ Monitor *m = NULL; ++ int found = 0; ++ int i = 0; ++ ++ wl_list_for_each(m, &mons, link) { ++ if (!m->wlr_output->enabled) ++ i--; ++ else if (i == num) { ++ found = true; ++ break; ++ } ++ i++; ++ } ++ return found ? m : NULL; ++} ++ + void + focusclient(Client *c, int lift) + { +@@ -1320,6 +1343,16 @@ focusmon(const Arg *arg) + focusclient(focustop(selmon), 1); + } + ++void ++focusnthmon(const Arg *arg) ++{ ++ Monitor *m = numtomon(arg->i); ++ if (!m || m == selmon) ++ return; ++ selmon = m; ++ focusclient(focustop(selmon), 1); ++} ++ + void + focusstack(const Arg *arg) + { +@@ -2569,6 +2602,19 @@ tagmon(const Arg *arg) + setmon(sel, dirtomon(arg->i), 0); + } + ++void ++tagnthmon(const Arg *arg) ++{ ++ Client *sel = focustop(selmon); ++ Monitor *m = numtomon(arg->i); ++ if (!m || !sel) ++ return; ++ setmon(sel, m, 0); ++ ++ arrange(selmon); ++ arrange(m); ++} ++ + void + tile(Monitor *m) + { +-- +2.44.0 + diff --git a/patches/ipc.patch b/patches/ipc.patch new file mode 100644 index 0000000..9f989e7 --- /dev/null +++ b/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/patches/warpcursor.patch b/patches/warpcursor.patch new file mode 100644 index 0000000..6ac0329 --- /dev/null +++ b/patches/warpcursor.patch @@ -0,0 +1,71 @@ +From 4951ccc89dac2b557994b2f6c3aacb2398e2d1b1 Mon Sep 17 00:00:00 2001 +From: Ben Collerson +Date: Thu, 4 Jan 2024 20:30:01 +1000 +Subject: [PATCH] warpcursor + +--- + dwl.c | 27 +++++++++++++++++++++++++++ + 1 file changed, 27 insertions(+) + +diff --git a/dwl.c b/dwl.c +index 145fd018..f7ad6c13 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -347,6 +347,7 @@ static void urgent(struct wl_listener *listener, void *data); + static void view(const Arg *arg); + static void virtualkeyboard(struct wl_listener *listener, void *data); + static void virtualpointer(struct wl_listener *listener, void *data); ++static void warpcursor(const Client *c); + static Monitor *xytomon(double x, double y); + static void xytonode(double x, double y, struct wlr_surface **psurface, + Client **pc, LayerSurface **pl, double *nx, double *ny); +@@ -514,6 +515,7 @@ arrange(Monitor *m) + m->lt[m->sellt]->arrange(m); + motionnotify(0, NULL, 0, 0, 0, 0); + checkidleinhibitor(NULL); ++ warpcursor(focustop(selmon)); + } + + void +@@ -1323,6 +1325,10 @@ focusclient(Client *c, int lift) + if (locked) + return; + ++ /* Warp cursor to center of client if it is outside */ ++ if (lift) ++ warpcursor(c); ++ + /* Raise client in stacking order if requested */ + if (c && lift) + wlr_scene_node_raise_to_top(&c->scene->node); +@@ -2927,6 +2933,27 @@ virtualpointer(struct wl_listener *listener, void *data) + wlr_cursor_map_input_to_output(cursor, &pointer.base, event->suggested_output); + } + ++void ++warpcursor(const Client *c) { ++ if (cursor_mode != CurNormal) { ++ return; ++ } ++ if (!c && selmon) { ++ wlr_cursor_warp_closest(cursor, ++ NULL, ++ selmon->w.x + selmon->w.width / 2.0 , ++ selmon->w.y + selmon->w.height / 2.0); ++ } ++ else if ( c && (cursor->x < c->geom.x || ++ cursor->x > c->geom.x + c->geom.width || ++ cursor->y < c->geom.y || ++ cursor->y > c->geom.y + c->geom.height)) ++ wlr_cursor_warp_closest(cursor, ++ NULL, ++ c->geom.x + c->geom.width / 2.0, ++ c->geom.y + c->geom.height / 2.0); ++} ++ + Monitor * + xytomon(double x, double y) + { +-- +2.45.2 + 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. + + + + +