diff --git a/brightnessctl-0.5.1/.gitignore b/brightnessctl-0.5.1/.gitignore new file mode 100644 index 0000000..a3ca076 --- /dev/null +++ b/brightnessctl-0.5.1/.gitignore @@ -0,0 +1,2 @@ +brightnessctl +*.swp diff --git a/brightnessctl-0.5.1/90-brightnessctl.rules b/brightnessctl-0.5.1/90-brightnessctl.rules new file mode 100644 index 0000000..99a4c4d --- /dev/null +++ b/brightnessctl-0.5.1/90-brightnessctl.rules @@ -0,0 +1,4 @@ +ACTION=="add", SUBSYSTEM=="backlight", RUN+="/bin/chgrp video /sys/class/backlight/%k/brightness" +ACTION=="add", SUBSYSTEM=="backlight", RUN+="/bin/chmod g+w /sys/class/backlight/%k/brightness" +ACTION=="add", SUBSYSTEM=="leds", RUN+="/bin/chgrp input /sys/class/leds/%k/brightness" +ACTION=="add", SUBSYSTEM=="leds", RUN+="/bin/chmod g+w /sys/class/leds/%k/brightness" diff --git a/brightnessctl-0.5.1/LICENSE b/brightnessctl-0.5.1/LICENSE new file mode 100644 index 0000000..a3be582 --- /dev/null +++ b/brightnessctl-0.5.1/LICENSE @@ -0,0 +1,25 @@ +Copyright (c) 2016 Mykyta Holuakha + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +[Except as contained in this notice, the name of Mykyta Holubakha +shall not be used in advertising or otherwise to promote the sale, use +or other dealings in this Software without prior written authorization +from Mykyta Holubakha.] diff --git a/brightnessctl-0.5.1/Makefile b/brightnessctl-0.5.1/Makefile new file mode 100644 index 0000000..105f5d6 --- /dev/null +++ b/brightnessctl-0.5.1/Makefile @@ -0,0 +1,40 @@ +VERSION = 0.5 +CFLAGS += -std=c99 -g -Wall -Wextra -DVERSION=\"${VERSION}\" -D_POSIX_C_SOURCE=200809L +LDLIBS = -lm + +PREFIX ?= /usr +BINDIR = ${DESTDIR}${PREFIX}/bin +MANDIR = ${DESTDIR}${PREFIX}/share/man + +INSTALL_UDEV_RULES = 1 + +INSTALL_UDEV_1 = install_udev_rules +UDEVDIR ?= /lib/udev/rules.d + +MODE_0 = 4711 +MODE_1 = 0755 +MODE = ${MODE_${INSTALL_UDEV_RULES}} + +ifdef ENABLE_SYSTEMD + CFLAGS += ${shell pkg-config --cflags libsystemd} + LDLIBS += ${shell pkg-config --libs libsystemd} + CPPFLAGS += -DENABLE_SYSTEMD + INSTALL_UDEV_RULES=0 + MODE = 0755 +endif + +all: brightnessctl brightnessctl.1 + +install: all ${INSTALL_UDEV_${INSTALL_UDEV_RULES}} + install -d ${BINDIR} ${MANDIR}/man1 + install -m ${MODE} brightnessctl ${BINDIR}/ + install -m 0644 brightnessctl.1 ${MANDIR}/man1 + +install_udev_rules: + install -d ${DESTDIR}${UDEVDIR} + install -m 0644 90-brightnessctl.rules ${DESTDIR}${UDEVDIR} + +clean: + rm -f brightnessctl + +.PHONY: all install clean diff --git a/brightnessctl-0.5.1/README.md b/brightnessctl-0.5.1/README.md new file mode 100644 index 0000000..4a710c9 --- /dev/null +++ b/brightnessctl-0.5.1/README.md @@ -0,0 +1,64 @@ +# brightnessctl + +This program allows you read and control device brightness. Devices, by default, include backlight and LEDs (searched for in corresponding classes). If omitted, the first found device is selected. + +It can also preserve current brightness before applying the operation (allowing for usecases like disabling backlight on lid close). + +## Installation + +The program is available in: +* [Alpine Linux](https://pkgs.alpinelinux.org/package/edge/community/x86_64/brightnessctl) - starting with 3.11 and Edge +* [Arch Linux](https://www.archlinux.org/packages/community/x86_64/brightnessctl/) +* [Void Linux](https://github.com/void-linux/void-packages/blob/master/srcpkgs/brightnessctl/template) +* [Debian](https://packages.debian.org/testing/source/brightnessctl) - starting with Buster (and derivatives) +* [Ubuntu](https://packages.ubuntu.com/source/bionic/brightnessctl) - starting with 18.04 (and derivatives) +* [openSUSE](https://build.opensuse.org/package/show/utilities/brightnessctl) - available in Tumbleweed, use OBS `utilities/brightnessctl` devel project for Leap < 15.1 +* [Fedora/EPEL](https://apps.fedoraproject.org/packages/brightnessctl) (orphaned, deleted since F30, maintainer wanted) +* [NixOS/nix](https://nixos.org/nixos/packages.html?attr=brightnessctl) - starting with 17.09, please see the [NixOS Wiki page](https://nixos.wiki/wiki/Backlight#brightnessctl) for the "best-practice" configuration file based installation + +One can build and install the program using `make install`. Consult the Makefile for relevant build-time options. + +## Permissions + +Modifying brightness requires write permissions for device files or systemd support. `brightnessctl` accomplishes this (without using `sudo`/`su`/etc.) by either of the following means: + +1) installing relevant udev rules to add permissions to backlight class devices for users in `video` and leds for users in `input`. (done by default) + +2) installing `brightnessctl` as a suid binary. + +3) using the `systemd-logind` API. + +The behavior is controlled by the `INSTALL_UDEV_RULES` flag (setting it to `1` installs the udev rules, it is the default value). + +The systemd support (since v243) is controlled by the `ENABLE_SYSTEMD` flag (udev rules will not be installed by default). + +## Usage +``` +Usage: brightnessctl [options] [operation] [value] + +Options: + -l, --list list devices with available brightness controls. + -q, --quiet suppress output. + -p, --pretend do not perform write operations. + -m, --machine-readable produce machine-readable output. + -n, --min-value set minimum brightness, defaults to 1. + -e, --exponent[=K] changes percentage curve to exponential. + -s, --save save previous state in a temporary file. + -r, --restore restore previous saved state. + -h, --help print this help. + -d, --device=DEVICE specify device name (can be a wildcard). + -c, --class=CLASS specify device class. + -V, --version print version and exit. + +Operations: + i, info get device info. + g, get get current brightness of the device. + m, max get maximum brightness of the device. + s, set VALUE set brightness of the device. + +Valid values: + specific value Example: 500 + percentage value Example: 50% + specific delta Example: 50- or +10 + percentage delta Example: 50%- or +10% + ``` diff --git a/brightnessctl-0.5.1/brightnessctl.1 b/brightnessctl-0.5.1/brightnessctl.1 new file mode 100644 index 0000000..83faf8d --- /dev/null +++ b/brightnessctl-0.5.1/brightnessctl.1 @@ -0,0 +1,157 @@ +.TH "BRIGHTNESSCTL" "1" "24th Jan 2018" "brightnessctl" "brightnessctl" + +.SH "NAME" +brightnessctl \- read and control device brightness + + +.SH "SYNOPSIS" + +.sp +\fIbrightnessctl\fR [options] \fI[operation]\fR [value...] + + +.SH "OPTIONS" + +.sp +\fB\-h, \-\-help\fP +.RS 4 +Print this help. +.RE + +.sp +\fB\-l, \-\-list\fP +.RS 4 +List devices with available brightness controls. +.RE + +.sp +\fB\-q, \-\-quiet\fP +.RS 4 +Suppress output. +.RE + +.sp +\fB\-p, \-\-pretend\fP +.RS 4 +Do not perform write operations. +.RE + +.sp +\fB\-m, \-\-machine\-readable\fP +.RS 4 +Produce machine\-readable output. +.RE + +.sp +\fB\-n, \-\-min\-value\fP=\fIVALUE\fP +.RS 4 +Set minimum brightness when using delta values, defaults to 1. +.RE + +.sp +\fB\-e, \-\-exponent\fP=\fIK\fP +.RS 4 +Changes percentage scaling curve to exponential (linear by default). Default exponent is 4. + +Percentage equation: % = \fI[VALUE]\fR^\fI[K]\fR * \fI[MAX]\fR * 100^-\fI[K]\fR. + +The exponential curve may make the adjustments perceptually equal. +.RE + +.sp +\fB\-s, \-\-save\fP +.RS 4 +Save state in a temporary file. +.RE + +.sp +\fB\-r, \-\-restore\fP +.RS 4 +Restore previously\-saved state. +.RE + +.sp +\fB\-d, \-\-device\fP=\fIDEVICE\fP +.RS 4 +Specify device name (can be a wildcard). +.RE + +.sp +\fB\-c, \-\-class\fP=\fICLASS\fP +.RS 4 +Specify device class. +.RE + +.sp +\fB\-v, \-\-version\fP +.RS 4 +Print version and exit. +.RE + + +.SH "OPERATIONS" + +.sp +\fBi, info\fP +.RS 4 +Get device info. +.RE + +.sp +\fBg, get\fP +.RS 4 +Get the current brightness of the device. +.RE + +.sp +\fBm, max\fP +.RS 4 +Get the maximum brightness of the device. +.RE + +.sp +\fBs, set\fP \fIVALUE\fP +.RS 4 +Set the brightness of the device. + +.SS VALUES +.P +You may specify \fIVALUE\fR for the \fBset\fR command in absolute or relative form, and +as a value or a delta from the current value. For example: +.P +\fBbrightnessctl set 500\fR +.RS "4" +Sets brightness to 500. +.P +.RE +\fBbrightnessctl set 50%\fR +.RS "4" +Sets brightness to 50% of the maximum. +.P +.RE +\fBbrightnessctl set 50-\fR +.RS "4" +Subtracts 50 from the current brightness. +.P +.RE +\fBbrightnessctl set +10\fR +.RS "4" +Adds 10 to the current brightness. +.P +.RE +\fBbrightnessctl set 50%-\fR +.RS "4" +Subtracts 50% of the maximum from the current brightness. +.P +.RE +\fBbrightnessctl set +10%\fR +.RS "4" +Adds 10% of the maximum to the current brightness. +.P +.RE +.SH AUTHORS +.P +Maintained by Mykyta Holubakha, who is assisted by other open source +contributors. For more information about brightnessctl development, visit: +.P +https://github.com/Hummer12007/brightnessctl diff --git a/brightnessctl-0.5.1/brightnessctl.c b/brightnessctl-0.5.1/brightnessctl.c new file mode 100644 index 0000000..6045ec3 --- /dev/null +++ b/brightnessctl-0.5.1/brightnessctl.c @@ -0,0 +1,668 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef ENABLE_SYSTEMD +# include +#endif + +static char *path = "/sys/class"; +static char *classes[] = { "backlight", "leds", NULL }; + +static char *run_dir = "/tmp/brightnessctl"; + +struct value; +struct device; + +enum operation; + +static void fail(char *, ...); +static void usage(void); +#define cat_with(...) _cat_with(__VA_ARGS__, NULL) +static char *_cat_with(char, ...); +static char *dir_child(char *, char*); +static char *device_path(struct device *); +static char *class_path(char *); +static unsigned int calc_value(struct device *, struct value *); +static int apply_operation(struct device *, enum operation, struct value *); +static bool parse_value(struct value *, char *); +static bool do_write_device(struct device *); +static bool read_device(struct device *, char *, char *); +static int read_class(struct device **, char *); +static int read_devices(struct device **); +static void print_device(struct device *); +static void list_devices(struct device **); +static struct device *find_device(struct device **, char *); +static bool save_device_data(struct device *); +static bool restore_device_data(struct device *); +static bool ensure_dir(char *); +static bool ensure_dev_dir(struct device *); +#define ensure_run_dir() ensure_dir(run_dir) + +#ifdef ENABLE_SYSTEMD +static bool logind_set_brightness(struct device *); +#endif + +struct device { + char *class; + char *id; + unsigned int curr_brightness; + unsigned int max_brightness; +}; + +enum value_type { ABSOLUTE, RELATIVE }; +enum delta_type { DIRECT, DELTA }; +enum sign { PLUS, MINUS }; + +struct value { + unsigned long val; + enum value_type v_type; + enum delta_type d_type; + enum sign sign; +}; + +enum operation { INFO, GET, MAX, SET }; + +struct params { + char *class; + char *device; + struct value val; + long min; + enum operation operation; + bool quiet; + bool list; + bool pretend; + bool mach; + bool save; + bool restore; + float exponent; +}; + +static struct params p; + +static const struct option options[] = { + {"class", required_argument, NULL, 'c'}, + {"device", required_argument, NULL, 'd'}, + {"help", no_argument, NULL, 'h'}, + {"list", no_argument, NULL, 'l'}, + {"machine-readable", no_argument, NULL, 'm'}, + {"min-value", optional_argument, NULL, 'n'}, + {"exponent", optional_argument, NULL, 'e'}, + {"quiet", no_argument, NULL, 'q'}, + {"pretend", no_argument, NULL, 'p'}, + {"restore", no_argument, NULL, 'r'}, + {"save", no_argument, NULL, 's'}, + {"version", no_argument, NULL, 'V'}, + {NULL,} +}; + +static bool (*write_device)(struct device *) = do_write_device; + +int main(int argc, char **argv) { + struct device *devs[255]; + struct device *dev; + struct utsname name; + char *dev_name, *file_path, *sys_run_dir; + int n, c, phelp = 0; + if (uname(&name)) + fail("Unable to determine current OS. Exiting!\n"); + if (strcmp(name.sysname, "Linux")) + fail("This program only supports Linux.\n"); + p.exponent = 1; + while (1) { + if ((c = getopt_long(argc, argv, "lqpmn::e::srhVc:d:", options, NULL)) < 0) + break; + switch (c) { + case 'l': + p.list = true; + break; + case 'q': + p.quiet = true; + break; + case 'p': + p.pretend = true; + break; + case 's': + p.save = true; + break; + case 'r': + p.restore = true; + break; + case 'm': + p.mach = true; + break; + case 'n': + if (optarg) + p.min = atol(optarg); + else + p.min = 1; + break; + case 'e': + if (optarg) + p.exponent = atof(optarg); + else + p.exponent = 4; + break; + case 'h': + usage(); + exit(EXIT_SUCCESS); + case 'c': + p.class = strdup(optarg); + break; + case 'd': + p.device = strdup(optarg); + break; + case 'V': + printf("%s\n", VERSION); + exit(0); + break; + default: + phelp++; + } + } + if (phelp) { + usage(); + exit(EXIT_FAILURE); + } + argc -= optind; + argv += optind; + if (p.class) { + if (!(n = read_class(devs, p.class))) + fail("Failed to read any devices of class '%s'.\n", p.class); + } else { + if (!(n = read_devices(devs))) + fail("Failed to read any devices.\n"); + } + devs[n] = NULL; + if (p.list) { + list_devices(devs); + return 0; + } + dev_name = p.device; + if (!dev_name) + dev_name = devs[0]->id; + if (argc == 0) + p.operation = INFO; + else switch (argv[0][0]) { + case 'm': + p.operation = MAX; break; + case 's': + p.operation = SET; break; + case 'g': + p.operation = GET; break; + default: + case 'i': + p.operation = INFO; break; + } + argc--; + argv++; + if (p.operation == SET && argc == 0) + fail("You need to provide a value to set.\n"); + if (p.operation == SET && !parse_value(&p.val, argv[0])) + fail("Invalid value given"); + if (!(dev = find_device(devs, dev_name))) + fail("Device '%s' not found.\n", dev_name); + if ((p.operation == SET || p.restore) && !p.pretend && geteuid()) { + errno = 0; + file_path = cat_with('/', path, dev->class, dev->id, "brightness"); + if (access(file_path, W_OK)) { +#ifdef ENABLE_SYSTEMD + write_device = logind_set_brightness; +#else + perror("Can't modify brightness"); + fail("\nYou should run this program with root privileges.\n" + "Alternatively, get write permissions for device files.\n"); +#endif + } + free(file_path); + } + if ((sys_run_dir = getenv("XDG_RUNTIME_DIR"))) + run_dir = dir_child(sys_run_dir, "brightnessctl"); + if (p.save) + if (!save_device_data(dev)) + fprintf(stderr, "Could not save data for device '%s'.\n", dev->id); + if (p.restore) { + if (restore_device_data(dev)) + write_device(dev); + } + return apply_operation(dev, p.operation, &p.val); +} + +int apply_operation(struct device *dev, enum operation operation, struct value *val) { + switch (operation) { + case INFO: + print_device(dev); + return 0; + case GET: + fprintf(stdout, "%u\n", dev->curr_brightness); + return 0; + case MAX: + fprintf(stdout, "%u\n", dev->max_brightness); + return 0; + case SET: + dev->curr_brightness = calc_value(dev, val); + if (!p.pretend) + if (!write_device(dev)) + goto fail; + if (!p.quiet) { + if (!p.mach) + fprintf(stdout, "Updated device '%s':\n", dev->id); + print_device(dev); + } + return 0; + /* FALLTHRU */ + fail: + default: + return 1; + } +} + +bool parse_value(struct value *val, char *str) { + long n; + char c; + char *buf; + errno = 0; + val->v_type = ABSOLUTE; + val->d_type = DIRECT; + val->sign = PLUS; + if (!str || !*str) + return false; + if (*str == '+' || *str == '-') { + val->sign = *str == '+' ? PLUS : MINUS; + val->d_type = DELTA; + str++; + } + n = strtol(str, &buf, 10); + if (errno || buf == str) + return false; + val->val = labs(n) % LONG_MAX; + while ((c = *(buf++))) switch(c) { + case '+': + val->sign = PLUS; + val->d_type = DELTA; + break; + case '-': + val->sign = MINUS; + val->d_type = DELTA; + break; + case '%': + val->v_type = RELATIVE; + break; + } + return true; +} + +struct device *find_device(struct device **devs, char *name) { + struct device *dev; + while ((dev = *(devs++))) + if (!fnmatch(name, dev->id, 0)) + return dev; + return NULL; +} + +void list_devices(struct device **devs) { + struct device *dev; + if (!p.mach) + fprintf(stdout, "Available devices:\n"); + while ((dev = *(devs++))) + print_device(dev); +} + +float val_to_percent(float val, struct device *d, bool rnd) { + if (val < 0) + return 0; + float ret = powf(val / d->max_brightness, 1.0f / p.exponent) * 100; + return rnd ? roundf(ret) : ret; +} + +unsigned long percent_to_val(float percent, struct device *d) { + return roundf(powf(percent / 100, p.exponent) * d->max_brightness); +} + +void print_device(struct device *dev) { + char *format = p.mach ? "%s,%s,%d,%d%%,%d\n" : + "Device '%s' of class '%s':\n\tCurrent brightness: %d (%d%%)\n\tMax brightness: %d\n\n"; + fprintf(stdout, format, + dev->id, dev->class, + dev->curr_brightness, + (int) val_to_percent(dev->curr_brightness, dev, true), + dev->max_brightness); +} + +unsigned int calc_value(struct device *d, struct value *val) { + long new = d->curr_brightness; + if (val->d_type == DIRECT) { + new = val->v_type == ABSOLUTE ? val->val : percent_to_val(val->val, d); + goto apply; + } + long mod = val->val; + if (val->sign == MINUS) + mod *= -1; + if (val->v_type == RELATIVE) { + mod = percent_to_val(val_to_percent(d->curr_brightness, d, false) + mod, d) - d->curr_brightness; + if (val->val != 0 && mod == 0) + mod = val->sign == PLUS ? 1 : -1; + } + new += mod; +apply: + if (new < p.min) + new = p.min; + if (new < 0) + new = 0; + if (new > d->max_brightness) + new = d->max_brightness; + return new; +} + +#ifdef ENABLE_SYSTEMD + +bool logind_set_brightness(struct device *d) { + sd_bus *bus = NULL; + int r = sd_bus_default_system(&bus); + if (r < 0) { + fprintf(stderr, "Can't connect to system bus: %s\n", strerror(-r)); + return false; + } + + r = sd_bus_call_method(bus, + "org.freedesktop.login1", + "/org/freedesktop/login1/session/auto", + "org.freedesktop.login1.Session", + "SetBrightness", + NULL, + NULL, + "ssu", + d->class, + d->id, + d->curr_brightness); + if (r < 0) + fprintf(stderr, "Failed to set brightness: %s\n", strerror(-r)); + + sd_bus_unref(bus); + + return r >= 0; +} + +#endif + +bool do_write_device(struct device *d) { + FILE *f; + char c[16]; + size_t s = sprintf(c, "%u", d->curr_brightness); + errno = 0; + if (s <= 0) { + errno = EINVAL; + goto fail; + } + if ((f = fopen(dir_child(device_path(d), "brightness"), "w"))) { + if (fwrite(c, 1, s, f) < s) + goto close; + } else + goto fail; + errno = 0; +close: + fclose(f); +fail: + if (errno) + perror("Error writing device"); + return !errno; +} + +bool read_device(struct device *d, char *class, char *id) { + DIR *dirp; + FILE *f; + char *dev_path = NULL; + char *ent_path; + int error = 0; + struct dirent *ent; + bool cur; + d->class = strdup(class); + d->id = strdup(id); + dev_path = device_path(d); + if (!(dirp = opendir(dev_path))) + goto dfail; + while ((ent = readdir(dirp))) { + if (!strcmp(ent->d_name, ".") && !strcmp(ent->d_name, "..")) + continue; + if ((cur = !strcmp(ent->d_name, "brightness")) || + !strcmp(ent->d_name, "max_brightness")) { + if (!(f = fopen(ent_path = dir_child(dev_path, ent->d_name), "r"))) + goto fail; + clearerr(f); + if (fscanf(f, "%u", cur ? &d->curr_brightness : &d->max_brightness) == EOF) { + fprintf(stderr, "End-of-file reading %s of device '%s'.", + cur ? "brightness" : "max brightness", d->id); + error++; + } else if (ferror(f)) { + fprintf(stderr, "Error reading %s of device '%s': %s.", + cur ? "brightness" : "max brightness", d->id, strerror(errno)); + error++; + } + fclose(f); + free(ent_path); + ent_path = NULL; + } + } + errno = 0; +fail: + closedir(dirp); +dfail: + free(dev_path); + free(ent_path); + if (errno) { + perror("Error reading device"); + error++; + } + return !error; +} + +int read_class(struct device **devs, char *class) { + DIR *dirp; + struct dirent *ent; + struct device *dev; + char *c_path; + int cnt = 0; + dirp = opendir(c_path = class_path(class)); + if (!dirp) + return 0; + while ((ent = readdir(dirp))) { + if (!strcmp(ent->d_name, ".") || !strcmp(ent->d_name, "..")) + continue; + dev = malloc(sizeof(struct device)); + if (!read_device(dev, class, ent->d_name)) { + free(dev); + continue; + } + devs[cnt++] = dev; + } + closedir(dirp); + free(c_path); + return cnt; +} + +int read_devices(struct device **devs) { + size_t n = 0; + char *class; + int cnt = 0; + while ((class = classes[n++])) + cnt += read_class(devs + cnt, class); + return cnt; +} + +bool save_device_data(struct device *dev) { + char c[16]; + size_t s = sprintf(c, "%u", dev->curr_brightness); + char *d_path = cat_with('/', run_dir, dev->class, dev->id); + FILE *fp; + mode_t old = 0; + int error = 0; + errno = 0; + if (s <= 0) { + fprintf(stderr, "Error converting device data."); + error++; + goto fail; + } + if (!ensure_dev_dir(dev)) + goto fail; + old = umask(0); + fp = fopen(d_path, "w"); + umask(old); + if (!fp) + goto fail; + if (fwrite(c, 1, s, fp) < s) { + fprintf(stderr, "Error writing to '%s'.\n", d_path); + error++; + } + fclose(fp); +fail: + free(d_path); + if (errno) { + perror("Error saving device data"); + error++; + } + return !error; +} + +bool restore_device_data(struct device *dev) { + char buf[16]; + char *filename = cat_with('/', run_dir, dev->class, dev->id); + char *end; + FILE *fp; + memset(buf, 0, 16); + errno = 0; + if (!(fp = fopen(filename, "r"))) + goto fail; + if (!fread(buf, 1, 15, fp)) + goto rfail; + dev->curr_brightness = strtol(buf, &end, 10); + if (end == buf) + errno = EINVAL; +rfail: + fclose(fp); +fail: + free(filename); + if (errno) { + perror("Error restoring device data"); + return false; + } + return true; +} + + +bool ensure_dir(char *dir) { + struct stat sb; + if (stat(dir, &sb)) { + if (errno != ENOENT) + return false; + errno = 0; + if (mkdir(dir, 0777)) { + return false; + } + if (stat(dir, &sb)) + return false; + } + if (!S_ISDIR(sb.st_mode)) { + errno = ENOTDIR; + return false; + } + return true; +} + +bool ensure_dev_dir(struct device *dev) { + char *cpath; + bool ret; + if (!ensure_run_dir()) + return false; + cpath = dir_child(run_dir, dev->class); + ret = ensure_dir(cpath); + free(cpath); + return ret; +} + +char *_cat_with(char c, ...) { + size_t size = 32; + size_t length = 0; + char *buf = calloc(1, size + 1); + char *curr; + char split[2] = {c, '\0'}; + va_list va; + va_start(va, c); + curr = va_arg(va, char *); + while (curr) { + length += strlen(curr); + while (length + 2 > size) + buf = realloc(buf, size *= 2); + strcat(buf, curr); + if ((curr = va_arg(va, char*))) { + length++; + strcat(buf, split); + } + } + return buf; +} + +char *dir_child(char *parent, char *child) { + return cat_with('/', parent, child); +} + +char *device_path(struct device *dev) { + return cat_with('/', path, dev->class, dev->id); +} + +char *class_path(char *class) { + return dir_child(path, class); +} + +void fail(char *err_msg, ...) { + va_list va; + va_start(va, err_msg); + vfprintf(stderr, err_msg, va); + va_end(va); + exit(EXIT_FAILURE); +} + +void usage() { + fprintf(stderr, "brightnessctl %s - read and control device brightness.\n\n", VERSION); + fprintf(stderr, +"Usage: brightnessctl [options] [operation] [value]\n\ +\n\ +Options:\n\ + -l, --list\t\t\tlist devices with available brightness controls.\n\ + -q, --quiet\t\t\tsuppress output.\n\ + -p, --pretend\t\t\tdo not perform write operations.\n\ + -m, --machine-readable\tproduce machine-readable output.\n\ + -n, --min-value\t\tset minimum brightness, defaults to 1.\n\ + -e, --exponent[=K]\t\tchanges percentage curve to exponential.\n\ + -s, --save\t\t\tsave previous state in a temporary file.\n\ + -r, --restore\t\t\trestore previous saved state.\n\ + -h, --help\t\t\tprint this help.\n\ + -d, --device=DEVICE\t\tspecify device name (can be a wildcard).\n\ + -c, --class=CLASS\t\tspecify device class.\n\ + -V, --version\t\t\tprint version and exit.\n\ +\n\ +Operations:\n\ + i, info\t\t\tget device info.\n\ + g, get\t\t\tget current brightness of the device.\n\ + m, max\t\t\tget maximum brightness of the device.\n\ + s, set VALUE\t\t\tset brightness of the device.\n\ +\n\ +Valid values:\n\ + specific value\t\tExample: 500\n\ + percentage value\t\tExample: 50%%\n\ + specific delta\t\tExample: 50- or +10\n\ + percentage delta\t\tExample: 50%%- or +10%%\n\ +\n"); +} + diff --git a/dwl-0.7/etc/portage/patches/gui-wm/dwl/autostart-0.7.patch b/dwl-0.7/etc/portage/patches/gui-wm/dwl/autostart-0.7.patch new file mode 100644 index 0000000..12e6d7e --- /dev/null +++ b/dwl-0.7/etc/portage/patches/gui-wm/dwl/autostart-0.7.patch @@ -0,0 +1,154 @@ +From 787f7252d63945996f009828aff3c44afd0f7781 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?= + +Date: Sat, 8 Jul 2023 17:11:36 -0600 +Subject: [PATCH] port autostart patch from dwm +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +https://dwm.suckless.org/patches/cool_autostart/ +Signed-off-by: Leonardo Hernández Hernández +--- + config.def.h | 7 +++++++ + dwl.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++----- + 2 files changed, 61 insertions(+), 5 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 22d2171..8dc6502 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -20,6 +20,13 @@ static const float fullscreen_bg[] = {0.1f, 0.1f, 0.1f, 1.0f}; /* You ca + /* logging */ + static int log_level = WLR_ERROR; + ++/* Autostart */ ++static const char *const autostart[] = { ++ "wbg", "/path/to/your/image", NULL, ++ NULL /* terminate */ ++}; ++ ++ + /* NOTE: ALWAYS keep a rule declared even if you don't use rules (e.g leave at least one example) */ + static const Rule rules[] = { + /* app_id title tags mask isfloating monitor */ +diff --git a/dwl.c b/dwl.c +index 5bf995e..e8b8727 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -249,6 +249,7 @@ static void arrange(Monitor *m); + static void arrangelayer(Monitor *m, struct wl_list *list, + struct wlr_box *usable_area, int exclusive); + static void arrangelayers(Monitor *m); ++static void autostartexec(void); + static void axisnotify(struct wl_listener *listener, void *data); + static void buttonpress(struct wl_listener *listener, void *data); + static void chvt(const Arg *arg); +@@ -432,6 +433,9 @@ static xcb_atom_t netatom[NetLast]; + /* attempt to encapsulate suck into one file */ + #include "client.h" + ++static pid_t *autostart_pids; ++static size_t autostart_len; ++ + /* function implementations */ + void + applybounds(Client *c, struct wlr_box *bbox) +@@ -580,6 +584,27 @@ arrangelayers(Monitor *m) + } + } + ++void ++autostartexec(void) { ++ const char *const *p; ++ size_t i = 0; ++ ++ /* count entries */ ++ for (p = autostart; *p; autostart_len++, p++) ++ while (*++p); ++ ++ autostart_pids = calloc(autostart_len, sizeof(pid_t)); ++ for (p = autostart; *p; i++, p++) { ++ if ((autostart_pids[i] = fork()) == 0) { ++ setsid(); ++ execvp(*p, (char *const *)p); ++ die("dwl: execvp %s:", *p); ++ } ++ /* skip arguments */ ++ while (*++p); ++ } ++} ++ + void + axisnotify(struct wl_listener *listener, void *data) + { +@@ -676,11 +701,21 @@ checkidleinhibitor(struct wlr_surface *exclude) + void + cleanup(void) + { ++ size_t i; + #ifdef XWAYLAND + wlr_xwayland_destroy(xwayland); + xwayland = NULL; + #endif + wl_display_destroy_clients(dpy); ++ ++ /* kill child processes */ ++ for (i = 0; i < autostart_len; i++) { ++ if (0 < autostart_pids[i]) { ++ kill(autostart_pids[i], SIGTERM); ++ waitpid(autostart_pids[i], NULL, 0); ++ } ++ } ++ + if (child_pid > 0) { + kill(-child_pid, SIGTERM); + waitpid(child_pid, NULL, 0); +@@ -1497,18 +1532,31 @@ void + handlesig(int signo) + { + if (signo == SIGCHLD) { +-#ifdef XWAYLAND + siginfo_t in; + /* wlroots expects to reap the XWayland process itself, so we + * use WNOWAIT to keep the child waitable until we know it's not + * XWayland. + */ + while (!waitid(P_ALL, 0, &in, WEXITED|WNOHANG|WNOWAIT) && in.si_pid +- && (!xwayland || in.si_pid != xwayland->server->pid)) +- waitpid(in.si_pid, NULL, 0); +-#else +- while (waitpid(-1, NULL, WNOHANG) > 0); ++#ifdef XWAYLAND ++ && (!xwayland || in.si_pid != xwayland->server->pid) + #endif ++ ) { ++ pid_t *p, *lim; ++ waitpid(in.si_pid, NULL, 0); ++ if (in.si_pid == child_pid) ++ child_pid = -1; ++ if (!(p = autostart_pids)) ++ continue; ++ lim = &p[autostart_len]; ++ ++ for (; p < lim; p++) { ++ if (*p == in.si_pid) { ++ *p = -1; ++ break; ++ } ++ } ++ } + } else if (signo == SIGINT || signo == SIGTERM) { + quit(NULL); + } +@@ -2224,6 +2272,7 @@ run(char *startup_cmd) + die("startup: backend_start"); + + /* Now that the socket exists and the backend is started, run the startup command */ ++ autostartexec(); + if (startup_cmd) { + int piperw[2]; + if (pipe(piperw) < 0) +-- +2.45.2 + diff --git a/dwl-0.7/etc/portage/patches/gui-wm/dwl/gaps.patch b/dwl-0.7/etc/portage/patches/gui-wm/dwl/gaps.patch new file mode 100644 index 0000000..09c3e63 --- /dev/null +++ b/dwl-0.7/etc/portage/patches/gui-wm/dwl/gaps.patch @@ -0,0 +1,127 @@ +From 50e3dd4746b6cb719efb9f8213b94ac52a5320d9 Mon Sep 17 00:00:00 2001 +From: peesock +Date: Mon, 24 Jun 2024 20:06:42 -0700 +Subject: [PATCH] gaps! + +Co-authored-by: sewn +Co-authored-by: serenevoid +--- + config.def.h | 4 ++++ + dwl.c | 34 ++++++++++++++++++++++++++-------- + 2 files changed, 30 insertions(+), 8 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 22d2171..b388b4e 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -6,6 +6,9 @@ + /* appearance */ + static const int sloppyfocus = 1; /* focus follows mouse */ + static const int bypass_surface_visibility = 0; /* 1 means idle inhibitors will disable idle tracking even if it's surface isn't visible */ ++static const int smartgaps = 0; /* 1 means no outer gap when there is only one window */ ++static int gaps = 1; /* 1 means gaps between windows are added */ ++static const unsigned int gappx = 10; /* gap pixel between windows */ + static const unsigned int borderpx = 1; /* border pixel of windows */ + static const float rootcolor[] = COLOR(0x222222ff); + static const float bordercolor[] = COLOR(0x444444ff); +@@ -135,6 +138,7 @@ static const Key keys[] = { + { MODKEY, XKB_KEY_l, setmfact, {.f = +0.05f} }, + { MODKEY, XKB_KEY_Return, zoom, {0} }, + { MODKEY, XKB_KEY_Tab, view, {0} }, ++ { MODKEY, XKB_KEY_g, togglegaps, {0} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_C, killclient, {0} }, + { MODKEY, XKB_KEY_t, setlayout, {.v = &layouts[0]} }, + { MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} }, +diff --git a/dwl.c b/dwl.c +index dc0437e..dc851df 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -199,6 +199,7 @@ struct Monitor { + struct wlr_box w; /* window area, layout-relative */ + struct wl_list layers[4]; /* LayerSurface.link */ + const Layout *lt[2]; + unsigned int seltags; ++ int gaps; + unsigned int sellt; + uint32_t tagset[2]; +@@ -336,6 +337,7 @@ static void tagmon(const Arg *arg); + static void tile(Monitor *m); + static void togglefloating(const Arg *arg); + static void togglefullscreen(const Arg *arg); ++static void togglegaps(const Arg *arg); + static void toggletag(const Arg *arg); + static void toggleview(const Arg *arg); + static void unlocksession(struct wl_listener *listener, void *data); +@@ -949,6 +951,8 @@ createmon(struct wl_listener *listener, void *data) + + wlr_output_state_init(&state); + /* Initialize monitor state using configured rules */ ++ m->gaps = gaps; ++ + m->tagset[0] = m->tagset[1] = 1; + for (r = monrules; r < END(monrules); r++) { + if (!r->name || strstr(wlr_output->name, r->name)) { +@@ -2638,7 +2642,7 @@ tagmon(const Arg *arg) + void + tile(Monitor *m) + { +- unsigned int mw, my, ty; ++ unsigned int h, r, e = m->gaps, mw, my, ty; + int i, n = 0; + Client *c; + +@@ -2647,23 +2651,30 @@ tile(Monitor *m) + n++; + if (n == 0) + return; ++ if (smartgaps == n) ++ e = 0; + + if (n > m->nmaster) +- mw = m->nmaster ? (int)roundf(m->w.width * m->mfact) : 0; ++ mw = m->nmaster ? (int)roundf((m->w.width + gappx*e) * m->mfact) : 0; + else + mw = m->w.width; +- i = my = ty = 0; ++ i = 0; ++ my = ty = gappx*e; + wl_list_for_each(c, &clients, link) { + if (!VISIBLEON(c, m) || c->isfloating || c->isfullscreen) + continue; + if (i < m->nmaster) { +- resize(c, (struct wlr_box){.x = m->w.x, .y = m->w.y + my, .width = mw, +- .height = (m->w.height - my) / (MIN(n, m->nmaster) - i)}, 0); +- my += c->geom.height; ++ r = MIN(n, m->nmaster) - i; ++ h = (m->w.height - my - gappx*e - gappx*e * (r - 1)) / r; ++ resize(c, (struct wlr_box){.x = m->w.x + gappx*e, .y = m->w.y + my, ++ .width = mw - 2*gappx*e, .height = h}, 0); ++ my += c->geom.height + gappx*e; + } else { ++ r = n - i; ++ h = (m->w.height - ty - gappx*e - gappx*e * (r - 1)) / r; + resize(c, (struct wlr_box){.x = m->w.x + mw, .y = m->w.y + ty, +- .width = m->w.width - mw, .height = (m->w.height - ty) / (n - i)}, 0); +- ty += c->geom.height; ++ .width = m->w.width - mw - gappx*e, .height = h}, 0); ++ ty += c->geom.height + gappx*e; + } + i++; + } +@@ -2686,6 +2697,13 @@ togglefullscreen(const Arg *arg) + setfullscreen(sel, !sel->isfullscreen); + } + ++void ++togglegaps(const Arg *arg) ++{ ++ selmon->gaps = !selmon->gaps; ++ arrange(selmon); ++} ++ + void + toggletag(const Arg *arg) + { +-- +2.45.2 + diff --git a/dwl-0.7/etc/portage/patches/gui-wm/dwl/ipc-edited.patch b/dwl-0.7/etc/portage/patches/gui-wm/dwl/ipc-edited.patch new file mode 100644 index 0000000..a0fa0f0 --- /dev/null +++ b/dwl-0.7/etc/portage/patches/gui-wm/dwl/ipc-edited.patch @@ -0,0 +1,600 @@ +From 6c6d655b68770ce82a24fde9b58c4d97b672553a Mon Sep 17 00:00:00 2001 +From: choc +Date: Mon, 23 Oct 2023 10:35:17 +0800 +Subject: [PATCH] implement dwl-ipc-unstable-v2 + https://codeberg.org/dwl/dwl-patches/wiki/ipc + +--- + Makefile | 14 +- + config.def.h | 1 + + dwl.c | 257 ++++++++++++++++++++++++++---- + protocols/dwl-ipc-unstable-v2.xml | 181 +++++++++++++++++++++ + 4 files changed, 419 insertions(+), 34 deletions(-) + create mode 100644 protocols/dwl-ipc-unstable-v2.xml + +diff --git a/Makefile b/Makefile +index 8db7409..a79a080 100644 +--- a/Makefile ++++ b/Makefile +@@ -17,12 +17,14 @@ DWLCFLAGS = `$(PKG_CONFIG) --cflags $(PKGS)` $(WLR_INCS) $(DWLCPPFLAGS) $(DWLDEV + LDLIBS = `$(PKG_CONFIG) --libs $(PKGS)` $(WLR_LIBS) -lm $(LIBS) + + all: dwl +-dwl: dwl.o util.o +- $(CC) dwl.o util.o $(DWLCFLAGS) $(LDFLAGS) $(LDLIBS) -o $@ ++dwl: dwl.o util.o dwl-ipc-unstable-v2-protocol.o ++ $(CC) dwl.o util.o dwl-ipc-unstable-v2-protocol.o $(DWLCFLAGS) $(LDFLAGS) $(LDLIBS) -o $@ + dwl.o: dwl.c client.h config.h config.mk cursor-shape-v1-protocol.h \ + pointer-constraints-unstable-v1-protocol.h wlr-layer-shell-unstable-v1-protocol.h \ +- wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h ++ wlr-output-power-management-unstable-v1-protocol.h xdg-shell-protocol.h \ ++ dwl-ipc-unstable-v2-protocol.h + util.o: util.c util.h ++dwl-ipc-unstable-v2-protocol.o: dwl-ipc-unstable-v2-protocol.c dwl-ipc-unstable-v2-protocol.h + + # wayland-scanner is a tool which generates C headers and rigging for Wayland + # protocols, which are specified in XML. wlroots requires you to rig these up +@@ -45,6 +47,12 @@ wlr-output-power-management-unstable-v1-protocol.h: + xdg-shell-protocol.h: + $(WAYLAND_SCANNER) server-header \ + $(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@ ++dwl-ipc-unstable-v2-protocol.h: ++ $(WAYLAND_SCANNER) server-header \ ++ protocols/dwl-ipc-unstable-v2.xml $@ ++dwl-ipc-unstable-v2-protocol.c: ++ $(WAYLAND_SCANNER) private-code \ ++ protocols/dwl-ipc-unstable-v2.xml $@ + + config.h: + cp config.def.h $@ +diff --git a/config.def.h b/config.def.h +index 22d2171..1593033 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -127,6 +127,7 @@ static const Key keys[] = { + /* modifier key function argument */ + { MODKEY, XKB_KEY_p, spawn, {.v = menucmd} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return, spawn, {.v = termcmd} }, ++ { MODKEY, XKB_KEY_b, togglebar, {0} }, + { MODKEY, XKB_KEY_j, focusstack, {.i = +1} }, + { MODKEY, XKB_KEY_k, focusstack, {.i = -1} }, + { MODKEY, XKB_KEY_i, incnmaster, {.i = +1} }, +diff --git a/dwl.c b/dwl.c +index 8a587d1..7a4949b 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -68,6 +68,7 @@ + #include + #endif + ++#include "dwl-ipc-unstable-v2-protocol.h" + #include "util.h" + + /* macros */ +@@ -144,6 +145,12 @@ typedef struct { + uint32_t resize; /* configure serial of a pending resize */ + } Client; + ++typedef struct { ++ struct wl_list link; ++ struct wl_resource *resource; ++ Monitor *mon; ++} DwlIpcOutput; ++ + typedef struct { + uint32_t mod; + xkb_keysym_t keysym; +@@ -189,6 +196,7 @@ typedef struct { + + struct Monitor { + struct wl_list link; ++ struct wl_list dwl_ipc_outputs; + struct wlr_output *wlr_output; + struct wlr_scene_output *scene_output; + struct wlr_scene_rect *fullscreen_bg; /* See createmon() for info */ +@@ -286,6 +294,17 @@ static void destroysessionlock(struct wl_listener *listener, void *data); + static void destroysessionmgr(struct wl_listener *listener, void *data); + static void destroykeyboardgroup(struct wl_listener *listener, void *data); + static Monitor *dirtomon(enum wlr_direction dir); ++static void dwl_ipc_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id); ++static void dwl_ipc_manager_destroy(struct wl_resource *resource); ++static void dwl_ipc_manager_get_output(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *output); ++static void dwl_ipc_manager_release(struct wl_client *client, struct wl_resource *resource); ++static void dwl_ipc_output_destroy(struct wl_resource *resource); ++static void dwl_ipc_output_printstatus(Monitor *monitor); ++static void dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output); ++static void dwl_ipc_output_set_client_tags(struct wl_client *client, struct wl_resource *resource, uint32_t and_tags, uint32_t xor_tags); ++static void dwl_ipc_output_set_layout(struct wl_client *client, struct wl_resource *resource, uint32_t index); ++static void dwl_ipc_output_set_tags(struct wl_client *client, struct wl_resource *resource, uint32_t tagmask, uint32_t toggle_tagset); ++static void dwl_ipc_output_release(struct wl_client *client, struct wl_resource *resource); + static void focusclient(Client *c, int lift); + static void focusmon(const Arg *arg); + static void focusstack(const Arg *arg); +@@ -338,6 +357,7 @@ static void startdrag(struct wl_listener *listener, void *data); + static void tag(const Arg *arg); + static void tagmon(const Arg *arg); + static void tile(Monitor *m); ++static void togglebar(const Arg *arg); + static void togglefloating(const Arg *arg); + static void togglefullscreen(const Arg *arg); + static void toggletag(const Arg *arg); +@@ -411,6 +431,9 @@ static struct wlr_box sgeom; + static struct wl_list mons; + static Monitor *selmon; + ++static struct zdwl_ipc_manager_v2_interface dwl_manager_implementation = {.release = dwl_ipc_manager_release, .get_output = dwl_ipc_manager_get_output}; ++static struct zdwl_ipc_output_v2_interface dwl_output_implementation = {.release = dwl_ipc_output_release, .set_tags = dwl_ipc_output_set_tags, .set_layout = dwl_ipc_output_set_layout, .set_client_tags = dwl_ipc_output_set_client_tags}; ++ + #ifdef XWAYLAND + static void activatex11(struct wl_listener *listener, void *data); + static void associatex11(struct wl_listener *listener, void *data); +@@ -703,6 +726,10 @@ cleanupmon(struct wl_listener *listener, void *data) + LayerSurface *l, *tmp; + size_t i; + ++ DwlIpcOutput *ipc_output, *ipc_output_tmp; ++ wl_list_for_each_safe(ipc_output, ipc_output_tmp, &m->dwl_ipc_outputs, link) ++ wl_resource_destroy(ipc_output->resource); ++ + /* m->layers[i] are intentionally not unlinked */ + for (i = 0; i < LENGTH(m->layers); i++) { + wl_list_for_each_safe(l, tmp, &m->layers[i], link) +@@ -983,6 +1010,8 @@ createmon(struct wl_listener *listener, void *data) + m = wlr_output->data = ecalloc(1, sizeof(*m)); + m->wlr_output = wlr_output; + ++ wl_list_init(&m->dwl_ipc_outputs); ++ + for (i = 0; i < LENGTH(m->layers); i++) + wl_list_init(&m->layers[i]); + +@@ -1334,6 +1363,192 @@ dirtomon(enum wlr_direction dir) + return selmon; + } + ++void ++dwl_ipc_manager_bind(struct wl_client *client, void *data, uint32_t version, uint32_t id) ++{ ++ struct wl_resource *manager_resource = wl_resource_create(client, &zdwl_ipc_manager_v2_interface, version, id); ++ if (!manager_resource) { ++ wl_client_post_no_memory(client); ++ return; ++ } ++ wl_resource_set_implementation(manager_resource, &dwl_manager_implementation, NULL, dwl_ipc_manager_destroy); ++ ++ zdwl_ipc_manager_v2_send_tags(manager_resource, TAGCOUNT); ++ ++ for (unsigned int i = 0; i < LENGTH(layouts); i++) ++ zdwl_ipc_manager_v2_send_layout(manager_resource, layouts[i].symbol); ++} ++ ++void ++dwl_ipc_manager_destroy(struct wl_resource *resource) ++{ ++ /* No state to destroy */ ++} ++ ++void ++dwl_ipc_manager_get_output(struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *output) ++{ ++ DwlIpcOutput *ipc_output; ++ Monitor *monitor = wlr_output_from_resource(output)->data; ++ struct wl_resource *output_resource = wl_resource_create(client, &zdwl_ipc_output_v2_interface, wl_resource_get_version(resource), id); ++ if (!output_resource) ++ return; ++ ++ ipc_output = ecalloc(1, sizeof(*ipc_output)); ++ ipc_output->resource = output_resource; ++ ipc_output->mon = monitor; ++ wl_resource_set_implementation(output_resource, &dwl_output_implementation, ipc_output, dwl_ipc_output_destroy); ++ wl_list_insert(&monitor->dwl_ipc_outputs, &ipc_output->link); ++ dwl_ipc_output_printstatus_to(ipc_output); ++} ++ ++void ++dwl_ipc_manager_release(struct wl_client *client, struct wl_resource *resource) ++{ ++ wl_resource_destroy(resource); ++} ++ ++static void ++dwl_ipc_output_destroy(struct wl_resource *resource) ++{ ++ DwlIpcOutput *ipc_output = wl_resource_get_user_data(resource); ++ wl_list_remove(&ipc_output->link); ++ free(ipc_output); ++} ++ ++void ++dwl_ipc_output_printstatus(Monitor *monitor) ++{ ++ DwlIpcOutput *ipc_output; ++ wl_list_for_each(ipc_output, &monitor->dwl_ipc_outputs, link) ++ dwl_ipc_output_printstatus_to(ipc_output); ++} ++ ++void ++dwl_ipc_output_printstatus_to(DwlIpcOutput *ipc_output) ++{ ++ Monitor *monitor = ipc_output->mon; ++ Client *c, *focused; ++ int tagmask, state, numclients, focused_client, tag; ++ const char *title, *appid; ++ focused = focustop(monitor); ++ zdwl_ipc_output_v2_send_active(ipc_output->resource, monitor == selmon); ++ ++ for (tag = 0 ; tag < TAGCOUNT; tag++) { ++ numclients = state = focused_client = 0; ++ tagmask = 1 << tag; ++ if ((tagmask & monitor->tagset[monitor->seltags]) != 0) ++ state |= ZDWL_IPC_OUTPUT_V2_TAG_STATE_ACTIVE; ++ ++ wl_list_for_each(c, &clients, link) { ++ if (c->mon != monitor) ++ continue; ++ if (!(c->tags & tagmask)) ++ continue; ++ if (c == focused) ++ focused_client = 1; ++ if (c->isurgent) ++ state |= ZDWL_IPC_OUTPUT_V2_TAG_STATE_URGENT; ++ ++ numclients++; ++ } ++ zdwl_ipc_output_v2_send_tag(ipc_output->resource, tag, state, numclients, focused_client); ++ } ++ title = focused ? client_get_title(focused) : ""; ++ appid = focused ? client_get_appid(focused) : ""; ++ ++ zdwl_ipc_output_v2_send_layout(ipc_output->resource, monitor->lt[monitor->sellt] - layouts); ++ zdwl_ipc_output_v2_send_title(ipc_output->resource, title); ++ zdwl_ipc_output_v2_send_appid(ipc_output->resource, appid); ++ zdwl_ipc_output_v2_send_layout_symbol(ipc_output->resource, monitor->ltsymbol); ++ if (wl_resource_get_version(ipc_output->resource) >= ZDWL_IPC_OUTPUT_V2_FULLSCREEN_SINCE_VERSION) { ++ zdwl_ipc_output_v2_send_fullscreen(ipc_output->resource, focused ? focused->isfullscreen : 0); ++ } ++ if (wl_resource_get_version(ipc_output->resource) >= ZDWL_IPC_OUTPUT_V2_FLOATING_SINCE_VERSION) { ++ zdwl_ipc_output_v2_send_floating(ipc_output->resource, focused ? focused->isfloating : 0); ++ } ++ zdwl_ipc_output_v2_send_frame(ipc_output->resource); ++} ++ ++void ++dwl_ipc_output_set_client_tags(struct wl_client *client, struct wl_resource *resource, uint32_t and_tags, uint32_t xor_tags) ++{ ++ DwlIpcOutput *ipc_output; ++ Monitor *monitor; ++ Client *selected_client; ++ unsigned int newtags = 0; ++ ++ ipc_output = wl_resource_get_user_data(resource); ++ if (!ipc_output) ++ return; ++ ++ monitor = ipc_output->mon; ++ selected_client = focustop(monitor); ++ if (!selected_client) ++ return; ++ ++ newtags = (selected_client->tags & and_tags) ^ xor_tags; ++ if (!newtags) ++ return; ++ ++ selected_client->tags = newtags; ++ if (selmon == monitor) ++ focusclient(focustop(monitor), 1); ++ arrange(selmon); ++ printstatus(); ++} ++ ++void ++dwl_ipc_output_set_layout(struct wl_client *client, struct wl_resource *resource, uint32_t index) ++{ ++ DwlIpcOutput *ipc_output; ++ Monitor *monitor; ++ ++ ipc_output = wl_resource_get_user_data(resource); ++ if (!ipc_output) ++ return; ++ ++ monitor = ipc_output->mon; ++ if (index >= LENGTH(layouts)) ++ return; ++ if (index != monitor->lt[monitor->sellt] - layouts) ++ monitor->sellt ^= 1; ++ ++ monitor->lt[monitor->sellt] = &layouts[index]; ++ arrange(monitor); ++ printstatus(); ++} ++ ++void ++dwl_ipc_output_set_tags(struct wl_client *client, struct wl_resource *resource, uint32_t tagmask, uint32_t toggle_tagset) ++{ ++ DwlIpcOutput *ipc_output; ++ Monitor *monitor; ++ unsigned int newtags = tagmask & TAGMASK; ++ ++ ipc_output = wl_resource_get_user_data(resource); ++ if (!ipc_output) ++ return; ++ monitor = ipc_output->mon; ++ ++ if (!newtags || newtags == monitor->tagset[monitor->seltags]) ++ return; ++ if (toggle_tagset) ++ monitor->seltags ^= 1; ++ ++ monitor->tagset[monitor->seltags] = newtags; ++ if (selmon == monitor) ++ focusclient(focustop(monitor), 1); ++ arrange(monitor); ++ printstatus(); ++} ++ ++void ++dwl_ipc_output_release(struct wl_client *client, struct wl_resource *resource) ++{ ++ wl_resource_destroy(resource); ++} ++ + void + focusclient(Client *c, int lift) + { +@@ -2033,41 +2248,9 @@ void + printstatus(void) + { + Monitor *m = NULL; +- Client *c; +- uint32_t occ, urg, sel; +- const char *appid, *title; + +- wl_list_for_each(m, &mons, link) { +- occ = urg = 0; +- wl_list_for_each(c, &clients, link) { +- if (c->mon != m) +- continue; +- occ |= c->tags; +- if (c->isurgent) +- urg |= c->tags; +- } +- if ((c = focustop(m))) { +- title = client_get_title(c); +- appid = client_get_appid(c); +- printf("%s title %s\n", m->wlr_output->name, title ? title : broken); +- printf("%s appid %s\n", m->wlr_output->name, appid ? appid : broken); +- printf("%s fullscreen %d\n", m->wlr_output->name, c->isfullscreen); +- printf("%s floating %d\n", m->wlr_output->name, c->isfloating); +- sel = c->tags; +- } else { +- printf("%s title \n", m->wlr_output->name); +- printf("%s appid \n", m->wlr_output->name); +- printf("%s fullscreen \n", m->wlr_output->name); +- printf("%s floating \n", m->wlr_output->name); +- sel = 0; +- } +- +- printf("%s selmon %u\n", m->wlr_output->name, m == selmon); +- printf("%s tags %"PRIu32" %"PRIu32" %"PRIu32" %"PRIu32"\n", +- m->wlr_output->name, occ, m->tagset[m->seltags], sel, urg); +- printf("%s layout %s\n", m->wlr_output->name, m->ltsymbol); +- } +- fflush(stdout); ++ wl_list_for_each(m, &mons, link) ++ dwl_ipc_output_printstatus(m); + } + + void +@@ -2584,6 +2770,8 @@ setup(void) + LISTEN_STATIC(&output_mgr->events.apply, outputmgrapply); + LISTEN_STATIC(&output_mgr->events.test, outputmgrtest); + ++ wl_global_create(dpy, &zdwl_ipc_manager_v2_interface, 2, NULL, dwl_ipc_manager_bind); ++ + /* Make sure XWayland clients don't connect to the parent X server, + * e.g when running in the x11 backend or the wayland backend and the + * compositor has Xwayland support */ +@@ -2681,6 +2869,13 @@ tile(Monitor *m) + } + } + ++void ++togglebar(const Arg *arg) { ++ DwlIpcOutput *ipc_output; ++ wl_list_for_each(ipc_output, &selmon->dwl_ipc_outputs, link) ++ zdwl_ipc_output_v2_send_toggle_visibility(ipc_output->resource); ++} ++ + void + togglefloating(const Arg *arg) + { +diff --git a/protocols/dwl-ipc-unstable-v2.xml b/protocols/dwl-ipc-unstable-v2.xml +new file mode 100644 +index 0000000..0a6e7e5 +--- /dev/null ++++ b/protocols/dwl-ipc-unstable-v2.xml +@@ -0,0 +1,181 @@ ++ ++ ++ ++ ++ This protocol allows clients to update and get updates from dwl. ++ ++ Warning! The protocol described in this file is experimental and ++ backward incompatible changes may be made. Backward compatible ++ changes may be added together with the corresponding interface ++ version bump. ++ Backward incompatible changes are done by bumping the version ++ number in the protocol and interface names and resetting the ++ interface version. Once the protocol is to be declared stable, ++ the 'z' prefix and the version number in the protocol and ++ interface names are removed and the interface version number is ++ reset. ++ ++ ++ ++ ++ This interface is exposed as a global in wl_registry. ++ ++ Clients can use this interface to get a dwl_ipc_output. ++ After binding the client will recieve the dwl_ipc_manager.tags and dwl_ipc_manager.layout events. ++ The dwl_ipc_manager.tags and dwl_ipc_manager.layout events expose tags and layouts to the client. ++ ++ ++ ++ ++ Indicates that the client will not the dwl_ipc_manager object anymore. ++ Objects created through this instance are not affected. ++ ++ ++ ++ ++ ++ Get a dwl_ipc_outout for the specified wl_output. ++ ++ ++ ++ ++ ++ ++ ++ This event is sent after binding. ++ A roundtrip after binding guarantees the client recieved all tags. ++ ++ ++ ++ ++ ++ ++ This event is sent after binding. ++ A roundtrip after binding guarantees the client recieved all layouts. ++ ++ ++ ++ ++ ++ ++ ++ Observe and control a dwl output. ++ ++ Events are double-buffered: ++ Clients should cache events and redraw when a dwl_ipc_output.frame event is sent. ++ ++ Request are not double-buffered: ++ The compositor will update immediately upon request. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ Indicates to that the client no longer needs this dwl_ipc_output. ++ ++ ++ ++ ++ ++ Indicates the client should hide or show themselves. ++ If the client is visible then hide, if hidden then show. ++ ++ ++ ++ ++ ++ Indicates if the output is active. Zero is invalid, nonzero is valid. ++ ++ ++ ++ ++ ++ ++ Indicates that a tag has been updated. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ Indicates a new layout is selected. ++ ++ ++ ++ ++ ++ ++ Indicates the title has changed. ++ ++ ++ ++ ++ ++ ++ Indicates the appid has changed. ++ ++ ++ ++ ++ ++ ++ Indicates the layout has changed. Since layout symbols are dynamic. ++ As opposed to the zdwl_ipc_manager.layout event, this should take precendence when displaying. ++ You can ignore the zdwl_ipc_output.layout event. ++ ++ ++ ++ ++ ++ ++ Indicates that a sequence of status updates have finished and the client should redraw. ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ The tags are updated as follows: ++ new_tags = (current_tags AND and_tags) XOR xor_tags ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ ++ Indicates if the selected client on this output is fullscreen. ++ ++ ++ ++ ++ ++ ++ Indicates if the selected client on this output is floating. ++ ++ ++ ++ ++ +-- +2.43.0 + diff --git a/dwl-0.7/etc/portage/patches/gui-wm/dwl/movestack.patch b/dwl-0.7/etc/portage/patches/gui-wm/dwl/movestack.patch new file mode 100644 index 0000000..37bed7d --- /dev/null +++ b/dwl-0.7/etc/portage/patches/gui-wm/dwl/movestack.patch @@ -0,0 +1,79 @@ +From b051f50233033b399db324b29ab24227257ac141 Mon Sep 17 00:00:00 2001 +From: wochap +Date: Tue, 5 Mar 2024 23:31:51 -0500 +Subject: [PATCH] apply NikitaIvanovV movestack patch + +source: https://github.com/djpohly/dwl/wiki/movestack +--- + config.def.h | 2 ++ + dwl.c | 35 +++++++++++++++++++++++++++++++++++ + 2 files changed, 37 insertions(+) + +diff --git a/config.def.h b/config.def.h +index db0babc..778a0dc 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -122,6 +122,8 @@ static const Key keys[] = { + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return, spawn, {.v = termcmd} }, + { MODKEY, XKB_KEY_j, focusstack, {.i = +1} }, + { MODKEY, XKB_KEY_k, focusstack, {.i = -1} }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_J, movestack, {.i = +1} }, ++ { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_K, movestack, {.i = -1} }, + { MODKEY, XKB_KEY_i, incnmaster, {.i = +1} }, + { MODKEY, XKB_KEY_d, incnmaster, {.i = -1} }, + { MODKEY, XKB_KEY_h, setmfact, {.f = -0.05} }, +diff --git a/dwl.c b/dwl.c +index ef27a1d..69b9756 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -279,6 +279,7 @@ static void maplayersurfacenotify(struct wl_listener *listener, void *data); + static void mapnotify(struct wl_listener *listener, void *data); + static void maximizenotify(struct wl_listener *listener, void *data); + static void monocle(Monitor *m); ++static void movestack(const Arg *arg); + static void motionabsolute(struct wl_listener *listener, void *data); + static void motionnotify(uint32_t time); + static void motionrelative(struct wl_listener *listener, void *data); +@@ -1603,6 +1604,40 @@ monocle(Monitor *m) + wlr_scene_node_raise_to_top(&c->scene->node); + } + ++void ++movestack(const Arg *arg) ++{ ++ Client *c, *sel = focustop(selmon); ++ ++ if (!sel) { ++ return; ++ } ++ ++ if (wl_list_length(&clients) <= 1) { ++ return; ++ } ++ ++ if (arg->i > 0) { ++ wl_list_for_each(c, &sel->link, link) { ++ if (VISIBLEON(c, selmon) || &c->link == &clients) { ++ break; /* found it */ ++ } ++ } ++ } else { ++ wl_list_for_each_reverse(c, &sel->link, link) { ++ if (VISIBLEON(c, selmon) || &c->link == &clients) { ++ break; /* found it */ ++ } ++ } ++ /* backup one client */ ++ c = wl_container_of(c->link.prev, c, link); ++ } ++ ++ wl_list_remove(&sel->link); ++ wl_list_insert(&c->link, &sel->link); ++ arrange(selmon); ++} ++ + void + motionabsolute(struct wl_listener *listener, void *data) + { +-- +2.42.0 diff --git a/dwl-0.7/etc/portage/patches/gui-wm/dwl/pertag.patch b/dwl-0.7/etc/portage/patches/gui-wm/dwl/pertag.patch new file mode 100644 index 0000000..971732a --- /dev/null +++ b/dwl-0.7/etc/portage/patches/gui-wm/dwl/pertag.patch @@ -0,0 +1,170 @@ +From d3b551ffe3ec85e16341962e322150b81af6722f Mon Sep 17 00:00:00 2001 +From: wochap +Date: Wed, 31 Jul 2024 08:27:26 -0500 +Subject: [PATCH] makes layout, mwfact and nmaster individual for every tag + +inspiration: https://github.com/djpohly/dwl/wiki/pertag +--- + dwl.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---- + 1 file changed, 70 insertions(+), 5 deletions(-) + +diff --git a/dwl.c b/dwl.c +index 145fd01..2f364bc 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -102,6 +102,7 @@ typedef struct { + const Arg arg; + } Button; + ++typedef struct Pertag Pertag; + typedef struct Monitor Monitor; + typedef struct { + /* Must keep these three elements in this order */ +@@ -199,6 +200,7 @@ struct Monitor { + struct wlr_box w; /* window area, layout-relative */ + struct wl_list layers[4]; /* LayerSurface.link */ + const Layout *lt[2]; ++ Pertag *pertag; + unsigned int seltags; + unsigned int sellt; + uint32_t tagset[2]; +@@ -427,6 +429,14 @@ static xcb_atom_t netatom[NetLast]; + /* attempt to encapsulate suck into one file */ + #include "client.h" + ++struct Pertag { ++ unsigned int curtag, prevtag; /* current and previous tag */ ++ int nmasters[TAGCOUNT + 1]; /* number of windows in master area */ ++ float mfacts[TAGCOUNT + 1]; /* mfacts per tag */ ++ unsigned int sellts[TAGCOUNT + 1]; /* selected layouts */ ++ const Layout *ltidxs[TAGCOUNT + 1][2]; /* matrix of tags and layouts indexes */ ++}; ++ + /* function implementations */ + void + applybounds(Client *c, struct wlr_box *bbox) +@@ -712,6 +722,7 @@ cleanupmon(struct wl_listener *listener, void *data) + wlr_output_layout_remove(output_layout, m->wlr_output); + wlr_scene_output_destroy(m->scene_output); + ++ free(m->pertag); + closemon(m); + wlr_scene_node_destroy(&m->fullscreen_bg->node); + free(m); +@@ -983,6 +994,18 @@ createmon(struct wl_listener *listener, void *data) + wl_list_insert(&mons, &m->link); + printstatus(); + ++ m->pertag = calloc(1, sizeof(Pertag)); ++ m->pertag->curtag = m->pertag->prevtag = 1; ++ ++ for (i = 0; i <= TAGCOUNT; i++) { ++ m->pertag->nmasters[i] = m->nmaster; ++ m->pertag->mfacts[i] = m->mfact; ++ ++ m->pertag->ltidxs[i][0] = m->lt[0]; ++ m->pertag->ltidxs[i][1] = m->lt[1]; ++ m->pertag->sellts[i] = m->sellt; ++ } ++ + /* The xdg-protocol specifies: + * + * If the fullscreened surface is not opaque, the compositor must make +@@ -1472,7 +1495,7 @@ incnmaster(const Arg *arg) + { + if (!arg || !selmon) + return; +- selmon->nmaster = MAX(selmon->nmaster + arg->i, 0); ++ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag] = MAX(selmon->nmaster + arg->i, 0); + arrange(selmon); + } + +@@ -2305,9 +2328,9 @@ setlayout(const Arg *arg) + if (!selmon) + return; + if (!arg || !arg->v || arg->v != selmon->lt[selmon->sellt]) +- selmon->sellt ^= 1; ++ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag] ^= 1; + if (arg && arg->v) +- selmon->lt[selmon->sellt] = (Layout *)arg->v; ++ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt] = (Layout *)arg->v; + strncpy(selmon->ltsymbol, selmon->lt[selmon->sellt]->symbol, LENGTH(selmon->ltsymbol)); + arrange(selmon); + printstatus(); +@@ -2324,7 +2347,7 @@ setmfact(const Arg *arg) + f = arg->f < 1.0f ? arg->f + selmon->mfact : arg->f - 1.0f; + if (f < 0.1 || f > 0.9) + return; +- selmon->mfact = f; ++ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag] = f; + arrange(selmon); + } + +@@ -2701,9 +2724,29 @@ void + toggleview(const Arg *arg) + { + uint32_t newtagset; ++ size_t i; + if (!(newtagset = selmon ? selmon->tagset[selmon->seltags] ^ (arg->ui & TAGMASK) : 0)) + return; + ++ if (newtagset == (uint32_t)~0) { ++ selmon->pertag->prevtag = selmon->pertag->curtag; ++ selmon->pertag->curtag = 0; ++ } ++ ++ /* test if the user did not select the same tag */ ++ if (!(newtagset & 1 << (selmon->pertag->curtag - 1))) { ++ selmon->pertag->prevtag = selmon->pertag->curtag; ++ for (i = 0; !(newtagset & 1 << i); i++) ; ++ selmon->pertag->curtag = i + 1; ++ } ++ ++ /* apply settings for this view */ ++ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; ++ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; ++ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; ++ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; ++ selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; ++ + selmon->tagset[selmon->seltags] = newtagset; + focusclient(focustop(selmon), 1); + arrange(selmon); +@@ -2892,11 +2935,33 @@ urgent(struct wl_listener *listener, void *data) + void + view(const Arg *arg) + { ++ size_t i, tmptag; ++ + if (!selmon || (arg->ui & TAGMASK) == selmon->tagset[selmon->seltags]) + return; + selmon->seltags ^= 1; /* toggle sel tagset */ +- if (arg->ui & TAGMASK) ++ if (arg->ui & ~0) { + selmon->tagset[selmon->seltags] = arg->ui & TAGMASK; ++ selmon->pertag->prevtag = selmon->pertag->curtag; ++ ++ if (arg->ui == TAGMASK) ++ selmon->pertag->curtag = 0; ++ else { ++ for (i = 0; !(arg->ui & 1 << i); i++) ; ++ selmon->pertag->curtag = i + 1; ++ } ++ } else { ++ tmptag = selmon->pertag->prevtag; ++ selmon->pertag->prevtag = selmon->pertag->curtag; ++ selmon->pertag->curtag = tmptag; ++ } ++ ++ selmon->nmaster = selmon->pertag->nmasters[selmon->pertag->curtag]; ++ selmon->mfact = selmon->pertag->mfacts[selmon->pertag->curtag]; ++ selmon->sellt = selmon->pertag->sellts[selmon->pertag->curtag]; ++ selmon->lt[selmon->sellt] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt]; ++ selmon->lt[selmon->sellt^1] = selmon->pertag->ltidxs[selmon->pertag->curtag][selmon->sellt^1]; ++ + focusclient(focustop(selmon), 1); + arrange(selmon); + printstatus(); +-- +2.45.2 + diff --git a/dwl-0.7/etc/portage/patches/gui-wm/dwl/xkb-rules-switcher-edited.patch b/dwl-0.7/etc/portage/patches/gui-wm/dwl/xkb-rules-switcher-edited.patch new file mode 100644 index 0000000..f4cef20 --- /dev/null +++ b/dwl-0.7/etc/portage/patches/gui-wm/dwl/xkb-rules-switcher-edited.patch @@ -0,0 +1,99 @@ +From 024d66e3c81d5acb35b5be51f4812fe84614530b Mon Sep 17 00:00:00 2001 +From: wochap +Date: Fri, 5 Jul 2024 11:07:06 -0500 +Subject: [PATCH] implement xkb-rules-switcher + +--- + config.def.h | 17 +++++++++++------ + dwl.c | 19 ++++++++++++++++++- + 2 files changed, 29 insertions(+), 7 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 22d2171..ab73cf0 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -52,12 +52,16 @@ static const MonitorRule monrules[] = { + }; + + /* keyboard */ +-static const struct xkb_rule_names xkb_rules = { +- /* can specify fields: rules, model, layout, variant, options */ +- /* example: +- .options = "ctrl:nocaps", +- */ +- .options = NULL, ++static const struct xkb_rule_names xkb_rules[] = { ++ { ++ /* can specify fields: rules, model, layout, variant, options */ ++ .layout = "us", ++ .options = NULL, ++ }, ++ { ++ .layout = "us", ++ .options = "compose:ralt", ++ }, + }; + + static const int repeat_rate = 25; +@@ -148,6 +152,7 @@ static const Key keys[] = { + { MODKEY, XKB_KEY_period, focusmon, {.i = WLR_DIRECTION_RIGHT} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_less, tagmon, {.i = WLR_DIRECTION_LEFT} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_greater, tagmon, {.i = WLR_DIRECTION_RIGHT} }, ++ { MODKEY, XKB_KEY_w, switchxkbrule, {0} }, + TAGKEYS( XKB_KEY_1, XKB_KEY_exclam, 0), + TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1), + TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2), +diff --git a/dwl.c b/dwl.c +index dc0437e..ecbdf9a 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -371,6 +371,7 @@ static void setsel(struct wl_listener *listener, void *data); + static void setup(void); + static void spawn(const Arg *arg); + static void startdrag(struct wl_listener *listener, void *data); ++static void switchxkbrule(const Arg *arg); + static void tag(const Arg *arg); + static void tagmon(const Arg *arg); + static void tile(Monitor *m); +@@ -400,6 +401,7 @@ static struct wl_listener lock_listener = {.notify = locksession}; + static struct wlr_seat *seat; + static KeyboardGroup *kb_group; + static unsigned int cursor_mode; ++static unsigned int xkb_rule_index = 0; + static Client *grabc; + static int grabcx, grabcy; /* client-relative */ + +@@ -838,7 +840,7 @@ createkeyboardgroup(void) + + /* Prepare an XKB keymap and assign it to the keyboard group. */ + context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); +- if (!(keymap = xkb_keymap_new_from_names(context, &xkb_rules, ++ if (!(keymap = xkb_keymap_new_from_names(context, &xkb_rules[xkb_rule_index], + XKB_KEYMAP_COMPILE_NO_FLAGS))) + die("failed to compile keymap"); + +@@ -2614,6 +2616,21 @@ startdrag(struct wl_listener *listener, void *data) + LISTEN_STATIC(&drag->icon->events.destroy, destroydragicon); + } + ++void ++switchxkbrule(const Arg *arg) ++{ ++ struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); ++ struct xkb_keymap *keymap; ++ ++ xkb_rule_index = (xkb_rule_index + 1) % LENGTH(xkb_rules); ++ if (!(keymap = xkb_keymap_new_from_names(context, &xkb_rules[xkb_rule_index], ++ XKB_KEYMAP_COMPILE_NO_FLAGS))) ++ die("failed to compile keymap"); ++ wlr_keyboard_set_keymap(&kb_group->wlr_group->keyboard, keymap); ++ xkb_keymap_unref(keymap); ++ xkb_context_unref(context); ++} ++ + void + tag(const Arg *arg) + { +-- +2.45.1 + diff --git a/dwl-0.7/etc/portage/patches/gui-wm/dwl/zzz-ipc-edited-pertag.patch b/dwl-0.7/etc/portage/patches/gui-wm/dwl/zzz-ipc-edited-pertag.patch new file mode 100644 index 0000000..97e3c33 --- /dev/null +++ b/dwl-0.7/etc/portage/patches/gui-wm/dwl/zzz-ipc-edited-pertag.patch @@ -0,0 +1,97 @@ +From c7d77ff4dec1da5a68b4da8aa42d4ed78dc41a00 Mon Sep 17 00:00:00 2001 +From: choc +Date: Thu, 14 Mar 2024 11:18:37 +0800 +Subject: [PATCH] fix ipc to work with pertag + +--- + dwl.c | 50 +++++++++++++++++++++++++++++++++++--------------- + 1 file changed, 35 insertions(+), 15 deletions(-) + +diff --git a/dwl.c b/dwl.c +index a1a7809..d86e6e2 100644 +--- a/dwl.c ++++ b/dwl.c +@@ -1526,28 +1526,37 @@ void + dwl_ipc_output_set_layout(struct wl_client *client, struct wl_resource *resource, uint32_t index) + { + DwlIpcOutput *ipc_output; +- Monitor *monitor; ++ Client *c = NULL; ++ Monitor *monitor = NULL; + + ipc_output = wl_resource_get_user_data(resource); + if (!ipc_output) + return; +- + monitor = ipc_output->mon; ++ ++ if (monitor != selmon) ++ c = focustop(selmon); ++ + if (index >= LENGTH(layouts)) + return; +- if (index != monitor->lt[monitor->sellt] - layouts) +- monitor->sellt ^= 1; + +- monitor->lt[monitor->sellt] = &layouts[index]; +- arrange(monitor); +- printstatus(); ++ if (c) { ++ monitor = selmon; ++ selmon = ipc_output->mon; ++ } ++ setlayout(&(Arg){.v = &layouts[index]}); ++ if (c) { ++ selmon = monitor; ++ focusclient(c, 0); ++ } + } + + void + dwl_ipc_output_set_tags(struct wl_client *client, struct wl_resource *resource, uint32_t tagmask, uint32_t toggle_tagset) + { + DwlIpcOutput *ipc_output; +- Monitor *monitor; ++ Client *c = NULL; ++ Monitor *monitor = NULL; + unsigned int newtags = tagmask & TAGMASK; + + ipc_output = wl_resource_get_user_data(resource); +@@ -1555,16 +1564,27 @@ dwl_ipc_output_set_tags(struct wl_client *client, struct wl_resource *resource, + return; + monitor = ipc_output->mon; + +- if (!newtags || newtags == monitor->tagset[monitor->seltags]) ++ if (monitor != selmon) ++ c = focustop(selmon); ++ ++ if (!newtags) + return; +- if (toggle_tagset) ++ ++ /* view toggles seltags for us so we un-toggle it */ ++ if (!toggle_tagset) { + monitor->seltags ^= 1; ++ monitor->tagset[monitor->seltags] = 0; ++ } + +- monitor->tagset[monitor->seltags] = newtags; +- if (selmon == monitor) +- focusclient(focustop(monitor), 1); +- arrange(monitor); +- printstatus(); ++ if (c) { ++ monitor = selmon; ++ selmon = ipc_output->mon; ++ } ++ view(&(Arg){.ui = newtags}); ++ if (c) { ++ selmon = monitor; ++ focusclient(c, 0); ++ } + } + + void +-- +2.43.0 + diff --git a/dwl-0.7/etc/portage/savedconfig/gui-wm/dwl-0.7 b/dwl-0.7/etc/portage/savedconfig/gui-wm/dwl-0.7 new file mode 100644 index 0000000..e57d553 --- /dev/null +++ b/dwl-0.7/etc/portage/savedconfig/gui-wm/dwl-0.7 @@ -0,0 +1,230 @@ +/* Taken from https://github.com/djpohly/dwl/issues/466 */ +#define COLOR(hex) { ((hex >> 24) & 0xFF) / 255.0f, \ + ((hex >> 16) & 0xFF) / 255.0f, \ + ((hex >> 8) & 0xFF) / 255.0f, \ + (hex & 0xFF) / 255.0f } +/* appearance */ +static const int sloppyfocus = 1; /* focus follows mouse */ +static const int bypass_surface_visibility = 0; /* 1 means idle inhibitors will disable idle tracking even if it's surface isn't visible */ +static const int smartgaps = 1; /* 1 means no outer gap when there is only one window */ +static int gaps = 1; /* 1 means gaps between windows are added */ +static const unsigned int gappx = 8; /* gap pixel between windows */ +static const unsigned int borderpx = 2; /* border pixel of windows */ +static const float rootcolor[] = COLOR(0x000000ff); +static const float bordercolor[] = COLOR(0x444444ff); +static const float focuscolor[] = COLOR(0xa3be8cff); +static const float urgentcolor[] = COLOR(0x770000ff); +/* This conforms to the xdg-protocol. Set the alpha to zero to restore the old behavior */ +static const float fullscreen_bg[] = {0.1f, 0.1f, 0.1f, 1.0f}; /* You can also use glsl colors */ + +/* tagging - TAGCOUNT must be no greater than 31 */ +#define TAGCOUNT (9) + +/* logging */ +static int log_level = WLR_ERROR; + +/* Autostart */ +static const char *const autostart[] = { + "mako", "--background-color=#222222FF", "--text-color=#A3BE8CFF", "--border-color=#A3BE8CFF", "", NULL, + // "cyclewallpapers", "/home/tanguy/Pictures/wallpapers", NULL, + "nm-applet", NULL, + "waybar", NULL, + "swaybg", "-i", "/home/tanguy/Pictures/wallpapers/P6012286.jpg", "-m", "fill", NULL, + "swaybg", "-o", "DP-2", "-i", "/home/tanguy/Pictures/wallpapers/P6012286.jpg", "-m", "fill", NULL, + "swaybg", "-o", "eDP-1", "-i", "/home/tanguy/Pictures/wallpapers/wallpaper11.jpg", "-m", "fill", NULL, + "wlsunset", "-l", "60.10", "-L", "24.56", NULL, + // "/usr/libexec/org_kde_powerdevil", NULL, + NULL /* terminate */ +}; + +/* NOTE: ALWAYS keep a rule declared even if you don't use rules (e.g leave at least one example) */ +static const Rule rules[] = { + /* app_id title tags mask isfloating monitor */ + /* examples: */ + { "Gimp_EXAMPLE", NULL, 0, 1, -1 }, /* Start on currently visible tags floating, not tiled */ + { "firefox_EXAMPLE", NULL, 1 << 8, 0, -1 }, /* Start on ONLY tag "9" */ +}; + +/* layout(s) */ +static const Layout layouts[] = { + /* symbol arrange function */ + { "[]=", tile }, + { "><>", NULL }, /* no layout function means floating behavior */ + { "[M]", monocle }, +}; + +/* monitors */ +/* (x=-1, y=-1) is reserved as an "autoconfigure" monitor position indicator + * WARNING: negative values other than (-1, -1) cause problems with Xwayland clients + * https://gitlab.freedesktop.org/xorg/xserver/-/issues/899 +*/ +/* NOTE: ALWAYS add a fallback rule, even if you are completely sure it won't be used */ +static const MonitorRule monrules[] = { + /* name mfact nmaster scale layout rotate/reflect x y */ + /* example of a HiDPI laptop monitor: + { "eDP-1", 0.5f, 1, 2, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 }, + */ + /* defaults */ + { "DP-2", 0.55f, 1, 1.0, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, 0, 0 }, + { NULL, 0.55f, 1, 1.3, &layouts[0], WL_OUTPUT_TRANSFORM_NORMAL, -1, -1 }, +}; + +static const struct xkb_rule_names xkb_rules[] = { + { + /* can specify fields: rules, model, layout, variant, options */ + .layout = "us", + .variant = "altgr-intl", + .options = NULL, + }, +// { +// .layout = "fi", +// //.variant = "", +// .options = NULL, +// }, +// { +// .layout = "ua", +// .variant = "phonetic", +// .options = NULL, +// }, +}; + + +static const int repeat_rate = 25; +static const int repeat_delay = 600; + +/* Trackpad */ +static const int tap_to_click = 1; +static const int tap_and_drag = 1; +static const int drag_lock = 1; +static const int natural_scrolling = 0; +static const int disable_while_typing = 1; +static const int left_handed = 0; +static const int middle_button_emulation = 0; +/* You can choose between: +LIBINPUT_CONFIG_SCROLL_NO_SCROLL +LIBINPUT_CONFIG_SCROLL_2FG +LIBINPUT_CONFIG_SCROLL_EDGE +LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN +*/ +static const enum libinput_config_scroll_method scroll_method = LIBINPUT_CONFIG_SCROLL_2FG; + +/* You can choose between: +LIBINPUT_CONFIG_CLICK_METHOD_NONE +LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS +LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER +*/ +static const enum libinput_config_click_method click_method = LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER; + +/* You can choose between: +LIBINPUT_CONFIG_SEND_EVENTS_ENABLED +LIBINPUT_CONFIG_SEND_EVENTS_DISABLED +LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE +*/ +static const uint32_t send_events_mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED; + +/* You can choose between: +LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT +LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE +*/ +static const enum libinput_config_accel_profile accel_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE; +static const double accel_speed = 0.0; + +/* You can choose between: +LIBINPUT_CONFIG_TAP_MAP_LRM -- 1/2/3 finger tap maps to left/right/middle +LIBINPUT_CONFIG_TAP_MAP_LMR -- 1/2/3 finger tap maps to left/middle/right +*/ +static const enum libinput_config_tap_button_map button_map = LIBINPUT_CONFIG_TAP_MAP_LRM; + +/* If you want to use the windows key for MODKEY, use WLR_MODIFIER_LOGO */ +#define MODKEY WLR_MODIFIER_LOGO + +#define TAGKEYS(KEY,SKEY,TAG) \ + { MODKEY, KEY, view, {.ui = 1 << TAG} }, \ + { MODKEY|WLR_MODIFIER_CTRL, KEY, toggleview, {.ui = 1 << TAG} }, \ + { MODKEY|WLR_MODIFIER_SHIFT, SKEY, tag, {.ui = 1 << TAG} }, \ + { MODKEY|WLR_MODIFIER_CTRL|WLR_MODIFIER_SHIFT,SKEY,toggletag, {.ui = 1 << TAG} } + +/* helper for spawning shell commands in the pre dwm-5.0 fashion */ +#define SHCMD(cmd) { .v = (const char*[]){ "/bin/sh", "-c", cmd, NULL } } + +/* commands */ +static const char *termcmd[] = { "foot", NULL }; +static const char *menucmd[] = { "rofi", "-show", "run", NULL }; +static const char *lockcmd[] = { "/usr/libexec/kscreenlocker_greet", NULL }; +static const char *brightupcmd[] = { "sudo", "/usr/bin/brightnessctl", "set", "10%+", NULL }; +static const char *brightdowncmd[] = { "sudo", "/usr/bin/brightnessctl", "set", "10%-", NULL }; +static const char *prevcmd[] = { "playerctl", "previous", NULL }; +static const char *playpausecmd[] = { "playerctl", "play-pause", NULL }; +static const char *nextcmd[] = { "playerctl", "next", NULL }; +static const char *mutecmd[] = { "wpctl", "set-mute", "@DEFAULT_SINK@", "toggle", NULL }; +static const char *volumeupcmd[] = { "wpctl", "set-volume", "@DEFAULT_SINK@", "-l", "1", "5%+", NULL }; +static const char *volumedowncmd[] = { "wpctl", "set-volume", "@DEFAULT_SINK@", "5%-", NULL }; + +static const Key keys[] = { + /* Note that Shift changes certain key codes: c -> C, 2 -> at, etc. */ + /* modifier key function argument */ + { MODKEY, XKB_KEY_p, spawn, {.v = menucmd} }, + { MODKEY, XKB_KEY_Return, spawn, {.v = termcmd} }, + { MODKEY|WLR_MODIFIER_CTRL, XKB_KEY_l, spawn, {.v = lockcmd} }, + { 0, XKB_KEY_XF86MonBrightnessUp, spawn, {.v = brightupcmd} }, + { 0, XKB_KEY_XF86MonBrightnessDown, spawn, {.v = brightdowncmd} }, + { 0, XKB_KEY_XF86AudioPrev, spawn, {.v = prevcmd} }, + { 0, XKB_KEY_XF86AudioPlay, spawn, {.v = playpausecmd} }, + { 0, XKB_KEY_XF86AudioNext, spawn, {.v = nextcmd} }, + { 0, XKB_KEY_XF86AudioMute, spawn, {.v = mutecmd} }, + { 0, XKB_KEY_XF86AudioRaiseVolume, spawn, {.v = volumeupcmd} }, + { 0, XKB_KEY_XF86AudioLowerVolume, spawn, {.v = volumedowncmd} }, + { MODKEY, XKB_KEY_b, togglebar, {0} }, + { MODKEY, XKB_KEY_j, focusstack, {.i = +1} }, + { MODKEY, XKB_KEY_k, focusstack, {.i = -1} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_J, movestack, {.i = +1} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_K, movestack, {.i = -1} }, + { MODKEY, XKB_KEY_i, incnmaster, {.i = +1} }, + { MODKEY, XKB_KEY_d, incnmaster, {.i = -1} }, + { MODKEY, XKB_KEY_h, setmfact, {.f = -0.05f} }, + { MODKEY, XKB_KEY_l, setmfact, {.f = +0.05f} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Return, zoom, {0} }, + { MODKEY, XKB_KEY_Tab, view, {0} }, + { MODKEY, XKB_KEY_g, togglegaps, {0} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_C, killclient, {0} }, + { MODKEY, XKB_KEY_t, setlayout, {.v = &layouts[0]} }, + { MODKEY, XKB_KEY_f, setlayout, {.v = &layouts[1]} }, + { MODKEY, XKB_KEY_m, setlayout, {.v = &layouts[2]} }, + { MODKEY, XKB_KEY_space, setlayout, {0} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_space, togglefloating, {0} }, + { MODKEY, XKB_KEY_e, togglefullscreen, {0} }, + { MODKEY, XKB_KEY_0, view, {.ui = ~0} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_parenright, tag, {.ui = ~0} }, + { MODKEY, XKB_KEY_comma, focusmon, {.i = WLR_DIRECTION_LEFT} }, + { MODKEY, XKB_KEY_period, focusmon, {.i = WLR_DIRECTION_RIGHT} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_less, tagmon, {.i = WLR_DIRECTION_LEFT} }, + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_greater, tagmon, {.i = WLR_DIRECTION_RIGHT} }, +// { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_question, switchxkbrule, {0} }, + { MODKEY, XKB_KEY_Escape, switchxkbrule, {0} }, + TAGKEYS( XKB_KEY_1, XKB_KEY_exclam, 0), + TAGKEYS( XKB_KEY_2, XKB_KEY_at, 1), + TAGKEYS( XKB_KEY_3, XKB_KEY_numbersign, 2), + TAGKEYS( XKB_KEY_4, XKB_KEY_dollar, 3), + TAGKEYS( XKB_KEY_5, XKB_KEY_percent, 4), + TAGKEYS( XKB_KEY_6, XKB_KEY_asciicircum, 5), + TAGKEYS( XKB_KEY_7, XKB_KEY_ampersand, 6), + TAGKEYS( XKB_KEY_8, XKB_KEY_asterisk, 7), + TAGKEYS( XKB_KEY_9, XKB_KEY_parenleft, 8), + { MODKEY|WLR_MODIFIER_SHIFT, XKB_KEY_Q, quit, {0} }, + + + /* Ctrl-Alt-Backspace and Ctrl-Alt-Fx used to be handled by X server */ + { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_Terminate_Server, quit, {0} }, + /* Ctrl-Alt-Fx is used to switch to another VT, if you don't know what a VT is + * do not remove them. + */ +#define CHVT(n) { WLR_MODIFIER_CTRL|WLR_MODIFIER_ALT,XKB_KEY_XF86Switch_VT_##n, chvt, {.ui = (n)} } + CHVT(1), CHVT(2), CHVT(3), CHVT(4), CHVT(5), CHVT(6), + CHVT(7), CHVT(8), CHVT(9), CHVT(10), CHVT(11), CHVT(12), +}; + +static const Button buttons[] = { + { MODKEY, BTN_LEFT, moveresize, {.ui = CurMove} }, + { MODKEY, BTN_MIDDLE, togglefloating, {0} }, + { MODKEY, BTN_RIGHT, moveresize, {.ui = CurResize} }, +}; diff --git a/startdwl b/startdwl new file mode 100755 index 0000000..4f64c1b --- /dev/null +++ b/startdwl @@ -0,0 +1,3 @@ +#!/bin/sh +gentoo-pipewire-launcher & +exec dbus-run-session ssh-agent /usr/bin/dwl