mirror of
				https://github.com/labwc/labwc.git
				synced 2025-10-29 05:40:24 -04:00 
			
		
		
		
	Merge pull request #2030 from Consolatis/wip/cosmic_workspaces
Initial implementation of cosmic-workspace-unstable-v1
This commit is contained in:
		
						commit
						d18e67eea8
					
				
					 22 changed files with 1578 additions and 38 deletions
				
			
		|  | @ -1,6 +1,8 @@ | ||||||
| /* SPDX-License-Identifier: GPL-2.0-only */ | /* SPDX-License-Identifier: GPL-2.0-only */ | ||||||
| #ifndef LABWC_ARRAY_H | #ifndef LABWC_ARRAY_H | ||||||
| #define LABWC_ARRAY_H | #define LABWC_ARRAY_H | ||||||
|  | #include <stdio.h> | ||||||
|  | #include <stdlib.h> | ||||||
| #include <wayland-server-core.h> | #include <wayland-server-core.h> | ||||||
| 
 | 
 | ||||||
| /*
 | /*
 | ||||||
|  | @ -41,4 +43,34 @@ wl_array_len(struct wl_array *array) | ||||||
| 		pos && (const char *)pos >= (const char *)(array)->data;               \ | 		pos && (const char *)pos >= (const char *)(array)->data;               \ | ||||||
| 		(pos)--) | 		(pos)--) | ||||||
| 
 | 
 | ||||||
|  | /**
 | ||||||
|  |  * array_add() - add item to wl_array and exit on allocation error | ||||||
|  |  * @_arr: wl_array to add the item to | ||||||
|  |  * @_val: the item to add to the array | ||||||
|  |  * | ||||||
|  |  * Let us illustrate the function of this macro by an example: | ||||||
|  |  * | ||||||
|  |  *   uint32_t value = 5; | ||||||
|  |  *   array_add(array, value); | ||||||
|  |  * | ||||||
|  |  * ...is the equivalent of the code below which is how you would | ||||||
|  |  * otherwise use the wl_array API: | ||||||
|  |  * | ||||||
|  |  *   uint32_t *elm = wl_array_add(array, sizeof(uint32_t)); | ||||||
|  |  *   if (!elm) { | ||||||
|  |  *       perror("failed to allocate memory"); | ||||||
|  |  *       exit(EXIT_FAILURE); | ||||||
|  |  *   } | ||||||
|  |  *   *elm = value; | ||||||
|  |  */ | ||||||
|  | #define array_add(_arr, _val) do {                           \ | ||||||
|  | 		__typeof__(_val) *_entry = wl_array_add(     \ | ||||||
|  | 			(_arr), sizeof(__typeof__(_val)));   \ | ||||||
|  | 		if (!_entry) {                               \ | ||||||
|  | 			perror("Failed to allocate memory"); \ | ||||||
|  | 			exit(EXIT_FAILURE);                  \ | ||||||
|  | 		}                                            \ | ||||||
|  | 		*_entry = (_val);                            \ | ||||||
|  | 	} while (0) | ||||||
|  | 
 | ||||||
