diff options
Diffstat (limited to 'dwl.c')
-rw-r--r-- | dwl.c | 586 |
1 files changed, 493 insertions, 93 deletions
@@ -4,6 +4,7 @@ #include <getopt.h> #include <libinput.h> #include <linux/input-event-codes.h> +#include <libdrm/drm_fourcc.h> #include <math.h> #include <signal.h> #include <stdio.h> @@ -58,6 +59,7 @@ #include <wlr/types/wlr_xdg_decoration_v1.h> #include <wlr/types/wlr_xdg_output_v1.h> #include <wlr/types/wlr_xdg_shell.h> +#include <wlr/interfaces/wlr_buffer.h> #include <wlr/util/log.h> #include <wlr/util/region.h> #include <xkbcommon/xkbcommon.h> @@ -68,6 +70,7 @@ #endif #include "util.h" +#include "drwl.h" /* macros */ #define MAX(A, B) ((A) > (B) ? (A) : (B)) @@ -76,14 +79,17 @@ #define VISIBLEON(C, M) ((M) && (C)->mon == (M) && ((C)->tags & (M)->tagset[(M)->seltags])) #define LENGTH(X) (sizeof X / sizeof X[0]) #define END(A) ((A) + LENGTH(A)) -#define TAGMASK ((1u << TAGCOUNT) - 1) +#define TAGMASK ((1u << LENGTH(tags)) - 1) #define LISTEN(E, L, H) wl_signal_add((E), ((L)->notify = (H), (L))) #define LISTEN_STATIC(E, H) do { static struct wl_listener _l = {.notify = (H)}; wl_signal_add((E), &_l); } while (0) +#define TEXTW(mon, text) (drwl_font_getwidth(mon->drw, text) + mon->lrpad) /* enums */ +enum { SchemeNorm, SchemeSel, SchemeUrg }; /* color schemes */ enum { CurNormal, CurPressed, CurMove, CurResize }; /* cursor */ enum { XDGShell, LayerShell, X11 }; /* client types */ enum { LyrBg, LyrBottom, LyrTile, LyrFloat, LyrTop, LyrFS, LyrOverlay, LyrBlock, NUM_LAYERS }; /* scene layers */ +enum { ClkTagBar, ClkLtSymbol, ClkStatus, ClkTitle, ClkClient, ClkRoot }; /* clicks */ #ifdef XWAYLAND enum { NetWMWindowTypeDialog, NetWMWindowTypeSplash, NetWMWindowTypeToolbar, NetWMWindowTypeUtility, NetLast }; /* EWMH atoms */ @@ -97,6 +103,7 @@ typedef union { } Arg; typedef struct { + unsigned int click; unsigned int mod; unsigned int button; void (*func)(const Arg *); @@ -190,6 +197,7 @@ struct Monitor { struct wl_list link; struct wlr_output *wlr_output; struct wlr_scene_output *scene_output; + struct wlr_scene_buffer *scene_buffer; /* bar buffer */ struct wlr_scene_rect *fullscreen_bg; /* See createmon() for info */ struct wl_listener frame; struct wl_listener destroy; @@ -197,20 +205,38 @@ struct Monitor { struct wl_listener destroy_lock_surface; struct wlr_session_lock_surface_v1 *lock_surface; struct wlr_box m; /* monitor area, layout-relative */ + struct { + int width, height; + int real_width, real_height; /* non-scaled */ + float scale; + } b; /* bar area */ struct wlr_box w; /* window area, layout-relative */ struct wl_list layers[4]; /* LayerSurface.link */ const Layout *lt[2]; + int gappih; /* horizontal gap between windows */ + int gappiv; /* vertical gap between windows */ + int gappoh; /* horizontal outer gaps */ + int gappov; /* vertical outer gaps */ unsigned int seltags; unsigned int sellt; uint32_t tagset[2]; float mfact; int gamma_lut_changed; int nmaster; + int showbar; char ltsymbol[16]; int asleep; + Drwl *drw; + int lrpad; }; typedef struct { + struct wlr_buffer base; + size_t stride; + uint32_t data[]; +} Buffer; + +typedef struct { const char *name; float mfact; int nmaster; @@ -250,6 +276,10 @@ static void arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, int exclusive); static void arrangelayers(Monitor *m); static void axisnotify(struct wl_listener *listener, void *data); +static bool bar_accepts_input(struct wlr_scene_buffer *buffer, double *sx, double *sy); +static void buffer_destroy(struct wlr_buffer *buffer); +static bool buffer_begin_data_ptr_access(struct wlr_buffer *buffer, uint32_t flags, void **data, uint32_t *format, size_t *stride); +static void buffer_end_data_ptr_access(struct wlr_buffer *buffer); static void buttonpress(struct wl_listener *listener, void *data); static void chvt(const Arg *arg); static void checkidleinhibitor(struct wlr_surface *exclude); @@ -273,6 +303,7 @@ static void createpopup(struct wl_listener *listener, void *data); static void cursorconstrain(struct wlr_pointer_constraint_v1 *constraint); static void cursorframe(struct wl_listener *listener, void *data); static void cursorwarptohint(void); +static void defaultgaps(const Arg *arg); static void destroydecoration(struct wl_listener *listener, void *data); static void destroydragicon(struct wl_listener *listener, void *data); static void destroyidleinhibitor(struct wl_listener *listener, void *data); @@ -285,6 +316,8 @@ static void destroysessionlock(struct wl_listener *listener, void *data); static void destroysessionmgr(struct wl_listener *listener, void *data); static void destroykeyboardgroup(struct wl_listener *listener, void *data); static Monitor *dirtomon(enum wlr_direction dir); +static void drawbar(Monitor *m); +static void drawbars(void); static void focusclient(Client *c, int lift); static void focusmon(const Arg *arg); static void focusstack(const Arg *arg); @@ -293,6 +326,13 @@ static void fullscreennotify(struct wl_listener *listener, void *data); static void gpureset(struct wl_listener *listener, void *data); static void handlesig(int signo); static void incnmaster(const Arg *arg); +static void incgaps(const Arg *arg); +static void incigaps(const Arg *arg); +static void incihgaps(const Arg *arg); +static void incivgaps(const Arg *arg); +static void incogaps(const Arg *arg); +static void incohgaps(const Arg *arg); +static void incovgaps(const Arg *arg); static void inputdevice(struct wl_listener *listener, void *data); static int keybinding(uint32_t mods, xkb_keysym_t sym); static void keypress(struct wl_listener *listener, void *data); @@ -314,7 +354,6 @@ static void outputmgrapplyortest(struct wlr_output_configuration_v1 *config, int static void outputmgrtest(struct wl_listener *listener, void *data); static void pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, uint32_t time); -static void printstatus(void); static void powermgrsetmode(struct wl_listener *listener, void *data); static void quit(const Arg *arg); static void rendermon(struct wl_listener *listener, void *data); @@ -328,6 +367,7 @@ static void setcursorshape(struct wl_listener *listener, void *data); static void setfloating(Client *c, int floating); static void setfullscreen(Client *c, int fullscreen); static void setgamma(struct wl_listener *listener, void *data); +static void setgaps(int oh, int ov, int ih, int iv); static void setlayout(const Arg *arg); static void setmfact(const Arg *arg); static void setmon(Client *c, Monitor *m, uint32_t newtags); @@ -336,17 +376,22 @@ static void setsel(struct wl_listener *listener, void *data); static void setup(void); static void spawn(const Arg *arg); static void startdrag(struct wl_listener *listener, void *data); +static void incxkbrules(const Arg *arg); +static int status_in(int fd, unsigned int mask, void *data); static void tag(const Arg *arg); static void tagmon(const Arg *arg); static void tile(Monitor *m); +static void togglebar(const Arg *arg); static void togglefloating(const Arg *arg); static void togglefullscreen(const Arg *arg); +static void togglegaps(const Arg *arg); static void toggletag(const Arg *arg); static void toggleview(const Arg *arg); static void unlocksession(struct wl_listener *listener, void *data); static void unmaplayersurfacenotify(struct wl_listener *listener, void *data); static void unmapnotify(struct wl_listener *listener, void *data); static void updatemons(struct wl_listener *listener, void *data); +static void updatebar(Monitor *m); static void updatetitle(struct wl_listener *listener, void *data); static void urgent(struct wl_listener *listener, void *data); static void view(const Arg *arg); @@ -405,6 +450,7 @@ static struct wl_listener lock_listener = {.notify = locksession}; static struct wlr_seat *seat; static KeyboardGroup *kb_group; +static unsigned int kblayout = 0; static unsigned int cursor_mode; static Client *grabc; static Client initial_grabc; @@ -415,6 +461,16 @@ static struct wlr_box sgeom; static struct wl_list mons; static Monitor *selmon; +static int enablegaps = 1; /* enables gaps, used by togglegaps */ +static char stext[256]; +static struct wl_event_source *status_event_source; + +static const struct wlr_buffer_impl buffer_impl = { + .destroy = buffer_destroy, + .begin_data_ptr_access = buffer_begin_data_ptr_access, + .end_data_ptr_access = buffer_end_data_ptr_access +}; + #ifdef XWAYLAND static void activatex11(struct wl_listener *listener, void *data); static void associatex11(struct wl_listener *listener, void *data); @@ -480,6 +536,10 @@ applyrules(Client *c) } } } + if (mon) { + c->geom.x = (mon->w.width - c->geom.width) / 2 + mon->m.x; + c->geom.y = (mon->w.height - c->geom.height) / 2 + mon->m.y; + } setmon(c, mon, newtags); } @@ -555,6 +615,11 @@ arrangelayers(Monitor *m) if (!m->wlr_output->enabled) return; + if (m->showbar) { + usable_area.height -= m->b.real_height; + usable_area.y += topbar ? m->b.real_height : 0; + } + /* Arrange exclusive surfaces from top->bottom */ for (i = 3; i >= 0; i--) arrangelayer(m, &m->layers[i], &usable_area, 1); @@ -597,17 +662,89 @@ axisnotify(struct wl_listener *listener, void *data) event->delta_discrete, event->source, event->relative_direction); } +bool +bar_accepts_input(struct wlr_scene_buffer *buffer, double *sx, double *sy) +{ + return true; +} + +void +buffer_destroy(struct wlr_buffer *wlr_buffer) +{ + Buffer *buf; + buf = wl_container_of(wlr_buffer, buf, base); + free(buf); +} + +bool +buffer_begin_data_ptr_access(struct wlr_buffer *wlr_buffer, uint32_t flags, + void **data, uint32_t *format, size_t *stride) +{ + Buffer *buf; + buf = wl_container_of(wlr_buffer, buf, base); + + if (flags & WLR_BUFFER_DATA_PTR_ACCESS_WRITE) return false; + + *data = buf->data; + *stride = buf->stride; + *format = DRM_FORMAT_ARGB8888; + + return true; +} + +void +buffer_end_data_ptr_access(struct wlr_buffer *buffer) +{ +} + void buttonpress(struct wl_listener *listener, void *data) { + unsigned int i = 0, x = 0, occ = 0; + unsigned int click; struct wlr_pointer_button_event *event = data; struct wlr_keyboard *keyboard; + struct wlr_scene_node *node; + struct wlr_scene_buffer *buffer; uint32_t mods; + Arg arg = {0}; Client *c; const Button *b; wlr_idle_notifier_v1_notify_activity(idle_notifier, seat); + click = ClkRoot; + xytonode(cursor->x, cursor->y, NULL, &c, NULL, NULL, NULL); + if (c) + click = ClkClient; + + if (!c && !exclusive_focus && + (node = wlr_scene_node_at(&layers[LyrBottom]->node, cursor->x, cursor->y, NULL, NULL)) && + (buffer = wlr_scene_buffer_from_node(node)) && buffer == selmon->scene_buffer) { + cursor->x *= selmon->wlr_output->scale; + cursor->y *= selmon->wlr_output->scale; + x = selmon->m.x; + wl_list_for_each(c, &clients, link) { + if (c->mon != selmon) + continue; + occ |= c->tags == TAGMASK ? 0 : c->tags; + } + do { + if (!(occ & 1 << i || selmon->tagset[selmon->seltags] & 1 << i)) + continue; + x += TEXTW(selmon, tags[i]); + } while (cursor->x >= x && ++i < LENGTH(tags)); + if (i < LENGTH(tags)) { + click = ClkTagBar; + arg.ui = 1 << i; + } else if (cursor->x < x + TEXTW(selmon, selmon->ltsymbol)) + click = ClkLtSymbol; + else if (cursor->x > selmon->w.width - (int)TEXTW(selmon, stext)) + click = ClkStatus; + else + click = ClkTitle; + } + switch (event->state) { case WL_POINTER_BUTTON_STATE_PRESSED: cursor_mode = CurPressed; @@ -617,15 +754,14 @@ buttonpress(struct wl_listener *listener, void *data) /* Change focus if the button was _pressed_ over a client */ xytonode(cursor->x, cursor->y, NULL, &c, NULL, NULL, NULL); - if (c && (!client_is_unmanaged(c) || client_wants_focus(c))) + if (click == ClkClient && (!client_is_unmanaged(c) || client_wants_focus(c))) focusclient(c, 1); keyboard = wlr_seat_get_keyboard(seat); mods = keyboard ? wlr_keyboard_get_modifiers(keyboard) : 0; for (b = buttons; b < END(buttons); b++) { - if (CLEANMASK(mods) == CLEANMASK(b->mod) && - event->button == b->button && b->func) { - b->func(&b->arg); + if (CLEANMASK(mods) == CLEANMASK(b->mod) && event->button == b->button && click == b->click && b->func) { + b->func(click == ClkTagBar && b->arg.i == 0 ? &arg : &b->arg); return; } } @@ -699,6 +835,8 @@ cleanup(void) /* Destroy after the wayland display (when the monitors are already destroyed) to avoid destroying them with an invalid scene output. */ wlr_scene_node_destroy(&scene->tree.node); + + drwl_fini(); } void @@ -714,6 +852,8 @@ cleanupmon(struct wl_listener *listener, void *data) wlr_layer_surface_v1_destroy(l->layer_surface); } + drwl_destroy(m->drw); + wl_list_remove(&m->destroy.link); wl_list_remove(&m->frame.link); wl_list_remove(&m->link); @@ -753,7 +893,7 @@ closemon(Monitor *m) setmon(c, selmon, c->tags); } focusclient(focustop(selmon), 1); - printstatus(); + drawbars(); } void @@ -892,7 +1032,7 @@ createkeyboardgroup(void) /* Prepare an XKB keymap and assign it to the keyboard group. */ context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - if (!(keymap = xkb_keymap_new_from_names(context, &xkb_rules, + if (!(keymap = xkb_keymap_new_from_names(context, &xkb_rules[kblayout], XKB_KEYMAP_COMPILE_NO_FLAGS))) die("failed to compile keymap"); @@ -990,6 +1130,11 @@ createmon(struct wl_listener *listener, void *data) for (i = 0; i < LENGTH(m->layers); i++) wl_list_init(&m->layers[i]); + m->gappih = gappih; + m->gappiv = gappiv; + m->gappoh = gappoh; + m->gappov = gappov; + wlr_output_state_init(&state); /* Initialize monitor state using configured rules */ m->tagset[0] = m->tagset[1] = 1; @@ -1023,8 +1168,16 @@ createmon(struct wl_listener *listener, void *data) wlr_output_commit_state(wlr_output, &state); wlr_output_state_finish(&state); + if (!(m->drw = drwl_create())) + die("failed to create drwl context"); + + m->scene_buffer = wlr_scene_buffer_create(layers[LyrBottom], NULL); + m->scene_buffer->point_accepts_input = bar_accepts_input; + m->showbar = showbar; + updatebar(m); + wl_list_insert(&mons, &m->link); - printstatus(); + drawbars(); /* The xdg-protocol specifies: * @@ -1173,6 +1326,12 @@ cursorwarptohint(void) } void +defaultgaps(const Arg *arg) +{ + setgaps(gappoh, gappov, gappih, gappiv); +} + +void destroydecoration(struct wl_listener *listener, void *data) { Client *c = wl_container_of(listener, c, destroy_decoration); @@ -1338,6 +1497,91 @@ dirtomon(enum wlr_direction dir) } void +drawbar(Monitor *m) +{ + int x, w, tw = 0; + int boxs = m->drw->font->height / 9; + int boxw = m->drw->font->height / 6 + 2; + uint32_t i, occ = 0, urg = 0; + int32_t stride, size; + Client *c; + Buffer *buf; + + if (!m->showbar) + return; + + stride = drwl_stride(m->b.width); + size = stride * m->b.height; + + buf = ecalloc(1, sizeof(Buffer) + size); + buf->stride = stride; + wlr_buffer_init(&buf->base, &buffer_impl, m->b.width, m->b.height); + + drwl_prepare_drawing(m->drw, m->b.width, m->b.height, buf->data, stride); + + /* draw status first so it can be overdrawn by tags later */ + if (m == selmon) { /* status is only drawn on selected monitor */ + drwl_setscheme(m->drw, colors[SchemeNorm]); + tw = TEXTW(m, stext) - m->lrpad + 2; /* 2px right padding */ + drwl_text(m->drw, m->b.width - tw, 0, tw, m->b.height, 0, stext, 0); + } + + wl_list_for_each(c, &clients, link) { + if (c->mon != m) + continue; + occ |= c->tags == TAGMASK ? 0 : c->tags; + if (c->isurgent) + urg |= c->tags; + } + x = 0; + c = focustop(m); + for (i = 0; i < LENGTH(tags); i++) { + if(!(occ & 1 << i || m->tagset[m->seltags] & 1 << i)) + continue; + w = TEXTW(m, tags[i]); + drwl_setscheme(m->drw, colors[m->tagset[m->seltags] & 1 << i ? SchemeSel : SchemeNorm]); + drwl_text(m->drw, x, 0, w, m->b.height, m->lrpad / 2, tags[i], urg & 1 << i); + if (occ & 1 << i) + drwl_rect(m->drw, x + boxs, boxs, boxw, boxw, + m == selmon && c && c->tags & 1 << i, + urg & 1 << i); + x += w; + } + w = TEXTW(m, m->ltsymbol); + drwl_setscheme(m->drw, colors[SchemeNorm]); + x = drwl_text(m->drw, x, 0, w, m->b.height, m->lrpad / 2, m->ltsymbol, 0); + + if ((w = m->b.width - tw - x) > m->b.height) { + if (c) { + drwl_setscheme(m->drw, colors[m == selmon ? SchemeSel : SchemeNorm]); + drwl_text(m->drw, x, 0, w, m->b.height, m->lrpad / 2, client_get_title(c), 0); + if (c && c->isfloating) + drwl_rect(m->drw, x + boxs, boxs, boxw, boxw, 0, 0); + } else { + drwl_setscheme(m->drw, colors[SchemeNorm]); + drwl_rect(m->drw, x, 0, w, m->b.height, 1, 1); + } + } + + drwl_finish_drawing(m->drw); + wlr_scene_buffer_set_dest_size(m->scene_buffer, + m->b.real_width, m->b.real_height); + wlr_scene_node_set_position(&m->scene_buffer->node, m->m.x, + m->m.y + (topbar ? 0 : m->m.height - m->b.real_height)); + wlr_scene_buffer_set_buffer(m->scene_buffer, &buf->base); + wlr_buffer_drop(&buf->base); +} + +void +drawbars(void) +{ + Monitor *m = NULL; + + wl_list_for_each(m, &mons, link) + drawbar(m); +} + +void focusclient(Client *c, int lift) { struct wlr_surface *old = seat->keyboard_state.focused_surface; @@ -1372,13 +1616,13 @@ focusclient(Client *c, int lift) /* Don't change border color if there is an exclusive focus or we are * handling a drag operation */ if (!exclusive_focus && !seat->drag) - client_set_border_color(c, focuscolor); + client_set_border_color(c, (float[])COLOR(colors[SchemeSel][ColBorder])); } /* Deactivate old client if focus is changing */ if (old && (!c || client_surface(c) != old)) { /* If an overlay is focused, don't focus or activate the client, - * but only update its position in fstack to render its border with focuscolor + * but only update its position in fstack to render its border with its color * and focus it after the overlay is closed. */ if (old_client_type == LayerShell && wlr_scene_node_coords( &old_l->scene->node, &unused_lx, &unused_ly) @@ -1389,12 +1633,11 @@ focusclient(Client *c, int lift) /* Don't deactivate old client if the new one wants focus, as this causes issues with winecfg * and probably other clients */ } else if (old_c && !client_is_unmanaged(old_c) && (!c || !client_wants_focus(c))) { - client_set_border_color(old_c, bordercolor); - + client_set_border_color(old_c, (float[])COLOR(colors[SchemeNorm][ColBorder])); client_activate_surface(old, 0); } } - printstatus(); + drawbars(); if (!c) { /* With no client, all we have left is to clear focus */ @@ -1526,6 +1769,83 @@ incnmaster(const Arg *arg) } void +incgaps(const Arg *arg) +{ + setgaps( + selmon->gappoh + arg->i, + selmon->gappov + arg->i, + selmon->gappih + arg->i, + selmon->gappiv + arg->i + ); +} + +void +incigaps(const Arg *arg) +{ + setgaps( + selmon->gappoh, + selmon->gappov, + selmon->gappih + arg->i, + selmon->gappiv + arg->i + ); +} + +void +incihgaps(const Arg *arg) +{ + setgaps( + selmon->gappoh, + selmon->gappov, + selmon->gappih + arg->i, + selmon->gappiv + ); +} + +void +incivgaps(const Arg *arg) +{ + setgaps( + selmon->gappoh, + selmon->gappov, + selmon->gappih, + selmon->gappiv + arg->i + ); +} + +void +incogaps(const Arg *arg) +{ + setgaps( + selmon->gappoh + arg->i, + selmon->gappov + arg->i, + selmon->gappih, + selmon->gappiv + ); +} + +void +incohgaps(const Arg *arg) +{ + setgaps( + selmon->gappoh + arg->i, + selmon->gappov, + selmon->gappih, + selmon->gappiv + ); +} + +void +incovgaps(const Arg *arg) +{ + setgaps( + selmon->gappoh, + selmon->gappov + arg->i, + selmon->gappih, + selmon->gappiv + ); +} + +void inputdevice(struct wl_listener *listener, void *data) { /* This event is raised by the backend when a new input device becomes @@ -1716,7 +2036,7 @@ mapnotify(struct wl_listener *listener, void *data) for (i = 0; i < 4; i++) { c->border[i] = wlr_scene_rect_create(c->scene, 0, 0, - c->isurgent ? urgentcolor : bordercolor); + (float[])COLOR(colors[c->isurgent ? SchemeUrg : SchemeNorm][ColBorder])); c->border[i]->node.data = c; } @@ -1735,11 +2055,15 @@ mapnotify(struct wl_listener *listener, void *data) * try to apply rules for them */ if ((p = client_get_parent(c))) { c->isfloating = 1; + if (p->mon) { + c->geom.x = (p->mon->w.width - c->geom.width) / 2 + p->mon->m.x; + c->geom.y = (p->mon->w.height - c->geom.height) / 2 + p->mon->m.y; + } setmon(c, p->mon, p->tags); } else { applyrules(c); } - printstatus(); + drawbars(); unset_fullscreen: m = c->mon ? c->mon : xytomon(c->geom.x, c->geom.y); @@ -1776,8 +2100,12 @@ monocle(Monitor *m) wl_list_for_each(c, &clients, link) { if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) continue; - resize(c, m->w, 0); n++; + if (!monoclegaps) + resize(c, m->w, 0); + else + resize(c, (struct wlr_box){.x = m->w.x + gappoh, .y = m->w.y + gappov, + .width = m->w.width - 2 * gappoh, .height = m->w.height - 2 * gappov}, 0); } if (n) snprintf(m->ltsymbol, LENGTH(m->ltsymbol), "[%d]", n); @@ -2118,46 +2446,6 @@ pointerfocus(Client *c, struct wlr_surface *surface, double sx, double sy, wlr_seat_pointer_notify_motion(seat, time, sx, sy); } -void -printstatus(void) -{ - Monitor *m = NULL; - Client *c; - uint32_t occ, urg, sel; - const char *appid, *title; - - wl_list_for_each(m, &mons, link) { - occ = urg = 0; - wl_list_for_each(c, &clients, link) { - if (c->mon != m) - continue; - occ |= c->tags; - if (c->isurgent) - urg |= c->tags; - } - if ((c = focustop(m))) { - title = client_get_title(c); - appid = client_get_appid(c); - printf("%s title %s\n", m->wlr_output->name, title ? title : broken); - printf("%s appid %s\n", m->wlr_output->name, appid ? appid : broken); - printf("%s fullscreen %d\n", m->wlr_output->name, c->isfullscreen); - printf("%s floating %d\n", m->wlr_output->name, c->isfloating); - sel = c->tags; - } else { - printf("%s title \n", m->wlr_output->name); - printf("%s appid \n", m->wlr_output->name); - printf("%s fullscreen \n", m->wlr_output->name); - printf("%s floating \n", m->wlr_output->name); - sel = 0; - } - - printf("%s selmon %u\n", m->wlr_output->name, m == selmon); - printf("%s tags %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32"\n", - m->wlr_output->name, occ, m->tagset[m->seltags], sel, urg); - printf("%s layout %s\n", m->wlr_output->name, m->ltsymbol); - } - fflush(stdout); -} void powermgrsetmode(struct wl_listener *listener, void *data) @@ -2312,22 +2600,14 @@ run(char *startup_cmd) /* Now that the socket exists and the backend is started, run the startup command */ if (startup_cmd) { - int piperw[2]; - if (pipe(piperw) < 0) - die("startup: pipe:"); if ((child_pid = fork()) < 0) die("startup: fork:"); if (child_pid == 0) { + close(STDIN_FILENO); setsid(); - dup2(piperw[0], STDIN_FILENO); - close(piperw[0]); - close(piperw[1]); execl("/bin/sh", "/bin/sh", "-c", startup_cmd, NULL); die("startup: execl:"); } - dup2(piperw[1], STDOUT_FILENO); - close(piperw[1]); - close(piperw[0]); } /* Mark stdout as non-blocking to avoid people who does not close stdin @@ -2335,7 +2615,7 @@ run(char *startup_cmd) if (fd_set_nonblock(STDOUT_FILENO) < 0) close(STDOUT_FILENO); - printstatus(); + drawbars(); /* At this point the outputs are initialized, choose initial selmon based on * cursor position, and set default cursor image */ @@ -2401,7 +2681,7 @@ setfloating(Client *c, int floating) (p && p->isfullscreen) ? LyrFS : c->isfloating ? LyrFloat : LyrTile]); arrange(c->mon); - printstatus(); + drawbars(); } void @@ -2424,7 +2704,7 @@ setfullscreen(Client *c, int fullscreen) resize(c, c->prev, 0); } arrange(c->mon); - printstatus(); + drawbars(); } void @@ -2439,6 +2719,16 @@ setgamma(struct wl_listener *listener, void *data) } void +setgaps(int oh, int ov, int ih, int iv) +{ + selmon->gappoh = MAX(oh, 0); + selmon->gappov = MAX(ov, 0); + selmon->gappih = MAX(ih, 0); + selmon->gappiv = MAX(iv, 0); + arrange(selmon); +} + +void setlayout(const Arg *arg) { if (!selmon) @@ -2449,7 +2739,7 @@ setlayout(const Arg *arg) selmon->lt[selmon->sellt] = (Layout *)arg->v; strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, LENGTH(selmon->ltsymbol)); arrange(selmon); - printstatus(); + drawbar(selmon); } /* arg > 1.0 will set mfact absolutely */ @@ -2522,6 +2812,7 @@ setup(void) for (i = 0; i < (int)LENGTH(sig); i++) sigaction(sig[i], &sa, NULL); + wlr_log_init(log_level, NULL); /* The Wayland display is managed by libwayland. It handles accepting @@ -2711,6 +3002,11 @@ setup(void) LISTEN_STATIC(&output_mgr->events.apply, outputmgrapply); LISTEN_STATIC(&output_mgr->events.test, outputmgrtest); + drwl_init(); + + status_event_source = wl_event_loop_add_fd(wl_display_get_event_loop(dpy), + STDIN_FILENO, WL_EVENT_READABLE, status_in, NULL); + /* Make sure XWayland clients don't connect to the parent X server, * e.g when running in the x11 backend or the wayland backend and the * compositor has Xwayland support */ @@ -2735,6 +3031,7 @@ void spawn(const Arg *arg) { if (fork() == 0) { + close(STDIN_FILENO); dup2(STDERR_FILENO, STDOUT_FILENO); setsid(); execvp(((char **)arg->v)[0], (char **)arg->v); @@ -2753,6 +3050,49 @@ startdrag(struct wl_listener *listener, void *data) LISTEN_STATIC(&drag->icon->events.destroy, destroydragicon); } +int +status_in(int fd, unsigned int mask, void *data) +{ + char status[1024]; + ssize_t n; + + if (mask & WL_EVENT_ERROR) + die("status in event error"); + if (mask & WL_EVENT_HANGUP) + wl_event_source_remove(status_event_source); + + n = read(fd, status, sizeof(status) - 1); + if (n < 0 && errno != EWOULDBLOCK) + die("read:"); + + status[n] = '\0'; + status[strcspn(status, "\n")] = '\0'; + + strncpy(stext, status, sizeof(stext)); + drawbars(); + + return 0; +} + +void +incxkbrules(const Arg *arg) +{ + KeyboardGroup *group; + struct wlr_keyboard_group *wlr_group; + const struct xkb_rule_names newrule = xkb_rules[(kblayout + 1) % LENGTH(xkb_rules)]; + + wl_list_for_each(group, &kb_group->link, link) { + wl_list_for_each(wlr_group, &group->wlr_group->keys, keys) { + struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + struct xkb_keymap *keymap = xkb_map_new_from_names(context, &newrule, + XKB_KEYMAP_COMPILE_NO_FLAGS); + wlr_keyboard_set_keymap(&wlr_group->keyboard, keymap); + xkb_keymap_unref(keymap); + xkb_context_unref(context); + } + } +} + void tag(const Arg *arg) { @@ -2763,7 +3103,7 @@ tag(const Arg *arg) sel->tags = arg->ui & TAGMASK; focusclient(focustop(selmon), 1); arrange(selmon); - printstatus(); + drawbars(); } void @@ -2777,7 +3117,7 @@ tagmon(const Arg *arg) void tile(Monitor *m) { - unsigned int mw, my, ty; + unsigned int mw, my, ty, h, r, oe = enablegaps, ie = enablegaps; int i, n = 0; Client *c; @@ -2787,28 +3127,45 @@ tile(Monitor *m) if (n == 0) return; + if (smartgaps == n) { + oe = 0; // outer gaps disabled + } + if (n > m->nmaster) - mw = m->nmaster ? (int)roundf(m->w.width * m->mfact) : 0; + mw = m->nmaster ? (int)roundf((m->w.width + m->gappiv*ie) * m->mfact) : 0; else - mw = m->w.width; - i = my = ty = 0; + mw = m->w.width - 2*m->gappov*oe + m->gappiv*ie; + i = 0; + my = ty = m->gappoh*oe; wl_list_for_each(c, &clients, link) { if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) continue; if (i < m->nmaster) { - resize(c, (struct wlr_box){.x = m->w.x, .y = m->w.y + my, .width = mw, - .height = (m->w.height - my) / (MIN(n, m->nmaster) - i)}, 0); - my += c->geom.height; + r = MIN(n, m->nmaster) - i; + h = (m->w.height - my - m->gappoh*oe - m->gappih*ie * (r - 1)) / r; + resize(c, (struct wlr_box){.x = m->w.x + m->gappov*oe, .y = m->w.y + my, + .width = mw - m->gappiv*ie, .height = h}, 0); + my += c->geom.height + m->gappih*ie; } else { - resize(c, (struct wlr_box){.x = m->w.x + mw, .y = m->w.y + ty, - .width = m->w.width - mw, .height = (m->w.height - ty) / (n - i)}, 0); - ty += c->geom.height; + r = n - i; + h = (m->w.height - ty - m->gappoh*oe - m->gappih*ie * (r - 1)) / r; + resize(c, (struct wlr_box){.x = m->w.x + mw + m->gappov*oe, .y = m->w.y + ty, + .width = m->w.width - mw - 2*m->gappov*oe, .height = h}, 0); + ty += c->geom.height + m->gappih*ie; } i++; } } void +togglebar(const Arg *arg) +{ + selmon->showbar = !selmon->showbar; + wlr_scene_node_set_enabled(&selmon->scene_buffer->node, selmon->showbar); + arrangelayers(selmon); +} + +void togglefloating(const Arg *arg) { Client *sel = focustop(selmon); @@ -2826,6 +3183,13 @@ togglefullscreen(const Arg *arg) } void +togglegaps(const Arg *arg) +{ + enablegaps = !enablegaps; + arrange(selmon); +} + +void toggletag(const Arg *arg) { uint32_t newtags; @@ -2836,7 +3200,7 @@ toggletag(const Arg *arg) sel->tags = newtags; focusclient(focustop(selmon), 1); arrange(selmon); - printstatus(); + drawbars(); } void @@ -2849,7 +3213,7 @@ toggleview(const Arg *arg) selmon->tagset[selmon->seltags] = newtagset; focusclient(focustop(selmon), 1); arrange(selmon); - printstatus(); + drawbars(); } void @@ -2897,7 +3261,7 @@ unmapnotify(struct wl_listener *listener, void *data) } wlr_scene_node_destroy(&c->scene->node); - printstatus(); + drawbars(); motionnotify(0, NULL, 0, 0, 0, 0); } @@ -2997,6 +3361,14 @@ updatemons(struct wl_listener *listener, void *data) } } + /* Update bar */ + if (stext[0] == '\0') + strncpy(stext, "dwl-"VERSION, sizeof(stext)); + wl_list_for_each(m, &mons, link) { + updatebar(m); + drawbar(m); + } + /* FIXME: figure out why the cursor image is at 0,0 after turning all * the monitors on. * Move the cursor image where it used to be. It does not generate a @@ -3008,11 +3380,35 @@ updatemons(struct wl_listener *listener, void *data) } void +updatebar(Monitor *m) +{ + int rw, rh; + char fontattrs[12]; + + wlr_output_transformed_resolution(m->wlr_output, &rw, &rh); + m->b.width = rw; + m->b.real_width = (int)((float)m->b.width / m->wlr_output->scale); + + if (m->b.scale == m->wlr_output->scale && m->drw) + return; + + drwl_destroy_font(m->drw->font); + snprintf(fontattrs, sizeof(fontattrs), "dpi=%.2f", 96. * m->wlr_output->scale); + if (!(drwl_load_font(m->drw, LENGTH(fonts), fonts, fontattrs))) + die("Could not load font"); + + m->b.scale = m->wlr_output->scale; + m->lrpad = m->drw->font->height; + m->b.height = m->drw->font->height + 2; + m->b.real_height = (int)((float)m->b.height / m->wlr_output->scale); +} + +void updatetitle(struct wl_listener *listener, void *data) { Client *c = wl_container_of(listener, c, set_title); if (c == focustop(c->mon)) - printstatus(); + drawbars(); } void @@ -3025,10 +3421,10 @@ urgent(struct wl_listener *listener, void *data) return; c->isurgent = 1; - printstatus(); + drawbars(); if (client_surface(c)->mapped) - client_set_border_color(c, urgentcolor); + client_set_border_color(c, (float[])COLOR(colors[SchemeUrg][ColBorder])); } void @@ -3041,7 +3437,7 @@ view(const Arg *arg) selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; focusclient(focustop(selmon), 1); arrange(selmon); - printstatus(); + drawbars(); } void @@ -3082,6 +3478,7 @@ xytonode(double x, double y, struct wlr_surface **psurface, { struct wlr_scene_node *node, *pnode; struct wlr_surface *surface = NULL; + struct wlr_scene_surface *scene_surface = NULL; Client *c = NULL; LayerSurface *l = NULL; int layer; @@ -3090,9 +3487,12 @@ xytonode(double x, double y, struct wlr_surface **psurface, if (!(node = wlr_scene_node_at(&layers[layer]->node, x, y, nx, ny))) continue; - if (node->type == WLR_SCENE_NODE_BUFFER) - surface = wlr_scene_surface_try_from_buffer( - wlr_scene_buffer_from_node(node))->surface; + if (node->type == WLR_SCENE_NODE_BUFFER) { + scene_surface = wlr_scene_surface_try_from_buffer( + wlr_scene_buffer_from_node(node)); + if (!scene_surface) continue; + surface = scene_surface->surface; + } /* Walk the tree to find a node that knows the client */ for (pnode = node; pnode && !c; pnode = &pnode->parent->node) c = pnode->data; @@ -3231,10 +3631,10 @@ sethints(struct wl_listener *listener, void *data) return; c->isurgent = xcb_icccm_wm_hints_get_urgency(c->surface.xwayland->hints); - printstatus(); + drawbars(); if (c->isurgent && surface && surface->mapped) - client_set_border_color(c, urgentcolor); + client_set_border_color(c, (float[])COLOR(colors[SchemeUrg][ColBorder])); } void |