/*->c.poll */


#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <stdio.h>
#include <time.h>

#include "h.os"
#include "h.wimp"
#include "h.wimpt"
#include "h.werr"
#include "h.swis"

#include "h.flex"

#include "h.etc"
#include "h.err"
#include "h.trans"
#include "h.temp"
#include "h.sp"
#include "h.task"
#include "h.wos"
#include "h.fsx"
#include "h.timex"
#include "h.ram"
#include "h.key"
#include "h.xhelp"
#include "h.bits"

#include "h.xpoll"

#include "h.poll"


#include "h.save"




static wimp_eventstr wimpevent;
static wimp_eventstr wimpfirst;

/*****************************************************************************/


#define POLLENTER    -2
#define POLLEXIT     -3
#define DATAOPEN     -4
#define DATAOPENPOST -5
#define DATALOAD     -6
#define DATALOADPOST -7
#define DATALOADTYPE -8
#define DATALOADNAME -9
#define EVENT        -10
#define POLLMASK     -11
#define PREQUIT      -12
#define SCHEDULE     -13
#define HELPMESSAGE  -14


typedef struct eventstr
{
 int type;      /* e.g. zero poll, redraw or message */
 int message;   /* sub type - message number         */
 int handle;    /* window handle */
 int icon;      /* icon */

 union
 {
  zerofn     zero;
  pollfn     poll;
  redrfn     redraw;
  openfn     open;
  closefn    close;
  ptrfn      ptr;
  clickfn    click;
  keyfn      key;
  caretfn    caret;
  messfn     message;
  loadtypefn loadtype;
  loadnamefn loadname;
  loadfn     load;
  loadpostfn loadpost;
  eventfn    event;
  pollmaskfn pollmask;
  prequitfn  prequit;
  helpfn     help;
 } fn;

} eventstr;


#define EVMIN   64
#define EVCHUNK (EVMIN*sizeof(eventstr))


static eventstr * eventstack;
static int        eventn;


typedef struct eventscanstr
{
 int                   eventscan;
 struct eventscanstr * next;
 struct eventscanstr * prev;
} eventscanstr;


static eventscanstr * eventscanlist;


/*****************************************************************************/

static int findblock(int type,int message,int handle,int icon,zerofn fn)
{
 int        i;

 if(eventstack)
 {
  for(i=0;i<eventn;i++)
  {
   if((type==-1    || eventstack[i].type==type) &&
      (message==-1 || eventstack[i].message==message) &&
      (handle==-1  || eventstack[i].handle==handle) &&
      (icon==-1    || eventstack[i].icon==icon) &&
      (!fn         || eventstack[i].fn.zero==fn)) return(i);
  }
 }
 return(-1);
}


static os_error * addblock(int type,int message,int handle,int icon,zerofn fn,
                                                             int * hit)
{
 os_error * err;
 int        i;

/* dprintf(0,"add block=%d",type);   */

 if(hit) *hit=0;

 if(!eventstack)
 {
  return(geterror(ENOEVENT));
 }

 for(i=0;i<eventn;i++)
 {
  if(eventstack[i].type==type &&
     eventstack[i].message==message &&
     eventstack[i].handle==handle &&
     eventstack[i].icon==icon &&
     eventstack[i].fn.zero==fn)
  {
   return(NULL);
  }
 }

 err=flex_chunk((flex_ptr)&eventstack,(eventn+1)*sizeof(eventstr),EVCHUNK);
 if(!err)
 {
  eventstack[eventn].type=type;
  eventstack[eventn].message=message;
  eventstack[eventn].handle=handle;
  eventstack[eventn].icon=icon;
  eventstack[eventn].fn.zero=fn;
  eventn++;
  if(hit) *hit=1;
 }

/* dprintf(0,"eventn+=%d",eventn);  */

 return(err);
}


static os_error * remblock(int type,int message,int handle,int icon,zerofn fn,
                                                        int * hit)
{
 int            i;
 int            j;
 eventscanstr * es;

 if(hit) *hit=0;

 if(!eventstack)
 {
  return(NULL);
 }

 for(i=0;i<eventn;i++)
 {
  if(eventstack[i].type==type &&
     eventstack[i].message==message &&
     eventstack[i].handle==handle &&
     eventstack[i].icon==icon &&
     eventstack[i].fn.zero==fn)
  {
   for(j=i;j<(eventn-1);j++) eventstack[j]=eventstack[j+1];
   eventn--;

   es=eventscanlist;
   while(es)
   {
    if(es->eventscan>=i) es->eventscan--;
    es=es->next;
   }

   if(hit) *hit=1;
   break;
  }
 }

/* dprintf(0,"eventn-=%d",eventn);  
 dprintf(1,"last=%d",eventstack[eventn-1].type);  */

 return(NULL);
}


static void starteventscan(eventscanstr * es)
{
 es->eventscan=-1;

 if(eventscanlist) eventscanlist->prev=es;
 es->next=eventscanlist;
 es->prev=NULL;
 eventscanlist=es;
}


static void restarteventscan(eventscanstr * es)
{
 es->eventscan=-1;
}



static void endeventscan(eventscanstr * es)
{
 eventscanstr * prev;
 eventscanstr * next;

 if(eventscanlist==es) eventscanlist=es->next;
 else
 {
  prev=es->prev;
  next=es->next;

  if(prev) prev->next=next;
  if(next) next->prev=prev;
 }
}



