diff options
Diffstat (limited to 'dwl-patches/patches/spawninfo')
-rw-r--r-- | dwl-patches/patches/spawninfo/README.md | 41 | ||||
-rwxr-xr-x | dwl-patches/patches/spawninfo/pamixerc | 134 | ||||
-rwxr-xr-x | dwl-patches/patches/spawninfo/screenshotwin | 21 | ||||
-rw-r--r-- | dwl-patches/patches/spawninfo/spawninfo.patch | 110 |
4 files changed, 306 insertions, 0 deletions
diff --git a/dwl-patches/patches/spawninfo/README.md b/dwl-patches/patches/spawninfo/README.md new file mode 100644 index 0000000..5ee345d --- /dev/null +++ b/dwl-patches/patches/spawninfo/README.md @@ -0,0 +1,41 @@ +### Description + +This patch adds `spawninfo` function that is very similar to `spawn`, except it +also passes some information about the focused client via stdin. + +The info is passed in this format: + + PID + TITLE + APPID + TAGS + X,Y WIDTHxHEIGHT + +I use it for 2 things: grabbing a screenshot of a focused window and adjusting +volume of audio produced by a focused window (so much simpler than having to +open pulsemixer every time). If you want to have the same functionality, you +need to put these scripts into your PATH and make them executable: + +[screenshotwin](/dwl/dwl-patches/raw/branch/main/patches/spawninfo/screenshotwin) +for taking a screenshot (`grim` is required): + +```c + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_S, spawninfo, SHCMD("screenshotwin") }, +``` + +[pamixerc](/dwl/dwl-patches/raw/branch/main/patches/spawninfo/pamixerc) +for adjusting volume (`pactl` is required): + +```c + { MODKEY, XKB_KEY_XF86AudioRaiseVolume,spawninfo,SHCMD("pamixerc -- -i 5") }, + { MODKEY, XKB_KEY_XF86AudioLowerVolume,spawninfo,SHCMD("pamixerc -- -d 5") }, + { MODKEY, XKB_KEY_XF86AudioMute, spawninfo, SHCMD("pamixerc -- -t") }, +``` + +### Download + +- [0.7](/dwl/dwl-patches/raw/branch/main/patches/spawninfo/spawninfo.patch) + +### Authors + +- [Nikita Ivanov](https://codeberg.org/nikitaivanov) ([GitHub](https://github.com/NikitaIvanovV)) diff --git a/dwl-patches/patches/spawninfo/pamixerc b/dwl-patches/patches/spawninfo/pamixerc new file mode 100755 index 0000000..91a41c4 --- /dev/null +++ b/dwl-patches/patches/spawninfo/pamixerc @@ -0,0 +1,134 @@ +#!/usr/bin/awk -f + +function get_parent(pid, cmd, ppid, arr) { + cmd = sprintf("/proc/%d/stat", pid) + ppid = "" + if ((getline line < cmd) > 0) { + split(line, arr) + ppid = arr[4] + } + close(cmd) + return ppid +} + +function proc_clients(line, arr) { + if (match(line, /^Client #(.*)/, arr)) { + store_client() + client_index = arr[1] + return + } + else if (match(line, /^\s*application\.process\.id = "(.*)"/, arr)) { + process_id = arr[1] + return + } + else if (match(line, /^\s*application\.name = "(.*)"/, arr)) { + application_name = arr[1] + return + } +} + +function store_client() { + if (client_index == "") + return + + clients[client_index] = 1 + clients[client_index, "pid"] = process_id + clients[client_index, "parent_pid"] = get_parent(process_id) + clients[client_index, "name"] = application_name + client_index = "" + process_id = "" +} + +function proc_sink_inputs(line, arr) { + if (match(line, /^Sink Input #(.*)/, arr)) { + apply_sink_input() + sink_index = arr[1] + return + } + else if (match(line, /^\s*application\.process\.id = "(.*)"/, arr)) { + process_id = arr[1] + return + } + else if (match(line, /^\s*Client: (.*)/, arr)) { + client_index = arr[1] + return + } + else if (match(line, /^\s*Mute: (.*)/, arr)) { + muted = (arr[1] == "yes") + return + } + else if (match(line, /^\s*Volume:.* ([0-9]+)%/, arr)) { + volume = strtonum(arr[1]) + return + } +} + +function apply_sink_input( cmd, header, msg, inc) { + if (sink_index == "") + return + + # Do stuff if PID matches + if (PID == clients[client_index, "pid"] || PID == clients[client_index, "parent_pid"]) { + switch (ACTION) { + case "-i": + case "-d": + inc = strtonum(ARG) + if (ACTION == "-i") + volume += inc + else + volume -= inc + volume = volume > 100 ? 100 : volume + volume = volume < 0 ? 0 : volume + cmd = sprintf("pactl set-sink-input-volume '%d' '%d'%", sink_index, volume) + system(cmd) + print cmd + break + case "-t": + muted = !muted + cmd = sprintf("pactl set-sink-input-mute '%d' '%s'", sink_index, muted ? "true" : "false") + system(cmd) + print cmd + break + } + # Display a "popup" with new volume + header = sprintf("Client Volume: %d%%%s", volume, muted ? " MUTED" : "") + msg = clients[client_index, "name"] + system(sprintf("notify-send -r 101 -u low -h 'int:value:%d' '%s' '%s'", volume, header, msg)) + } + + sink_index = "" +} + +BEGIN { + # Get arguments + # ACTION: -d, -i or -t (like pamixer) + # ARG: num arg for -d or -i + ACTION = ARGV[1] + ARG = ARGV[2] + for (i = 1; i < ARGC; i++) + delete ARGV[i] + + # Get client info + getline PID + getline TITLE # Not used + getline APPID # Not used + getline TAGS # Not used + getline GEOMETRY # Not used + + if (PID == "") + exit 1 + + # Process PulseAudio clients list and store PIDs for each + cmd = "pactl list clients" + while ((cmd | getline line) > 0) + proc_clients(line) + close(cmd) + store_client() + + # Process PulseAudio sink inputs list and run apply_sink_input() for each + cmd = "pactl list sink-inputs" + while ((cmd | getline line) > 0) + proc_sink_inputs(line) + close(cmd) + apply_sink_input() +} diff --git a/dwl-patches/patches/spawninfo/screenshotwin b/dwl-patches/patches/spawninfo/screenshotwin new file mode 100755 index 0000000..c8a3ebf --- /dev/null +++ b/dwl-patches/patches/spawninfo/screenshotwin @@ -0,0 +1,21 @@ +#!/bin/sh + +# Get client info +read -r PID +read -r TITLE +read -r APPID +read -r TAGS +read -r GEOMETRY + +[ -n "$GEOMETRY" ] || exit 1 + +tempdir="/tmp/screenshots" +mkdir -p "$tempdir" +file="$(mktemp -p "$tempdir" "XXXXXX.png")" + +# Grab the screenshot! Very conviniently, GEOMETRY format matches the one +# expected by grim +grim -g "$GEOMETRY" "$file" || exit + +wl-copy -t image/png < "$file" +notify-send -i "$file" "Screenshot taken!" "Image copied to clipboard and saved to $file" diff --git a/dwl-patches/patches/spawninfo/spawninfo.patch b/dwl-patches/patches/spawninfo/spawninfo.patch new file mode 100644 index 0000000..e452a4e --- /dev/null +++ b/dwl-patches/patches/spawninfo/spawninfo.patch @@ -0,0 +1,110 @@ +From 83b8dc03f5ea40f472e90d452671f8e55faf2c4c Mon Sep 17 00:00:00 2001 +From: Nikita Ivanov <nikita.vyach.ivanov@gmail.com> +Date: Sun, 9 Feb 2025 23:27:48 +0100 +Subject: [PATCH] spawninfo: spawn but pass client info via stdin + +--- + client.h | 12 ++++++++++++ + dwl.c | 42 ++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 54 insertions(+) + +diff --git a/client.h b/client.h +index 42f225f..bc9cad2 100644 +--- a/client.h ++++ b/client.h +@@ -131,6 +131,18 @@ client_get_appid(Client *c) + return c->surface.xdg->toplevel->app_id; + } + ++static inline int ++client_get_pid(Client *c) ++{ ++ pid_t pid; ++#ifdef XWAYLAND ++ if (client_is_x11(c)) ++ return c->surface.xwayland->pid; ++#endif ++ wl_client_get_credentials(c->surface.xdg->client->client, &pid, NULL, NULL); ++ return pid; ++} ++ + static inline void + client_get_clip(Client *c, struct wlr_box *clip) + { +diff --git a/dwl.c b/dwl.c +index def2562..859514c 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -141,6 +141,7 @@ typedef struct { + uint32_t tags; + int isfloating, isurgent, isfullscreen; + uint32_t resize; /* configure serial of a pending resize */ ++ pid_t pid; + } Client; + + typedef struct { +@@ -334,6 +335,7 @@ static void setpsel(struct wl_listener *listener, void *data); + static void setsel(struct wl_listener *listener, void *data); + static void setup(void); + static void spawn(const Arg *arg); ++static void spawninfo(const Arg *arg); + static void startdrag(struct wl_listener *listener, void *data); + static void tag(const Arg *arg); + static void tagmon(const Arg *arg); +@@ -466,6 +468,8 @@ applyrules(Client *c) + if (!(title = client_get_title(c))) + title = broken; + ++ c->pid = client_get_pid(c); ++ + for (r = rules; r < END(rules); r++) { + if ((!r->title || strstr(title, r->title)) + && (!r->id || strstr(appid, r->id))) { +@@ -2658,6 +2662,44 @@ spawn(const Arg *arg) + } + } + ++void ++spawninfo(const Arg *arg) ++{ ++ int fd[2]; ++ pid_t pid; ++ const char *title, *appid; ++ Client *c = focustop(selmon); ++ ++ if (pipe(fd) == -1) ++ return; ++ if ((pid = fork()) == -1) ++ return; ++ if (pid == 0) { ++ dup2(fd[0], STDIN_FILENO); ++ close(fd[0]); ++ close(fd[1]); ++ dup2(STDERR_FILENO, STDOUT_FILENO); ++ setsid(); ++ execvp(((char **)arg->v)[0], (char **)arg->v); ++ die("dwl: execvp %s failed:", ((char **)arg->v)[0]); ++ } ++ ++ close(fd[0]); ++ ++ if (c) { ++ if (!(title = client_get_title(c))) ++ title = ""; ++ if (!(appid = client_get_appid(c))) ++ appid = ""; ++ dprintf(fd[1], "%d\n%s\n%s\n%"PRIu32"\n%d,%d %dx%d\n", c->pid, ++ title, appid, c->tags, ++ c->geom.x + c->bw, c->geom.y + c->bw, ++ c->geom.width - 2 * c->bw, c->geom.height - 2 * c->bw); ++ } ++ ++ close(fd[1]); ++} ++ + void + startdrag(struct wl_listener *listener, void *data) + { +-- +2.48.1 + |