source: mainline/uspace/srv/hid/display/display.c@ ef734b7

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

Clean up debug messages and logging

  • Property mode set to 100644
File size: 16.4 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]210ds_window_t *ds_display_find_window(ds_display_t *display, ds_wnd_id_t id)
[6af4b4f]211{
[b3c185b6]212 ds_client_t *client;
213 ds_window_t *wnd;
[6af4b4f]214
[b3c185b6]215 client = ds_display_first_client(display);
216 while (client != NULL) {
217 wnd = ds_client_find_window(client, id);
[195b7b3]218 if (wnd != NULL)
[b3c185b6]219 return wnd;
[195b7b3]220
[b3c185b6]221 client = ds_display_next_client(client);
222 }
223
224 return NULL;
225}
226
[24cf391a]227/** Find window by display position.
228 *
229 * @param display Display
230 * @param pos Display position
231 */
232ds_window_t *ds_display_window_by_pos(ds_display_t *display, gfx_coord2_t *pos)
233{
234 ds_window_t *wnd;
[fb420e48]235 gfx_rect_t drect;
[24cf391a]236
237 wnd = ds_display_first_window(display);
238 while (wnd != NULL) {
[fb420e48]239 /* Window bounding rectangle on display */
240 gfx_rect_translate(&wnd->dpos, &wnd->rect, &drect);
241
242 if (gfx_pix_inside_rect(pos, &drect))
[24cf391a]243 return wnd;
244
245 wnd = ds_display_next_window(wnd);
246 }
247
248 return NULL;
249}
250
[fd777a2]251/** Add window to display.
252 *
253 * @param display Display
254 * @param wnd Window
255 */
256void ds_display_add_window(ds_display_t *display, ds_window_t *wnd)
257{
258 assert(wnd->display == NULL);
259 assert(!link_used(&wnd->ldwindows));
260
261 wnd->display = display;
262 list_prepend(&wnd->ldwindows, &display->windows);
263}
264
265/** Remove window from display.
266 *
267 * @param wnd Window
268 */
269void ds_display_remove_window(ds_window_t *wnd)
270{
271 list_remove(&wnd->ldwindows);
272 wnd->display = NULL;
273}
274
275/** Get first window in display.
276 *
277 * @param display Display
278 * @return First window or @c NULL if there is none
279 */
280ds_window_t *ds_display_first_window(ds_display_t *display)
281{
282 link_t *link = list_first(&display->windows);
283
284 if (link == NULL)
285 return NULL;
286
287 return list_get_instance(link, ds_window_t, ldwindows);
288}
289
[2012fe0]290/** Get last window in display.
291 *
292 * @param display Display
293 * @return Last window or @c NULL if there is none
294 */
295ds_window_t *ds_display_last_window(ds_display_t *display)
296{
297 link_t *link = list_last(&display->windows);
298
299 if (link == NULL)
300 return NULL;
301
302 return list_get_instance(link, ds_window_t, ldwindows);
303}
304
[fd777a2]305/** Get next window in client.
306 *
307 * @param wnd Current window
308 * @return Next window or @c NULL if there is none
309 */
310ds_window_t *ds_display_next_window(ds_window_t *wnd)
311{
312 link_t *link = list_next(&wnd->ldwindows, &wnd->display->windows);
313
314 if (link == NULL)
315 return NULL;
316
317 return list_get_instance(link, ds_window_t, ldwindows);
318}
319
[2012fe0]320/** Get previous window in client.
321 *
322 * @param wnd Current window
323 * @return Previous window or @c NULL if there is none
324 */
325ds_window_t *ds_display_prev_window(ds_window_t *wnd)
326{
327 link_t *link = list_prev(&wnd->ldwindows, &wnd->display->windows);
328
329 if (link == NULL)
330 return NULL;
331
332 return list_get_instance(link, ds_window_t, ldwindows);
333}
334
[cf32dbd]335/** Post keyboard event to a display.
336 *
337 * The event is routed to the correct window by first determining the
338 * seat the keyboard device belongs to and then the event is sent to the
339 * window focused by that seat.
340 *
341 * @param display Display
342 * @param event Event
343 */
[b3c185b6]344errno_t ds_display_post_kbd_event(ds_display_t *display, kbd_event_t *event)
345{
[cf32dbd]346 ds_seat_t *seat;
[b3c185b6]347
[cf32dbd]348 // TODO Determine which seat the event belongs to
349 seat = ds_display_first_seat(display);
350 if (seat == NULL)
[b3c185b6]351 return EOK;
352
[cf32dbd]353 return ds_seat_post_kbd_event(seat, event);
354}
355
[24cf391a]356/** Post position event to a display.
357 *
358 * @param display Display
359 * @param event Event
360 */
[4fbdc3d]361errno_t ds_display_post_ptd_event(ds_display_t *display, ptd_event_t *event)
[24cf391a]362{
363 ds_seat_t *seat;
364
[4fbdc3d]365 // TODO Determine which seat the event belongs to
366 seat = ds_display_first_seat(display);
367 if (seat == NULL)
368 return EOK;
[24cf391a]369
[4fbdc3d]370 return ds_seat_post_ptd_event(seat, event);
[24cf391a]371}
372
[cf32dbd]373/** Add seat to display.
374 *
375 * @param disp Display
376 * @param seat Seat
377 */
378void ds_display_add_seat(ds_display_t *disp, ds_seat_t *seat)
379{
380 assert(seat->display == NULL);
381 assert(!link_used(&seat->lseats));
382
383 seat->display = disp;
384 list_append(&seat->lseats, &disp->seats);
385}
386
387/** Remove seat from display.
388 *
389 * @param seat Seat
390 */
391void ds_display_remove_seat(ds_seat_t *seat)
392{
393 list_remove(&seat->lseats);
394 seat->display = NULL;
395}
396
397/** Get first seat in display.
398 *
399 * @param disp Display
400 * @return First seat or @c NULL if there is none
401 */
402ds_seat_t *ds_display_first_seat(ds_display_t *disp)
403{
404 link_t *link = list_first(&disp->seats);
405
406 if (link == NULL)
407 return NULL;
408
409 return list_get_instance(link, ds_seat_t, lseats);
410}
411
412/** Get next seat in display.
413 *
414 * @param seat Current seat
415 * @return Next seat or @c NULL if there is none
416 */
417ds_seat_t *ds_display_next_seat(ds_seat_t *seat)
418{
419 link_t *link = list_next(&seat->lseats, &seat->display->seats);
420
421 if (link == NULL)
422 return NULL;
[6af4b4f]423
[cf32dbd]424 return list_get_instance(link, ds_seat_t, lseats);
[6af4b4f]425}
426
[8aef01c]427/** Allocate back buffer for display.
428 *
429 * @param disp Display
430 * @return EOK on success or if no back buffer is required, otherwise
431 * an error code.
432 */
433static errno_t ds_display_alloc_backbuf(ds_display_t *disp)
434{
435 gfx_context_t *ugc;
436 gfx_bitmap_params_t params;
437 gfx_bitmap_alloc_t alloc;
438 errno_t rc;
439
440 /* Allocate backbuffer */
441 if ((disp->flags & df_disp_double_buf) == 0) {
442 /* Not double buffering. Nothing to do. */
443 return EOK;
444 }
445
446 ugc = ds_display_get_unbuf_gc(disp);
447
448 gfx_bitmap_params_init(&params);
449 params.rect = disp->rect;
450
451 rc = gfx_bitmap_create(ugc, &params, NULL,
452 &disp->backbuf);
453 if (rc != EOK)
454 goto error;
455
456 rc = gfx_bitmap_get_alloc(disp->backbuf, &alloc);
457 if (rc != EOK)
458 goto error;
459
460 rc = mem_gc_create(&disp->rect, &alloc,
461 ds_display_update_cb, (void *) disp, &disp->bbgc);
462 if (rc != EOK)
463 goto error;
464
465 disp->dirty_rect.p0.x = 0;
466 disp->dirty_rect.p0.y = 0;
467 disp->dirty_rect.p1.x = 0;
468 disp->dirty_rect.p1.y = 0;
469
470 return EOK;
471error:
472 if (disp->backbuf != NULL) {
473 gfx_bitmap_destroy(disp->backbuf);
474 disp->backbuf = NULL;
475 }
476
477 return rc;
478}
479
[87a7cdb]480/** Add display device to display.
481 *
482 * @param disp Display
483 * @param ddev Display device
[8aef01c]484 * @return EOK on success, or an error code
[87a7cdb]485 */
[8aef01c]486errno_t ds_display_add_ddev(ds_display_t *disp, ds_ddev_t *ddev)
[87a7cdb]487{
[8aef01c]488 errno_t rc;
489
[87a7cdb]490 assert(ddev->display == NULL);
491 assert(!link_used(&ddev->lddevs));
492
493 ddev->display = disp;
494 list_append(&ddev->lddevs, &disp->ddevs);
[8aef01c]495
496 /* First display device */
497 if (gfx_rect_is_empty(&disp->rect)) {
498 /* Set screen dimensions */
499 disp->rect = ddev->info.rect;
500
[5271e4c]501 /* Create cloning GC */
502 rc = ds_clonegc_create(ddev->gc, &disp->fbgc);
503 if (rc != EOK) {
504 // XXX Remove output
505 return ENOMEM;
506 }
507
[8aef01c]508 /* Allocate backbuffer */
509 rc = ds_display_alloc_backbuf(disp);
[5271e4c]510 if (rc != EOK) {
511 // XXX Remove output
512 // XXX Delete clone GC
513 goto error;
514 }
515 } else {
516 /* Add new output device to cloning GC */
517 rc = ds_clonegc_add_output(disp->fbgc, ddev->gc);
[8aef01c]518 if (rc != EOK)
519 goto error;
520 }
521
522 return EOK;
523error:
524 disp->rect.p0.x = 0;
525 disp->rect.p0.y = 0;
526 disp->rect.p1.x = 0;
527 disp->rect.p1.y = 0;
528 list_remove(&ddev->lddevs);
529 return rc;
[87a7cdb]530}
531
532/** Remove display device from display.
533 *
534 * @param ddev Display device
535 */
536void ds_display_remove_ddev(ds_ddev_t *ddev)
537{
538 list_remove(&ddev->lddevs);
539 ddev->display = NULL;
540}
541
542/** Get first display device in display.
543 *
544 * @param disp Display
545 * @return First display device or @c NULL if there is none
546 */
547ds_ddev_t *ds_display_first_ddev(ds_display_t *disp)
548{
549 link_t *link = list_first(&disp->ddevs);
550
551 if (link == NULL)
552 return NULL;
553
554 return list_get_instance(link, ds_ddev_t, lddevs);
555}
556
557/** Get next display device in display.
558 *
559 * @param ddev Current display device
560 * @return Next display device or @c NULL if there is none
561 */
562ds_ddev_t *ds_display_next_ddev(ds_ddev_t *ddev)
563{
564 link_t *link = list_next(&ddev->lddevs, &ddev->display->ddevs);
565
566 if (link == NULL)
567 return NULL;
568
569 return list_get_instance(link, ds_ddev_t, lddevs);
570}
571
[4d8002d]572/** Add cursor to display.
573 *
574 * @param display Display
575 * @param cursor Cursor
576 */
577void ds_display_add_cursor(ds_display_t *display, ds_cursor_t *cursor)
578{
579 assert(cursor->display == NULL);
580 assert(!link_used(&cursor->ldisplay));
581
582 cursor->display = display;
583 list_prepend(&cursor->ldisplay, &display->cursors);
584}
585
586/** Remove cursor from display.
587 *
588 * @param cursor Cursor
589 */
590void ds_display_remove_cursor(ds_cursor_t *cursor)
591{
592 list_remove(&cursor->ldisplay);
593 cursor->display = NULL;
594}
595
[84e74ea]596/** Get unbuffered GC.
597 *
598 * Get the display's (unbuffered) graphic context. If the display
599 * is double-buffered, this returns GC of the front buffer. If the display
600 * is unbuffered, this is the same as @c ds_display_get_gc().
601 *
602 * @param display Display
603 * @return Unbuffered GC
604 */
[8aef01c]605static gfx_context_t *ds_display_get_unbuf_gc(ds_display_t *display)
[87a7cdb]606{
[5271e4c]607 /* In case of unit tests */
608 if (display->fbgc == NULL)
[4fbdc3d]609 return NULL;
[87a7cdb]610
[5271e4c]611 return ds_clonegc_get_ctx(display->fbgc);
[87a7cdb]612}
613
[84e74ea]614/** Get display GC.
615 *
616 * Get the graphic context used to paint the display. This is to be used
617 * for all display server paint operations.
618 *
619 * @param display Display
620 * @return Graphic context for painting to the display
621 */
[8aef01c]622gfx_context_t *ds_display_get_gc(ds_display_t *display)
623{
624 if ((display->flags & df_disp_double_buf) != 0)
625 return mem_gc_get_ctx(display->bbgc);
626 else
627 return ds_display_get_unbuf_gc(display);
628}
629
[c79545e]630/** Paint display background.
631 *
632 * @param display Display
633 * @param rect Bounding rectangle or @c NULL to repaint entire display
634 */
635errno_t ds_display_paint_bg(ds_display_t *disp, gfx_rect_t *rect)
636{
637 gfx_rect_t crect;
638 gfx_context_t *gc;
639 errno_t rc;
640
641 if (rect != NULL)
[e1f2079]642 gfx_rect_clip(&disp->rect, rect, &crect);
[c79545e]643 else
[e1f2079]644 crect = disp->rect;
[c79545e]645
[84e74ea]646 gc = ds_display_get_gc(disp);
[f5191b4]647 if (gc == NULL)
648 return EOK;
[c79545e]649
650 rc = gfx_set_color(gc, disp->bg_color);
651 if (rc != EOK)
652 return rc;
653
654 return gfx_fill_rect(gc, &crect);
655}
656
[8aef01c]657/** Update front buffer from back buffer.
658 *
659 * If the display is not double-buffered, no action is taken.
660 *
661 * @param disp Display
662 * @return EOK on success, or an error code
663 */
664static errno_t ds_display_update(ds_display_t *disp)
665{
666 errno_t rc;
667
668 if (disp->backbuf == NULL) {
669 /* Not double-buffered, nothing to do. */
670 return EOK;
671 }
672
673 rc = gfx_bitmap_render(disp->backbuf, &disp->dirty_rect, NULL);
674 if (rc != EOK)
675 return rc;
676
677 disp->dirty_rect.p0.x = 0;
678 disp->dirty_rect.p0.y = 0;
679 disp->dirty_rect.p1.x = 0;
680 disp->dirty_rect.p1.y = 0;
681
682 return EOK;
683}
684
[2012fe0]685/** Paint display.
686 *
687 * @param display Display
688 * @param rect Bounding rectangle or @c NULL to repaint entire display
689 */
690errno_t ds_display_paint(ds_display_t *disp, gfx_rect_t *rect)
691{
692 errno_t rc;
693 ds_window_t *wnd;
[978c9bc5]694 ds_seat_t *seat;
[2012fe0]695
696 /* Paint background */
697 rc = ds_display_paint_bg(disp, rect);
698 if (rc != EOK)
699 return rc;
700
701 /* Paint windows bottom to top */
702 wnd = ds_display_last_window(disp);
703 while (wnd != NULL) {
704 rc = ds_window_paint(wnd, rect);
705 if (rc != EOK)
706 return rc;
707
708 wnd = ds_display_prev_window(wnd);
709 }
710
[6301a24f]711 /* Paint window previews for windows being resized or moved */
712 wnd = ds_display_last_window(disp);
713 while (wnd != NULL) {
714 rc = ds_window_paint_preview(wnd, rect);
715 if (rc != EOK)
716 return rc;
717
718 wnd = ds_display_prev_window(wnd);
719 }
720
721 /* Paint pointers */
[978c9bc5]722 seat = ds_display_first_seat(disp);
723 while (seat != NULL) {
724 rc = ds_seat_paint_pointer(seat, rect);
725 if (rc != EOK)
726 return rc;
727
728 seat = ds_display_next_seat(seat);
729 }
730
[8aef01c]731 return ds_display_update(disp);
732}
733
734/** Display update callback.
735 *
736 * Called by backbuffer memory GC when something is rendered into it.
737 * Updates the display's dirty rectangle.
738 *
739 * @param arg Argument (display cast as void *)
740 * @param rect Rectangle to update
741 */
742static void ds_display_update_cb(void *arg, gfx_rect_t *rect)
743{
744 ds_display_t *disp = (ds_display_t *) arg;
745 gfx_rect_t env;
746
747 gfx_rect_envelope(&disp->dirty_rect, rect, &env);
748 disp->dirty_rect = env;
[2012fe0]749}
750
[c8cf261]751/** @}
752 */
Note: See TracBrowser for help on using the repository browser.