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