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

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

Prototype UI and window classes

It is difficult to do any useful testing on ui_window without a real
display_t. Without display, there is no window, without window no GC,
without GC no UI resource.

As a compromise, in case of unit test, create UI with display == NULL,
in which case create window with NULL dwindow and a dummy GC
(so we have valid UI resource).

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