2 Simple DirectMedia Layer
3 Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
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.
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:
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.
22 #include "../../SDL_internal.h"
24 #if SDL_VIDEO_DRIVER_WAYLAND
26 #include "SDL_video.h"
27 #include "SDL_mouse.h"
28 #include "SDL_stdinc.h"
29 #include "../../events/SDL_events_c.h"
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_waylandvulkan.h"
40 #include <wayland-client.h>
41 #include <wayland-cursor.h>
42 #include <wayland-util.h>
43 #include <xkbcommon/xkbcommon.h>
47 # include "ivi-application-client-protocol.h"
48 # define IVI_SURFACE_ID 6000
52 # include <xdg-shell-client-protocol.h>
53 # include <tizen-extension-client-protocol.h>
54 //# include "subsurface-client-protocol.h"
56 #include "SDL_waylanddyn.h"
58 #define WAYLANDVID_DRIVER_NAME "wayland"
61 /* Initialization/Query functions */
63 Wayland_VideoInit(_THIS);
66 Wayland_GetDisplayModes(_THIS, SDL_VideoDisplay *sdl_display);
68 Wayland_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode);
71 Wayland_VideoQuit(_THIS);
73 /* Wayland driver bootstrap functions */
75 Wayland_Available(void)
77 struct wl_display *display = NULL;
78 if (SDL_WAYLAND_LoadSymbols()) {
79 display = WAYLAND_wl_display_connect(NULL);
80 if (display != NULL) {
81 WAYLAND_wl_display_disconnect(display);
83 SDL_WAYLAND_UnloadSymbols();
86 return (display != NULL);
90 Wayland_DeleteDevice(SDL_VideoDevice *device)
93 SDL_WAYLAND_UnloadSymbols();
96 static SDL_VideoDevice *
97 Wayland_CreateDevice(int devindex)
99 SDL_VideoDevice *device;
101 if (!SDL_WAYLAND_LoadSymbols()) {
105 /* Initialize all variables that we clean on shutdown */
106 device = SDL_calloc(1, sizeof(SDL_VideoDevice));
108 SDL_WAYLAND_UnloadSymbols();
113 /* Set the function pointers */
114 device->VideoInit = Wayland_VideoInit;
115 device->VideoQuit = Wayland_VideoQuit;
116 device->SetDisplayMode = Wayland_SetDisplayMode;
117 device->GetDisplayModes = Wayland_GetDisplayModes;
118 device->GetWindowWMInfo = Wayland_GetWindowWMInfo;
120 device->PumpEvents = Wayland_PumpEvents;
122 device->GL_SwapWindow = Wayland_GLES_SwapWindow;
123 device->GL_GetSwapInterval = Wayland_GLES_GetSwapInterval;
124 device->GL_SetSwapInterval = Wayland_GLES_SetSwapInterval;
125 device->GL_MakeCurrent = Wayland_GLES_MakeCurrent;
126 device->GL_CreateContext = Wayland_GLES_CreateContext;
127 device->GL_LoadLibrary = Wayland_GLES_LoadLibrary;
128 device->GL_UnloadLibrary = Wayland_GLES_UnloadLibrary;
129 device->GL_GetProcAddress = Wayland_GLES_GetProcAddress;
130 device->GL_DeleteContext = Wayland_GLES_DeleteContext;
132 device->CreateWindow = Wayland_CreateWindow;
133 device->ShowWindow = Wayland_ShowWindow;
134 device->SetWindowFullscreen = Wayland_SetWindowFullscreen;
135 device->SetWindowSize = Wayland_SetWindowSize;
136 device->DestroyWindow = Wayland_DestroyWindow;
137 device->SetWindowHitTest = Wayland_SetWindowHitTest;
139 device->free = Wayland_DeleteDevice;
142 device->vulkan_GetInstanceExtensions = Wayland_vulkan_GetInstanceExtensions;
143 device->vulkan_CreateSurface = Wayland_vulkan_CreateSurface;
144 device->vulkan_LoadLibrary = Wayland_vulkan_LoadLibrary;
149 VideoBootStrap Wayland_bootstrap = {
150 WAYLANDVID_DRIVER_NAME, "SDL Wayland video driver",
151 Wayland_Available, Wayland_CreateDevice
155 display_handle_geometry(void *data,
156 struct wl_output *output,
166 SDL_VideoDisplay *display = data;
168 display->name = strdup(model);
172 display_handle_mode(void *data,
173 struct wl_output *output,
179 SDL_VideoDisplay *display = data;
180 SDL_DisplayMode mode;
185 mode.refresh_rate = refresh / 1000; // mHz to Hz
186 SDL_AddDisplayMode(display, &mode);
188 if (flags & WL_OUTPUT_MODE_CURRENT) {
189 display->current_mode = mode;
190 display->desktop_mode = mode;
195 display_handle_done(void *data,
196 struct wl_output *output)
198 SDL_VideoDisplay *display = data;
199 SDL_AddVideoDisplay(display);
200 SDL_free(display->name);
205 display_handle_scale(void *data,
206 struct wl_output *output,
209 // TODO: do HiDPI stuff.
212 static const struct wl_output_listener output_listener = {
213 display_handle_geometry,
220 Wayland_add_display(SDL_VideoData *d, uint32_t id)
222 struct wl_output *output;
223 SDL_VideoDisplay *display = SDL_malloc(sizeof *display);
230 output = wl_registry_bind(d->registry, id, &wl_output_interface, 2);
232 SDL_SetError("Failed to retrieve output.");
237 wl_output_add_listener(output, &output_listener, display);
240 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
242 windowmanager_hints(void *data, struct qt_windowmanager *qt_windowmanager,
243 int32_t show_is_fullscreen)
248 windowmanager_quit(void *data, struct qt_windowmanager *qt_windowmanager)
253 static const struct qt_windowmanager_listener windowmanager_listener = {
257 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
261 xdg_shell_ping(void *data, struct xdg_shell *shell, uint32_t serial)
263 xdg_shell_pong(shell, serial);
266 static const struct xdg_shell_listener xdg_shell_listener =
272 _wl_cb_conformant(void *data, struct tizen_policy *tizen_policy, struct wl_surface *surface_resource, uint32_t is_conformant)
275 struct wl_surface *surface = surface_resource;
276 Ecore_Wl_Window *win = NULL;
278 if (!surface) return;
279 win = ecore_wl_window_surface_find(surface);
281 win->conformant = is_conformant;
286 _wl_cb_conformant_area(void *data, struct tizen_policy *tizen_policy, struct wl_surface *surface_resource, uint32_t conformant_part, uint32_t state, int32_t x, int32_t y, int32_t w, int32_t h)
289 struct wl_surface *surface = surface_resource;
290 Ecore_Wl_Window *win = NULL;
291 int org_x, org_y, org_w, org_h;
292 Eina_Bool changed = EINA_FALSE;
293 Ecore_Wl_Indicator_State ind_state;
294 Ecore_Wl_Virtual_Keyboard_State kbd_state;
295 Ecore_Wl_Clipboard_State clip_state;
297 if (!surface) return;
298 win = ecore_wl_window_surface_find(surface);
301 if (conformant_part == TIZEN_POLICY_CONFORMANT_PART_INDICATOR)
303 ecore_wl_window_indicator_geometry_get(win, &org_x, &org_y, &org_w, &org_h);
304 if ((org_x != x) || (org_y != y) || (org_w != w) || (org_h != h))
306 ecore_wl_window_indicator_geometry_set(win, x, y, w, h);
310 /* The given state is based on the visibility value of indicator.
311 * Thus we need to add 1 to it before comparing with indicator state.
313 ind_state = ecore_wl_window_indicator_state_get(win);
314 if ((state + 1) != ind_state)
316 ecore_wl_window_indicator_state_set(win, state + 1);
320 else if (conformant_part == TIZEN_POLICY_CONFORMANT_PART_KEYBOARD)
322 ecore_wl_window_keyboard_geometry_get(win, &org_x, &org_y, &org_w, &org_h);
323 if ((org_x != x) || (org_y != y) || (org_w != w) || (org_h != h))
325 ecore_wl_window_keyboard_geometry_set(win, x, y, w, h);
329 /* The given state is based on the visibility value of virtual keyboard window.
330 * Thus we need to add 1 to it before comparing with keyboard state.
332 kbd_state = ecore_wl_window_keyboard_state_get(win);
333 if ((state + 1) != (kbd_state))
335 ecore_wl_window_keyboard_state_set(win, state + 1);
339 else if (conformant_part == TIZEN_POLICY_CONFORMANT_PART_CLIPBOARD)
341 ecore_wl_window_clipboard_geometry_get(win, &org_x, &org_y, &org_w, &org_h);
342 if ((org_x != x) || (org_y != y) || (org_w != w) || (org_h != h))
344 ecore_wl_window_clipboard_geometry_set(win, x, y, w, h);
348 /* The given state is based on the visibility value of clipboard window.
349 * Thus we need to add 1 to it before comparing with clipboard state.
351 clip_state = ecore_wl_window_clipboard_state_get(win);
352 if ((state + 1) != clip_state)
354 ecore_wl_window_clipboard_state_set(win, state + 1);
360 _ecore_wl_window_conformant_area_send(win, conformant_part, state);
365 _wl_cb_notification_done(void *data , struct tizen_policy *tizen_policy , struct wl_surface *surface , int32_t level , uint32_t state )
370 _wl_cb_transient_for_done(void *data , struct tizen_policy *tizen_policy , uint32_t child_id )
375 _wl_cb_scr_mode_done(void *data , struct tizen_policy *tizen_policy , struct wl_surface *surface , uint32_t mode , uint32_t state )
380 _wl_cb_iconify_state_changed(void *data , struct tizen_policy *tizen_policy , struct wl_surface *surface_resource, uint32_t iconified, uint32_t force)
383 struct wl_surface *surface = surface_resource;
384 Ecore_Wl_Window *win = NULL;
385 Ecore_Wl_Event_Window_Iconify_State_Change *ev;
387 LOGFN(__FILE__, __LINE__, __FUNCTION__);
389 if (!surface) return;
390 win = ecore_wl_window_surface_find(surface);
393 if (!(ev = calloc(1, sizeof(Ecore_Wl_Event_Window_Iconify_State_Change)))) return;
395 ev->iconified = iconified;
398 ecore_event_add(ECORE_WL_EVENT_WINDOW_ICONIFY_STATE_CHANGE, ev, NULL, NULL);
403 _wl_cb_supported_aux_hints(void *data , struct tizen_policy *tizen_policy , struct wl_surface *surface_resource, struct wl_array *hints, uint32_t num_hints)
406 struct wl_surface *surface = surface_resource;
407 Ecore_Wl_Window *win = NULL;
410 const char *hint = NULL;
413 LOGFN(__FILE__, __LINE__, __FUNCTION__);
415 if (!surface) return;
416 win = ecore_wl_window_surface_find(surface);
420 str = calloc(num_hints, sizeof(char *));
423 _ecore_wl_window_aux_hint_free(win);
425 while ((const char *)p < ((const char *)hints->data + hints->size))
427 str[i] = (char *)eina_stringshare_add(p);
431 for (i = 0; i < num_hints; i++)
433 hint = eina_stringshare_add(str[i]);
434 win->supported_aux_hints =
435 eina_list_append(win->supported_aux_hints, hint);
439 for (i = 0; i < num_hints; i++)
443 eina_stringshare_del(str[i]);
453 _wl_cb_allowed_aux_hint(void *data , struct tizen_policy *tizen_policy , struct wl_surface *surface_resource, int id)
456 struct wl_surface *surface = surface_resource;
457 Ecore_Wl_Window *win = NULL;
458 Ecore_Wl_Event_Aux_Hint_Allowed *ev;
460 LOGFN(__FILE__, __LINE__, __FUNCTION__);
462 if (!surface) return;
463 win = ecore_wl_window_surface_find(surface);
466 if (!(ev = calloc(1, sizeof(Ecore_Wl_Event_Aux_Hint_Allowed)))) return;
469 ecore_event_add(ECORE_WL_EVENT_AUX_HINT_ALLOWED, ev, NULL, NULL);
474 _wl_cb_aux_message(void *data , struct tizen_policy *tizen_policy , struct wl_surface *surface_resource, const char *key, const char *val, struct wl_array *options)
477 Ecore_Wl_Window *win = NULL;
478 Ecore_Wl_Event_Aux_Message *ev;
479 char *p = NULL, *str = NULL;
480 Eina_List *opt_list = NULL;
482 LOGFN(__FILE__, __LINE__, __FUNCTION__);
484 if (!surface_resource) return;
485 win = ecore_wl_window_surface_find(surface_resource);
488 if (!(ev = calloc(1, sizeof(Ecore_Wl_Event_Aux_Message)))) return;
490 if ((options) && (options->size))
493 while ((const char *)p < ((const char *)options->data + options->size))
495 str = (char *)eina_stringshare_add(p);
496 opt_list = eina_list_append(opt_list, str);
502 ev->key = eina_stringshare_add(key);
503 ev->val = eina_stringshare_add(val);
504 ev->options = opt_list;
506 ecore_event_add(ECORE_WL_EVENT_AUX_MESSAGE, ev, _cb_aux_message_free, NULL);
511 static const struct tizen_policy_listener _tizen_policy_listener =
514 _wl_cb_conformant_area,
515 _wl_cb_notification_done,
516 _wl_cb_transient_for_done,
517 _wl_cb_scr_mode_done,
518 _wl_cb_iconify_state_changed,
519 _wl_cb_supported_aux_hints,
520 _wl_cb_allowed_aux_hint,
525 _display_handle_global(void *data, struct wl_registry *registry, uint32_t id,
526 const char *interface, uint32_t version)
528 SDL_VideoData *d = data;
529 int client_version = 1;
531 if (strcmp(interface, "wl_compositor") == 0) {
532 d->compositor = wl_registry_bind(d->registry, id, &wl_compositor_interface, 3);
533 } else if (!strcmp(interface, "wl_subcompositor")) {
535 wl_registry_bind(registry, id, &wl_subcompositor_interface, 1);
537 else if (strcmp(interface, "wl_output") == 0) {
538 Wayland_add_display(d, id);
539 } else if (strcmp(interface, "wl_seat") == 0) {
540 Wayland_display_add_input(d, id);
541 } else if (!strcmp(interface, "session_recovery")) {
542 } else if (strcmp(interface, "xdg_shell") == 0) {
544 d->xdgshell = wl_registry_bind(d->registry, id, &xdg_shell_interface, 1);
545 xdg_shell_use_unstable_version(d->xdgshell, XDG_VERSION);
546 xdg_shell_add_listener(d->xdgshell, &xdg_shell_listener,d->display);
548 } else if (strcmp(interface, "wl_shell") == 0) {
549 d->shell = wl_registry_bind(d->registry, id, &wl_shell_interface, 1);
550 } else if (strcmp(interface, "wl_shm") == 0) {
551 d->shm = wl_registry_bind(registry, id, &wl_shm_interface, 1);
552 d->cursor_theme = WAYLAND_wl_cursor_theme_load(NULL, 32, d->shm);
553 d->default_cursor = WAYLAND_wl_cursor_theme_get_cursor(d->cursor_theme, "left_ptr");
555 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
556 } else if (strcmp(interface, "qt_touch_extension") == 0) {
557 Wayland_touch_create(d, id);
558 } else if (strcmp(interface, "qt_surface_extension") == 0) {
559 d->surface_extension = wl_registry_bind(registry, id,
560 &qt_surface_extension_interface, 1);
561 } else if (strcmp(interface, "qt_windowmanager") == 0) {
562 d->windowmanager = wl_registry_bind(registry, id,
563 &qt_windowmanager_interface, 1);
564 qt_windowmanager_add_listener(d->windowmanager, &windowmanager_listener, d);
565 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
566 } else if (!strcmp(interface, "wl_data_device_manager")) {
567 d->data_device_manager =
568 wl_registry_bind(registry, id, &wl_data_device_manager_interface, 1);
569 } else if (!strcmp(interface, "tizen_policy")) {
573 client_version = version;
576 wl_registry_bind(registry, id, &tizen_policy_interface, client_version);
578 tizen_policy_add_listener(d->tz_policy, &_tizen_policy_listener, d->display);
582 static const struct wl_registry_listener registry_listener = {
583 _display_handle_global
587 Wayland_VideoInit(_THIS)
589 SDL_VideoData *data = SDL_malloc(sizeof *data);
591 return SDL_OutOfMemory();
592 memset(data, 0, sizeof *data);
594 _this->driverdata = data;
596 data->display = WAYLAND_wl_display_connect(NULL);
597 if (data->display == NULL) {
598 return SDL_SetError("Failed to connect to a Wayland display");
601 data->registry = wl_display_get_registry(data->display);
602 if (data->registry == NULL) {
603 return SDL_SetError("Failed to get the Wayland registry");
606 wl_registry_add_listener(data->registry, ®istry_listener, data);
608 // First roundtrip to receive all registry objects.
609 WAYLAND_wl_display_roundtrip(data->display);
611 // Second roundtrip to receive all output events.
612 WAYLAND_wl_display_roundtrip(data->display);
614 data->xkb_context = WAYLAND_xkb_context_new(0);
615 if (!data->xkb_context) {
616 return SDL_SetError("Failed to create XKB context");
621 WAYLAND_wl_display_flush(data->display);
627 Wayland_GetDisplayModes(_THIS, SDL_VideoDisplay *sdl_display)
629 // Nothing to do here, everything was already done in the wl_output
634 Wayland_SetDisplayMode(_THIS, SDL_VideoDisplay *display, SDL_DisplayMode *mode)
636 return SDL_Unsupported();
640 Wayland_VideoQuit(_THIS)
642 SDL_VideoData *data = _this->driverdata;
645 Wayland_FiniMouse ();
647 for (i = 0; i < _this->num_displays; ++i) {
648 SDL_VideoDisplay *display = &_this->displays[i];
649 wl_output_destroy(display->driverdata);
650 display->driverdata = NULL;
653 Wayland_display_destroy_input(data);
655 if (data->xkb_context) {
656 WAYLAND_xkb_context_unref(data->xkb_context);
657 data->xkb_context = NULL;
659 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
660 if (data->windowmanager)
661 qt_windowmanager_destroy(data->windowmanager);
663 if (data->surface_extension)
664 qt_surface_extension_destroy(data->surface_extension);
666 Wayland_touch_destroy(data);
667 #endif /* SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH */
670 wl_shm_destroy(data->shm);
672 if (data->cursor_theme)
673 WAYLAND_wl_cursor_theme_destroy(data->cursor_theme);
676 wl_shell_destroy(data->shell);
678 if (data->compositor)
679 wl_compositor_destroy(data->compositor);
682 wl_registry_destroy(data->registry);
685 WAYLAND_wl_display_flush(data->display);
686 WAYLAND_wl_display_disconnect(data->display);
690 _this->driverdata = NULL;
693 #endif /* SDL_VIDEO_DRIVER_WAYLAND */
695 /* vi: set ts=4 sw=4 expandtab: */