source: mainline/uspace/srv/hid/display/display.c@ 2ab8ab3

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

Client-side UI rendering

It is possible to turn on and off and if turned on, one can also
enable or disable window double buffering (currently both options
are build-time).

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