/* -*-c-*- */ /* * Copyright (C) 1994 Mark Boyns (boyns@sdsu.edu) and * Mark Scott (mscott@mcd.mot.com) * 1996-1998 Albrecht Kadlec (albrecht@auto.tuwien.ac.at) * * FvwmEvent version 1.0 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* * This module is based on FvwmModuleDebugger which has the following * copyright: * * This module, and the entire ModuleDebugger program, and the concept for * interfacing this module to the Window Manager, are all original work * by Robert Nation * * Copyright 1994, Robert Nation. No guarantees or warantees or anything * are provided or implied in any way whatsoever. Use this program at your * own risk. Permission to use this program for any purpose is given, * as long as the copyright is kept intact. */ /* ---------------------------- included header files ----------------------- */ #include "config.h" #include /* * rplay includes: */ #include "libs/Fplay.h" #include "libs/Module.h" #include "libs/fvwmlib.h" #include "libs/Parse.h" #include "libs/Strings.h" /* * fvwm includes: */ #include #include #include #include "libs/ftime.h" #include /* ---------------------------- local definitions --------------------------- */ #define BUILTIN_STARTUP 0 #define BUILTIN_SHUTDOWN 1 #define BUILTIN_UNKNOWN 2 #define MAX_BUILTIN 3 #define MAX_RPLAY_HOSTNAME_LEN MAX_MODULE_INPUT_TEXT_LEN #define SYNC_MASK_M (M_DESTROY_WINDOW | M_LOWER_WINDOW | \ M_RESTACK | M_CONFIGURE_WINDOW) #define SYNC_MASK_MX (M_EXTENDED_MSG) /* ---------------------------- local macros -------------------------------- */ /* ---------------------------- imports ------------------------------------- */ /* ---------------------------- included code files ------------------------- */ /* ---------------------------- local types --------------------------------- */ typedef struct { char *name; int action_arg; union { char *action; FPLAY *rplay; } action; } event_entry; /* ---------------------------- forward declarations ------------------------ */ void execute_event(event_entry*, short, unsigned long*); void config(void); RETSIGTYPE DeadPipe(int); static RETSIGTYPE TerminateHandler(int); /* ---------------------------- local variables ----------------------------- */ /* ---------------------------- exported variables (globals) ---------------- */ static char *MyName; static int MyNameLen; static int fd[2]; static char *cmd_line = NULL; static time_t audio_delay = 0; /* seconds */ static time_t last_time = 0; static time_t now; static time_t start_audio_delay = 0; /* don't tag on the windowID by default */ static Bool PassID = False; static Bool audio_compat = False; static char *audio_play_dir = NULL; #define ARG_NO_WINID 1024 /* just a large number */ #define EVENT_ENTRY(name,action_arg) { name, action_arg, {NULL} } static event_entry message_event_table[] = { EVENT_ENTRY( "new_page", -1 ), EVENT_ENTRY( "new_desk", 0 | ARG_NO_WINID ), EVENT_ENTRY( "old_add_window", 0 ), EVENT_ENTRY( "raise_window", 0 ), EVENT_ENTRY( "lower_window", 0 ), EVENT_ENTRY( "old_configure_window", 0 ), EVENT_ENTRY( "focus_change", 0 ), EVENT_ENTRY( "destroy_window", 0 ), EVENT_ENTRY( "iconify", 0 ), EVENT_ENTRY( "deiconify", 0 ), EVENT_ENTRY( "window_name", 0 ), EVENT_ENTRY( "icon_name", 0 ), EVENT_ENTRY( "res_class", 0 ), EVENT_ENTRY( "res_name", 0 ), EVENT_ENTRY( "end_windowlist", -1 ), EVENT_ENTRY( "icon_location", 0 ), EVENT_ENTRY( "map", 0 ), EVENT_ENTRY( "error", -1 ), EVENT_ENTRY( "config_info", -1 ), EVENT_ENTRY( "end_config_info", -1 ), EVENT_ENTRY( "icon_file", 0 ), EVENT_ENTRY( "default_icon", -1 ), EVENT_ENTRY( "string", -1 ), EVENT_ENTRY( "mini_icon", 0 ), EVENT_ENTRY( "windowshade", 0 ), EVENT_ENTRY( "dewindowshade", 0 ), EVENT_ENTRY( "visible_name", 0 ), EVENT_ENTRY( "sendconfig", -1 ), EVENT_ENTRY( "restack", -1 ), EVENT_ENTRY( "add_window", 0 ), EVENT_ENTRY( "configure_window", 0 ), EVENT_ENTRY(NULL,0) }; static event_entry extended_message_event_table[] = { EVENT_ENTRY( "visible_icon_name", 0 ), EVENT_ENTRY( "enter_window", 0 ), EVENT_ENTRY( "leave_window", 0 ), EVENT_ENTRY( "property_change", 0), EVENT_ENTRY( "reply", 0), /* FvwmEvent will never receive MX_REPLY */ EVENT_ENTRY(NULL,0) }; static event_entry builtin_event_table[] = { EVENT_ENTRY( "startup", -1 ), EVENT_ENTRY( "shutdown", -1 ), EVENT_ENTRY( "unknown", -1), EVENT_ENTRY(NULL,0) }; #if 0 /* These are some events described in the man page, which does not exist in fvwm. */ static event_entry future_event_table[] = { #ifdef M_BELL EVENT_ENTRY( "beep", -1 ), #endif #ifdef M_TOGGLEPAGE EVENT_ENTRY( "toggle_paging", -1 ), #endif EVENT_ENTRY(NULL,0) } #endif static event_entry* event_tables[] = { message_event_table, extended_message_event_table, builtin_event_table, NULL }; #undef EVENT_ENTRY static int rplay_fd = -1; static unsigned int m_selected = 0; static unsigned int mx_selected = 0; static unsigned int m_sync = 0; static unsigned int mx_sync = 0; static volatile sig_atomic_t isTerminated = False; /* ---------------------------- local functions ----------------------------- */ void unlock_event(unsigned long evtype) { unsigned int mask; if (evtype & M_EXTENDED_MSG) { mask = mx_sync; } else { mask = m_sync; } if (evtype & mask) { SendUnlockNotification(fd); } return; } int main(int argc, char **argv) { char *s; unsigned long header[FvwmPacketHeaderSize]; unsigned long body[FvwmPacketBodyMaxSize]; int total, remaining, count, event; int is_extended_msg; cmd_line = (char *)safemalloc(1); *cmd_line = 0; /* Save our program name - for error events */ if ((s=strrchr(argv[0], '/'))) { /* strip path */ s++; } else { /* no slash */ s = argv[0]; } if (argc == 7) { if (strcmp(argv[6], "-audio") == 0) { audio_compat = True; } else { /* use an alias */ s = argv[6]; } } /* account for '*' */ MyNameLen=strlen(s)+1; /* account for \0 */ MyName = safemalloc(MyNameLen+1); *MyName='*'; /* append name */ strcpy(MyName+1, s); if (StrEquals("FvwmAudio", s)) { /* catch the FvwmAudio alias */ audio_compat = True; } /* Now MyName is defined */ if ((argc != 6)&&(argc != 7)) { fprintf(stderr, "%s Version "VERSION" should only be " "executed by fvwm!\n", MyName+1); exit(1); } #ifdef HAVE_SIGACTION { struct sigaction sigact; sigemptyset(&sigact.sa_mask); # ifdef SA_INTERRUPT sigact.sa_flags = SA_INTERRUPT; # else sigact.sa_flags = 0; # endif sigact.sa_handler = TerminateHandler; /* Dead pipe == Fvwm died */ sigaction(SIGPIPE,&sigact,NULL); /* "polite" termination signal */ sigaction(SIGTERM,&sigact,NULL); } #else /* We don't have sigaction(), so fall back to less robust methods. */ signal(SIGPIPE, TerminateHandler); signal(SIGTERM, TerminateHandler); #endif fd[0] = atoi(argv[1]); fd[1] = atoi(argv[2]); /* configure events */ config(); /* Startup event */ execute_event(builtin_event_table, BUILTIN_STARTUP, NULL); if (start_audio_delay) { last_time = time(0); } /* tell fvwm we're running */ SetMessageMask(fd, m_selected); SetMessageMask(fd, mx_selected | M_EXTENDED_MSG); m_sync = (m_selected & SYNC_MASK_M); mx_sync = (mx_selected & SYNC_MASK_M) | M_EXTENDED_MSG; /* migo (19-Aug-2000): synchronize on M_DESTROY_WINDOW * dv (6-Jul-2002: synchronize on a number of events that can hide or * destroy a window */ if (m_sync) { SetSyncMask(fd, m_sync); } if (mx_sync != M_EXTENDED_MSG) { SetSyncMask(fd, mx_sync); } mx_sync &= ~M_EXTENDED_MSG; SendFinishedStartupNotification(fd); /* main loop */ while (!isTerminated) { unsigned long msg_bit; event_entry *event_table; if ((count = read(fd[1], header, FvwmPacketHeaderSize_byte)) <= 0) { isTerminated = 1; continue; } /* if this read is interrrupted EINTR, the wrong event is * triggered! */ if (header[0] != START_FLAG) { /* should find something better for resyncing */ unlock_event(header[1]); continue; } /* Ignore events that occur during the delay period. */ now = time(0); /* junk the event body */ total=0; remaining = (header[2] - FvwmPacketHeaderSize) * sizeof(unsigned long); while (remaining) { if ((count=read(fd[1],&body[total],remaining)) < 0) { isTerminated = 1; unlock_event(header[1]); continue; } remaining -= count; total +=count; } if (now < last_time + audio_delay + start_audio_delay) { /* quash event */ unlock_event(header[1]); continue; } else { start_audio_delay = 0; } /* event will equal the number of shifts in the base-2 * header[1] number. Could use log here but this should be * fast enough. */ event = -1; msg_bit = header[1]; is_extended_msg = (msg_bit & M_EXTENDED_MSG); msg_bit &= ~M_EXTENDED_MSG; while (msg_bit) { event++; msg_bit >>= 1; } if ( event == -1 || (event >= MAX_MESSAGES && !is_extended_msg) || (event >= MAX_EXTENDED_MESSAGES && is_extended_msg)) { event_table = builtin_event_table; event = BUILTIN_UNKNOWN; } else if (is_extended_msg) { event_table = extended_message_event_table; } else { event_table = message_event_table; } execute_event(event_table, event, body); unlock_event(header[1]); } /* while */ execute_event(builtin_event_table, BUILTIN_SHUTDOWN, NULL); return 0; } /* * * Procedure: * * execute_event - actually executes the actions from lookup table * */ void execute_event(event_entry *event_table, short event, unsigned long *body) { /* this is the sign that rplay is used */ if (rplay_fd != -1 && event_table[event].action.rplay != NULL) { if (Fplay(rplay_fd, event_table[event].action.rplay) >= 0) { last_time = now; } else { Fplay_perror("rplay"); } return; } if (event_table[event].action.action != NULL) { char *buf = NULL; int len = 0; char *action; action = event_table[event].action.action; len = strlen(cmd_line) + strlen(action) + 32; if (audio_play_dir) { len += strlen(audio_play_dir); } buf = (char *)safemalloc(len); if (audio_compat) { /* Don't use audio_play_dir if it's NULL or if * the sound file is an absolute pathname. */ if (!audio_play_dir || audio_play_dir[0] == '\0' || action[0] == '/') { sprintf(buf,"%s %s", cmd_line, action); } else { sprintf(buf,"%s %s/%s &", cmd_line, audio_play_dir, action); } if (!system(buf)) { last_time = now; } } else { int action_arg = event_table[event].action_arg; int fw = 0; if (action_arg != -1 && !(action_arg & ARG_NO_WINID)) { fw = body[action_arg]; } if (PassID && action_arg != -1) { if (action_arg & ARG_NO_WINID) { action_arg &= ~ARG_NO_WINID; sprintf(buf, "%s %s %ld", cmd_line, action, body[action_arg]); } else { sprintf(buf, "%s %s 0x%lx", cmd_line, action, body[action_arg]); } } else { sprintf(buf,"%s %s", cmd_line, action); } /* let fvwm execute the function */ SendText(fd, buf, fw); last_time = now; } free(buf); return; } return; } /* * * Procedure: * config - read the configuration file. * */ static int volume = FPLAY_DEFAULT_VOLUME; static int priority = FPLAY_DEFAULT_PRIORITY; void handle_config_line(char *buf, char **phost) { char *event; char *action; char *p; char *token; char **e; int i; int found; char *table[]= { "Cmd", "Delay", "Dir", "PassID", "PlayCmd", "RplayHost", "RplayPriority", "RplayVolume", "StartDelay" }; /* define entries here, if this list becomes unsorted, use FindToken */ p = buf + MyNameLen; /* config option ? */ if ((e = FindToken(p, table, char *))) { /* skip matched token */ p += strlen(*e); token = PeekToken(p, &p); switch (e - (char**)table) { case 4: /* PlayCmd */ if (!audio_compat) { /* PlayCmd */ fprintf(stderr, "%s: PlayCmd supported only when" " invoked as FvwmAudio\n", MyName+1); break; } /* fall through */ case 0: /* Cmd */ if (cmd_line) { free(cmd_line); } if (token) { cmd_line = safestrdup(token); } else { cmd_line = safestrdup(""); } break; case 1: /* Delay */ if (token) { audio_delay = atoi(token); } break; case 2: /* Dir */ if (!audio_compat) { fprintf(stderr, "%s: Dir supported only when invoked as" " FvwmAudio\n", MyName+1); break; } if (token) { if (audio_play_dir) { free(audio_play_dir); } audio_play_dir = safestrdup(token); } break; case 3: /* PassID */ PassID = True; break; case 5: /* RPlayHost */ if (*phost) { free(*phost); *phost = NULL; } if (token && (*token == '$')) { /* Check for $HOSTDISPLAY */ char *c1 = (char *)getenv(token + 1); char *c2; if (c1 != NULL) { *phost = safestrdup(c1); c2 = *phost; while (c1 && *c1 != ':') { *c2++ = *c1++; } *c2 = '\0'; } } else if (token) { *phost = safestrdup(token); } break; case 6: /* RplayPriority */ if (token) { priority = atoi(token); } break; case 7: /* RplayVolume */ if (token) { volume = atoi(token); } break; case 8: /* StartDelay */ if (token) { start_audio_delay = atoi(token); } break; } } else { event_entry **event_table; /* test for isspace(*p) ??? */ p = GetNextSimpleOption(p, &event); p = GetNextSimpleOption(p, &action); if (!event || !*event || !action || !*action) { if (event) { free(event); } if (action) { free(action); } fprintf(stderr, "%s: incomplete event definition %s\n", MyName + 1, buf); return; } event_table = event_tables; found = 0; while(found == 0 && *event_table != NULL) { event_entry *loop_event; for (loop_event = *event_table, i = 0; found == 0 && loop_event->name != NULL; loop_event++, i++) { if (MatchToken(event, loop_event->name)) { if (loop_event->action.action != NULL) { free(loop_event->action.action); } loop_event->action.action = action; found = 1; if (*event_table == message_event_table) { m_selected |= (1 << i); } else if ( *event_table == extended_message_event_table) { mx_selected |= (1 << i); } } } event_table++; } if (!found) { fprintf(stderr, "%s: unknown event type: %s\n", MyName+1, event); if (action) { free(action); } } if (event) { free(event); } } return; } void config(void) { char *buf; char *host = NULL; if (USE_FPLAY) { host = safestrdup(Fplay_default_host()); } /* get config lines with my name */ InitGetConfigLine(fd,MyName); while (GetConfigLine(fd,&buf), buf != NULL && *buf != 0) { if (buf[strlen(buf)-1] == '\n') { /* if line ends with newline, strip it off */ buf[strlen(buf)-1] = '\0'; } /* Search for MyName (normally *FvwmEvent) */ if (strncasecmp(buf, MyName, MyNameLen) == 0) { handle_config_line(buf, &host); } } /* Builtin rplay support is enabled when * FvwmAudioPlayCmd == builtin-rplay. */ if (USE_FPLAY && StrEquals(cmd_line, "builtin-rplay")) { event_entry **event_table; if ((rplay_fd = Fplay_open(host)) < 0) { Fplay_perror("rplay_open"); exit(1); } /* change actions to rplay objects */ event_table = event_tables; while(*event_table != NULL) { event_entry *loop_event; for (loop_event = *event_table; loop_event->name != NULL; loop_event++) { if (loop_event->action.action != NULL) { char *tmp_action; tmp_action = loop_event->action.action; loop_event->action.rplay = Fplay_create( FPLAY_PLAY); Fplay_set( loop_event->action.rplay, FPLAY_APPEND, FPLAY_SOUND, tmp_action, FPLAY_PRIORITY, priority, FPLAY_VOLUME, volume, NULL); free(tmp_action); } } event_table++; } } return; } /* * * Procedure: * SIGPIPE handler - SIGPIPE means fvwm is dying * */ static RETSIGTYPE TerminateHandler(int nonsense) { isTerminated = True; SIGNAL_RETURN; } /* * * Procedure: * Externally callable procedure to quit * */ RETSIGTYPE DeadPipe(int flag) { execute_event(builtin_event_table, BUILTIN_SHUTDOWN, NULL); exit(flag); SIGNAL_RETURN; }