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
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
;
43 struct wayland_window_surface
45 struct window_surface header
;
47 struct wayland_surface
*wayland_surface
;
48 struct wayland_buffer_queue
*wayland_buffer_queue
;
51 pthread_mutex_t mutex
;
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
);
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
);
133 if (queue
) wayland_buffer_queue_destroy(queue
);
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
);
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
;
164 /* Dynamically create up to 3 buffers. */
167 shm_buffer
= wayland_shm_buffer_create(queue
->width
, queue
->height
,
168 WL_SHM_FORMAT_XRGB8888
);
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
,
177 wl_list_insert(&queue
->buffer_list
, &shm_buffer
->link
);
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");
191 /* We don't have any buffers available, so block waiting for a buffer
193 if (wl_display_dispatch_queue(process_wayland
.wl_display
,
194 queue
->wl_event_queue
) == -1)
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
);
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
,
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
,
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
);
262 /***********************************************************************
263 * wayland_window_surface_set_region
265 static void wayland_window_surface_set_region(struct window_surface
*window_surface
,
271 /**********************************************************************
274 static RGNDATA
*get_region_data(HRGN region
)
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
))
291 /**********************************************************************
294 static void copy_pixel_region(char *src_pixels
, RECT
*src_rect
,
295 char *dst_pixels
, RECT
*dst_rect
,
298 static const int bpp
= WINEWAYLAND_BYTES_PER_PIXEL
;
299 RGNDATA
*rgndata
= get_region_data(region
);
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
++)
315 int y
, width_bytes
, height
;
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
);
335 for (y
= 0; y
< height
; y
++)
337 memcpy(dst
, src
, width_bytes
);
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
,
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 /***********************************************************************
361 * wayland_window_surface_flush
363 static void wayland_window_surface_flush(struct window_surface
*window_surface
)
365 struct wayland_window_surface
*wws
= wayland_window_surface_cast(window_surface
);
366 struct wayland_shm_buffer
*shm_buffer
= NULL
;
367 BOOL flushed
= FALSE
;
369 HRGN surface_damage_region
;
371 wayland_window_surface_lock(window_surface
);
373 if (!intersect_rect(&damage_rect
, &wws
->header
.rect
, &wws
->bounds
)) goto done
;
375 if (!wws
->wayland_surface
|| !wws
->wayland_buffer_queue
)
377 ERR("missing wayland surface=%p or buffer_queue=%p, returning\n",
378 wws
->wayland_surface
, wws
->wayland_buffer_queue
);
382 TRACE("surface=%p hwnd=%p surface_rect=%s bounds=%s\n", wws
, wws
->hwnd
,
383 wine_dbgstr_rect(&wws
->header
.rect
), wine_dbgstr_rect(&wws
->bounds
));
385 surface_damage_region
= NtGdiCreateRectRgn(damage_rect
.left
, damage_rect
.top
,
386 damage_rect
.right
, damage_rect
.bottom
);
387 if (!surface_damage_region
)
389 ERR("failed to create surface damage region\n");
393 wayland_buffer_queue_add_damage(wws
->wayland_buffer_queue
, surface_damage_region
);
394 NtGdiDeleteObjectApp(surface_damage_region
);
396 shm_buffer
= wayland_buffer_queue_get_free_buffer(wws
->wayland_buffer_queue
);
399 ERR("failed to acquire Wayland SHM buffer, returning\n");
403 wayland_window_surface_copy_to_buffer(wws
, shm_buffer
, shm_buffer
->damage_region
);
405 pthread_mutex_lock(&wws
->wayland_surface
->mutex
);
406 if (wws
->wayland_surface
->current_serial
)
408 wayland_surface_attach_shm(wws
->wayland_surface
, shm_buffer
);
409 wl_surface_commit(wws
->wayland_surface
->wl_surface
);
414 TRACE("Wayland surface not configured yet, not flushing\n");
416 pthread_mutex_unlock(&wws
->wayland_surface
->mutex
);
417 wl_display_flush(process_wayland
.wl_display
);
419 NtGdiSetRectRgn(shm_buffer
->damage_region
, 0, 0, 0, 0);
422 if (flushed
) reset_bounds(&wws
->bounds
);
423 wayland_window_surface_unlock(window_surface
);
426 /***********************************************************************
427 * wayland_window_surface_destroy
429 static void wayland_window_surface_destroy(struct window_surface
*window_surface
)
431 struct wayland_window_surface
*wws
= wayland_window_surface_cast(window_surface
);
433 TRACE("surface=%p\n", wws
);
435 pthread_mutex_destroy(&wws
->mutex
);
436 if (wws
->wayland_buffer_queue
)
437 wayland_buffer_queue_destroy(wws
->wayland_buffer_queue
);
442 static const struct window_surface_funcs wayland_window_surface_funcs
=
444 wayland_window_surface_lock
,
445 wayland_window_surface_unlock
,
446 wayland_window_surface_get_bitmap_info
,
447 wayland_window_surface_get_bounds
,
448 wayland_window_surface_set_region
,
449 wayland_window_surface_flush
,
450 wayland_window_surface_destroy
453 /***********************************************************************
454 * wayland_window_surface_create
456 struct window_surface
*wayland_window_surface_create(HWND hwnd
, const RECT
*rect
)
458 struct wayland_window_surface
*wws
;
459 int width
= rect
->right
- rect
->left
;
460 int height
= rect
->bottom
- rect
->top
;
461 pthread_mutexattr_t mutexattr
;
463 TRACE("hwnd %p rect %s\n", hwnd
, wine_dbgstr_rect(rect
));
465 wws
= calloc(1, sizeof(*wws
));
466 if (!wws
) return NULL
;
467 wws
->info
.bmiHeader
.biSize
= sizeof(wws
->info
.bmiHeader
);
468 wws
->info
.bmiHeader
.biClrUsed
= 0;
469 wws
->info
.bmiHeader
.biBitCount
= 32;
470 wws
->info
.bmiHeader
.biCompression
= BI_RGB
;
471 wws
->info
.bmiHeader
.biWidth
= width
;
472 wws
->info
.bmiHeader
.biHeight
= -height
; /* top-down */
473 wws
->info
.bmiHeader
.biPlanes
= 1;
474 wws
->info
.bmiHeader
.biSizeImage
= width
* height
* 4;
476 pthread_mutexattr_init(&mutexattr
);
477 pthread_mutexattr_settype(&mutexattr
, PTHREAD_MUTEX_RECURSIVE
);
478 pthread_mutex_init(&wws
->mutex
, &mutexattr
);
479 pthread_mutexattr_destroy(&mutexattr
);
481 wws
->header
.funcs
= &wayland_window_surface_funcs
;
482 wws
->header
.rect
= *rect
;
485 reset_bounds(&wws
->bounds
);
487 if (!(wws
->bits
= malloc(wws
->info
.bmiHeader
.biSizeImage
)))
490 TRACE("created %p hwnd %p %s bits [%p,%p)\n", wws
, hwnd
, wine_dbgstr_rect(rect
),
491 wws
->bits
, (char *)wws
->bits
+ wws
->info
.bmiHeader
.biSizeImage
);
496 wayland_window_surface_destroy(&wws
->header
);
500 /***********************************************************************
501 * wayland_window_surface_update_wayland_surface
503 void wayland_window_surface_update_wayland_surface(struct window_surface
*window_surface
,
504 struct wayland_surface
*wayland_surface
)
506 struct wayland_window_surface
*wws
= wayland_window_surface_cast(window_surface
);
508 wayland_window_surface_lock(window_surface
);
510 TRACE("surface=%p hwnd=%p wayland_surface=%p\n", wws
, wws
->hwnd
, wayland_surface
);
512 wws
->wayland_surface
= wayland_surface
;
514 /* We only need a buffer queue if we have a surface to commit to. */
515 if (wws
->wayland_surface
&& !wws
->wayland_buffer_queue
)
517 wws
->wayland_buffer_queue
=
518 wayland_buffer_queue_create(wws
->info
.bmiHeader
.biWidth
,
519 abs(wws
->info
.bmiHeader
.biHeight
));
521 else if (!wws
->wayland_surface
&& wws
->wayland_buffer_queue
)
523 wayland_buffer_queue_destroy(wws
->wayland_buffer_queue
);
524 wws
->wayland_buffer_queue
= NULL
;
527 wayland_window_surface_unlock(window_surface
);