Flutter Engine
fl_renderer_wayland.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #define WL_EGL_PLATFORM
6 
7 #include "fl_renderer_wayland.h"
8 #include "flutter/shell/platform/linux/egl_utils.h"
9 
10 #include <gdk/gdkwayland.h>
11 #include <wayland-egl-core.h>
12 #include <cstring>
13 
15  FlRenderer parent_instance;
16  wl_registry* registry;
17  wl_subcompositor* subcompositor;
18 
19  struct {
20  wl_subsurface* subsurface;
21  wl_surface* surface;
22  wl_egl_window* egl_window;
23  GdkRectangle geometry;
24  gint scale;
25  } subsurface;
26 
27  // The resource surface will not be mapped, but needs to be a wl_surface
28  // because ONLY window EGL surfaces are supported on Wayland.
29  struct {
30  wl_surface* surface;
31  wl_egl_window* egl_window;
32  } resource;
33 };
34 
35 G_DEFINE_TYPE(FlRendererWayland, fl_renderer_wayland, fl_renderer_get_type())
36 
37 // wl_registry.global callback.
38 static void registry_handle_global(void* data,
39  wl_registry* registry,
40  uint32_t id,
41  const char* name,
42  uint32_t max_version) {
43  FlRendererWayland* self = FL_RENDERER_WAYLAND(data);
44  if (strcmp(name, wl_subcompositor_interface.name) == 0) {
45  uint32_t version = MIN(
46  static_cast<uint32_t>(wl_subcompositor_interface.version), max_version);
47  self->subcompositor = static_cast<wl_subcompositor*>(
48  wl_registry_bind(registry, id, &wl_subcompositor_interface, version));
49  }
50 }
51 
52 // wl_registry.global_remove callback.
53 // Can be safely ignored unless we bind to globals that might be removed.
54 static void registry_handle_global_remove(void*, wl_registry*, uint32_t) {}
55 
56 static const wl_registry_listener registry_listener = {
57  .global = registry_handle_global,
58  .global_remove = registry_handle_global_remove,
59 };
60 
61 // The first time this function is called, all Wayland globals are initialized
62 // (which blocks for a round trip to the Wayland compositor).
63 // Subsequent calls return immediately.
64 static void fl_renderer_wayland_lazy_init_wl(FlRendererWayland* self) {
65  if (self->registry) {
66  return;
67  }
68 
69  GdkWaylandDisplay* gdk_display =
70  GDK_WAYLAND_DISPLAY(gdk_display_get_default());
71  g_return_if_fail(gdk_display);
72 
73  wl_display* display = gdk_wayland_display_get_wl_display(gdk_display);
74  self->registry = wl_display_get_registry(display);
75  wl_registry_add_listener(self->registry, &registry_listener, self);
76  wl_display_roundtrip(display);
77 }
78 
79 // Implements GObject::dispose.
80 static void fl_renderer_wayland_dispose(GObject* object) {
81  FlRendererWayland* self = FL_RENDERER_WAYLAND(object);
82 
83  g_clear_pointer(&self->registry, wl_registry_destroy);
84  g_clear_pointer(&self->subcompositor, wl_subcompositor_destroy);
85 
86  g_clear_pointer(&self->subsurface.subsurface, wl_subsurface_destroy);
87  g_clear_pointer(&self->subsurface.egl_window, wl_egl_window_destroy);
88  g_clear_pointer(&self->subsurface.surface, wl_surface_destroy);
89 
90  g_clear_pointer(&self->resource.egl_window, wl_egl_window_destroy);
91  g_clear_pointer(&self->resource.surface, wl_surface_destroy);
92 
93  G_OBJECT_CLASS(fl_renderer_wayland_parent_class)->dispose(object);
94 }
95 
96 // Implements FlRenderer::create_display.
97 static EGLDisplay fl_renderer_wayland_create_display(FlRenderer* /*renderer*/) {
98  GdkWaylandDisplay* gdk_display =
99  GDK_WAYLAND_DISPLAY(gdk_display_get_default());
100  g_return_val_if_fail(gdk_display, nullptr);
101  return eglGetDisplay(gdk_wayland_display_get_wl_display(gdk_display));
102 }
103 
104 static void fl_renderer_wayland_on_window_map(FlRendererWayland* self,
105  GtkWidget* widget) {
106  if (self->subsurface.subsurface) {
107  g_error("fl_renderer_wayland_on_window_map: already has a subsurface");
108  return;
109  }
110 
111  GdkWaylandDisplay* gdk_display =
112  GDK_WAYLAND_DISPLAY(gdk_display_get_default());
113 
114  wl_compositor* compositor =
115  gdk_wayland_display_get_wl_compositor(gdk_display);
116 
118  if (!self->subcompositor) {
119  g_error(
120  "fl_renderer_wayland_on_window_map: could not bind to "
121  "wl_subcompositor");
122  return;
123  }
124 
125  GdkWaylandWindow* window = GDK_WAYLAND_WINDOW(gtk_widget_get_window(widget));
126  if (!window) {
127  g_error("fl_renderer_wayland_on_window_map: not a Wayland window");
128  return;
129  }
130  wl_surface* toplevel_surface = gdk_wayland_window_get_wl_surface(window);
131  if (!toplevel_surface) {
132  g_error("fl_renderer_wayland_on_window_map: could not get wl_surface");
133  return;
134  }
135 
136  self->subsurface.subsurface = wl_subcompositor_get_subsurface(
137  self->subcompositor, self->subsurface.surface, toplevel_surface);
138  if (!self->subsurface.subsurface) {
139  g_error("fl_renderer_wayland_on_window_map: could not create subsurface");
140  return;
141  }
142 
143  wl_subsurface_set_desync(self->subsurface.subsurface);
144  wl_subsurface_set_position(self->subsurface.subsurface,
145  self->subsurface.geometry.x,
146  self->subsurface.geometry.y);
147 
148  // Give the subsurface an empty input region so the main surface gets input.
149  wl_region* region = wl_compositor_create_region(compositor);
150  wl_surface_set_input_region(self->subsurface.surface, region);
151  wl_region_destroy(region);
152 
153  wl_surface_commit(self->subsurface.surface);
154 }
155 
156 static void fl_renderer_wayland_on_window_unmap(FlRendererWayland* self,
157  GtkWidget* widget) {
158  g_clear_pointer(&self->subsurface.subsurface, wl_subsurface_destroy);
159 }
160 
161 // Implements FlRenderer::create_surfaces.
162 static gboolean fl_renderer_wayland_create_surfaces(FlRenderer* renderer,
163  GtkWidget* widget,
164  EGLDisplay display,
165  EGLConfig config,
166  EGLSurface* visible,
167  EGLSurface* resource,
168  GError** error) {
169  FlRendererWayland* self = FL_RENDERER_WAYLAND(renderer);
170 
171  if (self->subsurface.surface || self->subsurface.egl_window ||
172  self->resource.surface || self->resource.egl_window) {
174  "Surfaces already created");
175  return FALSE;
176  }
177 
178  if (!GDK_IS_WAYLAND_DISPLAY(gdk_display_get_default())) {
180  "Expected Wayland display");
181  return FALSE;
182  }
183  GdkWaylandDisplay* gdk_display =
184  GDK_WAYLAND_DISPLAY(gdk_display_get_default());
185 
186  wl_compositor* compositor =
187  gdk_wayland_display_get_wl_compositor(gdk_display);
188  if (!compositor) {
190  "No wl_compositor");
191  return FALSE;
192  }
193 
194  // Make sure size and scale is not <= 0
195  self->subsurface.scale = MAX(self->subsurface.scale, 1);
196  self->subsurface.geometry.width = MAX(self->subsurface.geometry.width, 1);
197  self->subsurface.geometry.height = MAX(self->subsurface.geometry.height, 1);
198 
199  self->subsurface.surface = wl_compositor_create_surface(compositor);
200  self->resource.surface = wl_compositor_create_surface(compositor);
201  if (!self->subsurface.surface || !self->resource.surface) {
203  "Failed to create wl_surfaces");
204  return FALSE;
205  }
206  wl_surface_set_buffer_scale(self->subsurface.surface, self->subsurface.scale);
207 
208  gint window_width = self->subsurface.geometry.width * self->subsurface.scale;
209  gint window_height =
210  self->subsurface.geometry.height * self->subsurface.scale;
211  self->subsurface.egl_window = wl_egl_window_create(
212  self->subsurface.surface, window_width, window_height);
213  self->resource.egl_window =
214  wl_egl_window_create(self->resource.surface, 1, 1);
215  if (!self->subsurface.egl_window || !self->resource.egl_window) {
217  "Failed to create EGL windows");
218  return FALSE;
219  }
220 
221  *visible = eglCreateWindowSurface(display, config,
222  self->subsurface.egl_window, nullptr);
223  *resource = eglCreateWindowSurface(display, config, self->resource.egl_window,
224  nullptr);
225  if (*visible == EGL_NO_SURFACE || *resource == EGL_NO_SURFACE) {
226  EGLint egl_error = eglGetError(); // must be before egl_config_to_string()
227  g_autofree gchar* config_string = egl_config_to_string(display, config);
229  "Failed to create EGL surfaces using configuration (%s): %s",
230  config_string, egl_error_to_string(egl_error));
231  return FALSE;
232  }
233 
234  GtkWidget* toplevel = gtk_widget_get_toplevel(widget);
235  if (!toplevel) {
237  "Renderer does not have a widget");
238  return FALSE;
239  }
240  g_signal_connect_object(toplevel, "map",
241  G_CALLBACK(fl_renderer_wayland_on_window_map), self,
242  G_CONNECT_SWAPPED);
243  g_signal_connect_object(toplevel, "unmap",
244  G_CALLBACK(fl_renderer_wayland_on_window_unmap), self,
245  G_CONNECT_SWAPPED);
246  if (gtk_widget_get_mapped(toplevel)) {
247  fl_renderer_wayland_on_window_map(self, toplevel);
248  }
249 
250  return TRUE;
251 }
252 
253 // Implements FlRenderer::set_geometry.
254 static void fl_renderer_wayland_set_geometry(FlRenderer* renderer,
255  GdkRectangle* geometry,
256  gint scale) {
257  FlRendererWayland* self = FL_RENDERER_WAYLAND(renderer);
258 
259  if (scale != self->subsurface.scale && self->subsurface.surface) {
260  wl_surface_set_buffer_scale(self->subsurface.surface, scale);
261  }
262 
263  // NOTE: position is unscaled but size is scaled.
264 
265  if ((geometry->x != self->subsurface.geometry.x ||
266  geometry->y != self->subsurface.geometry.y) &&
267  self->subsurface.subsurface) {
268  wl_subsurface_set_position(self->subsurface.subsurface, geometry->x,
269  geometry->y);
270  }
271 
272  if ((geometry->width != self->subsurface.geometry.width ||
273  geometry->height != self->subsurface.geometry.height ||
274  scale != self->subsurface.scale) &&
275  self->subsurface.egl_window) {
276  wl_egl_window_resize(self->subsurface.egl_window, geometry->width * scale,
277  geometry->height * scale, 0, 0);
278  }
279 
280  self->subsurface.geometry = *geometry;
281  self->subsurface.scale = scale;
282 }
283 
284 static void fl_renderer_wayland_class_init(FlRendererWaylandClass* klass) {
285  G_OBJECT_CLASS(klass)->dispose = fl_renderer_wayland_dispose;
286  FL_RENDERER_CLASS(klass)->create_display = fl_renderer_wayland_create_display;
287  FL_RENDERER_CLASS(klass)->create_surfaces =
289  FL_RENDERER_CLASS(klass)->set_geometry = fl_renderer_wayland_set_geometry;
290 }
291 
292 static void fl_renderer_wayland_init(FlRendererWayland* self) {}
293 
294 FlRendererWayland* fl_renderer_wayland_new() {
295  return FL_RENDERER_WAYLAND(
296  g_object_new(fl_renderer_wayland_get_type(), nullptr));
297 }
static void fl_renderer_wayland_on_window_unmap(FlRendererWayland *self, GtkWidget *widget)
wl_subsurface * subsurface
static EGLDisplay fl_renderer_wayland_create_display(FlRenderer *)
FlMethodResponse GError ** error
EGLDisplay eglGetDisplay(EGLNativeDisplayType display_id)
Definition: mock_egl.cc:248
struct _FlRendererWayland::@18 resource
EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list)
Definition: mock_egl.cc:142
GQuark fl_renderer_error_quark(void) G_GNUC_CONST
EGLint eglGetError()
Definition: mock_egl.cc:252
gchar * egl_config_to_string(EGLDisplay display, EGLConfig config)
Definition: egl_utils.cc:126
static void registry_handle_global_remove(void *, wl_registry *, uint32_t)
FlRendererWayland * fl_renderer_wayland_new()
static const wl_registry_listener registry_listener
static void fl_renderer_wayland_class_init(FlRendererWaylandClass *klass)
const gchar * egl_error_to_string(EGLint error)
Definition: egl_utils.cc:89
static void fl_renderer_wayland_init(FlRendererWayland *self)
wl_subcompositor * subcompositor
wl_egl_window * egl_window
static void registry_handle_global(void *data, wl_registry *registry, uint32_t id, const char *name, uint32_t max_version)
const char * name
Definition: fuchsia.cc:50
static void fl_renderer_wayland_set_geometry(FlRenderer *renderer, GdkRectangle *geometry, gint scale)
return TRUE
Definition: fl_view.cc:107
G_DEFINE_TYPE(FlBasicMessageChannelResponseHandle, fl_basic_message_channel_response_handle, G_TYPE_OBJECT) static void fl_basic_message_channel_response_handle_dispose(GObject *object)
static gboolean fl_renderer_wayland_create_surfaces(FlRenderer *renderer, GtkWidget *widget, EGLDisplay display, EGLConfig config, EGLSurface *visible, EGLSurface *resource, GError **error)
static void fl_renderer_wayland_dispose(GObject *object)
static void fl_renderer_wayland_on_window_map(FlRendererWayland *self, GtkWidget *widget)
static void fl_renderer_wayland_lazy_init_wl(FlRendererWayland *self)