source: mainline/uspace/lib/display/src/display.c@ 7cc30e9

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

Display server needs to store window caption

Even though it does not use it itself, it needs to provide it to
window managers (e.g. Task bar). We need to be able to set caption
for a new window and to change it for an existing window.

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