source: mainline/uspace/lib/display/src/display.c@ 9242ad9

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

Add libdisplay method for setting window cursor

  • Property mode set to 100644
File size: 15.8 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 return rc;
179 }
180
181 async_wait_for(req, &rc);
182 if (rc != EOK)
183 return rc;
184
185 window->display = display;
186 window->id = ipc_get_arg1(&answer);
187 window->cb = cb;
188 window->cb_arg = cb_arg;
189
190 list_append(&window->lwindows, &display->windows);
191 *rwindow = window;
192 return EOK;
193}
194
195/** Destroy display window.
196 *
197 * @param window Window
198 * @return EOK on success or an error code. In both cases @a window must
199 * not be accessed anymore
200 */
201errno_t display_window_destroy(display_window_t *window)
202{
203 async_exch_t *exch;
204 errno_t rc;
205
206 exch = async_exchange_begin(window->display->sess);
207 rc = async_req_1_0(exch, DISPLAY_WINDOW_DESTROY, window->id);
208
209 async_exchange_end(exch);
210
211 list_remove(&window->lwindows);
212 free(window);
213 return rc;
214}
215
216/** Create graphics context for drawing into a window.
217 *
218 * @param window Window
219 * @param rgc Place to store pointer to new graphics context
220 */
221errno_t display_window_get_gc(display_window_t *window, gfx_context_t **rgc)
222{
223 async_sess_t *sess;
224 async_exch_t *exch;
225 ipc_gc_t *gc;
226 errno_t rc;
227
228 exch = async_exchange_begin(window->display->sess);
229 sess = async_connect_me_to(exch, INTERFACE_GC, 0, window->id);
230 if (sess == NULL) {
231 async_exchange_end(exch);
232 return EIO;
233 }
234
235 async_exchange_end(exch);
236
237 rc = ipc_gc_create(sess, &gc);
238 if (rc != EOK) {
239 async_hangup(sess);
240 return ENOMEM;
241 }
242
243 *rgc = ipc_gc_get_ctx(gc);
244 return EOK;
245}
246
247/** Request a window move.
248 *
249 * Request the display service to initiate a user window move operation
250 * (i.e. let the user move the window). Used when the client detects
251 * mouse press on the title bar or such.
252 *
253 * @param window Window
254 * @param pos Position in the window where the button was pressed
255 * @return EOK on success or an error code
256 */
257errno_t display_window_move_req(display_window_t *window, gfx_coord2_t *pos)
258{
259 async_exch_t *exch;
260 aid_t req;
261 ipc_call_t answer;
262 errno_t rc;
263
264 exch = async_exchange_begin(window->display->sess);
265 req = async_send_1(exch, DISPLAY_WINDOW_MOVE_REQ, window->id, &answer);
266 rc = async_data_write_start(exch, (void *)pos, sizeof (gfx_coord2_t));
267 async_exchange_end(exch);
268 if (rc != EOK) {
269 async_forget(req);
270 return rc;
271 }
272
273 async_wait_for(req, &rc);
274 if (rc != EOK)
275 return rc;
276
277 return EOK;
278}
279
280/** Move display window.
281 *
282 * Set new display position of a window. Display position determines where
283 * the origin of the window coordinate system lies. Note that the top left
284 * corner of the window need not coincide with the window's 0,0 point.
285 *
286 * @param window Window
287 * @param dpos New display position
288 * @return EOK on success or an error code
289 */
290errno_t display_window_move(display_window_t *window, gfx_coord2_t *dpos)
291{
292 async_exch_t *exch;
293 aid_t req;
294 ipc_call_t answer;
295 errno_t rc;
296
297 exch = async_exchange_begin(window->display->sess);
298 req = async_send_1(exch, DISPLAY_WINDOW_MOVE, window->id, &answer);
299 rc = async_data_write_start(exch, dpos, sizeof (gfx_coord2_t));
300 async_exchange_end(exch);
301 if (rc != EOK) {
302 async_forget(req);
303 return rc;
304 }
305
306 async_wait_for(req, &rc);
307 if (rc != EOK)
308 return rc;
309
310 return EOK;
311}
312
313/** Request a window resize.
314 *
315 * Request the display service to initiate a user window resize operation
316 * (i.e. let the user resize the window). Used when the client detects
317 * mouse press on the window frame or such.
318 *
319 * @param window Window
320 * @param rsztype Resize type (which part of window frame is being dragged)
321 * @param pos Position in the window where the button was pressed
322 * @return EOK on success or an error code
323 */
324errno_t display_window_resize_req(display_window_t *window,
325 display_wnd_rsztype_t rsztype, gfx_coord2_t *pos)
326{
327 async_exch_t *exch;
328 aid_t req;
329 ipc_call_t answer;
330 errno_t rc;
331
332 exch = async_exchange_begin(window->display->sess);
333 req = async_send_2(exch, DISPLAY_WINDOW_RESIZE_REQ, window->id,
334 (sysarg_t) rsztype, &answer);
335 rc = async_data_write_start(exch, (void *)pos, sizeof (gfx_coord2_t));
336 async_exchange_end(exch);
337 if (rc != EOK) {
338 async_forget(req);
339 return rc;
340 }
341
342 async_wait_for(req, &rc);
343 if (rc != EOK)
344 return rc;
345
346 return EOK;
347}
348
349/** Resize display window.
350 *
351 * It seems resizing windows should be easy with bounding rectangles.
352 * You have an old bounding rectangle and a new bounding rectangle (@a nrect).
353 * Change .p0 and top-left corner moves. Change .p1 and bottom-right corner
354 * moves. Piece of cake!
355 *
356 * There's always a catch, though. By series of resizes and moves .p0 could
357 * drift outside of the range of @c gfx_coord_t. Now what? @a offs to the
358 * rescue! @a offs moves the @em boundaries of the window with respect
359 * to the display, while keeping the @em contents of the window in the
360 * same place (with respect to the display). In other words, @a offs shifts
361 * the window's internal coordinate system.
362 *
363 * A few examples follow:
364 *
365 * Enlarge window by moving bottom-right corner 1 right, 1 down:
366 *
367 * bound = (0, 0, 10, 10)
368 * offs = (0, 0)
369 * nrect = (0, 0, 11, 11)
370 *
371 * Enlarge window by moving top-left corner, 1 up, 1 left, allowing the
372 * window-relative coordinate of the top-left corner to drift (undesirable)
373 *
374 * bound = (0, 0, 10, 10)
375 * offs = (0, 0)
376 * nrect = (-1, -1, 10, 10) <- this is the new bounding rectangle
377 *
378 * Enlarge window by moving top-left corner 1 up, 1 left, keeping top-left
379 * corner locked to (0,0) window-relative coordinates (desirable):
380 *
381 * bound = (0, 0, 10, 10)
382 * off = (-1,-1) <- top-left corner goes 1 up, 1 left
383 * nrect = (0, 0, 11, 11) <- window still starts at 0,0 window-relative
384 *
385 * @param window Window
386 * @param nrect New bounding rectangle
387 * @param offs
388 * @return EOK on success or an error code
389 */
390errno_t display_window_resize(display_window_t *window, gfx_coord2_t *offs,
391 gfx_rect_t *nrect)
392{
393 async_exch_t *exch;
394 aid_t req;
395 ipc_call_t answer;
396 display_wnd_resize_t wresize;
397 errno_t rc;
398
399 wresize.offs = *offs;
400 wresize.nrect = *nrect;
401
402 exch = async_exchange_begin(window->display->sess);
403 req = async_send_1(exch, DISPLAY_WINDOW_RESIZE, window->id, &answer);
404 rc = async_data_write_start(exch, &wresize, sizeof (display_wnd_resize_t));
405 async_exchange_end(exch);
406 if (rc != EOK) {
407 async_forget(req);
408 return rc;
409 }
410
411 async_wait_for(req, &rc);
412 if (rc != EOK)
413 return rc;
414
415 return EOK;
416}
417
418/** Set window cursor.
419 *
420 * Set cursor that is displayed when pointer is over the window. The default
421 * is the arrow pointer.
422 *
423 * @param window Window
424 * @param cursor Cursor to display
425 * @return EOK on success or an error code
426 */
427errno_t display_window_set_cursor(display_window_t *window,
428 display_stock_cursor_t cursor)
429{
430 async_exch_t *exch;
431 errno_t rc;
432
433 exch = async_exchange_begin(window->display->sess);
434 rc = async_req_2_0(exch, DISPLAY_WINDOW_SET_CURSOR, window->id,
435 cursor);
436 async_exchange_end(exch);
437 return rc;
438}
439
440/** Get display event.
441 *
442 * @param display Display
443 * @param rwindow Place to store pointer to window that received event
444 * @param event Place to store event
445 * @return EOK on success or an error code
446 */
447static errno_t display_get_event(display_t *display, display_window_t **rwindow,
448 display_wnd_ev_t *event)
449{
450 async_exch_t *exch;
451 ipc_call_t answer;
452 aid_t req;
453 errno_t rc;
454 sysarg_t wnd_id;
455 display_window_t *window;
456
457 exch = async_exchange_begin(display->sess);
458 req = async_send_0(exch, DISPLAY_GET_EVENT, &answer);
459 rc = async_data_read_start(exch, event, sizeof(*event));
460 async_exchange_end(exch);
461 if (rc != EOK) {
462 async_forget(req);
463 return rc;
464 }
465
466 async_wait_for(req, &rc);
467 if (rc != EOK)
468 return rc;
469
470 wnd_id = ipc_get_arg1(&answer);
471 rc = display_get_window(display, wnd_id, &window);
472 if (rc != EOK)
473 return EIO;
474
475 *rwindow = window;
476 return EOK;
477}
478
479/** Get display information.
480 *
481 * @param display Display
482 * @param info Place to store display information
483 * @return EOK on success or an error code
484 */
485errno_t display_get_info(display_t *display, display_info_t *info)
486{
487 async_exch_t *exch;
488 ipc_call_t answer;
489 aid_t req;
490 errno_t rc;
491
492 exch = async_exchange_begin(display->sess);
493 req = async_send_0(exch, DISPLAY_GET_INFO, &answer);
494 rc = async_data_read_start(exch, info, sizeof(*info));
495 async_exchange_end(exch);
496 if (rc != EOK) {
497 async_forget(req);
498 return rc;
499 }
500
501 async_wait_for(req, &rc);
502 if (rc != EOK)
503 return rc;
504
505 return EOK;
506}
507
508/** Display events are pending.
509 *
510 * @param display Display
511 * @param icall Call data
512 */
513static void display_ev_pending(display_t *display, ipc_call_t *icall)
514{
515 errno_t rc;
516 display_window_t *window = NULL;
517 display_wnd_ev_t event;
518
519 while (true) {
520 fibril_mutex_lock(&display->lock);
521
522 if (display->sess != NULL)
523 rc = display_get_event(display, &window, &event);
524 else
525 rc = ENOENT;
526
527 fibril_mutex_unlock(&display->lock);
528
529 if (rc != EOK)
530 break;
531
532 switch (event.etype) {
533 case wev_close:
534 if (window->cb != NULL && window->cb->close_event != NULL) {
535 window->cb->close_event(window->cb_arg);
536 }
537 break;
538 case wev_focus:
539 if (window->cb != NULL && window->cb->focus_event != NULL) {
540 window->cb->focus_event(window->cb_arg);
541 }
542 break;
543 case wev_kbd:
544 if (window->cb != NULL && window->cb->kbd_event != NULL) {
545 window->cb->kbd_event(window->cb_arg,
546 &event.ev.kbd);
547 }
548 break;
549 case wev_pos:
550 if (window->cb != NULL && window->cb->pos_event != NULL) {
551 window->cb->pos_event(window->cb_arg,
552 &event.ev.pos);
553 }
554 break;
555 case wev_resize:
556 if (window->cb != NULL && window->cb->resize_event != NULL) {
557 window->cb->resize_event(window->cb_arg,
558 &event.ev.resize.rect);
559 }
560 break;
561 case wev_unfocus:
562 if (window->cb != NULL && window->cb->unfocus_event != NULL) {
563 window->cb->unfocus_event(window->cb_arg);
564 }
565 break;
566 }
567 }
568
569 async_answer_0(icall, EOK);
570}
571
572/** Callback connection handler.
573 *
574 * @param icall Connect call data
575 * @param arg Argument, display_t *
576 */
577static void display_cb_conn(ipc_call_t *icall, void *arg)
578{
579 display_t *display = (display_t *) arg;
580
581 while (true) {
582 ipc_call_t call;
583 async_get_call(&call);
584
585 if (!ipc_get_imethod(&call)) {
586 /* Hangup */
587 async_answer_0(&call, EOK);
588 goto out;
589 }
590
591 switch (ipc_get_imethod(&call)) {
592 case DISPLAY_EV_PENDING:
593 display_ev_pending(display, &call);
594 break;
595 default:
596 async_answer_0(&call, ENOTSUP);
597 break;
598 }
599 }
600
601out:
602 fibril_mutex_lock(&display->lock);
603 display->cb_done = true;
604 fibril_mutex_unlock(&display->lock);
605 fibril_condvar_broadcast(&display->cv);
606}
607
608/** Find window by ID.
609 *
610 * @param display Display
611 * @param wnd_id Window ID
612 * @param rwindow Place to store pointer to window
613 * @return EOK on success, ENOENT if not found
614 */
615static errno_t display_get_window(display_t *display, sysarg_t wnd_id,
616 display_window_t **rwindow)
617{
618 link_t *link;
619 display_window_t *window;
620
621 link = list_first(&display->windows);
622 while (link != NULL) {
623 window = list_get_instance(link, display_window_t, lwindows);
624 if (window->id == wnd_id) {
625 *rwindow = window;
626 return EOK;
627 }
628
629 link = list_next(link, &display->windows);
630 }
631
632 return ENOENT;
633}
634
635/** @}
636 */
Note: See TracBrowser for help on using the repository browser.