12 #include "kernel/menu.h"
13 #include "kernel/timer.h"
14 #include "timed_menu.h"
15 #include "kernel/action.h"
16 #include "kernel/event.h"
18 #define TIMED_MENU(m) ((ObMenu *)m)
19 #define TIMED_MENU_DATA(m) ((Timed_Menu_Data *)((ObMenu *)m)->plugin_data)
20 static char *PLUGIN_NAME
= "timed_menu";
23 TIMED_MENU_PIPE
, /* read entries from a child process' output */
24 TIMED_MENU_STAT
/* reread entries from file when timestamp changes */
30 char *command
; /* for the PIPE */
31 char *buf
; /* buffer to hold partially read menu */
32 unsigned long buflen
; /* how many bytes are in the buffer */
33 int fd
; /* file descriptor to read menu from */
34 pid_t pid
; /* pid of child process in PIPE */
35 time_t mtime
; /* time of last modification */
39 void plugin_setup_config() { }
42 void plugin_shutdown() { }
44 void timed_menu_clean_up(ObMenu
*m
) {
45 if (TIMED_MENU_DATA(m
)->buf
!= NULL
) {
46 fprintf(stderr
, "%s", TIMED_MENU_DATA(m
)->buf
);
47 g_free(TIMED_MENU_DATA(m
)->buf
);
48 TIMED_MENU_DATA(m
)->buf
= NULL
;
51 TIMED_MENU_DATA(m
)->buflen
= 0;
53 if (TIMED_MENU_DATA(m
)->fd
!= -1) {
54 event_remove_fd(TIMED_MENU_DATA(m
)->fd
);
55 close(TIMED_MENU_DATA(m
)->fd
);
56 TIMED_MENU_DATA(m
)->fd
= -1;
59 if (TIMED_MENU_DATA(m
)->pid
!= -1) {
60 waitpid(TIMED_MENU_DATA(m
)->pid
, NULL
, 0);
61 TIMED_MENU_DATA(m
)->pid
= -1;
64 TIMED_MENU_DATA(m
)->mtime
= 0;
67 void timed_menu_read_pipe(int fd
, void *d
)
71 unsigned long num_read
;
73 /* because gdb is dumb */
75 Timed_Menu_Data
*d
= TIMED_MENU_DATA(menu
);
79 unsigned long num_realloc
;
80 /* if we have less than a quarter BUFSIZ left, allocate more */
81 num_realloc
= (BUFSIZ
- (TIMED_MENU_DATA(menu
)->buflen
% BUFSIZ
) <
85 tmpbuf
= g_try_realloc(TIMED_MENU_DATA(menu
)->buf
,
86 TIMED_MENU_DATA(menu
)->buflen
+ num_realloc
);
89 g_warning("Unable to allocate memory for read()");
93 TIMED_MENU_DATA(menu
)->buf
= tmpbuf
;
96 TIMED_MENU_DATA(menu
)->buf
+ TIMED_MENU_DATA(menu
)->buflen
,
102 menu
->invalid
= TRUE
;
105 TIMED_MENU_DATA(menu
)->buf
[TIMED_MENU_DATA(menu
)->buflen
] = '\0';
107 doc
= xmlParseMemory(TIMED_MENU_DATA(menu
)->buf
,
108 TIMED_MENU_DATA(menu
)->buflen
);
110 node
= xmlDocGetRootElement(doc
);
112 if (!xmlStrcasecmp(node
->name
, (const xmlChar
*) "timed_menu")) {
113 if ((node
= parse_find_node("item", node
->xmlChildrenNode
)))
114 parse_menu_full(doc
, node
, menu
, FALSE
);
117 timed_menu_clean_up(menu
);
118 } else if (num_read
> 0) {
119 TIMED_MENU_DATA(menu
)->buflen
+= num_read
;
120 TIMED_MENU_DATA(menu
)->buf
[TIMED_MENU_DATA(menu
)->buflen
] = '\0';
121 } else { /* num_read < 1 */
122 g_warning("Error on read %s", strerror(errno
));
123 timed_menu_clean_up(menu
);
127 void timed_menu_timeout_handler(void *d
)
130 if (!data
->shown
&& TIMED_MENU_DATA(data
)->fd
== -1) {
131 switch (TIMED_MENU_DATA(data
)->type
) {
132 case (TIMED_MENU_PIPE
): {
133 /* if the menu is not shown, run a process and use its output
136 /* I hate you glib in all your hideous forms */
142 args
[2] = TIMED_MENU_DATA(data
)->command
;
144 if (g_spawn_async_with_pipes(
148 G_SPAWN_SEARCH_PATH
| G_SPAWN_DO_NOT_REAP_CHILD
,
156 event_fd_handler
*h
= g_new(event_fd_handler
, 1);
157 TIMED_MENU_DATA(data
)->fd
= h
->fd
= child_stdout
;
158 TIMED_MENU_DATA(data
)->pid
= child_pid
;
159 h
->handler
= timed_menu_read_pipe
;
161 event_add_fd_handler(h
);
163 g_warning("unable to spawn child");
168 case (TIMED_MENU_STAT
): {
169 struct stat stat_buf
;
171 if (stat(TIMED_MENU_DATA(data
)->command
, &stat_buf
) == -1) {
172 g_warning("Unable to stat %s: %s",
173 TIMED_MENU_DATA(data
)->command
,
178 if (stat_buf
.st_mtime
> TIMED_MENU_DATA(data
)->mtime
) {
179 g_warning("file changed");
180 TIMED_MENU_DATA(data
)->mtime
= stat_buf
.st_mtime
;
188 void *plugin_create(PluginMenuCreateData
*data
)
196 parse_attr_string("id", data
->node
, &id
);
197 parse_attr_string("label", data
->node
, &label
);
199 d
= g_new(Timed_Menu_Data
, 1);
201 m
= menu_new( (label
!= NULL
? label
: ""),
202 (id
!= NULL
? id
: PLUGIN_NAME
),
205 m
->plugin
= PLUGIN_NAME
;
208 menu_add_entry(data
->parent
, menu_entry_new_submenu(
209 (label
!= NULL
? label
: ""),
212 if (!parse_attr_string("command", data
->node
, &d
->command
)) {
213 d
->command
= g_strdup("");
216 if (parse_attr_string("timeout", data
->node
, &timeout
)) {
218 gdouble timeout_val
= g_strtod(timeout
, &endptr
);
220 d
->timer
= timer_start(timeout_val
* 1000000,
221 &timed_menu_timeout_handler
, m
);
223 d
->timer
= timer_start(600 * 1000000, &timed_menu_timeout_handler
, m
);
225 d
->type
= TIMED_MENU_PIPE
;
232 m
->plugin_data
= (void *)d
;
234 timed_menu_timeout_handler(m
);
238 void plugin_destroy (void *m
)
240 timed_menu_clean_up(m
);
241 /* this will be freed by timer_* */
242 timer_stop( ((Timed_Menu_Data
*)TIMED_MENU(m
)->plugin_data
)->timer
);
244 g_free( TIMED_MENU(m
)->plugin_data
);