#include #include #include #include #include #include "river-status-unstable-v1.h" #include "river-control-unstable-v1.h" const char usage[] = "Usage: river-shiftview [OPTIONS]\n" " --shift VALUE value by which to rotate selected tag.\n" " --num-tags VALUE number of tags to rotate\n" " --start VALUE First tag to rotate from.\n" " --help print this message and exit.\n" "\n" "\n"; int _shift = 1; int _num_tags = 9; int _start_tag = 1; size_t outputs_size = 0; int ret = EXIT_SUCCESS; struct output { struct wl_output *output; size_t index; }; struct wl_display *wl_display = NULL; struct output *wl_outputs = NULL; struct wl_seat *seat = NULL; struct zriver_status_manager_v1 *river_status_manager = NULL; struct zriver_control_v1 *river_controller = NULL; struct zriver_output_status_v1 **river_output_statuses = NULL; unsigned int *new_tags = NULL; size_t focused_output = 0; void rotate(unsigned int tagmask, size_t index, int shift, int start_tag, int num_tags) { // if multiple tags, chose the lowest one int current_tag = 1; while (!(tagmask & 1)) { tagmask = tagmask >> 1; current_tag += 1; } int new_tag = current_tag + shift; while (new_tag > num_tags) { new_tag = start_tag + (new_tag - num_tags) - 1; // -1 because new_tag - num_tags is always at least 1 and // if we overflow, we want to START at start_tag } while (new_tag < start_tag) { new_tag = num_tags + (new_tag - start_tag) + 1; // +1 because num_tags is INCLUSIVE and new_tag - start_tag // is always at least 1 } new_tags[index] = 1 << (new_tag - 1); } static void print_tagmask(void *data, struct zriver_output_status_v1 *output_status_v1, unsigned int tagmask) { rotate(tagmask, (size_t)data, _shift, _start_tag, _num_tags); } static void river_status_handle_view_tags(void *data, struct zriver_output_status_v1 *river_status, struct wl_array *tags) {} static void river_status_handle_urgent_tags( void *data, struct zriver_output_status_v1 *river_status, uint32_t tags) {} static const struct zriver_output_status_v1_listener tag_status_listener = { .focused_tags = print_tagmask, .view_tags = river_status_handle_view_tags, .urgent_tags = river_status_handle_urgent_tags, }; void get_focused_output(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, struct wl_output *output) { for (size_t i = 0; i < outputs_size; i++) { if (wl_outputs[i].output == output) { focused_output = i; } } } void unfocused_output_empty(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, struct wl_output *output) {} void focused_view_empty(void *data, struct zriver_seat_status_v1 *zriver_seat_status_v1, const char *title) {} static const struct zriver_seat_status_v1_listener output_status_listener = { .focused_output = get_focused_output, .unfocused_output = unfocused_output_empty, .focused_view = focused_view_empty, }; 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") == 0) { outputs_size++; wl_outputs = realloc(wl_outputs, outputs_size * sizeof(struct output)); if (wl_outputs == NULL) { fputs("REALLOC ERROR", stderr); exit(1); } wl_outputs[outputs_size - 1].output = wl_registry_bind(registry, id, &wl_output_interface, 1); wl_outputs[outputs_size - 1].index = outputs_size - 1; } else if (strcmp(interface, "wl_seat") == 0) { seat = wl_registry_bind(registry, id, &wl_seat_interface, 3); } else if (strcmp(interface, zriver_status_manager_v1_interface.name) == 0) { river_status_manager = wl_registry_bind( registry, id, &zriver_status_manager_v1_interface, 2); } else if (strcmp(interface, zriver_control_v1_interface.name) == 0) { river_controller = wl_registry_bind(registry, id, &zriver_control_v1_interface, 1); } } static void global_registry_remover(void *data, struct wl_registry *registry, uint32_t id) {} static const struct wl_registry_listener registry_listener = { global_registry_handler, global_registry_remover }; int main(int argc, char *argv[]) { enum { HELP, SHIFT_VALUE, START_TAG, NUM_TAGS }; static struct option opts[] = { { "help", no_argument, NULL, HELP }, { "shift", required_argument, NULL, SHIFT_VALUE }, { "num-tags", required_argument, NULL, NUM_TAGS }, { "start", required_argument, NULL, START_TAG }, { NULL, 0, NULL, 0 }, }; int opt; while ((opt = getopt_long(argc, argv, "h", opts, NULL)) != -1) { switch (opt) { case HELP: fputs(usage, stderr); return EXIT_SUCCESS; case SHIFT_VALUE: _shift = atoi(optarg); break; case START_TAG: _start_tag = atoi(optarg); break; case NUM_TAGS: _num_tags = atoi(optarg); break; default: return EXIT_FAILURE; } } 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; } struct wl_registry *registry = wl_display_get_registry(wl_display); wl_registry_add_listener(registry, ®istry_listener, NULL); wl_display_roundtrip(wl_display); river_output_statuses = malloc(outputs_size * sizeof(struct zriver_output_status_v1 *)); new_tags = malloc(outputs_size * sizeof(int *)); struct zriver_seat_status_v1 *seat_status_manager = zriver_status_manager_v1_get_river_seat_status(river_status_manager, seat); zriver_seat_status_v1_add_listener(seat_status_manager, &output_status_listener, NULL); for (size_t i = 0; i < outputs_size; i++) { river_output_statuses[i] = zriver_status_manager_v1_get_river_output_status( river_status_manager, wl_outputs[i].output); zriver_output_status_v1_add_listener(river_output_statuses[i], &tag_status_listener, (void *)i); new_tags[i] = 0; } /* Wait till new_tags is populated */ wl_display_roundtrip(wl_display); /* Set the new focused tags */ zriver_control_v1_add_argument(river_controller, "set-focused-tags"); char *tags_str = malloc((size_t)snprintf(NULL, 0, "%d", new_tags[focused_output])); sprintf(tags_str, "%d", new_tags[focused_output]); zriver_control_v1_add_argument(river_controller, tags_str); zriver_control_v1_run_command(river_controller, seat); zriver_control_v1_destroy(river_controller); wl_display_roundtrip(wl_display); free(wl_outputs); free(river_output_statuses); free(new_tags); return ret; }