1 /*
2 * Clutter.
3 *
4 * An OpenGL based 'interactive canvas' library.
5 *
6 * Copyright (C) 2010 Intel Corporation.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
20
21 * Authors:
22 * Matthew Allum
23 * Robert Bragg
24 * Kristian Høgsberg
25 */
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #include <glib.h>
32
33 #include "clutter-wayland.h"
34 #include "clutter-stage-wayland.h"
35 #include "clutter-backend-wayland.h"
36 #include "clutter-backend-wayland-priv.h"
37 #include "clutter-stage-window.h"
38 #include "clutter-stage-private.h"
39 #include "clutter-event-private.h"
40 #include "clutter-wayland.h"
41 #include <cogl/cogl.h>
42 #include <cogl/cogl-wayland-client.h>
43
44 static ClutterStageWindowIface *clutter_stage_window_parent_iface = NULL;
45
46 static void clutter_stage_window_iface_init (ClutterStageWindowIface *iface);
47
48 #define clutter_stage_wayland_get_type _clutter_stage_wayland_get_type
49
50 G_DEFINE_TYPE_WITH_CODE (ClutterStageWayland,
51 clutter_stage_wayland,
52 CLUTTER_TYPE_STAGE_COGL,
53 G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_STAGE_WINDOW,
54 clutter_stage_window_iface_init));
55
56 static void
handle_ping(void * data,struct wl_shell_surface * shell_surface,uint32_t serial)57 handle_ping (void *data,
58 struct wl_shell_surface *shell_surface,
59 uint32_t serial)
60 {
61 wl_shell_surface_pong(shell_surface, serial);
62 }
63
64 static void
handle_configure(void * data,struct wl_shell_surface * shell_surface,uint32_t edges,int32_t width,int32_t height)65 handle_configure (void *data,
66 struct wl_shell_surface *shell_surface,
67 uint32_t edges,
68 int32_t width,
69 int32_t height)
70 {
71 ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL(data);
72 CoglFramebuffer *fb = COGL_FRAMEBUFFER (stage_cogl->onscreen);
73
74 if (cogl_framebuffer_get_width (fb) != width ||
75 cogl_framebuffer_get_height (fb) != height)
76 clutter_actor_queue_relayout (CLUTTER_ACTOR (stage_cogl->wrapper));
77
78 clutter_actor_set_size (CLUTTER_ACTOR (stage_cogl->wrapper),
79 width, height);
80
81 /* the resize process is complete, so we can ask the stage
82 * to set up the GL viewport with the new size
83 */
84 clutter_stage_ensure_viewport (stage_cogl->wrapper);
85 }
86
87 static void
handle_popup_done(void * data,struct wl_shell_surface * shell_surface)88 handle_popup_done (void *data,
89 struct wl_shell_surface *shell_surface)
90 {
91 /* XXX: Fill me in. */
92 }
93
94 static const struct wl_shell_surface_listener shell_surface_listener = {
95 handle_ping,
96 handle_configure,
97 handle_popup_done,
98 };
99
100 static void
101 clutter_stage_wayland_set_fullscreen (ClutterStageWindow *stage_window,
102 gboolean fullscreen);
103
104 static gboolean
clutter_stage_wayland_realize(ClutterStageWindow * stage_window)105 clutter_stage_wayland_realize (ClutterStageWindow *stage_window)
106 {
107 ClutterStageWayland *stage_wayland = CLUTTER_STAGE_WAYLAND (stage_window);
108 ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
109 struct wl_surface *wl_surface;
110 struct wl_shell_surface *wl_shell_surface;
111
112 clutter_stage_window_parent_iface->realize (stage_window);
113
114 wl_surface = cogl_wayland_onscreen_get_surface (stage_cogl->onscreen);
115 wl_surface_set_user_data (wl_surface, stage_wayland);
116 stage_wayland->wayland_surface = wl_surface;
117
118 if (!stage_wayland->foreign_wl_surface)
119 {
120 wl_shell_surface =
121 cogl_wayland_onscreen_get_shell_surface (stage_cogl->onscreen);
122 wl_shell_surface_add_listener (wl_shell_surface,
123 &shell_surface_listener,
124 stage_wayland);
125 stage_wayland->wayland_shell_surface = wl_shell_surface;
126 }
127
128 if (stage_wayland->fullscreen)
129 clutter_stage_wayland_set_fullscreen (stage_window, TRUE);
130
131 return TRUE;
132 }
133
134 static void
clutter_stage_wayland_show(ClutterStageWindow * stage_window,gboolean do_raise)135 clutter_stage_wayland_show (ClutterStageWindow *stage_window,
136 gboolean do_raise)
137 {
138 ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
139 ClutterStageWayland *stage_wayland = CLUTTER_STAGE_WAYLAND (stage_window);
140
141 clutter_stage_window_parent_iface->show (stage_window, do_raise);
142
143 if (stage_wayland->wayland_shell_surface)
144 wl_shell_surface_set_toplevel (stage_wayland->wayland_shell_surface);
145
146 stage_wayland->shown = TRUE;
147
148 /* We need to queue a redraw after the stage is shown because all of
149 * the other queue redraws up to this point will have been ignored
150 * because the actor was not visible. The other backends do not need
151 * to do this because they will get expose events at some point, but
152 * that does not happen for Wayland. */
153 clutter_actor_queue_redraw (CLUTTER_ACTOR (stage_cogl->wrapper));
154 }
155
156 static void
clutter_stage_wayland_set_cursor_visible(ClutterStageWindow * stage_window,gboolean cursor_visible)157 clutter_stage_wayland_set_cursor_visible (ClutterStageWindow *stage_window,
158 gboolean cursor_visible)
159 {
160 ClutterStageWayland *stage_wayland = CLUTTER_STAGE_WAYLAND (stage_window);
161
162 stage_wayland->cursor_visible = cursor_visible;
163 }
164
165 static void
clutter_stage_wayland_set_fullscreen(ClutterStageWindow * stage_window,gboolean fullscreen)166 clutter_stage_wayland_set_fullscreen (ClutterStageWindow *stage_window,
167 gboolean fullscreen)
168 {
169 ClutterStageWayland *stage_wayland = CLUTTER_STAGE_WAYLAND (stage_window);
170 ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
171 ClutterBackend *backend = CLUTTER_BACKEND (stage_cogl->backend);
172 ClutterBackendWayland *backend_wayland = CLUTTER_BACKEND_WAYLAND (backend);
173 ClutterActor *stage = _clutter_stage_window_get_wrapper (stage_window);
174
175 stage_wayland->fullscreen = fullscreen;
176
177 if (!stage_wayland->wayland_shell_surface)
178 return;
179
180 if (fullscreen)
181 {
182 _clutter_stage_update_state (stage_cogl->wrapper,
183 0,
184 CLUTTER_STAGE_STATE_FULLSCREEN);
185
186 /* FIXME: In future versions of the Wayland protocol we'll get a
187 * configure with the dimensions we can use - but for now we have to
188 * use the dimensions from the output's mode
189 */
190 clutter_actor_set_size (stage,
191 backend_wayland->output_width,
192 backend_wayland->output_height);
193
194 /* FIXME: And we must force a redraw so that new sized buffer gets
195 * attached
196 */
197 _clutter_stage_window_redraw (stage_window);
198 wl_shell_surface_set_fullscreen (stage_wayland->wayland_shell_surface,
199 WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
200 0,
201 NULL);
202 }
203 else
204 {
205 _clutter_stage_update_state (stage_cogl->wrapper,
206 CLUTTER_STAGE_STATE_FULLSCREEN,
207 0);
208
209 wl_shell_surface_set_toplevel (stage_wayland->wayland_shell_surface);
210 }
211 }
212
213 static void
clutter_stage_wayland_resize(ClutterStageWindow * stage_window,gint width,gint height)214 clutter_stage_wayland_resize (ClutterStageWindow *stage_window,
215 gint width,
216 gint height)
217 {
218 ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window);
219 ClutterStageWayland *stage_wayland = CLUTTER_STAGE_WAYLAND (stage_window);
220
221 /* Resize preserving top left */
222 if (stage_cogl->onscreen)
223 {
224 cogl_wayland_onscreen_resize (stage_cogl->onscreen, width, height, 0, 0);
225
226 /* Only trigger a redraw if the stage window has been shown */
227 if (stage_wayland->shown)
228 _clutter_stage_window_redraw (stage_window);
229 }
230 }
231
232 static gboolean
clutter_stage_wayland_can_clip_redraws(ClutterStageWindow * stage_window)233 clutter_stage_wayland_can_clip_redraws (ClutterStageWindow *stage_window)
234 {
235 return TRUE;
236 }
237
238 static void
clutter_stage_wayland_init(ClutterStageWayland * stage_wayland)239 clutter_stage_wayland_init (ClutterStageWayland *stage_wayland)
240 {
241 stage_wayland->cursor_visible = TRUE;
242 }
243
244 static void
clutter_stage_window_iface_init(ClutterStageWindowIface * iface)245 clutter_stage_window_iface_init (ClutterStageWindowIface *iface)
246 {
247 clutter_stage_window_parent_iface = g_type_interface_peek_parent (iface);
248
249 iface->realize = clutter_stage_wayland_realize;
250 iface->show = clutter_stage_wayland_show;
251 iface->set_fullscreen = clutter_stage_wayland_set_fullscreen;
252 iface->set_cursor_visible = clutter_stage_wayland_set_cursor_visible;
253 iface->resize = clutter_stage_wayland_resize;
254 iface->can_clip_redraws = clutter_stage_wayland_can_clip_redraws;
255 }
256
257 static void
clutter_stage_wayland_class_init(ClutterStageWaylandClass * klass)258 clutter_stage_wayland_class_init (ClutterStageWaylandClass *klass)
259 {
260 }
261
262 /**
263 * clutter_wayland_stage_get_wl_shell_surface: (skip)
264 * @stage: a #ClutterStage
265 *
266 * Access the underlying data structure representing the shell surface that is
267 * backing the #ClutterStage
268 *
269 * Note: this function can only be called when running on the Wayland
270 * platform. Calling this function at any other time will return %NULL.
271 *
272 * Returns: (transfer none): the Wayland shell surface associated with
273 * @stage
274 *
275 * Since: 1.10
276 */
277 struct wl_shell_surface *
clutter_wayland_stage_get_wl_shell_surface(ClutterStage * stage)278 clutter_wayland_stage_get_wl_shell_surface (ClutterStage *stage)
279 {
280 ClutterStageWindow *stage_window = _clutter_stage_get_window (stage);
281 ClutterStageWayland *stage_wayland;
282
283 if (!CLUTTER_IS_STAGE_WAYLAND (stage_window))
284 return NULL;
285
286 stage_wayland = CLUTTER_STAGE_WAYLAND (stage_window);
287
288 return stage_wayland->wayland_shell_surface;
289 }
290
291 /**
292 * clutter_wayland_stage_get_wl_surface: (skip)
293 * @stage: a #ClutterStage
294 *
295 * Access the underlying data structure representing the surface that is
296 * backing the #ClutterStage
297 *
298 * Note: this function can only be called when running on the Wayland
299 * platform. Calling this function at any other time will return %NULL.
300 *
301 * Returns: (transfer none): the Wayland surface associated with @stage
302 *
303 * Since: 1.10
304 */
305 struct wl_surface *
clutter_wayland_stage_get_wl_surface(ClutterStage * stage)306 clutter_wayland_stage_get_wl_surface (ClutterStage *stage)
307 {
308 ClutterStageWindow *stage_window = _clutter_stage_get_window (stage);
309 ClutterStageWayland *stage_wayland;
310
311 if (!CLUTTER_IS_STAGE_WAYLAND (stage_window))
312 return NULL;
313
314 stage_wayland = CLUTTER_STAGE_WAYLAND (stage_window);
315
316 return stage_wayland->wayland_surface;
317
318 }
319
320 /**
321 * clutter_wayland_stage_set_wl_surface:
322 * @stage: a #ClutterStage
323 * @surface: A Wayland surface to associate with the @stage.
324 *
325 * Allows you to explicitly provide an existing Wayland surface to associate
326 * with @stage, preventing Cogl from allocating a surface and shell surface for
327 * the stage automatically.
328 *
329 * This function must be called before @stage is shown.
330 *
331 * Note: this function can only be called when running on the Wayland
332 * platform. Calling this function at any other time has no effect.
333 *
334 * Since: 1.16
335 */
336 void
clutter_wayland_stage_set_wl_surface(ClutterStage * stage,struct wl_surface * surface)337 clutter_wayland_stage_set_wl_surface (ClutterStage *stage,
338 struct wl_surface *surface)
339 {
340 ClutterStageWindow *stage_window = _clutter_stage_get_window (stage);
341 ClutterStageWayland *stage_wayland;
342 ClutterStageCogl *stage_cogl;
343
344 if (!CLUTTER_IS_STAGE_WAYLAND (stage_window))
345 return;
346
347 stage_cogl = CLUTTER_STAGE_COGL (stage_window);
348
349 if (stage_cogl->onscreen == NULL)
350 {
351 ClutterBackend *backend = clutter_get_default_backend ();
352
353 /* Use the same default dimensions as clutter_stage_cogl_realize() */
354 stage_cogl->onscreen = cogl_onscreen_new (backend->cogl_context,
355 800, 600);
356
357 cogl_wayland_onscreen_set_foreign_surface (stage_cogl->onscreen,
358 surface);
359
360 stage_wayland = CLUTTER_STAGE_WAYLAND (stage_window);
361 stage_wayland->foreign_wl_surface = TRUE;
362 }
363 else
364 g_warning (G_STRLOC ": cannot set foreign surface for stage");
365 }
366