diff options
author | ZachIR <zachir@librem.one> | 2022-08-31 17:51:22 -0500 |
---|---|---|
committer | ZachIR <zachir@librem.one> | 2022-08-31 17:51:22 -0500 |
commit | e45f978faab039be7bfab3865e1e2a5f0794f4d1 (patch) | |
tree | b5d5521c752dd65ca5c5eb2555e9da377b5894de | |
parent | 55ee442c3cf8833f93626ab1a9a59c9e3f758778 (diff) |
patched files
-rw-r--r-- | config.def.h | 48 | ||||
-rw-r--r-- | config.mk | 6 | ||||
-rw-r--r-- | dwl.c | 249 |
3 files changed, 281 insertions, 22 deletions
diff --git a/config.def.h b/config.def.h index 29c6dbf..639f92c 100644 --- a/config.def.h +++ b/config.def.h @@ -2,21 +2,35 @@ static const int sloppyfocus = 1; /* focus follows mouse */ static const unsigned int borderpx = 1; /* border pixel of windows */ static const int lockfullscreen = 1; /* 1 will force focus on the fullscreen window */ +static const int smartborders = 1; static const float rootcolor[] = {0.3, 0.3, 0.3, 1.0}; static const float bordercolor[] = {0.5, 0.5, 0.5, 1.0}; static const float focuscolor[] = {1.0, 0.0, 0.0, 1.0}; /* To conform the xdg-protocol, set the alpha to zero to restore the old behavior */ static const float fullscreen_bg[] = {0.1, 0.1, 0.1, 1.0}; +/* cursor warping */ +static const bool cursor_warp = true; + /* tagging */ static const char *tags[] = { "1", "2", "3", "4", "5", "6", "7", "8", "9" }; static const Rule rules[] = { - /* app_id title tags mask isfloating monitor */ + /* app_id title tags mask isfloating isterm noswallow monitor scratchkey */ /* examples: - { "Gimp", NULL, 0, 1, -1 }, ++ { "Gimp", NULL, 0, 1, 0, 1, -1, 0 }, + { "firefox", NULL, 1 << 8, 0, 0, 1, -1, 0 }, */ - { "firefox", NULL, 1 << 8, 0, -1 }, + { NULL, "sphtop", 0, 1, 0, 1, -1, 'z' }, + { NULL, "spterm", 0, 1, 0, 1, -1, 'x' }, + { NULL, "sppmxr", 0, 1, 0, 1, -1, 'c' }, + { NULL, "spblue", 0, 1, 0, 1, -1, 'v' }, + { NULL, "spncmp", 0, 1, 0, 1, -1, 'b' }, + { NULL, "spmutt", 0, 1, 0, 1, -1, 'a' }, + { NULL, "spprof", 0, 1, 0, 1, -1, 's' }, + { NULL, "spircc", 0, 1, 0, 1, -1, 'd' }, + { NULL, "sptodo", 0, 1, 0, 1, -1, 'f' }, + { NULL, "sptrem", 0, 1, 0, 1, -1, 'g' }, }; /* layout(s) */ @@ -86,6 +100,12 @@ LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE static const enum libinput_config_accel_profile accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE; static const double accel_speed = 0.0; +/* Autostart */ +static const char *const autostart[] = { + "sh", "-c", "swaybg --image /xap/local/background", NULL, + NULL /* terminate */ +}; + /* If you want to use the windows key change this to WLR_MODIFIER_LOGO */ #define MODKEY WLR_MODIFIER_ALT #define TAGKEYS(KEY,SKEY,TAG) \ @@ -101,11 +121,33 @@ static const double accel_speed = 0.0; static const char *termcmd[] = { "alacritty", NULL }; static const char *menucmd[] = { "bemenu-run", NULL }; +/* named scratchpads - First arg only serves to match against key in rules*/ +static const char *scratchpadcmd0[] = { "z", "alacritty", "-t", "sphtop", "htop", NULL }; +static const char *scratchpadcmd1[] = { "x", "alacritty", "-t", "spterm", "zsh", NULL }; +static const char *scratchpadcmd2[] = { "c", "alacritty", "-t", "sppmxr", "pulsemixer", NULL }; +static const char *scratchpadcmd3[] = { "v", "alacritty", "-t", "spblue", "bluetoothctl", NULL }; +static const char *scratchpadcmd4[] = { "b", "alacritty", "-t", "spncmp", "ncmpcpp", NULL }; +static const char *scratchpadcmd5[] = { "a", "alacritty", "-t", "spmutt", "neomutt", NULL }; +static const char *scratchpadcmd6[] = { "s", "alacritty", "-t", "spprof", "profanity", NULL }; +static const char *scratchpadcmd7[] = { "d", "alacritty", "-t", "spircc", "irssi", NULL }; +static const char *scratchpadcmd8[] = { "f", "alacritty", "-t", "sptodo", "todo", NULL }; +static const char *scratchpadcmd9[] = { "g", "alacritty", "-t", "sptrem", "tremc", NULL }; + static const Key keys[] = { /* Note that Shift changes certain key codes: c -> C, 2 -> at, etc. */ /* modifier key function argument */ { MODKEY, XKB_KEY_p, spawn, {.v = menucmd} }, { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return, spawn, {.v = termcmd} }, + { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_z, togglescratch, {.v = scratchpadcmd0 } }, + { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_x, togglescratch, {.v = scratchpadcmd1 } }, + { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_c, togglescratch, {.v = scratchpadcmd2 } }, + { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_v, togglescratch, {.v = scratchpadcmd3 } }, + { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_b, togglescratch, {.v = scratchpadcmd4 } }, + { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_a, togglescratch, {.v = scratchpadcmd5 } }, + { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_s, togglescratch, {.v = scratchpadcmd6 } }, + { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_d, togglescratch, {.v = scratchpadcmd7 } }, + { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_f, togglescratch, {.v = scratchpadcmd8 } }, + { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_g, togglescratch, {.v = scratchpadcmd9 } }, { MODKEY, XKB_KEY_j, focusstack, {.i = +1} }, { MODKEY, XKB_KEY_k, focusstack, {.i = -1} }, { MODKEY, XKB_KEY_i, incnmaster, {.i = +1} }, @@ -6,10 +6,10 @@ PREFIX = /usr/local MANDIR = $(PREFIX)/share/man # Compile flags that can be used -#CFLAGS = -pedantic -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare -Wno-unused-function -Wno-unused-variable -Wno-unused-result -Wdeclaration-after-statement +CFLAGS = -pedantic -Wall -Wextra -Werror -Wno-unused-parameter -Wno-sign-compare -Wno-unused-function -Wno-unused-variable -Wno-unused-result -Wdeclaration-after-statement XWAYLAND = XLIBS = # Uncomment to build XWayland support -#XWAYLAND = -DXWAYLAND -#XLIBS = xcb +XWAYLAND = -DXWAYLAND +XLIBS = xcb @@ -90,7 +90,8 @@ typedef struct { } Button; typedef struct Monitor Monitor; -typedef struct { +typedef struct Client Client; +struct Client{ /* Must keep these three elements in this order */ unsigned int type; /* XDGShell or X11* */ struct wlr_box geom; /* layout-relative, includes border */ @@ -119,9 +120,12 @@ typedef struct { #endif unsigned int bw; unsigned int tags; - int isfloating, isurgent, isfullscreen; + int isfloating, isurgent, isfullscreen, isterm, noswallow; uint32_t resize; /* configure serial of a pending resize */ -} Client; + pid_t pid; + Client *swallowing, *swallowedby; + char scratchkey; +}; typedef struct { uint32_t singular_anchor; @@ -200,7 +204,10 @@ typedef struct { const char *title; unsigned int tags; int isfloating; + int isterm; + int noswallow; int monitor; + const char scratchkey; } Rule; /* function declarations */ @@ -213,6 +220,7 @@ static void arrange(Monitor *m); static void arrangelayer(Monitor *m, struct wl_list *list, struct wlr_box *usable_area, int exclusive); static void arrangelayers(Monitor *m); +static void autostartexec(void); static void axisnotify(struct wl_listener *listener, void *data); static void buttonpress(struct wl_listener *listener, void *data); static void chvt(const Arg *arg); @@ -263,7 +271,7 @@ static void quit(const Arg *arg); static void quitsignal(int signo); static void rendermon(struct wl_listener *listener, void *data); static void requeststartdrag(struct wl_listener *listener, void *data); -static void resize(Client *c, struct wlr_box geo, int interact); +static void resize(Client *c, struct wlr_box geo, int interact, int draw_borders); static void run(char *startup_cmd); static Client *selclient(void); static void setcursor(struct wl_listener *listener, void *data); @@ -277,12 +285,14 @@ static void setsel(struct wl_listener *listener, void *data); static void setup(void); static void sigchld(int unused); static void spawn(const Arg *arg); +static void spawnscratch(const Arg *arg); static void startdrag(struct wl_listener *listener, void *data); static void tag(const Arg *arg); static void tagmon(const Arg *arg); static void tile(Monitor *m); static void togglefloating(const Arg *arg); static void togglefullscreen(const Arg *arg); +static void togglescratch(const Arg *arg); static void toggletag(const Arg *arg); static void toggleview(const Arg *arg); static void unmaplayersurfacenotify(struct wl_listener *listener, void *data); @@ -292,10 +302,15 @@ static void updatetitle(struct wl_listener *listener, void *data); static void urgent(struct wl_listener *listener, void *data); static void view(const Arg *arg); static void virtualkeyboard(struct wl_listener *listener, void *data); +static void warpcursor(const Client *c); static Monitor *xytomon(double x, double y); static struct wlr_scene_node *xytonode(double x, double y, struct wlr_surface **psurface, Client **pc, LayerSurface **pl, double *nx, double *ny); static void zoom(const Arg *arg); +static pid_t getparentprocess(pid_t p); +static int isdescprocess(pid_t p, pid_t c); +static Client *termforwin(Client *w); +static void swallow(Client *c, Client *w); /* variables */ static const char broken[] = "broken"; @@ -372,7 +387,9 @@ static Atom netatom[NetLast]; #endif /* configuration, allows nested code to access above variables */ +#include "push.h" #include "config.h" +#include "push.c" /* attempt to encapsulate suck into one file */ #include "client.h" @@ -380,6 +397,9 @@ static Atom netatom[NetLast]; /* compile-time check if all tags fit into an unsigned int bit array. */ struct NumTags { char limitexceeded[LENGTH(tags) > 31 ? -1 : 1]; }; +static pid_t *autostart_pids; +static size_t autostart_len; + /* function implementations */ void applybounds(Client *c, struct wlr_box *bbox) @@ -407,6 +427,29 @@ applybounds(Client *c, struct wlr_box *bbox) } void +autostartexec(void) { + const char *const *p; + size_t i = 0; + + /* count entries */ + for (p = autostart; *p; autostart_len++, p++) + while (*++p); + + autostart_pids = calloc(autostart_len, sizeof(pid_t)); + for (p = autostart; *p; i++, p++) { + if ((autostart_pids[i] = fork()) == 0) { + setsid(); + execvp(*p, (char *const *)p); + fprintf(stderr, "dwl: execvp %s\n", *p); + perror(" failed"); + _exit(EXIT_FAILURE); + } + /* skip arguments */ + while (*++p); + } +} + +void applyexclusive(struct wlr_box *usable_area, uint32_t anchor, int32_t exclusive, int32_t margin_top, int32_t margin_right, @@ -471,6 +514,7 @@ applyrules(Client *c) Monitor *mon = selmon, *m; c->isfloating = client_is_float_type(c); + c->scratchkey = 0; if (!(appid = client_get_appid(c))) appid = broken; if (!(title = client_get_title(c))) @@ -480,6 +524,9 @@ applyrules(Client *c) if ((!r->title || strstr(title, r->title)) && (!r->id || strstr(appid, r->id))) { c->isfloating = r->isfloating; + c->scratchkey = r->scratchkey; + c->isterm = r->isterm; + c->noswallow = r->noswallow; newtags |= r->tags; i = 0; wl_list_for_each(m, &mons, link) @@ -487,6 +534,8 @@ applyrules(Client *c) mon = m; } } + 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; wlr_scene_node_reparent(c->scene, layers[c->isfloating ? LyrFloat : LyrTile]); setmon(c, mon, newtags); } @@ -501,6 +550,9 @@ arrange(Monitor *m) if (m->lt[m->sellt]->arrange) m->lt[m->sellt]->arrange(m); motionnotify(0); + c = selclient(); + if (cursor_warp && c) + warpcursor(c); } void @@ -771,7 +823,7 @@ closemon(Monitor *m) wl_list_for_each(c, &clients, link) { if (c->isfloating && c->geom.x > m->m.width) resize(c, (struct wlr_box){.x = c->geom.x - m->w.width, .y = c->geom.y, - .width = c->geom.width, .height = c->geom.height}, 0); + .width = c->geom.width, .height = c->geom.height}, 0, 1); if (c->mon == m) setmon(c, selmon, c->tags); } @@ -992,6 +1044,8 @@ createnotify(struct wl_listener *listener, void *data) c->surface.xdg = xdg_surface; c->bw = borderpx; + wl_client_get_credentials(c->surface.xdg->client->client, &c->pid, NULL, NULL); + LISTEN(&xdg_surface->events.map, &c->map, mapnotify); LISTEN(&xdg_surface->events.unmap, &c->unmap, unmapnotify); LISTEN(&xdg_surface->events.destroy, &c->destroy, destroynotify); @@ -1131,6 +1185,10 @@ focusclient(Client *c, int lift) if (exclusive_focus) return; + /* Warp cursor to center of client if it is outside */ + if (cursor_warp && c) + warpcursor(c); + /* Raise client in stacking order if requested */ if (c && lift) wlr_scene_node_raise_to_top(c->scene); @@ -1253,6 +1311,61 @@ fullscreennotify(struct wl_listener *listener, void *data) setfullscreen(c, fullscreen); } +pid_t +getparentprocess(pid_t p) +{ + unsigned int v = 0; + + FILE *f; + char buf[256]; + snprintf(buf, sizeof(buf) - 1, "/proc/%u/stat", (unsigned)p); + + if (!(f = fopen(buf, "r"))) + return 0; + + fscanf(f, "%*u %*s %*c %u", &v); + fclose(f); + + return (pid_t)v; +} + +int +isdescprocess(pid_t p, pid_t c) +{ + while (p != c && c != 0) + c = getparentprocess(c); + + return (int)c; +} + +Client * +termforwin(Client *w) +{ + Client *c; + + if (!w->pid || w->isterm || w->noswallow) + return NULL; + + wl_list_for_each(c, &clients, link) + if (c->isterm && !c->swallowing && c->pid && isdescprocess(c->pid, w->pid)) + return c; + + return NULL; +} + +void +swallow(Client *c, Client *w) { + c->bw = w->bw; + c->isfloating = w->isfloating; + c->isurgent = w->isurgent; + c->isfullscreen = w->isfullscreen; + resize(c, w->geom, 0, 0); + wl_list_insert(&w->link, &c->link); + wl_list_insert(&w->flink, &c->flink); + wlr_scene_node_set_enabled(w->scene, 0); + wlr_scene_node_set_enabled(c->scene, 1); +} + void incnmaster(const Arg *arg) { @@ -1442,6 +1555,19 @@ mapnotify(struct wl_listener *listener, void *data) setfullscreen(c, 1); c->mon->un_map = 1; + if (!c->noswallow) { + Client *p = termforwin(c); + if (p) { + c->swallowedby = p; + p->swallowing = c; + wl_list_remove(&c->link); + wl_list_remove(&c->flink); + swallow(c,p); + wl_list_remove(&p->link); + wl_list_remove(&p->flink); + } + arrange(c->mon); + } } void @@ -1452,7 +1578,7 @@ monocle(Monitor *m) wl_list_for_each(c, &clients, link) { if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) continue; - resize(c, m->w, 0); + resize(c, m->w, 0, !smartborders); } focusclient(focustop(m), 1); } @@ -1495,11 +1621,11 @@ motionnotify(uint32_t time) if (cursor_mode == CurMove) { /* Move the grabbed client to the new position. */ resize(grabc, (struct wlr_box){.x = cursor->x - grabcx, .y = cursor->y - grabcy, - .width = grabc->geom.width, .height = grabc->geom.height}, 1); + .width = grabc->geom.width, .height = grabc->geom.height}, 1, 1); return; } else if (cursor_mode == CurResize) { resize(grabc, (struct wlr_box){.x = grabc->geom.x, .y = grabc->geom.y, - .width = cursor->x - grabc->geom.x, .height = cursor->y - grabc->geom.y}, 1); + .width = cursor->x - grabc->geom.x, .height = cursor->y - grabc->geom.y}, 1, 1); return; } @@ -1717,6 +1843,16 @@ printstatus(void) void quit(const Arg *arg) { + size_t i; + + /* kill child processes */ + for (i = 0; i < autostart_len; i++) { + if (0 < autostart_pids[i]) { + kill(autostart_pids[i], SIGTERM); + waitpid(autostart_pids[i], NULL, 0); + } + } + wl_display_terminate(dpy); } @@ -1772,10 +1908,11 @@ requeststartdrag(struct wl_listener *listener, void *data) } void -resize(Client *c, struct wlr_box geo, int interact) +resize(Client *c, struct wlr_box geo, int interact, int draw_borders) { struct wlr_box *bbox = interact ? &sgeom : &c->mon->w; c->geom = geo; + c->bw = draw_borders ? borderpx : 0; applybounds(c, bbox); /* Update scene-graph, including borders */ @@ -1804,6 +1941,7 @@ run(char *startup_cmd) setenv("WAYLAND_DISPLAY", socket, 1); /* Now that the socket exists, run the startup command */ + autostartexec(); if (startup_cmd) { int piperw[2]; if (pipe(piperw) < 0) @@ -1881,6 +2019,8 @@ setfloating(Client *c, int floating) { c->isfloating = floating; wlr_scene_node_reparent(c->scene, layers[c->isfloating ? LyrFloat : LyrTile]); + if (c->isfloating && !c->bw) + resize(c, c->mon->m, 0, 1); arrange(c->mon); printstatus(); } @@ -1894,7 +2034,7 @@ setfullscreen(Client *c, int fullscreen) if (fullscreen) { c->prev = c->geom; - resize(c, c->mon->m, 0); + resize(c, c->mon->m, 0, 0); /* The xdg-protocol specifies: * * If the fullscreened surface is not opaque, the compositor must make @@ -1912,7 +2052,7 @@ setfullscreen(Client *c, int fullscreen) } else { /* restore previous size instead of arrange for floating windows since * client positions are set by the user and cannot be recalculated */ - resize(c, c->prev, 0); + resize(c, c->prev, 0, 1); if (c->fullscreen_bg) { wlr_scene_node_destroy(&c->fullscreen_bg->node); c->fullscreen_bg = NULL; @@ -1929,6 +2069,12 @@ setlayout(const Arg *arg) selmon->sellt ^= 1; if (arg && arg->v) selmon->lt[selmon->sellt] = (Layout *)arg->v; + if (!selmon->lt[selmon->sellt]->arrange) { + /* floating layout, draw borders around all clients */ + Client *c; + wl_list_for_each(c, &clients, link) + resize(c, c->mon->m, 0, 1); + } /* TODO change layout symbol? */ arrange(selmon); printstatus(); @@ -1965,7 +2111,7 @@ setmon(Client *c, Monitor *m, unsigned int newtags) } if (m) { /* Make sure window actually overlaps with the monitor */ - resize(c, c->geom, 0); + resize(c, c->geom, 0, 1); wlr_surface_send_enter(client_surface(c), m->wlr_output); c->tags = newtags ? newtags : m->tagset[m->seltags]; /* assign tags of target monitor */ arrange(m); @@ -2179,11 +2325,24 @@ sigchld(int unused) * setting our own disposition for SIGCHLD. */ pid_t pid; + if (signal(SIGCHLD, sigchld) == SIG_ERR) die("can't install SIGCHLD handler:"); - while (0 < (pid = waitpid(-1, NULL, WNOHANG))) + while (0 < (pid = waitpid(-1, NULL, WNOHANG))) { + pid_t *p, *lim; if (pid == child_pid) child_pid = -1; + if (!(p = autostart_pids)) + continue; + lim = &p[autostart_len]; + + for (; p < lim; p++) { + if (*p == pid) { + *p = -1; + break; + } + } + } } void @@ -2197,6 +2356,16 @@ spawn(const Arg *arg) } } +void spawnscratch(const Arg *arg) +{ + if (fork() == 0) { + dup2(STDERR_FILENO, STDOUT_FILENO); + setsid(); + execvp(((char **)arg->v)[1], ((char **)arg->v)+1); + die("dwl: execvp %s failed:", ((char **)arg->v)[1]); + } +} + void startdrag(struct wl_listener *listener, void *data) { @@ -2234,7 +2403,7 @@ tagmon(const Arg *arg) void tile(Monitor *m) { - unsigned int i, n = 0, mw, my, ty; + unsigned int i, n = 0, mw, my, ty, draw_borders = 1; Client *c; wl_list_for_each(c, &clients, link) @@ -2243,6 +2412,9 @@ tile(Monitor *m) if (n == 0) return; + if (n == smartborders) + draw_borders = 0; + if (n > m->nmaster) mw = m->nmaster ? m->w.width * m->mfact : 0; else @@ -2253,11 +2425,11 @@ tile(Monitor *m) 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); + .height = (m->w.height - my) / (MIN(n, m->nmaster) - i)}, 0, draw_borders); my += c->geom.height; } 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); + .width = m->w.width - mw, .height = (m->w.height - ty) / (n - i)}, 0, draw_borders); ty += c->geom.height; } i++; @@ -2282,6 +2454,29 @@ togglefullscreen(const Arg *arg) } void +togglescratch(const Arg *arg) +{ + Client *c; + unsigned int found = 0; + + /* search for first window that matches the scratchkey */ + wl_list_for_each(c, &clients, link) + if (c->scratchkey == ((char**)arg->v)[0][0]) { + found = 1; + break; + } + + if (found) { + c->tags = VISIBLEON(c, selmon) ? 0 : selmon->tagset[selmon->seltags]; + + focusclient(c->tags == 0 ? focustop(selmon) : c, 1); + arrange(selmon); + } else{ + spawnscratch(arg); + } +} + +void toggletag(const Arg *arg) { unsigned int newtags; @@ -2334,6 +2529,16 @@ unmapnotify(struct wl_listener *listener, void *data) cursor_mode = CurNormal; grabc = NULL; } + if (c->swallowing) { + c->swallowing->swallowedby = NULL; + c->swallowing = NULL; + } + + if (c->swallowedby) { + swallow(c->swallowedby, c); + c->swallowedby->swallowing = NULL; + c->swallowedby = NULL; + } if (c->mon) c->mon->un_map = 1; @@ -2435,6 +2640,18 @@ virtualkeyboard(struct wl_listener *listener, void *data) createkeyboard(device); } +void +warpcursor(const Client *c) { + if (cursor->x < c->geom.x || + cursor->x > c->geom.x + c->geom.width || + cursor->y < c->geom.y || + cursor->y > c->geom.y + c->geom.height) + wlr_cursor_warp_closest(cursor, + NULL, + c->geom.x + c->geom.width / 2.0, + c->geom.y + c->geom.height / 2.0); +} + Monitor * xytomon(double x, double y) { |