#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; }