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 static void xdg_surface_handle_configure(void *data
, struct xdg_surface
*xdg_surface
,
39 struct wayland_surface
*surface
;
40 BOOL initial_configure
= FALSE
;
43 TRACE("serial=%u\n", serial
);
45 if (!(surface
= wayland_surface_lock_hwnd(hwnd
))) return;
47 /* Handle this event only if wayland_surface is still associated with
48 * the target xdg_surface. */
49 if (surface
->xdg_surface
== xdg_surface
)
51 /* If we have a previously requested config, we have already sent a
52 * WM_WAYLAND_CONFIGURE which hasn't been handled yet. In that case,
53 * avoid sending another message to reduce message queue traffic. */
54 BOOL should_post
= surface
->requested
.serial
== 0;
55 initial_configure
= surface
->current
.serial
== 0;
56 surface
->pending
.serial
= serial
;
57 surface
->requested
= surface
->pending
;
58 memset(&surface
->pending
, 0, sizeof(surface
->pending
));
59 if (should_post
) NtUserPostMessage(hwnd
, WM_WAYLAND_CONFIGURE
, 0, 0);
62 pthread_mutex_unlock(&surface
->mutex
);
64 /* Flush the window surface in case there is content that we weren't
65 * able to flush before due to the lack of the initial configure. */
66 if (initial_configure
) wayland_window_flush(hwnd
);
69 static const struct xdg_surface_listener xdg_surface_listener
=
71 xdg_surface_handle_configure
74 static void xdg_toplevel_handle_configure(void *data
,
75 struct xdg_toplevel
*xdg_toplevel
,
76 int32_t width
, int32_t height
,
77 struct wl_array
*states
)
79 struct wayland_surface
*surface
;
82 enum wayland_surface_config_state config_state
= 0;
84 wl_array_for_each(state
, states
)
88 case XDG_TOPLEVEL_STATE_MAXIMIZED
:
89 config_state
|= WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED
;
91 case XDG_TOPLEVEL_STATE_RESIZING
:
92 config_state
|= WAYLAND_SURFACE_CONFIG_STATE_RESIZING
;
94 case XDG_TOPLEVEL_STATE_TILED_LEFT
:
95 case XDG_TOPLEVEL_STATE_TILED_RIGHT
:
96 case XDG_TOPLEVEL_STATE_TILED_TOP
:
97 case XDG_TOPLEVEL_STATE_TILED_BOTTOM
:
98 config_state
|= WAYLAND_SURFACE_CONFIG_STATE_TILED
;
100 case XDG_TOPLEVEL_STATE_FULLSCREEN
:
101 config_state
|= WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN
;
108 TRACE("hwnd=%p %dx%d,%#x\n", hwnd
, width
, height
, config_state
);
110 if (!(surface
= wayland_surface_lock_hwnd(hwnd
))) return;
112 if (surface
->xdg_toplevel
== xdg_toplevel
)
114 surface
->pending
.width
= width
;
115 surface
->pending
.height
= height
;
116 surface
->pending
.state
= config_state
;
119 pthread_mutex_unlock(&surface
->mutex
);
122 static void xdg_toplevel_handle_close(void *data
, struct xdg_toplevel
*xdg_toplevel
)
124 NtUserPostMessage((HWND
)data
, WM_SYSCOMMAND
, SC_CLOSE
, 0);
127 static const struct xdg_toplevel_listener xdg_toplevel_listener
=
129 xdg_toplevel_handle_configure
,
130 xdg_toplevel_handle_close
133 /**********************************************************************
134 * wayland_surface_create
136 * Creates a role-less wayland surface.
138 struct wayland_surface
*wayland_surface_create(HWND hwnd
)
140 struct wayland_surface
*surface
;
142 surface
= calloc(1, sizeof(*surface
));
145 ERR("Failed to allocate space for Wayland surface\n");
149 TRACE("surface=%p\n", surface
);
151 pthread_mutex_init(&surface
->mutex
, NULL
);
153 surface
->hwnd
= hwnd
;
154 surface
->wl_surface
= wl_compositor_create_surface(process_wayland
.wl_compositor
);
155 if (!surface
->wl_surface
)
157 ERR("Failed to create wl_surface Wayland surface\n");
160 wl_surface_set_user_data(surface
->wl_surface
, hwnd
);
165 if (surface
) wayland_surface_destroy(surface
);
169 /**********************************************************************
170 * wayland_surface_destroy
172 * Destroys a wayland surface.
174 void wayland_surface_destroy(struct wayland_surface
*surface
)
176 pthread_mutex_lock(&process_wayland
.pointer
.mutex
);
177 if (process_wayland
.pointer
.focused_hwnd
== surface
->hwnd
)
179 process_wayland
.pointer
.focused_hwnd
= NULL
;
180 process_wayland
.pointer
.enter_serial
= 0;
182 pthread_mutex_unlock(&process_wayland
.pointer
.mutex
);
184 pthread_mutex_lock(&process_wayland
.keyboard
.mutex
);
185 if (process_wayland
.keyboard
.focused_hwnd
== surface
->hwnd
)
186 process_wayland
.keyboard
.focused_hwnd
= NULL
;
187 pthread_mutex_unlock(&process_wayland
.keyboard
.mutex
);
189 pthread_mutex_lock(&surface
->mutex
);
191 if (surface
->xdg_toplevel
)
193 xdg_toplevel_destroy(surface
->xdg_toplevel
);
194 surface
->xdg_toplevel
= NULL
;
197 if (surface
->xdg_surface
)
199 xdg_surface_destroy(surface
->xdg_surface
);
200 surface
->xdg_surface
= NULL
;
203 if (surface
->wl_surface
)
205 wl_surface_destroy(surface
->wl_surface
);
206 surface
->wl_surface
= NULL
;
209 pthread_mutex_unlock(&surface
->mutex
);
211 if (surface
->latest_window_buffer
)
212 wayland_shm_buffer_unref(surface
->latest_window_buffer
);
214 wl_display_flush(process_wayland
.wl_display
);
216 pthread_mutex_destroy(&surface
->mutex
);
221 /**********************************************************************
222 * wayland_surface_make_toplevel
224 * Gives the toplevel role to a plain wayland surface.
226 void wayland_surface_make_toplevel(struct wayland_surface
*surface
)
228 TRACE("surface=%p\n", surface
);
230 surface
->xdg_surface
=
231 xdg_wm_base_get_xdg_surface(process_wayland
.xdg_wm_base
, surface
->wl_surface
);
232 if (!surface
->xdg_surface
) goto err
;
233 xdg_surface_add_listener(surface
->xdg_surface
, &xdg_surface_listener
, surface
->hwnd
);
235 surface
->xdg_toplevel
= xdg_surface_get_toplevel(surface
->xdg_surface
);
236 if (!surface
->xdg_toplevel
) goto err
;
237 xdg_toplevel_add_listener(surface
->xdg_toplevel
, &xdg_toplevel_listener
, surface
->hwnd
);
239 wl_surface_commit(surface
->wl_surface
);
240 wl_display_flush(process_wayland
.wl_display
);
245 wayland_surface_clear_role(surface
);
246 ERR("Failed to assign toplevel role to wayland surface\n");
249 /**********************************************************************
250 * wayland_surface_clear_role
252 * Clears the role related Wayland objects of a Wayland surface, making it a
253 * plain surface again. We can later assign the same role (but not a
254 * different one!) to the surface.
256 void wayland_surface_clear_role(struct wayland_surface
*surface
)
258 TRACE("surface=%p\n", surface
);
260 if (surface
->xdg_toplevel
)
262 xdg_toplevel_destroy(surface
->xdg_toplevel
);
263 surface
->xdg_toplevel
= NULL
;
266 if (surface
->xdg_surface
)
268 xdg_surface_destroy(surface
->xdg_surface
);
269 surface
->xdg_surface
= NULL
;
272 memset(&surface
->pending
, 0, sizeof(surface
->pending
));
273 memset(&surface
->requested
, 0, sizeof(surface
->requested
));
274 memset(&surface
->processing
, 0, sizeof(surface
->processing
));
275 memset(&surface
->current
, 0, sizeof(surface
->current
));
277 /* Ensure no buffer is attached, otherwise future role assignments may fail. */
278 wl_surface_attach(surface
->wl_surface
, NULL
, 0, 0);
279 wl_surface_commit(surface
->wl_surface
);
281 wl_display_flush(process_wayland
.wl_display
);
284 /**********************************************************************
285 * wayland_surface_attach_shm
287 * Attaches a SHM buffer to a wayland surface.
289 * The buffer is marked as unavailable until committed and subsequently
290 * released by the compositor.
292 void wayland_surface_attach_shm(struct wayland_surface
*surface
,
293 struct wayland_shm_buffer
*shm_buffer
,
294 HRGN surface_damage_region
)
296 RGNDATA
*surface_damage
;
298 TRACE("surface=%p shm_buffer=%p (%dx%d)\n",
299 surface
, shm_buffer
, shm_buffer
->width
, shm_buffer
->height
);
301 shm_buffer
->busy
= TRUE
;
302 wayland_shm_buffer_ref(shm_buffer
);
304 wl_surface_attach(surface
->wl_surface
, shm_buffer
->wl_buffer
, 0, 0);
306 /* Add surface damage, i.e., which parts of the surface have changed since
307 * the last surface commit. Note that this is different from the buffer
309 surface_damage
= get_region_data(surface_damage_region
);
312 RECT
*rgn_rect
= (RECT
*)surface_damage
->Buffer
;
313 RECT
*rgn_rect_end
= rgn_rect
+ surface_damage
->rdh
.nCount
;
315 for (;rgn_rect
< rgn_rect_end
; rgn_rect
++)
317 wl_surface_damage_buffer(surface
->wl_surface
,
318 rgn_rect
->left
, rgn_rect
->top
,
319 rgn_rect
->right
- rgn_rect
->left
,
320 rgn_rect
->bottom
- rgn_rect
->top
);
322 free(surface_damage
);
326 /**********************************************************************
327 * wayland_surface_config_is_compatible
329 * Checks whether a wayland_surface_config object is compatible with the
330 * the provided arguments.
332 BOOL
wayland_surface_config_is_compatible(struct wayland_surface_config
*conf
,
333 int width
, int height
,
334 enum wayland_surface_config_state state
)
336 static enum wayland_surface_config_state mask
=
337 WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED
;
339 /* We require the same state. */
340 if ((state
& mask
) != (conf
->state
& mask
)) return FALSE
;
342 /* The maximized state requires the configured size. During surface
343 * reconfiguration we can use surface geometry to provide smaller areas
344 * from larger sizes, so only smaller sizes are incompatible. */
345 if ((conf
->state
& WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED
) &&
346 (width
< conf
->width
|| height
< conf
->height
))
351 /* The fullscreen state requires a size smaller or equal to the configured
352 * size. If we have a larger size, we can use surface geometry during
353 * surface reconfiguration to provide the smaller size, so we are always
354 * compatible with a fullscreen state. */
359 /**********************************************************************
360 * wayland_surface_get_rect_in_monitor
362 * Gets the largest rectangle within a surface's window (in window coordinates)
363 * that is visible in a monitor.
365 static void wayland_surface_get_rect_in_monitor(struct wayland_surface
*surface
,
371 mi
.cbSize
= sizeof(mi
);
372 if (!(hmonitor
= NtUserMonitorFromRect(&surface
->window
.rect
, 0)) ||
373 !NtUserGetMonitorInfo(hmonitor
, (MONITORINFO
*)&mi
))
379 intersect_rect(rect
, &mi
.rcMonitor
, &surface
->window
.rect
);
380 OffsetRect(rect
, -surface
->window
.rect
.left
, -surface
->window
.rect
.top
);
383 /**********************************************************************
384 * wayland_surface_reconfigure_geometry
386 * Sets the xdg_surface geometry
388 static void wayland_surface_reconfigure_geometry(struct wayland_surface
*surface
)
393 width
= surface
->window
.rect
.right
- surface
->window
.rect
.left
;
394 height
= surface
->window
.rect
.bottom
- surface
->window
.rect
.top
;
396 /* If the window size is bigger than the current state accepts, use the
397 * largest visible (from Windows' perspective) subregion of the window. */
398 if ((surface
->current
.state
& (WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED
|
399 WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN
)) &&
400 (width
> surface
->current
.width
|| height
> surface
->current
.height
))
402 wayland_surface_get_rect_in_monitor(surface
, &rect
);
404 /* If the window rect in the monitor is smaller than required,
405 * fall back to an appropriately sized rect at the top-left. */
406 if ((surface
->current
.state
& WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED
) &&
407 (rect
.right
- rect
.left
< surface
->current
.width
||
408 rect
.bottom
- rect
.top
< surface
->current
.height
))
410 SetRect(&rect
, 0, 0, surface
->current
.width
, surface
->current
.height
);
414 rect
.right
= min(rect
.right
, rect
.left
+ surface
->current
.width
);
415 rect
.bottom
= min(rect
.bottom
, rect
.top
+ surface
->current
.height
);
417 TRACE("Window is too large for Wayland state, using subregion\n");
421 SetRect(&rect
, 0, 0, width
, height
);
424 TRACE("hwnd=%p geometry=%s\n", surface
->hwnd
, wine_dbgstr_rect(&rect
));
426 if (!IsRectEmpty(&rect
))
428 xdg_surface_set_window_geometry(surface
->xdg_surface
,
430 rect
.right
- rect
.left
,
431 rect
.bottom
- rect
.top
);
435 /**********************************************************************
436 * wayland_surface_reconfigure
438 * Reconfigures the wayland surface as needed to match the latest requested
441 BOOL
wayland_surface_reconfigure(struct wayland_surface
*surface
)
443 struct wayland_window_config
*window
= &surface
->window
;
446 if (!surface
->xdg_toplevel
) return TRUE
;
448 width
= surface
->window
.rect
.right
- surface
->window
.rect
.left
;
449 height
= surface
->window
.rect
.bottom
- surface
->window
.rect
.top
;
451 TRACE("hwnd=%p window=%dx%d,%#x processing=%dx%d,%#x current=%dx%d,%#x\n",
452 surface
->hwnd
, width
, height
, window
->state
,
453 surface
->processing
.width
, surface
->processing
.height
,
454 surface
->processing
.state
, surface
->current
.width
,
455 surface
->current
.height
, surface
->current
.state
);
457 /* Acknowledge any compatible processed config. */
458 if (surface
->processing
.serial
&& surface
->processing
.processed
&&
459 wayland_surface_config_is_compatible(&surface
->processing
,
463 surface
->current
= surface
->processing
;
464 memset(&surface
->processing
, 0, sizeof(surface
->processing
));
465 xdg_surface_ack_configure(surface
->xdg_surface
, surface
->current
.serial
);
467 /* If this is the initial configure, and we have a compatible requested
468 * config, use that, in order to draw windows that don't go through the
469 * message loop (e.g., some splash screens). */
470 else if (!surface
->current
.serial
&& surface
->requested
.serial
&&
471 wayland_surface_config_is_compatible(&surface
->requested
,
475 surface
->current
= surface
->requested
;
476 memset(&surface
->requested
, 0, sizeof(surface
->requested
));
477 xdg_surface_ack_configure(surface
->xdg_surface
, surface
->current
.serial
);
479 else if (!surface
->current
.serial
||
480 !wayland_surface_config_is_compatible(&surface
->current
,
487 wayland_surface_reconfigure_geometry(surface
);
492 /**********************************************************************
493 * wayland_shm_buffer_ref
495 * Increases the reference count of a SHM buffer.
497 void wayland_shm_buffer_ref(struct wayland_shm_buffer
*shm_buffer
)
499 InterlockedIncrement(&shm_buffer
->ref
);
502 /**********************************************************************
503 * wayland_shm_buffer_unref
505 * Decreases the reference count of a SHM buffer (and may destroy it).
507 void wayland_shm_buffer_unref(struct wayland_shm_buffer
*shm_buffer
)
509 if (InterlockedDecrement(&shm_buffer
->ref
) > 0) return;
511 TRACE("destroying %p map=%p\n", shm_buffer
, shm_buffer
->map_data
);
513 if (shm_buffer
->wl_buffer
)
514 wl_buffer_destroy(shm_buffer
->wl_buffer
);
515 if (shm_buffer
->map_data
)
516 NtUnmapViewOfSection(GetCurrentProcess(), shm_buffer
->map_data
);
517 if (shm_buffer
->damage_region
)
518 NtGdiDeleteObjectApp(shm_buffer
->damage_region
);
523 /**********************************************************************
524 * wayland_shm_buffer_create
526 * Creates a SHM buffer with the specified width, height and format.
528 struct wayland_shm_buffer
*wayland_shm_buffer_create(int width
, int height
,
529 enum wl_shm_format format
)
531 struct wayland_shm_buffer
*shm_buffer
= NULL
;
534 SIZE_T view_size
= 0;
535 LARGE_INTEGER section_size
;
537 struct wl_shm_pool
*pool
;
540 stride
= width
* WINEWAYLAND_BYTES_PER_PIXEL
;
541 size
= stride
* height
;
544 ERR("Invalid shm_buffer size %dx%d\n", width
, height
);
548 shm_buffer
= calloc(1, sizeof(*shm_buffer
));
551 ERR("Failed to allocate space for SHM buffer\n");
555 TRACE("%p %dx%d format=%d size=%d\n", shm_buffer
, width
, height
, format
, size
);
558 shm_buffer
->width
= width
;
559 shm_buffer
->height
= height
;
560 shm_buffer
->map_size
= size
;
562 shm_buffer
->damage_region
= NtGdiCreateRectRgn(0, 0, width
, height
);
563 if (!shm_buffer
->damage_region
)
565 ERR("Failed to create buffer damage region\n");
569 section_size
.QuadPart
= size
;
570 status
= NtCreateSection(&handle
,
571 GENERIC_READ
| SECTION_MAP_READ
| SECTION_MAP_WRITE
,
572 NULL
, §ion_size
, PAGE_READWRITE
, SEC_COMMIT
, 0);
575 ERR("Failed to create SHM section status=0x%lx\n", (long)status
);
579 status
= NtMapViewOfSection(handle
, GetCurrentProcess(),
580 (PVOID
)&shm_buffer
->map_data
, 0, 0, NULL
,
581 &view_size
, ViewUnmap
, 0, PAGE_READWRITE
);
584 shm_buffer
->map_data
= NULL
;
585 ERR("Failed to create map SHM handle status=0x%lx\n", (long)status
);
589 status
= wine_server_handle_to_fd(handle
, FILE_READ_DATA
, &fd
, NULL
);
592 ERR("Failed to get fd from SHM handle status=0x%lx\n", (long)status
);
596 pool
= wl_shm_create_pool(process_wayland
.wl_shm
, fd
, size
);
599 ERR("Failed to create SHM pool fd=%d size=%d\n", fd
, size
);
602 shm_buffer
->wl_buffer
= wl_shm_pool_create_buffer(pool
, 0, width
, height
,
604 wl_shm_pool_destroy(pool
);
605 if (!shm_buffer
->wl_buffer
)
607 ERR("Failed to create SHM buffer %dx%d\n", width
, height
);
614 TRACE("=> map=%p\n", shm_buffer
->map_data
);
619 if (fd
>= 0) close(fd
);
620 if (handle
) NtClose(handle
);
621 if (shm_buffer
) wayland_shm_buffer_unref(shm_buffer
);