source: mainline/uspace/app/taskbar/wndlist.c@ 5d9403d5

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

Open start menu using Enter, fix input device ID

Pressing Enter opens start menu, but only if it is focused.
Need to fix input device ID so that the menu is opened in the
correct seat, both in case of clicking and in case a key is
pressed to open the menu.

  • Property mode set to 100644
File size: 16.3 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/** @addtogroup taskbar
30 * @{
31 */
32/** @file Task bar window list
33 */
34
35#include <gfx/coord.h>
36#include <gfx/render.h>
37#include <stdbool.h>
38#include <stddef.h>
39#include <stdio.h>
40#include <stdlib.h>
41#include <str.h>
42#include <ui/fixed.h>
43#include <ui/label.h>
44#include <ui/resource.h>
45#include <ui/ui.h>
46#include <ui/window.h>
47#include "wndlist.h"
48
49static void wndlist_wm_window_added(void *, sysarg_t);
50static void wndlist_wm_window_removed(void *, sysarg_t);
51static void wndlist_wm_window_changed(void *, sysarg_t);
52
53/** Window list WM callbacks */
54static wndmgt_cb_t wndlist_wndmgt_cb = {
55 .window_added = wndlist_wm_window_added,
56 .window_removed = wndlist_wm_window_removed,
57 .window_changed = wndlist_wm_window_changed
58};
59
60static void wndlist_button_clicked(ui_pbutton_t *, void *);
61
62/** Window list button callbacks */
63static ui_pbutton_cb_t wndlist_button_cb = {
64 .clicked = wndlist_button_clicked
65};
66
67enum {
68 /** Min. X distance between left edges of two consecutive buttons */
69 wndlist_button_pitch_min = 85,
70 /** Max. X distance between left edges of two consecutive buttons (text) */
71 wndlist_button_pitch_min_text = 10,
72 /** Min. X distance between left edges of two consecutive buttons */
73 wndlist_button_pitch_max = 165,
74 /** Max. X distance between left edges of two consecutive buttons (text) */
75 wndlist_button_pitch_max_text = 17,
76 /** Padding between buttons */
77 wndlist_button_pad = 5,
78 /** Padding between buttons (text) */
79 wndlist_button_pad_text = 1
80};
81
82/** Create task bar window list.
83 *
84 * @param window Containing window
85 * @param fixed Fixed layout to which buttons will be added
86 * @param rwndlist Place to store pointer to new window list
87 * @return @c EOK on success or an error code
88 */
89errno_t wndlist_create(ui_window_t *window, ui_fixed_t *fixed,
90 wndlist_t **rwndlist)
91{
92 ui_resource_t *res = ui_window_get_res(window);
93 wndlist_t *wndlist = NULL;
94 errno_t rc;
95
96 wndlist = calloc(1, sizeof(wndlist_t));
97 if (wndlist == NULL) {
98 rc = ENOMEM;
99 goto error;
100 }
101
102 wndlist->window = window;
103 wndlist->fixed = fixed;
104 list_initialize(&wndlist->entries);
105
106 if (ui_resource_is_textmode(res))
107 wndlist->pitch = wndlist_button_pitch_max_text;
108 else
109 wndlist->pitch = wndlist_button_pitch_max;
110
111 *rwndlist = wndlist;
112 return EOK;
113error:
114 return rc;
115}
116
117/** Set window list rectangle.
118 *
119 * @param wndlist Window list
120 * @param rect Rectangle
121 */
122void wndlist_set_rect(wndlist_t *wndlist, gfx_rect_t *rect)
123{
124 wndlist->rect = *rect;
125}
126
127/** Attach window management service to window list.
128 *
129 * @param wndlist Window list
130 * @param wndmgt_svc Window management service name
131 * @return @c EOK on success or an error code
132 */
133errno_t wndlist_open_wm(wndlist_t *wndlist, const char *wndmgt_svc)
134{
135 errno_t rc;
136 wndmgt_window_list_t *wlist = NULL;
137 wndmgt_window_info_t *winfo = NULL;
138 sysarg_t i;
139
140 rc = wndmgt_open(wndmgt_svc, &wndlist_wndmgt_cb, (void *)wndlist,
141 &wndlist->wndmgt);
142 if (rc != EOK)
143 goto error;
144
145 rc = wndmgt_get_window_list(wndlist->wndmgt, &wlist);
146 if (rc != EOK)
147 goto error;
148
149 for (i = 0; i < wlist->nwindows; i++) {
150 rc = wndmgt_get_window_info(wndlist->wndmgt, wlist->windows[i],
151 &winfo);
152 if (rc != EOK)
153 goto error;
154
155 if ((winfo->flags & (wndf_popup | wndf_system)) == 0) {
156 rc = wndlist_append(wndlist, wlist->windows[i],
157 winfo->caption, winfo->nfocus != 0, false);
158 if (rc != EOK) {
159 wndmgt_free_window_info(winfo);
160 goto error;
161 }
162 }
163
164 wndmgt_free_window_info(winfo);
165 }
166
167 return EOK;
168error:
169 if (wlist != NULL)
170 wndmgt_free_window_list(wlist);
171 if (wndlist->wndmgt != NULL) {
172 wndmgt_close(wndlist->wndmgt);
173 wndlist->wndmgt = NULL;
174 }
175 return rc;
176}
177
178/** Destroy task bar window list.
179 *
180 * @param wndlist Window list
181 */
182void wndlist_destroy(wndlist_t *wndlist)
183{
184 wndlist_entry_t *entry;
185
186 /* Close window management service */
187 if (wndlist->wndmgt)
188 wndmgt_close(wndlist->wndmgt);
189
190 /* Destroy entries */
191 entry = wndlist_first(wndlist);
192 while (entry != NULL) {
193 (void)wndlist_remove(wndlist, entry, false);
194 entry = wndlist_first(wndlist);
195 }
196
197 free(wndlist);
198}
199
200/** Append new entry to window list.
201 *
202 * @param wndlist Window list
203 * @param wnd_id Window ID
204 * @param caption Entry caption
205 * @param active @c true iff window is active
206 * @param paint @c true to paint immediately
207 * @return @c EOK on success or an error code
208 */
209errno_t wndlist_append(wndlist_t *wndlist, sysarg_t wnd_id,
210 const char *caption, bool active, bool paint)
211{
212 wndlist_entry_t *entry = NULL;
213 ui_resource_t *res;
214 wndlist_entry_t *e;
215 errno_t rc;
216
217 entry = calloc(1, sizeof(wndlist_entry_t));
218 if (entry == NULL) {
219 rc = ENOMEM;
220 goto error;
221 }
222
223 entry->wnd_id = wnd_id;
224 res = ui_window_get_res(wndlist->window);
225
226 rc = ui_pbutton_create(res, caption, &entry->button);
227 if (rc != EOK)
228 goto error;
229
230 entry->wndlist = wndlist;
231 list_append(&entry->lentries, &wndlist->entries);
232
233 entry->visible = false;
234
235 ui_pbutton_set_light(entry->button, active);
236
237 /* Set button callbacks */
238 ui_pbutton_set_cb(entry->button, &wndlist_button_cb, (void *)entry);
239
240 if (wndlist_update_pitch(wndlist)) {
241 /*
242 * Update rectangles for all entries, including @a entry, adding
243 * it to the layout, if applicable.
244 */
245 e = wndlist_first(wndlist);
246 while (e != NULL) {
247 wndlist_set_entry_rect(wndlist, e);
248 e = wndlist_next(e);
249 }
250
251 if (paint)
252 return wndlist_repaint(wndlist);
253 } else {
254 wndlist_set_entry_rect(wndlist, entry);
255 if (paint)
256 return wndlist_paint_entry(entry);
257 }
258
259 return EOK;
260error:
261 if (entry != NULL && entry->button != NULL)
262 ui_pbutton_destroy(entry->button);
263 if (entry != NULL)
264 free(entry);
265 return rc;
266
267}
268
269/** Remove entry from window list.
270 *
271 * @param wndlist Window list
272 * @param entry Window list entry
273 * @param paint @c true to repaint window list
274 * @return @c EOK on success or an error code
275 */
276errno_t wndlist_remove(wndlist_t *wndlist, wndlist_entry_t *entry,
277 bool paint)
278{
279 wndlist_entry_t *e;
280 wndlist_entry_t *next;
281 wndlist_entry_t *last;
282 errno_t rc = EOK;
283
284 assert(entry->wndlist == wndlist);
285 next = wndlist_next(entry);
286
287 /* Remember last entry */
288 last = wndlist_last(wndlist);
289
290 if (entry->visible)
291 ui_fixed_remove(wndlist->fixed, ui_pbutton_ctl(entry->button));
292
293 list_remove(&entry->lentries);
294
295 if (wndlist_update_pitch(wndlist)) {
296 /*
297 * Update rectangles for all entries.
298 */
299 e = wndlist_first(wndlist);
300 while (e != NULL) {
301 wndlist_set_entry_rect(wndlist, e);
302 e = wndlist_next(e);
303 }
304
305 if (paint)
306 rc = wndlist_repaint(wndlist);
307 } else {
308 /* Unpaint the last entry */
309 if (paint)
310 rc = wndlist_unpaint_entry(last);
311
312 /*
313 * Update rectangles for entries to the right
314 */
315
316 e = NULL;
317 while (next != NULL) {
318 e = next;
319
320 wndlist_set_entry_rect(wndlist, e);
321 if (paint) {
322 rc = wndlist_paint_entry(e);
323 if (rc != EOK)
324 return rc;
325 }
326
327 next = wndlist_next(e);
328 }
329 }
330
331 ui_pbutton_destroy(entry->button);
332 free(entry);
333 return rc;
334}
335
336/** Update button pitch.
337 *
338 * Recalculatebutton pitch @c wndlist->pitch based on current number
339 * of buttons.
340 *
341 * @param wndlist Window list
342 * @return @c true iff pitch changed
343 */
344bool wndlist_update_pitch(wndlist_t *wndlist)
345{
346 ui_resource_t *res;
347 size_t nbuttons;
348 gfx_coord_t pitch;
349 gfx_coord_t pitch_max;
350 gfx_coord_t pitch_min;
351 gfx_coord_t pad;
352
353 res = ui_window_get_res(wndlist->window);
354
355 if (ui_resource_is_textmode(res)) {
356 pitch_max = wndlist_button_pitch_max_text;
357 pitch_min = wndlist_button_pitch_min_text;
358 pad = wndlist_button_pad_text;
359 } else {
360 pitch_max = wndlist_button_pitch_max;
361 pitch_min = wndlist_button_pitch_min;
362 pad = wndlist_button_pad;
363 }
364
365 /* Compute pitch that fits all buttons perfectly */
366 nbuttons = wndlist_count(wndlist);
367 if (nbuttons > 0)
368 pitch = (wndlist->rect.p1.x - wndlist->rect.p0.x + pad) / nbuttons;
369 else
370 pitch = pitch_min;
371
372 if (pitch < pitch_min)
373 pitch = pitch_min;
374 if (pitch > pitch_max)
375 pitch = pitch_max;
376
377 /* Did the pitch change? */
378 if (pitch == wndlist->pitch)
379 return false;
380
381 wndlist->pitch = pitch;
382 return true;
383}
384
385/** Update window list entry.
386 *
387 * @param wndlist Window list
388 * @param entry Window list entry
389 * @param active @c true iff the window is active
390 * @return @c EOK on success or an error code
391 */
392errno_t wndlist_update(wndlist_t *wndlist, wndlist_entry_t *entry,
393 const char *caption, bool active)
394{
395 errno_t rc;
396 assert(entry->wndlist == wndlist);
397
398 rc = ui_pbutton_set_caption(entry->button, caption);
399 if (rc != EOK)
400 return rc;
401
402 ui_pbutton_set_light(entry->button, active);
403
404 rc = wndlist_paint_entry(entry);
405 if (rc != EOK)
406 return rc;
407
408 return EOK;
409}
410
411/** Compute and set window list entry rectangle.
412 *
413 * Compute rectangle for window list entry and set it.
414 *
415 * @param wndlist Window list
416 * @param entry Window list entry
417 */
418void wndlist_set_entry_rect(wndlist_t *wndlist, wndlist_entry_t *entry)
419{
420 wndlist_entry_t *e;
421 gfx_rect_t rect;
422 ui_resource_t *res;
423 gfx_coord_t pitch;
424 gfx_coord_t pad;
425 size_t idx;
426
427 /* Determine entry index */
428 idx = 0;
429 e = wndlist_first(wndlist);
430 while (e != entry) {
431 assert(e != NULL);
432 e = wndlist_next(e);
433 ++idx;
434 }
435
436 res = ui_window_get_res(wndlist->window);
437
438 if (ui_resource_is_textmode(res)) {
439 pad = wndlist_button_pad_text;
440 } else {
441 pad = wndlist_button_pad;
442 }
443
444 pitch = wndlist->pitch;
445
446 rect.p0.x = wndlist->rect.p0.x + pitch * idx;
447 rect.p0.y = wndlist->rect.p0.y;
448 rect.p1.x = wndlist->rect.p0.x + pitch * (idx + 1) - pad;
449 rect.p1.y = wndlist->rect.p1.y;
450
451 /* Entry does not fit? */
452 if (rect.p1.x > wndlist->rect.p1.x) {
453 /* Make entry invisible */
454 if (entry->visible) {
455 ui_fixed_remove(wndlist->fixed,
456 ui_pbutton_ctl(entry->button));
457 entry->visible = false;
458 }
459 } else {
460 /* Make entry visible */
461 if (!entry->visible) {
462 ui_fixed_add(wndlist->fixed,
463 ui_pbutton_ctl(entry->button));
464 entry->visible = true;
465 }
466 }
467
468 ui_pbutton_set_rect(entry->button, &rect);
469 entry->rect = rect;
470}
471
472/** Paint window list entry.
473 *
474 * @param entry Window list entry
475 * @return EOK on success or an error code
476 */
477errno_t wndlist_paint_entry(wndlist_entry_t *entry)
478{
479 ui_t *ui;
480
481 ui = ui_window_get_ui(entry->wndlist->window);
482 if (ui_is_suspended(ui))
483 return EOK;
484
485 return ui_pbutton_paint(entry->button);
486}
487
488/** Unpaint window list entry.
489 *
490 * @param entry Window list entry
491 * @return EOK on success or an error code
492 */
493errno_t wndlist_unpaint_entry(wndlist_entry_t *entry)
494{
495 errno_t rc;
496 ui_t *ui;
497 gfx_context_t *gc;
498 ui_resource_t *res;
499 gfx_color_t *color;
500
501 ui = ui_window_get_ui(entry->wndlist->window);
502 gc = ui_window_get_gc(entry->wndlist->window);
503 res = ui_window_get_res(entry->wndlist->window);
504 color = ui_resource_get_wnd_face_color(res);
505
506 if (ui_is_suspended(ui))
507 return EOK;
508
509 rc = gfx_set_color(gc, color);
510 if (rc != EOK)
511 return rc;
512
513 rc = gfx_fill_rect(gc, &entry->rect);
514 if (rc != EOK)
515 return rc;
516
517 rc = gfx_update(gc);
518 if (rc != EOK)
519 return rc;
520
521 return EOK;
522}
523
524/** Handle WM window added event.
525 *
526 * @param arg Argument (wndlist_t *)
527 * @param wnd_id Window ID
528 */
529static void wndlist_wm_window_added(void *arg, sysarg_t wnd_id)
530{
531 wndlist_t *wndlist = (wndlist_t *)arg;
532 wndmgt_window_info_t *winfo = NULL;
533 ui_t *ui;
534 errno_t rc;
535
536 ui = ui_window_get_ui(wndlist->window);
537 ui_lock(ui);
538
539 rc = wndmgt_get_window_info(wndlist->wndmgt, wnd_id, &winfo);
540 if (rc != EOK)
541 goto error;
542
543 if ((winfo->flags & (wndf_popup | wndf_system)) == 0) {
544 rc = wndlist_append(wndlist, wnd_id, winfo->caption,
545 winfo->nfocus != 0, true);
546 if (rc != EOK) {
547 wndmgt_free_window_info(winfo);
548 goto error;
549 }
550 }
551
552 wndmgt_free_window_info(winfo);
553 ui_unlock(ui);
554 return;
555error:
556 if (winfo != NULL)
557 wndmgt_free_window_info(winfo);
558 ui_unlock(ui);
559}
560
561/** Handle WM window removed event.
562 *
563 * @param arg Argument (wndlist_t *)
564 * @param wnd_id Window ID
565 */
566static void wndlist_wm_window_removed(void *arg, sysarg_t wnd_id)
567{
568 wndlist_t *wndlist = (wndlist_t *)arg;
569 wndlist_entry_t *entry;
570 ui_t *ui;
571
572 ui = ui_window_get_ui(wndlist->window);
573 ui_lock(ui);
574
575 entry = wndlist_entry_by_id(wndlist, wnd_id);
576 if (entry == NULL) {
577 ui_unlock(ui);
578 return;
579 }
580
581 (void) wndlist_remove(wndlist, entry, true);
582 ui_unlock(ui);
583}
584
585/** Handle WM window changed event.
586 *
587 * @param arg Argument (wndlist_t *)
588 * @param wnd_id Window ID
589 */
590static void wndlist_wm_window_changed(void *arg, sysarg_t wnd_id)
591{
592 wndlist_t *wndlist = (wndlist_t *)arg;
593 wndmgt_window_info_t *winfo = NULL;
594 wndlist_entry_t *entry;
595 ui_t *ui;
596 errno_t rc;
597
598 ui = ui_window_get_ui(wndlist->window);
599 ui_lock(ui);
600
601 entry = wndlist_entry_by_id(wndlist, wnd_id);
602 if (entry == NULL) {
603 ui_unlock(ui);
604 return;
605 }
606
607 rc = wndmgt_get_window_info(wndlist->wndmgt, wnd_id, &winfo);
608 if (rc != EOK) {
609 ui_unlock(ui);
610 return;
611 }
612
613 (void) wndlist_update(wndlist, entry, winfo->caption,
614 winfo->nfocus != 0);
615 wndmgt_free_window_info(winfo);
616 ui_unlock(ui);
617}
618
619/** Find window list entry by ID.
620 *
621 * @param wndlist Window list
622 * @param wnd_id Window ID
623 * @return Window list entry on success or @c NULL if not found
624 */
625wndlist_entry_t *wndlist_entry_by_id(wndlist_t *wndlist, sysarg_t wnd_id)
626{
627 wndlist_entry_t *entry;
628
629 entry = wndlist_first(wndlist);
630 while (entry != NULL) {
631 if (entry->wnd_id == wnd_id)
632 return entry;
633
634 entry = wndlist_next(entry);
635 }
636
637 return NULL;
638}
639
640/** Get first window list entry.
641 *
642 * @param wndlist Window list
643 * @return First entry or @c NULL if the list is empty
644 */
645wndlist_entry_t *wndlist_first(wndlist_t *wndlist)
646{
647 link_t *link;
648
649 link = list_first(&wndlist->entries);
650 if (link == NULL)
651 return NULL;
652
653 return list_get_instance(link, wndlist_entry_t, lentries);
654}
655
656/** Get last window list entry.
657 *
658 * @param wndlist Window list
659 * @return Last entry or @c NULL if the list is empty
660 */
661wndlist_entry_t *wndlist_last(wndlist_t *wndlist)
662{
663 link_t *link;
664
665 link = list_last(&wndlist->entries);
666 if (link == NULL)
667 return NULL;
668
669 return list_get_instance(link, wndlist_entry_t, lentries);
670}
671
672/** Get next window list entry.
673 *
674 * @param cur Current entry
675 * @return Next entry or @c NULL if @a cur is the last entry
676 */
677wndlist_entry_t *wndlist_next(wndlist_entry_t *cur)
678{
679 link_t *link;
680
681 link = list_next(&cur->lentries, &cur->wndlist->entries);
682 if (link == NULL)
683 return NULL;
684
685 return list_get_instance(link, wndlist_entry_t, lentries);
686}
687
688/** Get number of window list entries.
689 *
690 * @param wndlist Window list
691 * @return Number of entries
692 */
693size_t wndlist_count(wndlist_t *wndlist)
694{
695 return list_count(&wndlist->entries);
696}
697
698/** Repaint window list.
699 *
700 * @param wndlist Window list
701 * @return @c EOK on success or an error code
702 */
703errno_t wndlist_repaint(wndlist_t *wndlist)
704{
705 if (ui_is_suspended(ui_window_get_ui(wndlist->window)))
706 return EOK;
707
708 return ui_window_paint(wndlist->window);
709}
710
711/** Window button was clicked.
712 *
713 * @param pbutton Push button
714 * @param arg Argument (wndlist_entry_t *)
715 */
716static void wndlist_button_clicked(ui_pbutton_t *pbutton, void *arg)
717{
718 wndlist_entry_t *entry = (wndlist_entry_t *)arg;
719 sysarg_t dev_id;
720
721 /* ID of device that clicked the button */
722 dev_id = entry->wndlist->ev_idev_id;
723
724 (void) wndmgt_activate_window(entry->wndlist->wndmgt,
725 dev_id, entry->wnd_id);
726}
727
728/** @}
729 */
Note: See TracBrowser for help on using the repository browser.