aboutsummaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorNicholas Noll <nbnoll@eml.cc>2020-06-02 08:49:01 -0700
committerNicholas Noll <nbnoll@eml.cc>2020-06-02 08:49:01 -0700
commitcdc50fd76fd0e286f40416f579d60f14e19aa9f5 (patch)
tree9e67e5cd8a075e54877bfb1c205cdd723bc5898a /sys
parentc8b9286e2184b6d936e6007f81ead3367a4870b6 (diff)
feat: started port of dwm to wayland
Diffstat (limited to 'sys')
-rw-r--r--sys/cmd/dway/config.h77
-rw-r--r--sys/cmd/dway/dway.c1156
-rw-r--r--sys/cmd/dway/dway.h201
3 files changed, 1434 insertions, 0 deletions
diff --git a/sys/cmd/dway/config.h b/sys/cmd/dway/config.h
new file mode 100644
index 0000000..4dc7cb5
--- /dev/null
+++ b/sys/cmd/dway/config.h
@@ -0,0 +1,77 @@
+/* global appearance */
+
+static const int sloppyfocus = 1; /* focus follows mouse */
+static const uint borderpixel = 1; /* border pixel of windows */
+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 Layout layouts[] = {
+ /* symbol arrange function */
+ { "[]=", tile },
+ { "><>", nil }, /* no layout function means floating behavior */
+};
+
+/* monitors */
+
+/* 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 };
+
+/* keymaps */
+static const Key keys[] = {
+ /* modifier key function argument */
+ { MODKEY, XKB_KEY_Return, spawn, {.p = 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, {.p = &layouts[0]} },
+ { MODKEY, XKB_KEY_f, setlayout, {.p = &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/dway/dway.c b/sys/cmd/dway/dway.c
new file mode 100644
index 0000000..2aaf575
--- /dev/null
+++ b/sys/cmd/dway/dway.c
@@ -0,0 +1,1156 @@
+#include "dway.h"
+
+#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)
+
+/* global state */
+static struct
+{
+ struct wl_display *display;
+ struct wlr_backend *backend;
+ struct wlr_renderer *draw;
+ struct wlr_compositor *compositor;
+ struct wlr_xdg_shell *shell;
+ struct wlr_output_layout *layout;
+ struct wlr_seat *seat;
+
+ struct {
+ struct wl_event_loop *loop;
+ /* i/o devices */
+ struct wl_listener idev;
+ struct wl_listener odev;
+ struct wl_listener client;
+ struct wl_listener cursor;
+ struct wl_listener sel;
+ struct wl_listener psel;
+ } 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 wlr_box dim;
+} dway =
+{
+ .ev = {
+ .odev = {.notify=ev·newmonitor},
+ .idev = {.notify=ev·newidev},
+ .client = {.notify=ev·newclient},
+ .cursor = {.notify=ev·setcursor},
+ .sel = {.notify=ev·setsel},
+ .psel = {.notify=ev·setpsel},
+ },
+};
+
+static Monitor *monitor;
+static struct
+{
+ Client *c;
+ int x, y;
+} grab;
+
+static Mouse mouse = {
+ .ev = {
+ .axis = {.notify=ev·mousescroll},
+ .button = {.notify=ev·mouseclick},
+ .frame = {.notify=ev·mouseframe},
+ .motion = {.notify=ev·mouserelmove},
+ .absmotion = {.notify=ev·mouseabsmove},
+ },
+};
+
+static Keyboard keyboard = {
+ .ev = {
+ .modifier = {.notify=ev·modifier},
+ .keypress = {.notify=ev·keypress},
+ },
+};
+
+// -----------------------------------------------------------------------
+// utility functions
+
+static void arrange(Monitor *m);
+
+/* misc */
+static
+void
+fatal(byte *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+
+ verrorf(fmt, args);
+
+ va_end(args);
+ exit(1);
+}
+
+static
+void
+scale(struct wlr_box *box, float by)
+{
+ box->x *= by;
+ box->y *= by;
+ box->height *= by;
+ box->width *= by;
+}
+
+/* client operations */
+static
+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;
+}
+
+static
+void
+resize(Client *c, int x, int y, int w, int h, int interact)
+{
+ struct wlr_box *bbox;
+
+ bbox = interact ? &dway.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, c->dim.height - 2 * c->bw);
+}
+
+static
+Client *
+getclient(void)
+{
+ Client *c;
+
+ c = wl_container_of(dway.focus.next, c, pos.focus);
+ if (wl_list_empty(&dway.focus) || !VISIBLEON(c, monitor))
+ return nil;
+
+ return c;
+}
+
+static
+Client *
+getclientat(double x, double y)
+{
+ Client *c;
+ wl_list_for_each(c, &dway.stack, pos.stack)
+ if (VISIBLEON(c, c->m) && wlr_box_contains_point(&c->dim, x, y))
+ return c;
+ return nil;
+}
+
+
+static
+Client *
+lastfocus(void)
+{
+ Client *c;
+ wl_list_for_each(c, &dway.focus, pos.focus)
+ if (VISIBLEON(c, monitor))
+ return c;
+
+ return nil;
+}
+
+static
+void
+setfocus(Client *c, struct wlr_surface *surf, int lift)
+{
+ struct wlr_surface *prev_surface;
+ struct wlr_xdg_surface *previous;
+ struct wlr_keyboard *kb;
+
+ if (c) {
+ if (!surf)
+ surf = c->surf->surface;
+ monitor = c->m;
+ }
+
+ prev_surface = dway.seat->keyboard_state.focused_surface;
+ if (prev_surface == surf)
+ return;
+
+ if (prev_surface) {
+ previous = wlr_xdg_surface_from_wlr_surface(dway.seat->keyboard_state.focused_surface);
+ wlr_xdg_toplevel_set_activated(previous, 0);
+ }
+
+ kb = wlr_seat_get_keyboard(dway.seat);
+ wlr_seat_keyboard_notify_enter(dway.seat, surf, kb->keycodes, kb->num_keycodes, &kb->modifiers);
+ if (c) {
+ wl_list_remove(&c->pos.focus);
+ wl_list_insert(&dway.focus, &c->pos.focus);
+ if (lift) {
+ wl_list_remove(&c->pos.stack);
+ wl_list_insert(&dway.stack, &c->pos.stack);
+ }
+ wlr_xdg_toplevel_set_activated(c->surf, 1);
+ }
+}
+
+static
+void
+setfloating(Client *c, int f)
+{
+ if (c->floating == f)
+ return;
+
+ c->floating = f;
+ arrange(c->m);
+}
+
+static
+void
+pointerfocus(Client *c, struct wlr_surface *surf, double sx, double sy, uint32 time)
+{
+ if (c && !surf)
+ surf = c->surf->surface;
+
+ if (surf && surf == dway.seat->pointer_state.focused_surface) {
+ wlr_seat_pointer_notify_motion(dway.seat, time, sx, sy);
+ return;
+ }
+
+ wlr_seat_pointer_notify_enter(dway.seat, surf, sx, sy);
+ /* If keyboard focus follows mouse, enforce that */
+ if (sloppyfocus && surf)
+ setfocus(c, surf, 0);
+}
+
+/* monitor operations */
+static
+void
+arrange(Monitor *m)
+{
+ m->area.all = *wlr_output_layout_get_box(dway.layout, m->dev);
+ m->area.win = m->area.all;
+ if (m->lt[m->sellt]->arrange)
+ m->lt[m->sellt]->arrange(m);
+}
+
+static
+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);
+}
+
+static
+Monitor*
+getmonitor(int dir)
+{
+ Monitor *m;
+
+ if (dir > 0) {
+ if (monitor->link.next == &dway.odevs)
+ return wl_container_of(dway.odevs.next, m, link);
+ return wl_container_of(monitor->link.next, m, link);
+ }
+ if (monitor->link.prev == &dway.odevs)
+ return wl_container_of(dway.odevs.prev, m, link);
+ return wl_container_of(monitor->link.prev, m, link);
+}
+
+static
+Monitor *
+getmonitorat(double x, double y)
+{
+ struct wlr_output *dev;
+
+ dev = wlr_output_layout_output_at(dway.layout, x, y);
+ return dev ? dev->data : nil;
+}
+
+// -----------------------------------------------------------------------
+// event callbacks
+
+/* output devices */
+static
+void
+ev·newmonitor(struct wl_listener *ev, void *arg)
+{
+ Monitor *m;
+ struct wlr_output *odev;
+ struct wlr_output_mode *mode;
+
+ odev = arg;
+ if (!wl_list_empty(&odev->modes)) {
+ mode = wl_container_of(odev->modes.prev, mode, link);
+
+ wlr_output_set_mode(odev, mode);
+ wlr_output_enable(odev, true);
+ if (!wlr_output_commit(odev))
+ return;
+ }
+
+ m = calloc(1, sizeof(*m));
+ m->dev = odev;
+ wl_list_insert(&dway.odevs, &m->link);
+
+ /* install callbacks */
+ m->ev.free.notify = ev·freemonitor;
+ wl_signal_add(&odev->events.destroy, &m->ev.free);
+
+ m->ev.draw.notify = ev·render;
+ wl_signal_add(&odev->events.frame, &m->ev.draw);
+}
+
+static
+void
+ev·freemonitor(struct wl_listener *ev, void *arg)
+{
+ Monitor *m;
+
+ m = wl_container_of(ev, m, ev.free);
+
+ 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(dway.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(dway.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, &dway.stack, pos.stack) {
+ if (!VISIBLEON(c, c->m) || !wlr_output_layout_intersects(dway.layout, m->dev, &c->dim))
+ continue;
+
+ x = c->dim.x;
+ y = c->dim.y;
+
+ wlr_output_layout_output_coords(dway.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(dway.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
+ev·render(struct wl_listener *ev, void *arg)
+{
+ int w, h;
+ Monitor *m;
+ struct wlr_output *odev;
+ struct timespec now;
+
+ odev = arg;
+ 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(dway.draw, w, h);
+ wlr_renderer_clear(dway.draw, rootcolor);
+
+ renderclients(m, &now);
+
+ wlr_output_render_software_cursors(m->dev, nil);
+
+ wlr_renderer_end(dway.draw);
+ wlr_output_commit(m->dev);
+}
+
+/* xdg-surfaces */
+#define WLR_EDGE_ALL WLR_EDGE_TOP|WLR_EDGE_BOTTOM|WLR_EDGE_LEFT|WLR_EDGE_RIGHT
+static
+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;
+
+ wlr_xdg_toplevel_set_tiled(surf, WLR_EDGE_ALL);
+
+ /* install callbacks */
+ c->ev.map.notify = ev·mapclient;
+ wl_signal_add(&surf->events.map, &c->ev.map);
+
+ c->ev.unmap.notify = ev·unmapclient;
+ wl_signal_add(&surf->events.unmap, &c->ev.unmap);
+
+ c->ev.free.notify = ev·freeclient;
+ wl_signal_add(&surf->events.unmap, &c->ev.free);
+}
+
+static
+void
+ev·mapclient(struct wl_listener *ev, void *arg)
+{
+ Client *c;
+
+ c = wl_container_of(ev, c, ev.map);
+
+ wl_list_insert(&dway.tiles, &c->pos.tiles);
+ wl_list_insert(&dway.stack, &c->pos.stack);
+ wl_list_insert(&dway.focus, &c->pos.focus);
+
+ wlr_xdg_surface_get_geometry(c->surf, &c->dim);
+}
+
+static
+void
+ev·unmapclient(struct wl_listener *ev, void *arg)
+{
+ Client *c;
+
+ c = wl_container_of(ev, c, ev.unmap);
+ wl_list_remove(&c->pos.tiles);
+ wl_list_remove(&c->pos.stack);
+ wl_list_remove(&c->pos.focus);
+}
+
+static
+void
+ev·freeclient(struct wl_listener *ev, void *arg)
+{
+ Client *c;
+ c = wl_container_of(ev, c, ev.unmap);
+ free(c);
+}
+
+/* cursor images */
+
+static
+void
+ev·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 == dway.seat->pointer_state.focused_client)
+ wlr_cursor_set_surface(mouse.cursor, cur->surface, cur->hotspot_x, cur->hotspot_y);
+}
+
+static
+void
+ev·setpsel(struct wl_listener *ev, void *arg)
+{
+ struct wlr_seat_request_set_primary_selection_event *psel;
+
+ psel = arg;
+ wlr_seat_set_primary_selection(dway.seat, psel->source, psel->serial);
+}
+
+static
+void
+ev·setsel(struct wl_listener *ev, void *arg)
+{
+ struct wlr_seat_request_set_selection_event *sel;
+
+ sel = arg;
+ wlr_seat_set_selection(dway.seat, sel->source, sel->serial);
+}
+
+/* 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);
+
+ kb->ev.modifier.notify = ev·modifier;
+ wl_signal_add(&dev->keyboard->events.modifiers, &kb->ev.modifier);
+ kb->ev.keypress.notify = ev·keypress;
+ wl_signal_add(&dev->keyboard->events.key, &kb->ev.keypress);
+
+ wlr_seat_set_keyboard(dway.seat, dev);
+
+ wl_list_insert(&dway.keyboards, &kb->link);
+}
+
+static
+void
+newmouse(struct wlr_input_device *dev)
+{
+ wlr_cursor_attach_input_device(mouse.cursor, dev);
+}
+
+static
+void
+ev·newidev(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(&dway.keyboards))
+ c |= WL_SEAT_CAPABILITY_KEYBOARD;
+ wlr_seat_set_capabilities(dway.seat, c);
+}
+
+/* mouse input */
+
+static
+void
+ev·mousescroll(struct wl_listener *ev, void *arg)
+{
+ struct wlr_event_pointer_axis *axis;
+
+ axis = arg;
+ wlr_seat_pointer_notify_axis(
+ dway.seat,
+ axis->time_msec,
+ axis->orientation,
+ axis->delta,
+ axis->delta_discrete,
+ axis->source
+ );
+}
+
+static
+void
+ev·mouseclick(struct wl_listener *ev, void *arg)
+{
+ struct wlr_surface *surf;
+ struct wlr_keyboard *keyboard;
+ struct wlr_event_pointer_button *button;
+
+ button = arg;
+}
+
+static
+void
+ev·mouseframe(struct wl_listener *ev, void *arg)
+{
+ wlr_seat_pointer_notify_frame(dway.seat);
+}
+
+static
+void
+mousemoved(uint32 time)
+{
+ Client *c;
+ double sx, sy;
+ struct wlr_surface *surf;
+
+ 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 = getclientat(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
+ev·mouserelmove(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);
+ mousemoved(mv->time_msec);
+}
+
+static
+void
+ev·mouseabsmove(struct wl_listener *ev, void *arg)
+{
+ struct wlr_event_pointer_motion *mv;
+
+ mv = arg;
+ wlr_cursor_warp_absolute(mouse.cursor, mv->device, mv->delta_x, mv->delta_y);
+ mousemoved(mv->time_msec);
+}
+
+/* keyboard input */
+
+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
+ev·modifier(struct wl_listener *ev, void *arg)
+{
+ Keyboard *kb;
+
+ kb = wl_container_of(ev, kb, ev.modifier);
+ wlr_seat_set_keyboard(dway.seat, kb->dev);
+
+ wlr_seat_keyboard_notify_modifiers(dway.seat, &kb->dev->keyboard->modifiers);
+}
+
+static
+void
+ev·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(dway.seat, kb->dev);
+ wlr_seat_keyboard_notify_key(dway.seat, key->time_msec, key->keycode, key->state);
+ }
+}
+
+// -----------------------------------------------------------------------
+// user hook functions
+
+static
+void
+chvt(const Arg *arg)
+{
+ struct wlr_session *s;
+
+ s = wlr_backend_get_session(dway.backend);
+ if (!s)
+ return;
+
+ wlr_session_change_vt(s, arg->ui);
+}
+
+static
+void
+incmaster(const Arg *arg)
+{
+ monitor->nmaster = MAX(monitor->nmaster + arg->i, 0);
+ arrange(monitor);
+}
+
+static
+void
+focusmonitor(const Arg *arg)
+{
+ Monitor *m;
+
+ m = getmonitor(arg->i);
+
+ if (m == monitor)
+ return;
+
+ monitor = m;
+ setfocus(lastfocus(), nil, 1);
+}
+
+static
+void
+focusstack(const Arg *arg)
+{
+ Client *c, *sel;
+
+ sel = getclient();
+ if (!sel)
+ return;
+
+ if (arg->i > 0) {
+ wl_list_for_each(c, &sel->pos.tiles, pos.tiles) {
+ if (&c->pos.tiles == &dway.tiles)
+ continue;
+ if (VISIBLEON(c, monitor))
+ break;
+ }
+ } else {
+ wl_list_for_each_reverse(c, &sel->pos.tiles, pos.tiles) {
+ if (&c->pos.tiles == &dway.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, NULL, 1);
+}
+
+static
+void
+moveresize(const Arg *arg)
+{
+ grab.c = getclientat(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;
+ }
+}
+
+static
+void
+quit(const Arg *arg)
+{
+ wl_display_terminate(dway.display);
+}
+
+static
+void
+setlayout(const Arg *arg)
+{
+ if (!arg || !arg->p || arg->p != monitor->lt[monitor->sellt])
+ monitor->sellt ^= 1;
+ if (arg && arg->p)
+ monitor->lt[monitor->sellt] = (Layout *)arg->p;
+
+ arrange(monitor);
+}
+
+static
+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);
+}
+
+static
+void
+spawn(const Arg *arg)
+{
+ if (fork() == 0) {
+ setsid();
+ execvp(((char **)arg->p)[0], (char **)arg->p);
+ fatal("dwl: execvp %s", ((char **)arg->p)[0]);
+ }
+}
+
+static
+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);
+ }
+}
+
+static
+void
+tagmonitor(const Arg *arg)
+{
+ Client *c;
+
+ c = getclient();
+ if (!c)
+ return;
+ setmonitor(c, getmonitor(arg->i), 0);
+}
+
+static
+void
+togglefloating(const Arg *arg)
+{
+ Client *c;
+
+ c = getclient();
+ if (!c)
+ return;
+
+ setfloating(c, !c->floating);
+}
+
+static
+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);
+ }
+}
+
+static
+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);
+ }
+}
+
+static
+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);
+}
+
+// -----------------------------------------------------------------------
+// layouts
+
+static
+void
+tile(Monitor *m)
+{
+ Client *c;
+ uint i, n, h, mw, my, ty;
+
+ n = 0;
+ wl_list_for_each(c, &dway.tiles, pos.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, &dway.tiles, pos.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++;
+ }
+}
+
+// -----------------------------------------------------------------------
+// main point of entry
+
+void
+setup(void)
+{
+ /* wayland boilerplate */
+ dway.display = wl_display_create();
+ if (!dway.display)
+ fatal("failed to initialize display");
+
+ dway.ev.loop = wl_display_get_event_loop(dway.display);
+ if (!dway.ev.loop)
+ fatal("failed to initialize event loop");
+
+ dway.backend = wlr_backend_autocreate(dway.display, nil);
+ if (!dway.backend)
+ fatal("failed to create backend");
+
+ dway.draw = wlr_backend_get_renderer(dway.backend);
+ if (!dway.draw)
+ fatal("failed to initialize renderer");
+ wlr_renderer_init_wl_display(dway.draw, dway.display);
+
+ /* intialize the compositor & layout */
+ dway.compositor = wlr_compositor_create(dway.display, dway.draw);
+ if (!dway.compositor)
+ fatal("failed to initialize compositor");
+ wlr_data_device_manager_create(dway.display);
+
+ dway.layout = wlr_output_layout_create();
+
+ /* grab output devices */
+ wl_list_init(&dway.odevs);
+ wl_signal_add(&dway.backend->events.new_output, &dway.ev.odev);
+
+ dway.shell = wlr_xdg_shell_create(dway.display);
+ wl_signal_add(&dway.shell->events.new_surface, &dway.ev.client);
+
+ /* initialize window structures */
+ wl_list_init(&dway.tiles);
+ wl_list_init(&dway.stack);
+ wl_list_init(&dway.focus);
+
+ /* grab input devices and install callbacks */
+ wl_list_init(&dway.idevs);
+ wl_list_init(&dway.keyboards);
+ wl_signal_add(&dway.backend->events.new_input, &dway.ev.idev);
+
+ dway.seat = wlr_seat_create(dway.display, "seat0");
+ wl_signal_add(&dway.seat->events.request_set_cursor, &dway.ev.cursor);
+ wl_signal_add(&dway.seat->events.request_set_selection, &dway.ev.sel);
+ wl_signal_add(&dway.seat->events.request_set_primary_selection, &dway.ev.psel);
+
+ mouse.cursor = wlr_cursor_create();
+ if (!mouse.cursor)
+ fatal("no mouse found");
+
+ wlr_cursor_attach_output_layout(mouse.cursor, dway.layout);
+ mouse.manager = wlr_xcursor_manager_create(nil, 24);
+ wlr_xcursor_manager_load(mouse.manager, 1);
+
+ /* 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);
+}
+
+void
+run(void)
+{
+ byte *socket;
+
+ // socket = (byte*)wl_display_add_socket_auto(dway.display);
+ // if (!socket) {
+ // wlr_backend_destroy(dway.backend);
+ // fatal("could not open socket");
+ // }
+
+ if (!wlr_backend_start(dway.backend)) {
+ wlr_backend_destroy(dway.backend);
+ wl_display_destroy(dway.display);
+ fatal("failed to start backend");
+ }
+
+ // setenv("WAYLAND_DISPLAY", socket, true);
+
+ wlr_log(WLR_INFO, "running dway on WAYLAND_DISPLAY\n");
+ wl_display_run(dway.display);
+}
+
+void
+cleanup(void)
+{
+ wlr_backend_destroy(dway.backend);
+ wl_display_destroy(dway.display);
+}
+
+int
+main(int argc, byte *argv[])
+{
+ setup();
+ run();
+ cleanup();
+
+ return 0;
+}
diff --git a/sys/cmd/dway/dway.h b/sys/cmd/dway/dway.h
new file mode 100644
index 0000000..430f3b7
--- /dev/null
+++ b/sys/cmd/dway/dway.h
@@ -0,0 +1,201 @@
+#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_device.h>
+#include <wlr/types/wlr_input_device.h>
+#include <wlr/types/wlr_keyboard.h>
+#include <wlr/types/wlr_matrix.h>
+#include <wlr/types/wlr_output.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_xcursor_manager.h>
+#include <wlr/types/wlr_xdg_shell.h>
+#include <wlr/util/log.h>
+
+#include "xdg-shell-protocol.h"
+#include <xkbcommon/xkbcommon.h>
+
+#include <linux/input-event-codes.h>
+
+/* main types */
+
+typedef union Arg Arg;
+typedef struct Button Button;
+typedef struct Mouse Mouse;
+typedef struct Key Key;
+typedef struct Keyboard Keyboard;
+typedef struct Monitor Monitor;
+typedef struct Layout Layout;
+typedef struct Client Client;
+typedef struct Payload Payload;
+
+typedef struct Rule Rule;
+typedef struct MonitorRule MonitorRule;
+
+union Arg
+{
+ int i;
+ uint ui;
+ float f;
+ const void *p;
+};
+
+struct Button
+{
+ uint mod;
+ uint kind;
+ void (*func)(const Arg *);
+ Arg arg;
+};
+
+enum
+{
+ MouseNormal, 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;
+ } ev;
+};
+
+
+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 Monitor
+{
+ struct wl_list link;
+ struct wlr_output *dev;
+ struct {
+ struct wl_listener draw;
+ struct wl_listener free;
+ } ev;
+ struct {
+ struct wlr_box all;
+ struct wlr_box win;
+ } area;
+ const Layout *lt[2];
+ uint seltags;
+ uint sellt;
+ uint tagset[2];
+ double mfact;
+ int nmaster;
+};
+
+struct Layout
+{
+ char *sym;
+ void (*arrange)(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;
+ } pos;
+ struct {
+ struct wl_listener map;
+ struct wl_listener unmap;
+ struct wl_listener free;
+ } ev;
+ int bw;
+ uint tags;
+ int floating;
+ Monitor *m;
+};
+
+struct Payload
+{
+ struct wlr_output *dev;
+ struct timespec *now;
+ int x, y;
+};
+
+/* hooks provided to config */
+static void chvt(const Arg *arg);
+static void incmaster(const Arg *arg);
+static void focusmonitor(const Arg *arg);
+static void focusstack(const Arg *arg);
+static void moveresize(const Arg *arg);
+static void quit(const Arg *arg);
+static void setlayout(const Arg *arg);
+static void setmfact(const Arg *arg);
+static void spawn(const Arg *arg);
+static void tag(const Arg *arg);
+static void tagmonitor(const Arg *arg);
+static void togglefloating(const Arg *arg);
+static void toggletag(const Arg *arg);
+static void toggleview(const Arg *arg);
+static void view(const Arg *arg);
+
+/* layouts */
+static void tile(Monitor *m);
+
+#include "config.h"
+
+/* callback functions */
+static void ev·newmonitor(struct wl_listener *ev, void *arg);
+static void ev·freemonitor(struct wl_listener *ev, void *arg);
+
+static void ev·newidev(struct wl_listener *ev, void *arg);
+static void ev·freeidev(struct wl_listener *ev, void *arg);
+
+static void ev·render(struct wl_listener *ev, void *arg);
+static void ev·newclient(struct wl_listener *ev, void *arg);
+static void ev·mapclient(struct wl_listener *ev, void *arg);
+static void ev·unmapclient(struct wl_listener *ev, void *arg);
+static void ev·freeclient(struct wl_listener *ev, void *arg);
+
+static void ev·setcursor(struct wl_listener *ev, void *arg);
+static void ev·setsel(struct wl_listener *ev, void *arg);
+static void ev·setpsel(struct wl_listener *ev, void *arg);
+
+static void ev·mousescroll(struct wl_listener *ev, void *arg);
+static void ev·mouseframe(struct wl_listener *ev, void *arg);
+static void ev·mouseclick(struct wl_listener *ev, void *arg);
+static void ev·mouserelmove(struct wl_listener *ev, void *arg);
+static void ev·mouseabsmove(struct wl_listener *ev, void *arg);
+
+static void ev·keypress(struct wl_listener *ev, void *arg);
+static void ev·modifier(struct wl_listener *ev, void *arg);