river-shifttags/river-shifttags.c

232 lines
7.6 KiB
C

#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#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, &registry_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;
}