static int nextevent(eventscanstr * es,int type,int message,
                                                int handle,int icon)
{
 int i;

 es->eventscan++;

 for(i=es->eventscan;i<eventn;i++)
 {
  if((type==-1    || eventstack[i].type==type) &&
     (message==-1 || eventstack[i].message==message) &&
     (handle==-1  || eventstack[i].handle==handle) &&
     (icon==-1    || eventstack[i].icon==icon))
  {
   es->eventscan=i;
   return(i);
  }
 }

 return(-1);
}


/*****************************************************************************/

#define MAXTIMEOUT 6000


static int dopollidle;
static int timeout;
static int doschedule;
       int zerotime;



os_error * addscheduledevent(zerofn fn,int userhandle,int delay)
{
 os_error * err;
 int        hit;
 int        event;
 int        time;

 err=NULL;

 time=monotonic()+delay;
 if(time<timeout) timeout=time;

 event=findblock(SCHEDULE,0,userhandle,-1,fn);

 if(event>=0) eventstack[event].icon=time;
 else
 {
  err=addblock(SCHEDULE,0,userhandle,time,fn,&hit);
  if(!err && hit) doschedule++;
 }

 return(err);
}



/* unique in that we don't know the 'icon' value   */
/* so we do a search for first match               */
/* things would be easier if remblock matched -1's */


os_error * remscheduledevent(zerofn fn,int userhandle)
{
 os_error * err;
 int        hit;
 int        event;

 err=NULL;

 event=findblock(SCHEDULE,0,userhandle,-1,fn);
 if(event>=0) 
 {
  err=remblock(SCHEDULE,0,userhandle,eventstack[event].icon,fn,&hit);
  if(!err && hit) doschedule--;
 }

 return(err);
}


os_error * addzeroevent(zerofn fn,int userhandle)
{
 os_error * err;
 int        hit;

 err=addblock(wimp_ENULL,0,userhandle,0,fn,&hit);
 if(!err && hit) dopollidle++;

 return(err);
}

os_error * remzeroevent(zerofn fn,int userhandle)
{
 os_error * err;
 int        hit;

 err=remblock(wimp_ENULL,0,userhandle,0,fn,&hit);
 if(!err && hit) dopollidle--;

 return(err);
}


static void zero(void)
{
 int          mono=monotonic();
 int          event;
 int          ehandle;
 zerofn       efn;
 int          hit;
 eventscanstr es;

 zerotime=mono;

 if(dopollidle)
 {
  starteventscan(&es);

  while((event=nextevent(&es,wimp_ENULL,-1,-1,-1))>=0)
  {
   eventstack[event].fn.zero(eventstack[event].handle);
  }

  endeventscan(&es);
 }

 timeout=mono+MAXTIMEOUT; 

/* dprintf(0,"zero %d poll=%d sched=%d",zerotime,dopollidle,doschedule); */


 if(doschedule)
 {
  starteventscan(&es);

  while((event=nextevent(&es,SCHEDULE,-1,-1,-1))>=0)
  {
   if(eventstack[event].icon<=mono)
   {
    ehandle=eventstack[event].handle;
    efn=eventstack[event].fn.zero;

    remblock(SCHEDULE,0,ehandle,eventstack[event].icon,efn,&hit);
    doschedule--;    /* here to stop recursion */

    efn(ehandle);

    event=es.eventscan;
   }
   else timeout=MIN(timeout,eventstack[event].icon);
  }
  endeventscan(&es);
 }
}

/*****************************************************************************/

/* static int polltime; */


static void pollexit(void)
{
 int event;
 eventscanstr es;

 starteventscan(&es);

 while((event=nextevent(&es,POLLEXIT,-1,-1,-1))>=0)
 {
  eventstack[event].fn.poll(eventstack[event].handle);
 }

 endeventscan(&es);

/* dprintf(2,"polltime=%d",clock()-polltime); */

}


static void pollenter(void)
{
 int event;
 eventscanstr es;

/* polltime=clock(); */

 starteventscan(&es);

 while((event=nextevent(&es,POLLENTER,-1,-1,-1))>=0)
 {
  eventstack[event].fn.poll(eventstack[event].handle);
 }

 endeventscan(&es);
}


os_error * addpollexit(pollfn fn,int userhandle)
{
 os_error * err;
 err=addblock(POLLEXIT,0,userhandle,0,fn,NULL);
 return(err);
}


os_error * rempollexit(pollfn fn,int userhandle)
{
 os_error * err;
 err=remblock(POLLEXIT,0,userhandle,0,fn,NULL);
 return(err);
}


static int pollmask(void)
{
 int event;
 int claimed;
 eventscanstr es;

// dprintf(1,"poll mask in");

 claimed=0;
 starteventscan(&es);

// dprintf(2,"poll mask 1");

 while((event=nextevent(&es,POLLMASK,-1,-1,-1))>=0)
 {

//  dprintf(3,"poll mask 2");
  claimed|=eventstack[event].fn.pollmask(eventstack[event].handle,&wimpevent);
//  dprintf(4,"poll mask 3");
 }

// dprintf(4,"poll mask 4");

 endeventscan(&es);

// dprintf(5,"poll mask ex %d",claimed);

 return(claimed);
}



os_error * addpollenter(pollfn fn,int userhandle)
{
 os_error * err;
 err=addblock(POLLENTER,0,userhandle,0,fn,NULL);
 return(err);
}

os_error * rempollenter(pollfn fn,int userhandle)
{
 os_error * err;
 err=remblock(POLLENTER,0,userhandle,0,fn,NULL);
 return(err);
}


os_error * addpollmask(pollmaskfn fn,int userhandle)
{
 os_error * err;
 err=addblock(POLLMASK,0,userhandle,0,(zerofn)fn,NULL);
 return(err);
}


