commit
						5f4c64fbeb
					
				| @ -0,0 +1,3 @@ | |||||||
|  | tinywl | ||||||
|  | *-protocol.c | ||||||
|  | *-protocol.h | ||||||
| @ -0,0 +1,24 @@ | |||||||
|  | WAYLAND_PROTOCOLS=/usr/share/wayland-protocols | ||||||
|  | 
 | ||||||
|  | xdg-shell-protocol.h: | ||||||
|  | 	wayland-scanner server-header \
 | ||||||
|  | 		$(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@ | ||||||
|  | 
 | ||||||
|  | xdg-shell-protocol.c: xdg-shell-protocol.h | ||||||
|  | 	wayland-scanner private-code \
 | ||||||
|  | 		$(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@ | ||||||
|  | 
 | ||||||
|  | tinywl: tinywl.c xdg-shell-protocol.h xdg-shell-protocol.c | ||||||
|  | 	$(CC) $(CFLAGS) \
 | ||||||
|  | 		-g -Werror -I. \
 | ||||||
|  | 		-DWLR_USE_UNSTABLE \
 | ||||||
|  | 		$(shell pkg-config --cflags --libs wlroots) \
 | ||||||
|  | 		$(shell pkg-config --cflags --libs wayland-server) \
 | ||||||
|  | 		$(shell pkg-config --cflags --libs xkbcommon) \
 | ||||||
|  | 		-o $@ $< | ||||||
|  | 
 | ||||||
|  | clean: | ||||||
|  | 	rm -f tinywl xdg-shell-protocol.h xdg-shell-protocol.c | ||||||
|  | 
 | ||||||
|  | .DEFAULT_GOAL=tinywl | ||||||
|  | .PHONY: clean | ||||||
| @ -0,0 +1,620 @@ | |||||||
|  | #define _POSIX_C_SOURCE 200112L | ||||||
|  | #include <getopt.h> | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <time.h> | ||||||
|  | #include <unistd.h> | ||||||
|  | #include <wayland-server.h> | ||||||
|  | #include <wlr/backend.h> | ||||||
|  | #include <wlr/render/wlr_renderer.h> | ||||||
|  | #include <wlr/types/wlr_cursor.h> | ||||||
|  | #include <wlr/types/wlr_compositor.h> | ||||||
|  | #include <wlr/types/wlr_data_device.h> | ||||||
|  | #include <wlr/types/wlr_input_device.h> | ||||||
|  | #include <wlr/types/wlr_keyboard.h> | ||||||
|  | #include <wlr/types/wlr_linux_dmabuf_v1.h> | ||||||
|  | #include <wlr/types/wlr_matrix.h> | ||||||
|  | #include <wlr/types/wlr_output.h> | ||||||
|  | #include <wlr/types/wlr_output_layout.h> | ||||||
|  | #include <wlr/types/wlr_pointer.h> | ||||||
|  | #include <wlr/types/wlr_seat.h> | ||||||
|  | #include <wlr/types/wlr_xcursor_manager.h> | ||||||
|  | #include <wlr/types/wlr_xdg_shell.h> | ||||||
|  | #include <wlr/util/log.h> | ||||||
|  | #include <xkbcommon/xkbcommon.h> | ||||||
|  | 
 | ||||||
|  | enum tinywl_cursor_mode { | ||||||
|  | 	TINYWL_CURSOR_PASSTHROUGH, | ||||||
|  | 	TINYWL_CURSOR_MOVE, | ||||||
|  | 	TINYWL_CURSOR_RESIZE, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct tinywl_server { | ||||||
|  | 	struct wl_display *wl_display; | ||||||
|  | 	struct wlr_backend *backend; | ||||||
|  | 	struct wlr_renderer *renderer; | ||||||
|  | 
 | ||||||
|  | 	struct wlr_xdg_shell *xdg_shell; | ||||||
|  | 	struct wl_listener new_xdg_surface; | ||||||
|  | 	struct wl_list views; | ||||||
|  | 
 | ||||||
|  | 	struct wlr_cursor *cursor; | ||||||
|  | 	struct wlr_xcursor_manager *cursor_mgr; | ||||||
|  | 	struct wl_listener cursor_motion; | ||||||
|  | 	struct wl_listener cursor_motion_absolute; | ||||||
|  | 	struct wl_listener cursor_button; | ||||||
|  | 	struct wl_listener cursor_axis; | ||||||
|  | 
 | ||||||
|  | 	struct wlr_seat *seat; | ||||||
|  | 	struct wl_listener new_input; | ||||||
|  | 	struct wl_listener request_cursor; | ||||||
|  | 	struct wl_list keyboards; | ||||||
|  | 	enum tinywl_cursor_mode cursor_mode; | ||||||
|  | 	struct tinywl_view *grabbed_view; | ||||||
|  | 	double grab_x, grab_y; | ||||||
|  | 	int grab_width, grab_height; | ||||||
|  | 	uint32_t resize_edges; | ||||||
|  | 
 | ||||||
|  | 	struct wlr_output_layout *output_layout; | ||||||
|  | 	struct wl_list outputs; | ||||||
|  | 	struct wl_listener new_output; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct tinywl_output { | ||||||
|  | 	struct wl_list link; | ||||||
|  | 	struct tinywl_server *server; | ||||||
|  | 	struct wlr_output *wlr_output; | ||||||
|  | 	struct wl_listener frame; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct tinywl_view { | ||||||
|  | 	struct wl_list link; | ||||||
|  | 	struct tinywl_server *server; | ||||||
|  | 	struct wlr_xdg_surface *xdg_surface; | ||||||
|  | 	struct wl_listener map; | ||||||
|  | 	struct wl_listener unmap; | ||||||
|  | 	struct wl_listener destroy; | ||||||
|  | 	struct wl_listener request_move; | ||||||
|  | 	struct wl_listener request_resize; | ||||||
|  | 	bool mapped; | ||||||
|  | 	int x, y; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct tinywl_keyboard { | ||||||
|  | 	struct wl_list link; | ||||||
|  | 	struct tinywl_server *server; | ||||||
|  | 	struct wlr_input_device *device; | ||||||
|  | 
 | ||||||
|  | 	struct wl_listener modifiers; | ||||||
|  | 	struct wl_listener key; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct tinywl_pointer { | ||||||
|  | 	struct wl_list link; | ||||||
|  | 	struct tinywl_server *server; | ||||||
|  | 	struct wlr_input_device *device; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void keyboard_handle_modifiers( | ||||||
|  | 		struct wl_listener *listener, void *data) { | ||||||
|  | 	struct tinywl_keyboard *keyboard = | ||||||
|  | 		wl_container_of(listener, keyboard, modifiers); | ||||||
|  | 	wlr_seat_set_keyboard(keyboard->server->seat, keyboard->device); | ||||||
|  | 	wlr_seat_keyboard_notify_modifiers(keyboard->server->seat, | ||||||
|  | 		&keyboard->device->keyboard->modifiers); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void keyboard_handle_key( | ||||||
|  | 		struct wl_listener *listener, void *data) { | ||||||
|  | 	struct tinywl_keyboard *keyboard = | ||||||
|  | 		wl_container_of(listener, keyboard, key); | ||||||
|  | 	struct wlr_event_keyboard_key *event = data; | ||||||
|  | 	struct wlr_seat *seat = keyboard->server->seat; | ||||||
|  | 	// TODO: keybindings for moving windows about
 | ||||||
|  | 	wlr_seat_set_keyboard(seat, keyboard->device); | ||||||
|  | 	wlr_seat_keyboard_notify_key(seat, event->time_msec, | ||||||
|  | 		event->keycode, event->state); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void server_new_keyboard(struct tinywl_server *server, | ||||||
|  | 		struct wlr_input_device *device) { | ||||||
|  | 	struct tinywl_keyboard *keyboard = | ||||||
|  | 		calloc(1, sizeof(struct tinywl_keyboard)); | ||||||
|  | 	keyboard->server = server; | ||||||
|  | 	keyboard->device = device; | ||||||
|  | 
 | ||||||
|  | 	struct xkb_rule_names rules = { 0 }; | ||||||
|  | 	struct xkb_context *context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); | ||||||
|  | 	struct xkb_keymap *keymap = xkb_map_new_from_names(context, &rules, | ||||||
|  | 		XKB_KEYMAP_COMPILE_NO_FLAGS); | ||||||
|  | 
 | ||||||
|  | 	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->modifiers.notify = keyboard_handle_modifiers; | ||||||
|  | 	wl_signal_add(&device->keyboard->events.modifiers, &keyboard->modifiers); | ||||||
|  | 	keyboard->key.notify = keyboard_handle_key; | ||||||
|  | 	wl_signal_add(&device->keyboard->events.key, &keyboard->key); | ||||||
|  | 
 | ||||||
|  | 	wlr_seat_set_keyboard(server->seat, device); | ||||||
|  | 
 | ||||||
|  | 	wl_list_insert(&server->keyboards, &keyboard->link); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void server_new_pointer(struct tinywl_server *server, | ||||||
|  | 		struct wlr_input_device *device) { | ||||||
|  | 	wlr_cursor_attach_input_device(server->cursor, device); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void server_new_input(struct wl_listener *listener, void *data) { | ||||||
|  | 	struct tinywl_server *server = | ||||||
|  | 		wl_container_of(listener, server, new_input); | ||||||
|  | 	struct wlr_input_device *device = data; | ||||||
|  | 	switch (device->type) { | ||||||
|  | 	case WLR_INPUT_DEVICE_KEYBOARD: | ||||||
|  | 		server_new_keyboard(server, device); | ||||||
|  | 		break; | ||||||
|  | 	case WLR_INPUT_DEVICE_POINTER: | ||||||
|  | 		server_new_pointer(server, device); | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | 	uint32_t caps = WL_SEAT_CAPABILITY_POINTER; | ||||||
|  | 	if (!wl_list_empty(&server->keyboards)) { | ||||||
|  | 		caps |= WL_SEAT_CAPABILITY_KEYBOARD; | ||||||
|  | 	} | ||||||
|  | 	wlr_seat_set_capabilities(server->seat, caps); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void seat_request_cursor(struct wl_listener *listener, void *data) { | ||||||
|  | 	struct tinywl_server *server = wl_container_of( | ||||||
|  | 			listener, server, request_cursor); | ||||||
|  | 	struct wlr_seat_pointer_request_set_cursor_event *event = data; | ||||||
|  | 	struct wlr_seat_client *focused_client = | ||||||
|  | 		server->seat->pointer_state.focused_client; | ||||||
|  | 	if (focused_client == event->seat_client) { | ||||||
|  | 		wlr_cursor_set_surface(server->cursor, event->surface, | ||||||
|  | 				event->hotspot_x, event->hotspot_y); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool view_at(struct tinywl_view *view, | ||||||
|  | 		double lx, double ly, struct wlr_surface **surface, | ||||||
|  | 		double *sx, double *sy) { | ||||||
|  | 	double view_sx = lx - view->x; | ||||||
|  | 	double view_sy = ly - view->y; | ||||||
|  | 
 | ||||||
|  | 	struct wlr_surface_state *state = &view->xdg_surface->surface->current; | ||||||
|  | 	struct wlr_box box = { | ||||||
|  | 		.x = 0, .y = 0, | ||||||
|  | 		.width = state->width, .height = state->height, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	double _sx, _sy; | ||||||
|  | 	struct wlr_surface *_surface = NULL; | ||||||
|  | 	_surface = wlr_xdg_surface_surface_at( | ||||||
|  | 			view->xdg_surface, view_sx, view_sy, &_sx, &_sy); | ||||||
|  | 
 | ||||||
|  | 	if (_surface != NULL) { | ||||||
|  | 		*sx = _sx; | ||||||
|  | 		*sy = _sy; | ||||||
|  | 		*surface = _surface; | ||||||
|  | 		return true; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct tinywl_view *desktop_view_at( | ||||||
|  | 		struct tinywl_server *server, double lx, double ly, | ||||||
|  | 		struct wlr_surface **surface, double *sx, double *sy) { | ||||||
|  | 	struct tinywl_view *view; | ||||||
|  | 	wl_list_for_each(view, &server->views, link) { | ||||||
|  | 		if (view_at(view, lx, ly, surface, sx, sy)) { | ||||||
|  | 			return view; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void focus_view(struct tinywl_view *view, struct wlr_surface *surface) { | ||||||
|  | 	struct tinywl_server *server = view->server; | ||||||
|  | 	struct wlr_seat *seat = server->seat; | ||||||
|  | 	struct wlr_surface *prev_surface = seat->keyboard_state.focused_surface; | ||||||
|  | 	if (prev_surface == surface) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	if (prev_surface) { | ||||||
|  | 		struct wlr_xdg_surface *previous = wlr_xdg_surface_from_wlr_surface( | ||||||
|  | 					seat->keyboard_state.focused_surface); | ||||||
|  | 		wlr_xdg_toplevel_set_activated(previous, false); | ||||||
|  | 	} | ||||||
|  | 	struct wlr_keyboard *keyboard = wlr_seat_get_keyboard(seat); | ||||||
|  | 	wl_list_remove(&view->link); | ||||||
|  | 	wl_list_insert(&server->views, &view->link); | ||||||
|  | 	wlr_xdg_toplevel_set_activated(view->xdg_surface, true); | ||||||
|  | 	wlr_seat_keyboard_notify_enter(seat, view->xdg_surface->surface, | ||||||
|  | 		keyboard->keycodes, keyboard->num_keycodes, &keyboard->modifiers); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void process_cursor_move(struct tinywl_server *server, uint32_t time) { | ||||||
|  | 	server->grabbed_view->x = server->cursor->x - server->grab_x; | ||||||
|  | 	server->grabbed_view->y = server->cursor->y - server->grab_y; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void process_cursor_resize(struct tinywl_server *server, uint32_t time) { | ||||||
|  | 	struct tinywl_view *view = server->grabbed_view; | ||||||
|  | 	double dx = server->cursor->x - server->grab_x; | ||||||
|  | 	double dy = server->cursor->y - server->grab_y; | ||||||
|  | 	double x = view->x; | ||||||
|  | 	double y = view->y; | ||||||
|  | 	int width = server->grab_width; | ||||||
|  | 	int height = server->grab_height; | ||||||
|  | 	if (server->resize_edges & WLR_EDGE_TOP) { | ||||||
|  | 		y = server->grab_y + dy; | ||||||
|  | 		height -= dy; | ||||||
|  | 		if (height < 1) { | ||||||
|  | 			y += height; | ||||||
|  | 		} | ||||||
|  | 	} else if (server->resize_edges & WLR_EDGE_BOTTOM) { | ||||||
|  | 		height += dy; | ||||||
|  | 	} | ||||||
|  | 	if (server->resize_edges & WLR_EDGE_LEFT) { | ||||||
|  | 		x = server->grab_x + dx; | ||||||
|  | 		width -= dx; | ||||||
|  | 		if (width < 1) { | ||||||
|  | 			x += width; | ||||||
|  | 		} | ||||||
|  | 	} else if (server->resize_edges & WLR_EDGE_RIGHT) { | ||||||
|  | 		width += dx; | ||||||
|  | 	} | ||||||
|  | 	view->x = x; | ||||||
|  | 	view->y = y; | ||||||
|  | 	wlr_xdg_toplevel_set_size(view->xdg_surface, width, height); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void process_cursor_motion(struct tinywl_server *server, uint32_t time) { | ||||||
|  | 	if (server->cursor_mode == TINYWL_CURSOR_MOVE) { | ||||||
|  | 		process_cursor_move(server, time); | ||||||
|  | 		return; | ||||||
|  | 	} else if (server->cursor_mode == TINYWL_CURSOR_RESIZE) { | ||||||
|  | 		process_cursor_resize(server, time); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	double sx, sy; | ||||||
|  | 	struct wlr_seat *seat = server->seat; | ||||||
|  | 	struct wlr_surface *surface; | ||||||
|  | 	struct tinywl_view *view = desktop_view_at(server, | ||||||
|  | 			server->cursor->x, server->cursor->y, &surface, &sx, &sy); | ||||||
|  | 	if (!view) { | ||||||
|  | 		wlr_xcursor_manager_set_cursor_image( | ||||||
|  | 				server->cursor_mgr, "left_ptr", server->cursor); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (surface) { | ||||||
|  | 		bool focus_changed = seat->pointer_state.focused_surface != surface; | ||||||
|  | 		wlr_seat_pointer_notify_enter(seat, surface, sx, sy); | ||||||
|  | 		if (!focus_changed) { | ||||||
|  | 			wlr_seat_pointer_notify_motion(seat, time, sx, sy); | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		wlr_seat_pointer_clear_focus(seat); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void server_cursor_motion(struct wl_listener *listener, void *data) { | ||||||
|  | 	struct tinywl_server *server = | ||||||
|  | 		wl_container_of(listener, server, cursor_motion_absolute); | ||||||
|  | 	struct wlr_event_pointer_motion *event = data; | ||||||
|  | 	wlr_cursor_move(server->cursor, event->device, | ||||||
|  | 			event->delta_x, event->delta_y); | ||||||
|  | 	process_cursor_motion(server, event->time_msec); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void server_cursor_motion_absolute( | ||||||
|  | 		struct wl_listener *listener, void *data) { | ||||||
|  | 	struct tinywl_server *server = | ||||||
|  | 		wl_container_of(listener, server, cursor_motion_absolute); | ||||||
|  | 	struct wlr_event_pointer_motion_absolute *event = data; | ||||||
|  | 	wlr_cursor_warp_absolute(server->cursor, event->device, event->x, event->y); | ||||||
|  | 	process_cursor_motion(server, event->time_msec); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void server_cursor_button(struct wl_listener *listener, void *data) { | ||||||
|  | 	struct tinywl_server *server = | ||||||
|  | 		wl_container_of(listener, server, cursor_button); | ||||||
|  | 	struct wlr_event_pointer_button *event = data; | ||||||
|  | 	wlr_seat_pointer_notify_button(server->seat, | ||||||
|  | 			event->time_msec, event->button, event->state); | ||||||
|  | 	double sx, sy; | ||||||
|  | 	struct wlr_seat *seat = server->seat; | ||||||
|  | 	struct wlr_surface *surface; | ||||||
|  | 	struct tinywl_view *view = desktop_view_at(server, | ||||||
|  | 			server->cursor->x, server->cursor->y, &surface, &sx, &sy); | ||||||
|  | 	if (event->state == WLR_BUTTON_RELEASED) { | ||||||
|  | 		server->cursor_mode = TINYWL_CURSOR_PASSTHROUGH; | ||||||
|  | 	} else { | ||||||
|  | 		focus_view(view, surface); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void server_cursor_axis(struct wl_listener *listener, void *data) { | ||||||
|  | 	struct tinywl_server *server = | ||||||
|  | 		wl_container_of(listener, server, cursor_axis); | ||||||
|  | 	struct wlr_event_pointer_axis *event = data; | ||||||
|  | 	wlr_seat_pointer_notify_axis(server->seat, | ||||||
|  | 			event->time_msec, event->orientation, event->delta, | ||||||
|  | 			event->delta_discrete, event->source); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct render_data { | ||||||
|  | 	struct wlr_output *output; | ||||||
|  | 	struct wlr_renderer *renderer; | ||||||
|  | 	struct tinywl_view *view; | ||||||
|  | 	struct timespec *when; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void render_surface(struct wlr_surface *surface, | ||||||
|  | 		int sx, int sy, void *data) { | ||||||
|  | 	struct render_data *rdata = data; | ||||||
|  | 	struct tinywl_view *view = rdata->view; | ||||||
|  | 	struct wlr_output *output = rdata->output; | ||||||
|  | 
 | ||||||
|  | 	struct wlr_texture *texture = wlr_surface_get_texture(surface); | ||||||
|  | 	if (texture == NULL) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	double ox, oy; | ||||||
|  | 	wlr_output_layout_output_coords( | ||||||
|  | 			view->server->output_layout, output, &ox, &oy); | ||||||
|  | 	ox += view->x + sx, oy += view->y + sy; | ||||||
|  | 
 | ||||||
|  | 	struct wlr_box box = { | ||||||
|  | 		.x = ox * output->scale, | ||||||
|  | 		.y = oy * output->scale, | ||||||
|  | 		.width = surface->current.width * output->scale, | ||||||
|  | 		.height = surface->current.height * output->scale, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	float matrix[9]; | ||||||
|  | 	enum wl_output_transform transform = | ||||||
|  | 		wlr_output_transform_invert(surface->current.transform); | ||||||
|  | 	wlr_matrix_project_box(matrix, &box, transform, 0, | ||||||
|  | 		output->transform_matrix); | ||||||
|  | 
 | ||||||
|  | 	wlr_render_texture_with_matrix(rdata->renderer, texture, matrix, 1); | ||||||
|  | 
 | ||||||
|  | 	wlr_surface_send_frame_done(surface, rdata->when); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void output_frame(struct wl_listener *listener, void *data) { | ||||||
|  | 	struct tinywl_output *output = | ||||||
|  | 		wl_container_of(listener, output, frame); | ||||||
|  | 	struct wlr_renderer *renderer = output->server->renderer; | ||||||
|  | 
 | ||||||
|  | 	struct timespec now; | ||||||
|  | 	clock_gettime(CLOCK_MONOTONIC, &now); | ||||||
|  | 
 | ||||||
|  | 	wlr_output_make_current(output->wlr_output, NULL); | ||||||
|  | 	int width, height; | ||||||
|  | 	wlr_output_effective_resolution(output->wlr_output, &width, &height); | ||||||
|  | 	wlr_renderer_begin(renderer, width, height); | ||||||
|  | 
 | ||||||
|  | 	float color[4] = {0.3, 0.3, 0.3, 1.0}; | ||||||
|  | 	wlr_renderer_clear(renderer, color); | ||||||
|  | 
 | ||||||
|  | 	struct tinywl_view *view; | ||||||
|  | 	wl_list_for_each_reverse(view, &output->server->views, link) { | ||||||
|  | 		if (!view->mapped) { | ||||||
|  | 			continue; | ||||||
|  | 		} | ||||||
|  | 		struct render_data rdata = { | ||||||
|  | 			.output = output->wlr_output, | ||||||
|  | 			.view = view, | ||||||
|  | 			.renderer = renderer, | ||||||
|  | 			.when = &now, | ||||||
|  | 		}; | ||||||
|  | 		wlr_xdg_surface_for_each_surface(view->xdg_surface, | ||||||
|  | 				render_surface, &rdata); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	wlr_output_swap_buffers(output->wlr_output, NULL, NULL); | ||||||
|  | 	wlr_renderer_end(renderer); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void server_new_output(struct wl_listener *listener, void *data) { | ||||||
|  | 	struct tinywl_server *server = | ||||||
|  | 		wl_container_of(listener, server, new_output); | ||||||
|  | 	struct wlr_output *wlr_output = data; | ||||||
|  | 
 | ||||||
|  | 	struct tinywl_output *output = | ||||||
|  | 		calloc(1, sizeof(struct tinywl_output)); | ||||||
|  | 	output->wlr_output = wlr_output; | ||||||
|  | 	output->server = server; | ||||||
|  | 	output->frame.notify = output_frame; | ||||||
|  | 	wl_signal_add(&wlr_output->events.frame, &output->frame); | ||||||
|  | 	wl_list_insert(&server->outputs, &output->link); | ||||||
|  | 
 | ||||||
|  | 	wlr_output_layout_add_auto(server->output_layout, wlr_output); | ||||||
|  | 
 | ||||||
|  | 	wlr_output_create_global(wlr_output); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void xdg_surface_map(struct wl_listener *listener, void *data) { | ||||||
|  | 	struct tinywl_view *view = wl_container_of(listener, view, map); | ||||||
|  | 	view->mapped = true; | ||||||
|  | 	focus_view(view, view->xdg_surface->surface); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void xdg_surface_unmap(struct wl_listener *listener, void *data) { | ||||||
|  | 	struct tinywl_view *view = wl_container_of(listener, view, unmap); | ||||||
|  | 	view->mapped = false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void xdg_surface_destroy(struct wl_listener *listener, void *data) { | ||||||
|  | 	struct tinywl_view *view = wl_container_of(listener, view, destroy); | ||||||
|  | 	wl_list_remove(&view->link); | ||||||
|  | 	free(view); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void begin_interactive(struct tinywl_view *view, | ||||||
|  | 		enum tinywl_cursor_mode mode, uint32_t edges) { | ||||||
|  | 	struct tinywl_server *server = view->server; | ||||||
|  | 	struct wlr_surface *focused_surface = | ||||||
|  | 		server->seat->pointer_state.focused_surface; | ||||||
|  | 	if (view->xdg_surface->surface != focused_surface) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	server->grabbed_view = view; | ||||||
|  | 	server->cursor_mode = mode; | ||||||
|  | 	struct wlr_box geo_box; | ||||||
|  | 	wlr_xdg_surface_get_geometry(view->xdg_surface, &geo_box); | ||||||
|  | 	if (mode == TINYWL_CURSOR_MOVE) { | ||||||
|  | 		server->grab_x = server->cursor->x - view->x; | ||||||
|  | 		server->grab_y = server->cursor->y - view->y; | ||||||
|  | 	} else { | ||||||
|  | 		server->grab_x = server->cursor->x + geo_box.x; | ||||||
|  | 		server->grab_y = server->cursor->y + geo_box.y; | ||||||
|  | 	} | ||||||
|  | 	server->grab_width = geo_box.width; | ||||||
|  | 	server->grab_height = geo_box.height; | ||||||
|  | 	server->resize_edges = edges; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void xdg_toplevel_request_move( | ||||||
|  | 		struct wl_listener *listener, void *data) { | ||||||
|  | 	struct tinywl_view *view = wl_container_of(listener, view, request_move); | ||||||
|  | 	begin_interactive(view, TINYWL_CURSOR_MOVE, 0); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void xdg_toplevel_request_resize( | ||||||
|  | 		struct wl_listener *listener, void *data) { | ||||||
|  | 	struct wlr_xdg_toplevel_resize_event *event = data; | ||||||
|  | 	struct tinywl_view *view = wl_container_of(listener, view, request_resize); | ||||||
|  | 	begin_interactive(view, TINYWL_CURSOR_RESIZE, event->edges); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void server_new_xdg_surface(struct wl_listener *listener, void *data) { | ||||||
|  | 	struct tinywl_server *server = | ||||||
|  | 		wl_container_of(listener, server, new_xdg_surface); | ||||||
|  | 	struct wlr_xdg_surface *xdg_surface = data; | ||||||
|  | 	if (xdg_surface->role != WLR_XDG_SURFACE_ROLE_TOPLEVEL) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	struct tinywl_view *view = | ||||||
|  | 		calloc(1, sizeof(struct tinywl_view)); | ||||||
|  | 	view->server = server; | ||||||
|  | 	view->xdg_surface = xdg_surface; | ||||||
|  | 
 | ||||||
|  | 	view->map.notify = xdg_surface_map; | ||||||
|  | 	wl_signal_add(&xdg_surface->events.map, &view->map); | ||||||
|  | 	view->unmap.notify = xdg_surface_unmap; | ||||||
|  | 	wl_signal_add(&xdg_surface->events.unmap, &view->unmap); | ||||||
|  | 	view->destroy.notify = xdg_surface_destroy; | ||||||
|  | 	wl_signal_add(&xdg_surface->events.destroy, &view->destroy); | ||||||
|  | 
 | ||||||
|  | 	struct wlr_xdg_toplevel *toplevel = xdg_surface->toplevel; | ||||||
|  | 	view->request_move.notify = xdg_toplevel_request_move; | ||||||
|  | 	wl_signal_add(&toplevel->events.request_move, &view->request_move); | ||||||
|  | 	view->request_resize.notify = xdg_toplevel_request_resize; | ||||||
|  | 	wl_signal_add(&toplevel->events.request_resize, &view->request_resize); | ||||||
|  | 
 | ||||||
|  | 	wl_list_insert(&server->views, &view->link); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int main(int argc, char *argv[]) { | ||||||
|  | 	char *startup_cmd = NULL; | ||||||
|  | 
 | ||||||
|  | 	int c; | ||||||
|  | 	while ((c = getopt(argc, argv, "s:h")) != -1) { | ||||||
|  | 		switch (c) { | ||||||
|  | 		case 's': | ||||||
|  | 			startup_cmd = optarg; | ||||||
|  | 			break; | ||||||
|  | 		default: | ||||||
|  | 			printf("Usage: %s [-s startup command]\n", argv[0]); | ||||||
|  | 			return 0; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if (optind < argc) { | ||||||
|  | 		printf("Usage: %s [-s startup command]\n", argv[0]); | ||||||
|  | 		return 0; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	struct tinywl_server server; | ||||||
|  | 	server.wl_display = wl_display_create(); | ||||||
|  | 	server.backend = wlr_backend_autocreate(server.wl_display, NULL); | ||||||
|  | 
 | ||||||
|  | 	server.renderer = wlr_backend_get_renderer(server.backend); | ||||||
|  | 	wlr_renderer_init_wl_display(server.renderer, server.wl_display); | ||||||
|  | 
 | ||||||
|  | 	wlr_compositor_create(server.wl_display, server.renderer); | ||||||
|  | 	wlr_linux_dmabuf_v1_create(server.wl_display, server.renderer); | ||||||
|  | 	wlr_data_device_manager_create(server.wl_display); | ||||||
|  | 
 | ||||||
|  | 	server.output_layout = wlr_output_layout_create(); | ||||||
|  | 
 | ||||||
|  | 	wl_list_init(&server.outputs); | ||||||
|  | 	server.new_output.notify = server_new_output; | ||||||
|  | 	wl_signal_add(&server.backend->events.new_output, &server.new_output); | ||||||
|  | 
 | ||||||
|  | 	wl_list_init(&server.views); | ||||||
|  | 	server.xdg_shell = wlr_xdg_shell_create(server.wl_display); | ||||||
|  | 	server.new_xdg_surface.notify = server_new_xdg_surface; | ||||||
|  | 	wl_signal_add(&server.xdg_shell->events.new_surface, | ||||||
|  | 			&server.new_xdg_surface); | ||||||
|  | 
 | ||||||
|  | 	server.cursor = wlr_cursor_create(); | ||||||
|  | 	wlr_cursor_attach_output_layout(server.cursor, server.output_layout); | ||||||
|  | 
 | ||||||
|  | 	server.cursor_mgr = wlr_xcursor_manager_create(NULL, 24); | ||||||
|  | 	wlr_xcursor_manager_load(server.cursor_mgr, 1); | ||||||
|  | 
 | ||||||
|  | 	server.cursor_motion.notify = server_cursor_motion; | ||||||
|  | 	wl_signal_add(&server.cursor->events.motion, &server.cursor_motion); | ||||||
|  | 	server.cursor_motion_absolute.notify = server_cursor_motion_absolute; | ||||||
|  | 	wl_signal_add(&server.cursor->events.motion_absolute, | ||||||
|  | 			&server.cursor_motion_absolute); | ||||||
|  | 	server.cursor_button.notify = server_cursor_button; | ||||||
|  | 	wl_signal_add(&server.cursor->events.button, &server.cursor_button); | ||||||
|  | 	server.cursor_axis.notify = server_cursor_axis; | ||||||
|  | 	wl_signal_add(&server.cursor->events.axis, &server.cursor_axis); | ||||||
|  | 
 | ||||||
|  | 	wl_list_init(&server.keyboards); | ||||||
|  | 	server.new_input.notify = server_new_input; | ||||||
|  | 	wl_signal_add(&server.backend->events.new_input, &server.new_input); | ||||||
|  | 	server.seat = wlr_seat_create(server.wl_display, "seat0"); | ||||||
|  | 	server.request_cursor.notify = seat_request_cursor; | ||||||
|  | 	wl_signal_add(&server.seat->events.request_set_cursor, | ||||||
|  | 			&server.request_cursor); | ||||||
|  | 
 | ||||||
|  | 	const char *socket = wl_display_add_socket_auto(server.wl_display); | ||||||
|  | 	if (!socket) { | ||||||
|  | 		wlr_backend_destroy(server.backend); | ||||||
|  | 		return 1; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (!wlr_backend_start(server.backend)) { | ||||||
|  | 		wlr_backend_destroy(server.backend); | ||||||
|  | 		wl_display_destroy(server.wl_display); | ||||||
|  | 		return 1; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	setenv("WAYLAND_DISPLAY", socket, true); | ||||||
|  | 	if (startup_cmd) { | ||||||
|  | 		if (fork() == 0) { | ||||||
|  | 			execl("/bin/sh", "/bin/sh", "-c", startup_cmd, (void *)NULL); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	wl_display_run(server.wl_display); | ||||||
|  | 
 | ||||||
|  | 	wl_display_destroy_clients(server.wl_display); | ||||||
|  | 	wl_display_destroy(server.wl_display); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
					Loading…
					
					
				
		Reference in new issue