windowscodecs: Enable WICPixelFormat32bppBGRA in BMP encoder.
[wine.git] / dlls / winewayland.drv / window.c
blobfff3749e9ad29607597fc0b2599b12652fcf519f
1 /*
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
21 #if 0
22 #pragma makedep unix
23 #endif
25 #include "config.h"
27 #include <assert.h>
28 #include <stdlib.h>
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 */
41 HWND hwnd;
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 */
47 RECT window_rect;
48 /* USER client rectangle relative to win32 parent window client area */
49 RECT client_rect;
52 static int wayland_win_data_cmp_rb(const void *key,
53 const struct rb_entry *entry)
55 HWND key_hwnd = (HWND)key; /* cast to work around const */
56 const struct wayland_win_data *entry_win_data =
57 RB_ENTRY_VALUE(entry, const struct wayland_win_data, entry);
59 if (key_hwnd < entry_win_data->hwnd) return -1;
60 if (key_hwnd > entry_win_data->hwnd) return 1;
61 return 0;
64 static pthread_mutex_t win_data_mutex = PTHREAD_MUTEX_INITIALIZER;
65 static struct rb_tree win_data_rb = { wayland_win_data_cmp_rb };
67 /***********************************************************************
68 * wayland_win_data_create
70 * Create a data window structure for an existing window.
72 static struct wayland_win_data *wayland_win_data_create(HWND hwnd,
73 const RECT *window_rect,
74 const RECT *client_rect)
76 struct wayland_win_data *data;
77 struct rb_entry *rb_entry;
78 HWND parent;
80 /* Don't create win data for desktop or HWND_MESSAGE windows. */
81 if (!(parent = NtUserGetAncestor(hwnd, GA_PARENT))) return NULL;
82 if (parent != NtUserGetDesktopWindow() && !NtUserGetAncestor(parent, GA_PARENT))
83 return NULL;
85 if (!(data = calloc(1, sizeof(*data)))) return NULL;
87 data->hwnd = hwnd;
88 data->window_rect = *window_rect;
89 data->client_rect = *client_rect;
91 pthread_mutex_lock(&win_data_mutex);
93 /* Check that another thread hasn't already created the wayland_win_data. */
94 if ((rb_entry = rb_get(&win_data_rb, hwnd)))
96 free(data);
97 return RB_ENTRY_VALUE(rb_entry, struct wayland_win_data, entry);
100 rb_put(&win_data_rb, hwnd, &data->entry);
102 TRACE("hwnd=%p\n", data->hwnd);
104 return data;
107 /***********************************************************************
108 * wayland_win_data_destroy
110 static void wayland_win_data_destroy(struct wayland_win_data *data)
112 TRACE("hwnd=%p\n", data->hwnd);
114 rb_remove(&win_data_rb, &data->entry);
116 pthread_mutex_unlock(&win_data_mutex);
118 if (data->window_surface)
120 wayland_window_surface_update_wayland_surface(data->window_surface, NULL);
121 window_surface_release(data->window_surface);
123 if (data->wayland_surface) wayland_surface_destroy(data->wayland_surface);
124 free(data);
127 /***********************************************************************
128 * wayland_win_data_get
130 * Lock and return the data structure associated with a window.
132 static struct wayland_win_data *wayland_win_data_get(HWND hwnd)
134 struct rb_entry *rb_entry;
136 pthread_mutex_lock(&win_data_mutex);
138 if ((rb_entry = rb_get(&win_data_rb, hwnd)))
139 return RB_ENTRY_VALUE(rb_entry, struct wayland_win_data, entry);
141 pthread_mutex_unlock(&win_data_mutex);
143 return NULL;
146 /***********************************************************************
147 * wayland_win_data_release
149 * Release the data returned by wayland_win_data_get.
151 static void wayland_win_data_release(struct wayland_win_data *data)
153 assert(data);
154 pthread_mutex_unlock(&win_data_mutex);
157 static void wayland_win_data_get_config(struct wayland_win_data *data,
158 struct wayland_window_config *conf)
160 enum wayland_surface_config_state window_state = 0;
161 DWORD style;
163 conf->rect = data->window_rect;
164 conf->client_rect = data->client_rect;
165 style = NtUserGetWindowLongW(data->hwnd, GWL_STYLE);
167 TRACE("window=%s style=%#lx\n", wine_dbgstr_rect(&conf->rect), (long)style);
169 /* The fullscreen state is implied by the window position and style. */
170 if (NtUserIsWindowRectFullScreen(&conf->rect))
172 if ((style & WS_MAXIMIZE) && (style & WS_CAPTION) == WS_CAPTION)
173 window_state |= WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED;
174 else if (!(style & WS_MINIMIZE))
175 window_state |= WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN;
177 else if (style & WS_MAXIMIZE)
179 window_state |= WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED;
182 conf->state = window_state;
183 conf->scale = NtUserGetDpiForWindow(data->hwnd) / 96.0;
186 static void wayland_win_data_update_wayland_surface(struct wayland_win_data *data)
188 struct wayland_surface *surface = data->wayland_surface;
189 HWND parent = NtUserGetAncestor(data->hwnd, GA_PARENT);
190 BOOL visible, xdg_visible;
192 TRACE("hwnd=%p\n", data->hwnd);
194 /* We don't want wayland surfaces for child windows. */
195 if (parent != NtUserGetDesktopWindow() && parent != 0)
197 if (data->window_surface)
198 wayland_window_surface_update_wayland_surface(data->window_surface, NULL);
199 if (surface) wayland_surface_destroy(surface);
200 surface = NULL;
201 goto out;
204 /* Otherwise ensure that we have a wayland surface. */
205 if (!surface && !(surface = wayland_surface_create(data->hwnd))) return;
207 visible = (NtUserGetWindowLongW(data->hwnd, GWL_STYLE) & WS_VISIBLE) == WS_VISIBLE;
208 xdg_visible = surface->xdg_toplevel != NULL;
210 pthread_mutex_lock(&surface->mutex);
212 if (visible != xdg_visible)
214 /* If we have a pre-existing surface ensure it has no role. */
215 if (data->wayland_surface) wayland_surface_clear_role(surface);
216 /* If the window is a visible toplevel make it a wayland
217 * xdg_toplevel. Otherwise keep it role-less to avoid polluting the
218 * compositor with empty xdg_toplevels. */
219 if (visible) wayland_surface_make_toplevel(surface);
222 wayland_win_data_get_config(data, &surface->window);
224 pthread_mutex_unlock(&surface->mutex);
226 if (data->window_surface)
227 wayland_window_surface_update_wayland_surface(data->window_surface, surface);
229 out:
230 TRACE("hwnd=%p surface=%p=>%p\n", data->hwnd, data->wayland_surface, surface);
231 data->wayland_surface = surface;
234 static void wayland_win_data_update_wayland_state(struct wayland_win_data *data)
236 struct wayland_surface *surface = data->wayland_surface;
237 BOOL processing_config;
239 pthread_mutex_lock(&surface->mutex);
241 if (!surface->xdg_toplevel) goto out;
243 processing_config = surface->processing.serial &&
244 !surface->processing.processed;
246 TRACE("hwnd=%p window_state=%#x %s->state=%#x\n",
247 data->hwnd, surface->window.state,
248 processing_config ? "processing" : "current",
249 processing_config ? surface->processing.state : surface->current.state);
251 /* If we are not processing a compositor requested config, use the
252 * window state to determine and update the Wayland state. */
253 if (!processing_config)
255 /* First do all state unsettings, before setting new state. Some
256 * Wayland compositors misbehave if the order is reversed. */
257 if (!(surface->window.state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED) &&
258 (surface->current.state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED))
260 xdg_toplevel_unset_maximized(surface->xdg_toplevel);
262 if (!(surface->window.state & WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN) &&
263 (surface->current.state & WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN))
265 xdg_toplevel_unset_fullscreen(surface->xdg_toplevel);
268 if ((surface->window.state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED) &&
269 !(surface->current.state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED))
271 xdg_toplevel_set_maximized(surface->xdg_toplevel);
273 if ((surface->window.state & WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN) &&
274 !(surface->current.state & WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN))
276 xdg_toplevel_set_fullscreen(surface->xdg_toplevel, NULL);
279 else
281 surface->processing.processed = TRUE;
284 out:
285 pthread_mutex_unlock(&surface->mutex);
286 wl_display_flush(process_wayland.wl_display);
289 /***********************************************************************
290 * WAYLAND_DestroyWindow
292 void WAYLAND_DestroyWindow(HWND hwnd)
294 struct wayland_win_data *data;
296 TRACE("%p\n", hwnd);
298 if (!(data = wayland_win_data_get(hwnd))) return;
299 wayland_win_data_destroy(data);
302 /***********************************************************************
303 * WAYLAND_WindowPosChanging
305 BOOL WAYLAND_WindowPosChanging(HWND hwnd, HWND insert_after, UINT swp_flags,
306 const RECT *window_rect, const RECT *client_rect,
307 RECT *visible_rect, struct window_surface **surface)
309 struct wayland_win_data *data = wayland_win_data_get(hwnd);
310 HWND parent;
311 BOOL visible;
312 RECT surface_rect;
314 TRACE("hwnd %p window %s client %s visible %s after %p flags %08x\n",
315 hwnd, wine_dbgstr_rect(window_rect), wine_dbgstr_rect(client_rect),
316 wine_dbgstr_rect(visible_rect), insert_after, swp_flags);
318 if (!data && !(data = wayland_win_data_create(hwnd, window_rect, client_rect)))
319 return TRUE;
321 /* Release the dummy surface wine provides for toplevels. */
322 if (*surface) window_surface_release(*surface);
323 *surface = NULL;
325 parent = NtUserGetAncestor(hwnd, GA_PARENT);
326 visible = ((NtUserGetWindowLongW(hwnd, GWL_STYLE) & WS_VISIBLE) ||
327 (swp_flags & SWP_SHOWWINDOW)) &&
328 !(swp_flags & SWP_HIDEWINDOW);
330 /* Check if we don't want a dedicated window surface. */
331 if ((parent && parent != NtUserGetDesktopWindow()) || !visible) goto done;
333 surface_rect = *window_rect;
334 OffsetRect(&surface_rect, -surface_rect.left, -surface_rect.top);
336 /* Check if we can reuse our current window surface. */
337 if (data->window_surface &&
338 EqualRect(&data->window_surface->rect, &surface_rect))
340 window_surface_add_ref(data->window_surface);
341 *surface = data->window_surface;
342 TRACE("reusing surface %p\n", *surface);
343 goto done;
346 *surface = wayland_window_surface_create(data->hwnd, &surface_rect);
348 done:
349 wayland_win_data_release(data);
350 return TRUE;
353 /***********************************************************************
354 * WAYLAND_WindowPosChanged
356 void WAYLAND_WindowPosChanged(HWND hwnd, HWND insert_after, UINT swp_flags,
357 const RECT *window_rect, const RECT *client_rect,
358 const RECT *visible_rect, const RECT *valid_rects,
359 struct window_surface *surface)
361 struct wayland_win_data *data = wayland_win_data_get(hwnd);
363 TRACE("hwnd %p window %s client %s visible %s after %p flags %08x\n",
364 hwnd, wine_dbgstr_rect(window_rect), wine_dbgstr_rect(client_rect),
365 wine_dbgstr_rect(visible_rect), insert_after, swp_flags);
367 if (!data) return;
369 data->window_rect = *window_rect;
370 data->client_rect = *client_rect;
372 if (surface) window_surface_add_ref(surface);
373 if (data->window_surface) window_surface_release(data->window_surface);
374 data->window_surface = surface;
376 wayland_win_data_update_wayland_surface(data);
377 if (data->wayland_surface) wayland_win_data_update_wayland_state(data);
379 wayland_win_data_release(data);
382 static void wayland_resize_desktop(void)
384 RECT virtual_rect = NtUserGetVirtualScreenRect();
385 NtUserSetWindowPos(NtUserGetDesktopWindow(), 0,
386 virtual_rect.left, virtual_rect.top,
387 virtual_rect.right - virtual_rect.left,
388 virtual_rect.bottom - virtual_rect.top,
389 SWP_NOZORDER | SWP_NOACTIVATE | SWP_DEFERERASE);
392 static void wayland_configure_window(HWND hwnd)
394 struct wayland_surface *surface;
395 INT width, height, window_width, window_height;
396 INT window_surf_width, window_surf_height;
397 UINT flags = 0;
398 uint32_t state;
399 DWORD style;
400 BOOL needs_enter_size_move = FALSE;
401 BOOL needs_exit_size_move = FALSE;
403 if (!(surface = wayland_surface_lock_hwnd(hwnd))) return;
405 if (!surface->xdg_toplevel)
407 TRACE("missing xdg_toplevel, returning\n");
408 pthread_mutex_unlock(&surface->mutex);
409 return;
412 if (!surface->requested.serial)
414 TRACE("requested configure event already handled, returning\n");
415 pthread_mutex_unlock(&surface->mutex);
416 return;
419 surface->processing = surface->requested;
420 memset(&surface->requested, 0, sizeof(surface->requested));
422 state = surface->processing.state;
423 /* Ignore size hints if we don't have a state that requires strict
424 * size adherence, in order to avoid spurious resizes. */
425 if (state)
427 width = surface->processing.width;
428 height = surface->processing.height;
430 else
432 width = height = 0;
435 if ((state & WAYLAND_SURFACE_CONFIG_STATE_RESIZING) && !surface->resizing)
437 surface->resizing = TRUE;
438 needs_enter_size_move = TRUE;
441 if (!(state & WAYLAND_SURFACE_CONFIG_STATE_RESIZING) && surface->resizing)
443 surface->resizing = FALSE;
444 needs_exit_size_move = TRUE;
447 /* Transitions between normal/max/fullscreen may entail a frame change. */
448 if ((state ^ surface->current.state) &
449 (WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED |
450 WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN))
452 flags |= SWP_FRAMECHANGED;
455 wayland_surface_coords_from_window(surface,
456 surface->window.rect.right -
457 surface->window.rect.left,
458 surface->window.rect.bottom -
459 surface->window.rect.top,
460 &window_surf_width, &window_surf_height);
462 /* If the window is already fullscreen and its size is compatible with what
463 * the compositor is requesting, don't force a resize, since some applications
464 * are very insistent on a particular fullscreen size (which may not match
465 * the monitor size). */
466 if ((surface->window.state & WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN) &&
467 wayland_surface_config_is_compatible(&surface->processing,
468 window_surf_width, window_surf_height,
469 surface->window.state))
471 flags |= SWP_NOSIZE;
474 wayland_surface_coords_to_window(surface, width, height,
475 &window_width, &window_height);
477 pthread_mutex_unlock(&surface->mutex);
479 TRACE("processing=%dx%d,%#x\n", width, height, state);
481 if (needs_enter_size_move) send_message(hwnd, WM_ENTERSIZEMOVE, 0, 0);
482 if (needs_exit_size_move) send_message(hwnd, WM_EXITSIZEMOVE, 0, 0);
484 flags |= SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOMOVE;
485 if (window_width == 0 || window_height == 0) flags |= SWP_NOSIZE;
487 style = NtUserGetWindowLongW(hwnd, GWL_STYLE);
488 if (!(state & WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED) != !(style & WS_MAXIMIZE))
489 NtUserSetWindowLong(hwnd, GWL_STYLE, style ^ WS_MAXIMIZE, FALSE);
491 /* The Wayland maximized and fullscreen states are very strict about
492 * surface size, so don't let the application override it. The tiled state
493 * is not as strict, but it indicates a strong size preference, so try to
494 * respect it. */
495 if (state & (WAYLAND_SURFACE_CONFIG_STATE_MAXIMIZED |
496 WAYLAND_SURFACE_CONFIG_STATE_FULLSCREEN |
497 WAYLAND_SURFACE_CONFIG_STATE_TILED))
499 flags |= SWP_NOSENDCHANGING;
502 NtUserSetWindowPos(hwnd, 0, 0, 0, window_width, window_height, flags);
505 /**********************************************************************
506 * WAYLAND_WindowMessage
508 LRESULT WAYLAND_WindowMessage(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
510 switch (msg)
512 case WM_WAYLAND_INIT_DISPLAY_DEVICES:
513 wayland_init_display_devices(TRUE);
514 wayland_resize_desktop();
515 return 0;
516 case WM_WAYLAND_CONFIGURE:
517 wayland_configure_window(hwnd);
518 return 0;
519 default:
520 FIXME("got window msg %x hwnd %p wp %lx lp %lx\n", msg, hwnd, (long)wp, lp);
521 return 0;
525 /**********************************************************************
526 * WAYLAND_DesktopWindowProc
528 LRESULT WAYLAND_DesktopWindowProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
530 switch (msg)
532 case WM_DISPLAYCHANGE:
533 wayland_resize_desktop();
534 break;
537 return NtUserMessageCall(hwnd, msg, wp, lp, 0, NtUserDefWindowProc, FALSE);
540 static enum xdg_toplevel_resize_edge hittest_to_resize_edge(WPARAM hittest)
542 switch (hittest)
544 case WMSZ_LEFT: return XDG_TOPLEVEL_RESIZE_EDGE_LEFT;
545 case WMSZ_RIGHT: return XDG_TOPLEVEL_RESIZE_EDGE_RIGHT;
546 case WMSZ_TOP: return XDG_TOPLEVEL_RESIZE_EDGE_TOP;
547 case WMSZ_TOPLEFT: return XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT;
548 case WMSZ_TOPRIGHT: return XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT;
549 case WMSZ_BOTTOM: return XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM;
550 case WMSZ_BOTTOMLEFT: return XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT;
551 case WMSZ_BOTTOMRIGHT: return XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT;
552 default: return XDG_TOPLEVEL_RESIZE_EDGE_NONE;
556 /***********************************************************************
557 * WAYLAND_SysCommand
559 LRESULT WAYLAND_SysCommand(HWND hwnd, WPARAM wparam, LPARAM lparam)
561 LRESULT ret = -1;
562 WPARAM command = wparam & 0xfff0;
563 uint32_t button_serial;
564 struct wl_seat *wl_seat;
565 struct wayland_surface *surface;
567 TRACE("cmd=%lx hwnd=%p, %lx, %lx\n",
568 (long)command, hwnd, (long)wparam, lparam);
570 pthread_mutex_lock(&process_wayland.pointer.mutex);
571 if (process_wayland.pointer.focused_hwnd == hwnd)
572 button_serial = process_wayland.pointer.button_serial;
573 else
574 button_serial = 0;
575 pthread_mutex_unlock(&process_wayland.pointer.mutex);
577 if (command == SC_MOVE || command == SC_SIZE)
579 if ((surface = wayland_surface_lock_hwnd(hwnd)))
581 pthread_mutex_lock(&process_wayland.seat.mutex);
582 wl_seat = process_wayland.seat.wl_seat;
583 if (wl_seat && surface->xdg_toplevel && button_serial)
585 if (command == SC_MOVE)
587 xdg_toplevel_move(surface->xdg_toplevel, wl_seat, button_serial);
589 else if (command == SC_SIZE)
591 xdg_toplevel_resize(surface->xdg_toplevel, wl_seat, button_serial,
592 hittest_to_resize_edge(wparam & 0x0f));
595 pthread_mutex_unlock(&process_wayland.seat.mutex);
596 pthread_mutex_unlock(&surface->mutex);
597 ret = 0;
601 wl_display_flush(process_wayland.wl_display);
602 return ret;
605 /**********************************************************************
606 * wayland_window_flush
608 * Flush the window_surface associated with a HWND.
610 void wayland_window_flush(HWND hwnd)
612 struct wayland_win_data *data = wayland_win_data_get(hwnd);
614 if (!data) return;
616 if (data->window_surface)
617 data->window_surface->funcs->flush(data->window_surface);
619 wayland_win_data_release(data);
622 /**********************************************************************
623 * wayland_surface_lock_hwnd
625 struct wayland_surface *wayland_surface_lock_hwnd(HWND hwnd)
627 struct wayland_win_data *data = wayland_win_data_get(hwnd);
628 struct wayland_surface *surface;
630 if (!data) return NULL;
632 if ((surface = data->wayland_surface)) pthread_mutex_lock(&surface->mutex);
634 wayland_win_data_release(data);
636 return surface;