source: mainline/uspace/lib/tbarcfg/src/tbarcfg.c@ ca48672

Last change on this file since ca48672 was ca48672, checked in by Jiri Svoboda <jiri@…>, 8 days ago

loc_service_register() needs to take a port ID argument.

  • Property mode set to 100644
File size: 16.7 KB
Line 
1/*
2 * Copyright (c) 2025 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 libtbarcfg
30 * @{
31 */
32/**
33 * @file Taskbar configuration
34 */
35
36#include <async.h>
37#include <errno.h>
38#include <sif.h>
39#include <ipc/tbarcfg.h>
40#include <loc.h>
41#include <task.h>
42#include <tbarcfg/tbarcfg.h>
43#include <stdio.h>
44#include <stdlib.h>
45#include <str.h>
46#include "../private/tbarcfg.h"
47
48static void tbarcfg_notify_conn(ipc_call_t *, void *);
49static errno_t smenu_entry_save(smenu_entry_t *, sif_node_t *);
50
51/** Create taskbar configuration.
52 *
53 * @param repopath Pathname of the new menu repository
54 * @param rtbcfg Place to store pointer to taskbar configuration
55 * @return EOK on success or an error code
56 */
57errno_t tbarcfg_create(const char *repopath, tbarcfg_t **rtbcfg)
58{
59 tbarcfg_t *tbcfg;
60 sif_doc_t *doc = NULL;
61 sif_node_t *rnode;
62 sif_node_t *nentries;
63 errno_t rc;
64
65 tbcfg = calloc(1, sizeof(tbarcfg_t));
66 if (tbcfg == NULL) {
67 rc = ENOMEM;
68 goto error;
69 }
70
71 list_initialize(&tbcfg->entries);
72 tbcfg->cfgpath = str_dup(repopath);
73 if (tbcfg->cfgpath == NULL) {
74 rc = ENOMEM;
75 goto error;
76 }
77
78 rc = sif_new(&doc);
79 if (rc != EOK)
80 goto error;
81
82 rnode = sif_get_root(doc);
83
84 rc = sif_node_append_child(rnode, "entries", &nentries);
85 if (rc != EOK)
86 goto error;
87
88 (void)nentries;
89
90 rc = sif_save(doc, repopath);
91 if (rc != EOK)
92 goto error;
93
94 sif_delete(doc);
95
96 *rtbcfg = tbcfg;
97 return EOK;
98error:
99 if (doc != NULL)
100 sif_delete(doc);
101 if (tbcfg != NULL && tbcfg->cfgpath != NULL)
102 free(tbcfg->cfgpath);
103 if (tbcfg != NULL)
104 free(tbcfg);
105 return rc;
106}
107
108/** Open taskbar configuration.
109 *
110 * @param repopath Pathname of the menu repository
111 * @param rtbcfg Place to store pointer to taskbar configuration
112 * @return EOK on success or an error code
113 */
114errno_t tbarcfg_open(const char *repopath, tbarcfg_t **rtbcfg)
115{
116 tbarcfg_t *tbcfg;
117 sif_doc_t *doc = NULL;
118 sif_node_t *nentries;
119 sif_node_t *rnode;
120 sif_node_t *nentry;
121 const char *ntype;
122 const char *separator;
123 const char *caption;
124 const char *cmd;
125 const char *terminal = NULL;
126 errno_t rc;
127
128 tbcfg = calloc(1, sizeof(tbarcfg_t));
129 if (tbcfg == NULL) {
130 rc = ENOMEM;
131 goto error;
132 }
133
134 list_initialize(&tbcfg->entries);
135 tbcfg->cfgpath = str_dup(repopath);
136 if (tbcfg->cfgpath == NULL) {
137 rc = ENOMEM;
138 goto error;
139 }
140
141 rc = sif_load(repopath, &doc);
142 if (rc != EOK)
143 goto error;
144
145 rnode = sif_get_root(doc);
146 nentries = sif_node_first_child(rnode);
147 ntype = sif_node_get_type(nentries);
148 if (str_cmp(ntype, "entries") != 0) {
149 rc = EIO;
150 goto error;
151 }
152
153 nentry = sif_node_first_child(nentries);
154 while (nentry != NULL) {
155 ntype = sif_node_get_type(nentry);
156 if (str_cmp(ntype, "entry") != 0) {
157 rc = EIO;
158 goto error;
159 }
160
161 separator = sif_node_get_attr(nentry, "separator");
162 if (separator != NULL && str_cmp(separator, "y") != 0) {
163 rc = EIO;
164 goto error;
165 }
166
167 if (separator == NULL) {
168 caption = sif_node_get_attr(nentry, "caption");
169 if (caption == NULL) {
170 rc = EIO;
171 goto error;
172 }
173
174 cmd = sif_node_get_attr(nentry, "cmd");
175 if (cmd == NULL) {
176 rc = EIO;
177 goto error;
178 }
179
180 terminal = sif_node_get_attr(nentry, "terminal");
181 if (terminal == NULL)
182 terminal = "n";
183
184 rc = smenu_entry_create(tbcfg, caption, cmd,
185 str_cmp(terminal, "y") == 0, NULL);
186 if (rc != EOK)
187 goto error;
188 } else {
189 rc = smenu_entry_sep_create(tbcfg, NULL);
190 if (rc != EOK)
191 goto error;
192 }
193
194 nentry = sif_node_next_child(nentry);
195 }
196
197 sif_delete(doc);
198 *rtbcfg = tbcfg;
199 return EOK;
200error:
201 if (doc != NULL)
202 sif_delete(doc);
203 if (tbcfg != NULL && tbcfg->cfgpath != NULL)
204 free(tbcfg->cfgpath);
205 if (tbcfg != NULL)
206 free(tbcfg);
207 return rc;
208}
209
210/** Close taskbar configuration.
211 *
212 * @param tbcfg Start menu
213 */
214void tbarcfg_close(tbarcfg_t *tbcfg)
215{
216 smenu_entry_t *entry;
217
218 entry = tbarcfg_smenu_first(tbcfg);
219 while (entry != NULL) {
220 smenu_entry_destroy(entry);
221 entry = tbarcfg_smenu_first(tbcfg);
222 }
223
224 free(tbcfg->cfgpath);
225 free(tbcfg);
226}
227
228/** Synchronize taskbar configuration to config file.
229 *
230 * @param repopath Pathname of the menu repository
231 * @param rtbcfg Place to store pointer to taskbar configuration
232 * @return EOK on success or an error code
233 */
234errno_t tbarcfg_sync(tbarcfg_t *tbcfg)
235{
236 sif_doc_t *doc = NULL;
237 sif_node_t *nentries;
238 sif_node_t *rnode;
239 smenu_entry_t *entry;
240 errno_t rc;
241
242 rc = sif_new(&doc);
243 if (rc != EOK)
244 goto error;
245
246 rnode = sif_get_root(doc);
247
248 rc = sif_node_append_child(rnode, "entries", &nentries);
249 if (rc != EOK)
250 goto error;
251
252 entry = tbarcfg_smenu_first(tbcfg);
253 while (entry != NULL) {
254 rc = smenu_entry_save(entry, nentries);
255 if (rc != EOK)
256 goto error;
257
258 entry = tbarcfg_smenu_next(entry);
259 }
260
261 rc = sif_save(doc, tbcfg->cfgpath);
262 if (rc != EOK)
263 goto error;
264
265 sif_delete(doc);
266 return EOK;
267error:
268 if (doc != NULL)
269 sif_delete(doc);
270 if (tbcfg != NULL && tbcfg->cfgpath != NULL)
271 free(tbcfg->cfgpath);
272 if (tbcfg != NULL)
273 free(tbcfg);
274 return rc;
275}
276
277/** Get first start menu entry.
278 *
279 * @param tbcfg Taskbar configuration
280 * @return First entry or @c NULL if the menu is empty
281 */
282smenu_entry_t *tbarcfg_smenu_first(tbarcfg_t *tbcfg)
283{
284 link_t *link;
285
286 link = list_first(&tbcfg->entries);
287 if (link == NULL)
288 return NULL;
289
290 return list_get_instance(link, smenu_entry_t, lentries);
291}
292
293/** Get next start menu entry.
294 *
295 * @param cur Current entry
296 * @return Next entry or @c NULL if @a cur is the last entry
297 */
298smenu_entry_t *tbarcfg_smenu_next(smenu_entry_t *cur)
299{
300 link_t *link;
301
302 link = list_next(&cur->lentries, &cur->smenu->entries);
303 if (link == NULL)
304 return NULL;
305
306 return list_get_instance(link, smenu_entry_t, lentries);
307}
308
309/** Get last start menu entry.
310 *
311 * @param tbcfg Taskbar configuration
312 * @return Previous entry or @c NULL if the menu is empty
313 */
314smenu_entry_t *tbarcfg_smenu_last(tbarcfg_t *tbcfg)
315{
316 link_t *link;
317
318 link = list_last(&tbcfg->entries);
319 if (link == NULL)
320 return NULL;
321
322 return list_get_instance(link, smenu_entry_t, lentries);
323}
324
325/** Get previous start menu entry.
326 *
327 * @param cur Current entry
328 * @return Previous entry or @c NULL if @a cur is the last entry
329 */
330smenu_entry_t *tbarcfg_smenu_prev(smenu_entry_t *cur)
331{
332 link_t *link;
333
334 link = list_prev(&cur->lentries, &cur->smenu->entries);
335 if (link == NULL)
336 return NULL;
337
338 return list_get_instance(link, smenu_entry_t, lentries);
339}
340
341/** Get start menu entry caption.
342 *
343 * @param entry Start menu entry
344 * @return Caption (with accelerator markup)
345 */
346const char *smenu_entry_get_caption(smenu_entry_t *entry)
347{
348 assert(!entry->separator);
349 return entry->caption;
350}
351
352/** Get start menu entry command.
353 *
354 * @param entry Start menu entry
355 * @return Command to run
356 */
357const char *smenu_entry_get_cmd(smenu_entry_t *entry)
358{
359 assert(!entry->separator);
360 return entry->cmd;
361}
362
363/** Get start menu entry start in terminal flag.
364 *
365 * @param entry Start menu entry
366 * @return Start in terminal flag
367 */
368bool smenu_entry_get_terminal(smenu_entry_t *entry)
369{
370 assert(!entry->separator);
371 return entry->terminal;
372}
373
374/** Get start menu entry separator flag.
375 *
376 * @param entry Start menu entry
377 * @return Separator flag
378 */
379bool smenu_entry_get_separator(smenu_entry_t *entry)
380{
381 return entry->separator;
382}
383
384/** Set start menu entry caption.
385 *
386 * Note: To make the change visible to others and persistent,
387 * you must call @c tbarcfg_sync()
388 *
389 * @param entry Start menu entry
390 * @param caption New caption
391 * @return EOK on success, ENOMEM if out of memory
392 */
393errno_t smenu_entry_set_caption(smenu_entry_t *entry, const char *caption)
394{
395 char *dcap;
396
397 assert(!entry->separator);
398
399 dcap = str_dup(caption);
400 if (dcap == NULL)
401 return ENOMEM;
402
403 free(entry->caption);
404 entry->caption = dcap;
405 return EOK;
406}
407
408/** Set start menu entry command.
409 *
410 * Note: To make the change visible to others and persistent,
411 * you must call @c tbarcfg_sync()
412 *
413 * @param entry Start menu entry
414 * @param cmd New command
415 * @return EOK on success, ENOMEM if out of memory
416 */
417errno_t smenu_entry_set_cmd(smenu_entry_t *entry, const char *cmd)
418{
419 char *dcmd;
420
421 assert(!entry->separator);
422
423 dcmd = str_dup(cmd);
424 if (dcmd == NULL)
425 return ENOMEM;
426
427 free(entry->cmd);
428 entry->cmd = dcmd;
429 return EOK;
430}
431
432/** Set start menu entry start in terminal flag.
433 *
434 * Note: To make the change visible to others and persistent,
435 * you must call @c tbarcfg_sync()
436 *
437 * @param entry Start menu entry
438 * @param terminal Start in terminal flag
439 */
440void smenu_entry_set_terminal(smenu_entry_t *entry, bool terminal)
441{
442 assert(!entry->separator);
443 entry->terminal = terminal;
444}
445
446/** Save start menu entry using transaction.
447 *
448 * @param entry Start menu entry
449 * @param nentries Entries node
450 */
451static errno_t smenu_entry_save(smenu_entry_t *entry, sif_node_t *nentries)
452{
453 sif_node_t *nentry = NULL;
454 errno_t rc;
455
456 rc = sif_node_append_child(nentries, "entry", &nentry);
457 if (rc != EOK)
458 goto error;
459
460 if (entry->separator) {
461 rc = sif_node_set_attr(nentry, "separator", "y");
462 if (rc != EOK)
463 goto error;
464 } else {
465 rc = sif_node_set_attr(nentry, "cmd", entry->cmd);
466 if (rc != EOK)
467 goto error;
468
469 rc = sif_node_set_attr(nentry, "caption",
470 entry->caption);
471 if (rc != EOK)
472 goto error;
473
474 rc = sif_node_set_attr(nentry, "terminal",
475 entry->terminal ? "y" : "n");
476 if (rc != EOK)
477 goto error;
478 }
479
480 return EOK;
481error:
482 if (nentry != NULL)
483 sif_node_destroy(nentry);
484 return rc;
485}
486
487/** Create new start menu entry and append it to the start menu (internal).
488 *
489 * This only creates the entry in memory, but does not update the repository.
490 *
491 * @param smenu Start menu
492 * @param caption Caption
493 * @param cmd Command to run
494 * @param terminal Start in terminal
495 * @param rentry Place to store pointer to new entry or @c NULL
496 */
497errno_t smenu_entry_create(tbarcfg_t *smenu, const char *caption,
498 const char *cmd, bool terminal, smenu_entry_t **rentry)
499{
500 smenu_entry_t *entry;
501 errno_t rc;
502
503 entry = calloc(1, sizeof(smenu_entry_t));
504 if (entry == NULL) {
505 rc = ENOMEM;
506 goto error;
507 }
508
509 entry->caption = str_dup(caption);
510 if (entry->caption == NULL) {
511 rc = ENOMEM;
512 goto error;
513 }
514
515 entry->cmd = str_dup(cmd);
516 if (entry->cmd == NULL) {
517 rc = ENOMEM;
518 goto error;
519 }
520
521 entry->terminal = terminal;
522
523 entry->smenu = smenu;
524 list_append(&entry->lentries, &smenu->entries);
525 if (rentry != NULL)
526 *rentry = entry;
527 return EOK;
528error:
529 if (entry != NULL) {
530 if (entry->caption != NULL)
531 free(entry->caption);
532 if (entry->cmd != NULL)
533 free(entry->cmd);
534 free(entry);
535 }
536
537 return rc;
538}
539
540/** Create new start menu separator entry and append it to the start menu
541 * (internal).
542 *
543 * This only creates the entry in memory, but does not update the repository.
544 *
545 * @param smenu Start menu
546 * @param rentry Place to store pointer to new entry or @c NULL
547 */
548errno_t smenu_entry_sep_create(tbarcfg_t *smenu, smenu_entry_t **rentry)
549{
550 smenu_entry_t *entry;
551 errno_t rc;
552
553 entry = calloc(1, sizeof(smenu_entry_t));
554 if (entry == NULL) {
555 rc = ENOMEM;
556 goto error;
557 }
558
559 entry->separator = true;
560
561 entry->smenu = smenu;
562 list_append(&entry->lentries, &smenu->entries);
563 if (rentry != NULL)
564 *rentry = entry;
565
566 return EOK;
567error:
568 return rc;
569}
570
571/** Destroy start menu entry.
572 *
573 * This only deletes the entry from, but does not update the
574 * repository.
575 *
576 * @param entry Start menu entry
577 */
578void smenu_entry_destroy(smenu_entry_t *entry)
579{
580 list_remove(&entry->lentries);
581 if (entry->caption != NULL)
582 free(entry->caption);
583 if (entry->cmd != NULL)
584 free(entry->cmd);
585 free(entry);
586}
587
588/** Move start menu entry up.
589 *
590 * @param entry Start menu entry
591 */
592void smenu_entry_move_up(smenu_entry_t *entry)
593{
594 smenu_entry_t *prev;
595
596 prev = tbarcfg_smenu_prev(entry);
597 if (prev == NULL) {
598 /* Entry is already at first position, nothing to do. */
599 return;
600 }
601
602 list_remove(&entry->lentries);
603 list_insert_before(&entry->lentries, &prev->lentries);
604}
605
606/** Move start menu entry down.
607 *
608 * @param entry Start menu entry
609 */
610void smenu_entry_move_down(smenu_entry_t *entry)
611{
612 smenu_entry_t *next;
613
614 next = tbarcfg_smenu_next(entry);
615 if (next == NULL) {
616 /* Entry is already at last position, nothing to do. */
617 return;
618 }
619
620 list_remove(&entry->lentries);
621 list_insert_after(&entry->lentries, &next->lentries);
622}
623
624/** Create taskbar configuration listener.
625 *
626 * Listens for taskbar configuration change notifications.
627 *
628 * @param nchan Notification channel (TBARCFG_NOTIFY_DEFAULT)
629 * @param rlst Place to store pointer to new listener
630 * @return EOK on success or an error code
631 */
632errno_t tbarcfg_listener_create(const char *nchan, void (*cb)(void *),
633 void *arg, tbarcfg_listener_t **rlst)
634{
635 tbarcfg_listener_t *lst;
636 service_id_t svcid = 0;
637 loc_srv_t *srv = NULL;
638 task_id_t taskid;
639 char *svcname = NULL;
640 category_id_t catid;
641 port_id_t port = 0;
642 int rv;
643 errno_t rc;
644
645 lst = calloc(1, sizeof(tbarcfg_listener_t));
646 if (lst == NULL)
647 return ENOMEM;
648
649 lst->cb = cb;
650 lst->arg = arg;
651
652 rc = async_create_port(INTERFACE_TBARCFG_NOTIFY,
653 tbarcfg_notify_conn, (void *)lst, &port);
654 if (rc != EOK)
655 goto error;
656
657 rc = loc_server_register("tbarcfg-listener", &srv);
658 if (rc != EOK)
659 goto error;
660
661 taskid = task_get_id();
662
663 rv = asprintf(&svcname, "tbarcfg/%u", (unsigned)taskid);
664 if (rv < 0) {
665 rc = ENOMEM;
666 goto error;
667 }
668
669 rc = loc_service_register(srv, svcname, port, &svcid);
670 if (rc != EOK)
671 goto error;
672
673 rc = loc_category_get_id(nchan, &catid, 0);
674 if (rc != EOK)
675 goto error;
676
677 rc = loc_service_add_to_cat(srv, svcid, catid);
678 if (rc != EOK)
679 goto error;
680
681 *rlst = lst;
682 return EOK;
683error:
684 if (svcid != 0)
685 loc_service_unregister(srv, svcid);
686 if (srv != NULL)
687 loc_server_unregister(srv);
688 if (svcname != NULL)
689 free(svcname);
690 if (port != 0)
691 async_port_destroy(port);
692 return rc;
693}
694
695/** Destroy taskbar configuration listener.
696 *
697 * @param lst Listener
698 */
699void tbarcfg_listener_destroy(tbarcfg_listener_t *lst)
700{
701 free(lst);
702}
703
704/** Send taskbar configuration notification to a particular service ID.
705 *
706 * @param svcid Service ID
707 * @return EOK on success or an error code
708 */
709static errno_t tbarcfg_notify_svc(service_id_t svcid)
710{
711 async_sess_t *sess;
712 async_exch_t *exch;
713 errno_t rc;
714
715 sess = loc_service_connect(svcid, INTERFACE_TBARCFG_NOTIFY, 0);
716 if (sess == NULL)
717 return EIO;
718
719 exch = async_exchange_begin(sess);
720 rc = async_req_0_0(exch, TBARCFG_NOTIFY_NOTIFY);
721 if (rc != EOK) {
722 async_exchange_end(exch);
723 async_hangup(sess);
724 return rc;
725 }
726
727 async_exchange_end(exch);
728 async_hangup(sess);
729 return EOK;
730}
731
732/** Send taskbar configuration change notification.
733 *
734 * @param nchan Notification channel (TBARCFG_NOTIFY_DEFAULT)
735 */
736errno_t tbarcfg_notify(const char *nchan)
737{
738 errno_t rc;
739 category_id_t catid;
740 service_id_t *svcs = NULL;
741 size_t count, i;
742
743 rc = loc_category_get_id(nchan, &catid, 0);
744 if (rc != EOK)
745 return rc;
746
747 rc = loc_category_get_svcs(catid, &svcs, &count);
748 if (rc != EOK)
749 return rc;
750
751 for (i = 0; i < count; i++) {
752 rc = tbarcfg_notify_svc(svcs[i]);
753 if (rc != EOK)
754 goto error;
755 }
756
757 free(svcs);
758 return EOK;
759error:
760 free(svcs);
761 return rc;
762}
763
764/** Taskbar configuration connection handler.
765 *
766 * @param icall Initial call
767 * @param arg Argument (tbarcfg_listener_t *)
768 */
769static void tbarcfg_notify_conn(ipc_call_t *icall, void *arg)
770{
771 tbarcfg_listener_t *lst = (tbarcfg_listener_t *)arg;
772
773 /* Accept the connection */
774 async_accept_0(icall);
775
776 while (true) {
777 ipc_call_t call;
778 async_get_call(&call);
779 sysarg_t method = ipc_get_imethod(&call);
780
781 if (!method) {
782 /* The other side has hung up */
783 async_answer_0(&call, EOK);
784 return;
785 }
786
787 switch (method) {
788 case TBARCFG_NOTIFY_NOTIFY:
789 lst->cb(lst->arg);
790 async_answer_0(&call, EOK);
791 break;
792 default:
793 async_answer_0(&call, EINVAL);
794 }
795 }
796}
797
798/** @}
799 */
Note: See TracBrowser for help on using the repository browser.