/* * Wayland Support * * Copyright (C) 2012,2013 Intel Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program 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 * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "meta-wayland-private.h" #include "meta-xwayland-private.h" #include "meta-wayland-stage.h" #include "meta-window-actor-private.h" #include "meta-wayland-seat.h" #include "meta-wayland-keyboard.h" #include "meta-wayland-pointer.h" #include "meta-wayland-data-device.h" #include "meta-cursor-tracker-private.h" #include "display-private.h" #include "window-private.h" #include #include #include "frame.h" #include "meta-idle-monitor-private.h" #include "meta-weston-launch.h" #include "monitor-private.h" static MetaWaylandCompositor _meta_wayland_compositor; MetaWaylandCompositor * meta_wayland_compositor_get_default (void) { return &_meta_wayland_compositor; } static guint32 get_time (void) { struct timeval tv; gettimeofday (&tv, NULL); return tv.tv_sec * 1000 + tv.tv_usec / 1000; } static gboolean wayland_event_source_prepare (GSource *base, int *timeout) { WaylandEventSource *source = (WaylandEventSource *)base; *timeout = -1; wl_display_flush_clients (source->display); return FALSE; } static gboolean wayland_event_source_check (GSource *base) { WaylandEventSource *source = (WaylandEventSource *)base; return source->pfd.revents; } static gboolean wayland_event_source_dispatch (GSource *base, GSourceFunc callback, void *data) { WaylandEventSource *source = (WaylandEventSource *)base; struct wl_event_loop *loop = wl_display_get_event_loop (source->display); wl_event_loop_dispatch (loop, 0); return TRUE; } static GSourceFuncs wayland_event_source_funcs = { wayland_event_source_prepare, wayland_event_source_check, wayland_event_source_dispatch, NULL }; static GSource * wayland_event_source_new (struct wl_display *display) { WaylandEventSource *source; struct wl_event_loop *loop = wl_display_get_event_loop (display); source = (WaylandEventSource *) g_source_new (&wayland_event_source_funcs, sizeof (WaylandEventSource)); source->display = display; source->pfd.fd = wl_event_loop_get_fd (loop); source->pfd.events = G_IO_IN | G_IO_ERR; g_source_add_poll (&source->source, &source->pfd); return &source->source; } static void meta_wayland_buffer_destroy_handler (struct wl_listener *listener, void *data) { MetaWaylandBuffer *buffer = wl_container_of (listener, buffer, destroy_listener); wl_signal_emit (&buffer->destroy_signal, buffer); g_slice_free (MetaWaylandBuffer, buffer); } MetaWaylandBuffer * meta_wayland_buffer_from_resource (struct wl_resource *resource) { MetaWaylandBuffer *buffer; struct wl_listener *listener; listener = wl_resource_get_destroy_listener (resource, meta_wayland_buffer_destroy_handler); if (listener) { buffer = wl_container_of (listener, buffer, destroy_listener); } else { buffer = g_slice_new0 (MetaWaylandBuffer); buffer->resource = resource; wl_signal_init (&buffer->destroy_signal); buffer->destroy_listener.notify = meta_wayland_buffer_destroy_handler; wl_resource_add_destroy_listener (resource, &buffer->destroy_listener); } return buffer; } static void meta_wayland_buffer_reference_handle_destroy (struct wl_listener *listener, void *data) { MetaWaylandBufferReference *ref = wl_container_of (listener, ref, destroy_listener); g_assert (data == ref->buffer); ref->buffer = NULL; } void meta_wayland_buffer_reference (MetaWaylandBufferReference *ref, MetaWaylandBuffer *buffer) { if (ref->buffer && buffer != ref->buffer) { ref->buffer->busy_count--; if (ref->buffer->busy_count == 0) { g_clear_pointer (&ref->buffer->texture, cogl_object_unref); g_assert (wl_resource_get_client (ref->buffer->resource)); wl_resource_queue_event (ref->buffer->resource, WL_BUFFER_RELEASE); } wl_list_remove (&ref->destroy_listener.link); } if (buffer && buffer != ref->buffer) { buffer->busy_count++; wl_signal_add (&buffer->destroy_signal, &ref->destroy_listener); } ref->buffer = buffer; ref->destroy_listener.notify = meta_wayland_buffer_reference_handle_destroy; } void meta_wayland_compositor_set_input_focus (MetaWaylandCompositor *compositor, MetaWindow *window) { MetaWaylandSurface *surface = window ? window->surface : NULL; meta_wayland_keyboard_set_focus (&compositor->seat->keyboard, surface); meta_wayland_data_device_set_keyboard_focus (compositor->seat); } void meta_wayland_compositor_repick (MetaWaylandCompositor *compositor) { meta_wayland_seat_repick (compositor->seat, NULL); } static void meta_wayland_compositor_create_surface (struct wl_client *wayland_client, struct wl_resource *wayland_compositor_resource, guint32 id) { MetaWaylandCompositor *compositor = wl_resource_get_user_data (wayland_compositor_resource); MetaWaylandSurface *surface; surface = meta_wayland_surface_create (compositor, wayland_client, id, MIN (META_WL_SURFACE_VERSION, wl_resource_get_version (wayland_compositor_resource))); compositor->surfaces = g_list_prepend (compositor->surfaces, surface); } static void meta_wayland_region_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void meta_wayland_region_add (struct wl_client *client, struct wl_resource *resource, gint32 x, gint32 y, gint32 width, gint32 height) { MetaWaylandRegion *region = wl_resource_get_user_data (resource); cairo_rectangle_int_t rectangle = { x, y, width, height }; cairo_region_union_rectangle (region->region, &rectangle); } static void meta_wayland_region_subtract (struct wl_client *client, struct wl_resource *resource, gint32 x, gint32 y, gint32 width, gint32 height) { MetaWaylandRegion *region = wl_resource_get_user_data (resource); cairo_rectangle_int_t rectangle = { x, y, width, height }; cairo_region_subtract_rectangle (region->region, &rectangle); } const struct wl_region_interface meta_wayland_region_interface = { meta_wayland_region_destroy, meta_wayland_region_add, meta_wayland_region_subtract }; static void meta_wayland_region_resource_destroy_cb (struct wl_resource *resource) { MetaWaylandRegion *region = wl_resource_get_user_data (resource); cairo_region_destroy (region->region); g_slice_free (MetaWaylandRegion, region); } static void meta_wayland_compositor_create_region (struct wl_client *wayland_client, struct wl_resource *compositor_resource, uint32_t id) { MetaWaylandRegion *region = g_slice_new0 (MetaWaylandRegion); region->resource = wl_resource_create (wayland_client, &wl_region_interface, MIN (META_WL_REGION_VERSION, wl_resource_get_version (compositor_resource)), id); wl_resource_set_implementation (region->resource, &meta_wayland_region_interface, region, meta_wayland_region_resource_destroy_cb); region->region = cairo_region_create (); } typedef struct { MetaOutput *output; struct wl_global *global; int x, y; enum wl_output_transform transform; GList *resources; } MetaWaylandOutput; static void output_resource_destroy (struct wl_resource *res) { MetaWaylandOutput *wayland_output; wayland_output = wl_resource_get_user_data (res); wayland_output->resources = g_list_remove (wayland_output->resources, res); } static void bind_output (struct wl_client *client, void *data, guint32 version, guint32 id) { MetaWaylandOutput *wayland_output = data; MetaOutput *output = wayland_output->output; struct wl_resource *resource; guint mode_flags; resource = wl_resource_create (client, &wl_output_interface, MIN (META_WL_OUTPUT_VERSION, version), id); wayland_output->resources = g_list_prepend (wayland_output->resources, resource); wl_resource_set_user_data (resource, wayland_output); wl_resource_set_destructor (resource, output_resource_destroy); meta_verbose ("Binding output %p/%s (%u, %u, %u, %u) x %f\n", output, output->name, output->crtc->rect.x, output->crtc->rect.y, output->crtc->rect.width, output->crtc->rect.height, output->crtc->current_mode->refresh_rate); wl_resource_post_event (resource, WL_OUTPUT_GEOMETRY, (int)output->crtc->rect.x, (int)output->crtc->rect.y, output->width_mm, output->height_mm, /* Cogl values reflect XRandR values, and so does wayland */ output->subpixel_order, output->vendor, output->product, output->crtc->transform); g_assert (output->crtc->current_mode != NULL); mode_flags = WL_OUTPUT_MODE_CURRENT; if (output->crtc->current_mode == output->preferred_mode) mode_flags |= WL_OUTPUT_MODE_PREFERRED; wl_resource_post_event (resource, WL_OUTPUT_MODE, mode_flags, (int)output->crtc->current_mode->width, (int)output->crtc->current_mode->height, (int)output->crtc->current_mode->refresh_rate); if (version >= META_WL_OUTPUT_HAS_DONE) wl_resource_post_event (resource, WL_OUTPUT_DONE); } static void wayland_output_destroy_notify (gpointer data) { MetaWaylandOutput *wayland_output = data; GList *resources; /* Make sure the destructors don't mess with the list */ resources = wayland_output->resources; wayland_output->resources = NULL; wl_global_destroy (wayland_output->global); g_list_free (resources); g_slice_free (MetaWaylandOutput, wayland_output); } static void wayland_output_update_for_output (MetaWaylandOutput *wayland_output, MetaOutput *output) { GList *iter; guint mode_flags; g_assert (output->crtc->current_mode != NULL); mode_flags = WL_OUTPUT_MODE_CURRENT; if (output->crtc->current_mode == output->preferred_mode) mode_flags |= WL_OUTPUT_MODE_PREFERRED; for (iter = wayland_output->resources; iter; iter = iter->next) { struct wl_resource *resource = iter->data; if (wayland_output->x != output->crtc->rect.x || wayland_output->y != output->crtc->rect.y || wayland_output->transform != output->crtc->transform) { wl_resource_post_event (resource, WL_OUTPUT_GEOMETRY, (int)output->crtc->rect.x, (int)output->crtc->rect.y, output->width_mm, output->height_mm, output->subpixel_order, output->vendor, output->product, output->crtc->transform); } wl_resource_post_event (resource, WL_OUTPUT_MODE, mode_flags, (int)output->crtc->current_mode->width, (int)output->crtc->current_mode->height, (int)output->crtc->current_mode->refresh_rate); } /* It's very important that we change the output pointer here, as the old structure is about to be freed by MetaMonitorManager */ wayland_output->output = output; wayland_output->x = output->crtc->rect.x; wayland_output->y = output->crtc->rect.y; wayland_output->transform = output->crtc->transform; } static GHashTable * meta_wayland_compositor_update_outputs (MetaWaylandCompositor *compositor, MetaMonitorManager *monitors) { MetaOutput *outputs; unsigned int i, n_outputs; GHashTable *new_table; outputs = meta_monitor_manager_get_outputs (monitors, &n_outputs); new_table = g_hash_table_new_full (NULL, NULL, NULL, wayland_output_destroy_notify); for (i = 0; i < n_outputs; i++) { MetaOutput *output = &outputs[i]; MetaWaylandOutput *wayland_output; /* wayland does not expose disabled outputs */ if (output->crtc == NULL) { g_hash_table_remove (compositor->outputs, GSIZE_TO_POINTER (output->output_id)); continue; } wayland_output = g_hash_table_lookup (compositor->outputs, GSIZE_TO_POINTER (output->output_id)); if (wayland_output) { g_hash_table_steal (compositor->outputs, GSIZE_TO_POINTER (output->output_id)); } else { wayland_output = g_slice_new0 (MetaWaylandOutput); wayland_output->global = wl_global_create (compositor->wayland_display, &wl_output_interface, META_WL_OUTPUT_VERSION, wayland_output, bind_output); } wayland_output_update_for_output (wayland_output, output); g_hash_table_insert (new_table, GSIZE_TO_POINTER (output->output_id), wayland_output); } g_hash_table_destroy (compositor->outputs); return new_table; } const static struct wl_compositor_interface meta_wayland_compositor_interface = { meta_wayland_compositor_create_surface, meta_wayland_compositor_create_region }; void meta_wayland_compositor_paint_finished (MetaWaylandCompositor *compositor) { while (!wl_list_empty (&compositor->frame_callbacks)) { MetaWaylandFrameCallback *callback = wl_container_of (compositor->frame_callbacks.next, callback, link); wl_callback_send_done (callback->resource, get_time ()); wl_resource_destroy (callback->resource); } } static void compositor_bind (struct wl_client *client, void *data, guint32 version, guint32 id) { MetaWaylandCompositor *compositor = data; struct wl_resource *resource; resource = wl_resource_create (client, &wl_compositor_interface, MIN (META_WL_COMPOSITOR_VERSION, version), id); wl_resource_set_implementation (resource, &meta_wayland_compositor_interface, compositor, NULL); } static void stage_destroy_cb (void) { meta_quit (META_EXIT_SUCCESS); } void meta_wayland_compositor_update (MetaWaylandCompositor *compositor, const ClutterEvent *event) { ClutterInputDevice *device, *source_device; MetaIdleMonitor *core_monitor, *device_monitor; int device_id; device = clutter_event_get_device (event); if (device == NULL) return; device_id = clutter_input_device_get_device_id (device); core_monitor = meta_idle_monitor_get_core (); device_monitor = meta_idle_monitor_get_for_device (device_id); meta_idle_monitor_reset_idletime (core_monitor); meta_idle_monitor_reset_idletime (device_monitor); source_device = clutter_event_get_source_device (event); if (source_device != device) { device_id = clutter_input_device_get_device_id (device); device_monitor = meta_idle_monitor_get_for_device (device_id); meta_idle_monitor_reset_idletime (device_monitor); } switch (event->type) { case CLUTTER_MOTION: case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_RELEASE: case CLUTTER_SCROLL: meta_wayland_seat_update_pointer (compositor->seat, event); default: break; } } gboolean meta_wayland_compositor_handle_event (MetaWaylandCompositor *compositor, const ClutterEvent *event) { return meta_wayland_seat_handle_event (compositor->seat, event); } static void on_monitors_changed (MetaMonitorManager *monitors, MetaWaylandCompositor *compositor) { compositor->outputs = meta_wayland_compositor_update_outputs (compositor, monitors); } static void set_gnome_env (const char *name, const char *value) { GDBusConnection *session_bus; GError *error; setenv (name, value, TRUE); session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); g_assert (session_bus); error = NULL; g_dbus_connection_call_sync (session_bus, "org.gnome.SessionManager", "/org/gnome/SessionManager", "org.gnome.SessionManager", "Setenv", g_variant_new ("(ss)", name, value), NULL, G_DBUS_CALL_FLAGS_NONE, -1, NULL, &error); if (error) { meta_warning ("Failed to set environment variable %s for gnome-session: %s\n", name, error->message); g_clear_error (&error); } } static void meta_wayland_log_func (const char *fmt, va_list arg) { char *str = g_strdup_vprintf (fmt, arg); g_warning ("WL: %s", str); g_free (str); } static gboolean are_we_native (int *out_drm_fd) { ClutterBackend *backend = clutter_get_default_backend (); CoglContext *cogl_context = clutter_backend_get_cogl_context (backend); CoglRenderer *cogl_renderer = cogl_display_get_renderer (cogl_context_get_display (cogl_context)); if (cogl_renderer_get_winsys_id (cogl_renderer) == COGL_WINSYS_ID_EGL_KMS) { *out_drm_fd = cogl_kms_renderer_get_kms_fd (cogl_renderer); return TRUE; } else { return FALSE; } } void meta_wayland_init (void) { MetaWaylandCompositor *compositor = &_meta_wayland_compositor; MetaMonitorManager *monitors; char *display_name; int drm_fd; memset (compositor, 0, sizeof (MetaWaylandCompositor)); compositor->wayland_display = wl_display_create (); if (compositor->wayland_display == NULL) g_error ("failed to create wayland display"); wl_display_init_shm (compositor->wayland_display); wl_log_set_handler_server(meta_wayland_log_func); wl_list_init (&compositor->frame_callbacks); if (!wl_global_create (compositor->wayland_display, &wl_compositor_interface, META_WL_COMPOSITOR_VERSION, compositor, compositor_bind)) g_error ("Failed to register wayland compositor object"); compositor->wayland_loop = wl_display_get_event_loop (compositor->wayland_display); compositor->wayland_event_source = wayland_event_source_new (compositor->wayland_display); /* XXX: Here we are setting the wayland event source to have a * slightly lower priority than the X event source, because we are * much more likely to get confused being told about surface changes * relating to X clients when we don't know what's happened to them * according to the X protocol. * * At some point we could perhaps try and get the X protocol proxied * over the wayland protocol so that we don't have to worry about * synchronizing the two command streams. */ g_source_set_priority (compositor->wayland_event_source, GDK_PRIORITY_EVENTS + 1); g_source_attach (compositor->wayland_event_source, NULL); clutter_wayland_set_compositor_display (compositor->wayland_display); if (getenv ("WESTON_LAUNCHER_SOCK")) compositor->launcher = meta_launcher_new (); if (clutter_init (NULL, NULL) != CLUTTER_INIT_SUCCESS) g_error ("Failed to initialize Clutter"); if (are_we_native (&drm_fd)) { GError *error = NULL; if (!meta_launcher_set_drm_fd (compositor->launcher, drm_fd, &error)) { g_error ("Failed to set DRM fd to weston-launch and become DRM master: %s", error->message); g_error_free (error); } compositor->native = TRUE; } else { compositor->native = FALSE; } meta_monitor_manager_initialize (); monitors = meta_monitor_manager_get (); g_signal_connect (monitors, "monitors-changed", G_CALLBACK (on_monitors_changed), compositor); compositor->outputs = g_hash_table_new_full (NULL, NULL, NULL, wayland_output_destroy_notify); compositor->outputs = meta_wayland_compositor_update_outputs (compositor, monitors); compositor->stage = meta_wayland_stage_new (); g_signal_connect (compositor->stage, "destroy", G_CALLBACK (stage_destroy_cb), NULL); meta_wayland_data_device_manager_init (compositor->wayland_display); compositor->seat = meta_wayland_seat_new (compositor->wayland_display, compositor->native); meta_wayland_init_shell (compositor); clutter_actor_show (compositor->stage); /* FIXME: find the first free name instead */ compositor->display_name = g_strdup ("wayland-0"); if (wl_display_add_socket (compositor->wayland_display, compositor->display_name)) g_error ("Failed to create socket"); /* XXX: It's important that we only try and start xwayland after we * have initialized EGL because EGL implements the "wl_drm" * interface which xwayland requires to determine what drm device * name it should use. * * By waiting until we've shown the stage above we ensure that the * underlying GL resources for the surface have also been allocated * and so EGL must be initialized by this point. */ if (!meta_xwayland_start (compositor)) g_error ("Failed to start X Wayland"); display_name = g_strdup_printf (":%d", compositor->xwayland_display_index); set_gnome_env ("DISPLAY", display_name); g_free (display_name); set_gnome_env ("WAYLAND_DISPLAY", compositor->display_name); } void meta_wayland_finalize (void) { MetaWaylandCompositor *compositor; compositor = meta_wayland_compositor_get_default (); meta_xwayland_stop (compositor); meta_launcher_free (compositor->launcher); } MetaLauncher * meta_wayland_compositor_get_launcher (MetaWaylandCompositor *compositor) { return compositor->launcher; } gboolean meta_wayland_compositor_is_native (MetaWaylandCompositor *compositor) { return compositor->native; }