/* * 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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; }