Compare commits

...

5 commits

Author SHA1 Message Date
ce3a0db5bf slock: pantalla con reloj, fecha, batería y barra de estado
El fondo es fijo y el color del estado (inicio, escribiendo, error) pasa a
una barra inferior. Se muestran la hora, la fecha, un punto por carácter
tecleado, el usuario y la batería, refrescando reloj y batería cada
segundo. Usa Xft y los iconos de Symbols Nerd Font.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016UdJsHrbkmzhoCb6TthP2m
2026-09-27 14:29:49 +02:00
c736ea8b01 sistema: bloquear con slock antes de suspender y evitar la doble suspensión
elogind ignora la tapa y los botones para que solo actúe acpid (zzz), y un
hook de zzz lanza slock antes de suspender.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MymT1wzMgXhfC3ZwHqNAVF
2026-09-26 19:39:02 +02:00
6e3577087b slstatus: red sin procesos, zonas clicables, BT OFF y refresco cada 2 s
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MymT1wzMgXhfC3ZwHqNAVF
2026-09-26 19:39:02 +02:00
ee7e036c7f dwm: parches restartsig, preserveonrestart, statuscmd-nosignal y swallow
- Mod+Shift+r reinicia dwm sin cerrar ventanas y cada una sigue en su tag
- Clics en la barra: red (nmtui), volumen (pavucontrol, silenciar, micro,
  rueda) y bluetooth (blueman, encender/apagar)
- swallow: lo que se abre desde st sustituye a la terminal
- Reglas: flotan centradas pavucontrol, blueman, nmtui y ventanas de Firefox

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MymT1wzMgXhfC3ZwHqNAVF
2026-09-26 19:39:02 +02:00
be5e55f4a8 dwm: borde opaco para ventanas ARGB (st con alpha)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MymT1wzMgXhfC3ZwHqNAVF
2026-09-26 19:39:02 +02:00
17 changed files with 1746 additions and 71 deletions

View file

@ -4,6 +4,7 @@
static const unsigned int borderpx = 1; /* border pixel of windows */
static const unsigned int gappx = 5; /* gaps between windows */
static const unsigned int snap = 32; /* snap pixel */
static const int swallowfloating = 0; /* 1 means swallow floating windows by default */
static const int showbar = 1; /* 0 means no bar */
static const int topbar = 1; /* 0 means bottom bar */
static const char *fonts[] = { "monospace:size=10" };
@ -27,9 +28,11 @@ static const Rule rules[] = {
* WM_CLASS(STRING) = instance, class
* WM_NAME(STRING) = title
*/
/* class instance title tags mask isfloating monitor */
{ "Gimp", NULL, NULL, 0, 1, -1 },
{ "Firefox", NULL, NULL, 1 << 8, 0, -1 },
/* class instance title tags mask isfloating isterminal noswallow monitor */
{ "Gimp", NULL, NULL, 0, 1, 0, 0, -1 },
{ "Firefox", NULL, NULL, 1 << 8, 0, 0, -1, -1 },
{ "St", NULL, NULL, 0, 0, 1, 0, -1 },
{ NULL, NULL, "Event Tester", 0, 0, 0, 1, -1 }, /* xev */
};
/* layout(s) */
@ -62,6 +65,12 @@ static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn()
static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL };
static const char *termcmd[] = { "st", NULL };
/* commands spawned when clicking statusbar, the mouse button pressed is exported as BUTTON */
static const StatusCmd statuscmds[] = {
{ "notify-send Mouse$BUTTON", 1 },
};
static const char *statuscmd[] = { "/bin/sh", "-c", NULL, NULL };
static const Key keys[] = {
/* modifier key function argument */
{ MODKEY, XK_p, spawn, {.v = dmenucmd } },
@ -100,6 +109,7 @@ static const Key keys[] = {
TAGKEYS( XK_8, 7)
TAGKEYS( XK_9, 8)
{ MODKEY|ShiftMask, XK_q, quit, {0} },
{ MODKEY|ControlMask|ShiftMask, XK_q, quit, {1} },
};
/* button definitions */
@ -109,7 +119,9 @@ static const Button buttons[] = {
{ ClkLtSymbol, 0, Button1, setlayout, {0} },
{ ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} },
{ ClkWinTitle, 0, Button2, zoom, {0} },
{ ClkStatusText, 0, Button2, spawn, {.v = termcmd } },
{ ClkStatusText, 0, Button1, spawn, {.v = statuscmd } },
{ ClkStatusText, 0, Button2, spawn, {.v = statuscmd } },
{ ClkStatusText, 0, Button3, spawn, {.v = statuscmd } },
{ ClkClientWin, MODKEY, Button1, movemouse, {0} },
{ ClkClientWin, MODKEY, Button2, togglefloating, {0} },
{ ClkClientWin, MODKEY, Button3, resizemouse, {0} },

View file

@ -6,6 +6,7 @@
static const unsigned int borderpx = 2; /* border pixel of windows */
static const unsigned int gappx = 4; /* gaps between windows */
static const unsigned int snap = 32; /* snap pixel */
static const int swallowfloating = 0; /* 1 means swallow floating windows by default */
static const int showbar = 1; /* 0 means no bar */
static const int topbar = 1; /* 0 means bottom bar */
static const char *fonts[] = { "Fira Code:size=11:antialias=true:autohint=true" };
@ -32,8 +33,18 @@ static const Rule rules[] = {
* WM_CLASS(STRING) = instance, class
* WM_NAME(STRING) = title
*/
/* class instance title tags mask isfloating monitor */
{ "Gimp", NULL, NULL, 0, 1, -1 },
/* Las ventanas que flotan por una regla se abren centradas.
* swallow: lo que se abre desde una terminal (isterminal) la sustituye
* hasta que se cierra; noswallow = 1 lo evita para esa ventana. */
/* class instance title tags mask isfloating isterminal noswallow monitor */
{ "st-256color", NULL, NULL, 0, 0, 1, 0, -1 },
{ "st-float", NULL, NULL, 0, 1, 0, 1, -1 }, /* st -c st-float (p. ej. nmtui) */
{ "Gimp", NULL, NULL, 0, 1, 0, 0, -1 },
{ NULL, "pavucontrol", NULL, 0, 1, 0, 0, -1 },
{ NULL, "blueman-manager", NULL, 0, 1, 0, 0, -1 },
{ "Firefox", "Places", NULL, 0, 1, 0, 0, -1 }, /* biblioteca/descargas */
{ "Firefox", "Toolkit", NULL, 0, 1, 0, 0, -1 }, /* picture-in-picture */
{ NULL, NULL, "Event Tester", 0, 0, 0, 1, -1 }, /* xev */
};
/* layout(s) */
@ -70,10 +81,42 @@ static const char *lockcmd[] = { "slock", NULL };
/* Audio: tras cada cambio se avisa a slstatus (SIGUSR1) para que refresque la barra al momento */
#define STATUSREFRESH "; pkill -USR1 -x slstatus"
#define VOLUP SHCMD("wpctl set-volume -l 1.0 @DEFAULT_AUDIO_SINK@ 5%+" STATUSREFRESH)
#define VOLDOWN SHCMD("wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-" STATUSREFRESH)
#define VOLMUTE SHCMD("wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle" STATUSREFRESH)
#define MICMUTE SHCMD("wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle" STATUSREFRESH)
#define VOLUPCMD "wpctl set-volume -l 1.0 @DEFAULT_AUDIO_SINK@ 5%+" STATUSREFRESH
#define VOLDOWNCMD "wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-" STATUSREFRESH
#define VOLMUTECMD "wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle" STATUSREFRESH
#define MICMUTECMD "wpctl set-mute @DEFAULT_AUDIO_SOURCE@ toggle" STATUSREFRESH
#define VOLUP SHCMD(VOLUPCMD)
#define VOLDOWN SHCMD(VOLDOWNCMD)
#define VOLMUTE SHCMD(VOLMUTECMD)
#define MICMUTE SHCMD(MICMUTECMD)
/*
* Clics en la barra (parche statuscmd). El número es el byte que rodea cada
* zona en el config.h de slstatus; el botón pulsado llega en $BUTTON
* (1 izquierdo, 2 central, 3 derecho, 4/5 rueda arriba/abajo).
*/
static const StatusCmd statuscmds[] = {
/* red: izquierdo conectarse a una red, derecho el menú completo de nmtui */
{ "case $BUTTON in "
"1) st -c st-float -g 90x30 -e nmtui connect ;; "
"3) st -c st-float -g 90x30 -e nmtui ;; "
"esac", 1 },
/* volumen: izquierdo pavucontrol, central silenciar, derecho micro, rueda subir/bajar */
{ "case $BUTTON in "
"1) pavucontrol ;; "
"2) " VOLMUTECMD " ;; "
"3) " MICMUTECMD " ;; "
"4) " VOLUPCMD " ;; "
"5) " VOLDOWNCMD " ;; "
"esac", 2 },
/* bluetooth: izquierdo blueman, derecho encender/apagar el adaptador */
{ "case $BUTTON in "
"1) blueman-manager ;; "
"3) if bluetoothctl show | grep -q 'Powered: yes'; "
"then bluetoothctl power off; else bluetoothctl power on; fi >/dev/null" STATUSREFRESH " ;; "
"esac", 3 },
};
static const char *statuscmd[] = { "/bin/sh", "-c", NULL, NULL };
/* Capturas: se guardan en ~/Imágenes/Capturas y se copian al portapapeles */
#define SCREENSHOT(opts) SHCMD( \
@ -123,6 +166,7 @@ static const Key keys[] = {
TAGKEYS( XK_8, 7)
TAGKEYS( XK_9, 8)
{ MODKEY|ShiftMask, XK_m, quit, {0} },
{ MODKEY|ShiftMask, XK_r, quit, {1} }, /* reiniciar dwm sin cerrar ventanas */
/* audio */
{ 0, XF86XK_AudioRaiseVolume, spawn, VOLUP },
@ -143,7 +187,11 @@ static const Button buttons[] = {
{ ClkLtSymbol, 0, Button1, setlayout, {0} },
{ ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} },
{ ClkWinTitle, 0, Button2, zoom, {0} },
{ ClkStatusText, 0, Button2, spawn, {.v = termcmd } },
{ ClkStatusText, 0, Button1, spawn, {.v = statuscmd } },
{ ClkStatusText, 0, Button2, spawn, {.v = statuscmd } },
{ ClkStatusText, 0, Button3, spawn, {.v = statuscmd } },
{ ClkStatusText, 0, Button4, spawn, {.v = statuscmd } },
{ ClkStatusText, 0, Button5, spawn, {.v = statuscmd } },
{ ClkClientWin, MODKEY, Button1, movemouse, {0} },
{ ClkClientWin, MODKEY, Button2, togglefloating, {0} },
{ ClkClientWin, MODKEY, Button3, resizemouse, {0} },

View file

@ -20,10 +20,11 @@ FREETYPEINC = /usr/include/freetype2
# OpenBSD (uncomment)
#FREETYPEINC = ${X11INC}/freetype2
#MANPREFIX = ${PREFIX}/man
#KVMLIB = -lkvm
# includes and libs
INCS = -I${X11INC} -I${FREETYPEINC}
LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS}
LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} -lX11-xcb -lxcb -lxcb-res ${KVMLIB}
# flags
CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}

