From ce05175372a9ddca1a225db0765ace1127a39293 Mon Sep 17 00:00:00 2001 From: Nicholas Date: Fri, 12 Nov 2021 09:22:01 -0800 Subject: chore: simplified organizational structure --- src/cmd/wm/monitor.c | 386 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 386 insertions(+) create mode 100644 src/cmd/wm/monitor.c (limited to 'src/cmd/wm/monitor.c') diff --git a/src/cmd/wm/monitor.c b/src/cmd/wm/monitor.c new file mode 100644 index 0000000..93073f3 --- /dev/null +++ b/src/cmd/wm/monitor.c @@ -0,0 +1,386 @@ +#include "wm.h" + +/* callbacks */ +void +monitor_change(struct wl_listener *l, void *data) +{ + Monitor *monitor; + struct wlr_output_configuration_v1 *config; + + config = wlr_output_configuration_v1_create(); + server.monitor.geometry = *wlr_output_layout_get_box(server.monitor.layout, nil); + + wl_list_for_each(monitor, &server.monitor.list, link) { + struct wlr_output_configuration_head_v1 *head = + wlr_output_configuration_head_v1_create(config, monitor->output); + + monitor->geometry = monitor->window = *wlr_output_layout_get_box(server.monitor.layout, monitor->output); + + stratify(monitor); + arrange(monitor); + + head->state.enabled = monitor->output->enabled; + head->state.mode = monitor->output->current_mode; + head->state.x = monitor->geometry.x; + head->state.y = monitor->geometry.y; + } + + wlr_output_manager_v1_set_configuration(server.monitor.manager, config); +} + +static +void +trylayout(struct wlr_output_configuration_v1 *config, int force) +{ + int ok; + struct wlr_output_configuration_head_v1 *head; + + ok = 1; + wl_list_for_each(head, &config->heads, link) { + struct wlr_output *output= head->state.output; + wlr_output_enable(output, head->state.enabled); + if (head->state.enabled) { + if (head->state.mode) + wlr_output_set_mode(output, head->state.mode); + else + wlr_output_set_custom_mode( + output, + head->state.custom_mode.width, + head->state.custom_mode.height, + head->state.custom_mode.refresh + ); + + wlr_output_layout_move(server.monitor.layout, output, + head->state.x, head->state.y); + wlr_output_set_transform(output, head->state.transform); + } + + if(!(ok=wlr_output_test(output))) + break; + } + + wl_list_for_each(head, &config->heads, link) { + if(ok && force) + wlr_output_commit(head->state.output); + else + wlr_output_rollback(head->state.output); + } + + if(ok) + wlr_output_configuration_v1_send_succeeded(config); + else + wlr_output_configuration_v1_send_failed(config); + + wlr_output_configuration_v1_destroy(config); +} + +void +monitor_apply(struct wl_listener *l, void *data) +{ + struct wlr_output_configuration_v1 *config = data; + trylayout(config, 1); +} + +void +monitor_test(struct wl_listener *l, void *data) +{ + struct wlr_output_configuration_v1 *config = data; + trylayout(config, 0); +} + +void +make_monitor(struct wl_listener *l, void *data) +{ + int i; + Client *client; + Monitor *monitor; + MonitorRule *rule; + struct wlr_output_mode *mode; + struct wlr_output *output = data; + + /* + * XXX: needed? + if (wl_list_empty(&output->modes)) + return; + */ + + monitor = output->data = calloc(1, sizeof(*monitor)); + monitor->output = output; + + for(i=0; i < arrlen(monitor->layer); i++) + wl_list_init(&monitor->layer[i]); + monitor->tag.set[0] = monitor->tag.set[1] = 1; + + for(rule=cfg·monitorrule; rule != cfg·endmonitorrule; ++rule) { + if(!rule->name || strstr(output->name, rule->name)) { + monitor->master.len = rule->master.len; + monitor->master.frac = rule->master.frac; + + wlr_output_set_scale(output, rule->scale); + wlr_xcursor_manager_load(server.cursor.manager, rule->scale); + monitor->layouts[0] = monitor->layouts[1] = monitor->layout = rule->layout; + + wlr_output_set_transform(output, rule->transform); + break; + } + } + + mode = wlr_output_preferred_mode(output); + wlr_output_set_mode(output, mode); + wlr_output_enable_adaptive_sync(output, true); + + monitor->event.render.notify = render_monitor; + wl_signal_add(&output->events.frame, &monitor->event.render); + monitor->event.destroy.notify = free_monitor; + wl_signal_add(&output->events.destroy, &monitor->event.destroy); + + wlr_output_enable(output, true); + if(!wlr_output_commit(output)) + return; + + wl_list_insert(&server.monitor.list, &monitor->link); + + wlr_output_layout_add(server.monitor.layout, output, rule->x, rule->y); + server.monitor.geometry = *wlr_output_layout_get_box(server.monitor.layout, nil); + + /* update the geometries of all monitors */ + wl_list_for_each(monitor, &server.monitor.list, link) { + /* first monitor in the list = most recently added */ + wl_list_for_each(client, &server.client.list, link) { + if(client->isfloating) + resize(client, client->geometry.x+monitor->window.width, client->geometry.y, + client->geometry.width, client->geometry.height, 0); + } + return; + } +} + +void +free_monitor(struct wl_listener *l, void *data) +{ + int i, len; + Client *client; + struct wlr_output *output = data; + Monitor *monitor = output->data; + + wl_list_remove(&monitor->event.destroy.link); + wl_list_remove(&monitor->event.render.link); + wl_list_remove(&monitor->link); + + wlr_output_layout_remove(server.monitor.layout, monitor->output); + + for(i=0, len=wl_list_length(&server.monitor.list); i < len; i++) { + server.monitor.selected = wl_container_of(server.monitor.list.prev, server.monitor.selected, link); + if(server.monitor.selected->output->enabled) + break; + } + + focus(focused_client(server.monitor.selected), 1); + + /* move closed monitor's clients to newly selected one */ + wl_list_for_each(client, &server.client.list, link) { + if(client->isfloating && client->geometry.x > monitor->geometry.width) + resize(client, + client->geometry.x - monitor->window.width, + client->geometry.y, + client->geometry.width, + client->geometry.height, + 0 + ); + if(client->monitor == monitor) + attach(client, monitor, client->tags); + } + + free(monitor); +} + +/* methods */ +void +arrange(Monitor *monitor) +{ + if(monitor->layout->arrange) + monitor->layout->arrange(monitor); +} + +void +stratum(Monitor *monitor, struct wl_list *list, struct wlr_box *area, int exclusive) +{ + Layer *layer; + struct wlr_box full = monitor->geometry; + + wl_list_for_each(layer, list, link) { + struct wlr_layer_surface_v1 *surface = layer->surface; + struct wlr_layer_surface_v1_state *state = &surface->current; + struct wlr_box bounds; + struct wlr_box box = { + .width = state->desired_width, + .height = state->desired_height + }; + const uint32 horizontal = ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT + | ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT; + const uint32 vertical = ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP + | ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM; + + if (exclusive != (state->exclusive_zone > 0)) + continue; + + bounds = state->exclusive_zone == -1 ? full : *area; + + // horizontal axis + if((state->anchor & horizontal) && box.width == 0) { + box.x = bounds.x; + box.width = bounds.width; + } else if((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { + box.x = bounds.x; + } else if((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { + box.x = bounds.x + (bounds.width - box.width); + } else { + box.x = bounds.x + ((bounds.width / 2) - (box.width / 2)); + } + + // vertical axis + if((state->anchor & vertical) && box.height == 0) { + box.y = bounds.y; + box.height = bounds.height; + } else if((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { + box.y = bounds.y; + } else if((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { + box.y = bounds.y + (bounds.height - box.height); + } else { + box.y = bounds.y + ((bounds.height / 2) - (box.height / 2)); + } + + // margin + if((state->anchor & horizontal) == horizontal) { + box.x += state->margin.left; + box.width -= state->margin.left + state->margin.right; + } else if((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT)) { + box.x += state->margin.left; + } else if((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT)) { + box.x -= state->margin.right; + } + + if((state->anchor & vertical) == vertical) { + box.y += state->margin.top; + box.height -= state->margin.top + state->margin.bottom; + } else if((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP)) { + box.y += state->margin.top; + } else if((state->anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM)) { + box.y -= state->margin.bottom; + } + if(box.width < 0 || box.height < 0) { + wlr_layer_surface_v1_close(surface); + continue; + } + layer->geometry = box; + + if (state->exclusive_zone > 0) + exclude(area, + state->anchor, state->exclusive_zone, + state->margin.top, state->margin.right, + state->margin.bottom, state->margin.left); + wlr_layer_surface_v1_configure(surface, box.width, box.height); + } +} + +void +stratify(Monitor *monitor) +{ + int i; + Layer *layer; + struct wlr_box area = monitor->geometry; + uint32_t overlays[] = { + ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY, + ZWLR_LAYER_SHELL_V1_LAYER_TOP, + }; + struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(server.input.seat); + + // arrange exclusive surfaces from top->bottom + stratum(monitor, &monitor->layer[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], &area, 1); + stratum(monitor, &monitor->layer[ZWLR_LAYER_SHELL_V1_LAYER_TOP], &area, 1); + stratum(monitor, &monitor->layer[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], &area, 1); + stratum(monitor, &monitor->layer[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], &area, 1); + + if(memcmp(&area, &monitor->window, sizeof(area))) { + monitor->window = area; + arrange(monitor); + } + + // arrange non-exlusive surfaces from top->bottom + stratum(monitor, &monitor->layer[ZWLR_LAYER_SHELL_V1_LAYER_OVERLAY], &area, 0); + stratum(monitor, &monitor->layer[ZWLR_LAYER_SHELL_V1_LAYER_TOP], &area, 0); + stratum(monitor, &monitor->layer[ZWLR_LAYER_SHELL_V1_LAYER_BOTTOM], &area, 0); + stratum(monitor, &monitor->layer[ZWLR_LAYER_SHELL_V1_LAYER_BACKGROUND], &area, 0); + + // find topmost keyboard interactive layer, if such a layer exists + for(i = 0; i < arrlen(overlays); i++) { + wl_list_for_each_reverse(layer, &monitor->layer[overlays[i]], link) { + if (layer->surface->current.keyboard_interactive && layer->surface->mapped) { + // Deactivate the focused client. + focus(nil, 0); + wlr_seat_keyboard_notify_enter( + server.input.seat, + layer->surface->surface, + keyboard->keycodes, + keyboard->num_keycodes, + &keyboard->modifiers + ); + return; + } + } + } +} + +Client * +focused_client(Monitor *monitor) +{ + Client *client; + wl_list_for_each(client, &server.client.focus, focus) { + if(VISIBLE_ON(client, monitor)) + return client; + } + + return nil; +} + +void +tile(Monitor *monitor) +{ + Client *client; + uint i, n, h, mw, my, ty; + + n = 0; + wl_list_for_each(client, &server.client.list, link) { + if(VISIBLE_ON(client, monitor) && !client->isfloating) + n++; + } + if(!n) return; + + if(n > monitor->master.len) + mw = monitor->master.len ? monitor->window.width * monitor->master.frac : 0; + else + mw = monitor->window.width; + + i = my = ty = 0; + wl_list_for_each(client, &server.client.list, link) { + if(!VISIBLE_ON(client,monitor) || client->isfloating || client->isfullscreen) + continue; + if(i < monitor->master.len) { + h = (monitor->window.height - my) / (MIN(n, monitor->master.len) - i); + resize(client, monitor->window.x, monitor->window.y + my, mw, h, 0); + my += client->geometry.height; + } else { + h = (monitor->window.height - ty) / (n - i); + resize(client, monitor->window.x + mw, monitor->window.y + ty, monitor->window.width - mw, h, 0); + ty += client->geometry.height; + } + i++; + } +} + +Monitor * +monitor_at(double x, double y) +{ + struct wlr_output *output = wlr_output_layout_output_at(server.monitor.layout, x, y); + return output ? output->data : nil; +} -- cgit v1.2.1