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 TIMED_MENU_DATA(menu
)->buf
[TIMED_MENU_DATA(menu
)->buflen
] = '\0';
104 xmlDocPtr doc
= xmlParseMemory(TIMED_MENU_DATA(menu
)->buf
,
105 TIMED_MENU_DATA(menu
)->buflen
);
107 xmlNodePtr node
= xmlDocGetRootElement(doc
);
109 if (!xmlStrcasecmp(node
->name
, (const xmlChar
*) "timed_menu")) {
110 if ((node
= parse_find_node("item", node
->xmlChildrenNode
)))
111 parse_menu_full(doc
, node
, menu
, FALSE
);
114 timed_menu_clean_up(menu
);
115 } else if (num_read
> 0) {
116 TIMED_MENU_DATA(menu
)->buflen
+= num_read
;
117 TIMED_MENU_DATA(menu
)->buf
[TIMED_MENU_DATA(menu
)->buflen
] = '\0';
118 } else { /* num_read < 1 */
119 g_warning("Error on read %s", strerror(errno
));
120 timed_menu_clean_up(menu
);
124 void timed_menu_timeout_handler(void *d
)
127 if (!data
->shown
&& TIMED_MENU_DATA(data
)->fd
== -1) {
128 switch (TIMED_MENU_DATA(data
)->type
) {
129 case (TIMED_MENU_PIPE
): {
130 /* if the menu is not shown, run a process and use its output
133 /* I hate you glib in all your hideous forms */
139 args
[2] = TIMED_MENU_DATA(data
)->command
;
141 if (g_spawn_async_with_pipes(
145 G_SPAWN_SEARCH_PATH
| G_SPAWN_DO_NOT_REAP_CHILD
,
153 event_fd_handler
*h
= g_new(event_fd_handler
, 1);
154 TIMED_MENU_DATA(data
)->fd
= h
->fd
= child_stdout
;
155 TIMED_MENU_DATA(data
)->pid
= child_pid
;
156 h
->handler
= timed_menu_read_pipe
;
158 event_add_fd_handler(h
);
160 g_warning("unable to spawn child");
165 case (TIMED_MENU_STAT
): {
166 struct stat stat_buf
;
168 if (stat(TIMED_MENU_DATA(data
)->command
, &stat_buf
) == -1) {
169 g_warning("Unable to stat %s: %s",
170 TIMED_MENU_DATA(data
)->command
,
175 if (stat_buf
.st_mtime
> TIMED_MENU_DATA(data
)->mtime
) {
176 g_warning("file changed");
177 TIMED_MENU_DATA(data
)->mtime
= stat_buf
.st_mtime
;
185 void *plugin_create(PluginMenuCreateData
*data
)
193 parse_attr_string("id", data
->node
, &id
);
194 parse_attr_string("label", data
->node
, &label
);
196 d
= g_new(Timed_Menu_Data
, 1);
198 m
= menu_new( (label
!= NULL
? label
: ""),
199 (id
!= NULL
? id
: PLUGIN_NAME
),
202 m
->plugin
= PLUGIN_NAME
;
205 menu_add_entry(data
->parent
, menu_entry_new_submenu(
206 (label
!= NULL
? label
: ""),
209 if (!parse_attr_string("command", data
->node
, &d
->command
)) {
210 d
->command
= g_strdup("");
213 if (parse_attr_string("timeout", data
->node
, &timeout
)) {
215 gdouble timeout_val
= g_strtod(timeout
, &endptr
);
217 d
->timer
= timer_start(timeout_val
* 1000000,
218 &timed_menu_timeout_handler
, m
);
220 d
->timer
= timer_start(600 * 1000000, &timed_menu_timeout_handler
, m
);
222 d
->type
= TIMED_MENU_PIPE
;
229 m
->plugin_data
= (void *)d
;
231 timed_menu_timeout_handler(m
);
235 void plugin_destroy (void *m
)
237 timed_menu_clean_up(m
);
238 /* this will be freed by timer_* */
239 timer_stop( ((Timed_Menu_Data
*)TIMED_MENU(m
)->plugin_data
)->timer
);
241 g_free( TIMED_MENU(m
)->plugin_data
);