source: mainline/uspace/app/taskbar/wndlist.c

Last change on this file was 95fc538, checked in by Jiri Svoboda <jiri@…>, 20 months ago

It should be Taskbar not Task Bar

  • 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 Taskbar 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 taskbar 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 taskbar 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.