SDL  2.0
SDL_waylandvideo.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2017 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 #include "SDL_waylandclipboard.h"
38 #include "SDL_waylandvulkan.h"
39 
40 #include <sys/types.h>
41 #include <unistd.h>
42 #include <fcntl.h>
43 #include <xkbcommon/xkbcommon.h>
44 
45 #include "SDL_waylanddyn.h"
46 #include <wayland-util.h>
47 
48 #define WAYLANDVID_DRIVER_NAME "wayland"
49 
50 /* Initialization/Query functions */
51 static int
52 Wayland_VideoInit(_THIS);
53 
54 static void
55 Wayland_GetDisplayModes(_THIS, SDL_VideoDisplay *sdl_display);
56 static int
57 Wayland_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode);
58 
59 static void
60 Wayland_VideoQuit(_THIS);
61 
62 /* Find out what class name we should use
63  * Based on src/video/x11/SDL_x11video.c */
64 static char *
65 get_classname()
66 {
67  char *spot;
68 #if defined(__LINUX__) || defined(__FREEBSD__)
69  char procfile[1024];
70  char linkfile[1024];
71  int linksize;
72 #endif
73 
74  /* First allow environment variable override */
75  spot = SDL_getenv("SDL_VIDEO_WAYLAND_WMCLASS");
76  if (spot) {
77  return SDL_strdup(spot);
78  } else {
79  /* Fallback to the "old" envvar */
80  spot = SDL_getenv("SDL_VIDEO_X11_WMCLASS");
81  if (spot) {
82  return SDL_strdup(spot);
83  }
84  }
85 
86  /* Next look at the application's executable name */
87 #if defined(__LINUX__) || defined(__FREEBSD__)
88 #if defined(__LINUX__)
89  SDL_snprintf(procfile, SDL_arraysize(procfile), "/proc/%d/exe", getpid());
90 #elif defined(__FREEBSD__)
91  SDL_snprintf(procfile, SDL_arraysize(procfile), "/proc/%d/file",
92  getpid());
93 #else
94 #error Where can we find the executable name?
95 #endif
96  linksize = readlink(procfile, linkfile, sizeof(linkfile) - 1);
97  if (linksize > 0) {
98  linkfile[linksize] = '\0';
99  spot = SDL_strrchr(linkfile, '/');
100  if (spot) {
101  return SDL_strdup(spot + 1);
102  } else {
103  return SDL_strdup(linkfile);
104  }
105  }
106 #endif /* __LINUX__ || __FREEBSD__ */
107 
108  /* Finally use the default we've used forever */
109  return SDL_strdup("SDL_App");
110 }
111 
112 /* Wayland driver bootstrap functions */
113 static int
114 Wayland_Available(void)
115 {
116  struct wl_display *display = NULL;
117  if (SDL_WAYLAND_LoadSymbols()) {
118  display = WAYLAND_wl_display_connect(NULL);
119  if (display != NULL) {
120  WAYLAND_wl_display_disconnect(display);
121  }
123  }
124 
125  return (display != NULL);
126 }
127 
128 static void
129 Wayland_DeleteDevice(SDL_VideoDevice *device)
130 {
131  SDL_free(device);
133 }
134 
135 static SDL_VideoDevice *
136 Wayland_CreateDevice(int devindex)
137 {
139 
140  if (!SDL_WAYLAND_LoadSymbols()) {
141  return NULL;
142  }
143 
144  /* Initialize all variables that we clean on shutdown */
145  device = SDL_calloc(1, sizeof(SDL_VideoDevice));
146  if (!device) {
148  SDL_OutOfMemory();
149  return NULL;
150  }
151 
152  /* Set the function pointers */
153  device->VideoInit = Wayland_VideoInit;
154  device->VideoQuit = Wayland_VideoQuit;
155  device->SetDisplayMode = Wayland_SetDisplayMode;
156  device->GetDisplayModes = Wayland_GetDisplayModes;
158 
159  device->PumpEvents = Wayland_PumpEvents;
160 
170 
172  device->ShowWindow = Wayland_ShowWindow;
180 
184 
185 #if SDL_VIDEO_VULKAN
186  device->Vulkan_LoadLibrary = Wayland_Vulkan_LoadLibrary;
187  device->Vulkan_UnloadLibrary = Wayland_Vulkan_UnloadLibrary;
188  device->Vulkan_GetInstanceExtensions = Wayland_Vulkan_GetInstanceExtensions;
189  device->Vulkan_CreateSurface = Wayland_Vulkan_CreateSurface;
190 #endif
191 
192  device->free = Wayland_DeleteDevice;
193 
194  return device;
195 }
196 
198  WAYLANDVID_DRIVER_NAME, "SDL Wayland video driver",
199  Wayland_Available, Wayland_CreateDevice
200 };
201 
202 static void
203 display_handle_geometry(void *data,
204  struct wl_output *output,
205  int x, int y,
206  int physical_width,
207  int physical_height,
208  int subpixel,
209  const char *make,
210  const char *model,
211  int transform)
212 
213 {
214  SDL_VideoDisplay *display = data;
215 
216  display->name = SDL_strdup(model);
217  display->driverdata = output;
218 }
219 
220 static void
221 display_handle_mode(void *data,
222  struct wl_output *output,
223  uint32_t flags,
224  int width,
225  int height,
226  int refresh)
227 {
228  SDL_VideoDisplay *display = data;
230 
231  SDL_zero(mode);
233  mode.w = width;
234  mode.h = height;
235  mode.refresh_rate = refresh / 1000; // mHz to Hz
236  SDL_AddDisplayMode(display, &mode);
237 
238  if (flags & WL_OUTPUT_MODE_CURRENT) {
239  display->current_mode = mode;
240  display->desktop_mode = mode;
241  }
242 }
243 
244 static void
245 display_handle_done(void *data,
246  struct wl_output *output)
247 {
248  SDL_VideoDisplay *display = data;
249  SDL_AddVideoDisplay(display);
250  SDL_free(display->name);
251  SDL_free(display);
252 }
253 
254 static void
255 display_handle_scale(void *data,
256  struct wl_output *output,
257  int32_t factor)
258 {
259  // TODO: do HiDPI stuff.
260 }
261 
262 static const struct wl_output_listener output_listener = {
263  display_handle_geometry,
264  display_handle_mode,
265  display_handle_done,
266  display_handle_scale
267 };
268 
269 static void
270 Wayland_add_display(SDL_VideoData *d, uint32_t id)
271 {
272  struct wl_output *output;
273  SDL_VideoDisplay *display = SDL_malloc(sizeof *display);
274  if (!display) {
275  SDL_OutOfMemory();
276  return;
277  }
278  SDL_zero(*display);
279 
280  output = wl_registry_bind(d->registry, id, &wl_output_interface, 2);
281  if (!output) {
282  SDL_SetError("Failed to retrieve output.");
283  SDL_free(display);
284  return;
285  }
286 
287  wl_output_add_listener(output, &output_listener, display);
288 }
289 
290 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
291 static void
292 windowmanager_hints(void *data, struct qt_windowmanager *qt_windowmanager,
293  int32_t show_is_fullscreen)
294 {
295 }
296 
297 static void
298 windowmanager_quit(void *data, struct qt_windowmanager *qt_windowmanager)
299 {
300  SDL_SendQuit();
301 }
302 
303 static const struct qt_windowmanager_listener windowmanager_listener = {
304  windowmanager_hints,
305  windowmanager_quit,
306 };
307 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
308 
309 static void
310 display_handle_global(void *data, struct wl_registry *registry, uint32_t id,
311  const char *interface, uint32_t version)
312 {
313  SDL_VideoData *d = data;
314 
315  if (strcmp(interface, "wl_compositor") == 0) {
317  } else if (strcmp(interface, "wl_output") == 0) {
318  Wayland_add_display(d, id);
319  } else if (strcmp(interface, "wl_seat") == 0) {
321  } else if (strcmp(interface, "wl_shell") == 0) {
323  } else if (strcmp(interface, "wl_shm") == 0) {
324  d->shm = wl_registry_bind(registry, id, &wl_shm_interface, 1);
325  d->cursor_theme = WAYLAND_wl_cursor_theme_load(NULL, 32, d->shm);
326  } else if (strcmp(interface, "zwp_relative_pointer_manager_v1") == 0) {
328  } else if (strcmp(interface, "zwp_pointer_constraints_v1") == 0) {
330  } else if (strcmp(interface, "wl_data_device_manager") == 0) {
332 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
333  } else if (strcmp(interface, "qt_touch_extension") == 0) {
334  Wayland_touch_create(d, id);
335  } else if (strcmp(interface, "qt_surface_extension") == 0) {
336  d->surface_extension = wl_registry_bind(registry, id,
337  &qt_surface_extension_interface, 1);
338  } else if (strcmp(interface, "qt_windowmanager") == 0) {
339  d->windowmanager = wl_registry_bind(registry, id,
340  &qt_windowmanager_interface, 1);
341  qt_windowmanager_add_listener(d->windowmanager, &windowmanager_listener, d);
342 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
343  }
344 }
345 
346 static const struct wl_registry_listener registry_listener = {
347  display_handle_global,
348  NULL, /* global_remove */
349 };
350 
351 int
352 Wayland_VideoInit(_THIS)
353 {
354  SDL_VideoData *data = SDL_malloc(sizeof *data);
355  if (data == NULL)
356  return SDL_OutOfMemory();
357  memset(data, 0, sizeof *data);
358 
359  _this->driverdata = data;
360 
361  data->xkb_context = WAYLAND_xkb_context_new(0);
362  if (!data->xkb_context) {
363  return SDL_SetError("Failed to create XKB context");
364  }
365 
366  data->display = WAYLAND_wl_display_connect(NULL);
367  if (data->display == NULL) {
368  return SDL_SetError("Failed to connect to a Wayland display");
369  }
370 
372  if (data->registry == NULL) {
373  return SDL_SetError("Failed to get the Wayland registry");
374  }
375 
376  wl_registry_add_listener(data->registry, &registry_listener, data);
377 
378  // First roundtrip to receive all registry objects.
379  WAYLAND_wl_display_roundtrip(data->display);
380 
381  // Second roundtrip to receive all output events.
382  WAYLAND_wl_display_roundtrip(data->display);
383 
384  Wayland_InitMouse();
385 
386  /* Get the surface class name, usually the name of the application */
387  data->classname = get_classname();
388 
389  WAYLAND_wl_display_flush(data->display);
390 
391  return 0;
392 }
393 
394 static void
395 Wayland_GetDisplayModes(_THIS, SDL_VideoDisplay *sdl_display)
396 {
397  // Nothing to do here, everything was already done in the wl_output
398  // callbacks.
399 }
400 
401 static int
402 Wayland_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode)
403 {
404  return SDL_Unsupported();
405 }
406 
407 void
408 Wayland_VideoQuit(_THIS)
409 {
410  SDL_VideoData *data = _this->driverdata;
411  int i;
412 
413  Wayland_FiniMouse ();
414 
415  for (i = 0; i < _this->num_displays; ++i) {
416  SDL_VideoDisplay *display = &_this->displays[i];
417  wl_output_destroy(display->driverdata);
418  display->driverdata = NULL;
419  }
420 
424 
425  if (data->xkb_context) {
426  WAYLAND_xkb_context_unref(data->xkb_context);
427  data->xkb_context = NULL;
428  }
429 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
430  if (data->windowmanager)
431  qt_windowmanager_destroy(data->windowmanager);
432 
433  if (data->surface_extension)
434  qt_surface_extension_destroy(data->surface_extension);
435 
436  Wayland_touch_destroy(data);
437 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
438 
439  if (data->shm)
440  wl_shm_destroy(data->shm);
441 
442  if (data->cursor_theme)
443  WAYLAND_wl_cursor_theme_destroy(data->cursor_theme);
444 
445  if (data->shell)
446  wl_shell_destroy(data->shell);
447 
448  if (data->compositor)
450 
451  if (data->registry)
453 
454  if (data->display) {
455  WAYLAND_wl_display_flush(data->display);
456  WAYLAND_wl_display_disconnect(data->display);
457  }
458 
459  SDL_free(data->classname);
460  SDL_free(data);
461  _this->driverdata = NULL;
462 }
463 
464 #endif /* SDL_VIDEO_DRIVER_WAYLAND */
465 
466 /* vi: set ts=4 sw=4 expandtab: */
int(* Vulkan_LoadLibrary)(_THIS, const char *path)
Definition: SDL_sysvideo.h:270
static void wl_registry_destroy(struct wl_registry *wl_registry)
static struct wl_registry * wl_display_get_registry(struct wl_display *wl_display)
void Wayland_display_destroy_pointer_constraints(SDL_VideoData *d)
void Wayland_SetWindowSize(_THIS, SDL_Window *window)
void(* RestoreWindow)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:227
const struct wl_interface wl_shm_interface
void Wayland_SetWindowFullscreen(_THIS, SDL_Window *window, SDL_VideoDisplay *_display, SDL_bool fullscreen)
void Wayland_display_destroy_relative_pointer_manager(SDL_VideoData *d)
void SDL_WAYLAND_UnloadSymbols(void)
char * Wayland_GetClipboardText(_THIS)
int(* SetWindowHitTest)(SDL_Window *window, SDL_bool enabled)
Definition: SDL_sysvideo.h:305
GLint GLint GLint GLint GLint x
Definition: SDL_opengl.h:1574
signed int int32_t
int Wayland_SetWindowHitTest(SDL_Window *window, SDL_bool enabled)
struct wl_display * display
void Wayland_MaximizeWindow(_THIS, SDL_Window *window)
VideoBootStrap Wayland_bootstrap
void Wayland_GLES_DeleteContext(_THIS, SDL_GLContext context)
void(* free)(_THIS)
Definition: SDL_sysvideo.h:390
void Wayland_display_add_pointer_constraints(SDL_VideoData *d, uint32_t id)
int Wayland_SetClipboardText(_THIS, const char *text)
static void wl_shell_destroy(struct wl_shell *wl_shell)
#define Wayland_GLES_UnloadLibrary
static void * wl_registry_bind(struct wl_registry *wl_registry, uint32_t name, const struct wl_interface *interface, uint32_t version)
int(* GL_SetSwapInterval)(_THIS, int interval)
Definition: SDL_sysvideo.h:260
void(* ShowWindow)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:222
The structure that defines a display mode.
Definition: SDL_video.h:53
#define memset
Definition: SDL_malloc.c:619
void Wayland_ShowWindow(_THIS, SDL_Window *window)
void(* SetWindowSize)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:215
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
#define Wayland_GLES_GetSwapInterval
struct wl_cursor_theme * cursor_theme
int Wayland_CreateWindow(_THIS, SDL_Window *window)
const struct wl_interface wl_output_interface
int SDL_AddVideoDisplay(const SDL_VideoDisplay *display)
Definition: SDL_video.c:606
int(* GL_LoadLibrary)(_THIS, const char *path)
Definition: SDL_sysvideo.h:254
static void wl_compositor_destroy(struct wl_compositor *wl_compositor)
GLint GLint GLsizei width
Definition: SDL_opengl.h:1572
int(* SetDisplayMode)(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode)
Definition: SDL_sysvideo.h:204
SDL_GLContext Wayland_GLES_CreateContext(_THIS, SDL_Window *window)
void Wayland_display_add_relative_pointer_manager(SDL_VideoData *d, uint32_t id)
static SDL_VideoDevice * _this
Definition: SDL_video.c:121
static SDL_AudioDeviceID device
Definition: loopwave.c:37
SDL_bool(* Vulkan_GetInstanceExtensions)(_THIS, SDL_Window *window, unsigned *count, const char **names)
Definition: SDL_sysvideo.h:272
#define Wayland_GLES_SetSwapInterval
SDL_bool(* GetWindowWMInfo)(_THIS, SDL_Window *window, struct SDL_SysWMinfo *info)
Definition: SDL_sysvideo.h:247
SDL_GLContext(* GL_CreateContext)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:257
struct wl_data_device_manager * data_device_manager
void Wayland_SetWindowTitle(_THIS, SDL_Window *window)
void Wayland_DestroyWindow(_THIS, SDL_Window *window)
SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char const char SDL_SCANF_FORMAT_STRING const char return SDL_ThreadFunction const char void return Uint32 return Uint32 SDL_AssertionHandler void SDL_SpinLock SDL_atomic_t int int return SDL_atomic_t return void void void return void return int return SDL_AudioSpec SDL_AudioSpec return int int return return int SDL_RWops int SDL_AudioSpec Uint8 ** d
SDL_bool(* Vulkan_CreateSurface)(_THIS, SDL_Window *window, VkInstance instance, VkSurfaceKHR *surface)
Definition: SDL_sysvideo.h:273
#define _THIS
int(* GL_MakeCurrent)(_THIS, SDL_Window *window, SDL_GLContext context)
Definition: SDL_sysvideo.h:258
#define SDL_free
void(* Vulkan_UnloadLibrary)(_THIS)
Definition: SDL_sysvideo.h:271
static void wl_shm_destroy(struct wl_shm *wl_shm)
struct xkb_context * xkb_context
int SDL_WAYLAND_LoadSymbols(void)
SDL_bool Wayland_GetWindowWMInfo(_THIS, SDL_Window *window, SDL_SysWMinfo *info)
struct wl_shell * shell
SDL_DisplayMode current_mode
Definition: SDL_sysvideo.h:132
GLenum mode
SDL_VideoDisplay * displays
Definition: SDL_sysvideo.h:312
void(* DestroyWindow)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:234
#define SDL_zero(x)
Definition: SDL_stdinc.h:416
GLint GLint GLint GLint GLint GLint y
Definition: SDL_opengl.h:1574
int Wayland_GLES_MakeCurrent(_THIS, SDL_Window *window, SDL_GLContext context)
void Wayland_display_add_input(SDL_VideoData *d, uint32_t id)
#define SDL_getenv
void(* GL_UnloadLibrary)(_THIS)
Definition: SDL_sysvideo.h:256
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Definition: SDL_x11sym.h:50
void(* GetDisplayModes)(_THIS, SDL_VideoDisplay *display)
Definition: SDL_sysvideo.h:196
int Wayland_GLES_SwapWindow(_THIS, SDL_Window *window)
#define Wayland_GLES_GetProcAddress
static int wl_output_add_listener(struct wl_output *wl_output, const struct wl_output_listener *listener, void *data)
static void wl_output_destroy(struct wl_output *wl_output)
#define NULL
Definition: begin_code.h:164
int(* CreateSDLWindow)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:210
#define SDL_OutOfMemory()
Definition: SDL_error.h:52
SDL_DisplayMode desktop_mode
Definition: SDL_sysvideo.h:131
unsigned int uint32_t
void(* VideoQuit)(_THIS)
Definition: SDL_sysvideo.h:166
#define SDL_SetError
const struct wl_interface wl_compositor_interface
GLbitfield flags
struct wl_compositor * compositor
GLint GLint GLsizei GLsizei height
Definition: SDL_opengl.h:1572
void Wayland_RestoreWindow(_THIS, SDL_Window *window)
#define SDL_calloc
const struct wl_interface wl_shell_interface
void Wayland_display_destroy_input(SDL_VideoData *d)
void Wayland_PumpEvents(_THIS)
int(* GL_SwapWindow)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:262
#define SDL_strdup
GLuint GLenum GLenum transform
struct wl_registry * registry
SDL_bool Wayland_HasClipboardText(_THIS)
SDL_bool(* HasClipboardText)(_THIS)
Definition: SDL_sysvideo.h:299
SDL_bool SDL_AddDisplayMode(SDL_VideoDisplay *display, const SDL_DisplayMode *mode)
Definition: SDL_video.c:743
int(* VideoInit)(_THIS)
Definition: SDL_sysvideo.h:160
#define SDL_snprintf
#define SDL_arraysize(array)
Definition: SDL_stdinc.h:93
void(* SetWindowFullscreen)(_THIS, SDL_Window *window, SDL_VideoDisplay *display, SDL_bool fullscreen)
Definition: SDL_sysvideo.h:230
#define SDL_malloc
void(* GL_DeleteContext)(_THIS, SDL_GLContext context)
Definition: SDL_sysvideo.h:263
char *(* GetClipboardText)(_THIS)
Definition: SDL_sysvideo.h:298
Uint32 format
Definition: SDL_video.h:55
void(* SetWindowTitle)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:212
const struct wl_interface wl_data_device_manager_interface
int(* GL_GetSwapInterval)(_THIS)
Definition: SDL_sysvideo.h:261
void(* MaximizeWindow)(_THIS, SDL_Window *window)
Definition: SDL_sysvideo.h:225
int(* SetClipboardText)(_THIS, const char *text)
Definition: SDL_sysvideo.h:297
int Wayland_GLES_LoadLibrary(_THIS, const char *path)
struct wl_shm * shm
#define SDL_strrchr
#define SDL_Unsupported()
Definition: SDL_error.h:53
void *(* GL_GetProcAddress)(_THIS, const char *proc)
Definition: SDL_sysvideo.h:255
int SDL_SendQuit(void)
Definition: SDL_quit.c:137
static int wl_registry_add_listener(struct wl_registry *wl_registry, const struct wl_registry_listener *listener, void *data)
void(* PumpEvents)(_THIS)
Definition: SDL_sysvideo.h:280