summaryrefslogtreecommitdiffstats
path: root/dwl-patches/patches/spawninfo
diff options
context:
space:
mode:
authorLeonard Kugis <leonard@kug.is>2025-05-23 11:41:09 +0000
committerLeonard Kugis <leonard@kug.is>2025-05-23 11:41:09 +0000
commitc70505d7c7b7b48600f273357694b56ccf5d2a15 (patch)
tree21c27ac6ffced8d6d904e35bdb39baa5d685d829 /dwl-patches/patches/spawninfo
downloaddotfiles-master.tar.gz
dotfiles-master.tar.bz2
dotfiles-master.zip
Initial commitHEADmaster
Diffstat (limited to 'dwl-patches/patches/spawninfo')
-rw-r--r--dwl-patches/patches/spawninfo/README.md41
-rwxr-xr-xdwl-patches/patches/spawninfo/pamixerc134
-rwxr-xr-xdwl-patches/patches/spawninfo/screenshotwin21
-rw-r--r--dwl-patches/patches/spawninfo/spawninfo.patch110
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
+