output: take a wlr_buffer in set_cursor
Instead of passing a wlr_texture to the backend, directly pass a wlr_buffer. Use get_cursor_size and get_cursor_formats to create a wlr_buffer that can be used as a cursor. We don't want to pass a wlr_texture because we want to remove as many rendering bits from the backend as possible.
This commit is contained in:
		
							parent
							
								
									01e0f51fad
								
							
						
					
					
						commit
						2b0a1aeed5
					
				|  | @ -25,7 +25,9 @@ | |||
| #include "backend/drm/iface.h" | ||||
| #include "backend/drm/util.h" | ||||
| #include "render/pixel_format.h" | ||||
| #include "render/drm_format_set.h" | ||||
| #include "render/swapchain.h" | ||||
| #include "render/wlr_renderer.h" | ||||
| #include "types/wlr_buffer.h" | ||||
| #include "util/signal.h" | ||||
| 
 | ||||
|  | @ -329,7 +331,7 @@ static bool drm_connector_attach_render(struct wlr_output *output, | |||
| static void drm_plane_set_committed(struct wlr_drm_plane *plane) { | ||||
| 	drm_fb_move(&plane->queued_fb, &plane->pending_fb); | ||||
| 
 | ||||
| 	if (plane->queued_fb) { | ||||
| 	if (plane->queued_fb && plane->surf.swapchain) { | ||||
| 		wlr_swapchain_set_buffer_submitted(plane->surf.swapchain, | ||||
| 			plane->queued_fb->wlr_buf); | ||||
| 	} | ||||
|  | @ -834,9 +836,7 @@ struct wlr_output_mode *wlr_drm_connector_add_mode(struct wlr_output *output, | |||
| } | ||||
| 
 | ||||
| static bool drm_connector_set_cursor(struct wlr_output *output, | ||||
| 		struct wlr_texture *texture, float scale, | ||||
| 		enum wl_output_transform transform, | ||||
| 		int32_t hotspot_x, int32_t hotspot_y, bool update_texture) { | ||||
| 		struct wlr_buffer *buffer, int hotspot_x, int hotspot_y) { | ||||
| 	struct wlr_drm_connector *conn = get_drm_connector_from_output(output); | ||||
| 	struct wlr_drm_backend *drm = conn->backend; | ||||
| 	struct wlr_drm_crtc *crtc = conn->crtc; | ||||
|  | @ -850,90 +850,59 @@ static bool drm_connector_set_cursor(struct wlr_output *output, | |||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!plane->surf.swapchain) { | ||||
| 		int ret; | ||||
| 		uint64_t w, h; | ||||
| 		ret = drmGetCap(drm->fd, DRM_CAP_CURSOR_WIDTH, &w); | ||||
| 		w = ret ? 64 : w; | ||||
| 		ret = drmGetCap(drm->fd, DRM_CAP_CURSOR_HEIGHT, &h); | ||||
| 		h = ret ? 64 : h; | ||||
| 
 | ||||
| 		if (!drm_plane_init_surface(plane, drm, w, h, true)) { | ||||
| 			wlr_drm_conn_log(conn, WLR_ERROR, "Cannot allocate cursor resources"); | ||||
| 			return false; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	struct wlr_box hotspot = { .x = hotspot_x, .y = hotspot_y }; | ||||
| 	wlr_box_transform(&hotspot, &hotspot, | ||||
| 		wlr_output_transform_invert(output->transform), | ||||
| 		plane->surf.width, plane->surf.height); | ||||
| 
 | ||||
| 	if (plane->cursor_hotspot_x != hotspot.x || | ||||
| 			plane->cursor_hotspot_y != hotspot.y) { | ||||
| 	if (plane->cursor_hotspot_x != hotspot_x || | ||||
| 			plane->cursor_hotspot_y != hotspot_y) { | ||||
| 		// Update cursor hotspot
 | ||||
| 		conn->cursor_x -= hotspot.x - plane->cursor_hotspot_x; | ||||
| 		conn->cursor_y -= hotspot.y - plane->cursor_hotspot_y; | ||||
| 		plane->cursor_hotspot_x = hotspot.x; | ||||
| 		plane->cursor_hotspot_y = hotspot.y; | ||||
| 		conn->cursor_x -= hotspot_x - plane->cursor_hotspot_x; | ||||
| 		conn->cursor_y -= hotspot_y - plane->cursor_hotspot_y; | ||||
| 		plane->cursor_hotspot_x = hotspot_x; | ||||
| 		plane->cursor_hotspot_y = hotspot_y; | ||||
| 
 | ||||
| 		wlr_output_update_needs_frame(output); | ||||
| 	} | ||||
| 
 | ||||
| 	if (!update_texture) { | ||||
| 		// Don't update cursor image
 | ||||
| 		return true; | ||||
| 	} | ||||
| 
 | ||||
| 	plane->cursor_enabled = false; | ||||
| 	if (texture != NULL) { | ||||
| 		int width = texture->width * output->scale / scale; | ||||
| 		int height = texture->height * output->scale / scale; | ||||
| 
 | ||||
| 		if (width > (int)plane->surf.width || height > (int)plane->surf.height) { | ||||
| 			wlr_drm_conn_log(conn, WLR_ERROR, "Cursor too large (max %dx%d)", | ||||
| 				(int)plane->surf.width, (int)plane->surf.height); | ||||
| 	if (buffer != NULL) { | ||||
| 		if ((uint64_t)buffer->width != drm->cursor_width || | ||||
| 				(uint64_t)buffer->height != drm->cursor_height) { | ||||
| 			wlr_drm_conn_log(conn, WLR_DEBUG, "Cursor buffer size mismatch"); | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		if (!drm_surface_make_current(&plane->surf, NULL)) { | ||||
| 			return false; | ||||
| 		struct wlr_buffer *local_buf; | ||||
| 		if (drm->parent) { | ||||
| 			struct wlr_drm_format *format = | ||||
| 				drm_plane_pick_render_format(plane, &drm->renderer); | ||||
| 			if (format == NULL) { | ||||
| 				wlr_log(WLR_ERROR, "Failed to pick cursor plane format"); | ||||
| 				return false; | ||||
| 			} | ||||
| 
 | ||||
| 			bool ok = init_drm_surface(&plane->mgpu_surf, &drm->renderer, | ||||
| 				buffer->width, buffer->height, format); | ||||
| 			free(format); | ||||
| 			if (!ok) { | ||||
| 				return false; | ||||
| 			} | ||||
| 
 | ||||
| 			local_buf = drm_surface_blit(&plane->mgpu_surf, buffer); | ||||
| 			if (local_buf == NULL) { | ||||
| 				return false; | ||||
| 			} | ||||
| 		} else { | ||||
| 			local_buf = wlr_buffer_lock(buffer); | ||||
| 		} | ||||
| 
 | ||||
| 		struct wlr_renderer *rend = plane->surf.renderer->wlr_rend; | ||||
| 
 | ||||
| 		struct wlr_box cursor_box = { .width = width, .height = height }; | ||||
| 
 | ||||
| 		float output_matrix[9]; | ||||
| 		wlr_matrix_identity(output_matrix); | ||||
| 		if (output->transform != WL_OUTPUT_TRANSFORM_NORMAL) { | ||||
| 			struct wlr_box tr_size = { | ||||
| 				.width = plane->surf.width, | ||||
| 				.height = plane->surf.height, | ||||
| 			}; | ||||
| 			wlr_box_transform(&tr_size, &tr_size, output->transform, 0, 0); | ||||
| 
 | ||||
| 			wlr_matrix_translate(output_matrix, plane->surf.width / 2.0, | ||||
| 					plane->surf.height / 2.0); | ||||
| 			wlr_matrix_transform(output_matrix, output->transform); | ||||
| 			wlr_matrix_translate(output_matrix, - tr_size.width / 2.0, | ||||
| 					- tr_size.height / 2.0); | ||||
| 		} | ||||
| 
 | ||||
| 		float matrix[9]; | ||||
| 		wlr_matrix_project_box(matrix, &cursor_box, transform, 0, | ||||
| 				output_matrix); | ||||
| 
 | ||||
| 		wlr_renderer_begin(rend, plane->surf.width, plane->surf.height); | ||||
| 		wlr_renderer_clear(rend, (float[]){ 0.0, 0.0, 0.0, 0.0 }); | ||||
| 		wlr_render_texture_with_matrix(rend, texture, matrix, 1.0); | ||||
| 		wlr_renderer_end(rend); | ||||
| 
 | ||||
| 		if (!drm_plane_lock_surface(plane, drm)) { | ||||
| 		bool ok = drm_fb_import(&plane->pending_fb, drm, local_buf, | ||||
| 			&plane->formats); | ||||
| 		wlr_buffer_unlock(local_buf); | ||||
| 		if (!ok) { | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		plane->cursor_enabled = true; | ||||
| 		plane->cursor_width = buffer->width; | ||||
| 		plane->cursor_height = buffer->height; | ||||
| 	} | ||||
| 
 | ||||
| 	wlr_output_update_needs_frame(output); | ||||
|  | @ -977,8 +946,8 @@ bool drm_connector_is_cursor_visible(struct wlr_drm_connector *conn) { | |||
| 	return plane->cursor_enabled && | ||||
| 		conn->cursor_x < conn->output.width && | ||||
| 		conn->cursor_y < conn->output.height && | ||||
| 		conn->cursor_x + (int)plane->surf.width >= 0 && | ||||
| 		conn->cursor_y + (int)plane->surf.height >= 0; | ||||
| 		conn->cursor_x + plane->cursor_width >= 0 && | ||||
| 		conn->cursor_y + plane->cursor_height >= 0; | ||||
| } | ||||
| 
 | ||||
| static void dealloc_crtc(struct wlr_drm_connector *conn); | ||||
|  |  | |||
|  | @ -62,7 +62,7 @@ void finish_drm_renderer(struct wlr_drm_renderer *renderer) { | |||
| 	gbm_device_destroy(renderer->gbm); | ||||
| } | ||||
| 
 | ||||
| static bool init_drm_surface(struct wlr_drm_surface *surf, | ||||
| bool init_drm_surface(struct wlr_drm_surface *surf, | ||||
| 		struct wlr_drm_renderer *renderer, uint32_t width, uint32_t height, | ||||
| 		const struct wlr_drm_format *drm_format) { | ||||
| 	if (surf->width == width && surf->height == height) { | ||||
|  | @ -126,7 +126,7 @@ void drm_surface_unset_current(struct wlr_drm_surface *surf) { | |||
| 	surf->back_buffer = NULL; | ||||
| } | ||||
| 
 | ||||
| static struct wlr_buffer *drm_surface_blit(struct wlr_drm_surface *surf, | ||||
| struct wlr_buffer *drm_surface_blit(struct wlr_drm_surface *surf, | ||||
| 		struct wlr_buffer *buffer) { | ||||
| 	struct wlr_renderer *renderer = surf->renderer->wlr_rend; | ||||
| 
 | ||||
|  | @ -191,7 +191,7 @@ static struct wlr_drm_format *create_linear_format(uint32_t format) { | |||
| 	return fmt; | ||||
| } | ||||
| 
 | ||||
| static struct wlr_drm_format *drm_plane_pick_render_format( | ||||
| struct wlr_drm_format *drm_plane_pick_render_format( | ||||
| 		struct wlr_drm_plane *plane, struct wlr_drm_renderer *renderer) { | ||||
| 	const struct wlr_drm_format_set *render_formats = | ||||
| 		wlr_renderer_get_render_formats(renderer->wlr_rend); | ||||
|  |  | |||
|  | @ -406,28 +406,12 @@ static void output_rollback_render(struct wlr_output *wlr_output) { | |||
| } | ||||
| 
 | ||||
| static bool output_set_cursor(struct wlr_output *wlr_output, | ||||
| 		struct wlr_texture *texture, float scale, | ||||
| 		enum wl_output_transform transform, | ||||
| 		int32_t hotspot_x, int32_t hotspot_y, bool update_texture) { | ||||
| 		struct wlr_buffer *wlr_buffer, int hotspot_x, int hotspot_y) { | ||||
| 	struct wlr_wl_output *output = get_wl_output_from_output(wlr_output); | ||||
| 	struct wlr_wl_backend *backend = output->backend; | ||||
| 	struct wlr_renderer *renderer = wlr_backend_get_renderer(&backend->backend); | ||||
| 	struct wlr_allocator *allocator = backend_get_allocator(&backend->backend); | ||||
| 
 | ||||
| 	struct wlr_box hotspot = { .x = hotspot_x, .y = hotspot_y }; | ||||
| 	wlr_box_transform(&hotspot, &hotspot, | ||||
| 		wlr_output_transform_invert(wlr_output->transform), | ||||
| 		output->cursor.width, output->cursor.height); | ||||
| 
 | ||||
| 	// TODO: use output->wlr_output.transform to transform pixels and hotpot
 | ||||
| 	output->cursor.hotspot_x = hotspot.x; | ||||
| 	output->cursor.hotspot_y = hotspot.y; | ||||
| 
 | ||||
| 	if (!update_texture) { | ||||
| 		// Update hotspot without changing cursor image
 | ||||
| 		update_wl_output_cursor(output); | ||||
| 		return true; | ||||
| 	} | ||||
| 	output->cursor.hotspot_x = hotspot_x; | ||||
| 	output->cursor.hotspot_y = hotspot_y; | ||||
| 
 | ||||
| 	if (output->cursor.surface == NULL) { | ||||
| 		output->cursor.surface = | ||||
|  | @ -435,58 +419,7 @@ static bool output_set_cursor(struct wlr_output *wlr_output, | |||
| 	} | ||||
| 	struct wl_surface *surface = output->cursor.surface; | ||||
| 
 | ||||
| 	if (texture != NULL) { | ||||
| 		int width = texture->width * wlr_output->scale / scale; | ||||
| 		int height = texture->height * wlr_output->scale / scale; | ||||
| 
 | ||||
| 		if (output->cursor.swapchain == NULL || | ||||
| 				output->cursor.swapchain->width != width || | ||||
| 				output->cursor.swapchain->height != height) { | ||||
| 			wlr_swapchain_destroy(output->cursor.swapchain); | ||||
| 			output->cursor.swapchain = wlr_swapchain_create(allocator, | ||||
| 				width, height, output->backend->format); | ||||
| 			if (output->cursor.swapchain == NULL) { | ||||
| 				return false; | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
| 		struct wlr_buffer *wlr_buffer = | ||||
| 			wlr_swapchain_acquire(output->cursor.swapchain, NULL); | ||||
| 		if (wlr_buffer == NULL) { | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		if (!wlr_renderer_bind_buffer(renderer, wlr_buffer)) { | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		struct wlr_box cursor_box = { | ||||
| 			.width = width, | ||||
| 			.height = height, | ||||
| 		}; | ||||
| 
 | ||||
| 		float output_matrix[9]; | ||||
| 		wlr_matrix_identity(output_matrix); | ||||
| 		if (wlr_output->transform != WL_OUTPUT_TRANSFORM_NORMAL) { | ||||
| 			struct wlr_box tr_size = { .width = width, .height = height }; | ||||
| 			wlr_box_transform(&tr_size, &tr_size, wlr_output->transform, 0, 0); | ||||
| 
 | ||||
| 			wlr_matrix_translate(output_matrix, width / 2.0, height / 2.0); | ||||
| 			wlr_matrix_transform(output_matrix, wlr_output->transform); | ||||
| 			wlr_matrix_translate(output_matrix, | ||||
| 				- tr_size.width / 2.0, - tr_size.height / 2.0); | ||||
| 		} | ||||
| 
 | ||||
| 		float matrix[9]; | ||||
| 		wlr_matrix_project_box(matrix, &cursor_box, transform, 0, output_matrix); | ||||
| 
 | ||||
| 		wlr_renderer_begin(renderer, width, height); | ||||
| 		wlr_renderer_clear(renderer, (float[]){ 0.0, 0.0, 0.0, 0.0 }); | ||||
| 		wlr_render_texture_with_matrix(renderer, texture, matrix, 1.0); | ||||
| 		wlr_renderer_end(renderer); | ||||
| 
 | ||||
| 		wlr_renderer_bind_buffer(renderer, NULL); | ||||
| 
 | ||||
| 	if (wlr_buffer != NULL) { | ||||
| 		struct wlr_wl_buffer *buffer = | ||||
| 			get_or_create_wl_buffer(output->backend, wlr_buffer); | ||||
| 		if (buffer == NULL) { | ||||
|  | @ -496,11 +429,6 @@ static bool output_set_cursor(struct wlr_output *wlr_output, | |||
| 		wl_surface_attach(surface, buffer->wl_buffer, 0, 0); | ||||
| 		wl_surface_damage_buffer(surface, 0, 0, INT32_MAX, INT32_MAX); | ||||
| 		wl_surface_commit(surface); | ||||
| 
 | ||||
| 		wlr_buffer_unlock(wlr_buffer); | ||||
| 
 | ||||
| 		output->cursor.width = width; | ||||
| 		output->cursor.height = height; | ||||
| 	} else { | ||||
| 		wl_surface_attach(surface, NULL, 0, 0); | ||||
| 		wl_surface_commit(surface); | ||||
|  | @ -530,7 +458,6 @@ static void output_destroy(struct wlr_output *wlr_output) { | |||
| 
 | ||||
| 	wl_list_remove(&output->link); | ||||
| 
 | ||||
| 	wlr_swapchain_destroy(output->cursor.swapchain); | ||||
| 	if (output->cursor.surface) { | ||||
| 		wl_surface_destroy(output->cursor.surface); | ||||
| 	} | ||||
|  |  | |||
|  | @ -420,83 +420,38 @@ static void update_x11_output_cursor(struct wlr_x11_output *output, | |||
| } | ||||
| 
 | ||||
| static bool output_cursor_to_picture(struct wlr_x11_output *output, | ||||
| 		struct wlr_texture *texture, enum wl_output_transform transform, | ||||
| 		int width, int height) { | ||||
| 		struct wlr_buffer *buffer) { | ||||
| 	struct wlr_x11_backend *x11 = output->x11; | ||||
| 	struct wlr_allocator *allocator = backend_get_allocator(&x11->backend); | ||||
| 	struct wlr_renderer *renderer = wlr_backend_get_renderer(&x11->backend); | ||||
| 	int depth = 32; | ||||
| 	int stride = width * 4; | ||||
| 
 | ||||
| 	if (output->cursor.pic != XCB_NONE) { | ||||
| 		xcb_render_free_picture(x11->xcb, output->cursor.pic); | ||||
| 	} | ||||
| 	output->cursor.pic = XCB_NONE; | ||||
| 
 | ||||
| 	if (texture == NULL) { | ||||
| 	if (buffer == NULL) { | ||||
| 		return true; | ||||
| 	} | ||||
| 
 | ||||
| 	if (output->cursor.swapchain == NULL || | ||||
| 			output->cursor.swapchain->width != width || | ||||
| 			output->cursor.swapchain->height != height) { | ||||
| 		wlr_swapchain_destroy(output->cursor.swapchain); | ||||
| 		output->cursor.swapchain = wlr_swapchain_create(allocator, | ||||
| 			width, height, x11->drm_format); | ||||
| 		if (output->cursor.swapchain == NULL) { | ||||
| 			return false; | ||||
| 		} | ||||
| 	} | ||||
| 	int depth = 32; | ||||
| 	int stride = buffer->width * 4; | ||||
| 
 | ||||
| 	struct wlr_buffer *wlr_buffer = | ||||
| 		wlr_swapchain_acquire(output->cursor.swapchain, NULL); | ||||
| 	if (wlr_buffer == NULL) { | ||||
| 	if (!wlr_renderer_bind_buffer(renderer, buffer)) { | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!wlr_renderer_bind_buffer(renderer, wlr_buffer)) { | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	uint8_t *data = malloc(width * height * 4); | ||||
| 	uint8_t *data = malloc(buffer->height * stride); | ||||
| 	if (data == NULL) { | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	struct wlr_box cursor_box = { | ||||
| 		.width = width, | ||||
| 		.height = height, | ||||
| 	}; | ||||
| 
 | ||||
| 	float output_matrix[9]; | ||||
| 	wlr_matrix_identity(output_matrix); | ||||
| 	if (output->wlr_output.transform != WL_OUTPUT_TRANSFORM_NORMAL) { | ||||
| 		struct wlr_box tr_size = { .width = width, .height = height }; | ||||
| 		wlr_box_transform(&tr_size, &tr_size, output->wlr_output.transform, 0, 0); | ||||
| 
 | ||||
| 		wlr_matrix_translate(output_matrix, width / 2.0, height / 2.0); | ||||
| 		wlr_matrix_transform(output_matrix, output->wlr_output.transform); | ||||
| 		wlr_matrix_translate(output_matrix, | ||||
| 			- tr_size.width / 2.0, - tr_size.height / 2.0); | ||||
| 	} | ||||
| 
 | ||||
| 	float matrix[9]; | ||||
| 	wlr_matrix_project_box(matrix, &cursor_box, transform, 0, output_matrix); | ||||
| 
 | ||||
| 	wlr_renderer_begin(renderer, width, height); | ||||
| 	wlr_renderer_clear(renderer, (float[]){ 0.0, 0.0, 0.0, 0.0 }); | ||||
| 	wlr_render_texture_with_matrix(renderer, texture, matrix, 1.0); | ||||
| 	wlr_renderer_end(renderer); | ||||
| 
 | ||||
| 	bool result = wlr_renderer_read_pixels( | ||||
| 		renderer, DRM_FORMAT_ARGB8888, NULL, | ||||
| 		width * 4, width, height, 0, 0, 0, 0, | ||||
| 		stride, buffer->width, buffer->height, 0, 0, 0, 0, | ||||
| 		data); | ||||
| 
 | ||||
| 	wlr_renderer_bind_buffer(renderer, NULL); | ||||
| 
 | ||||
| 	wlr_buffer_unlock(wlr_buffer); | ||||
| 
 | ||||
| 	if (!result) { | ||||
| 		free(data); | ||||
| 		return false; | ||||
|  | @ -504,7 +459,7 @@ static bool output_cursor_to_picture(struct wlr_x11_output *output, | |||
| 
 | ||||
| 	xcb_pixmap_t pix = xcb_generate_id(x11->xcb); | ||||
| 	xcb_create_pixmap(x11->xcb, depth, pix, output->win, | ||||
| 		width, height); | ||||
| 		buffer->width, buffer->height); | ||||
| 
 | ||||
| 	output->cursor.pic = xcb_generate_id(x11->xcb); | ||||
| 	xcb_render_create_picture(x11->xcb, output->cursor.pic, | ||||
|  | @ -514,9 +469,8 @@ static bool output_cursor_to_picture(struct wlr_x11_output *output, | |||
| 	xcb_create_gc(x11->xcb, gc, pix, 0, NULL); | ||||
| 
 | ||||
| 	xcb_put_image(x11->xcb, XCB_IMAGE_FORMAT_Z_PIXMAP, | ||||
| 		pix, gc, width, height, 0, 0, 0, depth, | ||||
| 		stride * height * sizeof(uint8_t), | ||||
| 		data); | ||||
| 		pix, gc, buffer->width, buffer->height, 0, 0, 0, depth, | ||||
| 		stride * buffer->height * sizeof(uint8_t), data); | ||||
| 	free(data); | ||||
| 	xcb_free_gc(x11->xcb, gc); | ||||
| 	xcb_free_pixmap(x11->xcb, pix); | ||||
|  | @ -525,55 +479,32 @@ static bool output_cursor_to_picture(struct wlr_x11_output *output, | |||
| } | ||||
| 
 | ||||
| static bool output_set_cursor(struct wlr_output *wlr_output, | ||||
| 		struct wlr_texture *texture, float scale, | ||||
| 		enum wl_output_transform transform, | ||||
| 		int32_t hotspot_x, int32_t hotspot_y, bool update_texture) { | ||||
| 		struct wlr_buffer *buffer, int32_t hotspot_x, int32_t hotspot_y) { | ||||
| 	struct wlr_x11_output *output = get_x11_output_from_output(wlr_output); | ||||
| 	struct wlr_x11_backend *x11 = output->x11; | ||||
| 	int width = 0, height = 0; | ||||
| 
 | ||||
| 	if (x11->argb32 == XCB_NONE) { | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	if (texture != NULL) { | ||||
| 		width = texture->width * wlr_output->scale / scale; | ||||
| 		height = texture->height * wlr_output->scale / scale; | ||||
| 
 | ||||
| 	if (buffer != NULL) { | ||||
| 		if (hotspot_x < 0) { | ||||
| 			hotspot_x = 0; | ||||
| 		} | ||||
| 		if ((uint32_t)hotspot_x > texture->width) { | ||||
| 			hotspot_x = texture->width; | ||||
| 		if (hotspot_x > buffer->width) { | ||||
| 			hotspot_x = buffer->width; | ||||
| 		} | ||||
| 		if (hotspot_y < 0) { | ||||
| 			hotspot_y = 0; | ||||
| 		} | ||||
| 		if ((uint32_t)hotspot_y > texture->height) { | ||||
| 			hotspot_y = texture->height; | ||||
| 		if (hotspot_y > buffer->height) { | ||||
| 			hotspot_y = buffer->height; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	struct wlr_box hotspot = { .x = hotspot_x, .y = hotspot_y }; | ||||
| 	wlr_box_transform(&hotspot, &hotspot, | ||||
| 		wlr_output_transform_invert(wlr_output->transform), | ||||
| 		width, height); | ||||
| 	bool success = output_cursor_to_picture(output, buffer); | ||||
| 
 | ||||
| 	if (!update_texture) { | ||||
| 		// This means we previously had a failure of some sort.
 | ||||
| 		if (texture != NULL && output->cursor.pic == XCB_NONE) { | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		// Update hotspot without changing cursor image
 | ||||
| 		update_x11_output_cursor(output, hotspot.x, hotspot.y); | ||||
| 		return true; | ||||
| 	} | ||||
| 
 | ||||
| 	bool success = output_cursor_to_picture(output, texture, transform, | ||||
| 		width, height); | ||||
| 
 | ||||
| 	update_x11_output_cursor(output, hotspot.x, hotspot.y); | ||||
| 	update_x11_output_cursor(output, hotspot_x, hotspot_y); | ||||
| 
 | ||||
| 	return success; | ||||
| } | ||||
|  |  | |||
|  | @ -34,9 +34,10 @@ struct wlr_drm_plane { | |||
| 
 | ||||
| 	struct wlr_drm_format_set formats; | ||||
| 
 | ||||
| 	// Only used by cursor
 | ||||
| 	// Only used by cursor plane
 | ||||
| 	bool cursor_enabled; | ||||
| 	int32_t cursor_hotspot_x, cursor_hotspot_y; | ||||
| 	int cursor_width, cursor_height; | ||||
| 	int cursor_hotspot_x, cursor_hotspot_y; | ||||
| 
 | ||||
| 	union wlr_drm_plane_props props; | ||||
| }; | ||||
|  |  | |||
|  | @ -43,6 +43,9 @@ bool init_drm_renderer(struct wlr_drm_backend *drm, | |||
| 	struct wlr_drm_renderer *renderer); | ||||
| void finish_drm_renderer(struct wlr_drm_renderer *renderer); | ||||
| 
 | ||||
| bool init_drm_surface(struct wlr_drm_surface *surf, | ||||
| 	struct wlr_drm_renderer *renderer, uint32_t width, uint32_t height, | ||||
| 	const struct wlr_drm_format *drm_format); | ||||
| bool drm_surface_make_current(struct wlr_drm_surface *surf, int *buffer_age); | ||||
| void drm_surface_unset_current(struct wlr_drm_surface *surf); | ||||
| 
 | ||||
|  | @ -53,8 +56,12 @@ void drm_fb_destroy(struct wlr_drm_fb *fb); | |||
| void drm_fb_clear(struct wlr_drm_fb **fb); | ||||
| void drm_fb_move(struct wlr_drm_fb **new, struct wlr_drm_fb **old); | ||||
| 
 | ||||
| struct wlr_buffer *drm_surface_blit(struct wlr_drm_surface *surf, | ||||
| 	struct wlr_buffer *buffer); | ||||
| bool drm_surface_render_black_frame(struct wlr_drm_surface *surf); | ||||
| 
 | ||||
| struct wlr_drm_format *drm_plane_pick_render_format( | ||||
| 		struct wlr_drm_plane *plane, struct wlr_drm_renderer *renderer); | ||||
| bool drm_plane_init_surface(struct wlr_drm_plane *plane, | ||||
| 		struct wlr_drm_backend *drm, int32_t width, uint32_t height, | ||||
| 		bool with_modifiers); | ||||
|  |  | |||
|  | @ -83,9 +83,7 @@ struct wlr_wl_output { | |||
| 	struct { | ||||
| 		struct wlr_wl_pointer *pointer; | ||||
| 		struct wl_surface *surface; | ||||
| 		struct wlr_swapchain *swapchain; | ||||
| 		int32_t hotspot_x, hotspot_y; | ||||
| 		int32_t width, height; | ||||
| 	} cursor; | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -24,22 +24,15 @@ struct wlr_output_impl { | |||
| 	/**
 | ||||
| 	 * Set the output cursor plane image. | ||||
| 	 * | ||||
| 	 * The parameters describe the image texture, its scale and its transform. | ||||
| 	 * If the scale and transform doesn't match the output's, the backend is | ||||
| 	 * responsible for scaling and transforming the texture appropriately. | ||||
| 	 * If texture is NULL, the cursor should be hidden. | ||||
| 	 * If buffer is NULL, the cursor should be hidden. | ||||
| 	 * | ||||
| 	 * The hotspot indicates the offset that needs to be applied to the | ||||
| 	 * top-left corner of the image to match the cursor position. In other | ||||
| 	 * words, the image should be displayed at (x - hotspot_x, y - hotspot_y). | ||||
| 	 * The hotspot is given in the texture's coordinate space. | ||||
| 	 * | ||||
| 	 * If update_texture is true, all parameters need to be taken into account. | ||||
| 	 * If update_texture is false, only the hotspot is to be updated. | ||||
| 	 */ | ||||
| 	bool (*set_cursor)(struct wlr_output *output, struct wlr_texture *texture, | ||||
| 		float scale, enum wl_output_transform transform, | ||||
| 		int32_t hotspot_x, int32_t hotspot_y, bool update_texture); | ||||
| 	bool (*set_cursor)(struct wlr_output *output, struct wlr_buffer *buffer, | ||||
| 		int hotspot_x, int hotspot_y); | ||||
| 	/**
 | ||||
| 	 * Set the output cursor plane position. | ||||
| 	 * | ||||
|  |  | |||
|  | @ -181,6 +181,8 @@ struct wlr_output { | |||
| 
 | ||||
| 	struct wl_list cursors; // wlr_output_cursor::link
 | ||||
| 	struct wlr_output_cursor *hardware_cursor; | ||||
| 	struct wlr_swapchain *cursor_swapchain; | ||||
| 	struct wlr_buffer *cursor_front_buffer; | ||||
| 	int software_cursor_locks; // number of locks forcing software cursors
 | ||||
| 
 | ||||
| 	struct wl_listener display_destroy; | ||||
|  |  | |||
|  | @ -16,6 +16,11 @@ | |||
| #include <wlr/types/wlr_surface.h> | ||||
| #include <wlr/util/log.h> | ||||
| #include <wlr/util/region.h> | ||||
| #include "backend/backend.h" | ||||
| #include "render/allocator.h" | ||||
| #include "render/drm_format_set.h" | ||||
| #include "render/swapchain.h" | ||||
| #include "render/wlr_renderer.h" | ||||
| #include "util/global.h" | ||||
| #include "util/signal.h" | ||||
| 
 | ||||
|  | @ -385,6 +390,9 @@ void wlr_output_destroy(struct wlr_output *output) { | |||
| 		wlr_output_cursor_destroy(cursor); | ||||
| 	} | ||||
| 
 | ||||
| 	wlr_swapchain_destroy(output->cursor_swapchain); | ||||
| 	wlr_buffer_unlock(output->cursor_front_buffer); | ||||
| 
 | ||||
| 	if (output->idle_frame != NULL) { | ||||
| 		wl_event_source_remove(output->idle_frame); | ||||
| 	} | ||||
|  | @ -829,8 +837,7 @@ void wlr_output_lock_software_cursors(struct wlr_output *output, bool lock) { | |||
| 
 | ||||
| 	if (output->software_cursor_locks > 0 && output->hardware_cursor != NULL) { | ||||
| 		assert(output->impl->set_cursor); | ||||
| 		output->impl->set_cursor(output, NULL, 1, | ||||
| 			WL_OUTPUT_TRANSFORM_NORMAL, 0, 0, true); | ||||
| 		output->impl->set_cursor(output, NULL, 0, 0); | ||||
| 		output_cursor_damage_whole(output->hardware_cursor); | ||||
| 		output->hardware_cursor = NULL; | ||||
| 	} | ||||
|  | @ -1001,8 +1008,62 @@ static void output_cursor_update_visible(struct wlr_output_cursor *cursor) { | |||
| 	cursor->visible = visible; | ||||
| } | ||||
| 
 | ||||
| static bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) { | ||||
| 	float scale = cursor->output->scale; | ||||
| static struct wlr_drm_format *output_pick_cursor_format(struct wlr_output *output) { | ||||
| 	struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); | ||||
| 	struct wlr_allocator *allocator = backend_get_allocator(output->backend); | ||||
| 	assert(renderer != NULL && allocator != NULL); | ||||
| 
 | ||||
| 	const struct wlr_drm_format_set *render_formats = | ||||
| 		wlr_renderer_get_render_formats(renderer); | ||||
| 	if (render_formats == NULL) { | ||||
| 		wlr_log(WLR_ERROR, "Failed to get render formats"); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	const struct wlr_drm_format_set *display_formats; | ||||
| 	if (output->impl->get_cursor_formats) { | ||||
| 		display_formats = | ||||
| 			output->impl->get_cursor_formats(output, allocator->buffer_caps); | ||||
| 		if (display_formats == NULL) { | ||||
| 			wlr_log(WLR_ERROR, "Failed to get display formats"); | ||||
| 			return NULL; | ||||
| 		} | ||||
| 	} else { | ||||
| 		// The backend can display any format
 | ||||
| 		display_formats = render_formats; | ||||
| 	} | ||||
| 
 | ||||
| 	uint32_t fmt = DRM_FORMAT_ARGB8888; | ||||
| 
 | ||||
| 	const struct wlr_drm_format *render_format = | ||||
| 		wlr_drm_format_set_get(render_formats, fmt); | ||||
| 	if (render_format == NULL) { | ||||
| 		wlr_log(WLR_DEBUG, "Renderer doesn't support format 0x%"PRIX32, fmt); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	const struct wlr_drm_format *display_format = | ||||
| 		wlr_drm_format_set_get(display_formats, fmt); | ||||
| 	if (display_format == NULL) { | ||||
| 		wlr_log(WLR_DEBUG, "Output doesn't support format 0x%"PRIX32, fmt); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	struct wlr_drm_format *format = | ||||
| 		wlr_drm_format_intersect(display_format, render_format); | ||||
| 	if (format == NULL) { | ||||
| 		wlr_log(WLR_DEBUG, "Failed to intersect display and render " | ||||
| 			"modifiers for format 0x%"PRIX32, fmt); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	return format; | ||||
| } | ||||
| 
 | ||||
| static struct wlr_buffer *render_cursor_buffer(struct wlr_output_cursor *cursor) { | ||||
| 	struct wlr_output *output = cursor->output; | ||||
| 
 | ||||
| 	float scale = output->scale; | ||||
| 	enum wl_output_transform transform = WL_OUTPUT_TRANSFORM_NORMAL; | ||||
| 	struct wlr_texture *texture = cursor->texture; | ||||
| 	if (cursor->surface != NULL) { | ||||
|  | @ -1010,25 +1071,150 @@ static bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) { | |||
| 		scale = cursor->surface->current.scale; | ||||
| 		transform = cursor->surface->current.transform; | ||||
| 	} | ||||
| 	if (texture == NULL) { | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (cursor->output->software_cursor_locks > 0) { | ||||
| 	struct wlr_renderer *renderer = wlr_backend_get_renderer(output->backend); | ||||
| 	if (renderer == NULL) { | ||||
| 		wlr_log(WLR_ERROR, "Failed to get backend renderer"); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	struct wlr_allocator *allocator = backend_get_allocator(output->backend); | ||||
| 	if (allocator == NULL) { | ||||
| 		wlr_log(WLR_ERROR, "Failed to get backend allocator"); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	int width = texture->width; | ||||
| 	int height = texture->height; | ||||
| 	if (output->impl->get_cursor_size) { | ||||
| 		// Apply hardware limitations on buffer size
 | ||||
| 		output->impl->get_cursor_size(cursor->output, &width, &height); | ||||
| 		if ((int)texture->width > width || (int)texture->height > height) { | ||||
| 			wlr_log(WLR_DEBUG, "Cursor texture too large (%dx%d), " | ||||
| 				"exceeds hardware limitations (%dx%d)", texture->width, | ||||
| 				texture->height, width, height); | ||||
| 			return NULL; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (output->cursor_swapchain == NULL || | ||||
| 			output->cursor_swapchain->width != width || | ||||
| 			output->cursor_swapchain->height != height) { | ||||
| 		struct wlr_drm_format *format = | ||||
| 			output_pick_cursor_format(output); | ||||
| 		if (format == NULL) { | ||||
| 			wlr_log(WLR_ERROR, "Failed to pick cursor format"); | ||||
| 			return NULL; | ||||
| 		} | ||||
| 
 | ||||
| 		wlr_swapchain_destroy(output->cursor_swapchain); | ||||
| 		output->cursor_swapchain = wlr_swapchain_create(allocator, | ||||
| 			width, height, format); | ||||
| 		if (output->cursor_swapchain == NULL) { | ||||
| 			wlr_log(WLR_ERROR, "Failed to create cursor swapchain"); | ||||
| 			return NULL; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	struct wlr_buffer *buffer = | ||||
| 		wlr_swapchain_acquire(output->cursor_swapchain, NULL); | ||||
| 	if (buffer == NULL) { | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!wlr_renderer_bind_buffer(renderer, buffer)) { | ||||
| 		wlr_buffer_unlock(buffer); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	struct wlr_box cursor_box = { | ||||
| 		.width = texture->width * output->scale / scale, | ||||
| 		.height = texture->height * output->scale / scale, | ||||
| 	}; | ||||
| 
 | ||||
| 	float output_matrix[9]; | ||||
| 	wlr_matrix_identity(output_matrix); | ||||
| 	if (output->transform != WL_OUTPUT_TRANSFORM_NORMAL) { | ||||
| 		struct wlr_box tr_size = { | ||||
| 			.width = buffer->width, | ||||
| 			.height = buffer->height, | ||||
| 		}; | ||||
| 		wlr_box_transform(&tr_size, &tr_size, output->transform, 0, 0); | ||||
| 
 | ||||
| 		wlr_matrix_translate(output_matrix, buffer->width / 2.0, | ||||
| 			buffer->height / 2.0); | ||||
| 		wlr_matrix_transform(output_matrix, output->transform); | ||||
| 		wlr_matrix_translate(output_matrix, - tr_size.width / 2.0, | ||||
| 			- tr_size.height / 2.0); | ||||
| 	} | ||||
| 
 | ||||
| 	float matrix[9]; | ||||
| 	wlr_matrix_project_box(matrix, &cursor_box, transform, 0, output_matrix); | ||||
| 
 | ||||
| 	wlr_renderer_begin(renderer, width, height); | ||||
| 	wlr_renderer_clear(renderer, (float[]){ 0.0, 0.0, 0.0, 0.0 }); | ||||
| 	wlr_render_texture_with_matrix(renderer, texture, matrix, 1.0); | ||||
| 	wlr_renderer_end(renderer); | ||||
| 
 | ||||
| 	wlr_renderer_bind_buffer(renderer, NULL); | ||||
| 
 | ||||
| 	return buffer; | ||||
| } | ||||
| 
 | ||||
| static bool output_cursor_attempt_hardware(struct wlr_output_cursor *cursor) { | ||||
| 	struct wlr_output *output = cursor->output; | ||||
| 
 | ||||
| 	if (!output->impl->set_cursor || | ||||
| 			output->software_cursor_locks > 0) { | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	struct wlr_output_cursor *hwcur = cursor->output->hardware_cursor; | ||||
| 	if (cursor->output->impl->set_cursor && (hwcur == NULL || hwcur == cursor)) { | ||||
| 		// If the cursor was hidden or was a software cursor, the hardware
 | ||||
| 		// cursor position is outdated
 | ||||
| 		assert(cursor->output->impl->move_cursor); | ||||
| 		cursor->output->impl->move_cursor(cursor->output, | ||||
| 			(int)cursor->x, (int)cursor->y); | ||||
| 		if (cursor->output->impl->set_cursor(cursor->output, texture, | ||||
| 				scale, transform, cursor->hotspot_x, cursor->hotspot_y, true)) { | ||||
| 			cursor->output->hardware_cursor = cursor; | ||||
| 			return true; | ||||
| 	struct wlr_output_cursor *hwcur = output->hardware_cursor; | ||||
| 	if (hwcur != NULL && hwcur != cursor) { | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	struct wlr_texture *texture = cursor->texture; | ||||
| 	if (cursor->surface != NULL) { | ||||
| 		// TODO: try using the surface buffer directly
 | ||||
| 		texture = wlr_surface_get_texture(cursor->surface); | ||||
| 	} | ||||
| 
 | ||||
| 	// If the cursor was hidden or was a software cursor, the hardware
 | ||||
| 	// cursor position is outdated
 | ||||
| 	output->impl->move_cursor(cursor->output, | ||||
| 		(int)cursor->x, (int)cursor->y); | ||||
| 
 | ||||
| 	struct wlr_buffer *buffer = NULL; | ||||
| 	if (texture != NULL) { | ||||
| 		buffer = render_cursor_buffer(cursor); | ||||
| 		if (buffer == NULL) { | ||||
| 			wlr_log(WLR_ERROR, "Failed to render cursor buffer"); | ||||
| 			return false; | ||||
| 		} | ||||
| 	} | ||||
| 	return false; | ||||
| 
 | ||||
| 	struct wlr_box hotspot = { | ||||
| 		.x = cursor->hotspot_x, | ||||
| 		.y = cursor->hotspot_y, | ||||
| 	}; | ||||
| 	wlr_box_transform(&hotspot, &hotspot, | ||||
| 		wlr_output_transform_invert(output->transform), | ||||
| 		buffer ? buffer->width : 0, buffer ? buffer->height : 0); | ||||
| 
 | ||||
| 	bool ok = output->impl->set_cursor(cursor->output, buffer, | ||||
| 		hotspot.x, hotspot.y); | ||||
| 	if (ok) { | ||||
| 		wlr_buffer_unlock(output->cursor_front_buffer); | ||||
| 		output->cursor_front_buffer = buffer; | ||||
| 		output->hardware_cursor = cursor; | ||||
| 	} else { | ||||
| 		wlr_buffer_unlock(buffer); | ||||
| 	} | ||||
| 	return ok; | ||||
| } | ||||
| 
 | ||||
| bool wlr_output_cursor_set_image(struct wlr_output_cursor *cursor, | ||||
|  | @ -1130,9 +1316,19 @@ void wlr_output_cursor_set_surface(struct wlr_output_cursor *cursor, | |||
| 		if (cursor->output->hardware_cursor != cursor) { | ||||
| 			output_cursor_damage_whole(cursor); | ||||
| 		} else { | ||||
| 			struct wlr_buffer *buffer = cursor->output->cursor_front_buffer; | ||||
| 
 | ||||
| 			struct wlr_box hotspot = { | ||||
| 				.x = cursor->hotspot_x, | ||||
| 				.y = cursor->hotspot_y, | ||||
| 			}; | ||||
| 			wlr_box_transform(&hotspot, &hotspot, | ||||
| 				wlr_output_transform_invert(cursor->output->transform), | ||||
| 				buffer ? buffer->width : 0, buffer ? buffer->height : 0); | ||||
| 
 | ||||
| 			assert(cursor->output->impl->set_cursor); | ||||
| 			cursor->output->impl->set_cursor(cursor->output, NULL, | ||||
| 				1, WL_OUTPUT_TRANSFORM_NORMAL, hotspot_x, hotspot_y, false); | ||||
| 			cursor->output->impl->set_cursor(cursor->output, | ||||
| 				buffer, hotspot.x, hotspot.y); | ||||
| 		} | ||||
| 		return; | ||||
| 	} | ||||
|  | @ -1156,8 +1352,7 @@ void wlr_output_cursor_set_surface(struct wlr_output_cursor *cursor, | |||
| 
 | ||||
| 		if (cursor->output->hardware_cursor == cursor) { | ||||
| 			assert(cursor->output->impl->set_cursor); | ||||
| 			cursor->output->impl->set_cursor(cursor->output, NULL, 1, | ||||
| 				WL_OUTPUT_TRANSFORM_NORMAL, 0, 0, true); | ||||
| 			cursor->output->impl->set_cursor(cursor->output, NULL, 0, 0); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  | @ -1219,8 +1414,7 @@ void wlr_output_cursor_destroy(struct wlr_output_cursor *cursor) { | |||
| 	if (cursor->output->hardware_cursor == cursor) { | ||||
| 		// If this cursor was the hardware cursor, disable it
 | ||||
| 		if (cursor->output->impl->set_cursor) { | ||||
| 			cursor->output->impl->set_cursor(cursor->output, NULL, 1, | ||||
| 				WL_OUTPUT_TRANSFORM_NORMAL, 0, 0, true); | ||||
| 			cursor->output->impl->set_cursor(cursor->output, NULL, 0, 0); | ||||
| 		} | ||||
| 		cursor->output->hardware_cursor = NULL; | ||||
| 	} | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue