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