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

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

Moving window by client request, emulate window placement flags

  • Property mode set to 100644
File size: 15.1 KB
RevLine 
[c8cf261]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>
[959b7ec]31#include <display/event.h>
[c8cf261]32#include <errno.h>
[b3c185b6]33#include <fibril_synch.h>
[c8cf261]34#include <ipc/display.h>
35#include <ipc/services.h>
36#include <ipcgfx/client.h>
37#include <loc.h>
[4d9c807]38#include <mem.h>
[c8cf261]39#include <stdlib.h>
[0e6e77f]40#include "../private/params.h"
[c8cf261]41
[b3c185b6]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
[c8cf261]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
[959b7ec]62 fibril_mutex_initialize(&display->lock);
63 fibril_condvar_initialize(&display->cv);
[b3c185b6]64 list_initialize(&display->windows);
65
[c8cf261]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
[b3c185b6]82 rc = display_callback_create(display);
83 if (rc != EOK) {
84 async_hangup(display->sess);
85 free(display);
86 return EIO;
87 }
88
[c8cf261]89 *rdisplay = display;
90 return EOK;
91}
92
[b3c185b6]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
[c8cf261]119/** Close display service.
120 *
121 * @param display Display session
122 */
123void display_close(display_t *display)
124{
125 async_hangup(display->sess);
[b3c185b6]126
127 /* Wait for callback handler to terminate */
128
129 fibril_mutex_lock(&display->lock);
130 while (!display->cb_done)
131 fibril_condvar_wait(&display->cv, &display->lock);
132 fibril_mutex_unlock(&display->lock);
133
[c8cf261]134 free(display);
135}
136
[4d9c807]137/** Initialize window parameters structure.
[a8eed5f]138 *
139 * Window parameters structure must always be initialized using this function
140 * first.
[4d9c807]141 *
142 * @param params Window parameters structure
143 */
144void display_wnd_params_init(display_wnd_params_t *params)
145{
146 memset(params, 0, sizeof(*params));
147}
148
[c8cf261]149/** Create a display window.
150 *
151 * @param display Display
[4d9c807]152 * @param params Window parameters
[b3c185b6]153 * @param cb Callback functions
154 * @param cb_arg Argument to callback functions
[c8cf261]155 * @param rwindow Place to store pointer to new window
156 * @return EOK on success or an error code
157 */
[4d9c807]158errno_t display_window_create(display_t *display, display_wnd_params_t *params,
159 display_wnd_cb_t *cb, void *cb_arg, display_window_t **rwindow)
[c8cf261]160{
161 display_window_t *window;
162 async_exch_t *exch;
[4d9c807]163 aid_t req;
164 ipc_call_t answer;
[c8cf261]165 errno_t rc;
166
167 window = calloc(1, sizeof(display_window_t));
168 if (window == NULL)
169 return ENOMEM;
170
171 exch = async_exchange_begin(display->sess);
[4d9c807]172 req = async_send_0(exch, DISPLAY_WINDOW_CREATE, &answer);
173 rc = async_data_write_start(exch, params, sizeof (display_wnd_params_t));
[c8cf261]174 async_exchange_end(exch);
175 if (rc != EOK) {
[4d9c807]176 async_forget(req);
[c8cf261]177 return rc;
178 }
179
[4d9c807]180 async_wait_for(req, &rc);
181 if (rc != EOK)
182 return rc;
183
[c8cf261]184 window->display = display;
[4d9c807]185 window->id = ipc_get_arg1(&answer);
[b3c185b6]186 window->cb = cb;
187 window->cb_arg = cb_arg;
188
189 list_append(&window->lwindows, &display->windows);
[c8cf261]190 *rwindow = window;
191 return EOK;
192}
193
194/** Destroy display window.
195 *
196 * @param window Window
197 * @return EOK on success or an error code. In both cases @a window must
198 * not be accessed anymore
199 */
200errno_t display_window_destroy(display_window_t *window)
201{
202 async_exch_t *exch;
203 errno_t rc;
204
205 exch = async_exchange_begin(window->display->sess);
206 rc = async_req_1_0(exch, DISPLAY_WINDOW_DESTROY, window->id);
207
208 async_exchange_end(exch);
209
[dcac756]210 list_remove(&window->lwindows);
[c8cf261]211 free(window);
212 return rc;
213}
214
215/** Create graphics context for drawing into a window.
216 *
217 * @param window Window
218 * @param rgc Place to store pointer to new graphics context
219 */
220errno_t display_window_get_gc(display_window_t *window, gfx_context_t **rgc)
221{
222 async_sess_t *sess;
223 async_exch_t *exch;
224 ipc_gc_t *gc;
225 errno_t rc;
226
227 exch = async_exchange_begin(window->display->sess);
228 sess = async_connect_me_to(exch, INTERFACE_GC, 0, window->id);
229 if (sess == NULL) {
230 async_exchange_end(exch);
231 return EIO;
232 }
233
234 async_exchange_end(exch);
235
236 rc = ipc_gc_create(sess, &gc);
237 if (rc != EOK) {
238 async_hangup(sess);
239 return ENOMEM;
240 }
241
242 *rgc = ipc_gc_get_ctx(gc);
243 return EOK;
244}
245
[a2e104e]246/** Request a window move.
247 *
248 * Request the display service to initiate a user window move operation
249 * (i.e. let the user move the window). Used when the client detects
250 * mouse press on the title bar or such.
251 *
252 * @param window Window
[1e4a937]253 * @param pos Position in the window where the button was pressed
[a2e104e]254 * @return EOK on success or an error code
255 */
256errno_t display_window_move_req(display_window_t *window, gfx_coord2_t *pos)
257{
258 async_exch_t *exch;
259 aid_t req;
260 ipc_call_t answer;
261 errno_t rc;
262
263 exch = async_exchange_begin(window->display->sess);
264 req = async_send_1(exch, DISPLAY_WINDOW_MOVE_REQ, window->id, &answer);
265 rc = async_data_write_start(exch, (void *)pos, sizeof (gfx_coord2_t));
266 async_exchange_end(exch);
267 if (rc != EOK) {
268 async_forget(req);
269 return rc;
270 }
271
272 async_wait_for(req, &rc);
273 if (rc != EOK)
274 return rc;
275
276 return EOK;
277}
278
[0680854]279/** Move display window.
280 *
281 * Set new display position of a window. Display position determines where
282 * the origin of the window coordinate system lies. Note that the top left
283 * corner of the window need not coincide with the window's 0,0 point.
284 *
285 * @param window Window
286 * @param dpos New display position
287 * @return EOK on success or an error code
288 */
289errno_t display_window_move(display_window_t *window, gfx_coord2_t *dpos)
290{
291 async_exch_t *exch;
292 aid_t req;
293 ipc_call_t answer;
294 errno_t rc;
295
296 exch = async_exchange_begin(window->display->sess);
297 req = async_send_1(exch, DISPLAY_WINDOW_MOVE, window->id, &answer);
298 rc = async_data_write_start(exch, dpos, sizeof (gfx_coord2_t));
299 async_exchange_end(exch);
300 if (rc != EOK) {
301 async_forget(req);
302 return rc;
303 }
304
305 async_wait_for(req, &rc);
306 if (rc != EOK)
307 return rc;
308
309 return EOK;
310}
311
312
[1e4a937]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
[0e6e77f]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
[a2e104e]388 * @return EOK on success or an error code
[0e6e77f]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;
[7bb45e3]396 display_wnd_resize_t wresize;
[0e6e77f]397 errno_t rc;
398
[7bb45e3]399 wresize.offs = *offs;
400 wresize.nrect = *nrect;
[0e6e77f]401
402 exch = async_exchange_begin(window->display->sess);
403 req = async_send_1(exch, DISPLAY_WINDOW_RESIZE, window->id, &answer);
[7bb45e3]404 rc = async_data_write_start(exch, &wresize, sizeof (display_wnd_resize_t));
[0e6e77f]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
[b3c185b6]418/** Get display event.
419 *
420 * @param display Display
[aeb3037]421 * @param rwindow Place to store pointer to window that received event
[b3c185b6]422 * @param event Place to store event
423 * @return EOK on success or an error code
424 */
425static errno_t display_get_event(display_t *display, display_window_t **rwindow,
426 display_wnd_ev_t *event)
427{
428 async_exch_t *exch;
429 ipc_call_t answer;
430 aid_t req;
431 errno_t rc;
432 sysarg_t wnd_id;
433 display_window_t *window;
434
435 exch = async_exchange_begin(display->sess);
436 req = async_send_0(exch, DISPLAY_GET_EVENT, &answer);
437 rc = async_data_read_start(exch, event, sizeof(*event));
[6427f083]438 async_exchange_end(exch);
[b3c185b6]439 if (rc != EOK) {
440 async_forget(req);
441 return rc;
442 }
443
444 async_wait_for(req, &rc);
445 if (rc != EOK)
446 return rc;
447
448 wnd_id = ipc_get_arg1(&answer);
449 rc = display_get_window(display, wnd_id, &window);
450 if (rc != EOK)
451 return EIO;
452
453 *rwindow = window;
454 return EOK;
455}
456
[aeb3037]457/** Get display information.
458 *
459 * @param display Display
460 * @param info Place to store display information
461 * @return EOK on success or an error code
462 */
463errno_t display_get_info(display_t *display, display_info_t *info)
464{
465 async_exch_t *exch;
466 ipc_call_t answer;
467 aid_t req;
468 errno_t rc;
469
470 exch = async_exchange_begin(display->sess);
471 req = async_send_0(exch, DISPLAY_GET_INFO, &answer);
472 rc = async_data_read_start(exch, info, sizeof(*info));
473 async_exchange_end(exch);
474 if (rc != EOK) {
475 async_forget(req);
476 return rc;
477 }
478
479 async_wait_for(req, &rc);
480 if (rc != EOK)
481 return rc;
482
483 return EOK;
484}
485
[b3c185b6]486/** Display events are pending.
487 *
488 * @param display Display
489 * @param icall Call data
490 */
491static void display_ev_pending(display_t *display, ipc_call_t *icall)
492{
493 errno_t rc;
494 display_window_t *window = NULL;
495 display_wnd_ev_t event;
496
497 while (true) {
498 rc = display_get_event(display, &window, &event);
499 if (rc != EOK)
500 break;
501
[f7fb2b21]502 switch (event.etype) {
[338d0935]503 case wev_close:
504 if (window->cb != NULL && window->cb->close_event != NULL) {
505 window->cb->close_event(window->cb_arg);
506 }
507 break;
[b0a94854]508 case wev_focus:
509 if (window->cb != NULL && window->cb->focus_event != NULL) {
510 window->cb->focus_event(window->cb_arg);
511 }
512 break;
[f7fb2b21]513 case wev_kbd:
514 if (window->cb != NULL && window->cb->kbd_event != NULL) {
515 window->cb->kbd_event(window->cb_arg,
516 &event.ev.kbd);
517 }
518 break;
519 case wev_pos:
520 if (window->cb != NULL && window->cb->pos_event != NULL) {
521 window->cb->pos_event(window->cb_arg,
522 &event.ev.pos);
523 }
524 break;
[e022819]525 case wev_resize:
526 if (window->cb != NULL && window->cb->resize_event != NULL) {
527 window->cb->resize_event(window->cb_arg,
528 &event.ev.resize.rect);
529 }
530 break;
[b0a94854]531 case wev_unfocus:
532 if (window->cb != NULL && window->cb->unfocus_event != NULL) {
533 window->cb->unfocus_event(window->cb_arg);
534 }
535 break;
[f7fb2b21]536 }
[b3c185b6]537 }
538
539 async_answer_0(icall, EOK);
540}
541
542/** Callback connection handler.
543 *
544 * @param icall Connect call data
545 * @param arg Argument, display_t *
546 */
547static void display_cb_conn(ipc_call_t *icall, void *arg)
548{
549 display_t *display = (display_t *) arg;
550
551 while (true) {
552 ipc_call_t call;
553 async_get_call(&call);
554
555 if (!ipc_get_imethod(&call)) {
556 /* Hangup */
557 async_answer_0(&call, EOK);
558 goto out;
559 }
560
561 switch (ipc_get_imethod(&call)) {
562 case DISPLAY_EV_PENDING:
563 display_ev_pending(display, &call);
564 break;
565 default:
566 async_answer_0(&call, ENOTSUP);
567 break;
568 }
569 }
570
571out:
572 fibril_mutex_lock(&display->lock);
573 display->cb_done = true;
574 fibril_mutex_unlock(&display->lock);
575 fibril_condvar_broadcast(&display->cv);
576}
577
578/** Find window by ID.
579 *
580 * @param display Display
581 * @param wnd_id Window ID
582 * @param rwindow Place to store pointer to window
583 * @return EOK on success, ENOENT if not found
584 */
585static errno_t display_get_window(display_t *display, sysarg_t wnd_id,
586 display_window_t **rwindow)
587{
588 link_t *link;
589 display_window_t *window;
590
591 link = list_first(&display->windows);
592 while (link != NULL) {
593 window = list_get_instance(link, display_window_t, lwindows);
594 if (window->id == wnd_id) {
595 *rwindow = window;
596 return EOK;
597 }
598
599 link = list_next(link, &display->windows);
600 }
601
602 return ENOENT;
603}
604
[c8cf261]605/** @}
606 */
Note: See TracBrowser for help on using the repository browser.