os_error * rempollmask(pollmaskfn fn,int userhandle)
{
 os_error * err;
 err=remblock(POLLMASK,0,userhandle,0,(zerofn)fn,NULL);
 return(err);
}



/*****************************************************************************/

os_error * event(int eventn,void * data,int data1)
{
 os_error   * err;
 int          event;
 eventscanstr es;

 err=NULL;

 starteventscan(&es);

/* dprintf(0,"eventn=%x",eventn); */

 while((event=nextevent(&es,EVENT,eventn,-1,-1))>=0)
 {
/*  dprintf(0,"eventx"); */
  err=eventstack[event].fn.event(eventn,eventstack[event].handle,data,data1);
  if(err) break;
 }

 endeventscan(&es);

 return(err);
}

os_error * addevent(int eventn,eventfn fn,int userhandle)
{
 os_error * err;
 err=addblock(EVENT,eventn,userhandle,0,(zerofn)fn,NULL);
 return(err);
}

os_error * remevent(int eventn,eventfn fn,int userhandle)
{
 os_error * err;
 err=remblock(EVENT,eventn,userhandle,0,(zerofn)fn,NULL);
 return(err);
}

/*****************************************************************************/

static void redraw(void)
{
 int          event;
 int          handle;
 eventscanstr es;

 handle=wimpevent.data.o.w;

 starteventscan(&es);

 while((event=nextevent(&es,wimp_EREDRAW,-1,handle,-1))>=0)
 {
  report(eventstack[event].fn.redraw(handle,eventstack[event].icon));
 }

 endeventscan(&es);
}


os_error * addredrawevent(redrfn fn,int handle,int userhandle)
{
 os_error * err;
 err=addblock(wimp_EREDRAW,0,handle,userhandle,(zerofn)fn,NULL);
 return(err);
}


os_error * remredrawevent(redrfn fn,int handle,int userhandle)
{
 os_error * err;
 err=remblock(wimp_EREDRAW,0,handle,userhandle,(zerofn)fn,NULL);
 return(err);
}


/*****************************************************************************/

static void wopen(void)
{
 int        event;
 int        handle;
 int        hit;
 eventscanstr es;

 handle=wimpevent.data.o.w;

 starteventscan(&es);
 hit=0;

 while((event=nextevent(&es,wimp_EOPEN,-1,handle,-1))>=0)
 {
  hit=1;
  report(eventstack[event].fn.open(handle,eventstack[event].icon,
                                                          &wimpevent.data.o));
 }

 endeventscan(&es);

 if(!hit) report(wimp_open_wind(&wimpevent.data.o));
}


os_error * addopenevent(openfn fn,int handle,int userhandle)
{
 os_error * err;
 err=addblock(wimp_EOPEN,0,handle,userhandle,(zerofn)fn,NULL);
 return(err);
}

os_error * remopenevent(openfn fn,int handle,int userhandle)
{
 os_error * err;
 err=remblock(wimp_EOPEN,0,handle,userhandle,(zerofn)fn,NULL);
 return(err);
}

/****************************************************************************/

static void wclose(void)
{
 int         event;
 int         handle;
 int         hit;
 eventscanstr es;

 handle=wimpevent.data.o.w;
 starteventscan(&es);
 hit=0;

 if((event=nextevent(&es,wimp_ECLOSE,-1,handle,-1))>=0)
 {
  hit=1;
  report(eventstack[event].fn.close(handle,eventstack[event].icon));
 }

 endeventscan(&es);

 if(!hit)
 {
  report(wimp_close_wind(handle));
/*  report(wimp_delete_wind(handle)); */
 }
}

os_error * addcloseevent(closefn fn,int handle,int userhandle)
{
 os_error * err;
 err=addblock(wimp_ECLOSE,0,handle,userhandle,(zerofn)fn,NULL);
 return(err);
}

os_error * remcloseevent(closefn fn,int handle,int userhandle)
{
 os_error * err;
 err=remblock(wimp_ECLOSE,0,handle,userhandle,(zerofn)fn,NULL);
 return(err);
}

/***************************************************************************/

static void pointerevent(int enter)
{
 int          event;
 int          handle;
 eventscanstr es;

 handle=wimpevent.data.o.w;
 starteventscan(&es);

 while((event=nextevent(&es,wimp_EPTRLEAVE,-1,handle,-1))>=0)
 {
  report(eventstack[event].fn.ptr(handle,eventstack[event].icon,enter));
 }

 endeventscan(&es);
}

os_error * addpointerevent(ptrfn fn,int handle,int userhandle)
{
 os_error * err;
 err=addblock(wimp_EPTRLEAVE,0,handle,userhandle,(zerofn)fn,NULL);
 return(err);
}

os_error * rempointerevent(ptrfn fn,int handle,int userhandle)
{
 os_error * err;
 err=remblock(wimp_EPTRLEAVE,0,handle,userhandle,(zerofn)fn,NULL);
 return(err);
}

/***************************************************************************/

static void decodeclick(void)
{
 int          event;
 int          handle;
 eventscanstr es;

/* wimpevent.data.but.m.bbits=fixbuttons(wimpevent.data.but.m.bbits); */

 handle=wimpevent.data.but.m.w;
 starteventscan(&es);

 while((event=nextevent(&es,wimp_EBUT,-1,handle,-1))>=0)
 {
  report(eventstack[event].fn.click(handle,eventstack[event].icon,
                                                 &wimpevent.data.but.m));
  break;
 }

 endeventscan(&es);
}

os_error * addclickevent(clickfn fn,int handle,int userhandle)
{
 os_error * err;
 err=addblock(wimp_EBUT,0,handle,userhandle,(zerofn)fn,NULL);
 return(err);
}

