source: mainline/uspace/lib/display/src/display.c@ d1582b50

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

Make display_t and display_window_t declaration opaque

  • Property mode set to 100644
File size: 15.9 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#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, IPC_FLAG_BLOCKING);
71 if (rc != EOK) {
72 free(display);
73 return ENOENT;
74 }
75
76 display->sess = loc_service_connect(display_svc, INTERFACE_DISPLAY,
77 IPC_FLAG_BLOCKING);
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/** Request a window resize.
322 *
323 * Request the display service to initiate a user window resize operation
324 * (i.e. let the user resize the window). Used when the client detects
325 * mouse press on the window frame or such.
326 *
327 * @param window Window
328 * @param rsztype Resize type (which part of window frame is being dragged)
329 * @param pos Position in the window where the button was pressed
330 * @return EOK on success or an error code
331 */
332errno_t display_window_resize_req(display_window_t *window,
333 display_wnd_rsztype_t rsztype, gfx_coord2_t *pos)
334{
335 async_exch_t *exch;
336 aid_t req;
337 ipc_call_t answer;
338 errno_t rc;
339
340 exch = async_exchange_begin(window->display->sess);
341 req = async_send_2(exch, DISPLAY_WINDOW_RESIZE_REQ, window->id,
342 (sysarg_t) rsztype, &answer);
343 rc = async_data_write_start(exch, (void *)pos, sizeof (gfx_coord2_t));
344 async_exchange_end(exch);
345 if (rc != EOK) {
346 async_forget(req);
347 return rc;
348 }
349
350 async_wait_for(req, &rc);
351 if (rc != EOK)
352 return rc;
353
354 return EOK;
355}
356
357/** Resize display window.
358 *
359 * It seems resizing windows should be easy with bounding rectangles.
360 * You have an old bounding rectangle and a new bounding rectangle (@a nrect).
361 * Change .p0 and top-left corner moves. Change .p1 and bottom-right corner
362 * moves. Piece of cake!
363 *
364 * There's always a catch, though. By series of resizes and moves .p0 could
365 * drift outside of the range of @c gfx_coord_t. Now what? @a offs to the
366 * rescue! @a offs moves the @em boundaries of the window with respect
367 * to the display, while keeping the @em contents of the window in the
368 * same place (with respect to the display). In other words, @a offs shifts
369 * the window's internal coordinate system.
370 *
371 * A few examples follow:
372 *
373 * Enlarge window by moving bottom-right corner 1 right, 1 down:
374 *
375 * bound = (0, 0, 10, 10)
376 * offs = (0, 0)
377 * nrect = (0, 0, 11, 11)
378 *
379 * Enlarge window by moving top-left corner, 1 up, 1 left, allowing the
380 * window-relative coordinate of the top-left corner to drift (undesirable)
381 *
382 * bound = (0, 0, 10, 10)
383 * offs = (0, 0)
384 * nrect = (-1, -1, 10, 10) <- this is the new bounding rectangle
385 *
386 * Enlarge window by moving top-left corner 1 up, 1 left, keeping top-left
387 * corner locked to (0,0) window-relative coordinates (desirable):
388 *
389 * bound = (0, 0, 10, 10)
390 * off = (-1,-1) <- top-left corner goes 1 up, 1 left
391 * nrect = (0, 0, 11, 11) <- window still starts at 0,0 window-relative
392 *
393 * @param window Window
394 * @param nrect New bounding rectangle
395 * @param offs
396 * @return EOK on success or an error code
397 */
398errno_t display_window_resize(display_window_t *window, gfx_coord2_t *offs,
399 gfx_rect_t *nrect)
400{
401 async_exch_t *exch;
402 aid_t req;
403 ipc_call_t answer;
404 display_wnd_resize_t wresize;
405 errno_t rc;
406
407 wresize.offs = *offs;
408 wresize.nrect = *nrect;
409
410 exch = async_exchange_begin(window->display->sess);
411 req = async_send_1(exch, DISPLAY_WINDOW_RESIZE, window->id, &answer);
412 rc = async_data_write_start(exch, &wresize, sizeof (display_wnd_resize_t));
413 async_exchange_end(exch);
414 if (rc != EOK) {
415 async_forget(req);
416 return rc;
417 }
418
419 async_wait_for(req, &rc);
420 if (rc != EOK)
421 return rc;
422
423 return EOK;
424}
425
426/** Set window cursor.
427 *
428 * Set cursor that is displayed when pointer is over the window. The default
429 * is the arrow pointer.
430 *
431 * @param window Window
432 * @param cursor Cursor to display
433 * @return EOK on success or an error code
434 */
435errno_t display_window_set_cursor(display_window_t *window,
436 display_stock_cursor_t cursor)
437{
438 async_exch_t *exch;
439 errno_t rc;
440
441 exch = async_exchange_begin(window->display->sess);
442 rc = async_req_2_0(exch, DISPLAY_WINDOW_SET_CURSOR, window->id,
443 cursor);
444 async_exchange_end(exch);
445 return rc;
446}
447
448/** Get display event.
449 *
450 * @param display Display
451 * @param rwindow Place to store pointer to window that received event
452 * @param event Place to store event
453 * @return EOK on success or an error code
454 */
455static errno_t display_get_event(display_t *display, display_window_t **rwindow,
456 display_wnd_ev_t *event)
457{
458 async_exch_t *exch;
459 ipc_call_t answer;
460 aid_t req;
461 errno_t rc;
462 sysarg_t wnd_id;
463 display_window_t *window;
464
465 exch = async_exchange_begin(display->sess);
466 req = async_send_0(exch, DISPLAY_GET_EVENT, &answer);
467 rc = async_data_read_start(exch, event, sizeof(*event));
468 async_exchange_end(exch);
469 if (rc != EOK) {
470 async_forget(req);
471 return rc;
472 }
473
474 async_wait_for(req, &rc);
475 if (rc != EOK)
476 return rc;
477
478 wnd_id = ipc_get_arg1(&answer);
479 rc = display_get_window(display, wnd_id, &window);
480 if (rc != EOK)
481 return EIO;
482
483 *rwindow = window;
484 return EOK;
485}
486
487/** Get display information.
488 *
489 * @param display Display
490 * @param info Place to store display information
491 * @return EOK on success or an error code
492 */
493errno_t display_get_info(display_t *display, display_info_t *info)
494{
495 async_exch_t *exch;
496 ipc_call_t answer;
497 aid_t req;
498 errno_t rc;
499
500 exch = async_exchange_begin(display->sess);
501 req = async_send_0(exch, DISPLAY_GET_INFO, &answer);
502 rc = async_data_read_start(exch, info, sizeof(*info));
503 async_exchange_end(exch);
504 if (rc != EOK) {
505 async_forget(req);
506 return rc;
507 }
508
509 async_wait_for(req, &rc);
510 if (rc != EOK)
511 return rc;
512
513 return EOK;
514}
515
516/** Display events are pending.
517 *
518 * @param display Display
519 * @param icall Call data
520 */
521static void display_ev_pending(display_t *display, ipc_call_t *icall)
522{
523 errno_t rc;
524 display_window_t *window = NULL;
525 display_wnd_ev_t event;
526
527 while (true) {
528 fibril_mutex_lock(&display->lock);
529
530 if (display->sess != NULL)
531 rc = display_get_event(display, &window, &event);
532 else
533 rc = ENOENT;
534
535 fibril_mutex_unlock(&display->lock);
536
537 if (rc != EOK)
538 break;
539
540 switch (event.etype) {
541 case wev_close:
542 if (window->cb != NULL && window->cb->close_event != NULL) {
543 window->cb->close_event(window->cb_arg);
544 }
545 break;
546 case wev_focus:
547 if (window->cb != NULL && window->cb->focus_event != NULL) {
548 window->cb->focus_event(window->cb_arg);
549 }
550 break;
551 case wev_kbd:
552 if (window->cb != NULL && window->cb->kbd_event != NULL) {
553 window->cb->kbd_event(window->cb_arg,
554 &event.ev.kbd);
555 }
556 break;
557 case wev_pos:
558 if (window->cb != NULL && window->cb->pos_event != NULL) {
559 window->cb->pos_event(window->cb_arg,
560 &event.ev.pos);
561 }
562 break;
563 case wev_resize:
564 if (window->cb != NULL && window->cb->resize_event != NULL) {
565 window->cb->resize_event(window->cb_arg,
566 &event.ev.resize.rect);
567 }
568 break;
569 case wev_unfocus:
570 if (window->cb != NULL && window->cb->unfocus_event != NULL) {
571 window->cb->unfocus_event(window->cb_arg);
572 }
573 break;
574 }
575 }
576
577 async_answer_0(icall, EOK);
578}
579
580/** Callback connection handler.
581 *
582 * @param icall Connect call data
583 * @param arg Argument, display_t *
584 */
585static void display_cb_conn(ipc_call_t *icall, void *arg)
586{
587 display_t *display = (display_t *) arg;
588
589 while (true) {
590 ipc_call_t call;
591 async_get_call(&call);
592
593 if (!ipc_get_imethod(&call)) {
594 /* Hangup */
595 async_answer_0(&call, EOK);
596 goto out;
597 }
598
599 switch (ipc_get_imethod(&call)) {
600 case DISPLAY_EV_PENDING:
601 display_ev_pending(display, &call);
602 break;
603 default:
604 async_answer_0(&call, ENOTSUP);
605 break;
606 }
607 }
608
609out:
610 fibril_mutex_lock(&display->lock);
611 display->cb_done = true;
612 fibril_mutex_unlock(&display->lock);
613 fibril_condvar_broadcast(&display->cv);
614}
615
616/** Find window by ID.
617 *
618 * @param display Display
619 * @param wnd_id Window ID
620 * @param rwindow Place to store pointer to window
621 * @return EOK on success, ENOENT if not found
622 */
623static errno_t display_get_window(display_t *display, sysarg_t wnd_id,
624 display_window_t **rwindow)
625{
626 link_t *link;
627 display_window_t *window;
628
629 link = list_first(&display->windows);
630 while (link != NULL) {
631 window = list_get_instance(link, display_window_t, lwindows);
632 if (window->id == wnd_id) {
633 *rwindow = window;
634 return EOK;
635 }
636
637 link = list_next(link, &display->windows);
638 }
639
640 return ENOENT;
641}
642
643/** @}
644 */
Note: See TracBrowser for help on using the repository browser.