source: mainline/uspace/lib/display/src/display.c@ 46a47c0

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

Make sure window is only show as inactive when it loses last focus

This currently affects the title bar and also the cursor in Terminal.

  • Property mode set to 100644
File size: 19.5 KB
Line 
1/*
2 * Copyright (c) 2023 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/** Minimize window.
513 *
514 * @param window Window
515 * @return EOK on success or an error code
516 */
517errno_t display_window_minimize(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_MINIMIZE, window->id);
524 async_exchange_end(exch);
525
526 return rc;
527}
528
529/** Maximize window.
530 *
531 * @param window Window
532 * @return EOK on success or an error code
533 */
534errno_t display_window_maximize(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_MAXIMIZE, window->id);
541 async_exchange_end(exch);
542
543 return rc;
544}
545
546/** Unmaximize window.
547 *
548 * @param window Window
549 * @return EOK on success or an error code
550 */
551errno_t display_window_unmaximize(display_window_t *window)
552{
553 async_exch_t *exch;
554 errno_t rc;
555
556 exch = async_exchange_begin(window->display->sess);
557 rc = async_req_1_0(exch, DISPLAY_WINDOW_UNMAXIMIZE, window->id);
558 async_exchange_end(exch);
559
560 return rc;
561}
562
563/** Set window cursor.
564 *
565 * Set cursor that is displayed when pointer is over the window. The default
566 * is the arrow pointer.
567 *
568 * @param window Window
569 * @param cursor Cursor to display
570 * @return EOK on success or an error code
571 */
572errno_t display_window_set_cursor(display_window_t *window,
573 display_stock_cursor_t cursor)
574{
575 async_exch_t *exch;
576 errno_t rc;
577
578 exch = async_exchange_begin(window->display->sess);
579 rc = async_req_2_0(exch, DISPLAY_WINDOW_SET_CURSOR, window->id,
580 cursor);
581 async_exchange_end(exch);
582 return rc;
583}
584
585/** Set display window caption.
586 *
587 * @param window Window
588 * @param caption New caption
589 * @return EOK on success or an error code
590 */
591errno_t display_window_set_caption(display_window_t *window,
592 const char *caption)
593{
594 async_exch_t *exch;
595 aid_t req;
596 ipc_call_t answer;
597 size_t cap_size;
598 errno_t rc;
599
600 cap_size = str_size(caption);
601
602 exch = async_exchange_begin(window->display->sess);
603 req = async_send_1(exch, DISPLAY_WINDOW_SET_CAPTION, window->id,
604 &answer);
605
606 /* Write caption */
607 rc = async_data_write_start(exch, caption, cap_size);
608 async_exchange_end(exch);
609 if (rc != EOK) {
610 async_forget(req);
611 return rc;
612 }
613
614 async_wait_for(req, &rc);
615 return rc;
616}
617
618/** Get display event.
619 *
620 * @param display Display
621 * @param rwindow Place to store pointer to window that received event
622 * @param event Place to store event
623 * @return EOK on success or an error code
624 */
625static errno_t display_get_event(display_t *display, display_window_t **rwindow,
626 display_wnd_ev_t *event)
627{
628 async_exch_t *exch;
629 ipc_call_t answer;
630 aid_t req;
631 errno_t rc;
632 sysarg_t wnd_id;
633 display_window_t *window;
634
635 exch = async_exchange_begin(display->sess);
636 req = async_send_0(exch, DISPLAY_GET_EVENT, &answer);
637 rc = async_data_read_start(exch, event, sizeof(*event));
638 async_exchange_end(exch);
639 if (rc != EOK) {
640 async_forget(req);
641 return rc;
642 }
643
644 async_wait_for(req, &rc);
645 if (rc != EOK)
646 return rc;
647
648 wnd_id = ipc_get_arg1(&answer);
649 rc = display_get_window(display, wnd_id, &window);
650 if (rc != EOK)
651 return EIO;
652
653 *rwindow = window;
654 return EOK;
655}
656
657/** Get display information.
658 *
659 * @param display Display
660 * @param info Place to store display information
661 * @return EOK on success or an error code
662 */
663errno_t display_get_info(display_t *display, display_info_t *info)
664{
665 async_exch_t *exch;
666 ipc_call_t answer;
667 aid_t req;
668 errno_t rc;
669
670 exch = async_exchange_begin(display->sess);
671 req = async_send_0(exch, DISPLAY_GET_INFO, &answer);
672 rc = async_data_read_start(exch, info, sizeof(*info));
673 async_exchange_end(exch);
674 if (rc != EOK) {
675 async_forget(req);
676 return rc;
677 }
678
679 async_wait_for(req, &rc);
680 if (rc != EOK)
681 return rc;
682
683 return EOK;
684}
685
686/** Display events are pending.
687 *
688 * @param display Display
689 * @param icall Call data
690 */
691static void display_ev_pending(display_t *display, ipc_call_t *icall)
692{
693 errno_t rc;
694 display_window_t *window = NULL;
695 display_wnd_ev_t event;
696
697 while (true) {
698 fibril_mutex_lock(&display->lock);
699
700 if (display->sess != NULL)
701 rc = display_get_event(display, &window, &event);
702 else
703 rc = ENOENT;
704
705 fibril_mutex_unlock(&display->lock);
706
707 if (rc != EOK)
708 break;
709
710 switch (event.etype) {
711 case wev_close:
712 if (window->cb != NULL && window->cb->close_event != NULL) {
713 window->cb->close_event(window->cb_arg);
714 }
715 break;
716 case wev_focus:
717 if (window->cb != NULL && window->cb->focus_event != NULL) {
718 window->cb->focus_event(window->cb_arg,
719 event.ev.focus.nfocus);
720 }
721 break;
722 case wev_kbd:
723 if (window->cb != NULL && window->cb->kbd_event != NULL) {
724 window->cb->kbd_event(window->cb_arg,
725 &event.ev.kbd);
726 }
727 break;
728 case wev_pos:
729 if (window->cb != NULL && window->cb->pos_event != NULL) {
730 window->cb->pos_event(window->cb_arg,
731 &event.ev.pos);
732 }
733 break;
734 case wev_resize:
735 if (window->cb != NULL && window->cb->resize_event != NULL) {
736 window->cb->resize_event(window->cb_arg,
737 &event.ev.resize.rect);
738 }
739 break;
740 case wev_unfocus:
741 if (window->cb != NULL && window->cb->unfocus_event != NULL) {
742 window->cb->unfocus_event(window->cb_arg,
743 event.ev.unfocus.nfocus);
744 }
745 break;
746 }
747 }
748
749 async_answer_0(icall, EOK);
750}
751
752/** Callback connection handler.
753 *
754 * @param icall Connect call data
755 * @param arg Argument, display_t *
756 */
757static void display_cb_conn(ipc_call_t *icall, void *arg)
758{
759 display_t *display = (display_t *) arg;
760
761 while (true) {
762 ipc_call_t call;
763 async_get_call(&call);
764
765 if (!ipc_get_imethod(&call)) {
766 /* Hangup */
767 async_answer_0(&call, EOK);
768 goto out;
769 }
770
771 switch (ipc_get_imethod(&call)) {
772 case DISPLAY_EV_PENDING:
773 display_ev_pending(display, &call);
774 break;
775 default:
776 async_answer_0(&call, ENOTSUP);
777 break;
778 }
779 }
780
781out:
782 fibril_mutex_lock(&display->lock);
783 display->cb_done = true;
784 fibril_mutex_unlock(&display->lock);
785 fibril_condvar_broadcast(&display->cv);
786}
787
788/** Find window by ID.
789 *
790 * @param display Display
791 * @param wnd_id Window ID
792 * @param rwindow Place to store pointer to window
793 * @return EOK on success, ENOENT if not found
794 */
795static errno_t display_get_window(display_t *display, sysarg_t wnd_id,
796 display_window_t **rwindow)
797{
798 link_t *link;
799 display_window_t *window;
800
801 link = list_first(&display->windows);
802 while (link != NULL) {
803 window = list_get_instance(link, display_window_t, lwindows);
804 if (window->id == wnd_id) {
805 *rwindow = window;
806 return EOK;
807 }
808
809 link = list_next(link, &display->windows);
810 }
811
812 return ENOENT;
813}
814
815/** @}
816 */
Note: See TracBrowser for help on using the repository browser.