os_error * remclickevent(clickfn fn,int handle,int userhandle)
{
 os_error * err;
 err=remblock(wimp_EBUT,0,handle,userhandle,(zerofn)fn,NULL);
 return(err);
}

/***************************************************************************/

static dragfn dfn;
static int    draghandle;
static int    draguserhandle;

static void decodedrag(void)
{
 if(dfn) report(dfn(draghandle,draguserhandle,&wimpevent.data.dragbox));
 dfn=NULL;    /* to stop colour picker drags causing trouble */
}

os_error * startdrag(int handle,int userhandle,dragfn fn)
{
 dfn=fn;
 draghandle=handle;
 draguserhandle=userhandle;
 return(NULL);
}

/***************************************************************************/


int keytime;


static void keypress(void)
{
 int        event;
 int        handle;
 int        icon;
 int        key;
 int        pass;
 int        good;
 eventscanstr es;

 keytime=monotonic();
 key=wimpevent.data.key.chcode;
 handle=wimpevent.data.key.c.w;
 icon=wimpevent.data.key.c.i;

/* dprintf(0,"key=%x",key); */

 keypress1(&key,&pass,&good);

 if(good)
 {
  starteventscan(&es);

  while((event=nextevent(&es,wimp_EKEY,-1,handle,-1))>=0)
  {
   report(eventstack[event].fn.key(handle,eventstack[event].icon,icon,&key));
   if(key==-1) break;
  }

  endeventscan(&es);
 }

 keypress2(&key,pass);

 if(key!=-1)
 {
  wimp_processkey(key);
 }
}

os_error * addkeyevent(keyfn fn,int handle,int userhandle)
{
 os_error * err;
 err=addblock(wimp_EKEY,0,handle,userhandle,(zerofn)fn,NULL);
 return(err);
}

os_error * remkeyevent(keyfn fn,int handle,int userhandle)
{
 os_error * err;
 err=remblock(wimp_EKEY,0,handle,userhandle,(zerofn)fn,NULL);
 return(err);
}


/****************************************************************************/

static menufn mfn;

os_error * addmenuhandler(menufn fn)
{
 mfn=fn;
 return(NULL);
}

static void decodemen(void)
{
 if(mfn) report(mfn(wimpevent.data.menu));
}

/***************************************************************************/

static void caretevent(int gain)
{
 int         event;
 int         handle;
 eventscanstr es;

 handle=wimpevent.data.o.w;
 starteventscan(&es);

 while((event=nextevent(&es,wimp_ELOSECARET,-1,handle,-1))>=0)
 {
  report(eventstack[event].fn.caret(handle,eventstack[event].icon,gain));
 }

 endeventscan(&es);
}

os_error * addcaretevent(caretfn fn,int handle,int userhandle)
{
 os_error * err;
 err=addblock(wimp_ELOSECARET,0,handle,userhandle,(zerofn)fn,NULL);
 return(err);
}

os_error * remcaretevent(caretfn fn,int handle,int userhandle)
{
 os_error * err;
 err=remblock(wimp_ELOSECARET,0,handle,userhandle,(zerofn)fn,NULL);
 return(err);
}

/***************************************************************************/


/*****************************************************************************/
/* This chunk is concerned with saving stuff to other apps                   */

/* something expects us to save data to it */

static os_error * datasaveok(void)
{
 os_error    * err;
 wimp_msgstr   msg;

 if(wimpevent.data.msg.hdr.your_ref!=saveref) err=geterror(EUXDS);
 else
 {
  msg=wimpevent.data.msg;

  err=savefile(wimpevent.data.msg.data.datasaveok.name,
               wimpevent.data.msg.data.datasaveok.type);
  if(!err)
  {
   msg.hdr.your_ref=msg.hdr.my_ref;
   msg.hdr.action=(wimp_msgaction)3;
   err=wimp_sendmessage((wimp_etype)17,&msg,msg.hdr.task);
  }
 }

 return(err);
}


static os_error * ramfetch(void)
{
 os_error * err;


 if(wimpevent.data.msg.hdr.your_ref==writemyref) 
                                 err=writemess(&wimpevent.data.msg);
 else
 {
  /* this is the first we know about it if something */
  /* else is trying to load a file                   */
  /* next comes a datasaveack -> scrap file          */

  /* means destination wants stuff saving to RAM */

  if(wimpevent.data.msg.hdr.your_ref!=saveref) err=geterror(EUXRF);
  else
  {
   if(saveram(0)==0)
   {
    err=ramnextwritefile(&wimpevent.data.msg);
    if(!err) savefile(NULL,0); 
    ramoff();
   }
   else err=NULL;
  }
 }

 return(err);
}

/****************************************************************************/
/* This chunk is concerned with loading files from other apps               */

static int         scrapref=-1;
static int         sscrapref=-1;
static char        scrapname[32];
static int         datasaveref;
static wimp_msgstr scmsg;
static int         loadhandle;


static os_error * scrapfile(void)
{
 os_error * err;

 if(!getenv("Wimp$Scrap")) err=geterror(ENOWS);
 else
 {
  wimpevent.data.msg=scmsg;

  strcpy(scrapname,wimpevent.data.msg.data.datasave.leaf);

  wimpevent.data.msg.data.datasaveok.estsize=-1; /* not secure */
  strcpy(wimpevent.data.msg.data.datasaveok.name,"<Wimp$Scrap>");
  wimpevent.data.msg.hdr.size=((48+12) & 0xFFFFFFFC);
  wimpevent.data.msg.hdr.your_ref=wimpevent.data.msg.hdr.my_ref;
  wimpevent.data.msg.hdr.action=(wimp_msgaction)2;
  err=wimp_sendmessage((wimp_etype)17,
                           &wimpevent.data.msg,wimpevent.data.msg.hdr.task);
  scrapref=wimpevent.data.msg.hdr.my_ref;
 }

 return(err);
}



