static void clutter_backend_wayland_dispose (GObject *gobject) { ClutterBackendWayland *backend_wayland = CLUTTER_BACKEND_WAYLAND (gobject); if (backend_wayland->device_manager) { g_object_unref (backend_wayland->device_manager); backend_wayland->device_manager = NULL; } if (backend_wayland->cursor_buffer) { wl_buffer_destroy (backend_wayland->cursor_buffer); backend_wayland->cursor_buffer = NULL; } if (backend_wayland->cursor_theme) { wl_cursor_theme_destroy (backend_wayland->cursor_theme); backend_wayland->cursor_theme = NULL; } G_OBJECT_CLASS (clutter_backend_wayland_parent_class)->dispose (gobject); }
static void wayland_swap_buffers (ClutterStageWayland *stage_wayland) { ClutterBackend *backend = clutter_get_default_backend (); ClutterBackendWayland *backend_wayland = CLUTTER_BACKEND_WAYLAND (backend); ClutterStageWaylandWaylandBuffer *buffer; buffer = stage_wayland->front_buffer; stage_wayland->front_buffer = stage_wayland->back_buffer; stage_wayland->back_buffer = buffer; wayland_damage_buffer(stage_wayland->front_buffer); wl_surface_attach (stage_wayland->wayland_surface, stage_wayland->front_buffer->wayland_buffer, /* 0,0 here is "relative to the old buffer," not absolute */ 0, 0); wl_surface_map_toplevel (stage_wayland->wayland_surface); stage_wayland->pending_swaps++; wl_display_frame_callback (backend_wayland->wayland_display, wayland_frame_callback, stage_wayland); _cogl_swap_buffers_notify (); }
void _clutter_device_manager_wayland_add_input_group (ClutterDeviceManager *manager, uint32_t id) { ClutterBackend *backend = _clutter_device_manager_get_backend (manager); ClutterBackendWayland *backend_wayland = CLUTTER_BACKEND_WAYLAND (backend); ClutterInputDeviceWayland *device; device = g_object_new (CLUTTER_TYPE_INPUT_DEVICE_WAYLAND, "id", id, "device-type", CLUTTER_POINTER_DEVICE, "name", "wayland device", "enabled", TRUE, NULL); device->input_device = wl_display_bind (backend_wayland->wayland_display, id, &wl_input_device_interface); wl_input_device_add_listener (device->input_device, &_clutter_input_device_wayland_listener, device); wl_input_device_set_user_data (device->input_device, device); device->xkb = _clutter_xkb_desc_new (NULL, option_xkb_layout, option_xkb_variant, option_xkb_options); if (!device->xkb) CLUTTER_NOTE (BACKEND, "Failed to compile keymap"); _clutter_device_manager_add_device (manager, CLUTTER_INPUT_DEVICE (device)); }
static ClutterStageWaylandWaylandBuffer * wayland_create_buffer (ClutterGeometry *geom) { ClutterBackend *backend = clutter_get_default_backend (); ClutterBackendWayland *backend_wayland = CLUTTER_BACKEND_WAYLAND (backend); ClutterStageWaylandWaylandBuffer *buffer; cairo_rectangle_int_t rect; if (backend_wayland->drm_enabled && backend_wayland->wayland_drm != NULL) buffer = wayland_create_drm_buffer (backend_wayland, geom); else if (backend_wayland->wayland_shm != NULL) buffer = wayland_create_shm_buffer (backend_wayland, geom); else return NULL; buffer->offscreen = cogl_offscreen_new_to_texture (buffer->tex); rect.x = geom->x; rect.y = geom->y; rect.width = geom->width; rect.height = geom->height; buffer->dirty_region = cairo_region_create_rectangle (&rect); return buffer; }
void _clutter_events_wayland_init (ClutterBackend *backend) { ClutterBackendWayland *backend_wayland = CLUTTER_BACKEND_WAYLAND (backend); /* XXX: We actually create the wayland device manager in the backend * post_parse vfunc because that's the point where we connect to a compositor * and that's also the point where we will be notified of input devices so we * need the device-manager to exist early on. * * To be consistent with other clutter backends though we only associate the * device manager with the backend when _clutter_events_wayland_init() is * called in _clutter_backend_init_events(). This should still allow the * runtime selection of an alternative input backend if desired and in that * case the wayland device manager will be benign. * * FIXME: At some point we could perhaps collapse the * _clutter_backend_post_parse(), and _clutter_backend_init_events() * functions into one called something like _clutter_backend_init() which * would allow the real backend to manage the precise order of * initialization. */ backend->device_manager = g_object_ref (backend_wayland->device_manager); }
static ClutterFeatureFlags clutter_backend_wayland_get_features (ClutterBackend *backend) { ClutterBackendWayland *backend_wayland = CLUTTER_BACKEND_WAYLAND (backend); ClutterFeatureFlags flags = 0; g_assert (backend_wayland->egl_context != NULL); flags |= CLUTTER_FEATURE_STAGE_MULTIPLE | CLUTTER_FEATURE_SWAP_EVENTS | CLUTTER_FEATURE_SYNC_TO_VBLANK; CLUTTER_NOTE (BACKEND, "Checking features\n" "GL_VENDOR: %s\n" "GL_RENDERER: %s\n" "GL_VERSION: %s\n" "EGL_VENDOR: %s\n" "EGL_VERSION: %s\n" "EGL_EXTENSIONS: %s\n", glGetString (GL_VENDOR), glGetString (GL_RENDERER), glGetString (GL_VERSION), eglQueryString (backend_wayland->edpy, EGL_VENDOR), eglQueryString (backend_wayland->edpy, EGL_VERSION), eglQueryString (backend_wayland->edpy, EGL_EXTENSIONS)); return flags; }
static void clutter_backend_wayland_dispose (GObject *gobject) { ClutterBackendWayland *backend_wayland = CLUTTER_BACKEND_WAYLAND (gobject); /* We chain up before disposing our own resources so that ClutterBackend will destroy all of the stages before we destroy the egl context. Otherwise the actors may try to make GL calls during destruction which causes a crash */ G_OBJECT_CLASS (_clutter_backend_wayland_parent_class)->dispose (gobject); if (backend_wayland->egl_context) { eglDestroyContext (backend_wayland->edpy, backend_wayland->egl_context); backend_wayland->egl_context = NULL; } if (backend_wayland->edpy) { eglTerminate (backend_wayland->edpy); backend_wayland->edpy = 0; } if (backend_wayland->drm_fd != -1) { close (backend_wayland->drm_fd); backend_wayland->drm_fd = -1; } }
static gboolean clutter_backend_wayland_create_context (ClutterBackend *backend, GError **error) { ClutterBackendWayland *backend_wayland = CLUTTER_BACKEND_WAYLAND (backend); const gchar *egl_extensions = NULL; gboolean status; int retry_cookie; gboolean try_fallback; GError *try_error = NULL; if (backend_wayland->egl_context != EGL_NO_CONTEXT) return TRUE; #if defined(HAVE_COGL_GL) eglBindAPI (EGL_OPENGL_API); #else eglBindAPI (EGL_OPENGL_ES_API); #endif egl_extensions = eglQueryString (backend_wayland->edpy, EGL_EXTENSIONS); if (!_cogl_check_extension (_COGL_SURFACELESS_EXTENSION, egl_extensions)) { g_debug("Could not find the " _COGL_SURFACELESS_EXTENSION " extension; falling back to binding a dummy surface"); if (!make_dummy_surface(backend_wayland)) { g_set_error (error, CLUTTER_INIT_ERROR, CLUTTER_INIT_ERROR_BACKEND, "Could not create dummy surface"); return FALSE; } } else { backend_wayland->egl_config = NULL; backend_wayland->egl_surface = EGL_NO_SURFACE; } retry_cookie = 0; while (!(status = try_create_context (backend, retry_cookie, &try_fallback, &try_error)) && try_fallback) { g_warning ("Failed to create context: %s\nWill try fallback...", try_error->message); g_error_free (try_error); try_error = NULL; retry_cookie++; } if (!status) g_propagate_error (error, try_error); return status; }
static gboolean try_create_context (ClutterBackend *backend, int retry_cookie, gboolean *try_fallback, GError **error) { ClutterBackendWayland *backend_wayland = CLUTTER_BACKEND_WAYLAND (backend); const char *error_message; if (backend_wayland->egl_context == EGL_NO_CONTEXT) { #if defined(HAVE_COGL_GL) static const EGLint *attribs = NULL; #else static const EGLint attribs[] = { EGL_CONTEXT_CLIENT_VERSION, _COGL_GLES_VERSION, EGL_NONE }; #endif backend_wayland->egl_context = eglCreateContext (backend_wayland->edpy, backend_wayland->egl_config, EGL_NO_CONTEXT, attribs); if (backend_wayland->egl_context == EGL_NO_CONTEXT) { error_message = "Unable to create a suitable EGL context"; goto fail; } CLUTTER_NOTE (GL, "Created EGL Context"); } if (!eglMakeCurrent (backend_wayland->edpy, backend_wayland->egl_surface, backend_wayland->egl_surface, backend_wayland->egl_context)) { g_set_error (error, CLUTTER_INIT_ERROR, CLUTTER_INIT_ERROR_BACKEND, "Unable to MakeCurrent"); return FALSE; } return TRUE; fail: { *try_fallback = FALSE; g_set_error (error, CLUTTER_INIT_ERROR, CLUTTER_INIT_ERROR_BACKEND, "%s", error_message); return FALSE; } }
static gboolean clutter_backend_wayland_post_parse (ClutterBackend *backend, GError **error) { ClutterBackendWayland *backend_wayland = CLUTTER_BACKEND_WAYLAND (backend); /* TODO: expose environment variable/commandline option for this... */ backend_wayland->wayland_display = wl_display_connect (NULL); if (!backend_wayland->wayland_display) { g_set_error (error, CLUTTER_INIT_ERROR, CLUTTER_INIT_ERROR_BACKEND, "Failed to open Wayland display socket"); return FALSE; } backend_wayland->wayland_registry = wl_display_get_registry (backend_wayland->wayland_display); backend_wayland->wayland_source = _clutter_event_source_wayland_new (backend_wayland->wayland_display); g_source_attach (backend_wayland->wayland_source, NULL); g_object_set (clutter_settings_get_default (), "font-dpi", 96 * 1024, NULL); /* XXX: We require the device manager to exist as soon as we connect to the * compositor and setup an event handler because we will immediately be * notified of the available input devices which need to be associated with * the device-manager. * * FIXME: At some point we could perhaps just collapse the * _clutter_backend_post_parse(), and _clutter_backend_init_events() * functions into one called something like _clutter_backend_init() which * would allow the real backend to manage the precise order of * initialization. */ backend_wayland->device_manager = _clutter_device_manager_wayland_new (backend); /* Set up listener so we'll catch all events. */ wl_registry_add_listener (backend_wayland->wayland_registry, &wayland_registry_listener, backend_wayland); /* Wait until we have been notified about the compositor and shell objects */ while (!(backend_wayland->wayland_compositor && backend_wayland->wayland_shell)) wl_display_roundtrip (backend_wayland->wayland_display); /* We need the shm object before we can create the cursor */ clutter_backend_wayland_load_cursor (backend_wayland); return TRUE; }
static void clutter_wayland_handle_pointer_enter (void *data, struct wl_pointer *pointer, uint32_t serial, struct wl_surface *surface, wl_fixed_t x, wl_fixed_t y) { ClutterInputDeviceWayland *device = data; ClutterStageCogl *stage_cogl; ClutterEvent *event; ClutterBackend *backend; ClutterBackendWayland *backend_wayland; stage_cogl = wl_surface_get_user_data (surface); device->pointer_focus = stage_cogl; _clutter_input_device_set_stage (CLUTTER_INPUT_DEVICE (device), stage_cogl->wrapper); event = clutter_event_new (CLUTTER_ENTER); event->crossing.stage = stage_cogl->wrapper; event->crossing.time = 0; /* ?! */ event->crossing.x = wl_fixed_to_double(x); event->crossing.y = wl_fixed_to_double(y); event->crossing.source = CLUTTER_ACTOR (stage_cogl->wrapper); event->crossing.device = CLUTTER_INPUT_DEVICE (device); device->x = event->crossing.x; device->y = event->crossing.y; _clutter_event_push (event, FALSE); /* Set the cursor to the cursor loaded at backend initialisation */ backend = clutter_get_default_backend (); backend_wayland = CLUTTER_BACKEND_WAYLAND (backend); wl_pointer_set_cursor (pointer, serial, backend_wayland->cursor_surface, backend_wayland->cursor_x, backend_wayland->cursor_y); wl_surface_attach (backend_wayland->cursor_surface, backend_wayland->cursor_buffer, 0, 0); wl_surface_damage (backend_wayland->cursor_surface, 0, 0, 32, /* XXX: FFS */ 32); wl_surface_commit (backend_wayland->cursor_surface); }
static void wayland_free_drm_buffer (ClutterStageWaylandWaylandBuffer *generic_buffer) { ClutterBackend *backend = clutter_get_default_backend (); ClutterBackendWayland *backend_wayland = CLUTTER_BACKEND_WAYLAND (backend); EGLDisplay edpy = clutter_wayland_get_egl_display (); ClutterStageWaylandWaylandBufferDRM *buffer; buffer = (ClutterStageWaylandWaylandBufferDRM *)generic_buffer; glDeleteTextures (1, &buffer->texture); backend_wayland->destroy_image (edpy, buffer->drm_image); g_slice_free (ClutterStageWaylandWaylandBufferDRM, buffer); }
static void clutter_stage_wayland_set_fullscreen (ClutterStageWindow *stage_window, gboolean fullscreen) { ClutterStageWayland *stage_wayland = CLUTTER_STAGE_WAYLAND (stage_window); ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window); ClutterBackend *backend = CLUTTER_BACKEND (stage_cogl->backend); ClutterBackendWayland *backend_wayland = CLUTTER_BACKEND_WAYLAND (backend); ClutterActor *stage = _clutter_stage_window_get_wrapper (stage_window); stage_wayland->fullscreen = fullscreen; if (!stage_wayland->wayland_shell_surface) /* Not realized yet */ return; if (fullscreen) { _clutter_stage_update_state (stage_cogl->wrapper, 0, CLUTTER_STAGE_STATE_FULLSCREEN); /* FIXME: In future versions of the Wayland protocol we'll get a * configure with the dimensions we can use - but for now we have to * use the dimensions from the output's mode */ clutter_actor_set_size (stage, backend_wayland->output_width, backend_wayland->output_height); /* FIXME: And we must force a redraw so that new sized buffer gets * attached */ _clutter_stage_window_redraw (stage_window); wl_shell_surface_set_fullscreen (stage_wayland->wayland_shell_surface, WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, 0, NULL); } else { _clutter_stage_update_state (stage_cogl->wrapper, CLUTTER_STAGE_STATE_FULLSCREEN, 0); wl_shell_surface_set_toplevel (stage_wayland->wayland_shell_surface); } }
static ClutterStageWindow * clutter_backend_wayland_create_stage (ClutterBackend *backend, ClutterStage *wrapper, GError **error) { ClutterBackendWayland *backend_wayland = CLUTTER_BACKEND_WAYLAND (backend); ClutterStageWindow *stage; ClutterStageWayland *stage_wayland; stage = g_object_new (CLUTTER_TYPE_STAGE_WAYLAND, NULL); stage_wayland = CLUTTER_STAGE_WAYLAND (stage); stage_wayland->backend = backend_wayland; stage_wayland->wrapper = wrapper; return stage; }
static CoglRenderer * clutter_backend_wayland_get_renderer (ClutterBackend *backend, GError **error) { ClutterBackendWayland *backend_wayland = CLUTTER_BACKEND_WAYLAND (backend); CoglRenderer *renderer; CLUTTER_NOTE (BACKEND, "Creating a new wayland renderer"); renderer = cogl_renderer_new (); cogl_wayland_renderer_set_event_dispatch_enabled (renderer, !_no_event_dispatch); cogl_renderer_set_winsys_id (renderer, COGL_WINSYS_ID_EGL_WAYLAND); cogl_wayland_renderer_set_foreign_display (renderer, backend_wayland->wayland_display); return renderer; }
static gboolean clutter_stage_wayland_realize (ClutterStageWindow *stage_window) { ClutterStageWayland *stage_wayland = CLUTTER_STAGE_WAYLAND (stage_window); ClutterBackend *backend = clutter_get_default_backend (); ClutterBackendWayland *backend_wayland = CLUTTER_BACKEND_WAYLAND (backend); gfloat width, height; clutter_actor_get_size (CLUTTER_ACTOR (stage_wayland->wrapper), &width, &height); stage_wayland->pending_allocation.width = (gint)width; stage_wayland->pending_allocation.height = (gint)height; stage_wayland->allocation = stage_wayland->pending_allocation; stage_wayland->wayland_surface = wl_compositor_create_surface (backend_wayland->wayland_compositor); wl_surface_set_user_data (stage_wayland->wayland_surface, stage_wayland); stage_wayland->pick_buffer = wayland_create_buffer (&stage_wayland->allocation); return TRUE; }
static GObject * clutter_backend_wayland_constructor (GType gtype, guint n_params, GObjectConstructParam *params) { GObjectClass *parent_class; GObject *retval; if (!backend_singleton) { parent_class = G_OBJECT_CLASS (_clutter_backend_wayland_parent_class); retval = parent_class->constructor (gtype, n_params, params); backend_singleton = CLUTTER_BACKEND_WAYLAND (retval); return retval; } g_warning ("Attempting to create a new backend object. This should " "never happen, so we return the singleton instance."); return g_object_ref (backend_singleton); }
static gboolean clutter_backend_wayland_post_parse (ClutterBackend *backend, GError **error) { ClutterBackendWayland *backend_wayland = CLUTTER_BACKEND_WAYLAND (backend); EGLBoolean status; g_atexit (clutter_backend_at_exit); /* TODO: expose environment variable/commandline option for this... */ backend_wayland->wayland_display = wl_display_connect (NULL); if (!backend_wayland->wayland_display) { g_set_error (error, CLUTTER_INIT_ERROR, CLUTTER_INIT_ERROR_BACKEND, "Failed to open Wayland display socket"); return FALSE; } backend_wayland->wayland_source = _clutter_event_source_wayland_new (backend_wayland->wayland_display); g_source_attach (backend_wayland->wayland_source, NULL); /* Set up listener so we'll catch all events. */ wl_display_add_global_listener (backend_wayland->wayland_display, display_handle_global, backend_wayland); /* Process connection events. */ wl_display_iterate (backend_wayland->wayland_display, WL_DISPLAY_READABLE); if (!try_get_display(backend_wayland, error)) return FALSE; status = eglInitialize (backend_wayland->edpy, &backend_wayland->egl_version_major, &backend_wayland->egl_version_minor); if (status != EGL_TRUE) { g_set_error (error, CLUTTER_INIT_ERROR, CLUTTER_INIT_ERROR_BACKEND, "Unable to Initialize EGL"); return FALSE; } CLUTTER_NOTE (BACKEND, "EGL Reports version %i.%i", backend_wayland->egl_version_major, backend_wayland->egl_version_minor); backend_wayland->drm_enabled = try_enable_drm(backend_wayland, error); if (!backend_wayland->drm_enabled) { if (backend_wayland->wayland_shm == NULL) return FALSE; g_debug("Could not enable DRM buffers, falling back to SHM buffers"); g_clear_error(error); } return TRUE; }