diff --git a/chkblayout/Makefile b/chkblayout/Makefile index 3e8e780a..f45ae07e 100644 --- a/chkblayout/Makefile +++ b/chkblayout/Makefile @@ -11,7 +11,7 @@ CFLAGS += -Wall -Wextra BINARIES = chkblayout MANPAGES1 = chkblayout.1 -LIBS = +LIBS = -ldisplay all: $(BINARIES) diff --git a/chkblayout/chkblayout.c b/chkblayout/chkblayout.c index 1bbf900b..da3f2eee 100644 --- a/chkblayout/chkblayout.c +++ b/chkblayout/chkblayout.c @@ -20,6 +20,7 @@ #include +#include #include #include #include @@ -34,6 +35,20 @@ #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, int error) +{ + (void) ctx; + if ( id != CHKBLAYOUT_ID ) + return; + chkblayout_error = error; + chkblayout_ack_received = true; +} + int main(int argc, char* argv[]) { bool list = false; @@ -103,8 +118,29 @@ int main(int argc, char* argv[]) err(1, "read: %s", kblayout_path); close(kblayout_fd); - if ( tcsetblob(tty_fd, "kblayout", kblayout, kblayout_size) < 0 ) - err(1, "tcsetblob: kblayout: %s:", kblayout_path); + 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); diff --git a/display/connection.c b/display/connection.c index d8519613..51507d1c 100644 --- a/display/connection.c +++ b/display/connection.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2014, 2015, 2016, 2023 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 @@ -28,6 +29,7 @@ #include #include #include +#include #include #include "connection.h" @@ -97,14 +99,23 @@ struct window* connection_find_window(struct connection* connection, #define CONNECTION_MESSAGE_HANDLER_NO_AUX(message_name) \ void connection_handler_##message_name(struct connection* connection, \ struct display_##message_name* msg, \ - void* auxilerary __attribute__((unused)), \ - size_t auxilerary_size __attribute__((unused))) + void* auxiliary __attribute__((unused)), \ + size_t auxiliary_size __attribute__((unused)), \ + const struct server* server __attribute__((unused))) #define CONNECTION_MESSAGE_HANDLER(message_name) \ void connection_handler_##message_name(struct connection* connection, \ struct display_##message_name* msg, \ - unsigned char* auxilerary, \ - size_t auxilerary_size) + unsigned char* auxiliary, \ + size_t auxiliary_size, \ + const struct server* server __attribute__((unused))) + +#define CONNECTION_MESSAGE_HANDLER_SERVER(message_name) \ +void connection_handler_##message_name(struct connection* connection, \ + struct display_##message_name* msg, \ + unsigned char* auxiliary, \ + size_t auxiliary_size, \ + const struct server* server) CONNECTION_MESSAGE_HANDLER_NO_AUX(shutdown) { @@ -157,9 +168,9 @@ CONNECTION_MESSAGE_HANDLER(render_window) src.xres = msg->width; src.yres = msg->height; src.pitch = msg->width; - src.buffer = (uint32_t*) auxilerary; + src.buffer = (uint32_t*) auxiliary; - if ( auxilerary_size < sizeof(uint32_t) * src.xres * src.yres ) + if ( auxiliary_size < sizeof(uint32_t) * src.xres * src.yres ) return; struct framebuffer dst = @@ -175,9 +186,9 @@ CONNECTION_MESSAGE_HANDLER(title_window) if ( !window ) return; - const char* title = (char*) auxilerary; + const char* title = (char*) auxiliary; free(window->title); - window->title = strndup(title, auxilerary_size); + window->title = strndup(title, auxiliary_size); window_render_frame(window); } @@ -198,10 +209,27 @@ CONNECTION_MESSAGE_HANDLER_NO_AUX(hide_window) window->show = false; } +CONNECTION_MESSAGE_HANDLER_SERVER(chkblayout) +{ + struct event_ack event; + event.id = msg->id; + event.error = 0; + if ( tcsetblob(server->tty_fd, "kblayout", auxiliary, auxiliary_size) < 0 ) + event.error = errno; + + struct display_packet_header header; + header.message_id = EVENT_ACK; + header.message_length = sizeof(event); + + connection_schedule_transmit(connection, &header, sizeof(header)); + connection_schedule_transmit(connection, &event, sizeof(event)); +} + typedef void (*connection_message_handler)(struct connection* connection, void* msg, - void* auxilerary, - size_t auxilerary_size); + void* auxiliary, + size_t auxiliary_size, + const struct server* server); struct connection_message_handler_registration { @@ -223,6 +251,7 @@ struct connection_message_handler_registration connection_message_handlers[] = REGISTER_CONNECTION_MESSAGE_HANDLER(show_window), REGISTER_CONNECTION_MESSAGE_HANDLER(hide_window), REGISTER_CONNECTION_MESSAGE_HANDLER(shutdown), + REGISTER_CONNECTION_MESSAGE_HANDLER(chkblayout), }; size_t num_connection_message_handlers = sizeof(connection_message_handlers) / @@ -233,7 +262,8 @@ short connection_interested_poll_events(struct connection* connection) return POLLIN | (connection->outgoing_used ? POLLOUT : 0); } -void connection_can_read(struct connection* connection) +void connection_can_read(struct connection* connection, + const struct server* server) { while ( connection->packet_header_received < sizeof(connection->packet_header) ) { @@ -272,10 +302,10 @@ void connection_can_read(struct connection* connection) { struct connection_message_handler_registration* handler = &connection_message_handlers[packet_id]; - unsigned char* auxilerary = connection->packet + handler->message_size; - size_t auxilerary_size = packet_length - handler->message_size; + unsigned char* auxiliary = connection->packet + handler->message_size; + size_t auxiliary_size = packet_length - handler->message_size; handler->handler(connection, connection->packet, - auxilerary, auxilerary_size); + auxiliary, auxiliary_size, server); } connection->packet_header_received = 0; diff --git a/display/connection.h b/display/connection.h index 736984e5..2fb1898c 100644 --- a/display/connection.h +++ b/display/connection.h @@ -25,6 +25,7 @@ #include +#include "server.h" #include "window.h" struct display; @@ -58,7 +59,8 @@ struct window* connection_find_window_raw(struct connection* connection, struct window* connection_find_window(struct connection* connection, uint32_t window_id); short connection_interested_poll_events(struct connection* connection); -void connection_can_read(struct connection* connection); +void connection_can_read(struct connection* connection, + const struct server* server); void connection_can_write(struct connection* connection); void connection_destroy(struct connection* connection); diff --git a/display/server.c b/display/server.c index 24074e77..d2b5db62 100644 --- a/display/server.c +++ b/display/server.c @@ -68,7 +68,7 @@ void server_initialize(struct server* server, struct display* display) load_font(); server->tty_fd = 0; - if ( !isatty(0) ) + if ( !isatty(server->tty_fd) ) { server->tty_fd = open("/dev/tty", O_RDONLY); if ( server->tty_fd < 0 ) @@ -245,7 +245,7 @@ void server_poll(struct server* server) if ( pfd->revents & POLLOUT ) connection_can_write(connection); if ( pfd->revents & POLLIN ) - connection_can_read(connection); + connection_can_read(connection, server); } // Compact the array down here so the pfds match the connections above. diff --git a/libdisplay/include/display-protocol.h b/libdisplay/include/display-protocol.h index 04fa4857..4a185f8e 100644 --- a/libdisplay/include/display-protocol.h +++ b/libdisplay/include/display-protocol.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2014, 2015, 2016 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 @@ -84,6 +85,13 @@ struct display_shutdown uint32_t code; }; +#define DISPLAY_CHKBLAYOUT 8 +struct display_chkblayout +{ + uint32_t id; + /* keyboard layout data bytes follow */ +}; + #define EVENT_DISCONNECT 0 struct event_disconnect { @@ -110,4 +118,11 @@ struct event_keyboard uint32_t codepoint; }; +#define EVENT_ACK 4 +struct event_ack +{ + uint32_t id; + int error; +}; + #endif diff --git a/libdisplay/include/display.h b/libdisplay/include/display.h index 5af674c5..9d85f3f6 100644 --- a/libdisplay/include/display.h +++ b/libdisplay/include/display.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2014, 2015, 2016, 2017 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 @@ -46,8 +47,8 @@ void display_destroy_window(struct display_connection* connection, uint32_t window_id); void display_resize_window(struct display_connection* connection, uint32_t window_id, - uint32_t width, uint32_t - height); + uint32_t width, + uint32_t height); void display_render_window(struct display_connection* connection, uint32_t window_id, uint32_t left, @@ -56,18 +57,24 @@ void display_render_window(struct display_connection* connection, uint32_t height, uint32_t* data); void display_title_window(struct display_connection* connection, - uint32_t window_id, - const char* title); + uint32_t window_id, + const char* title); void display_show_window(struct display_connection* connection, uint32_t window_id); void display_hide_window(struct display_connection* connection, - uint32_t window_id); + uint32_t window_id); + +void display_chkblayout(struct display_connection* connection, + uint32_t id, + void* data, + uint32_t kblayout_bytes); typedef void (*display_event_disconnect_handler_t)(void*); typedef void (*display_event_quit_handler_t)(void*, uint32_t); typedef void (*display_event_resize_handler_t)(void*, uint32_t, uint32_t, uint32_t); typedef void (*display_event_keyboard_handler_t)(void*, uint32_t, uint32_t); +typedef void (*display_event_ack_handler_t)(void*, uint32_t, int); struct display_event_handlers { @@ -76,6 +83,7 @@ struct display_event_handlers display_event_quit_handler_t quit_handler; display_event_resize_handler_t resize_handler; display_event_keyboard_handler_t keyboard_handler; + display_event_ack_handler_t ack_handler; }; int display_poll_event(struct display_connection* connection, diff --git a/libdisplay/libdisplay.c b/libdisplay/libdisplay.c index 1a0efbe1..7bab3c26 100644 --- a/libdisplay/libdisplay.c +++ b/libdisplay/libdisplay.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2014, 2015, 2016, 2017 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 @@ -90,7 +91,8 @@ struct display_connection* display_connect(const char* socket_path) if ( (connection->fd = open_local_client_socket(socket_path, 0)) < 0 ) return free(connection), (struct display_connection*) NULL; size_t send_buffer_size = 2 * 1024 * 1024; - setsockopt(connection->fd, SOL_SOCKET, SO_SNDBUF, &send_buffer_size, sizeof(send_buffer_size)); + setsockopt(connection->fd, SOL_SOCKET, SO_SNDBUF, &send_buffer_size, + sizeof(send_buffer_size)); return connection; } @@ -113,20 +115,25 @@ int display_connection_fd(struct display_connection* connection) return connection->fd; } -static void send_message(struct display_connection* connection, uint32_t message_id, - const void* message, size_t message_size, - const void* auxilerary, size_t auxilerary_size) +static void send_message(struct display_connection* connection, + uint32_t message_id, + const void* message, + size_t message_size, + const void* auxiliary, + size_t auxiliary_size) { struct display_packet_header header; header.message_id = message_id; - header.message_length = message_size + auxilerary_size; + header.message_length = message_size + auxiliary_size; writeall(connection->fd, &header, sizeof(header)); writeall(connection->fd, message, message_size); - writeall(connection->fd, auxilerary, auxilerary_size); + writeall(connection->fd, auxiliary, auxiliary_size); } -static void send_message_no_aux(struct display_connection* connection, uint32_t message_id, - const void* message, size_t message_size) +static void send_message_no_aux(struct display_connection* connection, + uint32_t message_id, + const void* message, + size_t message_size) { send_message(connection, message_id, message, message_size, 0, 0); } @@ -138,21 +145,26 @@ void display_shutdown(struct display_connection* connection, uint32_t code) send_message_no_aux(connection, DISPLAY_SHUTDOWN, &msg, sizeof(msg)); } -void display_create_window(struct display_connection* connection, uint32_t window_id) +void display_create_window(struct display_connection* connection, + uint32_t window_id) { struct display_create_window msg; msg.window_id = window_id; send_message_no_aux(connection, DISPLAY_CREATE_WINDOW, &msg, sizeof(msg)); } -void display_destroy_window(struct display_connection* connection, uint32_t window_id) +void display_destroy_window(struct display_connection* connection, + uint32_t window_id) { struct display_destroy_window msg; msg.window_id = window_id; send_message_no_aux(connection, DISPLAY_DESTROY_WINDOW, &msg, sizeof(msg)); } -void display_resize_window(struct display_connection* connection, uint32_t window_id, uint32_t width, uint32_t height) +void display_resize_window(struct display_connection* connection, + uint32_t window_id, + uint32_t width, + uint32_t height) { struct display_resize_window msg; msg.window_id = window_id; @@ -161,7 +173,13 @@ void display_resize_window(struct display_connection* connection, uint32_t windo send_message_no_aux(connection, DISPLAY_RESIZE_WINDOW, &msg, sizeof(msg)); } -void display_render_window(struct display_connection* connection, uint32_t window_id, uint32_t left, uint32_t top, uint32_t width, uint32_t height, uint32_t* data) +void display_render_window(struct display_connection* connection, + uint32_t window_id, + uint32_t left, + uint32_t top, + uint32_t width, + uint32_t height, + uint32_t* data) { struct display_render_window msg; msg.window_id = window_id; @@ -173,27 +191,42 @@ void display_render_window(struct display_connection* connection, uint32_t windo data, sizeof(uint32_t) * width * height); } -void display_title_window(struct display_connection* connection, uint32_t window_id, const char* title) +void display_title_window(struct display_connection* connection, + uint32_t window_id, + const char* title) { struct display_title_window msg; msg.window_id = window_id; send_message(connection, DISPLAY_TITLE_WINDOW, &msg, sizeof(msg), title, strlen(title)); } -void display_show_window(struct display_connection* connection, uint32_t window_id) +void display_show_window(struct display_connection* connection, + uint32_t window_id) { struct display_show_window msg; msg.window_id = window_id; send_message_no_aux(connection, DISPLAY_SHOW_WINDOW, &msg, sizeof(msg)); } -void display_hide_window(struct display_connection* connection, uint32_t window_id) +void display_hide_window(struct display_connection* connection, + uint32_t window_id) { struct display_hide_window msg; msg.window_id = window_id; send_message_no_aux(connection, DISPLAY_HIDE_WINDOW, &msg, sizeof(msg)); } +void display_chkblayout(struct display_connection* connection, + uint32_t id, + void* data, + uint32_t kblayout_bytes) +{ + struct display_chkblayout msg; + msg.id = id; + send_message(connection, DISPLAY_CHKBLAYOUT, &msg, sizeof(msg), + data, kblayout_bytes); +} + static bool display_read_event(struct display_connection* connection) { while ( connection->header_bytes < sizeof(connection->header) ) @@ -236,7 +269,8 @@ static bool display_read_event(struct display_connection* connection) return true; } -static int display_dispatch_event(struct display_connection* connection, struct display_event_handlers* handlers) +static int display_dispatch_event(struct display_connection* connection, + struct display_event_handlers* handlers) { if ( connection->header_bytes == sizeof(connection->header) && connection->payload && @@ -281,6 +315,14 @@ static int display_dispatch_event(struct display_connection* connection, struct handlers->keyboard_handler(handlers->context, event->window_id, event->codepoint); } + if ( connection->header.message_id == EVENT_ACK && + connection->header.message_length >= sizeof(struct event_ack) ) + { + struct event_ack* event = (struct event_ack*) connection->payload; + if ( handlers->ack_handler ) + handlers->ack_handler(handlers->context, event->id, event->error); + } + connection->header_bytes = 0; free(connection->payload), connection->payload = NULL; connection->payload_bytes = 0; @@ -300,7 +342,8 @@ static int display_event_read_hangup(struct display_event_handlers* handlers) return -1; } -int display_poll_event(struct display_connection* connection, struct display_event_handlers* handlers) +int display_poll_event(struct display_connection* connection, + struct display_event_handlers* handlers) { fcntl(connection->fd, F_SETFL, fcntl(connection->fd, F_GETFL) | O_NONBLOCK); bool read_success = display_read_event(connection); @@ -310,7 +353,8 @@ int display_poll_event(struct display_connection* connection, struct display_eve return display_dispatch_event(connection, handlers); } -int display_wait_event(struct display_connection* connection, struct display_event_handlers* handlers) +int display_wait_event(struct display_connection* connection, + struct display_event_handlers* handlers) { if ( !display_read_event(connection) ) return display_event_read_hangup(handlers); diff --git a/sysinstall/sysinstall.c b/sysinstall/sysinstall.c index 6d5dd0ce..b66299a5 100644 --- a/sysinstall/sysinstall.c +++ b/sysinstall/sysinstall.c @@ -568,8 +568,8 @@ int main(void) conf.release_sig_url); install_configurationf("upgrade.conf", "a", "src = yes\n"); - // TODO: GUI support. - bool kblayout_setable = 0 <= tcgetblob(0, "kblayout", NULL, 0); + bool kblayout_setable = 0 <= tcgetblob(0, "kblayout", NULL, 0) || + getenv("DISPLAY_SOCKET"); while ( kblayout_setable ) { // TODO: Detect the name of the current keyboard layout. diff --git a/sysinstall/sysupgrade.c b/sysinstall/sysupgrade.c index bb00fe0f..f0da6903 100644 --- a/sysinstall/sysupgrade.c +++ b/sysinstall/sysupgrade.c @@ -466,7 +466,8 @@ int main(void) prompt(input, sizeof(input), "ready", "Ready?", ready); text("\n"); - bool kblayout_setable = 0 <= tcgetblob(0, "kblayout", NULL, 0); + bool kblayout_setable = 0 <= tcgetblob(0, "kblayout", NULL, 0) || + getenv("DISPLAY_SOCKET"); while ( kblayout_setable ) { // TODO: Detect the name of the current keyboard layout.