/****************************************************************************
* 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 <FVWMconfig.h>
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include <fcntl.h>
#include <ctype.h>
#include <X11/keysym.h>
#include <sys/types.h>
#include <sys/socket.h>
#include "fvwm.h"
#include "menus.h"
#include "misc.h"
#include "parse.h"
#include "screen.h"
#include "module.h"
int npipes;
int *readPipes;
int *writePipes;
int *pipeOn;
char **pipeName;
unsigned long *PipeMask;
struct queue_buff_struct **pipeQueue;
inline int PositiveWrite(int module, unsigned long *ptr, int size);
void DeleteQueueBuff(int module);
void AddToQueue(int module, unsigned long *ptr, int size, int done);
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);
pipeName = (char **)safemalloc(sizeof(char *)*npipes);
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;
pipeQueue[i] = (struct queue_buff_struct *)NULL;
pipeName[i] = NULL;
}
}
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;
}
while(pipeQueue[i] != NULL)
{
DeleteQueueBuff(i);
}
}
}
void executeModule(XEvent *eventp,Window w,FvwmWindow *tmp_win,
unsigned long context, char *action,int* Module)
{
int fvwm_to_app[2],app_to_fvwm[2];
int i,val,nargs = 0;
char *cptr;
char *args[20];
char *arg1 = NULL;
char arg2[20];
char arg3[20];
char arg5[20];
char arg6[20];
extern char *ModulePath;
extern char *fvwm_file;
Window win;
if(eventp->type != KeyPress)
UngrabEm();
if(action == NULL)
return;
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);
arg1 = findIconFile(cptr,ModulePath,X_OK);
if(arg1 == NULL)
{
if(cptr != NULL)
{
fvwm_msg(ERR,"executeModule",
"No such module %s%s",ModulePath,cptr);
free(cptr);
}
return;
}
/* Look for an available pipe slot */
for (i=0; (i < npipes) && (writePipes[i] >= 0); ++i);
if(i >= npipes)
{
fvwm_msg(ERR,"executeModule","Too many Accessories!");
if(cptr != NULL)
free(cptr);
return;
}
/* 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");
return;
}
if(pipe(app_to_fvwm)!=0)
{
fvwm_msg(ERR,"executeModule","Failed to open pipe2");
if(cptr != NULL)
free(cptr);
close(fvwm_to_app[0]);
close(fvwm_to_app[1]);
return;
}
pipeName[i] = stripcpy(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;
if(fvwm_file != NULL)
args[3]=fvwm_file;
else
args[3]="none";
args[4]=arg5;
args[5]=arg6;
nargs = 6;
while((action != NULL)&&(nargs < 20)&&(strlen(args[nargs-1]) > 0))
{
args[nargs] = 0;
action = GetNextToken(action,&args[nargs]);
nargs++;
}
if(strlen(args[nargs-1]) <= 0)
{
nargs--;
if(args[nargs] != NULL)
free(args[nargs]);
}
args[nargs] = 0;
/* 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;
free(arg1);
pipeQueue[i] = NULL;
/* make the PositiveWrite pipe non-blocking. Don't want to jam up
fvwm because of an uncooperative module */
fcntl(writePipes[i],F_SETFL,O_NDELAY);
/* 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]);
}
if(cptr != NULL)
free(cptr);
}
else if (val ==0)
{
/* this is the child */
/* this fork execs the module */
close(fvwm_to_app[1]);
close(app_to_fvwm[0]);
execvp(arg1,args);
fvwm_msg(ERR,"executeModule","Execution of module failed: %s",arg1);
perror("");
close(app_to_fvwm[1]);
close(fvwm_to_app[0]);
exit(1);
}
else
{
fvwm_msg(ERR,"executeModule","Fork failed");
free(arg1);
}
return;
}
void HandleModuleInput(Window w, int channel)
{
char text[256];
int size;
int cont,n;
/* Already read a (possibly NULL) window id from the pipe,
* Now read an fvwm bultin command line */
n = read(readPipes[channel], &size, sizeof(size));
if(n < sizeof(size))
{
KillModule(channel,1);
return;
}
if(size >255)
{
fvwm_msg(ERR,"HandleModuleInput",
"Module command is too big (%d)",(void *)size);
size=255;
}
pipeOn[channel] = 1;
n = read(readPipes[channel],text, size);
if(n < size)
{
KillModule(channel,2);
return;
}
text[n]=0;
n = read(readPipes[channel],&cont, sizeof(cont));
if(n < sizeof(cont))
{
KillModule(channel,3);
return;
}
if(cont == 0)
{
KillModule(channel,4);
}
if(strlen(text)>0)
{
extern int Context;
FvwmWindow *tmp_win;
/* first, check to see if module config line */
if (text[0] == '*')
{
AddToModList(text);
return;
}
/* perhaps the module would like us to kill it? */
if(strncasecmp(text,"KillMe",6)==0)
{
KillModule(channel,12);
return;
}
/*Event.xany.type = ButtonRelease; hmmm...*/
/* If a module does XUngrabPointer(), it can now get proper Popups */
if (strncasecmp(text,"popup",5)==0)
Event.xany.type = ButtonPress;
else
Event.xany.type = ButtonRelease;
Event.xany.window = w;
if (XFindContext (dpy, w, FvwmContext, (caddr_t *) &tmp_win) == XCNOENT)
{
tmp_win = NULL;
w = None;
}
if(tmp_win)
{
Event.xbutton.button = 1;
Event.xbutton.x_root = tmp_win->frame_x;
Event.xbutton.y_root = tmp_win->frame_y;
Event.xbutton.x = 0;
Event.xbutton.y = 0;
Event.xbutton.subwindow = None;
}
else
{
Event.xbutton.button = 1;
Event.xbutton.x_root = 0;
Event.xbutton.y_root = 0;
Event.xbutton.x = 0;
Event.xbutton.y = 0;
Event.xbutton.subwindow = None;
}
Context = GetContext(tmp_win,&Event,&w);
ExecuteFunction(text,tmp_win,&Event,Context ,channel);
SendPacket(channel,M_FUNCTION_END,1,0,0,0,0,0,0,0);
}
return;
}
void DeadPipe(int nonsense)
{
signal(SIGPIPE, DeadPipe);
}
void KillModule(int channel, int place)
{
close(readPipes[channel]);
close(writePipes[channel]);
readPipes[channel] = -1;
writePipes[channel] = -1;
pipeOn[channel] = -1;
while(pipeQueue[channel] != NULL)
{
DeleteQueueBuff(channel);
}
if(pipeName[channel] != NULL)
{
free(pipeName[channel]);
pipeName[channel] = NULL;
}
return;
}
void KillModuleByName(char *name)
{
int i = 0;
if(name == NULL)
return;
while(i<npipes)
{
if((pipeName[i] != NULL)&&(matchWildcards(name,pipeName[i])))
{
KillModule(i,10);
}
i++;
}
return;
}
void Broadcast(unsigned long event_type, unsigned long num_datum,
unsigned long data1, unsigned long data2, unsigned long data3,
unsigned long data4, unsigned long data5, unsigned long data6,
unsigned long data7)
{
int i;
unsigned long body[11];
extern Time lastTimestamp;
body[0] = START_FLAG;
body[1] = event_type;
body[2] = num_datum+HEADER_SIZE;
body[3] = lastTimestamp;
if(num_datum>0)
body[HEADER_SIZE] = data1;
if(num_datum>1)
body[HEADER_SIZE+1] = data2;
if(num_datum>2)
body[HEADER_SIZE+2] = data3;
if(num_datum>3)
body[HEADER_SIZE+3] = data4;
if(num_datum>4)
body[HEADER_SIZE+4] = data5;
if(num_datum>5)
body[HEADER_SIZE+5] = data6;
if(num_datum>6)
body[HEADER_SIZE+6] = data7;
for(i=0;i<npipes;i++)
PositiveWrite(i,body, (num_datum+4)*sizeof(body[0]));
}
void SendPacket(int module, unsigned long event_type, unsigned long num_datum,
unsigned long data1, unsigned long data2, unsigned long data3,
unsigned long data4, unsigned long data5, unsigned long data6,
unsigned long data7)
{
unsigned long body[11];
extern Time lastTimestamp;
body[0] = START_FLAG;
body[1] = event_type;
body[2] = num_datum+HEADER_SIZE;
body[3] = lastTimestamp;
if(num_datum>0)
body[HEADER_SIZE] = data1;
if(num_datum>1)
body[HEADER_SIZE+1] = data2;
if(num_datum>2)
body[HEADER_SIZE+2] = data3;
if(num_datum>3)
body[HEADER_SIZE+3] = data4;
if(num_datum>4)
body[HEADER_SIZE+4] = data5;
if(num_datum>5)
body[HEADER_SIZE+5] = data6;
if(num_datum>6)
body[HEADER_SIZE+6] = data7;
PositiveWrite(module,body,(num_datum+4)*sizeof(body[0]));
}
void SendConfig(int module, unsigned long event_type, FvwmWindow *t)
{
unsigned long body[MAX_BODY_SIZE+HEADER_SIZE];
extern Time lastTimestamp;
body[0] = START_FLAG;
body[1] = event_type;
body[2] = HEADER_SIZE+24;
body[3] = lastTimestamp;
body[HEADER_SIZE] = t->w;
body[HEADER_SIZE+1] = t->frame;
body[HEADER_SIZE+2] = (unsigned long)t;
body[HEADER_SIZE+3] = t->frame_x;
body[HEADER_SIZE+4] = t->frame_y;
body[HEADER_SIZE+5] = t->frame_width;
body[HEADER_SIZE+6] = t->frame_height;
body[HEADER_SIZE+7] = t->Desk;
body[HEADER_SIZE+8] = t->flags;
body[HEADER_SIZE+9] = t->title_height;
body[HEADER_SIZE+10] = t->boundary_width;
body[HEADER_SIZE+11] = t->hints.base_width;
body[HEADER_SIZE+12] = t->hints.base_height;
body[HEADER_SIZE+13] = t->hints.width_inc;
body[HEADER_SIZE+14] = t->hints.height_inc;
body[HEADER_SIZE+15] = t->hints.min_width;
body[HEADER_SIZE+16] = t->hints.min_height;
body[HEADER_SIZE+17] = t->hints.max_width;
body[HEADER_SIZE+18] = t->hints.max_height;
body[HEADER_SIZE+19] = t->icon_w;
body[HEADER_SIZE+20] = t->icon_pixmap_w;
body[HEADER_SIZE+21] = t->hints.win_gravity;
body[HEADER_SIZE+22] = t->TextPixel;
body[HEADER_SIZE+23] = t->BackPixel;
PositiveWrite(module,body,(HEADER_SIZE+24)*sizeof(body[0]));
}
void BroadcastConfig(unsigned long event_type, FvwmWindow *t)
{
unsigned long body[MAX_BODY_SIZE+HEADER_SIZE];
int i;
extern Time lastTimestamp;
body[0] = START_FLAG;
body[1] = event_type;
body[2] = HEADER_SIZE+24;
body[3] = lastTimestamp;
body[HEADER_SIZE] = t->w;
body[HEADER_SIZE+1] = t->frame;
body[HEADER_SIZE+2] = (unsigned long)t;
body[HEADER_SIZE+3] = t->frame_x;
body[HEADER_SIZE+4] = t->frame_y;
body[HEADER_SIZE+5] = t->frame_width;
body[HEADER_SIZE+6] = t->frame_height;
body[HEADER_SIZE+7] = t->Desk;
body[HEADER_SIZE+8] = t->flags;
body[HEADER_SIZE+9] = t->title_height;
body[HEADER_SIZE+10] = t->boundary_width;
body[HEADER_SIZE+11] = t->hints.base_width;
body[HEADER_SIZE+12] = t->hints.base_height;
body[HEADER_SIZE+13] = t->hints.width_inc;
body[HEADER_SIZE+14] = t->hints.height_inc;
body[HEADER_SIZE+15] = t->hints.min_width;
body[HEADER_SIZE+16] = t->hints.min_height;
body[HEADER_SIZE+17] = t->hints.max_width;
body[HEADER_SIZE+18] = t->hints.max_height;
body[HEADER_SIZE+19] = t->icon_w;
body[HEADER_SIZE+20] = t->icon_pixmap_w;
body[HEADER_SIZE+21] = t->hints.win_gravity;
body[HEADER_SIZE+22] = t->TextPixel;
body[HEADER_SIZE+23] = t->BackPixel;
for(i=0;i<npipes;i++)
{
PositiveWrite(i,body,(HEADER_SIZE+24)*sizeof(body[0]));
}
}
void BroadcastName(unsigned long event_type, unsigned long data1,
unsigned long data2, unsigned long data3, char *name)
{
int l,i;
unsigned long *body;
extern Time lastTimestamp;
if(name==NULL)
return;
l=strlen(name)/(sizeof(*body))+HEADER_SIZE+4;
body = (unsigned long *)safemalloc(l*sizeof(*body));
body[0] = START_FLAG;
body[1] = event_type;
body[2] = l;
body[3] = lastTimestamp;
body[HEADER_SIZE] = data1;
body[HEADER_SIZE+1] = data2;
body[HEADER_SIZE+2] = data3;
strcpy((char *)&body[HEADER_SIZE+3],name);
for(i=0;i<npipes;i++)
PositiveWrite(i,(unsigned long *)body, l*sizeof(*body));
free(body);
}
void SendName(int module, unsigned long event_type,
unsigned long data1,unsigned long data2,
unsigned long data3, char *name)
{
int l;
unsigned long *body;
extern Time lastTimestamp;
if(name == NULL)
return;
l=strlen(name)/(sizeof(unsigned long))+HEADER_SIZE+4;
body = (unsigned long *)safemalloc(l*sizeof(unsigned long));
body[0] = START_FLAG;
body[1] = event_type;
body[2] = l;
body[3] = lastTimestamp;
body[HEADER_SIZE] = data1;
body[HEADER_SIZE+1] = data2;
body[HEADER_SIZE+2] = data3;
strcpy((char *)&body[HEADER_SIZE+3],name);
PositiveWrite(module,(unsigned long *)body, l*sizeof(unsigned long));
free(body);
}
/*
* send an arbitrary string to all instances of a module
*/
void SendStrToModule(XEvent *eventp,Window junk,FvwmWindow *tmp_win,
unsigned long context, char *action,int* Module)
{
char *module,*str;
int i;
GetNextToken(action,&module);
str = strdup(action + strlen(module) + 1);
for (i=0;i<npipes;i++)
{
if((pipeName[i] != NULL)&&(matchWildcards(module,pipeName[i])))
{
SendName(i,M_STRING,0,0,0,str);
}
}
free(module);
free(str);
}
#include <sys/errno.h>
inline int PositiveWrite(int module, unsigned long *ptr, int size)
{
if((pipeOn[module]<0)||(!((PipeMask[module]) & ptr[1])))
return -1;
AddToQueue(module,ptr,size,0);
return size;
}
void AddToQueue(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(d,ptr,size);
e = pipeQueue[module];
if(e == NULL)
{
pipeQueue[module] = c;
return;
}
while(e->next != NULL)
e = e->next;
e->next = c;
}
void DeleteQueueBuff(int module)
{
struct queue_buff_struct *a;
if(pipeQueue[module] == NULL)
return;
a = pipeQueue[module];
pipeQueue[module] = a->next;
free(a->data);
free(a);
return;
}
void FlushQueue(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,123);
return;
}
}
DeleteQueueBuff(module);
}
}
void send_list_func(XEvent *eventp,Window w,FvwmWindow *tmp_win,
unsigned long context, char *action,
int *Module)
{
FvwmWindow *t;
if(*Module >= 0)
{
SendPacket(*Module,M_NEW_DESK,1,Scr.CurrentDesk,0,0,0,0,0,0);
SendPacket(*Module,M_NEW_PAGE,5,Scr.Vx,Scr.Vy,Scr.CurrentDesk,
Scr.VxMax,Scr.VyMax,0,0);
if(Scr.Hilite != NULL)
SendPacket(*Module,M_FOCUS_CHANGE,5,Scr.Hilite->w,Scr.Hilite->frame,
(unsigned long)Scr.Hilite,Scr.ActiveTitleColors.fore,
Scr.ActiveTitleColors.back,0,0);
else
SendPacket(*Module,M_FOCUS_CHANGE,5,0,0,0,Scr.ActiveTitleColors.fore,
Scr.ActiveTitleColors.back,0,0);
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((t->flags & ICONIFIED)&&(!(t->flags & ICON_UNMAPPED)))
SendPacket(*Module,M_ICONIFY,7,t->w,t->frame,
(unsigned long)t,
t->icon_x_loc,t->icon_y_loc,
t->icon_w_width,
t->icon_w_height+t->icon_p_height);
if((t->flags & ICONIFIED) && (t->flags & ICON_UNMAPPED))
SendPacket(*Module,M_ICONIFY,7,t->w,t->frame,
(unsigned long)t,0,0,0,0);
if (t->title_icon != NULL)
SendPacket(*Module, M_MINI_ICON, 6,
t->w, /* Watch Out ! : I reduced the set of infos... */
t->title_icon->picture,
t->title_icon->mask,
t->title_icon->width,
t->title_icon->height,
t->title_icon->depth, 0);
}
if(Scr.Hilite == NULL)
{
Broadcast(M_FOCUS_CHANGE,5,0,0,0,Scr.ActiveTitleColors.fore,
Scr.ActiveTitleColors.back,0,0);
}
else
{
Broadcast(M_FOCUS_CHANGE,5,Scr.Hilite->w,
Scr.Hilite->frame,(unsigned long)Scr.Hilite,
Scr.ActiveTitleColors.fore,Scr.ActiveTitleColors.back,0,0);
}
SendPacket(*Module,M_END_WINDOWLIST,0,0,0,0,0,0,0,0);
}
}
void set_mask_function(XEvent *eventp,Window w,FvwmWindow *tmp_win,
unsigned long context, char *action,int* Module)
{
int n, val1_unit;
long val1;
n = GetOneArgument(action, &val1, &val1_unit);
PipeMask[*Module] = (unsigned long)val1;
}
syntax highlighted by Code2HTML, v. 0.9.1