source: mainline/uspace/lib/display/src/display.c@ 8bf9058

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 8bf9058 was 762f989, checked in by jxsvoboda <5887334+jxsvoboda@…>, 5 years ago

Avoid some leaks in display module
when display_window_create return with error.

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