source: mainline/uspace/srv/hid/display/display.c@ 5271e4c

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 5271e4c was 5271e4c, checked in by Jiri Svoboda <jiri@…>, 5 years ago

Duplicate rendering to additional output devices using a cloning GC

This gives the display server a pretty good illusion of rendering to just
one output device, while supporting multiple. This makes RFB work properly.

  • Property mode set to 100644
File size: 16.7 KB
RevLine 
[c8cf261]1/*
2 * Copyright (c) 2019 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup display
30 * @{
31 */
32/**
[38e5f36c]33 * @file Display server display
[c8cf261]34 */
35
36#include <errno.h>
[8aef01c]37#include <gfx/bitmap.h>
[159776f]38#include <gfx/context.h>
[c79545e]39#include <gfx/render.h>
[c8cf261]40#include <io/log.h>
[8aef01c]41#include <memgfx/memgc.h>
[6af4b4f]42#include <stdlib.h>
[b3c185b6]43#include "client.h"
[5271e4c]44#include "clonegc.h"
[4d8002d]45#include "cursimg.h"
46#include "cursor.h"
[cf32dbd]47#include "seat.h"
[6af4b4f]48#include "window.h"
[b3c185b6]49#include "display.h"
[c8cf261]50
[8aef01c]51static gfx_context_t *ds_display_get_unbuf_gc(ds_display_t *);
52static void ds_display_update_cb(void *, gfx_rect_t *);
53
[6af4b4f]54/** Create display.
55 *
[159776f]56 * @param gc Graphics context for displaying output
[8aef01c]57 * @param flags Display flags
[6af4b4f]58 * @param rdisp Place to store pointer to new display.
59 * @return EOK on success, ENOMEM if out of memory
60 */
[8aef01c]61errno_t ds_display_create(gfx_context_t *gc, ds_display_flags_t flags,
62 ds_display_t **rdisp)
[6af4b4f]63{
64 ds_display_t *disp;
[4d8002d]65 ds_cursor_t *cursor;
66 int i;
[c79545e]67 errno_t rc;
[6af4b4f]68
69 disp = calloc(1, sizeof(ds_display_t));
70 if (disp == NULL)
71 return ENOMEM;
72
[c79545e]73 rc = gfx_color_new_rgb_i16(0x8000, 0xc800, 0xffff, &disp->bg_color);
74 if (rc != EOK) {
75 free(disp);
76 return ENOMEM;
77 }
78
[4d8002d]79 list_initialize(&disp->cursors);
80
81 for (i = 0; i < dcurs_limit; i++) {
82 rc = ds_cursor_create(disp, &ds_cursimg[i].rect,
83 ds_cursimg[i].image, &cursor);
84 if (rc != EOK)
85 goto error;
86
87 disp->cursor[i] = cursor;
88 }
89
[c11ee605]90 fibril_mutex_initialize(&disp->lock);
[b3c185b6]91 list_initialize(&disp->clients);
92 disp->next_wnd_id = 1;
[87a7cdb]93 list_initialize(&disp->ddevs);
[cf32dbd]94 list_initialize(&disp->seats);
[fd777a2]95 list_initialize(&disp->windows);
[8aef01c]96 disp->flags = flags;
[6af4b4f]97 *rdisp = disp;
98 return EOK;
[4d8002d]99error:
100 ds_display_destroy(disp);
101 return rc;
[6af4b4f]102}
103
104/** Destroy display.
105 *
106 * @param disp Display
107 */
108void ds_display_destroy(ds_display_t *disp)
109{
[b3c185b6]110 assert(list_empty(&disp->clients));
[cf32dbd]111 assert(list_empty(&disp->seats));
[4d8002d]112 /* XXX destroy cursors */
[c79545e]113 gfx_color_delete(disp->bg_color);
[6af4b4f]114 free(disp);
115}
116
[c11ee605]117/** Lock display.
118 *
119 * This should be called in any thread that wishes to access the display
120 * or its child objects (e.g. windows).
121 *
122 * @param disp Display
123 */
124void ds_display_lock(ds_display_t *disp)
125{
126 fibril_mutex_lock(&disp->lock);
127}
128
129/** Unlock display.
130 *
131 * @param disp Display
132 */
133void ds_display_unlock(ds_display_t *disp)
134{
135 fibril_mutex_unlock(&disp->lock);
136}
137
[aeb3037]138/** Get display information.
139 *
140 * @param disp Display
141 */
142void ds_display_get_info(ds_display_t *disp, display_info_t *info)
143{
144 info->rect = disp->rect;
145}
146
[b3c185b6]147/** Add client to display.
[6af4b4f]148 *
149 * @param disp Display
[cf32dbd]150 * @param client Client
[6af4b4f]151 */
[b3c185b6]152void ds_display_add_client(ds_display_t *disp, ds_client_t *client)
[6af4b4f]153{
[b3c185b6]154 assert(client->display == NULL);
155 assert(!link_used(&client->lclients));
[6af4b4f]156
[b3c185b6]157 client->display = disp;
158 list_append(&client->lclients, &disp->clients);
[c8cf261]159}
160
[b3c185b6]161/** Remove client from display.
[6af4b4f]162 *
[cf32dbd]163 * @param client Client
[6af4b4f]164 */
[b3c185b6]165void ds_display_remove_client(ds_client_t *client)
[6af4b4f]166{
[b3c185b6]167 list_remove(&client->lclients);
168 client->display = NULL;
[6af4b4f]169}
170
[b3c185b6]171/** Get first client in display.
[6af4b4f]172 *
173 * @param disp Display
[b3c185b6]174 * @return First client or @c NULL if there is none
[6af4b4f]175 */
[b3c185b6]176ds_client_t *ds_display_first_client(ds_display_t *disp)
[6af4b4f]177{
[b3c185b6]178 link_t *link = list_first(&disp->clients);
[6af4b4f]179
[b3c185b6]180 if (link == NULL)
181 return NULL;
[6af4b4f]182
[b3c185b6]183 return list_get_instance(link, ds_client_t, lclients);
[6af4b4f]184}
185
[b3c185b6]186/** Get next client in display.
[6af4b4f]187 *
[b3c185b6]188 * @param client Current client
189 * @return Next client or @c NULL if there is none
[6af4b4f]190 */
[b3c185b6]191ds_client_t *ds_display_next_client(ds_client_t *client)
[6af4b4f]192{
[b3c185b6]193 link_t *link = list_next(&client->lclients, &client->display->clients);
[6af4b4f]194
195 if (link == NULL)
196 return NULL;
197
[b3c185b6]198 return list_get_instance(link, ds_client_t, lclients);
[6af4b4f]199}
200
[b3c185b6]201/** Find window in all clients by ID.
[6af4b4f]202 *
[b3c185b6]203 * XXX This is just a hack needed to match GC connection to a window,
204 * as we don't have a good safe way to pass the GC endpoint to our client
205 * on demand.
206 *
207 * @param display Display
208 * @param id Window ID
[6af4b4f]209 */
[b3c185b6]210#include <stdio.h>
211ds_window_t *ds_display_find_window(ds_display_t *display, ds_wnd_id_t id)
[6af4b4f]212{
[b3c185b6]213 ds_client_t *client;
214 ds_window_t *wnd;
[6af4b4f]215
[bf22cb78]216 printf("ds_display_find_window: id=0x%x\n", (unsigned) id);
[b3c185b6]217
218 client = ds_display_first_client(display);
219 while (client != NULL) {
220 printf("ds_display_find_window: client=%p\n", client);
221 wnd = ds_client_find_window(client, id);
222 if (wnd != NULL) {
[bf22cb78]223 printf("ds_display_find_window: found wnd=%p id=0x%x\n",
224 wnd, (unsigned) wnd->id);
[b3c185b6]225 return wnd;
226 }
227 client = ds_display_next_client(client);
228 }
229
230 printf("ds_display_find_window: not found\n");
231 return NULL;
232}
233
[24cf391a]234/** Find window by display position.
235 *
236 * @param display Display
237 * @param pos Display position
238 */
239ds_window_t *ds_display_window_by_pos(ds_display_t *display, gfx_coord2_t *pos)
240{
241 ds_window_t *wnd;
[fb420e48]242 gfx_rect_t drect;
[24cf391a]243
244 wnd = ds_display_first_window(display);
245 while (wnd != NULL) {
[fb420e48]246 /* Window bounding rectangle on display */
247 gfx_rect_translate(&wnd->dpos, &wnd->rect, &drect);
248
249 if (gfx_pix_inside_rect(pos, &drect))
[24cf391a]250 return wnd;
251
252 wnd = ds_display_next_window(wnd);
253 }
254
255 return NULL;
256}
257
[fd777a2]258/** Add window to display.
259 *
260 * @param display Display
261 * @param wnd Window
262 */
263void ds_display_add_window(ds_display_t *display, ds_window_t *wnd)
264{
265 assert(wnd->display == NULL);
266 assert(!link_used(&wnd->ldwindows));
267
268 wnd->display = display;
269 list_prepend(&wnd->ldwindows, &display->windows);
270}
271
272/** Remove window from display.
273 *
274 * @param wnd Window
275 */
276void ds_display_remove_window(ds_window_t *wnd)
277{
278 list_remove(&wnd->ldwindows);
279 wnd->display = NULL;
280}
281
282/** Get first window in display.
283 *
284 * @param display Display
285 * @return First window or @c NULL if there is none
286 */
287ds_window_t *ds_display_first_window(ds_display_t *display)
288{
289 link_t *link = list_first(&display->windows);
290
291 if (link == NULL)
292 return NULL;
293
294 return list_get_instance(link, ds_window_t, ldwindows);
295}
296
[2012fe0]297/** Get last window in display.
298 *
299 * @param display Display
300 * @return Last window or @c NULL if there is none
301 */
302ds_window_t *ds_display_last_window(ds_display_t *display)
303{
304 link_t *link = list_last(&display->windows);
305
306 if (link == NULL)
307 return NULL;
308
309 return list_get_instance(link, ds_window_t, ldwindows);
310}
311
[fd777a2]312/** Get next window in client.
313 *
314 * @param wnd Current window
315 * @return Next window or @c NULL if there is none
316 */
317ds_window_t *ds_display_next_window(ds_window_t *wnd)
318{
319 link_t *link = list_next(&wnd->ldwindows, &wnd->display->windows);
320
321 if (link == NULL)
322 return NULL;
323
324 return list_get_instance(link, ds_window_t, ldwindows);
325}
326
[2012fe0]327/** Get previous window in client.
328 *
329 * @param wnd Current window
330 * @return Previous window or @c NULL if there is none
331 */
332ds_window_t *ds_display_prev_window(ds_window_t *wnd)
333{
334 link_t *link = list_prev(&wnd->ldwindows, &wnd->display->windows);
335
336 if (link == NULL)
337 return NULL;
338
339 return list_get_instance(link, ds_window_t, ldwindows);
340}
341
[cf32dbd]342/** Post keyboard event to a display.
343 *
344 * The event is routed to the correct window by first determining the
345 * seat the keyboard device belongs to and then the event is sent to the
346 * window focused by that seat.
347 *
348 * @param display Display
349 * @param event Event
350 */
[b3c185b6]351errno_t ds_display_post_kbd_event(ds_display_t *display, kbd_event_t *event)
352{
[cf32dbd]353 ds_seat_t *seat;
[b3c185b6]354
[cf32dbd]355 // TODO Determine which seat the event belongs to
356 seat = ds_display_first_seat(display);
357 if (seat == NULL)
[b3c185b6]358 return EOK;
359
[cf32dbd]360 return ds_seat_post_kbd_event(seat, event);
361}
362
[24cf391a]363/** Post position event to a display.
364 *
365 * @param display Display
366 * @param event Event
367 */
[4fbdc3d]368errno_t ds_display_post_ptd_event(ds_display_t *display, ptd_event_t *event)
[24cf391a]369{
370 ds_seat_t *seat;
371
[4fbdc3d]372 // TODO Determine which seat the event belongs to
373 seat = ds_display_first_seat(display);
374 if (seat == NULL)
375 return EOK;
[24cf391a]376
[4fbdc3d]377 return ds_seat_post_ptd_event(seat, event);
[24cf391a]378}
379
[cf32dbd]380/** Add seat to display.
381 *
382 * @param disp Display
383 * @param seat Seat
384 */
385void ds_display_add_seat(ds_display_t *disp, ds_seat_t *seat)
386{
387 assert(seat->display == NULL);
388 assert(!link_used(&seat->lseats));
389
390 seat->display = disp;
391 list_append(&seat->lseats, &disp->seats);
392}
393
394/** Remove seat from display.
395 *
396 * @param seat Seat
397 */
398void ds_display_remove_seat(ds_seat_t *seat)
399{
400 list_remove(&seat->lseats);
401 seat->display = NULL;
402}
403
404/** Get first seat in display.
405 *
406 * @param disp Display
407 * @return First seat or @c NULL if there is none
408 */
409ds_seat_t *ds_display_first_seat(ds_display_t *disp)
410{
411 link_t *link = list_first(&disp->seats);
412
413 if (link == NULL)
414 return NULL;
415
416 return list_get_instance(link, ds_seat_t, lseats);
417}
418
419/** Get next seat in display.
420 *
421 * @param seat Current seat
422 * @return Next seat or @c NULL if there is none
423 */
424ds_seat_t *ds_display_next_seat(ds_seat_t *seat)
425{
426 link_t *link = list_next(&seat->lseats, &seat->display->seats);
427
428 if (link == NULL)
429 return NULL;
[6af4b4f]430
[cf32dbd]431 return list_get_instance(link, ds_seat_t, lseats);
[6af4b4f]432}
433
[8aef01c]434/** Allocate back buffer for display.
435 *
436 * @param disp Display
437 * @return EOK on success or if no back buffer is required, otherwise
438 * an error code.
439 */
440static errno_t ds_display_alloc_backbuf(ds_display_t *disp)
441{
442 gfx_context_t *ugc;
443 gfx_bitmap_params_t params;
444 gfx_bitmap_alloc_t alloc;
445 errno_t rc;
446
447 /* Allocate backbuffer */
448 if ((disp->flags & df_disp_double_buf) == 0) {
449 /* Not double buffering. Nothing to do. */
450 return EOK;
451 }
452
453 ugc = ds_display_get_unbuf_gc(disp);
454
455 gfx_bitmap_params_init(&params);
456 params.rect = disp->rect;
457
458 rc = gfx_bitmap_create(ugc, &params, NULL,
459 &disp->backbuf);
460 if (rc != EOK)
461 goto error;
462
463 rc = gfx_bitmap_get_alloc(disp->backbuf, &alloc);
464 if (rc != EOK)
465 goto error;
466
467 rc = mem_gc_create(&disp->rect, &alloc,
468 ds_display_update_cb, (void *) disp, &disp->bbgc);
469 if (rc != EOK)
470 goto error;
471
472 disp->dirty_rect.p0.x = 0;
473 disp->dirty_rect.p0.y = 0;
474 disp->dirty_rect.p1.x = 0;
475 disp->dirty_rect.p1.y = 0;
476
477 return EOK;
478error:
479 if (disp->backbuf != NULL) {
480 gfx_bitmap_destroy(disp->backbuf);
481 disp->backbuf = NULL;
482 }
483
484 return rc;
485}
486
[87a7cdb]487/** Add display device to display.
488 *
489 * @param disp Display
490 * @param ddev Display device
[8aef01c]491 * @return EOK on success, or an error code
[87a7cdb]492 */
[8aef01c]493errno_t ds_display_add_ddev(ds_display_t *disp, ds_ddev_t *ddev)
[87a7cdb]494{
[8aef01c]495 errno_t rc;
496
[87a7cdb]497 assert(ddev->display == NULL);
498 assert(!link_used(&ddev->lddevs));
499
500 ddev->display = disp;
501 list_append(&ddev->lddevs, &disp->ddevs);
[8aef01c]502
503 /* First display device */
504 if (gfx_rect_is_empty(&disp->rect)) {
505 /* Set screen dimensions */
506 disp->rect = ddev->info.rect;
507
[5271e4c]508 /* Create cloning GC */
509 rc = ds_clonegc_create(ddev->gc, &disp->fbgc);
510 if (rc != EOK) {
511 // XXX Remove output
512 return ENOMEM;
513 }
514
[8aef01c]515 /* Allocate backbuffer */
516 rc = ds_display_alloc_backbuf(disp);
[5271e4c]517 if (rc != EOK) {
518 // XXX Remove output
519 // XXX Delete clone GC
520 goto error;
521 }
522 } else {
523 /* Add new output device to cloning GC */
524 rc = ds_clonegc_add_output(disp->fbgc, ddev->gc);
[8aef01c]525 if (rc != EOK)
526 goto error;
527 }
528
529 return EOK;
530error:
531 disp->rect.p0.x = 0;
532 disp->rect.p0.y = 0;
533 disp->rect.p1.x = 0;
534 disp->rect.p1.y = 0;
535 list_remove(&ddev->lddevs);
536 return rc;
[87a7cdb]537}
538
539/** Remove display device from display.
540 *
541 * @param ddev Display device
542 */
543void ds_display_remove_ddev(ds_ddev_t *ddev)
544{
545 list_remove(&ddev->lddevs);
546 ddev->display = NULL;
547}
548
549/** Get first display device in display.
550 *
551 * @param disp Display
552 * @return First display device or @c NULL if there is none
553 */
554ds_ddev_t *ds_display_first_ddev(ds_display_t *disp)
555{
556 link_t *link = list_first(&disp->ddevs);
557
558 if (link == NULL)
559 return NULL;
560
561 return list_get_instance(link, ds_ddev_t, lddevs);
562}
563
564/** Get next display device in display.
565 *
566 * @param ddev Current display device
567 * @return Next display device or @c NULL if there is none
568 */
569ds_ddev_t *ds_display_next_ddev(ds_ddev_t *ddev)
570{
571 link_t *link = list_next(&ddev->lddevs, &ddev->display->ddevs);
572
573 if (link == NULL)
574 return NULL;
575
576 return list_get_instance(link, ds_ddev_t, lddevs);
577}
578
[4d8002d]579/** Add cursor to display.
580 *
581 * @param display Display
582 * @param cursor Cursor
583 */
584void ds_display_add_cursor(ds_display_t *display, ds_cursor_t *cursor)
585{
586 assert(cursor->display == NULL);
587 assert(!link_used(&cursor->ldisplay));
588
589 cursor->display = display;
590 list_prepend(&cursor->ldisplay, &display->cursors);
591}
592
593/** Remove cursor from display.
594 *
595 * @param cursor Cursor
596 */
597void ds_display_remove_cursor(ds_cursor_t *cursor)
598{
599 list_remove(&cursor->ldisplay);
600 cursor->display = NULL;
601}
602
[84e74ea]603/** Get unbuffered GC.
604 *
605 * Get the display's (unbuffered) graphic context. If the display
606 * is double-buffered, this returns GC of the front buffer. If the display
607 * is unbuffered, this is the same as @c ds_display_get_gc().
608 *
609 * @param display Display
610 * @return Unbuffered GC
611 */
[8aef01c]612static gfx_context_t *ds_display_get_unbuf_gc(ds_display_t *display)
[87a7cdb]613{
[5271e4c]614 /* In case of unit tests */
615 if (display->fbgc == NULL)
[4fbdc3d]616 return NULL;
[87a7cdb]617
[5271e4c]618 return ds_clonegc_get_ctx(display->fbgc);
[87a7cdb]619}
620
[84e74ea]621/** Get display GC.
622 *
623 * Get the graphic context used to paint the display. This is to be used
624 * for all display server paint operations.
625 *
626 * @param display Display
627 * @return Graphic context for painting to the display
628 */
[8aef01c]629gfx_context_t *ds_display_get_gc(ds_display_t *display)
630{
631 if ((display->flags & df_disp_double_buf) != 0)
632 return mem_gc_get_ctx(display->bbgc);
633 else
634 return ds_display_get_unbuf_gc(display);
635}
636
[c79545e]637/** Paint display background.
638 *
639 * @param display Display
640 * @param rect Bounding rectangle or @c NULL to repaint entire display
641 */
642errno_t ds_display_paint_bg(ds_display_t *disp, gfx_rect_t *rect)
643{
644 gfx_rect_t crect;
645 gfx_context_t *gc;
646 errno_t rc;
647
648 if (rect != NULL)
[e1f2079]649 gfx_rect_clip(&disp->rect, rect, &crect);
[c79545e]650 else
[e1f2079]651 crect = disp->rect;
[c79545e]652
[84e74ea]653 gc = ds_display_get_gc(disp);
[f5191b4]654 if (gc == NULL)
655 return EOK;
[c79545e]656
657 rc = gfx_set_color(gc, disp->bg_color);
658 if (rc != EOK)
659 return rc;
660
661 return gfx_fill_rect(gc, &crect);
662}
663
[8aef01c]664/** Update front buffer from back buffer.
665 *
666 * If the display is not double-buffered, no action is taken.
667 *
668 * @param disp Display
669 * @return EOK on success, or an error code
670 */
671static errno_t ds_display_update(ds_display_t *disp)
672{
673 errno_t rc;
674
675 if (disp->backbuf == NULL) {
676 /* Not double-buffered, nothing to do. */
677 return EOK;
678 }
679
680 rc = gfx_bitmap_render(disp->backbuf, &disp->dirty_rect, NULL);
681 if (rc != EOK)
682 return rc;
683
684 disp->dirty_rect.p0.x = 0;
685 disp->dirty_rect.p0.y = 0;
686 disp->dirty_rect.p1.x = 0;
687 disp->dirty_rect.p1.y = 0;
688
689 return EOK;
690}
691
[2012fe0]692/** Paint display.
693 *
694 * @param display Display
695 * @param rect Bounding rectangle or @c NULL to repaint entire display
696 */
697errno_t ds_display_paint(ds_display_t *disp, gfx_rect_t *rect)
698{
699 errno_t rc;
700 ds_window_t *wnd;
[978c9bc5]701 ds_seat_t *seat;
[2012fe0]702
703 /* Paint background */
704 rc = ds_display_paint_bg(disp, rect);
705 if (rc != EOK)
706 return rc;
707
708 /* Paint windows bottom to top */
709 wnd = ds_display_last_window(disp);
710 while (wnd != NULL) {
711 rc = ds_window_paint(wnd, rect);
712 if (rc != EOK)
713 return rc;
714
715 wnd = ds_display_prev_window(wnd);
716 }
717
[6301a24f]718 /* Paint window previews for windows being resized or moved */
719 wnd = ds_display_last_window(disp);
720 while (wnd != NULL) {
721 rc = ds_window_paint_preview(wnd, rect);
722 if (rc != EOK)
723 return rc;
724
725 wnd = ds_display_prev_window(wnd);
726 }
727
728 /* Paint pointers */
[978c9bc5]729 seat = ds_display_first_seat(disp);
730 while (seat != NULL) {
731 rc = ds_seat_paint_pointer(seat, rect);
732 if (rc != EOK)
733 return rc;
734
735 seat = ds_display_next_seat(seat);
736 }
737
[8aef01c]738 return ds_display_update(disp);
739}
740
741/** Display update callback.
742 *
743 * Called by backbuffer memory GC when something is rendered into it.
744 * Updates the display's dirty rectangle.
745 *
746 * @param arg Argument (display cast as void *)
747 * @param rect Rectangle to update
748 */
749static void ds_display_update_cb(void *arg, gfx_rect_t *rect)
750{
751 ds_display_t *disp = (ds_display_t *) arg;
752 gfx_rect_t env;
753
754 gfx_rect_envelope(&disp->dirty_rect, rect, &env);
755 disp->dirty_rect = env;
[2012fe0]756}
757
[c8cf261]758/** @}
759 */
Note: See TracBrowser for help on using the repository browser.