Merge pull request #786 from emersion/x11-backend-multiple-outputs
	
		
	
				
					
				
			backend/x11: add support for multiple outputsmaster
						commit
						96c6091f80
					
				| @ -0,0 +1,136 @@ | |||||||
|  | #include <wlr/interfaces/wlr_input_device.h> | ||||||
|  | #include <wlr/interfaces/wlr_keyboard.h> | ||||||
|  | #include <wlr/interfaces/wlr_pointer.h> | ||||||
|  | #include <wlr/util/log.h> | ||||||
|  | #include <xcb/xcb.h> | ||||||
|  | #ifdef __linux__ | ||||||
|  | #include <linux/input-event-codes.h> | ||||||
|  | #elif __FreeBSD__ | ||||||
|  | #include <dev/evdev/input-event-codes.h> | ||||||
|  | #endif | ||||||
|  | #include "backend/x11.h" | ||||||
|  | #include "util/signal.h" | ||||||
|  | 
 | ||||||
|  | static uint32_t xcb_button_to_wl(uint32_t button) { | ||||||
|  | 	switch (button) { | ||||||
|  | 	case XCB_BUTTON_INDEX_1: return BTN_LEFT; | ||||||
|  | 	case XCB_BUTTON_INDEX_2: return BTN_MIDDLE; | ||||||
|  | 	case XCB_BUTTON_INDEX_3: return BTN_RIGHT; | ||||||
|  | 	// XXX: I'm not sure the scroll-wheel direction is right
 | ||||||
|  | 	case XCB_BUTTON_INDEX_4: return BTN_GEAR_UP; | ||||||
|  | 	case XCB_BUTTON_INDEX_5: return BTN_GEAR_DOWN; | ||||||
|  | 	default: return 0; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool x11_handle_input_event(struct wlr_x11_backend *x11, | ||||||
|  | 		xcb_generic_event_t *event) { | ||||||
|  | 	switch (event->response_type & XCB_EVENT_RESPONSE_TYPE_MASK) { | ||||||
|  | 	case XCB_KEY_PRESS: | ||||||
|  | 	case XCB_KEY_RELEASE: { | ||||||
|  | 		xcb_key_press_event_t *ev = (xcb_key_press_event_t *)event; | ||||||
|  | 		struct wlr_event_keyboard_key key = { | ||||||
|  | 			.time_msec = ev->time, | ||||||
|  | 			.keycode = ev->detail - 8, | ||||||
|  | 			.state = event->response_type == XCB_KEY_PRESS ? | ||||||
|  | 				WLR_KEY_PRESSED : WLR_KEY_RELEASED, | ||||||
|  | 			.update_state = true, | ||||||
|  | 		}; | ||||||
|  | 
 | ||||||
|  | 		// TODO use xcb-xkb for more precise modifiers state?
 | ||||||
|  | 		wlr_keyboard_notify_key(&x11->keyboard, &key); | ||||||
|  | 		x11->time = ev->time; | ||||||
|  | 		return true; | ||||||
|  | 	} | ||||||
|  | 	case XCB_BUTTON_PRESS: { | ||||||
|  | 		xcb_button_press_event_t *ev = (xcb_button_press_event_t *)event; | ||||||
|  | 
 | ||||||
|  | 		if (ev->detail == XCB_BUTTON_INDEX_4 || | ||||||
|  | 				ev->detail == XCB_BUTTON_INDEX_5) { | ||||||
|  | 			double delta = (ev->detail == XCB_BUTTON_INDEX_4 ? -15 : 15); | ||||||
|  | 			struct wlr_event_pointer_axis axis = { | ||||||
|  | 				.device = &x11->pointer_dev, | ||||||
|  | 				.time_msec = ev->time, | ||||||
|  | 				.source = WLR_AXIS_SOURCE_WHEEL, | ||||||
|  | 				.orientation = WLR_AXIS_ORIENTATION_VERTICAL, | ||||||
|  | 				.delta = delta, | ||||||
|  | 			}; | ||||||
|  | 			wlr_signal_emit_safe(&x11->pointer.events.axis, &axis); | ||||||
|  | 			x11->time = ev->time; | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	/* fallthrough */ | ||||||
|  | 	case XCB_BUTTON_RELEASE: { | ||||||
|  | 		xcb_button_press_event_t *ev = (xcb_button_press_event_t *)event; | ||||||
|  | 
 | ||||||
|  | 		if (ev->detail != XCB_BUTTON_INDEX_4 && | ||||||
|  | 				ev->detail != XCB_BUTTON_INDEX_5) { | ||||||
|  | 			struct wlr_event_pointer_button button = { | ||||||
|  | 				.device = &x11->pointer_dev, | ||||||
|  | 				.time_msec = ev->time, | ||||||
|  | 				.button = xcb_button_to_wl(ev->detail), | ||||||
|  | 				.state = event->response_type == XCB_BUTTON_PRESS ? | ||||||
|  | 					WLR_BUTTON_PRESSED : WLR_BUTTON_RELEASED, | ||||||
|  | 			}; | ||||||
|  | 
 | ||||||
|  | 			wlr_signal_emit_safe(&x11->pointer.events.button, &button); | ||||||
|  | 		} | ||||||
|  | 		x11->time = ev->time; | ||||||
|  | 		return true; | ||||||
|  | 	} | ||||||
|  | 	case XCB_MOTION_NOTIFY: { | ||||||
|  | 		xcb_motion_notify_event_t *ev = (xcb_motion_notify_event_t *)event; | ||||||
|  | 
 | ||||||
|  | 		struct wlr_x11_output *output = | ||||||
|  | 			x11_output_from_window_id(x11, ev->event); | ||||||
|  | 		if (output == NULL) { | ||||||
|  | 			return false; | ||||||
|  | 		} | ||||||
|  | 		struct wlr_output *wlr_output = &output->wlr_output; | ||||||
|  | 
 | ||||||
|  | 		struct wlr_box box = { .x = ev->event_x, .y = ev->event_y }; | ||||||
|  | 		wlr_box_transform(&box, wlr_output->transform, wlr_output->width, | ||||||
|  | 			wlr_output->height, &box); | ||||||
|  | 		box.x /= wlr_output->scale; | ||||||
|  | 		box.y /= wlr_output->scale; | ||||||
|  | 
 | ||||||
|  | 		struct wlr_box layout_box; | ||||||
|  | 		x11_output_layout_get_box(x11, &layout_box); | ||||||
|  | 
 | ||||||
|  | 		double ox = wlr_output->lx / (double)layout_box.width; | ||||||
|  | 		double oy = wlr_output->ly / (double)layout_box.height; | ||||||
|  | 
 | ||||||
|  | 		struct wlr_event_pointer_motion_absolute wlr_event = { | ||||||
|  | 			.device = &x11->pointer_dev, | ||||||
|  | 			.time_msec = ev->time, | ||||||
|  | 			.x = box.x / (double)layout_box.width + ox, | ||||||
|  | 			.y = box.y / (double)layout_box.height + oy, | ||||||
|  | 		}; | ||||||
|  | 
 | ||||||
|  | 		wlr_signal_emit_safe(&x11->pointer.events.motion_absolute, &wlr_event); | ||||||
|  | 
 | ||||||
|  | 		x11->time = ev->time; | ||||||
|  | 		return true; | ||||||
|  | 	} | ||||||
|  | 	default: | ||||||
|  | #ifdef WLR_HAS_XCB_XKB | ||||||
|  | 		if (x11->xkb_supported && event->response_type == x11->xkb_base_event) { | ||||||
|  | 			xcb_xkb_state_notify_event_t *ev = | ||||||
|  | 				(xcb_xkb_state_notify_event_t *)event; | ||||||
|  | 			wlr_keyboard_notify_modifiers(&x11->keyboard, ev->baseMods, | ||||||
|  | 				ev->latchedMods, ev->lockedMods, ev->lockedGroup); | ||||||
|  | 			return true; | ||||||
|  | 		} | ||||||
|  | #endif | ||||||
|  | 		break; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const struct wlr_input_device_impl input_device_impl = { 0 }; | ||||||
|  | 
 | ||||||
|  | bool wlr_input_device_is_x11(struct wlr_input_device *wlr_dev) { | ||||||
|  | 	return wlr_dev->impl == &input_device_impl; | ||||||
|  | } | ||||||
| @ -0,0 +1,182 @@ | |||||||
|  | #include <assert.h> | ||||||
|  | #include <stdlib.h> | ||||||
|  | #include <wlr/interfaces/wlr_output.h> | ||||||
|  | #include <wlr/util/log.h> | ||||||
|  | #include "backend/x11.h" | ||||||
|  | #include "util/signal.h" | ||||||
|  | 
 | ||||||
|  | static int signal_frame(void *data) { | ||||||
|  | 	struct wlr_x11_output *output = data; | ||||||
|  | 	wlr_output_send_frame(&output->wlr_output); | ||||||
|  | 	wl_event_source_timer_update(output->frame_timer, output->frame_delay); | ||||||
|  | 	return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void parse_xcb_setup(struct wlr_output *output, xcb_connection_t *xcb_conn) { | ||||||
|  | 	const xcb_setup_t *xcb_setup = xcb_get_setup(xcb_conn); | ||||||
|  | 
 | ||||||
|  | 	snprintf(output->make, sizeof(output->make), "%.*s", | ||||||
|  | 			xcb_setup_vendor_length(xcb_setup), | ||||||
|  | 			xcb_setup_vendor(xcb_setup)); | ||||||
|  | 	snprintf(output->model, sizeof(output->model), "%"PRIu16".%"PRIu16, | ||||||
|  | 			xcb_setup->protocol_major_version, | ||||||
|  | 			xcb_setup->protocol_minor_version); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool output_set_custom_mode(struct wlr_output *wlr_output, int32_t width, | ||||||
|  | 		int32_t height, int32_t refresh) { | ||||||
|  | 	struct wlr_x11_output *output = (struct wlr_x11_output *)wlr_output; | ||||||
|  | 	struct wlr_x11_backend *x11 = output->x11; | ||||||
|  | 
 | ||||||
|  | 	wlr_output_update_custom_mode(&output->wlr_output, wlr_output->width, | ||||||
|  | 		wlr_output->height, refresh); | ||||||
|  | 	output->frame_delay = 1000000 / refresh; | ||||||
|  | 
 | ||||||
|  | 	const uint32_t values[] = { width, height }; | ||||||
|  | 	xcb_configure_window(x11->xcb_conn, output->win, | ||||||
|  | 		XCB_CONFIG_WINDOW_WIDTH | XCB_CONFIG_WINDOW_HEIGHT, values); | ||||||
|  | 	return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void output_transform(struct wlr_output *wlr_output, | ||||||
|  | 		enum wl_output_transform transform) { | ||||||
|  | 	struct wlr_x11_output *output = (struct wlr_x11_output *)wlr_output; | ||||||
|  | 	output->wlr_output.transform = transform; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void output_destroy(struct wlr_output *wlr_output) { | ||||||
|  | 	struct wlr_x11_output *output = (struct wlr_x11_output *)wlr_output; | ||||||
|  | 	struct wlr_x11_backend *x11 = output->x11; | ||||||
|  | 
 | ||||||
|  | 	wl_list_remove(&output->link); | ||||||
|  | 	wl_event_source_remove(output->frame_timer); | ||||||
|  | 	eglDestroySurface(x11->egl.display, output->surf); | ||||||
|  | 	xcb_destroy_window(x11->xcb_conn, output->win); | ||||||
|  | 	free(output); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool output_make_current(struct wlr_output *wlr_output, int *buffer_age) { | ||||||
|  | 	struct wlr_x11_output *output = (struct wlr_x11_output *)wlr_output; | ||||||
|  | 	struct wlr_x11_backend *x11 = output->x11; | ||||||
|  | 
 | ||||||
|  | 	return wlr_egl_make_current(&x11->egl, output->surf, buffer_age); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool output_swap_buffers(struct wlr_output *wlr_output, | ||||||
|  | 		pixman_region32_t *damage) { | ||||||
|  | 	struct wlr_x11_output *output = (struct wlr_x11_output *)wlr_output; | ||||||
|  | 	struct wlr_x11_backend *x11 = output->x11; | ||||||
|  | 
 | ||||||
|  | 	return wlr_egl_swap_buffers(&x11->egl, output->surf, damage); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const struct wlr_output_impl output_impl = { | ||||||
|  | 	.set_custom_mode = output_set_custom_mode, | ||||||
|  | 	.transform = output_transform, | ||||||
|  | 	.destroy = output_destroy, | ||||||
|  | 	.make_current = output_make_current, | ||||||
|  | 	.swap_buffers = output_swap_buffers, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct wlr_output *wlr_x11_output_create(struct wlr_backend *backend) { | ||||||
|  | 	assert(wlr_backend_is_x11(backend)); | ||||||
|  | 	struct wlr_x11_backend *x11 = (struct wlr_x11_backend *)backend; | ||||||
|  | 
 | ||||||
|  | 	if (!x11->started) { | ||||||
|  | 		++x11->requested_outputs; | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	struct wlr_x11_output *output = calloc(1, sizeof(struct wlr_x11_output)); | ||||||
|  | 	if (output == NULL) { | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  | 	output->x11 = x11; | ||||||
|  | 
 | ||||||
|  | 	struct wlr_output *wlr_output = &output->wlr_output; | ||||||
|  | 	wlr_output_init(wlr_output, &x11->backend, &output_impl, x11->wl_display); | ||||||
|  | 
 | ||||||
|  | 	wlr_output->refresh = 60 * 1000000; | ||||||
|  | 	output->frame_delay = 16; // 60 Hz
 | ||||||
|  | 
 | ||||||
|  | 	snprintf(wlr_output->name, sizeof(wlr_output->name), "X11-%d", | ||||||
|  | 		wl_list_length(&x11->outputs) + 1); | ||||||
|  | 	parse_xcb_setup(wlr_output, x11->xcb_conn); | ||||||
|  | 
 | ||||||
|  | 	uint32_t mask = XCB_CW_BACK_PIXEL | XCB_CW_EVENT_MASK; | ||||||
|  | 	uint32_t values[2] = { | ||||||
|  | 		x11->screen->white_pixel, | ||||||
|  | 		XCB_EVENT_MASK_EXPOSURE | | ||||||
|  | 		XCB_EVENT_MASK_KEY_PRESS | XCB_EVENT_MASK_KEY_RELEASE | | ||||||
|  | 		XCB_EVENT_MASK_BUTTON_PRESS | XCB_EVENT_MASK_BUTTON_RELEASE | | ||||||
|  | 		XCB_EVENT_MASK_POINTER_MOTION | | ||||||
|  | 		XCB_EVENT_MASK_STRUCTURE_NOTIFY | ||||||
|  | 	}; | ||||||
|  | 	output->win = xcb_generate_id(x11->xcb_conn); | ||||||
|  | 	xcb_create_window(x11->xcb_conn, XCB_COPY_FROM_PARENT, output->win, | ||||||
|  | 		x11->screen->root, 0, 0, 1024, 768, 1, XCB_WINDOW_CLASS_INPUT_OUTPUT, | ||||||
|  | 		x11->screen->root_visual, mask, values); | ||||||
|  | 
 | ||||||
|  | 	output->surf = wlr_egl_create_surface(&x11->egl, &output->win); | ||||||
|  | 	if (!output->surf) { | ||||||
|  | 		wlr_log(L_ERROR, "Failed to create EGL surface"); | ||||||
|  | 		free(output); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	xcb_change_property(x11->xcb_conn, XCB_PROP_MODE_REPLACE, output->win, | ||||||
|  | 		x11->atoms.wm_protocols, XCB_ATOM_ATOM, 32, 1, | ||||||
|  | 		&x11->atoms.wm_delete_window); | ||||||
|  | 
 | ||||||
|  | 	char title[32]; | ||||||
|  | 	if (snprintf(title, sizeof(title), "wlroots - %s", wlr_output->name)) { | ||||||
|  | 		xcb_change_property(x11->xcb_conn, XCB_PROP_MODE_REPLACE, output->win, | ||||||
|  | 			x11->atoms.net_wm_name, x11->atoms.utf8_string, 8, | ||||||
|  | 			strlen(title), title); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	xcb_map_window(x11->xcb_conn, output->win); | ||||||
|  | 	xcb_flush(x11->xcb_conn); | ||||||
|  | 
 | ||||||
|  | 	struct wl_event_loop *ev = wl_display_get_event_loop(x11->wl_display); | ||||||
|  | 	output->frame_timer = wl_event_loop_add_timer(ev, signal_frame, output); | ||||||
|  | 
 | ||||||
|  | 	wl_event_source_timer_update(output->frame_timer, output->frame_delay); | ||||||
|  | 	wlr_output_update_enabled(wlr_output, true); | ||||||
|  | 
 | ||||||
|  | 	wl_list_insert(&x11->outputs, &output->link); | ||||||
|  | 	wlr_signal_emit_safe(&x11->backend.events.new_output, wlr_output); | ||||||
|  | 
 | ||||||
|  | 	return wlr_output; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void x11_output_handle_configure_notify(struct wlr_x11_output *output, | ||||||
|  | 		xcb_configure_notify_event_t *ev) { | ||||||
|  | 	struct wlr_x11_backend *x11 = output->x11; | ||||||
|  | 
 | ||||||
|  | 	wlr_output_update_custom_mode(&output->wlr_output, ev->width, | ||||||
|  | 		ev->height, output->wlr_output.refresh); | ||||||
|  | 
 | ||||||
|  | 	// Move the pointer to its new location
 | ||||||
|  | 	xcb_query_pointer_cookie_t cookie = | ||||||
|  | 		xcb_query_pointer(x11->xcb_conn, output->win); | ||||||
|  | 	xcb_query_pointer_reply_t *pointer = | ||||||
|  | 		xcb_query_pointer_reply(x11->xcb_conn, cookie, NULL); | ||||||
|  | 	if (!pointer) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	struct wlr_event_pointer_motion_absolute abs = { | ||||||
|  | 		.device = &x11->pointer_dev, | ||||||
|  | 		.time_msec = x11->time, | ||||||
|  | 		.x = (double)pointer->root_x / output->wlr_output.width, | ||||||
|  | 		.y = (double)pointer->root_y / output->wlr_output.height, | ||||||
|  | 	}; | ||||||
|  | 
 | ||||||
|  | 	wlr_signal_emit_safe(&x11->pointer.events.motion_absolute, &abs); | ||||||
|  | 	free(pointer); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool wlr_output_is_x11(struct wlr_output *wlr_output) { | ||||||
|  | 	return wlr_output->impl == &output_impl; | ||||||
|  | } | ||||||
					Loading…
					
					
				
		Reference in new issue