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
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
);
51 static void xdg_surface_handle_configure(void *data
, struct xdg_surface
*xdg_surface
,
54 struct wayland_surface
*surface
;
55 BOOL initial_configure
= FALSE
;
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;
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
));
96 ERR("Failed to allocate space for Wayland surface\n");
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");
115 if (surface
) wayland_surface_destroy(surface
);
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
);
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
);
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
);
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
;
281 SIZE_T view_size
= 0;
282 LARGE_INTEGER section_size
;
284 struct wl_shm_pool
*pool
;
287 stride
= width
* WINEWAYLAND_BYTES_PER_PIXEL
;
288 size
= stride
* height
;
291 ERR("Invalid shm_buffer size %dx%d\n", width
, height
);
295 shm_buffer
= calloc(1, sizeof(*shm_buffer
));
298 ERR("Failed to allocate space for SHM buffer\n");
302 TRACE("%p %dx%d format=%d size=%d\n", shm_buffer
, width
, height
, format
, size
);
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");
316 section_size
.QuadPart
= size
;
317 status
= NtCreateSection(&handle
,
318 GENERIC_READ
| SECTION_MAP_READ
| SECTION_MAP_WRITE
,
319 NULL
, §ion_size
, PAGE_READWRITE
, SEC_COMMIT
, 0);
322 ERR("Failed to create SHM section status=0x%lx\n", (long)status
);
326 status
= NtMapViewOfSection(handle
, GetCurrentProcess(),
327 (PVOID
)&shm_buffer
->map_data
, 0, 0, NULL
,
328 &view_size
, ViewUnmap
, 0, PAGE_READWRITE
);
331 shm_buffer
->map_data
= NULL
;
332 ERR("Failed to create map SHM handle status=0x%lx\n", (long)status
);
336 status
= wine_server_handle_to_fd(handle
, FILE_READ_DATA
, &fd
, NULL
);
339 ERR("Failed to get fd from SHM handle status=0x%lx\n", (long)status
);
343 pool
= wl_shm_create_pool(process_wayland
.wl_shm
, fd
, size
);
346 ERR("Failed to create SHM pool fd=%d size=%d\n", fd
, size
);
349 shm_buffer
->wl_buffer
= wl_shm_pool_create_buffer(pool
, 0, width
, height
,
351 wl_shm_pool_destroy(pool
);
352 if (!shm_buffer
->wl_buffer
)
354 ERR("Failed to create SHM buffer %dx%d\n", width
, height
);
361 TRACE("=> map=%p\n", shm_buffer
->map_data
);
366 if (fd
>= 0) close(fd
);
367 if (handle
) NtClose(handle
);
368 if (shm_buffer
) wayland_shm_buffer_unref(shm_buffer
);