Compare commits

..

30 Commits

Author SHA1 Message Date
f26cfd3bb5 wmstatus: don't spam X session logs without MPD
All checks were successful
Alpine 3.21 Success
OpenBSD 7.6 Success
Allow and default to setting the MPD address to null.
2025-08-10 00:22:22 +02:00
f2ec611c26 Add genpass: a tool to generate passwords
All checks were successful
Alpine 3.20 Success
OpenBSD 7.5 Success
2025-03-31 21:06:21 +02:00
5b64c639ac CMakeLists.txt: don't enforce setuid bit
All checks were successful
Alpine 3.20 Success
OpenBSD 7.5 Success
2024-11-25 06:12:20 +01:00
8096a1b2c9 Move elksmart-comm to another repository 2024-11-25 03:31:07 +01:00
bb4fdcd936 wmstatus: fix noise adjustment logic
All checks were successful
Alpine 3.20 Success
OpenBSD 7.5 Success
It was possible to trigger an untracked playback stream.
2024-10-12 15:36:34 +02:00
d06beedcaa CMakeLists.txt: install optional targets
All checks were successful
Alpine 3.20 Success
OpenBSD 7.5 Success
2024-10-12 14:20:31 +02:00
dc3f0d6d05 elksmart-comm: add support for EKX5S-T
All checks were successful
Alpine 3.20 Success
OpenBSD 7.5 Success
This device seems to be very picky about USB ports,
but at least learning is reliable,
and it uses the same protocol as EKX4S.
2024-10-12 14:02:30 +02:00
9e91058ed9 Add elksmart-comm for transceiving infrared codes
All checks were successful
Alpine 3.20 Success
OpenBSD 7.5 Success
The receive functionality is quite unstable,
however useful enough for something that is officially unsupported.

The gadget is picky about cables,
but it has ridiculous reach when it works.
2024-08-30 02:55:35 +02:00
fbc7454647 wmstatus: cleanup 2024-08-10 08:51:43 +02:00
94bc8c251c wmstatus: improve Sway forwards of xkb-lock-group
All checks were successful
Alpine 3.20 Success
OpenBSD 7.5 Success
2024-08-08 14:58:02 +02:00
e83cfa3c15 Fix calloc argument order, add some consts 2024-08-08 14:39:28 +02:00
29c89942ce Bump liberty
All checks were successful
Alpine 3.20 Success
OpenBSD 7.5 Success
2024-08-08 08:58:36 +02:00
9042aeaa93 wmstatus: fix the binding parser
All checks were successful
Alpine 3.20 Success
OpenBSD 7.5 Success
2024-08-07 18:30:46 +02:00
180b16faee wmstatus: add an option to import bindings to Sway
All checks were successful
Alpine 3.20 Success
OpenBSD 7.5 Success
We still want to retain the ability to bind them on our own under X11.

With this, the Wayland situation has considerably improved,
but the activity watch and keyboard layout switching are still broken.
2024-08-07 17:01:35 +02:00
674ea6d9a6 wmstatus: add IPC capabilities
All checks were successful
Alpine 3.20 Success
OpenBSD 7.5 Success
And remove the odd prefix functionality.
2024-08-07 12:32:33 +02:00
9ccdc3430c wmstatus: make bindings configurable 2024-08-07 11:51:14 +02:00
4a22708f52 wmstatus: move to libertyconf 2024-08-07 11:51:14 +02:00
128ef14c39 wmstatus: round in noise playback setting
All checks were successful
Alpine 3.19 Success
OpenBSD 7.3 Success
2024-04-17 00:42:55 +02:00
9def673a2b Install some binaries with the setuid bit
All checks were successful
Alpine 3.19 Success
OpenBSD 7.3 Success
2024-03-27 08:26:49 +01:00
36df9cc6c9 Fix gdm-switch-user build 2024-02-25 01:56:09 +01:00
d97a6e3f16 Bump liberty 2024-02-24 00:39:50 +01:00
e073fc400e wmstatus: MPD play/toggle 2024-02-24 00:39:50 +01:00
fefeb242ae wmstatus-weather.pl: fix weather icons
The API seems to have been removed entirely.
2024-01-19 04:31:52 +01:00
37b6ce3560 wmstatus: try a bit harder to get openat() 2023-07-24 09:56:07 +02:00
5c32057c42 wmstatus: fix an OpenBSD build warning
Note that _GNU_SOURCE is there to imply _DEFAULT_SOURCE, for BYTE_ORDER.
2023-07-04 07:24:24 +02:00
87e5285622 Don't install orphan supplementary files 2023-07-04 02:46:33 +02:00
7e30dfb6f0 iexec: enable not exitting together with the child 2023-06-19 19:06:37 +02:00
4cc1baf429 iexec: enable watching a different path 2023-06-19 19:06:16 +02:00
957aed63a8 iexec: cleanup 2023-06-19 19:06:08 +02:00
59b78ebc5c Bump liberty 2023-06-19 17:10:31 +02:00
13 changed files with 1154 additions and 313 deletions

