SDL_waylandvideo.c (12284B)
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2014 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 <fcntl.h> 39#include <xkbcommon/xkbcommon.h> 40 41#include "SDL_waylanddyn.h" 42#include <wayland-util.h> 43 44#define WAYLANDVID_DRIVER_NAME "wayland" 45 46struct wayland_mode { 47 SDL_DisplayMode mode; 48 struct wl_list link; 49}; 50 51/* Initialization/Query functions */ 52static int 53Wayland_VideoInit(_THIS); 54 55static void 56Wayland_GetDisplayModes(_THIS, SDL_VideoDisplay *sdl_display); 57static int 58Wayland_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode); 59 60static void 61Wayland_VideoQuit(_THIS); 62 63/* Wayland driver bootstrap functions */ 64static int 65Wayland_Available(void) 66{ 67 struct wl_display *display = NULL; 68 if (SDL_WAYLAND_LoadSymbols()) { 69 display = WAYLAND_wl_display_connect(NULL); 70 if (display != NULL) { 71 WAYLAND_wl_display_disconnect(display); 72 } 73 SDL_WAYLAND_UnloadSymbols(); 74 } 75 76 return (display != NULL); 77} 78 79static void 80Wayland_DeleteDevice(SDL_VideoDevice *device) 81{ 82 SDL_free(device); 83 SDL_WAYLAND_UnloadSymbols(); 84} 85 86static SDL_VideoDevice * 87Wayland_CreateDevice(int devindex) 88{ 89 SDL_VideoDevice *device; 90 91 if (!SDL_WAYLAND_LoadSymbols()) { 92 return NULL; 93 } 94 95 /* Initialize all variables that we clean on shutdown */ 96 device = SDL_calloc(1, sizeof(SDL_VideoDevice)); 97 if (!device) { 98 SDL_WAYLAND_UnloadSymbols(); 99 SDL_OutOfMemory(); 100 return NULL; 101 } 102 103 /* Set the function pointers */ 104 device->VideoInit = Wayland_VideoInit; 105 device->VideoQuit = Wayland_VideoQuit; 106 device->SetDisplayMode = Wayland_SetDisplayMode; 107 device->GetDisplayModes = Wayland_GetDisplayModes; 108 device->GetWindowWMInfo = Wayland_GetWindowWMInfo; 109 110 device->PumpEvents = Wayland_PumpEvents; 111 112 device->GL_SwapWindow = Wayland_GLES_SwapWindow; 113 device->GL_GetSwapInterval = Wayland_GLES_GetSwapInterval; 114 device->GL_SetSwapInterval = Wayland_GLES_SetSwapInterval; 115 device->GL_MakeCurrent = Wayland_GLES_MakeCurrent; 116 device->GL_CreateContext = Wayland_GLES_CreateContext; 117 device->GL_LoadLibrary = Wayland_GLES_LoadLibrary; 118 device->GL_UnloadLibrary = Wayland_GLES_UnloadLibrary; 119 device->GL_GetProcAddress = Wayland_GLES_GetProcAddress; 120 device->GL_DeleteContext = Wayland_GLES_DeleteContext; 121 122 device->CreateWindow = Wayland_CreateWindow; 123 device->ShowWindow = Wayland_ShowWindow; 124 device->SetWindowFullscreen = Wayland_SetWindowFullscreen; 125 device->SetWindowSize = Wayland_SetWindowSize; 126 device->DestroyWindow = Wayland_DestroyWindow; 127 128 device->free = Wayland_DeleteDevice; 129 130 return device; 131} 132 133VideoBootStrap Wayland_bootstrap = { 134 WAYLANDVID_DRIVER_NAME, "SDL Wayland video driver", 135 Wayland_Available, Wayland_CreateDevice 136}; 137 138static void 139wayland_add_mode(SDL_VideoData *d, SDL_DisplayMode m) 140{ 141 struct wayland_mode *mode; 142 143 /* Check for duplicate mode */ 144 wl_list_for_each(mode, &d->modes_list, link) 145 if (mode->mode.w == m.w && mode->mode.h == m.h && 146 mode->mode.refresh_rate == m.refresh_rate) 147 return; 148 149 /* Add new mode to the list */ 150 mode = (struct wayland_mode *) SDL_calloc(1, sizeof *mode); 151 152 if (!mode) 153 return; 154 155 mode->mode = m; 156 WAYLAND_wl_list_insert(&d->modes_list, &mode->link); 157} 158 159static void 160display_handle_geometry(void *data, 161 struct wl_output *output, 162 int x, int y, 163 int physical_width, 164 int physical_height, 165 int subpixel, 166 const char *make, 167 const char *model, 168 int transform) 169 170{ 171 SDL_VideoData *d = data; 172 173 d->screen_allocation.x = x; 174 d->screen_allocation.y = y; 175} 176 177static void 178display_handle_mode(void *data, 179 struct wl_output *wl_output, 180 uint32_t flags, 181 int width, 182 int height, 183 int refresh) 184{ 185 SDL_VideoData *d = data; 186 SDL_DisplayMode mode; 187 188 SDL_zero(mode); 189 mode.w = width; 190 mode.h = height; 191 mode.refresh_rate = refresh / 1000; 192 193 wayland_add_mode(d, mode); 194 195 if (flags & WL_OUTPUT_MODE_CURRENT) { 196 d->screen_allocation.width = width; 197 d->screen_allocation.height = height; 198 } 199} 200 201static const struct wl_output_listener output_listener = { 202 display_handle_geometry, 203 display_handle_mode 204}; 205 206static void 207shm_handle_format(void *data, 208 struct wl_shm *shm, 209 uint32_t format) 210{ 211 SDL_VideoData *d = data; 212 213 d->shm_formats |= (1 << format); 214} 215 216static const struct wl_shm_listener shm_listener = { 217 shm_handle_format 218}; 219 220#ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH 221static void 222windowmanager_hints(void *data, struct qt_windowmanager *qt_windowmanager, 223 int32_t show_is_fullscreen) 224{ 225} 226 227static void 228windowmanager_quit(void *data, struct qt_windowmanager *qt_windowmanager) 229{ 230 SDL_SendQuit(); 231} 232 233static const struct qt_windowmanager_listener windowmanager_listener = { 234 windowmanager_hints, 235 windowmanager_quit, 236}; 237#endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */ 238 239static void 240display_handle_global(void *data, struct wl_registry *registry, uint32_t id, 241 const char *interface, uint32_t version) 242{ 243 SDL_VideoData *d = data; 244 245 if (strcmp(interface, "wl_compositor") == 0) { 246 d->compositor = wl_registry_bind(d->registry, id, &wl_compositor_interface, 1); 247 } else if (strcmp(interface, "wl_output") == 0) { 248 d->output = wl_registry_bind(d->registry, id, &wl_output_interface, 1); 249 wl_output_add_listener(d->output, &output_listener, d); 250 } else if (strcmp(interface, "wl_seat") == 0) { 251 Wayland_display_add_input(d, id); 252 } else if (strcmp(interface, "wl_shell") == 0) { 253 d->shell = wl_registry_bind(d->registry, id, &wl_shell_interface, 1); 254 } else if (strcmp(interface, "wl_shm") == 0) { 255 d->shm = wl_registry_bind(registry, id, &wl_shm_interface, 1); 256 d->cursor_theme = WAYLAND_wl_cursor_theme_load(NULL, 32, d->shm); 257 d->default_cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "left_ptr"); 258 wl_shm_add_listener(d->shm, &shm_listener, d); 259 260#ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH 261 } else if (strcmp(interface, "qt_touch_extension") == 0) { 262 Wayland_touch_create(d, id); 263 } else if (strcmp(interface, "qt_surface_extension") == 0) { 264 d->surface_extension = wl_registry_bind(registry, id, 265 &qt_surface_extension_interface, 1); 266 } else if (strcmp(interface, "qt_windowmanager") == 0) { 267 d->windowmanager = wl_registry_bind(registry, id, 268 &qt_windowmanager_interface, 1); 269 qt_windowmanager_add_listener(d->windowmanager, &windowmanager_listener, d); 270#endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */ 271 } 272} 273 274static const struct wl_registry_listener registry_listener = { 275 display_handle_global 276}; 277 278int 279Wayland_VideoInit(_THIS) 280{ 281 SDL_VideoData *data; 282 SDL_VideoDisplay display; 283 SDL_DisplayMode mode; 284 int i; 285 286 data = malloc(sizeof *data); 287 if (data == NULL) 288 return 0; 289 memset(data, 0, sizeof *data); 290 291 _this->driverdata = data; 292 293 WAYLAND_wl_list_init(&data->modes_list); 294 295 data->display = WAYLAND_wl_display_connect(NULL); 296 if (data->display == NULL) { 297 SDL_SetError("Failed to connect to a Wayland display"); 298 return 0; 299 } 300 301 data->registry = wl_display_get_registry(data->display); 302 303 if ( data->registry == NULL) { 304 SDL_SetError("Failed to get the Wayland registry"); 305 return 0; 306 } 307 308 wl_registry_add_listener(data->registry, ®istry_listener, data); 309 310 for (i=0; i < 100; i++) { 311 if (data->screen_allocation.width != 0 || WAYLAND_wl_display_get_error(data->display) != 0) { 312 break; 313 } 314 WAYLAND_wl_display_dispatch(data->display); 315 } 316 317 if (data->screen_allocation.width == 0) { 318 SDL_SetError("Failed while waiting for screen allocation: %d ", WAYLAND_wl_display_get_error(data->display)); 319 return 0; 320 } 321 322 data->xkb_context = WAYLAND_xkb_context_new(0); 323 if (!data->xkb_context) { 324 SDL_SetError("Failed to create XKB context"); 325 return 0; 326 } 327 328 /* Use a fake 32-bpp desktop mode */ 329 mode.format = SDL_PIXELFORMAT_RGB888; 330 mode.w = data->screen_allocation.width; 331 mode.h = data->screen_allocation.height; 332 mode.refresh_rate = 0; 333 mode.driverdata = NULL; 334 wayland_add_mode(data, mode); 335 SDL_zero(display); 336 display.desktop_mode = mode; 337 display.current_mode = mode; 338 display.driverdata = NULL; 339 SDL_AddVideoDisplay(&display); 340 341 Wayland_InitMouse (); 342 343 WAYLAND_wl_display_flush(data->display); 344 345 return 0; 346} 347 348static void 349Wayland_GetDisplayModes(_THIS, SDL_VideoDisplay *sdl_display) 350{ 351 SDL_VideoData *data = _this->driverdata; 352 SDL_DisplayMode mode; 353 struct wayland_mode *m; 354 355 Wayland_PumpEvents(_this); 356 357 wl_list_for_each(m, &data->modes_list, link) { 358 m->mode.format = SDL_PIXELFORMAT_RGB888; 359 SDL_AddDisplayMode(sdl_display, &m->mode); 360 m->mode.format = SDL_PIXELFORMAT_RGBA8888; 361 SDL_AddDisplayMode(sdl_display, &m->mode); 362 } 363 364 mode.w = data->screen_allocation.width; 365 mode.h = data->screen_allocation.height; 366 mode.refresh_rate = 0; 367 mode.driverdata = NULL; 368 369 mode.format = SDL_PIXELFORMAT_RGB888; 370 SDL_AddDisplayMode(sdl_display, &mode); 371 mode.format = SDL_PIXELFORMAT_RGBA8888; 372 SDL_AddDisplayMode(sdl_display, &mode); 373} 374 375static int 376Wayland_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode) 377{ 378 return 0; 379} 380 381void 382Wayland_VideoQuit(_THIS) 383{ 384 SDL_VideoData *data = _this->driverdata; 385 struct wayland_mode *t, *m; 386 387 Wayland_FiniMouse (); 388 389 if (data->output) 390 wl_output_destroy(data->output); 391 392 Wayland_display_destroy_input(data); 393 394 if (data->xkb_context) { 395 WAYLAND_xkb_context_unref(data->xkb_context); 396 data->xkb_context = NULL; 397 } 398#ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH 399 if (data->windowmanager) 400 qt_windowmanager_destroy(data->windowmanager); 401 402 if (data->surface_extension) 403 qt_surface_extension_destroy(data->surface_extension); 404 405 Wayland_touch_destroy(data); 406#endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */ 407 408 if (data->shm) 409 wl_shm_destroy(data->shm); 410 411 if (data->cursor_theme) 412 WAYLAND_wl_cursor_theme_destroy(data->cursor_theme); 413 414 if (data->shell) 415 wl_shell_destroy(data->shell); 416 417 if (data->compositor) 418 wl_compositor_destroy(data->compositor); 419 420 if (data->display) { 421 WAYLAND_wl_display_flush(data->display); 422 WAYLAND_wl_display_disconnect(data->display); 423 } 424 425 wl_list_for_each_safe(m, t, &data->modes_list, link) { 426 WAYLAND_wl_list_remove(&m->link); 427 free(m); 428 } 429 430 431 free(data); 432 _this->driverdata = NULL; 433} 434 435#endif /* SDL_VIDEO_DRIVER_WAYLAND */ 436 437/* vi: set ts=4 sw=4 expandtab: */