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(&surface
->mutex
);
186 if (surface
->xdg_toplevel
)
188 xdg_toplevel_destroy(surface
->xdg_toplevel
);
189 surface
->xdg_toplevel
= NULL
;
192 if (surface
->xdg_surface
)
194 xdg_surface_destroy(surface
->xdg_surface
);
195 surface
->xdg_surface
= NULL
;
198 if (surface
->wl_surface
)
200 wl_surface_destroy(surface
->wl_surface
);
201 surface
->wl_surface
= NULL
;
204 pthread_mutex_unlock(&surface
->mutex
);
206 if (surface
->latest_window_buffer
)
207 wayland_shm_buffer_unref(surface
->latest_window_buffer
);
209 wl_display_flush(process_wayland
.wl_display
);
211 pthread_mutex_destroy(&surface
->mutex
);
216 /**********************************************************************
217 * wayland_surface_make_toplevel
219 * Gives the toplevel role to a plain wayland surface.
221 void wayland_surface_make_toplevel(struct wayland_surface
*surface
)
223 TRACE("surface=%p\n", surface
);
225 surface
->xdg_surface
=
226 xdg_wm_base_get_xdg_surface(process_wayland
.xdg_wm_base
, surface
->wl_surface
);
227 if (!surface
->xdg_surface
) goto err
;
228 xdg_surface_add_listener(surface
->xdg_surface
, &xdg_surface_listener
, surface
->hwnd
);
230 surface
->xdg_toplevel
= xdg_surface_get_toplevel(surface
->xdg_surface
);
231 if (!surface
->xdg_toplevel
) goto err
;
232 xdg_toplevel_add_listener(surface
->xdg_toplevel
, &xdg_toplevel_listener
, surface
->hwnd
);
234 wl_surface_commit(surface
->wl_surface
);
235 wl_display_flush(process_wayland
.wl_display
);
240 wayland_surface_clear_role(surface
);
241 ERR("Failed to assign toplevel role to wayland surface\n");
244 /**********************************************************************
245 * wayland_surface_clear_role
247 * Clears the role related Wayland objects of a Wayland surface, making it a
248 * plain surface again. We can later assign the same role (but not a
249 * different one!) to the surface.
251 void wayland_surface_clear_role(struct wayland_surface
*surface
)
253 TRACE("surface=%p\n", surface
);
255 if (surface
->xdg_toplevel
)
257 xdg_toplevel_destroy(surface
->xdg_toplevel
);
258 surface
->xdg_toplevel
= NULL
;
261 if (surface
->xdg_surface
)
263 xdg_surface_destroy(surface
->xdg_surface
);
264 surface
->xdg_surface
= NULL
;
267 memset(&surface
->pending
, 0, sizeof(surface
->pending
));
268 memset(&surface
->requested
, 0, sizeof(surface
->requested
));
269 memset(&surface
->processing
, 0, sizeof(surface
->processing
));
270 memset(&surface
->current
, 0, sizeof(surface
->current
));
272 /* Ensure no buffer is attached, otherwise future role assignments may fail. */
273 wl_surface_attach(surface
->wl_surface
, NULL
, 0, 0);
274 wl_surface_commit(surface
->wl_surface
);
276 wl_display_flush(process_wayland
.wl_display
);
279 /**********************************************************************
280 * wayland_surface_attach_shm
282 * Attaches a SHM buffer to a wayland surface.
284 * The buffer is marked as unavailable until committed and subsequently
285 * released by the compositor.
287 void wayland_surface_attach_shm(struct wayland_surface
*surface
,
288 struct wayland_shm_buffer
*shm_buffer
,
289 HRGN surface_damage_region
)
291 RGNDATA
*surface_damage
;
293 TRACE("surface=%p shm_buffer=%p (%dx%d)\n",
294 surface
, shm_buffer
, shm_buffer
->width
, shm_buffer
->height
);
296 shm_buffer
->busy
= TRUE
;
297 wayland_shm_buffer_ref(shm_buffer
);
299 wl_surface_attach(surface
->wl_surface
, shm_buffer
->wl_buffer
, 0, 0);
301 /* Add surface damage, i.e., which parts of the surface have changed since
302 * the last surface commit. Note that this is different from the buffer
304 surface_damage
= get_region_data(surface_damage_region
);
307 RECT
*rgn_rect
= (RECT
*)surface_damage
->Buffer
;
308 RECT
*rgn_rect_end
= rgn_rect
+ surface_damage
->rdh
.nCount
;
310 for (;rgn_rect
< rgn_rect_end
; rgn_rect
++)
312 wl_surface_damage_buffer(surface
->wl_surface
,
313 rgn_rect
->left
, rgn_rect
->top
,
314 rgn_rect
->right
- rgn_rect
->left
,
315 rgn_rect
->bottom
- rgn_rect
->top
);
317 free(surface_damage
);
321 /**********************************************************************
322 * wayland_surface_config_is_compatible
324 * Checks whether a wayland_surface_config object is compatible with the
325 * the provided arguments.
327 BOOL
wayland_surface_config_is_compatible(struct wayland_surface_config
*conf
,
328 int width
, int height
,
329 enum wayland_surface_config_state state
)
331 static enum wayland_surface_config_state mask
=
332 WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED
;
334 /* We require the same state. */
335 if ((state
& mask
) != (conf
->state
& mask
)) return FALSE
;
337 /* The maximized state requires the configured size. During surface
338 * reconfiguration we can use surface geometry to provide smaller areas
339 * from larger sizes, so only smaller sizes are incompatible. */
340 if ((conf
->state
& WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED
) &&
341 (width
< conf
->width
|| height
< conf
->height
))
346 /* The fullscreen state requires a size smaller or equal to the configured
347 * size. If we have a larger size, we can use surface geometry during
348 * surface reconfiguration to provide the smaller size, so we are always
349 * compatible with a fullscreen state. */
354 /**********************************************************************
355 * wayland_surface_get_rect_in_monitor
357 * Gets the largest rectangle within a surface's window (in window coordinates)
358 * that is visible in a monitor.
360 static void wayland_surface_get_rect_in_monitor(struct wayland_surface
*surface
,
366 mi
.cbSize
= sizeof(mi
);
367 if (!(hmonitor
= NtUserMonitorFromRect(&surface
->window
.rect
, 0)) ||
368 !NtUserGetMonitorInfo(hmonitor
, (MONITORINFO
*)&mi
))
374 intersect_rect(rect
, &mi
.rcMonitor
, &surface
->window
.rect
);
375 OffsetRect(rect
, -surface
->window
.rect
.left
, -surface
->window
.rect
.top
);
378 /**********************************************************************
379 * wayland_surface_reconfigure_geometry
381 * Sets the xdg_surface geometry
383 static void wayland_surface_reconfigure_geometry(struct wayland_surface
*surface
)
388 width
= surface
->window
.rect
.right
- surface
->window
.rect
.left
;
389 height
= surface
->window
.rect
.bottom
- surface
->window
.rect
.top
;
391 /* If the window size is bigger than the current state accepts, use the
392 * largest visible (from Windows' perspective) subregion of the window. */
393 if ((surface
->current
.state
& (WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED
|
394 WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN
)) &&
395 (width
> surface
->current
.width
|| height
> surface
->current
.height
))
397 wayland_surface_get_rect_in_monitor(surface
, &rect
);
399 /* If the window rect in the monitor is smaller than required,
400 * fall back to an appropriately sized rect at the top-left. */
401 if ((surface
->current
.state
& WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED
) &&
402 (rect
.right
- rect
.left
< surface
->current
.width
||
403 rect
.bottom
- rect
.top
< surface
->current
.height
))
405 SetRect(&rect
, 0, 0, surface
->current
.width
, surface
->current
.height
);
409 rect
.right
= min(rect
.right
, rect
.left
+ surface
->current
.width
);
410 rect
.bottom
= min(rect
.bottom
, rect
.top
+ surface
->current
.height
);
412 TRACE("Window is too large for Wayland state, using subregion\n");
416 SetRect(&rect
, 0, 0, width
, height
);
419 TRACE("hwnd=%p geometry=%s\n", surface
->hwnd
, wine_dbgstr_rect(&rect
));
421 if (!IsRectEmpty(&rect
))
423 xdg_surface_set_window_geometry(surface
->xdg_surface
,
425 rect
.right
- rect
.left
,
426 rect
.bottom
- rect
.top
);
430 /**********************************************************************
431 * wayland_surface_reconfigure
433 * Reconfigures the wayland surface as needed to match the latest requested
436 BOOL
wayland_surface_reconfigure(struct wayland_surface
*surface
)
438 struct wayland_window_config
*window
= &surface
->window
;
441 if (!surface
->xdg_toplevel
) return TRUE
;
443 width
= surface
->window
.rect
.right
- surface
->window
.rect
.left
;
444 height
= surface
->window
.rect
.bottom
- surface
->window
.rect
.top
;
446 TRACE("hwnd=%p window=%dx%d,%#x processing=%dx%d,%#x current=%dx%d,%#x\n",
447 surface
->hwnd
, width
, height
, window
->state
,
448 surface
->processing
.width
, surface
->processing
.height
,
449 surface
->processing
.state
, surface
->current
.width
,
450 surface
->current
.height
, surface
->current
.state
);
452 /* Acknowledge any compatible processed config. */
453 if (surface
->processing
.serial
&& surface
->processing
.processed
&&
454 wayland_surface_config_is_compatible(&surface
->processing
,
458 surface
->current
= surface
->processing
;
459 memset(&surface
->processing
, 0, sizeof(surface
->processing
));
460 xdg_surface_ack_configure(surface
->xdg_surface
, surface
->current
.serial
);
462 /* If this is the initial configure, and we have a compatible requested
463 * config, use that, in order to draw windows that don't go through the
464 * message loop (e.g., some splash screens). */
465 else if (!surface
->current
.serial
&& surface
->requested
.serial
&&
466 wayland_surface_config_is_compatible(&surface
->requested
,
470 surface
->current
= surface
->requested
;
471 memset(&surface
->requested
, 0, sizeof(surface
->requested
));
472 xdg_surface_ack_configure(surface
->xdg_surface
, surface
->current
.serial
);
474 else if (!surface
->current
.serial
||
475 !wayland_surface_config_is_compatible(&surface
->current
,
482 wayland_surface_reconfigure_geometry(surface
);
487 /**********************************************************************
488 * wayland_shm_buffer_ref
490 * Increases the reference count of a SHM buffer.
492 void wayland_shm_buffer_ref(struct wayland_shm_buffer
*shm_buffer
)
494 InterlockedIncrement(&shm_buffer
->ref
);
497 /**********************************************************************
498 * wayland_shm_buffer_unref
500 * Decreases the reference count of a SHM buffer (and may destroy it).
502 void wayland_shm_buffer_unref(struct wayland_shm_buffer
*shm_buffer
)
504 if (InterlockedDecrement(&shm_buffer
->ref
) > 0) return;
506 TRACE("destroying %p map=%p\n", shm_buffer
, shm_buffer
->map_data
);
508 if (shm_buffer
->wl_buffer
)
509 wl_buffer_destroy(shm_buffer
->wl_buffer
);
510 if (shm_buffer
->map_data
)
511 NtUnmapViewOfSection(GetCurrentProcess(), shm_buffer
->map_data
);
512 if (shm_buffer
->damage_region
)
513 NtGdiDeleteObjectApp(shm_buffer
->damage_region
);
518 /**********************************************************************
519 * wayland_shm_buffer_create
521 * Creates a SHM buffer with the specified width, height and format.
523 struct wayland_shm_buffer
*wayland_shm_buffer_create(int width
, int height
,
524 enum wl_shm_format format
)
526 struct wayland_shm_buffer
*shm_buffer
= NULL
;
529 SIZE_T view_size
= 0;
530 LARGE_INTEGER section_size
;
532 struct wl_shm_pool
*pool
;
535 stride
= width
* WINEWAYLAND_BYTES_PER_PIXEL
;
536 size
= stride
* height
;
539 ERR("Invalid shm_buffer size %dx%d\n", width
, height
);
543 shm_buffer
= calloc(1, sizeof(*shm_buffer
));
546 ERR("Failed to allocate space for SHM buffer\n");
550 TRACE("%p %dx%d format=%d size=%d\n", shm_buffer
, width
, height
, format
, size
);
553 shm_buffer
->width
= width
;
554 shm_buffer
->height
= height
;
555 shm_buffer
->map_size
= size
;
557 shm_buffer
->damage_region
= NtGdiCreateRectRgn(0, 0, width
, height
);
558 if (!shm_buffer
->damage_region
)
560 ERR("Failed to create buffer damage region\n");
564 section_size
.QuadPart
= size
;
565 status
= NtCreateSection(&handle
,
566 GENERIC_READ
| SECTION_MAP_READ
| SECTION_MAP_WRITE
,
567 NULL
, §ion_size
, PAGE_READWRITE
, SEC_COMMIT
, 0);
570 ERR("Failed to create SHM section status=0x%lx\n", (long)status
);
574 status
= NtMapViewOfSection(handle
, GetCurrentProcess(),
575 (PVOID
)&shm_buffer
->map_data
, 0, 0, NULL
,
576 &view_size
, ViewUnmap
, 0, PAGE_READWRITE
);
579 shm_buffer
->map_data
= NULL
;
580 ERR("Failed to create map SHM handle status=0x%lx\n", (long)status
);
584 status
= wine_server_handle_to_fd(handle
, FILE_READ_DATA
, &fd
, NULL
);
587 ERR("Failed to get fd from SHM handle status=0x%lx\n", (long)status
);
591 pool
= wl_shm_create_pool(process_wayland
.wl_shm
, fd
, size
);
594 ERR("Failed to create SHM pool fd=%d size=%d\n", fd
, size
);
597 shm_buffer
->wl_buffer
= wl_shm_pool_create_buffer(pool
, 0, width
, height
,
599 wl_shm_pool_destroy(pool
);
600 if (!shm_buffer
->wl_buffer
)
602 ERR("Failed to create SHM buffer %dx%d\n", width
, height
);
609 TRACE("=> map=%p\n", shm_buffer
->map_data
);
614 if (fd
>= 0) close(fd
);
615 if (handle
) NtClose(handle
);
616 if (shm_buffer
) wayland_shm_buffer_unref(shm_buffer
);