diff options
Diffstat (limited to 'dwl.c')
-rw-r--r-- | dwl.c | 343 |
1 files changed, 172 insertions, 171 deletions
@@ -43,7 +43,7 @@ #define LENGTH(X) (sizeof X / sizeof X[0]) #define END(A) ((A) + LENGTH(A)) #define TAGMASK ((1 << LENGTH(tags)) - 1) -#define WLR_SURFACE(C) ((C)->type != XDGShell ? (C)->xwayland_surface->surface : (C)->xdg_surface->surface) +#define WLR_SURFACE(C) ((C)->type != XDGShell ? (C)->surface.xwayland->surface : (C)->surface.xdg->surface) /* enums */ enum { CurNormal, CurMove, CurResize }; /* cursor */ @@ -71,10 +71,11 @@ typedef struct { struct wl_list flink; struct wl_list slink; union { - struct wlr_xdg_surface *xdg_surface; - struct wlr_xwayland_surface *xwayland_surface; - }; + struct wlr_xdg_surface *xdg; + struct wlr_xwayland_surface *xwayland; + } surface; struct wl_listener activate; + struct wl_listener commit; struct wl_listener map; struct wl_listener unmap; struct wl_listener destroy; @@ -84,6 +85,7 @@ typedef struct { int bw; unsigned int tags; int isfloating; + uint32_t resize; /* configure serial of a pending resize */ } Client; typedef struct { @@ -172,9 +174,10 @@ static void cursorframe(struct wl_listener *listener, void *data); static void destroynotify(struct wl_listener *listener, void *data); static void destroyxdeco(struct wl_listener *listener, void *data); static Monitor *dirtomon(int dir); -static void focusclient(Client *c, struct wlr_surface *surface, int lift); +static void focusclient(Client *old, Client *c, int lift); static void focusmon(const Arg *arg); static void focusstack(const Arg *arg); +static Client *focustop(Monitor *m); static Atom getatom(xcb_connection_t *xc, const char *name); static void getxdecomode(struct wl_listener *listener, void *data); static void incnmaster(const Arg *arg); @@ -183,7 +186,6 @@ static int keybinding(uint32_t mods, xkb_keysym_t sym); static void keypress(struct wl_listener *listener, void *data); static void keypressmod(struct wl_listener *listener, void *data); static void killclient(const Arg *arg); -static Client *lastfocused(void); static void maprequest(struct wl_listener *listener, void *data); static void motionabsolute(struct wl_listener *listener, void *data); static void motionnotify(uint32_t time); @@ -272,6 +274,9 @@ static struct wl_listener xwayland_ready = {.notify = xwaylandready}; /* configuration, allows nested code to access above variables */ #include "config.h" +/* compile-time check if all tags fit into an unsigned int bit array. */ +struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; + /* function implementations */ void activatex11(struct wl_listener *listener, void *data) @@ -280,7 +285,7 @@ activatex11(struct wl_listener *listener, void *data) /* Only "managed" windows can be activated */ if (c->type == X11Managed) - wlr_xwayland_surface_activate(c->xwayland_surface, 1); + wlr_xwayland_surface_activate(c->surface.xwayland, 1); } void @@ -310,10 +315,10 @@ applyrules(Client *c) /* rule matching */ c->isfloating = 0; - appid = c->type != XDGShell ? c->xwayland_surface->class : - c->xdg_surface->toplevel->app_id; - title = c->type != XDGShell ? c->xwayland_surface->title : - c->xdg_surface->toplevel->title; + appid = c->type != XDGShell ? c->surface.xwayland->class : + c->surface.xdg->toplevel->app_id; + title = c->type != XDGShell ? c->surface.xwayland->title : + c->surface.xdg->toplevel->title; if (!appid) appid = broken; if (!title) @@ -321,8 +326,7 @@ applyrules(Client *c) for (r = rules; r < END(rules); r++) { if ((!r->title || strstr(title, r->title)) - && (!r->id || strstr(appid, r->id))) - { + && (!r->id || strstr(appid, r->id))) { c->isfloating = r->isfloating; newtags |= r->tags; i = 0; @@ -362,7 +366,6 @@ void buttonpress(struct wl_listener *listener, void *data) { struct wlr_event_pointer_button *event = data; - struct wlr_surface *surface; struct wlr_keyboard *keyboard; uint32_t mods; Client *c; @@ -371,17 +374,8 @@ buttonpress(struct wl_listener *listener, void *data) switch (event->state) { case WLR_BUTTON_PRESSED:; /* Change focus if the button was _pressed_ over a client */ - if ((c = xytoclient(cursor->x, cursor->y))) { - if (c->type != XDGShell) - surface = wlr_surface_surface_at(c->xwayland_surface->surface, - cursor->x - c->geom.x - c->bw, - cursor->y - c->geom.y - c->bw, NULL, NULL); - else - surface = wlr_xdg_surface_surface_at(c->xdg_surface, - cursor->x - c->geom.x - c->bw, - cursor->y - c->geom.y - c->bw, NULL, NULL); - focusclient(c, surface, 1); - } + if ((c = xytoclient(cursor->x, cursor->y))) + focusclient(selclient(), c, 1); keyboard = wlr_seat_get_keyboard(seat); mods = wlr_keyboard_get_modifiers(keyboard); @@ -443,6 +437,16 @@ cleanupmon(struct wl_listener *listener, void *data) } void +commitnotify(struct wl_listener *listener, void *data) +{ + Client *c = wl_container_of(listener, c, commit); + + /* mark a pending resize as completed */ + if (c->resize && c->resize <= c->surface.xdg->configure_serial) + c->resize = 0; +} + +void createkeyboard(struct wlr_input_device *device) { struct xkb_context *context; @@ -544,15 +548,17 @@ createnotify(struct wl_listener *listener, void *data) /* Allocate a Client for this surface */ c = xdg_surface->data = calloc(1, sizeof(*c)); - c->xdg_surface = xdg_surface; + c->surface.xdg = xdg_surface; c->type = XDGShell; c->bw = borderpx; /* Tell the client not to try anything fancy */ - wlr_xdg_toplevel_set_tiled(c->xdg_surface, WLR_EDGE_TOP | + wlr_xdg_toplevel_set_tiled(c->surface.xdg, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT | WLR_EDGE_RIGHT); /* Listen to the various events it can emit */ + c->commit.notify = commitnotify; + wl_signal_add(&xdg_surface->surface->events.commit, &c->commit); c->map.notify = maprequest; wl_signal_add(&xdg_surface->events.map, &c->map); c->unmap.notify = unmapnotify; @@ -569,7 +575,7 @@ createnotifyx11(struct wl_listener *listener, void *data) /* Allocate a Client for this surface */ c = xwayland_surface->data = calloc(1, sizeof(*c)); - c->xwayland_surface = xwayland_surface; + c->surface.xwayland = xwayland_surface; c->type = xwayland_surface->override_redirect ? X11Unmanaged : X11Managed; c->bw = borderpx; @@ -628,6 +634,10 @@ destroynotify(struct wl_listener *listener, void *data) wl_list_remove(&c->map.link); wl_list_remove(&c->unmap.link); wl_list_remove(&c->destroy.link); + if (c->type == XDGShell) + wl_list_remove(&c->commit.link); + if (c->type == X11Managed) + wl_list_remove(&c->activate.link); free(c); } @@ -659,78 +669,58 @@ dirtomon(int dir) } void -focusclient(Client *c, struct wlr_surface *surface, int lift) +focusclient(Client *old, Client *c, int lift) { - Client *sel = selclient(); - struct wlr_keyboard *kb; - /* Previous and new xdg toplevel surfaces */ - Client *ptl = sel; - Client *tl = c; - /* Previously focused surface */ - struct wlr_surface *psurface = seat->keyboard_state.focused_surface; - - if (c) { - /* assert(VISIBLEON(c, c->mon)); ? */ - /* Use top-level wlr_surface if nothing more specific given */ - if (!surface) - surface = WLR_SURFACE(c); - - /* Focus the correct monitor (must come after selclient!) */ - selmon = c->mon; - - /* Move the client to the front of the focus stack */ - wl_list_remove(&c->flink); - wl_list_insert(&fstack, &c->flink); - - /* Also raise client in stacking order if requested */ - if (lift) { - wl_list_remove(&c->slink); - wl_list_insert(&stack, &c->slink); - } - } + struct wlr_keyboard *kb = wlr_seat_get_keyboard(seat); - /* - * If the focused surface has changed, tell the seat to have the - * keyboard enter the new surface. wlroots will keep track of this and - * automatically send key events to the appropriate clients. If surface - * is NULL, we clear the focus instead. - */ - if (!surface) { - wlr_seat_keyboard_notify_clear_focus(seat); - } else if (surface != psurface) { - kb = wlr_seat_get_keyboard(seat); - wlr_seat_keyboard_notify_enter(seat, surface, - kb->keycodes, kb->num_keycodes, &kb->modifiers); + /* Raise client in stacking order if requested */ + if (c && lift) { + wl_list_remove(&c->slink); + wl_list_insert(&stack, &c->slink); } - /* - * If the focused toplevel has changed, deactivate the old one. Always - * activate the current one. This lets the clients know to repaint - * accordingly, e.g. show/hide a caret. - */ - if (tl != ptl && ptl) { - if (ptl->type != XDGShell) - wlr_xwayland_surface_activate(ptl->xwayland_surface, 0); + /* Nothing else to do? */ + if (c == old) + return; + + /* Deactivate old client if focus is changing */ + if (c != old && old) { + if (old->type != XDGShell) + wlr_xwayland_surface_activate(old->surface.xwayland, 0); else - wlr_xdg_toplevel_set_activated(ptl->xdg_surface, 0); + wlr_xdg_toplevel_set_activated(old->surface.xdg, 0); } - if (tl) { - if (tl->type != XDGShell) - wlr_xwayland_surface_activate(tl->xwayland_surface, 1); - else - wlr_xdg_toplevel_set_activated(tl->xdg_surface, 1); + + /* Update wlroots' keyboard focus */ + if (!c) { + /* With no client, all we have left is to clear focus */ + wlr_seat_keyboard_notify_clear_focus(seat); + return; } + + /* Have a client, so focus its top-level wlr_surface */ + wlr_seat_keyboard_notify_enter(seat, WLR_SURFACE(c), + kb->keycodes, kb->num_keycodes, &kb->modifiers); + + /* Put the new client atop the focus stack and select its monitor */ + wl_list_remove(&c->flink); + wl_list_insert(&fstack, &c->flink); + selmon = c->mon; + + /* Activate the new client */ + if (c->type != XDGShell) + wlr_xwayland_surface_activate(c->surface.xwayland, 1); + else + wlr_xdg_toplevel_set_activated(c->surface.xdg, 1); } void focusmon(const Arg *arg) { - Monitor *m = dirtomon(arg->i); + Client *sel = selclient(); - if (m == selmon) - return; - selmon = m; - focusclient(lastfocused(), NULL, 1); + selmon = dirtomon(arg->i); + focusclient(sel, focustop(selmon), 1); } void @@ -756,7 +746,17 @@ focusstack(const Arg *arg) } } /* If only one client is visible on selmon, then c == sel */ - focusclient(c, NULL, 1); + focusclient(sel, c, 1); +} + +Client * +focustop(Monitor *m) +{ + Client *c; + wl_list_for_each(c, &fstack, flink) + if (VISIBLEON(c, m)) + return c; + return NULL; } Atom @@ -895,19 +895,9 @@ killclient(const Arg *arg) return; if (sel->type != XDGShell) - wlr_xwayland_surface_close(sel->xwayland_surface); + wlr_xwayland_surface_close(sel->surface.xwayland); else - wlr_xdg_toplevel_send_close(sel->xdg_surface); -} - -Client * -lastfocused(void) -{ - Client *c; - wl_list_for_each(c, &fstack, flink) - if (VISIBLEON(c, selmon)) - return c; - return NULL; + wlr_xdg_toplevel_send_close(sel->surface.xdg); } void @@ -928,12 +918,12 @@ maprequest(struct wl_listener *listener, void *data) wl_list_insert(&stack, &c->slink); if (c->type != XDGShell) { - c->geom.x = c->xwayland_surface->x; - c->geom.y = c->xwayland_surface->y; - c->geom.width = c->xwayland_surface->width + 2 * c->bw; - c->geom.height = c->xwayland_surface->height + 2 * c->bw; + c->geom.x = c->surface.xwayland->x; + c->geom.y = c->surface.xwayland->y; + c->geom.width = c->surface.xwayland->width + 2 * c->bw; + c->geom.height = c->surface.xwayland->height + 2 * c->bw; } else { - wlr_xdg_surface_get_geometry(c->xdg_surface, &c->geom); + wlr_xdg_surface_get_geometry(c->surface.xdg, &c->geom); c->geom.width += 2 * c->bw; c->geom.height += 2 * c->bw; } @@ -983,11 +973,11 @@ motionnotify(uint32_t time) /* Otherwise, find the client under the pointer and send the event along. */ if ((c = xytoclient(cursor->x, cursor->y))) { if (c->type != XDGShell) - surface = wlr_surface_surface_at(c->xwayland_surface->surface, + surface = wlr_surface_surface_at(c->surface.xwayland->surface, cursor->x - c->geom.x - c->bw, cursor->y - c->geom.y - c->bw, &sx, &sy); else - surface = wlr_xdg_surface_surface_at(c->xdg_surface, + surface = wlr_xdg_surface_surface_at(c->surface.xdg, cursor->x - c->geom.x - c->bw, cursor->y - c->geom.y - c->bw, &sx, &sy); } @@ -1052,23 +1042,23 @@ pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, if (c && !surface) surface = WLR_SURFACE(c); - /* If surface is already focused, only notify of motion */ - if (surface && surface == seat->pointer_state.focused_surface) { - wlr_seat_pointer_notify_motion(seat, time, sx, sy); + /* If surface is NULL, clear pointer focus */ + if (!surface) { + wlr_seat_pointer_notify_clear_focus(seat); return; } - /* If surface is NULL, clear pointer focus, otherwise let the client - * know that the mouse cursor has entered one of its surfaces. */ - if (!surface) { - wlr_seat_pointer_notify_clear_focus(seat); + /* If surface is already focused, only notify of motion */ + if (surface == seat->pointer_state.focused_surface) { + wlr_seat_pointer_notify_motion(seat, time, sx, sy); return; } + /* Otherwise, let the client know that the mouse cursor has entered one + * of its surfaces, and make keyboard focus follow if desired. */ wlr_seat_pointer_notify_enter(seat, surface, sx, sy); - /* If keyboard focus follows mouse, enforce that */ if (sloppyfocus) - focusclient(c, surface, 0); + focusclient(selclient(), c, 0); } void @@ -1176,14 +1166,14 @@ renderclients(Monitor *m, struct timespec *now) /* This calls our render function for each surface among the * xdg_surface's toplevel and popups. */ - rdata.output = m->wlr_output, - rdata.when = now, - rdata.x = c->geom.x + c->bw, + rdata.output = m->wlr_output; + rdata.when = now; + rdata.x = c->geom.x + c->bw; rdata.y = c->geom.y + c->bw; if (c->type != XDGShell) - wlr_surface_for_each_surface(c->xwayland_surface->surface, render, &rdata); + wlr_surface_for_each_surface(c->surface.xwayland->surface, render, &rdata); else - wlr_xdg_surface_for_each_surface(c->xdg_surface, render, &rdata); + wlr_xdg_surface_for_each_surface(c->surface.xdg, render, &rdata); } } @@ -1194,30 +1184,30 @@ renderindependents(struct wlr_output *output, struct timespec *now) struct render_data rdata; struct wlr_box geom; - wl_list_for_each_reverse(c, &independents, link) - { - geom.x = c->xwayland_surface->x; - geom.y = c->xwayland_surface->y; - geom.width = c->xwayland_surface->width; - geom.height = c->xwayland_surface->height; + wl_list_for_each_reverse(c, &independents, link) { + geom.x = c->surface.xwayland->x; + geom.y = c->surface.xwayland->y; + geom.width = c->surface.xwayland->width; + geom.height = c->surface.xwayland->height; /* Only render visible clients which show on this output */ if (!wlr_output_layout_intersects(output_layout, output, &geom)) continue; - rdata.output = output, - rdata.when = now, - rdata.x = c->xwayland_surface->x; - rdata.y = c->xwayland_surface->y; + rdata.output = output; + rdata.when = now; + rdata.x = c->surface.xwayland->x; + rdata.y = c->surface.xwayland->y; - wlr_surface_for_each_surface(c->xwayland_surface->surface, render, &rdata); + wlr_surface_for_each_surface(c->surface.xwayland->surface, render, &rdata); } } void rendermon(struct wl_listener *listener, void *data) { - struct wlr_output *output = data; + Client *c; + int render = 1; /* This function is called every time an output is ready to display a frame, * generally at the output's refresh rate (e.g. 60Hz). */ @@ -1226,28 +1216,39 @@ rendermon(struct wl_listener *listener, void *data) struct timespec now; clock_gettime(CLOCK_MONOTONIC, &now); + /* Do not render if any XDG clients have an outstanding resize. */ + wl_list_for_each(c, &stack, slink) { + if (c->resize) { + wlr_surface_send_frame_done(WLR_SURFACE(c), &now); + render = 0; + } + } + /* wlr_output_attach_render makes the OpenGL context current. */ if (!wlr_output_attach_render(m->wlr_output, NULL)) return; - /* Begin the renderer (calls glViewport and some other GL sanity checks) */ - wlr_renderer_begin(drw, m->wlr_output->width, m->wlr_output->height); - wlr_renderer_clear(drw, rootcolor); - - renderclients(m, &now); - renderindependents(output, &now); - - /* Hardware cursors are rendered by the GPU on a separate plane, and can be - * moved around without re-rendering what's beneath them - which is more - * efficient. However, not all hardware supports hardware cursors. For this - * reason, wlroots provides a software fallback, which we ask it to render - * here. wlr_cursor handles configuring hardware vs software cursors for you, - * and this function is a no-op when hardware cursors are in use. */ - wlr_output_render_software_cursors(m->wlr_output, NULL); + if (render) { + /* Begin the renderer (calls glViewport and some other GL sanity checks) */ + wlr_renderer_begin(drw, m->wlr_output->width, m->wlr_output->height); + wlr_renderer_clear(drw, rootcolor); + + renderclients(m, &now); + renderindependents(m->wlr_output, &now); + + /* Hardware cursors are rendered by the GPU on a separate plane, and can be + * moved around without re-rendering what's beneath them - which is more + * efficient. However, not all hardware supports hardware cursors. For this + * reason, wlroots provides a software fallback, which we ask it to render + * here. wlr_cursor handles configuring hardware vs software cursors for you, + * and this function is a no-op when hardware cursors are in use. */ + wlr_output_render_software_cursors(m->wlr_output, NULL); + + /* Conclude rendering and swap the buffers, showing the final frame + * on-screen. */ + wlr_renderer_end(drw); + } - /* Conclude rendering and swap the buffers, showing the final frame - * on-screen. */ - wlr_renderer_end(drw); wlr_output_commit(m->wlr_output); } @@ -1267,11 +1268,11 @@ resize(Client *c, int x, int y, int w, int h, int interact) applybounds(c, bbox); /* wlroots makes this a no-op if size hasn't changed */ if (c->type != XDGShell) - wlr_xwayland_surface_configure(c->xwayland_surface, + wlr_xwayland_surface_configure(c->surface.xwayland, c->geom.x, c->geom.y, c->geom.width - 2 * c->bw, c->geom.height - 2 * c->bw); else - wlr_xdg_toplevel_set_size(c->xdg_surface, + c->resize = wlr_xdg_toplevel_set_size(c->surface.xdg, c->geom.width - 2 * c->bw, c->geom.height - 2 * c->bw); } @@ -1414,28 +1415,26 @@ setmfact(const Arg *arg) void setmon(Client *c, Monitor *m, unsigned int newtags) { - int hadfocus; Monitor *oldmon = c->mon; - struct wlr_surface *surface = WLR_SURFACE(c); + Client *oldsel = selclient(); + if (oldmon == m) return; - hadfocus = (c == selclient()); c->mon = m; + /* XXX leave/enter is not optimal but works */ if (oldmon) { - wlr_surface_send_leave(surface, oldmon->wlr_output); + wlr_surface_send_leave(WLR_SURFACE(c), oldmon->wlr_output); arrange(oldmon); } if (m) { /* Make sure window actually overlaps with the monitor */ applybounds(c, &m->m); - wlr_surface_send_enter(surface, m->wlr_output); + wlr_surface_send_enter(WLR_SURFACE(c), m->wlr_output); c->tags = newtags ? newtags : m->tagset[m->seltags]; /* assign tags of target monitor */ arrange(m); } - /* Focus can change if c is the top of selmon before or after */ - if (hadfocus || c == selclient()) - focusclient(lastfocused(), NULL, 1); + focusclient(oldsel, focustop(selmon), 1); } void @@ -1598,7 +1597,7 @@ tag(const Arg *arg) Client *sel = selclient(); if (sel && arg->ui & TAGMASK) { sel->tags = arg->ui & TAGMASK; - focusclient(lastfocused(), NULL, 1); + focusclient(sel, focustop(selmon), 1); arrange(selmon); } } @@ -1665,7 +1664,7 @@ toggletag(const Arg *arg) newtags = sel->tags ^ (arg->ui & TAGMASK); if (newtags) { sel->tags = newtags; - focusclient(lastfocused(), NULL, 1); + focusclient(sel, focustop(selmon), 1); arrange(selmon); } } @@ -1673,11 +1672,12 @@ toggletag(const Arg *arg) void toggleview(const Arg *arg) { + Client *sel = selclient(); unsigned int newtagset = selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK); if (newtagset) { selmon->tagset[selmon->seltags] = newtagset; - focusclient(lastfocused(), NULL, 1); + focusclient(sel, focustop(selmon), 1); arrange(selmon); } } @@ -1701,11 +1701,11 @@ updatewindowtype(Client *c) size_t i; if (c->type != XDGShell) - for (i = 0; i < c->xwayland_surface->window_type_len; i++) - if (c->xwayland_surface->window_type[i] == netatom[NetWMWindowTypeDialog] || - c->xwayland_surface->window_type[i] == netatom[NetWMWindowTypeSplash] || - c->xwayland_surface->window_type[i] == netatom[NetWMWindowTypeToolbar] || - c->xwayland_surface->window_type[i] == netatom[NetWMWindowTypeUtility]) + for (i = 0; i < c->surface.xwayland->window_type_len; i++) + if (c->surface.xwayland->window_type[i] == netatom[NetWMWindowTypeDialog] || + c->surface.xwayland->window_type[i] == netatom[NetWMWindowTypeSplash] || + c->surface.xwayland->window_type[i] == netatom[NetWMWindowTypeToolbar] || + c->surface.xwayland->window_type[i] == netatom[NetWMWindowTypeUtility]) c->isfloating = 1; } @@ -1733,12 +1733,13 @@ xwaylandready(struct wl_listener *listener, void *data) { void view(const Arg *arg) { + Client *sel = selclient(); if ((arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) return; selmon->seltags ^= 1; /* toggle sel tagset */ if (arg->ui & TAGMASK) selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; - focusclient(lastfocused(), NULL, 1); + focusclient(sel, focustop(selmon), 1); arrange(selmon); } @@ -1764,7 +1765,7 @@ xytomon(double x, double y) void zoom(const Arg *arg) { - Client *c, *sel = selclient(); + Client *c, *sel = selclient(), *oldsel = sel; if (!sel || !selmon->lt[selmon->sellt]->arrange || sel->isfloating) return; @@ -1789,7 +1790,7 @@ zoom(const Arg *arg) wl_list_remove(&sel->link); wl_list_insert(&clients, &sel->link); - focusclient(sel, NULL, 1); + focusclient(oldsel, sel, 1); arrange(selmon); } |