/* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/****************************************************************************
* This module is all original code
* by Rob Nation
* Copyright 1993, Robert Nation
* You may use this code for any purpose, as long as the original
* copyright remains in the source code and all documentation
****************************************************************************/
/***********************************************************************
*
* code for launching fvwm modules.
*
***********************************************************************/
#include "config.h"
#include <stdio.h>
#include <stdarg.h>
#include <errno.h>
#include <time.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#include "libs/fvwmlib.h"
#include "libs/Colorset.h"
#include "libs/FScreen.h"
#include "fvwm.h"
#include "externs.h"
#include "cursor.h"
#include "functions.h"
#include "bindings.h"
#include "misc.h"
#include "screen.h"
#include "module_interface.h"
#include "read.h"
#include "events.h"
#include "fvwmsignal.h"
/*
* Use POSIX behaviour if we can, otherwise use SysV instead
*/
#ifndef O_NONBLOCK
# define O_NONBLOCK O_NDELAY
#endif
int npipes;
int *readPipes;
int *writePipes;
int *pipeOn;
char **pipeName;
#ifndef WITHOUT_KILLMODULE_ALIAS_SUPPORT
char **pipeAlias; /* as given in: Module FvwmPager MyAlias */
#endif
unsigned long *PipeMask;
unsigned long *SyncMask;
unsigned long *NoGrabMask;
struct queue_buff_struct **pipeQueue;
extern fd_set init_fdset;
static void DeleteMessageQueueBuff(int module);
static void AddToMessageQueue(int module, unsigned long *ptr, int size, int done);
static void AddToCommandQueue(Window w, int module, char * command);
void initModules(void)
{
int i;
npipes = GetFdWidth();
writePipes = (int *)safemalloc(sizeof(int)*npipes);
readPipes = (int *)safemalloc(sizeof(int)*npipes);
pipeOn = (int *)safemalloc(sizeof(int)*npipes);
PipeMask = (unsigned long *)safemalloc(sizeof(unsigned long)*npipes);
SyncMask = (unsigned long *)safemalloc(sizeof(unsigned long)*npipes);
NoGrabMask = (unsigned long *)safemalloc(sizeof(unsigned long)*npipes);
pipeName = (char **)safemalloc(sizeof(char *)*npipes);
#ifndef WITHOUT_KILLMODULE_ALIAS_SUPPORT
pipeAlias= (char **)safemalloc(sizeof(char *)*npipes);
#endif
pipeQueue=(struct queue_buff_struct **)
safemalloc(sizeof(struct queue_buff_struct *)*npipes);
for(i=0;i<npipes;i++)
{
writePipes[i]= -1;
readPipes[i]= -1;
pipeOn[i] = -1;
PipeMask[i] = MAX_MASK;
SyncMask[i] = 0;
NoGrabMask[i] = 0;
pipeQueue[i] = (struct queue_buff_struct *)NULL;
pipeName[i] = NULL;
#ifndef WITHOUT_KILLMODULE_ALIAS_SUPPORT
pipeAlias[i] = NULL;
#endif
}
DBUG("initModules", "Zeroing init module array\n");
FD_ZERO(&init_fdset);
}
void ClosePipes(void)
{
int i;
for(i=0; i<npipes; ++i)
{
if(writePipes[i] > 0)
{
close(writePipes[i]);
close(readPipes[i]);
}
if(pipeName[i] != NULL)
{
free(pipeName[i]);
pipeName[i] = 0;
}
#ifndef WITHOUT_KILLMODULE_ALIAS_SUPPORT
if(pipeAlias[i] != NULL)
{
free(pipeAlias[i]);
pipeAlias[i] = NULL;
}
#endif
while(pipeQueue[i] != NULL)
{
DeleteMessageQueueBuff(i);
}
}
}
static int do_execute_module(F_CMD_ARGS, Bool desperate)
{
int fvwm_to_app[2],app_to_fvwm[2];
int i,val,nargs = 0;
int ret_pipe;
char *cptr;
char **args;
char *arg1 = NULL;
char arg2[20];
char arg3[20];
char arg5[20];
char arg6[20];
char *token;
extern char *ModulePath;
Window win;
args = (char **)safemalloc(7 * sizeof(char *));
/* Olivier: Why ? */
/* if(eventp->type != KeyPress) */
/* UngrabEm(); */
if(action == NULL)
{
free(args);
return -1;
}
if(tmp_win)
win = tmp_win->w;
else
win = None;
/* If we execute a module, don't wait for buttons to come up,
* that way, a pop-up menu could be implemented */
*Module = 0;
action = GetNextToken(action, &cptr);
if (!cptr)
{
free(args);
return -1;
}
arg1 = searchPath( ModulePath, cptr, EXECUTABLE_EXTENSION, X_OK );
if(arg1 == NULL)
{
/* If this function is called in 'desparate' mode this means fvwm is
* trying a module name as a last resort. In this case the error message
* is inappropriate because it was most likely a typo in a command, not
* a module name. */
if (!desperate)
{
fvwm_msg(ERR,"executeModule",
"No such module '%s' in ModulePath '%s'",cptr,ModulePath);
}
free(args);
free(cptr);
return -1;
}
#ifdef REMOVE_EXECUTABLE_EXTENSION
{
char* p = arg1 + strlen( arg1 ) - strlen( EXECUTABLE_EXTENSION );
if ( strcmp( p, EXECUTABLE_EXTENSION ) == 0 )
*p = 0;
}
#endif
/* Look for an available pipe slot */
i=0;
while((i<npipes) && (writePipes[i] >=0))
i++;
if(i>=npipes)
{
fvwm_msg(ERR,"executeModule","Too many Accessories!");
free(arg1);
free(cptr);
free(args);
return -1;
}
ret_pipe = i;
/* I want one-ended pipes, so I open two two-ended pipes,
* and close one end of each. I need one ended pipes so that
* I can detect when the module crashes/malfunctions */
if(pipe(fvwm_to_app)!=0)
{
fvwm_msg(ERR,"executeModule","Failed to open pipe");
free(arg1);
free(cptr);
free(args);
return -1;
}
if(pipe(app_to_fvwm)!=0)
{
fvwm_msg(ERR,"executeModule","Failed to open pipe2");
free(arg1);
free(cptr);
close(fvwm_to_app[0]);
close(fvwm_to_app[1]);
free(args);
return -1;
}
pipeName[i] = stripcpy(cptr);
free(cptr);
sprintf(arg2,"%d",app_to_fvwm[1]);
sprintf(arg3,"%d",fvwm_to_app[0]);
sprintf(arg5,"%lx",(unsigned long)win);
sprintf(arg6,"%lx",(unsigned long)context);
args[0] = arg1;
args[1] = arg2;
args[2] = arg3;
args[3] = (char *)get_current_read_file();
if (!args[3])
args[3] = "none";
args[4] = arg5;
args[5] = arg6;
for (nargs = 6; action = GetNextToken(action, &token), token; nargs++)
{
args = (char **)saferealloc((void *)args, (nargs + 2) * sizeof(char *));
args[nargs] = token;
#ifndef WITHOUT_KILLMODULE_ALIAS_SUPPORT
if (pipeAlias[i] == NULL)
{
const char *ptr = skipModuleAliasToken(args[nargs]);
if (ptr && *ptr == '\0')
pipeAlias[i] = stripcpy(args[nargs]);
}
#endif
}
args[nargs] = NULL;
/* Try vfork instead of fork. The man page says that vfork is better! */
/* Also, had to change exit to _exit() */
/* Not everyone has vfork! */
val = fork();
if(val > 0)
{
/* This fork remains running fvwm */
/* close appropriate descriptors from each pipe so
* that fvwm will be able to tell when the app dies */
close(app_to_fvwm[1]);
close(fvwm_to_app[0]);
/* add these pipes to fvwm's active pipe list */
writePipes[i] = fvwm_to_app[1];
readPipes[i] = app_to_fvwm[0];
pipeOn[i] = -1;
PipeMask[i] = MAX_MASK;
SyncMask[i] = 0;
NoGrabMask[i] = 0;
free(arg1);
pipeQueue[i] = NULL;
if (DoingCommandLine) {
/* add to the list of command line modules */
DBUG("executeModule", "starting commandline module\n");
FD_SET(i, &init_fdset);
}
/* make the PositiveWrite pipe non-blocking. Don't want to jam up
fvwm because of an uncooperative module */
fcntl(writePipes[i], F_SETFL, O_NONBLOCK);
/* Mark the pipes close-on exec so other programs
* won`t inherit them */
if (fcntl(readPipes[i], F_SETFD, 1) == -1)
fvwm_msg(ERR,"executeModule","module close-on-exec failed");
if (fcntl(writePipes[i], F_SETFD, 1) == -1)
fvwm_msg(ERR,"executeModule","module close-on-exec failed");
for(i=6;i<nargs;i++)
{
if(args[i] != 0)
free(args[i]);
}
}
else if (val ==0)
{
/* this is the child */
/* this fork execs the module */
#ifdef FORK_CREATES_CHILD
close(fvwm_to_app[1]);
close(app_to_fvwm[0]);
#endif
if (!Pdefault) {
char *visualid, *colormap;
visualid = safemalloc(32);
sprintf(visualid, "FVWM_VISUALID=%lx", XVisualIDFromVisual(Pvisual));
putenv(visualid);
colormap = safemalloc(32);
sprintf(colormap, "FVWM_COLORMAP=%lx", Pcmap);
putenv(colormap);
}
/** Why is this execvp?? We've already searched the module path! **/
execvp(arg1,args);
fvwm_msg(ERR,"executeModule","Execution of module failed: %s",arg1);
perror("");
close(app_to_fvwm[1]);
close(fvwm_to_app[0]);
#ifdef FORK_CREATES_CHILD
exit(1);
#endif
}
else
{
fvwm_msg(ERR,"executeModule","Fork failed");
free(arg1);
for(i=6;i<nargs;i++)
{
if(args[i] != 0)
free(args[i]);
}
free(args);
return -1;
}
free(args);
return ret_pipe;
}
int executeModuleDesperate(F_CMD_ARGS)
{
return do_execute_module(F_PASS_ARGS, True);
}
void CMD_Module(F_CMD_ARGS)
{
do_execute_module(F_PASS_ARGS, False);
return;
}
void CMD_ModuleSynchronous(F_CMD_ARGS)
{
extern char *ModuleFinished; /* defined in libs/Module.c */
int sec = 0;
char *next;
char *token;
char *expect = ModuleFinished;
int pipe_slot;
fd_set in_fdset;
fd_set out_fdset;
Window targetWindow;
extern fd_set_size_t fd_width;
time_t start_time;
Bool done = False;
Bool need_ungrab = False;
char *escape = NULL;
XEvent tmpevent;
extern FvwmWindow *Tmp_win;
if (!action)
return;
token = PeekToken(action, &next);
if (StrEquals(token, "expect"))
{
token = PeekToken(next, &next);
if (token)
{
expect = alloca(strlen(token) + 1);
strcpy(expect, token);
}
action = next;
token = PeekToken(action, &next);
}
if (token && StrEquals(token, "timeout"))
{
if (GetIntegerArguments(next, &next, &sec, 1) > 0 && sec > 0)
{
/* we have a delay, skip the number */
action = next;
}
else
{
fvwm_msg(ERR, "executeModuleSync", "illegal timeout");
return;
}
}
if (!action)
{
/* no module name */
return;
}
pipe_slot = do_execute_module(F_PASS_ARGS, False);
if (pipe_slot == -1)
{
/* executing the module failed, just return */
return;
}
/* Busy cursor stuff */
if (Scr.BusyCursor & BUSY_MODULESYNCHRONOUS)
{
if (GrabEm(CRS_WAIT, GRAB_BUSY))
need_ungrab = True;
}
/* wait for module input */
start_time = time(NULL);
while (!done)
{
struct timeval timeout;
int num_fd;
/* A signal here could interrupt the select call. We would
* then need to restart our select, unless the signal was
* a "terminate" signal. Note that we need to reinitialise
* all of select's parameters after it has returned. */
do
{
FD_ZERO(&in_fdset);
FD_ZERO(&out_fdset);
if (readPipes[pipe_slot] >= 0)
FD_SET(readPipes[pipe_slot], &in_fdset);
if (pipeQueue[pipe_slot] != NULL)
FD_SET(writePipes[pipe_slot], &out_fdset);
timeout.tv_sec = 0;
timeout.tv_usec = 1; /* use 1 usec timeout in select */
num_fd = fvwmSelect(fd_width, &in_fdset, &out_fdset, 0, &timeout);
}
while ( (num_fd < 0) && !isTerminated );
/* Exit if we have received a "terminate" signal */
if ( isTerminated ) break;
if (num_fd > 0)
{
if ((readPipes[pipe_slot] >= 0) &&
FD_ISSET(readPipes[pipe_slot], &in_fdset))
{
/* Check for module input. */
if (read(readPipes[pipe_slot], &targetWindow, sizeof(Window)) > 0)
{
if (HandleModuleInput(targetWindow, pipe_slot, expect, False))
{
/* we got the message we were waiting for */
done = True;
}
}
else
{
KillModule(pipe_slot);
done = True;
}
}
if ((writePipes[pipe_slot] >= 0) &&
FD_ISSET(writePipes[pipe_slot], &out_fdset))
{
FlushMessageQueue(pipe_slot);
}
}
usleep(1000);
if (difftime(time(NULL), start_time) >= sec && sec)
{
/* timeout */
done = True;
}
/* Check for "escape function" */
if (XPending(dpy) && XCheckMaskEvent(dpy, KeyPressMask, &tmpevent))
{
escape = CheckBinding(Scr.AllBindings,
STROKE_ARG(0)
tmpevent.xkey.keycode,
tmpevent.xkey.state, GetUnusedModifiers(),
GetContext(Tmp_win, &tmpevent, &targetWindow),
KEY_BINDING);
if (escape != NULL)
{
if (!strcasecmp(escape,"escapefunc"))
done = True;
}
}
} /* while */
if (need_ungrab)
UngrabEm(GRAB_BUSY);
}
/* read input from a module and either execute it or queue it
* returns True and does NOP if the module input matches the expect string */
Bool HandleModuleInput(Window w, int module, char *expect, Bool queue)
{
char text[MAX_MODULE_INPUT_TEXT_LEN];
unsigned long size;
unsigned long cont;
int n;
int channel = readPipes[module];
/* Already read a (possibly NULL) window id from the pipe,
* Now read an fvwm bultin command line */
n = read(channel, &size, sizeof(size));
if (n < sizeof(size))
{
fvwm_msg(ERR, "HandleModuleInput",
"Fail to read (Module: %i, read: %i, size: %i)",
module, n, sizeof(size));
KillModule(module);
return False;
}
if (size > sizeof(text))
{
fvwm_msg(ERR, "HandleModuleInput",
"Module(%i) command is too big (%ld), limit is %d",
module, size, sizeof(text));
/* The rest of the output from this module is going to be scrambled
* so let's kill it rather than risk interpreting garbage */
KillModule(module);
return False;
}
pipeOn[module] = 1;
n = read(channel, text, size);
if (n < size)
{
fvwm_msg(ERR, "HandleModuleInput",
"Fail to read command (Module: %i, read: %i, size: %ld)",
module, n, size);
KillModule(module);
return False;
}
text[n] = '\0';
n = read(channel, &cont, sizeof(cont));
if (n < sizeof(cont))
{
fvwm_msg(ERR, "HandleModuleInput",
"Module %i, Size Problems (read: %d, size: %d)",
module, n, sizeof(cont));
KillModule(module);
return False;
}
if (cont == 0)
{
/* this is documented as a valid way for a module to quit
* so let's not complain
fvwm_msg(ERR, "HandleModuleInput",
"Module %i, Size Problem: cont is zero", module);
*/
KillModule(module);
}
if (strlen(text)>0)
{
if (expect && (strncasecmp(text, expect, strlen(expect)) == 0))
/* the module sent the expected string */
return True;
if (queue)
AddToCommandQueue(w, module, text);
else
ExecuteModuleCommand(w, module, text);
}
return False;
}
/* run the command as if it cames from a button press or release */
void ExecuteModuleCommand(Window w, int module, char *text)
{
extern FvwmWindow *ButtonWindow;
extern int Context;
FvwmWindow *tmp_win;
if (XFindContext (dpy, w, FvwmContext, (caddr_t *) &tmp_win) == XCNOENT)
{
tmp_win = NULL;
w = None;
}
/* Query the pointer, the pager-drag-out feature doesn't work properly.*/
/* This is OK now that the Pager uses "Move pointer" */
/* A real fix would be for the modules to pass the button press coords */
if (XQueryPointer(dpy, Scr.Root, &JunkRoot, &JunkChild, &JunkX,&JunkY,
&Event.xbutton.x_root,&Event.xbutton.y_root,&JunkMask) ==
False)
{
/* pointer is not on this screen */
/* If a module does XUngrabPointer(), it can now get proper Popups */
Event.xbutton.window = Scr.Root;
Event.xbutton.button = 1;
Event.xbutton.subwindow = None;
tmp_win = NULL;
ButtonWindow = NULL;
}
else
{
Event.xbutton.window = w;
Event.xbutton.button = 1;
Event.xbutton.subwindow = None;
Context = GetContext(tmp_win,&Event,&w);
ButtonWindow = tmp_win;
}
/* If a module does XUngrabPointer(), it can now get proper Popups */
if(StrEquals(text, "popup"))
Event.xbutton.type = ButtonPress;
else
Event.xbutton.type = ButtonRelease;
Event.xbutton.x = 0;
Event.xbutton.y = 0;
Context = GetContext(tmp_win,&Event,&w);
old_execute_function(text, tmp_win, &Event, Context, module, 0, NULL);
ButtonWindow = NULL;
}
RETSIGTYPE DeadPipe(int sig)
{}
void KillModule(int channel)
{
close(readPipes[channel]);
close(writePipes[channel]);
readPipes[channel] = -1;
writePipes[channel] = -1;
pipeOn[channel] = -1;
while(pipeQueue[channel] != NULL)
{
DeleteMessageQueueBuff(channel);
}
if(pipeName[channel] != NULL)
{
free(pipeName[channel]);
pipeName[channel] = NULL;
}
#ifndef WITHOUT_KILLMODULE_ALIAS_SUPPORT
if(pipeAlias[channel] != NULL)
{
free(pipeAlias[channel]);
pipeAlias[channel] = NULL;
}
#endif
if (fFvwmInStartup) {
/* remove from list of command line modules */
DBUG("killModule", "ending command line module\n");
FD_CLR(channel, &init_fdset);
}
return;
}
static void KillModuleByName(char *name
#ifndef WITHOUT_KILLMODULE_ALIAS_SUPPORT
, char *alias
#endif
)
{
int i = 0;
if(name == NULL)
return;
while(i<npipes)
{
if((pipeName[i] != NULL)&&(matchWildcards(name,pipeName[i]))
#ifndef WITHOUT_KILLMODULE_ALIAS_SUPPORT
&& (!alias || (pipeAlias[i] && matchWildcards(alias, pipeAlias[i])))
#endif
)
{
KillModule(i);
}
i++;
}
return;
}
void CMD_KillModule(F_CMD_ARGS)
{
char *module;
#ifndef WITHOUT_KILLMODULE_ALIAS_SUPPORT
char *alias = NULL;
#endif
action = GetNextToken(action,&module);
if (!module)
return;
#ifndef WITHOUT_KILLMODULE_ALIAS_SUPPORT
GetNextToken(action, &alias);
KillModuleByName(module, alias);
#else
KillModuleByName(module);
#endif
free(module);
}
static unsigned long *
make_vpacket(unsigned long *body, unsigned long event_type,
unsigned long num, va_list ap)
{
extern Time lastTimestamp;
unsigned long *bp = body;
/* truncate long packets */
if (num > FvwmPacketMaxSize)
num = FvwmPacketMaxSize;
*(bp++) = START_FLAG;
*(bp++) = event_type;
*(bp++) = num+FvwmPacketHeaderSize;
*(bp++) = lastTimestamp;
for (; num > 0; --num)
*(bp++) = va_arg(ap, unsigned long);
return body;
}
/*************************************************************
RBW - 04/16/1999 - new packet builder for GSFR --
Arguments are pairs of lengths and argument data pointers.
RBW - 05/01/2000 -
A length of zero means that an int is being passed which
must be stored in the packet as an unsigned long. This is
a special hack to accommodate the old CONFIGARGSNEW
technique of sending the args for the M_CONFIGURE_WINDOW
packet.
**************************************************************/
static unsigned long
make_new_vpacket(unsigned char *body, unsigned long event_type,
unsigned long num, va_list ap)
{
extern Time lastTimestamp;
unsigned long arglen;
unsigned long bodylen = 0;
unsigned long *bp = (unsigned long *)body;
unsigned long *bp1 = bp;
unsigned long plen = 0;
int *tmpint;
Bool expandint;
*(bp++) = START_FLAG;
*(bp++) = event_type;
/* Skip length field, we don't know it yet. */
bp++;
*(bp++) = lastTimestamp;
for (; num > 0; --num) {
arglen = va_arg(ap, unsigned long);
if (arglen != 0) {
expandint = False;
} else {
expandint = True;
arglen = sizeof(unsigned long);
}
bodylen += arglen;
if (bodylen < FvwmPacketMaxSize_byte) {
if (! expandint) {
register char *tmp = (char *)bp;
memcpy(tmp, va_arg(ap, char *), arglen);
tmp += arglen;
bp = (unsigned long *)tmp;
} else {
tmpint = va_arg(ap, int *);
*bp = (unsigned long) *tmpint;
bp++;
}
}
}
/*
Round up to a long word boundary. Most of the module interface
still thinks in terms of an array of long ints, so let's humor it.
*/
plen = (unsigned long) ((char *)bp - (char *)bp1);
plen = ((plen + (sizeof(long) - 1)) / sizeof(long)) * sizeof(long);
*(((unsigned long*)bp1)+2) = (plen / (sizeof(unsigned long)));
return plen;
}
void
SendPacket(int module, unsigned long event_type, unsigned long num_datum, ...)
{
unsigned long body[FvwmPacketMaxSize];
va_list ap;
va_start(ap, num_datum);
make_vpacket(body, event_type, num_datum, ap);
va_end(ap);
PositiveWrite(module, body, (num_datum+FvwmPacketHeaderSize)*sizeof(body[0]));
}
void
BroadcastPacket(unsigned long event_type, unsigned long num_datum, ...)
{
unsigned long body[FvwmPacketMaxSize];
va_list ap;
int i;
va_start(ap,num_datum);
make_vpacket(body, event_type, num_datum, ap);
va_end(ap);
for (i=0; i<npipes; i++)
PositiveWrite(i, body, (num_datum+FvwmPacketHeaderSize)*sizeof(body[0]));
}
/* ************************************************************
RBW - 04/16/1999 - new style packet senders for GSFR --
************************************************************ */
static void SendNewPacket(int module, unsigned long event_type,
unsigned long num_datum, ...)
{
unsigned char body[FvwmPacketMaxSize_byte];
va_list ap;
unsigned long plen;
va_start(ap,num_datum);
plen = make_new_vpacket(body, event_type, num_datum, ap);
va_end(ap);
PositiveWrite(module, (void *) &body, plen);
}
static void BroadcastNewPacket(unsigned long event_type,
unsigned long num_datum, ...)
{
unsigned char body[FvwmPacketMaxSize_byte];
va_list ap;
int i;
unsigned long plen;
va_start(ap,num_datum);
plen = make_new_vpacket(body, event_type, num_datum, ap);
va_end(ap);
for (i=0; i<npipes; i++) {
PositiveWrite(i, (void *) &body, plen);
}
}
/* this is broken, the flags may not fit in a word */
#define CONFIGARGS(_t) 24,\
(_t)->w,\
(_t)->frame,\
(unsigned long)(_t),\
(_t)->frame_g.x,\
(_t)->frame_g.y,\
(_t)->frame_g.width,\
(_t)->frame_g.height,\
(_t)->Desk,\
(_t)->flags,\
(_t)->title_g.height,\
(_t)->boundary_width,\
(_t)->hints.base_width,\
(_t)->hints.base_height,\
(_t)->hints.width_inc,\
(_t)->hints.height_inc,\
(_t)->hints.min_width,\
(_t)->hints.min_height,\
(_t)->hints.max_width,\
(_t)->hints.max_height,\
(_t)->icon_w,\
(_t)->icon_pixmap_w,\
(_t)->hints.win_gravity,\
(_t)->colors.fore,\
(_t)->colors.back
#ifndef DISABLE_MBC
#define OLDCONFIGARGS(_t) 24,\
(_t)->w,\
(_t)->frame,\
(unsigned long)(_t),\
(_t)->frame_g.x,\
(_t)->frame_g.y,\
(_t)->frame_g.width,\
(_t)->frame_g.height,\
(_t)->Desk,\
old_flags,\
(_t)->title_g.height,\
(_t)->boundary_width,\
(_t)->hints.base_width,\
(_t)->hints.base_height,\
(_t)->hints.width_inc,\
(_t)->hints.height_inc,\
(_t)->hints.min_width,\
(_t)->hints.min_height,\
(_t)->hints.max_width,\
(_t)->hints.max_height,\
(_t)->icon_w,\
(_t)->icon_pixmap_w,\
(_t)->hints.win_gravity,\
(_t)->colors.fore,\
(_t)->colors.back
#define SETOLDFLAGS \
{ int i = 1; \
old_flags |= DO_START_ICONIC(t) ? i : 0; i<<=1; \
old_flags |= False /* OnTop */ ? i : 0; i<<=1; \
old_flags |= IS_STICKY(t) ? i : 0; i<<=1; \
old_flags |= DO_SKIP_WINDOW_LIST(t) ? i : 0; i<<=1; \
old_flags |= IS_ICON_SUPPRESSED(t) ? i : 0; i<<=1; \
old_flags |= HAS_NO_ICON_TITLE(t) ? i : 0; i<<=1; \
old_flags |= IS_LENIENT(t) ? i : 0; i<<=1; \
old_flags |= IS_ICON_STICKY(t) ? i : 0; i<<=1; \
old_flags |= DO_SKIP_ICON_CIRCULATE(t) ? i : 0; i<<=1; \
old_flags |= DO_SKIP_CIRCULATE(t) ? i : 0; i<<=1; \
old_flags |= HAS_CLICK_FOCUS(t) ? i : 0; i<<=1; \
old_flags |= HAS_SLOPPY_FOCUS(t) ? i : 0; i<<=1; \
old_flags |= !DO_NOT_SHOW_ON_MAP(t) ? i : 0; i<<=1; \
old_flags |= HAS_BORDER(t) ? i : 0; i<<=1; \
old_flags |= HAS_TITLE(t) ? i : 0; i<<=1; \
old_flags |= IS_MAPPED(t) ? i : 0; i<<=1; \
old_flags |= IS_ICONIFIED(t) ? i : 0; i<<=1; \
old_flags |= IS_TRANSIENT(t) ? i : 0; i<<=1; \
old_flags |= False /* Raised */ ? i : 0; i<<=1; \
old_flags |= IS_FULLY_VISIBLE(t) ? i : 0; i<<=1; \
old_flags |= IS_ICON_OURS(t) ? i : 0; i<<=1; \
old_flags |= IS_PIXMAP_OURS(t) ? i : 0; i<<=1; \
old_flags |= IS_ICON_SHAPED(t) ? i : 0; i<<=1; \
old_flags |= IS_MAXIMIZED(t) ? i : 0; i<<=1; \
old_flags |= WM_TAKES_FOCUS(t) ? i : 0; i<<=1; \
old_flags |= WM_DELETES_WINDOW(t) ? i : 0; i<<=1; \
old_flags |= IS_ICON_MOVED(t) ? i : 0; i<<=1; \
old_flags |= IS_ICON_UNMAPPED(t) ? i : 0; i<<=1; \
old_flags |= IS_MAP_PENDING(t) ? i : 0; i<<=1; \
old_flags |= HAS_MWM_OVERRIDE_HINTS(t) ? i : 0; i<<=1; \
old_flags |= HAS_MWM_BUTTONS(t) ? i : 0; i<<=1; \
old_flags |= HAS_MWM_BORDER(t) ? i : 0; }
#endif /* DISABLE_MBC */
/****************************************************************
RBW - 04/16/1999 - new version for GSFR --
- args are now pairs:
- length of arg data
- pointer to arg data
- number of arguments is the number of length/pointer pairs.
- the 9th field, where flags used to be, is temporarily left
as a dummy to preserve alignment of the other fields in the
old packet: we should drop this before the next release.
*****************************************************************/
#define CONFIGARGSNEW(_t) 25,\
(unsigned long)(sizeof(unsigned long)),\
&(*(_t))->w,\
(unsigned long)(sizeof(unsigned long)),\
&(*(_t))->frame,\
(unsigned long)(sizeof(unsigned long)),\
&(_t),\
(unsigned long)(0),\
&(*(_t))->frame_g.x,\
(unsigned long)(0),\
&(*(_t))->frame_g.y,\
(unsigned long)(0),\
&(*(_t))->frame_g.width,\
(unsigned long)(0),\
&(*(_t))->frame_g.height,\
(unsigned long)(0),\
&(*(_t))->Desk,\
(unsigned long)(0),\
&(*(_t))->layer,\
(unsigned long)(0),\
&(*(_t))->title_g.height,\
(unsigned long)(0),\
&(*(_t))->boundary_width,\
(unsigned long)(0),\
&(*(_t))->hints.base_width,\
(unsigned long)(0),\
&(*(_t))->hints.base_height,\
(unsigned long)(0),\
&(*(_t))->hints.width_inc,\
(unsigned long)(0),\
&(*(_t))->hints.height_inc,\
(unsigned long)(0),\
&(*(_t))->hints.min_width,\
(unsigned long)(0),\
&(*(_t))->hints.min_height,\
(unsigned long)(0),\
&(*(_t))->hints.max_width,\
(unsigned long)(0),\
&(*(_t))->hints.max_height,\
(unsigned long)(0),\
&(*(_t))->icon_w,\
(unsigned long)(sizeof(unsigned long)),\
&(*(_t))->icon_pixmap_w,\
(unsigned long)(0),\
&(*(_t))->hints.win_gravity,\
(unsigned long)(sizeof(unsigned long)),\
&(*(_t))->colors.fore,\
(unsigned long)(sizeof(unsigned long)),\
&(*(_t))->colors.back,\
(unsigned long)(sizeof((*(_t))->flags)),\
&(*(_t))->flags
void SendConfig(int module, unsigned long event_type, const FvwmWindow *t)
{
const FvwmWindow **t1 = &t;
/* RBW- SendPacket(module, event_type, CONFIGARGS(t)); */
SendNewPacket(module, event_type, CONFIGARGSNEW(t1));
#ifndef DISABLE_MBC
/* send out an old version of the packet to keep old mouldules happy */
/* fixme: should test to see if module wants this packet first */
{
long old_flags = 0;
SETOLDFLAGS
SendPacket(module, event_type == M_ADD_WINDOW ?
M_OLD_ADD_WINDOW : M_OLD_CONFIGURE_WINDOW,
OLDCONFIGARGS(t));
}
#endif /* DISABLE_MBC */
}
void BroadcastConfig(unsigned long event_type, const FvwmWindow *t)
{
const FvwmWindow **t1 = &t;
/* RBW- BroadcastPacket(event_type, CONFIGARGS(t)); */
BroadcastNewPacket(event_type, CONFIGARGSNEW(t1));
#ifndef DISABLE_MBC
/* send out an old version of the packet to keep old mouldules happy */
{
long old_flags = 0;
SETOLDFLAGS
BroadcastPacket(event_type == M_ADD_WINDOW ?
M_OLD_ADD_WINDOW : M_OLD_CONFIGURE_WINDOW,
OLDCONFIGARGS(t));
}
#endif /* DISABLE_MBC */
}
static unsigned long *
make_named_packet(int *len, unsigned long event_type, const char *name,
int num, ...)
{
unsigned long *body;
va_list ap;
/* Packet is the header plus the items plus enough items to hold the name
* string. */
*len =
FvwmPacketHeaderSize + num + (strlen(name) / sizeof(unsigned long)) + 1;
/* truncate long packets */
if (*len > FvwmPacketMaxSize)
*len = FvwmPacketMaxSize;
body = (unsigned long *)safemalloc(*len * sizeof(unsigned long));
body[*len-1] = 0; /* Zero out end of memory to avoid uninit memory access. */
va_start(ap, num);
make_vpacket(body, event_type, num, ap);
va_end(ap);
strncpy((char *)&body[FvwmPacketHeaderSize+num], name,
(*len - FvwmPacketHeaderSize - num)*sizeof(unsigned long) - 1);
body[2] = *len;
#if 0
DB(("Packet (%lu): %lu %lu %lu `%s'", *len,
body[FvwmPacketHeaderSize], body[FvwmPacketHeaderSize+1],
body[FvwmPacketHeaderSize+2], name));
#endif
return (body);
}
void
SendName(int module, unsigned long event_type,
unsigned long data1,unsigned long data2, unsigned long data3,
const char *name)
{
unsigned long *body;
int l;
if (name == NULL)
return;
body = make_named_packet(&l, event_type, name, 3, data1, data2, data3);
PositiveWrite(module, body, l*sizeof(unsigned long));
free(body);
}
void
BroadcastName(unsigned long event_type,
unsigned long data1, unsigned long data2, unsigned long data3,
const char *name)
{
unsigned long *body;
int i, l;
if (name == NULL)
return;
body = make_named_packet(&l, event_type, name, 3, data1, data2, data3);
for (i=0; i < npipes; i++)
PositiveWrite(i, body, l*sizeof(unsigned long));
free(body);
}
#ifdef MINI_ICONS
static void
SendMiniIcon(int module, unsigned long event_type,
unsigned long data1, unsigned long data2,
unsigned long data3, unsigned long data4,
unsigned long data5, unsigned long data6,
unsigned long data7, unsigned long data8,
const char *name)
{
unsigned long *body;
int l;
if ((name == NULL) || (event_type != M_MINI_ICON))
return;
body = make_named_packet(&l, event_type, name, 8, data1, data2, data3,
data4, data5, data6, data7, data8);
PositiveWrite(module, body, l*sizeof(unsigned long));
free(body);
}
void
BroadcastMiniIcon(unsigned long event_type,
unsigned long data1, unsigned long data2,
unsigned long data3, unsigned long data4,
unsigned long data5, unsigned long data6,
unsigned long data7, unsigned long data8,
const char *name)
{
unsigned long *body;
int i, l;
body = make_named_packet(&l, event_type, name, 8, data1, data2, data3,
data4, data5, data6, data7, data8);
for (i=0; i < npipes; i++)
PositiveWrite(i, body, l*sizeof(unsigned long));
free(body);
}
#endif /* MINI_ICONS */
/**********************************************************************
* Reads a colorset command from a module and broadcasts it back out
**********************************************************************/
void BroadcastColorset(int n)
{
int i;
char *buf;
buf = DumpColorset(n, &Colorset[n]);
for (i=0; i < npipes; i++)
{
/* just a quick check to save us lots of overhead */
if (pipeOn[i] >= 0)
SendName(i, M_CONFIG_INFO, 0, 0, 0, buf);
}
}
/**********************************************************************
* Broadcasts a string to all modules as M_CONFIG_INFO.
**********************************************************************/
void BroadcastConfigInfoString(char *string)
{
int i;
for (i = 0; i < npipes; i++)
{
/* just a quick check to save us lots of overhead */
if (pipeOn[i] >= 0)
SendName(i, M_CONFIG_INFO, 0, 0, 0, string);
}
}
/**********************************************************************
* Broadcasts the state of Xinerama support to all modules as M_CONFIG_INFO.
**********************************************************************/
void broadcast_xinerama_state(void)
{
BroadcastConfigInfoString((char *)FScreenGetConfiguration());
return;
}
/*
** send an arbitrary string to all instances of a module
*/
void CMD_SendToModule(F_CMD_ARGS)
{
char *module,*str;
unsigned long data0, data1, data2;
int i;
/* FIXME: Without this, popup menus can't be implemented properly in
* modules. Olivier: Why ? */
/* UngrabEm(); */
if (!action)
return;
str = GetNextToken(action, &module);
if (!module)
return;
if (tmp_win) {
/* Modules may need to know which window this applies to */
data0 = tmp_win->w;
data1 = tmp_win->frame;
data2 = (unsigned long)tmp_win;
} else {
data0 = 0;
data1 = 0;
data2 = 0;
}
for (i=0;i<npipes;i++)
{
if ((pipeName[i] != NULL && matchWildcards(module,pipeName[i]))
#ifndef WITHOUT_KILLMODULE_ALIAS_SUPPORT
|| (pipeAlias[i] && matchWildcards(module, pipeAlias[i]))
#endif
)
{
SendName(i,M_STRING,data0,data1,data2,str);
FlushMessageQueue(i);
}
}
free(module);
}
/* This used to be marked "fvwm_inline". I removed this
when I added the lockonsend logic. The routine seems too big to
want to inline. dje 9/4/98 */
extern int myxgrabcount; /* defined in libs/Grab.c */
extern char *ModuleUnlock; /* defined in libs/Module.c */
void PositiveWrite(int module, unsigned long *ptr, int size)
{
extern int moduleTimeout;
if(ptr == NULL)
{
return;
}
if((pipeOn[module]<0)||(!((PipeMask[module]) & ptr[1])))
{
return;
}
/* a dirty hack to prevent FvwmAnimate triggering during Recapture */
/* would be better to send RecaptureStart and RecaptureEnd messages. */
/* If module is lock on send for iconify message and it's an
* iconify event and server grabbed, then return */
if ((NoGrabMask[module] & SyncMask[module] & ptr[1]) && (myxgrabcount != 0))
{
return;
}
AddToMessageQueue(module,ptr,size,0);
/* dje, from afterstep, for FvwmAnimate, allows modules to sync with fvwm.
* this is disabled when the server is grabbed, otherwise deadlocks happen.
* M_LOCKONSEND has been replaced by a separated mask which defines on
* which messages the fvwm-to-module communication need to be lock
* on send. olicha Nov 13, 1999 */
/* migo (19-Aug-2000): removed !myxgrabcount to sync M_DESTROY_WINDOW */
/*if ((SyncMask[module] & ptr[1]) && !myxgrabcount) */
if ((SyncMask[module] & ptr[1]))
{
Window targetWindow;
fd_set readSet;
struct timeval timeout;
int channel = readPipes[module];
FlushMessageQueue(module);
do
{
int rc = 0;
/*
* We give the read a long timeout; if the module fails to
* respond within this time then it deserves to be KILLED!
*
* NOTE: rather than impose an arbitrary timeout on the user,
* we will make this a configuration parameter.
*/
do
{
timeout.tv_sec = moduleTimeout;
timeout.tv_usec = 0;
FD_ZERO(&readSet);
FD_SET(channel, &readSet);
/* Wait for input to arrive on just one descriptor, with a timeout */
/* (fvwmSelect <= 0) or read() returning wrong size is bad news */
rc = fvwmSelect(channel + 1, &readSet, NULL, NULL, &timeout);
/* retry if select() failed with EINTR */
} while ((rc < 0) && !isTerminated && (errno == EINTR));
if ( isTerminated ) break;
if (rc <= 0 || read(channel, &targetWindow, sizeof(targetWindow))
!= sizeof(targetWindow))
{
char *name;
if (pipeName[module] != NULL)
{
#ifndef WITHOUT_KILLMODULE_ALIAS_SUPPORT
if (pipeAlias[module] != NULL)
{
name = CatString3(pipeName[module], " ", pipeAlias[module]);
}
else
#endif
{
name = pipeName[module];
}
}
else
{
name = "(null)";
}
/* Doh! Something has gone wrong - get rid of the offender!! */
fvwm_msg(ERR, "PositiveWrite",
"Failed to read descriptor from '%s':\n"
"- data available=%c\n"
"- terminate signal=%c\n",
name,
(FD_ISSET(channel, &readSet) ? 'Y' : 'N'),
isTerminated ? 'Y' : 'N');
KillModule(module);
break;
}
/* Execute all messages from the module until UNLOCK is received
* N.B. This may cause recursion if a command results in a sync message
* to another module, which in turn may send a command that results in
* another sync message to this module.
* Hippo: I don't think this will cause deadlocks, but the third time we
* get here the first times UNLOCK will be read and then on returning up
* the third level UNLOCK will be read at the first level. This could be
* difficult to fix without turning queueing on. Turning queueing on
* may be bad because it can be useful for modules to be able to inject
* commands from modules in a synchronous manner. e.g. FvwmIconMan
* can tell FvwmAnimate to do an animation when a window is de-iconified
* from the IconMan, queueing make s this happen too late.
*/
}
while (!HandleModuleInput(targetWindow, module, ModuleUnlock, False));
}
}
static void AddToMessageQueue(int module, unsigned long *ptr, int size, int done)
{
struct queue_buff_struct *c,*e;
unsigned long *d;
c = (struct queue_buff_struct *)safemalloc(sizeof(struct queue_buff_struct));
c->next = NULL;
c->size = size;
c->done = done;
d = (unsigned long *)safemalloc(size);
c->data = d;
memcpy((void*)d,(const void*)ptr,size);
e = pipeQueue[module];
if(e == NULL)
{
pipeQueue[module] = c;
return;
}
while(e->next != NULL)
e = e->next;
e->next = c;
}
static void DeleteMessageQueueBuff(int module)
{
struct queue_buff_struct *a;
if(pipeQueue[module] == NULL)
return;
a = pipeQueue[module];
pipeQueue[module] = a->next;
if (a->data != NULL)
free(a->data);
free(a);
return;
}
void FlushMessageQueue(int module)
{
char *dptr;
struct queue_buff_struct *d;
int a;
extern int errno;
if((pipeOn[module] <= 0)||(pipeQueue[module] == NULL))
return;
while(pipeQueue[module] != NULL)
{
d = pipeQueue[module];
dptr = (char *)d->data;
while(d->done < d->size)
{
a = write(writePipes[module],&dptr[d->done], d->size - d->done);
if(a >=0)
d->done += a;
/* the write returns EWOULDBLOCK or EAGAIN if the pipe is full.
* (This is non-blocking I/O). SunOS returns EWOULDBLOCK, OSF/1
* returns EAGAIN under these conditions. Hopefully other OSes
* return one of these values too. Solaris 2 doesn't seem to have
* a man page for write(2) (!) */
else if ((errno == EWOULDBLOCK)||(errno == EAGAIN)||(errno==EINTR))
{
return;
}
else
{
KillModule(module);
return;
}
}
DeleteMessageQueueBuff(module);
}
}
void FlushAllMessageQueues(void)
{
int i;
for(i = 0; i < npipes; i++) {
if(writePipes[i] >= 0) {
FlushMessageQueue(i);
}
}
}
/* A queue of commands from the modules */
typedef struct CommandQueue
{
struct CommandQueue *next;
Window window;
int module;
char *command;
} CommandQueue;
static CommandQueue *CQstart = NULL;
static CommandQueue *CQlast = NULL;
/***********************************************************************
*
* Procedure:
* AddToCommandQueue - add a module command to the command queue
*
************************************************************************/
static void AddToCommandQueue(Window window, int module, char *command)
{
CommandQueue *new;
if (!command)
return;
new = (CommandQueue *)safemalloc(sizeof(CommandQueue));
new->window = window;
new->module = module;
new->command = safestrdup(command);
new->next = NULL;
DBUG("AddToCommandQueue", command);
if (CQlast)
CQlast->next = new;
CQlast = new;
if (!CQstart)
CQstart = CQlast;
}
/***********************************************************************
*
* Procedure:
* EmptyCommandQueue - runs command from the module command queue
* This may be called recursively if a module command runs a function
* that does a Wait, so it must be re-entrant
*
************************************************************************/
void ExecuteCommandQueue(void)
{
CommandQueue *temp;
while (CQstart)
{
/* remember the first command */
temp = CQstart;
/* remove it from the queue */
CQstart = CQstart->next;
/* fix the end pointer if the queue is now empty */
if (CQstart == NULL)
CQlast = NULL;
/* run the first command (this may add stuff to the queue) */
if (temp->command)
{
DBUG("EmptyCommandQueue", temp->command);
ExecuteModuleCommand(temp->window, temp->module, temp->command);
free(temp->command);
}
free(temp);
}
}
void CMD_Send_WindowList(F_CMD_ARGS)
{
FvwmWindow *t;
if(*Module >= 0)
{
SendPacket(*Module, M_NEW_DESK, 1, Scr.CurrentDesk);
SendPacket(*Module, M_NEW_PAGE, 5,
Scr.Vx, Scr.Vy, Scr.CurrentDesk, Scr.VxMax, Scr.VyMax);
if(Scr.Hilite != NULL)
{
SendPacket(*Module, M_FOCUS_CHANGE, 5,
Scr.Hilite->w,
Scr.Hilite->frame,
(unsigned long)True,
Scr.Hilite->hicolors.fore,
Scr.Hilite->hicolors.back);
}
else
{
SendPacket(*Module, M_FOCUS_CHANGE, 5,
0, 0, (unsigned long)True,
GetColor(DEFAULT_FORE_COLOR),
GetColor(DEFAULT_BACK_COLOR));
}
if (Scr.DefaultIcon != NULL)
SendName(*Module, M_DEFAULTICON, 0, 0, 0, Scr.DefaultIcon);
for (t = Scr.FvwmRoot.next; t != NULL; t = t->next)
{
SendConfig(*Module,M_CONFIGURE_WINDOW,t);
SendName(*Module,M_WINDOW_NAME,t->w,t->frame,
(unsigned long)t,t->name);
SendName(*Module,M_ICON_NAME,t->w,t->frame,
(unsigned long)t,t->icon_name);
if (t->icon_bitmap_file != NULL
&& t->icon_bitmap_file != Scr.DefaultIcon)
SendName(*Module,M_ICON_FILE,t->w,t->frame,
(unsigned long)t,t->icon_bitmap_file);
SendName(*Module,M_RES_CLASS,t->w,t->frame,
(unsigned long)t,t->class.res_class);
SendName(*Module,M_RES_NAME,t->w,t->frame,
(unsigned long)t,t->class.res_name);
if((IS_ICONIFIED(t))&&(!IS_ICON_UNMAPPED(t)))
SendPacket(*Module, M_ICONIFY, 7, t->w, t->frame,
(unsigned long)t,
t->icon_g.x, t->icon_g.y,
t->icon_g.width, t->icon_g.height+t->icon_p_height);
if((IS_ICONIFIED(t))&&(IS_ICON_UNMAPPED(t)))
SendPacket(*Module, M_ICONIFY, 7, t->w, t->frame,
(unsigned long)t,
0, 0, 0, 0);
#ifdef MINI_ICONS
if (t->mini_icon != NULL)
SendMiniIcon(*Module, M_MINI_ICON,
t->w, t->frame, (unsigned long)t,
t->mini_icon->width,
t->mini_icon->height,
t->mini_icon->depth,
t->mini_icon->picture,
t->mini_icon->mask,
t->mini_pixmap_file);
#endif
}
if(Scr.Hilite == NULL)
{
BroadcastPacket(M_FOCUS_CHANGE, 5,
0, 0, (unsigned long)True,
GetColor(DEFAULT_FORE_COLOR),
GetColor(DEFAULT_BACK_COLOR));
}
else
{
BroadcastPacket(M_FOCUS_CHANGE, 5,
Scr.Hilite->w,
Scr.Hilite->frame,
(unsigned long)True,
Scr.Hilite->hicolors.fore,
Scr.Hilite->hicolors.back);
}
SendPacket(*Module, M_END_WINDOWLIST, 0);
}
}
void CMD_set_mask(F_CMD_ARGS)
{
int val = 0;
GetIntegerArguments(action, NULL, &val, 1);
PipeMask[*Module] = (unsigned long)val;
}
void CMD_set_sync_mask(F_CMD_ARGS)
{
int val = 0;
GetIntegerArguments(action, NULL, &val, 1);
SyncMask[*Module] = (unsigned long)val;
}
void CMD_set_nograb_mask(F_CMD_ARGS)
{
int val = 0;
GetIntegerArguments(action, NULL, &val, 1);
NoGrabMask[*Module] = (unsigned long)val;
}
/*
* returns a pointer inside a string (just after the alias) if ok or NULL
*/
char *skipModuleAliasToken(const char *string)
{
#define is_valid_first_alias_char(ch) (isalpha(ch) || (ch) == '/')
#define is_valid_alias_char(ch) (is_valid_first_alias_char(ch) \
|| isalnum(ch) || (ch) == '-' || (ch) == '.' || (ch) == '/')
if (is_valid_first_alias_char(*string))
{
int len = 1;
string++;
while (*string && is_valid_alias_char(*string))
{
if (++len > 250) return NULL;
string++;
}
return (char *)string;
}
return NULL;
}
syntax highlighted by Code2HTML, v. 0.9.1