View File

@@ -25,7 +25,7 @@ include_directories (
link_directories (
${x_LIBRARY_DIRS} ${pulse_LIBRARY_DIRS} ${dbus_LIBRARY_DIRS})
option (WITH_GDM "Compile with GDM support" ${gdm_FOUND})
option (WITH_GDM "Compile with GDM utilities" ${gdm_FOUND})
# Generate a configuration file
configure_file (${PROJECT_SOURCE_DIR}/config.h.in
@@ -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 genpass)
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 ()
@@ -58,9 +58,10 @@ target_link_libraries (wmstatus
add_threads (wmstatus)
if (WITH_GDM)
include_directories (${gdm_INCLUDE_DIRS})
link_directories (${gdm_LIBRARY_DIRS})
list (APPEND targets gdm-switch-user)
add_executable (gdm-switch-user gdm-switch-user.c)
target_include_directories (gdm-switch-user PUBLIC ${gdm_INCLUDE_DIRS})
target_link_directories (gdm-switch-user PUBLIC ${gdm_LIBRARY_DIRS})
target_link_libraries (gdm-switch-user ${gdm_LIBRARIES})
endif ()
@@ -71,27 +72,43 @@ 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)
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)
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}")
# 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.
set (SETUID "SETUID" CACHE STRING "Set this empty on permission issues")
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})

View File

@@ -1,4 +1,4 @@
Copyright (c) 2015 - 2021, Přemysl Eric Janouch <p@janouch.name>
Copyright (c) 2015 - 2025, 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.

View File

@@ -37,7 +37,8 @@ Regular releases are sporadic. git master should be stable enough.
Building
--------
Build dependencies: CMake, pkg-config, liberty (included) +
Runtime dependencies: libpulse, libx11, dbus-1, libgdm (optional)
Runtime dependencies: libpulse, libx11, dbus-1 +
Optional runtime dependencies: libgdm (gdm-switch-user)
$ git clone --recursive https://git.janouch.name/p/desktop-tools.git
$ mkdir desktop-tools/build

View File

@@ -46,7 +46,7 @@ log_message_custom (void *user_data, const char *quote, const char *fmt,
static void
wait_ms (long ms)
{
struct timespec ts = { 0, ms * 1000 * 1000 };
struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 };
nanosleep (&ts, NULL);
}

View File

