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
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_update_cb(void *, gfx_rect_t *);
53
54/** Create display.
55 *
56 * @param gc Graphics context for displaying output
57 * @param flags Display flags
58 * @param rdisp Place to store pointer to new display.
59 * @return EOK on success, ENOMEM if out of memory
60 */
61errno_t ds_display_create(gfx_context_t *gc, ds_display_flags_t flags,
62 ds_display_t **rdisp)
63{
64 ds_display_t *disp;
65 ds_cursor_t *cursor;
66 int i;
67 errno_t rc;
68
69 disp = calloc(1, sizeof(ds_display_t));
70 if (disp == NULL)
71 return ENOMEM;
72
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
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
90 fibril_mutex_initialize(&disp->lock);
91 list_initialize(&disp->clients);
92 disp->next_wnd_id = 1;
93 list_initialize(&disp->ddevs);
94 list_initialize(&disp->seats);
95 list_initialize(&disp->windows);
96 disp->flags = flags;
97 *rdisp = disp;
98 return EOK;
99error:
100 ds_display_destroy(disp);
101 return rc;
102}
103
104/** Destroy display.
105 *
106 * @param disp Display
107 */
108void ds_display_destroy(ds_display_t *disp)
109{
110 assert(list_empty(&disp->clients));
111 assert(list_empty(&disp->seats));
112 /* XXX destroy cursors */
113 gfx_color_delete(disp->bg_color);
114 free(disp);
115}
116
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
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
147/** Add client to display.
148 *
149 * @param disp Display
150 * @param client Client
151 */
152void ds_display_add_client(ds_display_t *disp, ds_client_t *client)
153{
154 assert(client->display == NULL);
155 assert(!link_used(&client->lclients));
156
157 client->display = disp;
158 list_append(&client->lclients, &disp->clients);
159}
160
161/** Remove client from display.
162 *
163 * @param client Client
164 */
165void ds_display_remove_client(ds_client_t *client)
166{
167 list_remove(&client->lclients);
168 client->display = NULL;
169}
170
171/** Get first client in display.
172 *
173 * @param disp Display
174 * @return First client or @c NULL if there is none
175 */
176ds_client_t *ds_display_first_client(ds_display_t *disp)
177{
178 link_t *link = list_first(&disp->clients);
179
180 if (link == NULL)
181 return NULL;
182
183 return list_get_instance(link, ds_client_t, lclients);
184}
185
186/** Get next client in display.
187 *
188 * @param client Current client
189 * @return Next client or @c NULL if there is none
190 */
191ds_client_t *ds_display_next_client(ds_client_t *client)
192{
193 link_t *link = list_next(&client->lclients, &client->display->clients);
194
195 if (link == NULL)
196 return NULL;
197
198 return list_get_instance(link, ds_client_t, lclients);
199}
200
201/** Find window in all clients by ID.
202 *
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
209 */
210#include <stdio.h>
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 printf("ds_display_find_window: id=0x%x\n", (unsigned) id);
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) {
223 printf("ds_display_find_window: found wnd=%p id=0x%x\n",
224 wnd, (unsigned) wnd->id);
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
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;
242 gfx_rect_t drect;
243
244 wnd = ds_display_first_window(display);
245 while (wnd != NULL) {
246 /* Window bounding rectangle on display */
247 gfx_rect_translate(&wnd->dpos, &wnd->rect, &drect);
248
249 if (gfx_pix_inside_rect(pos, &drect))
250 return wnd;
251
252 wnd = ds_display_next_window(wnd);
253 }
254
255 return NULL;
256}
257
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
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
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
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
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 */
351errno_t ds_display_post_kbd_event(ds_display_t *display, kbd_event_t *event)
352{
353 ds_seat_t *seat;
354
355 // TODO Determine which seat the event belongs to
356 seat = ds_display_first_seat(display);
357 if (seat == NULL)
358 return EOK;
359
360 return ds_seat_post_kbd_event(seat, event);
361}
362
363/** Post position event to a display.
364 *
365 * @param display Display
366 * @param event Event
367 */
368errno_t ds_display_post_ptd_event(ds_display_t *display, ptd_event_t *event)
369{
370 ds_seat_t *seat;
371
372 // TODO Determine which seat the event belongs to
373 seat = ds_display_first_seat(display);
374 if (seat == NULL)
375 return EOK;
376
377 return ds_seat_post_ptd_event(seat, event);
378}
379
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;
430
431 return list_get_instance(link, ds_seat_t, lseats);
432}
433
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
487/** Add display device to display.
488 *
489 * @param disp Display
490 * @param ddev Display device
491 * @return EOK on success, or an error code
492 */
493errno_t ds_display_add_ddev(ds_display_t *disp, ds_ddev_t *ddev)
494{
495 errno_t rc;
496
497 assert(ddev->display == NULL);
498 assert(!link_used(&ddev->lddevs));
499
500 ddev->display = disp;
501 list_append(&ddev->lddevs, &disp->ddevs);
502
503 /* First display device */
504 if (gfx_rect_is_empty(&disp->rect)) {
505 /* Set screen dimensions */
506 disp->rect = ddev->info.rect;
507
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
515 /* Allocate backbuffer */
516 rc = ds_display_alloc_backbuf(disp);
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);
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;
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
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
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 */
612static gfx_context_t *ds_display_get_unbuf_gc(ds_display_t *display)
613{
614 /* In case of unit tests */
615 if (display->fbgc == NULL)
616 return NULL;
617
618 return ds_clonegc_get_ctx(display->fbgc);
619}
620
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 */
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
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)
649 gfx_rect_clip(&disp->rect, rect, &crect);
650 else
651 crect = disp->rect;
652
653 gc = ds_display_get_gc(disp);
654 if (gc == NULL)
655 return EOK;
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
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
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;
701 ds_seat_t *seat;
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
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 */
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
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;
756}
757
758/** @}
759 */
Note: See TracBrowser for help on using the repository browser.