// NOTE: Most of this code was stolen from wlr-randr, available at // https://git.sr.ht/~emersion/wlr-randr/tree/master/item/main.c #include "output_manager.h" #include #include #include #include #include "wlr-output-management-unstable-v1.h" static void head_handle_name(void* data, struct zwlr_output_head_v1*, const char* name) { struct convertablet_wlr_head* head = data; head->name = strdup(name); } static void head_handle_description(void*, struct zwlr_output_head_v1*, const char*) { // NO-OP } static void head_handle_physical_size(void*, struct zwlr_output_head_v1*, int32_t, int32_t) { // NO-OP } static void head_handle_mode(void*, struct zwlr_output_head_v1*, struct zwlr_output_mode_v1*) { // NO-OP } static void head_handle_enabled(void*, struct zwlr_output_head_v1*, int32_t) { // NO-OP } static void head_handle_current_mode(void*, struct zwlr_output_head_v1*, struct zwlr_output_mode_v1*) { // NO-OP } static void head_handle_position(void*, struct zwlr_output_head_v1*, int32_t, int32_t) { // NO-OP } static void head_handle_transform(void*, struct zwlr_output_head_v1*, int32_t) { // NO-OP } static void head_handle_scale(void*, struct zwlr_output_head_v1*, wl_fixed_t) { // NO-OP } static void head_handle_finished(void*, struct zwlr_output_head_v1*) { // NO-OP } static void head_handle_make(void*, struct zwlr_output_head_v1*, const char*) { // NO-OP } static void head_handle_model(void*, struct zwlr_output_head_v1*, const char*) { // NO-OP } static void head_handle_serial_number(void*, struct zwlr_output_head_v1*, const char*) { // NO-OP } static void head_handle_adaptive_sync(void*, struct zwlr_output_head_v1*, uint32_t) { // NO-OP } static const struct zwlr_output_head_v1_listener head_listener = { .name = head_handle_name, .description = head_handle_description, .physical_size = head_handle_physical_size, .mode = head_handle_mode, .enabled = head_handle_enabled, .current_mode = head_handle_current_mode, .position = head_handle_position, .transform = head_handle_transform, .scale = head_handle_scale, .finished = head_handle_finished, .make = head_handle_make, .model = head_handle_model, .serial_number = head_handle_serial_number, .adaptive_sync = head_handle_adaptive_sync, }; static void output_manager_handle_head(void* data, struct zwlr_output_manager_v1*, struct zwlr_output_head_v1* wlr_head) { struct convertablet_wlr_state* state = data; struct convertablet_wlr_head* head = calloc(1, sizeof(*head)); head->wlr_head = wlr_head; wl_list_insert(state->heads.prev, &head->link); zwlr_output_head_v1_add_listener(wlr_head, &head_listener, head); } static void output_manager_handle_done(void* data, struct zwlr_output_manager_v1*, uint32_t serial) { struct convertablet_wlr_state* state = data; state->serial = serial; } static void output_manager_handle_finished(void*, struct zwlr_output_manager_v1*) { // NO-OP } static const struct zwlr_output_manager_v1_listener output_manager_listener = { .head = output_manager_handle_head, .done = output_manager_handle_done, .finished = output_manager_handle_finished, }; static void registry_handle_global(void* data, struct wl_registry* registry, uint32_t name, const char* interface, uint32_t) { struct convertablet_wlr_state* state = data; if (strcmp(interface, zwlr_output_manager_v1_interface.name) == 0) { // TODO: Don't always bind version 4? state->output_manager = wl_registry_bind( registry, name, &zwlr_output_manager_v1_interface, 4); zwlr_output_manager_v1_add_listener( state->output_manager, &output_manager_listener, state); } } static void registry_handle_global_remove(void*, struct wl_registry*, uint32_t) { // NO-OP } static void config_handle_succeeded(void* data, struct zwlr_output_configuration_v1* config) { struct convertablet_wlr_state* state = data; state->is_working = false; zwlr_output_configuration_v1_destroy(config); printf("Applied configuration successfully!\n"); } static void config_handle_failed(void* data, struct zwlr_output_configuration_v1* config) { struct convertablet_wlr_state* state = data; state->is_working = false; zwlr_output_configuration_v1_destroy(config); fprintf(stderr, "Failed to apply configuration\n"); } static void config_handle_cancelled(void* data, struct zwlr_output_configuration_v1* config) { struct convertablet_wlr_state* state = data; state->is_working = false; zwlr_output_configuration_v1_destroy(config); fprintf(stderr, "Configuration cancelled\n"); } static const struct zwlr_output_configuration_v1_listener config_listener = { .succeeded = config_handle_succeeded, .failed = config_handle_failed, .cancelled = config_handle_cancelled, }; struct convertablet_wlr_state* convertablet_wlr_init() { struct wl_display* display = wl_display_connect(NULL); if (!display) { fprintf(stderr, "Couldn't connect to display\n"); return 0; } struct wl_registry* registry = wl_display_get_registry(display); if (!registry) { fprintf(stderr, "Couldn't get display registry\n"); return 0; } struct convertablet_wlr_state* state = calloc(1, sizeof(struct convertablet_wlr_state)); state->is_working = false; state->display = display; wl_list_init(&state->heads); static const struct wl_registry_listener registry_listener = { .global = registry_handle_global, .global_remove = registry_handle_global_remove, }; wl_registry_add_listener(registry, ®istry_listener, state); wl_display_dispatch(display); wl_display_roundtrip(display); wl_registry_destroy(registry); if (!state->output_manager) { fprintf(stderr, "Couldn't get output manager. Does your compositor support " "wlr-outpput-management-unstable-v1?\n"); return 0; } return state; } char** convertablet_wlr_get_head_names( struct convertablet_wlr_state const* state) { int head_count = wl_list_length(&state->heads); char** head_name_list = calloc(head_count + 1, sizeof(char*)); size_t head_index = 0; struct convertablet_wlr_head *head, *tmp_head; wl_list_for_each_safe(head, tmp_head, &state->heads, link) { head_name_list[head_index] = head->name; ++head_index; } head_name_list[head_index] = 0; return head_name_list; } void convertablet_wlr_head_set_transform(struct convertablet_wlr_state* state, char const* const head_name, enum wl_output_transform transform) { struct zwlr_output_configuration_v1* output_config = zwlr_output_manager_v1_create_configuration(state->output_manager, state->serial); zwlr_output_configuration_v1_add_listener( output_config, &config_listener, state); struct convertablet_wlr_head* head; struct convertablet_wlr_head* tmp_head; wl_list_for_each_safe(head, tmp_head, &state->heads, link) { if (strcmp(head->name, head_name) != 0) continue; printf("Found head %s\n", head->name); struct zwlr_output_configuration_head_v1* config_head = zwlr_output_configuration_v1_enable_head(output_config, head->wlr_head); state->is_working = true; zwlr_output_configuration_head_v1_set_transform(config_head, transform); zwlr_output_configuration_v1_apply(output_config); } while (state->is_working && wl_display_dispatch(state->display) > 0) ; } void miix_wlr_cleanup(struct convertablet_wlr_state* state) { struct convertablet_wlr_head* head; struct convertablet_wlr_head* tmp_head; wl_list_for_each_safe(head, tmp_head, &state->heads, link) { zwlr_output_head_v1_destroy(head->wlr_head); free(head->name); free(head); } zwlr_output_manager_v1_destroy(state->output_manager); wl_display_disconnect(state->display); }