static os_error * sparkscrapfile(char * tempname)
{
 os_error * err;

 wimpevent.data.msg.data.datasaveok.estsize=-1; /* not secure */
 strcpy(wimpevent.data.msg.data.datasaveok.name,tempname);
 wimpevent.data.msg.hdr.size=((48+(strlen(tempname)+3)) & 0xFFFFFFFC);
 wimpevent.data.msg.hdr.your_ref=wimpevent.data.msg.hdr.my_ref;
 wimpevent.data.msg.hdr.action=(wimp_msgaction)2;
 err=wimp_sendmessage((wimp_etype)17,&wimpevent.data.msg,
                                     wimpevent.data.msg.hdr.task);
 sscrapref=wimpevent.data.msg.hdr.my_ref;

 return(err);
}


static os_error * dataloadok(void)
{
 return(NULL);
}




static int dataloadcompleted;


os_error * dataloadcomplete(char * name)
{
 os_error * err;

 err=NULL;

 dataloadcompleted=1;

 if(wimpfirst.data.msg.hdr.your_ref==scrapref)
 {
  err=fs_delete("<Wimp$Scrap>");
  scrapref=-1;
 }
 else
 if(wimpfirst.data.msg.hdr.your_ref==sscrapref && err)
 {
  fs_delete(name);
  sscrapref=-1;
 }

 wimpfirst.data.msg.hdr.your_ref=wimpfirst.data.msg.hdr.my_ref;
 wimpfirst.data.msg.hdr.action=(wimp_msgaction)4;
 wimp_sendmessage((wimp_etype)17,&wimpfirst.data.msg,
                                        wimpfirst.data.msg.hdr.task);

 return(err);
}





static os_error * dataload(void)
{
 os_error * err;
 os_error * err2;
 int        type;
 char     * name;
 int        xvolatile;
 mousestr   mouse;
 int        event;
 int        code;
 eventscanstr es;

 type=wimpevent.data.msg.data.dataload.type;
 name=wimpevent.data.msg.data.dataload.name;

 getpointer(&mouse);
 mouse.x=wimpevent.data.msg.data.dataload.x;
 mouse.y=wimpevent.data.msg.data.dataload.y;
 mouse.icon=wimpevent.data.msg.data.dataload.i;
 loadhandle=mouse.handle=wimpevent.data.msg.data.dataload.w;


 xvolatile=((wimpfirst.data.msg.hdr.your_ref==scrapref) ||
              (wimpfirst.data.msg.hdr.your_ref==sscrapref)); 

 err=err2=NULL;

 starteventscan(&es);
 if((event=nextevent(&es,DATALOADTYPE,-1,loadhandle,-1))>=0)
 {
  code=xvolatile;
  err=eventstack[event].fn.loadtype(type,&mouse,eventstack[event].icon,&code);
  if(code)
  {
   restarteventscan(&es);
   if((event=nextevent(&es,DATALOAD,-1,loadhandle,-1))>=0)
   {
    dataloadcompleted=0;

    err=eventstack[event].fn.load(name,type,eventstack[event].icon,xvolatile);

    /* saying ok, we loaded it ok */
    if(!dataloadcompleted) err2=dataloadcomplete(name);

    if(!err)
    {
     restarteventscan(&es);
     if((event=nextevent(&es,DATALOADPOST,-1,loadhandle,-1))>=0)
     {
      err=eventstack[event].fn.loadpost(eventstack[event].icon);
     }
    }
   }
  }
 }
 endeventscan(&es);

 if(err) return(err);
 else    return(err2);
}



/* means destination wants us to load stuff from RAM */

static os_error * ramtransmit(void)
{
 os_error * err;
 int        type;
 char     * name;
 int        event;
 eventscanstr es;

 if(wimpevent.data.msg.hdr.your_ref==readmyref) 
                                 err=readmess(&wimpevent.data.msg);
 else
 {
  type=scmsg.data.datasave.type;
  name=wimpevent.data.msg.data.datasave.leaf;

  err=ramloadbuffer(&wimpevent.data.msg);
  if(!err)
  {
   err=armfetch(&wimpevent.data.msg);
   if(!err)
   {
    starteventscan(&es);
    if((event=nextevent(&es,DATALOAD,-1,loadhandle,-1))>=0)
    {
     err=eventstack[event].fn.load(name,type,eventstack[event].icon,1);

     if(!err)
     {
      restarteventscan(&es);
      if((event=nextevent(&es,DATALOADPOST,-1,loadhandle,-1))>=0)
      {
       err=eventstack[event].fn.loadpost(eventstack[event].icon);
      }
     }
    }
    endeventscan(&es);
   }
  }
 }
 return(err);
}


static os_error * datasaveack(wimp_msgstr * msg)
{
 os_error * err;

 err=ramoff();
 if(!err) err=scrapfile();
 return(err);

 msg=msg;
}


/* something is trying to send us a file */

