1 /*
2  * Copyright (C) 2001 Havoc Pennington
3  * Copyright (C) 2005 Elijah Newren
4  * Copyright (C) 2020 Red Hat Inc.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #include "config.h"
21 
22 #include "compositor/meta-later-private.h"
23 
24 #include "cogl/cogl.h"
25 #include "compositor/compositor-private.h"
26 #include "core/display-private.h"
27 #include "meta/meta-later.h"
28 
29 typedef struct _MetaLater
30 {
31   unsigned int id;
32   unsigned int ref_count;
33   MetaLaterType when;
34 
35   GSourceFunc func;
36   gpointer user_data;
37   GDestroyNotify destroy_notify;
38 
39   guint source_id;
40   gboolean run_once;
41 } MetaLater;
42 
43 #define META_LATER_N_TYPES (META_LATER_IDLE + 1)
44 
45 struct _MetaLaters
46 {
47   MetaCompositor *compositor;
48 
49   unsigned int last_later_id;
50 
51   GSList *laters[META_LATER_N_TYPES];
52 
53   gulong before_update_handler_id;
54 };
55 
56 static MetaLater *
meta_later_ref(MetaLater * later)57 meta_later_ref (MetaLater *later)
58 {
59   later->ref_count++;
60   return later;
61 }
62 
63 static void
meta_later_unref(MetaLater * later)64 meta_later_unref (MetaLater *later)
65 {
66   if (--later->ref_count == 0)
67     {
68       if (later->destroy_notify)
69         {
70           later->destroy_notify (later->user_data);
71           later->destroy_notify = NULL;
72         }
73 
74       g_free (later);
75     }
76 }
77 
78 static void
meta_later_destroy(MetaLater * later)79 meta_later_destroy (MetaLater *later)
80 {
81   g_clear_handle_id (&later->source_id, g_source_remove);
82   later->func = NULL;
83   meta_later_unref (later);
84 }
85 
86 #ifdef COGL_HAS_TRACING
87 static const char *
later_type_to_string(MetaLaterType when)88 later_type_to_string (MetaLaterType when)
89 {
90   switch (when)
91     {
92     case META_LATER_RESIZE:
93       return "Later (resize)";
94     case META_LATER_CALC_SHOWING:
95       return "Later (calc-showing)";
96     case META_LATER_CHECK_FULLSCREEN:
97       return "Later (check-fullscreen)";
98     case META_LATER_SYNC_STACK:
99       return "Later (sync-stack)";
100     case META_LATER_BEFORE_REDRAW:
101       return "Later (before-redraw)";
102     case META_LATER_IDLE:
103       return "Later (idle)";
104     }
105 
106   return "unknown";
107 }
108 #endif
109 
110 static gboolean
meta_later_invoke(MetaLater * later)111 meta_later_invoke (MetaLater *later)
112 {
113   COGL_TRACE_BEGIN_SCOPED (later, later_type_to_string (later->when));
114   return later->func (later->user_data);
115 }
116 
117 static gboolean
remove_later_from_list(unsigned int later_id,GSList ** laters_list)118 remove_later_from_list (unsigned int   later_id,
119                         GSList       **laters_list)
120 {
121   GSList *l;
122 
123   for (l = *laters_list; l; l = l->next)
124     {
125       MetaLater *later = l->data;
126 
127       if (later->id == later_id)
128         {
129           *laters_list = g_slist_delete_link (*laters_list, l);
130           meta_later_destroy (later);
131           return TRUE;
132         }
133     }
134 
135   return FALSE;
136 }
137 
138 static void
run_repaint_laters(GSList ** laters_list)139 run_repaint_laters (GSList **laters_list)
140 {
141   g_autoptr (GSList) laters_copy = NULL;
142   GSList *l;
143 
144   for (l = *laters_list; l; l = l->next)
145     {
146       MetaLater *later = l->data;
147 
148       if (!later->source_id ||
149           (later->when <= META_LATER_BEFORE_REDRAW && !later->run_once))
150         laters_copy = g_slist_prepend (laters_copy, meta_later_ref (later));
151     }
152   laters_copy = g_slist_reverse (laters_copy);
153 
154   for (l = laters_copy; l; l = l->next)
155     {
156       MetaLater *later = l->data;
157 
158       if (!later->func)
159         remove_later_from_list (later->id, laters_list);
160       else if (!meta_later_invoke (later))
161         remove_later_from_list (later->id, laters_list);
162 
163       meta_later_unref (later);
164     }
165 }
166 
167 static void
on_before_update(ClutterStage * stage,ClutterStageView * stage_view,MetaLaters * laters)168 on_before_update (ClutterStage     *stage,
169                   ClutterStageView *stage_view,
170                   MetaLaters       *laters)
171 {
172   unsigned int i;
173   GSList *l;
174   gboolean needs_schedule_update = FALSE;
175 
176   for (i = 0; i < G_N_ELEMENTS (laters->laters); i++)
177     run_repaint_laters (&laters->laters[i]);
178 
179   for (i = 0; i < G_N_ELEMENTS (laters->laters); i++)
180     {
181       for (l = laters->laters[i]; l; l = l->next)
182         {
183           MetaLater *later = l->data;
184 
185           if (!later->source_id)
186             needs_schedule_update = TRUE;
187         }
188     }
189 
190   if (needs_schedule_update)
191     clutter_stage_schedule_update (stage);
192 }
193 
194 static gboolean
invoke_later_idle(gpointer data)195 invoke_later_idle (gpointer data)
196 {
197   MetaLater *later = data;
198 
199   if (!later->func (later->user_data))
200     {
201       meta_later_remove (later->id);
202       return FALSE;
203     }
204   else
205     {
206       later->run_once = TRUE;
207       return TRUE;
208     }
209 }
210 
211 static unsigned int
meta_laters_add(MetaLaters * laters,MetaLaterType when,GSourceFunc func,gpointer user_data,GDestroyNotify notify)212 meta_laters_add (MetaLaters     *laters,
213                  MetaLaterType   when,
214                  GSourceFunc     func,
215                  gpointer        user_data,
216                  GDestroyNotify  notify)
217 {
218   ClutterStage *stage = meta_compositor_get_stage (laters->compositor);
219   MetaLater *later = g_new0 (MetaLater, 1);
220 
221   later->id = ++laters->last_later_id;
222   later->ref_count = 1;
223   later->when = when;
224   later->func = func;
225   later->user_data = user_data;
226   later->destroy_notify = notify;
227 
228   laters->laters[when] = g_slist_prepend (laters->laters[when], later);
229 
230   switch (when)
231     {
232     case META_LATER_RESIZE:
233       later->source_id = g_idle_add_full (META_PRIORITY_RESIZE,
234                                           invoke_later_idle,
235                                           later, NULL);
236       g_source_set_name_by_id (later->source_id, "[mutter] invoke_later_idle");
237       clutter_stage_schedule_update (stage);
238       break;
239     case META_LATER_CALC_SHOWING:
240     case META_LATER_CHECK_FULLSCREEN:
241     case META_LATER_SYNC_STACK:
242     case META_LATER_BEFORE_REDRAW:
243       clutter_stage_schedule_update (stage);
244       break;
245     case META_LATER_IDLE:
246       later->source_id = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE,
247                                           invoke_later_idle,
248                                           later, NULL);
249       g_source_set_name_by_id (later->source_id, "[mutter] invoke_later_idle");
250       break;
251     }
252 
253   return later->id;
254 }
255 
256 /**
257  * meta_later_add:
258  * @when: enumeration value determining the phase at which to run the callback
259  * @func: callback to run later
260  * @data: data to pass to the callback
261  * @notify: function to call to destroy @data when it is no longer in use, or %NULL
262  *
263  * Sets up a callback  to be called at some later time. @when determines the
264  * particular later occasion at which it is called. This is much like g_idle_add(),
265  * except that the functions interact properly with clutter event handling.
266  * If a "later" function is added from a clutter event handler, and is supposed
267  * to be run before the stage is redrawn, it will be run before that redraw
268  * of the stage, not the next one.
269  *
270  * Return value: an integer ID (guaranteed to be non-zero) that can be used
271  *  to cancel the callback and prevent it from being run.
272  */
273 unsigned int
meta_later_add(MetaLaterType when,GSourceFunc func,gpointer data,GDestroyNotify notify)274 meta_later_add (MetaLaterType  when,
275                 GSourceFunc    func,
276                 gpointer       data,
277                 GDestroyNotify notify)
278 {
279   MetaDisplay *display = meta_get_display ();
280   MetaCompositor *compositor;
281 
282   g_return_val_if_fail (display, 0);
283   g_return_val_if_fail (display->compositor, 0);
284 
285   compositor = display->compositor;
286   return meta_laters_add (meta_compositor_get_laters (compositor),
287                           when, func, data, notify);
288 }
289 
290 static void
meta_laters_remove(MetaLaters * laters,unsigned int later_id)291 meta_laters_remove (MetaLaters   *laters,
292                     unsigned int  later_id)
293 {
294   unsigned int i;
295 
296   for (i = 0; i < G_N_ELEMENTS (laters->laters); i++)
297     {
298       if (remove_later_from_list (later_id, &laters->laters[i]))
299         return;
300     }
301 }
302 
303 /**
304  * meta_later_remove:
305  * @later_id: the integer ID returned from meta_later_add()
306  *
307  * Removes a callback added with meta_later_add()
308  */
309 void
meta_later_remove(unsigned int later_id)310 meta_later_remove (unsigned int later_id)
311 {
312   MetaDisplay *display = meta_get_display ();
313   MetaCompositor *compositor;
314 
315   g_return_if_fail (display);
316 
317   compositor = display->compositor;
318   if (!compositor)
319     return;
320 
321   meta_laters_remove (meta_compositor_get_laters (compositor), later_id);
322 }
323 
324 MetaLaters *
meta_laters_new(MetaCompositor * compositor)325 meta_laters_new (MetaCompositor *compositor)
326 {
327   ClutterStage *stage = meta_compositor_get_stage (compositor);
328   MetaLaters *laters;
329 
330   laters = g_new0 (MetaLaters, 1);
331   laters->compositor = compositor;
332 
333   laters->before_update_handler_id =
334     g_signal_connect (stage, "before-update",
335                       G_CALLBACK (on_before_update),
336                       laters);
337 
338   return laters;
339 }
340 
341 void
meta_laters_free(MetaLaters * laters)342 meta_laters_free (MetaLaters *laters)
343 {
344   ClutterStage *stage = meta_compositor_get_stage (laters->compositor);
345   unsigned int i;
346 
347   for (i = 0; i < G_N_ELEMENTS (laters->laters); i++)
348     g_slist_free_full (laters->laters[i], (GDestroyNotify) meta_later_unref);
349 
350   g_clear_signal_handler (&laters->before_update_handler_id, stage);
351   g_free (laters);
352 }
353