aboutsummaryrefslogtreecommitdiff
path: root/sys/cmd/dwm
diff options
context:
space:
mode:
authorNicholas Noll <nbnoll@eml.cc>2020-06-04 19:10:07 -0700
committerNicholas Noll <nbnoll@eml.cc>2020-06-04 19:10:07 -0700
commit0a8f62d8c7116be9e344f351df679599908fb29c (patch)
treebcf7fcf995b4cb97cec0b947e2b299d6bcc8ae1d /sys/cmd/dwm
parentea50cbe1bf103372a3461c80cb172f4fb4167088 (diff)
refactored
Diffstat (limited to 'sys/cmd/dwm')
-rw-r--r--sys/cmd/dwm/client.c254
-rw-r--r--sys/cmd/dwm/config.h91
-rw-r--r--sys/cmd/dwm/decoration.c52
-rw-r--r--sys/cmd/dwm/dwm.c184
-rw-r--r--sys/cmd/dwm/dwm.h346
-rw-r--r--sys/cmd/dwm/func.c217
-rw-r--r--sys/cmd/dwm/input.c333
-rw-r--r--sys/cmd/dwm/layer.c329
-rw-r--r--sys/cmd/dwm/layout.c37
-rw-r--r--sys/cmd/dwm/output.c257
-rw-r--r--sys/cmd/dwm/rules.mk50
-rw-r--r--sys/cmd/dwm/util.c22
12 files changed, 2172 insertions, 0 deletions
diff --git a/sys/cmd/dwm/client.c b/sys/cmd/dwm/client.c
new file mode 100644
index 0000000..cc9730b
--- /dev/null
+++ b/sys/cmd/dwm/client.c
@@ -0,0 +1,254 @@
+#include "dwm.h"
+
+#define WLR_EDGE_ALL WLR_EDGE_TOP|WLR_EDGE_BOTTOM|WLR_EDGE_LEFT|WLR_EDGE_RIGHT
+
+
+/* local callbacks */
+static void map(struct wl_listener *ev, void *arg);
+static void unmap(struct wl_listener *ev, void *arg);
+static void delete(struct wl_listener *ev, void *arg);
+
+/* globals */
+static char *broken = "<broken>";
+
+// -----------------------------------------------------------------------
+// methods
+
+void
+applybounds(Client *c, struct wlr_box *bbox)
+{
+ c->dim.width = MAX(1, c->dim.width);
+ c->dim.height = MAX(1, c->dim.height);
+
+ if (c->dim.x >= bbox->x + bbox->width)
+ c->dim.x = bbox->x + bbox->width - c->dim.width;
+ if (c->dim.y >= bbox->y + bbox->height)
+ c->dim.y = bbox->y + bbox->height - c->dim.height;
+ if (c->dim.x + c->dim.width + 2 * c->bw <= bbox->x)
+ c->dim.x = bbox->x;
+ if (c->dim.y + c->dim.height + 2 * c->bw <= bbox->y)
+ c->dim.y = bbox->y;
+}
+
+void
+applyrules(Client *c)
+{
+ char *id, *title;
+ uint i, newtags = 0;
+ const Rule *r;
+ Monitor *m, *it;
+
+ m = monitor;
+ /* rule matching */
+ c->floating = 0;
+ if (!(id = c->surf->toplevel->app_id))
+ id = broken;
+ if (!(title = c->surf->toplevel->title))
+ title = broken;
+
+ for (r = apprules; r < arrend(apprules); r++) {
+ if ((!r->title || strstr(title, r->title))
+ && (!r->id || strstr(id, r->id)))
+ {
+ c->floating = r->floating;
+ newtags |= r->tags;
+ i = 0;
+ wl_list_for_each(it, &dwm.odevs, link)
+ if (r->monitor == i++)
+ m = it;
+ }
+ }
+ setmonitor(c, m, newtags);
+}
+
+void
+resize(Client *c, int x, int y, int w, int h, int interact)
+{
+ struct wlr_box *bbox;
+
+ bbox = interact ? &dwm.dim : &c->m->area.win;
+ c->dim.x = x;
+ c->dim.y = y;
+ c->dim.width = w;
+ c->dim.height = h;
+
+ applybounds(c, bbox);
+ /* wlroots makes this a no-op if size hasn't changed */
+ wlr_xdg_toplevel_set_size(c->surf, c->dim.width - 2*c->bw - gapwidth, c->dim.height - 2*c->bw - gapwidth);
+}
+
+Client *
+getclient(void)
+{
+ Client *c;
+
+ c = wl_container_of(dwm.focus.next, c, link.focus);
+ if (wl_list_empty(&dwm.focus) || !VISIBLEON(c, monitor))
+ return nil;
+
+ return c;
+}
+
+Client *
+clientat(double x, double y)
+{
+ Client *c;
+ wl_list_for_each(c, &dwm.stack, link.stack)
+ if (VISIBLEON(c, c->m) && wlr_box_contains_point(&c->dim, x, y))
+ return c;
+ return nil;
+}
+
+
+Client *
+lastfocus(void)
+{
+ Client *c;
+ wl_list_for_each(c, &dwm.focus, link.focus)
+ if (VISIBLEON(c, monitor))
+ return c;
+
+ return nil;
+}
+
+void
+setfocus(Client *c, struct wlr_surface *surf, int lift)
+{
+ struct wlr_surface *prev_surface;
+ struct wlr_xdg_surface *prev;
+ struct wlr_keyboard *kb;
+
+ if (c) {
+ if (!surf)
+ surf = c->surf->surface;
+ monitor = c->m;
+ }
+
+ prev_surface = dwm.seat->keyboard_state.focused_surface;
+ if (prev_surface == surf)
+ return;
+
+ if (prev_surface) {
+ prev = wlr_xdg_surface_from_wlr_surface(dwm.seat->keyboard_state.focused_surface);
+ wlr_xdg_toplevel_set_activated(prev, 0);
+ }
+
+ kb = wlr_seat_get_keyboard(dwm.seat);
+ wlr_seat_keyboard_notify_enter(dwm.seat, surf, kb->keycodes, kb->num_keycodes, &kb->modifiers);
+
+ if (c) {
+ wl_list_remove(&c->link.focus);
+ wl_list_insert(&dwm.focus, &c->link.focus);
+ if (lift) {
+ wl_list_remove(&c->link.stack);
+ wl_list_insert(&dwm.stack, &c->link.stack);
+ }
+ wlr_xdg_toplevel_set_activated(c->surf, 1);
+ }
+}
+
+void
+setfloating(Client *c, int f)
+{
+ if (c->floating == f)
+ return;
+
+ c->floating = f;
+ arrange(c->m);
+}
+
+void
+pointerfocus(Client *c, struct wlr_surface *surf, double sx, double sy, uint32 time)
+{
+ if (c && !surf)
+ surf = c->surf->surface;
+
+ if (surf && surf == dwm.seat->pointer_state.focused_surface) {
+ wlr_seat_pointer_notify_motion(dwm.seat, time, sx, sy);
+ return;
+ }
+
+ wlr_seat_pointer_notify_enter(dwm.seat, surf, sx, sy);
+ /* If keyboard focus follows mouse, enforce that */
+ if (sloppyfocus && surf)
+ setfocus(c, surf, 0);
+}
+
+
+// -----------------------------------------------------------------------
+// callback function implementations
+
+/* xdg-surfaces */
+void
+ev·newclient(struct wl_listener *ev, void *arg)
+{
+ Client *c;
+ struct wlr_xdg_surface *surf;
+
+ surf = arg;
+ if (surf->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL)
+ return;
+
+ c = surf->data = calloc(1, sizeof(*c));
+ c->surf = surf;
+ c->bw = borderwidth;
+
+ wlr_xdg_toplevel_set_tiled(surf, WLR_EDGE_ALL);
+
+ /* install callbacks */
+ c->ev.map.notify = map;
+ c->ev.unmap.notify = unmap;
+ c->ev.free.notify = delete;
+
+ wl_signal_add(&surf->events.map, &c->ev.map);
+ wl_signal_add(&surf->events.unmap, &c->ev.unmap);
+ wl_signal_add(&surf->events.destroy, &c->ev.free);
+}
+
+static
+void
+map(struct wl_listener *ev, void *arg)
+{
+ Client *c;
+
+ c = wl_container_of(ev, c, ev.map);
+
+ wl_list_insert(&dwm.tiles, &c->link.tiles);
+ wl_list_insert(&dwm.focus, &c->link.focus);
+ wl_list_insert(&dwm.stack, &c->link.stack);
+
+ wlr_xdg_surface_get_geometry(c->surf, &c->dim);
+
+ c->dim.width += 2 * c->bw;
+ c->dim.height += 2 * c->bw;
+
+ applyrules(c);
+}
+
+static
+void
+unmap(struct wl_listener *ev, void *arg)
+{
+ Monitor *m;
+ Client *c;
+
+ c = wl_container_of(ev, c, ev.unmap);
+ m = c->m;
+ setmonitor(c, nil, 0);
+
+ wl_list_remove(&c->link.tiles);
+ wl_list_remove(&c->link.stack);
+ wl_list_remove(&c->link.focus);
+
+ c->surf = nil;
+ arrange(m);
+}
+
+static
+void
+delete(struct wl_listener *ev, void *arg)
+{
+ Client *c;
+ c = wl_container_of(ev, c, ev.free);
+ free(c);
+}
diff --git a/sys/cmd/dwm/config.h b/sys/cmd/dwm/config.h
new file mode 100644
index 0000000..c0e142c
--- /dev/null
+++ b/sys/cmd/dwm/config.h
@@ -0,0 +1,91 @@
+/* global appearance */
+
+static const int sloppyfocus = 1; /* focus follows mouse */
+static const uint borderwidth = 1; /* width window borders (pixel) */
+static const uint gapwidth = 4; /* width of gaps(pixel) */
+static const float rootcolor[] = {0.3, 0.3, 0.3, 1.0};
+static const float bordercolor[] = {0.5, 0.5, 0.5, 1.0};
+
+/* tagging */
+static const byte *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };
+
+static const Rule apprules[] = {
+ /* app_id title tags mask isfloating monitor */
+};
+
+static const Layout layouts[] = {
+ /* symbol arrange function */
+ { "[]=", tile },
+ { "><>", nil }, /* no layout function means floating behavior */
+};
+
+/* monitors */
+static const MonitorRule monitorrules[] = {
+ /* name mfact nmaster scale layout rotate/reflect */
+ /* example of a HiDPI laptop monitor:
+ { "eDP-1", 0.5, 1, 2, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL },
+ */
+ { nil, 0.55, 1, 1, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL },
+};
+
+/* keyboard */
+static const struct xkb_rule_names xkb_rules = {
+ /* can specify fields: rules, model, layout, variant, options */
+ /* example:
+ .options = "ctrl:nocaps",
+ */
+};
+
+#define MODKEY WLR_MODIFIER_LOGO
+#define TAGKEYS(KEY,SKEY,TAG) \
+ { 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} }
+
+/* commands */
+static const byte *termcmd[] = { "alacritty", 0 };
+static const byte *menucmd[] = { "waybar", 0 };
+
+/* keymaps */
+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_j, focusstack, {.i = +1} },
+ { MODKEY, XKB_KEY_k, focusstack, {.i = -1} },
+ { MODKEY, XKB_KEY_i, incmaster, {.i = +1} },
+ { MODKEY, XKB_KEY_d, incmaster, {.i = -1} },
+ { MODKEY, XKB_KEY_h, setmfact, {.f = -0.05} },
+ { MODKEY, XKB_KEY_l, setmfact, {.f = +0.05} },
+ { MODKEY, XKB_KEY_Tab, view, {0} },
+ { MODKEY, XKB_KEY_t, setlayout, {.v = &layouts[0]} },
+ { MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} },
+ { MODKEY, XKB_KEY_0, view, {.ui = ~0} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} },
+ { MODKEY, XKB_KEY_comma, focusmonitor, {.i = -1} },
+ { MODKEY, XKB_KEY_period, focusmonitor, {.i = +1} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_less, tagmonitor, {.i = -1} },
+ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_greater, tagmonitor, {.i = +1} },
+ TAGKEYS( XKB_KEY_1, XKB_KEY_exclam, 0),
+ TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1),
+ TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2),
+ TAGKEYS( XKB_KEY_4, XKB_KEY_dollar, 3),
+ TAGKEYS( XKB_KEY_5, XKB_KEY_percent, 4),
+ TAGKEYS( XKB_KEY_6, XKB_KEY_caret, 5),
+ 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} },
+ { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_Terminate_Server, quit, {0} },
+#define CHVT(n) { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_XF86Switch_VT_##n, chvt, {.ui = (n)} }
+ CHVT(1), CHVT(2), CHVT(3), CHVT(4), CHVT(5), CHVT(6),
+ CHVT(7), CHVT(8), CHVT(9), CHVT(10), CHVT(11), CHVT(12),
+};
+
+static const Button buttons[] = {
+ { MODKEY, BTN_LEFT, moveresize, {.ui = MouseMove} },
+ { MODKEY, BTN_MIDDLE, togglefloating, {0} },
+ { MODKEY, BTN_RIGHT, moveresize, {.ui = MouseResize} },
+};
diff --git a/sys/cmd/dwm/decoration.c b/sys/cmd/dwm/decoration.c
new file mode 100644
index 0000000..af0dc5f
--- /dev/null
+++ b/sys/cmd/dwm/decoration.c
@@ -0,0 +1,52 @@
+#include "dwm.h"
+
+/* internal callbacks */
+static void mode(struct wl_listener *ev, void *arg);
+static void delete(struct wl_listener *ev, void *arg);
+
+// -----------------------------------------------------------------------
+// implementations
+
+void
+ev·newdecoration(struct wl_listener *ev, void *arg)
+{
+ struct wlr_server_decoration *wlr;
+ Deco *deco;
+
+ wlr = arg;
+
+ deco = calloc(1, sizeof(*deco));
+ if (!deco)
+ return;
+
+ deco->wlr = wlr;
+
+ wl_signal_add(&wlr->events.destroy, &deco->ev.free);
+ deco->ev.free.notify = delete;
+
+ wl_signal_add(&wlr->events.mode, &deco->ev.mode);
+ deco->ev.mode.notify = mode;
+
+ wl_list_insert(&dwm.decos, &deco->link);
+}
+
+static
+void
+delete(struct wl_listener *ev, void *arg)
+{
+ Deco *deco;
+
+ deco = wl_container_of(ev, deco, ev.free);
+
+ wl_list_remove(&deco->ev.free.link);
+ wl_list_remove(&deco->ev.mode.link);
+ wl_list_remove(&deco->link);
+ free(deco);
+}
+
+static
+void
+mode(struct wl_listener *ev, void *arg)
+{
+ /* no op */
+}
diff --git a/sys/cmd/dwm/dwm.c b/sys/cmd/dwm/dwm.c
new file mode 100644
index 0000000..a915cde
--- /dev/null
+++ b/sys/cmd/dwm/dwm.c
@@ -0,0 +1,184 @@
+#include "dwm.h"
+
+/* globals */
+WindowManager dwm = {
+ 0,
+ .ev = {
+ .loop = nil,
+ .output = {.notify=ev·newmonitor},
+ .input = {.notify=ev·newinput},
+ .client = {.notify=ev·newclient},
+ .layer = {.notify=ev·newlayershell},
+ .deco = {.notify=ev·newdecoration},
+ },
+};
+
+void
+setup(void)
+{
+ /* wayland boilerplate */
+ dwm.display = wl_display_create();
+ if (!dwm.display)
+ fatal("failed to initialize display");
+
+ dwm.backend = wlr_backend_autocreate(dwm.display, nil);
+ if (!dwm.backend)
+ fatal("failed to create backend");
+
+ dwm.draw = wlr_backend_get_renderer(dwm.backend);
+ if (!dwm.draw)
+ fatal("failed to initialize renderer");
+ wlr_renderer_init_wl_display(dwm.draw, dwm.display);
+
+ /* intialize the compositor and some automated handlers */
+ dwm.compositor = wlr_compositor_create(dwm.display, dwm.draw);
+
+ wlr_data_device_manager_create(dwm.display);
+ wlr_gamma_control_manager_v1_create(dwm.display);
+ wlr_gtk_primary_selection_device_manager_create(dwm.display);
+
+ /* middle management */
+ wlr_export_dmabuf_manager_v1_create(dwm.display);
+ wlr_screencopy_manager_v1_create(dwm.display);
+ wlr_data_control_manager_v1_create(dwm.display);
+ wlr_primary_selection_v1_device_manager_create(dwm.display);
+
+ dwm.ev.loop = wl_display_get_event_loop(dwm.display);
+ if (!dwm.ev.loop)
+ fatal("failed to initialize event loop");
+
+ dwm.layout = wlr_output_layout_create();
+ wlr_xdg_output_manager_v1_create(dwm.display, dwm.layout);
+
+ /* grab output devices */
+ wl_list_init(&dwm.odevs);
+ wl_signal_add(&dwm.backend->events.new_output, &dwm.ev.output);
+
+ /* initialize window structures */
+ wl_list_init(&dwm.tiles);
+ wl_list_init(&dwm.stack);
+ wl_list_init(&dwm.focus);
+ wl_list_init(&dwm.decos);
+
+ /* layer shell */
+ dwm.laysh = wlr_layer_shell_v1_create(dwm.display);
+ wl_signal_add(&dwm.laysh->events.new_surface, &dwm.ev.layer);
+
+ /* xdg shell */
+ dwm.xdgsh = wlr_xdg_shell_create(dwm.display);
+ wl_signal_add(&dwm.xdgsh->events.new_surface, &dwm.ev.client);
+
+ /* decorations */
+ dwm.mngr.deco = wlr_server_decoration_manager_create(dwm.display);
+ wlr_server_decoration_manager_set_default_mode(dwm.mngr.deco, WLR_SERVER_DECORATION_MANAGER_MODE_SERVER);
+ wl_signal_add(&dwm.mngr.deco->events.new_decoration, &dwm.ev.deco);
+ wl_list_init(&dwm.decos);
+
+ dwm.mngr.deco = wlr_server_decoration_manager_create(dwm.display);
+
+ /* grab input devices and install callbacks */
+ mouse.cursor = wlr_cursor_create();
+ if (!mouse.cursor)
+ fatal("no mouse found");
+ wlr_cursor_attach_output_layout(mouse.cursor, dwm.layout);
+ mouse.manager = wlr_xcursor_manager_create(nil, 24);
+
+ /* attach the static cursor object to event handlers */
+ wl_signal_add(&mouse.cursor->events.axis, &mouse.ev.axis);
+ wl_signal_add(&mouse.cursor->events.frame, &mouse.ev.frame);
+ wl_signal_add(&mouse.cursor->events.button, &mouse.ev.button);
+ wl_signal_add(&mouse.cursor->events.motion, &mouse.ev.motion);
+ wl_signal_add(&mouse.cursor->events.motion_absolute, &mouse.ev.absmotion);
+
+ wl_list_init(&dwm.idevs);
+ wl_list_init(&dwm.keyboards);
+
+ wl_signal_add(&dwm.backend->events.new_input, &dwm.ev.input);
+
+ dwm.seat = wlr_seat_create(dwm.display, "seat0");
+
+ wl_signal_add(&dwm.seat->events.request_set_cursor, &mouse.ev.cursor);
+ wl_signal_add(&dwm.seat->events.request_set_selection, &mouse.ev.sel);
+ wl_signal_add(&dwm.seat->events.request_set_primary_selection, &mouse.ev.psel);
+}
+
+void
+run(void)
+{
+ byte *socket;
+ pid_t start = -1;
+
+ socket = (byte*)wl_display_add_socket_auto(dwm.display);
+ if (!socket) {
+ wlr_backend_destroy(dwm.backend);
+ fatal("could not open socket");
+ }
+
+ if (!wlr_backend_start(dwm.backend)) {
+ wlr_backend_destroy(dwm.backend);
+ wl_display_destroy(dwm.display);
+ fatal("failed to start backend");
+ }
+
+ monitor = monitorat(mouse.cursor->x, mouse.cursor->y);
+ if (!monitor)
+ fatal("no monitor found");
+
+ wlr_cursor_warp_closest(mouse.cursor, nil, mouse.cursor->x, mouse.cursor->y);
+ wlr_xcursor_manager_set_cursor_image(mouse.manager, "left_ptr", mouse.cursor);
+
+ setenv("WAYLAND_DISPLAY", socket, 1);
+
+ wlr_log(WLR_INFO, "running dwm on WAYLAND_DISPLAY\n");
+ wl_display_run(dwm.display);
+}
+
+void
+cleanup(void)
+{
+ wl_display_destroy_clients(dwm.display);
+ wl_display_destroy(dwm.display);
+ wlr_backend_destroy(dwm.backend);
+}
+
+void
+usage(void)
+{
+ printf("usage: %s [-qvd] [-s startup_cmd]\n", argv0);
+ exit(1);
+}
+
+int
+main(int argc, byte *argv[])
+{
+ byte *cmd;
+ enum wlr_log_importance lvl;
+
+ cmd = nil;
+ lvl = WLR_ERROR;
+
+ ARGBEGIN {
+ case 'q':
+ lvl = WLR_SILENT;
+ break;
+ case 'v':
+ lvl = WLR_INFO;
+ break;
+ case 'd':
+ lvl = WLR_DEBUG;
+ break;
+ case 's':
+ cmd = EARGF(usage());
+ break;
+ default:
+ usage();
+ } ARGEND;
+
+ wlr_log_init(lvl, nil);
+
+ setup();
+ run();
+ cleanup();
+
+ return 0;
+}
diff --git a/sys/cmd/dwm/dwm.h b/sys/cmd/dwm/dwm.h
new file mode 100644
index 0000000..148ca89
--- /dev/null
+++ b/sys/cmd/dwm/dwm.h
@@ -0,0 +1,346 @@
+#pragma once
+
+#include <u.h>
+#include <libn.h>
+
+#include <wayland-server-core.h>
+
+#define WLR_USE_UNSTABLE
+#include <wlr/backend.h>
+#include <wlr/render/wlr_renderer.h>
+#include <wlr/types/wlr_compositor.h>
+#include <wlr/types/wlr_cursor.h>
+#include <wlr/types/wlr_data_control_v1.h>
+#include <wlr/types/wlr_data_device.h>
+#include <wlr/types/wlr_export_dmabuf_v1.h>
+#include <wlr/types/wlr_gamma_control_v1.h>
+#include <wlr/types/wlr_gtk_primary_selection.h>
+#include <wlr/types/wlr_idle.h>
+#include <wlr/types/wlr_input_device.h>
+#include <wlr/types/wlr_keyboard.h>
+#include <wlr/types/wlr_layer_shell_v1.h>
+#include <wlr/types/wlr_matrix.h>
+#include <wlr/types/wlr_output.h>
+#include <wlr/types/wlr_output_damage.h>
+#include <wlr/types/wlr_output_layout.h>
+#include <wlr/types/wlr_pointer.h>
+#include <wlr/types/wlr_primary_selection.h>
+#include <wlr/types/wlr_primary_selection_v1.h>
+#include <wlr/types/wlr_screencopy_v1.h>
+#include <wlr/types/wlr_seat.h>
+#include <wlr/types/wlr_server_decoration.h>
+#include <wlr/types/wlr_xcursor_manager.h>
+#include <wlr/types/wlr_xdg_decoration_v1.h>
+#include <wlr/types/wlr_xdg_shell.h>
+#include <wlr/types/wlr_xdg_output_v1.h>
+#include <wlr/util/log.h>
+
+#include "xdg-shell.h"
+#include "wlr-layer-shell.h"
+
+#include <signal.h>
+#include <wait.h>
+#include <xkbcommon/xkbcommon.h>
+#include <linux/input-event-codes.h>
+
+
+/* global macros */
+#define VISIBLEON(C, M) ((C)->m == (M) && ((C)->tags & (M)->tagset[(M)->seltags]))
+#define CLEANMASK(mask) (mask & ~WLR_MODIFIER_CAPS)
+#define TAGMASK ((1 << arrlen(tags)) - 1)
+
+/* main types */
+
+typedef union Arg Arg;
+typedef struct Button Button;
+typedef struct Mouse Mouse;
+typedef struct Grab Grab;
+typedef struct Key Key;
+typedef struct Keyboard Keyboard;
+typedef struct Monitor Monitor;
+typedef struct Layout Layout;
+typedef struct Client Client;
+typedef struct Deco Deco;
+typedef struct Layer Layer;
+typedef struct Payload Payload;
+
+typedef struct Rule Rule;
+typedef struct MonitorRule MonitorRule;
+
+typedef struct WindowManager WindowManager;
+
+union Arg
+{
+ int i;
+ uint ui;
+ float f;
+ const void *v;
+};
+
+struct Button
+{
+ uint mod;
+ uint kind;
+ void (*func)(const Arg *);
+ Arg arg;
+};
+
+enum
+{
+ MouseNormal=0, MouseMove, MouseResize,
+};
+
+struct Mouse
+{
+ uint mode;
+ struct wlr_xcursor_manager *manager;
+ struct wlr_cursor *cursor;
+ struct {
+ struct wl_listener axis;
+ struct wl_listener frame;
+ struct wl_listener button;
+ struct wl_listener motion;
+ struct wl_listener absmotion;
+ struct wl_listener cursor;
+ struct wl_listener sel;
+ struct wl_listener psel;
+ } ev;
+};
+
+struct Grab
+{
+ Client *c;
+ int x, y;
+};
+
+struct Key
+{
+ uint32 mod;
+ xkb_keysym_t sym;
+ void (*func)(const Arg *);
+ Arg arg;
+};
+
+struct Keyboard
+{
+ struct wl_list link;
+ struct wlr_input_device *dev;
+ struct {
+ struct wl_listener modifier;
+ struct wl_listener keypress;
+ } ev;
+};
+
+struct Layer
+{
+ struct wl_list link;
+ struct wlr_layer_surface_v1 *surf;
+
+ struct {
+ struct wl_listener map;
+ struct wl_listener unmap;
+ struct wl_listener free;
+ struct wl_listener commit;
+ struct wl_listener detach;
+ /* struct wl_listener popup; */
+ } ev;
+
+ struct wlr_box dim;
+ enum zwlr_layer_shell_v1_layer z;
+};
+struct Monitor {
+ struct wl_list link;
+ struct wlr_output *dev;
+ struct {
+ struct wl_listener draw;
+ struct wl_listener free;
+ } ev;
+ struct {
+ struct wl_signal kill;
+ } sig;
+ struct {
+ struct wlr_box all;
+ struct wlr_box win;
+ } area;
+
+ struct wl_list layers[4];
+ struct wlr_output_damage damage;
+
+ const Layout *lt[2];
+ uint seltags;
+ uint sellt;
+ uint tagset[2];
+ double mfact;
+ int nmaster;
+};
+
+struct Layout
+{
+ char *sym;
+ void (*arrange)(Monitor *);
+};
+
+struct MonitorRule {
+ char *name;
+ float mfact;
+ int nmaster;
+ float scale;
+ const Layout *lt;
+ enum wl_output_transform rr;
+};
+
+struct Rule {
+ char *id;
+ char *title;
+ uint tags;
+ int floating;
+ int monitor;
+};
+
+struct Client
+{
+ struct wlr_xdg_surface *surf;
+ struct wlr_box dim;
+ struct {
+ struct wl_list tiles;
+ struct wl_list stack;
+ struct wl_list focus;
+ } link;
+ struct {
+ struct wl_listener map;
+ struct wl_listener unmap;
+ struct wl_listener free;
+ } ev;
+ int bw;
+ uint tags;
+ int floating;
+ Monitor *m;
+};
+
+struct Deco
+{
+ struct wl_list link;
+ struct wlr_server_decoration *wlr;
+
+ struct {
+ struct wl_listener free;
+ struct wl_listener mode;
+ } ev;
+};
+
+struct Payload
+{
+ struct wlr_output *dev;
+ struct timespec *now;
+ int x, y;
+};
+
+struct WindowManager
+{
+ struct wl_display *display;
+ struct wlr_backend *backend;
+ struct wlr_renderer *draw;
+ struct wlr_compositor *compositor;
+ struct wlr_xdg_shell *xdgsh;
+ struct wlr_layer_shell_v1 *laysh;
+ struct wlr_output_layout *layout;
+ struct wlr_idle *idle;
+ struct wlr_seat *seat;
+
+ struct {
+ struct wlr_server_decoration_manager *deco;
+ struct wlr_xdg_decoration_manager *xdeco;
+ } mngr;
+
+ struct {
+ struct wl_event_loop *loop;
+ /* i/o devices */
+ struct wl_listener input;
+ struct wl_listener output;
+ struct wl_listener client;
+ /* shells */
+ struct wl_listener layer;
+ struct wl_listener deco;
+ } ev;
+
+ struct {
+ struct wl_list odevs;
+ struct wl_list idevs;
+ struct wl_list tiles; /* order of tiles */
+ struct wl_list stack; /* order w/in stack */
+ struct wl_list focus; /* order of focus */
+ struct wl_list keyboards;
+ struct wl_list decos;
+ };
+
+ /* geometry of union of output devices */
+ struct wlr_box dim;
+};
+
+/* funcs.c */
+void chvt(const Arg *arg);
+void incmaster(const Arg *arg);
+void focusmonitor(const Arg *arg);
+void focusstack(const Arg *arg);
+void moveresize(const Arg *arg);
+void quit(const Arg *arg);
+void setlayout(const Arg *arg);
+void setmfact(const Arg *arg);
+void spawn(const Arg *arg);
+void tag(const Arg *arg);
+void tagmonitor(const Arg *arg);
+void togglefloating(const Arg *arg);
+void toggletag(const Arg *arg);
+void toggleview(const Arg *arg);
+void view(const Arg *arg);
+
+/* layouts */
+void tile(Monitor *m);
+
+#include "config.h"
+
+// -----------------------------------------------------------------------
+// global variables
+
+extern Mouse mouse;
+extern Grab grab;
+extern Monitor *monitor; /* currently focused */
+extern WindowManager dwm;
+
+// -----------------------------------------------------------------------
+// global functions
+
+/* util.c */
+void fatal(byte *fmt, ...);
+void scale(struct wlr_box *box, float by);
+
+/* input.c */
+void ev·newinput(struct wl_listener *ev, void *arg);
+
+/* client.c */
+void ev·newclient(struct wl_listener *ev, void *arg);
+
+void applybounds(Client *c, struct wlr_box *bbox);
+void applyrules(Client *c);
+Client *clientat(double x, double y);
+Client *getclient(void);
+Client *lastfocus(void);
+void resize(Client *c, int x, int y, int w, int h, int interact);
+void setfocus(Client *c, struct wlr_surface *surf, int lift);
+void setfloating(Client *c, int f);
+void pointerfocus(Client *c, struct wlr_surface *surf, double sx, double sy, uint32 time);
+
+/* output.c */
+void ev·newmonitor(struct wl_listener *ev, void *arg);
+
+void arrange(Monitor *m);
+void setmonitor(Client *c, Monitor *m, uint newtags);
+Monitor *getmonitor(int dir);
+Monitor *monitorat(double x, double y);
+
+/* layer.c */
+void ev·newlayershell(struct wl_listener *ev, void *arg);
+void arrangelayers(Monitor *m);
+
+/* decoration.c */
+void ev·newdecoration(struct wl_listener *ev, void *arg);
diff --git a/sys/cmd/dwm/func.c b/sys/cmd/dwm/func.c
new file mode 100644
index 0000000..5b0df24
--- /dev/null
+++ b/sys/cmd/dwm/func.c
@@ -0,0 +1,217 @@
+#include "dwm.h"
+
+void
+chvt(const Arg *arg)
+{
+ struct wlr_session *s;
+
+ s = wlr_backend_get_session(dwm.backend);
+ if (!s)
+ return;
+
+ wlr_session_change_vt(s, arg->ui);
+}
+
+void
+incmaster(const Arg *arg)
+{
+ monitor->nmaster = MAX(monitor->nmaster + arg->i, 0);
+ arrange(monitor);
+}
+
+void
+focusmonitor(const Arg *arg)
+{
+ Monitor *m;
+
+ m = getmonitor(arg->i);
+
+ if (m == monitor)
+ return;
+
+ monitor = m;
+ setfocus(lastfocus(), nil, 1);
+}
+
+void
+focusstack(const Arg *arg)
+{
+ Client *c, *sel;
+
+ sel = getclient();
+ if (!sel)
+ return;
+
+ if (arg->i > 0) {
+ wl_list_for_each(c, &sel->link.tiles, link.tiles) {
+ if (&c->link.tiles == &dwm.tiles)
+ continue;
+ if (VISIBLEON(c, monitor))
+ break;
+ }
+ } else {
+ wl_list_for_each_reverse(c, &sel->link.tiles, link.tiles) {
+ if (&c->link.tiles == &dwm.tiles)
+ continue; /* wrap past the sentinel node */
+ if (VISIBLEON(c, monitor))
+ break; /* found it */
+ }
+ }
+ /* If only one client is visible on selmon, then c == sel */
+ setfocus(c, nil, 1);
+}
+
+void
+moveresize(const Arg *arg)
+{
+ grab.c = clientat(mouse.cursor->x, mouse.cursor->y);
+ if (!grab.c)
+ return;
+
+ /* Float the window and tell motionnotify to grab it */
+ setfloating(grab.c, 1);
+ switch (mouse.mode = arg->ui) {
+ case MouseMove:
+ grab.x = mouse.cursor->x - grab.c->dim.x;
+ grab.y = mouse.cursor->y - grab.c->dim.y;
+ wlr_xcursor_manager_set_cursor_image(mouse.manager, "fleur", mouse.cursor);
+ break;
+ case MouseResize:
+ wlr_cursor_warp_closest(mouse.cursor, nil,
+ grab.c->dim.x + grab.c->dim.width,
+ grab.c->dim.y + grab.c->dim.height);
+ wlr_xcursor_manager_set_cursor_image(mouse.manager, "bottom_right_corner", mouse.cursor);
+ break;
+ }
+}
+
+void
+quit(const Arg *arg)
+{
+ wl_display_terminate(dwm.display);
+}
+
+void
+setlayout(const Arg *arg)
+{
+ if (!arg || !arg->v || arg->v != monitor->lt[monitor->sellt])
+ monitor->sellt ^= 1;
+ if (arg && arg->v)
+ monitor->lt[monitor->sellt] = (Layout *)arg->v;
+
+ arrange(monitor);
+}
+
+void
+setmfact(const Arg *arg)
+{
+ float f;
+
+ if (!arg || !monitor->lt[monitor->sellt]->arrange)
+ return;
+ f = arg->f < 1.0 ? arg->f + monitor->mfact : arg->f - 1.0;
+ if (f < 0.1 || f > 0.9)
+ return;
+ monitor->mfact = f;
+ arrange(monitor);
+}
+
+void
+spawn(const Arg *arg)
+{
+ pid_t pid;
+ if ((pid=fork()) == 0) {
+ setsid();
+
+ printf("running %s\n", ((char**)arg->v)[0]);
+ execvp(((char **)arg->v)[0], (char **)arg->v);
+
+ fprintf(stderr, "dwl: execvp %s", ((char **)arg->v)[0]);
+ perror(" failed");
+ exit(EXIT_FAILURE);
+ } else if (pid < 0)
+ fatal("failed to fork");
+}
+
+void
+tag(const Arg *arg)
+{
+ Client *c;
+
+ c = getclient();
+ if (c && arg->ui & TAGMASK) {
+ c->tags = arg->ui & TAGMASK;
+ setfocus(lastfocus(), nil, 1);
+ arrange(monitor);
+ }
+}
+
+void
+tagmonitor(const Arg *arg)
+{
+ Client *c;
+
+ c = getclient();
+ if (!c)
+ return;
+ setmonitor(c, getmonitor(arg->i), 0);
+}
+
+void
+togglefloating(const Arg *arg)
+{
+ Client *c;
+
+ c = getclient();
+ if (!c)
+ return;
+
+ setfloating(c, !c->floating);
+}
+
+void
+toggletag(const Arg *arg)
+{
+ Client *c;
+ uint newtags;
+
+ c = getclient();
+ if (!c)
+ return;
+ newtags = c->tags ^ (arg->ui & TAGMASK);
+ if (newtags) {
+ c->tags = newtags;
+ setfocus(lastfocus(), nil, 1);
+ arrange(monitor);
+ }
+}
+
+void
+toggleview(const Arg *arg)
+{
+ uint newtagset;
+
+ newtagset = monitor->tagset[monitor->seltags] ^ (arg->ui & TAGMASK);
+
+ if (newtagset) {
+ monitor->tagset[monitor->seltags] = newtagset;
+ setfocus(lastfocus(), nil, 1);
+ arrange(monitor);
+ }
+}
+
+void
+view(const Arg *arg)
+{
+ if ((arg->ui & TAGMASK) == monitor->tagset[monitor->seltags])
+ return;
+
+ monitor->seltags ^= 1; /* toggle sel tagset */
+ if (arg->ui & TAGMASK)
+ monitor->tagset[monitor->seltags] = arg->ui & TAGMASK;
+ setfocus(lastfocus(), nil, 1);
+
+ arrange(monitor);
+}
+
+
diff --git a/sys/cmd/dwm/input.c b/sys/cmd/dwm/input.c
new file mode 100644
index 0000000..b035e9a
--- /dev/null
+++ b/sys/cmd/dwm/input.c
@@ -0,0 +1,333 @@
+#include "dwm.h"
+
+/* local callbacks */
+static void scroll(struct wl_listener *ev, void *arg);
+static void frame(struct wl_listener *ev, void *arg);
+static void click(struct wl_listener *ev, void *arg);
+static void relmove(struct wl_listener *ev, void *arg);
+static void absmove(struct wl_listener *ev, void *arg);
+
+static void setcursor(struct wl_listener *ev, void *arg);
+static void setsel(struct wl_listener *ev, void *arg);
+static void setpsel(struct wl_listener *ev, void *arg);
+
+static void keypress(struct wl_listener *ev, void *arg);
+static void modifier(struct wl_listener *ev, void *arg);
+
+/* global variables */
+Mouse mouse = {
+ .mode = 0,
+ .manager = nil,
+ .cursor = nil,
+ .ev = {
+ .axis = {.notify=scroll},
+ .button = {.notify=click},
+ .frame = {.notify=frame},
+ .motion = {.notify=relmove},
+ .absmotion = {.notify=absmove},
+ .cursor = {.notify=setcursor},
+ .sel = {.notify=setsel},
+ .psel = {.notify=setpsel},
+ },
+};
+
+Grab grab = { 0 };
+
+// -----------------------------------------------------------------------
+// callback function implementations
+
+/* input devices */
+static
+void
+newkeyboard(struct wlr_input_device *dev)
+{
+ Keyboard *kb;
+ struct xkb_context *ctx;
+ struct xkb_keymap *map;
+
+ kb = dev->data = calloc(1, sizeof(*kb));
+ kb->dev = dev;
+
+ ctx = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
+ map = xkb_map_new_from_names(ctx, &xkb_rules, XKB_KEYMAP_COMPILE_NO_FLAGS);
+
+ wlr_keyboard_set_keymap(dev->keyboard, map);
+ xkb_keymap_unref(map);
+ xkb_context_unref(ctx);
+
+ wlr_keyboard_set_repeat_info(dev->keyboard, 25, 600);
+
+ /* install callbacks */
+ kb->ev.modifier.notify = modifier;
+ kb->ev.keypress.notify = keypress;
+
+ wl_signal_add(&dev->keyboard->events.modifiers, &kb->ev.modifier);
+ wl_signal_add(&dev->keyboard->events.key, &kb->ev.keypress);
+
+ /* NOTE: we only look at the last keyboard given right now */
+ wlr_seat_set_keyboard(dwm.seat, dev);
+
+ wl_list_insert(&dwm.keyboards, &kb->link);
+}
+
+static
+void
+newmouse(struct wlr_input_device *dev)
+{
+ /* NOTE: we only look at one mouse right now */
+ wlr_cursor_attach_input_device(mouse.cursor, dev);
+}
+
+void
+ev·newinput(struct wl_listener *ev, void *arg)
+{
+ uint32 c;
+ struct wlr_input_device *dev;
+
+ dev = arg;
+ switch (dev->type) {
+ case WLR_INPUT_DEVICE_KEYBOARD:
+ newkeyboard(dev);
+ break;
+ case WLR_INPUT_DEVICE_POINTER:
+ newmouse(dev);
+ break;
+ default:
+ ;
+ }
+
+ c = WL_SEAT_CAPABILITY_POINTER;
+ if (!wl_list_empty(&dwm.keyboards))
+ c |= WL_SEAT_CAPABILITY_KEYBOARD;
+ wlr_seat_set_capabilities(dwm.seat, c);
+}
+
+/* mouse input */
+
+static
+void
+scroll(struct wl_listener *ev, void *arg)
+{
+ struct wlr_event_pointer_axis *axis;
+
+ axis = arg;
+ wlr_seat_pointer_notify_axis(dwm.seat,
+ axis->time_msec,
+ axis->orientation,
+ axis->delta,
+ axis->delta_discrete,
+ axis->source);
+}
+
+void
+click(struct wl_listener *ev, void *arg)
+{
+ uint32 mods;
+ Client *c;
+ const Button *b;
+ struct wlr_surface *surf;
+ struct wlr_keyboard *keyb;
+ struct wlr_event_pointer_button *button;
+
+ button = arg;
+ switch (button->state) {
+ case WLR_BUTTON_PRESSED:
+ if ((c = clientat(mouse.cursor->x, mouse.cursor->y))) {
+ surf = wlr_xdg_surface_surface_at(c->surf,
+ mouse.cursor->x - c->dim.x - c->bw,
+ mouse.cursor->y - c->dim.y - c->bw,
+ nil, nil
+ );
+ setfocus(c, surf, 1);
+ }
+
+ keyb = wlr_seat_get_keyboard(dwm.seat);
+ mods = wlr_keyboard_get_modifiers(keyb);
+ for (b = buttons; b < arrend(buttons); b++) {
+ if (CLEANMASK(mods) == CLEANMASK(b->mod) &&
+ button->button == b->kind && b->func) {
+ b->func(&b->arg);
+ return;
+ }
+ }
+ break;
+
+ case WLR_BUTTON_RELEASED:
+ if (mouse.mode != MouseNormal) {
+ wlr_xcursor_manager_set_cursor_image(mouse.manager, "left_ptr", mouse.cursor);
+ mouse.mode = MouseNormal;
+ monitor = monitorat(mouse.cursor->x, mouse.cursor->y);
+ setmonitor(grab.c, monitor, 0);
+ }
+ }
+
+ wlr_seat_pointer_notify_button(dwm.seat, button->time_msec, button->button, button->state);
+}
+
+static
+void
+frame(struct wl_listener *ev, void *arg)
+{
+ wlr_seat_pointer_notify_frame(dwm.seat);
+}
+
+static
+void
+notify(uint32 time)
+{
+ Client *c;
+ double sx, sy;
+ struct wlr_surface *surf;
+
+ if (sloppyfocus)
+ monitor = monitorat(mouse.cursor->x, mouse.cursor->y);
+
+ switch (mouse.mode) {
+ case MouseMove:
+ resize(grab.c,
+ mouse.cursor->x - grab.x,
+ mouse.cursor->y - grab.y,
+ grab.c->dim.width, grab.c->dim.height, 1);
+ return;
+
+ case MouseResize:
+ resize(grab.c,
+ grab.c->dim.x, grab.c->dim.y,
+ mouse.cursor->x - grab.c->dim.x,
+ mouse.cursor->y - grab.c->dim.y, 1);
+ return;
+
+ case MouseNormal:
+ default:
+ ;
+ }
+
+ surf = nil;
+ if ((c = clientat(mouse.cursor->x, mouse.cursor->y)))
+ surf = wlr_xdg_surface_surface_at(c->surf,
+ mouse.cursor->x - c->dim.x - c->bw,
+ mouse.cursor->y - c->dim.y - c->bw,
+ &sx, &sy);
+
+ if (!surf)
+ wlr_xcursor_manager_set_cursor_image(mouse.manager, "left_ptr", mouse.cursor);
+
+ pointerfocus(c, surf, sx, sy, time);
+}
+
+static
+void
+relmove(struct wl_listener *ev, void *arg)
+{
+ struct wlr_event_pointer_motion *mv;
+
+ mv = arg;
+ wlr_cursor_move(mouse.cursor, mv->device, mv->delta_x, mv->delta_y);
+ notify(mv->time_msec);
+}
+
+static
+void
+absmove(struct wl_listener *ev, void *arg)
+{
+ struct wlr_event_pointer_motion_absolute *mv;
+
+ mv = arg;
+ wlr_cursor_warp_absolute(mouse.cursor, mv->device, mv->x, mv->y);
+ notify(mv->time_msec);
+}
+
+/* cursor images */
+static
+void
+setcursor(struct wl_listener *ev, void *arg)
+{
+ struct wlr_seat_pointer_request_set_cursor_event *cur;
+
+ cur = arg;
+ if (mouse.mode != MouseNormal)
+ return;
+
+ if (cur->seat_client == dwm.seat->pointer_state.focused_client)
+ wlr_cursor_set_surface(mouse.cursor, cur->surface, cur->hotspot_x, cur->hotspot_y);
+}
+
+static
+void
+setpsel(struct wl_listener *ev, void *arg)
+{
+ struct wlr_seat_request_set_primary_selection_event *psel;
+
+ psel = arg;
+ wlr_seat_set_primary_selection(dwm.seat, psel->source, psel->serial);
+}
+
+static
+void
+setsel(struct wl_listener *ev, void *arg)
+{
+ struct wlr_seat_request_set_selection_event *sel;
+
+ sel = arg;
+ wlr_seat_set_selection(dwm.seat, sel->source, sel->serial);
+}
+
+/* keyboards */
+
+static
+int
+dokey(uint32 mods, xkb_keysym_t sym)
+{
+ int h;
+ const Key *k, *e;
+
+ h = 0;
+ for (k = keys, e = arrend(keys); k < e; k++)
+ if (CLEANMASK(mods) == CLEANMASK(k->mod) && sym == k->sym && k->func) {
+ k->func(&k->arg);
+ h = 1;
+ }
+
+ return h;
+}
+
+static
+void
+modifier(struct wl_listener *ev, void *arg)
+{
+ Keyboard *kb;
+
+ kb = wl_container_of(ev, kb, ev.modifier);
+ wlr_seat_set_keyboard(dwm.seat, kb->dev);
+
+ wlr_seat_keyboard_notify_modifiers(dwm.seat, &kb->dev->keyboard->modifiers);
+}
+
+static
+void
+keypress(struct wl_listener *ev, void *arg)
+{
+ Keyboard *kb;
+ int i, n, hit;
+ uint32 code, mods;
+ const xkb_keysym_t *syms;
+ struct wlr_event_keyboard_key *key;
+
+ key = arg;
+ kb = wl_container_of(ev, kb, ev.keypress);
+
+ code = key->keycode + 8;
+ n = xkb_state_key_get_syms(kb->dev->keyboard->xkb_state, code, &syms);
+ mods = wlr_keyboard_get_modifiers(kb->dev->keyboard);
+
+ hit = 0;
+ if (key->state == WLR_KEY_PRESSED)
+ for (i = 0; i < n; i++)
+ hit += dokey(mods, syms[i]);
+
+ /* if no binding found, pass the event to the client */
+ if (!hit) {
+ wlr_seat_set_keyboard(dwm.seat, kb->dev);
+ wlr_seat_keyboard_notify_key(dwm.seat, key->time_msec, key->keycode, key->state);
+ }
+}
diff --git a/sys/cmd/dwm/layer.c b/sys/cmd/dwm/layer.c
new file mode 100644
index 0000000..4c1ddcb
--- /dev/null
+++ b/sys/cmd/dwm/layer.c
@@ -0,0 +1,329 @@
+#include "dwm.h"
+
+/* local callbacks */
+static void delete(struct wl_listener *ev, void *arg);
+static void map(struct wl_listener *ev, void *arg);
+static void unmap(struct wl_listener *ev, void *arg);
+static void commit(struct wl_listener *ev, void *arg);
+static void detach(struct wl_listener *ev, void *arg);
+
+// -----------------------------------------------------------------------
+// methods
+
+static
+void
+applyexclusive(struct wlr_box *area, uint32 anchor, int ex, int32 mt, int32 mr, int32 mb, int32 ml)
+{
+ if (ex <= 0)
+ return;
+ struct {
+ uint32 singular_anchor;
+ uint32 anchor_triplet;
+ int *positive_axis;
+ int *negative_axis;
+ int margin;
+ } edges[] = {
+ // Top
+ {
+ .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP,
+ .anchor_triplet =
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP,
+ .positive_axis = &area->y,
+ .negative_axis = &area->height,
+ .margin = mt,
+ },
+ // Bottom
+ {
+ .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
+ .anchor_triplet =
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
+ .positive_axis = NULL,
+ .negative_axis = &area->height,
+ .margin = mb,
+ },
+ // Left
+ {
+ .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT,
+ .anchor_triplet =
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
+ .positive_axis = &area->x,
+ .negative_axis = &area->width,
+ .margin = ml,
+ },
+ // Right
+ {
+ .singular_anchor = ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT,
+ .anchor_triplet =
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP |
+ ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM,
+ .positive_axis = NULL,
+ .negative_axis = &area->width,
+ .margin = mr,
+ },
+ };
+ for (size_t i = 0; i < sizeof(edges) / sizeof(edges[0]); ++i) {
+ if ((anchor == edges[i].singular_anchor || anchor == edges[i].anchor_triplet)
+ && ex + edges[i].margin > 0) {
+ if (edges[i].positive_axis) {
+ *edges[i].positive_axis += ex + edges[i].margin;
+ }
+ if (edges[i].negative_axis) {
+ *edges[i].negative_axis -= ex + edges[i].margin;
+ }
+ break;
+ }
+ }
+}
+
+static
+void
+arrangelayer(Monitor *m, struct wl_list *layers, struct wlr_box *area, int ex)
+{
+ Layer *l;
+ uint32 horiz, vert;
+ struct wlr_box fullarea;
+ struct wlr_box box;
+ struct wlr_layer_surface_v1 *layer;
+ struct wlr_layer_surface_v1_state *state;
+
+ wlr_output_effective_resolution(m->dev, &fullarea.width, &fullarea.height);
+ wl_list_for_each(l, layers, link) {
+ layer = l->surf;
+ state = &layer->current;
+
+ if (ex != (state->exclusive_zone > 0))
+ continue;
+
+ struct wlr_box bounds;
+ if (state->exclusive_zone == -1)
+ bounds = fullarea;
+ else
+ bounds = *area;
+
+ box = (struct wlr_box){
+ .width = state->desired_width,
+ .height = state->desired_height
+ };
+
+ // Horizontal axis
+ horiz = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT
+ | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT;
+ if ((state->anchor & horiz) && box.width == 0) {
+ box.x = bounds.x;
+ box.width = bounds.width;
+ } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) {
+ box.x = bounds.x;
+ } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
+ box.x = bounds.x + (bounds.width - box.width);
+ } else {
+ box.x = bounds.x + ((bounds.width / 2) - (box.width / 2));
+ }
+ // Vertical axis
+ vert = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP
+ | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM;
+ if ((state->anchor & vert) && box.height == 0) {
+ box.y = bounds.y;
+ box.height = bounds.height;
+ } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) {
+ box.y = bounds.y;
+ } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) {
+ box.y = bounds.y + (bounds.height - box.height);
+ } else {
+ box.y = bounds.y + ((bounds.height / 2) - (box.height / 2));
+ }
+ // Margin
+ if ((state->anchor & horiz) == horiz) {
+ box.x += state->margin.left;
+ box.width -= state->margin.left + state->margin.right;
+ } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) {
+ box.x += state->margin.left;
+ } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) {
+ box.x -= state->margin.right;
+ }
+ if ((state->anchor & vert) == vert) {
+ box.y += state->margin.top;
+ box.height -= state->margin.top + state->margin.bottom;
+ } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) {
+ box.y += state->margin.top;
+ } else if ((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) {
+ box.y -= state->margin.bottom;
+ }
+ if (box.width < 0 || box.height < 0) {
+ // TODO: Bubble up a protocol error?
+ wlr_layer_surface_v1_close(layer);
+ continue;
+ }
+ l->dim= box;
+ applyexclusive(area, state->anchor, state->exclusive_zone,
+ state->margin.top, state->margin.right, state->margin.bottom, state->margin.left);
+
+ wlr_layer_surface_v1_configure(layer, box.width, box.height);
+ }
+}
+
+void
+arrangelayers(Monitor *m)
+{
+ int i;
+ Layer *it, *top;
+ struct wlr_box area;
+
+ wlr_output_effective_resolution(m->dev, &area.width, &area.height);
+
+ arrangelayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], &area, 1);
+ arrangelayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], &area, 1);
+ arrangelayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], &area, 1);
+ arrangelayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], &area, 1);
+
+ if (memcmp(&area, &m->area.win, sizeof(struct wlr_box)) != 0) {
+ memcpy(&m->area.win, &area, sizeof(struct wlr_box));
+ arrange(m);
+ }
+
+ arrangelayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], &area, 0);
+ arrangelayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_TOP], &area, 0);
+ arrangelayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], &area, 0);
+ arrangelayer(m, &m->layers[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], &area, 0);
+
+ uint32 layers[] = {
+ ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY,
+ ZWLR_LAYER_SHELL_V1_LAYER_TOP,
+ };
+
+ top = nil;
+ for (i = 0; i < arrlen(layers); i++) {
+ wl_list_for_each_reverse(it, &m->layers[layers[i]], link) {
+ if (it->surf->current.keyboard_interactive && it->surf->mapped) {
+ top = it;
+ break;
+ }
+ }
+ if (top)
+ break;
+ }
+ /* TODO: set focus here? */
+}
+
+
+// -----------------------------------------------------------------------
+// implementations
+
+void
+ev·newlayershell(struct wl_listener *ev, void *arg)
+{
+ Monitor *m;
+ Layer *layer;
+ struct wlr_layer_surface_v1 *surf;
+ struct wlr_layer_surface_v1_state old;
+
+ surf = arg;
+ if (!surf->output)
+ surf->output = monitor->dev;
+
+ layer = surf->data = calloc(1, sizeof(*layer));
+ layer->surf = surf;
+
+ /* install callbacks */
+ layer->ev.free.notify = delete;
+ layer->ev.map.notify = map;
+ layer->ev.unmap.notify = unmap;
+ layer->ev.commit.notify = commit;
+ layer->ev.detach.notify = detach;
+
+ wl_signal_add(&surf->events.destroy, &layer->ev.free);
+ wl_signal_add(&surf->events.map, &layer->ev.map);
+ wl_signal_add(&surf->events.unmap, &layer->ev.unmap);
+ wl_signal_add(&surf->surface->events.commit, &layer->ev.commit);
+ wl_signal_add(&monitor->sig.kill, &layer->ev.detach);
+
+ m = surf->output->data;
+ wl_list_insert(&m->layers[surf->client_pending.layer], &layer->link);
+
+ /* song and dance: set as pending, arrange, then reset */
+ old = surf->current;
+ surf->current = surf->client_pending;
+ arrangelayers(m);
+}
+
+static
+void
+delete(struct wl_listener *ev, void *arg)
+{
+ Layer *l;
+
+ l = wl_container_of(ev, l, ev.map);
+
+ wl_list_remove(&l->link);
+ wl_list_remove(&l->ev.free.link);
+ wl_list_remove(&l->ev.map.link);
+ wl_list_remove(&l->ev.unmap.link);
+ wl_list_remove(&l->ev.detach.link);
+ wl_list_remove(&l->ev.commit.link);
+
+ free(l);
+}
+
+static
+void
+detach(struct wl_listener *ev, void *arg)
+{
+ /* no op */
+}
+
+
+static
+void
+map(struct wl_listener *ev, void *arg)
+{
+ Layer *l;
+ Monitor *m;
+
+ l = wl_container_of(ev, l, ev.map);
+ m = l->surf->output->data;
+ /* TODO: damage */
+ wlr_surface_send_enter(l->surf->surface, l->surf->output);
+ /* TODO: cursor */
+}
+
+static
+void
+unmap(struct wl_listener *ev, void *arg)
+{
+ /* TODO: fill in */
+}
+
+static
+void
+commit(struct wl_listener *ev, void *arg)
+{
+ Layer *l;
+ Monitor *m;
+ char newdim, newlayer;
+ struct wlr_box old;
+ struct wlr_layer_surface_v1 *surf;
+ struct wlr_output *dev;
+
+ l = wl_container_of(ev, l, ev.commit);
+ surf = l->surf;
+ dev = surf->output;
+
+ m = dev->data;
+ old = l->dim;
+ arrangelayers(m);
+
+ newdim = memcmp(&old, &l->dim, sizeof(struct wlr_box)) != 0;
+ newlayer = l->z != surf->current.layer;
+
+ if (newdim) {
+ wl_list_remove(&l->link);
+ wl_list_insert(&m->layers[surf->current.layer], &l->link);
+ l->z = surf->current.layer;
+ }
+}
diff --git a/sys/cmd/dwm/layout.c b/sys/cmd/dwm/layout.c
new file mode 100644
index 0000000..f7a2c60
--- /dev/null
+++ b/sys/cmd/dwm/layout.c
@@ -0,0 +1,37 @@
+#include "dwm.h"
+
+void
+tile(Monitor *m)
+{
+ Client *c;
+ uint i, n, h, mw, my, ty;
+
+ n = 0;
+ wl_list_for_each(c, &dwm.tiles, link.tiles)
+ if (VISIBLEON(c, m) && !c->floating)
+ n++;
+
+ if (n == 0)
+ return;
+
+ if (n > m->nmaster)
+ mw = m->nmaster ? m->area.win.width * m->mfact : 0;
+ else
+ mw = m->area.win.width;
+
+ i = my = ty = 0;
+ wl_list_for_each(c, &dwm.tiles, link.tiles) {
+ if (!VISIBLEON(c, m) || c->floating)
+ continue;
+ if (i < m->nmaster) {
+ h = (m->area.win.height - my) / (MIN(n, m->nmaster) - i);
+ resize(c, m->area.win.x, m->area.win.y + my, mw, h, 0);
+ my += c->dim.height;
+ } else {
+ h = (m->area.win.height - ty) / (n - i);
+ resize(c, m->area.win.x + mw, m->area.win.y + ty, m->area.win.width - mw, h, 0);
+ ty += c->dim.height;
+ }
+ i++;
+ }
+}
diff --git a/sys/cmd/dwm/output.c b/sys/cmd/dwm/output.c
new file mode 100644
index 0000000..918d9a6
--- /dev/null
+++ b/sys/cmd/dwm/output.c
@@ -0,0 +1,257 @@
+#include "dwm.h"
+
+/* local callbacks */
+static void delete(struct wl_listener *ev, void *arg);
+static void draw(struct wl_listener *ev, void *arg);
+
+/* global variables */
+Monitor *monitor = nil;
+
+// -----------------------------------------------------------------------
+// methods
+
+void
+damageat(Monitor* m, double x, double y)
+{
+ /* TODO: don't damage the whole screen */
+ wlr_output_damage_add_whole(&m->damage);
+}
+
+void
+arrange(Monitor *m)
+{
+ m->area.all = *wlr_output_layout_get_box(dwm.layout, m->dev);
+ m->area.win = m->area.all;
+ if (m->lt[m->sellt]->arrange)
+ m->lt[m->sellt]->arrange(m);
+}
+
+void
+setmonitor(Client *c, Monitor *m, uint newtags)
+{
+ int hadfocus;
+ Monitor *old;
+
+ old = c->m;
+ if (old == m)
+ return;
+
+ hadfocus = (c == getclient());
+
+ c->m = m;
+ if (old) {
+ wlr_surface_send_leave(c->surf->surface, old->dev);
+ arrange(old);
+ }
+
+ if (m) {
+ applybounds(c, &m->area.all);
+ wlr_surface_send_enter(c->surf->surface, m->dev);
+ c->tags = newtags ? newtags : m->tagset[m->seltags];
+ arrange(m);
+ }
+
+ if (hadfocus || (c == getclient()))
+ setfocus(lastfocus(), nil, 1);
+}
+
+Monitor*
+getmonitor(int dir)
+{
+ Monitor *m;
+
+ if (dir > 0) {
+ if (monitor->link.next == &dwm.odevs)
+ return wl_container_of(dwm.odevs.next, m, link);
+ return wl_container_of(monitor->link.next, m, link);
+ }
+ if (monitor->link.prev == &dwm.odevs)
+ return wl_container_of(dwm.odevs.prev, m, link);
+ return wl_container_of(monitor->link.prev, m, link);
+}
+
+Monitor *
+monitorat(double x, double y)
+{
+ struct wlr_output *dev;
+
+ dev = wlr_output_layout_output_at(dwm.layout, x, y);
+ return dev ? dev->data : nil;
+}
+
+// -----------------------------------------------------------------------
+// callback function implementations
+void
+ev·newmonitor(struct wl_listener *ev, void *arg)
+{
+ Monitor *m;
+ const MonitorRule *r;
+ struct wlr_output *dev;
+ struct wlr_output_mode *mode;
+
+ dev = arg;
+ wlr_output_set_mode(dev, wlr_output_preferred_mode(dev));
+
+ m = dev->data = calloc(1, sizeof(*m));
+ m->dev = dev;
+ m->tagset[0] = m->tagset[1] = 1;
+ /* look for rules to apply */
+ for (r = monitorrules; r < arrend(monitorrules); r++) {
+ if (!r->name || strstr(dev->name, r->name)) {
+ m->mfact = r->mfact;
+ m->nmaster = r->nmaster;
+ wlr_output_set_scale(dev, r->scale);
+ wlr_xcursor_manager_load(mouse.manager, r->scale);
+ m->lt[0] = m->lt[1] = r->lt;
+ wlr_output_set_transform(dev, r->rr);
+ break;
+ }
+ }
+
+ /* install callbacks */
+ m->ev.draw.notify = draw;
+ m->ev.free.notify = delete;
+
+ wl_signal_add(&dev->events.frame, &m->ev.draw);
+ wl_signal_add(&dev->events.destroy, &m->ev.free);
+
+ wl_list_insert(&dwm.odevs, &m->link);
+
+ wl_list_init(&m->layers[0]);
+ wl_list_init(&m->layers[1]);
+ wl_list_init(&m->layers[2]);
+ wl_list_init(&m->layers[3]);
+
+ wlr_output_enable(dev, 1);
+ if (!wlr_output_commit(dev))
+ return;
+
+ wl_signal_init(&m->sig.kill);
+
+ wlr_output_layout_add_auto(dwm.layout, dev);
+ dwm.dim = *wlr_output_layout_get_box(dwm.layout, nil);
+}
+
+static
+void
+delete(struct wl_listener *ev, void *arg)
+{
+ Monitor *m;
+
+ m = wl_container_of(ev, m, ev.free);
+ wl_signal_emit(&m->sig.kill, m);
+
+ wl_list_remove(&m->link);
+ wl_list_remove(&m->ev.draw.link);
+ wl_list_remove(&m->ev.free.link);
+
+ free(m);
+}
+
+static
+void
+render(struct wlr_surface *surf, int sx, int sy, void *arg)
+{
+ Payload *data;
+ double x, y;
+ float mtx[9];
+ struct wlr_box box;
+ struct wlr_output *dev;
+ struct wlr_texture *tex;
+ enum wl_output_transform transform;
+
+ data = arg;
+ dev = data->dev;
+
+ tex = wlr_surface_get_texture(surf);
+ if (!tex)
+ return;
+
+ /* convert to device local coord */
+ wlr_output_layout_output_coords(dwm.layout, dev, &x, &y);
+
+ box.x = x + data->x + sx;
+ box.y = y + data->y + sy;
+ box.width = surf->current.width;
+ box.height = surf->current.height;
+ scale(&box, dev->scale);
+
+ /* orthographic projection */
+ transform = wlr_output_transform_invert(surf->current.transform);
+ wlr_matrix_project_box(mtx, &box, transform, 0, dev->transform_matrix);
+
+ /* render with GPU */
+ wlr_render_texture_with_matrix(dwm.draw, tex, mtx, 1);
+
+ /* notify the client we are done */
+ wlr_surface_send_frame_done(surf, data->now);
+}
+
+static
+void
+renderclients(Monitor *m, struct timespec *now)
+{
+ Client *c;
+ double x, y;
+ int i, w, h;
+ Payload data;
+ struct wlr_box *borders;
+
+ wl_list_for_each_reverse(c, &dwm.stack, link.stack) {
+ if (!VISIBLEON(c, c->m) || !wlr_output_layout_intersects(dwm.layout, m->dev, &c->dim))
+ continue;
+
+ x = c->dim.x;
+ y = c->dim.y;
+
+ wlr_output_layout_output_coords(dwm.layout, m->dev, &x, &y);
+
+ w = c->surf->surface->current.width;
+ h = c->surf->surface->current.height;
+
+ borders = (struct wlr_box[4]) {
+ {x, y, w + 2 * c->bw, c->bw}, /* top */
+ {x, y + c->bw, c->bw, h}, /* left */
+ {x + c->bw + w, y + c->bw, c->bw, h}, /* right */
+ {x, y + c->bw + h, w + 2 * c->bw, c->bw}, /* bottom */
+ };
+ for (i = 0; i < 4; i++) {
+ scale(&borders[i], m->dev->scale);
+ wlr_render_rect(dwm.draw, &borders[i], bordercolor, m->dev->transform_matrix);
+ }
+
+ data.dev = m->dev;
+ data.now = now;
+ data.x = c->dim.x + c->bw;
+ data.y = c->dim.y + c->bw;
+
+ wlr_xdg_surface_for_each_surface(c->surf, render, &data);
+ }
+}
+
+static
+void
+draw(struct wl_listener *ev, void *arg)
+{
+ int w, h;
+ Monitor *m;
+ struct timespec now;
+
+ m = wl_container_of(ev, m, ev.draw);
+
+ if (!wlr_output_attach_render(m->dev, nil))
+ return;
+
+ clock_gettime(CLOCK_MONOTONIC, &now);
+ wlr_output_effective_resolution(m->dev, &w, &h);
+
+ wlr_renderer_begin(dwm.draw, w, h);
+ wlr_renderer_clear(dwm.draw, rootcolor);
+
+ renderclients(m, &now);
+
+ wlr_output_render_software_cursors(m->dev, nil);
+
+ wlr_renderer_end(dwm.draw);
+ wlr_output_commit(m->dev);
+}
diff --git a/sys/cmd/dwm/rules.mk b/sys/cmd/dwm/rules.mk
new file mode 100644
index 0000000..610987a
--- /dev/null
+++ b/sys/cmd/dwm/rules.mk
@@ -0,0 +1,50 @@
+include share/push.mk
+
+WL_DIR = $(shell pkg-config --variable=pkgdatadir wayland-protocols)
+WL_SCAN = $(shell pkg-config --variable=wayland_scanner wayland-scanner)
+PROTO_DIR := $(d)/protocols
+
+# Local sources
+SRCS_$(d) := \
+ $(d)/xdg-shell.c \
+ $(d)/wlr-layer-shell.c \
+ $(d)/util.c \
+ $(d)/output.c \
+ $(d)/input.c \
+ $(d)/client.c \
+ $(d)/decoration.c \
+ $(d)/layer.c \
+ $(d)/layout.c \
+ $(d)/func.c \
+ $(d)/dwm.c
+
+BINS_$(d) := $(d)/dwm
+
+include share/paths.mk
+
+# Local rules
+$(d)/xdg-shell.h:
+ $(WL_SCAN) server-header $(WL_DIR)/stable/xdg-shell/xdg-shell.xml $@
+
+$(d)/xdg-shell.c: $(d)/xdg-shell.h
+ $(WL_SCAN) private-code $(WL_DIR)/stable/xdg-shell/xdg-shell.xml $@
+
+# could make this a patterned rule...
+$(d)/wlr-layer-shell.h: $(PROTO_DIR)/wlr-layer-shell-unstable-v1.xml
+ $(WL_SCAN) server-header $(PROTO_DIR)/wlr-layer-shell-unstable-v1.xml $@
+
+$(d)/wlr-layer-shell.c: $(d)/wlr-layer-shell.h
+ $(WL_SCAN) private-code $(PROTO_DIR)/wlr-layer-shell-unstable-v1.xml $@
+
+include share/dynamic.mk
+$(BINS_$(d)): TCFLAGS := $(shell pkg-config --cflags xkbcommon) \
+ $(shell pkg-config --cflags wlroots) \
+ $(shell pkg-config --cflags wayland-server)
+$(BINS_$(d)): TCLIBS := $(shell pkg-config --libs xkbcommon) \
+ $(shell pkg-config --libs wlroots) \
+ $(shell pkg-config --libs wayland-server)
+
+$(BINS_$(d)): $(OBJS_$(d)) $(OBJ_DIR)/libn/libn.a
+ $(COMPLINK)
+
+include share/pop.mk
diff --git a/sys/cmd/dwm/util.c b/sys/cmd/dwm/util.c
new file mode 100644
index 0000000..1875d89
--- /dev/null
+++ b/sys/cmd/dwm/util.c
@@ -0,0 +1,22 @@
+#include "dwm.h"
+
+void
+fatal(byte *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+
+ verrorf(fmt, args);
+
+ va_end(args);
+ exit(1);
+}
+
+void
+scale(struct wlr_box *box, float by)
+{
+ box->x *= by;
+ box->y *= by;
+ box->height *= by;
+ box->width *= by;
+}