static os_error * datasave(void)
{
 os_error   * err;
 mousestr     mouse;
 int          type;
 int          code;
 int          event;
 eventscanstr es;

 getpointer(&mouse);
 mouse.x=wimpevent.data.msg.data.datasave.x;
 mouse.y=wimpevent.data.msg.data.datasave.y;
 mouse.icon=wimpevent.data.msg.data.datasave.i;
 loadhandle=mouse.handle=wimpevent.data.msg.data.datasave.w;

 scmsg=wimpevent.data.msg;
 type=scmsg.data.datasave.type;

 err=NULL;

 starteventscan(&es);
 if((event=nextevent(&es,DATALOADTYPE,-1,mouse.handle,-1))>=0)
 {
  code=-1;
  err=eventstack[event].fn.loadtype(type,&mouse,eventstack[event].icon,&code);

  if(!err)
  {
   if(code==1)
   {
    /* OK, we can load this filetype this way */
    /* send a ram fetch message               */
    err=ramnextreadfile(&wimpevent.data.msg);  /* setting size and        */
                                               /* address of ram buffer   */
    datasaveref=readmyref; /* so ack goes through routine above           */
    addack(datasaveack,datasaveref);  /* we are grabbing the ram ack here */
    readmyref=-1;
   }
   else
   if(code==2)
   {
    char name[FSMAXPATH];

    restarteventscan(&es);
    if((event=nextevent(&es,DATALOADNAME,-1,mouse.handle,-1))>=0)
    {
     err=eventstack[event].fn.loadname(scmsg.data.datasave.leaf,
                                                 name,eventstack[event].icon);
     if(!err) err=sparkscrapfile(name);
    }
   } 
  }
 }
 endeventscan(&es);

 return(err);
}


os_error * adddataload(int handle,int userhandle,loadfn fn)
{
 os_error * err;
 err=addblock(DATALOAD,0,handle,userhandle,(zerofn)fn,NULL);
 return(err);
}

os_error * adddataloadpost(int handle,int userhandle,loadpostfn fn)
{
 os_error * err;
 err=addblock(DATALOADPOST,0,handle,userhandle,(zerofn)fn,NULL);
 return(err);
}

os_error * adddataloadtype(int handle,int userhandle,loadtypefn fn)
{
 os_error * err;
 err=addblock(DATALOADTYPE,0,handle,userhandle,(zerofn)fn,NULL);
 return(err);
}

os_error * adddataloadname(int handle,int userhandle,loadnamefn fn)
{
 os_error * err;
 err=addblock(DATALOADNAME,0,handle,userhandle,(zerofn)fn,NULL);
 return(err);
}

os_error * remdataload(int handle,int userhandle,loadfn fn)
{
 os_error * err;
 err=remblock(DATALOAD,0,handle,userhandle,(zerofn)fn,NULL);
 return(err);
}

os_error * remdataloadpost(int handle,int userhandle,loadpostfn fn)
{
 os_error * err;
 err=remblock(DATALOADPOST,0,handle,userhandle,(zerofn)fn,NULL);
 return(err);
}

os_error * remdataloadtype(int handle,int userhandle,loadtypefn fn)
{
 os_error * err;
 err=remblock(DATALOADTYPE,0,handle,userhandle,(zerofn)fn,NULL);
 return(err);
}

os_error * remdataloadname(int handle,int userhandle,loadnamefn fn)
{
 os_error * err;
 err=remblock(DATALOADNAME,0,handle,userhandle,(zerofn)fn,NULL);
 return(err);
}

/*****************************************************************************/

static os_error * dataopens(wimp_msgstr * msg,int type,char * name,int * ack)
{ 
 os_error   * err;
 int          event;
 eventscanstr es;

 err=NULL;

 if(ack) *ack=0;

 starteventscan(&es);

 if((event=nextevent(&es,DATAOPEN,-1,type,-1))>=0)
 {
  err=eventstack[event].fn.load(name,type,0,0);

  if(msg)
  {
   wimpfirst.data.msg.hdr.your_ref=wimpfirst.data.msg.hdr.my_ref;
   wimpfirst.data.msg.hdr.action=(wimp_msgaction)4;
   wimp_sendmessage((wimp_etype)17,&wimpfirst.data.msg,
                                     wimpfirst.data.msg.hdr.task);
  }

  if(ack) *ack=1;

  restarteventscan(&es);

  if(!err)
  {
   if((event=nextevent(&es,DATAOPENPOST,-1,type,-1))>=0)
   {
    err=eventstack[event].fn.loadpost(0);
   }
  }
 }

 endeventscan(&es);

 return(err);
}


static os_error * dataopen(wimp_msgstr * msg)
{ 
 return(dataopens(msg,msg->data.dataopen.type,
                      wimpevent.data.msg.data.dataload.name,NULL));
}

os_error * fakedataopen(int type,char * name,int * ack)
{
 return(dataopens(NULL,type,name,ack));
}




os_error * adddataopen(int type,loadfn fn)
{
 os_error * err;
 err=addblock(DATAOPEN,0,type,0,(zerofn)fn,NULL);
 return(err);
}

os_error * adddataopenpost(int type,loadpostfn fn)
{
 os_error * err;
 err=addblock(DATAOPENPOST,0,type,0,(zerofn)fn,NULL);
 return(err);
}


os_error * remdataopen(int type,loadfn fn)
{
 os_error * err;
 err=remblock(DATAOPEN,0,type,0,(zerofn)fn,NULL);
 return(err);
}

os_error * remdataopenpost(int type,loadpostfn fn)
{
 os_error * err;
 err=remblock(DATAOPENPOST,0,type,0,(zerofn)fn,NULL);
 return(err);
}


/*****************************************************************************/


