Propagate Layer changes via Style command on-the-fly.
[fvwm.git] / modules / FvwmAuto / FvwmAuto.c
blobd35e7bee0c127a8992c39dbdd3bae66558d29644
1 /* -*-c-*- */
2 /* This module, and the entire FvwmAuto program, and the concept for
3 * interfacing this module to the Window Manager, are all original work
4 * by Robert Nation
6 * Copyright 1994, Robert Nation. No guarantees or warantees or anything
7 * are provided or implied in any way whatsoever. Use this program at your
8 * own risk. Permission to use this program for any purpose is given,
9 * as long as the copyright is kept intact.
11 * reworked by A.Kadlec (albrecht@auto.tuwien.ac.at) 09/96
12 * to support an arbitrary enter_fn & leave_fn (command line arguments)
13 * completely reworked, while I was there.
15 * Modified by John Aughey (jha@cs.purdue.edu) 11/96
16 * to not perform any action when entering the root window
18 * Minor patch by C. Hines 12/96.
21 /* This program is free software; you can redistribute it and/or modify
22 * it under the terms of the GNU General Public License as published by
23 * the Free Software Foundation; either version 2 of the License, or
24 * (at your option) any later version.
26 * This program is distributed in the hope that it will be useful,
27 * but WITHOUT ANY WARRANTY; without even the implied warranty of
28 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
29 * GNU General Public License for more details.
31 * You should have received a copy of the GNU General Public License
32 * along with this program; if not, write to the Free Software
33 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
36 #include "config.h"
38 #if HAVE_SYS_BSDTYPES_H
39 #include <sys/bsdtypes.h> /* Saul */
40 #endif
42 #include <stdio.h>
43 #include <fcntl.h>
44 #include <errno.h>
45 #include <sys/wait.h>
46 #include "libs/ftime.h"
48 #include <unistd.h>
49 #include <ctype.h>
50 #include "libs/Module.h"
51 #include "libs/fvwmlib.h"
52 #include "libs/fvwmsignal.h"
53 #include "libs/Parse.h"
54 #include "libs/Strings.h"
55 #include "libs/wild.h"
57 /* here is the old double parens trick. */
58 /* #define DEBUG */
59 #ifdef DEBUGTOFILE
60 #define DEBUG
61 #endif
62 #ifdef DEBUG
63 #define myfprintf(X) \
64 fprintf X;\
65 fflush (stderr);
66 #else
67 #define myfprintf(X)
68 #endif
71 static RETSIGTYPE TerminateHandler(int signo);
75 * Procedure:
76 * Termination procedure : *not* a signal handler
79 RETSIGTYPE DeadPipe(int nonsense)
81 (void)nonsense;
82 myfprintf((stderr,"Leaving via DeadPipe\n"));
83 exit(0);
84 SIGNAL_RETURN;
89 * Procedure:
90 * Signal handler that tells the module to quit
93 static RETSIGTYPE
94 TerminateHandler(int signo)
96 fvwmSetTerminate(signo);
97 SIGNAL_RETURN;
102 * Procedure:
103 * main - start of module
107 main(int argc, char **argv)
109 /* The struct holding the module info */
110 static ModuleArgs* module;
111 char *enter_fn="Silent Raise"; /* default */
112 char *leave_fn=NULL;
113 char *buf;
114 int len;
115 unsigned long m_mask;
116 unsigned long mx_mask;
117 unsigned long last_win = 0; /* last window handled */
118 unsigned long focus_win = 0; /* current focus */
119 unsigned long raised_win = 0;
120 fd_set_size_t fd_width;
121 int fd[2];
122 int timeout;
123 int sec = 0;
124 int usec = 0;
125 int n;
126 struct timeval value;
127 struct timeval *delay;
128 fd_set in_fdset;
129 Bool do_pass_id = False;
130 Bool use_enter_mode = False;
131 Bool use_leave_mode = False;
132 #ifdef DEBUG
133 int count = 0;
134 #endif
136 #ifdef DEBUGTOFILE
137 freopen(".FvwmAutoDebug","w",stderr);
138 #endif
140 module = ParseModuleArgs(argc,argv,0); /* no alias in this module */
141 if (module==NULL)
143 fprintf(stderr,"FvwmAuto Version "VERSION" should only be executed by fvwm!\n");
144 exit(1);
148 if (module->user_argc < 1 || module->user_argc > 5)
150 fprintf(stderr,"FvwmAuto can use one to five arguments.\n");
151 exit(1);
154 /* Dead pipes mean fvwm died */
155 #ifdef HAVE_SIGACTION
157 struct sigaction sigact;
159 sigemptyset(&sigact.sa_mask);
160 sigaddset(&sigact.sa_mask, SIGPIPE);
161 sigaddset(&sigact.sa_mask, SIGINT);
162 sigaddset(&sigact.sa_mask, SIGHUP);
163 sigaddset(&sigact.sa_mask, SIGQUIT);
164 sigaddset(&sigact.sa_mask, SIGTERM);
165 #ifdef SA_RESTART
166 sigact.sa_flags = SA_RESTART;
167 # else
168 sigact.sa_flags = 0;
169 #endif
170 sigact.sa_handler = TerminateHandler;
172 sigaction(SIGPIPE, &sigact, NULL);
173 sigaction(SIGINT, &sigact, NULL);
174 sigaction(SIGHUP, &sigact, NULL);
175 sigaction(SIGQUIT, &sigact, NULL);
176 sigaction(SIGTERM, &sigact, NULL);
178 #else
179 /* We don't have sigaction(), so fall back to less robust methods. */
180 #ifdef USE_BSD_SIGNALS
181 fvwmSetSignalMask( sigmask(SIGPIPE) |
182 sigmask(SIGINT) |
183 sigmask(SIGHUP) |
184 sigmask(SIGQUIT) |
185 sigmask(SIGTERM) );
186 #endif
188 signal(SIGPIPE, TerminateHandler);
189 signal(SIGINT, TerminateHandler);
190 signal(SIGHUP, TerminateHandler);
191 signal(SIGQUIT, TerminateHandler);
192 signal(SIGTERM, TerminateHandler);
193 #ifdef HAVE_SIGINTERRUPT
194 siginterrupt(SIGPIPE, 0);
195 siginterrupt(SIGINT, 0);
196 siginterrupt(SIGHUP, 0);
197 siginterrupt(SIGQUIT, 0);
198 siginterrupt(SIGTERM, 0);
199 #endif
200 #endif
202 fd[0] = module->to_fvwm;
203 fd[1] = module->from_fvwm;
205 if ((timeout = atoi(module->user_argv[0]) ))
207 sec = timeout / 1000;
208 usec = (timeout % 1000) * 1000;
210 else
212 sec = 0;
213 usec = 1000;
215 delay = &value;
217 n = 1;
218 if (n < module->user_argc && module->user_argv[n])
220 char *token;
222 /* -passid option */
223 if (n < module->user_argc && *module->user_argv[n] && StrEquals(module->user_argv[n], "-passid"))
225 do_pass_id = True;
226 n++;
228 if (n < module->user_argc && *module->user_argv[n] && StrEquals(module->user_argv[n], "-menterleave"))
230 /* enterleave mode */
231 use_leave_mode = True;
232 use_enter_mode = True;
233 n++;
235 else if (n < module->user_argc && *module->user_argv[n] && StrEquals(module->user_argv[n], "-menter"))
237 /* enter mode */
238 use_leave_mode = False;
239 use_enter_mode = True;
240 n++;
242 else if (n < module->user_argc && *module->user_argv[n] && StrEquals(module->user_argv[n], "-mfocus"))
244 /* focus mode */
245 use_leave_mode = False;
246 use_enter_mode = False;
247 n++;
249 /*** enter command ***/
250 if (n < module->user_argc && *module->user_argv[n] && StrEquals(module->user_argv[n], "Nop"))
252 /* nop */
253 enter_fn = NULL;
254 n++;
256 else if (n < module->user_argc)
258 /* override default */
259 enter_fn = module->user_argv[n];
260 n++;
262 /* This is a hack to prevent user interaction with old configs.
264 if (enter_fn)
266 token = PeekToken(enter_fn, NULL);
267 if (!StrEquals(token, "Silent"))
269 enter_fn = safestrdup(
270 CatString2("Silent ", enter_fn));
273 /*** leave command ***/
274 if (n < module->user_argc && module->user_argv[n] && *module->user_argv[n] &&
275 StrEquals(module->user_argv[n], "Nop"))
277 /* nop */
278 leave_fn = NULL;
279 n++;
281 else if (n < module->user_argc)
283 /* leave function specified */
284 leave_fn=module->user_argv[n];
285 n++;
287 if (leave_fn)
289 token = PeekToken(leave_fn, NULL);
290 if (!StrEquals(token, "Silent"))
292 leave_fn = safestrdup(
293 CatString2("Silent ", leave_fn));
298 /* Exit if nothing to do. */
299 if (!enter_fn && !leave_fn)
301 return -1;
304 if (use_enter_mode)
306 m_mask = 0;
307 mx_mask = MX_ENTER_WINDOW | MX_LEAVE_WINDOW | M_EXTENDED_MSG;
309 else
311 mx_mask = M_EXTENDED_MSG;
312 m_mask = M_FOCUS_CHANGE;
314 /* Disable special raise/lower support on general actions. *
315 * This works as expected in most of cases. */
316 if (matchWildcards("*Raise*", CatString2(enter_fn, leave_fn)) ||
317 matchWildcards("*Lower*", CatString2(enter_fn, leave_fn)))
319 m_mask |= M_RAISE_WINDOW | M_LOWER_WINDOW;
322 /* migo (04/May/2000): It is simply incorrect to listen to raise/lower
323 * packets and change the state if the action itself has no raise/lower.
324 * Detecting whether to listen or not by the action name is good enough.
325 m_mask = M_FOCUS_CHANGE | M_RAISE_WINDOW | M_LOWER_WINDOW;
328 SetMessageMask(fd, m_mask);
329 SetMessageMask(fd, mx_mask);
330 /* tell fvwm we're running */
331 SendFinishedStartupNotification(fd);
332 /* tell fvwm that we want to be lock on send */
333 SetSyncMask(fd, m_mask);
334 SetSyncMask(fd, mx_mask);
336 fd_width = fd[1] + 1;
337 FD_ZERO(&in_fdset);
339 /* create the command buffer */
340 len = 0;
341 if (enter_fn != 0)
343 len = strlen(enter_fn);
345 if (leave_fn != NULL)
347 len = max(len, strlen(leave_fn));
349 if (do_pass_id)
351 len += 32;
353 buf = safemalloc(len);
355 while (!isTerminated)
357 char raise_window_now;
358 static char have_new_window = 0;
360 FD_SET(fd[1], &in_fdset);
362 myfprintf(
363 (stderr, "\nstart %d (hnw = %d, usec = %d)\n", count++,
364 have_new_window, usec));
365 /* fill in struct - modified by select() */
366 delay->tv_sec = sec;
367 delay->tv_usec = usec;
368 #ifdef DEBUG
370 char tmp[32];
372 sprintf(tmp, "%d usecs", (delay) ?
373 (int)delay->tv_usec : -1);
374 myfprintf((stderr, "select: delay = %s\n",
375 (have_new_window) ? tmp : "infinite" ));
377 #endif
378 if (fvwmSelect(fd_width, &in_fdset, NULL, NULL,
379 (have_new_window) ? delay : NULL) == -1)
381 myfprintf(
382 (stderr, "select: error! (%s)\n",
383 strerror(errno)));
384 break;
387 raise_window_now = 0;
388 if (FD_ISSET(fd[1], &in_fdset))
390 FvwmPacket *packet = ReadFvwmPacket(fd[1]);
392 if (packet == NULL)
394 myfprintf(
395 (stderr,
396 "Leaving because of null packet\n"));
397 break;
400 myfprintf(
401 (stderr,
402 "pw = 0x%x, fw=0x%x, rw = 0x%x, lw=0x%x\n",
403 (int)packet->body[0], (int)focus_win,
404 (int)raised_win, (int)last_win));
406 switch (packet->type)
408 case MX_ENTER_WINDOW:
409 focus_win = packet->body[0];
410 if (focus_win != raised_win)
412 myfprintf((stderr,
413 "entered new window\n"));
414 have_new_window = 1;
415 raise_window_now = 0;
417 else if (focus_win == last_win)
419 have_new_window = 0;
421 else
423 myfprintf((stderr,
424 "entered other window\n"));
426 break;
428 case MX_LEAVE_WINDOW:
429 if (use_leave_mode)
431 if (focus_win == raised_win)
433 focus_win = 0;
435 myfprintf((stderr,
436 "left raised window\n"));
437 have_new_window = 1;
438 raise_window_now = 0;
440 break;
442 case M_FOCUS_CHANGE:
443 /* it's a focus package */
444 focus_win = packet->body[0];
445 if (focus_win != raised_win)
447 myfprintf((stderr,
448 "focus on new window\n"));
449 have_new_window = 1;
450 raise_window_now = 0;
452 else
454 myfprintf((stderr,
455 "focus on old window\n"));
457 break;
459 case M_RAISE_WINDOW:
460 myfprintf(
461 (stderr, "raise packet 0x%x\n",
462 (int)packet->body[0]));
463 raised_win = packet->body[0];
464 if (have_new_window && focus_win == raised_win)
466 myfprintf(
467 (stderr, "its the old window:"
468 " don't raise\n"));
469 have_new_window = 0;
471 break;
473 case M_LOWER_WINDOW:
474 myfprintf(
475 (stderr, "lower packet 0x%x\n",
476 (int)packet->body[0]));
477 if (have_new_window &&
478 focus_win == packet->body[0])
480 myfprintf(
481 (stderr,
482 "window was explicitly"
483 " lowered, don't raise it"
484 " again\n"));
485 have_new_window = 0;
487 break;
488 } /* switch */
489 SendUnlockNotification(fd);
491 else
493 if (have_new_window)
495 myfprintf((stderr, "must raise now\n"));
496 raise_window_now = 1;
500 if (raise_window_now)
502 myfprintf((stderr, "raising 0x%x\n", (int)focus_win));
504 if (leave_fn &&
505 ((last_win && !use_leave_mode) ||
506 (raised_win && use_enter_mode)))
508 /* if focus_win isn't the root */
509 if (do_pass_id)
511 sprintf(buf, "%s 0x%x\n", leave_fn,
512 (int)last_win);
514 else
516 sprintf(buf, "%s\n", leave_fn);
518 SendInfo(fd, buf, last_win);
519 if (use_enter_mode)
521 raised_win = 0;
525 if (focus_win && enter_fn)
527 /* if focus_win isn't the root */
528 if (do_pass_id)
530 sprintf(buf, "%s 0x%x\n", enter_fn,
531 (int)focus_win);
533 else
535 sprintf(buf, "%s\n", enter_fn);
537 SendInfo(fd, buf, focus_win);
538 raised_win = focus_win;
540 else if (focus_win && enter_fn == NULL)
542 raised_win = focus_win;
544 /* force fvwm to synchronise on slow X connections to
545 * avoid a race condition. Still possible, but much
546 * less likely. */
547 SendInfo(fd, "XSync", focus_win);
549 /* switch to wait mode again */
550 last_win = focus_win;
551 have_new_window = 0;
553 } /* while */
555 return 0;