/* !RunImage.c
Wimp2 task control
by N. Douglas
Started: 08-01-1999
Last updated: 26-01-1999 version 0.27
*/

#include "kernel.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define BOOL char
#define FALSE 0
#define TRUE 1

static BOOL quit=FALSE;
static char indir[8192], *indirptr;
static char templ[1024];
static char menus[512];
static int windh;
static int maxic=0;

#define XOR_SPECIAL 0x1d872b41
static unsigned int seed=0xabcdef12;

static int codeshandler(int, int);
static int codeshandler2(int, int);
static void CopyLine(int, char *, char *, char *, char *, char *, char *);
int ROReportError(int flags, int *blk);
int ROReportErrora(int flags, int errno, char *errstr);
void StdErrHandlr(_kernel_oserror *);
unsigned int rnd(int);

int main(void)
{
  char taskname[]="Wimp2 Task Manager";
  char q[256];
  _kernel_swi_regs r;
  int a;
  int tbefore=0,tafter=1,tinc=100;
  BOOL update=TRUE;
  int totaltasks=0;
  long int selectedtask=0;

  r.r[0]=200;
  r.r[1]=0x4b534154;
  r.r[2]=(int) taskname;
  StdErrHandlr(_kernel_swi(0x400c0/*Wimp_Initialise*/, &r, &r));

  /* Load some templates */
  {
    const char templatenm[]="<Wimp2Ctrl$Dir>.Templates";
    const char main[]="main";

    r.r[1]=(int) templatenm;
    StdErrHandlr(_kernel_swi(0x400d9/*Wimp_OpenTemplate*/, &r, &r));

    r.r[1]=(int) templ;
    r.r[2]=(int) indir;
    r.r[3]=(int) indir+8192;
    r.r[4]=-1;
    r.r[5]=(int) main;
    r.r[6]=0;
    StdErrHandlr(_kernel_swi(0x400db/*Wimp_LoadTemplate*/, &r, &r));
    maxic=*(int *)(templ+84);
    StdErrHandlr(_kernel_swi(0x400c1/*Wimp_CreateWindow*/, &r, &r));
    windh=r.r[0];
    indirptr=(char *) r.r[2];

    StdErrHandlr(_kernel_swi(0x400da/*Wimp_CloseTemplate*/, &r, &r));
  }

  StdErrHandlr(_kernel_swi(0x42/*OS_ReadMonotonicTime*/, &r, &r));
  seed=r.r[0];

  r.r[1]=(int) q;
  *(int *)(q+0)=windh;
  StdErrHandlr(_kernel_swi(0x400cb/*Wimp_GetWindowState*/, &r, &r));
  {
    int dx=(*(int *)(q+12))-(*(int *)(q+4)),dy=(*(int *)(q+16))-(*(int *)(q+8));
    *(int *)(q+4)=rnd(640);
    *(int *)(q+8)=rnd(512);
    *(int *)(q+12)=(*(int *)(q+4))+dx;
    *(int *)(q+16)=(*(int *)(q+8))+dy;
  }
  StdErrHandlr(_kernel_swi(0x400c5/*Wimp_OpenWindow*/, &r, &r));

  CopyLine(6,"-","Other tasks", "-", "-", "-", "-");

  /* Enter main message loop */
  do
  {
    /* Update lists */
    if(update)
    {
      int timeused=0;
      int topic=6,line,newtotaltasks=0;
      double usused;
      char tusage[16];
      char tsused[16];
      r.r[0]=0;
      for(line=2;line<3+totaltasks;line++)
      {
        char taskh[12]="", usage[8]="", threads[16]="", sused[12]="", smax[12]="";
        if(r.r[0]>=0) StdErrHandlr(_kernel_swi(0xc004d/*Wimp2_EnumerateTasks*/, &r, &r));
        if(r.r[0]>=0)
        {
          _kernel_swi_regs r2;
          r2.r[1]=r.r[1];
          r2.r[4]=0;
          r2.r[5]=0;
          _kernel_swi(0xc004e/*Wimp2_SetTaskData*/, &r2, &r2);

          newtotaltasks++;
          sprintf(taskh, "%08x", r.r[1]);
          if(tafter-tbefore==0)
            usused=0;
          else
            usused=(r.r[5]*10.0)/(tafter-tbefore);
          sprintf(usage, "%.1f%%", usused);
          sprintf(smax,"%d", r.r[4]);
          sprintf(sused, "%d", r.r[5]);
          sprintf(threads,"%d", r.r[6]);
          timeused+=r.r[5];
          CopyLine(topic*line, taskh, (char *) r.r[3], usage, threads, sused, smax);
        }
        else
          CopyLine(topic*line, NULL, NULL, NULL, NULL, NULL, NULL);
      }
      if(tafter-tbefore==0)
        usused=0;
      else
        usused=(timeused*10.0)/(tafter-tbefore);
      {
        int na;
        na=(int)(usused*10.0);
        na=1000-na;
        usused=na/10.0;
      }
      sprintf(tusage,"%.1f%%",usused);
      sprintf(tsused,"%d", (tafter-tbefore)*10-timeused);
      CopyLine(topic,"-","Normal tasks", tusage, "-", tsused, "-");

      /* Update window */
      *(int *)(q+0)=windh;
      *(int *)(q+4)=0;
      *(int *)(q+8)=-1792;
      *(int *)(q+12)=1096;
      *(int *)(q+16)=0;
      r.r[1]=(int) q;
      StdErrHandlr(_kernel_swi(0x400c9/*Wimp_UpdateWindow*/, &r, &r));
      while(r.r[0])
        StdErrHandlr(_kernel_swi(0x400ca/*Wimp_GetRectangle*/, &r, &r));

      totaltasks=newtotaltasks;
      StdErrHandlr(_kernel_swi(0x42/*OS_ReadMonotonicTime*/, &r, &r));
      tbefore=r.r[0];
    }

    r.r[0]=0;
    r.r[1]=(int) q;
    r.r[2]=tbefore+tinc;
    StdErrHandlr(_kernel_swi(0x400e1/*Wimp_PollIdle*/, &r, &r));
    a=r.r[0];
    StdErrHandlr(_kernel_swi(0x42/*OS_ReadMonotonicTime*/, &r, &r));
    if(r.r[0]>=tbefore+tinc)
    {
      tafter=r.r[0];
      update=TRUE;
    }
    else update=FALSE;

    switch(a)
    {
      case 2:
      StdErrHandlr(_kernel_swi(0x400c5/*Wimp_OpenWindow*/, &r, &r));
      break;
      case 3:
      {
        StdErrHandlr(_kernel_swi(0x400c6/*Wimp_CloseWindow*/, &r, &r));
        if(*(int *)(r.r[1])==windh) quit=TRUE;
        break;
      }
      case 6:
      if((*(int *)(q+12))==-2)/*ie; the icon bar*/
      {
        if((*(int *)(q+8) & 4)==4)
        {
          /* Open the window */
          *(int *)(q+0)=windh;
          r.r[1]=(int) q;
          StdErrHandlr(_kernel_swi(0x400cb/*Wimp_GetWindowState*/, &r, &r));
          *(int *)(q+28)=-1;
          StdErrHandlr(_kernel_swi(0x400c5/*Wimp_OpenWindow*/, &r, &r));
        }
        else
        {
          StdErrHandlr(_kernel_swi(0x107, &r, &r));
        }
        break;
      }
      else if((*(int *)(q+12))==windh) /* ie; it's the window */
      {
        if((*(int *)(q+8) & 2)==2)
        {
          int baseic;
          /* Open menu */

          /* Deduce where the mouse pointer is */
          if(*(int *)(q+16)!=-1)
          {
            baseic=*(int *)(q+16);
            baseic=baseic/6;
            if(baseic<=1) baseic=0;
            baseic=baseic*6;
          }
          else
            baseic=0;
          if(baseic>0)
          {
            const char menuname[]="Wimp2Ctrl";
            const char setslice[]="Set Slice";
            const char killtask[]="Kill Task";
            char *menuptr, *menu2, *end;

            /* Set baseic */
            *(int *)(q+128)=windh;
            *(int *)(q+128+4)=baseic;
            r.r[1]=(int) q+128;
            _kernel_swi(0x400ce/*Wimp_GetIconState*/, &r, &r);
            selectedtask=strtoul((char *)(*(int *)(q+128+8+20)), &end, 16);

            /*Create a submenu */
            menu2=menus+256;
            {
              char *menu2ptr;
              const char menuname[]="Priority";
              const char lowp[]="Low";
              const char normalp[]="Normal";
              const char highp[]="High";
              const char ouchp[]="Ouch!";
              const char writeablel[]="A0-9";

              *(int *)(menu2+0)=*(int *)(menuname+0);
              *(int *)(menu2+4)=*(int *)(menuname+4);
              *(int *)(menu2+8)=*(int *)(menuname+8);
              *(char *)(menu2+12)=7;
              *(char *)(menu2+13)=2;
              *(char *)(menu2+14)=7;
              *(char *)(menu2+15)=0;
              *(int *)(menu2+16)=4*16;
              *(int *)(menu2+20)=44;
              *(int *)(menu2+24)=0;
              menu2ptr=menu2+28;

              *(int *)(menu2ptr+0)=0x0;
              *(int *)(menu2ptr+4)=-1;
              *(int *)(menu2ptr+8)=0x7005031;
              *(int *)(menu2ptr+12)=*(int *)(lowp+0);
              *(int *)(menu2ptr+16)=*(int *)(lowp+4);
              *(int *)(menu2ptr+20)=*(int *)(lowp+8);
              menu2ptr+=24;
              *(int *)(menu2ptr+0)=0x0;
              *(int *)(menu2ptr+4)=-1;
              *(int *)(menu2ptr+8)=0x7005031;
              *(int *)(menu2ptr+12)=*(int *)(normalp+0);
              *(int *)(menu2ptr+16)=*(int *)(normalp+4);
              *(int *)(menu2ptr+20)=*(int *)(normalp+8);
              menu2ptr+=24;
              *(int *)(menu2ptr+0)=0x0;
              *(int *)(menu2ptr+4)=-1;
              *(int *)(menu2ptr+8)=0x7005031;
              *(int *)(menu2ptr+12)=*(int *)(highp+0);
              *(int *)(menu2ptr+16)=*(int *)(highp+4);
              *(int *)(menu2ptr+20)=*(int *)(highp+8);
              menu2ptr+=24;
              *(int *)(menu2ptr+0)=0x0;
              *(int *)(menu2ptr+4)=-1;
              *(int *)(menu2ptr+8)=0x7005031;
              *(int *)(menu2ptr+12)=*(int *)(ouchp+0);
              *(int *)(menu2ptr+16)=*(int *)(ouchp+4);
              *(int *)(menu2ptr+20)=*(int *)(ouchp+8);
              menu2ptr+=24;
              *(int *)(menu2ptr+0)=0x84;
              *(int *)(menu2ptr+4)=-1;
              *(int *)(menu2ptr+8)=0x7009131;
              *(int *)(menu2ptr+12)=(int) menus+500;
              *(int *)(menu2ptr+16)=(int) writeablel;
              *(int *)(menu2ptr+20)=4;
              menu2ptr+=24;
            }

            /*Open a quit menu */
            *(int *)(menus+0)=*(int *)(menuname+0);
            *(int *)(menus+4)=*(int *)(menuname+4);
            *(int *)(menus+8)=*(int *)(menuname+8);
            *(char *)(menus+12)=7;
            *(char *)(menus+13)=2;
            *(char *)(menus+14)=7;
            *(char *)(menus+15)=0;
            *(int *)(menus+16)=4*16;	/* I see this is ignored on RO3 */
            *(int *)(menus+20)=44;
            *(int *)(menus+24)=0;
            menuptr=menus+28;

            *(int *)(menuptr+0)=0x0;
            *(int *)(menuptr+4)=(int) menu2;
            *(int *)(menuptr+8)=0x7005031;
            *(int *)(menuptr+12)=*(int *)(setslice+0);
            *(int *)(menuptr+16)=*(int *)(setslice+4);
            *(int *)(menuptr+20)=*(int *)(setslice+8);
            menuptr+=24;
            *(int *)(menuptr+0)=0x80;
            *(int *)(menuptr+4)=-1;
            *(int *)(menuptr+8)=0x7005031 | (1<<22);
            *(int *)(menuptr+12)=*(int *)(killtask+0);
            *(int *)(menuptr+16)=*(int *)(killtask+4);
            *(int *)(menuptr+20)=*(int *)(killtask+8);
            r.r[1]=(int) menus;
            r.r[2]=(*(int *)(q+0))-64;
            r.r[3]=(*(int *)(q+4))+24;
            StdErrHandlr(_kernel_swi(0x400d4/*Wimp_CreateMenu*/, &r, &r));
          }
          else
            _kernel_swi(0x107, &r, &r);
        }
        break;
      }
      break;
      case 9:
      {
        switch(*(int *)(q+0))
        {
          case 0: /* set slice */
          {
            r.r[1]=(int) selectedtask;
            r.r[5]=-1;
            switch(*(int *)(q+4))
            {
              case 0: /* Low */
              {
                r.r[4]=2;
                break;
              }
              case 1: /* Normal */
              {
                r.r[4]=5;
                break;
              }
              case 2: /* High */
              {
                r.r[4]=25;
                break;
              }
              case 3: /* Ouch! */
              {
                r.r[4]=100;
                break;
              }
              case 4: /* User defined */
              {
                r.r[4]=atoi(menus+500);
                break;
              }
              default:
              {
                r.r[4]=0;
                break;
              }
            }
            _kernel_swi(0xc004e/*Wimp2_SetTaskData*/, &r, &r);
            break;
          }
          case 1: /* kill task */
          {
            r.r[0]=(int) selectedtask;
            _kernel_swi(0xc004f/*Wimp2_KillProcess*/, &r, &r);
            break;
          }
        }
        break;
      }
      case 17:
      case 18:
      switch(*(int *) (r.r[1]+16))
      {
        default:
        {
          codeshandler(a, r.r[1]);
          break;
        }
      }
      break;
      default:
      break;
    }
  }
  while(!quit);

  /* Shut her down */
  r.r[1]=0;
  _kernel_swi(0x400dd/*Wimp_CloseDown*/, &r, &r);

  return 0;
}