static void scanmessage(wimp_msgstr * msg)
{
 int        event;
 int        ack;
 int        acked;
 eventscanstr es;

 acked=0;

 starteventscan(&es);

 while((event=nextevent(&es,wimp_ESEND,msg->hdr.action,-1,-1))>=0)
 {
  ack=0;
  if(eventstack[event].fn.message)
                          report(eventstack[event].fn.message(msg,&ack));
  if(ack && !acked)
  {
   acked=1;
   wimpfirst.data.msg.hdr.your_ref=wimpfirst.data.msg.hdr.my_ref;
   wimp_sendmessage((wimp_etype)19,&wimpfirst.data.msg,
                                   wimpfirst.data.msg.hdr.task);
  }
 }

 endeventscan(&es);
}


static void terminate(wimp_msgstr * msg) 
{
 scanmessage(msg);
 exit(0);
}


/* set up mode specific things like width */

static void setmodevars(wimp_msgstr * msg)
{
 if(wimpt_checkmode()) 
 {
  vdumodevars();
 }

 tempmode();

 scanmessage(msg);
}


/* set up palette */

static void setpalvars(wimp_msgstr * msg)
{
 vdupalvars();
 scanmessage(msg);
}

/****************************************************************************/

os_error * addprequit(prequitfn fn,int userhandle)
{
 os_error * err;
 err=addblock(PREQUIT,0,userhandle,0,(zerofn)fn,NULL);
 return(err);
}

os_error * remprequit(prequitfn fn,int userhandle)
{
 os_error * err;
 err=remblock(PREQUIT,0,userhandle,0,(zerofn)fn,NULL);
 return(err);
}


static void prequit(wimp_msgstr * msg)
{
 int  origtask;
 int  cblock[7];
 int  event;
 int  ack;
 int  acked;
 int  norestart;
 eventscanstr es;


 starteventscan(&es);

 acked=0;

 while((event=nextevent(&es,PREQUIT,-1,-1,-1))>=0)
 {
  ack=0;
  report(eventstack[event].fn.prequit(eventstack[event].handle,&ack,1));
  acked|=ack;
 }

 endeventscan(&es);


 if(acked)   /* stop the prequit sequence */
 {
  if(msg->hdr.size>=24) norestart=msg->data.words[0] & 0x1;
  else                  norestart=0;

/* dprintf(2,"size=%d flags=%x",msg->hdr.size,msg->data.words[0]); */

  origtask=msg->hdr.task;
  wimp_get_caret_pos((wimp_caretstr *)cblock);
  wimpfirst.data.msg.hdr.your_ref=wimpfirst.data.msg.hdr.my_ref;
  wimp_sendmessage((wimp_etype)19,&wimpfirst.data.msg,
                                   wimpfirst.data.msg.hdr.task);

  starteventscan(&es);

  acked=0;
  while((event=nextevent(&es,PREQUIT,-1,-1,-1))>=0)
  {
   ack=0;
   report(eventstack[event].fn.prequit(eventstack[event].handle,&ack,0));
   acked|=ack;
  }

  endeventscan(&es);

  poll();

  if(!acked)
  {
   if(!norestart)
   {
    cblock[6]=0x1FC;
    wimp_sendmessage((wimp_etype)8,(wimp_msgstr *)cblock,origtask);
   }
   else
   {
    wimpfirst.data.msg.hdr.action=wimp_MCLOSEDOWN;
    wimpfirst.data.msg.hdr.task=taskhandle;
    terminate(&wimpfirst.data.msg);
   }
  }
 }
}


/*****************************************************************************/

static os_error * helpmessage(wimp_msgstr * msg)
{
 os_error * err;
 int        handle;
 int        icon;
 int        event;
 char     * p;
 eventscanstr es;

 err=NULL;


 starteventscan(&es);

 handle=msg->data.helprequest.m.w;
 icon=msg->data.helprequest.m.i;

 p=NULL;

 if((event=nextevent(&es,HELPMESSAGE,-1,handle,-1))>=0)
 {
  if(eventstack[event].fn.help)
   err=eventstack[event].fn.help(handle,eventstack[event].icon,icon,&p);
  else
   p=helpfindtagstring(eventstack[event].icon,icon);
 }

 endeventscan(&es);

 if(p)
 {
  xstrncpy(wimpevent.data.msg.data.helpreply.text,p,236);
  wimpevent.data.msg.hdr.size=((20+strlen(p)+1+3) & 0xFFFFFFFC);
  wimpevent.data.msg.hdr.your_ref=wimpevent.data.msg.hdr.my_ref;
  wimpevent.data.msg.hdr.action=wimp_MHELPREPLY;
  wimp_sendmessage((wimp_etype)17,&wimpevent.data.msg,
                                   wimpevent.data.msg.hdr.task);
 }

 return(err);
}

os_error * addhelpevent(helpfn fn,int handle,int userhandle)
{
 os_error * err;
 err=addblock(HELPMESSAGE,0,handle,userhandle,(zerofn)fn,NULL);
 return(err);
}

os_error * remhelpevent(helpfn fn,int handle,int userhandle)
{
 os_error * err;
 err=remblock(HELPMESSAGE,0,handle,userhandle,(zerofn)fn,NULL);
 return(err);
}

/*****************************************************************************/


