FreeRDP
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Modules Pages
uwac-window.c
1/*
2 * Copyright © 2014 David FORT <contact@hardening-consulting.com>
3 *
4 * Permission to use, copy, modify, distribute, and sell this software and its
5 * documentation for any purpose is hereby granted without fee, provided that
6 * the above copyright notice appear in all copies and that both that copyright
7 * notice and this permission notice appear in supporting documentation, and
8 * that the name of the copyright holders not be used in advertising or
9 * publicity pertaining to distribution of the software without specific,
10 * written prior permission. The copyright holders make no representations
11 * about the suitability of this software for any purpose. It is provided "as
12 * is" without express or implied warranty.
13 *
14 * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
15 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
16 * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
17 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
18 * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
19 * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
20 * OF THIS SOFTWARE.
21 */
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <fcntl.h>
26#include <unistd.h>
27#include <assert.h>
28#include <sys/mman.h>
29#include <errno.h>
30
31#include "uwac-priv.h"
32#include "uwac-utils.h"
33#include "uwac-os.h"
34
35#include <uwac/config.h>
36
37#include <winpr/cast.h>
38
39#define UWAC_INITIAL_BUFFERS 3ull
40
41static int bppFromShmFormat(enum wl_shm_format format)
42{
43 switch (format)
44 {
45 case WL_SHM_FORMAT_ARGB8888:
46 case WL_SHM_FORMAT_XRGB8888:
47 default:
48 return 4;
49 }
50}
51
52static void buffer_release(void* data, struct wl_buffer* buffer)
53{
54 UwacBufferReleaseData* releaseData = data;
55 UwacBuffer* uwacBuffer = &releaseData->window->buffers[releaseData->bufferIdx];
56 uwacBuffer->used = false;
57}
58
59static const struct wl_buffer_listener buffer_listener = { buffer_release };
60
61static void UwacWindowDestroyBuffers(UwacWindow* w)
62{
63 for (size_t i = 0; i < w->nbuffers; i++)
64 {
65 UwacBuffer* buffer = &w->buffers[i];
66#ifdef UWAC_HAVE_PIXMAN_REGION
67 pixman_region32_fini(&buffer->damage);
68#else
69 region16_uninit(&buffer->damage);
70#endif
71 UwacBufferReleaseData* releaseData =
72 (UwacBufferReleaseData*)wl_buffer_get_user_data(buffer->wayland_buffer);
73 wl_buffer_destroy(buffer->wayland_buffer);
74 free(releaseData);
75 munmap(buffer->data, buffer->size);
76 }
77
78 w->nbuffers = 0;
79 free(w->buffers);
80 w->buffers = NULL;
81}
82
83static int UwacWindowShmAllocBuffers(UwacWindow* w, uint64_t nbuffers, uint64_t allocSize,
84 uint32_t width, uint32_t height, enum wl_shm_format format);
85
86static void xdg_handle_toplevel_configure(void* data, struct xdg_toplevel* xdg_toplevel,
87 int32_t width, int32_t height, struct wl_array* states)
88{
89 UwacWindow* window = (UwacWindow*)data;
90 int scale = window->display->actual_scale;
91 int32_t actual_width = width;
92 int32_t actual_height = height;
93 width *= scale;
94 height *= scale;
95 UwacConfigureEvent* event = NULL;
96 int ret = 0;
97 int surfaceState = 0;
98 enum xdg_toplevel_state* state = NULL;
99 surfaceState = 0;
100 wl_array_for_each(state, states)
101 {
102 switch (*state)
103 {
104 case XDG_TOPLEVEL_STATE_MAXIMIZED:
105 surfaceState |= UWAC_WINDOW_MAXIMIZED;
106 break;
107
108 case XDG_TOPLEVEL_STATE_FULLSCREEN:
109 surfaceState |= UWAC_WINDOW_FULLSCREEN;
110 break;
111
112 case XDG_TOPLEVEL_STATE_ACTIVATED:
113 surfaceState |= UWAC_WINDOW_ACTIVATED;
114 break;
115
116 case XDG_TOPLEVEL_STATE_RESIZING:
117 surfaceState |= UWAC_WINDOW_RESIZING;
118 break;
119
120 default:
121 break;
122 }
123 }
124 window->surfaceStates = surfaceState;
125 event = (UwacConfigureEvent*)UwacDisplayNewEvent(window->display, UWAC_EVENT_CONFIGURE);
126
127 if (!event)
128 {
129 assert(uwacErrorHandler(window->display, UWAC_ERROR_NOMEMORY,
130 "failed to allocate a configure event\n"));
131 return;
132 }
133
134 event->window = window;
135 event->states = surfaceState;
136
137 if ((width > 0 && height > 0) && (width != window->width || height != window->height))
138 {
139 event->width = width;
140 event->height = height;
141 UwacWindowDestroyBuffers(window);
142 window->width = width;
143 window->stride = width * bppFromShmFormat(window->format);
144 window->height = height;
145 ret =
146 UwacWindowShmAllocBuffers(window, UWAC_INITIAL_BUFFERS, 1ull * window->stride * height,
147 width, height, window->format);
148
149 if (ret != UWAC_SUCCESS)
150 {
151 assert(
152 uwacErrorHandler(window->display, ret, "failed to reallocate a wayland buffers\n"));
153 window->drawingBufferIdx = window->pendingBufferIdx = -1;
154 return;
155 }
156
157 window->drawingBufferIdx = 0;
158 if (window->pendingBufferIdx != -1)
159 window->pendingBufferIdx = window->drawingBufferIdx;
160
161 if (window->viewport)
162 {
163 wp_viewport_set_source(window->viewport, wl_fixed_from_int(0), wl_fixed_from_int(0),
164 wl_fixed_from_int(actual_width),
165 wl_fixed_from_int(actual_height));
166 wp_viewport_set_destination(window->viewport, actual_width, actual_height);
167 }
168 }
169 else
170 {
171 event->width = window->width;
172 event->height = window->height;
173 }
174}
175
176static void xdg_handle_toplevel_close(void* data, struct xdg_toplevel* xdg_toplevel)
177{
178 UwacCloseEvent* event = NULL;
179 UwacWindow* window = (UwacWindow*)data;
180 event = (UwacCloseEvent*)UwacDisplayNewEvent(window->display, UWAC_EVENT_CLOSE);
181
182 if (!event)
183 {
184 assert(uwacErrorHandler(window->display, UWAC_ERROR_INTERNAL,
185 "failed to allocate a close event\n"));
186 return;
187 }
188
189 event->window = window;
190}
191
192static const struct xdg_toplevel_listener xdg_toplevel_listener = {
193 xdg_handle_toplevel_configure,
194 xdg_handle_toplevel_close,
195};
196
197static void xdg_handle_surface_configure(void* data, struct xdg_surface* xdg_surface,
198 uint32_t serial)
199{
200 xdg_surface_ack_configure(xdg_surface, serial);
201}
202
203static const struct xdg_surface_listener xdg_surface_listener = {
204 .configure = xdg_handle_surface_configure,
205};
206
207#if BUILD_IVI
208
209static void ivi_handle_configure(void* data, struct ivi_surface* surface, int32_t width,
210 int32_t height)
211{
212 UwacWindow* window = (UwacWindow*)data;
213 UwacConfigureEvent* event = NULL;
214 int ret = 0;
215 event = (UwacConfigureEvent*)UwacDisplayNewEvent(window->display, UWAC_EVENT_CONFIGURE);
216
217 if (!event)
218 {
219 assert(uwacErrorHandler(window->display, UWAC_ERROR_NOMEMORY,
220 "failed to allocate a configure event\n"));
221 return;
222 }
223
224 event->window = window;
225 event->states = 0;
226
227 if (width && height)
228 {
229 event->width = width;
230 event->height = height;
231 UwacWindowDestroyBuffers(window);
232 window->width = width;
233 window->stride = width * bppFromShmFormat(window->format);
234 window->height = height;
235 ret =
236 UwacWindowShmAllocBuffers(window, UWAC_INITIAL_BUFFERS, 1ull * window->stride * height,
237 width, height, window->format);
238
239 if (ret != UWAC_SUCCESS)
240 {
241 assert(
242 uwacErrorHandler(window->display, ret, "failed to reallocate a wayland buffers\n"));
243 window->drawingBufferIdx = window->pendingBufferIdx = -1;
244 return;
245 }
246
247 window->drawingBufferIdx = 0;
248 if (window->pendingBufferIdx != -1)
249 window->pendingBufferIdx = window->drawingBufferIdx;
250 }
251 else
252 {
253 event->width = window->width;
254 event->height = window->height;
255 }
256}
257
258static const struct ivi_surface_listener ivi_surface_listener = {
259 ivi_handle_configure,
260};
261#endif
262
263static void shell_ping(void* data, struct wl_shell_surface* surface, uint32_t serial)
264{
265 wl_shell_surface_pong(surface, serial);
266}
267
268static void shell_configure(void* data, struct wl_shell_surface* surface, uint32_t edges,
269 int32_t width, int32_t height)
270{
271 UwacWindow* window = (UwacWindow*)data;
272 UwacConfigureEvent* event = NULL;
273 int ret = 0;
274 event = (UwacConfigureEvent*)UwacDisplayNewEvent(window->display, UWAC_EVENT_CONFIGURE);
275
276 if (!event)
277 {
278 assert(uwacErrorHandler(window->display, UWAC_ERROR_NOMEMORY,
279 "failed to allocate a configure event\n"));
280 return;
281 }
282
283 event->window = window;
284 event->states = 0;
285
286 if (width && height)
287 {
288 event->width = width;
289 event->height = height;
290 UwacWindowDestroyBuffers(window);
291 window->width = width;
292 window->stride = width * bppFromShmFormat(window->format);
293 window->height = height;
294 ret =
295 UwacWindowShmAllocBuffers(window, UWAC_INITIAL_BUFFERS, 1ull * window->stride * height,
296 width, height, window->format);
297
298 if (ret != UWAC_SUCCESS)
299 {
300 assert(
301 uwacErrorHandler(window->display, ret, "failed to reallocate a wayland buffers\n"));
302 window->drawingBufferIdx = window->pendingBufferIdx = -1;
303 return;
304 }
305
306 window->drawingBufferIdx = 0;
307 if (window->pendingBufferIdx != -1)
308 window->pendingBufferIdx = window->drawingBufferIdx;
309 }
310 else
311 {
312 event->width = window->width;
313 event->height = window->height;
314 }
315}
316
317static void shell_popup_done(void* data, struct wl_shell_surface* surface)
318{
319}
320
321static const struct wl_shell_surface_listener shell_listener = { shell_ping, shell_configure,
322 shell_popup_done };
323
324int UwacWindowShmAllocBuffers(UwacWindow* w, uint64_t nbuffers, uint64_t allocSize, uint32_t width,
325 uint32_t height, enum wl_shm_format format)
326{
327 int ret = UWAC_SUCCESS;
328 int fd = 0;
329 void* data = NULL;
330 struct wl_shm_pool* pool = NULL;
331
332 if ((width > INT32_MAX) || (height > INT32_MAX))
333 return UWAC_ERROR_NOMEMORY;
334
335 const int64_t pagesize = sysconf(_SC_PAGESIZE);
336 if (pagesize <= 0)
337 return UWAC_ERROR_NOMEMORY;
338
339 /* round up to a multiple of PAGESIZE to page align data for each buffer */
340 const uint64_t test = (1ull * allocSize + (size_t)pagesize - 1ull) & ~((size_t)pagesize - 1);
341 if (test > INT64_MAX)
342 return UWAC_ERROR_NOMEMORY;
343
344 allocSize = test;
345
346 UwacBuffer* newBuffers =
347 xrealloc(w->buffers, (0ull + w->nbuffers + nbuffers) * sizeof(UwacBuffer));
348
349 if (!newBuffers)
350 return UWAC_ERROR_NOMEMORY;
351
352 w->buffers = newBuffers;
353 memset(w->buffers + w->nbuffers, 0, sizeof(UwacBuffer) * nbuffers);
354
355 const size_t allocbuffersize = 1ull * allocSize * nbuffers;
356 if (allocbuffersize > INT32_MAX)
357 return UWAC_ERROR_NOMEMORY;
358
359 fd = uwac_create_anonymous_file(WINPR_ASSERTING_INT_CAST(off_t, allocbuffersize));
360
361 if (fd < 0)
362 {
363 return UWAC_ERROR_INTERNAL;
364 }
365
366 data = mmap(NULL, allocbuffersize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
367
368 if (data == MAP_FAILED)
369 {
370 ret = UWAC_ERROR_NOMEMORY;
371 goto error_mmap;
372 }
373
374 pool = wl_shm_create_pool(w->display->shm, fd, (int32_t)allocbuffersize);
375
376 if (!pool)
377 {
378 munmap(data, allocbuffersize);
379 ret = UWAC_ERROR_NOMEMORY;
380 goto error_mmap;
381 }
382
383 for (uint64_t i = 0; i < nbuffers; i++)
384 {
385 const size_t idx = (size_t)i;
386 const size_t bufferIdx = w->nbuffers + idx;
387 UwacBuffer* buffer = &w->buffers[bufferIdx];
388
389#ifdef UWAC_HAVE_PIXMAN_REGION
390 pixman_region32_init(&buffer->damage);
391#else
392 region16_init(&buffer->damage);
393#endif
394 const size_t offset = allocSize * idx;
395 if (offset > INT32_MAX)
396 goto error_mmap;
397
398 buffer->data = &((char*)data)[allocSize * idx];
399 buffer->size = allocSize;
400 buffer->wayland_buffer = wl_shm_pool_create_buffer(pool, (int32_t)offset, (int32_t)width,
401 (int32_t)height, w->stride, format);
402 UwacBufferReleaseData* listener_data = xmalloc(sizeof(UwacBufferReleaseData));
403 listener_data->window = w;
404 listener_data->bufferIdx = bufferIdx;
405 wl_buffer_add_listener(buffer->wayland_buffer, &buffer_listener, listener_data);
406 }
407
408 wl_shm_pool_destroy(pool);
409 w->nbuffers += nbuffers;
410error_mmap:
411 close(fd);
412 return ret;
413}
414
415static UwacBuffer* UwacWindowFindFreeBuffer(UwacWindow* w, ssize_t* index)
416{
417 int ret = 0;
418
419 if (index)
420 *index = -1;
421
422 size_t i = 0;
423 for (; i < w->nbuffers; i++)
424 {
425 if (!w->buffers[i].used)
426 {
427 w->buffers[i].used = true;
428 if (index)
429 *index = WINPR_ASSERTING_INT_CAST(ssize_t, i);
430 return &w->buffers[i];
431 }
432 }
433
434 ret = UwacWindowShmAllocBuffers(w, 2, 1ull * w->stride * w->height, w->width, w->height,
435 w->format);
436
437 if (ret != UWAC_SUCCESS)
438 {
439 w->display->last_error = ret;
440 return NULL;
441 }
442
443 w->buffers[i].used = true;
444 if (index)
445 *index = WINPR_ASSERTING_INT_CAST(ssize_t, i);
446 return &w->buffers[i];
447}
448
449static UwacReturnCode UwacWindowSetDecorations(UwacWindow* w)
450{
451 if (!w || !w->display)
452 return UWAC_ERROR_INTERNAL;
453
454 if (w->display->deco_manager)
455 {
456 w->deco = zxdg_decoration_manager_v1_get_toplevel_decoration(w->display->deco_manager,
457 w->xdg_toplevel);
458 if (!w->deco)
459 {
460 uwacErrorHandler(w->display, UWAC_NOT_FOUND,
461 "Current window manager does not allow decorating with SSD");
462 }
463 else
464 zxdg_toplevel_decoration_v1_set_mode(w->deco,
465 ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE);
466 }
467 else if (w->display->kde_deco_manager)
468 {
469 w->kde_deco =
470 org_kde_kwin_server_decoration_manager_create(w->display->kde_deco_manager, w->surface);
471 if (!w->kde_deco)
472 {
473 uwacErrorHandler(w->display, UWAC_NOT_FOUND,
474 "Current window manager does not allow decorating with SSD");
475 }
476 else
477 org_kde_kwin_server_decoration_request_mode(w->kde_deco,
478 ORG_KDE_KWIN_SERVER_DECORATION_MODE_SERVER);
479 }
480 return UWAC_SUCCESS;
481}
482
483UwacWindow* UwacCreateWindowShm(UwacDisplay* display, uint32_t width, uint32_t height,
484 enum wl_shm_format format)
485{
486 UwacWindow* w = NULL;
487 int ret = 0;
488
489 if (!display)
490 {
491 return NULL;
492 }
493
494 w = xzalloc(sizeof(*w));
495
496 if (!w)
497 {
498 display->last_error = UWAC_ERROR_NOMEMORY;
499 return NULL;
500 }
501
502 w->display = display;
503 w->format = format;
504 w->width = WINPR_ASSERTING_INT_CAST(int32_t, width);
505 w->height = WINPR_ASSERTING_INT_CAST(int32_t, height);
506 w->stride = WINPR_ASSERTING_INT_CAST(int32_t, width* bppFromShmFormat(format));
507 const size_t allocSize = 1ULL * w->stride * height;
508 ret = UwacWindowShmAllocBuffers(w, UWAC_INITIAL_BUFFERS, allocSize, width, height, format);
509
510 if (ret != UWAC_SUCCESS)
511 {
512 display->last_error = ret;
513 goto out_error_free;
514 }
515
516 w->buffers[0].used = true;
517 w->drawingBufferIdx = 0;
518 w->pendingBufferIdx = -1;
519 w->surface = wl_compositor_create_surface(display->compositor);
520
521 if (!w->surface)
522 {
523 display->last_error = UWAC_ERROR_NOMEMORY;
524 goto out_error_surface;
525 }
526
527 wl_surface_set_user_data(w->surface, w);
528
529#if BUILD_IVI
530 uint32_t ivi_surface_id = 1;
531 // NOLINTNEXTLINE(concurrency-mt-unsafe)
532 char* env = getenv("IVI_SURFACE_ID");
533 if (env)
534 {
535 unsigned long val = 0;
536 char* endp = NULL;
537
538 errno = 0;
539 val = strtoul(env, &endp, 10);
540
541 if (!errno && val != 0 && val != UINT32_MAX)
542 ivi_surface_id = val;
543 }
544
545 if (display->ivi_application)
546 {
547 w->ivi_surface =
548 ivi_application_surface_create(display->ivi_application, ivi_surface_id, w->surface);
549 assert(w->ivi_surface);
550 ivi_surface_add_listener(w->ivi_surface, &ivi_surface_listener, w);
551 }
552 else
553#endif
554#if BUILD_FULLSCREEN_SHELL
555 if (display->fullscreen_shell)
556 {
557 zwp_fullscreen_shell_v1_present_surface(display->fullscreen_shell, w->surface,
558 ZWP_FULLSCREEN_SHELL_V1_PRESENT_METHOD_CENTER,
559 NULL);
560 }
561 else
562#endif
563 if (display->xdg_base)
564 {
565 w->xdg_surface = xdg_wm_base_get_xdg_surface(display->xdg_base, w->surface);
566
567 if (!w->xdg_surface)
568 {
569 display->last_error = UWAC_ERROR_NOMEMORY;
570 goto out_error_shell;
571 }
572
573 xdg_surface_add_listener(w->xdg_surface, &xdg_surface_listener, w);
574
575 w->xdg_toplevel = xdg_surface_get_toplevel(w->xdg_surface);
576 if (!w->xdg_toplevel)
577 {
578 display->last_error = UWAC_ERROR_NOMEMORY;
579 goto out_error_shell;
580 }
581
582 assert(w->xdg_surface);
583 xdg_toplevel_add_listener(w->xdg_toplevel, &xdg_toplevel_listener, w);
584 wl_surface_commit(w->surface);
585 wl_display_roundtrip(w->display->display);
586 }
587 else
588 {
589 w->shell_surface = wl_shell_get_shell_surface(display->shell, w->surface);
590 assert(w->shell_surface);
591 wl_shell_surface_add_listener(w->shell_surface, &shell_listener, w);
592 wl_shell_surface_set_toplevel(w->shell_surface);
593 }
594
595 if (display->viewporter)
596 {
597 w->viewport = wp_viewporter_get_viewport(display->viewporter, w->surface);
598 if (display->actual_scale != 1)
599 wl_surface_set_buffer_scale(w->surface, display->actual_scale);
600 }
601
602 wl_list_insert(display->windows.prev, &w->link);
603 display->last_error = UWAC_SUCCESS;
604 UwacWindowSetDecorations(w);
605 return w;
606out_error_shell:
607 wl_surface_destroy(w->surface);
608out_error_surface:
609 UwacWindowDestroyBuffers(w);
610out_error_free:
611 free(w);
612 return NULL;
613}
614
615UwacReturnCode UwacDestroyWindow(UwacWindow** pwindow)
616{
617 UwacWindow* w = NULL;
618 assert(pwindow);
619 w = *pwindow;
620 UwacWindowDestroyBuffers(w);
621
622 if (w->deco)
623 zxdg_toplevel_decoration_v1_destroy(w->deco);
624
625 if (w->kde_deco)
626 org_kde_kwin_server_decoration_destroy(w->kde_deco);
627
628 if (w->xdg_surface)
629 xdg_surface_destroy(w->xdg_surface);
630
631#if BUILD_IVI
632
633 if (w->ivi_surface)
634 ivi_surface_destroy(w->ivi_surface);
635
636#endif
637
638 if (w->opaque_region)
639 wl_region_destroy(w->opaque_region);
640
641 if (w->input_region)
642 wl_region_destroy(w->input_region);
643
644 if (w->viewport)
645 wp_viewport_destroy(w->viewport);
646
647 wl_surface_destroy(w->surface);
648 wl_list_remove(&w->link);
649 free(w);
650 *pwindow = NULL;
651 return UWAC_SUCCESS;
652}
653
654UwacReturnCode UwacWindowSetOpaqueRegion(UwacWindow* window, uint32_t x, uint32_t y, uint32_t width,
655 uint32_t height)
656{
657 assert(window);
658
659 if (window->opaque_region)
660 wl_region_destroy(window->opaque_region);
661
662 window->opaque_region = wl_compositor_create_region(window->display->compositor);
663
664 if (!window->opaque_region)
665 return UWAC_ERROR_NOMEMORY;
666
667 wl_region_add(window->opaque_region, WINPR_ASSERTING_INT_CAST(int32_t, x),
668 WINPR_ASSERTING_INT_CAST(int32_t, y), WINPR_ASSERTING_INT_CAST(int32_t, width),
669 WINPR_ASSERTING_INT_CAST(int32_t, height));
670 wl_surface_set_opaque_region(window->surface, window->opaque_region);
671 return UWAC_SUCCESS;
672}
673
674UwacReturnCode UwacWindowSetInputRegion(UwacWindow* window, uint32_t x, uint32_t y, uint32_t width,
675 uint32_t height)
676{
677 assert(window);
678
679 if (window->input_region)
680 wl_region_destroy(window->input_region);
681
682 window->input_region = wl_compositor_create_region(window->display->compositor);
683
684 if (!window->input_region)
685 return UWAC_ERROR_NOMEMORY;
686
687 wl_region_add(window->input_region, WINPR_ASSERTING_INT_CAST(int32_t, x),
688 WINPR_ASSERTING_INT_CAST(int32_t, y), WINPR_ASSERTING_INT_CAST(int32_t, width),
689 WINPR_ASSERTING_INT_CAST(int32_t, height));
690 wl_surface_set_input_region(window->surface, window->input_region);
691 return UWAC_SUCCESS;
692}
693
694void* UwacWindowGetDrawingBuffer(UwacWindow* window)
695{
696 UwacBuffer* buffer = NULL;
697
698 if (window->drawingBufferIdx < 0)
699 return NULL;
700
701 buffer = &window->buffers[window->drawingBufferIdx];
702 if (!buffer)
703 return NULL;
704
705 return buffer->data;
706}
707
708static void frame_done_cb(void* data, struct wl_callback* callback, uint32_t time);
709
710static const struct wl_callback_listener frame_listener = { frame_done_cb };
711
712#ifdef UWAC_HAVE_PIXMAN_REGION
713static void damage_surface(UwacWindow* window, UwacBuffer* buffer, int scale)
714{
715 int nrects = 0;
716 const pixman_box32_t* box = pixman_region32_rectangles(&buffer->damage, &nrects);
717
718 for (int i = 0; i < nrects; i++, box++)
719 {
720 const int x = ((int)floor(box->x1 / scale)) - 1;
721 const int y = ((int)floor(box->y1 / scale)) - 1;
722 const int w = ((int)ceil((box->x2 - box->x1) / scale)) + 2;
723 const int h = ((int)ceil((box->y2 - box->y1) / scale)) + 2;
724 wl_surface_damage(window->surface, x, y, w, h);
725 }
726
727 pixman_region32_clear(&buffer->damage);
728}
729#else
730static void damage_surface(UwacWindow* window, UwacBuffer* buffer, int scale)
731{
732 uint32_t nrects = 0;
733 const RECTANGLE_16* boxes = region16_rects(&buffer->damage, &nrects);
734
735 for (UINT32 i = 0; i < nrects; i++)
736 {
737 const RECTANGLE_16* box = &boxes[i];
738 const double dx = floor(1.0 * box->left / scale);
739 const double dy = floor(1.0 * box->top / scale);
740 const double dw = ceil(1.0 * (box->right - box->left) / scale);
741 const double dh = ceil(1.0 * (box->bottom - box->top) / scale);
742 const int x = ((int)dx) - 1;
743 const int y = ((int)dy) - 1;
744 const int w = ((int)dw) + 2;
745 const int h = ((int)dh) + 2;
746 wl_surface_damage(window->surface, x, y, w, h);
747 }
748
749 region16_clear(&buffer->damage);
750}
751#endif
752
753static void UwacSubmitBufferPtr(UwacWindow* window, UwacBuffer* buffer)
754{
755 wl_surface_attach(window->surface, buffer->wayland_buffer, 0, 0);
756
757 int scale = window->display->actual_scale;
758 damage_surface(window, buffer, scale);
759
760 struct wl_callback* frame_callback = wl_surface_frame(window->surface);
761 wl_callback_add_listener(frame_callback, &frame_listener, window);
762 wl_surface_commit(window->surface);
763 buffer->dirty = false;
764}
765
766static void frame_done_cb(void* data, struct wl_callback* callback, uint32_t time)
767{
768 UwacWindow* window = (UwacWindow*)data;
769 UwacFrameDoneEvent* event = NULL;
770
771 wl_callback_destroy(callback);
772 window->pendingBufferIdx = -1;
773 event = (UwacFrameDoneEvent*)UwacDisplayNewEvent(window->display, UWAC_EVENT_FRAME_DONE);
774
775 if (event)
776 event->window = window;
777}
778
779#ifdef UWAC_HAVE_PIXMAN_REGION
780UwacReturnCode UwacWindowAddDamage(UwacWindow* window, uint32_t x, uint32_t y, uint32_t width,
781 uint32_t height)
782{
783 UwacBuffer* buf = NULL;
784
785 if (window->drawingBufferIdx < 0)
786 return UWAC_ERROR_INTERNAL;
787
788 buf = &window->buffers[window->drawingBufferIdx];
789 if (!pixman_region32_union_rect(&buf->damage, &buf->damage, x, y, width, height))
790 return UWAC_ERROR_INTERNAL;
791
792 buf->dirty = true;
793 return UWAC_SUCCESS;
794}
795#else
796UwacReturnCode UwacWindowAddDamage(UwacWindow* window, uint32_t x, uint32_t y, uint32_t width,
797 uint32_t height)
798{
799 RECTANGLE_16 box;
800 UwacBuffer* buf = NULL;
801
802 box.left = x;
803 box.top = y;
804 box.right = x + width;
805 box.bottom = y + height;
806
807 if (window->drawingBufferIdx < 0)
808 return UWAC_ERROR_INTERNAL;
809
810 buf = &window->buffers[window->drawingBufferIdx];
811 if (!buf)
812 return UWAC_ERROR_INTERNAL;
813
814 if (!region16_union_rect(&buf->damage, &buf->damage, &box))
815 return UWAC_ERROR_INTERNAL;
816
817 buf->dirty = true;
818 return UWAC_SUCCESS;
819}
820#endif
821
822UwacReturnCode UwacWindowGetDrawingBufferGeometry(UwacWindow* window, UwacSize* geometry,
823 size_t* stride)
824{
825 if (!window || (window->drawingBufferIdx < 0))
826 return UWAC_ERROR_INTERNAL;
827
828 if (geometry)
829 {
830 geometry->width = window->width;
831 geometry->height = window->height;
832 }
833
834 if (stride)
835 *stride = window->stride;
836
837 return UWAC_SUCCESS;
838}
839
840UwacReturnCode UwacWindowSubmitBuffer(UwacWindow* window, bool copyContentForNextFrame)
841{
842 UwacBuffer* currentDrawingBuffer = NULL;
843 UwacBuffer* nextDrawingBuffer = NULL;
844 UwacBuffer* pendingBuffer = NULL;
845
846 if (window->drawingBufferIdx < 0)
847 return UWAC_ERROR_INTERNAL;
848
849 currentDrawingBuffer = &window->buffers[window->drawingBufferIdx];
850
851 if ((window->pendingBufferIdx >= 0) || !currentDrawingBuffer->dirty)
852 return UWAC_SUCCESS;
853
854 window->pendingBufferIdx = window->drawingBufferIdx;
855 nextDrawingBuffer = UwacWindowFindFreeBuffer(window, &window->drawingBufferIdx);
856 pendingBuffer = &window->buffers[window->pendingBufferIdx];
857
858 if ((!nextDrawingBuffer) || (window->drawingBufferIdx < 0))
859 return UWAC_ERROR_NOMEMORY;
860
861 if (copyContentForNextFrame)
862 memcpy(nextDrawingBuffer->data, pendingBuffer->data,
863 1ull * window->stride * window->height);
864
865 UwacSubmitBufferPtr(window, pendingBuffer);
866 return UWAC_SUCCESS;
867}
868
869UwacReturnCode UwacWindowGetGeometry(UwacWindow* window, UwacSize* geometry)
870{
871 assert(window);
872 assert(geometry);
873 geometry->width = window->width;
874 geometry->height = window->height;
875 return UWAC_SUCCESS;
876}
877
878UwacReturnCode UwacWindowSetFullscreenState(UwacWindow* window, UwacOutput* output,
879 bool isFullscreen)
880{
881 if (window->xdg_toplevel)
882 {
883 if (isFullscreen)
884 {
885 xdg_toplevel_set_fullscreen(window->xdg_toplevel, output ? output->output : NULL);
886 }
887 else
888 {
889 xdg_toplevel_unset_fullscreen(window->xdg_toplevel);
890 }
891 }
892 else if (window->shell_surface)
893 {
894 if (isFullscreen)
895 {
896 wl_shell_surface_set_fullscreen(window->shell_surface,
897 WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, 0,
898 output ? output->output : NULL);
899 }
900 else
901 {
902 wl_shell_surface_set_toplevel(window->shell_surface);
903 }
904 }
905
906 return UWAC_SUCCESS;
907}
908
909void UwacWindowSetTitle(UwacWindow* window, const char* name)
910{
911 if (window->xdg_toplevel)
912 xdg_toplevel_set_title(window->xdg_toplevel, name);
913 else if (window->shell_surface)
914 wl_shell_surface_set_title(window->shell_surface, name);
915}
916
917void UwacWindowSetAppId(UwacWindow* window, const char* app_id)
918{
919 if (window->xdg_toplevel)
920 xdg_toplevel_set_app_id(window->xdg_toplevel, app_id);
921}