Compare commits

...

9 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
7 changed files with 182 additions and 24 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)
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)
@@ -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 ()
@@ -95,6 +96,7 @@ endif ()
# 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})
@@ -103,7 +105,7 @@ foreach (target brightness input-switch)
OWNER_WRITE OWNER_READ OWNER_EXECUTE
GROUP_READ GROUP_EXECUTE
WORLD_READ WORLD_EXECUTE
SETUID)
${SETUID})
endif ()
endforeach ()

View File

@@ -1,4 +1,4 @@
Copyright (c) 2015 - 2024, 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);
}

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

View File

@@ -809,10 +809,10 @@ static const struct config_schema g_config_general[] =
static const struct config_schema g_config_mpd[] =
{
// XXX: We might want to allow config item defaults to not disable nulls.
{ .name = "address",
.comment = "MPD host or socket",
.type = CONFIG_ITEM_STRING,
.default_ = "\"localhost\"" },
.type = CONFIG_ITEM_STRING },
{ .name = "service",
.comment = "MPD service name or port",
.type = CONFIG_ITEM_STRING,
@@ -1809,8 +1809,12 @@ mpd_on_io_hook (void *user_data, bool outgoing, const char *line)
static void
on_mpd_reconnect (void *user_data)
{
// FIXME: the user should be able to disable MPD
struct app_context *ctx = user_data;
struct config_item *root = ctx->config.root;
const char *address = get_config_string (root, "mpd.address");
const char *service = get_config_string (root, "mpd.service");
if (!address)
return;
struct mpd_client *c = &ctx->mpd_client;
c->user_data = ctx;
@@ -1820,10 +1824,7 @@ on_mpd_reconnect (void *user_data)
c->on_io_hook = mpd_on_io_hook;
struct error *e = NULL;
struct config_item *root = ctx->config.root;
if (!mpd_client_connect (&ctx->mpd_client,
get_config_string (root, "mpd.address"),
get_config_string (root, "mpd.service"), &e))
if (!mpd_client_connect (&ctx->mpd_client, address, service, &e))
{
print_error ("%s: %s", "cannot connect to MPD", e->message);
error_free (e);
@@ -2249,7 +2250,7 @@ action_noise_adjust (struct app_context *ctx, const struct strv *args)
long arg = strtol (args->vector[0], NULL, 10);
ctx->noise_fadeout_samples = 0;
ctx->noise_fadeout_iterator = 0;
if (!ctx->noise_end_time && (arg < 0 || !noise_start (ctx)))
if (!ctx->noise_end_time && (arg <= 0 || !noise_start (ctx)))
return;
time_t now = time (NULL);
@@ -2701,14 +2702,14 @@ parse_key_modifier (const char *modifier, unsigned *mods)
}
modifiers[] =
{
{"Shift", ShiftMask},
{"Lock", LockMask},
{"Control", ControlMask},
{"Mod1", Mod1Mask},
{"Mod2", Mod2Mask},
{"Mod3", Mod3Mask},
{"Mod4", Mod4Mask},
{"Mod5", Mod5Mask},
{ "Shift", ShiftMask },
{ "Lock", LockMask },
{ "Control", ControlMask },
{ "Mod1", Mod1Mask },
{ "Mod2", Mod2Mask },
{ "Mod3", Mod3Mask },
{ "Mod4", Mod4Mask },
{ "Mod5", Mod5Mask },
};
for (size_t k = 0; k < N_ELEMENTS (modifiers); k++)

View File

@@ -57,4 +57,7 @@ keys = {
"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"
}