aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt51
-rw-r--r--LICENSE2
-rw-r--r--gdm-switch-user.c2
-rw-r--r--iexec.c113
-rw-r--r--input-switch.c88
m---------liberty0
-rwxr-xr-xwmstatus-weather.pl3
-rw-r--r--wmstatus.c56
8 files changed, 207 insertions, 108 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0e18b66..f1d4292 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -33,7 +33,7 @@ configure_file (${PROJECT_SOURCE_DIR}/config.h.in
include_directories (${PROJECT_BINARY_DIR})
# Build
-set (targets wmstatus paswitch siprandom big-brother)
+set (targets wmstatus paswitch siprandom)
if ("${CMAKE_SYSTEM_NAME}" STREQUAL Linux)
# These use Linux i2c APIs, but can be made to work on macOS
list (APPEND targets brightness input-switch)
@@ -47,7 +47,7 @@ elseif (APPLE)
add_definitions (-D_DARWIN_C_SOURCE)
endif ()
-foreach (name ${targets})
+foreach (name big-brother ${targets})
add_executable (${name} ${name}.c)
endforeach ()
@@ -71,27 +71,42 @@ include (GNUInstallDirs)
set (SYSTEMD_UNITDIR /lib/systemd/system
CACHE PATH "Base directory for systemd unit files")
-configure_file (${PROJECT_SOURCE_DIR}/fancontrol-ng.service.in
- ${PROJECT_BINARY_DIR}/fancontrol-ng.service @ONLY)
-install (FILES fancontrol-ng.conf.example
- DESTINATION ${CMAKE_INSTALL_DATADIR}/fancontrol-ng)
-
-configure_file (${PROJECT_SOURCE_DIR}/priod.service.in
- ${PROJECT_BINARY_DIR}/priod.service @ONLY)
-install (FILES priod.conf.example
- DESTINATION ${CMAKE_INSTALL_DATADIR}/priod)
-
-# System-wide unit files should be installed under /lib and not /usr/lib
-install (FILES
- ${PROJECT_BINARY_DIR}/fancontrol-ng.service
- ${PROJECT_BINARY_DIR}/priod.service
- DESTINATION "${SYSTEMD_UNITDIR}")
+if ("${CMAKE_SYSTEM_NAME}" STREQUAL Linux)
+ configure_file (${PROJECT_SOURCE_DIR}/fancontrol-ng.service.in
+ ${PROJECT_BINARY_DIR}/fancontrol-ng.service @ONLY)
+ install (FILES fancontrol-ng.conf.example
+ DESTINATION ${CMAKE_INSTALL_DATADIR}/fancontrol-ng)
+
+ configure_file (${PROJECT_SOURCE_DIR}/priod.service.in
+ ${PROJECT_BINARY_DIR}/priod.service @ONLY)
+ install (FILES priod.conf.example
+ DESTINATION ${CMAKE_INSTALL_DATADIR}/priod)
+
+ # System-wide unit files should be installed under /lib and not /usr/lib
+ install (FILES
+ ${PROJECT_BINARY_DIR}/fancontrol-ng.service
+ ${PROJECT_BINARY_DIR}/priod.service
+ DESTINATION "${SYSTEMD_UNITDIR}")
+endif ()
if (WITH_GDM)
install (TARGETS gdm-switch-user DESTINATION ${CMAKE_INSTALL_BINDIR})
endif ()
-list (REMOVE_ITEM targets big-brother)
+# These should be accessible by users, but need to touch system devices.
+# Use the setuid bit, for simplicity.
+foreach (target brightness input-switch)
+ if (${target} IN_LIST targets)
+ list (REMOVE_ITEM targets ${target})
+ install (TARGETS ${target} DESTINATION ${CMAKE_INSTALL_BINDIR}
+ PERMISSIONS
+ OWNER_WRITE OWNER_READ OWNER_EXECUTE
+ GROUP_READ GROUP_EXECUTE
+ WORLD_READ WORLD_EXECUTE
+ SETUID)
+ endif ()
+endforeach ()
+
install (TARGETS ${targets} DESTINATION ${CMAKE_INSTALL_BINDIR})
install (PROGRAMS shellify DESTINATION ${CMAKE_INSTALL_BINDIR})
install (FILES LICENSE DESTINATION ${CMAKE_INSTALL_DOCDIR})
diff --git a/LICENSE b/LICENSE
index 7eda545..43ac88f 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2015 - 2021, Přemysl Eric Janouch <p@janouch.name>
+Copyright (c) 2015 - 2024, Přemysl Eric Janouch <p@janouch.name>
Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted.
diff --git a/gdm-switch-user.c b/gdm-switch-user.c
index 5718c31..55d8ba3 100644
--- a/gdm-switch-user.c
+++ b/gdm-switch-user.c
@@ -1,5 +1,5 @@
// Public domain
-#include <gdm-user-switching.h>
+#include <gdm/gdm-user-switching.h>
int
main (int argc, char *argv[])
diff --git a/iexec.c b/iexec.c
index 04e8e91..562d49d 100644
--- a/iexec.c
+++ b/iexec.c
@@ -1,7 +1,7 @@
/*
* iexec.c: run a program and restart on file change
*
- * Copyright (c) 2017, Přemysl Eric Janouch <p@janouch.name>
+ * Copyright (c) 2017 - 2023, Přemysl Eric Janouch <p@janouch.name>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted.
@@ -24,34 +24,54 @@
// This can also work on BSD if someone puts in the effort to support kqueue
#include <sys/inotify.h>
-static pid_t g_child;
-static bool g_restarting = false;
-static int g_inotify_fd, g_inotify_wd;
+static struct
+{
+ pid_t child; ///< Watched child or 0
+ bool exits; ///< Don't restart child when it exits
+ bool respawn; ///< Respawn child ASAP
+ bool killing; ///< Waiting for child to die
+ int inotify_fd, inotify_wd;
+}
+g;
+// Note that this program doesn't queue up file-based restarts
static void
-handle_file_change (const char *base)
+handle_inotify_event (const struct inotify_event *e, const char *base)
{
- char buf[4096]; ssize_t len; const struct inotify_event *e;
- while ((len = read (g_inotify_fd, buf, sizeof buf)) > 0)
- for (char *ptr = buf; ptr < buf + len; ptr += sizeof *e + e->len)
- {
- e = (const struct inotify_event *) buf;
- if (e->wd != g_inotify_wd || strcmp (e->name, base))
- continue;
+ if (e->wd != g.inotify_wd || strcmp (e->name, base))
+ return;
+ if (g.child)
+ {
print_debug ("file changed, killing child");
- g_restarting = true;
- if (kill (g_child, SIGINT))
+ if (kill (g.child, SIGINT))
print_error ("kill: %s", strerror (errno));
+ g.killing = true;
+ }
+ else
+ {
+ print_debug ("file changed, respawning");
+ g.respawn = true;
}
}
static void
+handle_file_change (const char *base)
+{
+ char buf[4096];
+ ssize_t len = 0;
+ struct inotify_event *e = NULL;
+ while ((len = read (g.inotify_fd, buf, sizeof buf)) > 0)
+ for (char *ptr = buf; ptr < buf + len; ptr += sizeof *e + e->len)
+ handle_inotify_event ((e = (struct inotify_event *) buf), base);
+}
+
+static void
spawn (char *argv[])
{
- if ((g_child = fork ()) == -1)
+ if ((g.child = fork ()) == -1)
exit_fatal ("fork: %s", strerror (errno));
- else if (g_child)
+ else if (g.child)
return;
// A linker can create spurious CLOSE_WRITEs, wait until it's executable
@@ -64,23 +84,22 @@ spawn (char *argv[])
}
static bool
-check_child_death (char *argv[])
+check_child_death (void)
{
- if (waitpid (g_child, NULL, WNOHANG) != g_child)
+ int status = 0;
+ if (waitpid (g.child, &status, WNOHANG) != g.child)
return true;
- if (!g_restarting)
+ g.child = 0;
+ if (!g.killing)
{
print_debug ("child died on its own, not respawning");
- return false;
- }
- else
- {
- print_debug ("child died on request, respawning");
- spawn (argv);
- g_restarting = false;
- return true;
+ return g.exits;
}
+
+ g.killing = false;
+ print_debug ("child died on request, respawning");
+ return g.respawn = true;
}
static void
@@ -93,8 +112,11 @@ sigchld_handler (int signum)
int
main (int argc, char *argv[])
{
+ const char *target = NULL;
static const struct opt opts[] =
{
+ { 'f', "file", "PATH", 0, "watch this path rather than the program" },
+ { 'e', "exits", NULL, 0, "allow the program to exit on its own" },
{ 'd', "debug", NULL, 0, "run in debug mode" },
{ 'h', "help", NULL, 0, "display this help and exit" },
{ 'V', "version", NULL, 0, "output version information and exit" },
@@ -111,6 +133,12 @@ main (int argc, char *argv[])
while ((c = opt_handler_get (&oh)) != -1)
switch (c)
{
+ case 'f':
+ target = optarg;
+ break;
+ case 'e':
+ g.exits = true;
+ break;
case 'd':
g_debug_mode = true;
break;
@@ -136,6 +164,9 @@ main (int argc, char *argv[])
argc -= optind;
argv += optind;
+ if (!target)
+ target = argv[0];
+
(void) signal (SIGPIPE, SIG_IGN);
struct sigaction sa = { .sa_handler = sigchld_handler };
sigemptyset (&sa.sa_mask);
@@ -148,27 +179,33 @@ main (int argc, char *argv[])
if (sigprocmask (SIG_BLOCK, &chld, &orig))
exit_fatal ("sigprocmask: %s", strerror (errno));
- char *path = xstrdup (argv[0]);
+ char *path = NULL;
+ char *dir = dirname ((path = xstrdup (target)));
- if ((g_inotify_fd = inotify_init1 (IN_NONBLOCK)) < 0)
+ if ((g.inotify_fd = inotify_init1 (IN_NONBLOCK)) < 0)
exit_fatal ("inotify_init1: %s", strerror (errno));
- if ((g_inotify_wd = inotify_add_watch (g_inotify_fd,
- dirname (path), IN_MOVED_TO | IN_CLOSE_WRITE)) < 0)
+ if ((g.inotify_wd = inotify_add_watch (g.inotify_fd,
+ dir, IN_MOVED_TO | IN_CLOSE_WRITE)) < 0)
exit_fatal ("inotify_add_watch: %s", strerror (errno));
free (path);
- char *base = basename ((path = xstrdup (argv[0])));
- spawn (argv);
-
+ char *base = basename ((path = xstrdup (target)));
+ g.respawn = true;
do
{
- fd_set r; FD_SET (g_inotify_fd, &r);
- (void) pselect (g_inotify_fd + 1, &r, NULL, NULL, NULL, &orig);
+ if (g.respawn)
+ {
+ spawn (argv);
+ g.respawn = false;
+ }
+
+ fd_set r; FD_SET (g.inotify_fd, &r);
+ (void) pselect (g.inotify_fd + 1, &r, NULL, NULL, NULL, &orig);
handle_file_change (base);
}
- while (check_child_death (argv));
+ while (check_child_death ());
free (path);
- close (g_inotify_fd);
+ xclose (g.inotify_fd);
return EXIT_SUCCESS;
}
diff --git a/input-switch.c b/input-switch.c
index da93670..5788f4a 100644
--- a/input-switch.c
+++ b/input-switch.c
@@ -1,7 +1,7 @@
/*
* input-switch.c: switches display input via DDC/CI
*
- * Copyright (c) 2017, Přemysl Eric Janouch <p@janouch.name>
+ * Copyright (c) 2017 - 2022, Přemysl Eric Janouch <p@janouch.name>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted.
@@ -28,11 +28,60 @@
#include "ddc-ci.c"
#include <dirent.h>
+// This list is from the MCCS 2.2a specification
+struct
+{
+ int code; ///< Input code
+ const char *name; ///< Input name
+ int index; ///< Input index
+}
+g_inputs[] =
+{
+ { 0x01, "VGA", 1, }, // Analog video (R/G/B) 1
+ { 0x02, "VGA", 2, }, // Analog video (R/G/B) 2
+ { 0x03, "DVI", 1, }, // Digital video (TMDS) 1 DVI 1
+ { 0x04, "DVI", 2, }, // Digital video (TMDS) 2 DVI 2
+ { 0x05, "composite", 1, }, // Composite video 1
+ { 0x06, "composite", 2, }, // Composite video 2
+ { 0x07, "S-Video", 1, }, // S-video 1
+ { 0x08, "S-Video", 2, }, // S-video 2
+ { 0x09, "tuner", 1, }, // Tuner 1
+ { 0x0A, "tuner", 2, }, // Tuner 2
+ { 0x0B, "tuner", 3, }, // Tuner 3
+ { 0x0C, "component", 1, }, // Component video (YPbPr/YCbCr) 1
+ { 0x0D, "component", 2, }, // Component video (YPbPr/YCbCr) 2
+ { 0x0E, "component", 3, }, // Component video (YPbPr/YCbCr) 3
+ { 0x0F, "DP", 1, }, // DisplayPort 1
+ { 0x10, "DP", 2, }, // DisplayPort 2
+ { 0x11, "HDMI", 1, }, // Digital Video (TMDS) 3 HDMI 1
+ { 0x12, "HDMI", 2, }, // Digital Video (TMDS) 4 HDMI 2
+ { 0x15, "bnq-tb", 1, }, // Thunderbolt on BenQ PD3220U (no spec)
+};
+
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
typedef bool (*ActionFunc) (int fd, int param, struct error **);
static bool
+get_input_source (int fd, int input, struct error **e)
+{
+ struct vcp_feature_readout readout = {};
+ if (!vcp_get_feature (fd, VCP_INPUT_SOURCE, &readout, e))
+ return false;
+
+ (void) input;
+ for (size_t i = 0; i < N_ELEMENTS (g_inputs); i++)
+ if (g_inputs[i].code == readout.cur)
+ {
+ printf ("input is %s %d\n", g_inputs[i].name, g_inputs[i].index);
+ return true;
+ }
+
+ printf ("input is %d\n", readout.cur);
+ return true;
+}
+
+static bool
set_input_source (int fd, int input, struct error **e)
{
struct vcp_feature_readout readout = {};
@@ -114,36 +163,6 @@ i2c (ActionFunc action, int param)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-// This list is from the MCCS 2.2a specification
-struct
-{
- int code; ///< Input code
- const char *name; ///< Input name
- int index; ///< Input index
-}
-g_inputs[] =
-{
- { 0x01, "vga", 1, }, // Analog video (R/G/B) 1
- { 0x02, "vga", 2, }, // Analog video (R/G/B) 2
- { 0x03, "dvi", 1, }, // Digital video (TMDS) 1 DVI 1
- { 0x04, "dvi", 2, }, // Digital video (TMDS) 2 DVI 2
- { 0x05, "composite", 1, }, // Composite video 1
- { 0x06, "composite", 2, }, // Composite video 2
- { 0x07, "s-video", 1, }, // S-video 1
- { 0x08, "s-video", 2, }, // S-video 2
- { 0x09, "tuner", 1, }, // Tuner 1
- { 0x0A, "tuner", 2, }, // Tuner 2
- { 0x0B, "tuner", 3, }, // Tuner 3
- { 0x0C, "component", 1, }, // Component video (YPbPr/YCbCr) 1
- { 0x0D, "component", 2, }, // Component video (YPbPr/YCbCr) 2
- { 0x0E, "component", 3, }, // Component video (YPbPr/YCbCr) 3
- { 0x0F, "dp", 1, }, // DisplayPort 1
- { 0x10, "dp", 2, }, // DisplayPort 2
- { 0x11, "hdmi", 1, }, // Digital Video (TMDS) 3 HDMI 1
- { 0x12, "hdmi", 2, }, // Digital Video (TMDS) 4 HDMI 2
- { 0x15, "bnq-tb", 1, }, // Thunderbolt on BenQ PD3220U (no spec)
-};
-
int
main (int argc, char *argv[])
{
@@ -151,9 +170,14 @@ main (int argc, char *argv[])
if (argc <= 1)
{
- printf ("Usage: %s <input> [<index>]\n", argv[0]);
+ printf ("Usage: %s {? | INPUT [INDEX]}\n", argv[0]);
exit (EXIT_FAILURE);
}
+ if (!strcmp (argv[1], "?"))
+ {
+ i2c (get_input_source, -1);
+ exit (EXIT_SUCCESS);
+ }
unsigned long input_source = 0;
if (xstrtoul (&input_source, argv[1], 10))
diff --git a/liberty b/liberty
-Subproject 782a9a5977bd5f2101e8808b94d659fe52e2490
+Subproject ad5b2fb8cd4915de9c6d97c362839de02d4970a
diff --git a/wmstatus-weather.pl b/wmstatus-weather.pl
index d5252f2..2d60bc4 100755
--- a/wmstatus-weather.pl
+++ b/wmstatus-weather.pl
@@ -20,7 +20,8 @@ my %legends;
sub retrieve_legends {
# HTTP/Tiny supports TLS, but with non-core IO::Socket::SSL, so use cURL
open(my $sock, '-|', 'curl', '-sSA', $agent,
- "$base/weathericon/2.0/legends.txt") or return $!;
+ 'https://raw.githubusercontent.com/' .
+ 'metno/weathericons/main/weather/legend.csv') or return $!;
while (local $_ = <$sock>) { $legends{$1} = $2 if /^(.+?),(.+?),/ }
close($sock);
}
diff --git a/wmstatus.c b/wmstatus.c
index 29cfa0f..0885d97 100644
--- a/wmstatus.c
+++ b/wmstatus.c
@@ -1,7 +1,7 @@
/*
* wmstatus.c: simple PulseAudio-enabled status setter for dwm and i3/sway
*
- * Copyright (c) 2015 - 2021, Přemysl Eric Janouch <p@janouch.name>
+ * Copyright (c) 2015 - 2024, Přemysl Eric Janouch <p@janouch.name>
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted.
@@ -20,7 +20,10 @@
#define LIBERTY_WANT_ASYNC
#define LIBERTY_WANT_PROTO_MPD
-#define _GNU_SOURCE // openat
+// openat, dirfd
+#define _XOPEN_SOURCE 700
+#define _ATFILE_SOURCE
+#define _GNU_SOURCE
#include "config.h"
#undef PROGRAM_NAME
@@ -849,7 +852,7 @@ struct app_context
struct mpd_client mpd_client; ///< MPD client
char *mpd_song; ///< MPD current song
- char *mpd_status; ///< MPD status (overrides song)
+ bool mpd_stopped; ///< MPD stopped (overrides song)
// NUT:
@@ -989,7 +992,6 @@ app_context_free (struct app_context *self)
mpd_client_free (&self->mpd_client);
cstr_set (&self->mpd_song, NULL);
- cstr_set (&self->mpd_status, NULL);
nut_client_free (&self->nut_client);
str_map_free (&self->nut_ups_info);
@@ -1237,7 +1239,7 @@ refresh_status (struct app_context *ctx)
{
if (ctx->prefix) ctx->backend->add (ctx->backend, ctx->prefix);
- if (ctx->mpd_status) ctx->backend->add (ctx->backend, ctx->mpd_status);
+ if (ctx->mpd_stopped) ctx->backend->add (ctx->backend, "MPD stopped");
else if (ctx->mpd_song) ctx->backend->add (ctx->backend, ctx->mpd_song);
if (ctx->noise_end_time)
@@ -1504,9 +1506,8 @@ mpd_on_info_response (const struct mpd_response *response,
struct str_map map;
mpd_vector_to_map (data, &map);
- cstr_set (&ctx->mpd_status, NULL);
-
struct str s = str_make ();
+ ctx->mpd_stopped = false;
const char *value;
if ((value = str_map_find (&map, "state")))
@@ -1514,7 +1515,7 @@ mpd_on_info_response (const struct mpd_response *response,
// Unicode approximates since in proportional fonts ASCII looks ugly
// and I don't want to depend on a particular font with player chars
if (!strcmp (value, "stop"))
- ctx->mpd_status = xstrdup ("MPD stopped");
+ ctx->mpd_stopped = true;
else if (!strcmp (value, "pause"))
str_append (&s, "▯▯ " /* "|| " */);
else
@@ -1611,6 +1612,10 @@ mpd_on_failure (void *user_data)
struct app_context *ctx = user_data;
print_error ("connection to MPD failed");
mpd_queue_reconnect (ctx);
+
+ cstr_set (&ctx->mpd_song, NULL);
+ ctx->mpd_stopped = false;
+ refresh_status (ctx);
}
static void
@@ -2064,8 +2069,19 @@ on_noise_adjust (struct app_context *ctx, int arg)
if (!ctx->noise_end_time && (arg < 0 || !noise_start (ctx)))
return;
- // The granularity of noise playback is whole minutes
- ctx->noise_end_time += arg * 60;
+ time_t now = time (NULL);
+ int diff = difftime (ctx->noise_end_time, now);
+
+ // The granularity of noise playback setting is whole hours.
+ enum { SECOND = 1, MINUTE = 60, HOUR = 3600 };
+ if (arg > 0)
+ // Add a minute to enable stepping up from 0:59 to 2:00.
+ diff = (diff + arg * HOUR + MINUTE) / HOUR * HOUR;
+ else if (arg++ < 0)
+ // Remove a second to enable stepping down from 2:00 to 1:00.
+ diff = (diff + arg * HOUR - SECOND) / HOUR * HOUR;
+
+ ctx->noise_end_time = now + diff;
on_noise_timer (ctx);
}
@@ -2220,9 +2236,9 @@ spawn (char *argv[])
mpd_client_idle (c, 0); \
}
-// XXX: pause without argument is deprecated, we can watch play state
-// if we want to have the toggle pause/play functionality
-MPD_SIMPLE (play, "pause", NULL)
+
+MPD_SIMPLE (play, "play", NULL)
+MPD_SIMPLE (toggle, "pause", NULL)
MPD_SIMPLE (stop, "stop", NULL)
MPD_SIMPLE (prev, "previous", NULL)
MPD_SIMPLE (next, "next", NULL)
@@ -2230,6 +2246,12 @@ MPD_SIMPLE (forward, "seekcur", "+10", NULL)
MPD_SIMPLE (backward, "seekcur", "-10", NULL)
static void
+on_mpd_play_toggle (struct app_context *ctx, int arg)
+{
+ (ctx->mpd_stopped ? on_mpd_play : on_mpd_toggle) (ctx, arg);
+}
+
+static void
on_volume_finish (pa_context *context, int success, void *userdata)
{
(void) context;
@@ -2426,13 +2448,13 @@ g_keys[] =
// can be used to figure out which modifier is AltGr
// MPD
- { Mod4Mask, XK_Up, on_mpd_play, 0 },
+ { Mod4Mask, XK_Up, on_mpd_play_toggle, 0 },
{ Mod4Mask, XK_Down, on_mpd_stop, 0 },
{ Mod4Mask, XK_Left, on_mpd_prev, 0 },
{ Mod4Mask, XK_Right, on_mpd_next, 0 },
{ Mod4Mask | ShiftMask, XK_Left, on_mpd_backward, 0 },
{ Mod4Mask | ShiftMask, XK_Right, on_mpd_forward, 0 },
- { 0, XF86XK_AudioPlay, on_mpd_play, 0 },
+ { 0, XF86XK_AudioPlay, on_mpd_play_toggle, 0 },
{ 0, XF86XK_AudioPrev, on_mpd_prev, 0 },
{ 0, XF86XK_AudioNext, on_mpd_next, 0 },
@@ -2481,8 +2503,8 @@ g_keys[] =
{ 0, XF86XK_AudioMicMute, on_volume_mic_mute, 0 },
// Noise playback
- { ControlMask, XF86XK_AudioRaiseVolume, on_noise_adjust, 60 },
- { ControlMask, XF86XK_AudioLowerVolume, on_noise_adjust, -60 },
+ { ControlMask, XF86XK_AudioRaiseVolume, on_noise_adjust, 1 },
+ { ControlMask, XF86XK_AudioLowerVolume, on_noise_adjust, -1 },
};
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -