source: mainline/uspace/app/taskbar/tbsmenu.c@ 489f405

ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 489f405 was 5d9403d5, checked in by Jiri Svoboda <jiri@…>, 2 years 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: 11.1 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 start menu
33 */
34
35#include <gfx/coord.h>
36#include <startmenu/startmenu.h>
37#include <stdbool.h>
38#include <stddef.h>
39#include <stdlib.h>
40#include <str.h>
41#include <task.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_clicked(ui_pbutton_t *, void *);
58
59/** Start button callbacks */
60static ui_pbutton_cb_t tbsmenu_button_cb = {
61 .clicked = tbsmenu_button_clicked
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 task bar 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 startmenu_t *smenu = NULL;
130 startmenu_entry_t *sme;
131 const char *caption;
132 const char *cmd;
133 errno_t rc;
134
135 rc = startmenu_open(repopath, &smenu);
136 if (rc != EOK)
137 goto error;
138
139 sme = startmenu_first(smenu);
140 while (sme != NULL) {
141 caption = startmenu_entry_get_caption(sme);
142 cmd = startmenu_entry_get_cmd(sme);
143
144 rc = tbsmenu_add(tbsmenu, caption, cmd, &tentry);
145 if (rc != EOK)
146 goto error;
147
148 (void)tentry;
149
150 sme = startmenu_next(sme);
151 }
152
153 startmenu_close(smenu);
154 return EOK;
155error:
156 if (smenu != NULL)
157 startmenu_close(smenu);
158 return rc;
159}
160
161/** Set start menu rectangle.
162 *
163 * @param tbsmenu Start menu
164 * @param rect Rectangle
165 */
166void tbsmenu_set_rect(tbsmenu_t *tbsmenu, gfx_rect_t *rect)
167{
168 tbsmenu->rect = *rect;
169 ui_pbutton_set_rect(tbsmenu->sbutton, rect);
170}
171
172/** Open taskbar start menu.
173 *
174 * @param tbsmenu Start menu
175 */
176void tbsmenu_open(tbsmenu_t *tbsmenu)
177{
178 (void) ui_menu_open(tbsmenu->smenu, &tbsmenu->rect,
179 tbsmenu->ev_idev_id);
180}
181
182/** Close taskbar start menu.
183 *
184 * @param tbsmenu Start menu
185 */
186void tbsmenu_close(tbsmenu_t *tbsmenu)
187{
188 ui_menu_close(tbsmenu->smenu);
189}
190
191/** Determine if taskbar start menu is open.
192 *
193 * @param tbsmenu Start menu
194 * @return @c true iff start menu is open
195 */
196bool tbsmenu_is_open(tbsmenu_t *tbsmenu)
197{
198 return ui_menu_is_open(tbsmenu->smenu);
199}
200
201/** Destroy task bar start menu.
202 *
203 * @param tbsmenu Start menu
204 */
205void tbsmenu_destroy(tbsmenu_t *tbsmenu)
206{
207 tbsmenu_entry_t *entry;
208
209 /* Destroy entries */
210 entry = tbsmenu_first(tbsmenu);
211 while (entry != NULL) {
212 tbsmenu_remove(tbsmenu, entry, false);
213 entry = tbsmenu_first(tbsmenu);
214 }
215
216 ui_fixed_remove(tbsmenu->fixed, ui_pbutton_ctl(tbsmenu->sbutton));
217 ui_pbutton_destroy(tbsmenu->sbutton);
218 ui_menu_destroy(tbsmenu->smenu);
219
220 free(tbsmenu);
221}
222
223/** Add entry to start menu.
224 *
225 * @param tbsmenu Start menu
226 * @param caption Caption
227 * @param cmd Command to run
228 * @param entry Start menu entry
229 * @return @c EOK on success or an error code
230 */
231errno_t tbsmenu_add(tbsmenu_t *tbsmenu, const char *caption,
232 const char *cmd, tbsmenu_entry_t **rentry)
233{
234 errno_t rc;
235 tbsmenu_entry_t *entry;
236
237 entry = calloc(1, sizeof(tbsmenu_entry_t));
238 if (entry == NULL)
239 return ENOMEM;
240
241 entry->caption = str_dup(caption);
242 if (entry->caption == NULL) {
243 rc = ENOMEM;
244 goto error;
245 }
246
247 entry->cmd = str_dup(cmd);
248 if (entry->cmd == NULL) {
249 rc = ENOMEM;
250 goto error;
251 }
252
253 rc = ui_menu_entry_create(tbsmenu->smenu, caption, "", &entry->mentry);
254 if (rc != EOK)
255 goto error;
256
257 ui_menu_entry_set_cb(entry->mentry, tbsmenu_smenu_entry_cb,
258 (void *)entry);
259
260 entry->tbsmenu = tbsmenu;
261 list_append(&entry->lentries, &tbsmenu->entries);
262 *rentry = entry;
263 return EOK;
264error:
265 if (entry->caption != NULL)
266 free(entry->caption);
267 if (entry->cmd != NULL)
268 free(entry->cmd);
269 free(entry);
270 return rc;
271}
272
273/** Remove entry from start menu.
274 *
275 * @param tbsmenu Start menu
276 * @param entry Start menu entry
277 * @param paint @c true to repaint start menu
278 */
279void tbsmenu_remove(tbsmenu_t *tbsmenu, tbsmenu_entry_t *entry,
280 bool paint)
281{
282 assert(entry->tbsmenu == tbsmenu);
283
284 list_remove(&entry->lentries);
285
286 ui_menu_entry_destroy(entry->mentry);
287 free(entry->caption);
288 free(entry->cmd);
289 free(entry);
290}
291
292/** Handle start menu close request.
293 *
294 * @param menu Menu
295 * @param arg Argument (tbsmenu_t *)
296 * @param wnd_id Window ID
297 */
298static void tbsmenu_smenu_close_req(ui_menu_t *menu, void *arg)
299{
300 tbsmenu_t *tbsmenu = (tbsmenu_t *)arg;
301
302 (void)tbsmenu;
303 ui_menu_close(menu);
304}
305
306/** Start menu entry was activated.
307 *
308 * @param smentry Start menu entry
309 * @param arg Argument (tbsmenu_entry_t *)
310 */
311static void tbsmenu_smenu_entry_cb(ui_menu_entry_t *smentry, void *arg)
312{
313 tbsmenu_entry_t *entry = (tbsmenu_entry_t *)arg;
314
315 (void)tbsmenu_entry_start(entry);
316}
317
318/** Get first start menu entry.
319 *
320 * @param tbsmenu Start menu
321 * @return First entry or @c NULL if the menu is empty
322 */
323tbsmenu_entry_t *tbsmenu_first(tbsmenu_t *tbsmenu)
324{
325 link_t *link;
326
327 link = list_first(&tbsmenu->entries);
328 if (link == NULL)
329 return NULL;
330
331 return list_get_instance(link, tbsmenu_entry_t, lentries);
332}
333
334/** Get last start menu entry.
335 *
336 * @param tbsmenu Start menu
337 * @return Last entry or @c NULL if the menu is empty
338 */
339tbsmenu_entry_t *tbsmenu_last(tbsmenu_t *tbsmenu)
340{
341 link_t *link;
342
343 link = list_last(&tbsmenu->entries);
344 if (link == NULL)
345 return NULL;
346
347 return list_get_instance(link, tbsmenu_entry_t, lentries);
348}
349
350/** Get next start menu entry.
351 *
352 * @param cur Current entry
353 * @return Next entry or @c NULL if @a cur is the last entry
354 */
355tbsmenu_entry_t *tbsmenu_next(tbsmenu_entry_t *cur)
356{
357 link_t *link;
358
359 link = list_next(&cur->lentries, &cur->tbsmenu->entries);
360 if (link == NULL)
361 return NULL;
362
363 return list_get_instance(link, tbsmenu_entry_t, lentries);
364}
365
366/** Get number of start menu entries.
367 *
368 * @param tbsmenu Start menu
369 * @return Number of entries
370 */
371size_t tbsmenu_count(tbsmenu_t *tbsmenu)
372{
373 return list_count(&tbsmenu->entries);
374}
375
376/** Start button was clicked.
377 *
378 * @param pbutton Push button
379 * @param arg Argument (tbsmenu_entry_t *)
380 */
381static void tbsmenu_button_clicked(ui_pbutton_t *pbutton, void *arg)
382{
383 tbsmenu_t *tbsmenu = (tbsmenu_t *)arg;
384
385 if (!tbsmenu_is_open(tbsmenu)) {
386 tbsmenu_open(tbsmenu);
387 } else {
388 /* menu is open */
389 tbsmenu_close(tbsmenu);
390 }
391}
392
393/** Split command string into individual parts.
394 *
395 * Command arguments are separated by spaces. There is no way
396 * to provide an argument containing spaces.
397 *
398 * @param str Command with arguments separated by spaces
399 * @param cmd Command structure to fill in
400 * @return EOK on success or an error code
401 */
402static errno_t tbsmenu_cmd_split(const char *str, tbsmenu_cmd_t *cmd)
403{
404 char *arg;
405 char *next;
406 size_t cnt;
407
408 cmd->buf = str_dup(str);
409 if (cmd->buf == NULL)
410 return ENOMEM;
411
412 /* Count the entries */
413 cnt = 0;
414 arg = str_tok(cmd->buf, " ", &next);
415 while (arg != NULL) {
416 ++cnt;
417 arg = str_tok(next, " ", &next);
418 }
419
420 /* Need to copy again as buf was mangled */
421 free(cmd->buf);
422 cmd->buf = str_dup(str);
423 if (cmd->buf == NULL)
424 return ENOMEM;
425
426 cmd->argv = calloc(cnt + 1, sizeof(char *));
427 if (cmd->argv == NULL) {
428 free(cmd->buf);
429 return ENOMEM;
430 }
431
432 /* Fill in pointers */
433 cnt = 0;
434 arg = str_tok(cmd->buf, " ", &next);
435 while (arg != NULL) {
436 cmd->argv[cnt++] = arg;
437 arg = str_tok(next, " ", &next);
438 }
439
440 return EOK;
441}
442
443/** Free command structure.
444 *
445 * @param cmd Command
446 */
447static void tbsmenu_cmd_fini(tbsmenu_cmd_t *cmd)
448{
449 free(cmd->argv);
450 free(cmd->buf);
451}
452
453/** Execute start menu entry.
454 *
455 * @param entry Start menu entry
456 *
457 * @return EOK on success or an error code
458 */
459static errno_t tbsmenu_entry_start(tbsmenu_entry_t *entry)
460{
461 task_id_t id;
462 task_wait_t wait;
463 task_exit_t texit;
464 tbsmenu_cmd_t cmd;
465 int retval;
466 bool suspended;
467 errno_t rc;
468 ui_t *ui;
469
470 ui = ui_window_get_ui(entry->tbsmenu->window);
471 suspended = false;
472
473 rc = tbsmenu_cmd_split(entry->cmd, &cmd);
474 if (rc != EOK)
475 return rc;
476
477 /* Free up and clean console for the child task. */
478 rc = ui_suspend(ui);
479 if (rc != EOK)
480 goto error;
481
482 suspended = true;
483
484 rc = task_spawnv(&id, &wait, cmd.argv[0], (const char *const *)
485 cmd.argv);
486 if (rc != EOK)
487 goto error;
488
489 rc = task_wait(&wait, &texit, &retval);
490 if ((rc != EOK) || (texit != TASK_EXIT_NORMAL))
491 goto error;
492
493 /* Resume UI operation */
494 rc = ui_resume(ui);
495 if (rc != EOK)
496 goto error;
497
498 (void) ui_paint(ui);
499 return EOK;
500error:
501 tbsmenu_cmd_fini(&cmd);
502 if (suspended)
503 (void) ui_resume(ui);
504 (void) ui_paint(ui);
505 return rc;
506}
507
508/** @}
509 */
Note: See TracBrowser for help on using the repository browser.