2 * Wayland window handling
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"
32 #include "wine/debug.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(waylanddrv
);
36 /* private window data */
37 struct wayland_win_data
39 struct rb_entry entry
;
40 /* hwnd that this private data belongs to */
42 /* wayland surface (if any) for this window */
43 struct wayland_surface
*wayland_surface
;
44 /* wine window_surface backing this window */
45 struct window_surface
*window_surface
;
46 /* USER window rectangle relative to win32 parent window client area */
50 static int wayland_win_data_cmp_rb(const void *key
,
51 const struct rb_entry
*entry
)
53 HWND key_hwnd
= (HWND
)key
; /* cast to work around const */
54 const struct wayland_win_data
*entry_win_data
=
55 RB_ENTRY_VALUE(entry
, const struct wayland_win_data
, entry
);
57 if (key_hwnd
< entry_win_data
->hwnd
) return -1;
58 if (key_hwnd
> entry_win_data
->hwnd
) return 1;
62 static pthread_mutex_t win_data_mutex
= PTHREAD_MUTEX_INITIALIZER
;
63 static struct rb_tree win_data_rb
= { wayland_win_data_cmp_rb
};
65 /***********************************************************************
66 * wayland_win_data_create
68 * Create a data window structure for an existing window.
70 static struct wayland_win_data
*wayland_win_data_create(HWND hwnd
,
71 const RECT
*window_rect
)
73 struct wayland_win_data
*data
;
74 struct rb_entry
*rb_entry
;
77 /* Don't create win data for desktop or HWND_MESSAGE windows. */
78 if (!(parent
= NtUserGetAncestor(hwnd
, GA_PARENT
))) return NULL
;
79 if (parent
!= NtUserGetDesktopWindow() && !NtUserGetAncestor(parent
, GA_PARENT
))
82 if (!(data
= calloc(1, sizeof(*data
)))) return NULL
;
85 data
->window_rect
= *window_rect
;
87 pthread_mutex_lock(&win_data_mutex
);
89 /* Check that another thread hasn't already created the wayland_win_data. */
90 if ((rb_entry
= rb_get(&win_data_rb
, hwnd
)))
93 return RB_ENTRY_VALUE(rb_entry
, struct wayland_win_data
, entry
);
96 rb_put(&win_data_rb
, hwnd
, &data
->entry
);
98 TRACE("hwnd=%p\n", data
->hwnd
);
103 /***********************************************************************
104 * wayland_win_data_destroy
106 static void wayland_win_data_destroy(struct wayland_win_data
*data
)
108 TRACE("hwnd=%p\n", data
->hwnd
);
110 rb_remove(&win_data_rb
, &data
->entry
);
112 pthread_mutex_unlock(&win_data_mutex
);
114 if (data
->window_surface
)
116 wayland_window_surface_update_wayland_surface(data
->window_surface
, NULL
);
117 window_surface_release(data
->window_surface
);
119 if (data
->wayland_surface
) wayland_surface_destroy(data
->wayland_surface
);
123 /***********************************************************************
124 * wayland_win_data_get
126 * Lock and return the data structure associated with a window.
128 static struct wayland_win_data
*wayland_win_data_get(HWND hwnd
)
130 struct rb_entry
*rb_entry
;
132 pthread_mutex_lock(&win_data_mutex
);
134 if ((rb_entry
= rb_get(&win_data_rb
, hwnd
)))
135 return RB_ENTRY_VALUE(rb_entry
, struct wayland_win_data
, entry
);
137 pthread_mutex_unlock(&win_data_mutex
);
142 /***********************************************************************
143 * wayland_win_data_release
145 * Release the data returned by wayland_win_data_get.
147 static void wayland_win_data_release(struct wayland_win_data
*data
)
150 pthread_mutex_unlock(&win_data_mutex
);
153 static void wayland_win_data_get_config(struct wayland_win_data
*data
,
154 struct wayland_window_config
*conf
)
156 enum wayland_surface_config_state window_state
= 0;
159 conf
->rect
= data
->window_rect
;
160 style
= NtUserGetWindowLongW(data
->hwnd
, GWL_STYLE
);
162 TRACE("window=%s style=%#lx\n", wine_dbgstr_rect(&conf
->rect
), (long)style
);
164 /* The fullscreen state is implied by the window position and style. */
165 if (NtUserIsWindowRectFullScreen(&conf
->rect
))
167 if ((style
& WS_MAXIMIZE
) && (style
& WS_CAPTION
) == WS_CAPTION
)
168 window_state
|= WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED
;
169 else if (!(style
& WS_MINIMIZE
))
170 window_state
|= WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN
;
172 else if (style
& WS_MAXIMIZE
)
174 window_state
|= WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED
;
177 conf
->state
= window_state
;
180 static void wayland_win_data_update_wayland_surface(struct wayland_win_data
*data
)
182 struct wayland_surface
*surface
= data
->wayland_surface
;
183 HWND parent
= NtUserGetAncestor(data
->hwnd
, GA_PARENT
);
184 BOOL visible
, xdg_visible
;
186 TRACE("hwnd=%p\n", data
->hwnd
);
188 /* We don't want wayland surfaces for child windows. */
189 if (parent
!= NtUserGetDesktopWindow() && parent
!= 0)
191 if (data
->window_surface
)
192 wayland_window_surface_update_wayland_surface(data
->window_surface
, NULL
);
193 if (surface
) wayland_surface_destroy(surface
);
198 /* Otherwise ensure that we have a wayland surface. */
199 if (!surface
&& !(surface
= wayland_surface_create(data
->hwnd
))) return;
201 visible
= (NtUserGetWindowLongW(data
->hwnd
, GWL_STYLE
) & WS_VISIBLE
) == WS_VISIBLE
;
202 xdg_visible
= surface
->xdg_toplevel
!= NULL
;
204 pthread_mutex_lock(&surface
->mutex
);
206 if (visible
!= xdg_visible
)
208 /* If we have a pre-existing surface ensure it has no role. */
209 if (data
->wayland_surface
) wayland_surface_clear_role(surface
);
210 /* If the window is a visible toplevel make it a wayland
211 * xdg_toplevel. Otherwise keep it role-less to avoid polluting the
212 * compositor with empty xdg_toplevels. */
213 if (visible
) wayland_surface_make_toplevel(surface
);
216 wayland_win_data_get_config(data
, &surface
->window
);
218 pthread_mutex_unlock(&surface
->mutex
);
220 if (data
->window_surface
)
221 wayland_window_surface_update_wayland_surface(data
->window_surface
, surface
);
224 TRACE("hwnd=%p surface=%p=>%p\n", data
->hwnd
, data
->wayland_surface
, surface
);
225 data
->wayland_surface
= surface
;
228 static void wayland_win_data_update_wayland_state(struct wayland_win_data
*data
)
230 struct wayland_surface
*surface
= data
->wayland_surface
;
231 BOOL processing_config
;
233 pthread_mutex_lock(&surface
->mutex
);
235 if (!surface
->xdg_toplevel
) goto out
;
237 processing_config
= surface
->processing
.serial
&&
238 !surface
->processing
.processed
;
240 TRACE("hwnd=%p window_state=%#x %s->state=%#x\n",
241 data
->hwnd
, surface
->window
.state
,
242 processing_config
? "processing" : "current",
243 processing_config
? surface
->processing
.state
: surface
->current
.state
);
245 /* If we are not processing a compositor requested config, use the
246 * window state to determine and update the Wayland state. */
247 if (!processing_config
)
249 /* First do all state unsettings, before setting new state. Some
250 * Wayland compositors misbehave if the order is reversed. */
251 if (!(surface
->window
.state
& WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED
) &&
252 (surface
->current
.state
& WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED
))
254 xdg_toplevel_unset_maximized(surface
->xdg_toplevel
);
256 if (!(surface
->window
.state
& WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN
) &&
257 (surface
->current
.state
& WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN
))
259 xdg_toplevel_unset_fullscreen(surface
->xdg_toplevel
);
262 if ((surface
->window
.state
& WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED
) &&
263 !(surface
->current
.state
& WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED
))
265 xdg_toplevel_set_maximized(surface
->xdg_toplevel
);
267 if ((surface
->window
.state
& WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN
) &&
268 !(surface
->current
.state
& WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN
))
270 xdg_toplevel_set_fullscreen(surface
->xdg_toplevel
, NULL
);
275 surface
->processing
.processed
= TRUE
;
279 pthread_mutex_unlock(&surface
->mutex
);
280 wl_display_flush(process_wayland
.wl_display
);
283 /***********************************************************************
284 * WAYLAND_DestroyWindow
286 void WAYLAND_DestroyWindow(HWND hwnd
)
288 struct wayland_win_data
*data
;
292 if (!(data
= wayland_win_data_get(hwnd
))) return;
293 wayland_win_data_destroy(data
);
296 /***********************************************************************
297 * WAYLAND_WindowPosChanging
299 BOOL
WAYLAND_WindowPosChanging(HWND hwnd
, HWND insert_after
, UINT swp_flags
,
300 const RECT
*window_rect
, const RECT
*client_rect
,
301 RECT
*visible_rect
, struct window_surface
**surface
)
303 struct wayland_win_data
*data
= wayland_win_data_get(hwnd
);
308 TRACE("hwnd %p window %s client %s visible %s after %p flags %08x\n",
309 hwnd
, wine_dbgstr_rect(window_rect
), wine_dbgstr_rect(client_rect
),
310 wine_dbgstr_rect(visible_rect
), insert_after
, swp_flags
);
312 if (!data
&& !(data
= wayland_win_data_create(hwnd
, window_rect
))) return TRUE
;
314 /* Release the dummy surface wine provides for toplevels. */
315 if (*surface
) window_surface_release(*surface
);
318 parent
= NtUserGetAncestor(hwnd
, GA_PARENT
);
319 visible
= ((NtUserGetWindowLongW(hwnd
, GWL_STYLE
) & WS_VISIBLE
) ||
320 (swp_flags
& SWP_SHOWWINDOW
)) &&
321 !(swp_flags
& SWP_HIDEWINDOW
);
323 /* Check if we don't want a dedicated window surface. */
324 if ((parent
&& parent
!= NtUserGetDesktopWindow()) || !visible
) goto done
;
326 surface_rect
= *window_rect
;
327 OffsetRect(&surface_rect
, -surface_rect
.left
, -surface_rect
.top
);
329 /* Check if we can reuse our current window surface. */
330 if (data
->window_surface
&&
331 EqualRect(&data
->window_surface
->rect
, &surface_rect
))
333 window_surface_add_ref(data
->window_surface
);
334 *surface
= data
->window_surface
;
335 TRACE("reusing surface %p\n", *surface
);
339 *surface
= wayland_window_surface_create(data
->hwnd
, &surface_rect
);
342 wayland_win_data_release(data
);
346 /***********************************************************************
347 * WAYLAND_WindowPosChanged
349 void WAYLAND_WindowPosChanged(HWND hwnd
, HWND insert_after
, UINT swp_flags
,
350 const RECT
*window_rect
, const RECT
*client_rect
,
351 const RECT
*visible_rect
, const RECT
*valid_rects
,
352 struct window_surface
*surface
)
354 struct wayland_win_data
*data
= wayland_win_data_get(hwnd
);
356 TRACE("hwnd %p window %s client %s visible %s after %p flags %08x\n",
357 hwnd
, wine_dbgstr_rect(window_rect
), wine_dbgstr_rect(client_rect
),
358 wine_dbgstr_rect(visible_rect
), insert_after
, swp_flags
);
362 data
->window_rect
= *window_rect
;
364 if (surface
) window_surface_add_ref(surface
);
365 if (data
->window_surface
) window_surface_release(data
->window_surface
);
366 data
->window_surface
= surface
;
368 wayland_win_data_update_wayland_surface(data
);
369 if (data
->wayland_surface
) wayland_win_data_update_wayland_state(data
);
371 wayland_win_data_release(data
);
374 static void wayland_resize_desktop(void)
376 RECT virtual_rect
= NtUserGetVirtualScreenRect();
377 NtUserSetWindowPos(NtUserGetDesktopWindow(), 0,
378 virtual_rect
.left
, virtual_rect
.top
,
379 virtual_rect
.right
- virtual_rect
.left
,
380 virtual_rect
.bottom
- virtual_rect
.top
,
381 SWP_NOZORDER
| SWP_NOACTIVATE
| SWP_DEFERERASE
);
384 static void wayland_configure_window(HWND hwnd
)
386 struct wayland_surface
*surface
;
391 BOOL needs_enter_size_move
= FALSE
;
392 BOOL needs_exit_size_move
= FALSE
;
394 if (!(surface
= wayland_surface_lock_hwnd(hwnd
))) return;
396 if (!surface
->xdg_toplevel
)
398 TRACE("missing xdg_toplevel, returning\n");
399 pthread_mutex_unlock(&surface
->mutex
);
403 if (!surface
->requested
.serial
)
405 TRACE("requested configure event already handled, returning\n");
406 pthread_mutex_unlock(&surface
->mutex
);
410 surface
->processing
= surface
->requested
;
411 memset(&surface
->requested
, 0, sizeof(surface
->requested
));
413 width
= surface
->processing
.width
;
414 height
= surface
->processing
.height
;
415 state
= surface
->processing
.state
;
417 if ((state
& WAYLAND_SURFACE_CONFIG_STATE_RESIZING
) && !surface
->resizing
)
419 surface
->resizing
= TRUE
;
420 needs_enter_size_move
= TRUE
;
423 if (!(state
& WAYLAND_SURFACE_CONFIG_STATE_RESIZING
) && surface
->resizing
)
425 surface
->resizing
= FALSE
;
426 needs_exit_size_move
= TRUE
;
429 /* Transitions between normal/max/fullscreen may entail a frame change. */
430 if ((state
^ surface
->current
.state
) &
431 (WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED
|
432 WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN
))
434 flags
|= SWP_FRAMECHANGED
;
437 pthread_mutex_unlock(&surface
->mutex
);
439 TRACE("processing=%dx%d,%#x\n", width
, height
, state
);
441 if (needs_enter_size_move
) send_message(hwnd
, WM_ENTERSIZEMOVE
, 0, 0);
442 if (needs_exit_size_move
) send_message(hwnd
, WM_EXITSIZEMOVE
, 0, 0);
444 flags
|= SWP_NOACTIVATE
| SWP_NOZORDER
| SWP_NOOWNERZORDER
| SWP_NOMOVE
;
445 if (width
== 0 || height
== 0) flags
|= SWP_NOSIZE
;
447 style
= NtUserGetWindowLongW(hwnd
, GWL_STYLE
);
448 if (!(state
& WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED
) != !(style
& WS_MAXIMIZE
))
449 NtUserSetWindowLong(hwnd
, GWL_STYLE
, style
^ WS_MAXIMIZE
, FALSE
);
451 /* The Wayland maximized and fullscreen states are very strict about
452 * surface size, so don't let the application override it. The tiled state
453 * is not as strict, but it indicates a strong size preference, so try to
455 if (state
& (WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED
|
456 WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN
|
457 WAYLAND_SURFACE_CONFIG_STATE_TILED
))
459 flags
|= SWP_NOSENDCHANGING
;
462 NtUserSetWindowPos(hwnd
, 0, 0, 0, width
, height
, flags
);
465 /**********************************************************************
466 * WAYLAND_WindowMessage
468 LRESULT
WAYLAND_WindowMessage(HWND hwnd
, UINT msg
, WPARAM wp
, LPARAM lp
)
472 case WM_WAYLAND_INIT_DISPLAY_DEVICES
:
473 wayland_init_display_devices(TRUE
);
474 wayland_resize_desktop();
476 case WM_WAYLAND_CONFIGURE
:
477 wayland_configure_window(hwnd
);
480 FIXME("got window msg %x hwnd %p wp %lx lp %lx\n", msg
, hwnd
, (long)wp
, lp
);
485 /**********************************************************************
486 * WAYLAND_DesktopWindowProc
488 LRESULT
WAYLAND_DesktopWindowProc(HWND hwnd
, UINT msg
, WPARAM wp
, LPARAM lp
)
492 case WM_DISPLAYCHANGE
:
493 wayland_resize_desktop();
497 return NtUserMessageCall(hwnd
, msg
, wp
, lp
, 0, NtUserDefWindowProc
, FALSE
);
500 static enum xdg_toplevel_resize_edge
hittest_to_resize_edge(WPARAM hittest
)
504 case WMSZ_LEFT
: return XDG_TOPLEVEL_RESIZE_EDGE_LEFT
;
505 case WMSZ_RIGHT
: return XDG_TOPLEVEL_RESIZE_EDGE_RIGHT
;
506 case WMSZ_TOP
: return XDG_TOPLEVEL_RESIZE_EDGE_TOP
;
507 case WMSZ_TOPLEFT
: return XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT
;
508 case WMSZ_TOPRIGHT
: return XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT
;
509 case WMSZ_BOTTOM
: return XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM
;
510 case WMSZ_BOTTOMLEFT
: return XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT
;
511 case WMSZ_BOTTOMRIGHT
: return XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT
;
512 default: return XDG_TOPLEVEL_RESIZE_EDGE_NONE
;
516 /***********************************************************************
519 LRESULT
WAYLAND_SysCommand(HWND hwnd
, WPARAM wparam
, LPARAM lparam
)
522 WPARAM command
= wparam
& 0xfff0;
523 uint32_t button_serial
;
524 struct wl_seat
*wl_seat
;
525 struct wayland_surface
*surface
;
527 TRACE("cmd=%lx hwnd=%p, %lx, %lx\n",
528 (long)command
, hwnd
, (long)wparam
, lparam
);
530 pthread_mutex_lock(&process_wayland
.pointer
.mutex
);
531 if (process_wayland
.pointer
.focused_hwnd
== hwnd
)
532 button_serial
= process_wayland
.pointer
.button_serial
;
535 pthread_mutex_unlock(&process_wayland
.pointer
.mutex
);
537 if (command
== SC_MOVE
|| command
== SC_SIZE
)
539 if ((surface
= wayland_surface_lock_hwnd(hwnd
)))
541 pthread_mutex_lock(&process_wayland
.seat
.mutex
);
542 wl_seat
= process_wayland
.seat
.wl_seat
;
543 if (wl_seat
&& surface
->xdg_toplevel
&& button_serial
)
545 if (command
== SC_MOVE
)
547 xdg_toplevel_move(surface
->xdg_toplevel
, wl_seat
, button_serial
);
549 else if (command
== SC_SIZE
)
551 xdg_toplevel_resize(surface
->xdg_toplevel
, wl_seat
, button_serial
,
552 hittest_to_resize_edge(wparam
& 0x0f));
555 pthread_mutex_unlock(&process_wayland
.seat
.mutex
);
556 pthread_mutex_unlock(&surface
->mutex
);
561 wl_display_flush(process_wayland
.wl_display
);
565 /**********************************************************************
566 * wayland_window_flush
568 * Flush the window_surface associated with a HWND.
570 void wayland_window_flush(HWND hwnd
)
572 struct wayland_win_data
*data
= wayland_win_data_get(hwnd
);
576 if (data
->window_surface
)
577 data
->window_surface
->funcs
->flush(data
->window_surface
);
579 wayland_win_data_release(data
);
582 /**********************************************************************
583 * wayland_surface_lock_hwnd
585 struct wayland_surface
*wayland_surface_lock_hwnd(HWND hwnd
)
587 struct wayland_win_data
*data
= wayland_win_data_get(hwnd
);
588 struct wayland_surface
*surface
;
590 if (!data
) return NULL
;
592 if ((surface
= data
->wayland_surface
)) pthread_mutex_lock(&surface
->mutex
);
594 wayland_win_data_release(data
);