Files
amiwm-neo/module.c
2017-03-26 13:00:19 +02:00

574 lines
13 KiB
C

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_SYS_WAIT_H
#include <sys/wait.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef HAVE_SYS_RESOURCE_H
#include <sys/resource.h>
#endif
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#include <X11/Xlib.h>
#include "alloc.h"
#include "drawinfo.h"
#include "screen.h"
#include "prefs.h"
#include "module.h"
#include "client.h"
#include "icon.h"
#include "version.h"
extern XContext client_context, icon_context, screen_context;
extern FILE *rcfile;
extern Display *dpy;
extern void add_fd_to_set(int);
extern void remove_fd_from_set(int);
extern void screentoback();
extern void raiselowerclient(Client *, int);
extern void wberror(Scrn *, char *);
extern Icon *createappicon(struct module *, Window, char *,
Pixmap, Pixmap, Pixmap, int, int);
extern struct Item *own_items(struct module *, Scrn *,
int, int, int, struct Item *);
extern void disown_item_chain(struct module *, struct Item *);
struct module *modules = NULL;
struct mcmd_keygrab *keygrabs = NULL;
static struct mcmd_keygrab *find_keygrab(int keycode, unsigned int modifiers)
{
struct mcmd_keygrab *kg=keygrabs;
while(kg)
if(kg->keycode == keycode && kg->modifiers == modifiers)
return kg;
else kg=kg->next;
return NULL;
}
int create_keygrab(struct module *m, int keycode, unsigned int modifiers)
{
static int id=1;
struct mcmd_keygrab *kg=malloc(sizeof(struct mcmd_keygrab));
Client *c;
if(kg) {
kg->next = keygrabs;
kg->id=id++;
kg->owner=m;
kg->keycode=keycode;
kg->modifiers=modifiers;
keygrabs=kg;
for(c=clients; c; c=c->next)
if(c->parent && c->parent!=c->scr->root)
XGrabKey(dpy, keycode, modifiers, c->window, False,
GrabModeAsync, GrabModeAsync);
return kg->id;
} else return -1;
}
void delete_keygrab(struct module *m, int id)
{
struct mcmd_keygrab *kg=keygrabs, *last=NULL;
Client *c;
while(kg) {
if(kg->owner==m && (id<0 || kg->id==id)) {
if(last)
last->next=kg->next;
else
keygrabs=kg->next;
if(!find_keygrab(kg->keycode, kg->modifiers))
for(c=clients; c; c=c->next)
if(c->parent && c->parent!=c->scr->root)
XUngrabKey(dpy, kg->keycode, kg->modifiers, c->window);
} else last=kg;
kg=kg->next;
}
}
static void destroy_module(struct module *m)
{
Scrn *s=front;
delete_keygrab(m, -1);
do {
Icon *i, *ni;
for(i=scr->icons; i; i=ni) {
ni=i->next;
if(i->module==m)
rmicon(i);
}
s=s->behind;
} while(s!=front);
disown_item_chain(m, m->menuitems);
if(m->in_fd>=0) { remove_fd_from_set(m->in_fd); close(m->in_fd); }
if(m->out_fd>=0) { close(m->out_fd); }
free(m);
}
static void sieve_modules()
{
struct module *m, **p;
for(p=&modules; (m=*p); )
if(!m->pid) {
*p=m->next;
destroy_module(m);
} else p=&(m->next);
}
void reap_children(int sig)
{
pid_t pid;
int stat;
#ifdef HAVE_WAITPID
while((pid=waitpid(-1, &stat, WNOHANG))>0)
#else
#ifdef HAVE_WAIT3
while((pid=wait3(&stat, WNOHANG, NULL))>0)
#else
if((pid=wait(&stat))>0)
#endif
#endif
#ifdef WIFSTOPPED
if(!WIFSTOPPED(stat)) {
#else
{
#endif
struct module *m;
for(m=modules; m; m=m->next)
if(m->pid==pid) {
m->pid=0;
break;
}
}
if(pid<0 && errno!=ECHILD && errno!=EINTR)
perror("wait");
if(sig>0)
signal(sig, reap_children);
}
void init_modules()
{
modules = NULL;
#ifdef SIGCHLD
signal(SIGCHLD, reap_children);
#else
#ifdef SIGCLD
signal(SIGCLD, reap_children);
#endif
#endif
}
void create_module(Scrn *screen, char *module_name, char *module_arg)
{
pid_t pid;
int fds1[2], fds2[2];
char fd1num[16], fd2num[16], scrnum[16], destpath[1024], *pathelt=NULL;
struct module *m;
char *temppath;
#ifdef HAVE_WAITPID
reap_children(0);
#else
#ifdef HAVE_WAIT3
reap_children(0);
#endif
#endif
sieve_modules();
#ifdef HAVE_ALLOCA
temppath = alloca(strlen(prefs.module_path)+2);
{
#else
if((temppath = malloc(strlen(prefs.module_path)+2))) {
#endif
strcpy(temppath, prefs.module_path);
for(pathelt=strtok(temppath, ":"); pathelt;
pathelt=strtok(NULL, ":")) {
sprintf(destpath, "%s/%s", pathelt, module_name);
if(access(destpath, X_OK)>=0)
break;
}
#ifndef HAVE_ALLOCA
free(temppath);
#endif
}
if(!pathelt) {
fprintf(stderr, "%s: no such module\n", module_name);
return;
}
if(pipe(fds1)>=0) {
if(pipe(fds2)>=0) {
if((pid=fork())) {
close(fds1[0]);
close(fds2[1]);
if(pid<0)
perror("fork");
else {
m=calloc(sizeof(struct module),1);
m->pid=pid;
m->in_fd=fds2[0];
m->out_fd=fds1[1];
m->in_buf=malloc(m->in_buf_size=64);
m->in_phase=0;
m->in_ptr=(char *)&m->mcmd;
m->in_left=sizeof(m->mcmd);
m->next=modules;
modules=m;
add_fd_to_set(m->in_fd);
}
} else {
if(rcfile)
close(fileno(rcfile));
for(m=modules; m; m=m->next) {
if(m->in_fd>=0) close(m->in_fd);
if(m->out_fd>=0) close(m->out_fd);
}
close(fds1[1]);
close(fds2[0]);
sprintf(fd1num, "%d", fds1[0]);
sprintf(fd2num, "%d", fds2[1]);
sprintf(scrnum, "0x%08lx", (screen? (screen->back):None));
execl(destpath, module_name, fd1num, fd2num, scrnum, module_arg, NULL);
perror(destpath);
_exit(1);
}
} else {
perror("pipe");
close(fds1[0]);
close(fds1[1]);
}
} else perror("pipe");
}
static int m_write(int fd, char *ptr, int len)
{
char *p=ptr;
int r, tot=0;
while(len>0) {
if((r=write(fd, p, len))<0)
if(errno==EINTR)
continue;
else
return r;
if(!r)
return tot;
tot+=r;
p+=r;
len-=r;
}
return tot;
}
static void reply_module(struct module *m, char *data, int len)
{
m_write(m->out_fd, (char *)&len, sizeof(len));
if(len<0) len=~len;
if(len>0)
m_write(m->out_fd, data, len);
}
int dispatch_event_to_broker(XEvent *e, unsigned long mask, struct module *m)
{
Client *c;
Icon *i;
e->xany.display=(Display *)0;
if(!XFindContext(dpy, e->xany.window, client_context, (XPointer *)&c))
if(e->xany.window==c->window)
e->xany.display=(Display *)1;
else
e->xany.display=(Display *)2;
else if(!XFindContext(dpy, e->xany.window, icon_context, (XPointer *)&i))
if(e->xany.window==i->window)
e->xany.display=(Display *)3;
while(m) {
if(m->out_fd>=0 && ((m->broker.mask & mask)||(m->broker.exists && !mask))) {
struct mcmd_event me;
me.mask=mask;
me.event=*e;
reply_module(m, (char *)&me, ~sizeof(me));
return 1;
}
m=m->next;
}
return 0;
}
static void incoming_event(struct module *m, struct mcmd_event *me)
{
extern void internal_broker(XEvent *);
if(!dispatch_event_to_broker(&me->event, me->mask, m->next))
internal_broker(&me->event);
}
void mod_menuselect(struct module *m, int menu, int item, int subitem)
{
fprintf(stderr, "Nu valde någon minsann menyitem %d:%d:%d.\n"
"Man kanske skulle berätta detta för modul %d\n?",
menu, item, subitem, (int)m->pid);
}
static void handle_module_cmd(struct module *m, char *data, int data_len)
{
extern Scrn *getscreen(Window);
extern int iconcolormask;
XID id=m->mcmd.id;
Client *c;
switch(m->mcmd.cmd) {
case MCMD_NOP:
reply_module(m, NULL, 0);
break;
case MCMD_GET_VERSION:
reply_module(m, "amiwm "VERSION, sizeof("amiwm "VERSION));
break;
case MCMD_SEND_EVENT:
if(data_len>=sizeof(struct mcmd_event)) {
incoming_event(m, (struct mcmd_event *)data);
reply_module(m, NULL, 0);
} else
reply_module(m, NULL, -1);
break;
case MCMD_SET_BROKER:
if(data_len>=sizeof(m->broker.mask)) {
m->broker.mask=*(unsigned long *)data;
m->broker.exists=1;
reply_module(m, NULL, 0);
} else
reply_module(m, NULL, -1);
break;
case MCMD_ROTATE_SCREEN:
scr=getscreen(id);
screentoback();
reply_module(m, NULL, 0);
break;
case MCMD_ADD_KEYGRAB:
if(data_len>=sizeof(int[2])) {
int res=create_keygrab(m, ((int*)data)[0], ((int*)data)[1]);
reply_module(m, (char *)&res, sizeof(res));
} else reply_module(m, NULL, -1);
break;
case MCMD_DEL_KEYGRAB:
if(data_len>=sizeof(int)) {
delete_keygrab(m, ((int*)data)[0]);
reply_module(m, NULL, 0);
} else reply_module(m, NULL, -1);
break;
case MCMD_FRONT:
if(!XFindContext(dpy, id, client_context, (XPointer*)&c)) {
raiselowerclient(c, PlaceOnTop);
reply_module(m, NULL, 0);
} else
reply_module(m, NULL, -1);
break;
case MCMD_BACK:
if(!XFindContext(dpy, id, client_context, (XPointer*)&c)) {
raiselowerclient(c, PlaceOnBottom);
reply_module(m, NULL, 0);
} else
reply_module(m, NULL, -1);
break;
case MCMD_ICONIFY:
if(!XFindContext(dpy, id, client_context, (XPointer*)&c)) {
if(!(c->icon))
createicon(c);
XUnmapWindow(dpy, c->parent);
/* XUnmapWindow(dpy, c->window); */
adjusticon(c->icon);
XMapWindow(dpy, c->icon->window);
if(c->icon->labelwidth)
XMapWindow(dpy, c->icon->labelwin);
c->icon->mapped=1;
setclientstate(c, IconicState);
reply_module(m, NULL, 0);
} else
reply_module(m, NULL, -1);
break;
case MCMD_CREATEAPPICON:
if(data_len>=sizeof(struct NewAppIcon)) {
struct NewAppIcon *nai=(struct NewAppIcon *)data;
Window w=None;
Icon *i=createappicon(m, id, nai->name,
nai->pm1, nai->pm2, nai->pmm, nai->x, nai->y);
if(i!=NULL) w=i->window;
reply_module(m, (char *)&w, sizeof(w));
} else
reply_module(m, NULL, -1);
break;
case MCMD_ERRORMSG:
if(data_len>0) {
extern char *free_screentitle;
if(free_screentitle) free(free_screentitle);
free_screentitle=malloc(data_len+1);
if(free_screentitle==NULL)
reply_module(m, NULL, -1);
else {
scr=getscreen(id);
memcpy(free_screentitle, data, data_len);
free_screentitle[data_len]='\0';
wberror(scr, free_screentitle);
reply_module(m, NULL, 0);
}
} else
reply_module(m, NULL, -1);
break;
case MCMD_SETAPPWINDOW:
if(!XFindContext(dpy, id, client_context, (XPointer*)&c)) {
c->module=m;
reply_module(m, NULL, 0);
} else
reply_module(m, NULL, -1);
break;
case MCMD_GETICONDIR:
reply_module(m, prefs.icondir, strlen(prefs.icondir)+1);
break;
case MCMD_GETICONPALETTE:
if(!XFindContext(dpy, id, client_context, (XPointer*)&c)) {
scr=c->scr;
} else
if(XFindContext(dpy, id, screen_context, (XPointer*)&scr)) {
reply_module(m, NULL, -1);
break;
}
reply_module(m, (void *)scr->iconcolor,
(iconcolormask+1)*sizeof(unsigned long));
break;
case MCMD_MANAGEMENU:
if(data_len>=sizeof(int[3])) {
struct Item *i;
scr=getscreen(id);
if((i=own_items(m, scr, ((int*)data)[0], ((int*)data)[1],
((int*)data)[3], m->menuitems))) {
m->menuitems = i;
reply_module(m, NULL, 0);
} else
reply_module(m, NULL, -1);
} else
reply_module(m, NULL, -1);
break;
default:
reply_module(m, NULL, -1);
}
}
static void module_input_callback(struct module *m)
{
unsigned char buffer[8192], *p=buffer;
int r;
if(!(m->pid)) {
sieve_modules();
return;
}
r=read(m->in_fd, buffer, sizeof(buffer));
if(r==0 || (r<0 && errno!=EINTR)) {
if(r<0)
perror("module");
else
fprintf(stderr, "module %d exited!?\n", (int)m->pid);
remove_fd_from_set(m->in_fd);
close(m->in_fd);
close(m->out_fd);
m->in_fd=m->out_fd=-1;
#ifdef HAVE_WAITPID
reap_children(0);
#else
#ifdef HAVE_WAIT3
reap_children(0);
#endif
#endif
sieve_modules();
return;
}
while(r>0) {
int t=(r>m->in_left? m->in_left:r);
memcpy(m->in_ptr, p, t);
m->in_ptr+=t;
if(!(m->in_left-=t))
if(m->in_phase || ((!m->mcmd.len)&&(m->in_ptr=m->in_buf))) {
*m->in_ptr=0;
handle_module_cmd(m, m->in_buf, m->mcmd.len);
m->in_ptr=(char *)&m->mcmd;
m->in_left=sizeof(m->mcmd);
m->in_phase=0;
} else {
if(m->mcmd.len>=m->in_buf_size) {
if((m->in_buf_size<<=1)<=m->mcmd.len)
m->in_buf_size=m->mcmd.len+1; {
m->in_buf=realloc(m->in_buf, m->in_buf_size);
}
}
m->in_ptr=m->in_buf;
m->in_left=m->mcmd.len;
m->in_phase++;
}
p+=t;
r-=t;
}
}
void handle_module_input(fd_set *r)
{
struct module *m;
for(m=modules; m; m=m->next)
if(m->in_fd>=0 && FD_ISSET(m->in_fd, r)) {
module_input_callback(m);
break;
}
}
void flushmodules()
{
struct module *n, *m=modules;
int i;
extern char *progname;
while(m) {
n=m->next;
if(m->in_fd>=0) {
close(m->in_fd);
remove_fd_from_set(m->in_fd);
m->in_fd=-1;
}
if(m->out_fd>=0) {
close(m->out_fd);
m->out_fd=-1;
}
if(m->pid>0)
kill(m->pid, SIGHUP);
m=n;
}
for(i=5; i>0; --i) {
sieve_modules();
if(!modules)
break;
sleep(1);
}
sieve_modules();
if(modules)
fprintf(stderr, "%s: giving up waiting for modules to die!\n", progname);
}