| #endif /* LABWC_ARRAY_H */ | #endif /* LABWC_ARRAY_H */ | ||||||
|  |  | ||||||
|  | @ -299,9 +299,16 @@ struct server { | ||||||
| 	struct wlr_scene_tree *menu_tree; | 	struct wlr_scene_tree *menu_tree; | ||||||
| 
 | 
 | ||||||
| 	/* Workspaces */ | 	/* Workspaces */ | ||||||
| 	struct wl_list workspaces;  /* struct workspace.link */ | 	struct { | ||||||
| 	struct workspace *workspace_current; | 		struct wl_list all;  /* struct workspace.link */ | ||||||
| 	struct workspace *workspace_last; | 		struct workspace *current; | ||||||
|  | 		struct workspace *last; | ||||||
|  | 		struct lab_cosmic_workspace_manager *cosmic_manager; | ||||||
|  | 		struct lab_cosmic_workspace_group *cosmic_group; | ||||||
|  | 		struct { | ||||||
|  | 			struct wl_listener layout_output_added; | ||||||
|  | 		} on; | ||||||
|  | 	} workspaces; | ||||||
| 
 | 
 | ||||||
| 	struct wl_list outputs; | 	struct wl_list outputs; | ||||||
| 	struct wl_listener new_output; | 	struct wl_listener new_output; | ||||||
|  |  | ||||||
							
								
								
									
										61
									
								
								include/protocols/cosmic-workspaces-internal.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								include/protocols/cosmic-workspaces-internal.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,61 @@ | ||||||
|  | /* SPDX-License-Identifier: GPL-2.0-only */ | ||||||
|  | #ifndef LABWC_PROTOCOLS_COSMIC_WORKSPACES_INTERNAL_H | ||||||
|  | #define LABWC_PROTOCOLS_COSMIC_WORKSPACES_INTERNAL_H | ||||||
|  | 
 | ||||||
|  | struct lab_cosmic_workspace; | ||||||
|  | struct lab_cosmic_workspace_group; | ||||||
|  | struct lab_cosmic_workspace_manager; | ||||||
|  | 
 | ||||||
|  | enum pending_change { | ||||||
|  | 	/* group events */ | ||||||
|  | 	CW_PENDING_WS_CREATE     = 1 << 0, | ||||||
|  | 
 | ||||||
|  | 	/* ws events*/ | ||||||
|  | 	CW_PENDING_WS_ACTIVATE   = 1 << 1, | ||||||
|  | 	CW_PENDING_WS_DEACTIVATE = 1 << 2, | ||||||
|  | 	CW_PENDING_WS_REMOVE     = 1 << 3, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct transaction { | ||||||
|  | 	uint32_t change; | ||||||
|  | 	struct wl_list link; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct transaction_workspace { | ||||||
|  | 	struct transaction base; | ||||||
|  | 	struct lab_cosmic_workspace *workspace; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct transaction_group { | ||||||
|  | 	struct transaction base; | ||||||
|  | 	struct lab_cosmic_workspace_group *group; | ||||||
|  | 	char *new_workspace_name; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct session_context { | ||||||
|  | 	int ref_count; | ||||||
|  | 	struct wl_list transactions; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct wl_resource_addon { | ||||||
|  | 	struct session_context *ctx; | ||||||
|  | 	void *data; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct wl_resource_addon *resource_addon_create(struct session_context *ctx); | ||||||
|  | 
 | ||||||
|  | void transaction_add_workspace_ev(struct lab_cosmic_workspace *ws, | ||||||
|  | 	struct wl_resource *resource, enum pending_change change); | ||||||
|  | 
 | ||||||
|  | void transaction_add_workspace_group_ev(struct lab_cosmic_workspace_group *group, | ||||||
|  | 	struct wl_resource *resource, enum pending_change change, | ||||||
|  | 	const char *new_workspace_name); | ||||||
|  | 
 | ||||||
|  | void resource_addon_destroy(struct wl_resource_addon *addon); | ||||||
|  | 
 | ||||||
|  | void group_output_send_initial_state(struct lab_cosmic_workspace_group *group, | ||||||
|  | 	struct wl_resource *group_resource); | ||||||
|  | 
 | ||||||
|  | void manager_schedule_done_event(struct lab_cosmic_workspace_manager *manager); | ||||||
|  | 
 | ||||||
|  | #endif /* LABWC_PROTOCOLS_COSMIC_WORKSPACES_INTERNAL_H */ | ||||||
							
								
								
									
										94
									
								
								include/protocols/cosmic-workspaces.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								include/protocols/cosmic-workspaces.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,94 @@ | ||||||
|  | /* SPDX-License-Identifier: GPL-2.0-only */ | ||||||
|  | #ifndef LABWC_PROTOCOLS_COSMIC_WORKSPACES_H | ||||||
|  | #define LABWC_PROTOCOLS_COSMIC_WORKSPACES_H | ||||||
|  | 
 | ||||||
|  | #include <stdbool.h> | ||||||
|  | #include <wayland-server-core.h> | ||||||
|  | 
 | ||||||
|  | struct wlr_output; | ||||||
|  | 
 | ||||||
|  | struct lab_cosmic_workspace_manager { | ||||||
|  | 	struct wl_global *global; | ||||||
|  | 	struct wl_list groups; | ||||||
|  | 	uint32_t caps; | ||||||
|  | 	struct wl_event_source *idle_source; | ||||||
|  | 	struct wl_event_loop *event_loop; | ||||||
|  | 
 | ||||||
|  | 	struct { | ||||||
|  | 		struct wl_listener display_destroy; | ||||||
|  | 	} on; | ||||||
|  | 
 | ||||||
|  | 	struct wl_list resources; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct lab_cosmic_workspace_group { | ||||||
|  | 	struct lab_cosmic_workspace_manager *manager; | ||||||
|  | 	struct wl_list workspaces; | ||||||
|  | 	struct wl_array capabilities; | ||||||
|  | 	struct { | ||||||
|  | 		struct wl_signal create_workspace; | ||||||
|  | 		struct wl_signal destroy; | ||||||
|  | 	} events; | ||||||
|  | 
 | ||||||
|  | 	struct wl_list link; | ||||||
|  | 	struct wl_list outputs; | ||||||
|  | 	struct wl_list resources; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct lab_cosmic_workspace { | ||||||
|  | 	struct lab_cosmic_workspace_group *group; | ||||||
|  | 	char *name; | ||||||
|  | 	struct wl_array coordinates; | ||||||
|  | 	struct wl_array capabilities; | ||||||
|  | 	uint32_t state;         /* enum lab_cosmic_workspace_state */ | ||||||
|  | 	uint32_t state_pending; /* enum lab_cosmic_workspace_state */ | ||||||
|  | 
 | ||||||
|  | 	struct { | ||||||
|  | 		struct wl_signal activate; | ||||||
|  | 		struct wl_signal deactivate; | ||||||
|  | 		struct wl_signal remove; | ||||||
|  | 		struct wl_signal destroy; | ||||||
|  | 	} events; | ||||||
|  | 
 | ||||||
|  | 	struct wl_list link; | ||||||
|  | 	struct wl_list resources; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum lab_cosmic_workspace_caps { | ||||||
|  | 	CW_CAP_NONE          = 0, | ||||||
|  | 	CW_CAP_GRP_ALL       = 0x000000ff, | ||||||
|  | 	CW_CAP_WS_ALL        = 0x0000ff00, | ||||||
|  | 
 | ||||||
|  | 	/* group caps */ | ||||||
|  | 	CW_CAP_GRP_WS_CREATE = 1 << 0, | ||||||
|  | 
 | ||||||
|  | 	/* workspace caps */ | ||||||
|  | 	CW_CAP_WS_ACTIVATE   = 1 << 8, | ||||||
|  | 	CW_CAP_WS_DEACTIVATE = 1 << 9, | ||||||
|  | 	CW_CAP_WS_REMOVE     = 1 << 10, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | struct lab_cosmic_workspace_manager *lab_cosmic_workspace_manager_create( | ||||||
|  | 	struct wl_display *display, uint32_t caps, uint32_t version); | ||||||
|  | 
 | ||||||
|  | struct lab_cosmic_workspace_group *lab_cosmic_workspace_group_create( | ||||||
|  | 	struct lab_cosmic_workspace_manager *manager); | ||||||
|  | 
 | ||||||
|  | void lab_cosmic_workspace_group_output_enter( | ||||||
|  | 	struct lab_cosmic_workspace_group *group, struct wlr_output *output); | ||||||
|  | 
 | ||||||
|  | void lab_cosmic_workspace_group_output_leave( | ||||||
|  | 
 | ||||||
|  | 	struct lab_cosmic_workspace_group *group, struct wlr_output *output); | ||||||
|  | void lab_cosmic_workspace_group_destroy(struct lab_cosmic_workspace_group *group); | ||||||
|  | 
 | ||||||
|  | struct lab_cosmic_workspace *lab_cosmic_workspace_create(struct lab_cosmic_workspace_group *group); | ||||||
|  | void lab_cosmic_workspace_set_name(struct lab_cosmic_workspace *workspace, const char *name); | ||||||
|  | void lab_cosmic_workspace_set_active(struct lab_cosmic_workspace *workspace, bool enabled); | ||||||
|  | void lab_cosmic_workspace_set_urgent(struct lab_cosmic_workspace *workspace, bool enabled); | ||||||
|  | void lab_cosmic_workspace_set_hidden(struct lab_cosmic_workspace *workspace, bool enabled); | ||||||
|  | void lab_cosmic_workspace_set_coordinates(struct lab_cosmic_workspace *workspace, | ||||||
|  | 	struct wl_array *coordinates); | ||||||
|  | void lab_cosmic_workspace_destroy(struct lab_cosmic_workspace *workspace); | ||||||
|  | 
 | ||||||
|  | #endif /* LABWC_PROTOCOLS_COSMIC_WORKSPACES_H */ | ||||||
|  | @ -4,6 +4,7 @@ | ||||||
| 
 | 
 | ||||||
| #include <stdbool.h> | #include <stdbool.h> | ||||||
| #include <wayland-util.h> | #include <wayland-util.h> | ||||||
|  | #include <wayland-server-core.h> | ||||||
| 
 | 
 | ||||||
| struct seat; | struct seat; | ||||||
| struct server; | struct server; | ||||||
|  | @ -19,6 +20,13 @@ struct workspace { | ||||||
| 
 | 
 | ||||||
| 	char *name; | 	char *name; | ||||||
| 	struct wlr_scene_tree *tree; | 	struct wlr_scene_tree *tree; | ||||||
|  | 
 | ||||||
|  | 	struct lab_cosmic_workspace *cosmic_workspace; | ||||||
|  | 	struct { | ||||||
|  | 		struct wl_listener activate; | ||||||
|  | 		struct wl_listener deactivate; | ||||||
|  | 		struct wl_listener remove; | ||||||
|  | 	} on; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| void workspaces_init(struct server *server); | void workspaces_init(struct server *server); | ||||||
|  |  | ||||||
							
								
								
									
										364
									
								
								protocols/cosmic-workspace-unstable-v1.xml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										364
									
								
								protocols/cosmic-workspace-unstable-v1.xml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,364 @@ | ||||||
|  | <?xml version="1.0" encoding="UTF-8"?> | ||||||
|  | <protocol name="cosmic_workspace_unstable_v1"> | ||||||
|  |   <copyright> | ||||||
|  |     Copyright © 2019 Christopher Billington | ||||||
|  |     Copyright © 2020 Ilia Bozhinov | ||||||
|  |     Copyright © 2022 Victoria Brekenfeld | ||||||
|  | 
 | ||||||
|  |     Permission to use, copy, modify, distribute, and sell this | ||||||
|  |     software and its documentation for any purpose is hereby granted | ||||||
|  |     without fee, provided that the above copyright notice appear in | ||||||
|  |     all copies and that both that copyright notice and this permission | ||||||
|  |     notice appear in supporting documentation, and that the name of | ||||||
|  |     the copyright holders not be used in advertising or publicity | ||||||
|  |     pertaining to distribution of the software without specific, | ||||||
|  |     written prior permission.  The copyright holders make no | ||||||
|  |     representations about the suitability of this software for any | ||||||
|  |     purpose.  It is provided "as is" without express or implied | ||||||
|  |     warranty. | ||||||
|  | 
 | ||||||
|  |     THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS | ||||||
|  |     SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND | ||||||
|  |     FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||||||
|  |     SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | ||||||
|  |     WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN | ||||||
|  |     AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, | ||||||
|  |     ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF | ||||||
|  |     THIS SOFTWARE. | ||||||
|  |   </copyright> | ||||||
|  | 
 | ||||||
|  |   <interface name="zcosmic_workspace_manager_v1" version="1"> | ||||||
|  |     <description summary="list and control workspaces"> | ||||||
|  |       Workspaces, also called virtual desktops, are groups of surfaces. A | ||||||
|  |       compositor with a concept of workspaces may only show some such groups of | ||||||
|  |       surfaces (those of 'active' workspaces) at a time. 'Activating' a | ||||||
|  |       workspace is a request for the compositor to display that workspace's | ||||||
|  |       surfaces as normal, whereas the compositor may hide or otherwise | ||||||
|  |       de-emphasise surfaces that are associated only with 'inactive' workspaces. | ||||||
|  |       Workspaces are grouped by which sets of outputs they correspond to, and | ||||||
|  |       may contain surfaces only from those outputs. In this way, it is possible | ||||||
|  |       for each output to have its own set of workspaces, or for all outputs (or | ||||||
|  |       any other arbitrary grouping) to share workspaces. Compositors may | ||||||
|  |       optionally conceptually arrange each group of workspaces in an | ||||||
|  |       N-dimensional grid. | ||||||
|  | 
 | ||||||
|  |       The purpose of this protocol is to enable the creation of taskbars and | ||||||
|  |       docks by providing them with a list of workspaces and their properties, | ||||||
|  |       and allowing them to activate and deactivate workspaces. | ||||||
|  | 
 | ||||||
|  |       After a client binds the zcosmic_workspace_manager_v1, each workspace will be | ||||||
|  |       sent via the workspace event. | ||||||
|  |     </description> | ||||||
|  | 
 | ||||||
|  |     <event name="workspace_group"> | ||||||
|  |       <description summary="a workspace group has been created"> | ||||||
|  |         This event is emitted whenever a new workspace group has been created. | ||||||
|  | 
 | ||||||
|  |         All initial details of the workspace group (workspaces, outputs) will be | ||||||
|  |         sent immediately after this event via the corresponding events in | ||||||
|  |         zcosmic_workspace_group_handle_v1. | ||||||
|  |       </description> | ||||||
|  |       <arg name="workspace_group" type="new_id" interface="zcosmic_workspace_group_handle_v1"/> | ||||||
|  |     </event> | ||||||
|  | 
 | ||||||
|  |     <request name="commit"> | ||||||
|  |       <description summary="all requests about the workspaces have been sent"> | ||||||
|  |         The client must send this request after it has finished sending other | ||||||
|  |         requests. The compositor must process a series of requests preceding a | ||||||
|  |         commit request atomically. | ||||||
|  | 
 | ||||||
|  |         This allows changes to the workspace properties to be seen as atomic, | ||||||
|  |         even if they happen via multiple events, and even if they involve | ||||||
|  |         multiple zcosmic_workspace_handle_v1 objects, for example, deactivating one | ||||||
|  |         workspace and activating another. | ||||||
|  |       </description> | ||||||
|  |     </request> | ||||||
|  | 
 | ||||||
|  |     <event name="done"> | ||||||
|  |       <description summary="all information about the workspace groups has been sent"> | ||||||
|  |         This event is sent after all changes in all workspace groups have been | ||||||
|  |         sent. | ||||||
|  | 
 | ||||||
|  |         This allows changes to one or more zcosmic_workspace_group_handle_v1 | ||||||
|  |         properties and zcosmic_workspace_handle_v1 properties to be seen as atomic, | ||||||
|  |         even if they happen via multiple events. | ||||||
|  |         In particular, an output moving from one workspace group to | ||||||
|  |         another sends an output_enter event and an output_leave event to the two | ||||||
|  |         zcosmic_workspace_group_handle_v1 objects in question. The compositor sends | ||||||
|  |         the done event only after updating the output information in both | ||||||
|  |         workspace groups. | ||||||
|  |       </description> | ||||||
|  |     </event> | ||||||
|  | 
 | ||||||
|  |     <event name="finished"> | ||||||
|  |       <description summary="the compositor has finished with the workspace_manager"> | ||||||
|  |         This event indicates that the compositor is done sending events to the | ||||||
|  |         zcosmic_workspace_manager_v1. The server will destroy the object | ||||||
|  |         immediately after sending this request, so it will become invalid and | ||||||
|  |         the client should free any resources associated with it. | ||||||
|  |       </description> | ||||||
|  |     </event> | ||||||
|  | 
 | ||||||
|  |     <request name="stop"> | ||||||
|  |       <description summary="stop sending events"> | ||||||
|  |         Indicates the client no longer wishes to receive events for new | ||||||
|  |         workspace groups. However the compositor may emit further workspace | ||||||
|  |         events, until the finished event is emitted. | ||||||
|  | 
 | ||||||
|  |         The client must not send any more requests after this one. | ||||||
|  |       </description> | ||||||
|  |     </request> | ||||||
|  |   </interface> | ||||||
|  | 
 | ||||||
|  |   <interface name="zcosmic_workspace_group_handle_v1" version="1"> | ||||||
|  |     <description summary="a workspace group assigned to a set of outputs"> | ||||||
|  |       A zcosmic_workspace_group_handle_v1 object represents a a workspace group | ||||||
|  |       that is assigned a set of outputs and contains a number of workspaces. | ||||||
|  | 
 | ||||||
|  |       The set of outputs assigned to the workspace group is conveyed to the client via | ||||||
|  |       output_enter and output_leave events, and its workspaces are conveyed with | ||||||
|  |       workspace events. | ||||||
|  | 
 | ||||||
|  |       For example, a compositor which has a set of workspaces for each output may | ||||||
|  |       advertise a workspace group (and its workspaces) per output, whereas a compositor | ||||||
|  |       where a workspace spans all outputs may advertise a single workspace group for all | ||||||
|  |       outputs. | ||||||
|  |     </description> | ||||||
|  | 
 | ||||||
|  |     <enum name="zcosmic_workspace_group_capabilities_v1"> | ||||||
|  |       <entry name="create_workspace" value="1" summary="create_workspace request is available"/> | ||||||
|  |     </enum> | ||||||
|  | 
 | ||||||
|  |     <event name="capabilities"> | ||||||
|  |       <description summary="compositor capabilities"> | ||||||
|  |         This event advertises the capabilities supported by the compositor. If | ||||||
|  |         a capability isn't supported, clients should hide or disable the UI | ||||||
|  |         elements that expose this functionality. For instance, if the | ||||||
|  |         compositor doesn't advertise support for creating workspaces, a button | ||||||
|  |         triggering the create_workspace request should not be displayed. | ||||||
|  | 
 | ||||||
|  |         The compositor will ignore requests it doesn't support. For instance, | ||||||
|  |         a compositor which doesn't advertise support for creating workspaces will ignore | ||||||
|  |         create_workspace requests. | ||||||
|  | 
 | ||||||
|  |         Compositors must send this event once after creation of an | ||||||
|  |         zcosmic_workspace_group_handle_v1 . When the capabilities change, compositors | ||||||
|  |         must send this event again. | ||||||
|  | 
 | ||||||
|  |         The capabilities are sent as an array of 32-bit unsigned integers in | ||||||
|  |         native endianness. | ||||||
|  |       </description> | ||||||
|  |       <arg name="capabilities" type="array" summary="array of 32-bit capabilities"/> | ||||||
|  |     </event> | ||||||
|  | 
 | ||||||
|  |     <event name="output_enter"> | ||||||
|  |       <description summary="output assigned to workspace group"> | ||||||
|  |         This event is emitted whenever an output is assigned to the workspace | ||||||
|  |         group. | ||||||
|  |       </description> | ||||||
|  |       <arg name="output" type="object" interface="wl_output"/> | ||||||
|  |     </event> | ||||||
|  | 
 | ||||||
|  |     <event name="output_leave"> | ||||||
|  |       <description summary="output removed from workspace group"> | ||||||
|  |         This event is emitted whenever an output is removed from the workspace | ||||||
|  |         group. | ||||||
|  |       </description> | ||||||
|  |       <arg name="output" type="object" interface="wl_output"/> | ||||||
|  |     </event> | ||||||
|  | 
 | ||||||
|  |     <event name="workspace"> | ||||||
|  |       <description summary="workspace added to workspace group"> | ||||||
|  |         This event is emitted whenever a new workspace has been created. | ||||||
|  |         A workspace can only be a member of a single workspace group and cannot | ||||||
|  |         be re-assigned. | ||||||
|  | 
 | ||||||
|  |         All initial details of the workspace (name, coordinates, state) will | ||||||
|  |         be sent immediately after this event via the corresponding events in | ||||||
|  |         zcosmic_workspace_handle_v1. | ||||||
|  |       </description> | ||||||
|  |       <arg name="workspace" type="new_id" interface="zcosmic_workspace_handle_v1"/> | ||||||
|  |     </event> | ||||||
|  | 
 | ||||||
|  |     <event name="remove"> | ||||||
|  |       <description summary="this workspace group has been destroyed"> | ||||||
|  |         This event means the zcosmic_workspace_group_handle_v1 has been destroyed. | ||||||
|  |         It is guaranteed there won't be any more events for this | ||||||
|  |         zcosmic_workspace_group_handle_v1. The zext_workspace_group_handle_v1 becomes | ||||||
|  |         inert so any requests will be ignored except the destroy request. | ||||||
|  | 
 | ||||||
|  |         The compositor must remove all workspaces belonging to a workspace group | ||||||
|  |         before removing the workspace group. | ||||||
|  |       </description> | ||||||
|  |     </event> | ||||||
|  | 
 | ||||||
|  |     <request name="create_workspace"> | ||||||
|  |       <description summary="create a new workspace"> | ||||||
|  |         Request that the compositor create a new workspace with the given name. | ||||||
|  | 
 | ||||||
|  |         There is no guarantee that the compositor will create a new workspace, | ||||||
|  |         or that the created workspace will have the provided name. | ||||||
|  |       </description> | ||||||
|  |       <arg name="workspace" type="string"/> | ||||||
|  |     </request> | ||||||
|  | 
 | ||||||
|  |     <request name="destroy" type="destructor"> | ||||||
|  |       <description summary="destroy the zcosmic_workspace_group_handle_v1 object"> | ||||||
|  |         Destroys the zcosmic_workspace_group_handle_v1 object. | ||||||
|  | 
 | ||||||
|  |         This request should be called either when the client does not want to | ||||||
|  |         use the workspace object any more or after the remove event to finalize | ||||||
|  |         the destruction of the object. | ||||||
|  |       </description> | ||||||
|  |     </request> | ||||||
|  |   </interface> | ||||||
|  | 
 | ||||||
|  |   <interface name="zcosmic_workspace_handle_v1" version="1"> | ||||||
|  |     <description summary="a workspace handing a group of surfaces"> | ||||||
|  |       A zcosmic_workspace_handle_v1 object represents a a workspace that handles a | ||||||
|  |       group of surfaces. | ||||||
|  | 
 | ||||||
|  |       Each workspace has a name, conveyed to the client with the name event; a | ||||||
|  |       list of states, conveyed to the client with the state event; and | ||||||
|  |       optionally a set of coordinates, conveyed to the client with the | ||||||
|  |       coordinates event. The client may request that the compositor activate or | ||||||
|  |       deactivate the workspace. | ||||||
|  | 
 | ||||||
|  |       Each workspace can belong to only a single workspace group. | ||||||
|  |       Depepending on the compositor policy, there might be workspaces with | ||||||
|  |       the same name in different workspace groups, but these workspaces are still | ||||||
|  |       separate (e.g. one of them might be active while the other is not). | ||||||
|  |     </description> | ||||||
|  | 
 | ||||||
|  |     <event name="name"> | ||||||
|  |       <description summary="workspace name changed"> | ||||||
|  |         This event is emitted immediately after the zcosmic_workspace_handle_v1 is | ||||||
|  |         created and whenever the name of the workspace changes. | ||||||
|  |       </description> | ||||||
|  |       <arg name="name" type="string"/> | ||||||
|  |     </event> | ||||||
|  | 
 | ||||||
|  |     <event name="coordinates"> | ||||||
|  |       <description summary="workspace coordinates changed"> | ||||||
|  |         This event is used to organize workspaces into an N-dimensional grid | ||||||
|  |         within a workspace group, and if supported, is emitted immediately after | ||||||
|  |         the zcosmic_workspace_handle_v1 is created and whenever the coordinates of | ||||||
|  |         the workspace change. Compositors may not send this event if they do not | ||||||
|  |         conceptually arrange workspaces in this way. If compositors simply | ||||||
|  |         number workspaces, without any geometric interpretation, they may send | ||||||
|  |         1D coordinates, which clients should not interpret as implying any | ||||||
|  |         geometry. Sending an empty array means that the compositor no longer | ||||||
|  |         orders the workspace geometrically. | ||||||
|  | 
 | ||||||
|  |         Coordinates have an arbitrary number of dimensions N with an uint32 | ||||||
|  |         position along each dimension. By convention if N > 1, the first | ||||||
|  |         dimension is X, the second Y, the third Z, and so on. The compositor may | ||||||
|  |         chose to utilize these events for a more novel workspace layout | ||||||
|  |         convention, however. No guarantee is made about the grid being filled or | ||||||
|  |         bounded; there may be a workspace at coordinate 1 and another at | ||||||
|  |         coordinate 1000 and none in between. Within a workspace group, however, | ||||||
|  |         workspaces must have unique coordinates of equal dimensionality. | ||||||
|  |       </description> | ||||||
|  |       <arg name="coordinates" type="array"/> | ||||||
|  |     </event> | ||||||
|  | 
 | ||||||
|  |     <event name="state"> | ||||||
|  |       <description summary="the state of the workspace changed"> | ||||||
|  |         This event is emitted immediately after the zcosmic_workspace_handle_v1 is | ||||||
|  |         created and each time the workspace state changes, either because of a | ||||||
|  |         compositor action or because of a request in this protocol. | ||||||
|  |       </description> | ||||||
|  |       <arg name="state" type="array"/> | ||||||
|  |     </event> | ||||||
|  | 
 | ||||||
|  |     <enum name="state"> | ||||||
|  |       <description summary="types of states on the workspace"> | ||||||
|  |         The different states that a workspace can have. | ||||||
|  |       </description> | ||||||
|  | 
 | ||||||
|  |       <entry name="active" value="0" summary="the workspace is active"/> | ||||||
|  |       <entry name="urgent" value="1" summary="the workspace requests attention"/> | ||||||
|  |       <entry name="hidden" value="2"> | ||||||
|  |         <description summary="the workspace is not visible"> | ||||||
|  |           The workspace is not visible in its workspace group, and clients | ||||||
|  |           attempting to visualize the compositor workspace state should not | ||||||
|  |           display such workspaces. | ||||||
|  |         </description> | ||||||
|  |       </entry> | ||||||
|  |     </enum> | ||||||
|  | 
 | ||||||
|  |     <enum name="zcosmic_workspace_capabilities_v1"> | ||||||
|  |       <entry name="activate" value="1" summary="activate request is available"/> | ||||||
|  |       <entry name="deactivate" value="2" summary="deactivate request is available"/> | ||||||
|  |       <entry name="remove" value="3" summary="remove request is available"/> | ||||||
|  |     </enum> | ||||||
|  | 
 | ||||||
|  |     <event name="capabilities"> | ||||||
|  |       <description summary="compositor capabilities"> | ||||||
|  |         This event advertises the capabilities supported by the compositor. If | ||||||
|  |         a capability isn't supported, clients should hide or disable the UI | ||||||
|  |         elements that expose this functionality. For instance, if the | ||||||
|  |         compositor doesn't advertise support for removing workspaces, a button | ||||||
|  |         triggering the remove request should not be displayed. | ||||||
|  | 
 | ||||||
|  |         The compositor will ignore requests it doesn't support. For instance, | ||||||
|  |         a compositor which doesn't advertise support for remove will ignore | ||||||
|  |         remove requests. | ||||||
|  | 
 | ||||||
|  |         Compositors must send this event once after creation of an | ||||||
|  |         zcosmic_workspace_handle_v1 . When the capabilities change, compositors | ||||||
|  |         must send this event again. | ||||||
|  | 
 | ||||||
|  |         The capabilities are sent as an array of 32-bit unsigned integers in | ||||||
|  |         native endianness. | ||||||
|  |       </description> | ||||||
|  |       <arg name="capabilities" type="array" summary="array of 32-bit capabilities"/> | ||||||
|  |     </event> | ||||||
|  | 
 | ||||||
|  |     <event name="remove"> | ||||||
|  |       <description summary="this workspace has been destroyed"> | ||||||
|  |         This event means the zcosmic_workspace_handle_v1 has been destroyed. It is | ||||||
|  |         guaranteed there won't be any more events for this | ||||||
|  |         zcosmic_workspace_handle_v1. The zext_workspace_handle_v1 becomes inert so | ||||||
|  |         any requests will be ignored except the destroy request. | ||||||
|  |       </description> | ||||||
|  |     </event> | ||||||
|  | 
 | ||||||
|  |     <request name="destroy" type="destructor"> | ||||||
|  |       <description summary="destroy the zcosmic_workspace_handle_v1 object"> | ||||||
|  |         Destroys the zcosmic_workspace_handle_v1 object. | ||||||
|  | 
 | ||||||
|  |         This request should be called either when the client does not want to | ||||||
|  |         use the workspace object any more or after the remove event to finalize | ||||||
|  |         the destruction of the object. | ||||||
|  |       </description> | ||||||
|  |     </request> | ||||||
|  | 
 | ||||||
|  |     <request name="activate"> | ||||||
|  |       <description summary="activate the workspace"> | ||||||
|  |         Request that this workspace be activated. | ||||||
|  | 
 | ||||||
|  |         There is no guarantee the workspace will be actually activated, and | ||||||
|  |         behaviour may be compositor-dependent. For example, activating a | ||||||
|  |         workspace may or may not deactivate all other workspaces in the same | ||||||
|  |         group. | ||||||
|  |       </description> | ||||||
|  |     </request> | ||||||
|  | 
 | ||||||
|  |     <request name="deactivate"> | ||||||
|  |       <description summary="activate the workspace"> | ||||||
|  |         Request that this workspace be deactivated. | ||||||
|  | 
 | ||||||
|  |         There is no guarantee the workspace will be actually deactivated. | ||||||
|  |       </description> | ||||||
|  |     </request> | ||||||
|  | 
 | ||||||
|  |     <request name="remove"> | ||||||
|  |       <description summary="remove the workspace"> | ||||||
|  |         Request that this workspace be removed. | ||||||
|  | 
 | ||||||
|  |         There is no guarantee the workspace will be actually removed. | ||||||
|  |       </description> | ||||||
|  |     </request> | ||||||
|  |   </interface> | ||||||
|  | </protocol> | ||||||
|  | @ -21,6 +21,7 @@ server_protocols = [ | ||||||
| 	wl_protocol_dir / 'staging/drm-lease/drm-lease-v1.xml', | 	wl_protocol_dir / 'staging/drm-lease/drm-lease-v1.xml', | ||||||
| 	wl_protocol_dir / 'staging/xwayland-shell/xwayland-shell-v1.xml', | 	wl_protocol_dir / 'staging/xwayland-shell/xwayland-shell-v1.xml', | ||||||
| 	wl_protocol_dir / 'staging/tearing-control/tearing-control-v1.xml', | 	wl_protocol_dir / 'staging/tearing-control/tearing-control-v1.xml', | ||||||
|  | 	'cosmic-workspace-unstable-v1.xml', | ||||||
| 	'wlr-layer-shell-unstable-v1.xml', | 	'wlr-layer-shell-unstable-v1.xml', | ||||||
| 	'wlr-input-inhibitor-unstable-v1.xml', | 	'wlr-input-inhibitor-unstable-v1.xml', | ||||||
| 	'wlr-output-power-management-unstable-v1.xml', | 	'wlr-output-power-management-unstable-v1.xml', | ||||||
|  |  | ||||||
|  | @ -1110,7 +1110,7 @@ actions_run(struct view *activator, struct server *server, | ||||||
| 				 * a required argument for both SendToDesktop and GoToDesktop. | 				 * a required argument for both SendToDesktop and GoToDesktop. | ||||||
| 				 */ | 				 */ | ||||||
| 				struct workspace *target = workspaces_find( | 				struct workspace *target = workspaces_find( | ||||||
| 					server->workspace_current, to, wrap); | 					server->workspaces.current, to, wrap); | ||||||
| 				if (!target) { | 				if (!target) { | ||||||
| 					break; | 					break; | ||||||
| 				} | 				} | ||||||
|  |  | ||||||
|  | @ -106,7 +106,7 @@ get_special(struct server *server, struct wlr_scene_node *node) | ||||||
| 	} | 	} | ||||||
| 	if (node->parent == server->view_tree) { | 	if (node->parent == server->view_tree) { | ||||||
| 		struct workspace *workspace; | 		struct workspace *workspace; | ||||||
| 		wl_list_for_each(workspace, &server->workspaces, link) { | 		wl_list_for_each(workspace, &server->workspaces.all, link) { | ||||||
| 			if (&workspace->tree->node == node) { | 			if (&workspace->tree->node == node) { | ||||||
| 				return workspace->name; | 				return workspace->name; | ||||||
| 			} | 			} | ||||||
|  |  | ||||||
|  | @ -145,7 +145,7 @@ desktop_topmost_focusable_view(struct server *server) | ||||||
| 	struct view *view; | 	struct view *view; | ||||||
| 	struct wl_list *node_list; | 	struct wl_list *node_list; | ||||||
| 	struct wlr_scene_node *node; | 	struct wlr_scene_node *node; | ||||||
| 	node_list = &server->workspace_current->tree->children; | 	node_list = &server->workspaces.current->tree->children; | ||||||
| 	wl_list_for_each_reverse(node, node_list, link) { | 	wl_list_for_each_reverse(node, node_list, link) { | ||||||
| 		if (!node->data) { | 		if (!node->data) { | ||||||
| 			/* We found some non-view, most likely the region overlay */ | 			/* We found some non-view, most likely the region overlay */ | ||||||
|  | @ -185,7 +185,7 @@ desktop_focus_output(struct output *output) | ||||||
| 	struct wlr_scene_node *node; | 	struct wlr_scene_node *node; | ||||||
| 	struct wlr_output_layout *layout = output->server->output_layout; | 	struct wlr_output_layout *layout = output->server->output_layout; | ||||||
| 	struct wl_list *list_head = | 	struct wl_list *list_head = | ||||||
| 		&output->server->workspace_current->tree->children; | 		&output->server->workspaces.current->tree->children; | ||||||
| 	wl_list_for_each_reverse(node, list_head, link) { | 	wl_list_for_each_reverse(node, list_head, link) { | ||||||
| 		if (!node->data) { | 		if (!node->data) { | ||||||
| 			continue; | 			continue; | ||||||
|  |  | ||||||
|  | @ -57,3 +57,4 @@ subdir('decorations') | ||||||
| subdir('input') | subdir('input') | ||||||
| subdir('menu') | subdir('menu') | ||||||
| subdir('ssd') | subdir('ssd') | ||||||
|  | subdir('protocols') | ||||||
|  |  | ||||||
|  | @ -331,7 +331,7 @@ display_osd(struct output *output, struct wl_array *views) | ||||||
| 	struct server *server = output->server; | 	struct server *server = output->server; | ||||||
| 	struct theme *theme = server->theme; | 	struct theme *theme = server->theme; | ||||||
| 	bool show_workspace = wl_list_length(&rc.workspace_config.workspaces) > 1; | 	bool show_workspace = wl_list_length(&rc.workspace_config.workspaces) > 1; | ||||||
| 	const char *workspace_name = server->workspace_current->name; | 	const char *workspace_name = server->workspaces.current->name; | ||||||
| 
 | 
 | ||||||
| 	float scale = output->wlr_output->scale; | 	float scale = output->wlr_output->scale; | ||||||
| 	int w = theme->osd_window_switcher_width; | 	int w = theme->osd_window_switcher_width; | ||||||
|  |  | ||||||
|  | @ -27,6 +27,7 @@ | ||||||
| #include "node.h" | #include "node.h" | ||||||
| #include "output-state.h" | #include "output-state.h" | ||||||
| #include "output-virtual.h" | #include "output-virtual.h" | ||||||
|  | #include "protocols/cosmic-workspaces.h" | ||||||
| #include "regions.h" | #include "regions.h" | ||||||
| #include "view.h" | #include "view.h" | ||||||
| #include "xwayland.h" | #include "xwayland.h" | ||||||
|  | @ -299,6 +300,9 @@ add_output_to_layout(struct server *server, struct output *output) | ||||||
| 		wlr_scene_output_layout_add_output(server->scene_layout, | 		wlr_scene_output_layout_add_output(server->scene_layout, | ||||||
| 			layout_output, output->scene_output); | 			layout_output, output->scene_output); | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	lab_cosmic_workspace_group_output_enter( | ||||||
|  | 		server->workspaces.cosmic_group, output->wlr_output); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static void | static void | ||||||
|  | @ -597,6 +601,10 @@ output_config_apply(struct server *server, | ||||||
| 
 | 
 | ||||||
| 		if (need_to_remove) { | 		if (need_to_remove) { | ||||||
| 			regions_evacuate_output(output); | 			regions_evacuate_output(output); | ||||||
|  | 
 | ||||||
|  | 			lab_cosmic_workspace_group_output_leave( | ||||||
|  | 				server->workspaces.cosmic_group, output->wlr_output); | ||||||
|  | 
 | ||||||
| 			/*
 | 			/*
 | ||||||
| 			 * At time of writing, wlr_output_layout_remove() | 			 * At time of writing, wlr_output_layout_remove() | ||||||
| 			 * indirectly destroys the wlr_scene_output, but | 			 * indirectly destroys the wlr_scene_output, but | ||||||
|  |  | ||||||
							
								
								
									
										651
									
								
								src/protocols/cosmic_workspaces/cosmic-workspaces.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										651
									
								
								src/protocols/cosmic_workspaces/cosmic-workspaces.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,651 @@ | ||||||
|  | // SPDX-License-Identifier: GPL-2.0-only
 | ||||||
|  | #include <assert.h> | ||||||
|  | #include <wlr/types/wlr_output.h> | ||||||
|  | #include <wlr/util/log.h> | ||||||
|  | #include "common/array.h" | ||||||
|  | #include "common/mem.h" | ||||||
|  | #include "common/list.h" | ||||||
|  | #include "cosmic-workspace-unstable-v1-protocol.h" | ||||||
|  | #include "protocols/cosmic-workspaces.h" | ||||||
|  | #include "protocols/cosmic-workspaces-internal.h" | ||||||
|  | 
 | ||||||
|  | /*
 | ||||||
|  |  *	.--------------------. | ||||||
|  |  *	|        TODO        | | ||||||
|  |  *	|--------------------| | ||||||
|  |  *	| - prevent empty    | | ||||||
|  |  *	|   done events      | | ||||||
|  |  *	| - go through xml   | | ||||||
|  |  *	|   and verify impl  | | ||||||
|  |  *	| - assert pub API   | | ||||||
|  |  *	`--------------------´ | ||||||
|  |  * | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | /* Only used within an assert() */ | ||||||
|  | #ifndef NDEBUG | ||||||
|  | 	#define COSMIC_WORKSPACE_V1_VERSION 1 | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | /* These are just *waaay* too long */ | ||||||
|  | #define ZCOSMIC_CAP_WS_CREATE \ | ||||||
|  | 	ZCOSMIC_WORKSPACE_GROUP_HANDLE_V1_ZCOSMIC_WORKSPACE_GROUP_CAPABILITIES_V1_CREATE_WORKSPACE | ||||||
|  | #define ZCOSMIC_CAP_WS_ACTIVATE \ | ||||||
|  | 	ZCOSMIC_WORKSPACE_HANDLE_V1_ZCOSMIC_WORKSPACE_CAPABILITIES_V1_ACTIVATE | ||||||
|  | #define ZCOSMIC_CAP_WS_DEACTIVATE \ | ||||||
|  | 	ZCOSMIC_WORKSPACE_HANDLE_V1_ZCOSMIC_WORKSPACE_CAPABILITIES_V1_DEACTIVATE | ||||||
|  | #define ZCOSMIC_CAP_WS_REMOVE \ | ||||||
|  | 	ZCOSMIC_WORKSPACE_HANDLE_V1_ZCOSMIC_WORKSPACE_CAPABILITIES_V1_REMOVE | ||||||
|  | 
 | ||||||
|  | enum workspace_state { | ||||||
|  | 	CW_WS_STATE_ACTIVE  = 1 << 0, | ||||||
|  | 	CW_WS_STATE_URGENT  = 1 << 1, | ||||||
|  | 	CW_WS_STATE_HIDDEN  = 1 << 2, | ||||||
|  | 
 | ||||||
|  | 	/*
 | ||||||
|  | 	 * Set when creating a new workspace so we | ||||||
|  | 	 * don't end up having to send the state twice. | ||||||
|  | 	 */ | ||||||
|  | 	CW_WS_STATE_INVALID = 1 << 31, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | add_caps(struct wl_array *caps_arr, uint32_t caps) | ||||||
|  | { | ||||||
|  | 	if (caps == CW_CAP_NONE) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	if (caps & CW_CAP_GRP_WS_CREATE) { | ||||||
|  | 		array_add(caps_arr, ZCOSMIC_CAP_WS_CREATE); | ||||||
|  | 	} | ||||||
|  | 	if (caps & CW_CAP_WS_ACTIVATE) { | ||||||
|  | 		array_add(caps_arr, ZCOSMIC_CAP_WS_ACTIVATE); | ||||||
|  | 	} | ||||||
|  | 	if (caps & CW_CAP_WS_DEACTIVATE) { | ||||||
|  | 		array_add(caps_arr, ZCOSMIC_CAP_WS_DEACTIVATE); | ||||||
|  | 	} | ||||||
|  | 	if (caps & CW_CAP_WS_REMOVE) { | ||||||
|  | 		array_add(caps_arr, ZCOSMIC_CAP_WS_REMOVE); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Workspace */ | ||||||
|  | static void | ||||||
|  | workspace_handle_destroy(struct wl_client *client, struct wl_resource *resource) | ||||||
|  | { | ||||||
|  | 	wl_resource_destroy(resource); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | workspace_handle_activate(struct wl_client *client, struct wl_resource *resource) | ||||||
|  | { | ||||||
|  | 	struct wl_resource_addon *addon = wl_resource_get_user_data(resource); | ||||||
|  | 	if (!addon) { | ||||||
|  | 		/* workspace was destroyed from the compositor side */ | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	struct lab_cosmic_workspace *workspace = addon->data; | ||||||
|  | 	transaction_add_workspace_ev(workspace, resource, CW_PENDING_WS_ACTIVATE); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | workspace_handle_deactivate(struct wl_client *client, struct wl_resource *resource) | ||||||
|  | { | ||||||
|  | 	struct wl_resource_addon *addon = wl_resource_get_user_data(resource); | ||||||
|  | 	if (!addon) { | ||||||
|  | 		/* Workspace was destroyed from the compositor side */ | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	struct lab_cosmic_workspace *workspace = addon->data; | ||||||
|  | 	transaction_add_workspace_ev(workspace, resource, CW_PENDING_WS_DEACTIVATE); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | workspace_handle_remove(struct wl_client *client, struct wl_resource *resource) | ||||||
|  | { | ||||||
|  | 	struct wl_resource_addon *addon = wl_resource_get_user_data(resource); | ||||||
|  | 	if (!addon) { | ||||||
|  | 		/* workspace was destroyed from the compositor side */ | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	struct lab_cosmic_workspace *workspace = addon->data; | ||||||
|  | 	transaction_add_workspace_ev(workspace, resource, CW_PENDING_WS_REMOVE); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const struct zcosmic_workspace_handle_v1_interface workspace_impl = { | ||||||
|  | 	.destroy = workspace_handle_destroy, | ||||||
|  | 	.activate = workspace_handle_activate, | ||||||
|  | 	.deactivate = workspace_handle_deactivate, | ||||||
|  | 	.remove = workspace_handle_remove, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | workspace_instance_resource_destroy(struct wl_resource *resource) | ||||||
|  | { | ||||||
|  | 	struct wl_resource_addon *addon = wl_resource_get_user_data(resource); | ||||||
|  | 	if (addon) { | ||||||
|  | 		resource_addon_destroy(addon); | ||||||
|  | 		wl_resource_set_user_data(resource, NULL); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	wl_list_remove(wl_resource_get_link(resource)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct wl_resource * | ||||||
|  | workspace_resource_create(struct lab_cosmic_workspace *workspace, | ||||||
|  | 		struct wl_resource *group_resource, struct session_context *ctx) | ||||||
|  | { | ||||||
|  | 	struct wl_client *client = wl_resource_get_client(group_resource); | ||||||
|  | 	struct wl_resource *resource = wl_resource_create(client, | ||||||
|  | 			&zcosmic_workspace_handle_v1_interface, | ||||||
|  | 			wl_resource_get_version(group_resource), 0); | ||||||
|  | 	if (!resource) { | ||||||
|  | 		wl_client_post_no_memory(client); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	struct wl_resource_addon *addon = resource_addon_create(ctx); | ||||||
|  | 	addon->data = workspace; | ||||||
|  | 
 | ||||||
|  | 	wl_resource_set_implementation(resource, &workspace_impl, addon, | ||||||
|  | 		workspace_instance_resource_destroy); | ||||||
|  | 
 | ||||||
|  | 	wl_list_insert(&workspace->resources, wl_resource_get_link(resource)); | ||||||
|  | 	return resource; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Workspace internal helpers */ | ||||||
|  | static void | ||||||
|  | workspace_send_state(struct lab_cosmic_workspace *workspace, struct wl_resource *target) | ||||||
|  | { | ||||||
|  | 	struct wl_array state; | ||||||
|  | 	wl_array_init(&state); | ||||||
|  | 
 | ||||||
|  | 	if (workspace->state & CW_WS_STATE_ACTIVE) { | ||||||
|  | 		array_add(&state, ZCOSMIC_WORKSPACE_HANDLE_V1_STATE_ACTIVE); | ||||||
|  | 	} | ||||||
|  | 	if (workspace->state & CW_WS_STATE_URGENT) { | ||||||
|  | 		array_add(&state, ZCOSMIC_WORKSPACE_HANDLE_V1_STATE_URGENT); | ||||||
|  | 	} | ||||||
|  | 	if (workspace->state & CW_WS_STATE_HIDDEN) { | ||||||
|  | 		array_add(&state, ZCOSMIC_WORKSPACE_HANDLE_V1_STATE_HIDDEN); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (target) { | ||||||
|  | 		zcosmic_workspace_handle_v1_send_state(target, &state); | ||||||
|  | 	} else { | ||||||
|  | 		struct wl_resource *resource; | ||||||
|  | 		wl_resource_for_each(resource, &workspace->resources) { | ||||||
|  | 			zcosmic_workspace_handle_v1_send_state(resource, &state); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	wl_array_release(&state); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | workspace_send_initial_state(struct lab_cosmic_workspace *workspace, struct wl_resource *resource) | ||||||
|  | { | ||||||
|  | 	zcosmic_workspace_handle_v1_send_capabilities(resource, &workspace->capabilities); | ||||||
|  | 	if (workspace->coordinates.size > 0) { | ||||||
|  | 		zcosmic_workspace_handle_v1_send_coordinates(resource, &workspace->coordinates); | ||||||
|  | 	} | ||||||
|  | 	if (workspace->name) { | ||||||
|  | 		zcosmic_workspace_handle_v1_send_name(resource, workspace->name); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | workspace_set_state(struct lab_cosmic_workspace *workspace, | ||||||
|  | 		enum workspace_state state, bool enabled) | ||||||
|  | { | ||||||
|  | 	if (!!(workspace->state_pending & state) == enabled) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if (enabled) { | ||||||
|  | 		workspace->state_pending |= state; | ||||||
|  | 	} else { | ||||||
|  | 		workspace->state_pending &= ~state; | ||||||
|  | 	} | ||||||
|  | 	manager_schedule_done_event(workspace->group->manager); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Group */ | ||||||
|  | static void | ||||||
|  | group_handle_create_workspace(struct wl_client *client, | ||||||
|  | 		struct wl_resource *resource, const char *name) | ||||||
|  | { | ||||||
|  | 	struct wl_resource_addon *addon = wl_resource_get_user_data(resource); | ||||||
|  | 	if (!addon) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	struct lab_cosmic_workspace_group *group = addon->data; | ||||||
|  | 	transaction_add_workspace_group_ev(group, resource, CW_PENDING_WS_CREATE, name); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | group_handle_destroy(struct wl_client *client, struct wl_resource *resource) | ||||||
|  | { | ||||||
|  | 	wl_resource_destroy(resource); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const struct zcosmic_workspace_group_handle_v1_interface group_impl = { | ||||||
|  | 	.create_workspace = group_handle_create_workspace, | ||||||
|  | 	.destroy = group_handle_destroy, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | group_instance_resource_destroy(struct wl_resource *resource) | ||||||
|  | { | ||||||
|  | 	struct wl_resource_addon *addon = wl_resource_get_user_data(resource); | ||||||
|  | 	if (addon) { | ||||||
|  | 		resource_addon_destroy(addon); | ||||||
|  | 		wl_resource_set_user_data(resource, NULL); | ||||||
|  | 	} | ||||||
|  | 	wl_list_remove(wl_resource_get_link(resource)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static struct wl_resource * | ||||||
|  | group_resource_create(struct lab_cosmic_workspace_group *group, | ||||||
|  | 		struct wl_resource *manager_resource, struct session_context *ctx) | ||||||
|  | { | ||||||
|  | 	struct wl_client *client = wl_resource_get_client(manager_resource); | ||||||
|  | 	struct wl_resource *resource = wl_resource_create(client, | ||||||
|  | 			&zcosmic_workspace_group_handle_v1_interface, | ||||||
|  | 			wl_resource_get_version(manager_resource), 0); | ||||||
|  | 	if (!resource) { | ||||||
|  | 		wl_client_post_no_memory(client); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	struct wl_resource_addon *addon = resource_addon_create(ctx); | ||||||
|  | 	addon->data = group; | ||||||
|  | 
 | ||||||
|  | 	wl_resource_set_implementation(resource, &group_impl, addon, | ||||||
|  | 		group_instance_resource_destroy); | ||||||
|  | 
 | ||||||
|  | 	wl_list_insert(&group->resources, wl_resource_get_link(resource)); | ||||||
|  | 	return resource; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Group internal helpers */ | ||||||
|  | static void | ||||||
|  | group_send_state(struct lab_cosmic_workspace_group *group, struct wl_resource *resource) | ||||||
|  | { | ||||||
|  | 	zcosmic_workspace_group_handle_v1_send_capabilities( | ||||||
|  | 		resource, &group->capabilities); | ||||||
|  | 
 | ||||||
|  | 	group_output_send_initial_state(group, resource); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Manager itself */ | ||||||
|  | static void | ||||||
|  | manager_handle_commit(struct wl_client *client, struct wl_resource *resource) | ||||||
|  | { | ||||||
|  | 	struct wl_resource_addon *addon = wl_resource_get_user_data(resource); | ||||||
|  | 	if (!addon) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	struct transaction_group *trans_grp; | ||||||
|  | 	struct transaction_workspace *trans_ws; | ||||||
|  | 	struct transaction *trans, *trans_tmp; | ||||||
|  | 	wl_list_for_each_safe(trans, trans_tmp, &addon->ctx->transactions, link) { | ||||||
|  | 		switch (trans->change) { | ||||||
|  | 		case CW_PENDING_WS_CREATE: | ||||||
|  | 			trans_grp = wl_container_of(trans, trans_grp, base); | ||||||
|  | 			wl_signal_emit_mutable( | ||||||
|  | 				&trans_grp->group->events.create_workspace, | ||||||
|  | 				trans_grp->new_workspace_name); | ||||||
|  | 			free(trans_grp->new_workspace_name); | ||||||
|  | 			break; | ||||||
|  | 		case CW_PENDING_WS_ACTIVATE: | ||||||
|  | 			trans_ws = wl_container_of(trans, trans_ws, base); | ||||||
|  | 			wl_signal_emit_mutable(&trans_ws->workspace->events.activate, NULL); | ||||||
|  | 			break; | ||||||
|  | 		case CW_PENDING_WS_DEACTIVATE: | ||||||
|  | 			trans_ws = wl_container_of(trans, trans_ws, base); | ||||||
|  | 			wl_signal_emit_mutable(&trans_ws->workspace->events.deactivate, NULL); | ||||||
|  | 			break; | ||||||
|  | 		case CW_PENDING_WS_REMOVE: | ||||||
|  | 			trans_ws = wl_container_of(trans, trans_ws, base); | ||||||
|  | 			wl_signal_emit_mutable(&trans_ws->workspace->events.remove, NULL); | ||||||
|  | 			break; | ||||||
|  | 		default: | ||||||
|  | 			wlr_log(WLR_ERROR, "Invalid transaction state: %u", trans->change); | ||||||
|  | 		} | ||||||
|  | 		wl_list_remove(&trans->link); | ||||||
|  | 		free(trans); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | manager_handle_stop(struct wl_client *client, struct wl_resource *resource) | ||||||
|  | { | ||||||
|  | 	zcosmic_workspace_manager_v1_send_finished(resource); | ||||||
|  | 	wl_resource_destroy(resource); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static const struct zcosmic_workspace_manager_v1_interface manager_impl = { | ||||||
|  | 	.commit = manager_handle_commit, | ||||||
|  | 	.stop = manager_handle_stop, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | manager_instance_resource_destroy(struct wl_resource *resource) | ||||||
|  | { | ||||||
|  | 	struct wl_resource_addon *addon = wl_resource_get_user_data(resource); | ||||||
|  | 	if (addon) { | ||||||
|  | 		resource_addon_destroy(addon); | ||||||
|  | 		wl_resource_set_user_data(resource, NULL); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	wl_list_remove(wl_resource_get_link(resource)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | manager_handle_bind(struct wl_client *client, void *data, | ||||||
|  | 		uint32_t version, uint32_t id) | ||||||
|  | { | ||||||
|  | 	struct lab_cosmic_workspace_manager *manager = data; | ||||||
|  | 	struct wl_resource *resource = wl_resource_create(client, | ||||||
|  | 			&zcosmic_workspace_manager_v1_interface, | ||||||
|  | 			version, id); | ||||||
|  | 	if (!resource) { | ||||||
|  | 		wl_client_post_no_memory(client); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	struct wl_resource_addon *addon = resource_addon_create(/* session context*/ NULL); | ||||||
|  | 	addon->data = manager; | ||||||
|  | 
 | ||||||
|  | 	wl_resource_set_implementation(resource, &manager_impl, | ||||||
|  | 		addon, manager_instance_resource_destroy); | ||||||
|  | 
 | ||||||
|  | 	wl_list_insert(&manager->resources, wl_resource_get_link(resource)); | ||||||
|  | 
 | ||||||
|  | 	struct lab_cosmic_workspace *workspace; | ||||||
|  | 	struct lab_cosmic_workspace_group *group; | ||||||
|  | 	wl_list_for_each(group, &manager->groups, link) { | ||||||
|  | 		/* Create group resource */ | ||||||
|  | 		struct wl_resource *group_resource = | ||||||
|  | 			group_resource_create(group, resource, addon->ctx); | ||||||
|  | 		zcosmic_workspace_manager_v1_send_workspace_group(resource, group_resource); | ||||||
|  | 		group_send_state(group, group_resource); | ||||||
|  | 
 | ||||||
|  | 		/* Create workspace resource */ | ||||||
|  | 		wl_list_for_each(workspace, &group->workspaces, link) { | ||||||
|  | 			struct wl_resource *workspace_resource = | ||||||
|  | 				workspace_resource_create(workspace, group_resource, addon->ctx); | ||||||
|  | 			zcosmic_workspace_group_handle_v1_send_workspace( | ||||||
|  | 				group_resource, workspace_resource); | ||||||
|  | 			workspace_send_initial_state(workspace, workspace_resource); | ||||||
|  | 			/* Send the current workspace state manually */ | ||||||
|  | 			workspace_send_state(workspace, workspace_resource); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	zcosmic_workspace_manager_v1_send_done(resource); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | manager_handle_display_destroy(struct wl_listener *listener, void *data) | ||||||
|  | { | ||||||
|  | 	struct lab_cosmic_workspace_manager *manager = | ||||||
|  | 		wl_container_of(listener, manager, on.display_destroy); | ||||||
|  | 
 | ||||||
|  | 	wl_list_remove(&manager->on.display_destroy.link); | ||||||
|  | 	manager->event_loop = NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Manager internal helpers */ | ||||||
|  | static void | ||||||
|  | manager_idle_send_done(void *data) | ||||||
|  | { | ||||||
|  | 	struct lab_cosmic_workspace_manager *manager = data; | ||||||
|  | 
 | ||||||
|  | 	struct lab_cosmic_workspace *workspace; | ||||||
|  | 	struct lab_cosmic_workspace_group *group; | ||||||
|  | 	wl_list_for_each(group, &manager->groups, link) { | ||||||
|  | 		wl_list_for_each(workspace, &group->workspaces, link) { | ||||||
|  | 			if (workspace->state != workspace->state_pending) { | ||||||
|  | 				workspace->state = workspace->state_pending; | ||||||
|  | 				workspace_send_state(workspace, /*target*/ NULL); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	struct wl_resource *resource; | ||||||
|  | 	wl_resource_for_each(resource, &manager->resources) { | ||||||
|  | 		zcosmic_workspace_manager_v1_send_done(resource); | ||||||
|  | 	} | ||||||
|  | 	manager->idle_source = NULL; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Internal API */ | ||||||
|  | void | ||||||
|  | manager_schedule_done_event(struct lab_cosmic_workspace_manager *manager) | ||||||
|  | { | ||||||
|  | 	if (manager->idle_source) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	if (!manager->event_loop) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	manager->idle_source = wl_event_loop_add_idle( | ||||||
|  | 		manager->event_loop, manager_idle_send_done, manager); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Public API */ | ||||||
|  | struct lab_cosmic_workspace_manager * | ||||||
|  | lab_cosmic_workspace_manager_create(struct wl_display *display, uint32_t caps, uint32_t version) | ||||||
|  | { | ||||||
|  | 	assert(version <= COSMIC_WORKSPACE_V1_VERSION); | ||||||
|  | 
 | ||||||
|  | 	struct lab_cosmic_workspace_manager *manager = znew(*manager); | ||||||
|  | 	manager->global = wl_global_create(display, | ||||||
|  | 			&zcosmic_workspace_manager_v1_interface, | ||||||
|  | 			version, manager, manager_handle_bind); | ||||||
|  | 
 | ||||||
|  | 	if (!manager->global) { | ||||||
|  | 		free(manager); | ||||||
|  | 		return NULL; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	manager->caps = caps; | ||||||
|  | 	manager->event_loop = wl_display_get_event_loop(display); | ||||||
|  | 
 | ||||||
|  | 	manager->on.display_destroy.notify = manager_handle_display_destroy; | ||||||
|  | 	wl_display_add_destroy_listener(display, &manager->on.display_destroy); | ||||||
|  | 
 | ||||||
|  | 	wl_list_init(&manager->groups); | ||||||
|  | 	wl_list_init(&manager->resources); | ||||||
|  | 	return manager; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct lab_cosmic_workspace_group * | ||||||
|  | lab_cosmic_workspace_group_create(struct lab_cosmic_workspace_manager *manager) | ||||||
|  | { | ||||||
|  | 	assert(manager); | ||||||
|  | 
 | ||||||
|  | 	struct lab_cosmic_workspace_group *group = znew(*group); | ||||||
|  | 	group->manager = manager; | ||||||
|  | 
 | ||||||
|  | 	wl_array_init(&group->capabilities); | ||||||
|  | 	add_caps(&group->capabilities, manager->caps & CW_CAP_GRP_ALL); | ||||||
|  | 
 | ||||||
|  | 	wl_list_init(&group->outputs); | ||||||
|  | 	wl_list_init(&group->resources); | ||||||
|  | 	wl_list_init(&group->workspaces); | ||||||
|  | 	wl_signal_init(&group->events.create_workspace); | ||||||
|  | 	wl_signal_init(&group->events.destroy); | ||||||
|  | 
 | ||||||
|  | 	wl_list_append(&manager->groups, &group->link); | ||||||
|  | 
 | ||||||
|  | 	struct wl_resource *resource, *tmp; | ||||||
|  | 	wl_resource_for_each_safe(resource, tmp, &manager->resources) { | ||||||
|  | 		struct wl_resource_addon *addon = wl_resource_get_user_data(resource); | ||||||
|  | 		assert(addon && addon->ctx); | ||||||
|  | 		struct wl_resource *group_resource = | ||||||
|  | 			group_resource_create(group, resource, addon->ctx); | ||||||
|  | 		zcosmic_workspace_manager_v1_send_workspace_group(resource, group_resource); | ||||||
|  | 		group_send_state(group, group_resource); | ||||||
|  | 	} | ||||||
|  | 	manager_schedule_done_event(manager); | ||||||
|  | 
 | ||||||
|  | 	return group; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void | ||||||
|  | lab_cosmic_workspace_group_destroy(struct lab_cosmic_workspace_group *group) | ||||||
|  | { | ||||||
|  | 	if (!group) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	wl_signal_emit_mutable(&group->events.destroy, NULL); | ||||||
|  | 
 | ||||||
|  | 	struct lab_cosmic_workspace *ws, *ws_tmp; | ||||||
|  | 	wl_list_for_each_safe(ws, ws_tmp, &group->workspaces, link) { | ||||||
|  | 		lab_cosmic_workspace_destroy(ws); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	struct wl_resource *resource, *res_tmp; | ||||||
|  | 	wl_resource_for_each_safe(resource, res_tmp, &group->resources) { | ||||||
|  | 		struct wl_resource_addon *addon = wl_resource_get_user_data(resource); | ||||||
|  | 		if (addon) { | ||||||
|  | 			resource_addon_destroy(addon); | ||||||
|  | 			wl_resource_set_user_data(resource, NULL); | ||||||
|  | 		} | ||||||
|  | 		zcosmic_workspace_group_handle_v1_send_remove(resource); | ||||||
|  | 		wl_list_remove(wl_resource_get_link(resource)); | ||||||
|  | 		wl_list_init(wl_resource_get_link(resource)); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	wl_list_remove(&group->link); | ||||||
|  | 	wl_array_release(&group->capabilities); | ||||||
|  | 	free(group); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct lab_cosmic_workspace * | ||||||
|  | lab_cosmic_workspace_create(struct lab_cosmic_workspace_group *group) | ||||||
|  | { | ||||||
|  | 	assert(group); | ||||||
|  | 
 | ||||||
|  | 	struct lab_cosmic_workspace *workspace = znew(*workspace); | ||||||
|  | 	workspace->group = group; | ||||||
|  | 	/*
 | ||||||
|  | 	 * Ensures we are sending workspace->state_pending on the done event, | ||||||
|  | 	 * regardless if the compositor has changed any state in between here | ||||||
|  | 	 * and the scheduled done event or not. | ||||||
|  | 	 * | ||||||
|  | 	 * Without this we might have to send the state twice, first here and | ||||||
|  | 	 * then again in the scheduled done event when there were any changes. | ||||||
|  | 	 */ | ||||||
|  | 	workspace->state = CW_WS_STATE_INVALID; | ||||||
|  | 
 | ||||||
|  | 	wl_array_init(&workspace->capabilities); | ||||||
|  | 	add_caps(&workspace->capabilities, group->manager->caps & CW_CAP_WS_ALL); | ||||||
|  | 
 | ||||||
|  | 	wl_list_init(&workspace->resources); | ||||||
|  | 	wl_array_init(&workspace->coordinates); | ||||||
|  | 	wl_signal_init(&workspace->events.activate); | ||||||
|  | 	wl_signal_init(&workspace->events.deactivate); | ||||||
|  | 	wl_signal_init(&workspace->events.remove); | ||||||
|  | 	wl_signal_init(&workspace->events.destroy); | ||||||
|  | 
 | ||||||
|  | 	wl_list_append(&group->workspaces, &workspace->link); | ||||||
|  | 
 | ||||||
|  | 	/* Notify clients */ | ||||||
|  | 	struct wl_resource *group_resource; | ||||||
|  | 	wl_resource_for_each(group_resource, &group->resources) { | ||||||
|  | 		struct wl_resource_addon *addon = wl_resource_get_user_data(group_resource); | ||||||
|  | 		assert(addon && addon->ctx); | ||||||
|  | 		struct wl_resource *workspace_resource = | ||||||
|  | 			workspace_resource_create(workspace, group_resource, addon->ctx); | ||||||
|  | 		zcosmic_workspace_group_handle_v1_send_workspace( | ||||||
|  | 			group_resource, workspace_resource); | ||||||
|  | 		workspace_send_initial_state(workspace, workspace_resource); | ||||||
|  | 	} | ||||||
|  | 	manager_schedule_done_event(group->manager); | ||||||
|  | 
 | ||||||
|  | 	return workspace; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void | ||||||
|  | lab_cosmic_workspace_set_name(struct lab_cosmic_workspace *workspace, const char *name) | ||||||
|  | { | ||||||
|  | 	assert(workspace); | ||||||
|  | 	assert(name); | ||||||
|  | 
 | ||||||
|  | 	if (!workspace->name || strcmp(workspace->name, name)) { | ||||||
|  | 		free(workspace->name); | ||||||
|  | 		workspace->name = xstrdup(name); | ||||||
|  | 		struct wl_resource *resource; | ||||||
|  | 		wl_resource_for_each(resource, &workspace->resources) { | ||||||
|  | 			zcosmic_workspace_handle_v1_send_name(resource, workspace->name); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	manager_schedule_done_event(workspace->group->manager); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void | ||||||
|  | lab_cosmic_workspace_set_active(struct lab_cosmic_workspace *workspace, bool enabled) | ||||||
|  | { | ||||||
|  | 	workspace_set_state(workspace, CW_WS_STATE_ACTIVE, enabled); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void | ||||||
|  | lab_cosmic_workspace_set_urgent(struct lab_cosmic_workspace *workspace, bool enabled) | ||||||
|  | { | ||||||
|  | 	workspace_set_state(workspace, CW_WS_STATE_URGENT, enabled); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void | ||||||
|  | lab_cosmic_workspace_set_hidden(struct lab_cosmic_workspace *workspace, bool enabled) | ||||||
|  | { | ||||||
|  | 	workspace_set_state(workspace, CW_WS_STATE_HIDDEN, enabled); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void | ||||||
|  | lab_cosmic_workspace_set_coordinates(struct lab_cosmic_workspace *workspace, | ||||||
|  | 		struct wl_array *coordinates) | ||||||
|  | { | ||||||
|  | 	wl_array_release(&workspace->coordinates); | ||||||
|  | 	wl_array_init(&workspace->coordinates); | ||||||
|  | 	wl_array_copy(&workspace->coordinates, coordinates); | ||||||
|  | 
 | ||||||
|  | 	struct wl_resource *resource; | ||||||
|  | 	wl_resource_for_each(resource, &workspace->resources) { | ||||||
|  | 		zcosmic_workspace_handle_v1_send_coordinates(resource, &workspace->coordinates); | ||||||
|  | 	} | ||||||
|  | 	manager_schedule_done_event(workspace->group->manager); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void | ||||||
|  | lab_cosmic_workspace_destroy(struct lab_cosmic_workspace *workspace) | ||||||
|  | { | ||||||
|  | 	if (!workspace) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 	wl_signal_emit_mutable(&workspace->events.destroy, NULL); | ||||||
|  | 
 | ||||||
|  | 	struct wl_resource *resource, *tmp; | ||||||
|  | 	wl_resource_for_each_safe(resource, tmp, &workspace->resources) { | ||||||
|  | 		struct wl_resource_addon *addon = wl_resource_get_user_data(resource); | ||||||
|  | 		if (addon) { | ||||||
|  | 			resource_addon_destroy(addon); | ||||||
|  | 			wl_resource_set_user_data(resource, NULL); | ||||||
|  | 		} | ||||||
|  | 		zcosmic_workspace_handle_v1_send_remove(resource); | ||||||
|  | 		wl_list_remove(wl_resource_get_link(resource)); | ||||||
|  | 		wl_list_init(wl_resource_get_link(resource)); | ||||||
|  | 	} | ||||||
|  | 	manager_schedule_done_event(workspace->group->manager); | ||||||
|  | 
 | ||||||
|  | 	wl_list_remove(&workspace->link); | ||||||
|  | 	wl_array_release(&workspace->coordinates); | ||||||
|  | 	wl_array_release(&workspace->capabilities); | ||||||
|  | 	zfree(workspace->name); | ||||||
|  | 	free(workspace); | ||||||
|  | } | ||||||
							
								
								
									
										5
									
								
								src/protocols/cosmic_workspaces/meson.build
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/protocols/cosmic_workspaces/meson.build
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | ||||||
|  | labwc_sources += files( | ||||||
|  |   'cosmic-workspaces.c', | ||||||
|  |   'transactions.c', | ||||||
|  |   'output.c', | ||||||
|  | ) | ||||||
							
								
								
									
										174
									
								
								src/protocols/cosmic_workspaces/output.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										174
									
								
								src/protocols/cosmic_workspaces/output.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,174 @@ | ||||||
|  | // SPDX-License-Identifier: GPL-2.0-only
 | ||||||
|  | #include <wayland-server-core.h> | ||||||
|  | #include <wlr/types/wlr_output.h> | ||||||
|  | #include <wlr/util/log.h> | ||||||
|  | #include "common/mem.h" | ||||||
|  | #include "cosmic-workspace-unstable-v1-protocol.h" | ||||||
|  | #include "protocols/cosmic-workspaces.h" | ||||||
|  | #include "protocols/cosmic-workspaces-internal.h" | ||||||
|  | 
 | ||||||
|  | struct group_output { | ||||||
|  | 	struct wlr_output *wlr_output; | ||||||
|  | 	struct lab_cosmic_workspace_group *group; | ||||||
|  | 	struct { | ||||||
|  | 		struct wl_listener group_destroy; | ||||||
|  | 		struct wl_listener output_bind; | ||||||
|  | 		struct wl_listener output_destroy; | ||||||
|  | 	} on; | ||||||
|  | 
 | ||||||
|  | 	struct wl_list link; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /* Internal helpers */ | ||||||
|  | static void | ||||||
|  | group_output_send_event(struct wl_list *group_resources, struct wl_list *output_resources, | ||||||
|  | 		void (*notifier)(struct wl_resource *group, struct wl_resource *output)) | ||||||
|  | { | ||||||
|  | 	struct wl_client *client; | ||||||
|  | 	struct wl_resource *group_resource, *output_resource; | ||||||
|  | 	wl_resource_for_each(group_resource, group_resources) { | ||||||
|  | 		client = wl_resource_get_client(group_resource); | ||||||
|  | 		wl_resource_for_each(output_resource, output_resources) { | ||||||
|  | 			if (wl_resource_get_client(output_resource) == client) { | ||||||
|  | 				notifier(group_resource, output_resource); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | group_output_destroy(struct group_output *group_output) | ||||||
|  | { | ||||||
|  | 	group_output_send_event( | ||||||
|  | 		&group_output->group->resources, | ||||||
|  | 		&group_output->wlr_output->resources, | ||||||
|  | 		zcosmic_workspace_group_handle_v1_send_output_leave); | ||||||
|  | 
 | ||||||
|  | 	manager_schedule_done_event(group_output->group->manager); | ||||||
|  | 
 | ||||||
|  | 	wl_list_remove(&group_output->link); | ||||||
|  | 	wl_list_remove(&group_output->on.group_destroy.link); | ||||||
|  | 	wl_list_remove(&group_output->on.output_bind.link); | ||||||
|  | 	wl_list_remove(&group_output->on.output_destroy.link); | ||||||
|  | 	free(group_output); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Event handlers */ | ||||||
|  | static void | ||||||
|  | handle_output_bind(struct wl_listener *listener, void *data) | ||||||
|  | { | ||||||
|  | 	struct group_output *group_output = | ||||||
|  | 		wl_container_of(listener, group_output, on.output_bind); | ||||||
|  | 
 | ||||||
|  | 	struct wlr_output_event_bind *event = data; | ||||||
|  | 	struct wl_client *client = wl_resource_get_client(event->resource); | ||||||
|  | 
 | ||||||
|  | 	bool sent = false; | ||||||
|  | 	struct wl_resource *group_resource; | ||||||
|  | 	wl_resource_for_each(group_resource, &group_output->group->resources) { | ||||||
|  | 		if (wl_resource_get_client(group_resource) == client) { | ||||||
|  | 			zcosmic_workspace_group_handle_v1_send_output_enter( | ||||||
|  | 				group_resource, event->resource); | ||||||
|  | 			sent = true; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if (!sent) { | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	struct wl_resource *manager_resource; | ||||||
|  | 	struct wl_list *manager_resources = &group_output->group->manager->resources; | ||||||
|  | 	wl_resource_for_each(manager_resource, manager_resources) { | ||||||
|  | 		if (wl_resource_get_client(manager_resource) == client) { | ||||||
|  | 			zcosmic_workspace_manager_v1_send_done(manager_resource); | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | handle_output_destroy(struct wl_listener *listener, void *data) | ||||||
|  | { | ||||||
|  | 	struct group_output *group_output = | ||||||
|  | 		wl_container_of(listener, group_output, on.output_destroy); | ||||||
|  | 	group_output_destroy(group_output); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | handle_group_destroy(struct wl_listener *listener, void *data) | ||||||
|  | { | ||||||
|  | 	struct group_output *group_output = | ||||||
|  | 		wl_container_of(listener, group_output, on.group_destroy); | ||||||
|  | 	group_output_destroy(group_output); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Internal API*/ | ||||||
|  | void | ||||||
|  | group_output_send_initial_state(struct lab_cosmic_workspace_group *group, | ||||||
|  | 		struct wl_resource *group_resource) | ||||||
|  | { | ||||||
|  | 	struct group_output *group_output; | ||||||
|  | 	struct wl_resource *output_resource; | ||||||
|  | 	struct wl_client *client = wl_resource_get_client(group_resource); | ||||||
|  | 	wl_list_for_each(group_output, &group->outputs, link) { | ||||||
|  | 		wl_resource_for_each(output_resource, &group_output->wlr_output->resources) { | ||||||
|  | 			if (wl_resource_get_client(output_resource) == client) { | ||||||
|  | 				zcosmic_workspace_group_handle_v1_send_output_enter( | ||||||
|  | 					group_resource, output_resource); | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /* Public API */ | ||||||
|  | void | ||||||
|  | lab_cosmic_workspace_group_output_enter(struct lab_cosmic_workspace_group *group, | ||||||
|  | 		struct wlr_output *wlr_output) | ||||||
|  | { | ||||||
|  | 	struct group_output *group_output; | ||||||
|  | 	wl_list_for_each(group_output, &group->outputs, link) { | ||||||
|  | 		if (group_output->wlr_output == wlr_output) { | ||||||
|  | 			return; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	group_output = znew(*group_output); | ||||||
|  | 	group_output->wlr_output = wlr_output; | ||||||
|  | 	group_output->group = group; | ||||||
|  | 
 | ||||||
|  | 	group_output->on.group_destroy.notify = handle_group_destroy; | ||||||
|  | 	wl_signal_add(&group->events.destroy, &group_output->on.group_destroy); | ||||||
|  | 
 | ||||||
|  | 	group_output->on.output_bind.notify = handle_output_bind; | ||||||
|  | 	wl_signal_add(&wlr_output->events.bind, &group_output->on.output_bind); | ||||||
|  | 
 | ||||||
|  | 	group_output->on.output_destroy.notify = handle_output_destroy; | ||||||
|  | 	wl_signal_add(&wlr_output->events.destroy, &group_output->on.output_destroy); | ||||||
|  | 
 | ||||||
|  | 	wl_list_insert(&group->outputs, &group_output->link); | ||||||
|  | 
 | ||||||
|  | 	group_output_send_event( | ||||||
|  | 		&group_output->group->resources, | ||||||
|  | 		&group_output->wlr_output->resources, | ||||||
|  | 		zcosmic_workspace_group_handle_v1_send_output_enter); | ||||||
|  | 
 | ||||||
|  | 	manager_schedule_done_event(group->manager); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void | ||||||
|  | lab_cosmic_workspace_group_output_leave(struct lab_cosmic_workspace_group *group, | ||||||
|  | 		struct wlr_output *wlr_output) | ||||||
|  | { | ||||||
|  | 	struct group_output *tmp; | ||||||
|  | 	struct group_output *group_output = NULL; | ||||||
|  | 	wl_list_for_each(tmp, &group->outputs, link) { | ||||||
|  | 		if (tmp->wlr_output == wlr_output) { | ||||||
|  | 			group_output = tmp; | ||||||
|  | 			break; | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	if (!group_output) { | ||||||
|  | 		wlr_log(WLR_ERROR, "output %s was never entered", wlr_output->name); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	group_output_destroy(group_output); | ||||||
|  | } | ||||||
							
								
								
									
										93
									
								
								src/protocols/cosmic_workspaces/transactions.c
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								src/protocols/cosmic_workspaces/transactions.c
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,93 @@ | ||||||
|  | // SPDX-License-Identifier: GPL-2.0-only
 | ||||||
|  | #include <assert.h> | ||||||
|  | #include <wayland-server-core.h> | ||||||
|  | #include <wlr/util/log.h> | ||||||
|  | #include "common/list.h" | ||||||
|  | #include "common/mem.h" | ||||||
|  | #include "protocols/cosmic-workspaces-internal.h" | ||||||
|  | 
 | ||||||
|  | static void | ||||||
|  | transactions_destroy(struct wl_list *list) | ||||||
|  | { | ||||||
|  | 	struct transaction_group *group; | ||||||
|  | 	struct transaction *trans, *trans_tmp; | ||||||
|  | 	wl_list_for_each_safe(trans, trans_tmp, list, link) { | ||||||
|  | 		if (trans->change == CW_PENDING_WS_CREATE) { | ||||||
|  | 			group = wl_container_of(trans, group, base); | ||||||
|  | 			free(group->new_workspace_name); | ||||||
|  | 		} | ||||||
|  | 		wl_list_remove(&trans->link); | ||||||
|  | 		free(trans); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void | ||||||
|  | resource_addon_destroy(struct wl_resource_addon *addon) | ||||||
|  | { | ||||||
|  | 	assert(addon); | ||||||
|  | 	assert(addon->ctx); | ||||||
|  | 
 | ||||||
|  | 	addon->ctx->ref_count--; | ||||||
|  | 	assert(addon->ctx->ref_count >= 0); | ||||||
|  | 
 | ||||||
|  | 	wlr_log(WLR_DEBUG, "New refcount for session %p: %d", | ||||||
|  | 		addon->ctx, addon->ctx->ref_count); | ||||||
|  | 	if (!addon->ctx->ref_count) { | ||||||
|  | 		wlr_log(WLR_DEBUG, "Destroying session context"); | ||||||
|  | 		transactions_destroy(&addon->ctx->transactions); | ||||||
|  | 		free(addon->ctx); | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	free(addon); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | struct wl_resource_addon * | ||||||
|  | resource_addon_create(struct session_context *ctx) | ||||||
|  | { | ||||||
|  | 	struct wl_resource_addon *addon = znew(*addon); | ||||||
|  | 	if (!ctx) { | ||||||
|  | 		ctx = znew(*ctx); | ||||||
|  | 		wl_list_init(&ctx->transactions); | ||||||
|  | 	} | ||||||
|  | 	addon->ctx = ctx; | ||||||
|  | 	addon->ctx->ref_count++; | ||||||
|  | 	return addon; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void | ||||||
|  | transaction_add_workspace_ev(struct lab_cosmic_workspace *ws, | ||||||
|  | 		struct wl_resource *resource, enum pending_change change) | ||||||
|  | { | ||||||
|  | 	struct wl_resource_addon *addon = wl_resource_get_user_data(resource); | ||||||
|  | 	if (!addon) { | ||||||
|  | 		wlr_log(WLR_ERROR, "Failed to find manager addon for workspace transaction"); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	assert(change != CW_PENDING_WS_CREATE); | ||||||
|  | 
 | ||||||
|  | 	struct transaction_workspace *trans_ws = znew(*trans_ws); | ||||||
|  | 	trans_ws->workspace = ws; | ||||||
|  | 	trans_ws->base.change = change; | ||||||
|  | 	wl_list_append(&addon->ctx->transactions, &trans_ws->base.link); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void | ||||||
|  | transaction_add_workspace_group_ev(struct lab_cosmic_workspace_group *group, | ||||||
|  | 		struct wl_resource *resource, enum pending_change change, | ||||||
|  | 		const char *new_workspace_name) | ||||||
|  | { | ||||||
|  | 	struct wl_resource_addon *addon = wl_resource_get_user_data(resource); | ||||||
|  | 	if (!addon) { | ||||||
|  | 		wlr_log(WLR_ERROR, "Failed to find manager addon for group transaction"); | ||||||
|  | 		return; | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	assert(change == CW_PENDING_WS_CREATE); | ||||||
|  | 
 | ||||||
|  | 	struct transaction_group *trans_grp = znew(*trans_grp); | ||||||
|  | 	trans_grp->group = group; | ||||||
|  | 	trans_grp->base.change = change; | ||||||
|  | 	trans_grp->new_workspace_name = xstrdup(new_workspace_name); | ||||||
|  | 	wl_list_append(&addon->ctx->transactions, &trans_grp->base.link); | ||||||
|  | } | ||||||
							
								
								
									
										1
									
								
								src/protocols/meson.build
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								src/protocols/meson.build
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | subdir('cosmic_workspaces') | ||||||
|  | @ -137,7 +137,7 @@ matches_criteria(struct view *view, enum lab_view_criteria criteria) | ||||||
| 		 * special in that they live in a different tree. | 		 * special in that they live in a different tree. | ||||||
| 		 */ | 		 */ | ||||||
| 		struct server *server = view->server; | 		struct server *server = view->server; | ||||||
| 		if (view->scene_tree->node.parent != server->workspace_current->tree | 		if (view->scene_tree->node.parent != server->workspaces.current->tree | ||||||
| 				&& !view_is_always_on_top(view)) { | 				&& !view_is_always_on_top(view)) { | ||||||
| 			return false; | 			return false; | ||||||
| 		} | 		} | ||||||
|  | @ -1451,7 +1451,7 @@ view_toggle_always_on_top(struct view *view) | ||||||
| { | { | ||||||
| 	assert(view); | 	assert(view); | ||||||
| 	if (view_is_always_on_top(view)) { | 	if (view_is_always_on_top(view)) { | ||||||
| 		view->workspace = view->server->workspace_current; | 		view->workspace = view->server->workspaces.current; | ||||||
| 		wlr_scene_node_reparent(&view->scene_tree->node, | 		wlr_scene_node_reparent(&view->scene_tree->node, | ||||||
| 			view->workspace->tree); | 			view->workspace->tree); | ||||||
| 	} else { | 	} else { | ||||||
|  | @ -1473,7 +1473,7 @@ view_toggle_always_on_bottom(struct view *view) | ||||||
| { | { | ||||||
| 	assert(view); | 	assert(view); | ||||||
| 	if (view_is_always_on_bottom(view)) { | 	if (view_is_always_on_bottom(view)) { | ||||||
| 		view->workspace = view->server->workspace_current; | 		view->workspace = view->server->workspaces.current; | ||||||
| 		wlr_scene_node_reparent(&view->scene_tree->node, | 		wlr_scene_node_reparent(&view->scene_tree->node, | ||||||
| 			view->workspace->tree); | 			view->workspace->tree); | ||||||
| 	} else { | 	} else { | ||||||
|  |  | ||||||
|  | @ -15,10 +15,13 @@ | ||||||
| #include "common/mem.h" | #include "common/mem.h" | ||||||
| #include "input/keyboard.h" | #include "input/keyboard.h" | ||||||
| #include "labwc.h" | #include "labwc.h" | ||||||
|  | #include "protocols/cosmic-workspaces.h" | ||||||
| #include "view.h" | #include "view.h" | ||||||
| #include "workspaces.h" | #include "workspaces.h" | ||||||
| #include "xwayland.h" | #include "xwayland.h" | ||||||
| 
 | 
 | ||||||
|  | #define COSMIC_WORKSPACES_VERSION 1 | ||||||
|  | 
 | ||||||
| /* Internal helpers */ | /* Internal helpers */ | ||||||
| static size_t | static size_t | ||||||
| parse_workspace_index(const char *name) | parse_workspace_index(const char *name) | ||||||
|  | @ -66,7 +69,7 @@ _osd_update(struct server *server) | ||||||
| 		theme->osd_workspace_switcher_boxes_height == 0; | 		theme->osd_workspace_switcher_boxes_height == 0; | ||||||
| 
 | 
 | ||||||
| 	/* Dimensions */ | 	/* Dimensions */ | ||||||
| 	size_t workspace_count = wl_list_length(&server->workspaces); | 	size_t workspace_count = wl_list_length(&server->workspaces.all); | ||||||
| 	uint16_t marker_width = workspace_count * (rect_width + padding) - padding; | 	uint16_t marker_width = workspace_count * (rect_width + padding) - padding; | ||||||
| 	uint16_t width = margin * 2 + (marker_width < 200 ? 200 : marker_width); | 	uint16_t width = margin * 2 + (marker_width < 200 ? 200 : marker_width); | ||||||
| 	uint16_t height = margin * (hide_boxes ? 2 : 3) + rect_height + font_height(&rc.font_osd); | 	uint16_t height = margin * (hide_boxes ? 2 : 3) + rect_height + font_height(&rc.font_osd); | ||||||
|  | @ -106,8 +109,8 @@ _osd_update(struct server *server) | ||||||
| 		uint16_t x; | 		uint16_t x; | ||||||
| 		if (!hide_boxes) { | 		if (!hide_boxes) { | ||||||
| 			x = (width - marker_width) / 2; | 			x = (width - marker_width) / 2; | ||||||
| 			wl_list_for_each(workspace, &server->workspaces, link) { | 			wl_list_for_each(workspace, &server->workspaces.all, link) { | ||||||
| 				bool active =  workspace == server->workspace_current; | 				bool active =  workspace == server->workspaces.current; | ||||||
| 				set_cairo_color(cairo, server->theme->osd_label_text_color); | 				set_cairo_color(cairo, server->theme->osd_label_text_color); | ||||||
| 				cairo_rectangle(cairo, x, margin, | 				cairo_rectangle(cairo, x, margin, | ||||||
| 					rect_width - padding, rect_height); | 					rect_width - padding, rect_height); | ||||||
|  | @ -128,7 +131,7 @@ _osd_update(struct server *server) | ||||||
| 		pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); | 		pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); | ||||||
| 
 | 
 | ||||||
| 		/* Center workspace indicator on the x axis */ | 		/* Center workspace indicator on the x axis */ | ||||||
| 		int req_width = font_width(&rc.font_osd, server->workspace_current->name); | 		int req_width = font_width(&rc.font_osd, server->workspaces.current->name); | ||||||
| 		req_width = MIN(req_width, width - 2 * margin); | 		req_width = MIN(req_width, width - 2 * margin); | ||||||
| 		x = (width - req_width) / 2; | 		x = (width - req_width) / 2; | ||||||
| 		if (!hide_boxes) { | 		if (!hide_boxes) { | ||||||
|  | @ -141,7 +144,7 @@ _osd_update(struct server *server) | ||||||
| 		pango_layout_set_font_description(layout, desc); | 		pango_layout_set_font_description(layout, desc); | ||||||
| 		pango_layout_set_width(layout, req_width * PANGO_SCALE); | 		pango_layout_set_width(layout, req_width * PANGO_SCALE); | ||||||
| 		pango_font_description_free(desc); | 		pango_font_description_free(desc); | ||||||
| 		pango_layout_set_text(layout, server->workspace_current->name, -1); | 		pango_layout_set_text(layout, server->workspaces.current->name, -1); | ||||||
| 		pango_cairo_show_layout(cairo, layout); | 		pango_cairo_show_layout(cairo, layout); | ||||||
| 
 | 
 | ||||||
| 		g_object_unref(layout); | 		g_object_unref(layout); | ||||||
|  | @ -172,6 +175,15 @@ _osd_update(struct server *server) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /* cosmic workspace handlers */ | ||||||
|  | static void | ||||||
|  | handle_workspace_activate(struct wl_listener *listener, void *data) | ||||||
|  | { | ||||||
|  | 	struct workspace *workspace = wl_container_of(listener, workspace, on.activate); | ||||||
|  | 	workspaces_switch_to(workspace, /* update_focus */ true); | ||||||
|  | 	wlr_log(WLR_INFO, "activating workspace %s", workspace->name); | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /* Internal API */ | /* Internal API */ | ||||||
| static void | static void | ||||||
| add_workspace(struct server *server, const char *name) | add_workspace(struct server *server, const char *name) | ||||||
|  | @ -180,12 +192,21 @@ add_workspace(struct server *server, const char *name) | ||||||
| 	workspace->server = server; | 	workspace->server = server; | ||||||
| 	workspace->name = xstrdup(name); | 	workspace->name = xstrdup(name); | ||||||
| 	workspace->tree = wlr_scene_tree_create(server->view_tree); | 	workspace->tree = wlr_scene_tree_create(server->view_tree); | ||||||
| 	wl_list_append(&server->workspaces, &workspace->link); | 	wl_list_append(&server->workspaces.all, &workspace->link); | ||||||
| 	if (!server->workspace_current) { | 	if (!server->workspaces.current) { | ||||||
| 		server->workspace_current = workspace; | 		server->workspaces.current = workspace; | ||||||
| 	} else { | 	} else { | ||||||
| 		wlr_scene_node_set_enabled(&workspace->tree->node, false); | 		wlr_scene_node_set_enabled(&workspace->tree->node, false); | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	bool active = server->workspaces.current == workspace; | ||||||
|  | 	workspace->cosmic_workspace = lab_cosmic_workspace_create(server->workspaces.cosmic_group); | ||||||
|  | 	lab_cosmic_workspace_set_name(workspace->cosmic_workspace, name); | ||||||
|  | 	lab_cosmic_workspace_set_active(workspace->cosmic_workspace, active); | ||||||
|  | 	lab_cosmic_workspace_set_hidden(workspace->cosmic_workspace, !active); | ||||||
|  | 
 | ||||||
|  | 	workspace->on.activate.notify = handle_workspace_activate; | ||||||
|  | 	wl_signal_add(&workspace->cosmic_workspace->events.activate, &workspace->on.activate); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| static struct workspace * | static struct workspace * | ||||||
|  | @ -260,7 +281,14 @@ _osd_show(struct server *server) | ||||||
| void | void | ||||||
| workspaces_init(struct server *server) | workspaces_init(struct server *server) | ||||||
| { | { | ||||||
| 	wl_list_init(&server->workspaces); | 	server->workspaces.cosmic_manager = lab_cosmic_workspace_manager_create( | ||||||
|  | 		server->wl_display, /* capabilities */ CW_CAP_WS_ACTIVATE, | ||||||
|  | 		COSMIC_WORKSPACES_VERSION); | ||||||
|  | 
 | ||||||
|  | 	server->workspaces.cosmic_group = lab_cosmic_workspace_group_create( | ||||||
|  | 		server->workspaces.cosmic_manager); | ||||||
|  | 
 | ||||||
|  | 	wl_list_init(&server->workspaces.all); | ||||||
| 
 | 
 | ||||||
| 	struct workspace *conf; | 	struct workspace *conf; | ||||||
| 	wl_list_for_each(conf, &rc.workspace_config.workspaces, link) { | 	wl_list_for_each(conf, &rc.workspace_config.workspaces, link) { | ||||||
|  | @ -278,13 +306,18 @@ workspaces_switch_to(struct workspace *target, bool update_focus) | ||||||
| { | { | ||||||
| 	assert(target); | 	assert(target); | ||||||
| 	struct server *server = target->server; | 	struct server *server = target->server; | ||||||
| 	if (target == server->workspace_current) { | 	if (target == server->workspaces.current) { | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/* Disable the old workspace */ | 	/* Disable the old workspace */ | ||||||
| 	wlr_scene_node_set_enabled( | 	wlr_scene_node_set_enabled( | ||||||
| 		&server->workspace_current->tree->node, false); | 		&server->workspaces.current->tree->node, false); | ||||||
|  | 
 | ||||||
|  | 	lab_cosmic_workspace_set_active( | ||||||
|  | 		server->workspaces.current->cosmic_workspace, false); | ||||||
|  | 	lab_cosmic_workspace_set_hidden( | ||||||
|  | 		server->workspaces.current->cosmic_workspace, true); | ||||||
| 
 | 
 | ||||||
| 	/* Move Omnipresent views to new workspace */ | 	/* Move Omnipresent views to new workspace */ | ||||||
| 	struct view *view; | 	struct view *view; | ||||||
|  | @ -300,10 +333,10 @@ workspaces_switch_to(struct workspace *target, bool update_focus) | ||||||
| 	wlr_scene_node_set_enabled(&target->tree->node, true); | 	wlr_scene_node_set_enabled(&target->tree->node, true); | ||||||
| 
 | 
 | ||||||
| 	/* Save the last visited workspace */ | 	/* Save the last visited workspace */ | ||||||
| 	server->workspace_last = server->workspace_current; | 	server->workspaces.last = server->workspaces.current; | ||||||
| 
 | 
 | ||||||
| 	/* Make sure new views will spawn on the new workspace */ | 	/* Make sure new views will spawn on the new workspace */ | ||||||
| 	server->workspace_current = target; | 	server->workspaces.current = target; | ||||||
| 
 | 
 | ||||||
| 	/*
 | 	/*
 | ||||||
| 	 * Make sure we are focusing what the user sees. | 	 * Make sure we are focusing what the user sees. | ||||||
|  | @ -331,6 +364,9 @@ workspaces_switch_to(struct workspace *target, bool update_focus) | ||||||
| 
 | 
 | ||||||
| 	/* Ensure that only currently visible fullscreen windows hide the top layer */ | 	/* Ensure that only currently visible fullscreen windows hide the top layer */ | ||||||
| 	desktop_update_top_layer_visiblity(server); | 	desktop_update_top_layer_visiblity(server); | ||||||
|  | 
 | ||||||
|  | 	lab_cosmic_workspace_set_active(target->cosmic_workspace, true); | ||||||
|  | 	lab_cosmic_workspace_set_hidden(target->cosmic_workspace, false); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void | void | ||||||
|  | @ -362,7 +398,7 @@ workspaces_find(struct workspace *anchor, const char *name, bool wrap) | ||||||
| 	size_t index = 0; | 	size_t index = 0; | ||||||
| 	struct workspace *target; | 	struct workspace *target; | ||||||
| 	size_t wants_index = parse_workspace_index(name); | 	size_t wants_index = parse_workspace_index(name); | ||||||
| 	struct wl_list *workspaces = &anchor->server->workspaces; | 	struct wl_list *workspaces = &anchor->server->workspaces.all; | ||||||
| 
 | 
 | ||||||
| 	if (wants_index) { | 	if (wants_index) { | ||||||
| 		wl_list_for_each(target, workspaces, link) { | 		wl_list_for_each(target, workspaces, link) { | ||||||
|  | @ -373,7 +409,7 @@ workspaces_find(struct workspace *anchor, const char *name, bool wrap) | ||||||
| 	} else if (!strcasecmp(name, "current")) { | 	} else if (!strcasecmp(name, "current")) { | ||||||
| 		return anchor; | 		return anchor; | ||||||
| 	} else if (!strcasecmp(name, "last")) { | 	} else if (!strcasecmp(name, "last")) { | ||||||
| 		return anchor->server->workspace_last; | 		return anchor->server->workspaces.last; | ||||||
| 	} else if (!strcasecmp(name, "left")) { | 	} else if (!strcasecmp(name, "left")) { | ||||||
| 		return get_prev(anchor, workspaces, wrap); | 		return get_prev(anchor, workspaces, wrap); | ||||||
| 	} else if (!strcasecmp(name, "right")) { | 	} else if (!strcasecmp(name, "right")) { | ||||||
|  | @ -395,6 +431,8 @@ destroy_workspace(struct workspace *workspace) | ||||||
| 	wlr_scene_node_destroy(&workspace->tree->node); | 	wlr_scene_node_destroy(&workspace->tree->node); | ||||||
| 	zfree(workspace->name); | 	zfree(workspace->name); | ||||||
| 	wl_list_remove(&workspace->link); | 	wl_list_remove(&workspace->link); | ||||||
|  | 
 | ||||||
|  | 	lab_cosmic_workspace_destroy(workspace->cosmic_workspace); | ||||||
| 	free(workspace); | 	free(workspace); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -408,7 +446,7 @@ workspaces_reconfigure(struct server *server) | ||||||
| 	 *   - Destroy workspaces if fewer workspace are desired | 	 *   - Destroy workspaces if fewer workspace are desired | ||||||
| 	 */ | 	 */ | ||||||
| 
 | 
 | ||||||
| 	struct wl_list *actual_workspace_link = server->workspaces.next; | 	struct wl_list *actual_workspace_link = server->workspaces.all.next; | ||||||
| 
 | 
 | ||||||
| 	struct workspace *configured_workspace; | 	struct workspace *configured_workspace; | ||||||
| 	wl_list_for_each(configured_workspace, | 	wl_list_for_each(configured_workspace, | ||||||
|  | @ -416,7 +454,7 @@ workspaces_reconfigure(struct server *server) | ||||||
| 		struct workspace *actual_workspace = wl_container_of( | 		struct workspace *actual_workspace = wl_container_of( | ||||||
| 			actual_workspace_link, actual_workspace, link); | 			actual_workspace_link, actual_workspace, link); | ||||||
| 
 | 
 | ||||||
| 		if (actual_workspace_link == &server->workspaces) { | 		if (actual_workspace_link == &server->workspaces.all) { | ||||||
| 			/* # of configured workspaces increased */ | 			/* # of configured workspaces increased */ | ||||||
| 			wlr_log(WLR_DEBUG, "Adding workspace \"%s\"", | 			wlr_log(WLR_DEBUG, "Adding workspace \"%s\"", | ||||||
| 				configured_workspace->name); | 				configured_workspace->name); | ||||||
|  | @ -429,20 +467,22 @@ workspaces_reconfigure(struct server *server) | ||||||
| 				actual_workspace->name, configured_workspace->name); | 				actual_workspace->name, configured_workspace->name); | ||||||
| 			free(actual_workspace->name); | 			free(actual_workspace->name); | ||||||
| 			actual_workspace->name = xstrdup(configured_workspace->name); | 			actual_workspace->name = xstrdup(configured_workspace->name); | ||||||
|  | 			lab_cosmic_workspace_set_name( | ||||||
|  | 				actual_workspace->cosmic_workspace, actual_workspace->name); | ||||||
| 		} | 		} | ||||||
| 		actual_workspace_link = actual_workspace_link->next; | 		actual_workspace_link = actual_workspace_link->next; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if (actual_workspace_link == &server->workspaces) { | 	if (actual_workspace_link == &server->workspaces.all) { | ||||||
| 		return; | 		return; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	/* # of configured workspaces decreased */ | 	/* # of configured workspaces decreased */ | ||||||
| 	overlay_hide(&server->seat); | 	overlay_hide(&server->seat); | ||||||
| 	struct workspace *first_workspace = | 	struct workspace *first_workspace = | ||||||
| 		wl_container_of(server->workspaces.next, first_workspace, link); | 		wl_container_of(server->workspaces.all.next, first_workspace, link); | ||||||
| 
 | 
 | ||||||
| 	while (actual_workspace_link != &server->workspaces) { | 	while (actual_workspace_link != &server->workspaces.all) { | ||||||
| 		struct workspace *actual_workspace = wl_container_of( | 		struct workspace *actual_workspace = wl_container_of( | ||||||
| 			actual_workspace_link, actual_workspace, link); | 			actual_workspace_link, actual_workspace, link); | ||||||
| 
 | 
 | ||||||
|  | @ -456,12 +496,12 @@ workspaces_reconfigure(struct server *server) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		if (server->workspace_current == actual_workspace) { | 		if (server->workspaces.current == actual_workspace) { | ||||||
| 			workspaces_switch_to(first_workspace, | 			workspaces_switch_to(first_workspace, | ||||||
| 				/* update_focus */ true); | 				/* update_focus */ true); | ||||||
| 		} | 		} | ||||||
| 		if (server->workspace_last == actual_workspace) { | 		if (server->workspaces.last == actual_workspace) { | ||||||
| 			server->workspace_last = first_workspace; | 			server->workspaces.last = first_workspace; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 		actual_workspace_link = actual_workspace_link->next; | 		actual_workspace_link = actual_workspace_link->next; | ||||||
|  | @ -473,8 +513,8 @@ void | ||||||
| workspaces_destroy(struct server *server) | workspaces_destroy(struct server *server) | ||||||
| { | { | ||||||
| 	struct workspace *workspace, *tmp; | 	struct workspace *workspace, *tmp; | ||||||
| 	wl_list_for_each_safe(workspace, tmp, &server->workspaces, link) { | 	wl_list_for_each_safe(workspace, tmp, &server->workspaces.all, link) { | ||||||
| 		destroy_workspace(workspace); | 		destroy_workspace(workspace); | ||||||
| 	} | 	} | ||||||
| 	assert(wl_list_empty(&server->workspaces)); | 	assert(wl_list_empty(&server->workspaces.all)); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -895,7 +895,7 @@ xdg_toplevel_new(struct wl_listener *listener, void *data) | ||||||
| 			view->output->wlr_output->scale); | 			view->output->wlr_output->scale); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	view->workspace = server->workspace_current; | 	view->workspace = server->workspaces.current; | ||||||
| 	view->scene_tree = wlr_scene_tree_create(view->workspace->tree); | 	view->scene_tree = wlr_scene_tree_create(view->workspace->tree); | ||||||
| 	wlr_scene_node_set_enabled(&view->scene_tree->node, false); | 	wlr_scene_node_set_enabled(&view->scene_tree->node, false); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -975,7 +975,7 @@ xwayland_view_create(struct server *server, | ||||||
| 	xwayland_view->xwayland_surface = xsurface; | 	xwayland_view->xwayland_surface = xsurface; | ||||||
| 	xsurface->data = view; | 	xsurface->data = view; | ||||||
| 
 | 
 | ||||||
| 	view->workspace = server->workspace_current; | 	view->workspace = server->workspaces.current; | ||||||
| 	view->scene_tree = wlr_scene_tree_create(view->workspace->tree); | 	view->scene_tree = wlr_scene_tree_create(view->workspace->tree); | ||||||
| 	node_descriptor_create(&view->scene_tree->node, LAB_NODE_DESC_VIEW, view); | 	node_descriptor_create(&view->scene_tree->node, LAB_NODE_DESC_VIEW, view); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Johan Malm
						Johan Malm