View file

@ -176,6 +176,9 @@ drw_clr_create(Drw *drw, Clr *dest, const char *clrname)
DefaultColormap(drw->dpy, drw->screen),
clrname, dest))
die("error, cannot allocate color '%s'", clrname);
/* alfa opaco: si no, el borde de las ventanas ARGB (st con alpha) sale transparente */
dest->pixel |= 0xffU << 24;
}
/* Create color schemes. */

View file

@ -142,6 +142,9 @@ Add/remove all windows with nth tag to/from the view.
.TP
.B Mod1\-Shift\-q
Quit dwm.
.TP
.B Mod1\-Control\-Shift\-q
Restart dwm.
.SS Mouse commands
.TP
.B Mod1\-Button1
@ -155,6 +158,13 @@ Resize focused window while dragging. Tiled windows will be toggled to the float
.SH CUSTOMIZATION
dwm is customized by creating a custom config.h and (re)compiling the source
code. This keeps it fast, secure and simple.
.SH SIGNALS
.TP
.B SIGHUP - 1
Restart the dwm process.
.TP
.B SIGTERM - 15
Cleanly terminate the dwm process.
.SH SEE ALSO
.BR dmenu (1),
.BR st (1)

387
dwm/dwm.c
View file

@ -40,6 +40,12 @@
#include <X11/extensions/Xinerama.h>
#endif /* XINERAMA */
#include <X11/Xft/Xft.h>
#include <X11/Xlib-xcb.h>
#include <xcb/res.h>
#ifdef __OpenBSD__
#include <sys/sysctl.h>
#include <kvm.h>
#endif /* __OpenBSD */
#include "drw.h"
#include "util.h"
@ -61,7 +67,7 @@ enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
enum { SchemeNorm, SchemeSel }; /* color schemes */
enum { NetSupported, NetWMName, NetWMState, NetWMCheck,
NetWMFullscreen, NetActiveWindow, NetWMWindowType,
NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
NetWMWindowTypeDialog, NetClientList, NetClientInfo, NetLast }; /* EWMH atoms */
enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
@ -91,9 +97,11 @@ struct Client {
int basew, baseh, incw, inch, maxw, maxh, minw, minh, hintsvalid;
int bw, oldbw;
unsigned int tags;
int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen;
int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen, isterminal, noswallow;
pid_t pid;
Client *next;
Client *snext;
Client *swallowing;
Monitor *mon;
Window win;
};
@ -138,9 +146,16 @@ typedef struct {
const char *title;
unsigned int tags;
int isfloating;
int isterminal;
int noswallow;
int monitor;
} Rule;
typedef struct {
const char *cmd;
int id;
} StatusCmd;
/* function declarations */
static void applyrules(Client *c);
static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact);
@ -198,6 +213,7 @@ static void scan(void);
static int sendevent(Client *c, Atom proto);
static void sendmon(Client *c, Monitor *m);
static void setclientstate(Client *c, long state);
static void setclienttagprop(Client *c);
static void setfocus(Client *c);
static void setfullscreen(Client *c, int fullscreen);
static void setgaps(const Arg *arg);
@ -206,6 +222,8 @@ static void setmfact(const Arg *arg);
static void setup(void);
static void seturgent(Client *c, int urg);
static void showhide(Client *c);
static void sighup(int unused);
static void sigterm(int unused);
static void spawn(const Arg *arg);
static void tag(const Arg *arg);
static void tagmon(const Arg *arg);
@ -235,9 +253,18 @@ static int xerrordummy(Display *dpy, XErrorEvent *ee);
static int xerrorstart(Display *dpy, XErrorEvent *ee);
static void zoom(const Arg *arg);
static pid_t getparentprocess(pid_t p);
static int isdescprocess(pid_t p, pid_t c);
static Client *swallowingclient(Window w);
static Client *termforwin(const Client *c);
static pid_t winpid(Window w);
/* variables */
static const char broken[] = "broken";
static char stext[256];
static int statusw;
static int statuscmdn;
static char lastbutton[] = "-";
static int screen;
static int sw, sh; /* X display screen geometry width, height */
static int bh; /* bar height */
@ -261,6 +288,7 @@ static void (*handler[LASTEvent]) (XEvent *) = {
[UnmapNotify] = unmapnotify
};
static Atom wmatom[WMLast], netatom[NetLast];
static int restart = 0;
static int running = 1;
static Cur *cursor[CurLast];
static Clr **scheme;
@ -269,6 +297,8 @@ static Drw *drw;
static Monitor *mons, *selmon;
static Window root, wmcheckwin;
static xcb_connection_t *xcon;
/* configuration, allows nested code to access above variables */
#include "config.h"
@ -298,6 +328,8 @@ applyrules(Client *c)
&& (!r->class || strstr(class, r->class))
&& (!r->instance || strstr(instance, r->instance)))
{
c->isterminal = r->isterminal;
c->noswallow = r->noswallow;
c->isfloating = r->isfloating;
c->tags |= r->tags;
for (m = mons; m && m->num != r->monitor; m = m->next);
@ -416,6 +448,53 @@ attachstack(Client *c)
c->mon->stack = c;
}
void
swallow(Client *p, Client *c)
{
if (c->noswallow > 0 || c->isterminal)
return;
if (c->noswallow < 0 && !swallowfloating && c->isfloating)
return;
detach(c);
detachstack(c);
setclientstate(c, WithdrawnState);
XUnmapWindow(dpy, p->win);
p->swallowing = c;
c->mon = p->mon;
Window w = p->win;
p->win = c->win;
c->win = w;
updatetitle(p);
XMoveResizeWindow(dpy, p->win, p->x, p->y, p->w, p->h);
arrange(p->mon);
configure(p);
updateclientlist();
}
void
unswallow(Client *c)
{
c->win = c->swallowing->win;
free(c->swallowing);
c->swallowing = NULL;
/* unfullscreen the client */
setfullscreen(c, 0);
updatetitle(c);
arrange(c->mon);
XMapWindow(dpy, c->win);
XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);
setclientstate(c, NormalState);
focus(NULL);
arrange(c->mon);
}
void
buttonpress(XEvent *e)
{
@ -442,9 +521,28 @@ buttonpress(XEvent *e)
arg.ui = 1 << i;
} else if (ev->x < x + TEXTW(selmon->ltsymbol))
click = ClkLtSymbol;
else if (ev->x > selmon->ww - (int)TEXTW(stext) + lrpad - 2)
else if (ev->x > selmon->ww - statusw) {
char *text, *s, ch;
*lastbutton = '0' + ev->button;
x = selmon->ww - statusw;
click = ClkStatusText;
else
statuscmdn = 0;
for (text = s = stext; *s && x <= ev->x; s++) {
if ((unsigned char)(*s) < ' ') {
ch = *s;
*s = '\0';
x += TEXTW(text) - lrpad;
*s = ch;
text = s + 1;
if (x >= ev->x)
break;
/* el mismo byte al final cierra la sección clicable */
statuscmdn = (statuscmdn == ch) ? 0 : ch;
}
}
} else
click = ClkWinTitle;
} else if ((c = wintoclient(ev->window))) {
focus(c);
@ -657,6 +755,9 @@ destroynotify(XEvent *e)
if ((c = wintoclient(ev->window)))
unmanage(c, 1);
else if ((c = swallowingclient(ev->window)))
unmanage(c->swallowing, 1);
}
void
@ -711,9 +812,24 @@ drawbar(Monitor *m)
/* draw status first so it can be overdrawn by tags later */
if (m == selmon) { /* status is only drawn on selected monitor */
char *text, *s, ch;
drw_setscheme(drw, scheme[SchemeNorm]);
tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */
drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0);
x = 0;
for (text = s = stext; *s; s++) {
if ((unsigned char)(*s) < ' ') {
ch = *s;
*s = '\0';
tw = TEXTW(text) - lrpad;
drw_text(drw, m->ww - statusw + x, 0, tw, bh, 0, text, 0);
x += tw;
*s = ch;
text = s + 1;
}
}
tw = TEXTW(text) - lrpad + 2;
drw_text(drw, m->ww - statusw + x, 0, tw, bh, 0, text, 0);
tw = statusw;
}
for (c = m->clients; c; c = c->next) {
@ -1034,12 +1150,13 @@ killclient(const Arg *arg)
void
manage(Window w, XWindowAttributes *wa)
{
Client *c, *t = NULL;
Client *c, *t = NULL, *term = NULL;
Window trans = None;
XWindowChanges wc;
c = ecalloc(1, sizeof(Client));
c->win = w;
c->pid = winpid(w);
/* geometry */
c->x = c->oldx = wa->x;
c->y = c->oldy = wa->y;
@ -1054,6 +1171,12 @@ manage(Window w, XWindowAttributes *wa)
} else {
c->mon = selmon;
applyrules(c);
/* las ventanas que flotan por una regla se abren centradas */
if (c->isfloating) {
c->x = c->mon->wx + (c->mon->ww - WIDTH(c)) / 2;
c->y = c->mon->wy + (c->mon->wh - HEIGHT(c)) / 2;
}
term = termforwin(c);
}
if (c->x + WIDTH(c) > c->mon->wx + c->mon->ww)
@ -1071,6 +1194,26 @@ manage(Window w, XWindowAttributes *wa)
updatewindowtype(c);
updatesizehints(c);
updatewmhints(c);
{
int format;
unsigned long *data = NULL, n = 0, extra;
Monitor *m;
Atom atom;
if (XGetWindowProperty(dpy, c->win, netatom[NetClientInfo], 0L, 2L, False, XA_CARDINAL,
&atom, &format, &n, &extra, (unsigned char **)&data) == Success && n == 2 && (*data & TAGMASK)) {
c->tags = *data & TAGMASK;
for (m = mons; m; m = m->next) {
if (m->num == *(data+1)) {
c->mon = m;
break;
}
}
}
if (data)
XFree(data);
}
setclienttagprop(c);
XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask);
grabbuttons(c, 0);
if (!c->isfloating)
@ -1088,6 +1231,8 @@ manage(Window w, XWindowAttributes *wa)
c->mon->sel = c;
arrange(c->mon);
XMapWindow(dpy, c->win);
if (term)
swallow(term, c);
focus(NULL);
}
@ -1261,6 +1406,7 @@ propertynotify(XEvent *e)
void
quit(const Arg *arg)
{
if(arg->i) restart = 1;
running = 0;
}
@ -1432,6 +1578,7 @@ sendmon(Client *c, Monitor *m)
c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */
attach(c);
attachstack(c);
setclienttagprop(c);
if (c->isfullscreen)
resizeclient(c, m->mx, m->my, m->mw, m->mh);
focus(NULL);
@ -1566,6 +1713,9 @@ setup(void)
/* clean up any zombies (inherited from .xinitrc etc) immediately */
while (waitpid(-1, NULL, WNOHANG) > 0);
signal(SIGHUP, sighup);
signal(SIGTERM, sigterm);
/* init screen */
screen = DefaultScreen(dpy);
sw = DisplayWidth(dpy, screen);
@ -1592,6 +1742,7 @@ setup(void)
netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False);
netatom[NetClientInfo] = XInternAtom(dpy, "_NET_CLIENT_INFO", False);
/* init cursors */
cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr);
cursor[CurResize] = drw_cur_create(drw, XC_sizing);
@ -1615,6 +1766,7 @@ setup(void)
XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32,
PropModeReplace, (unsigned char *) netatom, NetLast);
XDeleteProperty(dpy, root, netatom[NetClientList]);
XDeleteProperty(dpy, root, netatom[NetClientInfo]);
/* select events */
wa.cursor = cursor[CurNormal]->cursor;
wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask
@ -1657,6 +1809,20 @@ showhide(Client *c)
}
}
void
sighup(int unused)
{
Arg a = {.i = 1};
quit(&a);
}
void
sigterm(int unused)
{
Arg a = {.i = 0};
quit(&a);
}
void
spawn(const Arg *arg)
{
@ -1667,6 +1833,17 @@ spawn(const Arg *arg)
if (fork() == 0) {
if (dpy)
close(ConnectionNumber(dpy));
if (arg->v == statuscmd) {
for (int i = 0; i < LENGTH(statuscmds); i++) {
if (statuscmdn == statuscmds[i].id) {
statuscmd[2] = statuscmds[i].cmd;
setenv("BUTTON", lastbutton, 1);
break;
}
}
if (!statuscmd[2])
exit(EXIT_SUCCESS);
}
setsid();
sigemptyset(&sa.sa_mask);
@ -1679,11 +1856,22 @@ spawn(const Arg *arg)
}
}
void
setclienttagprop(Client *c)
{
long data[] = { (long) c->tags, (long) c->mon->num };
XChangeProperty(dpy, c->win, netatom[NetClientInfo], XA_CARDINAL, 32,
PropModeReplace, (unsigned char *) data, 2);
}
void
tag(const Arg *arg)
{
Client *c;
if (selmon->sel && arg->ui & TAGMASK) {
c = selmon->sel;
selmon->sel->tags = arg->ui & TAGMASK;
setclienttagprop(c);
focus(NULL);
arrange(selmon);
}
@ -1758,6 +1946,7 @@ toggletag(const Arg *arg)
newtags = selmon->sel->tags ^ (arg->ui & TAGMASK);
if (newtags) {
selmon->sel->tags = newtags;
setclienttagprop(selmon->sel);
focus(NULL);
arrange(selmon);
}
@ -1794,6 +1983,20 @@ unmanage(Client *c, int destroyed)
Monitor *m = c->mon;
XWindowChanges wc;
if (c->swallowing) {
unswallow(c);
return;
}
Client *s = swallowingclient(c->win);
if (s) {
free(s->swallowing);
s->swallowing = NULL;
arrange(m);
focus(NULL);
return;
}
detach(c);
detachstack(c);
if (!destroyed) {
@ -1809,9 +2012,12 @@ unmanage(Client *c, int destroyed)
XUngrabServer(dpy);
}
free(c);
focus(NULL);
updateclientlist();
arrange(m);
if (!s) {
arrange(m);
focus(NULL);
updateclientlist();
}
}
void
@ -2018,8 +2224,23 @@ updatesizehints(Client *c)
void
updatestatus(void)
{
if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext)))
if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) {
strcpy(stext, "dwm-"VERSION);
statusw = TEXTW(stext) - lrpad + 2;
} else {
char *text, *s, ch;
statusw = 0;
for (text = s = stext; *s; s++) {
if ((unsigned char)(*s) < ' ') {
ch = *s;
*s = '\0';
statusw += TEXTW(text) - lrpad;
*s = ch;
text = s + 1;
}
}
statusw += TEXTW(text) - lrpad + 2;
}
drawbar(selmon);
}
@ -2075,6 +2296,136 @@ view(const Arg *arg)
arrange(selmon);
}
pid_t
winpid(Window w)
{
pid_t result = 0;
#ifdef __linux__
xcb_res_client_id_spec_t spec = {0};
spec.client = w;
spec.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID;
xcb_generic_error_t *e = NULL;
xcb_res_query_client_ids_cookie_t c = xcb_res_query_client_ids(xcon, 1, &spec);
xcb_res_query_client_ids_reply_t *r = xcb_res_query_client_ids_reply(xcon, c, &e);
if (!r)
return (pid_t)0;
xcb_res_client_id_value_iterator_t i = xcb_res_query_client_ids_ids_iterator(r);
for (; i.rem; xcb_res_client_id_value_next(&i)) {
spec = i.data->spec;
if (spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID) {
uint32_t *t = xcb_res_client_id_value_value(i.data);
result = *t;
break;
}
}
free(r);
if (result == (pid_t)-1)
result = 0;
#endif /* __linux__ */
#ifdef __OpenBSD__
Atom type;
int format;
unsigned long len, bytes;
unsigned char *prop;
pid_t ret;
if (XGetWindowProperty(dpy, w, XInternAtom(dpy, "_NET_WM_PID", 0), 0, 1, False, AnyPropertyType, &type, &format, &len, &bytes, &prop) != Success || !prop)
return 0;
ret = *(pid_t*)prop;
XFree(prop);
result = ret;
#endif /* __OpenBSD__ */
return result;
}
pid_t
getparentprocess(pid_t p)
{
unsigned int v = 0;
#ifdef __linux__
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);
#endif /* __linux__*/
#ifdef __OpenBSD__
int n;
kvm_t *kd;
struct kinfo_proc *kp;
kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, NULL);
if (!kd)
return 0;
kp = kvm_getprocs(kd, KERN_PROC_PID, p, sizeof(*kp), &n);
v = kp->p_ppid;
#endif /* __OpenBSD__ */
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(const Client *w)
{
Client *c;
Monitor *m;
if (!w->pid || w->isterminal)
return NULL;
for (m = mons; m; m = m->next) {
for (c = m->clients; c; c = c->next) {
if (c->isterminal && !c->swallowing && c->pid && isdescprocess(c->pid, w->pid))
return c;
}
}
return NULL;
}
Client *
swallowingclient(Window w)
{
Client *c;
Monitor *m;
for (m = mons; m; m = m->next) {
for (c = m->clients; c; c = c->next) {
if (c->swallowing && c->swallowing->win == w)
return c;
}
}
return NULL;
}
Client *
wintoclient(Window w)
{
@ -2164,14 +2515,26 @@ main(int argc, char *argv[])
fputs("warning: no locale support\n", stderr);
if (!(dpy = XOpenDisplay(NULL)))
die("dwm: cannot open display");
if (!(xcon = XGetXCBConnection(dpy)))
die("dwm: cannot get xcb connection\n");
checkotherwm();
setup();
#ifdef __OpenBSD__
if (pledge("stdio rpath proc exec", NULL) == -1)
if (pledge("stdio rpath proc exec ps", NULL) == -1)
die("pledge");
#endif /* __OpenBSD__ */
scan();
run();
if (restart) {
/* volver a mostrar las terminales tragadas (swallow); si no, el dwm
* nuevo no las ve y se quedan ocultas para siempre */
for (Monitor *m = mons; m; m = m->next)
for (Client *c = m->clients; c; c = c->next)
if (c->swallowing)
XMapWindow(dpy, c->swallowing->win);
XSync(dpy, False);
execvp(argv[0], argv);
}
cleanup();
XCloseDisplay(dpy);
return EXIT_SUCCESS;

View file

@ -0,0 +1,118 @@
From 713fa8650f5a20006451ebcccf57a4512e83bae8 Mon Sep 17 00:00:00 2001
From: Arda Atci <arda@phytech.io>
Date: Wed, 18 May 2022 17:23:16 +0300
Subject: [PATCH] preserve clients on old tags when renewing dwm
By default, when dwm is recompiled-restarted all clients will
lose it's current tag and collapse to first tag. This patch preserves
clients on old tags, however note that layout order is not preserved.
---
dwm.c | 38 +++++++++++++++++++++++++++++++++++++-
1 file changed, 37 insertions(+), 1 deletion(-)
diff --git a/dwm.c b/dwm.c
index a96f33c..a12e0bd 100644
--- a/dwm.c
+++ b/dwm.c
@@ -62,7 +62,7 @@ enum { CurNormal, CurResize, CurMove, CurLast }; /* cursor */
enum { SchemeNorm, SchemeSel }; /* color schemes */
enum { NetSupported, NetWMName, NetWMState, NetWMCheck,
NetWMFullscreen, NetActiveWindow, NetWMWindowType,
- NetWMWindowTypeDialog, NetClientList, NetLast }; /* EWMH atoms */
+ NetWMWindowTypeDialog, NetClientList, NetClientInfo, NetLast }; /* EWMH atoms */
enum { WMProtocols, WMDelete, WMState, WMTakeFocus, WMLast }; /* default atoms */
enum { ClkTagBar, ClkLtSymbol, ClkStatusText, ClkWinTitle,
ClkClientWin, ClkRootWin, ClkLast }; /* clicks */
@@ -198,6 +198,7 @@ static void scan(void);
static int sendevent(Client *c, Atom proto);
static void sendmon(Client *c, Monitor *m);
static void setclientstate(Client *c, long state);
+static void setclienttagprop(Client *c);
static void setfocus(Client *c);
static void setfullscreen(Client *c, int fullscreen);
static void setlayout(const Arg *arg);
@@ -1060,6 +1061,26 @@ manage(Window w, XWindowAttributes *wa)
updatewindowtype(c);
updatesizehints(c);
updatewmhints(c);
+ {
+ int format;
+ unsigned long *data, n, extra;
+ Monitor *m;
+ Atom atom;
+ if (XGetWindowProperty(dpy, c->win, netatom[NetClientInfo], 0L, 2L, False, XA_CARDINAL,
+ &atom, &format, &n, &extra, (unsigned char **)&data) == Success && n == 2) {
+ c->tags = *data;
+ for (m = mons; m; m = m->next) {
+ if (m->num == *(data+1)) {
+ c->mon = m;
+ break;
+ }
+ }
+ }
+ if (n > 0)
+ XFree(data);
+ }
+ setclienttagprop(c);
+
XSelectInput(dpy, w, EnterWindowMask|FocusChangeMask|PropertyChangeMask|StructureNotifyMask);
grabbuttons(c, 0);
if (!c->isfloating)
@@ -1423,6 +1444,7 @@ sendmon(Client *c, Monitor *m)
c->tags = m->tagset[m->seltags]; /* assign tags of target monitor */
attach(c);
attachstack(c);
+ setclienttagprop(c);
focus(NULL);
arrange(NULL);
}
@@ -1566,6 +1588,7 @@ setup(void)
netatom[NetWMWindowType] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE", False);
netatom[NetWMWindowTypeDialog] = XInternAtom(dpy, "_NET_WM_WINDOW_TYPE_DIALOG", False);
netatom[NetClientList] = XInternAtom(dpy, "_NET_CLIENT_LIST", False);
+ netatom[NetClientInfo] = XInternAtom(dpy, "_NET_CLIENT_INFO", False);
/* init cursors */
cursor[CurNormal] = drw_cur_create(drw, XC_left_ptr);
cursor[CurResize] = drw_cur_create(drw, XC_sizing);
@@ -1589,6 +1612,7 @@ setup(void)
XChangeProperty(dpy, root, netatom[NetSupported], XA_ATOM, 32,
PropModeReplace, (unsigned char *) netatom, NetLast);
XDeleteProperty(dpy, root, netatom[NetClientList]);
+ XDeleteProperty(dpy, root, netatom[NetClientInfo]);
/* select events */
wa.cursor = cursor[CurNormal]->cursor;
wa.event_mask = SubstructureRedirectMask|SubstructureNotifyMask
@@ -1656,11 +1680,22 @@ spawn(const Arg *arg)
}
}
+void
+setclienttagprop(Client *c)
+{
+ long data[] = { (long) c->tags, (long) c->mon->num };
+ XChangeProperty(dpy, c->win, netatom[NetClientInfo], XA_CARDINAL, 32,
+ PropModeReplace, (unsigned char *) data, 2);
+}
+
void
tag(const Arg *arg)
{
+ Client *c;
if (selmon->sel && arg->ui & TAGMASK) {
+ c = selmon->sel;
selmon->sel->tags = arg->ui & TAGMASK;
+ setclienttagprop(c);
focus(NULL);
arrange(selmon);
}
@@ -1735,6 +1770,7 @@ toggletag(const Arg *arg)
newtags = selmon->sel->tags ^ (arg->ui & TAGMASK);
if (newtags) {
selmon->sel->tags = newtags;
+ setclienttagprop(selmon->sel);
focus(NULL);
arrange(selmon);
}
--
2.36.1

View file

@ -0,0 +1,139 @@
From 2991f37f0aaf44b9f9b11e7893ff0af8eb88f649 Mon Sep 17 00:00:00 2001
From: Christopher Drelich <cd@cdrakka.com>
Date: Wed, 23 May 2018 22:50:38 -0400
Subject: [PATCH] Modifies quit to handle restarts and adds SIGHUP and SIGTERM
handlers.
Modified quit() to restart if it receives arg .i = 1
MOD+CTRL+SHIFT+Q was added to confid.def.h to do just that.
Signal handlers were handled for SIGHUP and SIGTERM.
If dwm receives these signals it calls quit() with
arg .i = to 1 or 0, respectively.
To restart dwm:
MOD+CTRL+SHIFT+Q
or
kill -HUP dwmpid
To quit dwm cleanly:
MOD+SHIFT+Q
or
kill -TERM dwmpid
---
config.def.h | 1 +
dwm.1 | 10 ++++++++++
dwm.c | 22 ++++++++++++++++++++++
3 files changed, 33 insertions(+)
diff --git a/config.def.h b/config.def.h
index a9ac303..e559429 100644
--- a/config.def.h
+++ b/config.def.h
@@ -94,6 +94,7 @@ static Key keys[] = {
TAGKEYS( XK_8, 7)
TAGKEYS( XK_9, 8)
{ MODKEY|ShiftMask, XK_q, quit, {0} },
+ { MODKEY|ControlMask|ShiftMask, XK_q, quit, {1} },
};
/* button definitions */
diff --git a/dwm.1 b/dwm.1
index 13b3729..36a331c 100644
--- a/dwm.1
+++ b/dwm.1
@@ -142,6 +142,9 @@ Add/remove all windows with nth tag to/from the view.
.TP
.B Mod1\-Shift\-q
Quit dwm.
+.TP
+.B Mod1\-Control\-Shift\-q
+Restart dwm.
.SS Mouse commands
.TP
.B Mod1\-Button1
@@ -155,6 +158,13 @@ Resize focused window while dragging. Tiled windows will be toggled to the float
.SH CUSTOMIZATION
dwm is customized by creating a custom config.h and (re)compiling the source
code. This keeps it fast, secure and simple.
+.SH SIGNALS
+.TP
+.B SIGHUP - 1
+Restart the dwm process.
+.TP
+.B SIGTERM - 15
+Cleanly terminate the dwm process.
.SH SEE ALSO
.BR dmenu (1),
.BR st (1)
diff --git a/dwm.c b/dwm.c
index bb95e26..286eecd 100644
--- a/dwm.c
+++ b/dwm.c
@@ -205,6 +205,8 @@ static void setup(void);
static void seturgent(Client *c, int urg);
static void showhide(Client *c);
static void sigchld(int unused);
+static void sighup(int unused);
+static void sigterm(int unused);
static void spawn(const Arg *arg);
static void tag(const Arg *arg);
static void tagmon(const Arg *arg);
@@ -260,6 +262,7 @@ static void (*handler[LASTEvent]) (XEvent *) = {
[UnmapNotify] = unmapnotify
};
static Atom wmatom[WMLast], netatom[NetLast];
+static int restart = 0;
static int running = 1;
static Cur *cursor[CurLast];
static Clr **scheme;
@@ -1248,6 +1251,7 @@ propertynotify(XEvent *e)
void
quit(const Arg *arg)
{
+ if(arg->i) restart = 1;
running = 0;
}
@@ -1536,6 +1540,9 @@ setup(void)
/* clean up any zombies immediately */
sigchld(0);
+ signal(SIGHUP, sighup);
+ signal(SIGTERM, sigterm);
+
/* init screen */
screen = DefaultScreen(dpy);
sw = DisplayWidth(dpy, screen);
@@ -1637,6 +1644,20 @@ sigchld(int unused)
}
void
+sighup(int unused)
+{
+ Arg a = {.i = 1};
+ quit(&a);
+}
+
+void
+sigterm(int unused)
+{
+ Arg a = {.i = 0};
+ quit(&a);
+}
+
+void
spawn(const Arg *arg)
{
if (arg->v == dmenucmd)
@@ -2139,6 +2160,7 @@ main(int argc, char *argv[])
setup();
scan();
run();
+ if(restart) execvp(argv[0], argv);
cleanup();
XCloseDisplay(dpy);
return EXIT_SUCCESS;
--
2.7.4

View file

@ -0,0 +1,167 @@
From 02c4a28dd7f3a88eef3a4e533ace35f79cf09d57 Mon Sep 17 00:00:00 2001
From: Daniel Bylinka <daniel.bylinka@gmail.com>
Date: Fri, 2 Apr 2021 19:34:38 +0200
Subject: [PATCH] [statuscmd] Run shell commands based on mouse location and
button
---
config.def.h | 10 ++++++-
dwm.c | 76 +++++++++++++++++++++++++++++++++++++++++++++++++---
2 files changed, 81 insertions(+), 5 deletions(-)
diff --git a/config.def.h b/config.def.h
index 1c0b587..8f88366 100644
--- a/config.def.h
+++ b/config.def.h
@@ -59,6 +59,12 @@ static char dmenumon[2] = "0"; /* component of dmenucmd, manipulated in spawn()
static const char *dmenucmd[] = { "dmenu_run", "-m", dmenumon, "-fn", dmenufont, "-nb", col_gray1, "-nf", col_gray3, "-sb", col_cyan, "-sf", col_gray4, NULL };
static const char *termcmd[] = { "st", NULL };
+/* commands spawned when clicking statusbar, the mouse button pressed is exported as BUTTON */
+static const StatusCmd statuscmds[] = {
+ { "notify-send Mouse$BUTTON", 1 },
+};
+static const char *statuscmd[] = { "/bin/sh", "-c", NULL, NULL };
+
static Key keys[] = {
/* modifier key function argument */
{ MODKEY, XK_p, spawn, {.v = dmenucmd } },
@@ -103,7 +109,9 @@ static Button buttons[] = {
{ ClkLtSymbol, 0, Button1, setlayout, {0} },
{ ClkLtSymbol, 0, Button3, setlayout, {.v = &layouts[2]} },
{ ClkWinTitle, 0, Button2, zoom, {0} },
- { ClkStatusText, 0, Button2, spawn, {.v = termcmd } },
+ { ClkStatusText, 0, Button1, spawn, {.v = statuscmd } },
+ { ClkStatusText, 0, Button2, spawn, {.v = statuscmd } },
+ { ClkStatusText, 0, Button3, spawn, {.v = statuscmd } },
{ ClkClientWin, MODKEY, Button1, movemouse, {0} },
{ ClkClientWin, MODKEY, Button2, togglefloating, {0} },
{ ClkClientWin, MODKEY, Button3, resizemouse, {0} },
diff --git a/dwm.c b/dwm.c
index b0b3466..eb478a5 100644
--- a/dwm.c
+++ b/dwm.c
@@ -141,6 +141,11 @@ typedef struct {
int monitor;
} Rule;
+typedef struct {
+ const char *cmd;
+ int id;
+} StatusCmd;
+
/* function declarations */
static void applyrules(Client *c);
static int applysizehints(Client *c, int *x, int *y, int *w, int *h, int interact);
@@ -238,6 +243,9 @@ static void zoom(const Arg *arg);
/* variables */
static const char broken[] = "broken";
static char stext[256];
+static int statusw;
+static int statuscmdn;
+static char lastbutton[] = "-";
static int screen;
static int sw, sh; /* X display screen geometry width, height */
static int bh, blw = 0; /* bar geometry */
@@ -440,8 +448,27 @@ buttonpress(XEvent *e)
arg.ui = 1 << i;
} else if (ev->x < x + blw)
click = ClkLtSymbol;
- else if (ev->x > selmon->ww - (int)TEXTW(stext))
+ else if (ev->x > selmon->ww - statusw) {
+ char *text, *s, ch;
+ *lastbutton = '0' + ev->button;
+
+ x = selmon->ww - statusw;
click = ClkStatusText;
+
+ statuscmdn = 0;
+ for (text = s = stext; *s && x <= ev->x; s++) {
+ if ((unsigned char)(*s) < ' ') {
+ ch = *s;
+ *s = '\0';
+ x += TEXTW(text) - lrpad;
+ *s = ch;
+ text = s + 1;
+ if (x >= ev->x)
+ break;
+ statuscmdn = ch;
+ }
+ }
+ }
else
click = ClkWinTitle;
} else if ((c = wintoclient(ev->window))) {
@@ -704,9 +731,24 @@ drawbar(Monitor *m)
/* draw status first so it can be overdrawn by tags later */
if (m == selmon) { /* status is only drawn on selected monitor */
+ char *text, *s, ch;
drw_setscheme(drw, scheme[SchemeNorm]);
- tw = TEXTW(stext) - lrpad + 2; /* 2px right padding */
- drw_text(drw, m->ww - tw, 0, tw, bh, 0, stext, 0);
+
+ x = 0;
+ for (text = s = stext; *s; s++) {
+ if ((unsigned char)(*s) < ' ') {
+ ch = *s;
+ *s = '\0';
+ tw = TEXTW(text) - lrpad;
+ drw_text(drw, m->ww - statusw + x, 0, tw, bh, 0, text, 0);
+ x += tw;
+ *s = ch;
+ text = s + 1;
+ }
+ }
+ tw = TEXTW(text) - lrpad + 2;
+ drw_text(drw, m->ww - statusw + x, 0, tw, bh, 0, text, 0);
+ tw = statusw;
}
for (c = m->clients; c; c = c->next) {
@@ -1645,6 +1687,17 @@ spawn(const Arg *arg)
if (fork() == 0) {
if (dpy)
close(ConnectionNumber(dpy));
+ if (arg->v == statuscmd) {
+ for (int i = 0; i < LENGTH(statuscmds); i++) {
+ if (statuscmdn == statuscmds[i].id) {
+ statuscmd[2] = statuscmds[i].cmd;
+ setenv("BUTTON", lastbutton, 1);
+ break;
+ }
+ }
+ if (!statuscmd[2])
+ exit(EXIT_SUCCESS);
+ }
setsid();
execvp(((char **)arg->v)[0], (char **)arg->v);
fprintf(stderr, "dwm: execvp %s", ((char **)arg->v)[0]);
@@ -1990,8 +2043,23 @@ updatesizehints(Client *c)
void
updatestatus(void)
{
- if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext)))
+ if (!gettextprop(root, XA_WM_NAME, stext, sizeof(stext))) {
strcpy(stext, "dwm-"VERSION);
+ statusw = TEXTW(stext) - lrpad + 2;
+ } else {
+ char *text, *s, ch;
+ statusw = 0;
+ for (text = s = stext; *s; s++) {
+ if ((unsigned char)(*s) < ' ') {
+ ch = *s;
+ *s = '\0';
+ statusw += TEXTW(text) - lrpad;
+ *s = ch;
+ text = s + 1;
+ }
+ }
+ statusw += TEXTW(text) - lrpad + 2;
+ }
drawbar(selmon);
}
--
2.31.0

View file

@ -0,0 +1,412 @@
From 0cf9a007511f7dfd7dd94171b172562ebac9b6d5 Mon Sep 17 00:00:00 2001
From: Tom Schwindl <schwindl@posteo.de>
Date: Sat, 10 Sep 2022 12:51:09 +0200
Subject: [PATCH] 6.3 swallow patch
---
config.def.h | 9 +-
config.mk | 3 +-
dwm.c | 235 +++++++++++++++++++++++++++++++++++++++++++++++++--
3 files changed, 237 insertions(+), 10 deletions(-)
diff --git a/config.def.h b/config.def.h
index 061ad662f82a..0b2b8ffd30d5 100644
--- a/config.def.h
+++ b/config.def.h
@@ -3,6 +3,7 @@
/* appearance */
static const unsigned int borderpx = 1; /* border pixel of windows */
static const unsigned int snap = 32; /* snap pixel */
+static const int swallowfloating = 0; /* 1 means swallow floating windows by default */
static const int showbar = 1; /* 0 means no bar */
static const int topbar = 1; /* 0 means bottom bar */
static const char *fonts[] = { "monospace:size=10" };
@@ -26,9 +27,11 @@ static const Rule rules[] = {
* WM_CLASS(STRING) = instance, class
* WM_NAME(STRING) = title
*/
- /* class instance title tags mask isfloating monitor */
- { "Gimp", NULL, NULL, 0, 1, -1 },
- { "Firefox", NULL, NULL, 1 << 8, 0, -1 },
+ /* class instance title tags mask isfloating isterminal noswallow monitor */
+ { "Gimp", NULL, NULL, 0, 1, 0, 0, -1 },
+ { "Firefox", NULL, NULL, 1 << 8, 0, 0, -1, -1 },
+ { "St", NULL, NULL, 0, 0, 1, 0, -1 },
+ { NULL, NULL, "Event Tester", 0, 0, 0, 1, -1 }, /* xev */
};
/* layout(s) */
diff --git a/config.mk b/config.mk
index 81c493ef4aff..52d1ebf30bec 100644
--- a/config.mk
+++ b/config.mk
@@ -20,10 +20,11 @@ FREETYPEINC = /usr/include/freetype2
# OpenBSD (uncomment)
#FREETYPEINC = ${X11INC}/freetype2
#MANPREFIX = ${PREFIX}/man
+#KVMLIB = -lkvm
# includes and libs
INCS = -I${X11INC} -I${FREETYPEINC}
-LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS}
+LIBS = -L${X11LIB} -lX11 ${XINERAMALIBS} ${FREETYPELIBS} -lX11-xcb -lxcb -lxcb-res ${KVMLIB}
# flags
CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_POSIX_C_SOURCE=200809L -DVERSION=\"${VERSION}\" ${XINERAMAFLAGS}
diff --git a/dwm.c b/dwm.c
index e5efb6a22806..e68294b6b679 100644
--- a/dwm.c
+++ b/dwm.c
@@ -40,6 +40,12 @@
#include <X11/extensions/Xinerama.h>
#endif /* XINERAMA */
#include <X11/Xft/Xft.h>
+#include <X11/Xlib-xcb.h>
+#include <xcb/res.h>
+#ifdef __OpenBSD__
+#include <sys/sysctl.h>
+#include <kvm.h>
+#endif /* __OpenBSD */
#include "drw.h"
#include "util.h"
@@ -92,9 +98,11 @@ struct Client {
int basew, baseh, incw, inch, maxw, maxh, minw, minh, hintsvalid;
int bw, oldbw;
unsigned int tags;
- int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen;
+ int isfixed, isfloating, isurgent, neverfocus, oldstate, isfullscreen, isterminal, noswallow;
+ pid_t pid;
Client *next;
Client *snext;
+ Client *swallowing;
Monitor *mon;
Window win;
};
@@ -138,6 +146,8 @@ typedef struct {
const char *title;
unsigned int tags;
int isfloating;
+ int isterminal;
+ int noswallow;
int monitor;
} Rule;
@@ -235,6 +245,12 @@ static int xerrordummy(Display *dpy, XErrorEvent *ee);
static int xerrorstart(Display *dpy, XErrorEvent *ee);
static void zoom(const Arg *arg);
+static pid_t getparentprocess(pid_t p);
+static int isdescprocess(pid_t p, pid_t c);
+static Client *swallowingclient(Window w);
+static Client *termforwin(const Client *c);
+static pid_t winpid(Window w);
+
/* variables */
static const char broken[] = "broken";
static char stext[256];
@@ -269,6 +285,8 @@ static Drw *drw;
static Monitor *mons, *selmon;
static Window root, wmcheckwin;
+static xcb_connection_t *xcon;
+
/* configuration, allows nested code to access above variables */
#include "config.h"
@@ -298,6 +316,8 @@ applyrules(Client *c)
&& (!r->class || strstr(class, r->class))
&& (!r->instance || strstr(instance, r->instance)))
{
+ c->isterminal = r->isterminal;
+ c->noswallow = r->noswallow;
c->isfloating = r->isfloating;
c->tags |= r->tags;
for (m = mons; m && m->num != r->monitor; m = m->next);
@@ -416,6 +436,53 @@ attachstack(Client *c)
c->mon->stack = c;
}
+void
+swallow(Client *p, Client *c)
+{
+
+ if (c->noswallow || c->isterminal)
+ return;
+ if (c->noswallow && !swallowfloating && c->isfloating)
+ return;
+
+ detach(c);
+ detachstack(c);
+
+ setclientstate(c, WithdrawnState);
+ XUnmapWindow(dpy, p->win);
+
+ p->swallowing = c;
+ c->mon = p->mon;
+
+ Window w = p->win;
+ p->win = c->win;
+ c->win = w;
+ updatetitle(p);
+ XMoveResizeWindow(dpy, p->win, p->x, p->y, p->w, p->h);
+ arrange(p->mon);
+ configure(p);
+ updateclientlist();
+}
+
+void
+unswallow(Client *c)
+{
+ c->win = c->swallowing->win;
+
+ free(c->swallowing);
+ c->swallowing = NULL;
+
+ /* unfullscreen the client */
+ setfullscreen(c, 0);
+ updatetitle(c);
+ arrange(c->mon);
+ XMapWindow(dpy, c->win);
+ XMoveResizeWindow(dpy, c->win, c->x, c->y, c->w, c->h);
+ setclientstate(c, NormalState);
+ focus(NULL);
+ arrange(c->mon);
+}
+
void
buttonpress(XEvent *e)
{
@@ -656,6 +723,9 @@ destroynotify(XEvent *e)
if ((c = wintoclient(ev->window)))
unmanage(c, 1);
+
+ else if ((c = swallowingclient(ev->window)))
+ unmanage(c->swallowing, 1);
}
void
@@ -1022,12 +1092,13 @@ killclient(const Arg *arg)
void
manage(Window w, XWindowAttributes *wa)
{
- Client *c, *t = NULL;
+ Client *c, *t = NULL, *term = NULL;
Window trans = None;
XWindowChanges wc;
c = ecalloc(1, sizeof(Client));
c->win = w;
+ c->pid = winpid(w);
/* geometry */
c->x = c->oldx = wa->x;
c->y = c->oldy = wa->y;
@@ -1042,6 +1113,7 @@ manage(Window w, XWindowAttributes *wa)
} else {
c->mon = selmon;
applyrules(c);
+ term = termforwin(c);
}
if (c->x + WIDTH(c) > c->mon->wx + c->mon->ww)
@@ -1076,6 +1148,8 @@ manage(Window w, XWindowAttributes *wa)
c->mon->sel = c;
arrange(c->mon);
XMapWindow(dpy, c->win);
+ if (term)
+ swallow(term, c);
focus(NULL);
}
@@ -1763,6 +1837,20 @@ unmanage(Client *c, int destroyed)
Monitor *m = c->mon;
XWindowChanges wc;
+ if (c->swallowing) {
+ unswallow(c);
+ return;
+ }
+
+ Client *s = swallowingclient(c->win);
+ if (s) {
+ free(s->swallowing);
+ s->swallowing = NULL;
+ arrange(m);
+ focus(NULL);
+ return;
+ }
+
detach(c);
detachstack(c);
if (!destroyed) {
@@ -1778,9 +1866,12 @@ unmanage(Client *c, int destroyed)
XUngrabServer(dpy);
}
free(c);
- focus(NULL);
- updateclientlist();
- arrange(m);
+
+ if (!s) {
+ arrange(m);
+ focus(NULL);
+ updateclientlist();
+ }
}
void
@@ -2044,6 +2135,136 @@ view(const Arg *arg)
arrange(selmon);
}
+pid_t
+winpid(Window w)
+{
+
+ pid_t result = 0;
+
+#ifdef __linux__
+ xcb_res_client_id_spec_t spec = {0};
+ spec.client = w;
+ spec.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID;
+
+ xcb_generic_error_t *e = NULL;
+ xcb_res_query_client_ids_cookie_t c = xcb_res_query_client_ids(xcon, 1, &spec);
+ xcb_res_query_client_ids_reply_t *r = xcb_res_query_client_ids_reply(xcon, c, &e);
+
+ if (!r)
+ return (pid_t)0;
+
+ xcb_res_client_id_value_iterator_t i = xcb_res_query_client_ids_ids_iterator(r);
+ for (; i.rem; xcb_res_client_id_value_next(&i)) {
+ spec = i.data->spec;
+ if (spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID) {
+ uint32_t *t = xcb_res_client_id_value_value(i.data);
+ result = *t;
+ break;
+ }
+ }
+
+ free(r);
+
+ if (result == (pid_t)-1)
+ result = 0;
+
+#endif /* __linux__ */
+
+#ifdef __OpenBSD__
+ Atom type;
+ int format;
+ unsigned long len, bytes;
+ unsigned char *prop;
+ pid_t ret;
+
+ if (XGetWindowProperty(dpy, w, XInternAtom(dpy, "_NET_WM_PID", 0), 0, 1, False, AnyPropertyType, &type, &format, &len, &bytes, &prop) != Success || !prop)
+ return 0;
+
+ ret = *(pid_t*)prop;
+ XFree(prop);
+ result = ret;
+
+#endif /* __OpenBSD__ */
+ return result;
+}
+
+pid_t
+getparentprocess(pid_t p)
+{
+ unsigned int v = 0;
+
+#ifdef __linux__
+ 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);
+#endif /* __linux__*/
+
+#ifdef __OpenBSD__
+ int n;
+ kvm_t *kd;
+ struct kinfo_proc *kp;
+
+ kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, NULL);
+ if (!kd)
+ return 0;
+
+ kp = kvm_getprocs(kd, KERN_PROC_PID, p, sizeof(*kp), &n);
+ v = kp->p_ppid;
+#endif /* __OpenBSD__ */
+
+ 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(const Client *w)
+{
+ Client *c;
+ Monitor *m;
+
+ if (!w->pid || w->isterminal)
+ return NULL;
+
+ for (m = mons; m; m = m->next) {
+ for (c = m->clients; c; c = c->next) {
+ if (c->isterminal && !c->swallowing && c->pid && isdescprocess(c->pid, w->pid))
+ return c;
+ }
+ }
+
+ return NULL;
+}
+
+Client *
+swallowingclient(Window w)
+{
+ Client *c;
+ Monitor *m;
+
+ for (m = mons; m; m = m->next) {
+ for (c = m->clients; c; c = c->next) {
+ if (c->swallowing && c->swallowing->win == w)
+ return c;
+ }
+ }
+
+ return NULL;
+}
+
Client *
wintoclient(Window w)
{
@@ -2133,10 +2354,12 @@ main(int argc, char *argv[])
fputs("warning: no locale support\n", stderr);
if (!(dpy = XOpenDisplay(NULL)))
die("dwm: cannot open display");
+ if (!(xcon = XGetXCBConnection(dpy)))
+ die("dwm: cannot get xcb connection\n");
checkotherwm();
setup();
#ifdef __OpenBSD__
- if (pledge("stdio rpath proc exec", NULL) == -1)
+ if (pledge("stdio rpath proc exec ps", NULL) == -1)
die("pledge");
#endif /* __OpenBSD__ */
scan();
--
2.37.2

View file

@ -0,0 +1,11 @@
# acpid ya se encarga de la tapa y del botón de suspender (llama a zzz) y del
# botón de encendido. Si elogind también reacciona, el portátil se suspende
# dos veces: al abrir la tapa se enciende, se vuelve a suspender y luego
# arranca. Así solo lo gestiona acpid.
[Login]
HandlePowerKey=ignore
HandleSuspendKey=ignore
HandleHibernateKey=ignore
HandleLidSwitch=ignore
HandleLidSwitchExternalPower=ignore
HandleLidSwitchDocked=ignore

View file

@ -0,0 +1,22 @@
#!/bin/sh
# Bloquear con slock ANTES de suspender, para que al volver no se vea el
# escritorio. zzz ejecuta este hook como root; slock se lanza como el
# usuario de la sesión de dwm, con su DISPLAY y XAUTHORITY.
pgrep -x slock >/dev/null && exit 0
pid=$(pgrep -xo dwm) || exit 0
user=$(getent passwd "$(stat -c %u "/proc/$pid")" | cut -d: -f1)
env=$(tr '\0' '\n' <"/proc/$pid/environ" | grep -E '^(DISPLAY|XAUTHORITY)=')
[ -n "$user" ] && [ -n "$env" ] || exit 0
env $env setsid chpst -u "$user" /usr/local/bin/slock >/dev/null 2>&1 &
# Esperar a que slock arranque y tape la pantalla (máximo 2 s) antes de
# dejar que zzz siga con la suspensión.
i=0
while ! pgrep -x slock >/dev/null && [ "$i" -lt 20 ]; do
sleep 0.1
i=$((i + 1))
done
sleep 0.5

View file

@ -8,5 +8,21 @@ static const char *colorname[NUMCOLS] = {
[FAILED] = "#CC3333", /* wrong password */
};
/* lock screen */
static const char *bgcolor = "black";
static const char *fgcolor = "#eeeeee";
static const char *dimcolor = "#888888";
static const char *clockfont = "monospace:bold:pixelsize=160";
static const char *datefont = "monospace:pixelsize=34";
static const char *textfont = "monospace:pixelsize=22";
static const char *iconfont = "Symbols Nerd Font Mono:pixelsize=24";
static const int barheight = 10; /* height of the bottom bar */
static const unsigned int maxdots = 24; /* max dots shown while typing */
static const char *battery = "BAT0"; /* /sys/class/power_supply/... */
static const int batterylow = 20; /* percent shown as low */
/* treat a cleared input like a wrong password (color) */
static const int failonclear = 1;

View file

@ -2,11 +2,28 @@
static const char *user = "nobody";
static const char *group = "nogroup";
/* colores de la barra inferior según el estado */
static const char *colorname[NUMCOLS] = {
[INIT] = "#2d353b", /* after initialization (fondo Everforest) */
[INIT] = "#475258", /* after initialization (gris Everforest) */
[INPUT] = "#a7c080", /* during input (verde acento) */
[FAILED] = "#e67e80", /* wrong password (rojo Everforest) */
};
/* pantalla de bloqueo (paleta Everforest) */
static const char *bgcolor = "#2d353b"; /* fondo */
static const char *fgcolor = "#d3c6aa"; /* reloj y texto */
static const char *dimcolor = "#859289"; /* fecha y avisos */
static const char *clockfont = "Fira Code:style=Bold:pixelsize=160:antialias=true";
static const char *datefont = "Fira Code:pixelsize=34:antialias=true";
static const char *textfont = "Fira Code:pixelsize=22:antialias=true";
static const char *iconfont = "Symbols Nerd Font Mono:pixelsize=24:antialias=true";
static const int barheight = 10; /* alto de la barra inferior */
static const unsigned int maxdots = 24; /* puntos máximos al teclear */
static const char *battery = "BAT1"; /* /sys/class/power_supply/... */
static const int batterylow = 20; /* % para mostrarla en rojo */
/* treat a cleared input like a wrong password (color) */
static const int failonclear = 1;

View file

@ -11,8 +11,8 @@ X11INC = /usr/X11R6/include
X11LIB = /usr/X11R6/lib
# includes and libs
INCS = -I. -I/usr/include -I${X11INC}
LIBS = -L/usr/lib -lc -lcrypt -L${X11LIB} -lX11 -lXext -lXrandr
INCS = -I. -I/usr/include -I${X11INC} -I/usr/include/freetype2
LIBS = -L/usr/lib -lc -lcrypt -L${X11LIB} -lX11 -lXext -lXrandr -lXft -lfontconfig
# flags
CPPFLAGS = -DVERSION=\"${VERSION}\" -D_DEFAULT_SOURCE -DHAVE_SHADOW_H
@ -21,7 +21,7 @@ LDFLAGS = -s ${LIBS}
COMPATSRC = explicit_bzero.c
# On OpenBSD and Darwin remove -lcrypt from LIBS
#LIBS = -L/usr/lib -lc -L${X11LIB} -lX11 -lXext -lXrandr
#LIBS = -L/usr/lib -lc -L${X11LIB} -lX11 -lXext -lXrandr -lXft -lfontconfig
# On *BSD remove -DHAVE_SHADOW_H from CPPFLAGS
# On NetBSD add -D_NETBSD_SOURCE to CPPFLAGS
#CPPFLAGS = -DVERSION=\"${VERSION}\" -D_BSD_SOURCE -D_NETBSD_SOURCE

View file

@ -12,12 +12,15 @@
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/select.h>
#include <sys/types.h>
#include <X11/extensions/Xrandr.h>
#include <X11/keysym.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xft/Xft.h>
#include "util.h"
@ -30,13 +33,40 @@ enum {
NUMCOLS
};
enum {
COLBG,
COLFG,
COLDIM,
COLCHARGE,
COLLOW,
NUMUICOLS
};
struct lock {
int screen;
Window root, win;
Pixmap pmap;
unsigned long colors[NUMCOLS];
/* dibujado: doble búfer del tamaño de la pantalla */
int w, h;
Pixmap buf;
XftDraw *draw;
XftColor bar[NUMCOLS];
XftColor ui[NUMUICOLS];
XftFont *fclock, *fdate, *ftext, *ficon;
};
/* estado que se muestra en la pantalla de bloqueo */
struct state {
unsigned int len; /* caracteres tecleados */
int color; /* INIT, INPUT o FAILED */
int failure; /* ha habido una contraseña incorrecta */
char clock[16], date[64], bat[16];
int batlevel, charging;
};
static char username[64];
struct xrandr {
int active;
int evbase;
@ -123,23 +153,296 @@ gethash(void)
return hash;
}
static XftFont *
loadfont(Display *dpy, int screen, const char *name)
{
XftFont *f;
if (!(f = XftFontOpenName(dpy, screen, name)) &&
!(f = XftFontOpenName(dpy, screen, "monospace")))
die("slock: cannot load font %s\n", name);
return f;
}
static void
setupdraw(Display *dpy, struct lock *lock)
{
Visual *vis = DefaultVisual(dpy, lock->screen);
Colormap cmap = DefaultColormap(dpy, lock->screen);
const char *uinames[NUMUICOLS] = {
[COLBG] = bgcolor, [COLFG] = fgcolor, [COLDIM] = dimcolor,
[COLCHARGE] = colorname[INPUT], [COLLOW] = colorname[FAILED],
};
int i;
for (i = 0; i < NUMCOLS; i++)
if (!XftColorAllocName(dpy, vis, cmap, colorname[i], &lock->bar[i]))
die("slock: cannot allocate color %s\n", colorname[i]);
for (i = 0; i < NUMUICOLS; i++)
if (!XftColorAllocName(dpy, vis, cmap, uinames[i], &lock->ui[i]))
die("slock: cannot allocate color %s\n", uinames[i]);
lock->fclock = loadfont(dpy, lock->screen, clockfont);
lock->fdate = loadfont(dpy, lock->screen, datefont);
lock->ftext = loadfont(dpy, lock->screen, textfont);
lock->ficon = loadfont(dpy, lock->screen, iconfont);
lock->w = DisplayWidth(dpy, lock->screen);
lock->h = DisplayHeight(dpy, lock->screen);
lock->buf = XCreatePixmap(dpy, lock->root, lock->w, lock->h,
DefaultDepth(dpy, lock->screen));
lock->draw = XftDrawCreate(dpy, lock->buf, vis, cmap);
}
static void
resizedraw(Display *dpy, struct lock *lock, int w, int h)
{
if (w == lock->w && h == lock->h)
return;
lock->w = w;
lock->h = h;
XFreePixmap(dpy, lock->buf);
lock->buf = XCreatePixmap(dpy, lock->root, w, h,
DefaultDepth(dpy, lock->screen));
XftDrawChange(lock->draw, lock->buf);
}
static int
textw(Display *dpy, XftFont *f, const char *s)
{
XGlyphInfo ext;
XftTextExtentsUtf8(dpy, f, (const FcChar8 *)s, strlen(s), &ext);
return ext.xOff;
}
static int
iconw(Display *dpy, XftFont *f, FcChar32 icon)
{
XGlyphInfo ext;
if (!icon || !XftCharExists(dpy, f, icon))
return 0;
XftTextExtents32(dpy, f, &icon, 1, &ext);
return ext.xOff;
}
/* icono (opcional) + separación + texto, con la línea base en y */
static int
drawlabel(Display *dpy, struct lock *lock, XftFont *f, XftColor *c,
int x, int y, FcChar32 icon, const char *s)
{
int iw = iconw(dpy, lock->ficon, icon), gap = iw ? f->height / 2 : 0;
if (iw)
XftDrawString32(lock->draw, c, lock->ficon, x, y, &icon, 1);
XftDrawStringUtf8(lock->draw, c, f, x + iw + gap, y,
(const FcChar8 *)s, strlen(s));
return iw + gap + textw(dpy, f, s);
}
static int
labelw(Display *dpy, struct lock *lock, XftFont *f, FcChar32 icon,
const char *s)
{
int iw = iconw(dpy, lock->ficon, icon);
return iw + (iw ? f->height / 2 : 0) + textw(dpy, f, s);
}
/* Actualiza hora, fecha y batería. Devuelve 1 si algo ha cambiado. */
static int
updatestate(struct state *st)
{
static const char *wdays[] = { "domingo", "lunes", "martes",
"miércoles", "jueves", "viernes", "sábado" };
static const char *months[] = { "enero", "febrero", "marzo", "abril",
"mayo", "junio", "julio", "agosto", "septiembre", "octubre",
"noviembre", "diciembre" };
char clk[sizeof(st->clock)], date[sizeof(st->date)], bat[sizeof(st->bat)];
char path[128], status[32] = "";
int level = -1, charging = 0, changed;
time_t t = time(NULL);
struct tm *tm = localtime(&t);
FILE *f;
strftime(clk, sizeof(clk), "%H:%M", tm);
snprintf(date, sizeof(date), "%s, %d de %s", wdays[tm->tm_wday],
tm->tm_mday, months[tm->tm_mon]);
date[0] = toupper((unsigned char)date[0]);
snprintf(path, sizeof(path), "/sys/class/power_supply/%s/capacity",
battery);
if ((f = fopen(path, "r"))) {
if (fscanf(f, "%d", &level) != 1)
level = -1;
fclose(f);
}
snprintf(path, sizeof(path), "/sys/class/power_supply/%s/status",
battery);
if ((f = fopen(path, "r"))) {
if (fscanf(f, "%31s", status) != 1)
status[0] = '\0';
fclose(f);
}
charging = !strcmp(status, "Charging") || !strcmp(status, "Full");
if (level >= 0)
snprintf(bat, sizeof(bat), "%d%%", level);
else
bat[0] = '\0';
changed = strcmp(clk, st->clock) || strcmp(date, st->date) ||
strcmp(bat, st->bat) || charging != st->charging;
memcpy(st->clock, clk, sizeof(clk));
memcpy(st->date, date, sizeof(date));
memcpy(st->bat, bat, sizeof(bat));
st->batlevel = level;
st->charging = charging;
return changed;
}
/* Símbolos de Nerd Font (Material Design) */
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define ICON_LOCK 0xF033E
#define ICON_ALERT 0xF0026
#define ICON_USER 0xF0004
#define ICON_CHARGING 0xF0084
#define ICON_BAT10 0xF007A /* 10 % .. 90 % seguidos; 100 % es 0xF0079 */
#define ICON_BAT100 0xF0079
static FcChar32
baticon(const struct state *st)
{
if (st->charging)
return ICON_CHARGING;
if (st->batlevel >= 95)
return ICON_BAT100;
if (st->batlevel < 10)
return ICON_ALERT;
return ICON_BAT10 + st->batlevel / 10 - 1;
}
/* Dibuja la pantalla de bloqueo en el rectángulo de un monitor */
static void
drawmon(Display *dpy, struct lock *lock, const struct state *st,
int mx, int my, int mw, int mh)
{
XftColor *fg = &lock->ui[COLFG], *dim = &lock->ui[COLDIM], *msgc;
char msg[128];
FcChar32 icon = 0;
int x, y, w, pad = mh / 24, low;
unsigned int i, n;
/* reloj y fecha, algo por encima del centro */
y = my + mh * 2 / 5;
x = mx + (mw - textw(dpy, lock->fclock, st->clock)) / 2;
XftDrawStringUtf8(lock->draw, fg, lock->fclock, x, y,
(const FcChar8 *)st->clock, strlen(st->clock));
y += lock->fdate->height * 3 / 2;
x = mx + (mw - textw(dpy, lock->fdate, st->date)) / 2;
XftDrawStringUtf8(lock->draw, dim, lock->fdate, x, y,
(const FcChar8 *)st->date, strlen(st->date));
/* mensaje de estado: indicación, puntos o error */
msgc = dim;
if (st->color == INPUT) {
n = MIN(st->len, maxdots);
for (i = 0, msg[0] = '\0'; i < n; i++)
strcat(msg, i ? " ●" : "●");
msgc = &lock->bar[INPUT];
} else if (st->color == FAILED && st->failure) {
icon = ICON_ALERT;
snprintf(msg, sizeof(msg), "Contraseña incorrecta");
msgc = &lock->bar[FAILED];
} else {
icon = ICON_LOCK;
snprintf(msg, sizeof(msg), "Escribe la contraseña");
}
y = my + mh * 3 / 5 + lock->ftext->ascent;
x = mx + (mw - labelw(dpy, lock, lock->ftext, icon, msg)) / 2;
drawlabel(dpy, lock, lock->ftext, msgc, x, y, icon, msg);
/* abajo: usuario a la izquierda, batería a la derecha */
y = my + mh - barheight - pad;
drawlabel(dpy, lock, lock->ftext, dim, mx + pad, y, ICON_USER, username);
if (st->bat[0]) {
low = !st->charging && st->batlevel <= batterylow;
w = labelw(dpy, lock, lock->ftext, baticon(st), st->bat);
drawlabel(dpy, lock, lock->ftext,
low ? &lock->ui[COLLOW] :
st->charging ? &lock->ui[COLCHARGE] : fg,
mx + mw - pad - w, y, baticon(st), st->bat);
}
/* barra con el color del estado */
XftDrawRect(lock->draw, &lock->bar[st->color], mx, my + mh - barheight,
mw, barheight);
}
static void
drawlock(Display *dpy, struct lock *lock, const struct state *st)
{
XRRMonitorInfo *mons;
int i, n = 0;
XftDrawRect(lock->draw, &lock->ui[COLBG], 0, 0, lock->w, lock->h);
mons = XRRGetMonitors(dpy, lock->root, True, &n);
for (i = 0; i < n; i++)
drawmon(dpy, lock, st, mons[i].x, mons[i].y,
mons[i].width, mons[i].height);
if (mons)
XRRFreeMonitors(mons);
if (n <= 0)
drawmon(dpy, lock, st, 0, 0, lock->w, lock->h);
XCopyArea(dpy, lock->buf, lock->win, DefaultGC(dpy, lock->screen),
0, 0, lock->w, lock->h, 0, 0);
}
static void
drawall(Display *dpy, struct lock **locks, int nscreens,
const struct state *st)
{
int screen;
for (screen = 0; screen < nscreens; screen++)
drawlock(dpy, locks[screen], st);
XFlush(dpy);
}
static void
readpw(Display *dpy, struct xrandr *rr, struct lock **locks, int nscreens,
const char *hash)
{
XRRScreenChangeNotifyEvent *rre;
char buf[32], passwd[256], *inputhash;
int num, screen, running, failure, oldc;
unsigned int len, color;
int num, screen, running, xfd;
struct state st = { 0 };
struct timeval tv;
KeySym ksym;
XEvent ev;
fd_set fds;
len = 0;
running = 1;
failure = 0;
oldc = INIT;
st.color = INIT;
xfd = ConnectionNumber(dpy);
updatestate(&st);
drawall(dpy, locks, nscreens, &st);
while (running && !XNextEvent(dpy, &ev)) {
while (running) {
/* sin eventos pendientes: esperar como mucho 1 s y refrescar
* reloj y batería si han cambiado (también tras suspender) */
if (!XPending(dpy)) {
FD_ZERO(&fds);
FD_SET(xfd, &fds);
tv.tv_sec = 1;
tv.tv_usec = 0;
if (select(xfd + 1, &fds, NULL, NULL, &tv) <= 0 &&
updatestate(&st))
drawall(dpy, locks, nscreens, &st);
continue;
}
XNextEvent(dpy, &ev);
if (ev.type == KeyPress) {
explicit_bzero(&buf, sizeof(buf));
num = XLookupString(&ev.xkey, buf, sizeof(buf), &ksym, 0);
@ -157,7 +460,7 @@ readpw(Display *dpy, struct xrandr *rr, struct lock **locks, int nscreens,
continue;
switch (ksym) {
case XK_Return:
passwd[len] = '\0';
passwd[st.len] = '\0';
errno = 0;
if (!(inputhash = crypt(passwd, hash)))
fprintf(stderr, "slock: crypt: %s\n", strerror(errno));
@ -165,52 +468,56 @@ readpw(Display *dpy, struct xrandr *rr, struct lock **locks, int nscreens,
running = !!strcmp(inputhash, hash);
if (running) {
XBell(dpy, 100);
failure = 1;
st.failure = 1;
}
explicit_bzero(&passwd, sizeof(passwd));
len = 0;
st.len = 0;
break;
case XK_Escape:
explicit_bzero(&passwd, sizeof(passwd));
len = 0;
st.len = 0;
break;
case XK_BackSpace:
if (len)
passwd[--len] = '\0';
if (st.len)
passwd[--st.len] = '\0';
break;
default:
if (num && !iscntrl((unsigned char)buf[0]) &&
(len + num < sizeof(passwd))) {
memcpy(passwd + len, buf, num);
len += num;
(st.len + num < sizeof(passwd))) {
memcpy(passwd + st.len, buf, num);
st.len += num;
} else if (buf[0] == '\025') { /* ctrl-u clears input */
explicit_bzero(&passwd, sizeof(passwd));
len = 0;
st.len = 0;
}
break;
}
color = len ? INPUT : ((failure || failonclear) ? FAILED : INIT);
if (running && oldc != color) {
for (screen = 0; screen < nscreens; screen++) {
XSetWindowBackground(dpy,
locks[screen]->win,
locks[screen]->colors[color]);
XClearWindow(dpy, locks[screen]->win);
}
oldc = color;
st.color = st.len ? INPUT :
((st.failure || failonclear) ? FAILED : INIT);
if (running) {
updatestate(&st);
drawall(dpy, locks, nscreens, &st);
}
} else if (ev.type == Expose) {
if (ev.xexpose.count == 0)
drawall(dpy, locks, nscreens, &st);
} else if (rr->active && ev.type == rr->evbase + RRScreenChangeNotify) {
rre = (XRRScreenChangeNotifyEvent*)&ev;
for (screen = 0; screen < nscreens; screen++) {
if (locks[screen]->win == rre->window) {
if (rre->rotation == RR_Rotate_90 ||
rre->rotation == RR_Rotate_270)
rre->rotation == RR_Rotate_270) {
XResizeWindow(dpy, locks[screen]->win,
rre->height, rre->width);
else
resizedraw(dpy, locks[screen],
rre->height, rre->width);
} else {
XResizeWindow(dpy, locks[screen]->win,
rre->width, rre->height);
XClearWindow(dpy, locks[screen]->win);
resizedraw(dpy, locks[screen],
rre->width, rre->height);
}
drawlock(dpy, locks[screen], &st);
break;
}
}
@ -243,16 +550,20 @@ lockscreen(Display *dpy, struct xrandr *rr, int screen)
lock->colors[i] = color.pixel;
}
setupdraw(dpy, lock);
/* init */
wa.override_redirect = 1;
wa.background_pixel = lock->colors[INIT];
wa.background_pixel = lock->ui[COLBG].pixel;
wa.event_mask = ExposureMask;
lock->win = XCreateWindow(dpy, lock->root, 0, 0,
DisplayWidth(dpy, lock->screen),
DisplayHeight(dpy, lock->screen),
0, DefaultDepth(dpy, lock->screen),
CopyFromParent,
DefaultVisual(dpy, lock->screen),
CWOverrideRedirect | CWBackPixel, &wa);
CWOverrideRedirect | CWBackPixel | CWEventMask,
&wa);
lock->pmap = XCreateBitmapFromData(dpy, lock->win, curs, 8, 8);
invisible = XCreatePixmapCursor(dpy, lock->pmap, lock->pmap,
&color, &color, 0, 0);
@ -340,6 +651,10 @@ main(int argc, char **argv) {
dontkillme();
#endif
/* nombre del usuario que bloquea, para mostrarlo */
if ((pwd = getpwuid(getuid())))
snprintf(username, sizeof(username), "%s", pwd->pw_name);
hash = gethash();
errno = 0;
if (!crypt("", hash))

View file

@ -1,7 +1,7 @@
/* See LICENSE file for copyright and license details. */
/* interval between updates (in ms) */
const unsigned int interval = 1000;
const unsigned int interval = 2000;
/* text to show if no value can be retrieved */
static const char unknown_str[] = "N/A";
@ -21,18 +21,39 @@ static const char unknown_str[] = "N/A";
* run_command output of a shell command command
* wifi_essid WiFi network name interface name (wlan0)
*/
/*
* Red sin lanzar procesos: ETH si hay cable en enp3s0; si no, la red WiFi de
* wlp4s0 (se lee por netlink con el componente wifi_essid de slstatus).
*/
static const char *
red(const char *unused)
{
const char *s;
if ((s = cat("/sys/class/net/enp3s0/operstate")) && !strcmp(s, "up"))
return "ETH";
if (!(s = wifi_essid("wlp4s0")))
return "WiFi: N/A";
return bprintf("WiFi: %s", s);
}
/*
* Los bytes \001, \002 y \003 delimitan las zonas clicables de la barra
* (parche statuscmd de dwm): la red, el volumen y el bluetooth. Lo que hace
* cada clic está en statuscmds, en el config.h de dwm.
*/
static const struct arg args[] = {
/* function format argument */
{ cpu_perc, "[ CPU: %s%% ] ", NULL },
{ ram_perc, "[ RAM: %s%% ] ", NULL },
/* ETH si hay cable en enp3s0; si no, la red WiFi de wlp4s0 */
{ run_command, "[ %s ] ", "if [ \"$(cat /sys/class/net/enp3s0/operstate)\" = up ]; then echo ETH; "
"else s=$(iw dev wlp4s0 link | sed -n 's/^[[:space:]]*SSID: //p'); echo \"WiFi: ${s:-N/A}\"; fi" },
{ battery_perc, "[ BAT: %s%%", "BAT1" },
{ battery_state, " %s ] ", "BAT1" },
/* function format argument */
{ cpu_perc, "[ CPU: %s%% ] ", NULL },
{ ram_perc, "[ RAM: %s%% ] ", NULL },
{ red, "\001[ %s ]\001 ", NULL },
{ battery_perc, "[ BAT: %s%%", "BAT1" },
{ battery_state, " %s ] ", "BAT1" },
/* mismo volumen que maneja wpctl (PipeWire); MIC OFF si el micro está silenciado */
{ run_command, "[ VOL: %s ] ", "wpctl get-volume @DEFAULT_AUDIO_SINK@ | awk '{ printf \"%d%%\", $2 * 100 + 0.5 } /MUTED/ { printf \" MUT\" }'; "
"wpctl get-volume @DEFAULT_AUDIO_SOURCE@ | grep -q MUTED && printf ' MIC OFF'" },
{ run_command, "[ BT: %s ] ", "bluetoothctl devices Connected | head -1 | cut -d' ' -f3-" },
{ datetime, "[ %s ]", "%d/%m/%y %H:%M:%S" },
{ run_command, "\002[ VOL: %s ]\002 ", "wpctl get-volume @DEFAULT_AUDIO_SINK@ | awk '{ printf \"%d%%\", $2 * 100 + 0.5 } /MUTED/ { printf \" MUT\" }'; "
"wpctl get-volume @DEFAULT_AUDIO_SOURCE@ | grep -q MUTED && printf ' MIC OFF'" },
/* OFF si el adaptador está apagado; si no, el primer dispositivo conectado */
{ run_command, "\003[ BT: %s ]\003 ", "bluetoothctl show | grep -q 'Powered: yes' || { echo OFF; exit; }; "
"bluetoothctl devices Connected | head -1 | cut -d' ' -f3-" },
{ datetime, "[ %s ]", "%d/%m/%y %H:%M" },
};