source: mainline/uspace/app/taskbar/wndlist.c@ 1eaead4

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

Need to update GC when unpainting a button

This caused nothing to happen when the last window was closed until
the next second ticked and the task bar was repainted because of
the clock.

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