source: mainline/uspace/lib/display/src/display.c@ 1e4a937

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

Add libdisplay method for initiating window resize

  • Property mode set to 100644
File size: 13.5 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 async_hangup(display->sess);
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
134 free(display);
135}
136
137/** Initialize window parameters structure.
138 *
139 * Window parameters structure must always be initialized using this function
140 * first.
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
149/** Create a display window.
150 *
151 * @param display Display
152 * @param params Window parameters
153 * @param cb Callback functions
154 * @param cb_arg Argument to callback functions
155 * @param rwindow Place to store pointer to new window
156 * @return EOK on success or an error code
157 */
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)
160{
161 display_window_t *window;
162 async_exch_t *exch;
163 aid_t req;
164 ipc_call_t answer;
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);
172 req = async_send_0(exch, DISPLAY_WINDOW_CREATE, &answer);
173 rc = async_data_write_start(exch, params, sizeof (display_wnd_params_t));
174 async_exchange_end(exch);
175 if (rc != EOK) {
176 async_forget(req);
177 return rc;
178 }
179
180 async_wait_for(req, &rc);
181 if (rc != EOK)
182 return rc;
183
184 window->display = display;
185 window->id = ipc_get_arg1(&answer);
186 window->cb = cb;
187 window->cb_arg = cb_arg;
188
189 list_append(&window->lwindows, &display->windows);
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
210 list_remove(&window->lwindows);
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
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
253 * @param pos Position in the window where the button was pressed
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
279/** Request a window resize.
280 *
281 * Request the display service to initiate a user window resize operation
282 * (i.e. let the user resize the window). Used when the client detects
283 * mouse press on the window frame or such.
284 *
285 * @param window Window
286 * @param rsztype Resize type (which part of window frame is being dragged)
287 * @param pos Position in the window where the button was pressed
288 * @return EOK on success or an error code
289 */
290errno_t display_window_resize_req(display_window_t *window,
291 display_wnd_rsztype_t rsztype, gfx_coord2_t *pos)
292{
293 async_exch_t *exch;
294 aid_t req;
295 ipc_call_t answer;
296 errno_t rc;
297
298 exch = async_exchange_begin(window->display->sess);
299 req = async_send_2(exch, DISPLAY_WINDOW_RESIZE_REQ, window->id,
300 (sysarg_t) rsztype, &answer);
301 rc = async_data_write_start(exch, (void *)pos, sizeof (gfx_coord2_t));
302 async_exchange_end(exch);
303 if (rc != EOK) {
304 async_forget(req);
305 return rc;
306 }
307
308 async_wait_for(req, &rc);
309 if (rc != EOK)
310 return rc;
311
312 return EOK;
313}
314
315/** Resize display window.
316 *
317 * It seems resizing windows should be easy with bounding rectangles.
318 * You have an old bounding rectangle and a new bounding rectangle (@a nrect).
319 * Change .p0 and top-left corner moves. Change .p1 and bottom-right corner
320 * moves. Piece of cake!
321 *
322 * There's always a catch, though. By series of resizes and moves .p0 could
323 * drift outside of the range of @c gfx_coord_t. Now what? @a offs to the
324 * rescue! @a offs moves the @em boundaries of the window with respect
325 * to the display, while keeping the @em contents of the window in the
326 * same place (with respect to the display). In other words, @a offs shifts
327 * the window's internal coordinate system.
328 *
329 * A few examples follow:
330 *
331 * Enlarge window by moving bottom-right corner 1 right, 1 down:
332 *
333 * bound = (0, 0, 10, 10)
334 * offs = (0, 0)
335 * nrect = (0, 0, 11, 11)
336 *
337 * Enlarge window by moving top-left corner, 1 up, 1 left, allowing the
338 * window-relative coordinate of the top-left corner to drift (undesirable)
339 *
340 * bound = (0, 0, 10, 10)
341 * offs = (0, 0)
342 * nrect = (-1, -1, 10, 10) <- this is the new bounding rectangle
343 *
344 * Enlarge window by moving top-left corner 1 up, 1 left, keeping top-left
345 * corner locked to (0,0) window-relative coordinates (desirable):
346 *
347 * bound = (0, 0, 10, 10)
348 * off = (-1,-1) <- top-left corner goes 1 up, 1 left
349 * nrect = (0, 0, 11, 11) <- window still starts at 0,0 window-relative
350 *
351 * @param window Window
352 * @param nrect New bounding rectangle
353 * @param offs
354 * @return EOK on success or an error code
355 */
356errno_t display_window_resize(display_window_t *window, gfx_coord2_t *offs,
357 gfx_rect_t *nrect)
358{
359 async_exch_t *exch;
360 aid_t req;
361 ipc_call_t answer;
362 display_wnd_resize_t wresize;
363 errno_t rc;
364
365 wresize.offs = *offs;
366 wresize.nrect = *nrect;
367
368 exch = async_exchange_begin(window->display->sess);
369 req = async_send_1(exch, DISPLAY_WINDOW_RESIZE, window->id, &answer);
370 rc = async_data_write_start(exch, &wresize, sizeof (display_wnd_resize_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/** Get display event.
385 *
386 * @param display Display
387 * @param rwindow Place to store pointe to window that received event
388 * @param event Place to store event
389 * @return EOK on success or an error code
390 */
391static errno_t display_get_event(display_t *display, display_window_t **rwindow,
392 display_wnd_ev_t *event)
393{
394 async_exch_t *exch;
395 ipc_call_t answer;
396 aid_t req;
397 errno_t rc;
398 sysarg_t wnd_id;
399 display_window_t *window;
400
401 exch = async_exchange_begin(display->sess);
402 req = async_send_0(exch, DISPLAY_GET_EVENT, &answer);
403 rc = async_data_read_start(exch, event, sizeof(*event));
404 async_exchange_end(exch);
405 if (rc != EOK) {
406 async_forget(req);
407 return rc;
408 }
409
410 async_wait_for(req, &rc);
411 if (rc != EOK)
412 return rc;
413
414 wnd_id = ipc_get_arg1(&answer);
415 rc = display_get_window(display, wnd_id, &window);
416 if (rc != EOK)
417 return EIO;
418
419 *rwindow = window;
420 return EOK;
421}
422
423/** Display events are pending.
424 *
425 * @param display Display
426 * @param icall Call data
427 */
428static void display_ev_pending(display_t *display, ipc_call_t *icall)
429{
430 errno_t rc;
431 display_window_t *window = NULL;
432 display_wnd_ev_t event;
433
434 while (true) {
435 rc = display_get_event(display, &window, &event);
436 if (rc != EOK)
437 break;
438
439 switch (event.etype) {
440 case wev_close:
441 if (window->cb != NULL && window->cb->close_event != NULL) {
442 window->cb->close_event(window->cb_arg);
443 }
444 break;
445 case wev_focus:
446 if (window->cb != NULL && window->cb->focus_event != NULL) {
447 window->cb->focus_event(window->cb_arg);
448 }
449 break;
450 case wev_kbd:
451 if (window->cb != NULL && window->cb->kbd_event != NULL) {
452 window->cb->kbd_event(window->cb_arg,
453 &event.ev.kbd);
454 }
455 break;
456 case wev_pos:
457 if (window->cb != NULL && window->cb->pos_event != NULL) {
458 window->cb->pos_event(window->cb_arg,
459 &event.ev.pos);
460 }
461 break;
462 case wev_unfocus:
463 if (window->cb != NULL && window->cb->unfocus_event != NULL) {
464 window->cb->unfocus_event(window->cb_arg);
465 }
466 break;
467 }
468 }
469
470 async_answer_0(icall, EOK);
471}
472
473/** Callback connection handler.
474 *
475 * @param icall Connect call data
476 * @param arg Argument, display_t *
477 */
478static void display_cb_conn(ipc_call_t *icall, void *arg)
479{
480 display_t *display = (display_t *) arg;
481
482 while (true) {
483 ipc_call_t call;
484 async_get_call(&call);
485
486 if (!ipc_get_imethod(&call)) {
487 /* Hangup */
488 async_answer_0(&call, EOK);
489 goto out;
490 }
491
492 switch (ipc_get_imethod(&call)) {
493 case DISPLAY_EV_PENDING:
494 display_ev_pending(display, &call);
495 break;
496 default:
497 async_answer_0(&call, ENOTSUP);
498 break;
499 }
500 }
501
502out:
503 fibril_mutex_lock(&display->lock);
504 display->cb_done = true;
505 fibril_mutex_unlock(&display->lock);
506 fibril_condvar_broadcast(&display->cv);
507}
508
509/** Find window by ID.
510 *
511 * @param display Display
512 * @param wnd_id Window ID
513 * @param rwindow Place to store pointer to window
514 * @return EOK on success, ENOENT if not found
515 */
516static errno_t display_get_window(display_t *display, sysarg_t wnd_id,
517 display_window_t **rwindow)
518{
519 link_t *link;
520 display_window_t *window;
521
522 link = list_first(&display->windows);
523 while (link != NULL) {
524 window = list_get_instance(link, display_window_t, lwindows);
525 if (window->id == wnd_id) {
526 *rwindow = window;
527 return EOK;
528 }
529
530 link = list_next(link, &display->windows);
531 }
532
533 return ENOENT;
534}
535
536/** @}
537 */
Note: See TracBrowser for help on using the repository browser.