Added a menu to read from a pipe.
[dana/openbox.git] / plugins / menu / timed_menu.c
1 #include <glib.h>
2 #include <stdlib.h>
3 #include <unistd.h>
4 #include <errno.h>
5 #include <string.h>
6
7 #include "kernel/menu.h"
8 #include "kernel/timer.h"
9 #include "timed_menu.h"
10 #include "kernel/action.h"
11 #include "kernel/event.h"
12
13 #define TIMED_MENU(m) ((Menu *)m)
14 #define TIMED_MENU_DATA(m) ((Timed_Menu_Data *)((Menu *)m)->plugin_data)
15 static char *PLUGIN_NAME = "timed_menu";
16
17 typedef enum {
18     TIMED_MENU_PIPE
19 } Timed_Menu_Type;
20
21 /* we can use various GIO channels to support reading menus (can we add them to
22    the event loop? )
23    stat() based update
24    exec() based read
25 */
26 typedef struct {
27     Timed_Menu_Type type;
28     Timer *timer; /* timer code handles free */
29     char *command; /* for the PIPE */
30     char *buf;
31     unsigned long buflen;
32     int fd;
33 } Timed_Menu_Data;
34
35
36 void plugin_setup_config() { }
37 void plugin_startup()
38 { }
39 void plugin_shutdown() { }
40
41 void timed_menu_clean_up(Menu *m) {
42     if (TIMED_MENU_DATA(m)->buf != NULL) {
43         fprintf(stderr, "%s", TIMED_MENU_DATA(m)->buf);
44         g_free(TIMED_MENU_DATA(m)->buf);
45         TIMED_MENU_DATA(m)->buf = NULL;
46     }
47
48     TIMED_MENU_DATA(m)->buflen = 0;
49
50     if (TIMED_MENU_DATA(m)->fd != -1) {
51         event_remove_fd(TIMED_MENU_DATA(m)->fd);
52         close(TIMED_MENU_DATA(m)->fd);
53         TIMED_MENU_DATA(m)->fd = -1;
54     }
55
56     /* child is reaped by glib ? */
57 }
58
59 void timed_menu_read_pipe(int fd, Menu *menu)
60 {
61     char *tmpbuf = NULL;
62     unsigned long num_read;
63 #ifdef DEBUG
64     Timed_Menu_Data *d = TIMED_MENU_DATA(menu);
65 #endif
66
67     unsigned long num_realloc;
68     /* if we have less than a quarter BUFSIZ left, allocate more */
69     num_realloc = (BUFSIZ - (TIMED_MENU_DATA(menu)->buflen % BUFSIZ) <
70                    BUFSIZ >> 2) ?
71                   0 : BUFSIZ;
72     
73     tmpbuf = g_try_realloc(TIMED_MENU_DATA(menu)->buf,             
74                            TIMED_MENU_DATA(menu)->buflen + num_realloc);
75
76     if (tmpbuf == NULL) {
77         g_warning("Unable to allocate memory for read()");
78         return;
79     }
80     
81     TIMED_MENU_DATA(menu)->buf = tmpbuf;
82     
83     num_read = read(fd,
84                     TIMED_MENU_DATA(menu)->buf + TIMED_MENU_DATA(menu)->buflen,
85                     num_realloc);
86     if (num_read == 0) {
87         unsigned long count = 0;
88         char *found = NULL;
89         menu->invalid = TRUE;
90         menu_clear(menu);
91
92         /* TEMP: list them */
93         while (NULL !=
94                (found = strchr(&TIMED_MENU_DATA(menu)->buf[count], '\n'))) {
95             TIMED_MENU_DATA(menu)->buf
96                 [found - TIMED_MENU_DATA(menu)->buf] = '\0';
97             menu_add_entry(menu,
98                 menu_entry_new_separator
99                 (&TIMED_MENU_DATA(menu)->buf[count]));
100             count = found - TIMED_MENU_DATA(menu)->buf + 1;
101         }
102             
103
104         TIMED_MENU_DATA(menu)->buf[TIMED_MENU_DATA(menu)->buflen] = '\0';
105         timed_menu_clean_up(menu);
106     } else if (num_read > 0) {
107         TIMED_MENU_DATA(menu)->buflen += num_read;
108         TIMED_MENU_DATA(menu)->buf[TIMED_MENU_DATA(menu)->buflen] = '\0';
109     } else { /* num_read < 1 */
110         g_warning("Error on read %s", strerror(errno));
111         timed_menu_clean_up(menu);
112     }
113 }
114
115 void timed_menu_timeout_handler(Menu *data)
116 {
117     Action *a;
118
119     if (!data->shown && TIMED_MENU_DATA(data)->fd == -1) {
120         switch (TIMED_MENU_DATA(data)->type) {
121             case (TIMED_MENU_PIPE):
122             {
123                 /* if the menu is not shown, run a process and use its output
124                    as menu */
125
126                 /* I hate you glib in all your hideous forms */
127                 char *args[] = {"/bin/sh", "-c", TIMED_MENU_DATA(data)->command,
128                                 NULL};
129                 int child_stdout;
130                 if (g_spawn_async_with_pipes(
131                         NULL,
132                         args,
133                         NULL,
134                         G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
135                         NULL,
136                         NULL,
137                         NULL,
138                         NULL,
139                         &child_stdout,
140                         NULL,
141                         NULL)) {
142                     event_fd_handler *h = g_new(event_fd_handler, 1);
143                     TIMED_MENU_DATA(data)->fd = h->fd = child_stdout;
144                     h->handler = timed_menu_read_pipe;
145                     h->data = data;
146                     event_add_fd_handler(h);
147                 } else {
148                     g_warning("unable to spawn child");
149                 }
150                     
151                 break;
152             }
153         }
154     }
155 }
156
157 void *plugin_create()
158 {
159     Timed_Menu_Data *d = g_new(Timed_Menu_Data, 1);
160     Menu *m = menu_new("", PLUGIN_NAME, NULL);
161     
162     m->plugin = PLUGIN_NAME;
163
164     d->type = TIMED_MENU_PIPE;
165     d->timer = timer_start(60000000, &timed_menu_timeout_handler, m);
166     d->command = "find /media -name *.m3u";
167     d->buf = NULL;
168     d->buflen = 0;
169     d->fd = -1;
170     
171     m->plugin_data = (void *)d;
172
173     timed_menu_timeout_handler(m);
174     return (void *)m;
175 }
176
177 void plugin_destroy (void *m)
178 {
179     /* this will be freed by timer_* */
180     timer_stop( ((Timed_Menu_Data *)TIMED_MENU(m)->plugin_data)->timer);
181     
182     g_free( TIMED_MENU(m)->plugin_data );
183 }