static void message(wimp_msgstr * msg)
{
 os_error    * err;

 wimpfirst=wimpevent;
 err=NULL;

/* dprintf(0,"mess=%x myref=%x yourref=%d write=%x",
                msg->hdr.action,msg->hdr.my_ref,
                                msg->hdr.your_ref,writemyref);   */

 switch(msg->hdr.action)
 {
  case    wimp_MCLOSEDOWN:
                          terminate(msg);
                          break;

  case wimp_PALETTECHANGE:
                          setpalvars(msg);
                          break;

  case   wimp_MMODECHANGE:
                          setmodevars(msg);
                          break;

  case     wimp_MDATASAVE:
                          err=datasave();
                          break;

  case   wimp_MDATASAVEOK:
                          err=datasaveok();
                          break;

  case     wimp_MDATALOAD:
                          err=dataload();
                          break;

  case   wimp_MDATALOADOK:
                          err=dataloadok();
                          break;

  case     wimp_MDATAOPEN:
                          err=dataopen(msg);
                          break;

  case     wimp_MRAMFETCH:
                          err=ramfetch();
                          break;

  case  wimp_MRAMTRANSMIT:
                          err=ramtransmit();
                          break;

  case      wimp_MPREQUIT:
                          prequit(msg);
                          break;

  case  wimp_MHELPREQUEST:
                          err=helpmessage(msg);
                          break;

                  default:
                          scanmessage(msg);
                          break;
 }


 report(err);
}


os_error * addmessage(messfn fn,int message)
{
 os_error * err;
 err=addblock(wimp_ESEND,message,0,0,(zerofn)fn,NULL);
 return(err);
}

os_error * remmessage(messfn fn,int message)
{
 os_error * err;
 err=remblock(wimp_ESEND,message,0,0,(zerofn)fn,NULL);
 return(err);
}


/****************************************************************************/

static ackfn ack;
static int   ackref;

os_error * addack(ackfn fn,int ref)
{
 ack=fn;
 ackref=ref;
 return(NULL);
}


static os_error * acknowledged(wimp_msgstr * msg)
{
 if(msg->hdr.my_ref==ackref) return(ack(msg));
 else                        return(NULL);
}

/***************************************************************************/

#define wimp_POLLWORD 13
#define POLLWORDMASK (1<<22)


static int * pollword;

static pollwordfn pollwordhandler;


os_error * addpollword(pollwordfn fn,int * pw)
{
 pollword=pw;
 pollwordhandler=fn;
 return(NULL);
}

os_error * rempollword(void)
{
 pollword=NULL;
 return(NULL);
}

static void pollwordevent(void)
{
 if(pollwordhandler) report(pollwordhandler((int*)wimpevent.data.menu[0],
                                                  wimpevent.data.menu[1]));
}

/***************************************************************************/


static void pollevent(int e)
{                            
/* dprintf(0,"event %x",e); */

 switch(e)
 {
  case        wimp_ENULL:
                         zero(); 
                         break; 

  case      wimp_EREDRAW:
                         redraw(); 
                         break;            /* redraw window request */

  case        wimp_EOPEN:
                         wopen(); 
                         break;            /* open window request  */

  case       wimp_ECLOSE:
                         wclose(); 
                         break;            /* close window request */

  case    wimp_EPTRLEAVE:                  /* poi. leaving window  */
  case    wimp_EPTRENTER:                  /* poi. entering window */
                         pointerevent(wimpevent.e==wimp_EPTRENTER);
                         break;

  case         wimp_EBUT:
                         decodeclick();
                         break;            /* mouse click          */

  case    wimp_EUSERDRAG:
                         decodedrag();
                         break;            /* user drag box        */

  case         wimp_EKEY:
                         keypress();
                         break;            /* key press            */

  case        wimp_EMENU:
                         decodemen();
                         break;            /* menu select          */

  case      wimp_ESCROLL: 
                         break;            /* scroll request       */

  case   wimp_ELOSECARET:
  case   wimp_EGAINCARET:
                         caretevent(wimpevent.e==wimp_EGAINCARET);
                         break;

  case     wimp_POLLWORD:
                         pollwordevent();
                         break;

  case        wimp_ESEND:
  case wimp_ESENDWANTACK:
                         message(&wimpevent.data.msg);
                         break;

  case         wimp_EACK: 
                         report(acknowledged(&wimpevent.data.msg));
                         break;
 }
}



void poll(void)
{
 int pollmaskword;

 /* leaves here */

 pollexit();

 pollmaskword=wimp_ENULL;

 if(pollword) pollmaskword|=POLLWORDMASK;

 if(dopollidle)  
 {
  if(pollword) wimp_pollx((wimp_emask)pollmaskword,&wimpevent,pollword);
  else         wimp_poll((wimp_emask)pollmaskword,&wimpevent);
 }
 else
 {
  if(pollword) 
         wimp_pollidlex((wimp_emask)pollmaskword,&wimpevent,timeout,pollword);
  else   wimp_pollidle((wimp_emask)pollmaskword,&wimpevent,timeout);
 }

 pollenter();

 /* returns here */

 if(!pollmask()) pollevent(wimpevent.e);
}


void pollfake(wimp_eventstr * ep)
{
 wimpevent=*ep;
 pollevent(wimpevent.e);
}


os_error * addwimpmessages(int * mess)
{
 os_regset rx;
 rx.r[0]=(int)mess;
 return(os_swix(Wimp_AddMessages,&rx));
}


static int messagenumbers[]=
{
/* wimp_MCLOSEDOWN, seemingly sent to all tasks - this is Zero... */
 wimp_PALETTECHANGE,
 wimp_MMODECHANGE,
 wimp_MDATASAVE,
 wimp_MDATASAVEOK,
 wimp_MDATALOAD,
 wimp_MDATALOADOK,
 wimp_MDATAOPEN,
 wimp_MRAMFETCH,
 wimp_MRAMTRANSMIT,
 wimp_MPREQUIT,
 wimp_MHELPREQUEST,
 0
};


os_error * pollinit(void)
{
 os_error * err;

 eventn=0;

 err=flex_alloc((flex_ptr)&eventstack,EVCHUNK);
 if(!err) err=addwimpmessages(messagenumbers);

 return(NULL);
}


