FreeRDP
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 
41 static 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 
52 static 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 
59 static const struct wl_buffer_listener buffer_listener = { buffer_release };
60 
61 static 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 
83 static int UwacWindowShmAllocBuffers(UwacWindow* w, uint64_t nbuffers, uint64_t allocSize,
84  uint32_t width, uint32_t height, enum wl_shm_format format);
85 
86 static 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 
176 static 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 
192 static const struct xdg_toplevel_listener xdg_toplevel_listener = {
193  xdg_handle_toplevel_configure,
194  xdg_handle_toplevel_close,
195 };
196 
197 static 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 
203 static const struct xdg_surface_listener xdg_surface_listener = {
204  .configure = xdg_handle_surface_configure,
205 };
206 
207 #if BUILD_IVI
208 
209 static 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 
258 static const struct ivi_surface_listener ivi_surface_listener = {
259  ivi_handle_configure,
260 };
261 #endif
262 
263 static void shell_ping(void* data, struct wl_shell_surface* surface, uint32_t serial)
264 {
265  wl_shell_surface_pong(surface, serial);
266 }
267 
268 static 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 
317 static void shell_popup_done(void* data, struct wl_shell_surface* surface)
318 {
319 }
320 
321 static const struct wl_shell_surface_listener shell_listener = { shell_ping, shell_configure,
322  shell_popup_done };
323 
324 int 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;
410 error_mmap:
411  close(fd);
412  return ret;
413 }
414 
415 static 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 
449 static 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 
483 UwacWindow* 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  char* env = getenv("IVI_SURFACE_ID");
532  if (env)
533  {
534  unsigned long val = 0;
535  char* endp = NULL;
536 
537  errno = 0;
538  val = strtoul(env, &endp, 10);
539 
540  if (!errno && val != 0 && val != UINT32_MAX)
541  ivi_surface_id = val;
542  }
543 
544  if (display->ivi_application)
545  {
546  w->ivi_surface =
547  ivi_application_surface_create(display->ivi_application, ivi_surface_id, w->surface);
548  assert(w->ivi_surface);
549  ivi_surface_add_listener(w->ivi_surface, &ivi_surface_listener, w);
550  }
551  else
552 #endif
553 #if BUILD_FULLSCREEN_SHELL
554  if (display->fullscreen_shell)
555  {
556  zwp_fullscreen_shell_v1_present_surface(display->fullscreen_shell, w->surface,
557  ZWP_FULLSCREEN_SHELL_V1_PRESENT_METHOD_CENTER,
558  NULL);
559  }
560  else
561 #endif
562  if (display->xdg_base)
563  {
564  w->xdg_surface = xdg_wm_base_get_xdg_surface(display->xdg_base, w->surface);
565 
566  if (!w->xdg_surface)
567  {
568  display->last_error = UWAC_ERROR_NOMEMORY;
569  goto out_error_shell;
570  }
571 
572  xdg_surface_add_listener(w->xdg_surface, &xdg_surface_listener, w);
573 
574  w->xdg_toplevel = xdg_surface_get_toplevel(w->xdg_surface);
575  if (!w->xdg_toplevel)
576  {
577  display->last_error = UWAC_ERROR_NOMEMORY;
578  goto out_error_shell;
579  }
580 
581  assert(w->xdg_surface);
582  xdg_toplevel_add_listener(w->xdg_toplevel, &xdg_toplevel_listener, w);
583  wl_surface_commit(w->surface);
584  wl_display_roundtrip(w->display->display);
585  }
586  else
587  {
588  w->shell_surface = wl_shell_get_shell_surface(display->shell, w->surface);
589  assert(w->shell_surface);
590  wl_shell_surface_add_listener(w->shell_surface, &shell_listener, w);
591  wl_shell_surface_set_toplevel(w->shell_surface);
592  }
593 
594  if (display->viewporter)
595  {
596  w->viewport = wp_viewporter_get_viewport(display->viewporter, w->surface);
597  if (display->actual_scale != 1)
598  wl_surface_set_buffer_scale(w->surface, display->actual_scale);
599  }
600 
601  wl_list_insert(display->windows.prev, &w->link);
602  display->last_error = UWAC_SUCCESS;
603  UwacWindowSetDecorations(w);
604  return w;
605 out_error_shell:
606  wl_surface_destroy(w->surface);
607 out_error_surface:
608  UwacWindowDestroyBuffers(w);
609 out_error_free:
610  free(w);
611  return NULL;
612 }
613 
614 UwacReturnCode UwacDestroyWindow(UwacWindow** pwindow)
615 {
616  UwacWindow* w = NULL;
617  assert(pwindow);
618  w = *pwindow;
619  UwacWindowDestroyBuffers(w);
620 
621  if (w->deco)
622  zxdg_toplevel_decoration_v1_destroy(w->deco);
623 
624  if (w->kde_deco)
625  org_kde_kwin_server_decoration_destroy(w->kde_deco);
626 
627  if (w->xdg_surface)
628  xdg_surface_destroy(w->xdg_surface);
629 
630 #if BUILD_IVI
631 
632  if (w->ivi_surface)
633  ivi_surface_destroy(w->ivi_surface);
634 
635 #endif
636 
637  if (w->opaque_region)
638  wl_region_destroy(w->opaque_region);
639 
640  if (w->input_region)
641  wl_region_destroy(w->input_region);
642 
643  if (w->viewport)
644  wp_viewport_destroy(w->viewport);
645 
646  wl_surface_destroy(w->surface);
647  wl_list_remove(&w->link);
648  free(w);
649  *pwindow = NULL;
650  return UWAC_SUCCESS;
651 }
652 
653 UwacReturnCode UwacWindowSetOpaqueRegion(UwacWindow* window, uint32_t x, uint32_t y, uint32_t width,
654  uint32_t height)
655 {
656  assert(window);
657 
658  if (window->opaque_region)
659  wl_region_destroy(window->opaque_region);
660 
661  window->opaque_region = wl_compositor_create_region(window->display->compositor);
662 
663  if (!window->opaque_region)
664  return UWAC_ERROR_NOMEMORY;
665 
666  wl_region_add(window->opaque_region, WINPR_ASSERTING_INT_CAST(int32_t, x),
667  WINPR_ASSERTING_INT_CAST(int32_t, y), WINPR_ASSERTING_INT_CAST(int32_t, width),
668  WINPR_ASSERTING_INT_CAST(int32_t, height));
669  wl_surface_set_opaque_region(window->surface, window->opaque_region);
670  return UWAC_SUCCESS;
671 }
672 
673 UwacReturnCode UwacWindowSetInputRegion(UwacWindow* window, uint32_t x, uint32_t y, uint32_t width,
674  uint32_t height)
675 {
676  assert(window);
677 
678  if (window->input_region)
679  wl_region_destroy(window->input_region);
680 
681  window->input_region = wl_compositor_create_region(window->display->compositor);
682 
683  if (!window->input_region)
684  return UWAC_ERROR_NOMEMORY;
685 
686  wl_region_add(window->input_region, x, y, width, height);
687  wl_surface_set_input_region(window->surface, window->input_region);
688  return UWAC_SUCCESS;
689 }
690 
691 void* UwacWindowGetDrawingBuffer(UwacWindow* window)
692 {
693  UwacBuffer* buffer = NULL;
694 
695  if (window->drawingBufferIdx < 0)
696  return NULL;
697 
698  buffer = &window->buffers[window->drawingBufferIdx];
699  if (!buffer)
700  return NULL;
701 
702  return buffer->data;
703 }
704 
705 static void frame_done_cb(void* data, struct wl_callback* callback, uint32_t time);
706 
707 static const struct wl_callback_listener frame_listener = { frame_done_cb };
708 
709 #ifdef UWAC_HAVE_PIXMAN_REGION
710 static void damage_surface(UwacWindow* window, UwacBuffer* buffer, int scale)
711 {
712  int nrects = 0;
713  const pixman_box32_t* box = pixman_region32_rectangles(&buffer->damage, &nrects);
714 
715  for (int i = 0; i < nrects; i++, box++)
716  {
717  const int x = ((int)floor(box->x1 / scale)) - 1;
718  const int y = ((int)floor(box->y1 / scale)) - 1;
719  const int w = ((int)ceil((box->x2 - box->x1) / scale)) + 2;
720  const int h = ((int)ceil((box->y2 - box->y1) / scale)) + 2;
721  wl_surface_damage(window->surface, x, y, w, h);
722  }
723 
724  pixman_region32_clear(&buffer->damage);
725 }
726 #else
727 static void damage_surface(UwacWindow* window, UwacBuffer* buffer, int scale)
728 {
729  uint32_t nrects = 0;
730  const RECTANGLE_16* boxes = region16_rects(&buffer->damage, &nrects);
731 
732  for (UINT32 i = 0; i < nrects; i++)
733  {
734  const RECTANGLE_16* box = &boxes[i];
735  const double dx = floor(1.0 * box->left / scale);
736  const double dy = floor(1.0 * box->top / scale);
737  const double dw = ceil(1.0 * (box->right - box->left) / scale);
738  const double dh = ceil(1.0 * (box->bottom - box->top) / scale);
739  const int x = ((int)dx) - 1;
740  const int y = ((int)dy) - 1;
741  const int w = ((int)dw) + 2;
742  const int h = ((int)dh) + 2;
743  wl_surface_damage(window->surface, x, y, w, h);
744  }
745 
746  region16_clear(&buffer->damage);
747 }
748 #endif
749 
750 static void UwacSubmitBufferPtr(UwacWindow* window, UwacBuffer* buffer)
751 {
752  wl_surface_attach(window->surface, buffer->wayland_buffer, 0, 0);
753 
754  int scale = window->display->actual_scale;
755  damage_surface(window, buffer, scale);
756 
757  struct wl_callback* frame_callback = wl_surface_frame(window->surface);
758  wl_callback_add_listener(frame_callback, &frame_listener, window);
759  wl_surface_commit(window->surface);
760  buffer->dirty = false;
761 }
762 
763 static void frame_done_cb(void* data, struct wl_callback* callback, uint32_t time)
764 {
765  UwacWindow* window = (UwacWindow*)data;
766  UwacFrameDoneEvent* event = NULL;
767 
768  wl_callback_destroy(callback);
769  window->pendingBufferIdx = -1;
770  event = (UwacFrameDoneEvent*)UwacDisplayNewEvent(window->display, UWAC_EVENT_FRAME_DONE);
771 
772  if (event)
773  event->window = window;
774 }
775 
776 #ifdef UWAC_HAVE_PIXMAN_REGION
777 UwacReturnCode UwacWindowAddDamage(UwacWindow* window, uint32_t x, uint32_t y, uint32_t width,
778  uint32_t height)
779 {
780  UwacBuffer* buf = NULL;
781 
782  if (window->drawingBufferIdx < 0)
783  return UWAC_ERROR_INTERNAL;
784 
785  buf = &window->buffers[window->drawingBufferIdx];
786  if (!pixman_region32_union_rect(&buf->damage, &buf->damage, x, y, width, height))
787  return UWAC_ERROR_INTERNAL;
788 
789  buf->dirty = true;
790  return UWAC_SUCCESS;
791 }
792 #else
793 UwacReturnCode UwacWindowAddDamage(UwacWindow* window, uint32_t x, uint32_t y, uint32_t width,
794  uint32_t height)
795 {
796  RECTANGLE_16 box;
797  UwacBuffer* buf = NULL;
798 
799  box.left = x;
800  box.top = y;
801  box.right = x + width;
802  box.bottom = y + height;
803 
804  if (window->drawingBufferIdx < 0)
805  return UWAC_ERROR_INTERNAL;
806 
807  buf = &window->buffers[window->drawingBufferIdx];
808  if (!buf)
809  return UWAC_ERROR_INTERNAL;
810 
811  if (!region16_union_rect(&buf->damage, &buf->damage, &box))
812  return UWAC_ERROR_INTERNAL;
813 
814  buf->dirty = true;
815  return UWAC_SUCCESS;
816 }
817 #endif
818 
819 UwacReturnCode UwacWindowGetDrawingBufferGeometry(UwacWindow* window, UwacSize* geometry,
820  size_t* stride)
821 {
822  if (!window || (window->drawingBufferIdx < 0))
823  return UWAC_ERROR_INTERNAL;
824 
825  if (geometry)
826  {
827  geometry->width = window->width;
828  geometry->height = window->height;
829  }
830 
831  if (stride)
832  *stride = window->stride;
833 
834  return UWAC_SUCCESS;
835 }
836 
837 UwacReturnCode UwacWindowSubmitBuffer(UwacWindow* window, bool copyContentForNextFrame)
838 {
839  UwacBuffer* currentDrawingBuffer = NULL;
840  UwacBuffer* nextDrawingBuffer = NULL;
841  UwacBuffer* pendingBuffer = NULL;
842 
843  if (window->drawingBufferIdx < 0)
844  return UWAC_ERROR_INTERNAL;
845 
846  currentDrawingBuffer = &window->buffers[window->drawingBufferIdx];
847 
848  if ((window->pendingBufferIdx >= 0) || !currentDrawingBuffer->dirty)
849  return UWAC_SUCCESS;
850 
851  window->pendingBufferIdx = window->drawingBufferIdx;
852  nextDrawingBuffer = UwacWindowFindFreeBuffer(window, &window->drawingBufferIdx);
853  pendingBuffer = &window->buffers[window->pendingBufferIdx];
854 
855  if ((!nextDrawingBuffer) || (window->drawingBufferIdx < 0))
856  return UWAC_ERROR_NOMEMORY;
857 
858  if (copyContentForNextFrame)
859  memcpy(nextDrawingBuffer->data, pendingBuffer->data,
860  1ull * window->stride * window->height);
861 
862  UwacSubmitBufferPtr(window, pendingBuffer);
863  return UWAC_SUCCESS;
864 }
865 
866 UwacReturnCode UwacWindowGetGeometry(UwacWindow* window, UwacSize* geometry)
867 {
868  assert(window);
869  assert(geometry);
870  geometry->width = window->width;
871  geometry->height = window->height;
872  return UWAC_SUCCESS;
873 }
874 
875 UwacReturnCode UwacWindowSetFullscreenState(UwacWindow* window, UwacOutput* output,
876  bool isFullscreen)
877 {
878  if (window->xdg_toplevel)
879  {
880  if (isFullscreen)
881  {
882  xdg_toplevel_set_fullscreen(window->xdg_toplevel, output ? output->output : NULL);
883  }
884  else
885  {
886  xdg_toplevel_unset_fullscreen(window->xdg_toplevel);
887  }
888  }
889  else if (window->shell_surface)
890  {
891  if (isFullscreen)
892  {
893  wl_shell_surface_set_fullscreen(window->shell_surface,
894  WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT, 0,
895  output ? output->output : NULL);
896  }
897  else
898  {
899  wl_shell_surface_set_toplevel(window->shell_surface);
900  }
901  }
902 
903  return UWAC_SUCCESS;
904 }
905 
906 void UwacWindowSetTitle(UwacWindow* window, const char* name)
907 {
908  if (window->xdg_toplevel)
909  xdg_toplevel_set_title(window->xdg_toplevel, name);
910  else if (window->shell_surface)
911  wl_shell_surface_set_title(window->shell_surface, name);
912 }
913 
914 void UwacWindowSetAppId(UwacWindow* window, const char* app_id)
915 {
916  if (window->xdg_toplevel)
917  xdg_toplevel_set_app_id(window->xdg_toplevel, app_id);
918 }