/* * 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" #ifdef GDK_WAYLAND_USE_EGL #include #endif #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" 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 = list->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 output_handle_geometry(void *data, struct wl_output *wl_output, int x, int y, int physical_width, int physical_height, int subpixel, const char *make, const char *model) { /* g_signal_emit_by_name (screen, "monitors-changed"); g_signal_emit_by_name (screen, "size-changed"); */ } static void display_handle_mode(void *data, struct wl_output *wl_output, uint32_t flags, int width, int height, int refresh) { } static const struct wl_output_listener output_listener = { output_handle_geometry, display_handle_mode }; static void gdk_display_handle_global(struct wl_display *display, uint32_t id, const char *interface, uint32_t version, void *data) { GdkWaylandDisplay *display_wayland = data; GdkDisplay *gdk_display = GDK_DISPLAY_OBJECT (data); struct wl_seat *seat; if (strcmp(interface, "wl_compositor") == 0) { display_wayland->compositor = wl_display_bind(display, id, &wl_compositor_interface); } else if (strcmp(interface, "wl_shm") == 0) { display_wayland->shm = wl_display_bind(display, id, &wl_shm_interface); /* SHM interface is prerequisite */ _gdk_wayland_display_load_cursor_theme(display_wayland); } else if (strcmp(interface, "wl_shell") == 0) { display_wayland->shell = wl_display_bind(display, id, &wl_shell_interface); } else if (strcmp(interface, "wl_output") == 0) { display_wayland->output = wl_display_bind(display, id, &wl_output_interface); wl_output_add_listener(display_wayland->output, &output_listener, display_wayland); } else if (strcmp(interface, "wl_seat") == 0) { seat = wl_display_bind (display, id, &wl_seat_interface); _gdk_wayland_device_manager_add_device (gdk_display->device_manager, seat); } else if (strcmp(interface, "wl_data_device_manager") == 0) { display_wayland->data_device_manager = wl_display_bind(display, id, &wl_data_device_manager_interface); } } #ifdef GDK_WAYLAND_USE_EGL static gboolean gdk_display_init_egl(GdkDisplay *display) { GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (display); EGLint major, minor, i; void *p; static const struct { const char *f; unsigned int offset; } extension_functions[] = { { "glEGLImageTargetTexture2DOES", offsetof(GdkWaylandDisplay, image_target_texture_2d) }, { "eglCreateImageKHR", offsetof(GdkWaylandDisplay, create_image) }, { "eglDestroyImageKHR", offsetof(GdkWaylandDisplay, destroy_image) } }; display_wayland->egl_display = eglGetDisplay(display_wayland->wl_display); if (!eglInitialize(display_wayland->egl_display, &major, &minor)) { fprintf(stderr, "failed to initialize display\n"); return FALSE; } eglBindAPI(EGL_OPENGL_API); display_wayland->egl_context = eglCreateContext(display_wayland->egl_display, NULL, EGL_NO_CONTEXT, NULL); if (display_wayland->egl_context == NULL) { fprintf(stderr, "failed to create context\n"); return FALSE; } if (!eglMakeCurrent(display_wayland->egl_display, NULL, NULL, display_wayland->egl_context)) { fprintf(stderr, "faile to make context current\n"); return FALSE; } display_wayland->cairo_device = cairo_egl_device_create(display_wayland->egl_display, display_wayland->egl_context); if (cairo_device_status (display_wayland->cairo_device) != CAIRO_STATUS_SUCCESS) { fprintf(stderr, "failed to get cairo drm device\n"); return FALSE; } for (i = 0; i < G_N_ELEMENTS(extension_functions); i++) { p = eglGetProcAddress(extension_functions[i].f); *(void **) ((char *) display_wayland + extension_functions[i].offset) = p; if (p == NULL) { fprintf(stderr, "failed to look up %s\n", extension_functions[i].f); return FALSE; } } return TRUE; } #endif GdkDisplay * _gdk_wayland_display_open (const gchar *display_name) { struct wl_display *wl_display; GdkDisplay *display; GdkWaylandDisplay *display_wayland; 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. */ wl_display_add_global_listener(display_wayland->wl_display, gdk_display_handle_global, display_wayland); #ifdef GDK_WAYLAND_USE_EGL gdk_display_init_egl(display); #else wl_display_iterate(wl_display, WL_DISPLAY_READABLE); wl_display_roundtrip(wl_display); #endif display_wayland->event_source = _gdk_wayland_display_event_source_new (display); gdk_input_init (display); g_signal_emit_by_name (display, "opened"); g_signal_emit_by_name (gdk_display_manager_get(), "display_opened", display); return display; } static void gdk_wayland_display_dispose (GObject *object) { GdkWaylandDisplay *display_wayland = GDK_WAYLAND_DISPLAY (object); _gdk_wayland_display_manager_remove_display (gdk_display_manager_get (), GDK_DISPLAY (display_wayland)); 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; } #ifdef GDK_WAYLAND_USE_EGL eglTerminate(display_wayland->egl_display); #endif 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); /* Keymap */ if (display_wayland->keymap) g_object_unref (display_wayland->keymap); /* 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 gint gdk_wayland_display_get_n_screens (GdkDisplay *display) { return 1; } static GdkScreen * gdk_wayland_display_get_screen (GdkDisplay *display, gint screen_num) { g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL); g_return_val_if_fail (screen_num == 0, NULL); return GDK_WAYLAND_DISPLAY (display)->screen; } 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) _gdk_wayland_display_flush (display, GDK_WAYLAND_DISPLAY (display)->event_source); } 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 TRUE; } static gboolean gdk_wayland_display_supports_input_shapes (GdkDisplay *display) { return TRUE; } static gboolean gdk_wayland_display_supports_composite (GdkDisplay *display) { return TRUE; } 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; } void _gdk_wayland_display_make_default (GdkDisplay *display) { } /** * 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 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); printf ("startup message: %s\n", message->str); 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) { } static GdkKeymap * gdk_wayland_display_get_keymap (GdkDisplay *display) { GdkWaylandDisplay *display_wayland; g_return_val_if_fail (GDK_IS_DISPLAY (display), NULL); display_wayland = GDK_WAYLAND_DISPLAY (display); if (!display_wayland->keymap) display_wayland->keymap = _gdk_wayland_keymap_new (display); return display_wayland->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_n_screens = gdk_wayland_display_get_n_screens; display_class->get_screen = gdk_wayland_display_get_screen; 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_pixbuf = _gdk_wayland_display_get_cursor_for_pixbuf; 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) { _gdk_wayland_display_manager_add_display (gdk_display_manager_get (), GDK_DISPLAY (display)); } static void _gdk_wayland_display_load_cursor_theme (GdkWaylandDisplay *wayland_display) { guint w, h; gchar *theme_name = NULL; /* FIXME: Do something here */ g_assert (wayland_display); g_assert (wayland_display->shm); _gdk_wayland_display_get_default_cursor_size (GDK_DISPLAY (wayland_display), &w, &h); wayland_display->cursor_theme = wl_cursor_theme_load (theme_name, w, wayland_display->shm); }