~kennylevinsen/wlsunset-devel

This thread contains a patchset. You're looking at the original emails, but you may wish to use the patch review UI. Review patch
1

[PATCH] Add option to run a hook when changing temperature

Details
Message ID
<20220808191936.3659-1-r@gnzler.io>
DKIM signature
missing
Download raw message
Patch: +58 -2
Allows things like configuring gtk apps to prefer a dark
colorscheme (see sample hook script)

Signed-off-by: Robert Günzler <r@gnzler.io>
---
 contrib/sample_hook.sh | 21 +++++++++++++++++++++
 main.c                 | 34 ++++++++++++++++++++++++++++++++--
 wlsunset.1.scd         |  5 +++++
 3 files changed, 58 insertions(+), 2 deletions(-)
 create mode 100755 contrib/sample_hook.sh

diff --git a/contrib/sample_hook.sh b/contrib/sample_hook.sh
new file mode 100755
index 0000000..5b78b66
--- /dev/null
+++ b/contrib/sample_hook.sh
@@ -0,0 +1,21 @@
#!/bin/sh

set -e

log() {
	printf "wlsunset: changing to %s\n" "$1"
	notify-send -a wlsunset "wlsunset changing to $1" || true
}

case "${1:-daytime}" in
day*)
	log "$1"
	# gsettings set org.gnome.desktop.interface gtk-theme Adwaita
	# gsettings set org.gnome.desktop.interface color-scheme prefer-light
	;;
night)
	log "$1"
	# gsettings set org.gnome.desktop.interface gtk-theme Adwaita-dark
	# gsettings set org.gnome.desktop.interface color-scheme prefer-dark
	;;
esac
diff --git a/main.c b/main.c
index 95330bf..d36fc8b 100644
--- a/main.c
+++ b/main.c
@@ -109,6 +109,7 @@ struct config {
	time_t duration;

	struct str_vec output_names;
	char *hook;
};

enum state {
@@ -182,6 +183,29 @@ static void print_trajectory(struct context *ctx) {
	}
}

static char* phase_str(int low_temp, int high_temp, int temp) {
	if (temp == high_temp) {
		return "daytime";
	} else if (temp == low_temp) {
		return "night";
	} else if ((high_temp - low_temp) >= 2) {
		return "dusk";
	} else {
		return "dawn";
	}
}

static void run_hook(const struct context *ctx, const char *phase) {
	if (!ctx->config.hook) return;
	if (!phase) return;

	char *cmd = strcat(strcat(strdup(ctx->config.hook), " "), phase);
	fprintf(stderr, "running hook for phase %s\n", phase);

	if (system(cmd)) {;}
	free(cmd);
}

static int anim_kelvin_step = 25;

static void recalc_stops(struct context *ctx, time_t now) {
@@ -781,6 +805,7 @@ static int wlrun(struct config cfg) {

	int temp = get_temperature(&ctx, now);
	set_temperature(&ctx.outputs, temp, ctx.config.gamma);
	run_hook(&ctx, phase_str(ctx.config.low_temp, ctx.config.high_temp, temp));

	int old_temp = temp;
	while (display_dispatch(display, -1) != -1) {
@@ -801,6 +826,7 @@ static int wlrun(struct config cfg) {

			set_temperature(&ctx.outputs, temp, ctx.config.gamma);
		}
		run_hook(&ctx, phase_str(ctx.config.low_temp, ctx.config.high_temp, temp));
	}

	return EXIT_SUCCESS;
@@ -829,7 +855,8 @@ static const char usage[] = "usage: %s [options]\n"
"  -S <sunrise>   set manual sunrise (e.g. 06:30)\n"
"  -s <sunset>    set manual sunset (e.g. 18:30)\n"
"  -d <duration>  set manual duration in seconds (e.g. 1800)\n"
"  -g <gamma>     set gamma (default: 1.0)\n";
"  -g <gamma>     set gamma (default: 1.0)\n"
"  -H <hook>      set path to hook to execute on state change (e.g. /path/to/hook.sh)\n";

int main(int argc, char *argv[]) {
#ifdef SPEEDRUN
@@ -848,7 +875,7 @@ int main(int argc, char *argv[]) {

	int ret = EXIT_FAILURE;
	int opt;
	while ((opt = getopt(argc, argv, "hvo:t:T:l:L:S:s:d:g:")) != -1) {
	while ((opt = getopt(argc, argv, "hvo:t:T:l:L:S:s:d:g:H:")) != -1) {
		switch (opt) {
			case 'o':
				str_vec_push(&config.output_names, optarg);
@@ -885,6 +912,9 @@ int main(int argc, char *argv[]) {
			case 'g':
				config.gamma = strtod(optarg, NULL);
				break;
			case 'H':
				config.hook = optarg;
				break;
			case 'v':
				printf("wlsunset version %s\n", WLSUNSET_VERSION);
				ret = EXIT_SUCCESS;
diff --git a/wlsunset.1.scd b/wlsunset.1.scd
index c62f2bc..bd906a1 100644
--- a/wlsunset.1.scd
+++ b/wlsunset.1.scd
@@ -40,6 +40,11 @@ wlr-gamma-control-unstable-v1
*-g* <gamma>
	set gamma (default: 1.0)

*-H* <script-path>
	set path to a hook to execute on state change (e.g. /path/to/hook.sh)

	The hook will be called with the current phase as an argument.

# EXAMPLE

```
-- 
2.35.1
Details
Message ID
<4W45IR.AZEC90FEYBBM3@kl.wtf>
In-Reply-To
<20220808191936.3659-1-r@gnzler.io> (view parent)
DKIM signature
missing
Download raw message
On Mon, Aug 8 2022 at 09:19:36 PM +02:00:00, Robert Günzler 
<r@gnzler.io> wrote:
> +static char* phase_str(int low_temp, int high_temp, int temp) {
> +	if (temp == high_temp) {
> +		return "daytime";
> +	} else if (temp == low_temp) {
> +		return "night";
> +	} else if ((high_temp - low_temp) >= 2) {
> +		return "dusk";
> +	} else {
> +		return "dawn";
> +	}
> +}
> +

The phases are dictates by the solar angles, with their occurrence for 
the current  day accessible in `ctx->sun`. See `get_temperature`, and 
note the special handling in case `ctx->condition` is `POLAR_NIGHT` or 
`MIDNIGHT_SUN`. Trying to get back to the solar state based on the set 
temperature is backwards.

> +static void run_hook(const struct context *ctx, const char *phase) {
> + if (!ctx->config.hook) return;
> + if (!phase) return;

One might want a bit more information, e.g. percentage during 
transition (right now it will just spam "day day day day day day day 
day NIGHT!"), and possibly the temperature in case you want it used 
elsewhere. Perhaps the arguments to the called script should be:

    script <state> <day_percentage> <kelvin>

> + char *cmd = strcat(strcat(strdup(ctx->config.hook), " "), phase);

Buffer-overrun, strdup creates a buffer with a size equal to 
`strlen(ctx->config.hook)`. `strcat` requires the destination buffer to 
be sized appropriately, and so two following calls to `strcat` write 1 
+ `strlen(phase)` bytes outside the bounds of the created buffer.

> + fprintf(stderr, "running hook for phase %s\n", phase);
> +
> + if (system(cmd)) {;}

`system` is not the way to run stuff. See `fork` + `execve` and co., 
which take a list of arguments and saves you from string manipulation. 
`if (xyz) {;}` is also not useful.
Reply to thread Export thread (mbox)