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