mirror of
				https://gitlab.freedesktop.org/wlroots/wlroots.git
				synced 2025-10-29 05:40:12 -04:00 
			
		
		
		
	Compare commits
	
		
			78 commits
		
	
	
		
			2ca8a4c136
			...
			3bfcfeabbd
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 3bfcfeabbd | ||
|   | 879243e370 | ||
|   | 989cffe70d | ||
|   | 3e08e3be4a | ||
|   | 91f4890ec2 | ||
|   | 74ce6c22a5 | ||
|   | 0b58bddf13 | ||
|   | 3d36ab9211 | ||
|   | d786e07899 | ||
|   | 6d63871f05 | ||
|   | 19c5d22beb | ||
|   | 06275103f2 | ||
|   | 03e7966650 | ||
|   | 5529aae3e6 | ||
|   | 6e1c8748ff | ||
|   | d8fb7adcf0 | ||
|   | c2d9ae2142 | ||
|   | 6978509f64 | ||
|   | 2252854297 | ||
|   | dde07b6840 | ||
|   | 406aa5f7f5 | ||
|   | 2ec4012559 | ||
|   | d039ad8da3 | ||
|   | 3f0d338643 | ||
|   | 60d72724cd | ||
|   | 7cb3393e75 | ||
|   | 26c1476827 | ||
|   | 138210f01c | ||
|   | 845a7a581d | ||
|   | 108d94f798 | ||
|   | aaf82ee332 | ||
|   | d1c88e9497 | ||
|   | 3e88a79e6f | ||
|   | b2d09cdee9 | ||
|   | 35eba5f2fe | ||
|   | a91f96b391 | ||
|   | 6fee3623e4 | ||
|   | 7f6d66ea62 | ||
|   | 54374b6fe6 | ||
|   | dd7f543189 | ||
|   | d7ae9a866b | ||
|   | 462046ffdc | ||
|   | bd566225ea | ||
|   | b62c6878e1 | ||
|   | fd069ad4f2 | ||
|   | 5e5842cb1a | ||
|   | cdd2c7e006 | ||
|   | 905465b0fa | ||
|   | 102a6bd415 | ||
|   | 06aacb2a6f | ||
|   | 0166fd9eb7 | ||
|   | 423afc3fc9 | ||
|   | 122310a2de | ||
|   | b799ffc6ae | ||
|   | e95117b700 | ||
|   | 1a18e47efa | ||
|   | bbd9a49bdf | ||
|   | 7bf5ff4c02 | ||
|   | b0c886ec77 | ||
|   | 7431d840d0 | ||
|   | bb1f8673b3 | ||
|   | ad1b2f2819 | ||
|   | 812675ba34 | ||
|   | 7392b3313a | ||
|   | 07e92fb868 | ||
|   | 12316417b0 | ||
|   | dd3c63f5e6 | ||
|   | c8b7600adc | ||
|   | 51a78cb0ed | ||
| 1beb25a1c8 | |||
|   | 2f2c0dfcc6 | ||
|   | 47a90d6f1a | ||
|   | db5e9ca04c | ||
|   | efb17980a8 | ||
|   | be5e266211 | ||
|   | 80c7e0f772 | ||
|   | ccec4116b3 | ||
|   | 04dc72e8c1 | 
					 78 changed files with 1771 additions and 497 deletions
				
			
		|  | @ -41,9 +41,10 @@ tasks: | |||
|       cd wlroots/build-gcc/tinywl | ||||
|       sudo modprobe vkms | ||||
|       udevadm settle | ||||
|       card="/dev/dri/$(ls /sys/devices/faux/vkms/drm/ | grep ^card)" | ||||
|       export WLR_BACKENDS=drm | ||||
|       export WLR_RENDERER=pixman | ||||
|       export WLR_DRM_DEVICES=/dev/dri/by-path/platform-vkms-card | ||||
|       export WLR_DRM_DEVICES="$card" | ||||
|       export UBSAN_OPTIONS=halt_on_error=1 | ||||
|       sudo chmod ugo+rw /dev/dri/by-path/platform-vkms-card | ||||
|       sudo chmod ugo+rw "$card" | ||||
|       sudo -E seatd-launch -- ./tinywl -s 'kill $PPID' || [ $? = 143 ] | ||||
|  |  | |||
|  | @ -485,5 +485,10 @@ bool wlr_backend_commit(struct wlr_backend *backend, | |||
| 		output_apply_commit(state->output, &state->base); | ||||
| 	} | ||||
| 
 | ||||
| 	for (size_t i = 0; i < states_len; i++) { | ||||
| 		const struct wlr_backend_output_state *state = &states[i]; | ||||
| 		output_send_commit_event(state->output, &state->base); | ||||
| 	} | ||||
| 
 | ||||
| 	return true; | ||||
| } | ||||
|  |  | |||
|  | @ -186,6 +186,10 @@ static uint8_t convert_cta861_eotf(enum wlr_color_transfer_function tf) { | |||
| 		return 2; | ||||
| 	case WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR: | ||||
| 		abort(); // unsupported
 | ||||
| 	case WLR_COLOR_TRANSFER_FUNCTION_GAMMA22: | ||||
| 		abort(); // unsupported
 | ||||
| 	case WLR_COLOR_TRANSFER_FUNCTION_BT1886: | ||||
| 		abort(); // unsupported
 | ||||
| 	} | ||||
| 	abort(); // unreachable
 | ||||
