source: mainline/uspace/srv/hid/display/display.c@ 5877de74

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

Memory GC needs to be able to forward cursor control

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