void CopyLine(int startic, char *pid, char *name, char *usage, char *memory, char *sused, char *smax)
{
  char q[256];
  _kernel_swi_regs r;
  int n;
  BOOL del=FALSE;

  if(pid==NULL && name==NULL && usage==NULL && memory==NULL && sused==NULL && smax==NULL) del=TRUE;

  for(n=startic; n<startic+6;n++)
  {
    BOOL change=FALSE;
    if(n>=maxic)
    {
      int a;
      for(a=0; a<6;a++)
      {
        *(int *)(q+0)=windh;
        *(int *)(q+4)=a;
        r.r[1]=(int) q;
        _kernel_swi(0x400ce/*Wimp_GetIconState*/, &r, &r);
        *(int *)(q+4)=windh;
        (*(int *)(q+8+4))-=56*n/6;
        (*(int *)(q+8+12))-=56*n/6;
        memset(indirptr, 0, *(int *)(q+8+28));
        *(int *)(q+8+20)=(int) indirptr;
        indirptr+=*(int *)(q+8+28);
        r.r[1]=(int) q+4;
        _kernel_swi(0x400c2/*Wimp_CreateIcon*/, &r, &r);
        if(r.r[0]+1>maxic) maxic=r.r[0]+1;
      }
    }
    *(int *)(q+0)=windh;
    *(int *)(q+4)=n;
    r.r[1]=(int) q;
    _kernel_swi(0x400ce/*Wimp_GetIconState*/, &r, &r);
    if(del==TRUE && ((*(int *)(q+8+16)) & (1<<23))==0) change=TRUE;
    if(del==FALSE && ((*(int *)(q+8+16)) & (1<<23))!=0) change=TRUE;
    if(change)
    {
      int clear,eor;
      if(del)
      {
        clear=1<<23;
        eor=1<<23;
      }
      else
      {
        eor=0;
        clear=1<<23;
      }
      *(int *)(q+0)=windh;
      *(int *)(q+4)=n;
      *(int *)(q+8)=eor;
      *(int *)(q+12)=clear;
      r.r[1]=(int) q;
      _kernel_swi(0x400cd/*Wimp_SetIconState*/, &r, &r);
      /* And redraw everything */
      r.r[0]=windh;
      r.r[1]=0;
      r.r[2]=-1792;
      r.r[3]=1096;
      r.r[4]=0;
      _kernel_swi(0x400d1/*Wimp_ForceRedraw*/, &r, &r);
    }
    {
      switch(n % 6)
      {
        case 0:
        {
          strcpy((char *)(*(int *)(q+8+20)), pid);
          break;
        }
        case 1:
        {
          strcpy((char *)(*(int *)(q+8+20)), name);
          break;
        }
        case 2:
        {
          strcpy((char *)(*(int *)(q+8+20)), usage);
          break;
        }
        case 3:
        {
          strcpy((char *)(*(int *)(q+8+20)), memory);
          break;
        }
        case 4:
        {
          strcpy((char *)(*(int *)(q+8+20)), sused);
          break;
        }
        case 5:
        {
          strcpy((char *)(*(int *)(q+8+20)), smax);
          break;
        }
      }
    }
  }
}

