sortix-mirror/chkblayout/chkblayout.c

151 lines
3.8 KiB
C

/*
* Copyright (c) 2014 Jonas 'Sortie' Termansen.
* Copyright (c) 2023 Juhani 'nortti' Krekelä.
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* 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.
*
* chkblayout.c
* Changes the current keyboard layout.
*/
#include <sys/stat.h>
#include <display.h>
#include <err.h>
#include <errno.h>
#include <error.h>
#include <fcntl.h>
#include <getopt.h>
#include <ioleast.h>
#include <locale.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <termios.h>
#define CHKBLAYOUT_ID 0
static bool chkblayout_ack_received = false;
static int chkblayout_error;
static void on_ack(void* ctx, uint32_t id, int32_t error)
{
(void) ctx;
if ( id != CHKBLAYOUT_ID )
return;
chkblayout_error = error;
chkblayout_ack_received = true;
}
int main(int argc, char* argv[])
{
bool list = false;
setlocale(LC_ALL, "");
const struct option longopts[] =
{
{"list", no_argument, NULL, 'l'},
{0, 0, 0, 0}
};
const char* opts = "l";
int opt;
while ( (opt = getopt_long(argc, argv, opts, longopts, NULL)) != -1 )
{
switch ( opt )
{
case 'l': list = true; break;
default: return 1;
}
}
if ( list )
{
if ( 0 < argc - optind )
errx(1, "unexpected extra operand");
execlp("ls", "ls", "/share/kblayout", (const char*) NULL);
err(127, "ls");
}
const char* tty_path = "/dev/tty";
int tty_fd = open(tty_path, O_WRONLY);
if ( tty_fd < 0 )
err(1, "%s", tty_path);
if ( !isatty(tty_fd) )
err(1, "%s", tty_path);
if ( argc - optind == 0 )
errx(1, "expected new keyboard layout");
if ( 1 < argc - optind )
errx(1, "unexpected extra operand");
const char* kblayout_path = argv[optind];
if ( !strchr(kblayout_path, '/') )
{
char* new_kblayout_path;
if ( asprintf(&new_kblayout_path, "/share/kblayout/%s", kblayout_path) < 0 )
err(1, "asprintf");
kblayout_path = new_kblayout_path;
}
int kblayout_fd = open(kblayout_path, O_RDONLY);
if ( kblayout_fd < 0 )
err(1, "%s", kblayout_path);
struct stat kblayout_st;
if ( fstat(kblayout_fd, &kblayout_st) < 0 )
err(1, "stat: %s", kblayout_path);
if ( (size_t) kblayout_st.st_size != (uintmax_t) kblayout_st.st_size )
error(1, EFBIG, "%s", kblayout_path);
size_t kblayout_size = (size_t) kblayout_st.st_size;
unsigned char* kblayout = (unsigned char*) malloc(kblayout_size);
if ( !kblayout )
err(1, "malloc");
if ( readall(kblayout_fd, kblayout, kblayout_size) != kblayout_size )
err(1, "read: %s", kblayout_path);
close(kblayout_fd);
if ( getenv("DISPLAY_SOCKET") )
{
struct display_connection* connection = display_connect_default();
if ( !connection )
err(1, "Could not connect to display server");
display_chkblayout(connection, CHKBLAYOUT_ID, kblayout, kblayout_size);
struct display_event_handlers handlers = {0};
handlers.ack_handler = on_ack;
while ( !chkblayout_ack_received )
display_wait_event(connection, &handlers);
if ( chkblayout_error )
{
errno = chkblayout_error;
err(1, "tcsetblob: kblayout: %s", kblayout_path);
}
display_disconnect(connection);
}
else if ( tcsetblob(tty_fd, "kblayout", kblayout, kblayout_size) < 0 )
err(1, "tcsetblob: kblayout: %s", kblayout_path);
free(kblayout);
close(tty_fd);
return 0;
}