From cdc50fd76fd0e286f40416f579d60f14e19aa9f5 Mon Sep 17 00:00:00 2001 From: Nicholas Noll Date: Tue, 2 Jun 2020 08:49:01 -0700 Subject: feat: started port of dwm to wayland --- sys/cmd/dway/config.h | 77 ++++ sys/cmd/dway/dway.c | 1156 +++++++++++++++++++++++++++++++++++++++++++++++++ sys/cmd/dway/dway.h | 201 +++++++++ 3 files changed, 1434 insertions(+) create mode 100644 sys/cmd/dway/config.h create mode 100644 sys/cmd/dway/dway.c create mode 100644 sys/cmd/dway/dway.h (limited to 'sys') 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 +#include + +#include + +#define WLR_USE_UNSTABLE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "xdg-shell-protocol.h" +#include + +#include + +/* 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); -- cgit v1.2.1