| } | ||||
|  |  | |||
|  | @ -1717,7 +1717,11 @@ static bool connect_drm_connector(struct wlr_drm_connector *wlr_conn, | |||
| 	size_t edid_len = 0; | ||||
| 	uint8_t *edid = get_drm_prop_blob(drm->fd, | ||||
| 		wlr_conn->id, wlr_conn->props.edid, &edid_len); | ||||
| 	parse_edid(wlr_conn, edid_len, edid); | ||||
| 	if (edid_len > 0) { | ||||
| 		parse_edid(wlr_conn, edid_len, edid); | ||||
| 	} else { | ||||
| 		wlr_log(WLR_DEBUG, "Connector has no EDID"); | ||||
| 	} | ||||
| 	free(edid); | ||||
| 
 | ||||
| 	char *subconnector = NULL; | ||||
|  |  | |||
|  | @ -367,7 +367,10 @@ void wlr_session_close_file(struct wlr_session *session, | |||
| 	} | ||||
| 
 | ||||
| 	assert(wl_list_empty(&dev->events.change.listener_list)); | ||||
| 	assert(wl_list_empty(&dev->events.remove.listener_list)); | ||||
| 	// TODO: assert that the "remove" listener list is empty as well. Listeners
 | ||||
| 	// will typically call wlr_session_close_file() in response, and
 | ||||
| 	// wl_signal_emit_mutable() installs two phantom listeners, so we'd count
 | ||||
| 	// these two.
 | ||||
| 
 | ||||
| 	close(dev->fd); | ||||
| 	wl_list_remove(&dev->link); | ||||
|  | @ -516,8 +519,6 @@ ssize_t wlr_session_find_gpus(struct wlr_session *session, | |||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		bool is_boot_vga = false; | ||||
| 
 | ||||
| 		const char *path = udev_list_entry_get_name(entry); | ||||
| 		struct udev_device *dev = udev_device_new_from_syspath(session->udev, path); | ||||
| 		if (!dev) { | ||||
|  | @ -533,14 +534,20 @@ ssize_t wlr_session_find_gpus(struct wlr_session *session, | |||
| 			continue; | ||||
| 		} | ||||
| 
 | ||||
| 		// This is owned by 'dev', so we don't need to free it
 | ||||
| 		struct udev_device *pci = | ||||
| 			udev_device_get_parent_with_subsystem_devtype(dev, "pci", NULL); | ||||
| 		bool is_primary = false; | ||||
| 		const char *boot_display = udev_device_get_sysattr_value(dev, "boot_display"); | ||||
| 		if (boot_display && strcmp(boot_display, "1") == 0) { | ||||
| 		    is_primary = true; | ||||
| 		} else { | ||||
| 			// This is owned by 'dev', so we don't need to free it
 | ||||
| 			struct udev_device *pci = | ||||
| 				udev_device_get_parent_with_subsystem_devtype(dev, "pci", NULL); | ||||
| 
 | ||||
| 		if (pci) { | ||||
| 			const char *id = udev_device_get_sysattr_value(pci, "boot_vga"); | ||||
| 			if (id && strcmp(id, "1") == 0) { | ||||
| 				is_boot_vga = true; | ||||
| 			if (pci) { | ||||
| 				const char *id = udev_device_get_sysattr_value(pci, "boot_vga"); | ||||
| 				if (id && strcmp(id, "1") == 0) { | ||||
| 					is_primary = true; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 
 | ||||
|  | @ -554,7 +561,7 @@ ssize_t wlr_session_find_gpus(struct wlr_session *session, | |||
| 		udev_device_unref(dev); | ||||
| 
 | ||||
| 		ret[i] = wlr_dev; | ||||
| 		if (is_boot_vga) { | ||||
| 		if (is_primary) { | ||||
| 			struct wlr_device *tmp = ret[0]; | ||||
| 			ret[0] = ret[i]; | ||||
| 			ret[i] = tmp; | ||||
|  |  | |||
|  | @ -55,14 +55,6 @@ struct wlr_wl_backend *get_wl_backend_from_backend(struct wlr_backend *wlr_backe | |||
| static int dispatch_events(int fd, uint32_t mask, void *data) { | ||||
| 	struct wlr_wl_backend *wl = data; | ||||
| 
 | ||||
| 	if ((mask & WL_EVENT_HANGUP) || (mask & WL_EVENT_ERROR)) { | ||||
| 		if (mask & WL_EVENT_ERROR) { | ||||
| 			wlr_log(WLR_ERROR, "Failed to read from remote Wayland display"); | ||||
| 		} | ||||
| 		wlr_backend_destroy(&wl->backend); | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	int count = 0; | ||||
| 	if (mask & WL_EVENT_READABLE) { | ||||
| 		count = wl_display_dispatch(wl->remote_display); | ||||
|  | @ -75,6 +67,18 @@ static int dispatch_events(int fd, uint32_t mask, void *data) { | |||
| 		wl_display_flush(wl->remote_display); | ||||
| 	} | ||||
| 
 | ||||
| 	// Make sure we've consumed all data before disconnecting due to hangup,
 | ||||
| 	// so that we process any wl_display.error events
 | ||||
| 	if (!(mask & WL_EVENT_READABLE) && (mask & (WL_EVENT_HANGUP | WL_EVENT_ERROR))) { | ||||
| 		if (mask & WL_EVENT_ERROR) { | ||||
| 			wlr_log(WLR_ERROR, "Failed to read from remote Wayland display"); | ||||
| 		} else { | ||||
| 			wlr_log(WLR_DEBUG, "Disconnected from remote Wayland display"); | ||||
| 		} | ||||
| 		wlr_backend_destroy(&wl->backend); | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	if (count < 0) { | ||||
| 		wlr_log(WLR_ERROR, "Failed to dispatch remote Wayland display"); | ||||
| 		wlr_backend_destroy(&wl->backend); | ||||
|  |  | |||
|  | @ -9,6 +9,7 @@ enum wlr_color_transform_type { | |||
| 	COLOR_TRANSFORM_INVERSE_EOTF, | ||||
| 	COLOR_TRANSFORM_LCMS2, | ||||
| 	COLOR_TRANSFORM_LUT_3X1D, | ||||
| 	COLOR_TRANSFORM_PIPELINE, | ||||
| }; | ||||
| 
 | ||||
| struct wlr_color_transform { | ||||
|  | @ -39,6 +40,13 @@ struct wlr_color_transform_lut_3x1d { | |||
| 	size_t dim; | ||||
| }; | ||||
| 
 | ||||
| struct wlr_color_transform_pipeline { | ||||
| 	struct wlr_color_transform base; | ||||
| 
 | ||||
| 	struct wlr_color_transform **transforms; | ||||
| 	size_t len; | ||||
| }; | ||||
| 
 | ||||
| void wlr_color_transform_init(struct wlr_color_transform *tr, | ||||
| 	enum wlr_color_transform_type type); | ||||
| 
 | ||||
|  | @ -72,12 +80,6 @@ struct wlr_color_transform_inverse_eotf *wlr_color_transform_inverse_eotf_from_b | |||
| struct wlr_color_transform_lut_3x1d *color_transform_lut_3x1d_from_base( | ||||
| 	struct wlr_color_transform *tr); | ||||
| 
 | ||||
| /**
 | ||||
|  * Evaluate a 3x1D LUT color transform for a given RGB triplet. | ||||
|  */ | ||||
| void color_transform_lut_3x1d_eval(struct wlr_color_transform_lut_3x1d *tr, | ||||
| 	float out[static 3], const float in[static 3]); | ||||
| 
 | ||||
| /**
 | ||||
|  * Obtain primaries values from a well-known primaries name. | ||||
|  */ | ||||
|  |  | |||
|  | @ -153,6 +153,8 @@ enum wlr_vk_texture_transform { | |||
| 	WLR_VK_TEXTURE_TRANSFORM_IDENTITY = 0, | ||||
| 	WLR_VK_TEXTURE_TRANSFORM_SRGB = 1, | ||||
| 	WLR_VK_TEXTURE_TRANSFORM_ST2084_PQ = 2, | ||||
| 	WLR_VK_TEXTURE_TRANSFORM_GAMMA22 = 3, | ||||
| 	WLR_VK_TEXTURE_TRANSFORM_BT1886 = 4, | ||||
| }; | ||||
| 
 | ||||
| enum wlr_vk_shader_source { | ||||
|  | @ -167,6 +169,8 @@ enum wlr_vk_output_transform { | |||
| 	WLR_VK_OUTPUT_TRANSFORM_INVERSE_SRGB = 1, | ||||
| 	WLR_VK_OUTPUT_TRANSFORM_INVERSE_ST2084_PQ = 2, | ||||
| 	WLR_VK_OUTPUT_TRANSFORM_LUT3D = 3, | ||||
| 	WLR_VK_OUTPUT_TRANSFORM_INVERSE_GAMMA22 = 4, | ||||
| 	WLR_VK_OUTPUT_TRANSFORM_INVERSE_BT1886 = 5, | ||||
| }; | ||||
| 
 | ||||
| struct wlr_vk_pipeline_key { | ||||
|  | @ -199,11 +203,20 @@ struct wlr_vk_render_format_setup { | |||
| 	VkPipeline output_pipe_srgb; | ||||
| 	VkPipeline output_pipe_pq; | ||||
| 	VkPipeline output_pipe_lut3d; | ||||
| 	VkPipeline output_pipe_gamma22; | ||||
| 	VkPipeline output_pipe_bt1886; | ||||
| 
 | ||||
| 	struct wlr_vk_renderer *renderer; | ||||
| 	struct wl_list pipelines; // struct wlr_vk_pipeline.link
 | ||||
| }; | ||||
| 
 | ||||
| // Final output framebuffer and image view
 | ||||
| struct wlr_vk_render_buffer_out { | ||||
| 	VkImageView image_view; | ||||
| 	VkFramebuffer framebuffer; | ||||
| 	bool transitioned; | ||||
| }; | ||||
| 
 | ||||
| // Renderer-internal represenation of an wlr_buffer imported for rendering.
 | ||||
| struct wlr_vk_render_buffer { | ||||
| 	struct wlr_buffer *wlr_buffer; | ||||
|  | @ -215,36 +228,40 @@ struct wlr_vk_render_buffer { | |||
| 	uint32_t mem_count; | ||||
| 	VkImage image; | ||||
| 
 | ||||
| 	// Framebuffer and image view for rendering directly onto the buffer image,
 | ||||
| 	// without any color transform.
 | ||||
| 	struct { | ||||
| 		struct wlr_vk_render_buffer_out out; | ||||
| 		struct wlr_vk_render_format_setup *render_setup; | ||||
| 	} linear; | ||||
| 
 | ||||
| 	// Framebuffer and image view for rendering directly onto the buffer image.
 | ||||
| 	// This requires that the image support an _SRGB VkFormat, and does
 | ||||
| 	// not work with color transforms.
 | ||||
| 	struct { | ||||
| 		struct wlr_vk_render_buffer_out out; | ||||
| 		struct wlr_vk_render_format_setup *render_setup; | ||||
| 		VkImageView image_view; | ||||
| 		VkFramebuffer framebuffer; | ||||
| 		bool transitioned; | ||||
| 	} srgb; | ||||
| 
 | ||||
| 	// Framebuffer, image view, and blending image to render indirectly
 | ||||
| 	// onto the buffer image. This works for general image types and permits
 | ||||
| 	// color transforms.
 | ||||
| 	struct { | ||||
| 		struct wlr_vk_render_buffer_out out; | ||||
| 		struct wlr_vk_render_format_setup *render_setup; | ||||
| 
 | ||||
| 		VkImageView image_view; | ||||
| 		VkFramebuffer framebuffer; | ||||
| 		bool transitioned; | ||||
| 
 | ||||
| 		VkImage blend_image; | ||||
| 		VkImageView blend_image_view; | ||||
| 		VkDeviceMemory blend_memory; | ||||
| 		VkDescriptorSet blend_descriptor_set; | ||||
| 		struct wlr_vk_descriptor_pool *blend_attachment_pool; | ||||
| 		bool blend_transitioned; | ||||
| 	} plain; | ||||
| 	} two_pass; | ||||
| }; | ||||
| 
 | ||||
| bool vulkan_setup_plain_framebuffer(struct wlr_vk_render_buffer *buffer, | ||||
| bool vulkan_setup_one_pass_framebuffer(struct wlr_vk_render_buffer *buffer, | ||||
| 	const struct wlr_dmabuf_attributes *dmabuf, bool srgb); | ||||
| bool vulkan_setup_two_pass_framebuffer(struct wlr_vk_render_buffer *buffer, | ||||
| 	const struct wlr_dmabuf_attributes *dmabuf); | ||||
| 
 | ||||
| struct wlr_vk_command_buffer { | ||||
|  | @ -396,12 +413,14 @@ struct wlr_vk_render_pass { | |||
| 	struct wlr_render_pass base; | ||||
| 	struct wlr_vk_renderer *renderer; | ||||
| 	struct wlr_vk_render_buffer *render_buffer; | ||||
| 	struct wlr_vk_render_buffer_out *render_buffer_out; | ||||
| 	struct wlr_vk_render_format_setup *render_setup; | ||||
| 	struct wlr_vk_command_buffer *command_buffer; | ||||
| 	struct rect_union updated_region; | ||||
| 	VkPipeline bound_pipeline; | ||||
| 	float projection[9]; | ||||
| 	bool failed; | ||||
| 	bool srgb_pathway; // if false, rendering via intermediate blending buffer
 | ||||
| 	bool two_pass; // rendering via intermediate blending buffer
 | ||||
| 	struct wlr_color_transform *color_transform; | ||||
| 
 | ||||
| 	bool has_primaries; | ||||
|  |  | |||
|  | @ -27,6 +27,7 @@ void output_defer_present(struct wlr_output *output, struct wlr_output_event_pre | |||
| 
 | ||||
| bool output_prepare_commit(struct wlr_output *output, const struct wlr_output_state *state); | ||||
| void output_apply_commit(struct wlr_output *output, const struct wlr_output_state *state); | ||||
| void output_send_commit_event(struct wlr_output *output, const struct wlr_output_state *state); | ||||
| 
 | ||||
| void output_state_get_buffer_src_box(const struct wlr_output_state *state, | ||||
| 	struct wlr_fbox *out); | ||||
|  |  | |||
|  | @ -28,6 +28,8 @@ enum wlr_color_transfer_function { | |||
| 	WLR_COLOR_TRANSFER_FUNCTION_SRGB = 1 << 0, | ||||
| 	WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ = 1 << 1, | ||||
| 	WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR = 1 << 2, | ||||
| 	WLR_COLOR_TRANSFER_FUNCTION_GAMMA22 = 1 << 3, | ||||
| 	WLR_COLOR_TRANSFER_FUNCTION_BT1886 = 1 << 4, | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  | @ -139,6 +141,13 @@ struct wlr_color_transform *wlr_color_transform_init_linear_to_inverse_eotf( | |||
| struct wlr_color_transform *wlr_color_transform_init_lut_3x1d(size_t dim, | ||||
| 	const uint16_t *r, const uint16_t *g, const uint16_t *b); | ||||
| 
 | ||||
| /**
 | ||||
|  * Initialize a color transformation to apply a sequence of color transforms | ||||
|  * one after another. | ||||
|  */ | ||||
| struct wlr_color_transform *wlr_color_transform_init_pipeline( | ||||
| 	struct wlr_color_transform **transforms, size_t len); | ||||
| 
 | ||||
| /**
 | ||||
|  * Increase the reference count of the color transform by 1. | ||||
|  */ | ||||
|  | @ -150,4 +159,10 @@ struct wlr_color_transform *wlr_color_transform_ref(struct wlr_color_transform * | |||
|  */ | ||||
| void wlr_color_transform_unref(struct wlr_color_transform *tr); | ||||
| 
 | ||||
| /**
 | ||||
|  * Evaluate a color transform for a given RGB triplet. | ||||
|  */ | ||||
| void wlr_color_transform_eval(struct wlr_color_transform *tr, | ||||
| 	float out[static 3], const float in[static 3]); | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
|  | @ -31,8 +31,9 @@ struct wlr_render_timer; | |||
| struct wlr_buffer_pass_options { | ||||
| 	/* Timer to measure the duration of the render pass */ | ||||
| 	struct wlr_render_timer *timer; | ||||
| 	/* Color transform to apply to the output of the render pass,
 | ||||
| 	 * leave NULL to indicate sRGB/no custom transform */ | ||||
| 	/* Color transform to apply to the output of the render pass.
 | ||||
| 	 * Leave NULL to indicate the default transform (Gamma 2.2 encoding for | ||||
| 	 * sRGB monitors) */ | ||||
| 	struct wlr_color_transform *color_transform; | ||||
| 	/** Primaries describing the color volume of the destination buffer */ | ||||
| 	const struct wlr_color_primaries *primaries; | ||||
|  |  | |||
|  | @ -9,10 +9,10 @@ | |||
| #ifndef WLR_TYPES_WLR_COLOR_MANAGEMENT_V1_H | ||||
| #define WLR_TYPES_WLR_COLOR_MANAGEMENT_V1_H | ||||
| 
 | ||||
| #include <wayland-server.h> | ||||
| #include <wlr/render/color.h> | ||||
| #include <wayland-server-core.h> | ||||
| #include <wayland-protocols/color-management-v1-enum.h> | ||||
| 
 | ||||
| #include "color-management-v1-protocol.h" | ||||
| #include <wlr/render/color.h> | ||||
| 
 | ||||
| struct wlr_surface; | ||||
| 
 | ||||
|  | @ -58,6 +58,10 @@ struct wlr_color_manager_v1_options { | |||
| struct wlr_color_manager_v1 { | ||||
| 	struct wl_global *global; | ||||
| 
 | ||||
| 	struct { | ||||
| 		struct wl_signal destroy; | ||||
| 	} events; | ||||
| 
 | ||||
| 	struct { | ||||
| 		struct wlr_color_manager_v1_features features; | ||||
| 
 | ||||
|  | @ -89,4 +93,30 @@ void wlr_color_manager_v1_set_surface_preferred_image_description( | |||
| 	struct wlr_color_manager_v1 *manager, struct wlr_surface *surface, | ||||
| 	const struct wlr_image_description_v1_data *data); | ||||
| 
 | ||||
| /**
 | ||||
|  * Convert a protocol transfer function to enum wlr_color_transfer_function. | ||||
|  * Aborts if there is no matching wlroots entry. | ||||
|  */ | ||||
| enum wlr_color_transfer_function | ||||
| wlr_color_manager_v1_transfer_function_to_wlr(enum wp_color_manager_v1_transfer_function tf); | ||||
| 
 | ||||
| /**
 | ||||
|  * Convert an enum wlr_color_transfer_function value into a protocol transfer function. | ||||
|  */ | ||||
| enum wp_color_manager_v1_transfer_function | ||||
| wlr_color_manager_v1_transfer_function_from_wlr(enum wlr_color_transfer_function tf); | ||||
| 
 | ||||
| /**
 | ||||
|  * Convert a protocol named primaries to enum wlr_color_named_primaries. | ||||
|  * Aborts if there is no matching wlroots entry. | ||||
|  */ | ||||
| enum wlr_color_named_primaries | ||||
| wlr_color_manager_v1_primaries_to_wlr(enum wp_color_manager_v1_primaries primaries); | ||||
| 
 | ||||
| /**
 | ||||
|  * Convert an enum wlr_color_named_primaries value into protocol primaries. | ||||
|  */ | ||||
| enum wp_color_manager_v1_primaries | ||||
| wlr_color_manager_v1_primaries_from_wlr(enum wlr_color_named_primaries primaries); | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
|  | @ -10,10 +10,9 @@ | |||
| #define WLR_TYPES_WLR_COLOR_REPRESENTATION_V1_H | ||||
| 
 | ||||
| #include <wayland-server-core.h> | ||||
| #include <wayland-protocols/color-representation-v1-enum.h> | ||||
| #include <wlr/render/color.h> | ||||
| 
 | ||||
| #include "color-representation-v1-protocol.h" | ||||
| 
 | ||||
| struct wlr_surface; | ||||
| 
 | ||||
| // Supported coefficients and range are always paired together
 | ||||
|  |  | |||
|  | @ -10,7 +10,7 @@ | |||
| #define WLR_TYPES_WLR_CONTENT_TYPE_V1_H | ||||
| 
 | ||||
| #include <wayland-server-core.h> | ||||
| #include "content-type-v1-protocol.h" | ||||
| #include <wayland-protocols/content-type-v1-enum.h> | ||||
| 
 | ||||
| struct wlr_surface; | ||||
| 
 | ||||
|  |  | |||
|  | @ -10,7 +10,7 @@ | |||
| #define WLR_TYPES_WLR_CURSOR_SHAPE_V1_H | ||||
| 
 | ||||
| #include <wayland-server-core.h> | ||||
| #include "cursor-shape-v1-protocol.h" | ||||
| #include <wayland-protocols/cursor-shape-v1-enum.h> | ||||
| 
 | ||||
| /**
 | ||||
|  * Manager for the cursor-shape-v1 protocol. | ||||
|  |  | |||
|  | @ -12,6 +12,12 @@ | |||
| #include <wayland-server-core.h> | ||||
| #include <wlr/types/wlr_seat.h> | ||||
| 
 | ||||
| /**
 | ||||
|  * Deprecated: this protocol is legacy and superseded by ext-data-control-v1. | ||||
|  * The implementation will be dropped in a future wlroots version. | ||||
|  * | ||||
|  * Consider using `wlr_ext_data_control_manager_v1` as a replacement. | ||||
|  */ | ||||
| struct wlr_data_control_manager_v1 { | ||||
| 	struct wl_global *global; | ||||
| 	struct wl_list devices; // wlr_data_control_device_v1.link
 | ||||
|  |  | |||
|  | @ -62,8 +62,6 @@ struct wlr_drm_lease_connector_v1 { | |||
| 
 | ||||
| 	struct wlr_output *output; | ||||
| 	struct wlr_drm_lease_device_v1 *device; | ||||
| 	/** NULL if no client is currently leasing this connector */ | ||||
| 	struct wlr_drm_lease_v1 *active_lease; | ||||
| 
 | ||||
| 	struct wl_list link; // wlr_drm_lease_device_v1.connectors
 | ||||
| 
 | ||||
|  | @ -93,9 +91,6 @@ struct wlr_drm_lease_v1 { | |||
| 
 | ||||
| 	struct wlr_drm_lease_device_v1 *device; | ||||
| 
 | ||||
| 	struct wlr_drm_lease_connector_v1 **connectors; | ||||
| 	size_t n_connectors; | ||||
| 
 | ||||
| 	struct wl_list link; // wlr_drm_lease_device_v1.leases
 | ||||
| 
 | ||||
| 	void *data; | ||||
|  |  | |||
|  | @ -21,7 +21,9 @@ struct wlr_ext_data_control_manager_v1 { | |||
| 		struct wl_signal new_device; // wlr_ext_data_control_device_v1
 | ||||
| 	} events; | ||||
| 
 | ||||
| 	struct wl_listener display_destroy; | ||||
| 	struct { | ||||
| 		struct wl_listener display_destroy; | ||||
| 	} WLR_PRIVATE; | ||||
| }; | ||||
| 
 | ||||
| struct wlr_ext_data_control_device_v1 { | ||||
|  | @ -33,9 +35,11 @@ struct wlr_ext_data_control_device_v1 { | |||
| 	struct wl_resource *selection_offer_resource; // current selection offer
 | ||||
| 	struct wl_resource *primary_selection_offer_resource; // current primary selection offer
 | ||||
| 
 | ||||
| 	struct wl_listener seat_destroy; | ||||
| 	struct wl_listener seat_set_selection; | ||||
| 	struct wl_listener seat_set_primary_selection; | ||||
| 	struct { | ||||
| 		struct wl_listener seat_destroy; | ||||
| 		struct wl_listener seat_set_selection; | ||||
| 		struct wl_listener seat_set_primary_selection; | ||||
| 	} WLR_PRIVATE; | ||||
| }; | ||||
| 
 | ||||
| struct wlr_ext_data_control_manager_v1 *wlr_ext_data_control_manager_v1_create( | ||||
|  |  | |||
|  | @ -10,9 +10,9 @@ | |||
| #define WLR_TYPES_WLR_EXT_IMAGE_COPY_CAPTURE_V1_H | ||||
| 
 | ||||
| #include <pixman.h> | ||||
| #include <wayland-server.h> | ||||
| #include <wayland-server-protocol.h> | ||||
| #include <wayland-protocols/ext-image-copy-capture-v1-enum.h> | ||||
| #include <time.h> | ||||
| #include "ext-image-copy-capture-v1-protocol.h" | ||||
| 
 | ||||
| struct wlr_renderer; | ||||
| 
 | ||||
|  |  | |||
							
								
								
									
										28
									
								
								include/wlr/types/wlr_fixes.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								include/wlr/types/wlr_fixes.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,28 @@ | |||
| /*
 | ||||
|  * This an unstable interface of wlroots. No guarantees are made regarding the | ||||
|  * future consistency of this API. | ||||
|  */ | ||||
| #ifndef WLR_USE_UNSTABLE | ||||
| #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" | ||||
| #endif | ||||
| 
 | ||||
| #ifndef WLR_TYPES_WLR_FIXES_H | ||||
| #define WLR_TYPES_WLR_FIXES_H | ||||
| 
 | ||||
| #include <wayland-server-core.h> | ||||
| 
 | ||||
| struct wlr_fixes { | ||||
| 	struct wl_global *global; | ||||
| 
 | ||||
| 	struct { | ||||
| 		struct wl_signal destroy; | ||||
| 	} events; | ||||
| 
 | ||||
| 	struct { | ||||
| 		struct wl_listener display_destroy; | ||||
| 	} WLR_PRIVATE; | ||||
| }; | ||||
| 
 | ||||
| struct wlr_fixes *wlr_fixes_create(struct wl_display *display, uint32_t version); | ||||
| 
 | ||||
| #endif | ||||
|  | @ -10,6 +10,11 @@ struct wlr_gamma_control_manager_v1 { | |||
| 	struct wl_global *global; | ||||
| 	struct wl_list controls; // wlr_gamma_control_v1.link
 | ||||
| 
 | ||||
| 	// Fallback to use when an struct wlr_output doesn't support gamma LUTs.
 | ||||
| 	// Can be used to apply gamma LUTs via a struct wlr_renderer. Leave zero to
 | ||||
| 	// indicate that the fallback is unsupported.
 | ||||
| 	size_t fallback_gamma_size; | ||||
| 
 | ||||
| 	struct { | ||||
| 		struct wl_signal destroy; | ||||
| 		struct wl_signal set_gamma; // struct wlr_gamma_control_manager_v1_set_gamma_event
 | ||||
|  | @ -49,6 +54,8 @@ struct wlr_gamma_control_v1 *wlr_gamma_control_manager_v1_get_control( | |||
| 	struct wlr_gamma_control_manager_v1 *manager, struct wlr_output *output); | ||||
| bool wlr_gamma_control_v1_apply(struct wlr_gamma_control_v1 *gamma_control, | ||||
| 	struct wlr_output_state *output_state); | ||||
| struct wlr_color_transform *wlr_gamma_control_v1_get_color_transform( | ||||
| 	struct wlr_gamma_control_v1 *gamma_control); | ||||
| void wlr_gamma_control_v1_send_failed_and_destroy(struct wlr_gamma_control_v1 *gamma_control); | ||||
| 
 | ||||
| #endif | ||||
|  |  | |||
|  | @ -48,10 +48,10 @@ struct wlr_input_method_v2 { | |||
| 	struct wl_list link; | ||||
| 
 | ||||
| 	struct { | ||||
| 		struct wl_signal commit; // struct wlr_input_method_v2
 | ||||
| 		struct wl_signal commit; | ||||
| 		struct wl_signal new_popup_surface; // struct wlr_input_popup_surface_v2
 | ||||
| 		struct wl_signal grab_keyboard; // struct wlr_input_method_keyboard_grab_v2
 | ||||
| 		struct wl_signal destroy; // struct wlr_input_method_v2
 | ||||
| 		struct wl_signal destroy; | ||||
| 	} events; | ||||
| 
 | ||||
| 	struct { | ||||
|  | @ -94,8 +94,8 @@ struct wlr_input_method_manager_v2 { | |||
| 	struct wl_list input_methods; // struct wlr_input_method_v2.link
 | ||||
| 
 | ||||
| 	struct { | ||||
| 		struct wl_signal input_method; // struct wlr_input_method_v2
 | ||||
| 		struct wl_signal destroy; // struct wlr_input_method_manager_v2
 | ||||
| 		struct wl_signal new_input_method; // struct wlr_input_method_v2
 | ||||
| 		struct wl_signal destroy; | ||||
| 	} events; | ||||
| 
 | ||||
| 	struct { | ||||
|  |  | |||
|  | @ -267,6 +267,7 @@ struct wlr_output { | |||
| 	struct { | ||||
| 		struct wl_listener display_destroy; | ||||
| 		struct wlr_output_image_description image_description_value; | ||||
| 		struct wlr_color_transform *color_transform; | ||||
| 	} WLR_PRIVATE; | ||||
| }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -11,10 +11,10 @@ | |||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <wayland-server-core.h> | ||||
| #include <wayland-protocols/pointer-constraints-unstable-v1-enum.h> | ||||
| #include <pixman.h> | ||||
| #include <wlr/types/wlr_compositor.h> | ||||
| #include <wlr/types/wlr_seat.h> | ||||
| #include "pointer-constraints-unstable-v1-protocol.h" | ||||
| 
 | ||||
| struct wlr_seat; | ||||
| 
 | ||||
|  |  | |||
|  | @ -43,6 +43,7 @@ struct wlr_scene_output_layout; | |||
| struct wlr_presentation; | ||||
| struct wlr_linux_dmabuf_v1; | ||||
| struct wlr_gamma_control_manager_v1; | ||||
| struct wlr_color_manager_v1; | ||||
| struct wlr_output_state; | ||||
| 
 | ||||
| typedef bool (*wlr_scene_buffer_point_accepts_input_func_t)( | ||||
|  | @ -102,11 +103,13 @@ struct wlr_scene { | |||
| 	// May be NULL
 | ||||
| 	struct wlr_linux_dmabuf_v1 *linux_dmabuf_v1; | ||||
| 	struct wlr_gamma_control_manager_v1 *gamma_control_manager_v1; | ||||
| 	struct wlr_color_manager_v1 *color_manager_v1; | ||||
| 
 | ||||
| 	struct { | ||||
| 		struct wl_listener linux_dmabuf_v1_destroy; | ||||
| 		struct wl_listener gamma_control_manager_v1_destroy; | ||||
| 		struct wl_listener gamma_control_manager_v1_set_gamma; | ||||
| 		struct wl_listener color_manager_v1_destroy; | ||||
| 
 | ||||
| 		enum wlr_scene_debug_damage_option debug_damage_option; | ||||
| 		bool direct_scanout; | ||||
|  | @ -249,6 +252,11 @@ struct wlr_scene_output { | |||
| 
 | ||||
| 		bool gamma_lut_changed; | ||||
| 		struct wlr_gamma_control_v1 *gamma_lut; | ||||
| 		struct wlr_color_transform *gamma_lut_color_transform; | ||||
| 
 | ||||
| 		struct wlr_color_transform *prev_gamma_lut_color_transform; | ||||
| 		struct wlr_color_transform *prev_supplied_color_transform; | ||||
| 		struct wlr_color_transform *prev_combined_color_transform; | ||||
| 
 | ||||
| 		struct wl_listener output_commit; | ||||
| 		struct wl_listener output_damage; | ||||
|  | @ -366,6 +374,13 @@ void wlr_scene_set_linux_dmabuf_v1(struct wlr_scene *scene, | |||
| void wlr_scene_set_gamma_control_manager_v1(struct wlr_scene *scene, | ||||
| 	struct wlr_gamma_control_manager_v1 *gamma_control); | ||||
| 
 | ||||
| /**
 | ||||
|  * Handles color_management_v1 feedback for all surfaces in the scene. | ||||
|  * | ||||
|  * Asserts that a struct wlr_color_manager_v1 hasn't already been set for the scene. | ||||
|  */ | ||||
| void wlr_scene_set_color_manager_v1(struct wlr_scene *scene, struct wlr_color_manager_v1 *manager); | ||||
| 
 | ||||
| /**
 | ||||
|  * Add a node displaying nothing but its children. | ||||
|  */ | ||||
|  |  | |||
|  | @ -14,6 +14,13 @@ | |||
| #include <wlr/types/wlr_buffer.h> | ||||
| #include <wlr/util/box.h> | ||||
| 
 | ||||
| /**
 | ||||
|  * Deprecated: this protocol is deprecated and superseded by ext-image-copy-capture-v1. | ||||
|  * The implementation will be dropped in a future wlroots version. | ||||
|  * | ||||
|  * Consider using `wlr_ext_image_capture_source_v1` instead. | ||||
|  */ | ||||
| 
 | ||||
| struct wlr_screencopy_manager_v1 { | ||||
| 	struct wl_global *global; | ||||
| 	struct wl_list frames; // wlr_screencopy_frame_v1.link
 | ||||
|  |  | |||
|  | @ -10,10 +10,9 @@ | |||
| #define WLR_TYPES_WLR_TABLET_V2_H | ||||
| 
 | ||||
| #include <wayland-server-core.h> | ||||
| #include <wayland-protocols/tablet-v2-enum.h> | ||||
| #include <wlr/types/wlr_seat.h> | ||||
| 
 | ||||
| #include "tablet-v2-protocol.h" | ||||
| 
 | ||||
| /* This can probably be even lower,the tools don't have a lot of buttons */ | ||||
| #define WLR_TABLET_V2_TOOL_BUTTONS_CAP 16 | ||||
| 
 | ||||
|  |  | |||
|  | @ -11,11 +11,9 @@ | |||
| 
 | ||||
| #include <stdint.h> | ||||
| #include <wayland-server-core.h> | ||||
| #include <wayland-server-protocol.h> | ||||
| #include <wayland-protocols/tearing-control-v1-enum.h> | ||||
| #include <wlr/types/wlr_compositor.h> | ||||
| 
 | ||||
| #include "tearing-control-v1-protocol.h" | ||||
| 
 | ||||
| struct wlr_tearing_control_v1 { | ||||
| 	struct wl_client *client; | ||||
| 	struct wl_list link; | ||||
|  |  | |||
|  | @ -10,10 +10,10 @@ | |||
| #define WLR_TYPES_WLR_XDG_SHELL_H | ||||
| 
 | ||||
| #include <wayland-server-core.h> | ||||
| #include <wayland-protocols/xdg-shell-enum.h> | ||||
| #include <wlr/types/wlr_compositor.h> | ||||
| #include <wlr/types/wlr_seat.h> | ||||
| #include <wlr/util/box.h> | ||||
| #include "xdg-shell-protocol.h" | ||||
| 
 | ||||
| struct wlr_xdg_shell { | ||||
| 	struct wl_global *global; | ||||
|  |  | |||
							
								
								
									
										48
									
								
								include/wlr/util/rectpack.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								include/wlr/util/rectpack.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,48 @@ | |||
| /*
 | ||||
|  * This an unstable interface of wlroots. No guarantees are made regarding the | ||||
|  * future consistency of this API. | ||||
|  */ | ||||
| #ifndef WLR_USE_UNSTABLE | ||||
| #error "Add -DWLR_USE_UNSTABLE to enable unstable wlroots features" | ||||
| #endif | ||||
| 
 | ||||
| #ifndef WLR_UTIL_RECTPACK_H | ||||
| #define WLR_UTIL_RECTPACK_H | ||||
| 
 | ||||
| #include <pixman.h> | ||||
| #include <wayland-server-protocol.h> | ||||
| 
 | ||||
| #include <wlr/util/box.h> | ||||
| #include <wlr/util/edges.h> | ||||
| 
 | ||||
| struct wlr_layer_surface_v1; | ||||
| 
 | ||||
| struct wlr_rectpack_rules { | ||||
| 	// If true, the corresponding side will be stretched to take all available area
 | ||||
| 	bool grow_width, grow_height; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * Place a rectangle within bounds so that it doesn't intersect with the | ||||
|  * exclusive region. | ||||
|  * | ||||
|  * exclusive may be NULL. | ||||
|  * | ||||
|  * Returns false if there's not enough space or on memory allocation error. | ||||
|  */ | ||||
| bool wlr_rectpack_place(const struct wlr_box *bounds, pixman_region32_t *exclusive, | ||||
| 		const struct wlr_box *box, struct wlr_rectpack_rules *rules, struct wlr_box *out); | ||||
| 
 | ||||
| /**
 | ||||
|  * Place a struct wlr_layer_surface_v1 within bounds so that it doesn't | ||||
|  * intersect with the exclusive region. If the layer surface has exclusive zone, | ||||
|  * the corresponding area will be added to the exclusive region. | ||||
|  * | ||||
|  * Returns false if there's not enough space or on memory allocation error, in | ||||
|  * which case the exclusive region is left intact. | ||||
|  */ | ||||
| bool wlr_rectpack_place_wlr_layer_surface_v1(const struct wlr_box *bounds, | ||||
| 		pixman_region32_t *exclusive, struct wlr_layer_surface_v1 *surface, struct wlr_box *out); | ||||
| 
 | ||||
| 
 | ||||
| #endif | ||||
|  | @ -164,6 +164,7 @@ struct wlr_xwm { | |||
| 	struct wl_listener drop_focus_destroy; | ||||
| }; | ||||
| 
 | ||||
| // xwm_create takes ownership of wm_fd and will close it under all circumstances.
 | ||||
| struct wlr_xwm *xwm_create(struct wlr_xwayland *wlr_xwayland, int wm_fd); | ||||
| 
 | ||||
| void xwm_destroy(struct wlr_xwm *xwm); | ||||
|  |  | |||
|  | @ -86,7 +86,7 @@ internal_features = { | |||
| internal_config = configuration_data() | ||||
| 
 | ||||
| wayland_kwargs = { | ||||
| 	'version': '>=1.23.1', | ||||
| 	'version': '>=1.24.0', | ||||
| 	'fallback': 'wayland', | ||||
| 	'default_options': [ | ||||
| 		'tests=false', | ||||
|  |  | |||
|  | @ -1,9 +1,10 @@ | |||
| wayland_protos = dependency('wayland-protocols', | ||||
| 	version: '>=1.43', | ||||
| 	version: '>=1.44', | ||||
| 	fallback: 'wayland-protocols', | ||||
| 	default_options: ['tests=false'], | ||||
| ) | ||||
| wl_protocol_dir = wayland_protos.get_variable('pkgdatadir') | ||||
| wlr_deps += wayland_protos | ||||
| 
 | ||||
| wayland_scanner_dep = dependency('wayland-scanner', | ||||
| 	kwargs: wayland_kwargs, | ||||
|  |  | |||
|  | @ -43,7 +43,7 @@ | |||
| 
 | ||||
|     <request name="capture_output"> | ||||
|       <description summary="capture a frame from an output"> | ||||
|         Capture the next frame of a an entire output. | ||||
|         Capture the next frame of an entire output. | ||||
|       </description> | ||||
|       <arg name="frame" type="new_id" interface="zwlr_export_dmabuf_frame_v1"/> | ||||
|       <arg name="overlay_cursor" type="int" | ||||
|  | @ -136,7 +136,7 @@ | |||
|       <arg name="stride" type="uint" | ||||
|            summary="line size in bytes"/> | ||||
|       <arg name="plane_index" type="uint" | ||||
|            summary="index of the the plane the data in the object applies to"/> | ||||
|            summary="index of the plane the data in the object applies to"/> | ||||
|     </event> | ||||
| 
 | ||||
|     <event name="ready"> | ||||
|  |  | |||
|  | @ -58,7 +58,7 @@ | |||
|       </description> | ||||
|     </request> | ||||
| 
 | ||||
|     <event name="finished"> | ||||
|     <event name="finished" type="destructor"> | ||||
|       <description summary="the compositor has finished with the toplevel manager"> | ||||
|         This event indicates that the compositor is done sending events to the | ||||
|         zwlr_foreign_toplevel_manager_v1. The server will destroy the object | ||||
|  |  | |||
|  | @ -72,7 +72,7 @@ | |||
|       tables. At any time the compositor can send a failed event indicating that | ||||
|       this object is no longer valid. | ||||
| 
 | ||||
|       There must always be at most one gamma control object per output, which | ||||
|       There can only be at most one gamma control object per output, which | ||||
|       has exclusive access to this particular output. When the gamma control | ||||
|       object is destroyed, the gamma table is restored to its original value. | ||||
|     </description> | ||||
|  |  | |||
|  | @ -156,8 +156,8 @@ | |||
|         not assume that the name is a reflection of an underlying DRM | ||||
|         connector, X11 connection, etc. | ||||
| 
 | ||||
|         If the compositor implements the xdg-output protocol and this head is | ||||
|         enabled, the xdg_output.name event must report the same name. | ||||
|         If this head matches a wl_output, the wl_output.name event must report | ||||
|         the same name. | ||||
| 
 | ||||
|         The name event is sent after a wlr_output_head object is created. This | ||||
|         event is only sent once per object, and the name does not change over | ||||
|  | @ -176,8 +176,8 @@ | |||
|         the make, model, serial of the underlying DRM connector or the display | ||||
|         name of the underlying X11 connection, etc. | ||||
| 
 | ||||
|         If the compositor implements xdg-output and this head is enabled, | ||||
|         the xdg_output.description must report the same description. | ||||
|         If this head matches a wl_output, the wl_output.description event must | ||||
|         report the same name. | ||||
| 
 | ||||
|         The description event is sent after a wlr_output_head object is created. | ||||
|         This event is only sent once per object, and the description does not | ||||
|  | @ -191,6 +191,10 @@ | |||
|         This event describes the physical size of the head. This event is only | ||||
|         sent if the head has a physical size (e.g. is not a projector or a | ||||
|         virtual device). | ||||
| 
 | ||||
|         The physical size event is sent after a wlr_output_head object is created. This | ||||
|         event is only sent once per object, and the physical size does not change over | ||||
|         the lifetime of the wlr_output_head object. | ||||
|       </description> | ||||
|       <arg name="width" type="int" summary="width in millimeters of the output"/> | ||||
|       <arg name="height" type="int" summary="height in millimeters of the output"/> | ||||
|  | @ -264,9 +268,6 @@ | |||
|       <description summary="head manufacturer"> | ||||
|         This event describes the manufacturer of the head. | ||||
| 
 | ||||
|         This must report the same make as the wl_output interface does in its | ||||
|         geometry event. | ||||
| 
 | ||||
|         Together with the model and serial_number events the purpose is to | ||||
|         allow clients to recognize heads from previous sessions and for example | ||||
|         load head-specific configurations back. | ||||
|  | @ -278,6 +279,10 @@ | |||
|         identify the head by available information from other events but should | ||||
|         be aware that there is an increased risk of false positives. | ||||
| 
 | ||||
|         If sent, the make event is sent after a wlr_output_head object is | ||||
|         created and only sent once per object. The make does not change over | ||||
|         the lifetime of the wlr_output_head object. | ||||
| 
 | ||||
|         It is not recommended to display the make string in UI to users. For | ||||
|         that the string provided by the description event should be preferred. | ||||
|       </description> | ||||
|  | @ -288,9 +293,6 @@ | |||
|       <description summary="head model"> | ||||
|         This event describes the model of the head. | ||||
| 
 | ||||
|         This must report the same model as the wl_output interface does in its | ||||
|         geometry event. | ||||
| 
 | ||||
|         Together with the make and serial_number events the purpose is to | ||||
|         allow clients to recognize heads from previous sessions and for example | ||||
|         load head-specific configurations back. | ||||
|  | @ -302,6 +304,10 @@ | |||
|         identify the head by available information from other events but should | ||||
|         be aware that there is an increased risk of false positives. | ||||
| 
 | ||||
|         If sent, the model event is sent after a wlr_output_head object is | ||||
|         created and only sent once per object. The model does not change over | ||||
|         the lifetime of the wlr_output_head object. | ||||
| 
 | ||||
|         It is not recommended to display the model string in UI to users. For | ||||
|         that the string provided by the description event should be preferred. | ||||
|       </description> | ||||
|  | @ -323,6 +329,10 @@ | |||
|         available information from other events but should be aware that there | ||||
|         is an increased risk of false positives. | ||||
| 
 | ||||
|         If sent, the serial number event is sent after a wlr_output_head object | ||||
|         is created and only sent once per object. The serial number does not | ||||
|         change over the lifetime of the wlr_output_head object. | ||||
| 
 | ||||
|         It is not recommended to display the serial_number string in UI to | ||||
|         users. For that the string provided by the description event should be | ||||
|         preferred. | ||||
|  |  | |||
|  | @ -50,7 +50,7 @@ | |||
| 
 | ||||
|     <request name="get_output_power"> | ||||
|       <description summary="get a power management for an output"> | ||||
|         Create a output power management mode control that can be used to | ||||
|         Create an output power management mode control that can be used to | ||||
|         adjust the power management mode for a given output. | ||||
|       </description> | ||||
|       <arg name="id" type="new_id" interface="zwlr_output_power_v1"/> | ||||
|  | @ -79,7 +79,7 @@ | |||
|     </enum> | ||||
| 
 | ||||
|     <enum name="error"> | ||||
|       <entry name="invalid_mode" value="1" summary="inexistent power save mode"/> | ||||
|       <entry name="invalid_mode" value="1" summary="nonexistent power save mode"/> | ||||
|     </enum> | ||||
| 
 | ||||
|     <request name="set_mode"> | ||||
|  |  | |||
|  | @ -88,7 +88,7 @@ | |||
|       supported buffer type. The "buffer_done" event is sent afterwards to | ||||
|       indicate that all supported buffer types have been enumerated. The client | ||||
|       will then be able to send a "copy" request. If the capture is successful, | ||||
|       the compositor will send a "flags" followed by a "ready" event. | ||||
|       the compositor will send a "flags" event followed by a "ready" event. | ||||
| 
 | ||||
|       For objects version 2 or lower, wl_shm buffers are always supported, ie. | ||||
|       the "buffer" event is guaranteed to be sent. | ||||
|  | @ -114,12 +114,12 @@ | |||
| 
 | ||||
|     <request name="copy"> | ||||
|       <description summary="copy the frame"> | ||||
|         Copy the frame to the supplied buffer. The buffer must have a the | ||||
|         Copy the frame to the supplied buffer. The buffer must have the | ||||
|         correct size, see zwlr_screencopy_frame_v1.buffer and | ||||
|         zwlr_screencopy_frame_v1.linux_dmabuf. The buffer needs to have a | ||||
|         supported format. | ||||
| 
 | ||||
|         If the frame is successfully copied, a "flags" and a "ready" events are | ||||
|         If the frame is successfully copied, "flags" and "ready" events are | ||||
|         sent. Otherwise, a "failed" event is sent. | ||||
|       </description> | ||||
|       <arg name="buffer" type="object" interface="wl_buffer"/> | ||||
|  | @ -147,8 +147,7 @@ | |||
|     <event name="ready"> | ||||
|       <description summary="indicates frame is available for reading"> | ||||
|         Called as soon as the frame is copied, indicating it is available | ||||
|         for reading. This event includes the time at which presentation happened | ||||
|         at. | ||||
|         for reading. This event includes the time at which the presentation took place. | ||||
| 
 | ||||
|         The timestamp is expressed as tv_sec_hi, tv_sec_lo, tv_nsec triples, | ||||
|         each component being an unsigned 32-bit value. Whole seconds are in | ||||
|  |  | |||
							
								
								
									
										32
									
								
								release.sh
									
										
									
									
									
										Executable file
									
								
							
							
						
						
									
										32
									
								
								release.sh
									
										
									
									
									
										Executable file
									
								
							|  | @ -0,0 +1,32 @@ | |||
| #!/bin/sh -eu | ||||
| 
 | ||||
| prev=$(git describe --tags --abbrev=0) | ||||
| next=$(meson rewrite kwargs info project / | jq -r '.kwargs["project#/"].version') | ||||
| 
 | ||||
| case "$next" in | ||||
| *-dev) | ||||
| 	echo "This is a development version" | ||||
| 	exit 1 | ||||
| 	;; | ||||
| esac | ||||
| 
 | ||||
| if [ "$prev" = "$next" ]; then | ||||
| 	echo "Version not bumped in meson.build" | ||||
| 	exit 1 | ||||
| fi | ||||
| 
 | ||||
| if ! git diff-index --quiet HEAD -- meson.build; then | ||||
| 	echo "meson.build not committed" | ||||
| 	exit 1 | ||||
| fi | ||||
| 
 | ||||
| shortlog="$(git shortlog --no-merges "$prev..")" | ||||
| (echo "wlroots $next"; echo ""; echo "$shortlog") | git tag "$next" -ase -F - | ||||
| 
 | ||||
| prefix=wlroots-$next | ||||
| archive=$prefix.tar.gz | ||||
| git archive --prefix="$prefix/" -o "$archive" "$next" | ||||
| gpg --output "$archive".sig --detach-sig "$archive" | ||||
| 
 | ||||
| git push --follow-tags | ||||
| glab release create "$next" "$archive" "$archive.sig" --notes "" | ||||
|  | @ -97,7 +97,6 @@ static struct wlr_gbm_buffer *create_buffer(struct wlr_gbm_allocator *alloc, | |||
| 	} | ||||
| 	wlr_buffer_init(&buffer->base, &buffer_impl, width, height); | ||||
| 	buffer->gbm_bo = bo; | ||||
| 	wl_list_insert(&alloc->buffers, &buffer->link); | ||||
| 
 | ||||
| 	if (!export_gbm_bo(bo, &buffer->dmabuf)) { | ||||
| 		free(buffer); | ||||
|  | @ -112,6 +111,8 @@ static struct wlr_gbm_buffer *create_buffer(struct wlr_gbm_allocator *alloc, | |||
| 		buffer->dmabuf.modifier = fallback_modifier; | ||||
| 	} | ||||
| 
 | ||||
| 	wl_list_insert(&alloc->buffers, &buffer->link); | ||||
| 
 | ||||
| 	char *format_name = drmGetFormatName(buffer->dmabuf.format); | ||||
| 	char *modifier_name = drmGetFormatModifierName(buffer->dmabuf.modifier); | ||||
| 	wlr_log(WLR_DEBUG, "Allocated %dx%d GBM buffer " | ||||
|  |  | |||
							
								
								
									
										130
									
								
								render/color.c
									
										
									
									
									
								
							
							
						
						
									
										130
									
								
								render/color.c
									
										
									
									
									
								
							|  | @ -62,6 +62,33 @@ struct wlr_color_transform *wlr_color_transform_init_lut_3x1d(size_t dim, | |||
| 	return &tx->base; | ||||
| } | ||||
| 
 | ||||
| struct wlr_color_transform *wlr_color_transform_init_pipeline( | ||||
| 		struct wlr_color_transform **transforms, size_t len) { | ||||
| 	assert(len > 0); | ||||
| 
 | ||||
| 	struct wlr_color_transform **copy = calloc(len, sizeof(copy[0])); | ||||
| 	if (copy == NULL) { | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	struct wlr_color_transform_pipeline *tx = calloc(1, sizeof(*tx)); | ||||
| 	if (!tx) { | ||||
| 		free(copy); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	wlr_color_transform_init(&tx->base, COLOR_TRANSFORM_PIPELINE); | ||||
| 
 | ||||
| 	// TODO: flatten nested pipeline transforms
 | ||||
| 	for (size_t i = 0; i < len; i++) { | ||||
| 		copy[i] = wlr_color_transform_ref(transforms[i]); | ||||
| 	} | ||||
| 
 | ||||
| 	tx->transforms = copy; | ||||
| 	tx->len = len; | ||||
| 
 | ||||
| 	return &tx->base; | ||||
| } | ||||
| 
 | ||||
| static void color_transform_destroy(struct wlr_color_transform *tr) { | ||||
| 	switch (tr->type) { | ||||
| 	case COLOR_TRANSFORM_INVERSE_EOTF: | ||||
|  | @ -73,6 +100,14 @@ static void color_transform_destroy(struct wlr_color_transform *tr) { | |||
| 		struct wlr_color_transform_lut_3x1d *lut_3x1d = color_transform_lut_3x1d_from_base(tr); | ||||
| 		free(lut_3x1d->lut_3x1d); | ||||
| 		break; | ||||
| 	case COLOR_TRANSFORM_PIPELINE:; | ||||
| 		struct wlr_color_transform_pipeline *pipeline = | ||||
| 			wl_container_of(tr, pipeline, base); | ||||
| 		for (size_t i = 0; i < pipeline->len; i++) { | ||||
| 			wlr_color_transform_unref(pipeline->transforms[i]); | ||||
| 		} | ||||
| 		free(pipeline->transforms); | ||||
| 		break; | ||||
| 	} | ||||
| 	wlr_addon_set_finish(&tr->addons); | ||||
| 	free(tr); | ||||
|  | @ -108,8 +143,67 @@ struct wlr_color_transform_lut_3x1d *color_transform_lut_3x1d_from_base( | |||
| 	return lut_3x1d; | ||||
| } | ||||
| 
 | ||||
| static float srgb_eval_inverse_eotf(float x) { | ||||
| 	// See https://www.w3.org/Graphics/Color/srgb
 | ||||
| 	if (x <= 0.0031308) { | ||||
| 		return 12.92 * x; | ||||
| 	} else { | ||||
| 		return 1.055 * powf(x, 1.0 / 2.4) - 0.055; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static float st2084_pq_eval_inverse_eotf(float x) { | ||||
| 	// H.273 TransferCharacteristics code point 16
 | ||||
| 	float c1 = 0.8359375; | ||||
| 	float c2 = 18.8515625; | ||||
| 	float c3 = 18.6875; | ||||
| 	float m = 78.84375; | ||||
| 	float n = 0.1593017578125; | ||||
| 	if (x < 0) { | ||||
| 		x = 0; | ||||
| 	} | ||||
| 	if (x > 1) { | ||||
| 		x = 1; | ||||
| 	} | ||||
| 	float pow_n = powf(x, n); | ||||
| 	return powf((c1 + c2 * pow_n) / (1 + c3 * pow_n), m); | ||||
| } | ||||
| 
 | ||||
| static float bt1886_eval_inverse_eotf(float x) { | ||||
| 	float lb = powf(0.0001, 1.0 / 2.4); | ||||
| 	float lw = powf(1.0, 1.0 / 2.4); | ||||
| 	float a  = powf(lw - lb, 2.4); | ||||
| 	float b  = lb / (lw - lb); | ||||
| 	return powf(x / a, 1.0 / 2.4) - b; | ||||
| } | ||||
| 
 | ||||
| static float transfer_function_eval_inverse_eotf( | ||||
| 		enum wlr_color_transfer_function tf, float x) { | ||||
| 	switch (tf) { | ||||
| 	case WLR_COLOR_TRANSFER_FUNCTION_SRGB: | ||||
| 		return srgb_eval_inverse_eotf(x); | ||||
| 	case WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ: | ||||
| 		return st2084_pq_eval_inverse_eotf(x); | ||||
| 	case WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR: | ||||
| 		return x; | ||||
| 	case WLR_COLOR_TRANSFER_FUNCTION_GAMMA22: | ||||
| 		return powf(x, 1.0 / 2.2); | ||||
| 	case WLR_COLOR_TRANSFER_FUNCTION_BT1886: | ||||
| 		return bt1886_eval_inverse_eotf(x); | ||||
| 	} | ||||
| 	abort(); // unreachable
 | ||||
| } | ||||
| 
 | ||||
| static void color_transform_inverse_eotf_eval( | ||||
| 		struct wlr_color_transform_inverse_eotf *tr, | ||||
| 		float out[static 3], const float in[static 3]) { | ||||
| 	for (size_t i = 0; i < 3; i++) { | ||||
| 		out[i] = transfer_function_eval_inverse_eotf(tr->tf, in[i]); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static float lut_1d_get(const uint16_t *lut, size_t len, size_t i) { | ||||
| 	if (i > len) { | ||||
| 	if (i >= len) { | ||||
| 		i = len - 1; | ||||
| 	} | ||||
| 	return (float) lut[i] / UINT16_MAX; | ||||
|  | @ -125,13 +219,38 @@ static float lut_1d_eval(const uint16_t *lut, size_t len, float x) { | |||
| 	return a * (1 - frac_part) + b * frac_part; | ||||
| } | ||||
| 
 | ||||
| void color_transform_lut_3x1d_eval(struct wlr_color_transform_lut_3x1d *tr, | ||||
| static void color_transform_lut_3x1d_eval(struct wlr_color_transform_lut_3x1d *tr, | ||||
| 		float out[static 3], const float in[static 3]) { | ||||
| 	for (size_t i = 0; i < 3; i++) { | ||||
| 		out[i] = lut_1d_eval(&tr->lut_3x1d[tr->dim * i], tr->dim, in[i]); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void wlr_color_transform_eval(struct wlr_color_transform *tr, | ||||
| 		float out[static 3], const float in[static 3]) { | ||||
| 	switch (tr->type) { | ||||
| 	case COLOR_TRANSFORM_INVERSE_EOTF: | ||||
| 		color_transform_inverse_eotf_eval(wlr_color_transform_inverse_eotf_from_base(tr), out, in); | ||||
| 		break; | ||||
| 	case COLOR_TRANSFORM_LCMS2: | ||||
| 		color_transform_lcms2_eval(color_transform_lcms2_from_base(tr), out, in); | ||||
| 		break; | ||||
| 	case COLOR_TRANSFORM_LUT_3X1D: | ||||
| 		color_transform_lut_3x1d_eval(color_transform_lut_3x1d_from_base(tr), out, in); | ||||
| 		break; | ||||
| 	case COLOR_TRANSFORM_PIPELINE:; | ||||
| 		struct wlr_color_transform_pipeline *pipeline = | ||||
| 			wl_container_of(tr, pipeline, base); | ||||
| 		float color[3]; | ||||
| 		memcpy(color, in, sizeof(color)); | ||||
| 		for (size_t i = 0; i < pipeline->len; i++) { | ||||
| 			wlr_color_transform_eval(pipeline->transforms[i], color, color); | ||||
| 		} | ||||
| 		memcpy(out, color, sizeof(color)); | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void wlr_color_primaries_from_named(struct wlr_color_primaries *out, | ||||
| 		enum wlr_color_named_primaries named) { | ||||
| 	switch (named) { | ||||
|  | @ -202,6 +321,13 @@ void wlr_color_transfer_function_get_default_luminance(enum wlr_color_transfer_f | |||
| 			.reference = 203, | ||||
| 		}; | ||||
| 		break; | ||||
| 	case WLR_COLOR_TRANSFER_FUNCTION_BT1886: | ||||
| 		*lum = (struct wlr_color_luminances){ | ||||
| 			.min = 0.01, | ||||
| 			.max = 100, | ||||
| 			.reference = 100, | ||||
| 		}; | ||||
| 		break; | ||||
| 	default: | ||||
| 		*lum = (struct wlr_color_luminances){ | ||||
| 			.min = 0.2, | ||||
|  |  | |||
|  | @ -57,10 +57,7 @@ static void convert_pixman_box_to_vk_rect(const pixman_box32_t *box, VkRect2D *r | |||
| } | ||||
| 
 | ||||
| static float color_to_linear(float non_linear) { | ||||
| 	// See https://www.w3.org/Graphics/Color/srgb
 | ||||
| 	return (non_linear > 0.04045) ? | ||||
| 		pow((non_linear + 0.055) / 1.055, 2.4) : | ||||
| 		non_linear / 12.92; | ||||
| 	return pow(non_linear, 2.2); | ||||
| } | ||||
| 
 | ||||
| static float color_to_linear_premult(float non_linear, float alpha) { | ||||
|  | @ -175,7 +172,7 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { | |||
| 	assert(stage_cb != NULL); | ||||
| 	renderer->stage.cb = NULL; | ||||
| 
 | ||||
| 	if (!pass->srgb_pathway) { | ||||
| 	if (pass->two_pass) { | ||||
| 		// Apply output shader to map blend image to actual output image
 | ||||
| 		vkCmdNextSubpass(render_cb->vk, VK_SUBPASS_CONTENTS_INLINE); | ||||
| 
 | ||||
|  | @ -227,9 +224,9 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { | |||
| 
 | ||||
| 		VkPipeline pipeline = VK_NULL_HANDLE; | ||||
| 		if (pass->color_transform && pass->color_transform->type != COLOR_TRANSFORM_INVERSE_EOTF) { | ||||
| 			pipeline = render_buffer->plain.render_setup->output_pipe_lut3d; | ||||
| 			pipeline = render_buffer->two_pass.render_setup->output_pipe_lut3d; | ||||
| 		} else { | ||||
| 			enum wlr_color_transfer_function tf = WLR_COLOR_TRANSFER_FUNCTION_SRGB; | ||||
| 			enum wlr_color_transfer_function tf = WLR_COLOR_TRANSFER_FUNCTION_GAMMA22; | ||||
| 			if (pass->color_transform && pass->color_transform->type == COLOR_TRANSFORM_INVERSE_EOTF) { | ||||
| 				struct wlr_color_transform_inverse_eotf *inverse_eotf = | ||||
| 					wlr_color_transform_inverse_eotf_from_base(pass->color_transform); | ||||
|  | @ -238,13 +235,19 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { | |||
| 
 | ||||
| 			switch (tf) { | ||||
| 			case WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR: | ||||
| 				pipeline = render_buffer->plain.render_setup->output_pipe_identity; | ||||
| 				pipeline = render_buffer->two_pass.render_setup->output_pipe_identity; | ||||
| 				break; | ||||
| 			case WLR_COLOR_TRANSFER_FUNCTION_SRGB: | ||||
| 				pipeline = render_buffer->plain.render_setup->output_pipe_srgb; | ||||
| 				pipeline = render_buffer->two_pass.render_setup->output_pipe_srgb; | ||||
| 				break; | ||||
| 			case WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ: | ||||
| 				pipeline = render_buffer->plain.render_setup->output_pipe_pq; | ||||
| 				pipeline = render_buffer->two_pass.render_setup->output_pipe_pq; | ||||
| 				break; | ||||
| 			case WLR_COLOR_TRANSFER_FUNCTION_GAMMA22: | ||||
| 				pipeline = render_buffer->two_pass.render_setup->output_pipe_gamma22; | ||||
| 				break; | ||||
| 			case WLR_COLOR_TRANSFER_FUNCTION_BT1886: | ||||
| 				pipeline = render_buffer->two_pass.render_setup->output_pipe_bt1886; | ||||
| 				break; | ||||
| 			} | ||||
| 
 | ||||
|  | @ -268,7 +271,7 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { | |||
| 			lut_ds = renderer->output_ds_lut3d_dummy; | ||||
| 		} | ||||
| 		VkDescriptorSet ds[] = { | ||||
| 			render_buffer->plain.blend_descriptor_set, // set 0
 | ||||
| 			render_buffer->two_pass.blend_descriptor_set, // set 0
 | ||||
| 			lut_ds, // set 1
 | ||||
| 		}; | ||||
| 		size_t ds_len = sizeof(ds) / sizeof(ds[0]); | ||||
|  | @ -398,30 +401,26 @@ static bool render_pass_submit(struct wlr_render_pass *wlr_pass) { | |||
| 
 | ||||
| 	// also add acquire/release barriers for the current render buffer
 | ||||
| 	VkImageLayout src_layout = VK_IMAGE_LAYOUT_GENERAL; | ||||
| 	if (pass->srgb_pathway) { | ||||
| 		if (!render_buffer->srgb.transitioned) { | ||||
| 			src_layout = VK_IMAGE_LAYOUT_PREINITIALIZED; | ||||
| 			render_buffer->srgb.transitioned = true; | ||||
| 		} | ||||
| 	} else { | ||||
| 		if (!render_buffer->plain.transitioned) { | ||||
| 			src_layout = VK_IMAGE_LAYOUT_PREINITIALIZED; | ||||
| 			render_buffer->plain.transitioned = true; | ||||
| 		} | ||||
| 	if (!pass->render_buffer_out->transitioned) { | ||||
| 		src_layout = VK_IMAGE_LAYOUT_PREINITIALIZED; | ||||
| 		pass->render_buffer_out->transitioned = true; | ||||
| 	} | ||||
| 
 | ||||
| 	if (pass->two_pass) { | ||||
| 		// The render pass changes the blend image layout from
 | ||||
| 		// color attachment to read only, so on each frame, before
 | ||||
| 		// the render pass starts, we change it back
 | ||||
| 		VkImageLayout blend_src_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; | ||||
| 		if (!render_buffer->plain.blend_transitioned) { | ||||
| 		if (!render_buffer->two_pass.blend_transitioned) { | ||||
| 			blend_src_layout = VK_IMAGE_LAYOUT_UNDEFINED; | ||||
| 			render_buffer->plain.blend_transitioned = true; | ||||
| 			render_buffer->two_pass.blend_transitioned = true; | ||||
| 		} | ||||
| 
 | ||||
| 		VkImageMemoryBarrier blend_acq_barrier = { | ||||
| 			.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, | ||||
| 			.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||||
| 			.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, | ||||
| 			.image = render_buffer->plain.blend_image, | ||||
| 			.image = render_buffer->two_pass.blend_image, | ||||
| 			.oldLayout = blend_src_layout, | ||||
| 			.newLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, | ||||
| 			.srcAccessMask = VK_ACCESS_SHADER_READ_BIT, | ||||
|  | @ -618,7 +617,7 @@ error: | |||
| 
 | ||||
| static void render_pass_mark_box_updated(struct wlr_vk_render_pass *pass, | ||||
| 		const struct wlr_box *box) { | ||||
| 	if (pass->srgb_pathway) { | ||||
| 	if (!pass->two_pass) { | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
|  | @ -678,11 +677,8 @@ static void render_pass_add_rect(struct wlr_render_pass *wlr_pass, | |||
| 		wlr_matrix_project_box(matrix, &box, WL_OUTPUT_TRANSFORM_NORMAL, proj); | ||||
| 		wlr_matrix_multiply(matrix, pass->projection, matrix); | ||||
| 
 | ||||
| 		struct wlr_vk_render_format_setup *setup = pass->srgb_pathway ? | ||||
| 			pass->render_buffer->srgb.render_setup : | ||||
| 			pass->render_buffer->plain.render_setup; | ||||
| 		struct wlr_vk_pipeline *pipe = setup_get_or_create_pipeline( | ||||
| 			setup, | ||||
| 			pass->render_setup, | ||||
| 			&(struct wlr_vk_pipeline_key) { | ||||
| 				.source = WLR_VK_SHADER_SOURCE_SINGLE_COLOR, | ||||
| 				.layout = { .ycbcr_format = NULL }, | ||||
|  | @ -783,7 +779,7 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, | |||
| 
 | ||||
| 	enum wlr_color_transfer_function tf = options->transfer_function; | ||||
| 	if (tf == 0) { | ||||
| 		tf = WLR_COLOR_TRANSFER_FUNCTION_SRGB; | ||||
| 		tf = WLR_COLOR_TRANSFER_FUNCTION_GAMMA22; | ||||
| 	} | ||||
| 
 | ||||
| 	bool srgb_image_view = false; | ||||
|  | @ -803,13 +799,16 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, | |||
| 	case WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ: | ||||
| 		tex_transform = WLR_VK_TEXTURE_TRANSFORM_ST2084_PQ; | ||||
| 		break; | ||||
| 	case WLR_COLOR_TRANSFER_FUNCTION_GAMMA22: | ||||
| 		tex_transform = WLR_VK_TEXTURE_TRANSFORM_GAMMA22; | ||||
| 		break; | ||||
| 	case WLR_COLOR_TRANSFER_FUNCTION_BT1886: | ||||
| 		tex_transform = WLR_VK_TEXTURE_TRANSFORM_BT1886; | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	struct wlr_vk_render_format_setup *setup = pass->srgb_pathway ? | ||||
| 		pass->render_buffer->srgb.render_setup : | ||||
| 		pass->render_buffer->plain.render_setup; | ||||
| 	struct wlr_vk_pipeline *pipe = setup_get_or_create_pipeline( | ||||
| 		setup, | ||||
| 		pass->render_setup, | ||||
| 		&(struct wlr_vk_pipeline_key) { | ||||
| 			.source = WLR_VK_SHADER_SOURCE_TEXTURE, | ||||
| 			.layout = { | ||||
|  | @ -850,7 +849,8 @@ static void render_pass_add_texture(struct wlr_render_pass *wlr_pass, | |||
| 	} | ||||
| 
 | ||||
| 	float luminance_multiplier = 1; | ||||
| 	if (tf != WLR_COLOR_TRANSFER_FUNCTION_SRGB) { | ||||
| 	if (tf != WLR_COLOR_TRANSFER_FUNCTION_SRGB | ||||
| 			&& tf != WLR_COLOR_TRANSFER_FUNCTION_GAMMA22) { | ||||
| 		struct wlr_color_luminances src_lum, srgb_lum; | ||||
| 		wlr_color_transfer_function_get_default_luminance(tf, &src_lum); | ||||
| 		wlr_color_transfer_function_get_default_luminance( | ||||
|  | @ -964,19 +964,6 @@ static bool create_3d_lut_image(struct wlr_vk_renderer *renderer, | |||
| 	*ds = VK_NULL_HANDLE; | ||||
| 	*ds_pool = NULL; | ||||
| 
 | ||||
| 	struct wlr_color_transform_lcms2 *tr_lcms2 = NULL; | ||||
| 	struct wlr_color_transform_lut_3x1d *tr_lut_3x1d = NULL; | ||||
| 	switch (tr->type) { | ||||
| 	case COLOR_TRANSFORM_INVERSE_EOTF: | ||||
| 		abort(); // unreachable
 | ||||
| 	case COLOR_TRANSFORM_LCMS2: | ||||
| 		tr_lcms2 = color_transform_lcms2_from_base(tr); | ||||
| 		break; | ||||
| 	case COLOR_TRANSFORM_LUT_3X1D: | ||||
| 		tr_lut_3x1d = color_transform_lut_3x1d_from_base(tr); | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	// R32G32B32 is not a required Vulkan format
 | ||||
| 	// TODO: use it when available
 | ||||
| 	VkFormat format = VK_FORMAT_R32G32B32A32_SFLOAT; | ||||
|  | @ -1074,11 +1061,7 @@ static bool create_3d_lut_image(struct wlr_vk_renderer *renderer, | |||
| 					b_index * sample_range, | ||||
| 				}; | ||||
| 				float rgb_out[3]; | ||||
| 				if (tr_lcms2 != NULL) { | ||||
| 					color_transform_lcms2_eval(tr_lcms2, rgb_out, rgb_in); | ||||
| 				} else { | ||||
| 					color_transform_lut_3x1d_eval(tr_lut_3x1d, rgb_out, rgb_in); | ||||
| 				} | ||||
| 				wlr_color_transform_eval(tr, rgb_out, rgb_in); | ||||
| 
 | ||||
| 				dst[dst_offset] = rgb_out[0]; | ||||
| 				dst[dst_offset + 1] = rgb_out[1]; | ||||
|  | @ -1177,29 +1160,68 @@ static const struct wlr_addon_interface vk_color_transform_impl = { | |||
| 
 | ||||
| struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *renderer, | ||||
| 		struct wlr_vk_render_buffer *buffer, const struct wlr_buffer_pass_options *options) { | ||||
| 	bool using_srgb_pathway; | ||||
| 	uint32_t inv_eotf; | ||||
| 	if (options != NULL && options->color_transform != NULL) { | ||||
| 		using_srgb_pathway = false; | ||||
| 		if (options->color_transform->type == COLOR_TRANSFORM_INVERSE_EOTF) { | ||||
| 			struct wlr_color_transform_inverse_eotf *tr = | ||||
| 				wlr_color_transform_inverse_eotf_from_base(options->color_transform); | ||||
| 			inv_eotf = tr->tf; | ||||
| 		} else { | ||||
| 			// Color transform is not an inverse EOTF
 | ||||
| 			inv_eotf = 0; | ||||
| 		} | ||||
| 	} else { | ||||
| 		// This is the default when unspecified
 | ||||
| 		inv_eotf = WLR_COLOR_TRANSFER_FUNCTION_GAMMA22; | ||||
| 	} | ||||
| 
 | ||||
| 		if (!get_color_transform(options->color_transform, renderer)) { | ||||
| 	bool using_linear_pathway = inv_eotf == WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR; | ||||
| 	bool using_srgb_pathway = inv_eotf == WLR_COLOR_TRANSFER_FUNCTION_SRGB && | ||||
| 		buffer->srgb.out.framebuffer != VK_NULL_HANDLE; | ||||
| 	bool using_two_pass_pathway = !using_linear_pathway && !using_srgb_pathway; | ||||
| 
 | ||||
| 	if (using_linear_pathway && !buffer->linear.out.image_view) { | ||||
| 		struct wlr_dmabuf_attributes attribs; | ||||
| 		wlr_buffer_get_dmabuf(buffer->wlr_buffer, &attribs); | ||||
| 		if (!vulkan_setup_one_pass_framebuffer(buffer, &attribs, false)) { | ||||
| 			wlr_log(WLR_ERROR, "Failed to set up blend image"); | ||||
| 			return NULL; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (using_two_pass_pathway) { | ||||
| 		if (options != NULL && options->color_transform != NULL && | ||||
| 				!get_color_transform(options->color_transform, renderer)) { | ||||
| 			/* Try to create a new color transform */ | ||||
| 			if (!vk_color_transform_create(renderer, options->color_transform)) { | ||||
| 				wlr_log(WLR_ERROR, "Failed to create color transform"); | ||||
| 				return NULL; | ||||
| 			} | ||||
| 		} | ||||
| 	} else { | ||||
| 		// Use srgb pathway if it is the default/has already been set up
 | ||||
| 		using_srgb_pathway = buffer->srgb.framebuffer != VK_NULL_HANDLE; | ||||
| 
 | ||||
| 		if (!buffer->two_pass.out.image_view) { | ||||
| 			struct wlr_dmabuf_attributes attribs; | ||||
| 			wlr_buffer_get_dmabuf(buffer->wlr_buffer, &attribs); | ||||
| 			if (!vulkan_setup_two_pass_framebuffer(buffer, &attribs)) { | ||||
| 				wlr_log(WLR_ERROR, "Failed to set up blend image"); | ||||
| 				return NULL; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (!using_srgb_pathway && !buffer->plain.image_view) { | ||||
| 		struct wlr_dmabuf_attributes attribs; | ||||
| 		wlr_buffer_get_dmabuf(buffer->wlr_buffer, &attribs); | ||||
| 		if (!vulkan_setup_plain_framebuffer(buffer, &attribs)) { | ||||
| 			wlr_log(WLR_ERROR, "Failed to set up blend image"); | ||||
| 			return NULL; | ||||
| 		} | ||||
| 	struct wlr_vk_render_format_setup *render_setup; | ||||
| 	struct wlr_vk_render_buffer_out *buffer_out; | ||||
| 	if (using_two_pass_pathway) { | ||||
| 		render_setup = buffer->two_pass.render_setup; | ||||
| 		buffer_out = &buffer->two_pass.out; | ||||
| 	} else if (using_srgb_pathway) { | ||||
| 		render_setup = buffer->srgb.render_setup; | ||||
| 		buffer_out = &buffer->srgb.out; | ||||
| 	} else if (using_linear_pathway) { | ||||
| 		render_setup = buffer->linear.render_setup; | ||||
| 		buffer_out = &buffer->linear.out; | ||||
| 	} else { | ||||
| 		abort(); // unreachable
 | ||||
| 	} | ||||
| 
 | ||||
| 	struct wlr_vk_render_pass *pass = calloc(1, sizeof(*pass)); | ||||
|  | @ -1209,7 +1231,7 @@ struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *rend | |||
| 
 | ||||
| 	wlr_render_pass_init(&pass->base, &render_pass_impl); | ||||
| 	pass->renderer = renderer; | ||||
| 	pass->srgb_pathway = using_srgb_pathway; | ||||
| 	pass->two_pass = using_two_pass_pathway; | ||||
| 	if (options != NULL && options->color_transform != NULL) { | ||||
| 		pass->color_transform = wlr_color_transform_ref(options->color_transform); | ||||
| 	} | ||||
|  | @ -1257,14 +1279,9 @@ struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *rend | |||
| 		.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO, | ||||
| 		.renderArea = rect, | ||||
| 		.clearValueCount = 0, | ||||
| 		.renderPass = render_setup->render_pass, | ||||
| 		.framebuffer = buffer_out->framebuffer, | ||||
| 	}; | ||||
| 	if (pass->srgb_pathway) { | ||||
| 		rp_info.renderPass = buffer->srgb.render_setup->render_pass; | ||||
| 		rp_info.framebuffer = buffer->srgb.framebuffer; | ||||
| 	} else { | ||||
| 		rp_info.renderPass = buffer->plain.render_setup->render_pass; | ||||
| 		rp_info.framebuffer = buffer->plain.framebuffer; | ||||
| 	} | ||||
| 	vkCmdBeginRenderPass(cb->vk, &rp_info, VK_SUBPASS_CONTENTS_INLINE); | ||||
| 
 | ||||
| 	vkCmdSetViewport(cb->vk, 0, 1, &(VkViewport){ | ||||
|  | @ -1279,6 +1296,8 @@ struct wlr_vk_render_pass *vulkan_begin_render_pass(struct wlr_vk_renderer *rend | |||
| 
 | ||||
| 	wlr_buffer_lock(buffer->wlr_buffer); | ||||
| 	pass->render_buffer = buffer; | ||||
| 	pass->render_buffer_out = buffer_out; | ||||
| 	pass->render_setup = render_setup; | ||||
| 	pass->command_buffer = cb; | ||||
| 	return pass; | ||||
| } | ||||
|  |  | |||
|  | @ -66,59 +66,72 @@ static struct wlr_vk_descriptor_pool *alloc_ds( | |||
| 		struct wl_list *pool_list, size_t *last_pool_size) { | ||||
| 	VkResult res; | ||||
| 
 | ||||
| 	bool found = false; | ||||
| 	struct wlr_vk_descriptor_pool *pool; | ||||
| 	wl_list_for_each(pool, pool_list, link) { | ||||
| 		if (pool->free > 0) { | ||||
| 			found = true; | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (!found) { // create new pool
 | ||||
| 		pool = calloc(1, sizeof(*pool)); | ||||
| 		if (!pool) { | ||||
| 			wlr_log_errno(WLR_ERROR, "allocation failed"); | ||||
| 			return NULL; | ||||
| 		} | ||||
| 
 | ||||
| 		size_t count = 2 * (*last_pool_size); | ||||
| 		if (!count) { | ||||
| 			count = start_descriptor_pool_size; | ||||
| 		} | ||||
| 
 | ||||
| 		pool->free = count; | ||||
| 		VkDescriptorPoolSize pool_size = { | ||||
| 			.descriptorCount = count, | ||||
| 			.type = type, | ||||
| 		}; | ||||
| 
 | ||||
| 		VkDescriptorPoolCreateInfo dpool_info = { | ||||
| 			.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, | ||||
| 			.maxSets = count, | ||||
| 			.poolSizeCount = 1, | ||||
| 			.pPoolSizes = &pool_size, | ||||
| 			.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, | ||||
| 		}; | ||||
| 
 | ||||
| 		res = vkCreateDescriptorPool(renderer->dev->dev, &dpool_info, NULL, | ||||
| 			&pool->pool); | ||||
| 		if (res != VK_SUCCESS) { | ||||
| 			wlr_vk_error("vkCreateDescriptorPool", res); | ||||
| 			free(pool); | ||||
| 			return NULL; | ||||
| 		} | ||||
| 
 | ||||
| 		*last_pool_size = count; | ||||
| 		wl_list_insert(pool_list, &pool->link); | ||||
| 	} | ||||
| 
 | ||||
| 	VkDescriptorSetAllocateInfo ds_info = { | ||||
| 		.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, | ||||
| 		.descriptorSetCount = 1, | ||||
| 		.pSetLayouts = layout, | ||||
| 		.descriptorPool = pool->pool, | ||||
| 	}; | ||||
| 
 | ||||
| 	struct wlr_vk_descriptor_pool *pool; | ||||
| 	wl_list_for_each(pool, pool_list, link) { | ||||
| 		if (pool->free > 0) { | ||||
| 			ds_info.descriptorPool = pool->pool; | ||||
| 			res = vkAllocateDescriptorSets(renderer->dev->dev, &ds_info, ds); | ||||
| 			switch (res) { | ||||
| 			case VK_ERROR_FRAGMENTED_POOL: | ||||
| 			case VK_ERROR_OUT_OF_POOL_MEMORY: | ||||
| 				// Descriptor sets with more than one descriptor can cause us
 | ||||
| 				// to run out of pool memory early or lead to fragmentation
 | ||||
| 				// that makes the pool unable to service our allocation
 | ||||
| 				// request. Try the next pool or allocate a new one.
 | ||||
| 				continue; | ||||
| 			case VK_SUCCESS: | ||||
| 				--pool->free; | ||||
| 				return pool; | ||||
| 			default: | ||||
| 				wlr_vk_error("vkAllocateDescriptorSets", res); | ||||
| 				return NULL; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	pool = calloc(1, sizeof(*pool)); | ||||
| 	if (!pool) { | ||||
| 		wlr_log_errno(WLR_ERROR, "allocation failed"); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	size_t count = 2 * (*last_pool_size); | ||||
| 	if (!count) { | ||||
| 		count = start_descriptor_pool_size; | ||||
| 	} | ||||
| 
 | ||||
| 	pool->free = count; | ||||
| 	VkDescriptorPoolSize pool_size = { | ||||
| 		.descriptorCount = count, | ||||
| 		.type = type, | ||||
| 	}; | ||||
| 
 | ||||
| 	VkDescriptorPoolCreateInfo dpool_info = { | ||||
| 		.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, | ||||
| 		.maxSets = count, | ||||
| 		.poolSizeCount = 1, | ||||
| 		.pPoolSizes = &pool_size, | ||||
| 		.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT, | ||||
| 	}; | ||||
| 
 | ||||
| 	res = vkCreateDescriptorPool(renderer->dev->dev, &dpool_info, NULL, | ||||
| 		&pool->pool); | ||||
| 	if (res != VK_SUCCESS) { | ||||
| 		wlr_vk_error("vkCreateDescriptorPool", res); | ||||
| 		free(pool); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	*last_pool_size = count; | ||||
| 	wl_list_insert(pool_list, &pool->link); | ||||
| 
 | ||||
| 	ds_info.descriptorPool = pool->pool; | ||||
| 	res = vkAllocateDescriptorSets(renderer->dev->dev, &ds_info, ds); | ||||
| 	if (res != VK_SUCCESS) { | ||||
| 		wlr_vk_error("vkAllocateDescriptorSets", res); | ||||
|  | @ -162,6 +175,8 @@ static void destroy_render_format_setup(struct wlr_vk_renderer *renderer, | |||
| 	vkDestroyPipeline(dev, setup->output_pipe_srgb, NULL); | ||||
| 	vkDestroyPipeline(dev, setup->output_pipe_pq, NULL); | ||||
| 	vkDestroyPipeline(dev, setup->output_pipe_lut3d, NULL); | ||||
| 	vkDestroyPipeline(dev, setup->output_pipe_gamma22, NULL); | ||||
| 	vkDestroyPipeline(dev, setup->output_pipe_bt1886, NULL); | ||||
| 
 | ||||
| 	struct wlr_vk_pipeline *pipeline, *tmp_pipeline; | ||||
| 	wl_list_for_each_safe(pipeline, tmp_pipeline, &setup->pipelines, link) { | ||||
|  | @ -591,6 +606,12 @@ void vulkan_reset_command_buffer(struct wlr_vk_command_buffer *cb) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void finish_render_buffer_out(struct wlr_vk_render_buffer_out *out, | ||||
| 		VkDevice dev) { | ||||
| 	vkDestroyFramebuffer(dev, out->framebuffer, NULL); | ||||
| 	vkDestroyImageView(dev, out->image_view, NULL); | ||||
| } | ||||
| 
 | ||||
| static void destroy_render_buffer(struct wlr_vk_render_buffer *buffer) { | ||||
| 	wl_list_remove(&buffer->link); | ||||
| 	wlr_addon_finish(&buffer->addon); | ||||
|  | @ -604,17 +625,16 @@ static void destroy_render_buffer(struct wlr_vk_render_buffer *buffer) { | |||
| 		wlr_vk_error("vkQueueWaitIdle", res); | ||||
| 	} | ||||
| 
 | ||||
| 	vkDestroyFramebuffer(dev, buffer->srgb.framebuffer, NULL); | ||||
| 	vkDestroyImageView(dev, buffer->srgb.image_view, NULL); | ||||
| 	finish_render_buffer_out(&buffer->linear.out, dev); | ||||
| 	finish_render_buffer_out(&buffer->srgb.out, dev); | ||||
| 
 | ||||
| 	vkDestroyFramebuffer(dev, buffer->plain.framebuffer, NULL); | ||||
| 	vkDestroyImageView(dev, buffer->plain.image_view, NULL); | ||||
| 	vkDestroyImage(dev, buffer->plain.blend_image, NULL); | ||||
| 	vkFreeMemory(dev, buffer->plain.blend_memory, NULL); | ||||
| 	vkDestroyImageView(dev, buffer->plain.blend_image_view, NULL); | ||||
| 	if (buffer->plain.blend_attachment_pool) { | ||||
| 		vulkan_free_ds(buffer->renderer, buffer->plain.blend_attachment_pool, | ||||
| 			buffer->plain.blend_descriptor_set); | ||||
| 	finish_render_buffer_out(&buffer->two_pass.out, dev); | ||||
| 	vkDestroyImage(dev, buffer->two_pass.blend_image, NULL); | ||||
| 	vkFreeMemory(dev, buffer->two_pass.blend_memory, NULL); | ||||
| 	vkDestroyImageView(dev, buffer->two_pass.blend_image_view, NULL); | ||||
| 	if (buffer->two_pass.blend_attachment_pool) { | ||||
| 		vulkan_free_ds(buffer->renderer, buffer->two_pass.blend_attachment_pool, | ||||
| 			buffer->two_pass.blend_descriptor_set); | ||||
| 	} | ||||
| 
 | ||||
| 	vkDestroyImage(dev, buffer->image, NULL); | ||||
|  | @ -635,7 +655,7 @@ static struct wlr_addon_interface render_buffer_addon_impl = { | |||
| 	.destroy = handle_render_buffer_destroy, | ||||
| }; | ||||
| 
 | ||||
| bool vulkan_setup_plain_framebuffer(struct wlr_vk_render_buffer *buffer, | ||||
| bool vulkan_setup_two_pass_framebuffer(struct wlr_vk_render_buffer *buffer, | ||||
| 		const struct wlr_dmabuf_attributes *dmabuf) { | ||||
| 	struct wlr_vk_renderer *renderer = buffer->renderer; | ||||
| 	VkResult res; | ||||
|  | @ -663,15 +683,15 @@ bool vulkan_setup_plain_framebuffer(struct wlr_vk_render_buffer *buffer, | |||
| 		}, | ||||
| 	}; | ||||
| 
 | ||||
| 	res = vkCreateImageView(dev, &view_info, NULL, &buffer->plain.image_view); | ||||
| 	res = vkCreateImageView(dev, &view_info, NULL, &buffer->two_pass.out.image_view); | ||||
| 	if (res != VK_SUCCESS) { | ||||
| 		wlr_vk_error("vkCreateImageView failed", res); | ||||
| 		goto error; | ||||
| 	} | ||||
| 
 | ||||
| 	buffer->plain.render_setup = find_or_create_render_setup( | ||||
| 	buffer->two_pass.render_setup = find_or_create_render_setup( | ||||
| 		renderer, &fmt->format, true); | ||||
| 	if (!buffer->plain.render_setup) { | ||||
| 	if (!buffer->two_pass.render_setup) { | ||||
| 		goto error; | ||||
| 	} | ||||
| 
 | ||||
|  | @ -691,14 +711,14 @@ bool vulkan_setup_plain_framebuffer(struct wlr_vk_render_buffer *buffer, | |||
| 		.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_INPUT_ATTACHMENT_BIT, | ||||
| 	}; | ||||
| 
 | ||||
| 	res = vkCreateImage(dev, &img_info, NULL, &buffer->plain.blend_image); | ||||
| 	res = vkCreateImage(dev, &img_info, NULL, &buffer->two_pass.blend_image); | ||||
| 	if (res != VK_SUCCESS) { | ||||
| 		wlr_vk_error("vkCreateImage failed", res); | ||||
| 		goto error; | ||||
| 	} | ||||
| 
 | ||||
| 	VkMemoryRequirements mem_reqs; | ||||
| 	vkGetImageMemoryRequirements(dev, buffer->plain.blend_image, &mem_reqs); | ||||
| 	vkGetImageMemoryRequirements(dev, buffer->two_pass.blend_image, &mem_reqs); | ||||
| 
 | ||||
| 	int mem_type_index = vulkan_find_mem_type(renderer->dev, | ||||
| 		VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, mem_reqs.memoryTypeBits); | ||||
|  | @ -713,13 +733,13 @@ bool vulkan_setup_plain_framebuffer(struct wlr_vk_render_buffer *buffer, | |||
| 		.memoryTypeIndex = mem_type_index, | ||||
| 	}; | ||||
| 
 | ||||
| 	res = vkAllocateMemory(dev, &mem_info, NULL, &buffer->plain.blend_memory); | ||||
| 	res = vkAllocateMemory(dev, &mem_info, NULL, &buffer->two_pass.blend_memory); | ||||
| 	if (res != VK_SUCCESS) { | ||||
| 		wlr_vk_error("vkAllocatorMemory failed", res); | ||||
| 		goto error; | ||||
| 	} | ||||
| 
 | ||||
| 	res = vkBindImageMemory(dev, buffer->plain.blend_image, buffer->plain.blend_memory, 0); | ||||
| 	res = vkBindImageMemory(dev, buffer->two_pass.blend_image, buffer->two_pass.blend_memory, 0); | ||||
| 	if (res != VK_SUCCESS) { | ||||
| 		wlr_vk_error("vkBindMemory failed", res); | ||||
| 		goto error; | ||||
|  | @ -727,7 +747,7 @@ bool vulkan_setup_plain_framebuffer(struct wlr_vk_render_buffer *buffer, | |||
| 
 | ||||
| 	VkImageViewCreateInfo blend_view_info = { | ||||
| 		.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, | ||||
| 		.image = buffer->plain.blend_image, | ||||
| 		.image = buffer->two_pass.blend_image, | ||||
| 		.viewType = VK_IMAGE_VIEW_TYPE_2D, | ||||
| 		.format = img_info.format, | ||||
| 		.components.r = VK_COMPONENT_SWIZZLE_IDENTITY, | ||||
|  | @ -743,50 +763,50 @@ bool vulkan_setup_plain_framebuffer(struct wlr_vk_render_buffer *buffer, | |||
| 		}, | ||||
| 	}; | ||||
| 
 | ||||
| 	res = vkCreateImageView(dev, &blend_view_info, NULL, &buffer->plain.blend_image_view); | ||||
| 	res = vkCreateImageView(dev, &blend_view_info, NULL, &buffer->two_pass.blend_image_view); | ||||
| 	if (res != VK_SUCCESS) { | ||||
| 		wlr_vk_error("vkCreateImageView failed", res); | ||||
| 		goto error; | ||||
| 	} | ||||
| 
 | ||||
| 	buffer->plain.blend_attachment_pool = vulkan_alloc_blend_ds(renderer, | ||||
| 		&buffer->plain.blend_descriptor_set); | ||||
| 	if (!buffer->plain.blend_attachment_pool) { | ||||
| 	buffer->two_pass.blend_attachment_pool = vulkan_alloc_blend_ds(renderer, | ||||
| 		&buffer->two_pass.blend_descriptor_set); | ||||
| 	if (!buffer->two_pass.blend_attachment_pool) { | ||||
| 		wlr_log(WLR_ERROR, "failed to allocate descriptor"); | ||||
| 		goto error; | ||||
| 	} | ||||
| 
 | ||||
| 	VkDescriptorImageInfo ds_attach_info = { | ||||
| 		.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, | ||||
| 		.imageView = buffer->plain.blend_image_view, | ||||
| 		.imageView = buffer->two_pass.blend_image_view, | ||||
| 		.sampler = VK_NULL_HANDLE, | ||||
| 	}; | ||||
| 	VkWriteDescriptorSet ds_write = { | ||||
| 		.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, | ||||
| 		.descriptorCount = 1, | ||||
| 		.descriptorType = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, | ||||
| 		.dstSet = buffer->plain.blend_descriptor_set, | ||||
| 		.dstSet = buffer->two_pass.blend_descriptor_set, | ||||
| 		.dstBinding = 0, | ||||
| 		.pImageInfo = &ds_attach_info, | ||||
| 	}; | ||||
| 	vkUpdateDescriptorSets(dev, 1, &ds_write, 0, NULL); | ||||
| 
 | ||||
| 	VkImageView attachments[2] = { | ||||
| 		buffer->plain.blend_image_view, | ||||
| 		buffer->plain.image_view | ||||
| 	VkImageView attachments[] = { | ||||
| 		buffer->two_pass.blend_image_view, | ||||
| 		buffer->two_pass.out.image_view, | ||||
| 	}; | ||||
| 	VkFramebufferCreateInfo fb_info = { | ||||
| 		.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, | ||||
| 		.attachmentCount = 2, | ||||
| 		.attachmentCount = sizeof(attachments) / sizeof(attachments[0]), | ||||
| 		.pAttachments = attachments, | ||||
| 		.flags = 0u, | ||||
| 		.width = dmabuf->width, | ||||
| 		.height = dmabuf->height, | ||||
| 		.layers = 1u, | ||||
| 		.renderPass = buffer->plain.render_setup->render_pass, | ||||
| 		.renderPass = buffer->two_pass.render_setup->render_pass, | ||||
| 	}; | ||||
| 
 | ||||
| 	res = vkCreateFramebuffer(dev, &fb_info, NULL, &buffer->plain.framebuffer); | ||||
| 	res = vkCreateFramebuffer(dev, &fb_info, NULL, &buffer->two_pass.out.framebuffer); | ||||
| 	if (res != VK_SUCCESS) { | ||||
| 		wlr_vk_error("vkCreateFramebuffer", res); | ||||
| 		goto error; | ||||
|  | @ -800,8 +820,8 @@ error: | |||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| static bool vulkan_setup_srgb_framebuffer(struct wlr_vk_render_buffer *buffer, | ||||
| 		const struct wlr_dmabuf_attributes *dmabuf) { | ||||
| bool vulkan_setup_one_pass_framebuffer(struct wlr_vk_render_buffer *buffer, | ||||
| 		const struct wlr_dmabuf_attributes *dmabuf, bool srgb) { | ||||
| 	struct wlr_vk_renderer *renderer = buffer->renderer; | ||||
| 	VkResult res; | ||||
| 	VkDevice dev = renderer->dev->dev; | ||||
|  | @ -810,14 +830,18 @@ static bool vulkan_setup_srgb_framebuffer(struct wlr_vk_render_buffer *buffer, | |||
| 		renderer->dev, dmabuf->format); | ||||
| 	assert(fmt); | ||||
| 
 | ||||
| 	assert(fmt->format.vk_srgb); | ||||
| 	// Set up the srgb framebuffer by default; plain framebuffer and
 | ||||
| 	VkFormat vk_fmt = srgb ? fmt->format.vk_srgb : fmt->format.vk; | ||||
| 	assert(vk_fmt != VK_FORMAT_UNDEFINED); | ||||
| 
 | ||||
| 	struct wlr_vk_render_buffer_out *out = srgb ? &buffer->srgb.out : &buffer->linear.out; | ||||
| 
 | ||||
| 	// Set up the srgb framebuffer by default; two-pass framebuffer and
 | ||||
| 	// blending image will be set up later if necessary
 | ||||
| 	VkImageViewCreateInfo view_info = { | ||||
| 		.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, | ||||
| 		.image = buffer->image, | ||||
| 		.viewType = VK_IMAGE_VIEW_TYPE_2D, | ||||
| 		.format = fmt->format.vk_srgb, | ||||
| 		.format = vk_fmt, | ||||
| 		.components.r = VK_COMPONENT_SWIZZLE_IDENTITY, | ||||
| 		.components.g = VK_COMPONENT_SWIZZLE_IDENTITY, | ||||
| 		.components.b = VK_COMPONENT_SWIZZLE_IDENTITY, | ||||
|  | @ -831,35 +855,43 @@ static bool vulkan_setup_srgb_framebuffer(struct wlr_vk_render_buffer *buffer, | |||
| 		}, | ||||
| 	}; | ||||
| 
 | ||||
| 	res = vkCreateImageView(dev, &view_info, NULL, &buffer->srgb.image_view); | ||||
| 	res = vkCreateImageView(dev, &view_info, NULL, &out->image_view); | ||||
| 	if (res != VK_SUCCESS) { | ||||
| 		wlr_vk_error("vkCreateImageView failed", res); | ||||
| 		goto error; | ||||
| 	} | ||||
| 
 | ||||
| 	buffer->srgb.render_setup = find_or_create_render_setup( | ||||
| 		renderer, &fmt->format, false); | ||||
| 	if (!buffer->srgb.render_setup) { | ||||
| 	struct wlr_vk_render_format_setup *render_setup = | ||||
| 		find_or_create_render_setup(renderer, &fmt->format, false); | ||||
| 	if (!render_setup) { | ||||
| 		goto error; | ||||
| 	} | ||||
| 
 | ||||
| 	VkFramebufferCreateInfo fb_info = { | ||||
| 		.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, | ||||
| 		.attachmentCount = 1, | ||||
| 		.pAttachments = &buffer->srgb.image_view, | ||||
| 		.pAttachments = &out->image_view, | ||||
| 		.flags = 0u, | ||||
| 		.width = dmabuf->width, | ||||
| 		.height = dmabuf->height, | ||||
| 		.layers = 1u, | ||||
| 		.renderPass = buffer->srgb.render_setup->render_pass, | ||||
| 		.renderPass = render_setup->render_pass, | ||||
| 	}; | ||||
| 
 | ||||
| 	res = vkCreateFramebuffer(dev, &fb_info, NULL, &buffer->srgb.framebuffer); | ||||
| 	res = vkCreateFramebuffer(dev, &fb_info, NULL, &out->framebuffer); | ||||
| 	if (res != VK_SUCCESS) { | ||||
| 		wlr_vk_error("vkCreateFramebuffer", res); | ||||
| 		goto error; | ||||
| 	} | ||||
| 
 | ||||
| 	if (srgb) { | ||||
| 		buffer->srgb.render_setup = render_setup; | ||||
| 	} else { | ||||
| 		buffer->linear.render_setup = render_setup; | ||||
| 	} | ||||
| 
 | ||||
| 	return true; | ||||
| 
 | ||||
| error: | ||||
| 	// cleaning up everything is the caller's responsibility,
 | ||||
| 	// since it will need to do this anyway if framebuffer setup fails
 | ||||
|  | @ -903,12 +935,12 @@ static struct wlr_vk_render_buffer *create_render_buffer( | |||
| 	} | ||||
| 
 | ||||
| 	if (using_mutable_srgb) { | ||||
| 		if (!vulkan_setup_srgb_framebuffer(buffer, &dmabuf)) { | ||||
| 		if (!vulkan_setup_one_pass_framebuffer(buffer, &dmabuf, true)) { | ||||
| 			goto error; | ||||
| 		} | ||||
| 	} else { | ||||
| 		// Set up the plain framebuffer & blending image
 | ||||
| 		if (!vulkan_setup_plain_framebuffer(buffer, &dmabuf)) { | ||||
| 		// Set up the two-pass framebuffer & blending image
 | ||||
| 		if (!vulkan_setup_two_pass_framebuffer(buffer, &dmabuf)) { | ||||
| 			goto error; | ||||
| 		} | ||||
| 	} | ||||
|  | @ -1466,14 +1498,14 @@ static bool init_tex_layouts(struct wlr_vk_renderer *renderer, | |||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	VkPushConstantRange pc_ranges[2] = { | ||||
| 	VkPushConstantRange pc_ranges[] = { | ||||
| 		{ | ||||
| 			.size = sizeof(struct wlr_vk_vert_pcr_data), | ||||
| 			.stageFlags = VK_SHADER_STAGE_VERTEX_BIT, | ||||
| 		}, | ||||
| 		{ | ||||
| 			.offset = pc_ranges[0].size, | ||||
| 			.size = sizeof(float) * 4, // alpha or color
 | ||||
| 			.size = sizeof(struct wlr_vk_frag_texture_pcr_data), | ||||
| 			.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, | ||||
| 		}, | ||||
| 	}; | ||||
|  | @ -1482,7 +1514,7 @@ static bool init_tex_layouts(struct wlr_vk_renderer *renderer, | |||
| 		.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, | ||||
| 		.setLayoutCount = 1, | ||||
| 		.pSetLayouts = out_ds_layout, | ||||
| 		.pushConstantRangeCount = 2, | ||||
| 		.pushConstantRangeCount = sizeof(pc_ranges) / sizeof(pc_ranges[0]), | ||||
| 		.pPushConstantRanges = pc_ranges, | ||||
| 	}; | ||||
| 
 | ||||
|  | @ -1560,7 +1592,7 @@ static bool init_blend_to_output_layouts(struct wlr_vk_renderer *renderer) { | |||
| 	} | ||||
| 
 | ||||
| 	// pipeline layout -- standard vertex uniforms, no shader uniforms
 | ||||
| 	VkPushConstantRange pc_ranges[2] = { | ||||
| 	VkPushConstantRange pc_ranges[] = { | ||||
| 		{ | ||||
| 			.offset = 0, | ||||
| 			.size = sizeof(struct wlr_vk_vert_pcr_data), | ||||
|  | @ -1573,16 +1605,16 @@ static bool init_blend_to_output_layouts(struct wlr_vk_renderer *renderer) { | |||
| 		}, | ||||
| 	}; | ||||
| 
 | ||||
| 	VkDescriptorSetLayout out_ds_layouts[2] = { | ||||
| 	VkDescriptorSetLayout out_ds_layouts[] = { | ||||
| 		renderer->output_ds_srgb_layout, | ||||
| 		renderer->output_ds_lut3d_layout, | ||||
| 	}; | ||||
| 
 | ||||
| 	VkPipelineLayoutCreateInfo pl_info = { | ||||
| 		.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, | ||||
| 		.setLayoutCount = 2, | ||||
| 		.setLayoutCount = sizeof(out_ds_layouts) / sizeof(out_ds_layouts[0]), | ||||
| 		.pSetLayouts = out_ds_layouts, | ||||
| 		.pushConstantRangeCount = 2, | ||||
| 		.pushConstantRangeCount = sizeof(pc_ranges) / sizeof(pc_ranges[0]), | ||||
| 		.pPushConstantRanges = pc_ranges, | ||||
| 	}; | ||||
| 
 | ||||
|  | @ -1755,14 +1787,14 @@ struct wlr_vk_pipeline *setup_get_or_create_pipeline( | |||
| 		.scissorCount = 1, | ||||
| 	}; | ||||
| 
 | ||||
| 	VkDynamicState dynStates[2] = { | ||||
| 	VkDynamicState dyn_states[] = { | ||||
| 		VK_DYNAMIC_STATE_VIEWPORT, | ||||
| 		VK_DYNAMIC_STATE_SCISSOR, | ||||
| 	}; | ||||
| 	VkPipelineDynamicStateCreateInfo dynamic = { | ||||
| 		.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, | ||||
| 		.pDynamicStates = dynStates, | ||||
| 		.dynamicStateCount = 2, | ||||
| 		.pDynamicStates = dyn_states, | ||||
| 		.dynamicStateCount = sizeof(dyn_states) / sizeof(dyn_states[0]), | ||||
| 	}; | ||||
| 
 | ||||
| 	VkPipelineVertexInputStateCreateInfo vertex = { | ||||
|  | @ -1774,7 +1806,7 @@ struct wlr_vk_pipeline *setup_get_or_create_pipeline( | |||
| 		.layout = pipeline_layout->vk, | ||||
| 		.renderPass = setup->render_pass, | ||||
| 		.subpass = 0, | ||||
| 		.stageCount = 2, | ||||
| 		.stageCount = sizeof(stages) / sizeof(stages[0]), | ||||
| 		.pStages = stages, | ||||
| 
 | ||||
| 		.pInputAssemblyState = &assembly, | ||||
|  | @ -1817,7 +1849,7 @@ static bool init_blend_to_output_pipeline(struct wlr_vk_renderer *renderer, | |||
| 		.pData = &output_transform_type, | ||||
| 	}; | ||||
| 
 | ||||
| 	VkPipelineShaderStageCreateInfo tex_stages[2] = { | ||||
| 	VkPipelineShaderStageCreateInfo tex_stages[] = { | ||||
| 		{ | ||||
| 			.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, | ||||
| 			.stage = VK_SHADER_STAGE_VERTEX_BIT, | ||||
|  | @ -1872,14 +1904,14 @@ static bool init_blend_to_output_pipeline(struct wlr_vk_renderer *renderer, | |||
| 		.scissorCount = 1, | ||||
| 	}; | ||||
| 
 | ||||
| 	VkDynamicState dynStates[2] = { | ||||
| 	VkDynamicState dyn_states[] = { | ||||
| 		VK_DYNAMIC_STATE_VIEWPORT, | ||||
| 		VK_DYNAMIC_STATE_SCISSOR, | ||||
| 	}; | ||||
| 	VkPipelineDynamicStateCreateInfo dynamic = { | ||||
| 		.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO, | ||||
| 		.pDynamicStates = dynStates, | ||||
| 		.dynamicStateCount = 2, | ||||
| 		.pDynamicStates = dyn_states, | ||||
| 		.dynamicStateCount = sizeof(dyn_states) / sizeof(dyn_states[0]), | ||||
| 	}; | ||||
| 
 | ||||
| 	VkPipelineVertexInputStateCreateInfo vertex = { | ||||
|  | @ -1892,7 +1924,7 @@ static bool init_blend_to_output_pipeline(struct wlr_vk_renderer *renderer, | |||
| 		.layout = pipe_layout, | ||||
| 		.renderPass = rp, | ||||
| 		.subpass = 1, // second subpass!
 | ||||
| 		.stageCount = 2, | ||||
| 		.stageCount = sizeof(tex_stages) / sizeof(tex_stages[0]), | ||||
| 		.pStages = tex_stages, | ||||
| 		.pInputAssemblyState = &assembly, | ||||
| 		.pRasterizationState = &rasterization, | ||||
|  | @ -2185,7 +2217,7 @@ static struct wlr_vk_render_format_setup *find_or_create_render_setup( | |||
| 	VkResult res; | ||||
| 
 | ||||
| 	if (use_blending_buffer) { | ||||
| 		VkAttachmentDescription attachments[2] = { | ||||
| 		VkAttachmentDescription attachments[] = { | ||||
| 			{ | ||||
| 				.format = VK_FORMAT_R16G16B16A16_SFLOAT, | ||||
| 				.samples = VK_SAMPLE_COUNT_1_BIT, | ||||
|  | @ -2223,7 +2255,7 @@ static struct wlr_vk_render_format_setup *find_or_create_render_setup( | |||
| 			.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, | ||||
| 		}; | ||||
| 
 | ||||
| 		VkSubpassDescription subpasses[2] = { | ||||
| 		VkSubpassDescription subpasses[] = { | ||||
| 			{ | ||||
| 				.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS, | ||||
| 				.colorAttachmentCount = 1, | ||||
|  | @ -2238,7 +2270,7 @@ static struct wlr_vk_render_format_setup *find_or_create_render_setup( | |||
| 			} | ||||
| 		}; | ||||
| 
 | ||||
| 		VkSubpassDependency deps[3] = { | ||||
| 		VkSubpassDependency deps[] = { | ||||
| 			{ | ||||
| 				.srcSubpass = VK_SUBPASS_EXTERNAL, | ||||
| 				.srcStageMask = VK_PIPELINE_STAGE_HOST_BIT | | ||||
|  | @ -2280,11 +2312,11 @@ static struct wlr_vk_render_format_setup *find_or_create_render_setup( | |||
| 			.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, | ||||
| 			.pNext = NULL, | ||||
| 			.flags = 0, | ||||
| 			.attachmentCount = 2u, | ||||
| 			.attachmentCount = sizeof(attachments) / sizeof(attachments[0]), | ||||
| 			.pAttachments = attachments, | ||||
| 			.subpassCount = 2u, | ||||
| 			.subpassCount = sizeof(subpasses) / sizeof(subpasses[0]), | ||||
| 			.pSubpasses = subpasses, | ||||
| 			.dependencyCount = 3u, | ||||
| 			.dependencyCount = sizeof(deps) / sizeof(deps[0]), | ||||
| 			.pDependencies = deps, | ||||
| 		}; | ||||
| 
 | ||||
|  | @ -2315,6 +2347,16 @@ static struct wlr_vk_render_format_setup *find_or_create_render_setup( | |||
| 				&setup->output_pipe_pq, WLR_VK_OUTPUT_TRANSFORM_INVERSE_ST2084_PQ)) { | ||||
| 			goto error; | ||||
| 		} | ||||
| 		if (!init_blend_to_output_pipeline( | ||||
| 				renderer, setup->render_pass, renderer->output_pipe_layout, | ||||
| 				&setup->output_pipe_gamma22, WLR_VK_OUTPUT_TRANSFORM_INVERSE_GAMMA22)) { | ||||
| 			goto error; | ||||
| 		} | ||||
| 		if (!init_blend_to_output_pipeline( | ||||
| 			renderer, setup->render_pass, renderer->output_pipe_layout, | ||||
| 			&setup->output_pipe_bt1886, WLR_VK_OUTPUT_TRANSFORM_INVERSE_BT1886)) { | ||||
| 			goto error; | ||||
| 		} | ||||
| 	} else { | ||||
| 		assert(format->vk_srgb); | ||||
| 		VkAttachmentDescription attachment = { | ||||
|  | @ -2339,7 +2381,7 @@ static struct wlr_vk_render_format_setup *find_or_create_render_setup( | |||
| 			.pColorAttachments = &color_ref, | ||||
| 		}; | ||||
| 
 | ||||
| 		VkSubpassDependency deps[2] = { | ||||
| 		VkSubpassDependency deps[] = { | ||||
| 			{ | ||||
| 				.srcSubpass = VK_SUBPASS_EXTERNAL, | ||||
| 				.srcStageMask = VK_PIPELINE_STAGE_HOST_BIT | | ||||
|  | @ -2374,7 +2416,7 @@ static struct wlr_vk_render_format_setup *find_or_create_render_setup( | |||
| 			.pAttachments = &attachment, | ||||
| 			.subpassCount = 1, | ||||
| 			.pSubpasses = &subpass, | ||||
| 			.dependencyCount = 2u, | ||||
| 			.dependencyCount = sizeof(deps) / sizeof(deps[0]), | ||||
| 			.pDependencies = deps, | ||||
| 		}; | ||||
| 
 | ||||
|  |  | |||
|  | @ -22,6 +22,8 @@ layout (constant_id = 0) const int OUTPUT_TRANSFORM = 0; | |||
| #define OUTPUT_TRANSFORM_INVERSE_SRGB 1 | ||||
| #define OUTPUT_TRANSFORM_INVERSE_ST2084_PQ 2 | ||||
| #define OUTPUT_TRANSFORM_LUT_3D 3 | ||||
| #define OUTPUT_TRANSFORM_INVERSE_GAMMA22 4 | ||||
| #define OUTPUT_TRANSFORM_INVERSE_BT1886 5 | ||||
| 
 | ||||
| float linear_channel_to_srgb(float x) { | ||||
| 	return max(min(x * 12.92, 0.04045), 1.055 * pow(x, 1. / 2.4) - 0.055); | ||||
|  | @ -46,6 +48,14 @@ vec3 linear_color_to_pq(vec3 color) { | |||
| 	return pow((vec3(c1) + c2 * pow_n) / (vec3(1) + c3 * pow_n), vec3(m)); | ||||
| } | ||||
| 
 | ||||
| vec3 linear_color_to_bt1886(vec3 color) { | ||||
| 	float lb = pow(0.0001, 1.0 / 2.4); | ||||
| 	float lw = pow(1.0, 1.0 / 2.4); | ||||
| 	float a  = pow(lw - lb, 2.4); | ||||
| 	float b  = lb / (lw - lb); | ||||
| 	return pow(color / a, vec3(1.0 / 2.4)) - vec3(b); | ||||
| } | ||||
| 
 | ||||
| void main() { | ||||
| 	vec4 in_color = subpassLoad(in_color).rgba; | ||||
| 
 | ||||
|  | @ -71,6 +81,10 @@ void main() { | |||
| 	} else if (OUTPUT_TRANSFORM == OUTPUT_TRANSFORM_INVERSE_SRGB) { | ||||
| 		// Produce sRGB encoded values | ||||
| 		rgb = linear_color_to_srgb(rgb); | ||||
| 	} else if (OUTPUT_TRANSFORM == OUTPUT_TRANSFORM_INVERSE_GAMMA22) { | ||||
| 		rgb = pow(rgb, vec3(1. / 2.2)); | ||||
| 	} else if (OUTPUT_TRANSFORM == OUTPUT_TRANSFORM_INVERSE_BT1886) { | ||||
| 		rgb = linear_color_to_bt1886(rgb); | ||||
| 	} | ||||
| 
 | ||||
| 	// Back to pre-multiplied alpha | ||||
|  |  | |||
|  | @ -18,6 +18,8 @@ layout (constant_id = 0) const int TEXTURE_TRANSFORM = 0; | |||
| #define TEXTURE_TRANSFORM_IDENTITY 0 | ||||
| #define TEXTURE_TRANSFORM_SRGB 1 | ||||
| #define TEXTURE_TRANSFORM_ST2084_PQ 2 | ||||
| #define TEXTURE_TRANSFORM_GAMMA22 3 | ||||
| #define TEXTURE_TRANSFORM_BT1886 4 | ||||
| 
 | ||||
| float srgb_channel_to_linear(float x) { | ||||
| 	return mix(x / 12.92, | ||||
|  | @ -44,6 +46,14 @@ vec3 pq_color_to_linear(vec3 color) { | |||
| 	return pow(num / denom, vec3(inv_m1)); | ||||
| } | ||||
| 
 | ||||
| vec3 bt1886_color_to_linear(vec3 color) { | ||||
| 	float lb = pow(0.0001, 1.0 / 2.4); | ||||
| 	float lw = pow(1.0, 1.0 / 2.4); | ||||
| 	float a  = pow(lw - lb, 2.4); | ||||
| 	float b  = lb / (lw - lb); | ||||
| 	return a * pow(color + vec3(b), vec3(2.4)); | ||||
| } | ||||
| 
 | ||||
| void main() { | ||||
| 	vec4 in_color = textureLod(tex, uv, 0); | ||||
| 
 | ||||
|  | @ -60,6 +70,10 @@ void main() { | |||
| 		rgb = srgb_color_to_linear(rgb); | ||||
| 	} else if (TEXTURE_TRANSFORM == TEXTURE_TRANSFORM_ST2084_PQ) { | ||||
| 		rgb = pq_color_to_linear(rgb); | ||||
| 	} else if (TEXTURE_TRANSFORM == TEXTURE_TRANSFORM_GAMMA22) { | ||||
| 		rgb = pow(rgb, vec3(2.2)); | ||||
| 	} else if (TEXTURE_TRANSFORM == TEXTURE_TRANSFORM_BT1886) { | ||||
| 		rgb = bt1886_color_to_linear(rgb); | ||||
| 	} | ||||
| 
 | ||||
| 	rgb *= data.luminance_multiplier; | ||||
|  |  | |||
|  | @ -399,14 +399,14 @@ static struct wlr_texture *vulkan_texture_from_pixels( | |||
| 
 | ||||
| 	texture_set_format(texture, &fmt->format, fmt->shm.has_mutable_srgb); | ||||
| 
 | ||||
| 	VkFormat view_formats[2] = { | ||||
| 	VkFormat view_formats[] = { | ||||
| 		fmt->format.vk, | ||||
| 		fmt->format.vk_srgb, | ||||
| 	}; | ||||
| 	VkImageFormatListCreateInfoKHR list_info = { | ||||
| 		.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR, | ||||
| 		.pViewFormats = view_formats, | ||||
| 		.viewFormatCount = 2, | ||||
| 		.viewFormatCount = sizeof(view_formats) / sizeof(view_formats[0]), | ||||
| 	}; | ||||
| 	VkImageCreateInfo img_info = { | ||||
| 		.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, | ||||
|  | @ -600,14 +600,14 @@ VkImage vulkan_import_dmabuf(struct wlr_vk_renderer *renderer, | |||
| 	}; | ||||
| 	eimg.pNext = &mod_info; | ||||
| 
 | ||||
| 	VkFormat view_formats[2] = { | ||||
| 	VkFormat view_formats[] = { | ||||
| 		fmt->format.vk, | ||||
| 		fmt->format.vk_srgb, | ||||
| 	}; | ||||
| 	VkImageFormatListCreateInfoKHR list_info = { | ||||
| 		.sType = VK_STRUCTURE_TYPE_IMAGE_FORMAT_LIST_CREATE_INFO_KHR, | ||||
| 		.pViewFormats = view_formats, | ||||
| 		.viewFormatCount = 2, | ||||
| 		.viewFormatCount = sizeof(view_formats) / sizeof(view_formats[0]), | ||||
| 	}; | ||||
| 	if (mod->has_mutable_srgb) { | ||||
| 		mod_info.pNext = &list_info; | ||||
|  |  | |||
|  | @ -1,6 +1,4 @@ | |||
| PKG_CONFIG?=pkg-config | ||||
| WAYLAND_PROTOCOLS!=$(PKG_CONFIG) --variable=pkgdatadir wayland-protocols | ||||
| WAYLAND_SCANNER!=$(PKG_CONFIG) --variable=wayland_scanner wayland-scanner | ||||
| 
 | ||||
| PKGS="wlroots-0.20" wayland-server xkbcommon | ||||
| CFLAGS_PKG_CONFIG!=$(PKG_CONFIG) --cflags $(PKGS) | ||||
|  | @ -9,19 +7,12 @@ LIBS!=$(PKG_CONFIG) --libs $(PKGS) | |||
| 
 | ||||
| all: tinywl | ||||
| 
 | ||||
| # wayland-scanner is a tool which generates C headers and rigging for Wayland
 | ||||
| # protocols, which are specified in XML. wlroots requires you to rig these up
 | ||||
| # to your build system yourself and provide them in the include path.
 | ||||
| xdg-shell-protocol.h: | ||||
| 	$(WAYLAND_SCANNER) server-header \
 | ||||
| 		$(WAYLAND_PROTOCOLS)/stable/xdg-shell/xdg-shell.xml $@ | ||||
| 
 | ||||
| tinywl.o: tinywl.c xdg-shell-protocol.h | ||||
| tinywl.o: tinywl.c | ||||
| 	$(CC) -c $< -g -Werror $(CFLAGS) -I. -DWLR_USE_UNSTABLE -o $@ | ||||
| tinywl: tinywl.o | ||||
| 	$(CC) $^ $> -g -Werror $(CFLAGS) $(LDFLAGS) $(LIBS) -o $@ | ||||
| 
 | ||||
| clean: | ||||
| 	rm -f tinywl tinywl.o xdg-shell-protocol.h | ||||
| 	rm -f tinywl tinywl.o | ||||
| 
 | ||||
| .PHONY: all clean | ||||
|  |  | |||
|  | @ -1,5 +1,5 @@ | |||
| executable( | ||||
| 	'tinywl', | ||||
| 	['tinywl.c', protocols_server_header['xdg-shell']], | ||||
| 	'tinywl.c', | ||||
| 	dependencies: wlroots, | ||||
| ) | ||||
|  |  | |||
|  | @ -56,6 +56,7 @@ struct tinywl_server { | |||
| 	struct wlr_seat *seat; | ||||
| 	struct wl_listener new_input; | ||||
| 	struct wl_listener request_cursor; | ||||
| 	struct wl_listener pointer_focus_change; | ||||
| 	struct wl_listener request_set_selection; | ||||
| 	struct wl_list keyboards; | ||||
| 	enum tinywl_cursor_mode cursor_mode; | ||||
|  | @ -333,6 +334,18 @@ static void seat_request_cursor(struct wl_listener *listener, void *data) { | |||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void seat_pointer_focus_change(struct wl_listener *listener, void *data) { | ||||
| 	struct tinywl_server *server = wl_container_of( | ||||
| 			listener, server, pointer_focus_change); | ||||
| 	/* This event is raised when the pointer focus is changed, including when the
 | ||||
| 	 * client is closed. We set the cursor image to its default if target surface | ||||
| 	 * is NULL */ | ||||
| 	struct wlr_seat_pointer_focus_change_event *event = data; | ||||
| 	if (event->new_surface == NULL) { | ||||
| 		wlr_cursor_set_xcursor(server->cursor, server->cursor_mgr, "default"); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void seat_request_set_selection(struct wl_listener *listener, void *data) { | ||||
| 	/* This event is raised by the seat when a client wants to set the selection,
 | ||||
| 	 * usually when the user copies something. wlroots allows compositors to | ||||
|  | @ -1018,6 +1031,9 @@ int main(int argc, char *argv[]) { | |||
| 	server.request_cursor.notify = seat_request_cursor; | ||||
| 	wl_signal_add(&server.seat->events.request_set_cursor, | ||||
| 			&server.request_cursor); | ||||
| 	server.pointer_focus_change.notify = seat_pointer_focus_change; | ||||
| 	wl_signal_add(&server.seat->pointer_state.events.focus_change, | ||||
| 			&server.pointer_focus_change); | ||||
| 	server.request_set_selection.notify = seat_request_set_selection; | ||||
| 	wl_signal_add(&server.seat->events.request_set_selection, | ||||
| 			&server.request_set_selection); | ||||
|  | @ -1069,6 +1085,7 @@ int main(int argc, char *argv[]) { | |||
| 
 | ||||
| 	wl_list_remove(&server.new_input.link); | ||||
| 	wl_list_remove(&server.request_cursor.link); | ||||
| 	wl_list_remove(&server.pointer_focus_change.link); | ||||
| 	wl_list_remove(&server.request_set_selection.link); | ||||
| 
 | ||||
| 	wl_list_remove(&server.new_output.link); | ||||
|  |  | |||
|  | @ -308,6 +308,14 @@ static void drag_handle_touch_motion(struct wlr_seat_touch_grab *grab, | |||
| 				wl_fixed_from_double(point->sx), | ||||
| 				wl_fixed_from_double(point->sy)); | ||||
| 		} | ||||
| 
 | ||||
| 		struct wlr_drag_motion_event event = { | ||||
| 			.drag = drag, | ||||
| 			.time = time, | ||||
| 			.sx = point->sx, | ||||
| 			.sy = point->sy, | ||||
| 		}; | ||||
| 		wl_signal_emit_mutable(&drag->events.motion, &event); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -10,6 +10,7 @@ | |||
| #include <wlr/types/wlr_ext_image_capture_source_v1.h> | ||||
| #include <wlr/util/log.h> | ||||
| #include "ext-image-capture-source-v1-protocol.h" | ||||
| #include "render/wlr_renderer.h" | ||||
| 
 | ||||
| static void source_handle_destroy(struct wl_client *client, | ||||
| 		struct wl_resource *source_resource) { | ||||
|  | @ -96,6 +97,12 @@ static uint32_t get_swapchain_shm_format(struct wlr_swapchain *swapchain, | |||
| 	return format; | ||||
| } | ||||
| 
 | ||||
| static void add_drm_format(struct wlr_drm_format_set *set, const struct wlr_drm_format *fmt) { | ||||
| 	for (size_t i = 0; i < fmt->len; i++) { | ||||
| 		wlr_drm_format_set_add(set, fmt->format, fmt->modifiers[i]); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| bool wlr_ext_image_capture_source_v1_set_constraints_from_swapchain(struct wlr_ext_image_capture_source_v1 *source, | ||||
| 		struct wlr_swapchain *swapchain, struct wlr_renderer *renderer) { | ||||
| 	source->width = swapchain->width; | ||||
|  | @ -130,9 +137,21 @@ bool wlr_ext_image_capture_source_v1_set_constraints_from_swapchain(struct wlr_e | |||
| 		wlr_drm_format_set_finish(&source->dmabuf_formats); | ||||
| 		source->dmabuf_formats = (struct wlr_drm_format_set){0}; | ||||
| 
 | ||||
| 		for (size_t i = 0; i < swapchain->format.len; i++) { | ||||
| 			wlr_drm_format_set_add(&source->dmabuf_formats, | ||||
| 				swapchain->format.format, swapchain->format.modifiers[i]); | ||||
| 		add_drm_format(&source->dmabuf_formats, &swapchain->format); | ||||
| 
 | ||||
| 		const struct wlr_drm_format_set *render_formats = | ||||
| 			wlr_renderer_get_render_formats(renderer); | ||||
| 		assert(render_formats != NULL); | ||||
| 
 | ||||
| 		// Not all clients support fancy formats. Always ensure we provide
 | ||||
| 		// support for ARGB8888 and XRGB8888 for simple clients.
 | ||||
| 		uint32_t fallback_formats[] = { DRM_FORMAT_ARGB8888, DRM_FORMAT_XRGB8888 }; | ||||
| 		for (size_t i = 0; i < sizeof(fallback_formats) / sizeof(fallback_formats[0]); i++) { | ||||
| 			const struct wlr_drm_format *fmt = | ||||
| 				wlr_drm_format_set_get(render_formats, fallback_formats[i]); | ||||
| 			if (fmt != NULL && swapchain->format.format != fmt->format) { | ||||
| 				add_drm_format(&source->dmabuf_formats, fmt); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
|  |  | |||
|  | @ -6,10 +6,6 @@ | |||
| 
 | ||||
| #define FOREIGN_TOPLEVEL_IMAGE_SOURCE_MANAGER_V1_VERSION 1 | ||||
| 
 | ||||
| struct wlr_ext_foreign_toplevel_image_capture_source_v1 { | ||||
| 	struct wlr_ext_image_capture_source_v1 base; | ||||
| }; | ||||
| 
 | ||||
| static const struct ext_foreign_toplevel_image_capture_source_manager_v1_interface foreign_toplevel_manager_impl; | ||||
| 
 | ||||
| static struct wlr_ext_foreign_toplevel_image_capture_source_manager_v1 * | ||||
|  |  | |||
|  | @ -283,7 +283,8 @@ static void output_cursor_source_copy_frame(struct wlr_ext_image_capture_source_ | |||
| 	struct timespec now; | ||||
| 	clock_gettime(CLOCK_MONOTONIC, &now); | ||||
| 
 | ||||
| 	wlr_ext_image_copy_capture_frame_v1_ready(frame, WL_OUTPUT_TRANSFORM_NORMAL, &now); | ||||
| 	wlr_ext_image_copy_capture_frame_v1_ready(frame, | ||||
| 			cursor_source->output->transform, &now); | ||||
| } | ||||
| 
 | ||||
| static const struct wlr_ext_image_capture_source_v1_interface output_cursor_source_impl = { | ||||
|  |  | |||
|  | @ -39,19 +39,20 @@ wlr_files += files( | |||
| 	'buffer/resource.c', | ||||
| 	'wlr_alpha_modifier_v1.c', | ||||
| 	'wlr_color_management_v1.c', | ||||
| 	'wlr_color_representation_v1.c', | ||||
| 	'wlr_compositor.c', | ||||
| 	'wlr_content_type_v1.c', | ||||
| 	'wlr_cursor_shape_v1.c', | ||||
| 	'wlr_cursor.c', | ||||
| 	'wlr_cursor_shape_v1.c', | ||||
| 	'wlr_damage_ring.c', | ||||
| 	'wlr_data_control_v1.c', | ||||
| 	'wlr_drm.c', | ||||
| 	'wlr_export_dmabuf_v1.c', | ||||
| 	'wlr_foreign_toplevel_management_v1.c', | ||||
| 	'wlr_color_representation_v1.c', | ||||
| 	'wlr_ext_image_copy_capture_v1.c', | ||||
| 	'wlr_ext_foreign_toplevel_list_v1.c', | ||||
| 	'wlr_ext_data_control_v1.c', | ||||
| 	'wlr_ext_foreign_toplevel_list_v1.c', | ||||
| 	'wlr_ext_image_copy_capture_v1.c', | ||||
| 	'wlr_fixes.c', | ||||
| 	'wlr_foreign_toplevel_management_v1.c', | ||||
| 	'wlr_fractional_scale_v1.c', | ||||
| 	'wlr_gamma_control_v1.c', | ||||
| 	'wlr_idle_inhibit_v1.c', | ||||
|  |  | |||
|  | @ -288,18 +288,10 @@ static struct wlr_buffer *render_cursor_buffer(struct wlr_output_cursor *cursor) | |||
| 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) { | ||||
| 	if (!output->impl->set_cursor || output->software_cursor_locks > 0) { | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	struct wlr_output_cursor *hwcur = output->hardware_cursor; | ||||
| 	if (hwcur != NULL && hwcur != cursor) { | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	output->hardware_cursor = NULL; | ||||
| 
 | ||||
| 	struct wlr_texture *texture = cursor->texture; | ||||
| 
 | ||||
| 	// If the cursor was hidden or was a software cursor, the hardware
 | ||||
|  | @ -424,12 +416,15 @@ bool output_cursor_set_texture(struct wlr_output_cursor *cursor, | |||
| 		wl_list_init(&cursor->renderer_destroy.link); | ||||
| 	} | ||||
| 
 | ||||
| 	if (output_cursor_attempt_hardware(cursor)) { | ||||
| 		return true; | ||||
| 	if (output->hardware_cursor == NULL || output->hardware_cursor == cursor) { | ||||
| 		if (output_cursor_attempt_hardware(cursor)) { | ||||
| 			return true; | ||||
| 		} | ||||
| 
 | ||||
| 		wlr_log(WLR_DEBUG, "Falling back to software cursor on output '%s'", output->name); | ||||
| 		output_disable_hardware_cursor(output); | ||||
| 	} | ||||
| 
 | ||||
| 	wlr_log(WLR_DEBUG, "Falling back to software cursor on output '%s'", output->name); | ||||
| 	output_disable_hardware_cursor(output); | ||||
| 	output_cursor_damage_whole(cursor); | ||||
| 	return true; | ||||
| } | ||||
|  |  | |||
|  | @ -242,6 +242,15 @@ static void output_apply_state(struct wlr_output *output, | |||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	if (state->committed & WLR_OUTPUT_STATE_COLOR_TRANSFORM) { | ||||
| 		wlr_color_transform_unref(output->color_transform); | ||||
| 		if (state->color_transform != NULL) { | ||||
| 			output->color_transform = wlr_color_transform_ref(state->color_transform); | ||||
| 		} else { | ||||
| 			output->color_transform = NULL; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	bool geometry_updated = state->committed & | ||||
| 		(WLR_OUTPUT_STATE_MODE | WLR_OUTPUT_STATE_TRANSFORM | | ||||
| 		WLR_OUTPUT_STATE_SUBPIXEL); | ||||
|  | @ -407,6 +416,7 @@ void wlr_output_finish(struct wlr_output *output) { | |||
| 
 | ||||
| 	wlr_swapchain_destroy(output->cursor_swapchain); | ||||
| 	wlr_buffer_unlock(output->cursor_front_buffer); | ||||
| 	wlr_color_transform_unref(output->color_transform); | ||||
| 
 | ||||
| 	wlr_swapchain_destroy(output->swapchain); | ||||
| 
 | ||||
|  | @ -515,8 +525,7 @@ const struct wlr_output_image_description *output_pending_image_description( | |||
|  * Returns a bitfield of the unchanged fields. | ||||
|  * | ||||
|  * Some fields are not checked: damage always changes in-between frames, the | ||||
|  * gamma LUT is too expensive to check, the contents of the buffer might have | ||||
|  * changed, etc. | ||||
|  * contents of the buffer might have changed, etc. | ||||
|  */ | ||||
| static uint32_t output_compare_state(struct wlr_output *output, | ||||
| 		const struct wlr_output_state *state) { | ||||
|  | @ -562,6 +571,10 @@ static uint32_t output_compare_state(struct wlr_output *output, | |||
| 			output->subpixel == state->subpixel) { | ||||
| 		fields |= WLR_OUTPUT_STATE_SUBPIXEL; | ||||
| 	} | ||||
| 	if ((state->committed & WLR_OUTPUT_STATE_COLOR_TRANSFORM) && | ||||
| 			output->color_transform == state->color_transform) { | ||||
| 		fields |= WLR_OUTPUT_STATE_COLOR_TRANSFORM; | ||||
| 	} | ||||
| 	return fields; | ||||
| } | ||||
| 
 | ||||
|  | @ -759,7 +772,9 @@ void output_apply_commit(struct wlr_output *output, const struct wlr_output_stat | |||
| 	} | ||||
| 
 | ||||
| 	output_apply_state(output, state); | ||||
| } | ||||
| 
 | ||||
| void output_send_commit_event(struct wlr_output *output, const struct wlr_output_state *state) { | ||||
| 	struct timespec now; | ||||
| 	clock_gettime(CLOCK_MONOTONIC, &now); | ||||
| 	struct wlr_output_event_commit event = { | ||||
|  | @ -801,6 +816,7 @@ bool wlr_output_commit_state(struct wlr_output *output, | |||
| 	} | ||||
| 
 | ||||
| 	output_apply_commit(output, &pending); | ||||
| 	output_send_commit_event(output, &pending); | ||||
| 
 | ||||
| 	if (new_back_buffer) { | ||||
| 		wlr_buffer_unlock(pending.buffer); | ||||
|  |  | |||
|  | @ -35,16 +35,76 @@ static struct wlr_output *get_surface_frame_pacing_output(struct wlr_surface *su | |||
| 	return frame_pacing_output; | ||||
| } | ||||
| 
 | ||||
| static bool get_tf_preference(enum wlr_color_transfer_function tf) { | ||||
| 	switch (tf) { | ||||
| 	case WLR_COLOR_TRANSFER_FUNCTION_GAMMA22: | ||||
| 		return 0; | ||||
| 	case WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ: | ||||
| 		return 1; | ||||
| 	case WLR_COLOR_TRANSFER_FUNCTION_BT1886: | ||||
| 	case WLR_COLOR_TRANSFER_FUNCTION_SRGB: | ||||
| 	case WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR: | ||||
| 		return -1; | ||||
| 	} | ||||
| 	abort(); // unreachable
 | ||||
| } | ||||
| 
 | ||||
| static bool get_primaries_preference(enum wlr_color_named_primaries primaries) { | ||||
| 	switch (primaries) { | ||||
| 	case WLR_COLOR_NAMED_PRIMARIES_SRGB: | ||||
| 		return 0; | ||||
| 	case WLR_COLOR_NAMED_PRIMARIES_BT2020: | ||||
| 		return 1; | ||||
| 	} | ||||
| 	abort(); // unreachable
 | ||||
| } | ||||
| 
 | ||||
| static void get_surface_preferred_image_description(struct wlr_surface *surface, | ||||
| 		struct wlr_image_description_v1_data *out) { | ||||
| 	struct wlr_output_image_description preferred = { | ||||
| 		.transfer_function = WLR_COLOR_TRANSFER_FUNCTION_GAMMA22, | ||||
| 		.primaries = WLR_COLOR_NAMED_PRIMARIES_SRGB, | ||||
| 	}; | ||||
| 
 | ||||
| 	struct wlr_surface_output *surface_output; | ||||
| 	wl_list_for_each(surface_output, &surface->current_outputs, link) { | ||||
| 		const struct wlr_output_image_description *img_desc = | ||||
| 			surface_output->output->image_description; | ||||
| 		if (img_desc == NULL) { | ||||
| 			continue; | ||||
| 		} | ||||
| 		if (get_tf_preference(preferred.transfer_function) < get_tf_preference(img_desc->transfer_function)) { | ||||
| 			preferred.transfer_function = img_desc->transfer_function; | ||||
| 		} | ||||
| 		if (get_primaries_preference(preferred.primaries) < get_primaries_preference(img_desc->primaries)) { | ||||
| 			preferred.primaries = img_desc->primaries; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	*out = (struct wlr_image_description_v1_data){ | ||||
| 		.tf_named = wlr_color_manager_v1_transfer_function_from_wlr(preferred.transfer_function), | ||||
| 		.primaries_named = wlr_color_manager_v1_primaries_from_wlr(preferred.primaries), | ||||
| 	}; | ||||
| } | ||||
| 
 | ||||
| static void handle_scene_buffer_outputs_update( | ||||
| 		struct wl_listener *listener, void *data) { | ||||
| 	struct wlr_scene_surface *surface = | ||||
| 		wl_container_of(listener, surface, outputs_update); | ||||
| 	struct wlr_scene *scene = scene_node_get_root(&surface->buffer->node); | ||||
| 
 | ||||
| 	surface->frame_pacing_output = get_surface_frame_pacing_output(surface->surface); | ||||
| 
 | ||||
| 	double scale = get_surface_preferred_buffer_scale(surface->surface); | ||||
| 	wlr_fractional_scale_v1_notify_scale(surface->surface, scale); | ||||
| 	wlr_surface_set_preferred_buffer_scale(surface->surface, ceil(scale)); | ||||
| 
 | ||||
| 	if (scene->color_manager_v1 != NULL) { | ||||
| 		struct wlr_image_description_v1_data img_desc = {0}; | ||||
| 		get_surface_preferred_image_description(surface->surface, &img_desc); | ||||
| 		wlr_color_manager_v1_set_surface_preferred_image_description(scene->color_manager_v1, | ||||
| 			surface->surface, &img_desc); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static void handle_scene_buffer_output_enter( | ||||
|  | @ -190,35 +250,13 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) { | |||
| 		opacity = (float)alpha_modifier_state->multiplier; | ||||
| 	} | ||||
| 
 | ||||
| 	enum wlr_color_transfer_function tf = WLR_COLOR_TRANSFER_FUNCTION_SRGB; | ||||
| 	enum wlr_color_transfer_function tf = WLR_COLOR_TRANSFER_FUNCTION_GAMMA22; | ||||
| 	enum wlr_color_named_primaries primaries = WLR_COLOR_NAMED_PRIMARIES_SRGB; | ||||
| 	const struct wlr_image_description_v1_data *img_desc = | ||||
| 		wlr_surface_get_image_description_v1_data(surface); | ||||
| 	if (img_desc != NULL) { | ||||
| 		switch (img_desc->tf_named) { | ||||
| 		case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB: | ||||
| 			tf = WLR_COLOR_TRANSFER_FUNCTION_SRGB; | ||||
| 			break; | ||||
| 		case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ: | ||||
| 			tf = WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ; | ||||
| 			break; | ||||
| 		case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR: | ||||
| 			tf = WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR; | ||||
| 			break; | ||||
| 		default: | ||||
| 			abort(); | ||||
| 		} | ||||
| 
 | ||||
| 		switch (img_desc->primaries_named) { | ||||
| 		case WP_COLOR_MANAGER_V1_PRIMARIES_SRGB: | ||||
| 			primaries = WLR_COLOR_NAMED_PRIMARIES_SRGB; | ||||
| 			break; | ||||
| 		case WP_COLOR_MANAGER_V1_PRIMARIES_BT2020: | ||||
| 			primaries = WLR_COLOR_NAMED_PRIMARIES_BT2020; | ||||
| 			break; | ||||
| 		default: | ||||
| 			abort(); | ||||
| 		} | ||||
| 		tf = wlr_color_manager_v1_transfer_function_to_wlr(img_desc->tf_named); | ||||
| 		primaries = wlr_color_manager_v1_primaries_to_wlr(img_desc->primaries_named); | ||||
| 	} | ||||
| 
 | ||||
| 	wlr_scene_buffer_set_opaque_region(scene_buffer, &opaque); | ||||
|  | @ -239,10 +277,9 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) { | |||
| 		// can't use the cached scene_buffer->is_single_pixel_buffer
 | ||||
| 		// because that's only set later on.
 | ||||
| 		bool is_single_pixel_buffer = false; | ||||
| 		struct wlr_client_buffer *client_buffer = wlr_client_buffer_get(&surface->buffer->base); | ||||
| 		if (client_buffer != NULL && client_buffer->source != NULL) { | ||||
| 		if (surface->buffer->source != NULL) { | ||||
| 			struct wlr_single_pixel_buffer_v1 *spb = | ||||
| 				wlr_single_pixel_buffer_v1_try_from_buffer(client_buffer->source); | ||||
| 				wlr_single_pixel_buffer_v1_try_from_buffer(surface->buffer->source); | ||||
| 			is_single_pixel_buffer = spb != NULL; | ||||
| 		} | ||||
| 		if (!is_single_pixel_buffer) { | ||||
|  | @ -268,7 +305,8 @@ static void surface_reconfigure(struct wlr_scene_surface *scene_surface) { | |||
| 			&surface->buffer->base, &options); | ||||
| 
 | ||||
| 		if (syncobj_surface_state != NULL && | ||||
| 				(surface->current.committed & WLR_SURFACE_STATE_BUFFER)) { | ||||
| 				(surface->current.committed & WLR_SURFACE_STATE_BUFFER) && | ||||
| 				surface->buffer->source != NULL) { | ||||
| 			wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer(syncobj_surface_state, | ||||
| 				surface->buffer->source); | ||||
| 		} | ||||
|  |  | |||
|  | @ -5,6 +5,7 @@ | |||
| #include <wlr/render/swapchain.h> | ||||
| #include <wlr/render/drm_syncobj.h> | ||||
| #include <wlr/render/wlr_renderer.h> | ||||
| #include <wlr/types/wlr_color_management_v1.h> | ||||
| #include <wlr/types/wlr_compositor.h> | ||||
| #include <wlr/types/wlr_damage_ring.h> | ||||
| #include <wlr/types/wlr_gamma_control_v1.h> | ||||
|  | @ -1529,6 +1530,8 @@ static void scene_handle_gamma_control_manager_v1_set_gamma(struct wl_listener * | |||
| 
 | ||||
| 	output->gamma_lut_changed = true; | ||||
| 	output->gamma_lut = event->control; | ||||
| 	wlr_color_transform_unref(output->gamma_lut_color_transform); | ||||
| 	output->gamma_lut_color_transform = wlr_gamma_control_v1_get_color_transform(event->control); | ||||
| 	wlr_output_schedule_frame(output->output); | ||||
| } | ||||
| 
 | ||||
|  | @ -1546,6 +1549,8 @@ static void scene_handle_gamma_control_manager_v1_destroy(struct wl_listener *li | |||
| 	wl_list_for_each(output, &scene->outputs, link) { | ||||
| 		output->gamma_lut_changed = false; | ||||
| 		output->gamma_lut = NULL; | ||||
| 		wlr_color_transform_unref(output->gamma_lut_color_transform); | ||||
| 		output->gamma_lut_color_transform = NULL; | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
|  | @ -1562,6 +1567,21 @@ void wlr_scene_set_gamma_control_manager_v1(struct wlr_scene *scene, | |||
| 	wl_signal_add(&gamma_control->events.set_gamma, &scene->gamma_control_manager_v1_set_gamma); | ||||
| } | ||||
| 
 | ||||
| static void scene_handle_color_manager_v1_destroy(struct wl_listener *listener, void *data) { | ||||
| 	struct wlr_scene *scene = wl_container_of(listener, scene, color_manager_v1_destroy); | ||||
| 	wl_list_remove(&scene->color_manager_v1_destroy.link); | ||||
| 	wl_list_init(&scene->color_manager_v1_destroy.link); | ||||
| 	scene->color_manager_v1 = NULL; | ||||
| } | ||||
| 
 | ||||
| void wlr_scene_set_color_manager_v1(struct wlr_scene *scene, struct wlr_color_manager_v1 *manager) { | ||||
| 	assert(scene->color_manager_v1 == NULL); | ||||
| 	scene->color_manager_v1 = manager; | ||||
| 
 | ||||
| 	scene->color_manager_v1_destroy.notify = scene_handle_color_manager_v1_destroy; | ||||
| 	wl_signal_add(&manager->events.destroy, &scene->color_manager_v1_destroy); | ||||
| } | ||||
| 
 | ||||
| static void scene_output_handle_destroy(struct wlr_addon *addon) { | ||||
| 	struct wlr_scene_output *scene_output = | ||||
| 		wl_container_of(addon, scene_output, addon); | ||||
|  | @ -1750,6 +1770,10 @@ void wlr_scene_output_destroy(struct wlr_scene_output *scene_output) { | |||
| 	wl_list_remove(&scene_output->output_damage.link); | ||||
| 	wl_list_remove(&scene_output->output_needs_frame.link); | ||||
| 	wlr_drm_syncobj_timeline_unref(scene_output->in_timeline); | ||||
| 	wlr_color_transform_unref(scene_output->gamma_lut_color_transform); | ||||
| 	wlr_color_transform_unref(scene_output->prev_gamma_lut_color_transform); | ||||
| 	wlr_color_transform_unref(scene_output->prev_supplied_color_transform); | ||||
| 	wlr_color_transform_unref(scene_output->prev_combined_color_transform); | ||||
| 	wl_array_release(&scene_output->render_list); | ||||
| 	free(scene_output); | ||||
| } | ||||
|  | @ -1909,6 +1933,27 @@ static void scene_buffer_send_dmabuf_feedback(const struct wlr_scene *scene, | |||
| 	wlr_linux_dmabuf_feedback_v1_finish(&feedback); | ||||
| } | ||||
| 
 | ||||
| static bool color_management_is_scanout_allowed(const struct wlr_output_image_description *img_desc, | ||||
| 		const struct wlr_scene_buffer *buffer) { | ||||
| 	// Disallow scanout if the output has colorimetry information but buffer
 | ||||
| 	// doesn't; allow it only if the output also lacks it.
 | ||||
| 	if (buffer->transfer_function == 0 && buffer->primaries == 0) { | ||||
| 		return img_desc == NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	// If the output has colorimetry information, the buffer must match it for
 | ||||
| 	// direct scanout to be allowed.
 | ||||
| 	if (img_desc != NULL) { | ||||
| 		return img_desc->transfer_function == buffer->transfer_function && | ||||
| 				img_desc->primaries == buffer->primaries; | ||||
| 	} | ||||
| 	// If the output doesn't have colorimetry image description set, we can only
 | ||||
| 	// scan out buffers with default colorimetry (gamma2.2 transfer and sRGB
 | ||||
| 	// primaries) used in wlroots.
 | ||||
| 	return buffer->transfer_function == WLR_COLOR_TRANSFER_FUNCTION_GAMMA22 && | ||||
| 			buffer->primaries == WLR_COLOR_NAMED_PRIMARIES_SRGB; | ||||
| } | ||||
| 
 | ||||
| enum scene_direct_scanout_result { | ||||
| 	// This scene node is not a candidate for scanout
 | ||||
| 	SCANOUT_INELIGIBLE, | ||||
|  | @ -1966,20 +2011,8 @@ static enum scene_direct_scanout_result scene_entry_try_direct_scanout( | |||
| 	} | ||||
| 
 | ||||
| 	const struct wlr_output_image_description *img_desc = output_pending_image_description(scene_output->output, state); | ||||
| 	if (buffer->transfer_function != 0 || buffer->primaries != 0) { | ||||
| 		if (img_desc == NULL || img_desc->transfer_function != buffer->transfer_function || | ||||
| 				img_desc->primaries != buffer->primaries) { | ||||
| 			return false; | ||||
| 		} | ||||
| 	} else if (img_desc != NULL) { | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	if (buffer->transfer_function != 0 && buffer->transfer_function != WLR_COLOR_TRANSFER_FUNCTION_SRGB) { | ||||
| 		return false; | ||||
| 	} | ||||
| 	if (buffer->primaries != 0 && buffer->primaries != WLR_COLOR_NAMED_PRIMARIES_SRGB) { | ||||
| 		return false; | ||||
| 	if (!color_management_is_scanout_allowed(img_desc, buffer)) { | ||||
| 		return SCANOUT_INELIGIBLE; | ||||
| 	} | ||||
| 
 | ||||
| 	// We want to ensure optimal buffer selection, but as direct-scanout can be enabled and disabled
 | ||||
|  | @ -2079,16 +2112,15 @@ static void scene_output_state_attempt_gamma(struct wlr_scene_output *scene_outp | |||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!wlr_gamma_control_v1_apply(scene_output->gamma_lut, &gamma_pending)) { | ||||
| 		wlr_output_state_finish(&gamma_pending); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	wlr_output_state_set_color_transform(&gamma_pending, scene_output->gamma_lut_color_transform); | ||||
| 	scene_output->gamma_lut_changed = false; | ||||
| 
 | ||||
| 	if (!wlr_output_test_state(scene_output->output, &gamma_pending)) { | ||||
| 		wlr_gamma_control_v1_send_failed_and_destroy(scene_output->gamma_lut); | ||||
| 
 | ||||
| 		scene_output->gamma_lut = NULL; | ||||
| 		wlr_color_transform_unref(scene_output->gamma_lut_color_transform); | ||||
| 		scene_output->gamma_lut_color_transform = NULL; | ||||
| 		wlr_output_state_finish(&gamma_pending); | ||||
| 		return; | ||||
| 	} | ||||
|  | @ -2097,6 +2129,41 @@ static void scene_output_state_attempt_gamma(struct wlr_scene_output *scene_outp | |||
| 	wlr_output_state_finish(&gamma_pending); | ||||
| } | ||||
| 
 | ||||
| static struct wlr_color_transform *scene_output_combine_color_transforms( | ||||
| 		struct wlr_scene_output *scene_output, struct wlr_color_transform *supplied) { | ||||
| 	struct wlr_color_transform *gamma_lut = scene_output->gamma_lut_color_transform; | ||||
| 	assert(gamma_lut != NULL); | ||||
| 
 | ||||
| 	if (gamma_lut == scene_output->prev_gamma_lut_color_transform && | ||||
| 			supplied == scene_output->prev_supplied_color_transform) { | ||||
| 		return wlr_color_transform_ref(scene_output->prev_combined_color_transform); | ||||
| 	} | ||||
| 
 | ||||
| 	struct wlr_color_transform *combined; | ||||
| 	if (supplied == NULL) { | ||||
| 		combined = wlr_color_transform_ref(gamma_lut); | ||||
| 	} else { | ||||
| 		struct wlr_color_transform *transforms[] = { | ||||
| 			gamma_lut, | ||||
| 			supplied, | ||||
| 		}; | ||||
| 		size_t transforms_len = sizeof(transforms) / sizeof(transforms[0]); | ||||
| 		combined = wlr_color_transform_init_pipeline(transforms, transforms_len); | ||||
| 		if (combined == NULL) { | ||||
| 			return NULL; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	wlr_color_transform_unref(scene_output->prev_gamma_lut_color_transform); | ||||
| 	scene_output->prev_gamma_lut_color_transform = wlr_color_transform_ref(gamma_lut); | ||||
| 	wlr_color_transform_unref(scene_output->prev_supplied_color_transform); | ||||
| 	scene_output->prev_supplied_color_transform = supplied ? wlr_color_transform_ref(supplied) : NULL; | ||||
| 	wlr_color_transform_unref(scene_output->prev_combined_color_transform); | ||||
| 	scene_output->prev_combined_color_transform = wlr_color_transform_ref(combined); | ||||
| 
 | ||||
| 	return combined; | ||||
| } | ||||
| 
 | ||||
| bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, | ||||
| 		struct wlr_output_state *state, const struct wlr_scene_output_state_options *options) { | ||||
| 	struct wlr_scene_output_state_options default_options = {0}; | ||||
|  | @ -2120,6 +2187,16 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, | |||
| 	enum wlr_scene_debug_damage_option debug_damage = | ||||
| 		scene_output->scene->debug_damage_option; | ||||
| 
 | ||||
| 	bool render_gamma_lut = false; | ||||
| 	if (wlr_output_get_gamma_size(output) == 0 && output->renderer->features.output_color_transform) { | ||||
| 		if (scene_output->gamma_lut_color_transform != scene_output->prev_gamma_lut_color_transform) { | ||||
| 			scene_output_damage_whole(scene_output); | ||||
| 		} | ||||
| 		if (scene_output->gamma_lut_color_transform != NULL) { | ||||
| 			render_gamma_lut = true; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	struct render_data render_data = { | ||||
| 		.transform = output->transform, | ||||
| 		.scale = output->scale, | ||||
|  | @ -2220,7 +2297,7 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, | |||
| 	// - There are no color transforms that need to be applied
 | ||||
| 	// - Damage highlight debugging is not enabled
 | ||||
| 	enum scene_direct_scanout_result scanout_result = SCANOUT_INELIGIBLE; | ||||
| 	if (options->color_transform == NULL && list_len == 1 | ||||
| 	if (options->color_transform == NULL && !render_gamma_lut && list_len == 1 | ||||
| 			&& debug_damage != WLR_SCENE_DEBUG_DAMAGE_HIGHLIGHT) { | ||||
| 		scanout_result = scene_entry_try_direct_scanout(&list_data[0], state, &render_data); | ||||
| 	} | ||||
|  | @ -2294,6 +2371,17 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, | |||
| 		color_transform = wlr_color_transform_ref(options->color_transform); | ||||
| 	} | ||||
| 
 | ||||
| 	if (render_gamma_lut) { | ||||
| 		struct wlr_color_transform *combined = | ||||
| 			scene_output_combine_color_transforms(scene_output, color_transform); | ||||
| 		wlr_color_transform_unref(color_transform); | ||||
| 		if (combined == NULL) { | ||||
| 			wlr_buffer_unlock(buffer); | ||||
| 			return false; | ||||
| 		} | ||||
| 		color_transform = combined; | ||||
| 	} | ||||
| 
 | ||||
| 	scene_output->in_point++; | ||||
| 	struct wlr_render_pass *render_pass = wlr_renderer_begin_buffer_pass(output->renderer, buffer, | ||||
| 			&(struct wlr_buffer_pass_options){ | ||||
|  | @ -2416,7 +2504,9 @@ bool wlr_scene_output_build_state(struct wlr_scene_output *scene_output, | |||
| 			scene_output->in_point); | ||||
| 	} | ||||
| 
 | ||||
| 	scene_output_state_attempt_gamma(scene_output, state); | ||||
| 	if (!render_gamma_lut) { | ||||
| 		scene_output_state_attempt_gamma(scene_output, state); | ||||
| 	} | ||||
| 
 | ||||
| 	return true; | ||||
| } | ||||
|  |  | |||
|  | @ -7,6 +7,7 @@ | |||
| #include <wlr/types/wlr_output.h> | ||||
| #include <wlr/util/addon.h> | ||||
| 
 | ||||
| #include "color-management-v1-protocol.h" | ||||
| #include "render/color.h" | ||||
| #include "util/mem.h" | ||||
| 
 | ||||
|  | @ -64,32 +65,6 @@ static void resource_handle_destroy(struct wl_client *client, struct wl_resource | |||
| 	wl_resource_destroy(resource); | ||||
| } | ||||
| 
 | ||||
| static enum wlr_color_named_primaries named_primaries_to_wlr( | ||||
| 		enum wp_color_manager_v1_primaries primaries) { | ||||
| 	switch (primaries) { | ||||
| 	case WP_COLOR_MANAGER_V1_PRIMARIES_SRGB: | ||||
| 		return WLR_COLOR_NAMED_PRIMARIES_SRGB; | ||||
| 	case WP_COLOR_MANAGER_V1_PRIMARIES_BT2020: | ||||
| 		return WLR_COLOR_NAMED_PRIMARIES_BT2020; | ||||
| 	default: | ||||
| 		abort(); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static enum wlr_color_transfer_function transfer_function_to_wlr( | ||||
| 		enum wp_color_manager_v1_transfer_function tf) { | ||||
| 	switch (tf) { | ||||
| 	case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB: | ||||
| 		return WLR_COLOR_TRANSFER_FUNCTION_SRGB; | ||||
| 	case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ: | ||||
| 		return WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ; | ||||
| 	case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR: | ||||
| 		return WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR; | ||||
| 	default: | ||||
| 		abort(); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| static int32_t encode_cie1931_coord(float value) { | ||||
| 	return round(value * 1000 * 1000); | ||||
| } | ||||
|  | @ -131,10 +106,12 @@ static void image_desc_handle_get_information(struct wl_client *client, | |||
| 	} | ||||
| 
 | ||||
| 	struct wlr_color_primaries primaries; | ||||
| 	wlr_color_primaries_from_named(&primaries, named_primaries_to_wlr(image_desc->data.primaries_named)); | ||||
| 	wlr_color_primaries_from_named(&primaries, | ||||
| 		wlr_color_manager_v1_primaries_to_wlr(image_desc->data.primaries_named)); | ||||
| 
 | ||||
| 	struct wlr_color_luminances luminances; | ||||
| 	wlr_color_transfer_function_get_default_luminance(transfer_function_to_wlr(image_desc->data.tf_named), &luminances); | ||||
| 	wlr_color_transfer_function_get_default_luminance( | ||||
| 		wlr_color_manager_v1_transfer_function_to_wlr(image_desc->data.tf_named), &luminances); | ||||
| 
 | ||||
| 	wp_image_description_info_v1_send_primaries_named(resource, image_desc->data.primaries_named); | ||||
| 	wp_image_description_info_v1_send_primaries(resource, | ||||
|  | @ -235,9 +212,14 @@ static void cm_output_handle_get_image_description(struct wl_client *client, | |||
| 	} | ||||
| 
 | ||||
| 	struct wlr_image_description_v1_data data = { | ||||
| 		.tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB, | ||||
| 		.tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22, | ||||
| 		.primaries_named = WP_COLOR_MANAGER_V1_PRIMARIES_SRGB, | ||||
| 	}; | ||||
| 	const struct wlr_output_image_description *image_desc = cm_output->output->image_description; | ||||
| 	if (image_desc != NULL) { | ||||
| 		data.tf_named = wlr_color_manager_v1_transfer_function_from_wlr(image_desc->transfer_function); | ||||
| 		data.primaries_named = wlr_color_manager_v1_primaries_from_wlr(image_desc->primaries); | ||||
| 	} | ||||
| 	image_desc_create_ready(cm_output->manager, cm_output_resource, id, &data, true); | ||||
| } | ||||
| 
 | ||||
|  | @ -310,6 +292,13 @@ static void cm_surface_handle_set_image_description(struct wl_client *client, | |||
| 
 | ||||
| 	struct wlr_image_description_v1 *image_desc = image_desc_from_resource(image_desc_resource); | ||||
| 
 | ||||
| 	if (image_desc == NULL) { | ||||
| 		wl_resource_post_error(cm_surface_resource, | ||||
| 			WP_COLOR_MANAGEMENT_SURFACE_V1_ERROR_IMAGE_DESCRIPTION, | ||||
| 			"Image description to be set is invalid"); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	bool found = false; | ||||
| 	for (size_t i = 0; i < cm_surface->manager->render_intents_len; i++) { | ||||
| 		if (cm_surface->manager->render_intents[i] == render_intent) { | ||||
|  | @ -677,29 +666,35 @@ static void manager_handle_get_output(struct wl_client *client, | |||
| 	struct wlr_color_manager_v1 *manager = manager_from_resource(manager_resource); | ||||
| 	struct wlr_output *output = wlr_output_from_resource(output_resource); | ||||
| 
 | ||||
| 	uint32_t version = wl_resource_get_version(manager_resource); | ||||
| 	struct wl_resource *cm_output_resource = wl_resource_create(client, | ||||
| 		&wp_color_management_output_v1_interface, version, id); | ||||
| 	if (!cm_output_resource) { | ||||
| 		wl_client_post_no_memory(client); | ||||
| 		return; | ||||
| 	} | ||||
| 	wl_resource_set_implementation(cm_output_resource, &cm_output_impl, | ||||
| 		NULL, cm_output_handle_resource_destroy); | ||||
| 
 | ||||
| 	if (output == NULL) { | ||||
| 		return; // leave the wp_color_management_output_v1 resource inert
 | ||||
| 	} | ||||
| 
 | ||||
| 	struct wlr_color_management_output_v1 *cm_output = calloc(1, sizeof(*cm_output)); | ||||
| 	if (cm_output == NULL) { | ||||
| 		wl_client_post_no_memory(client); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	cm_output->resource = cm_output_resource; | ||||
| 	cm_output->manager = manager; | ||||
| 
 | ||||
| 	uint32_t version = wl_resource_get_version(manager_resource); | ||||
| 	cm_output->resource = wl_resource_create(client, | ||||
| 		&wp_color_management_output_v1_interface, version, id); | ||||
| 	if (!cm_output->resource) { | ||||
| 		wl_client_post_no_memory(client); | ||||
| 		free(cm_output); | ||||
| 		return; | ||||
| 	} | ||||
| 	wl_resource_set_implementation(cm_output->resource, &cm_output_impl, | ||||
| 		cm_output, cm_output_handle_resource_destroy); | ||||
| 	cm_output->output = output; | ||||
| 
 | ||||
| 	cm_output->output_destroy.notify = cm_output_handle_output_destroy; | ||||
| 	wl_signal_add(&output->events.destroy, &cm_output->output_destroy); | ||||
| 
 | ||||
| 	wl_list_insert(&manager->outputs, &cm_output->link); | ||||
| 	wl_resource_set_user_data(cm_output->resource, cm_output); | ||||
| } | ||||
| 
 | ||||
| static struct wlr_color_management_surface_v1 *cm_surface_from_surface(struct wlr_surface *surface) { | ||||
|  | @ -782,7 +777,7 @@ static void manager_handle_get_surface_feedback(struct wl_client *client, | |||
| 
 | ||||
| 	surface_feedback->surface = surface; | ||||
| 	surface_feedback->data = (struct wlr_image_description_v1_data){ | ||||
| 		.tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB, | ||||
| 		.tf_named = WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22, | ||||
| 		.primaries_named = WP_COLOR_MANAGER_V1_PRIMARIES_SRGB, | ||||
| 	}; | ||||
| 
 | ||||
|  | @ -892,6 +887,8 @@ static void manager_bind(struct wl_client *client, void *data, | |||
| 
 | ||||
| static void manager_handle_display_destroy(struct wl_listener *listener, void *data) { | ||||
| 	struct wlr_color_manager_v1 *manager = wl_container_of(listener, manager, display_destroy); | ||||
| 	wl_signal_emit_mutable(&manager->events.destroy, NULL); | ||||
| 	assert(wl_list_empty(&manager->events.destroy.listener_list)); | ||||
| 	wl_list_remove(&manager->display_destroy.link); | ||||
| 	wl_global_destroy(manager->global); | ||||
| 	free(manager->render_intents); | ||||
|  | @ -939,6 +936,7 @@ struct wlr_color_manager_v1 *wlr_color_manager_v1_create(struct wl_display *disp | |||
| 	manager->transfer_functions_len = options->transfer_functions_len; | ||||
| 	manager->primaries_len = options->primaries_len; | ||||
| 
 | ||||
| 	wl_signal_init(&manager->events.destroy); | ||||
| 	wl_list_init(&manager->outputs); | ||||
| 	wl_list_init(&manager->surface_feedbacks); | ||||
| 
 | ||||
|  | @ -985,3 +983,61 @@ void wlr_color_manager_v1_set_surface_preferred_image_description( | |||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| enum wlr_color_transfer_function | ||||
| wlr_color_manager_v1_transfer_function_to_wlr(enum wp_color_manager_v1_transfer_function tf) { | ||||
| 	switch (tf) { | ||||
| 	case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB: | ||||
| 		return WLR_COLOR_TRANSFER_FUNCTION_SRGB; | ||||
| 	case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ: | ||||
| 		return WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ; | ||||
| 	case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR: | ||||
| 		return WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR; | ||||
| 	case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22: | ||||
| 		return WLR_COLOR_TRANSFER_FUNCTION_GAMMA22; | ||||
| 	case WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_BT1886: | ||||
| 		return WLR_COLOR_TRANSFER_FUNCTION_BT1886; | ||||
| 	default: | ||||
| 		abort(); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| enum wp_color_manager_v1_transfer_function | ||||
| wlr_color_manager_v1_transfer_function_from_wlr(enum wlr_color_transfer_function tf) { | ||||
| 	switch (tf) { | ||||
| 	case WLR_COLOR_TRANSFER_FUNCTION_SRGB: | ||||
| 		return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_SRGB; | ||||
| 	case WLR_COLOR_TRANSFER_FUNCTION_ST2084_PQ: | ||||
| 		return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_ST2084_PQ; | ||||
| 	case WLR_COLOR_TRANSFER_FUNCTION_EXT_LINEAR: | ||||
| 		return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_EXT_LINEAR; | ||||
| 	case WLR_COLOR_TRANSFER_FUNCTION_GAMMA22: | ||||
| 		return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_GAMMA22; | ||||
| 	case WLR_COLOR_TRANSFER_FUNCTION_BT1886: | ||||
| 		return WP_COLOR_MANAGER_V1_TRANSFER_FUNCTION_BT1886; | ||||
| 	} | ||||
| 	abort(); | ||||
| } | ||||
| 
 | ||||
| enum wlr_color_named_primaries | ||||
| wlr_color_manager_v1_primaries_to_wlr(enum wp_color_manager_v1_primaries primaries) { | ||||
| 	switch (primaries) { | ||||
| 	case WP_COLOR_MANAGER_V1_PRIMARIES_SRGB: | ||||
| 		return WLR_COLOR_NAMED_PRIMARIES_SRGB; | ||||
| 	case WP_COLOR_MANAGER_V1_PRIMARIES_BT2020: | ||||
| 		return WLR_COLOR_NAMED_PRIMARIES_BT2020; | ||||
| 	default: | ||||
| 		abort(); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| enum wp_color_manager_v1_primaries | ||||
| wlr_color_manager_v1_primaries_from_wlr(enum wlr_color_named_primaries primaries) { | ||||
| 	switch (primaries) { | ||||
| 	case WLR_COLOR_NAMED_PRIMARIES_SRGB: | ||||
| 		return WP_COLOR_MANAGER_V1_PRIMARIES_SRGB; | ||||
| 	case WLR_COLOR_NAMED_PRIMARIES_BT2020: | ||||
| 		return WP_COLOR_MANAGER_V1_PRIMARIES_BT2020; | ||||
| 	} | ||||
| 	abort(); | ||||
| } | ||||
|  |  | |||
|  | @ -370,9 +370,11 @@ struct wlr_color_representation_manager_v1 *wlr_color_representation_manager_v1_ | |||
| 	ok &= memdup(&manager->supported_alpha_modes, | ||||
| 		options->supported_alpha_modes, | ||||
| 		sizeof(options->supported_alpha_modes[0]) * options->supported_alpha_modes_len); | ||||
| 	manager->supported_alpha_modes_len = options->supported_alpha_modes_len; | ||||
| 	ok &= memdup(&manager->supported_coeffs_and_ranges, | ||||
| 		options->supported_coeffs_and_ranges, | ||||
| 		sizeof(options->supported_coeffs_and_ranges[0]) * options->supported_coeffs_and_ranges_len); | ||||
| 	manager->supported_coeffs_and_ranges_len = options->supported_coeffs_and_ranges_len; | ||||
| 	if (!ok) { | ||||
| 		goto err_options; | ||||
| 	} | ||||
|  | @ -384,6 +386,8 @@ struct wlr_color_representation_manager_v1 *wlr_color_representation_manager_v1_ | |||
| 		goto err_options; | ||||
| 	} | ||||
| 
 | ||||
| 	wl_signal_init(&manager->events.destroy); | ||||
| 
 | ||||
| 	manager->display_destroy.notify = handle_display_destroy; | ||||
| 	wl_display_add_destroy_listener(display, &manager->display_destroy); | ||||
| 
 | ||||
|  |  | |||
|  | @ -882,11 +882,7 @@ void wlr_surface_reject_pending(struct wlr_surface *surface, struct wl_resource | |||
| 	va_list args; | ||||
| 	va_start(args, msg); | ||||
| 
 | ||||
| 	// XXX: libwayland could expose wl_resource_post_error_vargs() instead
 | ||||
| 	char buffer[128]; // Matches the size of the buffer used in libwayland
 | ||||
| 	vsnprintf(buffer, sizeof(buffer), msg, args); | ||||
| 
 | ||||
| 	wl_resource_post_error(resource, code, "%s", buffer); | ||||
| 	wl_resource_post_error_vargs(resource, code, msg, args); | ||||
| 	surface->pending_rejected = true; | ||||
| 
 | ||||
| 	va_end(args); | ||||
|  |  | |||
|  | @ -3,6 +3,8 @@ | |||
| #include <wlr/types/wlr_content_type_v1.h> | ||||
| #include <wlr/types/wlr_compositor.h> | ||||
| 
 | ||||
| #include "content-type-v1-protocol.h" | ||||
| 
 | ||||
| #define CONTENT_TYPE_VERSION 1 | ||||
| 
 | ||||
| struct wlr_content_type_v1_surface { | ||||
|  |  | |||
|  | @ -530,10 +530,6 @@ static void cursor_output_cursor_update(struct wlr_cursor_output_cursor *output_ | |||
| 	struct wlr_cursor *cur = output_cursor->cursor; | ||||
| 	struct wlr_output *output = output_cursor->output_cursor->output; | ||||
| 
 | ||||
| 	if (!output->enabled) { | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	cursor_output_cursor_reset_image(output_cursor); | ||||
| 
 | ||||
| 	if (cur->state->buffer != NULL) { | ||||
|  | @ -589,10 +585,11 @@ static void cursor_output_cursor_update(struct wlr_cursor_output_cursor *output_ | |||
| 			&src_box, dst_width, dst_height, surface->current.transform, | ||||
| 			hotspot_x, hotspot_y, wait_timeline, wait_point); | ||||
| 
 | ||||
| 		if (syncobj_surface_state != NULL && surface->buffer != NULL && | ||||
| 		if (syncobj_surface_state != NULL && | ||||
| 				surface->buffer != NULL && surface->buffer->source != NULL && | ||||
| 				(surface->current.committed & WLR_SURFACE_STATE_BUFFER)) { | ||||
| 			wlr_linux_drm_syncobj_v1_state_signal_release_with_buffer(syncobj_surface_state, | ||||
| 				&surface->buffer->base); | ||||
| 				surface->buffer->source); | ||||
| 		} | ||||
| 
 | ||||
| 		if (output_cursor->output_cursor->visible) { | ||||
|  |  | |||
|  | @ -5,6 +5,8 @@ | |||
| #include <wlr/types/wlr_cursor_shape_v1.h> | ||||
| #include <wlr/types/wlr_seat.h> | ||||
| #include <wlr/types/wlr_tablet_tool.h> | ||||
| 
 | ||||
| #include "cursor-shape-v1-protocol.h" | ||||
| #include "types/wlr_tablet_v2.h" | ||||
| 
 | ||||
| #define CURSOR_SHAPE_MANAGER_V1_VERSION 2 | ||||
|  |  | |||
|  | @ -68,10 +68,6 @@ static void drm_lease_connector_v1_destroy( | |||
| 
 | ||||
| 	wlr_log(WLR_DEBUG, "Destroying connector %s", connector->output->name); | ||||
| 
 | ||||
| 	if (connector->active_lease) { | ||||
| 		wlr_drm_lease_terminate(connector->active_lease->drm_lease); | ||||
| 	} | ||||
| 
 | ||||
| 	struct wl_resource *resource, *tmp; | ||||
| 	wl_resource_for_each_safe(resource, tmp, &connector->resources) { | ||||
| 		wp_drm_lease_connector_v1_send_withdrawn(resource); | ||||
|  | @ -140,14 +136,9 @@ static void lease_handle_destroy(struct wl_listener *listener, void *data) { | |||
| 
 | ||||
| 	wl_list_remove(&lease->destroy.link); | ||||
| 
 | ||||
| 	for (size_t i = 0; i < lease->n_connectors; ++i) { | ||||
| 		lease->connectors[i]->active_lease = NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	wl_list_remove(&lease->link); | ||||
| 	wl_resource_set_user_data(lease->resource, NULL); | ||||
| 
 | ||||
| 	free(lease->connectors); | ||||
| 	free(lease); | ||||
| } | ||||
| 
 | ||||
|  | @ -180,20 +171,6 @@ struct wlr_drm_lease_v1 *wlr_drm_lease_request_v1_grant( | |||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	lease->connectors = calloc(request->n_connectors, sizeof(*lease->connectors)); | ||||
| 	if (!lease->connectors) { | ||||
| 		wlr_log(WLR_ERROR, "Failed to allocate lease connectors list"); | ||||
| 		close(fd); | ||||
| 		wp_drm_lease_v1_send_finished(lease->resource); | ||||
| 		free(lease); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	lease->n_connectors = request->n_connectors; | ||||
| 	for (size_t i = 0; i < request->n_connectors; ++i) { | ||||
| 		lease->connectors[i] = request->connectors[i]; | ||||
| 		lease->connectors[i]->active_lease = lease; | ||||
| 	} | ||||
| 
 | ||||
| 	lease->destroy.notify = lease_handle_destroy; | ||||
| 	wl_signal_add(&lease->drm_lease->events.destroy, &lease->destroy); | ||||
| 
 | ||||
|  | @ -338,16 +315,6 @@ static void drm_lease_request_v1_handle_submit( | |||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	for (size_t i = 0; i < request->n_connectors; ++i) { | ||||
| 		struct wlr_drm_lease_connector_v1 *conn = request->connectors[i]; | ||||
| 		if (conn->active_lease) { | ||||
| 			wlr_log(WLR_ERROR, "Failed to create lease, connector %s has " | ||||
| 					"already been leased", conn->output->name); | ||||
| 			wp_drm_lease_v1_send_finished(lease_resource); | ||||
| 			return; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	request->lease_resource = lease_resource; | ||||
| 
 | ||||
| 	wl_signal_emit_mutable(&request->device->manager->events.request, | ||||
|  | @ -440,10 +407,6 @@ static struct wp_drm_lease_connector_v1_interface lease_connector_impl = { | |||
| static void drm_lease_connector_v1_send_to_client( | ||||
| 		struct wlr_drm_lease_connector_v1 *connector, | ||||
| 		struct wl_resource *resource) { | ||||
| 	if (connector->active_lease) { | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	struct wl_client *client = wl_resource_get_client(resource); | ||||
| 
 | ||||
| 	uint32_t version = wl_resource_get_version(resource); | ||||
|  | @ -490,10 +453,12 @@ static void lease_device_bind(struct wl_client *wl_client, void *data, | |||
| 	if (!device) { | ||||
| 		wlr_log(WLR_DEBUG, "Failed to bind lease device, " | ||||
| 				"the wlr_drm_lease_device_v1 has been destroyed"); | ||||
| 		wl_list_init(wl_resource_get_link(device_resource)); | ||||
| 		return; | ||||
| 	} | ||||
| 
 | ||||
| 	wl_resource_set_user_data(device_resource, device); | ||||
| 	wl_list_insert(&device->resources, wl_resource_get_link(device_resource)); | ||||
| 
 | ||||
| 	int fd = wlr_drm_backend_get_non_master_fd(device->backend); | ||||
| 	if (fd < 0) { | ||||
|  | @ -505,8 +470,6 @@ static void lease_device_bind(struct wl_client *wl_client, void *data, | |||
| 	wp_drm_lease_device_v1_send_drm_fd(device_resource, fd); | ||||
| 	close(fd); | ||||
| 
 | ||||
| 	wl_list_insert(&device->resources, wl_resource_get_link(device_resource)); | ||||
| 
 | ||||
| 	struct wlr_drm_lease_connector_v1 *connector; | ||||
| 	wl_list_for_each(connector, &device->connectors, link) { | ||||
| 		drm_lease_connector_v1_send_to_client(connector, device_resource); | ||||
|  |  | |||
|  | @ -6,6 +6,7 @@ | |||
| #include <wlr/types/wlr_ext_image_copy_capture_v1.h> | ||||
| #include <wlr/types/wlr_seat.h> | ||||
| #include <wlr/render/wlr_renderer.h> | ||||
| #include "ext-image-copy-capture-v1-protocol.h" | ||||
| #include "render/pixel_format.h" | ||||
| 
 | ||||
| #define IMAGE_COPY_CAPTURE_MANAGER_V1_VERSION 1 | ||||
|  |  | |||
							
								
								
									
										65
									
								
								types/wlr_fixes.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								types/wlr_fixes.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,65 @@ | |||
| #include <assert.h> | ||||
| #include <wayland-server-protocol.h> | ||||
| #include <stdlib.h> | ||||
| 
 | ||||
| #include <wlr/types/wlr_fixes.h> | ||||
| 
 | ||||
| #define FIXES_VERSION 1 | ||||
| 
 | ||||
| static void fixes_destroy(struct wl_client *client, struct wl_resource *resource) { | ||||
| 	wl_resource_destroy(resource); | ||||
| } | ||||
| 
 | ||||
| static void fixes_destroy_registry(struct wl_client *client, struct wl_resource *resource, | ||||
| 		struct wl_resource *registry) { | ||||
| 	wl_resource_destroy(registry); | ||||
| } | ||||
| 
 | ||||
| static const struct wl_fixes_interface fixes_impl = { | ||||
| 	.destroy = fixes_destroy, | ||||
| 	.destroy_registry = fixes_destroy_registry, | ||||
| }; | ||||
| 
 | ||||
| static void fixes_bind(struct wl_client *wl_client, void *data, uint32_t version, uint32_t id) { | ||||
| 	struct wlr_fixes *fixes = data; | ||||
| 
 | ||||
| 	struct wl_resource *resource = wl_resource_create(wl_client, &wl_fixes_interface, version, id); | ||||
| 	if (resource == NULL) { | ||||
| 		wl_client_post_no_memory(wl_client); | ||||
| 		return; | ||||
| 	} | ||||
| 	wl_resource_set_implementation(resource, &fixes_impl, fixes, NULL); | ||||
| } | ||||
| 
 | ||||
| static void fixes_handle_display_destroy(struct wl_listener *listener, void *data) { | ||||
| 	struct wlr_fixes *fixes = wl_container_of(listener, fixes, display_destroy); | ||||
| 	wl_signal_emit_mutable(&fixes->events.destroy, NULL); | ||||
| 
 | ||||
| 	assert(wl_list_empty(&fixes->events.destroy.listener_list)); | ||||
| 
 | ||||
| 	wl_list_remove(&fixes->display_destroy.link); | ||||
| 	wl_global_destroy(fixes->global); | ||||
| 	free(fixes); | ||||
| } | ||||
| 
 | ||||
| struct wlr_fixes *wlr_fixes_create(struct wl_display *display, uint32_t version) { | ||||
| 	assert(version <= FIXES_VERSION); | ||||
| 
 | ||||
| 	struct wlr_fixes *fixes = calloc(1, sizeof(*fixes)); | ||||
| 	if (fixes == NULL) { | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	fixes->global = wl_global_create(display, &wl_fixes_interface, version, fixes, fixes_bind); | ||||
| 	if (fixes->global == NULL) { | ||||
| 		free(fixes); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	wl_signal_init(&fixes->events.destroy); | ||||
| 
 | ||||
| 	fixes->display_destroy.notify = fixes_handle_display_destroy; | ||||
| 	wl_display_add_destroy_listener(display, &fixes->display_destroy); | ||||
| 
 | ||||
| 	return fixes; | ||||
| } | ||||
|  | @ -157,6 +157,9 @@ static void gamma_control_manager_get_gamma_control(struct wl_client *client, | |||
| 	} | ||||
| 
 | ||||
| 	size_t gamma_size = wlr_output_get_gamma_size(output); | ||||
| 	if (gamma_size == 0) { | ||||
| 		gamma_size = manager->fallback_gamma_size; | ||||
| 	} | ||||
| 	if (gamma_size == 0) { | ||||
| 		zwlr_gamma_control_v1_send_failed(resource); | ||||
| 		return; | ||||
|  | @ -262,15 +265,24 @@ struct wlr_gamma_control_v1 *wlr_gamma_control_manager_v1_get_control( | |||
| 	return NULL; | ||||
| } | ||||
| 
 | ||||
| struct wlr_color_transform *wlr_gamma_control_v1_get_color_transform( | ||||
| 		struct wlr_gamma_control_v1 *gamma_control) { | ||||
| 	if (gamma_control == NULL || gamma_control->table == NULL) { | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	const uint16_t *r = gamma_control->table; | ||||
| 	const uint16_t *g = gamma_control->table + gamma_control->ramp_size; | ||||
| 	const uint16_t *b = gamma_control->table + 2 * gamma_control->ramp_size; | ||||
| 
 | ||||
| 	return wlr_color_transform_init_lut_3x1d(gamma_control->ramp_size, r, g, b); | ||||
| } | ||||
| 
 | ||||
| bool wlr_gamma_control_v1_apply(struct wlr_gamma_control_v1 *gamma_control, | ||||
| 		struct wlr_output_state *output_state) { | ||||
| 	struct wlr_color_transform *tr = NULL; | ||||
| 	if (gamma_control != NULL && gamma_control->table != NULL) { | ||||
| 		const uint16_t *r = gamma_control->table; | ||||
| 		const uint16_t *g = gamma_control->table + gamma_control->ramp_size; | ||||
| 		const uint16_t *b = gamma_control->table + 2 * gamma_control->ramp_size; | ||||
| 
 | ||||
| 		tr = wlr_color_transform_init_lut_3x1d(gamma_control->ramp_size, r, g, b); | ||||
| 		tr = wlr_gamma_control_v1_get_color_transform(gamma_control); | ||||
| 		if (tr == NULL) { | ||||
| 			return false; | ||||
| 		} | ||||
|  |  | |||
|  | @ -56,7 +56,8 @@ static void input_method_destroy(struct wlr_input_method_v2 *input_method) { | |||
| 			popup_surface, tmp, &input_method->popup_surfaces, link) { | ||||
| 		popup_surface_destroy(popup_surface); | ||||
| 	} | ||||
| 	wl_signal_emit_mutable(&input_method->events.destroy, input_method); | ||||
| 	wlr_input_method_keyboard_grab_v2_destroy(input_method->keyboard_grab); | ||||
| 	wl_signal_emit_mutable(&input_method->events.destroy, NULL); | ||||
| 
 | ||||
| 	assert(wl_list_empty(&input_method->events.commit.listener_list)); | ||||
| 	assert(wl_list_empty(&input_method->events.new_popup_surface.listener_list)); | ||||
|  | @ -65,7 +66,6 @@ static void input_method_destroy(struct wlr_input_method_v2 *input_method) { | |||
| 
 | ||||
| 	wl_list_remove(wl_resource_get_link(input_method->resource)); | ||||
| 	wl_list_remove(&input_method->seat_client_destroy.link); | ||||
| 	wlr_input_method_keyboard_grab_v2_destroy(input_method->keyboard_grab); | ||||
| 	input_state_reset(&input_method->pending); | ||||
| 	input_state_reset(&input_method->current); | ||||
| 	free(input_method); | ||||
|  | @ -102,7 +102,7 @@ static void im_commit(struct wl_client *client, struct wl_resource *resource, | |||
| 	input_method->current = input_method->pending; | ||||
| 	input_method->pending = (struct wlr_input_method_v2_state){0}; | ||||
| 
 | ||||
| 	wl_signal_emit_mutable(&input_method->events.commit, input_method); | ||||
| 	wl_signal_emit_mutable(&input_method->events.commit, NULL); | ||||
| } | ||||
| 
 | ||||
| static void im_commit_string(struct wl_client *client, | ||||
|  | @ -271,8 +271,7 @@ void wlr_input_method_keyboard_grab_v2_destroy( | |||
| 	if (!keyboard_grab) { | ||||
| 		return; | ||||
| 	} | ||||
| 	wl_signal_emit_mutable(&keyboard_grab->events.destroy, keyboard_grab); | ||||
| 
 | ||||
| 	wl_signal_emit_mutable(&keyboard_grab->events.destroy, NULL); | ||||
| 	assert(wl_list_empty(&keyboard_grab->events.destroy.listener_list)); | ||||
| 
 | ||||
| 	keyboard_grab->input_method->keyboard_grab = NULL; | ||||
|  | @ -575,7 +574,7 @@ static void manager_get_input_method(struct wl_client *client, | |||
| 	wl_resource_set_user_data(im_resource, input_method); | ||||
| 	wl_list_insert(&im_manager->input_methods, | ||||
| 		wl_resource_get_link(input_method->resource)); | ||||
| 	wl_signal_emit_mutable(&im_manager->events.input_method, input_method); | ||||
| 	wl_signal_emit_mutable(&im_manager->events.new_input_method, input_method); | ||||
| } | ||||
| 
 | ||||
| static void manager_destroy(struct wl_client *client, | ||||
|  | @ -606,9 +605,9 @@ static void input_method_manager_bind(struct wl_client *wl_client, void *data, | |||
| static void handle_display_destroy(struct wl_listener *listener, void *data) { | ||||
| 	struct wlr_input_method_manager_v2 *manager = | ||||
| 		wl_container_of(listener, manager, display_destroy); | ||||
| 	wl_signal_emit_mutable(&manager->events.destroy, manager); | ||||
| 	wl_signal_emit_mutable(&manager->events.destroy, NULL); | ||||
| 
 | ||||
| 	assert(wl_list_empty(&manager->events.input_method.listener_list)); | ||||
| 	assert(wl_list_empty(&manager->events.new_input_method.listener_list)); | ||||
| 	assert(wl_list_empty(&manager->events.destroy.listener_list)); | ||||
| 
 | ||||
| 	wl_list_remove(&manager->display_destroy.link); | ||||
|  | @ -623,7 +622,7 @@ struct wlr_input_method_manager_v2 *wlr_input_method_manager_v2_create( | |||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
| 	wl_signal_init(&im_manager->events.input_method); | ||||
| 	wl_signal_init(&im_manager->events.new_input_method); | ||||
| 	wl_signal_init(&im_manager->events.destroy); | ||||
| 
 | ||||
| 	wl_list_init(&im_manager->input_methods); | ||||
|  |  | |||
|  | @ -26,7 +26,7 @@ struct wlr_linux_drm_syncobj_surface_v1 { | |||
| }; | ||||
| 
 | ||||
| struct wlr_linux_drm_syncobj_surface_v1_commit { | ||||
| 	struct wlr_linux_drm_syncobj_surface_v1 *surface; | ||||
| 	struct wlr_surface *surface; | ||||
| 	struct wlr_drm_syncobj_timeline_waiter waiter; | ||||
| 	uint32_t cached_seq; | ||||
| 
 | ||||
|  | @ -192,7 +192,7 @@ static struct wlr_linux_drm_syncobj_surface_v1 *surface_from_wlr_surface( | |||
| } | ||||
| 
 | ||||
| static void surface_commit_destroy(struct wlr_linux_drm_syncobj_surface_v1_commit *commit) { | ||||
| 	wlr_surface_unlock_cached(commit->surface->surface, commit->cached_seq); | ||||
| 	wlr_surface_unlock_cached(commit->surface, commit->cached_seq); | ||||
| 	wl_list_remove(&commit->surface_destroy.link); | ||||
| 	wlr_drm_syncobj_timeline_waiter_finish(&commit->waiter); | ||||
| 	free(commit); | ||||
|  | @ -237,7 +237,7 @@ static bool lock_surface_commit(struct wlr_linux_drm_syncobj_surface_v1 *surface | |||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	commit->surface = surface; | ||||
| 	commit->surface = surface->surface; | ||||
| 	commit->cached_seq = wlr_surface_lock_pending(surface->surface); | ||||
| 
 | ||||
| 	commit->surface_destroy.notify = surface_commit_handle_surface_destroy; | ||||
|  |  | |||
|  | @ -9,6 +9,8 @@ | |||
| #include <wlr/util/box.h> | ||||
| #include <wlr/util/log.h> | ||||
| 
 | ||||
| #include "pointer-constraints-unstable-v1-protocol.h" | ||||
| 
 | ||||
| static const struct zwp_locked_pointer_v1_interface locked_pointer_impl; | ||||
| static const struct zwp_confined_pointer_v1_interface confined_pointer_impl; | ||||
| static const struct zwp_pointer_constraints_v1_interface pointer_constraints_impl; | ||||
|  |  | |||
|  | @ -153,7 +153,7 @@ static void manager_handle_create_icon(struct wl_client *client, struct wl_resou | |||
| 
 | ||||
| 	struct wl_resource *icon_resource = wl_resource_create(client, | ||||
| 		&xdg_toplevel_icon_v1_interface, wl_resource_get_version(resource), id); | ||||
| 	if (resource == NULL) { | ||||
| 	if (icon_resource == NULL) { | ||||
| 		wl_client_post_no_memory(client); | ||||
| 		free(icon); | ||||
| 		return; | ||||
|  |  | |||
							
								
								
									
										13
									
								
								util/box.c
									
										
									
									
									
								
							
							
						
						
									
										13
									
								
								util/box.c
									
										
									
									
									
								
							|  | @ -19,16 +19,15 @@ void wlr_box_closest_point(const struct wlr_box *box, double x, double y, | |||
| 	//
 | ||||
| 	// In order to be consistent with e.g. wlr_box_contains_point(),
 | ||||
| 	// this function returns a point inside the bottom and right edges
 | ||||
| 	// of the box by at least 1/65536 of a unit (pixel). 1/65536 is
 | ||||
| 	// of the box by at least 1/256 of a unit (pixel). 1/256 is
 | ||||
| 	// small enough to avoid a "dead zone" with high-resolution mice
 | ||||
| 	// but large enough to avoid rounding to zero (due to loss of
 | ||||
| 	// significant digits) in simple floating-point calculations.
 | ||||
| 	// but large enough to avoid rounding to zero in wl_fixed_from_double().
 | ||||
| 
 | ||||
| 	// find the closest x point
 | ||||
| 	if (x < box->x) { | ||||
| 		*dest_x = box->x; | ||||
| 	} else if (x > box->x + box->width - 1/65536.0) { | ||||
| 		*dest_x = box->x + box->width - 1/65536.0; | ||||
| 	} else if (x > box->x + box->width - 1/256.0) { | ||||
| 		*dest_x = box->x + box->width - 1/256.0; | ||||
| 	} else { | ||||
| 		*dest_x = x; | ||||
| 	} | ||||
|  | @ -36,8 +35,8 @@ void wlr_box_closest_point(const struct wlr_box *box, double x, double y, | |||
| 	// find closest y point
 | ||||
| 	if (y < box->y) { | ||||
| 		*dest_y = box->y; | ||||
| 	} else if (y > box->y + box->height - 1/65536.0) { | ||||
| 		*dest_y = box->y + box->height - 1/65536.0; | ||||
| 	} else if (y > box->y + box->height - 1/256.0) { | ||||
| 		*dest_y = box->y + box->height - 1/256.0; | ||||
| 	} else { | ||||
| 		*dest_y = y; | ||||
| 	} | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ wlr_files += files( | |||
| 	'matrix.c', | ||||
| 	'mem.c', | ||||
| 	'rect_union.c', | ||||
| 	'rectpack.c', | ||||
| 	'region.c', | ||||
| 	'set.c', | ||||
| 	'shm.c', | ||||
|  |  | |||
							
								
								
									
										533
									
								
								util/rectpack.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										533
									
								
								util/rectpack.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,533 @@ | |||
| #include <assert.h> | ||||
| #include <limits.h> | ||||
| #include <stdlib.h> | ||||
| 
 | ||||
| #include <wlr/types/wlr_layer_shell_v1.h> | ||||
| #include <wlr/util/log.h> | ||||
| #include <wlr/util/rectpack.h> | ||||
| 
 | ||||
| struct wlr_rectpack_bandbuf { | ||||
| 	pixman_box32_t *data; | ||||
| 	size_t len; | ||||
| 	size_t cap; | ||||
| }; | ||||
| 
 | ||||
| static void bandbuf_init(struct wlr_rectpack_bandbuf *buf) { | ||||
| 	*buf = (struct wlr_rectpack_bandbuf){0}; | ||||
| } | ||||
| 
 | ||||
| static void bandbuf_finish(struct wlr_rectpack_bandbuf *buf) { | ||||
| 	free(buf->data); | ||||
| } | ||||
| 
 | ||||
| static bool bandbuf_add(struct wlr_rectpack_bandbuf *buf, pixman_box32_t *band) { | ||||
| 	if (buf->len == buf->cap) { | ||||
| 		buf->cap = buf->cap == 0 ? 32 : buf->cap * 2; | ||||
| 		pixman_box32_t *data = realloc(buf->data, sizeof(*data) * buf->cap); | ||||
| 		if (data == NULL) { | ||||
| 			return false; | ||||
| 		} | ||||
| 		buf->data = data; | ||||
| 	} | ||||
| 	buf->data[buf->len++] = *band; | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| static bool lines_overlap(int a1, int b1, int a2, int b2) { | ||||
| 	int max_a = a1 > a2 ? a1 : a2; | ||||
| 	int min_b = b1 < b2 ? b1 : b2; | ||||
| 	return min_b > max_a; | ||||
| } | ||||
| 
 | ||||
| // Returns false if the constraint overlaps with the origin
 | ||||
| static bool line_crop(int *a, int *b, int exclusive_a, int exclusive_b, | ||||
| 		int origin_a, int origin_b) { | ||||
| 	if (exclusive_a >= origin_b) { | ||||
| 		if (*b > exclusive_a) { | ||||
| 			*b = exclusive_a; | ||||
| 		} | ||||
| 	} else if (exclusive_b <= origin_a) { | ||||
| 		if (*a < exclusive_b) { | ||||
| 			*a = exclusive_b; | ||||
| 		} | ||||
| 	} else { | ||||
| 		return false; | ||||
| 	} | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| // Returns false on memory allocation error
 | ||||
| static bool grow_2d(const struct wlr_box *bounds, pixman_region32_t *exclusive, | ||||
| 		pixman_box32_t *target) { | ||||
| 	// The goal is to find the largest empty rectangle within the exclusive region such that it
 | ||||
| 	// would contain the target rectangle. To achieve this, we split the remaining empty space into
 | ||||
| 	// horizontal bands in such a way that they form two trapezoids (top and bottom), and then
 | ||||
| 	// iterate over pairs of bands from each trapezoid to find the largest rectangle.
 | ||||
| 
 | ||||
| 	// Note: Pixman regions are stored as sorted "y-x-banded" arrays of rectangles. For
 | ||||
| 	// implementation details, see pixman-region.c.
 | ||||
| 
 | ||||
| 	int n_exclusive_rects; | ||||
| 	pixman_box32_t *exclusive_rects = pixman_region32_rectangles(exclusive, &n_exclusive_rects); | ||||
| 
 | ||||
| 	// Step 1: find the middle band, split the exclusive region in 3 subregions:
 | ||||
| 	// - above the target;
 | ||||
| 	// - vertically overlapping with the target;
 | ||||
| 	// - below the target.
 | ||||
| 
 | ||||
| 	// The widest band, contains the target
 | ||||
| 	pixman_box32_t mid_band = (pixman_box32_t){ | ||||
| 		.x1 = bounds->x, | ||||
| 		.y1 = bounds->y, | ||||
| 		.x2 = bounds->x + bounds->width, | ||||
| 		.y2 = bounds->y + bounds->height, | ||||
| 	}; | ||||
| 
 | ||||
| 	// Find exclusive rectangles which are above the target, crop the middle band from the top
 | ||||
| 	int above_rect_i = 0; | ||||
| 	for (; above_rect_i < n_exclusive_rects; above_rect_i++) { | ||||
| 		pixman_box32_t *rect = &exclusive_rects[above_rect_i]; | ||||
| 		if (rect->y2 > target->y1) { | ||||
| 			break; | ||||
| 		} | ||||
| 		mid_band.y1 = rect->y2; | ||||
| 	} | ||||
| 
 | ||||
| 	// Find exclusive rectangles which vertically overlap with the target, crop the middle band from
 | ||||
| 	// the other sides
 | ||||
| 	int below_rect_i = above_rect_i--; | ||||
| 	for (; below_rect_i < n_exclusive_rects; below_rect_i++) { | ||||
| 		pixman_box32_t *rect = &exclusive_rects[below_rect_i]; | ||||
| 		if (rect->y1 >= target->y2) { | ||||
| 			mid_band.y2 = rect->y1; | ||||
| 			break; | ||||
| 		} | ||||
| 
 | ||||
| 		// Invariant: no exclusive rectangle overlaps with the minimum box
 | ||||
| 		line_crop(&mid_band.x1, &mid_band.x2, rect->x1, rect->x2, target->x1, target->x2); | ||||
| 	} | ||||
| 
 | ||||
| 	// The rest of the exclusive rectangles are below the target
 | ||||
| 
 | ||||
| 	// Step 2: find the rest of the bands.
 | ||||
| 
 | ||||
| 	bool ok = false; | ||||
| 
 | ||||
| 	struct wlr_rectpack_bandbuf bandbuf; | ||||
| 	bandbuf_init(&bandbuf); | ||||
| 
 | ||||
| 
 | ||||
| 	// Find all "above" bands, moving up from the middle
 | ||||
| 	// Note: this includes the middle band itself
 | ||||
| 	if (!bandbuf_add(&bandbuf, &mid_band)) { | ||||
| 		goto end; | ||||
| 	} | ||||
| 
 | ||||
| 	while (above_rect_i >= 0) { | ||||
| 		pixman_box32_t *rect = &exclusive_rects[above_rect_i]; | ||||
| 		pixman_box32_t *last = &bandbuf.data[bandbuf.len - 1]; | ||||
| 
 | ||||
| 		// Invariant: a band farther from the middle one is horizontally contained by a band closer
 | ||||
| 		// to the middle one
 | ||||
| 		pixman_box32_t band = { | ||||
| 			.x1 = last->x1, | ||||
| 			.y1 = rect->y1, | ||||
| 			.x2 = last->x2, | ||||
| 			.y2 = rect->y2, | ||||
| 		}; | ||||
| 		// Extend the last one up in case of free vertical space
 | ||||
| 		last->y1 = band.y2; | ||||
| 
 | ||||
| 		// Process the x-band of exclusive rectangles
 | ||||
| 		do { | ||||
| 			if (!line_crop(&band.x1, &band.x2, rect->x1, rect->x2, target->x1, target->x2)) { | ||||
| 				// A rectangle is horizontally overlapping with the target; it's not possible to go
 | ||||
| 				// further
 | ||||
| 				goto above_done; | ||||
| 			} else if (above_rect_i-- == 0) { | ||||
| 				// All rectangles processed
 | ||||
| 				break; | ||||
| 			} | ||||
| 			rect = &exclusive_rects[above_rect_i]; | ||||
| 		} while (rect->y1 == band.y1); | ||||
| 
 | ||||
| 		if (band.x1 == last->x1 && band.x2 == last->x2) { | ||||
| 			// Horizontally equal to the last; extend that up instead
 | ||||
| 			last->y1 = band.y1; | ||||
| 		} else { | ||||
| 			if (!bandbuf_add(&bandbuf, &band)) { | ||||
| 				goto end; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	// Extend the last one up in case of free vertical space
 | ||||
| 	bandbuf.data[bandbuf.len - 1].y1 = bounds->y; | ||||
| above_done:; | ||||
| 
 | ||||
| 	size_t split_i = bandbuf.len; | ||||
| 
 | ||||
| 	// Find all "below" bands, moving down from the middle
 | ||||
| 	// Same logic applies
 | ||||
| 
 | ||||
| 	if (!bandbuf_add(&bandbuf, &mid_band)) { | ||||
| 		goto end; | ||||
| 	} | ||||
| 
 | ||||
| 	while (below_rect_i < n_exclusive_rects) { | ||||
| 		pixman_box32_t *rect = &exclusive_rects[below_rect_i]; | ||||
| 		pixman_box32_t *last = &bandbuf.data[bandbuf.len - 1]; | ||||
| 
 | ||||
| 		pixman_box32_t band = { | ||||
| 			.x1 = last->x1, | ||||
| 			.y1 = rect->y1, | ||||
| 			.x2 = last->x2, | ||||
| 			.y2 = rect->y2, | ||||
| 		}; | ||||
| 		last->y2 = band.y1; | ||||
| 
 | ||||
| 		do { | ||||
| 			if (!line_crop(&band.x1, &band.x2, rect->x1, rect->x2, target->x1, target->x2)) { | ||||
| 				goto below_done; | ||||
| 			} else if (++below_rect_i == n_exclusive_rects) { | ||||
| 				break; | ||||
| 			} | ||||
| 			rect = &exclusive_rects[below_rect_i]; | ||||
| 		} while (rect->y1 == band.y1); | ||||
| 
 | ||||
| 		if (band.x1 == last->x1 && band.x2 == last->x2) { | ||||
| 			last->y2 = band.y2; | ||||
| 		} else { | ||||
| 			if (!bandbuf_add(&bandbuf, &band)) { | ||||
| 				goto end; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	bandbuf.data[bandbuf.len - 1].y2 = bounds->y + bounds->height; | ||||
| below_done:; | ||||
| 
 | ||||
| 	// Step 3: find the largest rectangle within the empty bands. Between rectangles with the same
 | ||||
| 	// area, pick the one that uses the smaller bounds space better; i.e. pick a "more vertical"
 | ||||
| 	// rectangle within horizontal bounds and vice versa.
 | ||||
| 
 | ||||
| 	bool bounds_horizontal = bounds->width > bounds->height; | ||||
| 	int best_area = (target->x2 - target->x1) * (target->y2 - target->y1); | ||||
| 
 | ||||
| 	// Note: the (mid_band, mid_band) pair is checked too
 | ||||
| 	for (size_t above_i = 0; above_i < split_i; above_i++) { | ||||
| 		pixman_box32_t *above = &bandbuf.data[above_i]; | ||||
| 		for (size_t below_i = split_i; below_i < bandbuf.len; below_i++) { | ||||
| 			pixman_box32_t *below = &bandbuf.data[below_i]; | ||||
| 
 | ||||
| 			pixman_box32_t curr = { | ||||
| 				.x1 = above->x1 > below->x1 ? above->x1 : below->x1, | ||||
| 				.y1 = above->y1, | ||||
| 				.x2 = above->x2 < below->x2 ? above->x2 : below->x2, | ||||
| 				.y2 = below->y2, | ||||
| 			}; | ||||
| 
 | ||||
| 			int width = curr.x2 - curr.x1, height = curr.y2 - curr.y1; | ||||
| 			int area = width * height; | ||||
| 			if (area > best_area || (area == best_area && bounds_horizontal != (width > height))) { | ||||
| 				*target = curr; | ||||
| 				best_area = area; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	ok = true; | ||||
| 
 | ||||
| end: | ||||
| 	bandbuf_finish(&bandbuf); | ||||
| 	return ok; | ||||
| } | ||||
| 
 | ||||
| bool wlr_rectpack_place(const struct wlr_box *bounds, pixman_region32_t *exclusive, | ||||
| 		const struct wlr_box *box, struct wlr_rectpack_rules *rules, struct wlr_box *out) { | ||||
| 	assert(!wlr_box_empty(box)); | ||||
| 
 | ||||
| 	if (bounds->width < box->width || bounds->height < box->height) { | ||||
| 		// Trivial case: the bounds are not big enough for the minimum box
 | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	int n_exclusive_rects = 0; | ||||
| 	pixman_box32_t *exclusive_rects = NULL; | ||||
| 	if (exclusive != NULL) { | ||||
| 		exclusive_rects = pixman_region32_rectangles(exclusive, &n_exclusive_rects); | ||||
| 	} | ||||
| 
 | ||||
| 	if (n_exclusive_rects == 0) { | ||||
| 		// Trivial case: the exclusive region is empty or ignored, just stretch to bounds as needed
 | ||||
| 		if (rules->grow_width) { | ||||
| 			out->x = bounds->x; | ||||
| 			out->width = bounds->width; | ||||
| 		} else { | ||||
| 			out->x = box->x; | ||||
| 			out->width = box->width; | ||||
| 		} | ||||
| 		if (rules->grow_height) { | ||||
| 			out->y = bounds->y; | ||||
| 			out->height = bounds->height; | ||||
| 		} else { | ||||
| 			out->y = box->y; | ||||
| 			out->height = box->height; | ||||
| 		} | ||||
| 		return true; | ||||
| 	} | ||||
| 
 | ||||
| 	// Step 1: fit the minimum box within the exclusive region.
 | ||||
| 
 | ||||
| 	// Instead of trying to fit a min_width×min_height rectangle, shrink the available region and
 | ||||
| 	// try to fit a 1×1 rectangle.
 | ||||
| 	int dwidth = box->width - 1; | ||||
| 	int dheight = box->height - 1; | ||||
| 
 | ||||
| 	pixman_box32_t shrunk_bounds = { | ||||
| 		.x1 = bounds->x, | ||||
| 		.y1 = bounds->y, | ||||
| 		.x2 = bounds->x + bounds->width - dwidth, | ||||
| 		.y2 = bounds->y + bounds->height - dheight, | ||||
| 	}; | ||||
| 
 | ||||
| 	pixman_region32_t available; | ||||
| 	pixman_region32_init(&available); | ||||
| 
 | ||||
| 	if (dwidth != 0 || dheight != 0) { | ||||
| 		pixman_box32_t *expanded_rects = calloc(n_exclusive_rects, sizeof(*expanded_rects)); | ||||
| 		if (expanded_rects == NULL) { | ||||
| 			wlr_log(WLR_ERROR, "Allocation failed"); | ||||
| 			pixman_region32_fini(&available); | ||||
| 			return false; | ||||
| 		} | ||||
| 
 | ||||
| 		for (int i = 0; i < n_exclusive_rects; i++) { | ||||
| 			pixman_box32_t *rect = &exclusive_rects[i]; | ||||
| 			expanded_rects[i] = (pixman_box32_t){ | ||||
| 				.x1 = rect->x1 - dwidth, | ||||
| 				.y1 = rect->y1 - dheight, | ||||
| 				.x2 = rect->x2, | ||||
| 				.y2 = rect->y2, | ||||
| 			}; | ||||
| 		} | ||||
| 
 | ||||
| 		pixman_region32_t expanded; | ||||
| 		pixman_region32_init_rects(&expanded, expanded_rects, n_exclusive_rects); | ||||
| 		pixman_region32_inverse(&available, &expanded, &shrunk_bounds); | ||||
| 		pixman_region32_fini(&expanded); | ||||
| 
 | ||||
| 		free(expanded_rects); | ||||
| 	} else { | ||||
| 		// Fast path: the minimum box is already 1×1
 | ||||
| 		pixman_region32_inverse(&available, exclusive, &shrunk_bounds); | ||||
| 	} | ||||
| 
 | ||||
| 	int n_available_rects; | ||||
| 	pixman_box32_t *available_rects = pixman_region32_rectangles(&available, &n_available_rects); | ||||
| 	if (n_available_rects == 0) { | ||||
| 		// Not enough free space within the exclusive region for the minimum box
 | ||||
| 		pixman_region32_fini(&available); | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	// Find the position closest to the desired one
 | ||||
| 	int best_x = box->x, best_y = box->y; | ||||
| 	int best_dist_sq = INT_MAX; | ||||
| 	for (int i = 0; i < n_available_rects; i++) { | ||||
| 		pixman_box32_t *rect = &available_rects[i]; | ||||
| 		int clamped_x = box->x < rect->x1 ? rect->x1 : | ||||
| 			box->x >= rect->x2 ? rect->x2 - 1 : box->x; | ||||
| 		int clamped_y = box->y < rect->y1 ? rect->y1 : | ||||
| 			box->y >= rect->y2 ? rect->y2 - 1 : box->y; | ||||
| 
 | ||||
| 		int dx = clamped_x - box->x, dy = clamped_y - box->y; | ||||
| 		int dist_sq = dx * dx + dy * dy; | ||||
| 		if (dist_sq < best_dist_sq) { | ||||
| 			best_dist_sq = dist_sq; | ||||
| 			best_x = clamped_x; | ||||
| 			best_y = clamped_y; | ||||
| 		} | ||||
| 
 | ||||
| 		if (best_dist_sq == 0) { | ||||
| 			break; | ||||
| 		} | ||||
| 	} | ||||
| 	pixman_region32_fini(&available); | ||||
| 
 | ||||
| 	// Step 2: grow the box as requested.
 | ||||
| 
 | ||||
| 	pixman_box32_t result = { | ||||
| 		.x1 = best_x, | ||||
| 		.y1 = best_y, | ||||
| 		.x2 = best_x + box->width, | ||||
| 		.y2 = best_y + box->height, | ||||
| 	}; | ||||
| 
 | ||||
| 	if (rules->grow_width && rules->grow_height) { | ||||
| 		if (!grow_2d(bounds, exclusive, &result)) { | ||||
| 			return false; | ||||
| 		} | ||||
| 	} else if (rules->grow_width) { | ||||
| 		// Stretch and then crop
 | ||||
| 		int o1 = result.x1, o2 = result.x2; | ||||
| 		result.x1 = bounds->x; | ||||
| 		result.x2 = bounds->x + bounds->width; | ||||
| 
 | ||||
| 		for (int i = 0; i < n_exclusive_rects; i++) { | ||||
| 			pixman_box32_t *rect = &exclusive_rects[i]; | ||||
| 			if (lines_overlap(result.y1, result.y2, rect->y1, rect->y2)) { | ||||
| 				// Invariant: no exclusive rectangle overlaps with the minimum box
 | ||||
| 				line_crop(&result.x1, &result.x2, rect->x1, rect->x2, o1, o2); | ||||
| 			} | ||||
| 		} | ||||
| 	} else if (rules->grow_height) { | ||||
| 		// Same as width
 | ||||
| 		int o1 = result.y1, o2 = result.y2; | ||||
| 		result.y1 = bounds->y; | ||||
| 		result.y2 = bounds->y + bounds->height; | ||||
| 
 | ||||
| 		for (int i = 0; i < n_exclusive_rects; i++) { | ||||
| 			pixman_box32_t *rect = &exclusive_rects[i]; | ||||
| 			if (lines_overlap(result.x1, result.x2, rect->x1, rect->x2)) { | ||||
| 				// Invariant: no exclusive rectangle overlaps with the minimum box
 | ||||
| 				line_crop(&result.y1, &result.y2, rect->y1, rect->y2, o1, o2); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	*out = (struct wlr_box){ | ||||
| 		.x = result.x1, | ||||
| 		.y = result.y1, | ||||
| 		.width = result.x2 - result.x1, | ||||
| 		.height = result.y2 - result.y1, | ||||
| 	}; | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| bool wlr_rectpack_place_wlr_layer_surface_v1(const struct wlr_box *bounds, | ||||
| 		pixman_region32_t *exclusive, struct wlr_layer_surface_v1 *surface, struct wlr_box *out) { | ||||
| 	struct wlr_layer_surface_v1_state *state = &surface->current; | ||||
| 	uint32_t anchor = state->anchor; | ||||
| 
 | ||||
| 	int m_top = anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_TOP ? state->margin.top : 0; | ||||
| 	int m_bottom = anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_BOTTOM ? state->margin.bottom : 0; | ||||
| 	int m_left = anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_LEFT ? state->margin.left : 0; | ||||
| 	int m_right = anchor & ZWLR_LAYER_SURFACE_V1_ANCHOR_RIGHT ? state->margin.right : 0; | ||||
| 
 | ||||
| 	int m_horiz = m_left + m_right; | ||||
| 	int m_verti = m_top + m_bottom; | ||||
| 
 | ||||
| 	enum wlr_edges exclusive_edge = wlr_layer_surface_v1_get_exclusive_edge(surface); | ||||
| 	int full_exclusive_zone = state->exclusive_zone; | ||||
| 
 | ||||
| 	switch (exclusive_edge) { | ||||
| 	case WLR_EDGE_LEFT: | ||||
| 		full_exclusive_zone += m_left; | ||||
| 		break; | ||||
| 	case WLR_EDGE_RIGHT: | ||||
| 		full_exclusive_zone += m_right; | ||||
| 		break; | ||||
| 	case WLR_EDGE_TOP: | ||||
| 		full_exclusive_zone += m_top; | ||||
| 		break; | ||||
| 	case WLR_EDGE_BOTTOM: | ||||
| 		full_exclusive_zone += m_bottom; | ||||
| 		break; | ||||
| 	case WLR_EDGE_NONE: | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	int desired_width = (int)state->desired_width, desired_height = (int)state->desired_height; | ||||
| 	bool grow_width = desired_width == 0, grow_height = desired_height == 0; | ||||
| 
 | ||||
| 	int min_width = (grow_width ? 1 : desired_width) + m_horiz; | ||||
| 	int min_height = (grow_height ? 1 : desired_height) + m_verti; | ||||
| 
 | ||||
| 	if (min_width < 1) { | ||||
| 		min_width = 1; | ||||
| 	} | ||||
| 	if (min_height < 1) { | ||||
| 		min_height = 1; | ||||
| 	} | ||||
| 
 | ||||
| 	switch (exclusive_edge) { | ||||
| 	case WLR_EDGE_LEFT: | ||||
| 	case WLR_EDGE_RIGHT: | ||||
| 		if (min_width < full_exclusive_zone) { | ||||
| 			min_width = full_exclusive_zone; | ||||
| 		} | ||||
| 		break; | ||||
| 	case WLR_EDGE_TOP: | ||||
| 	case WLR_EDGE_BOTTOM: | ||||
| 		if (min_height < full_exclusive_zone) { | ||||
| 			min_height = full_exclusive_zone; | ||||
| 		} | ||||
| 		break; | ||||
| 	case WLR_EDGE_NONE: | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	uint32_t edges = anchor; | ||||
| 	if ((edges & (WLR_EDGE_LEFT | WLR_EDGE_RIGHT)) == (WLR_EDGE_LEFT | WLR_EDGE_RIGHT)) { | ||||
| 		edges &= ~(WLR_EDGE_LEFT | WLR_EDGE_RIGHT); | ||||
| 	} | ||||
| 	if ((edges & (WLR_EDGE_TOP | WLR_EDGE_BOTTOM)) == (WLR_EDGE_TOP | WLR_EDGE_BOTTOM)) { | ||||
| 		edges &= ~(WLR_EDGE_TOP | WLR_EDGE_BOTTOM); | ||||
| 	} | ||||
| 
 | ||||
| 	struct wlr_box box = { | ||||
| 		.x = bounds->x, | ||||
| 		.y = bounds->y, | ||||
| 		.width = min_width, | ||||
| 		.height = min_height, | ||||
| 	}; | ||||
| 
 | ||||
| 	if ((anchor & (WLR_EDGE_LEFT | WLR_EDGE_RIGHT)) == WLR_EDGE_RIGHT) { | ||||
| 		box.x += bounds->width - box.width; | ||||
| 	} else if ((anchor & (WLR_EDGE_LEFT | WLR_EDGE_RIGHT)) != WLR_EDGE_LEFT) { | ||||
| 		box.x += bounds->width / 2 - box.width / 2; | ||||
| 	} | ||||
| 	if ((anchor & (WLR_EDGE_TOP | WLR_EDGE_BOTTOM)) == WLR_EDGE_BOTTOM) { | ||||
| 		box.y += bounds->height - box.height; | ||||
| 	} else if ((anchor & (WLR_EDGE_TOP | WLR_EDGE_BOTTOM)) != WLR_EDGE_TOP) { | ||||
| 		box.y += bounds->height / 2 - box.height / 2; | ||||
| 	} | ||||
| 
 | ||||
| 	struct wlr_rectpack_rules rules = { | ||||
| 		.grow_width = grow_width, | ||||
| 		.grow_height = grow_height, | ||||
| 	}; | ||||
| 
 | ||||
| 	if (!wlr_rectpack_place(bounds, state->exclusive_zone >= 0 ? exclusive : NULL, | ||||
| 			&box, &rules, out)) { | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	if (exclusive_edge != WLR_EDGE_NONE) { | ||||
| 		struct wlr_box exclusive_box = *out; | ||||
| 		switch (exclusive_edge) { | ||||
| 		case WLR_EDGE_RIGHT: | ||||
| 			exclusive_box.x += out->width - full_exclusive_zone; | ||||
| 			// Fallthrough
 | ||||
| 		case WLR_EDGE_LEFT: | ||||
| 			exclusive_box.width = full_exclusive_zone; | ||||
| 			break; | ||||
| 		case WLR_EDGE_BOTTOM: | ||||
| 			exclusive_box.y += out->height - full_exclusive_zone; | ||||
| 			// Fallthrough
 | ||||
| 		case WLR_EDGE_TOP: | ||||
| 			exclusive_box.height = full_exclusive_zone; | ||||
| 			break; | ||||
| 		case WLR_EDGE_NONE: | ||||
| 			abort(); // Unreachable
 | ||||
| 		} | ||||
| 
 | ||||
| 		struct wlr_box intersection; | ||||
| 		if (wlr_box_intersection(&intersection, &exclusive_box, bounds)) { | ||||
| 			pixman_region32_union_rect(exclusive, exclusive, intersection.x, | ||||
| 				intersection.y, (unsigned int)intersection.width, | ||||
| 				(unsigned int)intersection.height); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	return true; | ||||
| } | ||||
|  | @ -42,6 +42,9 @@ static void handle_server_start(struct wl_listener *listener, void *data) { | |||
| static void xwayland_mark_ready(struct wlr_xwayland *xwayland) { | ||||
| 	assert(xwayland->server->wm_fd[0] >= 0); | ||||
| 	xwayland->xwm = xwm_create(xwayland, xwayland->server->wm_fd[0]); | ||||
| 	// xwm_create takes ownership of wm_fd[0] under all circumstances
 | ||||
| 	xwayland->server->wm_fd[0] = -1; | ||||
| 
 | ||||
| 	if (!xwayland->xwm) { | ||||
| 		return; | ||||
| 	} | ||||
|  | @ -69,6 +72,11 @@ static void handle_shell_destroy(struct wl_listener *listener, void *data) { | |||
| 	struct wlr_xwayland *xwayland = | ||||
| 		wl_container_of(listener, xwayland, shell_destroy); | ||||
| 	xwayland->shell_v1 = NULL; | ||||
| 	wl_list_remove(&xwayland->shell_destroy.link); | ||||
| 	// Will remove this list in handle_shell_destroy().
 | ||||
| 	// This ensures the link is always initialized and
 | ||||
| 	// avoids the need to keep check conditions in sync.
 | ||||
| 	wl_list_init(&xwayland->shell_destroy.link); | ||||
| } | ||||
| 
 | ||||
| void wlr_xwayland_destroy(struct wlr_xwayland *xwayland) { | ||||
|  |  | |||
|  | @ -2530,6 +2530,7 @@ void xwm_set_cursor(struct wlr_xwm *xwm, const uint8_t *pixels, uint32_t stride, | |||
| struct wlr_xwm *xwm_create(struct wlr_xwayland *xwayland, int wm_fd) { | ||||
| 	struct wlr_xwm *xwm = calloc(1, sizeof(*xwm)); | ||||
| 	if (xwm == NULL) { | ||||
| 		close(wm_fd); | ||||
| 		return NULL; | ||||
| 	} | ||||
| 
 | ||||
|  | @ -2544,11 +2545,13 @@ struct wlr_xwm *xwm_create(struct wlr_xwayland *xwayland, int wm_fd) { | |||
| 
 | ||||
| 	xwm->ping_timeout = 10000; | ||||
| 
 | ||||
| 	// xcb_connect_to_fd takes ownership of the FD regardless of success/failure
 | ||||
| 	xwm->xcb_conn = xcb_connect_to_fd(wm_fd, NULL); | ||||
| 
 | ||||
| 	int rc = xcb_connection_has_error(xwm->xcb_conn); | ||||
| 	if (rc) { | ||||
| 		wlr_log(WLR_ERROR, "xcb connect failed: %d", rc); | ||||
| 		xcb_disconnect(xwm->xcb_conn); | ||||
| 		free(xwm); | ||||
| 		return NULL; | ||||
| 	} | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue