windowscodecs: Enable WICPixelFormat32bppBGRA in BMP encoder.
[wine.git] / dlls / winewayland.drv / window_surface.c
blob67cb2fe2d0b29c09dd15e821997344f83807aa6e
1 /*
2 * Wayland window surface implementation
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 <limits.h>
28 #include <stdlib.h>
30 #include "waylanddrv.h"
31 #include "wine/debug.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(waylanddrv);
35 struct wayland_buffer_queue
37 struct wl_event_queue *wl_event_queue;
38 struct wl_list buffer_list;
39 int width;
40 int height;
43 struct wayland_window_surface
45 struct window_surface header;
46 HWND hwnd;
47 struct wayland_surface *wayland_surface;
48 struct wayland_buffer_queue *wayland_buffer_queue;
49 RECT bounds;
50 void *bits;
51 pthread_mutex_t mutex;
52 BITMAPINFO info;
55 static struct wayland_window_surface *wayland_window_surface_cast(
56 struct window_surface *window_surface)
58 return (struct wayland_window_surface *)window_surface;
61 static inline void reset_bounds(RECT *bounds)
63 bounds->left = bounds->top = INT_MAX;
64 bounds->right = bounds->bottom = INT_MIN;
67 static void buffer_release(void *data, struct wl_buffer *buffer)
69 struct wayland_shm_buffer *shm_buffer = data;
70 TRACE("shm_buffer=%p\n", shm_buffer);
71 shm_buffer->busy = FALSE;
72 wayland_shm_buffer_unref(shm_buffer);
75 static const struct wl_buffer_listener buffer_listener = { buffer_release };
77 /**********************************************************************
78 * wayland_buffer_queue_destroy
80 * Destroys a buffer queue and any contained buffers.
82 static void wayland_buffer_queue_destroy(struct wayland_buffer_queue *queue)
84 struct wayland_shm_buffer *shm_buffer, *next;
86 wl_list_for_each_safe(shm_buffer, next, &queue->buffer_list, link)
88 wl_list_remove(&shm_buffer->link);
89 wl_list_init(&shm_buffer->link);
90 /* Since this buffer may still be busy, attach it to the per-process
91 * wl_event_queue to handle any future buffer release events. */
92 wl_proxy_set_queue((struct wl_proxy *)shm_buffer->wl_buffer,
93 process_wayland.wl_event_queue);
94 wayland_shm_buffer_unref(shm_buffer);
97 if (queue->wl_event_queue)
99 /* Dispatch the event queue before destruction to process any
100 * pending buffer release events. This is required after changing
101 * the buffer proxy event queue in the previous step, to avoid
102 * missing any events. */
103 wl_display_dispatch_queue_pending(process_wayland.wl_display,
104 queue->wl_event_queue);
105 wl_event_queue_destroy(queue->wl_event_queue);
108 free(queue);
111 /**********************************************************************
112 * wayland_buffer_queue_create
114 * Creates a buffer queue containing buffers with the specified width and height.
116 static struct wayland_buffer_queue *wayland_buffer_queue_create(int width, int height)
118 struct wayland_buffer_queue *queue;
120 queue = calloc(1, sizeof(*queue));
121 if (!queue) goto err;
123 queue->wl_event_queue = wl_display_create_queue(process_wayland.wl_display);
124 if (!queue->wl_event_queue) goto err;
125 queue->width = width;
126 queue->height = height;
128 wl_list_init(&queue->buffer_list);
130 return queue;
132 err:
133 if (queue) wayland_buffer_queue_destroy(queue);
134 return NULL;
137 /**********************************************************************
138 * wayland_buffer_queue_get_free_buffer
140 * Gets a free buffer from the buffer queue. If no free buffers
141 * are available this function blocks until it can provide one.
143 static struct wayland_shm_buffer *wayland_buffer_queue_get_free_buffer(struct wayland_buffer_queue *queue)
145 struct wayland_shm_buffer *shm_buffer;
147 TRACE("queue=%p\n", queue);
149 while (TRUE)
151 int nbuffers = 0;
153 /* Dispatch any pending buffer release events. */
154 wl_display_dispatch_queue_pending(process_wayland.wl_display,
155 queue->wl_event_queue);
157 /* Search through our buffers to find an available one. */
158 wl_list_for_each(shm_buffer, &queue->buffer_list, link)
160 if (!shm_buffer->busy) goto out;
161 nbuffers++;
164 /* Dynamically create up to 3 buffers. */
165 if (nbuffers < 3)
167 shm_buffer = wayland_shm_buffer_create(queue->width, queue->height,
168 WL_SHM_FORMAT_XRGB8888);
169 if (shm_buffer)
171 /* Buffer events go to their own queue so that we can dispatch
172 * them independently. */
173 wl_proxy_set_queue((struct wl_proxy *) shm_buffer->wl_buffer,
174 queue->wl_event_queue);
175 wl_buffer_add_listener(shm_buffer->wl_buffer, &buffer_listener,
176 shm_buffer);
177 wl_list_insert(&queue->buffer_list, &shm_buffer->link);
178 goto out;
180 else if (nbuffers < 2)
182 /* If we failed to allocate a new buffer, but we have at least two
183 * buffers busy, there is a good chance the compositor will
184 * eventually release one of them, so dispatch events and wait
185 * below. Otherwise, give up and return a NULL buffer. */
186 ERR(" => failed to acquire buffer\n");
187 return NULL;
191 /* We don't have any buffers available, so block waiting for a buffer
192 * release event. */
193 if (wl_display_dispatch_queue(process_wayland.wl_display,
194 queue->wl_event_queue) == -1)
196 return NULL;
200 out:
201 TRACE(" => %p %dx%d map=[%p, %p)\n",
202 shm_buffer, shm_buffer->width, shm_buffer->height, shm_buffer->map_data,
203 (unsigned char*)shm_buffer->map_data + shm_buffer->map_size);
205 return shm_buffer;
208 /**********************************************************************
209 * wayland_buffer_queue_add_damage
211 static void wayland_buffer_queue_add_damage(struct wayland_buffer_queue *queue, HRGN damage)
213 struct wayland_shm_buffer *shm_buffer;
215 wl_list_for_each(shm_buffer, &queue->buffer_list, link)
217 NtGdiCombineRgn(shm_buffer->damage_region, shm_buffer->damage_region,
218 damage, RGN_OR);
222 /***********************************************************************
223 * wayland_window_surface_lock
225 static void wayland_window_surface_lock(struct window_surface *window_surface)
227 struct wayland_window_surface *wws = wayland_window_surface_cast(window_surface);
228 pthread_mutex_lock(&wws->mutex);
231 /***********************************************************************
232 * wayland_window_surface_unlock
234 static void wayland_window_surface_unlock(struct window_surface *window_surface)
236 struct wayland_window_surface *wws = wayland_window_surface_cast(window_surface);
237 pthread_mutex_unlock(&wws->mutex);
240 /***********************************************************************
241 * wayland_window_surface_get_bitmap_info
243 static void *wayland_window_surface_get_bitmap_info(struct window_surface *window_surface,
244 BITMAPINFO *info)
246 struct wayland_window_surface *surface = wayland_window_surface_cast(window_surface);
247 /* We don't store any additional information at the end of our BITMAPINFO, so
248 * just copy the structure itself. */
249 memcpy(info, &surface->info, sizeof(*info));
250 return surface->bits;
253 /***********************************************************************
254 * wayland_window_surface_get_bounds
256 static RECT *wayland_window_surface_get_bounds(struct window_surface *window_surface)
258 struct wayland_window_surface *wws = wayland_window_surface_cast(window_surface);
259 return &wws->bounds;
262 /***********************************************************************
263 * wayland_window_surface_set_region
265 static void wayland_window_surface_set_region(struct window_surface *window_surface,
266 HRGN region)
268 /* TODO */
271 /**********************************************************************
272 * get_region_data
274 RGNDATA *get_region_data(HRGN region)
276 RGNDATA *data;
277 DWORD size;
279 if (!region) return NULL;
280 if (!(size = NtGdiGetRegionData(region, 0, NULL))) return NULL;
281 if (!(data = malloc(size))) return NULL;
282 if (!NtGdiGetRegionData(region, size, data))
284 free(data);
285 return NULL;
288 return data;
291 /**********************************************************************
292 * copy_pixel_region
294 static void copy_pixel_region(char *src_pixels, RECT *src_rect,
295 char *dst_pixels, RECT *dst_rect,
296 HRGN region)
298 static const int bpp = WINEWAYLAND_BYTES_PER_PIXEL;
299 RGNDATA *rgndata = get_region_data(region);
300 RECT *rgn_rect;
301 RECT *rgn_rect_end;
302 int src_stride, dst_stride;
304 if (!rgndata) return;
306 src_stride = (src_rect->right - src_rect->left) * bpp;
307 dst_stride = (dst_rect->right - dst_rect->left) * bpp;
309 rgn_rect = (RECT *)rgndata->Buffer;
310 rgn_rect_end = rgn_rect + rgndata->rdh.nCount;
312 for (;rgn_rect < rgn_rect_end; rgn_rect++)
314 char *src, *dst;
315 int y, width_bytes, height;
316 RECT rc;
318 TRACE("rect %s\n", wine_dbgstr_rect(rgn_rect));
320 if (!intersect_rect(&rc, rgn_rect, src_rect)) continue;
321 if (!intersect_rect(&rc, &rc, dst_rect)) continue;
323 src = src_pixels + rc.top * src_stride + rc.left * bpp;
324 dst = dst_pixels + rc.top * dst_stride + rc.left * bpp;
325 width_bytes = (rc.right - rc.left) * bpp;
326 height = rc.bottom - rc.top;
328 /* Fast path for full width rectangles. */
329 if (width_bytes == src_stride && width_bytes == dst_stride)
331 memcpy(dst, src, height * width_bytes);
332 continue;
335 for (y = 0; y < height; y++)
337 memcpy(dst, src, width_bytes);
338 src += src_stride;
339 dst += dst_stride;
343 free(rgndata);
346 /**********************************************************************
347 * wayland_window_surface_copy_to_buffer
349 static void wayland_window_surface_copy_to_buffer(struct wayland_window_surface *wws,
350 struct wayland_shm_buffer *buffer,
351 HRGN region)
353 RECT wws_rect = {0, 0, wws->info.bmiHeader.biWidth,
354 abs(wws->info.bmiHeader.biHeight)};
355 RECT buffer_rect = {0, 0, buffer->width, buffer->height};
356 TRACE("wws=%p buffer=%p\n", wws, buffer);
357 copy_pixel_region(wws->bits, &wws_rect, buffer->map_data, &buffer_rect, region);
360 static void wayland_shm_buffer_copy(struct wayland_shm_buffer *src,
361 struct wayland_shm_buffer *dst,
362 HRGN region)
364 RECT src_rect = {0, 0, src->width, src->height};
365 RECT dst_rect = {0, 0, dst->width, dst->height};
366 TRACE("src=%p dst=%p\n", src, dst);
367 copy_pixel_region(src->map_data, &src_rect, dst->map_data, &dst_rect, region);
370 /***********************************************************************
371 * wayland_window_surface_flush
373 static void wayland_window_surface_flush(struct window_surface *window_surface)
375 struct wayland_window_surface *wws = wayland_window_surface_cast(window_surface);
376 struct wayland_shm_buffer *shm_buffer = NULL;
377 BOOL flushed = FALSE;
378 RECT damage_rect;
379 HRGN surface_damage_region = NULL;
380 HRGN copy_from_window_region;
382 wayland_window_surface_lock(window_surface);
384 if (!intersect_rect(&damage_rect, &wws->header.rect, &wws->bounds)) goto done;
386 if (!wws->wayland_surface || !wws->wayland_buffer_queue)
388 ERR("missing wayland surface=%p or buffer_queue=%p, returning\n",
389 wws->wayland_surface, wws->wayland_buffer_queue);
390 goto done;
393 TRACE("surface=%p hwnd=%p surface_rect=%s bounds=%s\n", wws, wws->hwnd,
394 wine_dbgstr_rect(&wws->header.rect), wine_dbgstr_rect(&wws->bounds));
396 surface_damage_region = NtGdiCreateRectRgn(damage_rect.left, damage_rect.top,
397 damage_rect.right, damage_rect.bottom);
398 if (!surface_damage_region)
400 ERR("failed to create surface damage region\n");
401 goto done;
404 wayland_buffer_queue_add_damage(wws->wayland_buffer_queue, surface_damage_region);
406 shm_buffer = wayland_buffer_queue_get_free_buffer(wws->wayland_buffer_queue);
407 if (!shm_buffer)
409 ERR("failed to acquire Wayland SHM buffer, returning\n");
410 goto done;
413 if (wws->wayland_surface->latest_window_buffer)
415 TRACE("latest_window_buffer=%p\n", wws->wayland_surface->latest_window_buffer);
416 /* If we have a latest buffer, use it as the source of all pixel
417 * data that are not contained in the bounds of the flush... */
418 if (wws->wayland_surface->latest_window_buffer != shm_buffer)
420 HRGN copy_from_latest_region = NtGdiCreateRectRgn(0, 0, 0, 0);
421 if (!copy_from_latest_region)
423 ERR("failed to create copy_from_latest region\n");
424 goto done;
426 NtGdiCombineRgn(copy_from_latest_region, shm_buffer->damage_region,
427 surface_damage_region, RGN_DIFF);
428 wayland_shm_buffer_copy(wws->wayland_surface->latest_window_buffer,
429 shm_buffer, copy_from_latest_region);
430 NtGdiDeleteObjectApp(copy_from_latest_region);
432 /* ... and use the window_surface as the source of pixel data contained
433 * in the flush bounds. */
434 copy_from_window_region = surface_damage_region;
436 else
438 TRACE("latest_window_buffer=NULL\n");
439 /* If we don't have a latest buffer, use the window_surface as
440 * the source of all pixel data. */
441 copy_from_window_region = shm_buffer->damage_region;
444 wayland_window_surface_copy_to_buffer(wws, shm_buffer, copy_from_window_region);
446 pthread_mutex_lock(&wws->wayland_surface->mutex);
447 if (wayland_surface_reconfigure(wws->wayland_surface))
449 wayland_surface_attach_shm(wws->wayland_surface, shm_buffer,
450 surface_damage_region);
451 wl_surface_commit(wws->wayland_surface->wl_surface);
452 flushed = TRUE;
454 else
456 TRACE("Wayland surface not configured yet, not flushing\n");
458 pthread_mutex_unlock(&wws->wayland_surface->mutex);
459 wl_display_flush(process_wayland.wl_display);
461 NtGdiSetRectRgn(shm_buffer->damage_region, 0, 0, 0, 0);
462 /* Update the latest window buffer for the wayland surface. Note that we
463 * only care whether the buffer contains the latest window contents,
464 * it's irrelevant if it was actually committed or not. */
465 if (wws->wayland_surface->latest_window_buffer)
466 wayland_shm_buffer_unref(wws->wayland_surface->latest_window_buffer);
467 wayland_shm_buffer_ref((wws->wayland_surface->latest_window_buffer = shm_buffer));
469 done:
470 if (flushed) reset_bounds(&wws->bounds);
471 if (surface_damage_region) NtGdiDeleteObjectApp(surface_damage_region);
472 wayland_window_surface_unlock(window_surface);
475 /***********************************************************************
476 * wayland_window_surface_destroy
478 static void wayland_window_surface_destroy(struct window_surface *window_surface)
480 struct wayland_window_surface *wws = wayland_window_surface_cast(window_surface);
482 TRACE("surface=%p\n", wws);
484 pthread_mutex_destroy(&wws->mutex);
485 if (wws->wayland_buffer_queue)
486 wayland_buffer_queue_destroy(wws->wayland_buffer_queue);
487 free(wws->bits);
488 free(wws);
491 static const struct window_surface_funcs wayland_window_surface_funcs =
493 wayland_window_surface_lock,
494 wayland_window_surface_unlock,
495 wayland_window_surface_get_bitmap_info,
496 wayland_window_surface_get_bounds,
497 wayland_window_surface_set_region,
498 wayland_window_surface_flush,
499 wayland_window_surface_destroy
502 /***********************************************************************
503 * wayland_window_surface_create
505 struct window_surface *wayland_window_surface_create(HWND hwnd, const RECT *rect)
507 struct wayland_window_surface *wws;
508 int width = rect->right - rect->left;
509 int height = rect->bottom - rect->top;
510 pthread_mutexattr_t mutexattr;
512 TRACE("hwnd %p rect %s\n", hwnd, wine_dbgstr_rect(rect));
514 wws = calloc(1, sizeof(*wws));
515 if (!wws) return NULL;
516 wws->info.bmiHeader.biSize = sizeof(wws->info.bmiHeader);
517 wws->info.bmiHeader.biClrUsed = 0;
518 wws->info.bmiHeader.biBitCount = 32;
519 wws->info.bmiHeader.biCompression = BI_RGB;
520 wws->info.bmiHeader.biWidth = width;
521 wws->info.bmiHeader.biHeight = -height; /* top-down */
522 wws->info.bmiHeader.biPlanes = 1;
523 wws->info.bmiHeader.biSizeImage = width * height * 4;
525 pthread_mutexattr_init(&mutexattr);
526 pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_RECURSIVE);
527 pthread_mutex_init(&wws->mutex, &mutexattr);
528 pthread_mutexattr_destroy(&mutexattr);
530 wws->header.funcs = &wayland_window_surface_funcs;
531 wws->header.rect = *rect;
532 wws->header.ref = 1;
533 wws->hwnd = hwnd;
534 reset_bounds(&wws->bounds);
536 if (!(wws->bits = malloc(wws->info.bmiHeader.biSizeImage)))
537 goto failed;
539 TRACE("created %p hwnd %p %s bits [%p,%p)\n", wws, hwnd, wine_dbgstr_rect(rect),
540 wws->bits, (char *)wws->bits + wws->info.bmiHeader.biSizeImage);
542 return &wws->header;
544 failed:
545 wayland_window_surface_destroy(&wws->header);
546 return NULL;
549 /***********************************************************************
550 * wayland_window_surface_update_wayland_surface
552 void wayland_window_surface_update_wayland_surface(struct window_surface *window_surface,
553 struct wayland_surface *wayland_surface)
555 struct wayland_window_surface *wws = wayland_window_surface_cast(window_surface);
557 wayland_window_surface_lock(window_surface);
559 TRACE("surface=%p hwnd=%p wayland_surface=%p\n", wws, wws->hwnd, wayland_surface);
561 wws->wayland_surface = wayland_surface;
563 /* We only need a buffer queue if we have a surface to commit to. */
564 if (wws->wayland_surface && !wws->wayland_buffer_queue)
566 wws->wayland_buffer_queue =
567 wayland_buffer_queue_create(wws->info.bmiHeader.biWidth,
568 abs(wws->info.bmiHeader.biHeight));
570 else if (!wws->wayland_surface && wws->wayland_buffer_queue)
572 wayland_buffer_queue_destroy(wws->wayland_buffer_queue);
573 wws->wayland_buffer_queue = NULL;
576 wayland_window_surface_unlock(window_surface);