Add dwl config

This commit is contained in:
Tanguy Gérôme 2025-10-10 23:02:50 +03:00
parent 60afadae1e
commit 172b10ceac
Signed by: tanguy
GPG key ID: 10B2947233740B88
16 changed files with 2519 additions and 0 deletions

2
brightnessctl-0.5.1/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
brightnessctl
*.swp

View file

@ -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"

View file

@ -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.]

View file

@ -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

View file

@ -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%
```

View file

@ -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

View file

@ -0,0 +1,668 @@
#include <sys/types.h>
#include <sys/utsname.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#include <errno.h>
#include <fnmatch.h>
#include <limits.h>
#include <getopt.h>
#include <stdio.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#ifdef ENABLE_SYSTEMD
# include <systemd/sd-bus.h>
#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");
}

View file

@ -0,0 +1,154 @@
From 787f7252d63945996f009828aff3c44afd0f7781 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Leonardo=20Hern=C3=A1ndez=20Hern=C3=A1ndez?=
<leohdz172@proton.me>
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 <leohdz172@proton.me>
---
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

View file

@ -0,0 +1,127 @@
From 50e3dd4746b6cb719efb9f8213b94ac52a5320d9 Mon Sep 17 00:00:00 2001
From: peesock <kcormn@gmail.com>
Date: Mon, 24 Jun 2024 20:06:42 -0700
Subject: [PATCH] gaps!
Co-authored-by: sewn <sewn@disroot.org>
Co-authored-by: serenevoid <ajuph9224@gmail.com>
---
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

View file

@ -0,0 +1,600 @@
From 6c6d655b68770ce82a24fde9b58c4d97b672553a Mon Sep 17 00:00:00 2001
From: choc <notchoc@proton.me>
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 <xcb/xcb_icccm.h>
#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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+This is largely ripped from somebar's ipc patchset; just with some personal modifications.
+I would probably just submit raphi's patchset but I don't think that would be polite.
+-->
+<protocol name="dwl_ipc_unstable_v2">
+ <description summary="inter-proccess-communication about dwl's state">
+ 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.
+ </description>
+
+ <interface name="zdwl_ipc_manager_v2" version="2">
+ <description summary="manage dwl state">
+ 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.
+ </description>
+
+ <request name="release" type="destructor">
+ <description summary="release dwl_ipc_manager">
+ Indicates that the client will not the dwl_ipc_manager object anymore.
+ Objects created through this instance are not affected.
+ </description>
+ </request>
+
+ <request name="get_output">
+ <description summary="get a dwl_ipc_outout for a wl_output">
+ Get a dwl_ipc_outout for the specified wl_output.
+ </description>
+ <arg name="id" type="new_id" interface="zdwl_ipc_output_v2"/>
+ <arg name="output" type="object" interface="wl_output"/>
+ </request>
+
+ <event name="tags">
+ <description summary="Announces tag amount">
+ This event is sent after binding.
+ A roundtrip after binding guarantees the client recieved all tags.
+ </description>
+ <arg name="amount" type="uint"/>
+ </event>
+
+ <event name="layout">
+ <description summary="Announces a layout">
+ This event is sent after binding.
+ A roundtrip after binding guarantees the client recieved all layouts.
+ </description>
+ <arg name="name" type="string"/>
+ </event>
+ </interface>
+
+ <interface name="zdwl_ipc_output_v2" version="2">
+ <description summary="control dwl output">
+ 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.
+ </description>
+
+ <enum name="tag_state">
+ <entry name="none" value="0" summary="no state"/>
+ <entry name="active" value="1" summary="tag is active"/>
+ <entry name="urgent" value="2" summary="tag has at least one urgent client"/>
+ </enum>
+
+ <request name="release" type="destructor">
+ <description summary="release dwl_ipc_outout">
+ Indicates to that the client no longer needs this dwl_ipc_output.
+ </description>
+ </request>
+
+ <event name="toggle_visibility">
+ <description summary="Toggle client visibilty">
+ Indicates the client should hide or show themselves.
+ If the client is visible then hide, if hidden then show.
+ </description>
+ </event>
+
+ <event name="active">
+ <description summary="Update the selected output.">
+ Indicates if the output is active. Zero is invalid, nonzero is valid.
+ </description>
+ <arg name="active" type="uint"/>
+ </event>
+
+ <event name="tag">
+ <description summary="Update the state of a tag.">
+ Indicates that a tag has been updated.
+ </description>
+ <arg name="tag" type="uint" summary="Index of the tag"/>
+ <arg name="state" type="uint" enum="tag_state" summary="The state of the tag."/>
+ <arg name="clients" type="uint" summary="The number of clients in the tag."/>
+ <arg name="focused" type="uint" summary="If there is a focused client. Nonzero being valid, zero being invalid."/>
+ </event>
+
+ <event name="layout">
+ <description summary="Update the layout.">
+ Indicates a new layout is selected.
+ </description>
+ <arg name="layout" type="uint" summary="Index of the layout."/>
+ </event>
+
+ <event name="title">
+ <description summary="Update the title.">
+ Indicates the title has changed.
+ </description>
+ <arg name="title" type="string" summary="The new title name."/>
+ </event>
+
+ <event name="appid" since="1">
+ <description summary="Update the appid.">
+ Indicates the appid has changed.
+ </description>
+ <arg name="appid" type="string" summary="The new appid."/>
+ </event>
+
+ <event name="layout_symbol" since="1">
+ <description summary="Update the current layout symbol">
+ 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.
+ </description>
+ <arg name="layout" type="string" summary="The new layout"/>
+ </event>
+
+ <event name="frame">
+ <description summary="The update sequence is done.">
+ Indicates that a sequence of status updates have finished and the client should redraw.
+ </description>
+ </event>
+
+ <request name="set_tags">
+ <description summary="Set the active tags of this output"/>
+ <arg name="tagmask" type="uint" summary="bitmask of the tags that should be set."/>
+ <arg name="toggle_tagset" type="uint" summary="toggle the selected tagset, zero for invalid, nonzero for valid."/>
+ </request>
+
+ <request name="set_client_tags">
+ <description summary="Set the tags of the focused client.">
+ The tags are updated as follows:
+ new_tags = (current_tags AND and_tags) XOR xor_tags
+ </description>
+ <arg name="and_tags" type="uint"/>
+ <arg name="xor_tags" type="uint"/>
+ </request>
+
+ <request name="set_layout">
+ <description summary="Set the layout of this output"/>
+ <arg name="index" type="uint" summary="index of a layout recieved by dwl_ipc_manager.layout"/>
+ </request>
+
+ <!-- Version 2 -->
+ <event name="fullscreen" since="2">
+ <description summary="Update fullscreen status">
+ Indicates if the selected client on this output is fullscreen.
+ </description>
+ <arg name="is_fullscreen" type="uint" summary="If the selected client is fullscreen. Nonzero is valid, zero invalid"/>
+ </event>
+
+ <event name="floating" since="2">
+ <description summary="Update the floating status">
+ Indicates if the selected client on this output is floating.
+ </description>
+ <arg name="is_floating" type="uint" summary="If the selected client is floating. Nonzero is valid, zero invalid"/>
+ </event>
+ </interface>
+</protocol>
--
2.43.0

View file

@ -0,0 +1,79 @@
From b051f50233033b399db324b29ab24227257ac141 Mon Sep 17 00:00:00 2001
From: wochap <gean.marroquin@gmail.com>
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

View file

@ -0,0 +1,170 @@
From d3b551ffe3ec85e16341962e322150b81af6722f Mon Sep 17 00:00:00 2001
From: wochap <gean.marroquin@gmail.com>
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

View file

@ -0,0 +1,99 @@
From 024d66e3c81d5acb35b5be51f4812fe84614530b Mon Sep 17 00:00:00 2001
From: wochap <gean.marroquin@gmail.com>
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

View file

@ -0,0 +1,97 @@
From c7d77ff4dec1da5a68b4da8aa42d4ed78dc41a00 Mon Sep 17 00:00:00 2001
From: choc <notchoc@proton.me>
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

View file

@ -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} },
};

3
startdwl Executable file
View file

@ -0,0 +1,3 @@
#!/bin/sh
gentoo-pipewire-launcher &
exec dbus-run-session ssh-agent /usr/bin/dwl