diff options
author | anastasisg <anastasis@learnworlds.com> | 2025-02-11 16:07:33 +0200 |
---|---|---|
committer | anastasisg <anastasis@learnworlds.com> | 2025-02-11 16:20:39 +0200 |
commit | e846d66706f807b465121a7a70f668a985a5d39d (patch) | |
tree | 4813da9ba86f3038cb2d4cae2c6468050e388005 /rwm-mode-popup.c | |
download | rwm-gtk-goodies-e846d66706f807b465121a7a70f668a985a5d39d.tar.gz rwm-gtk-goodies-e846d66706f807b465121a7a70f668a985a5d39d.tar.bz2 rwm-gtk-goodies-e846d66706f807b465121a7a70f668a985a5d39d.zip |
initialmain
Diffstat (limited to 'rwm-mode-popup.c')
-rw-r--r-- | rwm-mode-popup.c | 450 |
1 files changed, 450 insertions, 0 deletions
diff --git a/rwm-mode-popup.c b/rwm-mode-popup.c new file mode 100644 index 0000000..1fcebf5 --- /dev/null +++ b/rwm-mode-popup.c @@ -0,0 +1,450 @@ +/****** + * + * Code heavily inspired from https://git.sr.ht/~leon_plickat/river-tag-overlay + * with help from the good people of irc.libera.chat#river + * + */ + +#include <wayland-client.h> +#include <poll.h> +#include <gtk/gtk.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <pthread.h> +#include <unistd.h> + +#include "river-status-unstable-v1.h" +#include "wlr-layer-shell-unstable-v1.h" + +GtkWidget *window = NULL; + +bool loop = true; + +struct wl_display *wl_display = NULL; +struct wl_registry *wl_registry = NULL; +struct wl_callback *sync_callback = NULL; +struct wl_compositor *wl_compositor = NULL; +struct wl_shm *wl_shm = NULL; +struct zriver_status_manager_v1 *river_status_manager = NULL; +struct zwlr_layer_shell_v1 *layer_shell = NULL; + +static void noop( ) { } + +struct Output { + struct wl_list link; + struct wl_output *wl_output; + uint32_t global_name; + struct zriver_output_status_v1 *river_status; + bool configured; +}; + +struct Seat { + struct wl_list link; + struct wl_seat *wl_seat; + uint32_t global_name; + struct zriver_seat_status_v1 *river_status; + bool configured; +}; + +char active_mode[1024] = ""; + +struct wl_list outputs; +struct wl_list seats; + +static void +handle_term(int signum) +{ + fputs("[river-popup] Terminated by signal.\n", stderr); + + /* If cleanup fails or hangs and causes this signal to be recieved again, + * let the default signal handler kill us. + */ + signal(signum, SIG_DFL); + + loop = false; +} + +static void +init_signals(void) +{ + signal(SIGINT, handle_term); + signal(SIGTERM, handle_term); +} + +void +create_gtk_window(const char *mode) +{ + printf("Spawning gtk popup\n"); + + window = gtk_window_new(); + gtk_window_set_title(GTK_WINDOW(window), mode); + gtk_window_set_default_size(GTK_WINDOW(window), 200, 140); + gtk_window_set_resizable(GTK_WINDOW(window), FALSE); + gtk_window_set_decorated(GTK_WINDOW(window), FALSE); + + GtkWidget *label = gtk_label_new(NULL); + gtk_label_set_markup(GTK_LABEL(label),"\n(e) <b>emacs</b>\n" + "(f) <b>firefox</b>\n" + "(s) <b>screenshot(grim)</b>\n" + "(l) <b>waylock</b>\n" + "(p) <b>pcmanfm</b>\n" + "(a) <b>toggle HDMI-A-1</b>\n"); + gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER); + gtk_window_set_child(GTK_WINDOW(window), label); + + /* GtkWidget *spinner = gtk_spinner_new(); */ + /* gtk_window_set_child(GTK_WINDOW(window), spinner); */ + /* gtk_spinner_start(GTK_SPINNER(spinner)); */ + + gtk_window_present(GTK_WINDOW(window)); +} + +/************ + * * + * Output * + * * + ************/ +static void river_output_status_handle_focused_tags(void *data, struct zriver_output_status_v1 *river_status, + uint32_t tags) +{ + noop(); +} + +static void river_output_status_handle_view_tags(void *data, struct zriver_output_status_v1 *river_status, + struct wl_array *tags) +{ + noop(); +} + +static void river_output_status_handle_urgent_tags(void *data, struct zriver_output_status_v1 *river_status, + uint32_t tags) +{ + noop(); +} + +static const struct zriver_output_status_v1_listener river_output_status_listener = { + .focused_tags = river_output_status_handle_focused_tags, + .view_tags = river_output_status_handle_view_tags, + .urgent_tags = river_output_status_handle_urgent_tags, +}; + +static void destroy_output(struct Output *output) +{ + if ( output->river_status != NULL ) + zriver_output_status_v1_destroy(output->river_status); + wl_output_destroy(output->wl_output); + wl_list_remove(&output->link); + free(output); +} + +static struct Output *output_from_global_name(uint32_t name) +{ + struct Output *output; + wl_list_for_each(output, &outputs, link) + if ( output->global_name == name ) + return output; + return NULL; +} + +static void configure_output(struct Output *output) +{ + output->river_status = zriver_status_manager_v1_get_river_output_status(river_status_manager, output->wl_output); + zriver_output_status_v1_add_listener(output->river_status, &river_output_status_listener, output); + output->configured = true; +} + +/********** + * * + * Seat * + * * + **********/ +static void river_seat_status_handle_focused_output(void *data, struct zriver_seat_status_v1 *seat_status, + struct wl_output *wl_output) +{ + if (strcmp(active_mode, "lnch") == 0) { + gtk_window_destroy(GTK_WINDOW(window)); + window = NULL; + create_gtk_window("lnch"); + } +} + +static void +river_seat_status_handle_focused_view(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, const char *title) +{ + noop(); +} + +static void +river_seat_status_handle_mode(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, const char *name) { + strcpy(active_mode, name); + if (strcmp(name, "lnch") == 0) { + if (!window) { + create_gtk_window(name); + } + } else { + if (window) { + gtk_window_destroy(GTK_WINDOW(window)); + window = NULL; + } + } +} + +static const struct zriver_seat_status_v1_listener river_seat_status_listener = { + .focused_output = river_seat_status_handle_focused_output, + .unfocused_output = noop, + .focused_view = river_seat_status_handle_focused_view, + .mode = river_seat_status_handle_mode, +}; + +static struct Seat *seat_from_global_name(uint32_t name) +{ + struct Seat *seat; + wl_list_for_each(seat, &seats, link) + if ( seat->global_name == name ) + return seat; + return NULL; +} + +static void destroy_seat(struct Seat *seat) +{ + if ( seat->river_status != NULL ) + zriver_seat_status_v1_destroy(seat->river_status); + wl_seat_destroy(seat->wl_seat); + wl_list_remove(&seat->link); + free(seat); +} + +static void configure_seat(struct Seat *seat) +{ + seat->river_status = zriver_status_manager_v1_get_river_seat_status(river_status_manager, seat->wl_seat); + zriver_seat_status_v1_add_listener(seat->river_status, &river_seat_status_listener, seat); + seat->configured = true; +} + +static char *check_for_interfaces(void) +{ + if ( wl_compositor == NULL ) + return "wl_compositor"; + if ( wl_shm == NULL ) + return "wl_shm"; + if ( layer_shell == NULL ) + return "wlr_layershell_v1"; + if ( river_status_manager == NULL ) + return "river_status_v1"; + return NULL; +} + +static void +sync_handle_done(void *data, struct wl_callback *wl_callback, uint32_t other) +{ + wl_callback_destroy(wl_callback); + sync_callback = NULL; + + const char *missing = check_for_interfaces(); + if ( missing != NULL ) { + fprintf(stderr, "ERROR, Wayland compositor does not support %s.\n", missing); + loop = false; + return; + } + + struct Output *output; + wl_list_for_each(output, &outputs, link) + if (! output->configured) + configure_output(output); + + struct Seat *seat; + wl_list_for_each(seat, &seats, link) + if (! seat->configured) + configure_seat(seat); +} + +static const struct wl_callback_listener sync_callback_listener = { + .done = sync_handle_done, +}; + +static void +global_registry_handler(void *data, struct wl_registry *registry, uint32_t id, + const char *interface, uint32_t version) +{ + if ( strcmp(interface, wl_output_interface.name) == 0 ) { + struct Output *output = calloc(1, sizeof(struct Output)); + if ( output == NULL ) { + fprintf(stderr, "ERROR: calloc: %s.\n", strerror(errno)); + return; + } + + output->wl_output = wl_registry_bind(registry, id, &wl_output_interface, 3); + output->global_name = id; + wl_output_set_user_data(output->wl_output, output); + wl_list_insert(&outputs, &output->link); + + if ( river_status_manager != NULL ) + configure_output(output); + } + else if ( strcmp(interface, wl_seat_interface.name) == 0 ) { + struct Seat *seat = calloc(1, sizeof(struct Seat)); + if (seat == NULL) { + fprintf(stderr, "ERROR: calloc: %s.\n", strerror(errno)); + return; + } + + seat->wl_seat = wl_registry_bind(registry, id, &wl_seat_interface, 7); + seat->global_name = id; + wl_seat_set_user_data(seat->wl_seat, seat); + wl_list_insert(&seats, &seat->link); + + if ( river_status_manager != NULL ) + configure_seat(seat); + } + else if ( strcmp(interface, zwlr_layer_shell_v1_interface.name) == 0 ) { + layer_shell = wl_registry_bind(registry, id, &zwlr_layer_shell_v1_interface, 1); + } + else if ( strcmp(interface, zriver_status_manager_v1_interface.name) == 0 ) { + river_status_manager = wl_registry_bind(registry, id, &zriver_status_manager_v1_interface, 3); + } + else if ( strcmp(interface, wl_compositor_interface.name) == 0 ) { + wl_compositor = wl_registry_bind(registry, id, &wl_compositor_interface, 4); + } + else if ( strcmp(interface, wl_shm_interface.name) == 0 ) { + wl_shm = wl_registry_bind(registry, id, &wl_shm_interface, 1); + } +} + +static void +global_registry_remover(void *data, struct wl_registry *registry, uint32_t id) +{ + struct Output *output = output_from_global_name(id); + if ( output != NULL ) { + destroy_output(output); + return; + } + + struct Seat *seat = seat_from_global_name(id); + if ( seat != NULL ) { + destroy_seat(seat); + return; + } +} + +static const struct wl_registry_listener registry_listener = { + .global = global_registry_handler, + .global_remove = global_registry_remover, +}; + +void* +gui_thread_function(void* arg) +{ + while (loop) { + if (window) + g_main_context_iteration(NULL, TRUE); + } + return NULL; +} + +int +main(int argc, char *argv[]) +{ + int status = 0; + + init_signals(); + + /* We query the display name here instead of letting wl_display_connect() + * figure it out itself, because libwayland (for legacy reasons) falls + * back to using "wayland-0" when $WAYLAND_DISPLAY is not set, which is + * generally not desirable. + */ + const char *display_name = getenv("WAYLAND_DISPLAY"); + if ( display_name == NULL ) { + fputs("ERROR: WAYLAND_DISPLAY is not set.\n", stderr); + return EXIT_FAILURE; + } + + wl_display = wl_display_connect(display_name); + if ( wl_display == NULL ) { + fputs("ERROR: Can not connect to wayland display.\n", stderr); + return EXIT_FAILURE; + } + + wl_list_init(&outputs); + wl_list_init(&seats); + + wl_registry = wl_display_get_registry(wl_display); + wl_registry_add_listener(wl_registry, ®istry_listener, NULL); + + sync_callback = wl_display_sync(wl_display); + wl_callback_add_listener(sync_callback, &sync_callback_listener, NULL); + + gtk_init(); + g_object_set(gtk_settings_get_default(), "gtk-application-prefer-dark-theme", TRUE, NULL); + + pthread_t gui_thread; + if (pthread_create(&gui_thread, NULL, gui_thread_function, NULL)) { + fprintf(stderr, "Error creating thread\n"); + return 1; + } + + struct pollfd pollfds[] = { + { + .fd = wl_display_get_fd(wl_display), + .events = POLLIN, + }, + }; + + while (loop) { + int current_timeout = -1; + + /* Flush pending Wayland events/requests. */ + int _status = 1; + while ( _status > 0 ) { + _status = wl_display_dispatch_pending(wl_display); + wl_display_flush(wl_display); + } + if ( _status < 0 ) { + fprintf(stderr, "ERROR: wl_display_dispatch_pending: %s\n", strerror(errno)); + status = EXIT_FAILURE; + break; + } + + if ( poll(pollfds, 1, current_timeout) < 0 ) { + if ( errno == EINTR ) + continue; + fprintf(stderr, "ERROR: poll: %s.\n", strerror(errno)); + status = EXIT_FAILURE; + break; + } + + if ( (pollfds[0].revents & POLLIN) && wl_display_dispatch(wl_display) == -1 ) { + fprintf(stderr, "ERROR: wl_display_dispatch: %s.\n", strerror(errno)); + break; + } + if ( (pollfds[0].revents & POLLOUT) && wl_display_flush(wl_display) == -1 ) { + fprintf(stderr, "ERROR: wl_display_flush: %s.\n", strerror(errno)); + break; + } + } + + close(pollfds[0].fd); + + pthread_cancel(gui_thread); + pthread_join(gui_thread, NULL); + + struct Output *output, *otmp; + wl_list_for_each_safe(output, otmp, &outputs, link) + destroy_output(output); + + if ( wl_compositor != NULL ) + wl_compositor_destroy(wl_compositor); + if ( wl_shm != NULL ) + wl_shm_destroy(wl_shm); + if ( layer_shell != NULL ) + zwlr_layer_shell_v1_destroy(layer_shell); + if ( river_status_manager != NULL ) + zriver_status_manager_v1_destroy(river_status_manager); + if ( sync_callback != NULL ) + wl_callback_destroy(sync_callback); + if ( wl_registry != NULL ) + wl_registry_destroy(wl_registry); + wl_display_disconnect(wl_display); + + return status; +} |