int codeshandler(int msgno, int msgblk)
{
  /*return 2;*/
  return codeshandler2(msgno, msgblk);
}

int codeshandler2(int msgno, int msgblk)
{
  switch(*(int *) (msgblk+16))
  {
    case 0:
    quit=TRUE;
    return 1;
    break;
    case 0x502:/*Message_HelpRequest*/
    if(*(int *)(msgblk+32)==windh)
    {
      char msg[]="This is a list of tasks being preemptively multitasked by Wimp2 and information about them. Click MENU to alter the configuration of the task.";
      _kernel_swi_regs r;

      strcpy((char *) msgblk+20,msg);
      *(int *)(msgblk+0)=256;
      *(int *)(msgblk+16)=0x503;/*Message_HelpGive*/
      *(int *)(msgblk+12)=*(int *)(msgblk+8);
      r.r[0]=17;
      r.r[1]=msgblk;
      r.r[2]=*(int *)(msgblk+4);
      _kernel_swi(0x400e7/*Wimp_SendMessage*/, &r, &r);
      return 1;
    }
    return 0;
    break;
    default:
    return 0;
    break;
  }
}

unsigned int rnd(int val)
{
  int n;

  for(n=0;n<32;n++)
  {
    seed=seed<<1;
    if(seed & (1<<30))
    {
      seed=seed^XOR_SPECIAL;
    }
  }
  n=seed % val;
  if(n>=0)
    return n;
  else
    return -n;
}

int ROReportError(int flags, int *blk)
{
  _kernel_swi_regs r;
  char tmesg[]="Wimp2 Demo";

  r.r[0]=(int) blk;
  r.r[1]=flags;
  r.r[2]=(int) tmesg;
  _kernel_swi(0x400df/*Wimp_ReportError*/, &r, &r);

  if(flags & 0x100)
    exit(EXIT_FAILURE);
  return r.r[1];
}

int ROReportErrora(int flags, int errno, char *errstr)
{
  char q[260];

  *(int *)(q+0)=errno;
  strcpy((q+4), errstr);
  return ROReportError(flags, (int *) q);
}

void StdErrHandlr(_kernel_oserror *blk)
{
  if(blk)
    ROReportError(0x102, (int *) blk);
}


/* Just for debugging purposes */
void end(void)
{
}
