source: mainline/uspace/srv/hid/display/display.c@ 7dbf1f6

Last change on this file since 7dbf1f6 was 5d380b6, checked in by Jiri Svoboda <jiri@…>, 3 years ago

Create menu windows in the correct seat

Add a mechanism to set the seat of a new display window, UI window,
UI popup - input device ID. This is set to the ID of the device which
activated the menu (mouse, keyboard). The display server determines
the correct seat from there.

This makes sure clicking outside closes the correct pop-up window.

  • Property mode set to 100644
File size: 25.1 KB
Line 
1/*
2 * Copyright (c) 2023 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 "display.h"
48#include "seat.h"
49#include "window.h"
50#include "wmclient.h"
51
52static gfx_context_t *ds_display_get_unbuf_gc(ds_display_t *);
53static void ds_display_invalidate_cb(void *, gfx_rect_t *);
54static void ds_display_update_cb(void *);
55
56static mem_gc_cb_t ds_display_mem_gc_cb = {
57 .invalidate = ds_display_invalidate_cb,
58 .update = ds_display_update_cb
59};
60
61/** Create display.
62 *
63 * @param gc Graphics context for displaying output
64 * @param flags Display flags
65 * @param rdisp Place to store pointer to new display.
66 * @return EOK on success, ENOMEM if out of memory
67 */
68errno_t ds_display_create(gfx_context_t *gc, ds_display_flags_t flags,
69 ds_display_t **rdisp)
70{
71 ds_display_t *disp;
72 ds_cursor_t *cursor;
73 int i;
74 errno_t rc;
75
76 disp = calloc(1, sizeof(ds_display_t));
77 if (disp == NULL)
78 return ENOMEM;
79
80 rc = gfx_color_new_rgb_i16(0x8000, 0xc800, 0xffff, &disp->bg_color);
81 if (rc != EOK) {
82 free(disp);
83 return ENOMEM;
84 }
85
86 list_initialize(&disp->cursors);
87
88 for (i = 0; i < dcurs_limit; i++) {
89 rc = ds_cursor_create(disp, &ds_cursimg[i].rect,
90 ds_cursimg[i].image, &cursor);
91 if (rc != EOK)
92 goto error;
93
94 disp->cursor[i] = cursor;
95 }
96
97 fibril_mutex_initialize(&disp->lock);
98 list_initialize(&disp->clients);
99 list_initialize(&disp->wmclients);
100 list_initialize(&disp->cfgclients);
101 disp->next_wnd_id = 1;
102 list_initialize(&disp->ddevs);
103 list_initialize(&disp->idevcfgs);
104 list_initialize(&disp->seats);
105 list_initialize(&disp->windows);
106 disp->flags = flags;
107 *rdisp = disp;
108 return EOK;
109error:
110 ds_display_destroy(disp);
111 return rc;
112}
113
114/** Destroy display.
115 *
116 * @param disp Display
117 */
118void ds_display_destroy(ds_display_t *disp)
119{
120 int i;
121
122 assert(list_empty(&disp->clients));
123 assert(list_empty(&disp->wmclients));
124 assert(list_empty(&disp->cfgclients));
125 assert(list_empty(&disp->seats));
126 assert(list_empty(&disp->ddevs));
127 assert(list_empty(&disp->idevcfgs));
128 assert(list_empty(&disp->seats));
129 assert(list_empty(&disp->windows));
130
131 /* Destroy cursors */
132 for (i = 0; i < dcurs_limit; i++) {
133 ds_cursor_destroy(disp->cursor[i]);
134 disp->cursor[i] = NULL;
135 }
136
137 gfx_color_delete(disp->bg_color);
138 free(disp);
139}
140
141/** Lock display.
142 *
143 * This should be called in any thread that wishes to access the display
144 * or its child objects (e.g. windows).
145 *
146 * @param disp Display
147 */
148void ds_display_lock(ds_display_t *disp)
149{
150 fibril_mutex_lock(&disp->lock);
151}
152
153/** Unlock display.
154 *
155 * @param disp Display
156 */
157void ds_display_unlock(ds_display_t *disp)
158{
159 fibril_mutex_unlock(&disp->lock);
160}
161
162/** Get display information.
163 *
164 * @param disp Display
165 */
166void ds_display_get_info(ds_display_t *disp, display_info_t *info)
167{
168 info->rect = disp->rect;
169}
170
171/** Add client to display.
172 *
173 * @param disp Display
174 * @param client Client
175 */
176void ds_display_add_client(ds_display_t *disp, ds_client_t *client)
177{
178 assert(client->display == NULL);
179 assert(!link_used(&client->lclients));
180
181 client->display = disp;
182 list_append(&client->lclients, &disp->clients);
183}
184
185/** Remove client from display.
186 *
187 * @param client Client
188 */
189void ds_display_remove_client(ds_client_t *client)
190{
191 list_remove(&client->lclients);
192 client->display = NULL;
193}
194
195/** Get first client in display.
196 *
197 * @param disp Display
198 * @return First client or @c NULL if there is none
199 */
200ds_client_t *ds_display_first_client(ds_display_t *disp)
201{
202 link_t *link = list_first(&disp->clients);
203
204 if (link == NULL)
205 return NULL;
206
207 return list_get_instance(link, ds_client_t, lclients);
208}
209
210/** Get next client in display.
211 *
212 * @param client Current client
213 * @return Next client or @c NULL if there is none
214 */
215ds_client_t *ds_display_next_client(ds_client_t *client)
216{
217 link_t *link = list_next(&client->lclients, &client->display->clients);
218
219 if (link == NULL)
220 return NULL;
221
222 return list_get_instance(link, ds_client_t, lclients);
223}
224
225/** Add WM client to display.
226 *
227 * @param disp Display
228 * @param wmclient WM client
229 */
230void ds_display_add_wmclient(ds_display_t *disp, ds_wmclient_t *wmclient)
231{
232 assert(wmclient->display == NULL);
233 assert(!link_used(&wmclient->lwmclients));
234
235 wmclient->display = disp;
236 list_append(&wmclient->lwmclients, &disp->wmclients);
237}
238
239/** Remove WM client from display.
240 *
241 * @param wmclient WM client
242 */
243void ds_display_remove_wmclient(ds_wmclient_t *wmclient)
244{
245 list_remove(&wmclient->lwmclients);
246 wmclient->display = NULL;
247}
248
249/** Add CFG client to display.
250 *
251 * @param disp Display
252 * @param cfgclient CFG client
253 */
254void ds_display_add_cfgclient(ds_display_t *disp, ds_cfgclient_t *cfgclient)
255{
256 assert(cfgclient->display == NULL);
257 assert(!link_used(&cfgclient->lcfgclients));
258
259 cfgclient->display = disp;
260 list_append(&cfgclient->lcfgclients, &disp->cfgclients);
261}
262
263/** Remove CFG client from display.
264 *
265 * @param cfgclient CFG client
266 */
267void ds_display_remove_cfgclient(ds_cfgclient_t *cfgclient)
268{
269 list_remove(&cfgclient->lcfgclients);
270 cfgclient->display = NULL;
271}
272
273/** Get first WM client in display.
274 *
275 * @param disp Display
276 * @return First WM client or @c NULL if there is none
277 */
278ds_wmclient_t *ds_display_first_wmclient(ds_display_t *disp)
279{
280 link_t *link = list_first(&disp->wmclients);
281
282 if (link == NULL)
283 return NULL;
284
285 return list_get_instance(link, ds_wmclient_t, lwmclients);
286}
287
288/** Get next WM client in display.
289 *
290 * @param wmclient Current WM client
291 * @return Next WM client or @c NULL if there is none
292 */
293ds_wmclient_t *ds_display_next_wmclient(ds_wmclient_t *wmclient)
294{
295 link_t *link = list_next(&wmclient->lwmclients,
296 &wmclient->display->wmclients);
297
298 if (link == NULL)
299 return NULL;
300
301 return list_get_instance(link, ds_wmclient_t, lwmclients);
302}
303
304/** Find window in all clients by ID.
305 *
306 * XXX This is just a hack needed to match GC connection to a window,
307 * as we don't have a good safe way to pass the GC endpoint to our client
308 * on demand.
309 *
310 * @param display Display
311 * @param id Window ID
312 */
313ds_window_t *ds_display_find_window(ds_display_t *display, ds_wnd_id_t id)
314{
315 ds_client_t *client;
316 ds_window_t *wnd;
317
318 client = ds_display_first_client(display);
319 while (client != NULL) {
320 wnd = ds_client_find_window(client, id);
321 if (wnd != NULL)
322 return wnd;
323
324 client = ds_display_next_client(client);
325 }
326
327 return NULL;
328}
329
330/** Find window by display position.
331 *
332 * @param display Display
333 * @param pos Display position
334 */
335ds_window_t *ds_display_window_by_pos(ds_display_t *display, gfx_coord2_t *pos)
336{
337 ds_window_t *wnd;
338 gfx_rect_t drect;
339
340 wnd = ds_display_first_window(display);
341 while (wnd != NULL) {
342 /* Window bounding rectangle on display */
343 gfx_rect_translate(&wnd->dpos, &wnd->rect, &drect);
344
345 if (gfx_pix_inside_rect(pos, &drect) &&
346 ds_window_is_visible(wnd))
347 return wnd;
348
349 wnd = ds_display_next_window(wnd);
350 }
351
352 return NULL;
353}
354
355/** Add window to window list.
356 *
357 * Topmost windows are enlisted before any other window. Non-topmost
358 * windows are enlisted before any other non-topmost window.
359 *
360 * @param display Display
361 * @param wnd Window
362 */
363void ds_display_enlist_window(ds_display_t *display, ds_window_t *wnd)
364{
365 ds_window_t *w;
366
367 assert(wnd->display == display);
368 assert(!link_used(&wnd->ldwindows));
369
370 if ((wnd->flags & wndf_topmost) == 0) {
371 /* Find the first non-topmost window */
372 w = ds_display_first_window(display);
373 while (w != NULL && (w->flags & wndf_topmost) != 0)
374 w = ds_display_next_window(w);
375
376 if (w != NULL)
377 list_insert_before(&wnd->ldwindows, &w->ldwindows);
378 else
379 list_append(&wnd->ldwindows, &display->windows);
380 } else {
381 /* Insert at the beginning */
382 list_prepend(&wnd->ldwindows, &display->windows);
383 }
384
385}
386
387/** Add window to display.
388 *
389 * @param display Display
390 * @param wnd Window
391 */
392void ds_display_add_window(ds_display_t *display, ds_window_t *wnd)
393{
394 ds_wmclient_t *wmclient;
395
396 assert(wnd->display == NULL);
397 assert(!link_used(&wnd->ldwindows));
398
399 wnd->display = display;
400 ds_display_enlist_window(display, wnd);
401
402 /* Notify window managers about the new window */
403 wmclient = ds_display_first_wmclient(display);
404 while (wmclient != NULL) {
405 ds_wmclient_post_wnd_added_event(wmclient, wnd->id);
406 wmclient = ds_display_next_wmclient(wmclient);
407 }
408}
409
410/** Remove window from display.
411 *
412 * @param wnd Window
413 */
414void ds_display_remove_window(ds_window_t *wnd)
415{
416 ds_wmclient_t *wmclient;
417 ds_display_t *display;
418
419 display = wnd->display;
420
421 list_remove(&wnd->ldwindows);
422 wnd->display = NULL;
423
424 /* Notify window managers about the removed window */
425 wmclient = ds_display_first_wmclient(display);
426 while (wmclient != NULL) {
427 ds_wmclient_post_wnd_removed_event(wmclient, wnd->id);
428 wmclient = ds_display_next_wmclient(wmclient);
429 }
430}
431
432/** Move window to top.
433 *
434 * @param display Display
435 * @param wnd Window
436 */
437void ds_display_window_to_top(ds_window_t *wnd)
438{
439 assert(wnd->display != NULL);
440 assert(link_used(&wnd->ldwindows));
441
442 list_remove(&wnd->ldwindows);
443 ds_display_enlist_window(wnd->display, wnd);
444}
445
446/** Get first window in display.
447 *
448 * @param display Display
449 * @return First window or @c NULL if there is none
450 */
451ds_window_t *ds_display_first_window(ds_display_t *display)
452{
453 link_t *link = list_first(&display->windows);
454
455 if (link == NULL)
456 return NULL;
457
458 return list_get_instance(link, ds_window_t, ldwindows);
459}
460
461/** Get last window in display.
462 *
463 * @param display Display
464 * @return Last window or @c NULL if there is none
465 */
466ds_window_t *ds_display_last_window(ds_display_t *display)
467{
468 link_t *link = list_last(&display->windows);
469
470 if (link == NULL)
471 return NULL;
472
473 return list_get_instance(link, ds_window_t, ldwindows);
474}
475
476/** Get next window in client.
477 *
478 * @param wnd Current window
479 * @return Next window or @c NULL if there is none
480 */
481ds_window_t *ds_display_next_window(ds_window_t *wnd)
482{
483 link_t *link = list_next(&wnd->ldwindows, &wnd->display->windows);
484
485 if (link == NULL)
486 return NULL;
487
488 return list_get_instance(link, ds_window_t, ldwindows);
489}
490
491/** Get previous window in client.
492 *
493 * @param wnd Current window
494 * @return Previous window or @c NULL if there is none
495 */
496ds_window_t *ds_display_prev_window(ds_window_t *wnd)
497{
498 link_t *link = list_prev(&wnd->ldwindows, &wnd->display->windows);
499
500 if (link == NULL)
501 return NULL;
502
503 return list_get_instance(link, ds_window_t, ldwindows);
504}
505
506/** Post keyboard event to a display.
507 *
508 * The event is routed to the correct window by first determining the
509 * seat the keyboard device belongs to and then the event is sent to the
510 * window focused by that seat.
511 *
512 * @param display Display
513 * @param event Event
514 */
515errno_t ds_display_post_kbd_event(ds_display_t *display, kbd_event_t *event)
516{
517 ds_seat_t *seat;
518
519 /* Determine which seat the event belongs to */
520 seat = ds_display_seat_by_idev(display, event->kbd_id);
521 if (seat == NULL)
522 return EOK;
523
524 return ds_seat_post_kbd_event(seat, event);
525}
526
527/** Post position event to a display.
528 *
529 * @param display Display
530 * @param event Event
531 */
532errno_t ds_display_post_ptd_event(ds_display_t *display, ptd_event_t *event)
533{
534 ds_seat_t *seat;
535
536 /* Determine which seat the event belongs to */
537 seat = ds_display_seat_by_idev(display, event->pos_id);
538 if (seat == NULL)
539 return EOK;
540
541 return ds_seat_post_ptd_event(seat, event);
542}
543
544/** Add seat to display.
545 *
546 * @param disp Display
547 * @param seat Seat
548 */
549void ds_display_add_seat(ds_display_t *disp, ds_seat_t *seat)
550{
551 assert(seat->display == NULL);
552 assert(!link_used(&seat->lseats));
553
554 seat->display = disp;
555 seat->id = disp->next_seat_id++;
556 list_append(&seat->lseats, &disp->seats);
557}
558
559/** Remove seat from display.
560 *
561 * @param seat Seat
562 */
563void ds_display_remove_seat(ds_seat_t *seat)
564{
565 list_remove(&seat->lseats);
566 seat->display = NULL;
567}
568
569/** Get first seat in display.
570 *
571 * @param disp Display
572 * @return First seat or @c NULL if there is none
573 */
574ds_seat_t *ds_display_first_seat(ds_display_t *disp)
575{
576 link_t *link = list_first(&disp->seats);
577
578 if (link == NULL)
579 return NULL;
580
581 return list_get_instance(link, ds_seat_t, lseats);
582}
583
584/** Get next seat in display.
585 *
586 * @param seat Current seat
587 * @return Next seat or @c NULL if there is none
588 */
589ds_seat_t *ds_display_next_seat(ds_seat_t *seat)
590{
591 link_t *link = list_next(&seat->lseats, &seat->display->seats);
592
593 if (link == NULL)
594 return NULL;
595
596 return list_get_instance(link, ds_seat_t, lseats);
597}
598
599/** Get default seat in display.
600 *
601 * @param disp Display
602 * @return First seat or @c NULL if there is none
603 */
604ds_seat_t *ds_display_default_seat(ds_display_t *disp)
605{
606 /* XXX Probably not the best solution */
607 return ds_display_first_seat(disp);
608}
609
610/** Find seat by ID.
611 *
612 * @param display Display
613 * @param id Seat ID
614 */
615ds_seat_t *ds_display_find_seat(ds_display_t *display, ds_seat_id_t id)
616{
617 ds_seat_t *seat;
618
619 seat = ds_display_first_seat(display);
620 while (seat != NULL) {
621 if (seat->id == id)
622 return seat;
623
624 seat = ds_display_next_seat(seat);
625 }
626
627 return NULL;
628}
629
630/** Get seat which owns the specified input device.
631 *
632 * @param disp Display
633 * @param idev_id Input device ID
634 * @return Seat which owns device with ID @a idev_id or @c NULL if not found
635 */
636ds_seat_t *ds_display_seat_by_idev(ds_display_t *disp, ds_idev_id_t idev_id)
637{
638 ds_idevcfg_t *idevcfg;
639
640 /*
641 * Find input device configuration entry that maps this input device
642 * to a seat.
643 */
644 idevcfg = ds_display_first_idevcfg(disp);
645 while (idevcfg != NULL) {
646 if (idevcfg->svc_id == idev_id)
647 return idevcfg->seat;
648
649 idevcfg = ds_display_next_idevcfg(idevcfg);
650 }
651
652 /* If none was found, return the default seat */
653 return ds_display_default_seat(disp);
654}
655
656/** Allocate back buffer for display.
657 *
658 * @param disp Display
659 * @return EOK on success or if no back buffer is required, otherwise
660 * an error code.
661 */
662static errno_t ds_display_alloc_backbuf(ds_display_t *disp)
663{
664 gfx_context_t *ugc;
665 gfx_bitmap_params_t params;
666 gfx_bitmap_alloc_t alloc;
667 errno_t rc;
668
669 /* Allocate backbuffer */
670 if ((disp->flags & df_disp_double_buf) == 0) {
671 /* Not double buffering. Nothing to do. */
672 return EOK;
673 }
674
675 ugc = ds_display_get_unbuf_gc(disp);
676
677 gfx_bitmap_params_init(&params);
678 params.rect = disp->rect;
679
680 rc = gfx_bitmap_create(ugc, &params, NULL,
681 &disp->backbuf);
682 if (rc != EOK)
683 goto error;
684
685 rc = gfx_bitmap_get_alloc(disp->backbuf, &alloc);
686 if (rc != EOK)
687 goto error;
688
689 rc = mem_gc_create(&disp->rect, &alloc, &ds_display_mem_gc_cb,
690 (void *) disp, &disp->bbgc);
691 if (rc != EOK)
692 goto error;
693
694 disp->dirty_rect.p0.x = 0;
695 disp->dirty_rect.p0.y = 0;
696 disp->dirty_rect.p1.x = 0;
697 disp->dirty_rect.p1.y = 0;
698
699 return EOK;
700error:
701 if (disp->backbuf != NULL) {
702 gfx_bitmap_destroy(disp->backbuf);
703 disp->backbuf = NULL;
704 }
705
706 return rc;
707}
708
709/** Add display device to display.
710 *
711 * @param disp Display
712 * @param ddev Display device
713 * @return EOK on success, or an error code
714 */
715errno_t ds_display_add_ddev(ds_display_t *disp, ds_ddev_t *ddev)
716{
717 errno_t rc;
718 gfx_rect_t old_disp_rect;
719
720 assert(ddev->display == NULL);
721 assert(!link_used(&ddev->lddevs));
722
723 old_disp_rect = disp->rect;
724
725 ddev->display = disp;
726 list_append(&ddev->lddevs, &disp->ddevs);
727
728 /* First display device */
729 if (gfx_rect_is_empty(&disp->rect)) {
730 /* Set screen dimensions */
731 disp->rect = ddev->info.rect;
732
733 /* Create cloning GC */
734 rc = ds_clonegc_create(ddev->gc, &disp->fbgc);
735 if (rc != EOK)
736 goto error;
737
738 /* Allocate backbuffer */
739 rc = ds_display_alloc_backbuf(disp);
740 if (rc != EOK) {
741 ds_clonegc_delete(disp->fbgc);
742 disp->fbgc = NULL;
743 goto error;
744 }
745 } else {
746 /* Add new output device to cloning GC */
747 rc = ds_clonegc_add_output(disp->fbgc, ddev->gc);
748 if (rc != EOK)
749 goto error;
750 }
751
752 ds_display_update_max_rect(disp);
753
754 return EOK;
755error:
756 disp->rect = old_disp_rect;
757 list_remove(&ddev->lddevs);
758 return rc;
759}
760
761/** Remove display device from display.
762 *
763 * @param ddev Display device
764 */
765void ds_display_remove_ddev(ds_ddev_t *ddev)
766{
767 list_remove(&ddev->lddevs);
768 ddev->display = NULL;
769}
770
771/** Get first display device in display.
772 *
773 * @param disp Display
774 * @return First display device or @c NULL if there is none
775 */
776ds_ddev_t *ds_display_first_ddev(ds_display_t *disp)
777{
778 link_t *link = list_first(&disp->ddevs);
779
780 if (link == NULL)
781 return NULL;
782
783 return list_get_instance(link, ds_ddev_t, lddevs);
784}
785
786/** Get next display device in display.
787 *
788 * @param ddev Current display device
789 * @return Next display device or @c NULL if there is none
790 */
791ds_ddev_t *ds_display_next_ddev(ds_ddev_t *ddev)
792{
793 link_t *link = list_next(&ddev->lddevs, &ddev->display->ddevs);
794
795 if (link == NULL)
796 return NULL;
797
798 return list_get_instance(link, ds_ddev_t, lddevs);
799}
800
801/** Add input device configuration entry to display.
802 *
803 * @param disp Display
804 * @param idevcfg Input device configuration
805 */
806void ds_display_add_idevcfg(ds_display_t *disp, ds_idevcfg_t *idevcfg)
807{
808 assert(idevcfg->display == NULL);
809 assert(!link_used(&idevcfg->ldispidcfgs));
810
811 idevcfg->display = disp;
812 list_append(&idevcfg->ldispidcfgs, &disp->idevcfgs);
813}
814
815/** Remove input device configuration entry from display.
816 *
817 * @param idevcfg Input device configuration entry
818 */
819void ds_display_remove_idevcfg(ds_idevcfg_t *idevcfg)
820{
821 list_remove(&idevcfg->ldispidcfgs);
822 idevcfg->display = NULL;
823}
824
825/** Get first input device configuration entry in display.
826 *
827 * @param disp Display
828 * @return First input device configuration entry or @c NULL if there is none
829 */
830ds_idevcfg_t *ds_display_first_idevcfg(ds_display_t *disp)
831{
832 link_t *link = list_first(&disp->idevcfgs);
833
834 if (link == NULL)
835 return NULL;
836
837 return list_get_instance(link, ds_idevcfg_t, ldispidcfgs);
838}
839
840/** Get next input device configuration entry in display.
841 *
842 * @param idevcfg Current input device configuration entry
843 * @return Next input device configuration entry or @c NULL if there is none
844 */
845ds_idevcfg_t *ds_display_next_idevcfg(ds_idevcfg_t *idevcfg)
846{
847 link_t *link = list_next(&idevcfg->ldispidcfgs, &idevcfg->display->idevcfgs);
848
849 if (link == NULL)
850 return NULL;
851
852 return list_get_instance(link, ds_idevcfg_t, ldispidcfgs);
853}
854
855/** Add cursor to display.
856 *
857 * @param display Display
858 * @param cursor Cursor
859 */
860void ds_display_add_cursor(ds_display_t *display, ds_cursor_t *cursor)
861{
862 assert(cursor->display == NULL);
863 assert(!link_used(&cursor->ldisplay));
864
865 cursor->display = display;
866 list_prepend(&cursor->ldisplay, &display->cursors);
867}
868
869/** Remove cursor from display.
870 *
871 * @param cursor Cursor
872 */
873void ds_display_remove_cursor(ds_cursor_t *cursor)
874{
875 list_remove(&cursor->ldisplay);
876 cursor->display = NULL;
877}
878
879/** Update display maximize rectangle.
880 *
881 * Recalculate the maximize rectangle (the rectangle used for maximized
882 * windows).
883 *
884 * @param display Display
885 */
886void ds_display_update_max_rect(ds_display_t *display)
887{
888 ds_window_t *wnd;
889 gfx_rect_t max_rect;
890 gfx_rect_t drect;
891
892 /* Start with the entire display */
893 max_rect = display->rect;
894
895 wnd = ds_display_first_window(display);
896 while (wnd != NULL) {
897 /* Should maximized windows avoid this window? */
898 if ((wnd->flags & wndf_avoid) != 0) {
899 /* Window bounding rectangle on display */
900 gfx_rect_translate(&wnd->dpos, &wnd->rect, &drect);
901
902 /* Crop maximized rectangle */
903 ds_display_crop_max_rect(&drect, &max_rect);
904 }
905
906 wnd = ds_display_next_window(wnd);
907 }
908
909 /* Update the maximize rectangle */
910 display->max_rect = max_rect;
911}
912
913/** Crop maximize rectangle.
914 *
915 * Use the avoid rectangle @a arect to crop off maximization rectangle
916 * @a mrect. If @a arect covers the top, bottom, left or right part
917 * of @a mrect, it will be cropped off. Otherwise there will be
918 * no effect.
919 *
920 * @param arect Avoid rectangle
921 * @param mrect Maximize rectangle to be modified
922 */
923void ds_display_crop_max_rect(gfx_rect_t *arect, gfx_rect_t *mrect)
924{
925 if (arect->p0.x == mrect->p0.x && arect->p0.y == mrect->p0.y &&
926 arect->p1.x == mrect->p1.x) {
927 /* Cropp off top part */
928 mrect->p0.y = arect->p1.y;
929 } else if (arect->p0.x == mrect->p0.x && arect->p1.x == mrect->p1.x &&
930 arect->p1.y == mrect->p1.y) {
931 /* Cropp off bottom part */
932 mrect->p1.y = arect->p0.y;
933 } else if (arect->p0.x == mrect->p0.x && arect->p0.y == mrect->p0.y &&
934 arect->p1.y == mrect->p1.y) {
935 /* Cropp off left part */
936 mrect->p0.x = arect->p1.x;
937 } else if (arect->p0.y == mrect->p0.y && arect->p1.x == mrect->p1.x &&
938 arect->p1.y == mrect->p1.y) {
939 /* Cropp off right part */
940 mrect->p1.x = arect->p0.x;
941 }
942}
943
944/** Get unbuffered GC.
945 *
946 * Get the display's (unbuffered) graphic context. If the display
947 * is double-buffered, this returns GC of the front buffer. If the display
948 * is unbuffered, this is the same as @c ds_display_get_gc().
949 *
950 * @param display Display
951 * @return Unbuffered GC
952 */
953static gfx_context_t *ds_display_get_unbuf_gc(ds_display_t *display)
954{
955 /* In case of unit tests */
956 if (display->fbgc == NULL)
957 return NULL;
958
959 return ds_clonegc_get_ctx(display->fbgc);
960}
961
962/** Get display GC.
963 *
964 * Get the graphic context used to paint the display. This is to be used
965 * for all display server paint operations.
966 *
967 * @param display Display
968 * @return Graphic context for painting to the display
969 */
970gfx_context_t *ds_display_get_gc(ds_display_t *display)
971{
972 if ((display->flags & df_disp_double_buf) != 0)
973 return mem_gc_get_ctx(display->bbgc);
974 else
975 return ds_display_get_unbuf_gc(display);
976}
977
978/** Paint display background.
979 *
980 * @param display Display
981 * @param rect Bounding rectangle or @c NULL to repaint entire display
982 */
983errno_t ds_display_paint_bg(ds_display_t *disp, gfx_rect_t *rect)
984{
985 gfx_rect_t crect;
986 gfx_context_t *gc;
987 errno_t rc;
988
989 if (rect != NULL)
990 gfx_rect_clip(&disp->rect, rect, &crect);
991 else
992 crect = disp->rect;
993
994 gc = ds_display_get_gc(disp);
995 if (gc == NULL)
996 return EOK;
997
998 rc = gfx_set_color(gc, disp->bg_color);
999 if (rc != EOK)
1000 return rc;
1001
1002 return gfx_fill_rect(gc, &crect);
1003}
1004
1005/** Update front buffer from back buffer.
1006 *
1007 * If the display is not double-buffered, no action is taken.
1008 *
1009 * @param disp Display
1010 * @return EOK on success, or an error code
1011 */
1012static errno_t ds_display_update(ds_display_t *disp)
1013{
1014 errno_t rc;
1015
1016 if (disp->backbuf == NULL) {
1017 /* Not double-buffered, nothing to do. */
1018 return EOK;
1019 }
1020
1021 rc = gfx_bitmap_render(disp->backbuf, &disp->dirty_rect, NULL);
1022 if (rc != EOK)
1023 return rc;
1024
1025 disp->dirty_rect.p0.x = 0;
1026 disp->dirty_rect.p0.y = 0;
1027 disp->dirty_rect.p1.x = 0;
1028 disp->dirty_rect.p1.y = 0;
1029
1030 return EOK;
1031}
1032
1033/** Paint display.
1034 *
1035 * @param display Display
1036 * @param rect Bounding rectangle or @c NULL to repaint entire display
1037 */
1038errno_t ds_display_paint(ds_display_t *disp, gfx_rect_t *rect)
1039{
1040 errno_t rc;
1041 ds_window_t *wnd;
1042 ds_seat_t *seat;
1043
1044 /* Paint background */
1045 rc = ds_display_paint_bg(disp, rect);
1046 if (rc != EOK)
1047 return rc;
1048
1049 /* Paint windows bottom to top */
1050 wnd = ds_display_last_window(disp);
1051 while (wnd != NULL) {
1052 rc = ds_window_paint(wnd, rect);
1053 if (rc != EOK)
1054 return rc;
1055
1056 wnd = ds_display_prev_window(wnd);
1057 }
1058
1059 /* Paint window previews for windows being resized or moved */
1060 wnd = ds_display_last_window(disp);
1061 while (wnd != NULL) {
1062 rc = ds_window_paint_preview(wnd, rect);
1063 if (rc != EOK)
1064 return rc;
1065
1066 wnd = ds_display_prev_window(wnd);
1067 }
1068
1069 /* Paint pointers */
1070 seat = ds_display_first_seat(disp);
1071 while (seat != NULL) {
1072 rc = ds_seat_paint_pointer(seat, rect);
1073 if (rc != EOK)
1074 return rc;
1075
1076 seat = ds_display_next_seat(seat);
1077 }
1078
1079 return ds_display_update(disp);
1080}
1081
1082/** Display invalidate callback.
1083 *
1084 * Called by backbuffer memory GC when something is rendered into it.
1085 * Updates the display's dirty rectangle.
1086 *
1087 * @param arg Argument (display cast as void *)
1088 * @param rect Rectangle to update
1089 */
1090static void ds_display_invalidate_cb(void *arg, gfx_rect_t *rect)
1091{
1092 ds_display_t *disp = (ds_display_t *) arg;
1093 gfx_rect_t env;
1094
1095 gfx_rect_envelope(&disp->dirty_rect, rect, &env);
1096 disp->dirty_rect = env;
1097}
1098
1099/** Display update callback.
1100 *
1101 * @param arg Argument (display cast as void *)
1102 */
1103static void ds_display_update_cb(void *arg)
1104{
1105 ds_display_t *disp = (ds_display_t *) arg;
1106
1107 (void) disp;
1108}
1109
1110/** @}
1111 */
Note: See TracBrowser for help on using the repository browser.