]> icculus.org git repositories - mikachu/openbox.git/blob - plugins/menu/fifo_menu.c
Menu parsing updates for plugins.
[mikachu/openbox.git] / plugins / menu / fifo_menu.c
1 #include <glib.h>
2 #include <sys/types.h>
3 #include <sys/stat.h>
4 #include <errno.h>
5 #include <fcntl.h>
6 #include <unistd.h>
7 #include <string.h>
8 #include <stdio.h>
9
10 #include "kernel/menu.h"
11 #include "kernel/event.h"
12
13 static char *PLUGIN_NAME = "fifo_menu";
14
15 typedef struct Fifo_Menu_Data{
16     char *fifo;
17     char *buf; /* buffer to hold partially read menu */
18     unsigned long buflen; /* how many bytes are in the buffer */
19     int fd; /* file descriptor to read menu from */
20     event_fd_handler *handler;
21 } Fifo_Menu_Data;
22
23 #define FIFO_MENU(m) ((ObMenu *)m)
24 #define FIFO_MENU_DATA(m) ((Fifo_Menu_Data *)((ObMenu *)m)->plugin_data)
25
26
27 void fifo_menu_clean_up(ObMenu *m) {
28     if (FIFO_MENU_DATA(m)->buf != NULL) {
29         g_free(FIFO_MENU_DATA(m)->buf);
30         FIFO_MENU_DATA(m)->buf = NULL;
31         FIFO_MENU_DATA(m)->buflen = 0;
32     }
33
34     if (FIFO_MENU_DATA(m)->fd != -1) {
35         close(FIFO_MENU_DATA(m)->fd);
36         FIFO_MENU_DATA(m)->fd = -1;
37     }
38 }
39
40 void plugin_setup_config() { }
41 void plugin_startup()
42 { }
43 void plugin_shutdown() { }
44
45 void fifo_menu_handler(int fd, void *d) {
46     ObMenu *menu = d;
47     char *tmpbuf = NULL;
48     unsigned long num_read;
49 #ifdef DEBUG
50     /* because gdb is dumb */
51 #if 0
52     Fifo_Menu_Data *d = FIFO_MENU_DATA(menu);
53 #endif
54 #endif
55     
56     /* if the menu is shown this will go into busy loop :(
57      fix me*/
58     if (!menu->shown) { 
59         unsigned long num_realloc;
60         /* if we have less than a quarter BUFSIZ left, allocate more */
61         num_realloc = (BUFSIZ - (FIFO_MENU_DATA(menu)->buflen % BUFSIZ) <
62                        BUFSIZ >> 2) ?
63             0 : BUFSIZ;
64     
65         tmpbuf = g_try_realloc(FIFO_MENU_DATA(menu)->buf,             
66                                FIFO_MENU_DATA(menu)->buflen + num_realloc);
67
68         if (tmpbuf == NULL) {
69             g_warning("Unable to allocate memory for read()");
70             return;
71         }
72     
73         FIFO_MENU_DATA(menu)->buf = tmpbuf;
74     
75         num_read = read(fd,
76                         FIFO_MENU_DATA(menu)->buf +
77                         FIFO_MENU_DATA(menu)->buflen,
78                         num_realloc);
79
80         if (num_read == 0) { /* eof */
81             unsigned long count = 0;
82             char *found = NULL;
83
84             menu->invalid = TRUE;
85             menu_clear(menu);
86
87             FIFO_MENU_DATA(menu)->buf[FIFO_MENU_DATA(menu)->buflen] = '\0';
88
89             xmlDocPtr doc = xmlParseMemory(FIFO_MENU_DATA(menu)->buf,
90                                            FIFO_MENU_DATA(menu)->buflen);
91
92             xmlNodePtr node = xmlDocGetRootElement(doc);
93
94             if (!xmlStrcasecmp(node->name, (const xmlChar*) "fifo")) {
95                 if ((node = parse_find_node("item", node->xmlChildrenNode)))
96                     parse_menu_full(doc, node, menu, FALSE);
97             }
98             
99             fifo_menu_clean_up(menu);
100             
101             event_remove_fd(FIFO_MENU_DATA(menu)->handler->fd);
102         
103             if ((FIFO_MENU_DATA(menu)->fd =
104                  open(FIFO_MENU_DATA(menu)->fifo,
105                       O_NONBLOCK | O_RDONLY)) == -1) {
106                 g_warning("Can't reopen FIFO");
107                 fifo_menu_clean_up(menu);
108                 return;
109             }
110
111             FIFO_MENU_DATA(menu)->handler->fd = FIFO_MENU_DATA(menu)->fd;
112         
113             event_add_fd_handler(FIFO_MENU_DATA(menu)->handler);
114         } else if (num_read > 0) {
115             FIFO_MENU_DATA(menu)->buflen += num_read;
116             FIFO_MENU_DATA(menu)->buf[FIFO_MENU_DATA(menu)->buflen] = '\0';
117         }
118     }
119 }
120
121 void plugin_destroy (ObMenu *m)
122 {
123     fifo_menu_clean_up(m);
124     if (FIFO_MENU_DATA(m)->handler != NULL) {
125         g_free(FIFO_MENU_DATA(m)->handler);
126         FIFO_MENU_DATA(m)->handler = NULL;
127     }
128
129     if (FIFO_MENU_DATA(m)->fifo != NULL) {
130         g_free(FIFO_MENU_DATA(m)->fifo);
131         FIFO_MENU_DATA(m)->fifo = NULL;
132     }
133
134     g_free(m->plugin_data);
135
136     menu_free(m->name);
137 }
138
139 void *plugin_create(PluginMenuCreateData *data)
140
141
142 {
143     char *fifo;
144     char *dir;
145     event_fd_handler *h;
146     Fifo_Menu_Data *d;
147     ObMenu *m;
148     char *label = NULL, *id = NULL;
149         
150     d = g_new(Fifo_Menu_Data, 1);
151
152     parse_attr_string("id", data->node, &id);
153     parse_attr_string("label", data->node, &label);
154
155     m = menu_new( (label != NULL ? label : ""),
156                  (id != NULL ? id : PLUGIN_NAME),
157                  data->parent);
158     menu_add_entry(data->parent, menu_entry_new_submenu(
159                        (label != NULL ? label : ""),
160                        m));
161
162     g_free(label);
163     g_free(id);
164     d->fd = -1;
165     d->buf = NULL;
166     d->buflen = 0;
167     d->handler = NULL;
168     
169     m->plugin = PLUGIN_NAME;
170
171     d->fd = -1;
172     
173     m->plugin_data = (void *)d;
174
175     dir = g_build_filename(g_get_home_dir(), ".openbox",
176                           PLUGIN_NAME, NULL);
177
178     if (mkdir(dir, S_IRWXU | S_IRWXG | S_IRWXO) == -1 && errno != EEXIST) {
179 /* technically, if ~/.openbox/fifo_menu exists and isn't a directory
180    this will fail, but we don't care because mkfifo will fail and warn
181    anyway */
182         g_warning("Can't create %s: %s", dir, strerror(errno));
183         g_free(dir);
184         plugin_destroy(m);
185         return NULL;
186     }
187
188     fifo = g_build_filename(g_get_home_dir(), ".openbox",
189                             PLUGIN_NAME,
190                             m->name, NULL);
191     if (mkfifo(fifo, S_IRUSR | S_IWUSR |
192                S_IRGRP | S_IWGRP | /* let umask do its thing */
193                S_IROTH | S_IWOTH) == -1 && errno != EEXIST) {
194         g_warning("Can't create FIFO %s: %s", fifo, strerror(errno));
195         g_free(fifo);
196         g_free(d);
197         menu_free(m->name);
198         return NULL;
199     }
200
201 /* open in non-blocking mode so we don't wait for a process to open FIFO
202    for writing */
203     if ((d->fd = open(fifo, O_NONBLOCK | O_RDONLY)) == -1) { 
204         g_warning("Can't open FIFO %s: %s", fifo, strerror(errno));
205         g_free(fifo);
206         g_free(d);
207         menu_free(m->name);
208         return NULL;
209     }
210
211     d->fifo = fifo;
212     
213     h = g_new(event_fd_handler, 1);
214
215     if (h == NULL) {
216         g_warning("Out of memory");
217         close(d->fd);
218         g_free(fifo);
219         g_free(d);
220         menu_free(m->name);
221         return NULL;
222     }
223     
224     h->fd = d->fd;
225     h->data = m;
226     h->handler = fifo_menu_handler;
227     d->handler = h;
228     
229     event_add_fd_handler(h);
230     
231     g_free(dir);
232     return (void *)m;
233 }