1 /*
2  * Copyright (C) 2020 Red Hat
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
17  * 02111-1307, USA.
18  *
19  * Author: Carlos Garnacho <carlosg@gnome.org>
20  */
21 
22 #include "config.h"
23 
24 #include "wayland/meta-wayland-activation.h"
25 
26 #include <glib.h>
27 #include <wayland-server.h>
28 
29 #include "wayland/meta-wayland-private.h"
30 #include "wayland/meta-wayland-versions.h"
31 
32 #include "xdg-activation-v1-server-protocol.h"
33 
34 typedef struct _MetaXdgActivationToken MetaXdgActivationToken;
35 
36 struct _MetaWaylandActivation
37 {
38   MetaWaylandCompositor *compositor;
39   struct wl_list resource_list;
40   struct wl_list token_list;
41   GHashTable *tokens;
42 };
43 
44 struct _MetaXdgActivationToken
45 {
46   MetaWaylandSurface *surface;
47   MetaWaylandSeat *seat;
48   MetaWaylandActivation *activation;
49   MetaStartupSequence *sequence;
50   char *app_id;
51   char *token;
52   uint32_t serial;
53   gulong sequence_complete_id;
54   gboolean committed;
55 };
56 
57 static void
unbind_resource(struct wl_resource * resource)58 unbind_resource (struct wl_resource *resource)
59 {
60   wl_list_remove (wl_resource_get_link (resource));
61 }
62 
63 static void
token_set_serial(struct wl_client * client,struct wl_resource * resource,uint32_t serial,struct wl_resource * seat_resource)64 token_set_serial (struct wl_client   *client,
65                   struct wl_resource *resource,
66                   uint32_t            serial,
67                   struct wl_resource *seat_resource)
68 {
69   MetaXdgActivationToken *token = wl_resource_get_user_data (resource);
70   MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource);
71 
72   token->serial = serial;
73   token->seat = seat;
74 }
75 
76 static void
token_set_app_id(struct wl_client * client,struct wl_resource * resource,const char * app_id)77 token_set_app_id (struct wl_client   *client,
78                   struct wl_resource *resource,
79                   const char         *app_id)
80 {
81   MetaXdgActivationToken *token = wl_resource_get_user_data (resource);
82 
83   g_clear_pointer (&token->app_id, g_free);
84   token->app_id = g_strdup (app_id);
85 }
86 
87 static void
token_set_surface(struct wl_client * client,struct wl_resource * resource,struct wl_resource * surface_resource)88 token_set_surface (struct wl_client   *client,
89                    struct wl_resource *resource,
90                    struct wl_resource *surface_resource)
91 {
92   MetaXdgActivationToken *token = wl_resource_get_user_data (resource);
93   MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
94 
95   token->surface = surface;
96 }
97 
98 static void
sequence_complete_cb(MetaStartupSequence * sequence,MetaXdgActivationToken * token)99 sequence_complete_cb (MetaStartupSequence    *sequence,
100                       MetaXdgActivationToken *token)
101 {
102   MetaWaylandActivation *activation = token->activation;
103   MetaDisplay *display = meta_get_display ();
104 
105   meta_startup_notification_remove_sequence (display->startup_notification,
106                                              sequence);
107   g_hash_table_remove (activation->tokens, token->token);
108 }
109 
110 static char *
create_startup_token(MetaWaylandActivation * activation,MetaDisplay * display)111 create_startup_token (MetaWaylandActivation *activation,
112                       MetaDisplay           *display)
113 {
114   g_autofree char *uuid = NULL, *token = NULL;
115 
116   do
117     {
118       g_clear_pointer (&uuid, g_free);
119       g_clear_pointer (&token, g_free);
120       uuid = g_uuid_string_random ();
121       token = g_strdup_printf ("%s_TIME%d", uuid,
122                                meta_display_get_current_time (display));
123     }
124   while (g_hash_table_contains (activation->tokens, token));
125 
126   return g_steal_pointer (&token);
127 }
128 
129 static void
token_commit(struct wl_client * client,struct wl_resource * resource)130 token_commit (struct wl_client   *client,
131               struct wl_resource *resource)
132 {
133   MetaXdgActivationToken *token = wl_resource_get_user_data (resource);
134   MetaWaylandActivation *activation = token->activation;
135   MetaDisplay *display = meta_get_display ();
136   uint32_t timestamp;
137 
138   if (token->committed)
139     {
140       wl_resource_post_error (resource,
141                               XDG_ACTIVATION_TOKEN_V1_ERROR_ALREADY_USED,
142                               "Activation token was already used");
143       return;
144     }
145 
146   timestamp = meta_display_get_current_time_roundtrip (display);
147 
148   token->committed = TRUE;
149   token->token = create_startup_token (activation, display);
150   token->sequence = g_object_new (META_TYPE_STARTUP_SEQUENCE,
151                                   "id", token->token,
152                                   "application-id", token->app_id,
153                                   "timestamp", timestamp,
154                                   NULL);
155 
156   token->sequence_complete_id =
157     g_signal_connect (token->sequence,
158                       "complete",
159                       G_CALLBACK (sequence_complete_cb),
160                       token);
161 
162   meta_startup_notification_add_sequence (display->startup_notification,
163                                           token->sequence);
164 
165   xdg_activation_token_v1_send_done (resource, token->token);
166   g_hash_table_insert (activation->tokens, token->token, token);
167 }
168 
169 static void
token_destroy(struct wl_client * client,struct wl_resource * resource)170 token_destroy (struct wl_client   *client,
171                struct wl_resource *resource)
172 {
173   wl_resource_destroy (resource);
174 }
175 
176 static const struct xdg_activation_token_v1_interface token_interface = {
177   token_set_serial,
178   token_set_app_id,
179   token_set_surface,
180   token_commit,
181   token_destroy,
182 };
183 
184 static void
meta_xdg_activation_token_free(MetaXdgActivationToken * token)185 meta_xdg_activation_token_free (MetaXdgActivationToken *token)
186 {
187   if (token->sequence)
188     {
189       g_clear_signal_handler (&token->sequence_complete_id,
190                               token->sequence);
191       g_clear_object (&token->sequence);
192     }
193 
194   g_free (token->app_id);
195   g_free (token->token);
196   g_free (token);
197 }
198 
199 static void
meta_wayland_activation_token_create_new_resource(MetaWaylandActivation * activation,struct wl_client * client,struct wl_resource * activation_resource,uint32_t id)200 meta_wayland_activation_token_create_new_resource (MetaWaylandActivation *activation,
201                                                    struct wl_client      *client,
202                                                    struct wl_resource    *activation_resource,
203                                                    uint32_t               id)
204 {
205   MetaXdgActivationToken *token;
206   struct wl_resource *token_resource;
207 
208   token = g_new0 (MetaXdgActivationToken, 1);
209   token->activation = activation;
210 
211   token_resource =
212     wl_resource_create (client, &xdg_activation_token_v1_interface,
213                         wl_resource_get_version (activation_resource),
214                         id);
215   wl_resource_set_implementation (token_resource, &token_interface,
216                                   token, unbind_resource);
217   wl_resource_set_user_data (token_resource, token);
218   wl_list_insert (&activation->token_list,
219                   wl_resource_get_link (token_resource));
220 }
221 
222 static void
activation_destroy(struct wl_client * client,struct wl_resource * resource)223 activation_destroy (struct wl_client   *client,
224                     struct wl_resource *resource)
225 {
226   wl_resource_destroy (resource);
227 }
228 
229 static void
activation_get_activation_token(struct wl_client * client,struct wl_resource * resource,uint32_t id)230 activation_get_activation_token (struct wl_client   *client,
231                                  struct wl_resource *resource,
232                                  uint32_t            id)
233 {
234   MetaWaylandActivation *activation = wl_resource_get_user_data (resource);
235 
236   meta_wayland_activation_token_create_new_resource (activation,
237                                                      client,
238                                                      resource,
239                                                      id);
240 }
241 
242 static void
activation_activate(struct wl_client * client,struct wl_resource * resource,const char * token_str,struct wl_resource * surface_resource)243 activation_activate (struct wl_client   *client,
244                      struct wl_resource *resource,
245                      const char         *token_str,
246                      struct wl_resource *surface_resource)
247 {
248   MetaWaylandActivation *activation = wl_resource_get_user_data (resource);
249   MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource);
250   MetaXdgActivationToken *token;
251   MetaWindow *window;
252 
253   window = meta_wayland_surface_get_window (surface);
254   if (!window)
255     return;
256 
257   token = g_hash_table_lookup (activation->tokens, token_str);
258   if (!token)
259     return;
260 
261   if (meta_wayland_seat_get_grab_info (token->seat,
262                                        token->surface,
263                                        token->serial,
264                                        FALSE, NULL, NULL))
265     {
266       uint32_t timestamp;
267       int32_t workspace_idx;
268 
269       workspace_idx = meta_startup_sequence_get_workspace (token->sequence);
270       timestamp = meta_startup_sequence_get_timestamp (token->sequence);
271 
272       if (workspace_idx >= 0)
273         meta_window_change_workspace_by_index (window, workspace_idx, TRUE);
274 
275       meta_window_activate_full (window, timestamp,
276                                  META_CLIENT_TYPE_APPLICATION, NULL);
277     }
278   else
279     {
280       meta_window_set_demands_attention (window);
281     }
282 
283   meta_startup_sequence_complete (token->sequence);
284 }
285 
286 static const struct xdg_activation_v1_interface activation_interface = {
287   activation_destroy,
288   activation_get_activation_token,
289   activation_activate,
290 };
291 
292 static void
bind_activation(struct wl_client * client,void * data,uint32_t version,uint32_t id)293 bind_activation (struct wl_client *client,
294                  void             *data,
295                  uint32_t          version,
296                  uint32_t          id)
297 {
298   MetaWaylandCompositor *compositor = data;
299   MetaWaylandActivation *activation = compositor->activation;
300   struct wl_resource *resource;
301 
302   resource = wl_resource_create (client, &xdg_activation_v1_interface,
303                                  MIN (version, META_XDG_ACTIVATION_V1_VERSION),
304                                  id);
305   wl_resource_set_implementation (resource, &activation_interface,
306                                   activation, unbind_resource);
307   wl_resource_set_user_data (resource, activation);
308   wl_list_insert (&activation->resource_list,
309                   wl_resource_get_link (resource));
310 }
311 
312 void
meta_wayland_activation_init(MetaWaylandCompositor * compositor)313 meta_wayland_activation_init (MetaWaylandCompositor *compositor)
314 {
315   MetaWaylandActivation *activation;
316 
317   activation = g_new0 (MetaWaylandActivation, 1);
318   activation->compositor = compositor;
319   wl_list_init (&activation->resource_list);
320   wl_list_init (&activation->token_list);
321 
322   activation->tokens =
323     g_hash_table_new_full (g_str_hash, g_str_equal,
324                            NULL,
325                            (GDestroyNotify) meta_xdg_activation_token_free);
326 
327   wl_global_create (compositor->wayland_display,
328                     &xdg_activation_v1_interface,
329                     META_XDG_ACTIVATION_V1_VERSION,
330                     compositor, bind_activation);
331 
332   compositor->activation = activation;
333 }
334