@@ -124,7 +124,7 @@ config_validate_nonnegative (const struct config_item *item, struct error **e)
return error_set (e, "must be non-negative");
}
static struct config_schema g_config_device[] =
static const struct config_schema g_config_device[] =
{
{ .name = "name",
.comment = "Device identifier",
@@ -137,7 +137,7 @@ static struct config_schema g_config_device[] =
{}
};
static struct config_schema g_config_pwm[] =
static const struct config_schema g_config_pwm[] =
{
{ .name = "temp",
.comment = "Path to temperature sensor output",
@@ -415,7 +415,7 @@ device_create (struct app_context *ctx, const char *path,
// There is no room for errors in the configuration, everything must be valid.
// Thus the reset to defaults on invalid values is effectively disabled here.
static bool
apply_schema (struct config_schema *schema, struct config_item *object,
apply_schema (const struct config_schema *schema, struct config_item *object,
struct error **e)
{
struct error *warning = NULL, *error = NULL;
@@ -445,7 +445,7 @@ static bool
check_device_configuration (struct config_item *subtree, struct error **e)
{
// Check regular fields in the device object
for (struct config_schema *s = g_config_device; s->name; s++)
for (const struct config_schema *s = g_config_device; s->name; s++)
if (!apply_schema (s, subtree, e))
return false;
@@ -465,7 +465,7 @@ check_device_configuration (struct config_item *subtree, struct error **e)
while ((pwm = str_map_iter_next (&iter)))
{
const char *subpath = iter.link->key;
for (struct config_schema *s = g_config_pwm; s->name; s++)
for (const struct config_schema *s = g_config_pwm; s->name; s++)
if (!apply_schema (s, pwm, &error))
{
error_set (e, "PWM `%s': %s", subpath, error->message);

View File

@@ -1,5 +1,5 @@
// Public domain
#include <gdm-user-switching.h>
#include <gdm/gdm-user-switching.h>
int
main (int argc, char *argv[])

151
genpass.c Normal file
View File

@@ -0,0 +1,151 @@
/*
* genpass.c: password generator
*
* Copyright (c) 2025, 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.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
*/
#include "config.h"
#undef PROGRAM_NAME
#define PROGRAM_NAME "genpass"
#include "liberty/liberty.c"
static struct str
parse_group (const char *group)
{
bool present[0x100] = {};
for (size_t i = 0; group[i]; i++)
{
unsigned char c = group[i];
if (!i || c != '-' || !group[i + 1])
present[c] = true;
else if (group[i + 1] < group[i - 1])
exit_fatal ("character ranges must be increasing");
else
for (c = group[i - 1]; ++c <= group[i + 1]; )
present[c] = true;
}
struct str alphabet = str_make ();
for (size_t i = 1; i < N_ELEMENTS (present); i++)
if (present[i])
str_append_c (&alphabet, i);
if (!alphabet.len)
exit_fatal ("empty group");
return alphabet;
}
static void
parse_program_arguments (int argc, char **argv,
unsigned long *length, struct strv *groups, struct str *alphabet)
{
static const struct opt opts[] =
{
{ 'l', "length", "CHARACTERS", 0, "set password length" },
{ '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" },
{ 0, NULL, NULL, 0, NULL }
};
struct opt_handler oh =
opt_handler_make (argc, argv, opts, "GROUP...", "Password generator.");
int c;
while ((c = opt_handler_get (&oh)) != -1)
switch (c)
{
case 'l':
if (!xstrtoul (length, optarg, 10) || *length <= 0)
print_fatal ("invalid length argument");
break;
case 'd':
g_debug_mode = true;
break;
case 'h':
opt_handler_usage (&oh, stdout);
exit (EXIT_SUCCESS);
case 'V':
printf (PROGRAM_NAME " " PROGRAM_VERSION "\n");
exit (EXIT_SUCCESS);
default:
print_error ("wrong options");
opt_handler_usage (&oh, stderr);
exit (EXIT_FAILURE);
}
argc -= optind;
argv += optind;
for (int i = 0; i < argc; i++)
{
struct str alphabet = parse_group (argv[i]);
strv_append_owned (groups, str_steal (&alphabet));
}
bool present[0x100] = {};
for (size_t i = 0; i < groups->len; i++)
for (size_t k = 0; groups->vector[i][k]; k++)
{
unsigned char c = groups->vector[i][k];
if (present[c])
exit_fatal ("groups are not disjunct");
present[c] = true;
}
for (size_t i = 1; i < N_ELEMENTS (present); i++)
if (present[i])
str_append_c (alphabet, i);
if (groups->len > *length)
exit_fatal ("the requested length is less than the number of groups");
if (!groups->len)
{
opt_handler_usage (&oh, stderr);
exit (EXIT_FAILURE);
}
opt_handler_free (&oh);
}
int
main (int argc, char *argv[])
{
unsigned long length = 8;
struct strv groups = strv_make ();
struct str alphabet = str_make ();
parse_program_arguments (argc, argv, &length, &groups, &alphabet);
unsigned seed = 0;
if (!random_bytes (&seed, sizeof seed, NULL))
exit_fatal ("failed to initialize random numbers");
srand (seed);
// Select from a joined alphabet, but make sure all groups are represented.
struct str candidate = str_make ();
while (true)
{
restart:
for (size_t i = length; i--; )
str_append_c (&candidate, alphabet.str[rand () % alphabet.len]);
for (size_t i = 0; i < groups.len; i++)
if (!strpbrk (candidate.str, groups.vector[i]))
{
str_reset (&candidate);
goto restart;
}
printf ("%s\n", candidate.str);
return 0;
}
}

119
iexec.c
View File

@@ -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_inotify_event (const struct inotify_event *e, const char *base)
{
if (e->wd != g.inotify_wd || strcmp (e->name, base))
return;
if (g.child)
{
print_debug ("file changed, killing child");
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; 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;
print_debug ("file changed, killing child");
g_restarting = true;
if (kill (g_child, SIGINT))
print_error ("kill: %s", strerror (errno));
}
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;
}

Submodule liberty updated: 782a9a5977...75fc6f1c37

View File

@@ -290,7 +290,7 @@ on_sink_info (pa_context *context, const pa_sink_info *info, int eol,
sink->ports_len++;
struct port *port = sink->ports =
xcalloc (sizeof *sink->ports, sink->ports_len);
xcalloc (sink->ports_len, sizeof *sink->ports);
for (struct pa_sink_port_info **iter = info->ports; *iter; iter++)
{
port->name = xstrdup ((*iter)->name);

View File

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

1053
wmstatus.c

File diff suppressed because it is too large Load Diff

63
wmstatus.conf.example Normal file
View File

@@ -0,0 +1,63 @@
# vim: set ft=libertyconf:
keys = {
# This key should be labeled L on normal Qwert[yz] layouts
"Mod4 n" = "exec dm-tool lock" # gdm-switch-user
# xmodmap grep -e Alt_R -e Meta_R -e ISO_Level3_Shift -e Mode_switch
# can be used to figure out which modifier is AltGr
"Mod4 Up" = "mpd-play-toggle"
"Mod4 Down" = "mpd stop"
"Mod4 Left" = "mpd previous"
"Mod4 Right" = "mpd next"
"Mod4 Shift Left" = "mpd seekcur -10"
"Mod4 Shift Right" = "mpd seekcur +10"
"XF86AudioPlay" = "mpd-play-toggle"
"XF86AudioPrev" = "mpd previous"
"XF86AudioNext" = "mpd next"
"Mod4 F1" = "xkb-lock-group 0"
"Mod4 F2" = "xkb-lock-group 1"
"Mod4 F3" = "xkb-lock-group 2"
"Mod4 F4" = "xkb-lock-group 3"
"Mod4 Control F1" = "exec input-switch vga 1"
"Mod4 Control Shift F1" = "exec input-switch vga 2"
"Mod4 Control F2" = "exec input-switch dvi 1"
"Mod4 Control Shift F2" = "exec input-switch dvi 2"
"Mod4 Control F3" = "exec input-switch hdmi 1"
"Mod4 Control Shift F3" = "exec input-switch hdmi 2"
"Mod4 Control F4" = "exec input-switch dp 1"
"Mod4 Control Shift F4" = "exec input-switch dp 2"
"Mod4 Home" = "exec brightness +10"
"Mod4 End" = "exec brightness -10"
"XF86MonBrightnessUp" = "exec brightness +10"
"XF86MonBrightnessDown" = "exec brightness -10"
# We need to wait a little while until user releases the key
"Mod4 F5" = "exec sh -c 'sleep 1; xset dpms force standby'"
"Mod4 Shift F5" = "insomnia"
"Mod4 Pause" = "exec sh -c 'sleep 1; xset dpms force standby'"
"Mod4 Shift Pause" = "insomnia"
"Mod4 Insert" = "audio-switch"
"Mod4 Delete" = "audio-mute"
"Mod4 Shift Delete" = "audio-mic-mute"
"Mod4 Page_Up" = "audio-volume +5"
"Mod4 Shift Page_Up" = "audio-volume +1"
"Mod4 Page_Down" = "audio-volume -5"
"Mod4 Shift Page_Down" = "audio-volume -1"
" XF86AudioRaiseVolume" = "audio-volume +5"
"Shift XF86AudioRaiseVolume" = "audio-volume +1"
" XF86AudioLowerVolume" = "audio-volume -5"
"Shift XF86AudioLowerVolume" = "audio-volume -1"
" XF86AudioMute" = "audio-mute"
" XF86AudioMicMute" = "audio-mic-mute"
"Control XF86AudioRaiseVolume" = "noise-adjust +1"
"Control XF86AudioLowerVolume" = "noise-adjust -1"
# Turns on or off Pioneer integrated amplifiers
"Mod4 Control Delete" = "exec elksmart-comm --nec A538"
}