* detect and disallow circular transient-for hints
[fvwm.git] / fvwm / schedule.c
blob845a34b43c9738411077a56cd1eddb5d9c37a738
1 /* -*-c-*- */
2 /* This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 #include "config.h"
19 #include <stdio.h>
21 #include "libs/fvwmlib.h"
22 #include "libs/queue.h"
23 #include "libs/charmap.h"
24 #include "libs/wcontext.h"
25 #include "libs/Parse.h"
26 #include "fvwm.h"
27 #include "externs.h"
28 #include "colorset.h"
29 #include "bindings.h"
30 #include "misc.h"
31 #include "cursor.h"
32 #include "functions.h"
33 #include "commands.h"
34 #include "screen.h"
36 typedef struct
38 int id;
39 Time time_to_execute;
40 Window window;
41 char *command;
42 int period; /* in milliseconds */
43 } sq_object_type;
45 static int last_schedule_id = 0;
46 static int next_schedule_id = -1;
47 static fqueue sq = FQUEUE_INIT;
49 static int cmp_times(Time t1, Time t2)
51 unsigned long ul1 = (unsigned long)t1;
52 unsigned long ul2 = (unsigned long)t2;
53 unsigned long diff;
54 signed long udiff;
56 diff = ul1 - ul2;
57 udiff = *(signed long *)&diff;
58 if (udiff > 0)
60 return 1;
62 else if (udiff < 0)
64 return -1;
66 else
68 return 0;
72 static int cmp_object_time(void *object1, void *object2, void *args)
74 sq_object_type *so1 = (sq_object_type *)object1;
75 sq_object_type *so2 = (sq_object_type *)object2;
77 return cmp_times(so1->time_to_execute, so2->time_to_execute);
80 static int check_deschedule_obj_func(void *object, void *args)
82 sq_object_type *obj = object;
84 return (obj->id == *(int *)args);
87 static void destroy_obj_func(void *object)
89 sq_object_type *obj = object;
91 if (obj->command != NULL)
93 free(obj->command);
95 free(obj);
97 return;
100 static void deschedule(int *pid)
102 int id;
104 if (FQUEUE_IS_EMPTY(&sq))
106 return;
108 /* get the job group id to deschedule */
109 if (pid != NULL)
111 id = *pid;
113 else
115 id = last_schedule_id;
117 /* deschedule matching jobs */
118 fqueue_remove_or_operate_all(
119 &sq, check_deschedule_obj_func, NULL, destroy_obj_func,
120 (void *)&id);
122 return;
125 static void schedule(
126 Window window, char *command, Time time_to_execute, int *pid,
127 int period)
129 sq_object_type *new_obj;
131 if (command == NULL || *command == 0)
133 return;
135 /* create the new object */
136 new_obj = (sq_object_type *)safemalloc(sizeof(sq_object_type));
137 memset(new_obj, 0, sizeof(sq_object_type));
138 new_obj->window = window;
139 new_obj->command = safestrdup(command);
140 new_obj->time_to_execute = time_to_execute;
141 new_obj->period = period; /* 0 if this is not a periodic command */
142 /* set the job group id */
143 if (pid != NULL)
145 new_obj->id = *pid;
147 else
149 new_obj->id = next_schedule_id;
150 next_schedule_id--;
151 if (next_schedule_id >= 0)
153 /* wrapped around */
154 next_schedule_id = -1;
157 last_schedule_id = new_obj->id;
158 /* insert into schedule queue */
159 fqueue_add_inside(&sq, new_obj, cmp_object_time, NULL);
161 return;
164 static int check_execute_obj_func(void *object, void *args)
166 sq_object_type *obj = object;
167 Time *ptime = (Time *)args;
169 return (cmp_times(*ptime, obj->time_to_execute) >= 0);
172 static void execute_obj_func(void *object, void *args)
174 sq_object_type *obj = object;
176 if (obj->command != NULL)
178 /* execute the command */
179 const exec_context_t *exc;
180 exec_context_changes_t ecc;
181 exec_context_change_mask_t mask = ECC_TYPE | ECC_WCONTEXT;
183 ecc.type = EXCT_SCHEDULE;
184 ecc.w.wcontext = C_ROOT;
185 if (XFindContext(
186 dpy, obj->window, FvwmContext,
187 (caddr_t *)&ecc.w.fw) != XCNOENT)
189 ecc.w.wcontext = C_WINDOW;
190 mask |= ECC_FW;
192 exc = exc_create_context(&ecc, mask);
193 execute_function(NULL, exc, obj->command, 0);
194 exc_destroy_context(exc);
196 if (obj->period > 0)
198 /* This is a periodic function, so reschedule it. */
199 sq_object_type *new_obj = (sq_object_type *)safemalloc(sizeof(sq_object_type));
200 memcpy(new_obj, obj, sizeof(sq_object_type));
201 obj->command = NULL; /* new_obj->command now points to cmd. */
202 new_obj->time_to_execute = fev_get_evtime() + new_obj->period;
203 /* insert into schedule queue */
204 fqueue_add_inside(&sq, new_obj, cmp_object_time, NULL);
206 XFlush(dpy);
208 return;
211 /* executes all scheduled commands that are due for execution */
212 void squeue_execute(void)
214 Time current_time;
216 if (FQUEUE_IS_EMPTY(&sq))
218 return;
220 current_time = get_server_time();
221 fqueue_remove_or_operate_all(
222 &sq, check_execute_obj_func, execute_obj_func,
223 destroy_obj_func, &current_time);
225 return;
228 /* returns the time in milliseconds to wait before next queue command must be
229 * executed or -1 if none is queued */
230 int squeue_get_next_ms(void)
232 int ms;
233 sq_object_type *obj;
235 if (fqueue_get_first(&sq, (void **)&obj) == 0)
237 return -1;
239 if (cmp_times(fev_get_evtime(), obj->time_to_execute) >= 0)
241 /* jobs pending to be executed immediately */
242 ms = 0;
244 else
246 /* execute jobs later */
247 ms = obj->time_to_execute - fev_get_evtime();
250 return ms;
253 int squeue_get_next_id(void)
255 return next_schedule_id;
258 int squeue_get_last_id(void)
260 return last_schedule_id;
263 void CMD_Schedule(F_CMD_ARGS)
265 Window xw;
266 Time time;
267 Time current_time;
268 char *taction;
269 char *token;
270 char *next;
271 int ms;
272 int id;
273 int *pid;
274 int n;
275 FvwmWindow * const fw = exc->w.fw;
276 Bool is_periodic = False;
278 token = PeekToken(action, &next);
279 if (token && strcasecmp(token, "periodic") == 0)
281 is_periodic = True;
282 action = next;
284 /* get the time to execute */
285 n = GetIntegerArguments(action, &action, &ms, 1);
286 if (n <= 0)
288 fvwm_msg(ERR, "CMD_Schedule",
289 "Requires time to schedule as argument");
290 return;
292 if (ms < 0)
294 ms = 0;
296 #if 0
297 /* eats up way too much cpu if schedule is used excessively */
298 current_time = get_server_time();
299 #else
300 /* with this version, scheduled commands may be executed earlier than
301 * intended. */
302 current_time = fev_get_evtime();
303 #endif
304 time = current_time + (Time)ms;
305 /* get the job group id to schedule */
306 n = GetIntegerArgumentsAnyBase(action, &taction, &id, 1);
307 if (n >= 1)
309 pid = &id;
310 action = taction;
312 else
314 pid = NULL;
316 /* get the window to operate on */
317 if (fw != NULL)
319 xw = FW_W(fw);
321 else
323 xw = None;
325 /* schedule the job */
326 schedule(xw, action, time, pid, (is_periodic == True ? ms : 0));
328 return;
331 void CMD_Deschedule(F_CMD_ARGS)
333 int id;
334 int *pid;
335 int n;
337 /* get the job group id to deschedule */
338 n = GetIntegerArgumentsAnyBase(action, &action, &id, 1);
339 if (n <= 0)
341 /* none, use default */
342 pid = NULL;
344 else
346 pid = &id;
348 /* deschedule matching jobs */
349 deschedule(pid);
351 return;