Copy purify.fvwm2rc to /tmp - add instructions in README.
[fvwm.git] / modules / FvwmEvent / FvwmEvent.c
bloba1eaf93908821e9d5b8c1d858e36e1076b72cf59
1 /* -*-c-*- */
2 /*
3 * Copyright (C) 1994 Mark Boyns (boyns@sdsu.edu) and
4 * Mark Scott (mscott@mcd.mot.com)
5 * 1996-1998 Albrecht Kadlec (albrecht@auto.tuwien.ac.at)
7 * FvwmEvent version 1.0
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 * This module is based on FvwmModuleDebugger which has the following
26 * copyright:
28 * This module, and the entire ModuleDebugger program, and the concept for
29 * interfacing this module to the Window Manager, are all original work
30 * by Robert Nation
32 * Copyright 1994, Robert Nation. No guarantees or warantees or anything
33 * are provided or implied in any way whatsoever. Use this program at your
34 * own risk. Permission to use this program for any purpose is given,
35 * as long as the copyright is kept intact.
38 /* ---------------------------- included header files ----------------------- */
40 #include "config.h"
42 #include <stdio.h>
44 * rplay includes:
46 #include "libs/Fplay.h"
48 #include "libs/Module.h"
49 #include "libs/fvwmlib.h"
50 #include "libs/Parse.h"
51 #include "libs/Strings.h"
54 * fvwm includes:
56 #include <stdio.h>
57 #include <signal.h>
58 #include <sys/wait.h>
59 #include "libs/ftime.h"
60 #include <ctype.h>
62 /* ---------------------------- local definitions --------------------------- */
64 #define BUILTIN_STARTUP 0
65 #define BUILTIN_SHUTDOWN 1
66 #define BUILTIN_UNKNOWN 2
67 #define MAX_BUILTIN 3
68 #define MAX_RPLAY_HOSTNAME_LEN MAX_MODULE_INPUT_TEXT_LEN
69 #define SYNC_MASK_M (M_DESTROY_WINDOW | M_LOWER_WINDOW | \
70 M_RESTACK | M_CONFIGURE_WINDOW)
71 #define SYNC_MASK_MX (M_EXTENDED_MSG)
73 /* ---------------------------- local macros -------------------------------- */
75 /* ---------------------------- imports ------------------------------------- */
77 /* ---------------------------- included code files ------------------------- */
79 /* ---------------------------- local types --------------------------------- */
81 typedef struct
83 char *name;
84 int action_arg;
85 union
87 char *action;
88 FPLAY *rplay;
89 } action;
90 } event_entry;
92 /* ---------------------------- forward declarations ------------------------ */
94 void execute_event(event_entry*, short, unsigned long*);
95 void config(void);
96 RETSIGTYPE DeadPipe(int);
97 static RETSIGTYPE TerminateHandler(int);
99 /* ---------------------------- local variables ----------------------------- */
101 /* ---------------------------- exported variables (globals) ---------------- */
103 static char *MyName;
104 static int MyNameLen;
105 static int fd[2];
106 static char *cmd_line = NULL;
107 static time_t audio_delay = 0; /* seconds */
108 static time_t last_time = 0;
109 static time_t now;
110 static time_t start_audio_delay = 0;
111 /* don't tag on the windowID by default */
112 static Bool PassID = False;
113 static Bool audio_compat = False;
114 static char *audio_play_dir = NULL;
116 #define ARG_NO_WINID 1024 /* just a large number */
118 #define EVENT_ENTRY(name,action_arg) { name, action_arg, {NULL} }
119 static event_entry message_event_table[] =
121 EVENT_ENTRY( "new_page", -1 ),
122 EVENT_ENTRY( "new_desk", 0 | ARG_NO_WINID ),
123 EVENT_ENTRY( "old_add_window", 0 ),
124 EVENT_ENTRY( "raise_window", 0 ),
125 EVENT_ENTRY( "lower_window", 0 ),
126 EVENT_ENTRY( "old_configure_window", 0 ),
127 EVENT_ENTRY( "focus_change", 0 ),
128 EVENT_ENTRY( "destroy_window", 0 ),
129 EVENT_ENTRY( "iconify", 0 ),
130 EVENT_ENTRY( "deiconify", 0 ),
131 EVENT_ENTRY( "window_name", 0 ),
132 EVENT_ENTRY( "icon_name", 0 ),
133 EVENT_ENTRY( "res_class", 0 ),
134 EVENT_ENTRY( "res_name", 0 ),
135 EVENT_ENTRY( "end_windowlist", -1 ),
136 EVENT_ENTRY( "icon_location", 0 ),
137 EVENT_ENTRY( "map", 0 ),
138 EVENT_ENTRY( "error", -1 ),
139 EVENT_ENTRY( "config_info", -1 ),
140 EVENT_ENTRY( "end_config_info", -1 ),
141 EVENT_ENTRY( "icon_file", 0 ),
142 EVENT_ENTRY( "default_icon", -1 ),
143 EVENT_ENTRY( "string", -1 ),
144 EVENT_ENTRY( "mini_icon", 0 ),
145 EVENT_ENTRY( "windowshade", 0 ),
146 EVENT_ENTRY( "dewindowshade", 0 ),
147 EVENT_ENTRY( "visible_name", 0 ),
148 EVENT_ENTRY( "sendconfig", -1 ),
149 EVENT_ENTRY( "restack", -1 ),
150 EVENT_ENTRY( "add_window", 0 ),
151 EVENT_ENTRY( "configure_window", 0 ),
152 EVENT_ENTRY(NULL,0)
154 static event_entry extended_message_event_table[] =
156 EVENT_ENTRY( "visible_icon_name", 0 ),
157 EVENT_ENTRY( "enter_window", 0 ),
158 EVENT_ENTRY( "leave_window", 0 ),
159 EVENT_ENTRY( "property_change", 0),
160 EVENT_ENTRY( "reply", 0), /* FvwmEvent will never receive MX_REPLY */
161 EVENT_ENTRY(NULL,0)
163 static event_entry builtin_event_table[] =
165 EVENT_ENTRY( "startup", -1 ),
166 EVENT_ENTRY( "shutdown", -1 ),
167 EVENT_ENTRY( "unknown", -1),
168 EVENT_ENTRY(NULL,0)
171 #if 0
172 /* These are some events described in the man page, which does not exist in
173 fvwm. */
174 static event_entry future_event_table[] =
176 #ifdef M_BELL
177 EVENT_ENTRY( "beep", -1 ),
178 #endif
179 #ifdef M_TOGGLEPAGE
180 EVENT_ENTRY( "toggle_paging", -1 ),
181 #endif
182 EVENT_ENTRY(NULL,0)
184 #endif
186 static event_entry* event_tables[] =
188 message_event_table,
189 extended_message_event_table,
190 builtin_event_table,
191 NULL
194 #undef EVENT_ENTRY
196 static int rplay_fd = -1;
197 static unsigned int m_selected = 0;
198 static unsigned int mx_selected = 0;
199 static unsigned int m_sync = 0;
200 static unsigned int mx_sync = 0;
203 static volatile sig_atomic_t isTerminated = False;
205 /* ---------------------------- local functions ----------------------------- */
207 void unlock_event(unsigned long evtype)
209 unsigned int mask;
211 if (evtype & M_EXTENDED_MSG)
213 mask = mx_sync;
215 else
217 mask = m_sync;
219 if (evtype & mask)
221 SendUnlockNotification(fd);
224 return;
227 int main(int argc, char **argv)
229 char *s;
230 unsigned long header[FvwmPacketHeaderSize];
231 unsigned long body[FvwmPacketBodyMaxSize];
232 int total, remaining, count, event;
233 int is_extended_msg;
235 cmd_line = (char *)safemalloc(1);
236 *cmd_line = 0;
237 /* Save our program name - for error events */
238 if ((s=strrchr(argv[0], '/')))
240 /* strip path */
241 s++;
243 else
245 /* no slash */
246 s = argv[0];
248 if (argc == 7)
250 if (strcmp(argv[6], "-audio") == 0)
252 audio_compat = True;
254 else
256 /* use an alias */
257 s = argv[6];
261 /* account for '*' */
262 MyNameLen=strlen(s)+1;
263 /* account for \0 */
264 MyName = safemalloc(MyNameLen+1);
265 *MyName='*';
266 /* append name */
267 strcpy(MyName+1, s);
268 if (StrEquals("FvwmAudio", s))
270 /* catch the FvwmAudio alias */
271 audio_compat = True;
274 /* Now MyName is defined */
275 if ((argc != 6)&&(argc != 7))
277 fprintf(stderr, "%s Version "VERSION" should only be "
278 "executed by fvwm!\n", MyName+1);
279 exit(1);
282 #ifdef HAVE_SIGACTION
284 struct sigaction sigact;
286 sigemptyset(&sigact.sa_mask);
287 # ifdef SA_INTERRUPT
288 sigact.sa_flags = SA_INTERRUPT;
289 # else
290 sigact.sa_flags = 0;
291 # endif
292 sigact.sa_handler = TerminateHandler;
294 /* Dead pipe == Fvwm died */
295 sigaction(SIGPIPE,&sigact,NULL);
296 /* "polite" termination signal */
297 sigaction(SIGTERM,&sigact,NULL);
299 #else
300 /* We don't have sigaction(), so fall back to less robust methods. */
301 signal(SIGPIPE, TerminateHandler);
302 signal(SIGTERM, TerminateHandler);
303 #endif
305 fd[0] = atoi(argv[1]);
306 fd[1] = atoi(argv[2]);
308 /* configure events */
309 config();
310 /* Startup event */
311 execute_event(builtin_event_table, BUILTIN_STARTUP, NULL);
312 if (start_audio_delay)
314 last_time = time(0);
316 /* tell fvwm we're running */
317 SetMessageMask(fd, m_selected);
318 SetMessageMask(fd, mx_selected | M_EXTENDED_MSG);
319 m_sync = (m_selected & SYNC_MASK_M);
320 mx_sync = (mx_selected & SYNC_MASK_M) | M_EXTENDED_MSG;
321 /* migo (19-Aug-2000): synchronize on M_DESTROY_WINDOW
322 * dv (6-Jul-2002: synchronize on a number of events that can hide or
323 * destroy a window */
324 if (m_sync)
326 SetSyncMask(fd, m_sync);
328 if (mx_sync != M_EXTENDED_MSG)
330 SetSyncMask(fd, mx_sync);
332 mx_sync &= ~M_EXTENDED_MSG;
333 SendFinishedStartupNotification(fd);
335 /* main loop */
336 while (!isTerminated)
338 unsigned long msg_bit;
339 event_entry *event_table;
341 if ((count = read(fd[1], header, FvwmPacketHeaderSize_byte)) <=
344 isTerminated = 1;
345 continue;
347 /* if this read is interrrupted EINTR, the wrong event is
348 * triggered! */
350 if (header[0] != START_FLAG)
352 /* should find something better for resyncing */
353 unlock_event(header[1]);
354 continue;
357 /* Ignore events that occur during the delay period. */
358 now = time(0);
360 /* junk the event body */
361 total=0;
362 remaining = (header[2] - FvwmPacketHeaderSize) *
363 sizeof(unsigned long);
364 while (remaining)
366 if ((count=read(fd[1],&body[total],remaining)) < 0)
368 isTerminated = 1;
369 unlock_event(header[1]);
370 continue;
372 remaining -= count;
373 total +=count;
376 if (now < last_time + audio_delay + start_audio_delay)
378 /* quash event */
379 unlock_event(header[1]);
380 continue;
382 else
384 start_audio_delay = 0;
387 /* event will equal the number of shifts in the base-2
388 * header[1] number. Could use log here but this should be
389 * fast enough. */
390 event = -1;
391 msg_bit = header[1];
392 is_extended_msg = (msg_bit & M_EXTENDED_MSG);
393 msg_bit &= ~M_EXTENDED_MSG;
394 while (msg_bit)
396 event++;
397 msg_bit >>= 1;
399 if (
400 event == -1 ||
401 (event >= MAX_MESSAGES && !is_extended_msg) ||
402 (event >= MAX_EXTENDED_MESSAGES && is_extended_msg))
404 event_table = builtin_event_table;
405 event = BUILTIN_UNKNOWN;
407 else if (is_extended_msg)
409 event_table = extended_message_event_table;
411 else
413 event_table = message_event_table;
415 execute_event(event_table, event, body);
416 unlock_event(header[1]);
417 } /* while */
418 execute_event(builtin_event_table, BUILTIN_SHUTDOWN, NULL);
420 return 0;
426 * Procedure:
428 * execute_event - actually executes the actions from lookup table
431 void execute_event(event_entry *event_table, short event, unsigned long *body)
433 /* this is the sign that rplay is used */
434 if (rplay_fd != -1 && event_table[event].action.rplay != NULL)
436 if (Fplay(rplay_fd, event_table[event].action.rplay) >= 0)
438 last_time = now;
440 else
442 Fplay_perror("rplay");
444 return;
447 if (event_table[event].action.action != NULL)
449 char *buf = NULL;
450 int len = 0;
451 char *action;
453 action = event_table[event].action.action;
454 len = strlen(cmd_line) + strlen(action) + 32;
455 if (audio_play_dir)
457 len += strlen(audio_play_dir);
459 buf = (char *)safemalloc(len);
460 if (audio_compat)
462 /* Don't use audio_play_dir if it's NULL or if
463 * the sound file is an absolute pathname. */
464 if (!audio_play_dir || audio_play_dir[0] == '\0' ||
465 action[0] == '/')
467 sprintf(buf,"%s %s", cmd_line, action);
469 else
471 sprintf(buf,"%s %s/%s &", cmd_line,
472 audio_play_dir, action);
474 if (!system(buf))
476 last_time = now;
479 else
481 int action_arg = event_table[event].action_arg;
482 int fw = 0;
484 if (action_arg != -1 && !(action_arg & ARG_NO_WINID))
486 fw = body[action_arg];
489 if (PassID && action_arg != -1)
491 if (action_arg & ARG_NO_WINID)
493 action_arg &= ~ARG_NO_WINID;
494 sprintf(buf, "%s %s %ld", cmd_line,
495 action, body[action_arg]);
497 else
499 sprintf(buf, "%s %s 0x%lx", cmd_line,
500 action, body[action_arg]);
503 else
505 sprintf(buf,"%s %s", cmd_line, action);
507 /* let fvwm execute the function */
508 SendText(fd, buf, fw);
509 last_time = now;
511 free(buf);
513 return;
516 return;
522 * Procedure:
523 * config - read the configuration file.
527 static int volume = FPLAY_DEFAULT_VOLUME;
528 static int priority = FPLAY_DEFAULT_PRIORITY;
529 void handle_config_line(char *buf, char **phost)
531 char *event;
532 char *action;
533 char *p;
534 char *token;
535 char **e;
536 int i;
537 int found;
539 char *table[]=
541 "Cmd",
542 "Delay",
543 "Dir",
544 "PassID",
545 "PlayCmd",
546 "RplayHost",
547 "RplayPriority",
548 "RplayVolume",
549 "StartDelay"
550 }; /* define entries here, if this list becomes unsorted, use FindToken */
553 p = buf + MyNameLen;
554 /* config option ? */
555 if ((e = FindToken(p, table, char *)))
557 /* skip matched token */
558 p += strlen(*e);
559 token = PeekToken(p, &p);
560 switch (e - (char**)table)
562 case 4:
563 /* PlayCmd */
564 if (!audio_compat)
566 /* PlayCmd */
567 fprintf(stderr,
568 "%s: PlayCmd supported only when"
569 " invoked as FvwmAudio\n", MyName+1);
570 break;
572 /* fall through */
573 case 0:
574 /* Cmd */
575 if (cmd_line)
577 free(cmd_line);
579 if (token)
581 cmd_line = safestrdup(token);
583 else
585 cmd_line = safestrdup("");
587 break;
589 case 1:
590 /* Delay */
591 if (token)
593 audio_delay = atoi(token);
595 break;
597 case 2:
598 /* Dir */
599 if (!audio_compat)
601 fprintf(stderr,
602 "%s: Dir supported only when invoked as"
603 " FvwmAudio\n", MyName+1);
604 break;
606 if (token)
608 if (audio_play_dir)
610 free(audio_play_dir);
612 audio_play_dir = safestrdup(token);
614 break;
616 case 3:
617 /* PassID */
618 PassID = True;
619 break;
621 case 5:
622 /* RPlayHost */
623 if (*phost)
625 free(*phost);
626 *phost = NULL;
628 if (token && (*token == '$'))
630 /* Check for $HOSTDISPLAY */
631 char *c1 = (char *)getenv(token + 1);
632 char *c2;
633 if (c1 != NULL)
635 *phost = safestrdup(c1);
636 c2 = *phost;
637 while (c1 && *c1 != ':')
639 *c2++ = *c1++;
641 *c2 = '\0';
644 else if (token)
646 *phost = safestrdup(token);
648 break;
650 case 6:
651 /* RplayPriority */
652 if (token)
654 priority = atoi(token);
656 break;
658 case 7:
659 /* RplayVolume */
660 if (token)
662 volume = atoi(token);
664 break;
666 case 8:
667 /* StartDelay */
668 if (token)
670 start_audio_delay = atoi(token);
672 break;
676 else
678 event_entry **event_table;
680 /* test for isspace(*p) ??? */
681 p = GetNextSimpleOption(p, &event);
682 p = GetNextSimpleOption(p, &action);
683 if (!event || !*event || !action || !*action)
685 if (event)
687 free(event);
689 if (action)
691 free(action);
693 fprintf(stderr, "%s: incomplete event definition %s\n",
694 MyName + 1, buf);
695 return;
697 event_table = event_tables;
698 found = 0;
699 while(found == 0 && *event_table != NULL)
701 event_entry *loop_event;
703 for (loop_event = *event_table, i = 0;
704 found == 0 && loop_event->name != NULL;
705 loop_event++, i++)
707 if (MatchToken(event, loop_event->name))
709 if (loop_event->action.action != NULL)
711 free(loop_event->action.action);
713 loop_event->action.action = action;
714 found = 1;
715 if (*event_table == message_event_table)
717 m_selected |= (1 << i);
719 else if (
720 *event_table ==
721 extended_message_event_table)
723 mx_selected |= (1 << i);
727 event_table++;
729 if (!found)
731 fprintf(stderr, "%s: unknown event type: %s\n",
732 MyName+1, event);
733 if (action)
735 free(action);
738 if (event)
740 free(event);
744 return;
747 void config(void)
749 char *buf;
750 char *host = NULL;
752 if (USE_FPLAY)
754 host = safestrdup(Fplay_default_host());
757 /* get config lines with my name */
758 InitGetConfigLine(fd,MyName);
759 while (GetConfigLine(fd,&buf), buf != NULL && *buf != 0)
761 if (buf[strlen(buf)-1] == '\n')
763 /* if line ends with newline, strip it off */
764 buf[strlen(buf)-1] = '\0';
767 /* Search for MyName (normally *FvwmEvent) */
768 if (strncasecmp(buf, MyName, MyNameLen) == 0)
770 handle_config_line(buf, &host);
774 /* Builtin rplay support is enabled when
775 * FvwmAudioPlayCmd == builtin-rplay. */
776 if (USE_FPLAY && StrEquals(cmd_line, "builtin-rplay"))
778 event_entry **event_table;
780 if ((rplay_fd = Fplay_open(host)) < 0)
782 Fplay_perror("rplay_open");
783 exit(1);
785 /* change actions to rplay objects */
786 event_table = event_tables;
787 while(*event_table != NULL)
789 event_entry *loop_event;
790 for (loop_event = *event_table;
791 loop_event->name != NULL; loop_event++)
793 if (loop_event->action.action != NULL)
795 char *tmp_action;
796 tmp_action = loop_event->action.action;
797 loop_event->action.rplay =
798 Fplay_create(
799 FPLAY_PLAY);
800 Fplay_set(
801 loop_event->action.rplay,
802 FPLAY_APPEND,
803 FPLAY_SOUND, tmp_action,
804 FPLAY_PRIORITY, priority,
805 FPLAY_VOLUME, volume, NULL);
806 free(tmp_action);
809 event_table++;
813 return;
818 * Procedure:
819 * SIGPIPE handler - SIGPIPE means fvwm is dying
822 static RETSIGTYPE
823 TerminateHandler(int nonsense)
825 isTerminated = True;
827 SIGNAL_RETURN;
832 * Procedure:
833 * Externally callable procedure to quit
836 RETSIGTYPE DeadPipe(int flag)
838 execute_event(builtin_event_table, BUILTIN_SHUTDOWN, NULL);
839 exit(flag);
840 SIGNAL_RETURN;