1 /*
2  * Clutter.
3  *
4  * An OpenGL based 'interactive canvas' library.
5  *
6  * Copyright (C) 2010, 2011  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 <sys/types.h>
32 #include <sys/stat.h>
33 #include <fcntl.h>
34 #include <unistd.h>
35 #include <sys/mman.h>
36 
37 #include <errno.h>
38 
39 #include "clutter-debug.h"
40 #include "clutter-private.h"
41 #include "clutter-main.h"
42 #include "clutter-stage-private.h"
43 
44 #include "wayland/clutter-backend-wayland.h"
45 #include "wayland/clutter-backend-wayland-priv.h"
46 #include "wayland/clutter-device-manager-wayland.h"
47 #include "wayland/clutter-event-wayland.h"
48 #include "wayland/clutter-stage-wayland.h"
49 #include "wayland/clutter-wayland.h"
50 #include "cogl/clutter-stage-cogl.h"
51 
52 #include <wayland-client.h>
53 #include <wayland-cursor.h>
54 
55 #include <gdk-pixbuf/gdk-pixbuf.h>
56 #include <cogl/cogl.h>
57 #include <cogl/cogl-wayland-client.h>
58 
59 G_DEFINE_TYPE (ClutterBackendWayland, clutter_backend_wayland, CLUTTER_TYPE_BACKEND);
60 
61 static struct wl_display *_foreign_display = NULL;
62 static gboolean _no_event_dispatch = FALSE;
63 
64 static void
clutter_backend_wayland_dispose(GObject * gobject)65 clutter_backend_wayland_dispose (GObject *gobject)
66 {
67   ClutterBackendWayland *backend_wayland = CLUTTER_BACKEND_WAYLAND (gobject);
68 
69   if (backend_wayland->device_manager)
70     {
71       g_object_unref (backend_wayland->device_manager);
72       backend_wayland->device_manager = NULL;
73     }
74 
75   if (backend_wayland->cursor_buffer)
76     {
77       wl_buffer_destroy (backend_wayland->cursor_buffer);
78       backend_wayland->cursor_buffer = NULL;
79     }
80 
81   if (backend_wayland->cursor_theme)
82     {
83       wl_cursor_theme_destroy (backend_wayland->cursor_theme);
84       backend_wayland->cursor_theme = NULL;
85     }
86 
87   G_OBJECT_CLASS (clutter_backend_wayland_parent_class)->dispose (gobject);
88 }
89 
90 
91 static void
output_handle_mode(void * data,struct wl_output * wl_output,uint32_t flags,int width,int height,int refresh)92 output_handle_mode (void             *data,
93                     struct wl_output *wl_output,
94                     uint32_t          flags,
95                     int               width,
96                     int               height,
97                     int               refresh)
98 {
99   ClutterBackendWayland *backend_wayland = data;
100 
101   if (flags & WL_OUTPUT_MODE_CURRENT)
102     {
103       backend_wayland->output_width = width;
104       backend_wayland->output_height = height;
105     }
106 }
107 
108 static void
output_handle_geometry(void * data,struct wl_output * wl_output,int x,int y,int physical_width,int physical_height,int subpixel,const char * make,const char * model,int32_t transform)109 output_handle_geometry (void             *data,
110                         struct wl_output *wl_output,
111                         int               x,
112                         int               y,
113                         int               physical_width,
114                         int               physical_height,
115                         int               subpixel,
116                         const char       *make,
117                         const char       *model,
118                         int32_t transform)
119 {
120 }
121 
122 
123 static const struct wl_output_listener wayland_output_listener = {
124   output_handle_geometry,
125   output_handle_mode,
126 };
127 
128 
129 static void
registry_handle_global(void * data,struct wl_registry * registry,uint32_t id,const char * interface,uint32_t version)130 registry_handle_global (void *data,
131                         struct wl_registry *registry,
132                         uint32_t id,
133                         const char *interface,
134                         uint32_t version)
135 {
136   ClutterBackendWayland *backend_wayland = data;
137 
138   if (strcmp (interface, "wl_compositor") == 0)
139     backend_wayland->wayland_compositor =
140       wl_registry_bind (registry, id, &wl_compositor_interface, 1);
141   else if (strcmp (interface, "wl_seat") == 0)
142     {
143       ClutterDeviceManager *device_manager = backend_wayland->device_manager;
144       _clutter_device_manager_wayland_add_input_group (device_manager, id);
145     }
146   else if (strcmp (interface, "wl_shell") == 0)
147     {
148       backend_wayland->wayland_shell =
149         wl_registry_bind (registry, id, &wl_shell_interface, 1);
150     }
151   else if (strcmp (interface, "wl_shm") == 0)
152     {
153       backend_wayland->wayland_shm =
154         wl_registry_bind (registry, id, &wl_shm_interface, 1);
155     }
156   else if (strcmp (interface, "wl_output") == 0)
157     {
158       /* FIXME: Support multiple outputs */
159       backend_wayland->wayland_output =
160         wl_registry_bind (registry, id, &wl_output_interface, 1);
161       wl_output_add_listener (backend_wayland->wayland_output,
162                               &wayland_output_listener,
163                               backend_wayland);
164     }
165 }
166 
167 static const struct wl_registry_listener wayland_registry_listener = {
168   registry_handle_global,
169 };
170 
171 static gboolean
clutter_backend_wayland_post_parse(ClutterBackend * backend,GError ** error)172 clutter_backend_wayland_post_parse (ClutterBackend  *backend,
173                                     GError         **error)
174 {
175   ClutterBackendWayland *backend_wayland = CLUTTER_BACKEND_WAYLAND (backend);
176 
177   /* TODO: expose environment variable/commandline option for this... */
178   backend_wayland->wayland_display = _foreign_display;
179   if (backend_wayland->wayland_display == NULL)
180     backend_wayland->wayland_display = wl_display_connect (NULL);
181 
182   if (!backend_wayland->wayland_display)
183     {
184       g_set_error (error, CLUTTER_INIT_ERROR,
185                   CLUTTER_INIT_ERROR_BACKEND,
186                   "Failed to open Wayland display socket");
187       return FALSE;
188     }
189 
190   backend_wayland->wayland_registry =
191     wl_display_get_registry (backend_wayland->wayland_display);
192 
193   backend_wayland->wayland_source =
194     _clutter_event_source_wayland_new (backend_wayland->wayland_display);
195   g_source_attach (backend_wayland->wayland_source, NULL);
196 
197   g_object_set (clutter_settings_get_default (), "font-dpi", 96 * 1024, NULL);
198 
199   /* XXX: We require the device manager to exist as soon as we connect to the
200    * compositor and setup an event handler because we will immediately be
201    * notified of the available input devices which need to be associated with
202    * the device-manager.
203    *
204    * FIXME: At some point we could perhaps just collapse the
205    * _clutter_backend_post_parse(), and _clutter_backend_init_events()
206    * functions into one called something like _clutter_backend_init() which
207    * would allow the real backend to manage the precise order of
208    * initialization.
209    */
210   backend_wayland->device_manager =
211     _clutter_device_manager_wayland_new (backend);
212 
213   /* Set up listener so we'll catch all events. */
214   wl_registry_add_listener (backend_wayland->wayland_registry,
215                             &wayland_registry_listener,
216                             backend_wayland);
217 
218   /* Wait until we have been notified about the compositor and shell objects */
219   while (!(backend_wayland->wayland_compositor &&
220            backend_wayland->wayland_shell))
221     wl_display_roundtrip (backend_wayland->wayland_display);
222 
223   return TRUE;
224 }
225 
226 static CoglRenderer *
clutter_backend_wayland_get_renderer(ClutterBackend * backend,GError ** error)227 clutter_backend_wayland_get_renderer (ClutterBackend  *backend,
228                                       GError         **error)
229 {
230   ClutterBackendWayland *backend_wayland = CLUTTER_BACKEND_WAYLAND (backend);
231   CoglRenderer *renderer;
232 
233   CLUTTER_NOTE (BACKEND, "Creating a new wayland renderer");
234 
235   renderer = cogl_renderer_new ();
236 
237   cogl_wayland_renderer_set_event_dispatch_enabled (renderer, !_no_event_dispatch);
238   cogl_renderer_set_winsys_id (renderer, COGL_WINSYS_ID_EGL_WAYLAND);
239 
240   cogl_wayland_renderer_set_foreign_display (renderer,
241                                              backend_wayland->wayland_display);
242 
243   return renderer;
244 }
245 
246 static CoglDisplay *
clutter_backend_wayland_get_display(ClutterBackend * backend,CoglRenderer * renderer,CoglSwapChain * swap_chain,GError ** error)247 clutter_backend_wayland_get_display (ClutterBackend  *backend,
248                                      CoglRenderer    *renderer,
249                                      CoglSwapChain   *swap_chain,
250                                      GError         **error)
251 {
252   CoglOnscreenTemplate *onscreen_template = NULL;
253   CoglDisplay *display;
254 
255   onscreen_template = cogl_onscreen_template_new (swap_chain);
256 
257   /* XXX: I have some doubts that this is a good design.
258    * Conceptually should we be able to check an onscreen_template
259    * without more details about the CoglDisplay configuration?
260    */
261   if (!cogl_renderer_check_onscreen_template (renderer,
262                                               onscreen_template,
263                                               error))
264     goto error;
265 
266   display = cogl_display_new (renderer, onscreen_template);
267 
268   return display;
269 
270 error:
271   if (onscreen_template)
272     cogl_object_unref (onscreen_template);
273 
274   return NULL;
275 }
276 
277 static void
clutter_backend_wayland_class_init(ClutterBackendWaylandClass * klass)278 clutter_backend_wayland_class_init (ClutterBackendWaylandClass *klass)
279 {
280   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
281   ClutterBackendClass *backend_class = CLUTTER_BACKEND_CLASS (klass);
282 
283   gobject_class->dispose = clutter_backend_wayland_dispose;
284 
285   backend_class->stage_window_type = CLUTTER_TYPE_STAGE_WAYLAND;
286 
287   backend_class->post_parse = clutter_backend_wayland_post_parse;
288   backend_class->get_renderer = clutter_backend_wayland_get_renderer;
289   backend_class->get_display = clutter_backend_wayland_get_display;
290 }
291 
292 void
_clutter_backend_wayland_ensure_cursor(ClutterBackendWayland * backend_wayland)293 _clutter_backend_wayland_ensure_cursor (ClutterBackendWayland *backend_wayland)
294 {
295   struct wl_cursor *cursor;
296 
297   backend_wayland->cursor_theme =
298     wl_cursor_theme_load (NULL, /* default */
299                           32,
300                           backend_wayland->wayland_shm);
301 
302   cursor = wl_cursor_theme_get_cursor (backend_wayland->cursor_theme,
303                                        "left_ptr");
304 
305   backend_wayland->cursor_buffer =
306     wl_cursor_image_get_buffer (cursor->images[0]);
307 
308   if (backend_wayland->cursor_buffer)
309     {
310       backend_wayland->cursor_x = cursor->images[0]->hotspot_x;
311       backend_wayland->cursor_y = cursor->images[0]->hotspot_y;
312     }
313 
314   backend_wayland->cursor_surface =
315     wl_compositor_create_surface (backend_wayland->wayland_compositor);
316 }
317 
318 static void
clutter_backend_wayland_init(ClutterBackendWayland * backend_wayland)319 clutter_backend_wayland_init (ClutterBackendWayland *backend_wayland)
320 {
321 }
322 
323 ClutterBackend *
clutter_backend_wayland_new(void)324 clutter_backend_wayland_new (void)
325 {
326   return g_object_new (CLUTTER_TYPE_BACKEND_WAYLAND, NULL);
327 }
328 
329 /**
330  * clutter_wayland_set_display
331  * @display: pointer to a wayland display
332  *
333  * Sets the display connection Clutter should use; must be called
334  * before clutter_init(), clutter_init_with_args() or other functions
335  * pertaining Clutter's initialization process.
336  *
337  * If you are parsing the command line arguments by retrieving Clutter's
338  * #GOptionGroup with clutter_get_option_group() and calling
339  * g_option_context_parse() yourself, you should also call
340  * clutter_wayland_set_display() before g_option_context_parse().
341  *
342  * Since: 1.16
343  */
344 void
clutter_wayland_set_display(struct wl_display * display)345 clutter_wayland_set_display (struct wl_display *display)
346 {
347   if (_clutter_context_is_initialized ())
348     {
349       g_warning ("%s() can only be used before calling clutter_init()",
350                  G_STRFUNC);
351       return;
352     }
353 
354   _foreign_display = display;
355 }
356 
357 /**
358  * clutter_wayland_disable_event_retrieval:
359  *
360  * Disables the dispatch of the events in the main loop.
361  *
362  * This is useful for integrating Clutter with another library that will do the
363  * event dispatch; in general only a single source should be acting on changes
364  * on the Wayland file descriptor.
365  *
366  * This function can only be called before calling clutter_init().
367  *
368  * This function should not be normally used by applications.
369  *
370  * Since: 1.16
371  */
372 void
clutter_wayland_disable_event_retrieval(void)373 clutter_wayland_disable_event_retrieval (void)
374 {
375   if (_clutter_context_is_initialized ())
376     {
377       g_warning ("%s() can only be used before calling clutter_init()",
378                  G_STRFUNC);
379       return;
380     }
381 
382   _no_event_dispatch = TRUE;
383 }
384