#include "wm.h" // ----------------------------------------------------------------------- // types enum { CursorPassthrough, CursorMove, CursorResize, }; typedef struct Keyboard Keyboard; typedef struct View View; typedef struct Monitor Monitor; typedef struct Server Server; typedef struct Payload Payload; struct Keyboard { struct wl_list link; struct wlr_input_device *device; struct { struct wl_listener press; struct wl_listener modify; } event; }; struct View { struct wl_list link; struct wlr_xdg_surface *xdg; struct { struct wl_listener map; struct wl_listener unmap; struct wl_listener destroy; struct wl_listener request_move; struct wl_listener request_resize; } event; int x, y, mapped; }; struct Monitor { struct wl_list link; struct wlr_output *output; struct { struct wl_listener render; } event; }; struct Payload { View *view; struct wlr_output *output; struct wlr_renderer *renderer; struct timespec *when; }; struct Server { struct wl_display *display; struct wlr_backend *backend; struct wlr_renderer *renderer; struct { struct wlr_xdg_shell *xdg; struct wl_list list; } shell; struct { View *view; double x, y; struct wlr_box box; } grab; uint32 resize; struct { struct wlr_output_layout *layout; struct wl_list list; } output; struct { struct wlr_cursor *dot; struct wlr_xcursor_manager *manager; int mode; } cursor; struct { struct wlr_seat *seat; struct wl_list keyboards; } input; struct { struct wl_listener new_output; struct wl_listener new_input; struct wl_listener cursor_move; struct wl_listener cursor_move_abs; struct wl_listener cursor_button; struct wl_listener cursor_axis; struct wl_listener cursor_frame; struct wl_listener request_cursor; struct wl_listener request_set_selection; struct wl_listener new_surface; } event; }; static struct Server server; static void focus(View *view, struct wlr_surface *new) { struct wlr_seat *seat; struct wlr_surface *old; struct wlr_xdg_surface *xdg; struct wlr_keyboard *keyboard; if(!view) return; seat = server.input.seat; old = seat->keyboard_state.focused_surface; if(old == new) return; if(old) { xdg = wlr_xdg_surface_from_wlr_surface(seat->keyboard_state.focused_surface); wlr_xdg_toplevel_set_activated(xdg, false); } keyboard = wlr_seat_get_keyboard(seat); wl_list_remove(&view->link); wl_list_insert(&server.shell.list, &view->link); wlr_xdg_toplevel_set_activated(view->xdg, true); wlr_seat_keyboard_notify_enter(seat, view->xdg->surface, keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers); } static int viewhas(View *view, double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { double x, y, vsx = lx - view->x, vsy = ly - view->y; struct wlr_surface *find = nil; find = wlr_xdg_surface_surface_at(view->xdg, vsx, vsy, &x, &y); if(find) { *sx = x; *sy = y; *surface = find; return true; } return false; } static View* viewat(double lx, double ly, struct wlr_surface **surface, double *sx, double *sy) { View *it; wl_list_for_each(it, &server.shell.list, link) { if(viewhas(it, lx, ly, surface, sx, sy)) return it; } return nil; } // ----------------------------------------------------------------------- // callbacks static void keymodifier(struct wl_listener *l, void *data) { Keyboard *keyboard = wl_container_of(l, keyboard, event.modify); wlr_seat_set_keyboard(server.input.seat, keyboard->device); wlr_seat_keyboard_notify_modifiers(server.input.seat, &keyboard->device->keyboard->modifiers); } static int keybinding(xkb_keysym_t sym) { View *current, *next; switch(sym) { case XKB_KEY_Escape: wl_display_terminate(server.display); break; case XKB_KEY_F1: /* cycle to the next view */ if(wl_list_length(&server.shell.list) < 2) break; current = wl_container_of(server.shell.list.next, current, link); next = wl_container_of(current->link.next, next, link); focus(next, next->xdg->surface); /* move previous view to the end of the list */ wl_list_remove(¤t->link); wl_list_insert(server.shell.list.prev, ¤t->link); break; default: return false; } return true; } static void keypress(struct wl_listener *l, void *data) { int i,h,n; uint32 keycode, modifier; const xkb_keysym_t *syms; struct Keyboard *keyboard = wl_container_of(l, keyboard, event.press); struct wlr_event_keyboard_key *event = data; struct wlr_seat *seat = server.input.seat; keycode = event->keycode + 8; h = 0; n = xkb_state_key_get_syms(keyboard->device->keyboard->xkb_state, keycode, &syms); modifier = wlr_keyboard_get_modifiers(keyboard->device->keyboard); if((modifier & WLR_MODIFIER_ALT) && event->state == WL_KEYBOARD_KEY_STATE_PRESSED) { for(i=0; idevice); wlr_seat_keyboard_notify_key(seat, event->time_msec, event->keycode, event->state); } } static void new_keyboard(struct wlr_input_device *device) { Keyboard *keyboard; struct xkb_context *context; struct xkb_keymap *keymap; context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); keymap = xkb_keymap_new_from_names(context, nil, XKB_KEYMAP_COMPILE_NO_FLAGS); wlr_keyboard_set_keymap(device->keyboard, keymap); wlr_keyboard_set_keymap(device->keyboard, keymap); xkb_keymap_unref(keymap); xkb_context_unref(context); wlr_keyboard_set_repeat_info(device->keyboard, 25, 600); keyboard = calloc(1, sizeof(*keyboard)); keyboard->device = device; keyboard->event.modify.notify = keymodifier; wl_signal_add(&device->keyboard->events.modifiers, &keyboard->event.modify); keyboard->event.press.notify = keypress; wl_signal_add(&device->keyboard->events.key, &keyboard->event.press); wlr_seat_set_keyboard(server.input.seat, device); wl_list_insert(&server.input.keyboards, &keyboard->link); } static void new_pointer(struct wlr_input_device *device) { wlr_cursor_attach_input_device(server.cursor.dot, device); } static void new_input(struct wl_listener *l, void *data) { uint32 capability; struct wlr_input_device *device = data; switch(device->type) { case WLR_INPUT_DEVICE_KEYBOARD: new_keyboard(device); break; case WLR_INPUT_DEVICE_POINTER: new_pointer(device); /* fallthrough */ default: break; } capability = WL_SEAT_CAPABILITY_POINTER; if(!wl_list_empty(&server.input.keyboards)) capability |= WL_SEAT_CAPABILITY_KEYBOARD; wlr_seat_set_capabilities(server.input.seat, capability); } static void process_cursor_move(uint32 time) { server.grab.x = server.cursor.dot->x - server.grab.x; server.grab.y = server.cursor.dot->y - server.grab.y; } static void process_cursor_resize(uint32 time) { struct wlr_box box; View *view = server.grab.view; double bx = server.cursor.dot->x - server.grab.x; double by = server.cursor.dot->y - server.grab.y; int new_left = server.grab.box.x; int new_right = server.grab.box.x + server.grab.box.width; int new_top = server.grab.box.y; int new_bottom = server.grab.box.y + server.grab.box.height; int new_width, new_height; if(server.resize & WLR_EDGE_TOP) { new_top = by; if (new_top >= new_bottom) new_top = new_bottom - 1; }else if (server.resize & WLR_EDGE_BOTTOM) { new_bottom = by; if (new_bottom <= new_top) new_bottom = new_top + 1; } if(server.resize & WLR_EDGE_LEFT) { new_left = bx; if (new_left >= new_right) new_left = new_right - 1; } else if(server.resize & WLR_EDGE_RIGHT) { new_right = bx; if (new_right <= new_left) new_right = new_left + 1; } wlr_xdg_surface_get_geometry(view->xdg, &box); view->x = new_left - box.x; view->y = new_top - box.y; new_width = new_right - new_left; new_height = new_bottom - new_top; wlr_xdg_toplevel_set_size(view->xdg, new_width, new_height); } static void process_cursor_motion(uint32 time) { double sx, sy; View *view; struct wlr_seat *seat; struct wlr_surface *surface; if(server.cursor.mode == CursorMove) return process_cursor_move(time); else if(server.cursor.mode == CursorResize) return process_cursor_resize(time); /* Otherwise, find the view under the pointer and send the event along. */ seat = server.input.seat; surface = nil; view = viewat(server.cursor.dot->x, server.cursor.dot->y, &surface, &sx, &sy); if(!view) wlr_xcursor_manager_set_cursor_image(server.cursor.manager, "left_ptr", server.cursor.dot); if(surface) { wlr_seat_pointer_notify_enter(seat, surface, sx, sy); wlr_seat_pointer_notify_motion(seat, time, sx, sy); } else { wlr_seat_pointer_clear_focus(seat); } } static void cursor_move(struct wl_listener *l, void *data) { struct wlr_event_pointer_motion *event = data; wlr_cursor_move(server.cursor.dot, event->device, event->delta_x, event->delta_y); process_cursor_motion(event->time_msec); } static void cursor_move_abs(struct wl_listener *l, void *data) { struct wlr_event_pointer_motion_absolute *event = data; wlr_cursor_warp_absolute(server.cursor.dot, event->device, event->x, event->y); process_cursor_motion(event->time_msec); } static void cursor_button(struct wl_listener *l, void *data) { View *view; double sx, sy; struct wlr_surface *surface; struct wlr_event_pointer_button *event = data; wlr_seat_pointer_notify_button(server.input.seat, event->time_msec, event->button, event->state); view = viewat(server.cursor.dot->x, server.cursor.dot->y, &surface, &sx, &sy); if (event->state == WLR_BUTTON_RELEASED) server.cursor.mode = CursorPassthrough; else focus(view, surface); } static void cursor_axis(struct wl_listener *l, void *data) { struct wlr_event_pointer_axis *event = data; /* Notify the client with pointer focus of the axis event. */ wlr_seat_pointer_notify_axis(server.input.seat, event->time_msec, event->orientation, event->delta, event->delta_discrete, event->source); } static void cursor_frame(struct wl_listener *l, void *data) { wlr_seat_pointer_notify_frame(server.input.seat); } static void request_cursor(struct wl_listener *l, void *data) { struct wlr_seat_pointer_request_set_cursor_event *event = data; struct wlr_seat_client *focused_client = server.input.seat->pointer_state.focused_client; if(focused_client == event->seat_client) wlr_cursor_set_surface(server.cursor.dot, event->surface, event->hotspot_x, event->hotspot_y); } static void request_set_selection(struct wl_listener *l, void *data) { struct wlr_seat_request_set_selection_event *event = data; wlr_seat_set_selection(server.input.seat, event->source, event->serial); } static void surface_map(struct wl_listener *l, void *data) { View *view = wl_container_of(l, view, event.map); view->mapped = true; focus(view, view->xdg->surface); } static void surface_unmap(struct wl_listener *l, void *data) { View *view = wl_container_of(l, view, event.unmap); view->mapped = false; } static void surface_destroy(struct wl_listener *l, void *data) { View *view = wl_container_of(l, view, event.destroy); wl_list_remove(&view->link); free(view); } static void interactive(View *view, int mode, uint32 edges) { double bx, by; struct wlr_box box; struct wlr_surface *focused = server.input.seat->pointer_state.focused_surface; if(view->xdg->surface != focused) return; server.grab.view = view; server.cursor.mode = mode; if(mode == CursorMove) { server.grab.x = server.cursor.dot->x - view->x; server.grab.y = server.cursor.dot->y - view->y; } else { wlr_xdg_surface_get_geometry(view->xdg, &box); bx = (view->x + box.x) + ((edges & WLR_EDGE_RIGHT) ? box.width : 0); by = (view->y + box.y) + ((edges & WLR_EDGE_BOTTOM) ? box.height : 0); server.grab.x = server.cursor.dot->x - bx; server.grab.y = server.cursor.dot->y - by; server.grab.box = box; server.grab.box.x += view->x; server.grab.box.y += view->y; server.resize = edges; } } static void toplevel_request_move(struct wl_listener *l, void *data) { View *view = wl_container_of(l, view, event.request_move); interactive(view, CursorMove, 0); } static void toplevel_request_resize(struct wl_listener *l, void *data) { struct wlr_xdg_toplevel_resize_event *event = data; View *view = wl_container_of(l, view, event.request_resize); interactive(view, CursorResize, event->edges); } static void new_surface(struct wl_listener *l, void *data) { View *view; struct wlr_xdg_toplevel *toplevel; struct wlr_xdg_surface *xdg = data; if(xdg->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) return; view = calloc(1, sizeof(*view)); view->xdg = xdg; view->event.map.notify = surface_map; wl_signal_add(&xdg->events.map, &view->event.map); view->event.unmap.notify = surface_unmap; wl_signal_add(&xdg->events.unmap, &view->event.unmap); view->event.destroy.notify = surface_destroy; wl_signal_add(&xdg->events.destroy, &view->event.destroy); /* cotd */ toplevel = xdg->toplevel; view->event.request_move.notify = toplevel_request_move; wl_signal_add(&toplevel->events.request_move, &view->event.request_move); view->event.request_resize.notify = toplevel_request_resize; wl_signal_add(&toplevel->events.request_resize, &view->event.request_resize); /* Add it to the list of views. */ wl_list_insert(&server.shell.list, &view->link); } static void draw(struct wlr_surface *surface, int sx, int sy, void *data) { float matrix[9]; double ox, oy; View *view; Payload *payload; struct wlr_box box; struct wlr_output *output; struct wlr_texture *texture; enum wl_output_transform transform; payload = data; view = payload->view; output = payload->output; texture = wlr_surface_get_texture(surface); if(!texture) return; ox = 0, oy = 0; wlr_output_layout_output_coords(server.output.layout, output, &ox, &oy); ox += view->x + sx, oy += view->y + sy; box = (struct wlr_box){ .x = ox * output->scale, .y = oy * output->scale, .width = surface->current.width * output->scale, .height = surface->current.height * output->scale, }; transform = wlr_output_transform_invert(surface->current.transform); wlr_matrix_project_box(matrix, &box, transform, 0, output->transform_matrix); wlr_render_texture_with_matrix(payload->renderer, texture, matrix, 1); wlr_surface_send_frame_done(surface, payload->when); } static void render(struct wl_listener *l, void *data) { int width, height; struct timespec now; View *view; Monitor *monitor; struct wlr_renderer *renderer; float color[4] = {0.3, 0.3, 0.3, 1.0}; monitor = wl_container_of(l, monitor, event.render); renderer = server.renderer; clock_gettime(CLOCK_MONOTONIC, &now); if(!wlr_output_attach_render(monitor->output, nil)) return; wlr_output_effective_resolution(monitor->output, &width, &height); /* start of rendering kernel */ wlr_renderer_begin(renderer, width, height); wlr_renderer_clear(renderer, color); wl_list_for_each_reverse(view, &server.shell.list, link) { if(!view->mapped) continue; Payload payload = { .output = monitor->output, .view = view, .renderer = renderer, .when = &now, }; wlr_xdg_surface_for_each_surface(view->xdg, draw, &payload); } wlr_output_render_software_cursors(monitor->output, nil); wlr_renderer_end(renderer); wlr_output_commit(monitor->output); } static void new_output(struct wl_listener *l, void *data) { struct wlr_output_mode *mode; struct Monitor *monitor; struct wlr_output *output = data; if (!wl_list_empty(&output->modes)) { mode = wlr_output_preferred_mode(output); wlr_output_set_mode(output, mode); wlr_output_enable(output, true); if (!wlr_output_commit(output)) { return; } } monitor = calloc(1, sizeof(*monitor)); monitor->output = output; monitor->event.render.notify = render; wl_signal_add(&output->events.frame, &monitor->event.render); wl_list_insert(&server.output.list, &monitor->link); wlr_output_layout_add_auto(server.output.layout, output); } // ----------------------------------------------------------------------- // main point of entry int main(int argc, char *argv[]) { char *socket; wlr_log_init(WLR_DEBUG, nil); server.display = wl_display_create(); server.backend = wlr_backend_autocreate(server.display); server.renderer = wlr_backend_get_renderer(server.backend); wlr_renderer_init_wl_display(server.renderer, server.display); wlr_compositor_create(server.display, server.renderer); wlr_data_device_manager_create(server.display); server.output.layout = wlr_output_layout_create(); wl_list_init(&server.output.list); server.event.new_output.notify = new_output; wl_signal_add(&server.backend->events.new_output, &server.event.new_output); wl_list_init(&server.shell.list); server.shell.xdg = wlr_xdg_shell_create(server.display); server.event.new_surface.notify = new_surface; wl_signal_add(&server.shell.xdg->events.new_surface, &server.event.new_surface); server.cursor.dot = wlr_cursor_create(); wlr_cursor_attach_output_layout(server.cursor.dot, server.output.layout); server.cursor.manager = wlr_xcursor_manager_create(nil, 24); wlr_xcursor_manager_load(server.cursor.manager, 1); server.event.cursor_move.notify = cursor_move; wl_signal_add(&server.cursor.dot->events.motion, &server.event.cursor_move); server.event.cursor_move_abs.notify = cursor_move_abs; wl_signal_add(&server.cursor.dot->events.motion_absolute, &server.event.cursor_move_abs); server.event.cursor_button.notify = cursor_button; wl_signal_add(&server.cursor.dot->events.button, &server.event.cursor_button); server.event.cursor_axis.notify = cursor_axis; wl_signal_add(&server.cursor.dot->events.axis, &server.event.cursor_axis); server.event.cursor_frame.notify = cursor_frame; wl_signal_add(&server.cursor.dot->events.frame, &server.event.cursor_frame); wl_list_init(&server.input.keyboards); server.event.new_input.notify = new_input; wl_signal_add(&server.backend->events.new_input, &server.event.new_input); server.input.seat = wlr_seat_create(server.display, "seat0"); server.event.request_cursor.notify = request_cursor; wl_signal_add(&server.input.seat->events.request_set_cursor, &server.event.request_cursor); server.event.request_set_selection.notify = request_set_selection; wl_signal_add(&server.input.seat->events.request_set_selection, &server.event.request_set_selection); if(!(socket=(char*)wl_display_add_socket_auto(server.display))) { wlr_backend_destroy(server.backend); return 1; } if(!(wlr_backend_start(server.backend))) { wlr_backend_destroy(server.backend); wl_display_destroy(server.display); return 1; } setenv("WAYLAND_DISPLAY", socket, true); wlr_log(WLR_INFO, "Running Wayland compositor on WAYLAND_DISPLAY=%s", socket); /* start loop */ wl_display_run(server.display); /* end loop */ wl_display_destroy_clients(server.display); wl_display_destroy(server.display); return 0; }