Huge codebase update

This commit is contained in:
Alexis Jhon Gaspar 2024-07-08 14:13:30 +08:00
parent 813717224b
commit 5de5ece675
158 changed files with 6407 additions and 3710 deletions

View file

@ -1,4 +1,4 @@
# Suckless Utilities version 6.4
# Suckless Utilities version 6.5
## About
These are my builds of suckless software such as dwm and st, based on the work for flexipatch by bakkeby. This aims for much more streamlined configuration and patching than 6.3 (which becomes more complicated over time and whenever more patches are integrated).
@ -119,6 +119,7 @@ Linux/Unix users:
- libexif(-devel or -dev) (for nsxiv)
- jq (for handling eww notifications)
- pamixer
- xcb-util(-devel or -dev)
Termux users:
- xorg

View file

@ -1,5 +1,5 @@
Similar to [dwm-flexipatch](https://github.com/bakkeby/dwm-flexipatch) this dmenu 5.2 (8df553e,
2023-09-22) project has a different take on patching. It uses preprocessor directives to decide
Similar to [dwm-flexipatch](https://github.com/bakkeby/dwm-flexipatch) this dmenu 5.3 (7be720c,
2024-03-19) project has a different take on patching. It uses preprocessor directives to decide
whether or not to include a patch during build time. Essentially this means that this build, for
better or worse, contains both the patched _and_ the original code. The aim being that you can
select which patches to include and the build will contain that code and nothing more.

View file

@ -1,5 +1,5 @@
# dmenu version
VERSION = 5.2
VERSION = 5.3
# paths
PREFIX = /usr/local

View file

