source: mainline/uspace/app/taskbar/tbsmenu.c@ e63e74a

Last change on this file since e63e74a was e63e74a, checked in by Jiri Svoboda <jiri@…>, 16 months ago

Start menu separator entry support

  • Property mode set to 100644
File size: 12.7 KB
Line 
1/*
2 * Copyright (c) 2024 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 start menu
33 */
34
35#include <gfx/coord.h>
36#include <stdbool.h>
37#include <stddef.h>
38#include <stdlib.h>
39#include <str.h>
40#include <task.h>
41#include <tbarcfg/tbarcfg.h>
42#include <ui/fixed.h>
43#include <ui/menu.h>
44#include <ui/menuentry.h>
45#include <ui/resource.h>
46#include <ui/ui.h>
47#include <ui/window.h>
48#include "tbsmenu.h"
49
50static void tbsmenu_smenu_close_req(ui_menu_t *, void *);
51
52/** Start menu callbacks */
53static ui_menu_cb_t tbsmenu_smenu_cb = {
54 .close_req = tbsmenu_smenu_close_req,
55};
56
57static void tbsmenu_button_down(ui_pbutton_t *, void *);
58
59/** Start button callbacks */
60static ui_pbutton_cb_t tbsmenu_button_cb = {
61 .down = tbsmenu_button_down
62};
63
64static void tbsmenu_smenu_entry_cb(ui_menu_entry_t *, void *);
65static errno_t tbsmenu_entry_start(tbsmenu_entry_t *);
66
67/** Create taskbar start menu.
68 *
69 * @param window Containing window
70 * @param fixed Fixed layout to which start button will be added
71 * @param rtbsmenu Place to store pointer to new start menu
72 * @return @c EOK on success or an error code
73 */
74errno_t tbsmenu_create(ui_window_t *window, ui_fixed_t *fixed,
75 tbsmenu_t **rtbsmenu)
76{
77 ui_resource_t *res = ui_window_get_res(window);
78 tbsmenu_t *tbsmenu = NULL;
79 errno_t rc;
80
81 tbsmenu = calloc(1, sizeof(tbsmenu_t));
82 if (tbsmenu == NULL) {
83 rc = ENOMEM;
84 goto error;
85 }
86
87 rc = ui_pbutton_create(res, "Start", &tbsmenu->sbutton);
88 if (rc != EOK)
89 goto error;
90
91 ui_pbutton_set_cb(tbsmenu->sbutton, &tbsmenu_button_cb,
92 (void *)tbsmenu);
93
94 ui_pbutton_set_default(tbsmenu->sbutton, true);
95
96 rc = ui_fixed_add(fixed, ui_pbutton_ctl(tbsmenu->sbutton));
97 if (rc != EOK)
98 goto error;
99
100 rc = ui_menu_create(window, &tbsmenu->smenu);
101 if (rc != EOK)
102 goto error;
103
104 ui_menu_set_cb(tbsmenu->smenu, &tbsmenu_smenu_cb, (void *)tbsmenu);
105
106 tbsmenu->window = window;
107 tbsmenu->fixed = fixed;
108 list_initialize(&tbsmenu->entries);
109
110 *rtbsmenu = tbsmenu;
111 return EOK;
112error:
113 if (tbsmenu != NULL)
114 ui_pbutton_destroy(tbsmenu->sbutton);
115 if (tbsmenu != NULL)
116 free(tbsmenu);
117 return rc;
118}
119
120/** Load start menu from repository.
121 *
122 * @param tbsmenu Start menu
123 * @param Repository path
124 * @return EOK on success or an error code
125 */
126errno_t tbsmenu_load(tbsmenu_t *tbsmenu, const char *repopath)
127{
128 tbsmenu_entry_t *tentry;
129 tbarcfg_t *tbcfg = NULL;
130 smenu_entry_t *sme;
131 bool separator;
132 const char *caption;
133 const char *cmd;
134 bool terminal;
135 errno_t rc;
136
137 rc = tbarcfg_open(repopath, &tbcfg);
138 if (rc != EOK)
139 goto error;
140
141 sme = tbarcfg_smenu_first(tbcfg);
142 while (sme != NULL) {
143 separator = smenu_entry_get_separator(sme);
144 if (separator == false) {
145 caption = smenu_entry_get_caption(sme);
146 cmd = smenu_entry_get_cmd(sme);
147 terminal = smenu_entry_get_terminal(sme);
148
149 rc = tbsmenu_add(tbsmenu, caption, cmd, terminal,
150 &tentry);
151 if (rc != EOK)
152 goto error;
153 } else {
154 rc = tbsmenu_add_sep(tbsmenu, &tentry);
155 if (rc != EOK)
156 goto error;
157 }
158
159 (void)tentry;
160
161 sme = tbarcfg_smenu_next(sme);
162 }
163
164 tbarcfg_close(tbcfg);
165 return EOK;
166error:
167 if (tbcfg != NULL)
168 tbarcfg_close(tbcfg);
169 return rc;
170}
171
172/** Set start menu rectangle.
173 *
174 * @param tbsmenu Start menu
175 * @param rect Rectangle
176 */
177void tbsmenu_set_rect(tbsmenu_t *tbsmenu, gfx_rect_t *rect)
178{
179 tbsmenu->rect = *rect;
180 ui_pbutton_set_rect(tbsmenu->sbutton, rect);
181}
182
183/** Open taskbar start menu.
184 *
185 * @param tbsmenu Start menu
186 */
187void tbsmenu_open(tbsmenu_t *tbsmenu)
188{
189 (void) ui_menu_open(tbsmenu->smenu, &tbsmenu->rect,
190 tbsmenu->ev_idev_id);
191}
192
193/** Close taskbar start menu.
194 *
195 * @param tbsmenu Start menu
196 */
197void tbsmenu_close(tbsmenu_t *tbsmenu)
198{
199 ui_menu_close(tbsmenu->smenu);
200}
201
202/** Determine if taskbar start menu is open.
203 *
204 * @param tbsmenu Start menu
205 * @return @c true iff start menu is open
206 */
207bool tbsmenu_is_open(tbsmenu_t *tbsmenu)
208{
209 return ui_menu_is_open(tbsmenu->smenu);
210}
211
212/** Destroy taskbar start menu.
213 *
214 * @param tbsmenu Start menu
215 */
216void tbsmenu_destroy(tbsmenu_t *tbsmenu)
217{
218 tbsmenu_entry_t *entry;
219
220 /* Destroy entries */
221 entry = tbsmenu_first(tbsmenu);
222 while (entry != NULL) {
223 tbsmenu_remove(tbsmenu, entry, false);
224 entry = tbsmenu_first(tbsmenu);
225 }
226
227 ui_fixed_remove(tbsmenu->fixed, ui_pbutton_ctl(tbsmenu->sbutton));
228 ui_pbutton_destroy(tbsmenu->sbutton);
229 ui_menu_destroy(tbsmenu->smenu);
230
231 free(tbsmenu);
232}
233
234/** Add entry to start menu.
235 *
236 * @param tbsmenu Start menu
237 * @param caption Caption
238 * @param cmd Command to run
239 * @param terminal Start in terminal
240 * @param entry Start menu entry
241 * @return @c EOK on success or an error code
242 */
243errno_t tbsmenu_add(tbsmenu_t *tbsmenu, const char *caption,
244 const char *cmd, bool terminal, tbsmenu_entry_t **rentry)
245{
246 errno_t rc;
247 tbsmenu_entry_t *entry;
248
249 entry = calloc(1, sizeof(tbsmenu_entry_t));
250 if (entry == NULL)
251 return ENOMEM;
252
253 entry->caption = str_dup(caption);
254 if (entry->caption == NULL) {
255 rc = ENOMEM;
256 goto error;
257 }
258
259 entry->cmd = str_dup(cmd);
260 if (entry->cmd == NULL) {
261 rc = ENOMEM;
262 goto error;
263 }
264
265 entry->terminal = terminal;
266
267 rc = ui_menu_entry_create(tbsmenu->smenu, caption, "", &entry->mentry);
268 if (rc != EOK)
269 goto error;
270
271 ui_menu_entry_set_cb(entry->mentry, tbsmenu_smenu_entry_cb,
272 (void *)entry);
273
274 entry->tbsmenu = tbsmenu;
275 list_append(&entry->lentries, &tbsmenu->entries);
276 *rentry = entry;
277 return EOK;
278error:
279 if (entry->caption != NULL)
280 free(entry->caption);
281 if (entry->cmd != NULL)
282 free(entry->cmd);
283 free(entry);
284 return rc;
285}
286
287/** Add separator entry to start menu.
288 *
289 * @param tbsmenu Start menu
290 * @param entry Start menu entry
291 * @return @c EOK on success or an error code
292 */
293errno_t tbsmenu_add_sep(tbsmenu_t *tbsmenu, tbsmenu_entry_t **rentry)
294{
295 errno_t rc;
296 tbsmenu_entry_t *entry;
297
298 entry = calloc(1, sizeof(tbsmenu_entry_t));
299 if (entry == NULL)
300 return ENOMEM;
301
302 rc = ui_menu_entry_sep_create(tbsmenu->smenu, &entry->mentry);
303 if (rc != EOK)
304 goto error;
305
306 ui_menu_entry_set_cb(entry->mentry, tbsmenu_smenu_entry_cb,
307 (void *)entry);
308
309 entry->tbsmenu = tbsmenu;
310 list_append(&entry->lentries, &tbsmenu->entries);
311 *rentry = entry;
312 return EOK;
313error:
314 free(entry);
315 return rc;
316}
317
318/** Remove entry from start menu.
319 *
320 * @param tbsmenu Start menu
321 * @param entry Start menu entry
322 * @param paint @c true to repaint start menu
323 */
324void tbsmenu_remove(tbsmenu_t *tbsmenu, tbsmenu_entry_t *entry,
325 bool paint)
326{
327 assert(entry->tbsmenu == tbsmenu);
328
329 list_remove(&entry->lentries);
330
331 ui_menu_entry_destroy(entry->mentry);
332 free(entry->caption);
333 free(entry->cmd);
334 free(entry);
335}
336
337/** Handle start menu close request.
338 *
339 * @param menu Menu
340 * @param arg Argument (tbsmenu_t *)
341 * @param wnd_id Window ID
342 */
343static void tbsmenu_smenu_close_req(ui_menu_t *menu, void *arg)
344{
345 tbsmenu_t *tbsmenu = (tbsmenu_t *)arg;
346
347 (void)tbsmenu;
348 ui_menu_close(menu);
349}
350
351/** Start menu entry was activated.
352 *
353 * @param smentry Start menu entry
354 * @param arg Argument (tbsmenu_entry_t *)
355 */
356static void tbsmenu_smenu_entry_cb(ui_menu_entry_t *smentry, void *arg)
357{
358 tbsmenu_entry_t *entry = (tbsmenu_entry_t *)arg;
359
360 (void)tbsmenu_entry_start(entry);
361}
362
363/** Get first start menu entry.
364 *
365 * @param tbsmenu Start menu
366 * @return First entry or @c NULL if the menu is empty
367 */
368tbsmenu_entry_t *tbsmenu_first(tbsmenu_t *tbsmenu)
369{
370 link_t *link;
371
372 link = list_first(&tbsmenu->entries);
373 if (link == NULL)
374 return NULL;
375
376 return list_get_instance(link, tbsmenu_entry_t, lentries);
377}
378
379/** Get last start menu entry.
380 *
381 * @param tbsmenu Start menu
382 * @return Last entry or @c NULL if the menu is empty
383 */
384tbsmenu_entry_t *tbsmenu_last(tbsmenu_t *tbsmenu)
385{
386 link_t *link;
387
388 link = list_last(&tbsmenu->entries);
389 if (link == NULL)
390 return NULL;
391
392 return list_get_instance(link, tbsmenu_entry_t, lentries);
393}
394
395/** Get next start menu entry.
396 *
397 * @param cur Current entry
398 * @return Next entry or @c NULL if @a cur is the last entry
399 */
400tbsmenu_entry_t *tbsmenu_next(tbsmenu_entry_t *cur)
401{
402 link_t *link;
403
404 link = list_next(&cur->lentries, &cur->tbsmenu->entries);
405 if (link == NULL)
406 return NULL;
407
408 return list_get_instance(link, tbsmenu_entry_t, lentries);
409}
410
411/** Get number of start menu entries.
412 *
413 * @param tbsmenu Start menu
414 * @return Number of entries
415 */
416size_t tbsmenu_count(tbsmenu_t *tbsmenu)
417{
418 return list_count(&tbsmenu->entries);
419}
420
421/** Start button was depressed.
422 *
423 * @param pbutton Push button
424 * @param arg Argument (tbsmenu_entry_t *)
425 */
426static void tbsmenu_button_down(ui_pbutton_t *pbutton, void *arg)
427{
428 tbsmenu_t *tbsmenu = (tbsmenu_t *)arg;
429
430 if (!tbsmenu_is_open(tbsmenu)) {
431 tbsmenu_open(tbsmenu);
432 } else {
433 /* menu is open */
434 tbsmenu_close(tbsmenu);
435 }
436}
437
438/** Split command string into individual parts.
439 *
440 * Command arguments are separated by spaces. There is no way
441 * to provide an argument containing spaces.
442 *
443 * @param str Command with arguments separated by spaces
444 * @param cmd Command structure to fill in
445 * @return EOK on success or an error code
446 */
447static errno_t tbsmenu_cmd_split(const char *str, tbsmenu_cmd_t *cmd)
448{
449 char *arg;
450 char *next;
451 size_t cnt;
452
453 cmd->buf = str_dup(str);
454 if (cmd->buf == NULL)
455 return ENOMEM;
456
457 /* Count the entries */
458 cnt = 0;
459 arg = str_tok(cmd->buf, " ", &next);
460 while (arg != NULL) {
461 ++cnt;
462 arg = str_tok(next, " ", &next);
463 }
464
465 /* Need to copy again as buf was mangled */
466 free(cmd->buf);
467 cmd->buf = str_dup(str);
468 if (cmd->buf == NULL)
469 return ENOMEM;
470
471 cmd->argv = calloc(cnt + 1, sizeof(char *));
472 if (cmd->argv == NULL) {
473 free(cmd->buf);
474 return ENOMEM;
475 }
476
477 /* Fill in pointers */
478 cnt = 0;
479 arg = str_tok(cmd->buf, " ", &next);
480 while (arg != NULL) {
481 cmd->argv[cnt++] = arg;
482 arg = str_tok(next, " ", &next);
483 }
484
485 cmd->argv[cnt] = NULL;
486
487 return EOK;
488}
489
490/** Free command structure.
491 *
492 * @param cmd Command
493 */
494static void tbsmenu_cmd_fini(tbsmenu_cmd_t *cmd)
495{
496 free(cmd->argv);
497 free(cmd->buf);
498}
499
500/** Execute start menu entry.
501 *
502 * @param entry Start menu entry
503 *
504 * @return EOK on success or an error code
505 */
506static errno_t tbsmenu_entry_start(tbsmenu_entry_t *entry)
507{
508 task_id_t id;
509 task_wait_t wait;
510 task_exit_t texit;
511 tbsmenu_cmd_t cmd;
512 int retval;
513 bool suspended;
514 int i;
515 int cnt;
516 char **cp;
517 const char **targv = NULL;
518 errno_t rc;
519 ui_t *ui;
520
521 ui = ui_window_get_ui(entry->tbsmenu->window);
522 suspended = false;
523
524 rc = tbsmenu_cmd_split(entry->cmd, &cmd);
525 if (rc != EOK)
526 return rc;
527
528 /* Free up and clean console for the child task. */
529 rc = ui_suspend(ui);
530 if (rc != EOK)
531 goto error;
532
533 suspended = true;
534
535 /* Don't start in terminal if not running in a window */
536 if (entry->terminal && !ui_is_fullscreen(ui)) {
537 cnt = 0;
538 cp = cmd.argv;
539 while (*cp != NULL) {
540 ++cnt;
541 ++cp;
542 }
543
544 targv = calloc(cnt + 3, sizeof(char **));
545 if (targv == NULL)
546 goto error;
547
548 targv[0] = "/app/terminal";
549 targv[1] = "-c";
550
551 for (i = 0; i <= cnt; i++) {
552 targv[2 + i] = cmd.argv[i];
553 }
554
555 rc = task_spawnv(&id, &wait, targv[0], targv);
556 if (rc != EOK)
557 goto error;
558
559 free(targv);
560 targv = NULL;
561 } else {
562 rc = task_spawnv(&id, &wait, cmd.argv[0], (const char *const *)
563 cmd.argv);
564 if (rc != EOK)
565 goto error;
566 }
567
568 rc = task_wait(&wait, &texit, &retval);
569 if ((rc != EOK) || (texit != TASK_EXIT_NORMAL))
570 goto error;
571
572 /* Resume UI operation */
573 rc = ui_resume(ui);
574 if (rc != EOK)
575 goto error;
576
577 (void) ui_paint(ui);
578 return EOK;
579error:
580 if (targv != NULL)
581 free(targv);
582 tbsmenu_cmd_fini(&cmd);
583 if (suspended) {
584 rc = ui_resume(ui);
585 if (rc != EOK) {
586 printf("Failed to resume UI.\n");
587 exit(1);
588 }
589 }
590 (void) ui_paint(ui);
591 return rc;
592}
593
594/** @}
595 */
Note: See TracBrowser for help on using the repository browser.