source: mainline/uspace/lib/display/src/display.c@ 552b69f

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

Dual-mode applications should automatically fall back to console

  • Property mode set to 100644
File size: 16.5 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#include <async.h>
30#include <display.h>
31#include <display/event.h>
32#include <errno.h>
33#include <fibril_synch.h>
34#include <ipc/display.h>
35#include <ipc/services.h>
36#include <ipcgfx/client.h>
37#include <loc.h>
38#include <mem.h>
39#include <stdlib.h>
40#include "../private/display.h"
41#include "../private/params.h"
42
43static errno_t display_callback_create(display_t *);
44static void display_cb_conn(ipc_call_t *, void *);
45static errno_t display_get_window(display_t *, sysarg_t, display_window_t **);
46
47/** Open display service.
48 *
49 * @param dsname Display service name or @c NULL to use default display
50 * @param rdisplay Place to store pointer to display session
51 * @return EOK on success or an error code
52 */
53errno_t display_open(const char *dsname, display_t **rdisplay)
54{
55 service_id_t display_svc;
56 display_t *display;
57 errno_t rc;
58
59 display = calloc(1, sizeof(display_t));
60 if (display == NULL)
61 return ENOMEM;
62
63 fibril_mutex_initialize(&display->lock);
64 fibril_condvar_initialize(&display->cv);
65 list_initialize(&display->windows);
66
67 if (dsname == NULL)
68 dsname = SERVICE_NAME_DISPLAY;
69
70 rc = loc_service_get_id(dsname, &display_svc, 0);
71 if (rc != EOK) {
72 free(display);
73 return ENOENT;
74 }
75
76 display->sess = loc_service_connect(display_svc, INTERFACE_DISPLAY,
77 0);
78 if (display->sess == NULL) {
79 free(display);
80 return ENOENT;
81 }
82
83 rc = display_callback_create(display);
84 if (rc != EOK) {
85 async_hangup(display->sess);
86 free(display);
87 return EIO;
88 }
89
90 *rdisplay = display;
91 return EOK;
92}
93
94/** Create callback connection from display service.
95 *
96 * @param display Display session
97 * @return EOK on success or an error code
98 */
99static errno_t display_callback_create(display_t *display)
100{
101 async_exch_t *exch = async_exchange_begin(display->sess);
102
103 aid_t req = async_send_0(exch, DISPLAY_CALLBACK_CREATE, NULL);
104
105 port_id_t port;
106 errno_t rc = async_create_callback_port(exch, INTERFACE_DISPLAY_CB, 0, 0,
107 display_cb_conn, display, &port);
108
109 async_exchange_end(exch);
110
111 if (rc != EOK)
112 return rc;
113
114 errno_t retval;
115 async_wait_for(req, &retval);
116
117 return retval;
118}
119
120/** Close display service.
121 *
122 * @param display Display session
123 */
124void display_close(display_t *display)
125{
126 fibril_mutex_lock(&display->lock);
127 async_hangup(display->sess);
128 display->sess = NULL;
129
130 /* Wait for callback handler to terminate */
131
132 while (!display->cb_done)
133 fibril_condvar_wait(&display->cv, &display->lock);
134 fibril_mutex_unlock(&display->lock);
135
136 free(display);
137}
138
139/** Initialize window parameters structure.
140 *
141 * Window parameters structure must always be initialized using this function
142 * first.
143 *
144 * @param params Window parameters structure
145 */
146void display_wnd_params_init(display_wnd_params_t *params)
147{
148 memset(params, 0, sizeof(*params));
149}
150
151/** Create a display window.
152 *
153 * @param display Display
154 * @param params Window parameters
155 * @param cb Callback functions
156 * @param cb_arg Argument to callback functions
157 * @param rwindow Place to store pointer to new window
158 * @return EOK on success or an error code
159 */
160errno_t display_window_create(display_t *display, display_wnd_params_t *params,
161 display_wnd_cb_t *cb, void *cb_arg, display_window_t **rwindow)
162{
163 display_window_t *window;
164 async_exch_t *exch;
165 aid_t req;
166 ipc_call_t answer;
167 errno_t rc;
168
169 window = calloc(1, sizeof(display_window_t));
170 if (window == NULL)
171 return ENOMEM;
172
173 exch = async_exchange_begin(display->sess);
174 req = async_send_0(exch, DISPLAY_WINDOW_CREATE, &answer);
175 rc = async_data_write_start(exch, params, sizeof (display_wnd_params_t));
176 async_exchange_end(exch);
177 if (rc != EOK) {
178 async_forget(req);
179 free(window);
180 return rc;
181 }
182
183 async_wait_for(req, &rc);
184 if (rc != EOK) {
185 free(window);
186 return rc;
187 }
188
189 window->display = display;
190 window->id = ipc_get_arg1(&answer);
191 window->cb = cb;
192 window->cb_arg = cb_arg;
193
194 list_append(&window->lwindows, &display->windows);
195 *rwindow = window;
196 return EOK;
197}
198
199/** Destroy display window.
200 *
201 * @param window Window or @c NULL
202 * @return EOK on success or an error code. In both cases @a window must
203 * not be accessed anymore
204 */
205errno_t display_window_destroy(display_window_t *window)
206{
207 async_exch_t *exch;
208 errno_t rc;
209
210 if (window == NULL)
211 return EOK;
212
213 exch = async_exchange_begin(window->display->sess);
214 rc = async_req_1_0(exch, DISPLAY_WINDOW_DESTROY, window->id);
215
216 async_exchange_end(exch);
217
218 list_remove(&window->lwindows);
219 free(window);
220 return rc;
221}
222
223/** Create graphics context for drawing into a window.
224 *
225 * @param window Window
226 * @param rgc Place to store pointer to new graphics context
227 * @return EOK on success or an error code
228 */
229errno_t display_window_get_gc(display_window_t *window, gfx_context_t **rgc)
230{
231 async_sess_t *sess;
232 async_exch_t *exch;
233 ipc_gc_t *gc;
234 errno_t rc;
235
236 exch = async_exchange_begin(window->display->sess);
237 sess = async_connect_me_to(exch, INTERFACE_GC, 0, window->id, &rc);
238 if (sess == NULL) {
239 async_exchange_end(exch);
240 return rc;
241 }
242
243 async_exchange_end(exch);
244
245 rc = ipc_gc_create(sess, &gc);
246 if (rc != EOK) {
247 async_hangup(sess);
248 return ENOMEM;
249 }
250
251 *rgc = ipc_gc_get_ctx(gc);
252 return EOK;
253}
254
255/** Request a window move.
256 *
257 * Request the display service to initiate a user window move operation
258 * (i.e. let the user move the window). Used when the client detects
259 * mouse press on the title bar or such.
260 *
261 * @param window Window
262 * @param pos Position in the window where the button was pressed
263 * @return EOK on success or an error code
264 */
265errno_t display_window_move_req(display_window_t *window, gfx_coord2_t *pos)
266{
267 async_exch_t *exch;
268 aid_t req;
269 ipc_call_t answer;
270 errno_t rc;
271
272 exch = async_exchange_begin(window->display->sess);
273 req = async_send_1(exch, DISPLAY_WINDOW_MOVE_REQ, window->id, &answer);
274 rc = async_data_write_start(exch, (void *)pos, sizeof (gfx_coord2_t));
275 async_exchange_end(exch);
276 if (rc != EOK) {
277 async_forget(req);
278 return rc;
279 }
280
281 async_wait_for(req, &rc);
282 if (rc != EOK)
283 return rc;
284
285 return EOK;
286}
287
288/** Move display window.
289 *
290 * Set new display position of a window. Display position determines where
291 * the origin of the window coordinate system lies. Note that the top left
292 * corner of the window need not coincide with the window's 0,0 point.
293 *
294 * @param window Window
295 * @param dpos New display position
296 * @return EOK on success or an error code
297 */
298errno_t display_window_move(display_window_t *window, gfx_coord2_t *dpos)
299{
300 async_exch_t *exch;
301 aid_t req;
302 ipc_call_t answer;
303 errno_t rc;
304
305 exch = async_exchange_begin(window->display->sess);
306 req = async_send_1(exch, DISPLAY_WINDOW_MOVE, window->id, &answer);
307 rc = async_data_write_start(exch, dpos, sizeof (gfx_coord2_t));
308 async_exchange_end(exch);
309 if (rc != EOK) {
310 async_forget(req);
311 return rc;
312 }
313
314 async_wait_for(req, &rc);
315 if (rc != EOK)
316 return rc;
317
318 return EOK;
319}
320
321/** Get display window position.
322 *
323 * Get display window position on the display.
324 *
325 * @param window Window
326 * @param dpos Place to store position
327 * @return EOK on success or an error code
328 */
329errno_t display_window_get_pos(display_window_t *window, gfx_coord2_t *dpos)
330{
331 async_exch_t *exch;
332 aid_t req;
333 ipc_call_t answer;
334 errno_t rc;
335
336 exch = async_exchange_begin(window->display->sess);
337 req = async_send_1(exch, DISPLAY_WINDOW_GET_POS, window->id, &answer);
338 rc = async_data_read_start(exch, dpos, sizeof (gfx_coord2_t));
339 async_exchange_end(exch);
340 if (rc != EOK) {
341 async_forget(req);
342 return rc;
343 }
344
345 async_wait_for(req, &rc);
346 if (rc != EOK)
347 return rc;
348
349 return EOK;
350}
351
352/** Request a window resize.
353 *
354 * Request the display service to initiate a user window resize operation
355 * (i.e. let the user resize the window). Used when the client detects
356 * mouse press on the window frame or such.
357 *
358 * @param window Window
359 * @param rsztype Resize type (which part of window frame is being dragged)
360 * @param pos Position in the window where the button was pressed
361 * @return EOK on success or an error code
362 */
363errno_t display_window_resize_req(display_window_t *window,
364 display_wnd_rsztype_t rsztype, gfx_coord2_t *pos)
365{
366 async_exch_t *exch;
367 aid_t req;
368 ipc_call_t answer;
369 errno_t rc;
370
371 exch = async_exchange_begin(window->display->sess);
372 req = async_send_2(exch, DISPLAY_WINDOW_RESIZE_REQ, window->id,
373 (sysarg_t) rsztype, &answer);
374 rc = async_data_write_start(exch, (void *)pos, sizeof (gfx_coord2_t));
375 async_exchange_end(exch);
376 if (rc != EOK) {
377 async_forget(req);
378 return rc;
379 }
380
381 async_wait_for(req, &rc);
382 if (rc != EOK)
383 return rc;
384
385 return EOK;
386}
387
388/** Resize display window.
389 *
390 * It seems resizing windows should be easy with bounding rectangles.
391 * You have an old bounding rectangle and a new bounding rectangle (@a nrect).
392 * Change .p0 and top-left corner moves. Change .p1 and bottom-right corner
393 * moves. Piece of cake!
394 *
395 * There's always a catch, though. By series of resizes and moves .p0 could
396 * drift outside of the range of @c gfx_coord_t. Now what? @a offs to the
397 * rescue! @a offs moves the @em boundaries of the window with respect
398 * to the display, while keeping the @em contents of the window in the
399 * same place (with respect to the display). In other words, @a offs shifts
400 * the window's internal coordinate system.
401 *
402 * A few examples follow:
403 *
404 * Enlarge window by moving bottom-right corner 1 right, 1 down:
405 *
406 * bound = (0, 0, 10, 10)
407 * offs = (0, 0)
408 * nrect = (0, 0, 11, 11)
409 *
410 * Enlarge window by moving top-left corner, 1 up, 1 left, allowing the
411 * window-relative coordinate of the top-left corner to drift (undesirable)
412 *
413 * bound = (0, 0, 10, 10)
414 * offs = (0, 0)
415 * nrect = (-1, -1, 10, 10) <- this is the new bounding rectangle
416 *
417 * Enlarge window by moving top-left corner 1 up, 1 left, keeping top-left
418 * corner locked to (0,0) window-relative coordinates (desirable):
419 *
420 * bound = (0, 0, 10, 10)
421 * off = (-1,-1) <- top-left corner goes 1 up, 1 left
422 * nrect = (0, 0, 11, 11) <- window still starts at 0,0 window-relative
423 *
424 * @param window Window
425 * @param nrect New bounding rectangle
426 * @param offs
427 * @return EOK on success or an error code
428 */
429errno_t display_window_resize(display_window_t *window, gfx_coord2_t *offs,
430 gfx_rect_t *nrect)
431{
432 async_exch_t *exch;
433 aid_t req;
434 ipc_call_t answer;
435 display_wnd_resize_t wresize;
436 errno_t rc;
437
438 wresize.offs = *offs;
439 wresize.nrect = *nrect;
440
441 exch = async_exchange_begin(window->display->sess);
442 req = async_send_1(exch, DISPLAY_WINDOW_RESIZE, window->id, &answer);
443 rc = async_data_write_start(exch, &wresize, sizeof (display_wnd_resize_t));
444 async_exchange_end(exch);
445 if (rc != EOK) {
446 async_forget(req);
447 return rc;
448 }
449
450 async_wait_for(req, &rc);
451 if (rc != EOK)
452 return rc;
453
454 return EOK;
455}
456
457/** Set window cursor.
458 *
459 * Set cursor that is displayed when pointer is over the window. The default
460 * is the arrow pointer.
461 *
462 * @param window Window
463 * @param cursor Cursor to display
464 * @return EOK on success or an error code
465 */
466errno_t display_window_set_cursor(display_window_t *window,
467 display_stock_cursor_t cursor)
468{
469 async_exch_t *exch;
470 errno_t rc;
471
472 exch = async_exchange_begin(window->display->sess);
473 rc = async_req_2_0(exch, DISPLAY_WINDOW_SET_CURSOR, window->id,
474 cursor);
475 async_exchange_end(exch);
476 return rc;
477}
478
479/** Get display event.
480 *
481 * @param display Display
482 * @param rwindow Place to store pointer to window that received event
483 * @param event Place to store event
484 * @return EOK on success or an error code
485 */
486static errno_t display_get_event(display_t *display, display_window_t **rwindow,
487 display_wnd_ev_t *event)
488{
489 async_exch_t *exch;
490 ipc_call_t answer;
491 aid_t req;
492 errno_t rc;
493 sysarg_t wnd_id;
494 display_window_t *window;
495
496 exch = async_exchange_begin(display->sess);
497 req = async_send_0(exch, DISPLAY_GET_EVENT, &answer);
498 rc = async_data_read_start(exch, event, sizeof(*event));
499 async_exchange_end(exch);
500 if (rc != EOK) {
501 async_forget(req);
502 return rc;
503 }
504
505 async_wait_for(req, &rc);
506 if (rc != EOK)
507 return rc;
508
509 wnd_id = ipc_get_arg1(&answer);
510 rc = display_get_window(display, wnd_id, &window);
511 if (rc != EOK)
512 return EIO;
513
514 *rwindow = window;
515 return EOK;
516}
517
518/** Get display information.
519 *
520 * @param display Display
521 * @param info Place to store display information
522 * @return EOK on success or an error code
523 */
524errno_t display_get_info(display_t *display, display_info_t *info)
525{
526 async_exch_t *exch;
527 ipc_call_t answer;
528 aid_t req;
529 errno_t rc;
530
531 exch = async_exchange_begin(display->sess);
532 req = async_send_0(exch, DISPLAY_GET_INFO, &answer);
533 rc = async_data_read_start(exch, info, sizeof(*info));
534 async_exchange_end(exch);
535 if (rc != EOK) {
536 async_forget(req);
537 return rc;
538 }
539
540 async_wait_for(req, &rc);
541 if (rc != EOK)
542 return rc;
543
544 return EOK;
545}
546
547/** Display events are pending.
548 *
549 * @param display Display
550 * @param icall Call data
551 */
552static void display_ev_pending(display_t *display, ipc_call_t *icall)
553{
554 errno_t rc;
555 display_window_t *window = NULL;
556 display_wnd_ev_t event;
557
558 while (true) {
559 fibril_mutex_lock(&display->lock);
560
561 if (display->sess != NULL)
562 rc = display_get_event(display, &window, &event);
563 else
564 rc = ENOENT;
565
566 fibril_mutex_unlock(&display->lock);
567
568 if (rc != EOK)
569 break;
570
571 switch (event.etype) {
572 case wev_close:
573 if (window->cb != NULL && window->cb->close_event != NULL) {
574 window->cb->close_event(window->cb_arg);
575 }
576 break;
577 case wev_focus:
578 if (window->cb != NULL && window->cb->focus_event != NULL) {
579 window->cb->focus_event(window->cb_arg);
580 }
581 break;
582 case wev_kbd:
583 if (window->cb != NULL && window->cb->kbd_event != NULL) {
584 window->cb->kbd_event(window->cb_arg,
585 &event.ev.kbd);
586 }
587 break;
588 case wev_pos:
589 if (window->cb != NULL && window->cb->pos_event != NULL) {
590 window->cb->pos_event(window->cb_arg,
591 &event.ev.pos);
592 }
593 break;
594 case wev_resize:
595 if (window->cb != NULL && window->cb->resize_event != NULL) {
596 window->cb->resize_event(window->cb_arg,
597 &event.ev.resize.rect);
598 }
599 break;
600 case wev_unfocus:
601 if (window->cb != NULL && window->cb->unfocus_event != NULL) {
602 window->cb->unfocus_event(window->cb_arg);
603 }
604 break;
605 }
606 }
607
608 async_answer_0(icall, EOK);
609}
610
611/** Callback connection handler.
612 *
613 * @param icall Connect call data
614 * @param arg Argument, display_t *
615 */
616static void display_cb_conn(ipc_call_t *icall, void *arg)
617{
618 display_t *display = (display_t *) arg;
619
620 while (true) {
621 ipc_call_t call;
622 async_get_call(&call);
623
624 if (!ipc_get_imethod(&call)) {
625 /* Hangup */
626 async_answer_0(&call, EOK);
627 goto out;
628 }
629
630 switch (ipc_get_imethod(&call)) {
631 case DISPLAY_EV_PENDING:
632 display_ev_pending(display, &call);
633 break;
634 default:
635 async_answer_0(&call, ENOTSUP);
636 break;
637 }
638 }
639
640out:
641 fibril_mutex_lock(&display->lock);
642 display->cb_done = true;
643 fibril_mutex_unlock(&display->lock);
644 fibril_condvar_broadcast(&display->cv);
645}
646
647/** Find window by ID.
648 *
649 * @param display Display
650 * @param wnd_id Window ID
651 * @param rwindow Place to store pointer to window
652 * @return EOK on success, ENOENT if not found
653 */
654static errno_t display_get_window(display_t *display, sysarg_t wnd_id,
655 display_window_t **rwindow)
656{
657 link_t *link;
658 display_window_t *window;
659
660 link = list_first(&display->windows);
661 while (link != NULL) {
662 window = list_get_instance(link, display_window_t, lwindows);
663 if (window->id == wnd_id) {
664 *rwindow = window;
665 return EOK;
666 }
667
668 link = list_next(link, &display->windows);
669 }
670
671 return ENOENT;
672}
673
674/** @}
675 */
Note: See TracBrowser for help on using the repository browser.