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