/* * Copyright © 2010 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library. If not, see . */ #include "config.h" #include #include #include #include #include #include #include "gdkwayland.h" #include "gdkdisplay.h" #include "gdkdisplay-wayland.h" #include "gdkscreen.h" #include "gdkinternals.h" #include "gdkdeviceprivate.h" #include "gdkdevicemanager.h" #include "gdkkeysprivate.h" #include "gdkprivate-wayland.h" /** * SECTION:wayland_interaction * @Short_description: Wayland backend-specific functions * @Title: Wayland Interaction * * The functions in this section are specific to the GDK Wayland backend. * To use them, you need to include the `` header and use * the Wayland-specific pkg-config files to build your application (either * `gdk-wayland-3.0` or `gtk+-wayland-3.0`). * * To make your code compile with other GDK backends, guard backend-specific * calls by an ifdef as follows. Since GDK may be built with multiple * backends, you should also check for the backend that is in use (e.g. by * using the GDK_IS_WAYLAND_DISPLAY() macro). * |[ * #ifdef GDK_WINDOWING_WAYLAND * if (GDK_IS_WAYLAND_DISPLAY (display)) * { * // make Wayland-specific calls here * } * else * #endif * #ifdef GDK_WINDOWING_X11 * if (GDK_IS_X11_DISPLAY (display)) * { * // make X11-specific calls here * } * else * #endif * g_error ("Unsupported GDK backend"); * ]| */ static void _gdk_wayland_display_load_cursor_theme (GdkWaylandDisplay *wayland_display); G_DEFINE_TYPE (GdkWaylandDisplay, gdk_wayland_display, GDK_TYPE_DISPLAY) static void gdk_input_init (GdkDisplay *display) { GdkWaylandDisplay *display_wayland; GdkDeviceManager *device_manager; GdkDevice *device; GList *list, *l; display_wayland = GDK_WAYLAND_DISPLAY (display); device_manager = gdk_display_get_device_manager (display); /* For backwards compatibility, just add * floating devices that are not keyboards. */ list = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_FLOATING); for (l = list; l; l = l->next) { device = l->data; if (gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD) continue; display_wayland->input_devices = g_list_prepend (display_wayland->input_devices, l->data); } g_list_free (list); /* Now set "core" pointer to the first * master device that is a pointer. */ list = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER); for (l = list; l; l = l->next) { device = l->data; if (gdk_device_get_source (device) != GDK_SOURCE_MOUSE) continue; display->core_pointer = device; break; } /* Add the core pointer to the devices list */ display_wayland->input_devices = g_list_prepend (display_wayland->input_devices, display->core_pointer); g_list_free (list); } static void init_sync_callback(void *data, struct wl_callback *callback, uint32_t serial) { GdkWaylandDisplay *display = data; display->init_ref_count--; wl_callback_destroy(callback); } static const struct wl_callback_listener init_sync_listener = { init_sync_callback }; static void wait_for_roundtrip(GdkWaylandDisplay *display) { struct wl_callback *callback; display->init_ref_count++; callback = wl_display_sync(display->wl_display); wl_callback_add_listener(callback, &init_sync_listener, display); } static void xdg_shell_ping (void *data, struct xdg_shell *xdg_shell, uint32_t serial) { GdkWaylandDisplay *wayland_display = data; _gdk_wayland_display_update_serial (wayland_display, serial); xdg_shell_pong (xdg_shell, serial); } static const struct xdg_shell_listener xdg_shell_listener = { xdg_shell_ping, }; static void gdk_registry_handle_global(void *data, struct wl_registry *registry, uint32_t id, const char *interface, uint32_t version) { GdkWaylandDisplay *display_wayland = data; GdkDisplay *gdk_display = GDK_DISPLAY_OBJECT (data); struct wl_seat *seat; struct wl_output *output; if (strcmp(interface, "wl_compositor") == 0) { display_wayland->compositor = wl_registry_bind(display_wayland->wl_registry, id, &wl_compositor_interface, MIN (version, 3)); display_wayland->compositor_version = MIN (version, 3); } else if (strcmp(interface, "wl_shm") == 0) { display_wayland->shm = wl_registry_bind(display_wayland->wl_registry, id, &wl_shm_interface, 1); /* SHM interface is prerequisite */ _gdk_wayland_display_load_cursor_theme(display_wayland); } else if (strcmp(interface, "xdg_shell") == 0) { display_wayland->xdg_shell = wl_registry_bind(display_wayland->wl_registry, id, &xdg_shell_interface, 1); xdg_shell_use_unstable_version(display_wayland->xdg_shell, XDG_SHELL_VERSION_CURRENT); xdg_shell_add_listener(display_wayland->xdg_shell, &xdg_shell_listener, display_wayland); } else if (strcmp(interface, "gtk_shell") == 0) { display_wayland->gtk_shell = wl_registry_bind(display_wayland->wl_registry, id, >k_shell_interface, 1); _gdk_wayland_screen_set_has_gtk_shell (display_wayland->screen); /* We need another roundtrip to receive the shell capabilities */ wait_for_roundtrip(display_wayland); } else if (strcmp(interface, "wl_output") == 0) { output = wl_registry_bind(display_wayland->wl_registry, id, &wl_output_interface, MIN (version, 2)); _gdk_wayland_screen_add_output(display_wayland->screen, id, output, MIN (version, 2)); /* We need another roundtrip to receive the modes and geometry * events for the output, which gives us the physical properties * and available modes on the output. */ wait_for_roundtrip(display_wayland); } else if (strcmp(interface, "wl_seat") == 0) { seat = wl_registry_bind(display_wayland->wl_registry, id, &wl_seat_interface, 1); _gdk_wayland_device_manager_add_seat (gdk_display->device_manager, id, seat); /* We need another roundtrip to receive the wl_seat capabilities * event which informs us of available input devices on this * seat. */ wait_for_roundtrip(display_wayland); } else if (strcmp(interface, "wl_data_device_manager") == 0) { display_wayland->data_device_manager = wl_registry_bind(display_wayland->wl_registry, id, &wl_data_device_manager_interface, 1); } } static void gdk_registry_handle_global_remove(void *data, struct wl_registry *registry, uint32_t id) { GdkWaylandDisplay *display_wayland = data; GdkDisplay *display = GDK_DISPLAY (display_wayland); _gdk_wayland_device_manager_remove_seat (display->device_manager, id); _gdk_wayland_screen_remove_output (display_wayland->screen, id); /* FIXME: the object needs to be destroyed here, we're leaking */ } static const struct wl_registry_listener registry_listener = { gdk_registry_handle_global, gdk_registry_handle_global_remove }; static void log_handler(const char *format, va_list args) { g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, format, args); } GdkDisplay * _gdk_wayland_display_open (const gchar *display_name) { struct wl_display *wl_display; GdkDisplay *display; GdkWaylandDisplay *display_wayland; wl_log_set_handler_client(log_handler); wl_display = wl_display_connect(display_name); if (!wl_display) return NULL; display = g_object_new (GDK_TYPE_WAYLAND_DISPLAY, NULL); display_wayland = GDK_WAYLAND_DISPLAY (display); display_wayland->wl_display = wl_display; display_wayland->screen = _gdk_wayland_screen_new (display); display->device_manager = _gdk_wayland_device_manager_new (display); /* Set up listener so we'll catch all events. */ display_wayland->wl_registry = wl_display_get_registry(display_wayland->wl_display); wl_registry_add_listener(display_wayland->wl_registry, ®istry_listener, display_wayland); /* We use init_ref_count to track whether some part of our * initialization still needs a roundtrip to complete. */ wait_for_roundtrip(display_wayland); while (display_wayland->init_ref_count > 0) wl_display_roundtrip(display_wayland->wl_display); display_wayland->event_source = _gdk_wayland_display_event_source_new (display); gdk_input_init (display); g_signal_emit_by_name (display, "opened"); return display; } static void gdk_wayland_display_dispose (GObject *object) { GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (object); g_list_foreach (display_wayland->input_devices, (GFunc) g_object_run_dispose, NULL); _gdk_screen_close (display_wayland->screen); if (display_wayland->event_source) { g_source_destroy (display_wayland->event_source); g_source_unref (display_wayland->event_source); display_wayland->event_source = NULL; } G_OBJECT_CLASS (gdk_wayland_display_parent_class)->dispose (object); } static void gdk_wayland_display_finalize (GObject *object) { GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (object); _gdk_wayland_display_finalize_cursors (display_wayland); /* input GdkDevice list */ g_list_free_full (display_wayland->input_devices, g_object_unref); g_object_unref (display_wayland->screen); g_free (display_wayland->startup_notification_id); G_OBJECT_CLASS (gdk_wayland_display_parent_class)->finalize (object); } static const gchar * gdk_wayland_display_get_name (GdkDisplay *display) { return "Wayland"; } static GdkScreen * gdk_wayland_display_get_default_screen (GdkDisplay *display) { g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL); return GDK_WAYLAND_DISPLAY (display)->screen; } static void gdk_wayland_display_beep (GdkDisplay *display) { g_return_if_fail (GDK_IS_DISPLAY (display)); } static void gdk_wayland_display_sync (GdkDisplay *display) { GdkWaylandDisplay *display_wayland; g_return_if_fail (GDK_IS_DISPLAY (display)); display_wayland = GDK_WAYLAND_DISPLAY (display); wl_display_roundtrip(display_wayland->wl_display); } static void gdk_wayland_display_flush (GdkDisplay *display) { g_return_if_fail (GDK_IS_DISPLAY (display)); if (!display->closed) wl_display_flush(GDK_WAYLAND_DISPLAY (display)->wl_display); } static gboolean gdk_wayland_display_has_pending (GdkDisplay *display) { return FALSE; } static GdkWindow * gdk_wayland_display_get_default_group (GdkDisplay *display) { g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL); return NULL; } static gboolean gdk_wayland_display_supports_selection_notification (GdkDisplay *display) { return TRUE; } static gboolean gdk_wayland_display_request_selection_notification (GdkDisplay *display, GdkAtom selection) { return FALSE; } static gboolean gdk_wayland_display_supports_clipboard_persistence (GdkDisplay *display) { return FALSE; } static void gdk_wayland_display_store_clipboard (GdkDisplay *display, GdkWindow *clipboard_window, guint32 time_, const GdkAtom *targets, gint n_targets) { } static gboolean gdk_wayland_display_supports_shapes (GdkDisplay *display) { return FALSE; } static gboolean gdk_wayland_display_supports_input_shapes (GdkDisplay *display) { return TRUE; } static gboolean gdk_wayland_display_supports_composite (GdkDisplay *display) { return FALSE; } static GList * gdk_wayland_display_list_devices (GdkDisplay *display) { g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL); return GDK_WAYLAND_DISPLAY (display)->input_devices; } static void gdk_wayland_display_before_process_all_updates (GdkDisplay *display) { } static void gdk_wayland_display_after_process_all_updates (GdkDisplay *display) { /* Post the damage here instead? */ } static gulong gdk_wayland_display_get_next_serial (GdkDisplay *display) { static gulong serial = 0; return ++serial; } /** * gdk_wayland_display_broadcast_startup_message: * @display: a #GdkDisplay * @message_type: startup notification message type ("new", "change", * or "remove") * @...: a list of key/value pairs (as strings), terminated by a * %NULL key. (A %NULL value for a key will cause that key to be * skipped in the output.) * * Sends a startup notification message of type @message_type to * @display. * * This is a convenience function for use by code that implements the * freedesktop startup notification specification. Applications should * not normally need to call it directly. See the * [Startup Notification Protocol specification](http://standards.freedesktop.org/startup-notification-spec/startup-notification-latest.txt) * for definitions of the message types and keys that can be used. * * Since: 2.12 **/ void gdk_wayland_display_broadcast_startup_message (GdkDisplay *display, const char *message_type, ...) { GString *message; va_list ap; const char *key, *value, *p; message = g_string_new (message_type); g_string_append_c (message, ':'); va_start (ap, message_type); while ((key = va_arg (ap, const char *))) { value = va_arg (ap, const char *); if (!value) continue; g_string_append_printf (message, " %s=\"", key); for (p = value; *p; p++) { switch (*p) { case ' ': case '"': case '\\': g_string_append_c (message, '\\'); break; } g_string_append_c (message, *p); } g_string_append_c (message, '\"'); } va_end (ap); g_string_free (message, TRUE); } static void gdk_wayland_display_notify_startup_complete (GdkDisplay *display, const gchar *startup_id) { gdk_wayland_display_broadcast_startup_message (display, "remove", "ID", startup_id, NULL); } static void gdk_wayland_display_event_data_copy (GdkDisplay *display, const GdkEvent *src, GdkEvent *dst) { } static void gdk_wayland_display_event_data_free (GdkDisplay *display, GdkEvent *event) { } GdkKeymap * _gdk_wayland_display_get_keymap (GdkDisplay *display) { GdkDeviceManager *device_manager; GList *list, *l; GdkDevice *core_keyboard = NULL; static GdkKeymap *tmp_keymap = NULL; device_manager = gdk_display_get_device_manager (display); list = gdk_device_manager_list_devices (device_manager, GDK_DEVICE_TYPE_MASTER); for (l = list; l; l = l->next) { GdkDevice *device; device = list->data; if (gdk_device_get_source (device) != GDK_SOURCE_KEYBOARD) continue; core_keyboard = device; break; } g_list_free (list); if (core_keyboard && tmp_keymap) { g_object_unref (tmp_keymap); tmp_keymap = NULL; } if (core_keyboard) return _gdk_wayland_device_get_keymap (core_keyboard); tmp_keymap = _gdk_wayland_keymap_new (); return tmp_keymap; } static void gdk_wayland_display_push_error_trap (GdkDisplay *display) { } static gint gdk_wayland_display_pop_error_trap (GdkDisplay *display, gboolean ignored) { return 0; } static void gdk_wayland_display_class_init (GdkWaylandDisplayClass * class) { GObjectClass *object_class = G_OBJECT_CLASS (class); GdkDisplayClass *display_class = GDK_DISPLAY_CLASS (class); object_class->dispose = gdk_wayland_display_dispose; object_class->finalize = gdk_wayland_display_finalize; display_class->window_type = gdk_wayland_window_get_type (); display_class->get_name = gdk_wayland_display_get_name; display_class->get_default_screen = gdk_wayland_display_get_default_screen; display_class->beep = gdk_wayland_display_beep; display_class->sync = gdk_wayland_display_sync; display_class->flush = gdk_wayland_display_flush; display_class->has_pending = gdk_wayland_display_has_pending; display_class->queue_events = _gdk_wayland_display_queue_events; display_class->get_default_group = gdk_wayland_display_get_default_group; display_class->supports_selection_notification = gdk_wayland_display_supports_selection_notification; display_class->request_selection_notification = gdk_wayland_display_request_selection_notification; display_class->supports_clipboard_persistence = gdk_wayland_display_supports_clipboard_persistence; display_class->store_clipboard = gdk_wayland_display_store_clipboard; display_class->supports_shapes = gdk_wayland_display_supports_shapes; display_class->supports_input_shapes = gdk_wayland_display_supports_input_shapes; display_class->supports_composite = gdk_wayland_display_supports_composite; display_class->list_devices = gdk_wayland_display_list_devices; display_class->get_app_launch_context = _gdk_wayland_display_get_app_launch_context; display_class->get_default_cursor_size = _gdk_wayland_display_get_default_cursor_size; display_class->get_maximal_cursor_size = _gdk_wayland_display_get_maximal_cursor_size; display_class->get_cursor_for_type = _gdk_wayland_display_get_cursor_for_type; display_class->get_cursor_for_name = _gdk_wayland_display_get_cursor_for_name; display_class->get_cursor_for_surface = _gdk_wayland_display_get_cursor_for_surface; display_class->supports_cursor_alpha = _gdk_wayland_display_supports_cursor_alpha; display_class->supports_cursor_color = _gdk_wayland_display_supports_cursor_color; display_class->before_process_all_updates = gdk_wayland_display_before_process_all_updates; display_class->after_process_all_updates = gdk_wayland_display_after_process_all_updates; display_class->get_next_serial = gdk_wayland_display_get_next_serial; display_class->notify_startup_complete = gdk_wayland_display_notify_startup_complete; display_class->event_data_copy = gdk_wayland_display_event_data_copy; display_class->event_data_free = gdk_wayland_display_event_data_free; display_class->create_window_impl = _gdk_wayland_display_create_window_impl; display_class->get_keymap = _gdk_wayland_display_get_keymap; display_class->push_error_trap = gdk_wayland_display_push_error_trap; display_class->pop_error_trap = gdk_wayland_display_pop_error_trap; display_class->get_selection_owner = _gdk_wayland_display_get_selection_owner; display_class->set_selection_owner = _gdk_wayland_display_set_selection_owner; display_class->send_selection_notify = _gdk_wayland_display_send_selection_notify; display_class->get_selection_property = _gdk_wayland_display_get_selection_property; display_class->convert_selection = _gdk_wayland_display_convert_selection; display_class->text_property_to_utf8_list = _gdk_wayland_display_text_property_to_utf8_list; display_class->utf8_to_string_target = _gdk_wayland_display_utf8_to_string_target; } static void gdk_wayland_display_init (GdkWaylandDisplay *display) { display->xkb_context = xkb_context_new (0); } void gdk_wayland_display_set_cursor_theme (GdkDisplay *display, const gchar *name, gint size) { GdkWaylandDisplay *wayland_display = GDK_WAYLAND_DISPLAY(display); struct wl_cursor_theme *theme; g_assert (wayland_display); g_assert (wayland_display->shm); theme = wl_cursor_theme_load (name, size, wayland_display->shm); if (theme == NULL) { g_warning ("Failed to load cursor theme %s\n", name); return; } _gdk_wayland_display_update_cursors (wayland_display, theme); if (wayland_display->cursor_theme != NULL) wl_cursor_theme_destroy (wayland_display->cursor_theme); wayland_display->cursor_theme = theme; } static void _gdk_wayland_display_load_cursor_theme (GdkWaylandDisplay *wayland_display) { guint size; const gchar *name; GValue v = G_VALUE_INIT; g_assert (wayland_display); g_assert (wayland_display->shm); g_value_init (&v, G_TYPE_INT); if (gdk_screen_get_setting (wayland_display->screen, "gtk-cursor-theme-size", &v)) size = g_value_get_int (&v); else size = 32; g_value_unset (&v); g_value_init (&v, G_TYPE_STRING); if (gdk_screen_get_setting (wayland_display->screen, "gtk-cursor-theme-name", &v)) name = g_value_get_string (&v); else name = "default"; gdk_wayland_display_set_cursor_theme (GDK_DISPLAY (wayland_display), name, size); g_value_unset (&v); } guint32 _gdk_wayland_display_get_serial (GdkWaylandDisplay *wayland_display) { return wayland_display->serial; } void _gdk_wayland_display_update_serial (GdkWaylandDisplay *wayland_display, guint32 serial) { wayland_display->serial = serial; } /** * gdk_wayland_display_get_wl_display * @display: (type GdkWaylandDisplay): a #GdkDisplay * * Returns the Wayland wl_display of a #GdkDisplay * * Returns: (transfer none): a Wayland wl_display * * Since: 3.8 */ struct wl_display * gdk_wayland_display_get_wl_display(GdkDisplay *display) { GdkWaylandDisplay *wayland_display = GDK_WAYLAND_DISPLAY(display); g_return_val_if_fail (GDK_IS_WAYLAND_DISPLAY(display), NULL); return wayland_display->wl_display; } /** * gdk_wayland_display_get_wl_compositor * @display: (type GdkWaylandDisplay): a #GdkDisplay * * Returns the Wayland global singleton compositor of a #GdkDisplay * * Returns: (transfer none): a Wayland wl_compositor * * Since: 3.8 */ struct wl_compositor * gdk_wayland_display_get_wl_compositor (GdkDisplay *display) { GdkWaylandDisplay *wayland_display = GDK_WAYLAND_DISPLAY(display); g_return_val_if_fail (GDK_IS_WAYLAND_DISPLAY(display), NULL); return wayland_display->compositor; } /** * gdk_wayland_display_get_xdg_shell * @display: (type GdkWaylandDisplay): a #GdkDisplay * * Returns the Wayland global singleton shell of a #GdkDisplay * * Returns: (transfer none): a Wayland xdg_shell * * Since: 3.8 */ struct xdg_shell * gdk_wayland_display_get_xdg_shell (GdkDisplay *display) { GdkWaylandDisplay *wayland_display = GDK_WAYLAND_DISPLAY(display); g_return_val_if_fail (GDK_IS_WAYLAND_DISPLAY(display), NULL); return wayland_display->xdg_shell; }