winewayland.drv: Track damaged buffer regions.
[wine.git] / dlls / winewayland.drv / wayland_surface.c
blob13218a977a61aac32ce6d2fd3ba4f63ccf41ec91
1 /*
2 * Wayland surfaces
4 * Copyright 2020 Alexandros Frantzis for Collabora Ltd
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #if 0
22 #pragma makedep unix
23 #endif
25 #include "config.h"
27 #include <stdlib.h>
28 #include <unistd.h>
30 #include "waylanddrv.h"
31 #include "wine/debug.h"
32 #include "wine/server.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(waylanddrv);
36 /* Protects access to the user data of xdg_surface */
37 static pthread_mutex_t xdg_data_mutex = PTHREAD_MUTEX_INITIALIZER;
39 static struct wayland_surface *wayland_surface_lock_xdg(struct xdg_surface *xdg_surface)
41 struct wayland_surface *surface;
43 pthread_mutex_lock(&xdg_data_mutex);
44 surface = xdg_surface_get_user_data(xdg_surface);
45 if (surface) pthread_mutex_lock(&surface->mutex);
46 pthread_mutex_unlock(&xdg_data_mutex);
48 return surface;
51 static void xdg_surface_handle_configure(void *data, struct xdg_surface *xdg_surface,
52 uint32_t serial)
54 struct wayland_surface *surface;
55 BOOL initial_configure = FALSE;
56 HWND hwnd;
58 TRACE("serial=%u\n", serial);
60 if (!(surface = wayland_surface_lock_xdg(xdg_surface))) return;
62 /* Handle this event only if wayland_surface is still associated with
63 * the target xdg_surface. */
64 if (surface->xdg_surface == xdg_surface)
66 initial_configure = surface->current_serial == 0;
67 hwnd = surface->hwnd;
68 surface->current_serial = serial;
69 xdg_surface_ack_configure(xdg_surface, serial);
72 pthread_mutex_unlock(&surface->mutex);
74 /* Flush the window surface in case there is content that we weren't
75 * able to flush before due to the lack of the initial configure. */
76 if (initial_configure) wayland_window_flush(hwnd);
79 static const struct xdg_surface_listener xdg_surface_listener =
81 xdg_surface_handle_configure
84 /**********************************************************************
85 * wayland_surface_create
87 * Creates a role-less wayland surface.
89 struct wayland_surface *wayland_surface_create(HWND hwnd)
91 struct wayland_surface *surface;
93 surface = calloc(1, sizeof(*surface));
94 if (!surface)
96 ERR("Failed to allocate space for Wayland surface\n");
97 goto err;
100 TRACE("surface=%p\n", surface);
102 pthread_mutex_init(&surface->mutex, NULL);
104 surface->hwnd = hwnd;
105 surface->wl_surface = wl_compositor_create_surface(process_wayland.wl_compositor);
106 if (!surface->wl_surface)
108 ERR("Failed to create wl_surface Wayland surface\n");
109 goto err;
112 return surface;
114 err:
115 if (surface) wayland_surface_destroy(surface);
116 return NULL;
119 /**********************************************************************
120 * wayland_surface_destroy
122 * Destroys a wayland surface.
124 void wayland_surface_destroy(struct wayland_surface *surface)
126 pthread_mutex_lock(&xdg_data_mutex);
127 pthread_mutex_lock(&surface->mutex);
129 if (surface->xdg_toplevel)
131 xdg_toplevel_destroy(surface->xdg_toplevel);
132 surface->xdg_toplevel = NULL;
135 if (surface->xdg_surface)
137 xdg_surface_set_user_data(surface->xdg_surface, NULL);
138 xdg_surface_destroy(surface->xdg_surface);
139 surface->xdg_surface = NULL;
142 if (surface->wl_surface)
144 wl_surface_destroy(surface->wl_surface);
145 surface->wl_surface = NULL;
148 pthread_mutex_unlock(&surface->mutex);
149 pthread_mutex_unlock(&xdg_data_mutex);
151 wl_display_flush(process_wayland.wl_display);
153 pthread_mutex_destroy(&surface->mutex);
155 free(surface);
158 /**********************************************************************
159 * wayland_surface_make_toplevel
161 * Gives the toplevel role to a plain wayland surface.
163 void wayland_surface_make_toplevel(struct wayland_surface *surface)
165 TRACE("surface=%p\n", surface);
167 surface->xdg_surface =
168 xdg_wm_base_get_xdg_surface(process_wayland.xdg_wm_base, surface->wl_surface);
169 if (!surface->xdg_surface) goto err;
170 xdg_surface_add_listener(surface->xdg_surface, &xdg_surface_listener, surface);
172 surface->xdg_toplevel = xdg_surface_get_toplevel(surface->xdg_surface);
173 if (!surface->xdg_toplevel) goto err;
175 wl_surface_commit(surface->wl_surface);
176 wl_display_flush(process_wayland.wl_display);
178 return;
180 err:
181 wayland_surface_clear_role(surface);
182 ERR("Failed to assign toplevel role to wayland surface\n");
185 /**********************************************************************
186 * wayland_surface_clear_role
188 * Clears the role related Wayland objects of a Wayland surface, making it a
189 * plain surface again. We can later assign the same role (but not a
190 * different one!) to the surface.
192 void wayland_surface_clear_role(struct wayland_surface *surface)
194 TRACE("surface=%p\n", surface);
196 if (surface->xdg_toplevel)
198 xdg_toplevel_destroy(surface->xdg_toplevel);
199 surface->xdg_toplevel = NULL;
202 if (surface->xdg_surface)
204 xdg_surface_destroy(surface->xdg_surface);
205 surface->xdg_surface = NULL;
208 surface->current_serial = 0;
210 /* Ensure no buffer is attached, otherwise future role assignments may fail. */
211 wl_surface_attach(surface->wl_surface, NULL, 0, 0);
212 wl_surface_commit(surface->wl_surface);
214 wl_display_flush(process_wayland.wl_display);
217 /**********************************************************************
218 * wayland_surface_attach_shm
220 * Attaches a SHM buffer to a wayland surface.
222 * The buffer is marked as unavailable until committed and subsequently
223 * released by the compositor.
225 void wayland_surface_attach_shm(struct wayland_surface *surface,
226 struct wayland_shm_buffer *shm_buffer)
228 TRACE("surface=%p shm_buffer=%p (%dx%d)\n",
229 surface, shm_buffer, shm_buffer->width, shm_buffer->height);
231 shm_buffer->busy = TRUE;
232 wayland_shm_buffer_ref(shm_buffer);
234 wl_surface_attach(surface->wl_surface, shm_buffer->wl_buffer, 0, 0);
235 wl_surface_damage_buffer(surface->wl_surface, 0, 0,
236 shm_buffer->width, shm_buffer->height);
239 /**********************************************************************
240 * wayland_shm_buffer_ref
242 * Increases the reference count of a SHM buffer.
244 void wayland_shm_buffer_ref(struct wayland_shm_buffer *shm_buffer)
246 InterlockedIncrement(&shm_buffer->ref);
249 /**********************************************************************
250 * wayland_shm_buffer_unref
252 * Decreases the reference count of a SHM buffer (and may destroy it).
254 void wayland_shm_buffer_unref(struct wayland_shm_buffer *shm_buffer)
256 if (InterlockedDecrement(&shm_buffer->ref) > 0) return;
258 TRACE("destroying %p map=%p\n", shm_buffer, shm_buffer->map_data);
260 if (shm_buffer->wl_buffer)
261 wl_buffer_destroy(shm_buffer->wl_buffer);
262 if (shm_buffer->map_data)
263 NtUnmapViewOfSection(GetCurrentProcess(), shm_buffer->map_data);
264 if (shm_buffer->damage_region)
265 NtGdiDeleteObjectApp(shm_buffer->damage_region);
267 free(shm_buffer);
270 /**********************************************************************
271 * wayland_shm_buffer_create
273 * Creates a SHM buffer with the specified width, height and format.
275 struct wayland_shm_buffer *wayland_shm_buffer_create(int width, int height,
276 enum wl_shm_format format)
278 struct wayland_shm_buffer *shm_buffer = NULL;
279 HANDLE handle = 0;
280 int fd = -1;
281 SIZE_T view_size = 0;
282 LARGE_INTEGER section_size;
283 NTSTATUS status;
284 struct wl_shm_pool *pool;
285 int stride, size;
287 stride = width * WINEWAYLAND_BYTES_PER_PIXEL;
288 size = stride * height;
289 if (size == 0)
291 ERR("Invalid shm_buffer size %dx%d\n", width, height);
292 goto err;
295 shm_buffer = calloc(1, sizeof(*shm_buffer));
296 if (!shm_buffer)
298 ERR("Failed to allocate space for SHM buffer\n");
299 goto err;
302 TRACE("%p %dx%d format=%d size=%d\n", shm_buffer, width, height, format, size);
304 shm_buffer->ref = 1;
305 shm_buffer->width = width;
306 shm_buffer->height = height;
307 shm_buffer->map_size = size;
309 shm_buffer->damage_region = NtGdiCreateRectRgn(0, 0, width, height);
310 if (!shm_buffer->damage_region)
312 ERR("Failed to create buffer damage region\n");
313 goto err;
316 section_size.QuadPart = size;
317 status = NtCreateSection(&handle,
318 GENERIC_READ | SECTION_MAP_READ | SECTION_MAP_WRITE,
319 NULL, &section_size, PAGE_READWRITE, SEC_COMMIT, 0);
320 if (status)
322 ERR("Failed to create SHM section status=0x%lx\n", (long)status);
323 goto err;
326 status = NtMapViewOfSection(handle, GetCurrentProcess(),
327 (PVOID)&shm_buffer->map_data, 0, 0, NULL,
328 &view_size, ViewUnmap, 0, PAGE_READWRITE);
329 if (status)
331 shm_buffer->map_data = NULL;
332 ERR("Failed to create map SHM handle status=0x%lx\n", (long)status);
333 goto err;
336 status = wine_server_handle_to_fd(handle, FILE_READ_DATA, &fd, NULL);
337 if (status)
339 ERR("Failed to get fd from SHM handle status=0x%lx\n", (long)status);
340 goto err;
343 pool = wl_shm_create_pool(process_wayland.wl_shm, fd, size);
344 if (!pool)
346 ERR("Failed to create SHM pool fd=%d size=%d\n", fd, size);
347 goto err;
349 shm_buffer->wl_buffer = wl_shm_pool_create_buffer(pool, 0, width, height,
350 stride, format);
351 wl_shm_pool_destroy(pool);
352 if (!shm_buffer->wl_buffer)
354 ERR("Failed to create SHM buffer %dx%d\n", width, height);
355 goto err;
358 close(fd);
359 NtClose(handle);
361 TRACE("=> map=%p\n", shm_buffer->map_data);
363 return shm_buffer;
365 err:
366 if (fd >= 0) close(fd);
367 if (handle) NtClose(handle);
368 if (shm_buffer) wayland_shm_buffer_unref(shm_buffer);
369 return NULL;