From 73d005dd3044eceb03d34226122b35375c2e5723 Mon Sep 17 00:00:00 2001 From: alejandrogs73 Date: Mon, 28 Sep 2026 15:36:16 +0200 Subject: [PATCH 1/3] =?UTF-8?q?Fuera=20avahi,=20xss-lock,=20chrony=20y=20n?= =?UTF-8?q?emo;=20.bashrc=20m=C3=A1s=20limpio?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - inactivo: slock cuando salta el salvapantallas de X (xssstate), en vez de xss-lock (glib, D-Bus y logind). - openntpd en vez de chrony. - Fuera avahi y nss-mdns (la impresora va por IP fija); quitar limpia nsswitch.conf. - Fuera nemo y upower (y con ellos gvfs y udisks2): las carpetas se abren en yazi dentro de st. - .bashrc: PATH sin repetidos y ex con bsdtar. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01C5v1qWDwgkaVM5kjVHGGh8 --- README-en.md | 38 ++++++++++------- README.md | 37 +++++++++------- home/.bashrc | 42 +++++++++---------- home/.config/mimeapps.list | 2 + home/.local/bin/inactivo | 12 ++++++ .../.local/share/applications/yazi-st.desktop | 8 ++++ home/.xinitrc | 6 +-- install.sh | 28 +++++++++---- 8 files changed, 110 insertions(+), 63 deletions(-) create mode 100755 home/.local/bin/inactivo create mode 100644 home/.local/share/applications/yazi-st.desktop diff --git a/README-en.md b/README-en.md index 37a61dc..5150556 100644 --- a/README-en.md +++ b/README-en.md @@ -54,21 +54,24 @@ for example) and repeated safely: 6. `sistema` (system): copies `sistema/` into `/`, sets up `wpa_supplicant.conf` so wheel users can use `wpa_cli` without root and save networks, enables the runit services (dbus, turnstiled, - wpa_supplicant, dhcpcd, bluetoothd, acpid, chronyd, tlp, automontaje, - cupsd), removes NetworkManager, elogind and polkitd (if something asks for - polkit, like udisks2 or libvirt, D-Bus starts it on its own) and adds the - user to the audio, video, input, network, bluetooth, lpadmin (printer - management) and `_pipewire` (realtime priority for audio without rtkit) - groups. `doas.conf` is checked with `doas -C` before it is installed with - mode 400. + wpa_supplicant, dhcpcd, bluetoothd, acpid, openntpd, tlp, automontaje, + cupsd), removes NetworkManager, elogind, chronyd, avahi-daemon (the printer + has a fixed IP) and polkitd (if something asks for polkit, like libvirt, + D-Bus starts it on its own) and adds the user to the audio, video, input, + network, bluetooth, lpadmin (printer management) and `_pipewire` (realtime + priority for audio without rtkit) groups. `doas.conf` is checked with `doas + -C` before it is installed with mode 400. 7. `quitar` (remove): uninstalls what the repo no longer uses because something else replaces it: sudo (doas), feh (xwallpaper and nsxiv), gammastep (sct), autorandr (`pantallas`), blueman and pavucontrol (the `bluetooth` and `volumen` scripts), pinentry-gtk (pinentry-dmenu), vlc (mpv), btop, NetworkManager and tlp-rdw (wpa_supplicant, dhcpcd and the - `wifi` script), rtkit and elogind (turnstile). sudo can be removed thanks - to `sistema/etc/xbps.d/sin-sudo.conf` (base-system depends on it), and it - is only removed once `/etc/doas.conf` is installed. + `wifi` script), rtkit, elogind (turnstile), xss-lock (`inactivo`, with + xssstate), chrony (openntpd), avahi and nss-mdns (also removed from + `/etc/nsswitch.conf`) and nemo with upower (yazi; gvfs and udisks2 go with + nemo). sudo can be removed thanks to `sistema/etc/xbps.d/sin-sudo.conf` + (base-system depends on it), and it is only removed once `/etc/doas.conf` + is installed. Afterwards, log out and back in (for the groups) and run `startx`. @@ -88,9 +91,9 @@ elogind is systemd's logind on its own, and it is not needed here: - Devices (sound, video, input, bluetooth) are reached through the user's groups, not through the ACLs elogind gave the active session. -What stops working: mounting disks from a file manager (udisks2 asks polkit -for an active session, and there is none). USB sticks are mounted by -`automontaje`. +What stops working: mounting disks from a graphical file manager (udisks2 +asked polkit for an active session; now there is neither a file manager nor +udisks2). USB sticks are mounted by `automontaje`. ## Rebuilding after changing the config @@ -201,8 +204,9 @@ preserveonrestart patches). when the SSH key is already unlocked (so pinentry never pops up) and retries every hour. `marcadores ya` uploads it right away. To restore: Firefox > Bookmarks > Manage bookmarks > Import and Backup > Restore. -- After 30 minutes idle the screen locks with slock (`xss-lock`) and one - minute later it turns off. +- After 30 minutes idle the screen locks with slock (`inactivo`, which + watches the X screensaver with `xssstate`) and one minute later it turns + off. Videos that ask to keep the screen on (mpv, Firefox) prevent it. - USB sticks and SD cards: the `automontaje` service (runit, as root) mounts them in `/mnt/LABEL`, or `/mnt/sdXY` if they have no label, and unmounts them when removed, with a notification. FAT, exFAT and NTFS are owned by @@ -210,6 +214,10 @@ preserveonrestart patches). wrote to, run `sync` (or `doas umount /mnt/...`). - User folders in English and without accents (`user-dirs.dirs`): Documents, Downloads and Images. +- Folders ("Open folder" in Firefox, `xdg-open`) open in yazi inside st + (`home/.local/share/applications/yazi-st.desktop`). +- `ex file...` extracts almost anything with `bsdtar` (a `.bashrc` + function). ## Notes diff --git a/README.md b/README.md index 9f3360e..724192e 100644 --- a/README.md +++ b/README.md @@ -51,20 +51,22 @@ Hace siete pasos, que también se pueden lanzar por separado 6. `sistema`: copia `sistema/` en `/`, prepara `wpa_supplicant.conf` para que los de wheel usen `wpa_cli` sin root y guarden redes, activa los servicios de runit (dbus, turnstiled, wpa_supplicant, dhcpcd, bluetoothd, acpid, - chronyd, tlp, automontaje, cupsd), quita NetworkManager, elogind y polkitd - (si algo pide polkit, como udisks2 o libvirt, D-Bus lo arranca solo) y - añade el usuario a los grupos audio, video, input, network, bluetooth, - lpadmin (para gestionar impresoras) y `_pipewire` (prioridad de tiempo real - para el audio sin rtkit). `doas.conf` se valida con `doas -C` antes de - instalarlo con modo 400. + openntpd, tlp, automontaje, cupsd), quita NetworkManager, elogind, chronyd, + avahi-daemon (la impresora va por IP fija) y polkitd (si algo pide polkit, + como libvirt, D-Bus lo arranca solo) y añade el usuario a los grupos audio, + video, input, network, bluetooth, lpadmin (para gestionar impresoras) y + `_pipewire` (prioridad de tiempo real para el audio sin rtkit). `doas.conf` + se valida con `doas -C` antes de instalarlo con modo 400. 7. `quitar`: desinstala lo que el repo ya no usa porque lo sustituye otra cosa: sudo (doas), feh (xwallpaper y nsxiv), gammastep (sct), autorandr (`pantallas`), blueman y pavucontrol (scripts `bluetooth` y `volumen`), pinentry-gtk (pinentry-dmenu), vlc (mpv), btop, NetworkManager y tlp-rdw - (wpa_supplicant, dhcpcd y el script `wifi`), rtkit y elogind (turnstile). - sudo se puede quitar gracias a `sistema/etc/xbps.d/sin-sudo.conf` - (base-system depende de él), y solo se quita si `/etc/doas.conf` ya está - instalado. + (wpa_supplicant, dhcpcd y el script `wifi`), rtkit, elogind (turnstile), + xss-lock (`inactivo`, con xssstate), chrony (openntpd), avahi y nss-mdns + (también los quita de `/etc/nsswitch.conf`) y nemo con upower (yazi; con + nemo se van gvfs y udisks2). sudo se puede quitar gracias a + `sistema/etc/xbps.d/sin-sudo.conf` (base-system depende de él), y solo se + quita si `/etc/doas.conf` ya está instalado. Después hay que cerrar sesión y volver a entrar (por los grupos) y lanzar `startx`. @@ -85,9 +87,9 @@ elogind es el logind de systemd por separado, y aquí sobra: - Los dispositivos (sonido, vídeo, entrada, bluetooth) van por los grupos del usuario, no por las ACL que ponía elogind a la sesión activa. -Lo que deja de ir: montar discos desde un gestor de archivos (udisks2 pide a -polkit una sesión activa, que ya no hay). Las memorias USB las monta -`automontaje`. +Lo que deja de ir: montar discos desde un gestor de archivos gráfico (udisks2 +pedía a polkit una sesión activa; ya no hay ni gestor ni udisks2). Las +memorias USB las monta `automontaje`. ## Recompilar tras cambiar la configuración @@ -198,8 +200,9 @@ ventanas (parches restartsig y preserveonrestart). con la clave SSH desbloqueada (para no sacar pinentry) y lo reintenta cada hora. `marcadores ya` la sube en el momento. Para restaurar: Firefox > Marcadores > Administrar marcadores > Importar y respaldar > Restaurar. -- Tras 30 minutos sin tocar nada se bloquea con slock (`xss-lock`) y un - minuto después se apaga la pantalla. +- Tras 30 minutos sin tocar nada se bloquea con slock (`inactivo`, que mira + el salvapantallas de X con `xssstate`) y un minuto después se apaga la + pantalla. Los vídeos que piden no apagarla (mpv, Firefox) lo evitan. - Memorias USB y tarjetas SD: el servicio `automontaje` (runit, como root) las monta en `/mnt/ETIQUETA`, o en `/mnt/sdXY` si no tienen etiqueta, y las desmonta al sacarlas, con una notificación. FAT, exFAT y NTFS quedan a @@ -207,6 +210,10 @@ ventanas (parches restartsig y preserveonrestart). memoria en la que se ha escrito, `sync` (o `doas umount /mnt/...`). - Carpetas del usuario en inglés y sin tildes (`user-dirs.dirs`): Documents, Downloads e Images. +- Las carpetas ("Abrir carpeta" en Firefox, `xdg-open`) se abren en yazi + dentro de st (`home/.local/share/applications/yazi-st.desktop`). +- `ex archivo...` descomprime casi cualquier cosa con `bsdtar` (función de + `.bashrc`). ## Notas diff --git a/home/.bashrc b/home/.bashrc index dcff353..2723258 100644 --- a/home/.bashrc +++ b/home/.bashrc @@ -3,8 +3,15 @@ # ===================================================================== # --- 1. Entorno y Rutas --- -# Priorizar binarios locales y scripts de usuario -export PATH="$HOME/.local/bin:/usr/local/bin:$PATH" +# Priorizar binarios locales y scripts de usuario. Si ya estaban (un bash +# dentro de otro), se quitan antes para no repetirlos. +for d in /usr/local/bin "$HOME/.local/bin"; do + PATH=":$PATH:" + PATH=${PATH//:$d:/:} + PATH=${PATH#:} + PATH="$d:${PATH%:}" +done +export PATH export SSH_AUTH_SOCK=$(gpgconf --list-dirs agent-ssh-socket) export GPG_TTY=$(tty) gpg-connect-agent updatestartuptty /bye >/dev/null 2>&1 @@ -71,27 +78,20 @@ alias esp32='doas chmod a+rw /dev/ttyUSB0' # --- 5. Funciones Útiles --- -# Comando 'ex': Descomprime LITERALMENTE CUALQUIER COSA -# Uso: ex archivo.zip | ex archivo.tar.gz +# ex: descomprime lo que sea. bsdtar se entiende con tar (gz, bz2, xz, zst), +# zip, 7z, rar e iso; los comprimidos sueltos, con su herramienta. +# Uso: ex archivo.zip [otro.tar.gz ...] ex() { - if [ -f "$1" ]; then - case $1 in - *.tar.bz2) tar xjf "$1" ;; - *.tar.gz) tar xzf "$1" ;; - *.bz2) bunzip2 "$1" ;; - *.rar) unrar x "$1" ;; - *.gz) gunzip "$1" ;; - *.tar) tar xf "$1" ;; - *.tbz2) tar xjf "$1" ;; - *.tgz) tar xzf "$1" ;; - *.zip) unzip "$1" ;; - *.Z) uncompress "$1";; - *.7z) 7z x "$1" ;; - *) echo "'$1' formato no reconocido por la función ex()" ;; + for f; do + case $f in + *.tar|*.tar.*|*.t?z|*.tbz2|*.zip|*.7z|*.rar|*.iso) bsdtar -xf "$f" ;; + *.gz) gunzip -k "$f" ;; + *.bz2) bunzip2 -k "$f" ;; + *.xz) unxz -k "$f" ;; + *.zst) unzstd "$f" ;; + *) echo "ex: no sé descomprimir '$f'" >&2 ;; esac - else - echo "'$1' no es un archivo válido" - fi + done } # Forzar IPv4 en Nix (IPv6 del sistema está roto) diff --git a/home/.config/mimeapps.list b/home/.config/mimeapps.list index 9d96999..8e6f10b 100644 --- a/home/.config/mimeapps.list +++ b/home/.config/mimeapps.list @@ -1,4 +1,5 @@ [Default Applications] +inode/directory=yazi-st.desktop x-scheme-handler/http=firefox.desktop x-scheme-handler/https=firefox.desktop x-scheme-handler/chrome=firefox.desktop @@ -11,6 +12,7 @@ application/x-extension-xhtml=firefox.desktop application/x-extension-xht=firefox.desktop [Added Associations] +inode/directory=yazi-st.desktop; x-scheme-handler/http=firefox.desktop; x-scheme-handler/https=firefox.desktop; x-scheme-handler/chrome=firefox.desktop; diff --git a/home/.local/bin/inactivo b/home/.local/bin/inactivo new file mode 100755 index 0000000..1e831a9 --- /dev/null +++ b/home/.local/bin/inactivo @@ -0,0 +1,12 @@ +#!/bin/sh +# Bloquea con slock cuando salta el salvapantallas de X (xset s en .xinitrc: +# 30 minutos sin tocar nada). Mira el estado con xssstate cada 5 s. Los +# vídeos que piden no apagar la pantalla (mpv, Firefox) paran el salvapantallas +# y así tampoco bloquea. Al suspender ya bloquea el hook de zzz. + +while :; do + if [ "$(xssstate -s)" = on ] && ! pgrep -x slock >/dev/null; then + slock + fi + sleep 5 +done diff --git a/home/.local/share/applications/yazi-st.desktop b/home/.local/share/applications/yazi-st.desktop new file mode 100644 index 0000000..1224d55 --- /dev/null +++ b/home/.local/share/applications/yazi-st.desktop @@ -0,0 +1,8 @@ +[Desktop Entry] +Type=Application +Name=yazi +Comment=Carpetas con yazi en st (para xdg-open y Firefox) +Exec=st -e yazi %f +Terminal=false +MimeType=inode/directory; +NoDisplay=true diff --git a/home/.xinitrc b/home/.xinitrc index 5c004a1..12b8624 100644 --- a/home/.xinitrc +++ b/home/.xinitrc @@ -35,10 +35,10 @@ luz & bateria & marcadores & -# Bloquear con slock tras 30 minutos sin tocar nada, y apagar la pantalla un -# minuto después. Al suspender ya bloquea el hook de zzz (--ignore-sleep). +# Bloquear con slock tras 30 minutos sin tocar nada (salvapantallas de X, +# lo vigila inactivo) y apagar la pantalla un minuto después. xset s 1800 1800 xset dpms 0 0 1860 -xss-lock --ignore-sleep -- slock & +inactivo & exec dwm diff --git a/install.sh b/install.sh index 6794881..92d6ddc 100755 --- a/install.sh +++ b/install.sh @@ -18,27 +18,28 @@ PAQUETES=" libXrender-devel xorgproto imlib2-devel zlib-devel libxcrypt-devel xorg xinit setxkbmap xrandr dbus turnstile - picom dunst libnotify xwallpaper nsxiv maim xclip sct xss-lock + picom dunst libnotify xwallpaper nsxiv maim xclip sct xssstate pipewire wireplumber libspa-bluetooth alsa-pipewire - wpa_supplicant dhcpcd bluez acpid chrony tlp + wpa_supplicant dhcpcd bluez acpid openntpd tlp cups cups-filters hplip font-firacode nerd-fonts-symbols-ttf papirus-icon-theme papirus-folders sassc gnome-themes-extra qt5ct qt6ct opendoas gnupg pinentry-dmenu firefox thunderbird mpv neovim git fuse-sshfs unzip - yazi file ffmpeg 7zip jq poppler fd ripgrep fzf zoxide resvg ImageMagick + yazi file bsdtar ffmpeg 7zip jq poppler fd ripgrep fzf zoxide resvg ImageMagick " SUCKLESS="dwm st dmenu slstatus slock scroll clipmenu" # Tema GTK Everforest, en una versión fija para que siempre salga igual GTK_TEMA_REPO=https://github.com/Fausto-Korpsvart/Everforest-GTK-Theme GTK_TEMA_COMMIT=9b8be4d6648ae9eaae3dd550105081f8c9054825 -SERVICIOS="dbus turnstiled wpa_supplicant dhcpcd bluetoothd acpid chronyd tlp automontaje cupsd" +SERVICIOS="dbus turnstiled wpa_supplicant dhcpcd bluetoothd acpid openntpd tlp automontaje cupsd" # La red la llevan wpa_supplicant y dhcpcd. polkitd no hace falta como -# servicio: si algo lo pide (udisks2, libvirt), D-Bus lo arranca. turnstiled -# sustituye a elogind. -SERVICIOS_FUERA="NetworkManager polkitd elogind" +# servicio: si algo lo pide (libvirt), D-Bus lo arranca. turnstiled sustituye +# a elogind y openntpd a chronyd. avahi-daemon (mDNS) no hace falta: la +# impresora va por IP fija. +SERVICIOS_FUERA="NetworkManager polkitd elogind chronyd avahi-daemon" # _pipewire da prioridad de tiempo real al audio sin rtkit # (/etc/security/limits.d/25-pw-rlimits.conf, de pipewire). GRUPOS="audio video input network bluetooth lpadmin _pipewire" @@ -46,11 +47,14 @@ GRUPOS="audio video input network bluetooth lpadmin _pipewire" # nsxiv), gammastep (sct), autorandr (pantallas), blueman y pavucontrol # (scripts bluetooth y volumen), pinentry-gtk (pinentry-dmenu), vlc (mpv), # NetworkManager (wpa_supplicant, dhcpcd y el script wifi), rtkit (el grupo -# _pipewire) y elogind (turnstile; Xorg arranca como root con Xorg.wrap y la -# tapa y los botones ya los lleva acpid). +# _pipewire), elogind (turnstile; Xorg arranca como root con Xorg.wrap y la +# tapa y los botones ya los lleva acpid), xss-lock (inactivo, con xssstate), +# chrony (openntpd), avahi y nss-mdns (nada los usa) y nemo con upower (yazi; +# con nemo se van gvfs y udisks2). QUITAR=" sudo feh gammastep autorandr blueman pavucontrol pinentry-gtk gtk-engine-murrine vlc btop NetworkManager tlp-rdw rtkit elogind + xss-lock chrony avahi nss-mdns nemo upower " msg() { printf '\033[1;32m==>\033[0m %s\n' "$*"; } @@ -260,6 +264,12 @@ quitar() { # shellcheck disable=SC2086 root xbps-remove -Ry $fuera + # Sin nss-mdns, "mdns" sobra en la línea hosts de nsswitch.conf + if grep -q '^hosts:.*mdns' /etc/nsswitch.conf; then + root sed -i '/^hosts:/s/ mdns[a-z0-9_]*\( \[NOTFOUND=return\]\)\{0,1\}//g' \ + /etc/nsswitch.conf + fi + # Lo que puso aquí una versión anterior de sistema/ para elogind if [ -e /etc/elogind/logind.conf.d/10-acpid.conf ]; then root rm /etc/elogind/logind.conf.d/10-acpid.conf From 3864c31ba1416dc11086a3fb7b679f1b76f1f9b4 Mon Sep 17 00:00:00 2001 From: alejandrogs73 Date: Mon, 28 Sep 2026 15:38:41 +0200 Subject: [PATCH 2/3] slstatus: VOL y BT cada 10 s o al recibir SIGUSR1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Componente lento: run_command con caché. slstatus.c cuenta las SIGUSR1 para que se refresque en cuanto un script o un clic lo pide. Así ya no se lanzan wpctl y bluetoothctl cada 2 s. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01C5v1qWDwgkaVM5kjVHGGh8 --- README-en.md | 7 ++++++ README.md | 7 ++++++ suckless/slstatus/config.h | 43 ++++++++++++++++++++++++++++++++++-- suckless/slstatus/slstatus.c | 3 +++ 4 files changed, 58 insertions(+), 2 deletions(-) diff --git a/README-en.md b/README-en.md index 5150556..9a2473a 100644 --- a/README-en.md +++ b/README-en.md @@ -168,6 +168,11 @@ and which area is which is marked by the `\001`...`\004` bytes in | BT | `OFF`, or the connected device and its battery if it reports it (`VJ 901 100%`) | `bluetooth` menu: connect, disconnect, scan and pair, power off | | Power on or off | | | Date | Day and time | This month's calendar (`calendario`) | | | | +VOL and BT are not read every 2 s like the rest, but every 10 s or as soon as +`SIGUSR1` arrives (`lento` component in slstatus's `config.h`): `volumen`, +`bluetooth`, `wifi` and the clicks send it, so your own changes show up at +once and wpctl and bluetoothctl are not spawned every 2 s. + After changing dwm's `config.h`, reinstall and restart it for new clicks to work: `pkill -HUP dwm` restarts it without closing windows (restartsig and preserveonrestart patches). @@ -236,5 +241,7 @@ preserveonrestart patches). - In st, Ctrl+Shift+right click on an image opens it in `nsxiv`. - st shows images with the kitty graphics protocol; yazi detects it on its own and shows previews without ueberzugpp. +- slstatus.c has a hand-made change (not a patch): it counts `SIGUSR1` + signals so `lento` knows when to refresh. - slock is modified by hand (not a patch): clock, date, battery and a bottom bar with the state colour. diff --git a/README.md b/README.md index 724192e..c0459e3 100644 --- a/README.md +++ b/README.md @@ -165,6 +165,11 @@ y qué zona es cada una lo marcan los bytes `\001`...`\004` de | BT | `OFF`, o el dispositivo conectado y su batería si la da (`VJ 901 100%`) | Menú `bluetooth`: conectar, desconectar, buscar y emparejar, apagar | | Encender o apagar | | | Fecha | Día y hora | Calendario del mes (`calendario`) | | | | +VOL y BT no se miran cada 2 s como el resto, sino cada 10 s o en cuanto +llega `SIGUSR1` (componente `lento` del `config.h` de slstatus): lo mandan +`volumen`, `bluetooth`, `wifi` y los clics, así que lo que cambias tú se ve al +momento y wpctl y bluetoothctl no se lanzan cada 2 s. + Si se cambia el `config.h` de dwm, hay que reinstalarlo y reiniciarlo para que los clics nuevos funcionen: `pkill -HUP dwm` lo relanza sin cerrar las ventanas (parches restartsig y preserveonrestart). @@ -231,5 +236,7 @@ ventanas (parches restartsig y preserveonrestart). - En st, Ctrl+Shift+clic derecho sobre una imagen la abre en `nsxiv`. - st muestra imágenes con el protocolo de gráficos de kitty; yazi lo detecta solo y enseña las vistas previas sin ueberzugpp. +- slstatus.c tiene un cambio a mano (no es un parche): cuenta las señales + `SIGUSR1` para que `lento` sepa cuándo refrescar. - slock está modificado a mano (no es un parche): reloj, fecha, batería y una barra inferior con el color del estado. diff --git a/suckless/slstatus/config.h b/suckless/slstatus/config.h index 4482779..ad21146 100644 --- a/suckless/slstatus/config.h +++ b/suckless/slstatus/config.h @@ -37,6 +37,45 @@ red(const char *unused) return bprintf("WiFi: %s", s); } +/* + * run_command, pero solo cada LENTO segundos o en cuanto llega SIGUSR1 (lo + * mandan los scripts volumen, bluetooth y wifi y los clics en la barra). Así + * VOL y BT no lanzan wpctl y bluetoothctl cada 2 s; lo que cambie por otro + * lado tarda como mucho LENTO segundos en verse. La cuenta de SIGUSR1 (usr1) + * la lleva slstatus.c. + */ +#define LENTO 10 + +static const char * +lento(const char *cmd) +{ + static struct { + const char *cmd; + char out[256]; + int ok; + sig_atomic_t usr1; + time_t t; + } c[4]; + struct timespec ahora; + const char *res; + size_t i; + + clock_gettime(CLOCK_MONOTONIC, &ahora); + for (i = 0; i < LEN(c) && c[i].cmd && c[i].cmd != cmd; i++) + ; + if (i == LEN(c)) + return run_command(cmd); + if (c[i].cmd && c[i].usr1 == usr1 && ahora.tv_sec - c[i].t < LENTO) + return c[i].ok ? c[i].out : NULL; + + c[i].cmd = cmd; + c[i].usr1 = usr1; + c[i].t = ahora.tv_sec; + if ((c[i].ok = (res = run_command(cmd)) != NULL)) + snprintf(c[i].out, sizeof(c[i].out), "%s", res); + return c[i].ok ? c[i].out : NULL; +} + /* * Los bytes \001 a \004 delimitan las zonas clicables de la barra * (parche statuscmd de dwm): la red, el volumen, el bluetooth y la fecha. Lo que hace @@ -50,11 +89,11 @@ static const struct arg args[] = { { battery_perc, "[ BAT: %s%%", "BAT1" }, { battery_state, " %s ] ", "BAT1" }, /* mismo volumen que maneja wpctl (PipeWire); MIC OFF si el micro está silenciado */ - { run_command, "\002[ VOL: %s ]\002 ", "wpctl get-volume @DEFAULT_AUDIO_SINK@ | awk '{ printf \"%d%%\", $2 * 100 + 0.5 } /MUTED/ { printf \" MUT\" }'; " + { lento, "\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 * y su batería, si la da ("Battery Percentage: 0x64 (100)" -> " 100%") */ - { run_command, "\003[ BT: %s ]\003 ", "bluetoothctl show | grep -q 'Powered: yes' || { echo OFF; exit; }; " + { lento, "\003[ BT: %s ]\003 ", "bluetoothctl show | grep -q 'Powered: yes' || { echo OFF; exit; }; " "set -- $(bluetoothctl devices Connected | head -1); [ $# -gt 0 ] || exit; " "m=$2; shift 2; printf %s \"$*\"; " "bluetoothctl info \"$m\" | awk -F'[()]' '/Battery Percentage/ { printf \" %s%%\", $2 }'" }, diff --git a/suckless/slstatus/slstatus.c b/suckless/slstatus/slstatus.c index 16d88fe..1a15525 100644 --- a/suckless/slstatus/slstatus.c +++ b/suckless/slstatus/slstatus.c @@ -19,6 +19,7 @@ struct arg { char buf[1024]; static volatile sig_atomic_t done; +static volatile sig_atomic_t usr1; /* SIGUSR1 recibidas, para lento() en config.h */ static Display *dpy; #include "config.h" @@ -28,6 +29,8 @@ terminate(const int signo) { if (signo != SIGUSR1) done = 1; + else + usr1++; } static void From 20cb3ae4a6848619c151d298f002d3ed21664cf6 Mon Sep 17 00:00:00 2001 From: alejandrogs73 Date: Mon, 28 Sep 2026 15:48:16 +0200 Subject: [PATCH 3/3] =?UTF-8?q?bluetooth:=20conectar=20los=20conocidos=20a?= =?UTF-8?q?unque=20no=20est=C3=A9n=20emparejados?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit El menú solo enseñaba los dispositivos con Paired: yes, así que un auricular de confianza que había perdido el emparejamiento no salía, y volver a emparejarlo fallaba porque bluetoothctl no tenía agente. Ahora salen los emparejados y los de confianza; al elegir uno se empareja si hace falta (agente NoInputNoOutput), se busca unos segundos si no está a la vista, y si falla lo dice (también si wireplumber no está en marcha). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01C5v1qWDwgkaVM5kjVHGGh8 --- home/.local/bin/bluetooth | 61 ++++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/home/.local/bin/bluetooth b/home/.local/bin/bluetooth index 6cc803a..4d531ab 100755 --- a/home/.local/bin/bluetooth +++ b/home/.local/bin/bluetooth @@ -1,8 +1,9 @@ #!/bin/sh -# Menú de bluetooth con dmenu (clic izquierdo en BT de la barra): conectar o -# desconectar los dispositivos emparejados (󰂱 = conectado), buscar y -# emparejar uno nuevo y apagar el adaptador. Si está apagado, ofrece -# encenderlo. +# Menú de bluetooth con dmenu (clic izquierdo en BT de la barra). Arriba, los +# dispositivos conocidos (emparejados o de confianza; 󰂱 = conectado): elegir +# uno lo desconecta si está conectado y, si no, lo busca y se conecta (y lo +# vuelve a emparejar si hace falta). Debajo, buscar y emparejar uno nuevo y +# apagar el adaptador. Si está apagado, ofrece encenderlo. avisar() { notify-send -h string:x-dunst-stack-tag:bluetooth -i bluetooth "Bluetooth" "$1" @@ -11,6 +12,36 @@ avisar() { # "Device AA:BB:CC:DD:EE:FF Nombre" -> "Nombre (AA:BB:CC:DD:EE:FF)" lista() { sed -n 's/^Device \([^ ]*\) \(.*\)/\2 (\1)/p'; } mac() { printf '%s\n' "$1" | sed 's/.*(\([^)]*\))$/\1/'; } +buscar() { bluetoothctl --timeout "$1" scan on >/dev/null; } + +# Sin agente, bluetoothctl no puede emparejar; los auriculares no piden PIN. +emparejar() { + bluetoothctl --agent NoInputNoOutput pair "$1" >/dev/null && + bluetoothctl trust "$1" >/dev/null +} + +# conectar MAC: si no está emparejado lo empareja; si no está a la vista, lo +# busca unos segundos y lo vuelve a intentar. +conectar() { + if ! bluetoothctl info "$1" | grep -q 'Paired: yes'; then + emparejar "$1" || { buscar 8 && emparejar "$1"; } || return 1 + fi + bluetoothctl connect "$1" >/dev/null || + { buscar 8 && bluetoothctl connect "$1" >/dev/null; } +} + +# conectar_y_avisar "Nombre (MAC)" +conectar_y_avisar() { + nombre=${1% (*} + avisar "Conectando: $nombre..." + if conectar "$(mac "$1")"; then + avisar "Conectado: $nombre" + elif ! pgrep -x wireplumber >/dev/null; then + avisar "No se pudo conectar $nombre: wireplumber no está en marcha (sin él no hay audio)" + else + avisar "No se pudo conectar $nombre: ¿está encendido y cerca?" + fi +} if ! bluetoothctl show | grep -q 'Powered: yes'; then [ "$(echo Encender | menu -p Bluetooth:)" = Encender ] || exit 0 @@ -19,10 +50,10 @@ if ! bluetoothctl show | grep -q 'Powered: yes'; then fi conectados=$(bluetoothctl devices Connected | lista) -emparejados=$(bluetoothctl devices Paired | lista) +conocidos=$({ bluetoothctl devices Paired; bluetoothctl devices Trusted; } | lista | sort -u) opcion=$( - printf '%s\n' "$emparejados" | while IFS= read -r d; do + printf '%s\n' "$conocidos" | while IFS= read -r d; do [ -n "$d" ] || continue if printf '%s\n' "$conectados" | grep -qxF "$d"; then echo "󰂱 $d" @@ -42,29 +73,19 @@ case $opcion in ;; *" Buscar dispositivos") avisar "Buscando dispositivos durante 10 s..." - bluetoothctl --timeout 10 scan on >/dev/null - nuevo=$(bluetoothctl devices | lista | awk -v e="$emparejados" ' + buscar 10 + nuevo=$(bluetoothctl devices | lista | awk -v e="$conocidos" ' BEGIN { n = split(e, a, "\n"); for (i = 1; i <= n; i++) visto[a[i]] } !($0 in visto)' | menu -p Emparejar:) [ -n "$nuevo" ] || exit 0 - m=$(mac "$nuevo") - if bluetoothctl pair "$m" >/dev/null && bluetoothctl trust "$m" >/dev/null && - bluetoothctl connect "$m" >/dev/null; then - avisar "Conectado: $nuevo" - else - avisar "No se pudo emparejar: $nuevo" - fi + conectar_y_avisar "$nuevo" ;; "󰂱 "*) bluetoothctl disconnect "$(mac "$opcion")" >/dev/null && avisar "Desconectado: ${opcion#"󰂱 "}" ;; *) - if bluetoothctl connect "$(mac "$opcion")" >/dev/null; then - avisar "Conectado: ${opcion#" "}" - else - avisar "No se pudo conectar: ${opcion#" "}" - fi + conectar_y_avisar "${opcion#" "}" ;; esac pkill -USR1 -x slstatus