@ -1645,7 +1645,11 @@ setup(void)
#if CENTER_PATCH
if (center) {
#if XYW_PATCH
mw = (dmw>0 ? dmw : MIN(MAX(max_textw() + promptw, min_width), info[i].width));
#else
mw = MIN(MAX(max_textw() + promptw, min_width), info[i].width);
#endif // XYW_PATCH
x = info[i].x_org + ((info[i].width - mw) / 2);
y = info[i].y_org + ((info[i].height - mh) / 2);
} else {
@ -1677,7 +1681,11 @@ setup(void)
parentwin);
#if CENTER_PATCH
if (center) {
#if XYW_PATCH
mw = (dmw>0 ? dmw : MIN(MAX(max_textw() + promptw, min_width), wa.width));
#else
mw = MIN(MAX(max_textw() + promptw, min_width), wa.width);
#endif // XYW_PATCH
x = (wa.width - mw) / 2;
y = (wa.height - mh) / 2;
} else {
@ -2054,7 +2062,7 @@ main(int argc, char *argv[])
prompt = argv[++i];
else if (!strcmp(argv[i], "-fn")) /* font or font set */
#if PANGO_PATCH
strcpy(font, argv[++i]);
font = argv[++i];
#else
fonts[0] = argv[++i];
#endif // PANGO_PATCH

View file

@ -234,7 +234,7 @@ xfont_free(Fnt *font)
#if PANGO_PATCH
Fnt*
drw_font_create(Drw* drw, const char font[])
drw_font_create(Drw* drw, const char *font)
{
Fnt *fnt = NULL;
@ -365,10 +365,10 @@ int
drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lpad, const char *text, int invert, Bool markup)
{
char buf[1024];
int ty;
unsigned int ew;
int i, ty, th;
unsigned int ew, eh;
XftDraw *d = NULL;
size_t i, len;
size_t len;
int render = x || y || w || h;
if (!drw || (render && !drw->scheme) || !text || !drw->font)
@ -393,10 +393,14 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
len = strlen(text);
if (len) {
drw_font_getexts(drw->font, text, len, &ew, NULL, markup);
drw_font_getexts(drw->font, text, len, &ew, &eh, markup);
th = eh;
/* shorten text if necessary */
for (len = MIN(len, sizeof(buf) - 1); len && ew > w; len--)
drw_font_getexts(drw->font, text, len, &ew, NULL, markup);
for (len = MIN(len, sizeof(buf) - 1); len && ew > w; len--) {
drw_font_getexts(drw->font, text, len, &ew, &eh, markup);
if (eh > th)
th = eh;
}
if (len) {
memcpy(buf, text, len);
@ -406,7 +410,7 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
; /* NOP */
if (render) {
ty = y + (h - drw->font->h) / 2;
ty = y + (h - th) / 2;
if (markup)
pango_layout_set_markup(drw->font->layout, buf, len);
else
@ -650,7 +654,7 @@ drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w,
if (w)
*w = r.width / PANGO_SCALE;
if (h)
*h = font->h;
*h = r.height / PANGO_SCALE;
}
#else
void

View file

@ -55,7 +55,7 @@ void drw_free(Drw *drw);
/* Fnt abstraction */
#if PANGO_PATCH
Fnt *drw_font_create(Drw* drw, const char font[]);
Fnt *drw_font_create(Drw* drw, const char *font);
void drw_font_free(Fnt* set);
unsigned int drw_font_getwidth(Drw *drw, const char *text, Bool markup);
unsigned int drw_fontset_getwidth_clamp(Drw *drw, const char *text, unsigned int n);

View file

@ -1,4 +1,4 @@
This dwm 6.4 (9f88553, 2023-09-22) side project has a different take on dwm patching. It uses preprocessor directives to decide whether or not to include a patch during build time. Essentially this means that this build, for better or worse, contains both the patched _and_ the original code. The aim being that you can select which patches to include and the build will contain that code and nothing more. Due to the complexity of some of the patches dwm-flexipatch has diverged from mainstream dwm by making some core patches non-optional for maintenance reasons. For the classic dwm-flexipatch build refer to branch [dwm-flexipatch-1.0](https://github.com/bakkeby/dwm-flexipatch/tree/dwm-flexipatch-1.0).
This dwm 6.5 (5687f46, 2024-06-08) side project has a different take on dwm patching. It uses preprocessor directives to decide whether or not to include a patch during build time. Essentially this means that this build, for better or worse, contains both the patched _and_ the original code. The aim being that you can select which patches to include and the build will contain that code and nothing more. Due to the complexity of some of the patches dwm-flexipatch has diverged from mainstream dwm by making some core patches non-optional for maintenance reasons. For the classic dwm-flexipatch build refer to branch [dwm-flexipatch-1.0](https://github.com/bakkeby/dwm-flexipatch/tree/dwm-flexipatch-1.0).
For example to include the `alpha` patch then you would only need to flip this setting from 0 to 1 in [patches.h](https://github.com/bakkeby/dwm-flexipatch/blob/master/patches.def.h):
```c
@ -19,6 +19,14 @@ Browsing patches? There is a [map of patches](https://coggle.it/diagram/X9IiSSM6
### Changelog:
2024-01-31 - Added the placedir patch
2023-12-22 - Added the do-not-die-on-color-allocation-failure patch
2023-12-01 - Added the sendmoncenter patch
2023-11-12 - Added the focusmaster-return patch variant
2023-06-27 - Added the focusfollowmouse and unmanaged patches
2023-06-25 - Added the toggletopbar patch
@ -355,6 +363,10 @@ Browsing patches? There is a [map of patches](https://coggle.it/diagram/X9IiSSM6
- i.e. if topbar is 0 then dmenu will appear at the bottom and if 1 then dmenu will appear at
the top
- do-not-die-on-color-allocation-failure
- avoids dwm terminating (dying) on color allocation failures
- useful for the xrdb (xresources) and status2d patches
- [dragcfact](https://github.com/bakkeby/patches/wiki/dragcfact/)
- lets you resize clients' size (i.e. modify cfact) by holding modkey + shift + right-click
and dragging the mouse
@ -442,6 +454,10 @@ Browsing patches? There is a [map of patches](https://coggle.it/diagram/X9IiSSM6
- [focusmaster](https://dwm.suckless.org/patches/focusmaster/)
- a simple patch that just puts focus back to the master client
- [focusmaster-return](https://dwm.suckless.org/patches/focusmaster/)
- a simple patch that just puts focus back to the master client
- additionally allows focus to be switched back to the previous client
- [focusonclick](https://dwm.suckless.org/patches/focusonclick/)
- this patch makes you switch focus only by mouse click and not sloppy (focus follows mouse
pointer)
@ -587,6 +603,9 @@ Browsing patches? There is a [map of patches](https://coggle.it/diagram/X9IiSSM6
- [pertag](https://dwm.suckless.org/patches/pertag/)
- adds nmaster, mfact, layouts and more per tag rather than per monitor
- [placedir](https://github.com/bakkeby/patches/wiki/placedir)
- allows tiled windows to be moved in any direction (up, down, left, right)
- [placemouse](https://github.com/bakkeby/patches/wiki/placemouse)
- lets the user change the position of a client in the stack using the mouse.
@ -644,6 +663,9 @@ Browsing patches? There is a [map of patches](https://coggle.it/diagram/X9IiSSM6
- [selfrestart](https://dwm.suckless.org/patches/selfrestart/)
- restart dwm without the unnecessary dependency of an external script
- [sendmoncenter](https://dwm.suckless.org/patches/sendmoncenter/)
- floating windows being sent to another monitor will be centered
- [sendmon\_keepfocus](https://github.com/bakkeby/patches/wiki/sendmon_keepfocus/)
- minor patch that allow clients to keep focus when being sent to another monitor

View file

@ -11,7 +11,7 @@ static const unsigned int borderpx = 1; /* border pixel of windows */
/* This allows the bar border size to be explicitly set separately from borderpx.
* If left as 0 then it will default to the borderpx value of the monitor and will
* automatically update with setborderpx. */
static const unsigned int barborderpx = 0; /* border pixel of bar */
static const unsigned int barborderpx = 1; /* border pixel of bar */
#endif // BAR_BORDER_PATCH
static const unsigned int snap = 32; /* snap pixel */
#if SWALLOW_PATCH
@ -95,14 +95,20 @@ static const int vertpadbar = 0; /* vertical padding for statusba
#endif // BAR_STATUSPADDING_PATCH
#if BAR_STATUSBUTTON_PATCH
static const char buttonbar[] = "󰕰 Start";
#endif // BAR_STATUSBUTTON_PATCH
#if BAR_DOCKBUTTONS_PATCH
static const char buttonbar2[] = " st";
static const char buttonbar3[] = "󰈹 Firefox";
static const char buttonbar4[] = "󰝚 mpd";
#endif // BAR_DOCKBUTTONS_PATCH
#if BAR_POWERBUTTON_PATCH
static const char buttonbar5[] = "󰗼 󰐥";
#endif // BAR_POWERBUTTON_PATCH
#if BAR_WINCONTROLBUTTONS_PATCH
static const char buttonbar6[] = "󰅖";
static const char buttonbar7[] = "󰖯";
static const char buttonbar8[] = "󰖰";
#endif // BAR_STATUSBUTTON_PATCH
#endif // BAR_WINCONTROLBUTTONS_PATCH
#if BAR_SYSTRAY_PATCH
static const unsigned int systrayspacing = 2; /* systray spacing */
static const int showsystray = 1; /* 0 means no systray */
@ -507,6 +513,17 @@ static const Rule rules[] = {
RULE(.wintype = WTYPE "DOCK", .unmanaged = 1)
RULE(.wintype = WTYPE "DIALOG", .isfloating = 1, .noswallow = 1)
RULE(.class = "Xmessage", .isfloating = 1, .noswallow = 1)
RULE(.class = "pce-atarist", .isfloating = 1, .noswallow = 1)
RULE(.class = "pce-cpm80", .isfloating = 1, .noswallow = 1)
RULE(.class = "pce-dos", .isfloating = 1, .noswallow = 1)
RULE(.class = "pce-ibmpc", .isfloating = 1, .noswallow = 1)
RULE(.class = "pce-img", .isfloating = 1, .noswallow = 1)
RULE(.class = "pce-macplus", .isfloating = 1, .noswallow = 1)
RULE(.class = "pce-rc759", .isfloating = 1, .noswallow = 1)
RULE(.class = "pce-sim405", .isfloating = 1, .noswallow = 1)
RULE(.class = "pce-simarm", .isfloating = 1, .noswallow = 1)
RULE(.class = "pce-sims32", .isfloating = 1, .noswallow = 1)
RULE(.class = "pce-vic20", .isfloating = 1, .noswallow = 1)
RULE(.wintype = WTYPE "UTILITY", .isfloating = 1, .noswallow = 1)
RULE(.wintype = WTYPE "TOOLBAR", .isfloating = 1, .noswallow = 1)
RULE(.wintype = WTYPE "SPLASH", .isfloating = 1, .noswallow = 1)
@ -570,6 +587,14 @@ static const BarRule barrules[] = {
{ -1, 1, BAR_ALIGN_LEFT, width_dcbutton2, draw_dcbutton2, click_dcbutton2, NULL, "dockbutton2" },
{ -1, 1, BAR_ALIGN_LEFT, width_dcbutton3, draw_dcbutton3, click_dcbutton3, NULL, "dockbutton3" },
#endif // BAR_DOCKBUTTONS_PATCH
#if BAR_POWERBUTTON_PATCH
{ -1, 0, BAR_ALIGN_RIGHT, width_powbutton, draw_powbutton, click_powbutton, NULL, "powerbutton" },
#endif // BAR_POWERBUTTON_PATCH
#if BAR_WINCONTROLBUTTONS_PATCH
{ -1, 1, BAR_ALIGN_RIGHT, width_winbutton, draw_winbutton, click_winbutton, NULL, "statusbutton6" },
{ -1, 1, BAR_ALIGN_RIGHT, width_winbutton2, draw_winbutton2, click_winbutton2, NULL, "statusbutton7" },
{ -1, 1, BAR_ALIGN_RIGHT, width_winbutton3, draw_winbutton3, click_winbutton3, NULL, "statusbutton8" },
#endif // BAR_WINCONTROLBUTTONS_PATCH
#if BAR_TAGGRID_PATCH
{ -1, 0, BAR_ALIGN_LEFT, width_taggrid, draw_taggrid, click_taggrid, NULL, "taggrid" },
#endif // BAR_TAGGRID_PATCH
@ -582,14 +607,6 @@ static const BarRule barrules[] = {
#if BAR_TAGLABELS_PATCH
{ -1, 0, BAR_ALIGN_LEFT, width_taglabels, draw_taglabels, click_taglabels, hover_taglabels, "taglabels" },
#endif // BAR_TAGLABELS_PATCH
#if BAR_POWERBUTTON_PATCH
{ -1, 0, BAR_ALIGN_RIGHT, width_powbutton, draw_powbutton, click_powbutton, NULL, "powerbutton" },
#endif // BAR_POWERBUTTON_PATCH
#if BAR_WINCONTROLBUTTONS_PATCH
{ -1, 1, BAR_ALIGN_RIGHT, width_winbutton, draw_winbutton, click_winbutton, NULL, "statusbutton6" },
{ -1, 1, BAR_ALIGN_RIGHT, width_winbutton2, draw_winbutton2, click_winbutton2, NULL, "statusbutton7" },
{ -1, 1, BAR_ALIGN_RIGHT, width_winbutton3, draw_winbutton3, click_winbutton3, NULL, "statusbutton8" },
#endif // BAR_WINCONTROLBUTTONS_PATCH
#if BAR_SYSTRAY_PATCH
{ 0, 0, BAR_ALIGN_RIGHT, width_systray, draw_systray, click_systray, NULL, "systray" },
#endif // BAR_SYSTRAY_PATCH
@ -997,9 +1014,9 @@ static const Key keys[] = {
#if TAB_PATCH
{ MODKEY|ControlMask, XK_b, tabmode, {-1} },
#endif // TAB_PATCH
#if FOCUSMASTER_PATCH
#if FOCUSMASTER_PATCH || FOCUSMASTER_RETURN_PATCH
{ MODKEY|ControlMask, XK_space, focusmaster, {0} },
#endif // FOCUSMASTER_PATCH
#endif // FOCUSMASTER_PATCH / FOCUSMASTER_RETURN_PATCH
#if STACKER_PATCH
STACKKEYS(MODKEY, focus)
STACKKEYS(MODKEY|ShiftMask, push)
@ -1013,6 +1030,12 @@ static const Key keys[] = {
{ MODKEY, XK_Up, focusdir, {.i = 2 } }, // up
{ MODKEY, XK_Down, focusdir, {.i = 3 } }, // down
#endif // FOCUSDIR_PATCH
#if PLACEDIR_PATCH
{ MODKEY|ControlMask, XK_Left, placedir, {.i = 0 } }, // left
{ MODKEY|ControlMask, XK_Right, placedir, {.i = 1 } }, // right
{ MODKEY|ControlMask, XK_Up, placedir, {.i = 2 } }, // up
{ MODKEY|ControlMask, XK_Down, placedir, {.i = 3 } }, // down
#endif // PLACEDIR_PATCH
#if SWAPFOCUS_PATCH && PERTAG_PATCH
{ MODKEY, XK_s, swapfocus, {.i = -1 } },
#endif // SWAPFOCUS_PATCH
@ -1227,8 +1250,8 @@ static const Key keys[] = {
{ MODKEY, XK_Right, viewtoright, {0} }, // note keybinding conflict with focusdir
{ MODKEY|ShiftMask, XK_Left, tagtoleft, {0} }, // note keybinding conflict with shifttag
{ MODKEY|ShiftMask, XK_Right, tagtoright, {0} }, // note keybinding conflict with shifttag
{ MODKEY|ControlMask, XK_Left, tagandviewtoleft, {0} },
{ MODKEY|ControlMask, XK_Right, tagandviewtoright, {0} },
{ MODKEY|ControlMask, XK_Left, tagandviewtoleft, {0} }, // note keybinding conflict with placedir
{ MODKEY|ControlMask, XK_Right, tagandviewtoright, {0} }, // note keybinding conflict with placedir
#endif // FOCUSADJACENTTAG_PATCH
#if TAGALL_PATCH
{ MODKEY|ShiftMask, XK_F1, tagall, {.v = "F1"} },
@ -1267,8 +1290,8 @@ static const Key keys[] = {
#if BAR_TAGGRID_PATCH
{ MODKEY|ControlMask, XK_Up, switchtag, { .ui = SWITCHTAG_UP | SWITCHTAG_VIEW } },
{ MODKEY|ControlMask, XK_Down, switchtag, { .ui = SWITCHTAG_DOWN | SWITCHTAG_VIEW } },
{ MODKEY|ControlMask, XK_Right, switchtag, { .ui = SWITCHTAG_RIGHT | SWITCHTAG_VIEW } },
{ MODKEY|ControlMask, XK_Left, switchtag, { .ui = SWITCHTAG_LEFT | SWITCHTAG_VIEW } },
{ MODKEY|ControlMask, XK_Right, switchtag, { .ui = SWITCHTAG_RIGHT | SWITCHTAG_VIEW } }, // note keybinding conflict with placedir
{ MODKEY|ControlMask, XK_Left, switchtag, { .ui = SWITCHTAG_LEFT | SWITCHTAG_VIEW } }, // note keybinding conflict with placedir
{ MODKEY|Mod4Mask, XK_Up, switchtag, { .ui = SWITCHTAG_UP | SWITCHTAG_TAG | SWITCHTAG_VIEW } },
{ MODKEY|Mod4Mask, XK_Down, switchtag, { .ui = SWITCHTAG_DOWN | SWITCHTAG_TAG | SWITCHTAG_VIEW } },
{ MODKEY|Mod4Mask, XK_Right, switchtag, { .ui = SWITCHTAG_RIGHT | SWITCHTAG_TAG | SWITCHTAG_VIEW } },

View file

@ -1,5 +1,5 @@
# dwm version
VERSION = 6.4
VERSION = 6.5
# Customize below to fit your system

View file

@ -337,14 +337,22 @@ drw_clr_create(
#if BAR_ALPHA_PATCH
if (!XftColorAllocName(drw->dpy, drw->visual, drw->cmap,
clrname, dest))
#if DO_NOT_DIE_ON_COLOR_ALLOCATION_FAILURE_PATCH
fprintf(stderr, "warning, cannot allocate color '%s'", clrname);
#else
die("error, cannot allocate color '%s'", clrname);
#endif // DO_NOT_DIE_ON_COLOR_ALLOCATION_FAILURE_PATCH
dest->pixel = (dest->pixel & 0x00ffffffU) | (alpha << 24);
#else
if (!XftColorAllocName(drw->dpy, DefaultVisual(drw->dpy, drw->screen),
DefaultColormap(drw->dpy, drw->screen),
clrname, dest))
#if DO_NOT_DIE_ON_COLOR_ALLOCATION_FAILURE_PATCH
fprintf(stderr, "warning, cannot allocate color '%s'", clrname);
#else
die("error, cannot allocate color '%s'", clrname);
#endif // DO_NOT_DIE_ON_COLOR_ALLOCATION_FAILURE_PATCH
#if NO_TRANSPARENT_BORDERS_PATCH
dest->pixel |= 0xff << 24;
@ -428,10 +436,10 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
{
#if BAR_PANGO_PATCH
char buf[1024];
int ty;
unsigned int ew;
int i, ty, th;
unsigned int ew, eh;
XftDraw *d = NULL;
size_t i, len;
size_t len;
int render = x || y || w || h;
if (!drw || (render && !drw->scheme) || !text || !drw->fonts)
@ -456,10 +464,14 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
len = strlen(text);
if (len) {
drw_font_getexts(drw->fonts, text, len, &ew, NULL, markup);
drw_font_getexts(drw->fonts, text, len, &ew, &eh, markup);
th = eh;
/* shorten text if necessary */
for (len = MIN(len, sizeof(buf) - 1); len && ew > w; len--)
drw_font_getexts(drw->fonts, text, len, &ew, NULL, markup);
for (len = MIN(len, sizeof(buf) - 1); len && ew > w; len--) {
drw_font_getexts(drw->fonts, text, len, &ew, &eh, markup);
if (eh > th)
th = eh;
}
if (len) {
memcpy(buf, text, len);
@ -469,7 +481,7 @@ drw_text(Drw *drw, int x, int y, unsigned int w, unsigned int h, unsigned int lp
; /* NOP */
if (render) {
ty = y + (h - drw->fonts->h) / 2;
ty = y + (h - th) / 2;
if (markup)
pango_layout_set_markup(drw->fonts->layout, buf, len);
else
@ -701,7 +713,7 @@ drw_font_getexts(Fnt *font, const char *text, unsigned int len, unsigned int *w,
if (w)
*w = r.width / PANGO_SCALE;
if (h)
*h = font->h;
*h = r.height / PANGO_SCALE;
}
#else
void

View file

@ -495,6 +495,9 @@ struct Monitor {
int gappiv; /* vertical gap between windows */
int gappoh; /* horizontal outer gaps */
int gappov; /* vertical outer gaps */
#if PERMON_VANITYGAPS_PATCH
int enablegaps; /* whether gaps are enabled */
#endif // PERMON_VANITYGAPS_PATCH
#endif // VANITYGAPS_PATCH
#if SETBORDERPX_PATCH
int borderpx;
@ -517,6 +520,9 @@ struct Monitor {
Client *clients;
Client *sel;
Client *stack;
#if FOCUSMASTER_RETURN_PATCH
Client *tagmarked[32];
#endif // FOCUSMASTER_RETURN_PATCH
Monitor *next;
Bar *bar;
const Layout *lt[2];
@ -695,6 +701,9 @@ static void maprequest(XEvent *e);
static void motionnotify(XEvent *e);
static void movemouse(const Arg *arg);
static Client *nexttiled(Client *c);
#if NOBORDER_PATCH
static int noborder(Client *c);
#endif // NOBORDER_PATCH
#if !ZOOMSWAP_PATCH || TAGINTOSTACK_ALLMASTER_PATCH || TAGINTOSTACK_ONEMASTER_PATCH
static void pop(Client *c);
#endif // !ZOOMSWAP_PATCH / TAGINTOSTACK_ALLMASTER_PATCH / TAGINTOSTACK_ONEMASTER_PATCH
@ -972,6 +981,7 @@ applyrules(Client *c)
if (r->switchtag)
#endif // SWALLOW_PATCH
{
unfocus(selmon->sel, 1, NULL);
selmon = c->mon;
if (r->switchtag == 2 || r->switchtag == 4)
newtagset = c->mon->tagset[c->mon->seltags] ^ c->tags;
@ -1115,6 +1125,11 @@ arrange(Monitor *m)
void
arrangemon(Monitor *m)
{
#if BAR_PADDING_SMART_PATCH
updatebarpos(selmon);
for (Bar *bar = selmon->bar; bar; bar = bar->next)
XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh);
#endif // BAR_PADDING_SMART_PATCH
#if TAB_PATCH
updatebarpos(m);
XMoveResizeWindow(dpy, m->tabwin, m->wx, m->ty, m->ww, th);
@ -1468,6 +1483,15 @@ configure(Client *c)
ce.width = c->w;
ce.height = c->h;
ce.border_width = c->bw;
#if NOBORDER_PATCH
if (noborder(c)) {
ce.width += c->bw * 2;
ce.height += c->bw * 2;
ce.border_width = 0;
}
#endif // NOBORDER_PATCH
ce.above = None;
ce.override_redirect = False;
XSendEvent(dpy, c->win, False, StructureNotifyMask, (XEvent *)&ce);
@ -1779,6 +1803,10 @@ createmon(void)
}
#endif // PERTAG_PATCH
#if PERMON_VANITYGAPS_PATCH
m->enablegaps = 1;
#endif // PERMON_VANITYGAPS_PATCH
#if SEAMLESS_RESTART_PATCH
restoremonitorstate(m);
#endif // SEAMLESS_RESTART_PATCH
@ -1831,6 +1859,11 @@ detach(Client *c)
#if SEAMLESS_RESTART_PATCH
c->idx = 0;
#endif // SEAMLESS_RESTART_PATCH
#if FOCUSMASTER_RETURN_PATCH
for (int i = 1; i < NUMTAGS; i++)
if (c == c->mon->tagmarked[i])
c->mon->tagmarked[i] = NULL;
#endif // FOCUSMASTER_RETURN_PATCH
for (tc = &c->mon->clients; *tc && *tc != c; tc = &(*tc)->next);
*tc = c->next;
@ -1871,7 +1904,7 @@ void
drawbar(Monitor *m)
{
Bar *bar;
#if !BAR_FLEXWINTITLE_PATCH
if (m->showbar)
#endif // BAR_FLEXWINTITLE_PATCH
@ -2059,6 +2092,10 @@ focus(Client *c)
if (!c || !ISVISIBLE(c))
c = getpointerclient();
#endif // FOCUSFOLLOWMOUSE_PATCH
#if STICKY_PATCH
if (!c || !ISVISIBLE(c))
for (c = selmon->stack; c && (!ISVISIBLE(c) || c->issticky); c = c->snext);
#endif // STICKY_PATCH
if (!c || !ISVISIBLE(c))
for (c = selmon->stack; c && !ISVISIBLE(c); c = c->snext);
if (selmon->sel && selmon->sel != c)
@ -2493,8 +2530,10 @@ manage(Window w, XWindowAttributes *wa)
#endif // CENTER_TRANSIENT_WINDOWS_PATCH | CENTER_TRANSIENT_WINDOWS_BY_PARENT_PATCH | CENTER_PATCH
} else {
#if SEAMLESS_RESTART_PATCH
if (!settings_restored)
if (!settings_restored || c->mon == NULL) {
c->mon = selmon;
settings_restored = 0;
}
#else
c->mon = selmon;
#endif // SEAMLESS_RESTART_PATCH
@ -2851,10 +2890,63 @@ nexttiled(Client *c)
return c;
}
#if NOBORDER_PATCH
int
noborder(Client *c)
{
int monocle_layout = 0;
#if MONOCLE_LAYOUT
if (&monocle == c->mon->lt[c->mon->sellt]->arrange)
monocle_layout = 1;
#endif // MONOCLE_LAYOUT
#if DECK_LAYOUT
if (&deck == c->mon->lt[c->mon->sellt]->arrange && c->mon->nmaster == 0)
monocle_layout = 1;
#endif // DECK_LAYOUT
#if FLEXTILE_DELUXE_LAYOUT
if (&flextile == c->mon->lt[c->mon->sellt]->arrange && (
(c->mon->ltaxis[LAYOUT] == NO_SPLIT && c->mon->ltaxis[MASTER] == MONOCLE) ||
(c->mon->ltaxis[STACK] == MONOCLE && c->mon->nmaster == 0)
)) {
monocle_layout = 1;
}
#endif //FLEXTILE_DELUXE_LAYOUT
if (!monocle_layout && (nexttiled(c->mon->clients) != c || nexttiled(c->next)))
return 0;
if (c->isfloating)
return 0;
if (!c->mon->lt[c->mon->sellt]->arrange)
return 0;
#if FAKEFULLSCREEN_CLIENT_PATCH && !FAKEFULLSCREEN_PATCH
if (c->fakefullscreen != 1 && c->isfullscreen)
return 0;
#elif !FAKEFULLSCREEN_PATCH
if (c->isfullscreen)
return 0;
#endif // FAKEFULLSCREEN_CLIENT_PATCH
return 1;
}
#endif // NOBORDER_PATCH
#if !ZOOMSWAP_PATCH || TAGINTOSTACK_ALLMASTER_PATCH || TAGINTOSTACK_ONEMASTER_PATCH
void
pop(Client *c)
{
#if FOCUSMASTER_RETURN_PATCH
int i;
for (i = 0; !(selmon->tagset[selmon->seltags] & 1 << i); i++);
i++;
c->mon->tagmarked[i] = nexttiled(c->mon->clients);
#endif // FOCUSMASTER_RETURN_PATCH
detach(c);
attach(c);
focus(c);
@ -2998,31 +3090,9 @@ resizeclient(Client *c, int x, int y, int w, int h)
drawroundedcorners(c);
#endif // ROUNDED_CORNERS_PATCH
#if NOBORDER_PATCH
if (((nexttiled(c->mon->clients) == c && !nexttiled(c->next))
#if MONOCLE_LAYOUT
|| &monocle == c->mon->lt[c->mon->sellt]->arrange
#endif // MONOCLE_LAYOUT
#if DECK_LAYOUT
|| (&deck == c->mon->lt[c->mon->sellt]->arrange &&
c->mon->nmaster == 0)
#endif // DECK_LAYOUT
#if FLEXTILE_DELUXE_LAYOUT
|| (&flextile == c->mon->lt[c->mon->sellt]->arrange && (
(c->mon->ltaxis[LAYOUT] == NO_SPLIT &&
c->mon->ltaxis[MASTER] == MONOCLE) ||
(c->mon->ltaxis[STACK] == MONOCLE &&
c->mon->nmaster == 0)))
#endif //FLEXTILE_DELUXE_LAYOUT
)
#if FAKEFULLSCREEN_CLIENT_PATCH && !FAKEFULLSCREEN_PATCH
&& (c->fakefullscreen == 1 || !c->isfullscreen)
#else
&& !c->isfullscreen
#endif // FAKEFULLSCREEN_CLIENT_PATCH
&& !c->isfloating
&& c->mon->lt[c->mon->sellt]->arrange) {
c->w = wc.width += c->bw * 2;
c->h = wc.height += c->bw * 2;
if (noborder(c)) {
wc.width += c->bw * 2;
wc.height += c->bw * 2;
wc.border_width = 0;
}
#endif // NOBORDER_PATCH
@ -3377,6 +3447,14 @@ sendmon(Client *c, Monitor *m)
#else
c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */
#endif // EMPTYVIEW_PATCH
#if SENDMON_CENTER_PATCH
c->x = m->mx + (m->mw - WIDTH(c)) / 2;
c->y = m->my + (m->mh - HEIGHT(c)) / 2;
#if SAVEFLOATS_PATCH
c->sfx = m->mx + (m->mw - c->sfw - 2 * c->bw) / 2;
c->sfy = m->my + (m->mh - c->sfh - 2 * c->bw) / 2;
#endif // SAVEFLOATS_PATCH
#endif // SENDMON_CENTER_PATCH
#if ATTACHABOVE_PATCH || ATTACHASIDE_PATCH || ATTACHBELOW_PATCH || ATTACHBOTTOM_PATCH
attachx(c);
#else
@ -4219,10 +4297,21 @@ togglefloating(const Arg *arg)
#endif // !FAKEFULLSCREEN_PATCH
c->isfloating = !c->isfloating || c->isfixed;
#if !BAR_FLEXWINTITLE_PATCH
#if RENAMED_SCRATCHPADS_PATCH
if (c->scratchkey != 0 && c->isfloating)
XSetWindowBorder(dpy, c->win, scheme[SchemeScratchSel][ColFloat].pixel);
else if (c->scratchkey != 0)
XSetWindowBorder(dpy, c->win, scheme[SchemeScratchSel][ColBorder].pixel);
else if (c->isfloating)
XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColFloat].pixel);
else
XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel);
#else
if (c->isfloating)
XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColFloat].pixel);
else
XSetWindowBorder(dpy, c->win, scheme[SchemeSel][ColBorder].pixel);
#endif // RENAMED_SCRATCHPADS_PATCH
#endif // BAR_FLEXWINTITLE_PATCH
if (c->isfloating) {
#if SAVEFLOATS_PATCH || EXRESIZE_PATCH
@ -4637,16 +4726,36 @@ updatebarpos(Monitor *m)
#if BAR_PADDING_VANITYGAPS_PATCH && VANITYGAPS_PATCH
#if PERTAG_VANITYGAPS_PATCH && PERTAG_PATCH
if (!selmon || selmon->pertag->enablegaps[selmon->pertag->curtag])
#elif PERMON_VANITYGAPS_PATCH
if (!selmon || selmon->enablegaps)
#else
if (enablegaps)
#endif // PERTAG_VANITYGAPS_PATCH
{
#if BAR_PADDING_SMART_PATCH
unsigned int n; Client *c;
for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++);
if (n > 1) {
y_pad = gappoh;
x_pad = gappov;
}
#else
y_pad = gappoh;
x_pad = gappov;
#endif // BAR_PADDING_SMART_PATCH
}
#elif BAR_PADDING_PATCH
#if BAR_PADDING_SMART_PATCH
unsigned int n; Client *c;
for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++);
if (n > 1) {
y_pad = vertpad;
x_pad = sidepad;
}
#else
y_pad = vertpad;
x_pad = sidepad;
#endif // BAR_PADDING_SMART_PATCH
#endif // BAR_PADDING_PATCH | BAR_PADDING_VANITYGAPS_PATCH
#if INSETS_PATCH
@ -4701,7 +4810,7 @@ updatebarpos(Monitor *m)
}
void
updateclientlist()
updateclientlist(void)
{
Client *c;
Monitor *m;
@ -5095,6 +5204,9 @@ void
zoom(const Arg *arg)
{
Client *c = selmon->sel;
#if FOCUSMASTER_RETURN_PATCH && ZOOMSWAP_PATCH
int i;
#endif // FOCUSMASTER_RETURN_PATCH
if (arg && arg->v)
c = (Client*)arg->v;
if (!c)
@ -5148,6 +5260,12 @@ zoom(const Arg *arg)
cold = nexttiled(c->mon->clients);
if (c != cold && !at)
at = findbefore(c);
#if FOCUSMASTER_RETURN_PATCH
for (i = 0; !(selmon->tagset[selmon->seltags] & 1 << i); i++);
i++;
c->mon->tagmarked[i] = cold;
#endif // FOCUSMASTER_RETURN_PATCH
detach(c);
attach(c);
/* swap windows instead of pushing the previous one down */

View file

@ -21,7 +21,7 @@ alttab()
/* redraw tab */
XRaiseWindow(dpy, alttabwin);
drawtab(ntabs, 0, m);
drawalttab(ntabs, 0, m);
}
void
@ -66,7 +66,7 @@ alttabend()
}
void
drawtab(int nwins, int first, Monitor *m)
drawalttab(int nwins, int first, Monitor *m)
{
Client *c;
int i, h;
@ -178,7 +178,7 @@ alttabstart(const Arg *arg)
i++;
}
drawtab(ntabs, 1, m);
drawalttab(ntabs, 1, m);
struct timespec ts = { .tv_sec = 0, .tv_nsec = 1000000 };

View file

@ -1,5 +1,5 @@
#include <time.h>
static void drawtab(int nwins, int first, Monitor *m);
static void drawalttab(int nwins, int first, Monitor *m);
static void alttabstart(const Arg *arg);
static void alttabend();

View file

@ -15,8 +15,7 @@ click_dcbutton(Bar *bar, Arg *arg, BarArg *a)
{
return ClkButton2;
}
int
width_dcbutton2(Bar *bar, BarArg *a)
int width_dcbutton2(Bar *bar, BarArg *a)
{
return TEXTW(buttonbar3);
}

View file

@ -102,6 +102,11 @@ click_pwrl_tags(Bar *bar, Arg *arg, BarArg *a)
if (i < NUMTAGS) {
arg->ui = 1 << i;
}
#if BAR_TAGPREVIEW_PATCH
if (selmon->previewshow != 0) {
hidetagpreview(selmon);
}
#endif // BAR_TAGPREVIEW_PATCH
return ClkTagBar;
}

View file

@ -1,3 +1,4 @@
static int width_stbutton(Bar *bar, BarArg *a);
static int draw_stbutton(Bar *bar, BarArg *a);
static int click_stbutton(Bar *bar, Arg *arg, BarArg *a);

View file

@ -13,14 +13,7 @@ width_systray(Bar *bar, BarArg *a)
if (!w)
XMoveWindow(dpy, systray->win, -systray->h, bar->by);
}
if (w) {
w += lrpad / 2 - systrayspacing;
#if !BAR_STATUS_PATCH
w += lrpad / 2;
#endif // BAR_STATUS_PATCH
}
return w;
return w ? w + lrpad - systrayspacing : 0;
}
int
@ -101,11 +94,13 @@ draw_systray(Bar *bar, BarArg *a)
i->mon = bar->mon;
}
unsigned int xpos = bar->bx + a->x + lrpad / 2;
#if BAR_STATUS_PATCH
xpos -= lrpad / 2;
#endif // BAR_STATUS_PATCH
XMoveResizeWindow(dpy, systray->win, xpos, (w ? bar->by + a->y + (a->h - systray->h) / 2: -bar->by - a->y), MAX(w, 1), systray->h);
#if !BAR_ALPHA_PATCH
wa.background_pixel = scheme[SchemeNorm][ColBg].pixel;
XChangeWindowAttributes(dpy, systray->win, CWBackPixel, &wa);
XClearWindow(dpy, systray->win);
#endif // BAR_ALPHA_PATCH
XMoveResizeWindow(dpy, systray->win, bar->bx + a->x + lrpad / 2, (w ? bar->by + a->y + (a->h - systray->h) / 2: -systray->h), MAX(w, 1), systray->h);
return w;
}

View file

@ -87,6 +87,11 @@ click_taglabels(Bar *bar, Arg *arg, BarArg *a)
if (i < NUMTAGS) {
arg->ui = 1 << i;
}
#if BAR_TAGPREVIEW_PATCH
if (selmon->previewshow != 0) {
hidetagpreview(selmon);
}
#endif // BAR_TAGPREVIEW_PATCH
return ClkTagBar;
}

View file

@ -88,6 +88,11 @@ click_tags(Bar *bar, Arg *arg, BarArg *a)
if (i < NUMTAGS) {
arg->ui = 1 << i;
}
#if BAR_TAGPREVIEW_PATCH
if (selmon->previewshow != 0) {
hidetagpreview(selmon);
}
#endif // BAR_TAGPREVIEW_PATCH
return ClkTagBar;
}

View file

@ -51,7 +51,7 @@ togglewin(const Arg *arg)
Client *c = (Client*)arg->v;
if (!c)
return;
if (c == selmon->sel)
if (!HIDDEN(c) && c == selmon->sel)
hide(c);
else {
if (HIDDEN(c))

View file

@ -1,14 +1,42 @@
void
focusmaster(const Arg *arg)
{
Client *c;
Client *master;
Monitor *m = selmon;
#if FOCUSMASTER_RETURN_PATCH
int i;
#endif // FOCUSMASTER_RETURN_PATCH
if (selmon->nmaster < 1)
if (m->nmaster < 1)
return;
#if !FAKEFULLSCREEN_PATCH
#if FAKEFULLSCREEN_CLIENT_PATCH
if (!m->sel || (m->sel->isfullscreen && m->sel->fakefullscreen != 1 && lockfullscreen))
return;
#else
if (!m->sel || (m->sel->isfullscreen && lockfullscreen))
return;
#endif // FAKEFULLSCREEN_CLIENT_PATCH
#endif // FAKEFULLSCREEN_PATCH
master = nexttiled(m->clients);
if (!master)
return;
c = nexttiled(selmon->clients);
#if FOCUSMASTER_RETURN_PATCH
for (i = 0; !(m->tagset[m->seltags] & 1 << i); i++);
i++;
if (c)
focus(c);
if (m->sel == master) {
if (m->tagmarked[i] && ISVISIBLE(m->tagmarked[i]))
focus(m->tagmarked[i]);
} else {
m->tagmarked[i] = m->sel;
focus(master);
}
#else
focus(master);
#endif // FOCUSMASTER_RETURN_PATCH
restack(m);
}

View file

@ -154,7 +154,7 @@
#if FOCUSFOLLOWMOUSE_PATCH
#include "focusfollowmouse.c"
#endif
#if FOCUSMASTER_PATCH
#if FOCUSMASTER_PATCH || FOCUSMASTER_RETURN_PATCH
#include "focusmaster.c"
#endif
#if FOCUSURGENT_PATCH
@ -208,6 +208,9 @@
#if PERTAG_PATCH
#include "pertag.c"
#endif
#if PLACEDIR_PATCH
#include "placedir.c"
#endif
#if PLACEMOUSE_PATCH
#include "placemouse.c"
#endif
@ -404,3 +407,4 @@
#if BAR_WINCONTROLBUTTONS_PATCH
#include "bar_wincontrolbutton.c"
#endif

View file

@ -157,7 +157,7 @@
#if FOCUSFOLLOWMOUSE_PATCH
#include "focusfollowmouse.h"
#endif
#if FOCUSMASTER_PATCH
#if FOCUSMASTER_PATCH || FOCUSMASTER_RETURN_PATCH
#include "focusmaster.h"
#endif
#if FOCUSURGENT_PATCH
@ -207,6 +207,9 @@
#if PERTAG_PATCH
#include "pertag.h"
#endif
#if PLACEDIR_PATCH
#include "placedir.h"
#endif
#if PLACEMOUSE_PATCH
#include "placemouse.h"
#endif

View file

@ -44,10 +44,9 @@ deck(Monitor *m)
#endif // VANITYGAPS_PATCH
getfacts(m, mh, sh, &mfacts, &sfacts, &mrest, &srest);
#if !MONOCLESYMBOL_PATCH
if (n - m->nmaster > 0) /* override layout symbol */
snprintf(m->ltsymbol, sizeof m->ltsymbol, "D %d", n - m->nmaster);
#endif // MONOCLESMYBOL_PATCH
for (i = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), i++)
if (i < m->nmaster) {

View file

@ -643,7 +643,7 @@ arrange_tatami(Monitor *m, int x, int y, int h, int w, int ih, int iv, int n, in
if (j < ai + cats) {
/* Arrange cats (all excess clients that can't be tiled as mats). Cats sleep on mats. */
switch (cats) {
switch (cats) {
case 1: // fill
break;
case 2: // up and down

View file

@ -0,0 +1,96 @@
void
placedir(const Arg *arg)
{
Client *s = selmon->sel, *f = NULL, *c, *next, *fprior, *sprior;
if (!s || s->isfloating)
return;
unsigned int score = -1;
unsigned int client_score;
int dist;
int dirweight = 20;
next = s->next;
if (!next)
next = s->mon->clients;
for (c = next; c != s; c = next) {
next = c->next;
if (!next)
next = s->mon->clients;
if (!ISVISIBLE(c)) // || HIDDEN(c)
continue;
switch (arg->i) {
case 0: // left
dist = s->x - c->x - c->w;
client_score =
dirweight * MIN(abs(dist), abs(dist + s->mon->ww)) +
abs(s->y - c->y);
break;
case 1: // right
dist = c->x - s->x - s->w;
client_score =
dirweight * MIN(abs(dist), abs(dist + s->mon->ww)) +
abs(c->y - s->y);
break;
case 2: // up
dist = s->y - c->y - c->h;
client_score =
dirweight * MIN(abs(dist), abs(dist + s->mon->wh)) +
abs(s->x - c->x);
break;
default:
case 3: // down
dist = c->y - s->y - s->h;
client_score =
dirweight * MIN(abs(dist), abs(dist + s->mon->wh)) +
abs(c->x - s->x);
break;
}
if (((arg->i == 0 || arg->i == 2) && client_score <= score) || client_score < score) {
score = client_score;
f = c;
}
}
if (f && f != s) {
for (fprior = f->mon->clients; fprior && fprior->next != f; fprior = fprior->next);
for (sprior = s->mon->clients; sprior && sprior->next != s; sprior = sprior->next);
if (s == fprior) {
next = f->next;
if (sprior)
sprior->next = f;
else
f->mon->clients = f;
f->next = s;
s->next = next;
} else if (f == sprior) {
next = s->next;
if (fprior)
fprior->next = s;
else
s->mon->clients = s;
s->next = f;
f->next = next;
} else { // clients are not adjacent to each other
next = f->next;
f->next = s->next;
s->next = next;
if (fprior)
fprior->next = s;
else
s->mon->clients = s;
if (sprior)
sprior->next = f;
else
f->mon->clients = f;
}
arrange(f->mon);
}
}

View file

@ -0,0 +1 @@
static void placedir(const Arg *arg);

View file

@ -149,3 +149,4 @@ togglescratch(const Arg *arg)
spawnscratch(arg);
}
}

View file

@ -39,12 +39,12 @@ persistclientstate(Client *c)
int
restoreclientstate(Client *c)
{
return getclienttags(c)
| getclientfields(c)
#if SAVEFLOATS_PATCH
| restorewindowfloatposition(c, c->mon ? c->mon : selmon)
#endif // SAVEFLOATS_PATCH
;
int restored = getclientfields(c);
getclienttags(c);
#if SAVEFLOATS_PATCH
restorewindowfloatposition(c, c->mon ? c->mon : selmon);
#endif // SAVEFLOATS_PATCH
return restored;
}
void setmonitorfields(Monitor *m)

View file

@ -6,7 +6,7 @@ shift(const Arg *arg, int clients)
unsigned int tagmask = 0;
#if SCRATCHPADS_PATCH && !RENAMED_SCRATCHPADS_PATCH
shifted.ui = selmon->tagset[selmon->seltags];
shifted.ui = selmon->tagset[selmon->seltags] & ~SPTAGMASK;
#else
shifted.ui = selmon->tagset[selmon->seltags];
#endif // SCRATCHPADS_PATCH
@ -18,6 +18,10 @@ shift(const Arg *arg, int clients)
for (c = selmon->clients; c && clients; c = c->next) {
if (c == selmon->sel)
continue;
#if STICKY_PATCH
if (c->issticky)
continue;
#endif // STICKY_PATCH
#if SCRATCHPADS_PATCH && !RENAMED_SCRATCHPADS_PATCH
if (!(c->tags & SPTAGMASK))
tagmask |= c->tags;

View file

@ -10,8 +10,15 @@ resizemousescroll(const Arg *arg)
if (!(c = selmon->sel))
return;
#if !FAKEFULLSCREEN_PATCH
#if FAKEFULLSCREEN_CLIENT_PATCH
if (c->isfullscreen && c->fakefullscreen != 1) /* no support resizing fullscreen windows by mouse */
return;
#else
if (c->isfullscreen) /* no support resizing fullscreen windows by mouse */
return;
#endif // FAKEFULLSCREEN_CLIENT_PATCH
#endif // !FAKEFULLSCREEN_PATCH
restack(selmon);
if (XGrabPointer(dpy, root, False, MOUSEMASK, GrabModeAsync, GrabModeAsync,
None, cursor[CurResize]->cursor, CurrentTime) != GrabSuccess)

View file

@ -1,5 +1,5 @@
/* Settings */
#if !PERTAG_VANITYGAPS_PATCH
#if !(PERTAG_VANITYGAPS_PATCH || PERMON_VANITYGAPS_PATCH)
static int enablegaps = 1;
#endif // PERTAG_VANITYGAPS_PATCH
@ -69,10 +69,12 @@ setgapsex(const Arg *arg)
#if PERTAG_VANITYGAPS_PATCH && PERTAG_PATCH
if (!selmon->pertag->enablegaps[selmon->pertag->curtag])
selmon->pertag->enablegaps[selmon->pertag->curtag] = 1;
#elif PERMON_VANITYGAPS_PATCH
selmon->enablegaps = 1;
#else
if (!enablegaps)
enablegaps = 1;
#endif // PERTAG_VANITYGAPS_PATCH
#endif // PERTAG_VANITYGAPS_PATCH | PERMON_VANITYGAPS_PATCH
setgaps(oh, ov, ih, iv);
}
@ -83,24 +85,35 @@ togglegaps(const Arg *arg)
{
#if PERTAG_VANITYGAPS_PATCH && PERTAG_PATCH
selmon->pertag->enablegaps[selmon->pertag->curtag] = !selmon->pertag->enablegaps[selmon->pertag->curtag];
#elif PERMON_VANITYGAPS_PATCH
selmon->enablegaps = !selmon->enablegaps;
#else
enablegaps = !enablegaps;
#endif // PERTAG_VANITYGAPS_PATCH
#endif // PERTAG_VANITYGAPS_PATCH | PERMON_VANITYGAPS_PATCH
#if BAR_PADDING_VANITYGAPS_PATCH
#if PERMON_VANITYGAPS_PATCH
updatebarpos(selmon);
for (Bar *bar = selmon->bar; bar; bar = bar->next)
XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh);
#else
for (Monitor *m = mons; m; m = m->next) {
updatebarpos(m);
for (Bar *bar = m->bar; bar; bar = bar->next)
XMoveResizeWindow(dpy, bar->win, bar->bx, bar->by, bar->bw, bar->bh);
}
#endif // PERMON_VANITYGAPS_PATCH
#if BAR_SYSTRAY_PATCH
drawbarwin(systray->bar);
#endif // BAR_SYSTRAY_PATCH
#endif // BAR_PADDING_VANITYGAPS_PATCH
#if PERTAG_VANITYGAPS_PATCH && PERTAG_PATCH
#if (PERTAG_VANITYGAPS_PATCH && PERTAG_PATCH) || PERMON_VANITYGAPS_PATCH
arrange(selmon);
#else
arrange(NULL);
#endif // PERTAG_VANITYGAPS_PATCH
#endif // PERTAG_VANITYGAPS_PATCH | PERMON_VANITYGAPS_PATCH
}
static void
@ -193,9 +206,11 @@ getgaps(Monitor *m, int *oh, int *ov, int *ih, int *iv, unsigned int *nc)
unsigned int n, oe, ie;
#if PERTAG_VANITYGAPS_PATCH && PERTAG_PATCH
oe = ie = m->pertag->enablegaps[m->pertag->curtag];
#elif PERMON_VANITYGAPS_PATCH
oe = ie = m->enablegaps;
#else
oe = ie = enablegaps;
#endif // PERTAG_VANITYGAPS_PATCH
#endif // PERTAG_VANITYGAPS_PATCH | PERMON_VANITYGAPS_PATCH
Client *c;
for (n = 0, c = nexttiled(m->clients); c; c = nexttiled(c->next), n++);

View file

@ -132,6 +132,11 @@ xrdb(const Arg *arg)
#endif // BAR_ALPHA_PATCH
ColCount
);
#if BAR_SYSTRAY_PATCH && !BAR_ALPHA_PATCH
if (systray) {
XMoveWindow(dpy, systray->win, -32000, -32000);
}
#endif // BAR_SYSTRAY_PATCH
arrange(NULL);
focus(NULL);
}

View file

@ -367,7 +367,13 @@
* toggled in unison when vanitygaps are toggled. Increasing or decreasing gaps during runtime
* will not affect the bar padding.
*/
#define BAR_PADDING_VANITYGAPS_PATCH 1
#define BAR_PADDING_VANITYGAPS_PATCH 1
/* Smart bar padding patch that automatically adjusts the padding when there is
* only one client on the monitor. Works well with vanitygaps and barpadding
* patches.
*/
#define BAR_PADDING_SMART_PATCH 0
/* This patch adds simple markup for status messages using pango markup.
* This depends on the pango library v1.44 or greater.
@ -572,6 +578,18 @@
*/
#define DISTRIBUTETAGS_PATCH 0
/* By default dwm will terminate on color allocation failure and the behaviour is intended to
* catch and inform the user of color configuration issues.
*
* Some patches like status2d and xresources / xrdb can change colours during runtime, which
* means that if a color can't be allocated at this time then the window manager will abruptly
* terminate.
*
* This patch will ignore color allocation failures and continue on as normal. The effect of
* this is that the existing color, that was supposed to be replaced, will remain as-is.
*/
#define DO_NOT_DIE_ON_COLOR_ALLOCATION_FAILURE_PATCH 1
/* Similarly to the dragmfact patch this allows you to click and drag clients to change the
* cfact to adjust the client's size in the stack. This patch depends on the cfacts patch.
*/
@ -657,6 +675,12 @@
*/
#define FOCUSMASTER_PATCH 0
/* A variant of the focusmaster patch that additionally allows the focus to be returned to the
* previously focused client
* https://dwm.suckless.org/patches/focusmaster/
*/
#define FOCUSMASTER_RETURN_PATCH 0
/* Switch focus only by mouse click and not sloppy (focus follows mouse pointer).
* https://dwm.suckless.org/patches/focusonclick/
*/
@ -885,11 +909,22 @@
*/
#define PERTAG_VANITYGAPS_PATCH 0
/* This patch allows configuring vanity gaps on a per-monitor basis rather than
* all monitors (default).
*/
#define PERMON_VANITYGAPS_PATCH 0
/* This controls whether or not to also store bar position on a per
* tag basis, or leave it as one bar per monitor.
*/
#define PERTAGBAR_PATCH 0
/* Similar to the focusdir patch this patch allow users to move a window in any direction
* in the tiled stack (up, down, left, right).
* https://github.com/bakkeby/patches/wiki/placedir
*/
#define PLACEDIR_PATCH 0
/* This patch lets you change the position of a client in the stack using the mouse.
* https://github.com/bakkeby/patches/wiki/placemouse
*/
@ -1022,6 +1057,11 @@
*/
#define SELFRESTART_PATCH 1
/* Floating windows being sent to another monitor will be centered.
* https://dwm.suckless.org/patches/sendmoncenter/
*/
#define SENDMON_CENTER_PATCH 0
/* This patch allow clients to keep focus when being sent to another monitor.
* https://github.com/bakkeby/patches/blob/master/dwm/dwm-sendmon_keepfocus-6.2.diff
*/

View file

@ -1,6 +1,8 @@
BasedOnStyle: Google
IndentWidth: 4
InsertBraces: true
ColumnLimit: 79
AlignArrayOfStructures: Left
AlignConsecutiveMacros: true
AlignConsecutiveMacros: Consecutive
AllowShortFunctionsOnASingleLine: None
AllowShortLoopsOnASingleLine: false
AllowShortIfStatementsOnASingleLine: Never

View file

@ -0,0 +1,30 @@
Checks: |
-*,
abseil-*,
bugprone-*,
clang-analyzer-*,
misc-*,
modernize-*,
performance-*,
portability-*,
readability-*,
llvm-*,
-bugprone-easily-swappable-parameters,
-readability-avoid-const-params-in-decls,
-readability-identifier-length
CheckOptions:
- key: readability-inconsistent-declaration-parameter-name.Strict
value: true
- key: readability-identifier-naming.StructCase
value: lower_case
- key: readability-identifier-naming.FunctionCase
value: lower_case
- key: readability-identifier-naming.VariableCase
value: lower_case
- key: readability-identifier-naming.EnumConstantCase
value: UPPER_CASE
- key: readability-identifier-naming.MacroDefinitionCase
value: UPPER_CASE
- key: readability-function-cognitive-complexity.Threshold
value: 15

View file

@ -1,6 +1,5 @@
CompileFlags:
Add:
- "-I."
- "-I./inc"
- "-I.."
- "-I../inc"
Diagnostics:
UnusedIncludes: Strict
MissingIncludes: Strict
Includes:
IgnoreHeader: bits/getopt_core.h

View file

@ -1,3 +1,3 @@
build/
.cache/
dwmblocks
compile_commands.json

View file

@ -3,19 +3,22 @@
BIN := dwmblocks
BUILD_DIR := build
SRC_DIR := src
INC_DIR := inc
INC_DIR := include
CC=cc
DEBUG := 0
VERBOSE := 0
LIBS := xcb-atom
PREFIX := /usr/local
CC = cc
CFLAGS := -Wall -Wextra -Ofast -I. -I$(INC_DIR)
CFLAGS += -Wall -Wextra -Wno-missing-field-initializers
LDLIBS := -lX11
CFLAGS := -Ofast -I. -I$(INC_DIR) -std=c99
CFLAGS += -DBINARY=\"$(BIN)\" -D_POSIX_C_SOURCE=200809L
CFLAGS += -Wall -Wpedantic -Wextra -Wswitch-enum
CFLAGS += $(shell pkg-config --cflags $(LIBS))
LDLIBS := $(shell pkg-config --libs $(LIBS))
VPATH := $(SRC_DIR)
OBJS := $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/%.o,$(wildcard $(SRC_DIR)/*.c))
OBJS += $(patsubst %.c,$(BUILD_DIR)/%.o,$(wildcard *.c))
SRCS := $(wildcard $(SRC_DIR)/*.c)
OBJS := $(subst $(SRC_DIR)/,$(BUILD_DIR)/,$(SRCS:.c=.o))
INSTALL_DIR := $(DESTDIR)$(PREFIX)/bin
@ -25,20 +28,21 @@ ifeq ($(VERBOSE), 0)
Q := @
endif
ifeq ($(DEBUG), 1)
CFLAGS += -g
endif
all: $(BUILD_DIR)/$(BIN)
$(BUILD_DIR)/%.o: $(SRC_DIR)/%.c config.h
$Qmkdir -p $(@D)
$(PRINTF) "CC" $@
$Q$(COMPILE.c) -o $@ $<
$(BUILD_DIR)/$(BIN): $(OBJS)
$(PRINTF) "LD" $@
$Q$(LINK.o) $^ $(LDLIBS) -o $@
$(BUILD_DIR)/%.o: %.c config.h | $(BUILD_DIR)
$(PRINTF) "CC" $@
$Q$(COMPILE.c) -o $@ $<
$(BUILD_DIR):
$(PRINTF) "MKDIR" $@
$Qmkdir -p $@
clean:
$(PRINTF) "CLEAN" $(BUILD_DIR)
$Q$(RM) $(BUILD_DIR)/*

View file

@ -85,21 +85,21 @@ dwmblocks &
### Modifying the blocks
You can define your status bar blocks in `config.c`:
You can define your status bar blocks in `config.h`:
```c
Block blocks[] = {
#define BLOCKS(X) \
...
{"volume", 0, 5},
{"date", 1800, 1},
X(" ", "wpctl get-volume @DEFAULT_AUDIO_SINK@ | cut -d' ' -f2", 0, 5) \
X("󰥔 ", "date '+%H:%M:%S'", 1, 1) \
...
}
```
Each block has the following properties:
| Property | Description |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
| Icon | An icon you wish to prepend to your block output. |
| Command | The command you wish to execute in your block. |
| Update interval | Time in seconds, after which you want the block to update. If `0`, the block will never be updated. |
| Update signal | Signal to be used for triggering the block. Must be a positive integer. If `0`, a signal won't be set up for the block and it will be unclickable. |
@ -107,17 +107,20 @@ Each block has the following properties:
Apart from defining the blocks, features can be toggled through `config.h`:
```c
// Maximum possible length of output from block, expressed in number of characters.
#define CMDLENGTH 50
// String used to delimit block outputs in the status.
#define DELIMITER " "
// The status bar's delimiter that appears in between each block.
#define DELIMITER " "
// Maximum number of Unicode characters that a block can output.
#define MAX_BLOCK_OUTPUT_LENGTH 45
// Adds a leading delimiter to the status bar, useful for powerline.
#define LEADING_DELIMITER 1
// Enable clickability for blocks. See the "Clickable blocks" section below.
// Control whether blocks are clickable.
#define CLICKABLE_BLOCKS 1
// Control whether a leading delimiter should be prepended to the status.
#define LEADING_DELIMITER 0
// Control whether a trailing delimiter should be appended to the status.
#define TRAILING_DELIMITER 0
```
### Signalling changes

View file

@ -1,17 +0,0 @@
#include "config.h"
#include "block.h"
#include "util.h"
Block blocks[] = {
{"sb-forecast", 900, 1 },
{"sb-disk", 1800, 2 },
{"sb-memory", 10, 3 },
{"sb-loadavg", 5, 4 },
{"sb-music", 1, 5 },
{"sb-volume", 1, 6 },
{"sb-date", 1, 7 },
{"sb-user", 0, 8 },
};
const unsigned short blockCount = LEN(blocks);

View file

@ -1,6 +1,29 @@
#pragma once
#ifndef CONFIG_H
#define CONFIG_H
#define CLICKABLE_BLOCKS 1 // Enable clickability for blocks
#define CMDLENGTH 45 // Trim block output to this length
#define DELIMITER " " // Delimiter string used to separate blocks
#define LEADING_DELIMITER 0 // Whether a leading separator should be used
// String used to delimit block outputs in the status.
#define DELIMITER " "
// Maximum number of Unicode characters that a block can output.
#define MAX_BLOCK_OUTPUT_LENGTH 45
// Control whether blocks are clickable.
#define CLICKABLE_BLOCKS 1
// Control whether a leading delimiter should be prepended to the status.
#define LEADING_DELIMITER 0
// Control whether a trailing delimiter should be appended to the status.
#define TRAILING_DELIMITER 0
// Define blocks for the status feed as X(icon, cmd, interval, signal).
#define BLOCKS(X) \
X("", "sb-forecast", 900, 1) \
X("", "sb-disk", 1800, 2) \
X("", "sb-memory", 10, 3) \
X("", "sb-loadavg", 5, 4) \
X("", "sb-music", 1, 5) \
X("", "sb-volume", 1, 6) \
X("", "sb-date", 1, 7) \
X("", "sb-user", 0, 8)
#endif // CONFIG_H

View file

@ -1,15 +0,0 @@
#pragma once
#include "block.h"
#include "config.h"
#include "util.h"
typedef struct {
char *current;
char *previous;
} BarStatus;
extern unsigned short debugMode;
void initStatus(BarStatus *);
void freeStatus(BarStatus *);
void writeStatus(BarStatus *);

View file

@ -1,17 +0,0 @@
#pragma once
#include "config.h"
typedef struct {
const char *command;
const unsigned int interval;
const unsigned int signal;
int pipe[2];
char output[CMDLENGTH * 4 + 1];
} Block;
extern Block blocks[];
extern const unsigned short blockCount;
void execBlock(const Block *, const char *);
void execBlocks(unsigned int);
void updateBlock(Block *);

View file

@ -1,8 +0,0 @@
#pragma once
#define LEN(arr) (sizeof(arr) / sizeof(arr[0]))
#define MAX(a, b) (a > b ? a : b)
int gcd(int, int);
void closePipe(int[2]);
void trimUTF8(char*, unsigned int);

View file

@ -1,5 +0,0 @@
#pragma once
int setupX();
int closeX();
void setXRootName(char *);

View file

@ -0,0 +1,29 @@
#ifndef BLOCK_H
#define BLOCK_H
#include <stdbool.h>
#include <stdint.h>
#include <sys/types.h>
#include "config.h"
#include "util.h"
typedef struct {
const char *const icon;
const char *const command;
const unsigned int interval;
const int signal;
int pipe[PIPE_FD_COUNT];
char output[MAX_BLOCK_OUTPUT_LENGTH * UTF8_MAX_BYTE_COUNT + 1];
pid_t fork_pid;
} block;
block block_new(const char *const icon, const char *const command,
const unsigned int interval, const int signal);
int block_init(block *const block);
int block_deinit(block *const block);
int block_execute(block *const block, const uint8_t button);
int block_update(block *const block);
#endif // BLOCK_H

View file

@ -0,0 +1,12 @@
#ifndef CLI_H
#define CLI_H
#include <stdbool.h>
typedef struct {
bool is_debug_mode;
} cli_arguments;
cli_arguments cli_parse_arguments(const char* const argv[], const int argc);
#endif // CLI_H

View file

@ -0,0 +1,16 @@
#ifndef MAIN_H
#define MAIN_H
#include <signal.h>
#include "config.h"
#include "util.h"
#define REFRESH_SIGNAL SIGUSR1
// Utilise C's adjacent string concatenation to count the number of blocks.
#define X(...) "."
enum { BLOCK_COUNT = LEN(BLOCKS(X)) - 1 };
#undef X
#endif // MAIN_H

View file

@ -0,0 +1,33 @@
#ifndef SIGNAL_HANDLER_H
#define SIGNAL_HANDLER_H
#include <signal.h>
#include "block.h"
#include "timer.h"
typedef sigset_t signal_set;
typedef int (*signal_refresh_callback)(block* const blocks,
const unsigned short block_count);
typedef int (*signal_timer_callback)(block* const blocks,
const unsigned short block_code,
timer* const timer);
typedef struct {
int fd;
const signal_refresh_callback refresh_callback;
const signal_timer_callback timer_callback;
block* const blocks;
const unsigned short block_count;
} signal_handler;
signal_handler signal_handler_new(
block* const blocks, const unsigned short block_count,
const signal_refresh_callback refresh_callback,
const signal_timer_callback timer_callback);
int signal_handler_init(signal_handler* const handler);
int signal_handler_deinit(signal_handler* const handler);
int signal_handler_process(signal_handler* const handler, timer* const timer);
#endif // SIGNAL_HANDLER_H

View file

@ -0,0 +1,31 @@
#ifndef STATUS_H
#define STATUS_H
#include <stdbool.h>
#include "block.h"
#include "config.h"
#include "main.h"
#include "util.h"
#include "x11.h"
typedef struct {
#define STATUS_LENGTH \
((BLOCK_COUNT * (MEMBER_LENGTH(block, output) - 1) + CLICKABLE_BLOCKS) + \
(BLOCK_COUNT - 1 + LEADING_DELIMITER + TRAILING_DELIMITER) * \
(LEN(DELIMITER) - 1) + \
1)
char current[STATUS_LENGTH];
char previous[STATUS_LENGTH];
#undef STATUS_LENGTH
const block* const blocks;
const unsigned short block_count;
} status;
status status_new(const block* const blocks, const unsigned short block_count);
bool status_update(status* const status);
int status_write(const status* const status, const bool is_debug_mode,
x11_connection* const connection);
#endif // STATUS_H

View file

@ -0,0 +1,21 @@
#ifndef TIMER_H
#define TIMER_H
#include <signal.h>
#include <stdbool.h>
#include "block.h"
#define TIMER_SIGNAL SIGALRM
typedef struct {
unsigned int time;
const unsigned int tick;
const unsigned int reset_value;
} timer;
timer timer_new(const block *const blocks, const unsigned short block_count);
int timer_arm(timer *const timer);
bool timer_must_run_block(const timer *const timer, const block *const block);
#endif // TIMER_H

View file

@ -0,0 +1,28 @@
#ifndef UTIL_H
#define UTIL_H
#include <stddef.h>
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define LEN(arr) (sizeof(arr) / sizeof((arr)[0]))
#define BIT(n) (1 << (n))
// NOLINTBEGIN(bugprone-macro-parentheses)
#define MEMBER_SIZE(type, member) sizeof(((type*)NULL)->member)
#define MEMBER_LENGTH(type, member) \
(MEMBER_SIZE(type, member) / MEMBER_SIZE(type, member[0]))
// NOLINTEND(bugprone-macro-parentheses)
#define UTF8_MAX_BYTE_COUNT 4
enum pipe_fd_index {
READ_END,
WRITE_END,
PIPE_FD_COUNT,
};
unsigned int gcd(unsigned int a, unsigned int b);
size_t truncate_utf8_string(char* const buffer, const size_t size,
const size_t char_limit);
#endif // UTIL_H

View file

@ -0,0 +1,28 @@
#ifndef WATCHER_H
#define WATCHER_H
#include <poll.h>
#include <stdbool.h>
#include "block.h"
#include "main.h"
enum watcher_fd_index {
SIGNAL_FD = BLOCK_COUNT,
WATCHER_FD_COUNT,
};
typedef struct pollfd watcher_fd;
typedef struct {
watcher_fd fds[WATCHER_FD_COUNT];
unsigned short active_blocks[BLOCK_COUNT];
unsigned short active_block_count;
bool got_signal;
} watcher;
int watcher_init(watcher *const watcher, const block *const blocks,
const unsigned short block_count, const int signal_fd);
int watcher_poll(watcher *const watcher, const int timeout_ms);
#endif // WATCHER_H

View file

@ -0,0 +1,13 @@
#ifndef X11_H
#define X11_H
#include <xcb/xcb.h>
typedef xcb_connection_t x11_connection;
x11_connection* x11_connection_open(void);
void x11_connection_close(x11_connection* const connection);
int x11_set_root_name(x11_connection* const connection,
const char* const name);
#endif // X11_H

View file

@ -1,62 +0,0 @@
#include "bar.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "block.h"
#include "x11.h"
void initStatus(BarStatus *status) {
const unsigned int statusLength =
(blockCount * (LEN(blocks[0].output) - 1)) +
(blockCount - 1 + LEADING_DELIMITER) * (LEN(DELIMITER) - 1);
status->current = (char *)malloc(statusLength);
status->previous = (char *)malloc(statusLength);
status->current[0] = '\0';
status->previous[0] = '\0';
}
void freeStatus(BarStatus *status) {
free(status->current);
free(status->previous);
}
int updateStatus(BarStatus *status) {
strcpy(status->previous, status->current);
status->current[0] = '\0';
for (int i = 0; i < blockCount; i++) {
Block *block = blocks + i;
if (strlen(block->output)) {
#if LEADING_DELIMITER
strcat(status->current, DELIMITER);
#else
if (status->current[0]) strcat(status->current, DELIMITER);
#endif
#if CLICKABLE_BLOCKS
if (!debugMode && block->signal) {
char signal[] = {block->signal, '\0'};
strcat(status->current, signal);
}
#endif
strcat(status->current, block->output);
}
}
return strcmp(status->current, status->previous);
}
void writeStatus(BarStatus *status) {
// Only write out if status has changed
if (!updateStatus(status)) return;
if (debugMode) {
printf("%s\n", status->current);
return;
}
setXRootName(status->current);
}

View file

@ -1,72 +1,147 @@
#include "block.h"
#define _GNU_SOURCE
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "config.h"
#include "util.h"
static int execLock = 0;
block block_new(const char *const icon, const char *const command,
const unsigned int interval, const int signal) {
block block = {
.icon = icon,
.command = command,
.interval = interval,
.signal = signal,
void execBlock(const Block *block, const char *button) {
unsigned short i = block - blocks;
.output = {[0] = '\0'},
.fork_pid = -1,
};
// Ensure only one child process exists per block at an instance
if (execLock & 1 << i) return;
// Lock execution of block until current instance finishes execution
execLock |= 1 << i;
return block;
}
if (fork() == 0) {
close(block->pipe[0]);
dup2(block->pipe[1], STDOUT_FILENO);
close(block->pipe[1]);
int block_init(block *const block) {
if (pipe(block->pipe) != 0) {
(void)fprintf(stderr,
"error: could not create a pipe for \"%s\" block\n",
block->command);
return 1;
}
if (button) setenv("BLOCK_BUTTON", button, 1);
return 0;
}
FILE *file = popen(block->command, "r");
if (!file) {
printf("\n");
int block_deinit(block *const block) {
int status = close(block->pipe[READ_END]);
status |= close(block->pipe[WRITE_END]);
if (status != 0) {
(void)fprintf(stderr, "error: could not close \"%s\" block's pipe\n",
block->command);
return 1;
}
return 0;
}
int block_execute(block *const block, const uint8_t button) {
// Ensure only one child process exists per block at an instance.
if (block->fork_pid != -1) {
return 0;
}
block->fork_pid = fork();
if (block->fork_pid == -1) {
(void)fprintf(
stderr, "error: could not create a subprocess for \"%s\" block\n",
block->command);
return 1;
}
if (block->fork_pid == 0) {
const int write_fd = block->pipe[WRITE_END];
int status = close(block->pipe[READ_END]);
if (button != 0) {
char button_str[4];
(void)snprintf(button_str, LEN(button_str), "%hhu", button);
status |= setenv("BLOCK_BUTTON", button_str, 1);
}
const char null = '\0';
if (status != 0) {
(void)write(write_fd, &null, sizeof(null));
exit(EXIT_FAILURE);
}
// Buffer will hold both '\n' and '\0'
char buffer[LEN(block->output) + 1];
if (fgets(buffer, LEN(buffer), file) == NULL) {
// Send an empty line in case of no output
printf("\n");
exit(EXIT_SUCCESS);
FILE *const file = popen(block->command, "r");
if (file == NULL) {
(void)write(write_fd, &null, sizeof(null));
exit(EXIT_FAILURE);
}
pclose(file);
// Trim to the max possible UTF-8 capacity
trimUTF8(buffer, LEN(buffer));
// Ensure null-termination since fgets() will leave buffer untouched on
// no output.
char buffer[LEN(block->output)] = {[0] = null};
(void)fgets(buffer, LEN(buffer), file);
// Remove trailing newlines.
const size_t length = strcspn(buffer, "\n");
buffer[length] = null;
// Exit if command execution failed or if file could not be closed.
if (pclose(file) != 0) {
(void)write(write_fd, &null, sizeof(null));
exit(EXIT_FAILURE);
}
const size_t output_size =
truncate_utf8_string(buffer, LEN(buffer), MAX_BLOCK_OUTPUT_LENGTH);
(void)write(write_fd, buffer, output_size);
printf("%s\n", buffer);
exit(EXIT_SUCCESS);
}
return 0;
}
void execBlocks(unsigned int time) {
for (int i = 0; i < blockCount; i++) {
const Block *block = blocks + i;
if (time == 0 ||
(block->interval != 0 && time % block->interval == 0)) {
execBlock(block, NULL);
}
}
}
void updateBlock(Block *block) {
int block_update(block *const block) {
char buffer[LEN(block->output)];
int bytesRead = read(block->pipe[0], buffer, LEN(buffer));
// String from pipe will always end with '\n'
buffer[bytesRead - 1] = '\0';
const ssize_t bytes_read =
read(block->pipe[READ_END], buffer, LEN(buffer));
if (bytes_read == -1) {
(void)fprintf(stderr,
"error: could not fetch output of \"%s\" block\n",
block->command);
return 2;
}
strcpy(block->output, buffer);
// Collect exit-status of the subprocess to avoid zombification.
int fork_status = 0;
if (waitpid(block->fork_pid, &fork_status, 0) == -1) {
(void)fprintf(stderr,
"error: could not obtain exit status for \"%s\" block\n",
block->command);
return 2;
}
block->fork_pid = -1;
// Remove execution lock for the current block
execLock &= ~(1 << (block - blocks));
if (fork_status != 0) {
(void)fprintf(stderr,
"error: \"%s\" block exited with non-zero status\n",
block->command);
return 1;
}
(void)strncpy(block->output, buffer, LEN(buffer));
return 0;
}

33
dwmblocks-async/src/cli.c Normal file
View file

@ -0,0 +1,33 @@
#include "cli.h"
#include <errno.h>
#include <getopt.h>
#include <stdbool.h>
#include <stdio.h>
cli_arguments cli_parse_arguments(const char *const argv[], const int argc) {
errno = 0;
cli_arguments args = {
.is_debug_mode = false,
};
int opt = -1;
opterr = 0; // Suppress getopt's built-in invalid opt message
while ((opt = getopt(argc, (char *const *)argv, "dh")) != -1) {
switch (opt) {
case 'd':
args.is_debug_mode = true;
break;
case '?':
(void)fprintf(stderr, "error: unknown option `-%c'\n", optopt);
// fall through
case 'h':
// fall through
default:
(void)fprintf(stderr, "usage: %s [-d]\n", BINARY);
errno = 1;
}
}
return args;
}

View file

@ -1,157 +1,168 @@
#define _GNU_SOURCE
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <sys/epoll.h>
#include <sys/signalfd.h>
#include "main.h"
#include <errno.h>
#include <stdbool.h>
#include <stddef.h>
#include "bar.h"
#include "block.h"
#include "cli.h"
#include "config.h"
#include "signal-handler.h"
#include "status.h"
#include "timer.h"
#include "util.h"
#include "watcher.h"
#include "x11.h"
static unsigned short statusContinue = 1;
unsigned short debugMode = 0;
static int epollFD, signalFD;
static unsigned int timerTick = 0, maxInterval = 1;
void signalHandler() {
struct signalfd_siginfo info;
read(signalFD, &info, sizeof(info));
unsigned int signal = info.ssi_signo;
static unsigned int timer = 0;
switch (signal) {
case SIGALRM:
// Schedule the next timer event and execute blocks
alarm(timerTick);
execBlocks(timer);
// Wrap `timer` to the interval [1, `maxInterval`]
timer = (timer + timerTick - 1) % maxInterval + 1;
return;
case SIGUSR1:
// Update all blocks on receiving SIGUSR1
execBlocks(0);
return;
}
for (int j = 0; j < blockCount; j++) {
const Block *block = blocks + j;
if (block->signal == signal - SIGRTMIN) {
char button[4]; // value can't be more than 255;
sprintf(button, "%d", info.ssi_int & 0xff);
execBlock(block, button);
break;
static int init_blocks(block *const blocks, const unsigned short block_count) {
for (unsigned short i = 0; i < block_count; ++i) {
block *const block = &blocks[i];
if (block_init(block) != 0) {
return 1;
}
}
}
void termHandler() {
statusContinue = 0;
}
void setupSignals() {
sigset_t handledSignals;
sigemptyset(&handledSignals);
sigaddset(&handledSignals, SIGUSR1);
sigaddset(&handledSignals, SIGALRM);
// Append all block signals to `handledSignals`
for (int i = 0; i < blockCount; i++)
if (blocks[i].signal > 0)
sigaddset(&handledSignals, SIGRTMIN + blocks[i].signal);
// Create a signal file descriptor for epoll to watch
signalFD = signalfd(-1, &handledSignals, 0);
// Block all realtime and handled signals
for (int i = SIGRTMIN; i <= SIGRTMAX; i++) sigaddset(&handledSignals, i);
sigprocmask(SIG_BLOCK, &handledSignals, NULL);
// Handle termination signals
signal(SIGINT, termHandler);
signal(SIGTERM, termHandler);
// Avoid zombie subprocesses
struct sigaction signalAction;
signalAction.sa_handler = SIG_DFL;
sigemptyset(&signalAction.sa_mask);
signalAction.sa_flags = SA_NOCLDWAIT;
sigaction(SIGCHLD, &signalAction, 0);
}
void statusLoop() {
// Update all blocks initially
raise(SIGALRM);
BarStatus status;
initStatus(&status);
struct epoll_event events[blockCount + 1];
while (statusContinue) {
int eventCount = epoll_wait(epollFD, events, LEN(events), 100);
for (int i = 0; i < eventCount; i++) {
unsigned short id = events[i].data.u32;
if (id < blockCount) {
updateBlock(blocks + id);
} else {
signalHandler();
}
}
if (eventCount != -1) writeStatus(&status);
}
freeStatus(&status);
}
void init() {
epollFD = epoll_create(blockCount);
struct epoll_event event = {
.events = EPOLLIN,
};
for (int i = 0; i < blockCount; i++) {
// Append each block's pipe's read end to `epollFD`
pipe(blocks[i].pipe);
event.data.u32 = i;
epoll_ctl(epollFD, EPOLL_CTL_ADD, blocks[i].pipe[0], &event);
// Calculate the max interval and tick size for the timer
if (blocks[i].interval) {
maxInterval = MAX(blocks[i].interval, maxInterval);
timerTick = gcd(blocks[i].interval, timerTick);
}
}
setupSignals();
// Watch signal file descriptor as well
event.data.u32 = blockCount;
epoll_ctl(epollFD, EPOLL_CTL_ADD, signalFD, &event);
}
int main(const int argc, const char *argv[]) {
if (setupX()) {
fprintf(stderr, "%s\n", "dwmblocks: Failed to open display");
return 1;
}
for (int i = 0; i < argc; i++) {
if (!strcmp("-d", argv[i])) {
debugMode = 1;
break;
}
}
init();
statusLoop();
if (closeX())
fprintf(stderr, "%s\n", "dwmblocks: Failed to close display");
close(epollFD);
close(signalFD);
for (int i = 0; i < blockCount; i++) closePipe(blocks[i].pipe);
return 0;
}
static int deinit_blocks(block *const blocks,
const unsigned short block_count) {
for (unsigned short i = 0; i < block_count; ++i) {
block *const block = &blocks[i];
if (block_deinit(block) != 0) {
return 1;
}
}
return 0;
}
static int execute_blocks(block *const blocks,
const unsigned short block_count,
const timer *const timer) {
for (unsigned short i = 0; i < block_count; ++i) {
block *const block = &blocks[i];
if (!timer_must_run_block(timer, block)) {
continue;
}
if (block_execute(&blocks[i], 0) != 0) {
return 1;
}
}
return 0;
}
static int trigger_event(block *const blocks, const unsigned short block_count,
timer *const timer) {
if (execute_blocks(blocks, block_count, timer) != 0) {
return 1;
}
if (timer_arm(timer) != 0) {
return 1;
}
return 0;
}
static int refresh_callback(block *const blocks,
const unsigned short block_count) {
if (execute_blocks(blocks, block_count, NULL) != 0) {
return 1;
}
return 0;
}
static int event_loop(block *const blocks, const unsigned short block_count,
const bool is_debug_mode,
x11_connection *const connection,
signal_handler *const signal_handler) {
timer timer = timer_new(blocks, block_count);
// Kickstart the event loop with an initial execution.
if (trigger_event(blocks, block_count, &timer) != 0) {
return 1;
}
watcher watcher;
if (watcher_init(&watcher, blocks, block_count, signal_handler->fd) != 0) {
return 1;
}
status status = status_new(blocks, block_count);
bool is_alive = true;
while (is_alive) {
if (watcher_poll(&watcher, -1) != 0) {
return 1;
}
if (watcher.got_signal) {
is_alive = signal_handler_process(signal_handler, &timer) == 0;
}
for (unsigned short i = 0; i < watcher.active_block_count; ++i) {
(void)block_update(&blocks[watcher.active_blocks[i]]);
}
const bool has_status_changed = status_update(&status);
if (has_status_changed &&
status_write(&status, is_debug_mode, connection) != 0) {
return 1;
}
}
return 0;
}
int main(const int argc, const char *const argv[]) {
const cli_arguments cli_args = cli_parse_arguments(argv, argc);
if (errno != 0) {
return 1;
}
x11_connection *const connection = x11_connection_open();
if (connection == NULL) {
return 1;
}
#define BLOCK(icon, command, interval, signal) \
block_new(icon, command, interval, signal),
block blocks[BLOCK_COUNT] = {BLOCKS(BLOCK)};
#undef BLOCK
const unsigned short block_count = LEN(blocks);
int status = 0;
if (init_blocks(blocks, block_count) != 0) {
status = 1;
goto x11_close;
}
signal_handler signal_handler = signal_handler_new(
blocks, block_count, refresh_callback, trigger_event);
if (signal_handler_init(&signal_handler) != 0) {
status = 1;
goto deinit_blocks;
}
if (event_loop(blocks, block_count, cli_args.is_debug_mode, connection,
&signal_handler) != 0) {
status = 1;
}
if (signal_handler_deinit(&signal_handler) != 0) {
status = 1;
}
deinit_blocks:
if (deinit_blocks(blocks, block_count) != 0) {
status = 1;
}
x11_close:
x11_connection_close(connection);
return status;
}

View file

@ -0,0 +1,124 @@
#include "signal-handler.h"
#include <signal.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/signalfd.h>
#include <sys/types.h>
#include <unistd.h>
#include "block.h"
#include "main.h"
#include "timer.h"
typedef struct signalfd_siginfo signal_info;
signal_handler signal_handler_new(
block *const blocks, const unsigned short block_count,
const signal_refresh_callback refresh_callback,
const signal_timer_callback timer_callback) {
signal_handler handler = {
.refresh_callback = refresh_callback,
.timer_callback = timer_callback,
.blocks = blocks,
.block_count = block_count,
};
return handler;
}
int signal_handler_init(signal_handler *const handler) {
signal_set set;
(void)sigemptyset(&set);
// Handle user-generated signal for refreshing the status.
(void)sigaddset(&set, REFRESH_SIGNAL);
// Handle SIGALRM generated by the timer.
(void)sigaddset(&set, TIMER_SIGNAL);
// Handle termination signals.
(void)sigaddset(&set, SIGINT);
(void)sigaddset(&set, SIGTERM);
for (unsigned short i = 0; i < handler->block_count; ++i) {
const block *const block = &handler->blocks[i];
if (block->signal > 0) {
if (sigaddset(&set, SIGRTMIN + block->signal) != 0) {
(void)fprintf(
stderr,
"error: invalid or unsupported signal specified for "
"\"%s\" block\n",
block->command);
return 1;
}
}
}
// Create a signal file descriptor for epoll to watch.
handler->fd = signalfd(-1, &set, 0);
if (handler->fd == -1) {
(void)fprintf(stderr,
"error: could not create file descriptor for signals\n");
return 1;
}
// Block all realtime and handled signals.
for (int i = SIGRTMIN; i <= SIGRTMAX; ++i) {
(void)sigaddset(&set, i);
}
(void)sigprocmask(SIG_BLOCK, &set, NULL);
return 0;
}
int signal_handler_deinit(signal_handler *const handler) {
if (close(handler->fd) != 0) {
(void)fprintf(stderr,
"error: could not close signal file descriptor\n");
return 1;
}
return 0;
}
int signal_handler_process(signal_handler *const handler, timer *const timer) {
signal_info info;
const ssize_t bytes_read = read(handler->fd, &info, sizeof(info));
if (bytes_read == -1) {
(void)fprintf(stderr, "error: could not read info of incoming signal");
return 1;
}
const int signal = (int)info.ssi_signo;
switch (signal) {
case TIMER_SIGNAL:
if (handler->timer_callback(handler->blocks, handler->block_count,
timer) != 0) {
return 1;
}
return 0;
case REFRESH_SIGNAL:
if (handler->refresh_callback(handler->blocks,
handler->block_count) != 0) {
return 1;
}
return 0;
case SIGTERM:
// fall through
case SIGINT:
return 1;
}
for (unsigned short i = 0; i < handler->block_count; ++i) {
block *const block = &handler->blocks[i];
if (block->signal == signal - SIGRTMIN) {
const uint8_t button = (uint8_t)info.ssi_int;
block_execute(block, button);
break;
}
}
return 0;
}

View file

@ -0,0 +1,78 @@
#include "status.h"
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include "block.h"
#include "config.h"
#include "util.h"
#include "x11.h"
static bool has_status_changed(const status *const status) {
return strcmp(status->current, status->previous) != 0;
}
status status_new(const block *const blocks,
const unsigned short block_count) {
status status = {
.current = {[0] = '\0'},
.previous = {[0] = '\0'},
.blocks = blocks,
.block_count = block_count,
};
return status;
}
bool status_update(status *const status) {
(void)strncpy(status->previous, status->current, LEN(status->current));
status->current[0] = '\0';
for (unsigned short i = 0; i < status->block_count; ++i) {
const block *const block = &status->blocks[i];
if (strlen(block->output) > 0) {
#if LEADING_DELIMITER
(void)strncat(status->current, DELIMITER, LEN(DELIMITER));
#else
if (status->current[0] != '\0') {
(void)strncat(status->current, DELIMITER, LEN(DELIMITER));
}
#endif
#if CLICKABLE_BLOCKS
if (block->signal > 0) {
const char signal[] = {(char)block->signal, '\0'};
(void)strncat(status->current, signal, LEN(signal));
}
#endif
(void)strncat(status->current, block->icon, LEN(block->output));
(void)strncat(status->current, block->output, LEN(block->output));
}
}
#if TRAILING_DELIMITER
if (status->current[0] != '\0') {
(void)strncat(status->current, DELIMITER, LEN(DELIMITER));
}
#endif
return has_status_changed(status);
}
int status_write(const status *const status, const bool is_debug_mode,
x11_connection *const connection) {
if (is_debug_mode) {
(void)printf("%s\n", status->current);
return 0;
}
if (x11_set_root_name(connection, status->current) != 0) {
return 1;
}
return 0;
}

View file

@ -0,0 +1,72 @@
#include "timer.h"
#include <errno.h>
#include <stdbool.h>
#include <stdio.h>
#include <unistd.h>
#include "block.h"
#include "util.h"
static unsigned int compute_tick(const block *const blocks,
const unsigned short block_count) {
unsigned int tick = 0;
for (unsigned short i = 0; i < block_count; ++i) {
const block *const block = &blocks[i];
tick = gcd(block->interval, tick);
}
return tick;
}
static unsigned int compute_reset_value(const block *const blocks,
const unsigned short block_count) {
unsigned int reset_value = 1;
for (unsigned short i = 0; i < block_count; ++i) {
const block *const block = &blocks[i];
reset_value = MAX(block->interval, reset_value);
}
return reset_value;
}
timer timer_new(const block *const blocks, const unsigned short block_count) {
const unsigned int reset_value = compute_reset_value(blocks, block_count);
timer timer = {
.time = reset_value, // Initial value to execute all blocks.
.tick = compute_tick(blocks, block_count),
.reset_value = reset_value,
};
return timer;
}
int timer_arm(timer *const timer) {
errno = 0;
(void)alarm(timer->tick);
if (errno != 0) {
(void)fprintf(stderr, "error: could not arm timer\n");
return 1;
}
// Wrap `time` to the interval [1, reset_value].
timer->time = (timer->time + timer->tick) % timer->reset_value;
return 0;
}
bool timer_must_run_block(const timer *const timer, const block *const block) {
if (timer == NULL || timer->time == timer->reset_value) {
return true;
}
if (block->interval == 0) {
return false;
}
return timer->time % block->interval == 0;
}

View file

@ -1,41 +1,49 @@
#include "util.h"
#include <unistd.h>
#define UTF8_MULTIBYTE_BIT BIT(7)
int gcd(int a, int b) {
int temp;
unsigned int gcd(unsigned int a, unsigned int b) {
while (b > 0) {
temp = a % b;
const unsigned int temp = a % b;
a = b;
b = temp;
}
return a;
}
void closePipe(int pipe[2]) {
close(pipe[0]);
close(pipe[1]);
}
void trimUTF8(char* buffer, unsigned int size) {
int length = (size - 1) / 4;
int count = 0, j = 0;
char ch = buffer[j];
while (ch != '\0' && ch != '\n' && count < length) {
// Skip continuation bytes, if any
int skip = 1;
while ((ch & 0xc0) > 0x80) {
ch <<= 1;
skip++;
size_t truncate_utf8_string(char* const buffer, const size_t size,
const size_t char_limit) {
size_t char_count = 0;
size_t i = 0;
while (char_count < char_limit) {
char ch = buffer[i];
if (ch == '\0') {
break;
}
j += skip;
ch = buffer[j];
count++;
unsigned short skip = 1;
// Multibyte unicode character.
if ((ch & UTF8_MULTIBYTE_BIT) != 0) {
// Skip continuation bytes.
ch <<= 1;
while ((ch & UTF8_MULTIBYTE_BIT) != 0) {
ch <<= 1;
++skip;
}
}
// Avoid buffer overflow.
if (i + skip >= size) {
break;
}
++char_count;
i += skip;
}
// Trim trailing newline and spaces
buffer[j] = ' ';
while (j >= 0 && buffer[j] == ' ') j--;
buffer[j + 1] = '\0';
buffer[i] = '\0';
return i + 1;
}

View file

@ -0,0 +1,69 @@
#include "watcher.h"
#include <errno.h>
#include <poll.h>
#include <stdbool.h>
#include <stdio.h>
#include "block.h"
#include "util.h"
static bool watcher_fd_is_readable(const watcher_fd* const watcher_fd) {
return (watcher_fd->revents & POLLIN) != 0;
}
int watcher_init(watcher* const watcher, const block* const blocks,
const unsigned short block_count, const int signal_fd) {
if (signal_fd == -1) {
(void)fprintf(
stderr,
"error: invalid signal file descriptor passed to watcher\n");
return 1;
}
watcher_fd* const fd = &watcher->fds[SIGNAL_FD];
fd->fd = signal_fd;
fd->events = POLLIN;
for (unsigned short i = 0; i < block_count; ++i) {
const int block_fd = blocks[i].pipe[READ_END];
if (block_fd == -1) {
(void)fprintf(
stderr,
"error: invalid block file descriptors passed to watcher\n");
return 1;
}
watcher_fd* const fd = &watcher->fds[i];
fd->fd = block_fd;
fd->events = POLLIN;
}
return 0;
}
int watcher_poll(watcher* watcher, const int timeout_ms) {
int event_count = poll(watcher->fds, LEN(watcher->fds), timeout_ms);
// Don't return non-zero status for signal interruptions.
if (event_count == -1 && errno != EINTR) {
(void)fprintf(stderr, "error: watcher could not poll blocks\n");
return 1;
}
watcher->got_signal = watcher_fd_is_readable(&watcher->fds[SIGNAL_FD]);
watcher->active_block_count = event_count - (int)watcher->got_signal;
unsigned short i = 0;
unsigned short j = 0;
while (i < event_count && j < LEN(watcher->active_blocks)) {
if (watcher_fd_is_readable(&watcher->fds[j])) {
watcher->active_blocks[i] = j;
++i;
}
++j;
}
return 0;
}

View file

@ -1,25 +1,44 @@
#include "x11.h"
#include <X11/Xlib.h>
#include <stdio.h>
#include <string.h>
#include <xcb/xcb.h>
#include <xcb/xproto.h>
static Display *display;
static Window rootWindow;
x11_connection *x11_connection_open(void) {
xcb_connection_t *const connection = xcb_connect(NULL, NULL);
if (xcb_connection_has_error(connection)) {
(void)fprintf(stderr, "error: could not connect to X server\n");
return NULL;
}
int setupX() {
display = XOpenDisplay(NULL);
if (!display) {
return connection;
}
void x11_connection_close(xcb_connection_t *const connection) {
xcb_disconnect(connection);
}
int x11_set_root_name(x11_connection *const connection, const char *name) {
xcb_screen_t *const screen =
xcb_setup_roots_iterator(xcb_get_setup(connection)).data;
const xcb_window_t root_window = screen->root;
const unsigned short name_format = 8;
const xcb_void_cookie_t cookie = xcb_change_property(
connection, XCB_PROP_MODE_REPLACE, root_window, XCB_ATOM_WM_NAME,
XCB_ATOM_STRING, name_format, strlen(name), name);
xcb_generic_error_t *error = xcb_request_check(connection, cookie);
if (error != NULL) {
(void)fprintf(stderr, "error: could not set X root name\n");
return 1;
}
if (xcb_flush(connection) <= 0) {
(void)fprintf(stderr, "error: could not flush X output buffer\n");
return 1;
}
rootWindow = DefaultRootWindow(display);
return 0;
}
int closeX() {
return XCloseDisplay(display);
}
void setXRootName(char *str) {
XStoreName(display, rootWindow, str);
XFlush(display);
}

View file

@ -47,7 +47,11 @@ jobs:
- name: build
run: |
# libinotify-kqueue isn't available on homebrew
make clean && make -s OPT_DEP_DEFAULT=1 HAVE_INOTIFY=0
make clean && make -s OPT_DEP_DEFAULT=1 HAVE_INOTIFY=0 \
CPPFLAGS="-I/opt/homebrew/include -I/opt/homebrew/include/freetype2" \
LDLIBS="-L/opt/homebrew/lib"
# force uninstallation with --ignore-dependencies
brew uninstall --ignore-dependencies libxft libexif
make clean && make -s OPT_DEP_DEFAULT=0
make clean && make -s OPT_DEP_DEFAULT=0 \
CPPFLAGS="-I/opt/homebrew/include" \
LDLIBS="-L/opt/homebrew/lib"

View file

@ -81,8 +81,8 @@ you want to build nsxiv on a distribution with separate runtime and development
packages (e.g. \*-dev on Debian).
\* [inotify][] is a Linux-specific API for monitoring filesystem changes.
It's not natively available on `*BSD` systems but can be enabed via installing
and linking against [libinotify-kqueue][].
It's not natively available on `*BSD` systems but can be enabled via
installing and linking against [libinotify-kqueue][].
[inotify]: https://www.man7.org/linux/man-pages/man7/inotify.7.html
[libinotify-kqueue]: https://github.com/libinotify-kqueue/libinotify-kqueue

View file

@ -85,8 +85,8 @@ void arl_add(arl_t *arl, const char *filepath)
add_watch(arl->fd, &arl->wd_file, filepath, IN_CLOSE_WRITE | IN_DELETE_SELF);
base = strrchr(filepath, '/');
assert(base != NULL); /* filepath must be result of `realpath(3)` */
dir = arl_scratch_push(filepath, base - filepath);
assert(base != NULL && "filepath must be result of realpath(3)");
dir = arl_scratch_push(filepath, MAX(base - filepath, 1));
add_watch(arl->fd, &arl->wd_dir, dir, IN_CREATE | IN_MOVED_TO);
arl->filename = arl_scratch_push(base + 1, strlen(base + 1));
}

View file

@ -13,7 +13,7 @@ static const char *MARK_FG[] = { "Nsxiv.mark.foreground", NULL };
#if HAVE_LIBFONTS
static const char *BAR_BG[] = { "Nsxiv.bar.background", NULL };
static const char *BAR_FG[] = { "Nsxiv.bar.foreground", NULL };
static const char *BAR_FONT[] = { "Nsxiv.bar.font", "Hack Nerd Font-11" };
static const char *BAR_FONT[] = { "Nsxiv.bar.font", "monospace-8" };
/* if true, statusbar appears on top of the window */
static const bool TOP_STATUSBAR = false;

View file

@ -25,7 +25,6 @@ CFLAGS = -Wall -pedantic -O2 -DNDEBUG
# icons that will be installed via `make icon`
ICONS = 16x16.png 32x32.png 48x48.png 64x64.png 128x128.png
CC = gcc
# Uncomment on OpenBSD
# HAVE_INOTIFY = 0
# lib_fonts_bsd_0 =
@ -34,3 +33,5 @@ CC = gcc
# inc_fonts_bsd_1 = -I/usr/X11R6/include/freetype2
# CPPFLAGS = -I/usr/X11R6/include -I/usr/local/include $(inc_fonts_bsd_$(HAVE_LIBFONTS))
# LDLIBS = -L/usr/X11R6/lib -L/usr/local/lib $(lib_fonts_bsd_$(HAVE_LIBFONTS))
CC = gcc

View file

@ -440,7 +440,9 @@ Zoom in.
.B Button5
Zoom out.
.SH CONFIGURATION
The following X resources are supported:
The following X resources are supported under "Nsxiv" (e.g.
.B Nsxiv.bar.font
):
.TP
.B window.background
Color of the window background

View file

@ -1,15 +1,8 @@
[Desktop Entry]
Comment=
Exec=nsxiv ~/Pictures
GenericName=Image Viewer
Icon=nsxiv
Type=Application
Name=nsxiv
GenericName=Image Viewer
Exec=nsxiv %F
MimeType=image/bmp;image/gif;image/jpeg;image/jpg;image/png;image/tiff;image/x-bmp;image/x-portable-anymap;image/x-portable-bitmap;image/x-portable-graymap;image/x-tga;image/x-xpixmap;image/webp;image/heic;image/svg+xml;application/postscript;image/jp2;image/jxl;image/avif;image/heif;
NoDisplay=true
Path=
StartupNotify=true
Terminal=false
TerminalOptions=
Type=Application
X-KDE-SubstituteUID=false
X-KDE-Username=
Icon=nsxiv

View file

@ -1,6 +1,7 @@
branches: master
when:
branch: master
pipeline:
steps:
analysis:
image: alpine
commands: |

View file

@ -1,7 +1,8 @@
branches: master
when:
branch: master
# NOTE: "stable" tcc is too old and fails at linking. instead fetching a recent known working commit.
pipeline:
steps:
build:
image: alpine
environment:

View file

@ -4,13 +4,13 @@ misc-*,android-cloexec-*,llvm-include-order
-readability-*,readability-duplicate-include,readability-misleading-indentation
# silence
-misc-unused-parameters
-misc-unused-parameters,-misc-include-cleaner,-misc-no-recursion
-bugprone-easily-swappable-parameters,-bugprone-narrowing-conversions,-bugprone-incorrect-roundings
-bugprone-implicit-widening-of-multiplication-result,-bugprone-integer-division
-android-cloexec-fopen,-android-cloexec-pipe,-cert-err33-c
-bugprone-assignment-in-if-condition
-bugprone-suspicious-realloc-usage
-bugprone-switch-missing-default-case
# false positive warnings
-clang-analyzer-valist.Uninitialized
-misc-no-recursion

View file

@ -1,10 +1,13 @@
branches: master
when:
branch: master
# NOTE: codespell not available on stable alpine, use edge
pipeline:
# NOTE: codespell not available on stable alpine, grab it from pip
steps:
spell-check:
image: alpine:edge
commands: |
apk add --no-cache python3 py3-pip git
python3 -m venv ~/py3-venv
. ~/py3-venv/bin/activate
pip install codespell
git ls-files | sed '/\.png$/d' | xargs codespell

View file

@ -234,7 +234,8 @@ static bool img_load_multiframe(img_t *img, const fileinfo_t *file)
imlib_context_set_blend(!!(finfo.frame_flags & IMLIB_FRAME_BLEND));
imlib_blend_image_onto_image(frame, has_alpha, 0, 0, sw, sh, sx, sy, sw, sh);
m->frames[m->cnt].im = canvas;
m->frames[m->cnt].delay = finfo.frame_delay ? finfo.frame_delay : DEF_ANIM_DELAY;
m->frames[m->cnt].delay = m->framedelay ? m->framedelay :
(finfo.frame_delay ? finfo.frame_delay : DEF_ANIM_DELAY);
m->length += m->frames[m->cnt].delay;
m->cnt++;
img_free(frame, false);
@ -676,8 +677,8 @@ void img_rotate(img_t *img, degree_t d)
ox = d == DEGREE_90 ? img->x : img->win->w - img->x - img->w * img->zoom;
oy = d == DEGREE_270 ? img->y : img->win->h - img->y - img->h * img->zoom;
img->x = oy + (img->win->w - img->win->h) / 2;
img->y = ox + (img->win->h - img->win->w) / 2;
img->x = oy + (int)(img->win->w - img->win->h) / 2;
img->y = ox + (int)(img->win->h - img->win->w) / 2;
tmp = img->w;
img->w = img->h;

View file

@ -25,6 +25,7 @@
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <locale.h>
#include <poll.h>
#include <signal.h>
@ -249,10 +250,10 @@ void reset_timeout(timeout_f handler)
static bool check_timeouts(int *t)
{
int i = 0, tdiff, tmin = -1;
int i = 0, tdiff, tmin;
struct timeval now;
while (i < (int)ARRLEN(timeouts)) {
for (i = 0; i < (int)ARRLEN(timeouts); ++i) {
if (timeouts[i].active) {
gettimeofday(&now, 0);
tdiff = TV_DIFF(&timeouts[i].when, &now);
@ -260,16 +261,22 @@ static bool check_timeouts(int *t)
timeouts[i].active = false;
if (timeouts[i].handler != NULL)
timeouts[i].handler();
i = tmin = -1;
} else if (tmin < 0 || tdiff < tmin) {
tmin = tdiff;
}
}
i++;
}
if (tmin > 0 && t != NULL)
*t = tmin;
return tmin > 0;
tmin = INT_MAX;
gettimeofday(&now, 0);
for (i = 0; i < (int)ARRLEN(timeouts); ++i) {
if (timeouts[i].active) {
tdiff = TV_DIFF(&timeouts[i].when, &now);
tmin = MIN(tmin, tdiff);
}
}
if (tmin != INT_MAX && t != NULL)
*t = MAX(tmin, 0);
return tmin != INT_MAX;
}
static void autoreload(void)
@ -328,8 +335,7 @@ static void open_title(void)
snprintf(fcnt, ARRLEN(fcnt), "%d", filecnt);
construct_argv(argv, ARRLEN(argv), wintitle.f.cmd, files[fileidx].path,
fidx, fcnt, w, h, z, NULL);
if ((wintitle.pid = spawn(&wintitle.fd, NULL, argv)) > 0)
fcntl(wintitle.fd, F_SETFL, O_NONBLOCK);
wintitle.pid = spawn(&wintitle.fd, NULL, O_NONBLOCK, argv);
}
void close_info(void)
@ -352,8 +358,7 @@ void open_info(void)
}
construct_argv(argv, ARRLEN(argv), cmd, files[fileidx].name, w, h,
files[fileidx].path, NULL);
if ((info.pid = spawn(&info.fd, NULL, argv)) > 0)
fcntl(info.fd, F_SETFL, O_NONBLOCK);
info.pid = spawn(&info.fd, NULL, O_NONBLOCK, argv);
}
static void read_info(void)
@ -455,7 +460,6 @@ static void update_info(void)
open_title();
}
/* update bar contents */
if (win.bar.h == 0 || extprefix)
return;
@ -601,12 +605,10 @@ void handle_key_handler(bool init)
if (win.bar.h == 0)
return;
if (init) {
close_info();
snprintf(win.bar.l.buf, win.bar.l.size,
snprintf(win.bar.r.buf, win.bar.r.size,
"Getting key handler input (%s to abort)...",
XKeysymToString(KEYHANDLER_ABORT));
} else { /* abort */
open_info();
update_info();
}
win_draw(&win);
@ -635,8 +637,7 @@ static bool run_key_handler(const char *key, unsigned int mask)
if (key == NULL)
return false;
close_info();
strncpy(win.bar.l.buf, "Running key handler...", win.bar.l.size);
strncpy(win.bar.r.buf, "Running key handler...", win.bar.r.size);
win_draw(&win);
win_set_cursor(&win, CURSOR_WATCH);
setenv("NSXIV_USING_NULL", options->using_null ? "1" : "0", 1);
@ -646,7 +647,7 @@ static bool run_key_handler(const char *key, unsigned int mask)
mask & Mod1Mask ? "M-" : "",
mask & ShiftMask ? "S-" : "", key);
construct_argv(argv, ARRLEN(argv), keyhandler.f.cmd, kstr, NULL);
if ((pid = spawn(NULL, &writefd, argv)) < 0)
if ((pid = spawn(NULL, &writefd, 0x0, argv)) < 0)
return false;
if ((pfs = fdopen(writefd, "w")) == NULL) {
error(0, errno, "open pipe");
@ -687,6 +688,8 @@ static bool run_key_handler(const char *key, unsigned int mask)
if (mode == MODE_IMAGE && changed) {
img_close(&img, true);
load_image(fileidx);
} else {
update_info();
}
free(oldst);
reset_cursor();
@ -801,13 +804,15 @@ static void run(void)
pfd[FD_INFO].fd = info.fd;
pfd[FD_TITLE].fd = wintitle.fd;
pfd[FD_ARL].fd = arl.fd;
pfd[FD_X].events = pfd[FD_INFO].events = pfd[FD_TITLE].events = pfd[FD_ARL].events = POLLIN;
pfd[FD_X].events = pfd[FD_ARL].events = POLLIN;
pfd[FD_INFO].events = pfd[FD_TITLE].events = 0;
if (poll(pfd, ARRLEN(pfd), to_set ? timeout : -1) < 0)
continue;
if (pfd[FD_INFO].revents & POLLIN)
if (pfd[FD_INFO].revents & POLLHUP)
read_info();
if (pfd[FD_TITLE].revents & POLLIN)
if (pfd[FD_TITLE].revents & POLLHUP)
read_title();
if ((pfd[FD_ARL].revents & POLLIN) && arl_handle(&arl)) {
img.autoreload_pending = true;
@ -835,7 +840,7 @@ static void run(void)
}
} while (discard);
switch (ev.type) { /* handle events */
switch (ev.type) {
case ButtonPress:
on_buttonpress(&ev.xbutton);
break;

View file

@ -101,7 +101,7 @@ typedef enum {
typedef struct {
const char *name; /* as given by user */
const char *path; /* always absolute */
const char *path; /* always absolute, result of realpath(3) */
fileflags_t flags;
} fileinfo_t;
@ -180,7 +180,7 @@ typedef struct {
unsigned int cnt;
unsigned int sel;
bool animate;
unsigned int framedelay;
int framedelay;
int length;
} multi_img_t;
@ -265,7 +265,7 @@ struct opt {
/* window: */
bool fullscreen;
bool hide_bar;
Window embed; /* unsigned long */
Window embed;
char *geometry;
char *res_name;
@ -356,7 +356,7 @@ int r_closedir(r_dir_t*);
char* r_readdir(r_dir_t*, bool);
int r_mkdir(char*);
void construct_argv(char**, unsigned int, ...);
pid_t spawn(int*, int*, char *const []);
pid_t spawn(int*, int*, int, char *const []);
/* window.c */

View file

@ -43,7 +43,7 @@ static char *tns_cache_filepath(const char *filepath)
size_t len;
char *cfile = NULL;
assert(*filepath == '/' && "filepath should be result of realpath(3)");
assert(*filepath == '/' && "filepath must be result of realpath(3)");
if (strncmp(filepath, cache_dir, strlen(cache_dir)) != 0) {
/* don't cache images inside the cache directory! */
@ -181,7 +181,7 @@ void tns_init(tns_t *tns, fileinfo_t *tns_files, const int *cnt, int *sel, win_t
memcpy(cache_tmpfile, cache_dir, len - 1);
cache_tmpfile_base = cache_tmpfile + len - 1;
} else {
error(0, 0, "Cache directory not found");
error(EXIT_FAILURE, 0, "Cache directory not found");
}
}
@ -198,6 +198,8 @@ CLEANUP void tns_free(tns_t *tns)
free(cache_dir);
cache_dir = NULL;
free(cache_tmpfile);
cache_tmpfile = cache_tmpfile_base = NULL;
}
static Imlib_Image tns_scale_down(Imlib_Image im, int dim)

View file

@ -21,6 +21,7 @@
#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <spawn.h>
#include <stdarg.h>
#include <stdio.h>
@ -230,25 +231,27 @@ void construct_argv(char **argv, unsigned int len, ...)
assert(argv[len - 1] == NULL && "argv should be NULL terminated");
}
static int mkspawn_pipe(posix_spawn_file_actions_t *fa, const char *cmd, int *pfd, int dupidx)
static int mkspawn_pipe(posix_spawn_file_actions_t *fa, const char *cmd, int *pfd, int dupidx, int pipeflags)
{
int err;
int err = 0;
if (pipe(pfd) < 0) {
error(0, errno, "pipe: %s", cmd);
return -1;
}
err = posix_spawn_file_actions_adddup2(fa, pfd[dupidx], dupidx);
if (pipeflags && (fcntl(pfd[0], F_SETFL, pipeflags) < 0 || fcntl(pfd[1], F_SETFL, pipeflags) < 0))
err = errno;
err = err ? err : posix_spawn_file_actions_adddup2(fa, pfd[dupidx], dupidx);
err = err ? err : posix_spawn_file_actions_addclose(fa, pfd[0]);
err = err ? err : posix_spawn_file_actions_addclose(fa, pfd[1]);
if (err) {
error(0, err, "posix_spawn_file_actions: %s", cmd);
error(0, err, "mkspawn_pipe: %s", cmd);
close(pfd[0]);
close(pfd[1]);
}
return err ? -1 : 0;
}
pid_t spawn(int *readfd, int *writefd, char *const argv[])
pid_t spawn(int *readfd, int *writefd, int pipeflags, char *const argv[])
{
pid_t pid = -1;
const char *cmd;
@ -263,9 +266,9 @@ pid_t spawn(int *readfd, int *writefd, char *const argv[])
return pid;
}
if (readfd != NULL && mkspawn_pipe(&fa, cmd, pfd_read, 1) < 0)
if (readfd != NULL && mkspawn_pipe(&fa, cmd, pfd_read, 1, pipeflags) < 0)
goto err_destroy_fa;
if (writefd != NULL && mkspawn_pipe(&fa, cmd, pfd_write, 0) < 0)
if (writefd != NULL && mkspawn_pipe(&fa, cmd, pfd_write, 0, pipeflags) < 0)
goto err_close_readfd;
if ((err = posix_spawnp(&pid, cmd, &fa, NULL, argv, environ)) != 0) {

View file

@ -470,17 +470,15 @@ static void win_draw_bar(win_t *win)
XSetBackground(e->dpy, gc, win->bar_bg.pixel);
if ((len = strlen(r->buf)) > 0) {
if ((tw = TEXTWIDTH(win, r->buf, len)) > w) {
XftDrawDestroy(d);
return;
if ((tw = TEXTWIDTH(win, r->buf, len)) <= w) {
x = win->w - tw - H_TEXT_PAD;
win_draw_text(win, d, &win->bar_fg, x, y, r->buf, len, tw);
}
x = win->w - tw - H_TEXT_PAD;
w -= tw;
win_draw_text(win, d, &win->bar_fg, x, y, r->buf, len, tw);
/* remaining width, also keeping gap between left and right parts */
w -= tw + (2 * H_TEXT_PAD);
}
if ((len = strlen(l->buf)) > 0) {
if ((len = strlen(l->buf)) > 0 && w > 0) {
x = H_TEXT_PAD;
w -= 2 * H_TEXT_PAD; /* gap between left and right parts */
win_draw_text(win, d, &win->bar_fg, x, y, l->buf, len, w);
}
XftDrawDestroy(d);

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show more