1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20 */
21
22 #include "../../SDL_internal.h"
23
24 #if SDL_VIDEO_DRIVER_WAYLAND
25
26 #include "SDL_video.h"
27 #include "SDL_mouse.h"
28 #include "SDL_stdinc.h"
29 #include "../../events/SDL_events_c.h"
30
31 #include "SDL_waylandvideo.h"
32 #include "SDL_waylandevents_c.h"
33 #include "SDL_waylandwindow.h"
34 #include "SDL_waylandopengles.h"
35 #include "SDL_waylandmouse.h"
36 #include "SDL_waylandtouch.h"
37
38 #include <sys/types.h>
39 #include <unistd.h>
40 #include <fcntl.h>
41 #include <xkbcommon/xkbcommon.h>
42
43 #include "SDL_waylanddyn.h"
44 #include <wayland-util.h>
45
46 #define WAYLANDVID_DRIVER_NAME "wayland"
47
48 /* Initialization/Query functions */
49 static int
50 Wayland_VideoInit(_THIS);
51
52 static void
53 Wayland_GetDisplayModes(_THIS, SDL_VideoDisplay *sdl_display);
54 static int
55 Wayland_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode);
56
57 static void
58 Wayland_VideoQuit(_THIS);
59
60 /* Find out what class name we should use
61 * Based on src/video/x11/SDL_x11video.c */
62 static char *
get_classname()63 get_classname()
64 {
65 char *spot;
66 #if defined(__LINUX__) || defined(__FREEBSD__)
67 char procfile[1024];
68 char linkfile[1024];
69 int linksize;
70 #endif
71
72 /* First allow environment variable override */
73 spot = SDL_getenv("SDL_VIDEO_WAYLAND_WMCLASS");
74 if (spot) {
75 return SDL_strdup(spot);
76 } else {
77 /* Fallback to the "old" envvar */
78 spot = SDL_getenv("SDL_VIDEO_X11_WMCLASS");
79 if (spot) {
80 return SDL_strdup(spot);
81 }
82 }
83
84 /* Next look at the application's executable name */
85 #if defined(__LINUX__) || defined(__FREEBSD__)
86 #if defined(__LINUX__)
87 SDL_snprintf(procfile, SDL_arraysize(procfile), "/proc/%d/exe", getpid());
88 #elif defined(__FREEBSD__)
89 SDL_snprintf(procfile, SDL_arraysize(procfile), "/proc/%d/file",
90 getpid());
91 #else
92 #error Where can we find the executable name?
93 #endif
94 linksize = readlink(procfile, linkfile, sizeof(linkfile) - 1);
95 if (linksize > 0) {
96 linkfile[linksize] = '\0';
97 spot = SDL_strrchr(linkfile, '/');
98 if (spot) {
99 return SDL_strdup(spot + 1);
100 } else {
101 return SDL_strdup(linkfile);
102 }
103 }
104 #endif /* __LINUX__ || __FREEBSD__ */
105
106 /* Finally use the default we've used forever */
107 return SDL_strdup("SDL_App");
108 }
109
110 /* Wayland driver bootstrap functions */
111 static int
Wayland_Available(void)112 Wayland_Available(void)
113 {
114 struct wl_display *display = NULL;
115 if (SDL_WAYLAND_LoadSymbols()) {
116 display = WAYLAND_wl_display_connect(NULL);
117 if (display != NULL) {
118 WAYLAND_wl_display_disconnect(display);
119 }
120 SDL_WAYLAND_UnloadSymbols();
121 }
122
123 return (display != NULL);
124 }
125
126 static void
Wayland_DeleteDevice(SDL_VideoDevice * device)127 Wayland_DeleteDevice(SDL_VideoDevice *device)
128 {
129 SDL_free(device);
130 SDL_WAYLAND_UnloadSymbols();
131 }
132
133 static SDL_VideoDevice *
Wayland_CreateDevice(int devindex)134 Wayland_CreateDevice(int devindex)
135 {
136 SDL_VideoDevice *device;
137
138 if (!SDL_WAYLAND_LoadSymbols()) {
139 return NULL;
140 }
141
142 /* Initialize all variables that we clean on shutdown */
143 device = SDL_calloc(1, sizeof(SDL_VideoDevice));
144 if (!device) {
145 SDL_WAYLAND_UnloadSymbols();
146 SDL_OutOfMemory();
147 return NULL;
148 }
149
150 /* Set the function pointers */
151 device->VideoInit = Wayland_VideoInit;
152 device->VideoQuit = Wayland_VideoQuit;
153 device->SetDisplayMode = Wayland_SetDisplayMode;
154 device->GetDisplayModes = Wayland_GetDisplayModes;
155 device->GetWindowWMInfo = Wayland_GetWindowWMInfo;
156
157 device->PumpEvents = Wayland_PumpEvents;
158
159 device->GL_SwapWindow = Wayland_GLES_SwapWindow;
160 device->GL_GetSwapInterval = Wayland_GLES_GetSwapInterval;
161 device->GL_SetSwapInterval = Wayland_GLES_SetSwapInterval;
162 device->GL_MakeCurrent = Wayland_GLES_MakeCurrent;
163 device->GL_CreateContext = Wayland_GLES_CreateContext;
164 device->GL_LoadLibrary = Wayland_GLES_LoadLibrary;
165 device->GL_UnloadLibrary = Wayland_GLES_UnloadLibrary;
166 device->GL_GetProcAddress = Wayland_GLES_GetProcAddress;
167 device->GL_DeleteContext = Wayland_GLES_DeleteContext;
168
169 device->CreateWindow = Wayland_CreateWindow;
170 device->ShowWindow = Wayland_ShowWindow;
171 device->SetWindowFullscreen = Wayland_SetWindowFullscreen;
172 device->MaximizeWindow = Wayland_MaximizeWindow;
173 device->RestoreWindow = Wayland_RestoreWindow;
174 device->SetWindowSize = Wayland_SetWindowSize;
175 device->SetWindowTitle = Wayland_SetWindowTitle;
176 device->DestroyWindow = Wayland_DestroyWindow;
177 device->SetWindowHitTest = Wayland_SetWindowHitTest;
178
179 device->free = Wayland_DeleteDevice;
180
181 return device;
182 }
183
184 VideoBootStrap Wayland_bootstrap = {
185 WAYLANDVID_DRIVER_NAME, "SDL Wayland video driver",
186 Wayland_Available, Wayland_CreateDevice
187 };
188
189 static void
display_handle_geometry(void * data,struct wl_output * output,int x,int y,int physical_width,int physical_height,int subpixel,const char * make,const char * model,int transform)190 display_handle_geometry(void *data,
191 struct wl_output *output,
192 int x, int y,
193 int physical_width,
194 int physical_height,
195 int subpixel,
196 const char *make,
197 const char *model,
198 int transform)
199
200 {
201 SDL_VideoDisplay *display = data;
202
203 display->name = SDL_strdup(model);
204 display->driverdata = output;
205 }
206
207 static void
display_handle_mode(void * data,struct wl_output * output,uint32_t flags,int width,int height,int refresh)208 display_handle_mode(void *data,
209 struct wl_output *output,
210 uint32_t flags,
211 int width,
212 int height,
213 int refresh)
214 {
215 SDL_VideoDisplay *display = data;
216 SDL_DisplayMode mode;
217
218 SDL_zero(mode);
219 mode.w = width;
220 mode.h = height;
221 mode.refresh_rate = refresh / 1000; // mHz to Hz
222 SDL_AddDisplayMode(display, &mode);
223
224 if (flags & WL_OUTPUT_MODE_CURRENT) {
225 display->current_mode = mode;
226 display->desktop_mode = mode;
227 }
228 }
229
230 static void
display_handle_done(void * data,struct wl_output * output)231 display_handle_done(void *data,
232 struct wl_output *output)
233 {
234 SDL_VideoDisplay *display = data;
235 SDL_AddVideoDisplay(display);
236 SDL_free(display->name);
237 SDL_free(display);
238 }
239
240 static void
display_handle_scale(void * data,struct wl_output * output,int32_t factor)241 display_handle_scale(void *data,
242 struct wl_output *output,
243 int32_t factor)
244 {
245 // TODO: do HiDPI stuff.
246 }
247
248 static const struct wl_output_listener output_listener = {
249 display_handle_geometry,
250 display_handle_mode,
251 display_handle_done,
252 display_handle_scale
253 };
254
255 static void
Wayland_add_display(SDL_VideoData * d,uint32_t id)256 Wayland_add_display(SDL_VideoData *d, uint32_t id)
257 {
258 struct wl_output *output;
259 SDL_VideoDisplay *display = SDL_malloc(sizeof *display);
260 if (!display) {
261 SDL_OutOfMemory();
262 return;
263 }
264 SDL_zero(*display);
265
266 output = wl_registry_bind(d->registry, id, &wl_output_interface, 2);
267 if (!output) {
268 SDL_SetError("Failed to retrieve output.");
269 return;
270 }
271
272 wl_output_add_listener(output, &output_listener, display);
273 }
274
275 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
276 static void
windowmanager_hints(void * data,struct qt_windowmanager * qt_windowmanager,int32_t show_is_fullscreen)277 windowmanager_hints(void *data, struct qt_windowmanager *qt_windowmanager,
278 int32_t show_is_fullscreen)
279 {
280 }
281
282 static void
windowmanager_quit(void * data,struct qt_windowmanager * qt_windowmanager)283 windowmanager_quit(void *data, struct qt_windowmanager *qt_windowmanager)
284 {
285 SDL_SendQuit();
286 }
287
288 static const struct qt_windowmanager_listener windowmanager_listener = {
289 windowmanager_hints,
290 windowmanager_quit,
291 };
292 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
293
294 static void
display_handle_global(void * data,struct wl_registry * registry,uint32_t id,const char * interface,uint32_t version)295 display_handle_global(void *data, struct wl_registry *registry, uint32_t id,
296 const char *interface, uint32_t version)
297 {
298 SDL_VideoData *d = data;
299
300 if (strcmp(interface, "wl_compositor") == 0) {
301 d->compositor = wl_registry_bind(d->registry, id, &wl_compositor_interface, 1);
302 } else if (strcmp(interface, "wl_output") == 0) {
303 Wayland_add_display(d, id);
304 } else if (strcmp(interface, "wl_seat") == 0) {
305 Wayland_display_add_input(d, id);
306 } else if (strcmp(interface, "wl_shell") == 0) {
307 d->shell = wl_registry_bind(d->registry, id, &wl_shell_interface, 1);
308 } else if (strcmp(interface, "wl_shm") == 0) {
309 d->shm = wl_registry_bind(registry, id, &wl_shm_interface, 1);
310 d->cursor_theme = WAYLAND_wl_cursor_theme_load(NULL, 32, d->shm);
311 } else if (strcmp(interface, "zwp_relative_pointer_manager_v1") == 0) {
312 Wayland_display_add_relative_pointer_manager(d, id);
313 } else if (strcmp(interface, "zwp_pointer_constraints_v1") == 0) {
314 Wayland_display_add_pointer_constraints(d, id);
315 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
316 } else if (strcmp(interface, "qt_touch_extension") == 0) {
317 Wayland_touch_create(d, id);
318 } else if (strcmp(interface, "qt_surface_extension") == 0) {
319 d->surface_extension = wl_registry_bind(registry, id,
320 &qt_surface_extension_interface, 1);
321 } else if (strcmp(interface, "qt_windowmanager") == 0) {
322 d->windowmanager = wl_registry_bind(registry, id,
323 &qt_windowmanager_interface, 1);
324 qt_windowmanager_add_listener(d->windowmanager, &windowmanager_listener, d);
325 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
326 }
327 }
328
329 static const struct wl_registry_listener registry_listener = {
330 display_handle_global
331 };
332
333 int
Wayland_VideoInit(_THIS)334 Wayland_VideoInit(_THIS)
335 {
336 SDL_VideoData *data = SDL_malloc(sizeof *data);
337 if (data == NULL)
338 return SDL_OutOfMemory();
339 memset(data, 0, sizeof *data);
340
341 _this->driverdata = data;
342
343 data->xkb_context = WAYLAND_xkb_context_new(0);
344 if (!data->xkb_context) {
345 return SDL_SetError("Failed to create XKB context");
346 }
347
348 data->display = WAYLAND_wl_display_connect(NULL);
349 if (data->display == NULL) {
350 return SDL_SetError("Failed to connect to a Wayland display");
351 }
352
353 data->registry = wl_display_get_registry(data->display);
354 if (data->registry == NULL) {
355 return SDL_SetError("Failed to get the Wayland registry");
356 }
357
358 wl_registry_add_listener(data->registry, ®istry_listener, data);
359
360 // First roundtrip to receive all registry objects.
361 WAYLAND_wl_display_roundtrip(data->display);
362
363 // Second roundtrip to receive all output events.
364 WAYLAND_wl_display_roundtrip(data->display);
365
366 Wayland_InitMouse();
367
368 /* Get the surface class name, usually the name of the application */
369 data->classname = get_classname();
370
371 WAYLAND_wl_display_flush(data->display);
372
373 return 0;
374 }
375
376 static void
Wayland_GetDisplayModes(_THIS,SDL_VideoDisplay * sdl_display)377 Wayland_GetDisplayModes(_THIS, SDL_VideoDisplay *sdl_display)
378 {
379 // Nothing to do here, everything was already done in the wl_output
380 // callbacks.
381 }
382
383 static int
Wayland_SetDisplayMode(_THIS,SDL_VideoDisplay * display,SDL_DisplayMode * mode)384 Wayland_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode)
385 {
386 return SDL_Unsupported();
387 }
388
389 void
Wayland_VideoQuit(_THIS)390 Wayland_VideoQuit(_THIS)
391 {
392 SDL_VideoData *data = _this->driverdata;
393 int i;
394
395 Wayland_FiniMouse ();
396
397 for (i = 0; i < _this->num_displays; ++i) {
398 SDL_VideoDisplay *display = &_this->displays[i];
399 wl_output_destroy(display->driverdata);
400 display->driverdata = NULL;
401 }
402
403 Wayland_display_destroy_input(data);
404 Wayland_display_destroy_pointer_constraints(data);
405 Wayland_display_destroy_relative_pointer_manager(data);
406
407 if (data->xkb_context) {
408 WAYLAND_xkb_context_unref(data->xkb_context);
409 data->xkb_context = NULL;
410 }
411 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
412 if (data->windowmanager)
413 qt_windowmanager_destroy(data->windowmanager);
414
415 if (data->surface_extension)
416 qt_surface_extension_destroy(data->surface_extension);
417
418 Wayland_touch_destroy(data);
419 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
420
421 if (data->shm)
422 wl_shm_destroy(data->shm);
423
424 if (data->cursor_theme)
425 WAYLAND_wl_cursor_theme_destroy(data->cursor_theme);
426
427 if (data->shell)
428 wl_shell_destroy(data->shell);
429
430 if (data->compositor)
431 wl_compositor_destroy(data->compositor);
432
433 if (data->registry)
434 wl_registry_destroy(data->registry);
435
436 if (data->display) {
437 WAYLAND_wl_display_flush(data->display);
438 WAYLAND_wl_display_disconnect(data->display);
439 }
440
441 SDL_free(data->classname);
442 free(data);
443 _this->driverdata = NULL;
444 }
445
446 #endif /* SDL_VIDEO_DRIVER_WAYLAND */
447
448 /* vi: set ts=4 sw=4 expandtab: */
449