source: mainline/uspace/lib/display/src/display.c@ 35cffea

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

Maximizing/unmaximizing a window

  • Property mode set to 100644
File size: 18.0 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 "../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/** Get display window maximized rectangle.
353 *
354 * Get the rectangle to which a window would be maximized.
355 *
356 * @param window Window
357 * @param rect Place to store maximized rectangle
358 * @return EOK on success or an error code
359 */
360errno_t display_window_get_max_rect(display_window_t *window, gfx_rect_t *rect)
361{
362 async_exch_t *exch;
363 aid_t req;
364 ipc_call_t answer;
365 errno_t rc;
366
367 exch = async_exchange_begin(window->display->sess);
368 req = async_send_1(exch, DISPLAY_WINDOW_GET_MAX_RECT, window->id,
369 &answer);
370 rc = async_data_read_start(exch, rect, sizeof (gfx_rect_t));
371 async_exchange_end(exch);
372 if (rc != EOK) {
373 async_forget(req);
374 return rc;
375 }
376
377 async_wait_for(req, &rc);
378 if (rc != EOK)
379 return rc;
380
381 return EOK;
382}
383
384/** Request a window resize.
385 *
386 * Request the display service to initiate a user window resize operation
387 * (i.e. let the user resize the window). Used when the client detects
388 * mouse press on the window frame or such.
389 *
390 * @param window Window
391 * @param rsztype Resize type (which part of window frame is being dragged)
392 * @param pos Position in the window where the button was pressed
393 * @return EOK on success or an error code
394 */
395errno_t display_window_resize_req(display_window_t *window,
396 display_wnd_rsztype_t rsztype, gfx_coord2_t *pos)
397{
398 async_exch_t *exch;
399 aid_t req;
400 ipc_call_t answer;
401 errno_t rc;
402
403 exch = async_exchange_begin(window->display->sess);
404 req = async_send_2(exch, DISPLAY_WINDOW_RESIZE_REQ, window->id,
405 (sysarg_t) rsztype, &answer);
406 rc = async_data_write_start(exch, (void *)pos, sizeof (gfx_coord2_t));
407 async_exchange_end(exch);
408 if (rc != EOK) {
409 async_forget(req);
410 return rc;
411 }
412
413 async_wait_for(req, &rc);
414 if (rc != EOK)
415 return rc;
416
417 return EOK;
418}
419
420/** Resize display window.
421 *
422 * It seems resizing windows should be easy with bounding rectangles.
423 * You have an old bounding rectangle and a new bounding rectangle (@a nrect).
424 * Change .p0 and top-left corner moves. Change .p1 and bottom-right corner
425 * moves. Piece of cake!
426 *
427 * There's always a catch, though. By series of resizes and moves .p0 could
428 * drift outside of the range of @c gfx_coord_t. Now what? @a offs to the
429 * rescue! @a offs moves the @em boundaries of the window with respect
430 * to the display, while keeping the @em contents of the window in the
431 * same place (with respect to the display). In other words, @a offs shifts
432 * the window's internal coordinate system.
433 *
434 * A few examples follow:
435 *
436 * Enlarge window by moving bottom-right corner 1 right, 1 down:
437 *
438 * bound = (0, 0, 10, 10)
439 * offs = (0, 0)
440 * nrect = (0, 0, 11, 11)
441 *
442 * Enlarge window by moving top-left corner, 1 up, 1 left, allowing the
443 * window-relative coordinate of the top-left corner to drift (undesirable)
444 *
445 * bound = (0, 0, 10, 10)
446 * offs = (0, 0)
447 * nrect = (-1, -1, 10, 10) <- this is the new bounding rectangle
448 *
449 * Enlarge window by moving top-left corner 1 up, 1 left, keeping top-left
450 * corner locked to (0,0) window-relative coordinates (desirable):
451 *
452 * bound = (0, 0, 10, 10)
453 * off = (-1,-1) <- top-left corner goes 1 up, 1 left
454 * nrect = (0, 0, 11, 11) <- window still starts at 0,0 window-relative
455 *
456 * @param window Window
457 * @param nrect New bounding rectangle
458 * @param offs
459 * @return EOK on success or an error code
460 */
461errno_t display_window_resize(display_window_t *window, gfx_coord2_t *offs,
462 gfx_rect_t *nrect)
463{
464 async_exch_t *exch;
465 aid_t req;
466 ipc_call_t answer;
467 display_wnd_resize_t wresize;
468 errno_t rc;
469
470 wresize.offs = *offs;
471 wresize.nrect = *nrect;
472
473 exch = async_exchange_begin(window->display->sess);
474 req = async_send_1(exch, DISPLAY_WINDOW_RESIZE, window->id, &answer);
475 rc = async_data_write_start(exch, &wresize, sizeof (display_wnd_resize_t));
476 async_exchange_end(exch);
477 if (rc != EOK) {
478 async_forget(req);
479 return rc;
480 }
481
482 async_wait_for(req, &rc);
483 if (rc != EOK)
484 return rc;
485
486 return EOK;
487}
488
489/** Maximize window.
490 *
491 * @param window Window
492 * @return EOK on success or an error code
493 */
494errno_t display_window_maximize(display_window_t *window)
495{
496 async_exch_t *exch;
497 errno_t rc;
498
499 exch = async_exchange_begin(window->display->sess);
500 rc = async_req_1_0(exch, DISPLAY_WINDOW_MAXIMIZE, window->id);
501 async_exchange_end(exch);
502
503 return rc;
504}
505
506/** Unmaximize window.
507 *
508 * @param window Window
509 * @return EOK on success or an error code
510 */
511errno_t display_window_unmaximize(display_window_t *window)
512{
513 async_exch_t *exch;
514 errno_t rc;
515
516 exch = async_exchange_begin(window->display->sess);
517 rc = async_req_1_0(exch, DISPLAY_WINDOW_UNMAXIMIZE, window->id);
518 async_exchange_end(exch);
519
520 return rc;
521}
522
523/** Set window cursor.
524 *
525 * Set cursor that is displayed when pointer is over the window. The default
526 * is the arrow pointer.
527 *
528 * @param window Window
529 * @param cursor Cursor to display
530 * @return EOK on success or an error code
531 */
532errno_t display_window_set_cursor(display_window_t *window,
533 display_stock_cursor_t cursor)
534{
535 async_exch_t *exch;
536 errno_t rc;
537
538 exch = async_exchange_begin(window->display->sess);
539 rc = async_req_2_0(exch, DISPLAY_WINDOW_SET_CURSOR, window->id,
540 cursor);
541 async_exchange_end(exch);
542 return rc;
543}
544
545/** Get display event.
546 *
547 * @param display Display
548 * @param rwindow Place to store pointer to window that received event
549 * @param event Place to store event
550 * @return EOK on success or an error code
551 */
552static errno_t display_get_event(display_t *display, display_window_t **rwindow,
553 display_wnd_ev_t *event)
554{
555 async_exch_t *exch;
556 ipc_call_t answer;
557 aid_t req;
558 errno_t rc;
559 sysarg_t wnd_id;
560 display_window_t *window;
561
562 exch = async_exchange_begin(display->sess);
563 req = async_send_0(exch, DISPLAY_GET_EVENT, &answer);
564 rc = async_data_read_start(exch, event, sizeof(*event));
565 async_exchange_end(exch);
566 if (rc != EOK) {
567 async_forget(req);
568 return rc;
569 }
570
571 async_wait_for(req, &rc);
572 if (rc != EOK)
573 return rc;
574
575 wnd_id = ipc_get_arg1(&answer);
576 rc = display_get_window(display, wnd_id, &window);
577 if (rc != EOK)
578 return EIO;
579
580 *rwindow = window;
581 return EOK;
582}
583
584/** Get display information.
585 *
586 * @param display Display
587 * @param info Place to store display information
588 * @return EOK on success or an error code
589 */
590errno_t display_get_info(display_t *display, display_info_t *info)
591{
592 async_exch_t *exch;
593 ipc_call_t answer;
594 aid_t req;
595 errno_t rc;
596
597 exch = async_exchange_begin(display->sess);
598 req = async_send_0(exch, DISPLAY_GET_INFO, &answer);
599 rc = async_data_read_start(exch, info, sizeof(*info));
600 async_exchange_end(exch);
601 if (rc != EOK) {
602 async_forget(req);
603 return rc;
604 }
605
606 async_wait_for(req, &rc);
607 if (rc != EOK)
608 return rc;
609
610 return EOK;
611}
612
613/** Display events are pending.
614 *
615 * @param display Display
616 * @param icall Call data
617 */
618static void display_ev_pending(display_t *display, ipc_call_t *icall)
619{
620 errno_t rc;
621 display_window_t *window = NULL;
622 display_wnd_ev_t event;
623
624 while (true) {
625 fibril_mutex_lock(&display->lock);
626
627 if (display->sess != NULL)
628 rc = display_get_event(display, &window, &event);
629 else
630 rc = ENOENT;
631
632 fibril_mutex_unlock(&display->lock);
633
634 if (rc != EOK)
635 break;
636
637 switch (event.etype) {
638 case wev_close:
639 if (window->cb != NULL && window->cb->close_event != NULL) {
640 window->cb->close_event(window->cb_arg);
641 }
642 break;
643 case wev_focus:
644 if (window->cb != NULL && window->cb->focus_event != NULL) {
645 window->cb->focus_event(window->cb_arg);
646 }
647 break;
648 case wev_kbd:
649 if (window->cb != NULL && window->cb->kbd_event != NULL) {
650 window->cb->kbd_event(window->cb_arg,
651 &event.ev.kbd);
652 }
653 break;
654 case wev_pos:
655 if (window->cb != NULL && window->cb->pos_event != NULL) {
656 window->cb->pos_event(window->cb_arg,
657 &event.ev.pos);
658 }
659 break;
660 case wev_resize:
661 if (window->cb != NULL && window->cb->resize_event != NULL) {
662 window->cb->resize_event(window->cb_arg,
663 &event.ev.resize.rect);
664 }
665 break;
666 case wev_unfocus:
667 if (window->cb != NULL && window->cb->unfocus_event != NULL) {
668 window->cb->unfocus_event(window->cb_arg);
669 }
670 break;
671 }
672 }
673
674 async_answer_0(icall, EOK);
675}
676
677/** Callback connection handler.
678 *
679 * @param icall Connect call data
680 * @param arg Argument, display_t *
681 */
682static void display_cb_conn(ipc_call_t *icall, void *arg)
683{
684 display_t *display = (display_t *) arg;
685
686 while (true) {
687 ipc_call_t call;
688 async_get_call(&call);
689
690 if (!ipc_get_imethod(&call)) {
691 /* Hangup */
692 async_answer_0(&call, EOK);
693 goto out;
694 }
695
696 switch (ipc_get_imethod(&call)) {
697 case DISPLAY_EV_PENDING:
698 display_ev_pending(display, &call);
699 break;
700 default:
701 async_answer_0(&call, ENOTSUP);
702 break;
703 }
704 }
705
706out:
707 fibril_mutex_lock(&display->lock);
708 display->cb_done = true;
709 fibril_mutex_unlock(&display->lock);
710 fibril_condvar_broadcast(&display->cv);
711}
712
713/** Find window by ID.
714 *
715 * @param display Display
716 * @param wnd_id Window ID
717 * @param rwindow Place to store pointer to window
718 * @return EOK on success, ENOENT if not found
719 */
720static errno_t display_get_window(display_t *display, sysarg_t wnd_id,
721 display_window_t **rwindow)
722{
723 link_t *link;
724 display_window_t *window;
725
726 link = list_first(&display->windows);
727 while (link != NULL) {
728 window = list_get_instance(link, display_window_t, lwindows);
729 if (window->id == wnd_id) {
730 *rwindow = window;
731 return EOK;
732 }
733
734 link = list_next(link, &display->windows);
735 }
736
737 return ENOENT;
738}
739
740/** @}
741 */
Note: See TracBrowser for help on using the repository browser.