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