/*////////////////////////////////////
/              T_A_S_K               /
/                                    /
/ Created by THOMAS BECKER in HÖXTER /
/         in Dec ANNO 88             /
/                                    /
/ Aufruf: [run] task                 /
/                                    /
/ Options: cc task.c -s +l           /
/          ln task.o -lc32 -lm32     /
////////////////////////////////////*/


#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <exec/types.h>
#include <exec/execbase.h>
#include <exec/memory.h>


/* Konstanten-Definitionen */

#define MAX_TASKS 40 /* bei Interlace 80 (wer hat schon einmal?)! */
#define SCHRITTE (0xffff/40)
#define SMALL_WIDTH 118
#define SMALL_HEIGHT 24
#define AREA_WIDTH 160
#define AREA_HEIGHT 24
#define GAD_WIDTH (AREA_WIDTH-8)
#define GAD_HEIGHT (AREA_HEIGHT-17)
#define SCREEN_HEIGHT (IntuitionBase->ActiveScreen->Height)
#define SCREEN_WIDTH (IntuitionBase->ActiveScreen->Width)
#define TOP 9
#define RAND 4
#define STELLE(zahl) for (i=0,p=first;i<zahl;i++,p=p->next) /* Liste durchforsten */
 

struct ExecBase *SysBase;
struct IntuitionBase *IntuitionBase=NULL;
struct NewWindow nw =
{
   0,0,SMALL_WIDTH,SMALL_HEIGHT,0,3,
   CLOSEWINDOW|MOUSEBUTTONS|GADGETUP|INACTIVEWINDOW,
   WINDOWDRAG|WINDOWDEPTH|WINDOWCLOSE|SMART_REFRESH|ACTIVATE|RMBTRAP,
   NULL,NULL,"TASK",NULL,NULL,0,0,0,0,WBENCHSCREEN
};

struct Window *win=NULL;
struct GfxBase *GfxBase=NULL;
struct IntuiMessage *Message;
struct Remember *rememberkey=NULL;


/* Liste-Gadget-Knoten-Struktur */

struct all {
   struct all *next;  /* nächster Knoten der Struktur all */
   struct Gadget gad; /* Gadget-Struktur */
   struct PropInfo pinfo; /* PropInfo-Struktur für Prop-Gadget */
   struct Image image[2]; /* zwei images für das task-area */
   struct IntuiText text; /* IntuiText für das task-area */
   char info_zeile [69]; /* Infozeile in der Fensterleiste */
   short stellung; /* letzte PropGadget-Stellung wird gemerkt */
} *p,*vorgaenger,*last,*first=NULL; /* einige Zeiger auf die Strucktur */
 

/* Image-Daten für die Skala des PropGadget */

UWORD skala [] = 
   { 
     0xE60,   0x0,   0x230,   0x0,   0x38,   0x0,   0x8,   0xC000,   0x7,   0x3000, 
     0x290,   0x0,   0x648,   0x0,   0x44,   0x0,   0x99,   0x2000,   0x21,   0x4800, 
     0x7490,   0x0,   0x7248,   0x0,   0x44,   0x0,   0x1C9,   0x2000,   0x72,   0x4800, 
     0x890,   0x0,   0x248,   0x0,   0x44,   0x0,   0x89,   0x2000,   0x24,   0x4800, 
     0xE60,   0x0,   0x730,   0x0,   0x38,   0x0,   0x1C,   0xC000,   0x7,   0x3000, 
     0x400,   0x0,   0x80,   0x0,   0x10,   0x0,   0x2,   0x0,   0x0,   0x4000, 
     0xE08,   0x1020,   0x41C1,   0x204,   0x838,   0x2040,   0x8107,   0x408,   0x1020,   0xE000, 
   };

/* Image-Daten für den PropGadget-Container */

USHORT container [] = {0xffff,0xffff,0xffff,0xffff};


UBYTE *state[8]= /* Task-Zustände */
   {
   "nonvalid",
   "added",
   "running",      
   "ready",
   "waiting",
   "except",
   "removed",
   "error"
   };   


struct Task *t_nodes[MAX_TASKS];
ULONG Class;
USHORT Code,gad_id;
SHORT big_x=0,big_y=0,k=0,MouseX,MouseY;
APTR Addr;

main () /* Hauptprogramm */
{
void ende(),print(),search_tasks();
SHORT scan_area(),old_pos=-1,new_pos,i,wert;
BOOL exist_task(),build_gads(),aufgebaut=FALSE;

/* Libraries öffnen */

if (!(IntuitionBase = (struct IntuitionBase *) OpenLibrary ("intuition.library",0))) ende ("Intuition-Library-ERROR");
if (!(GfxBase = (struct GfxBase *) OpenLibrary("graphics.library",0))) ende ("graphics-library-ERROR");

/* Window öffnen */

if (!(win = (struct Window *)OpenWindow(&nw))) ende ("window-ERROR"); 
print(win,"REFRESH",30,19); 

for (;;) /* Endlos-Schleife */
   {
   Wait (1<<win->UserPort->mp_SigBit); /*Warten bis Signal in win anliegt */
   while (Message= (struct IntuiMessage*)GetMsg(win->UserPort))
      {
      Class = Message->Class;   
      Code = Message->Code;
      Addr = Message->IAddress;   
      MouseX = Message->MouseX;
      MouseY = Message->MouseY; /* Daten der IntuiMessage-Struktur retten */
      ReplyMsg (Message); /* dann Message beantworten */
      switch (Class) /* alle möglichen Signale abfragen */
         {
         case INACTIVEWINDOW:
            {
            if (aufgebaut) /* wenn Window inaktiv dann kleines Warte-window */
               {
               aufgebaut = build_small_win();
               old_pos = -1;
               }
            break;
            }
         case MOUSEBUTTONS:
            {
            if (Code==SELECTUP||Code==MENUUP)
               if (aufgebaut) /* bei Hauptwindow: Info-task-zeile ausgeben */
                  {
                  if (old_pos!=(new_pos=scan_area ()))
                     {
                     STELLE(new_pos);
                     SetWindowTitles (win,p->info_zeile,-1);
                     old_pos = new_pos;            
                     } 
                  }
               else /* sonst Hauptwindow aufbauen */
                    {
                    search_tasks ();
                    if (!build_gads ()) ende ("Gadget-ERROR");
                    aufgebaut = build_big_win ();
                    };
            break;
            }
         case GADGETUP:
            {
            gad_id = ((struct Gadget*)Addr)->GadgetID;
            wert = ((struct Gadget *)Addr)->SpecialInfo->HorizPot/SCHRITTE-20;
            STELLE (((struct Gadget*)Addr)->GadgetID);
            if (p->stellung!=wert)
               {
               p->stellung=wert;
               if (exist_task (t_nodes[gad_id])) /* wenn der Task noch existiert */
               SetTaskPri (t_nodes[gad_id],wert);  /* neue Task-Priorität setzen */
               }
            else /* sonst muß neu refresht werden */
               {
               DisplayBeep (IntuitionBase->ActiveScreen);
               SetWindowTitles (win,"Task nicht mehr existent, refreshen !",-1);
               }
            break;
            }
         case CLOSEWINDOW:
            {
            ende (""); /* alles vorbei */
            }
         }
      }
   }
} /* end of main() */


BOOL build_gads () /* Gadgets dynamisch bereitstellen */
{
USHORT i,t,top=TOP+17,left=RAND,*q,*fi;

/* CHIP-Speicher reservieren für die beiden Images */

if (!(fi=q=(USHORT *)AllocRemember (rememberkey,sizeof(skala)+sizeof(container),MEMF_CHIP))) 
   ende ("mehr Chip-Speicher !");
for (t=0;t<(sizeof(skala)/2);t++) *q++=skala[t];
for (t=0;t<(sizeof(container)/2);t++) *q++=container [t];


/* für jeden Task ein Gadget vorbereiten und eine verkettete Liste bilden */

for (i=0,last=NULL,vorgaenger=NULL,first=NULL;i<=k;i++)
    { 
    if(!(last=(struct all*)AllocRemember (rememberkey,sizeof (struct all),0L)))
       ende ("mehr Speicher !");   
    if (!i) first = last;
    if(vorgaenger) vorgaenger->next = last;
    vorgaenger = last;
    last->pinfo.Flags = FREEHORIZ; /* alle Strukturen ausfüllen */
    last->pinfo.VertPot = 0;
    last->pinfo.HorizPot = ((t_nodes[i]->tc_Node.ln_Pri+20<0)?0:
       (t_nodes[i]->tc_Node.ln_Pri+20>40)?40:t_nodes[i]->tc_Node.ln_Pri+20)*SCHRITTE;
    last->pinfo.HorizBody = SCHRITTE;
    last->pinfo.VertBody = 0;
    last->pinfo.CWidth = 0;
    last->pinfo.CHeight = 0;
    last->pinfo.HPotRes = 0;
    last->pinfo.VPotRes = 0;
    last->pinfo.LeftBorder = 0;
    last->pinfo.TopBorder = 0;
    last->stellung= last->pinfo.HorizPot/SCHRITTE-20;
    last->gad.NextGadget = NULL;
    last->gad.LeftEdge = left;
    last->gad.TopEdge = top;
    last->gad.Width = GAD_WIDTH;
    last->gad.Height = GAD_HEIGHT;
    last->gad.Flags = GADGIMAGE;
    last->gad.Activation = RELVERIFY;
    last->gad.GadgetType = PROPGADGET;
    last->gad.GadgetRender = &last->image[0];
    last->gad.SelectRender = NULL;
    last->gad.GadgetText = &last->text;
    last->gad.MutualExclude = 0;
    last->gad.SpecialInfo = &last->pinfo;
    last->gad.GadgetID = i;
    last->gad.UserData = t_nodes[i];   

    last->text.FrontPen = 2;
    last->text.BackPen = 0;
    last->text.DrawMode = JAM1;
    last->text.LeftEdge = -1;
    last->text.TopEdge = -15;
    last->text.ITextFont = NULL;
    last->text.IText = t_nodes[i]->tc_Node.ln_Name;
    last->text.NextText = NULL;

    /* Info-Zeile im Window-Title wird aus den Task-Daten gebildet */

    sprintf (last->info_zeile,"%s: %s  Adr:$%lx  StackAdr:$%lx  Stack:%lu",last->text.IText,
         state[(int)t_nodes[i]->tc_State&7],t_nodes[i],t_nodes[i]->tc_SPLower,
         (ULONG)(t_nodes[i]->tc_SPUpper-t_nodes[i]->tc_SPLower-2));

    /* image-Struktur für container */
    last->image[0].NextImage = &last->image[1]; 
    last->image[0].LeftEdge = 0;
    last->image[0].TopEdge = 0;
    last->image[0].Width = 5;
    last->image[0].Height = 4;
    last->image[0].Depth = 1;
    last->image[0].ImageData = fi+sizeof(skala)/2;
    last->image[0].PlanePick = 1; 
    last->image[0].PlaneOnOff = 0;

    /* image-Struktur für Skala */
    last->image[1].LeftEdge = -4;
    last->image[1].TopEdge = -9;
    last->image[1].Width = 153;
    last->image[1].Height = 7;
    last->image[1].Depth = 1;
    last->image[1].ImageData = fi;
    last->image[1].PlanePick = 1;
    last->image[1].PlaneOnOff = 0;
    last->image[1].NextImage = NULL;
    
    left = left + AREA_WIDTH;
    if ((left+GAD_WIDTH+RAND)>SCREEN_WIDTH) 
      {left = RAND;top = top+AREA_HEIGHT;}

    if (!AddGadget (win,&last->gad,-1)) return (FALSE);
    }   
last->next = NULL; /* Liste mit NULL beenden */
return (TRUE);
} /* end of build_gads() */


build_big_win () /* großes Haupt-Gadget-Fenster wird eingerichtet */
{
int dx=0,dy=0;

if ((last->gad.TopEdge)==(TOP+17)) big_x = last->gad.LeftEdge+AREA_WIDTH;
else big_x = ((SCREEN_WIDTH/AREA_WIDTH)*AREA_WIDTH);
big_y = last->gad.TopEdge + GAD_HEIGHT +2;

if (win->LeftEdge>(SCREEN_WIDTH-big_x)) dx = SCREEN_WIDTH-big_x-win->LeftEdge;
if (win->TopEdge>(SCREEN_HEIGHT-big_y)) dy = SCREEN_HEIGHT-big_y-win->TopEdge;

SetRast (win->RPort,0); /* Window wird gecleant */
MoveWindow (win,dx,dy);
SizeWindow (win,big_x-SMALL_WIDTH,big_y-SMALL_HEIGHT);
SetWindowTitles (win,"Press any Mouse-Button on any Task-Area for Task-Info! (C) Th. Becker",-1);
RefreshWindowFrame (win);
RefreshGadgets (&first->gad,win,NULL);
SetAPen (win->RPort,2);
for (p=first;p;p=p->next) /* Task-Areas werden umrandet */
   {
   Move (win->RPort,p->gad.LeftEdge-RAND,p->gad.TopEdge + GAD_HEIGHT);
   Draw (win->RPort,p->gad.LeftEdge-RAND+AREA_WIDTH,p->gad.TopEdge+GAD_HEIGHT);
   Draw (win->RPort,p->gad.LeftEdge-RAND+AREA_WIDTH,p->gad.TopEdge+GAD_HEIGHT-AREA_HEIGHT);
   }
return (TRUE); 
} /* end of build_big_win() */


build_small_win () /* kleines Wartefenster wird eingerichtet */
{
struct Gadget *g;

for (g=&first->gad;g;g=g->NextGadget) 
    RemoveGadget (win,g); /* Gadgets aus Gad-Liste entfernen */
first = NULL;
FreeRemember (rememberkey,1);
SizeWindow (win,SMALL_WIDTH-big_x,SMALL_HEIGHT-big_y);
SetWindowTitles (win,"TASK",-1);
SetRast (win->RPort,0);
RefreshWindowFrame (win);
print (win,"REFRESH",30,19);
return (FALSE);
} /* end of build_small_win() */


VOID ende (fehler) /* alle Ressourcen werden zum Prg-Ende ordnungsgemäß geschlossen */
char *fehler;
{
struct Gadget *g;

printf ("%s\n",fehler);
if (first)
      {
      for (g=&first->gad;g;g=g->NextGadget) RemoveGadget (win,g);
      FreeRemember (rememberkey,1);
      }   
if (win) CloseWindow (win);
if (GfxBase) CloseLibrary (GfxBase);
if (IntuitionBase) CloseLibrary (IntuitionBase);
exit (TRUE);
} /* end of ende() */


VOID print (win,text,x,y) /* Text-Ausgabe-Funktion */
struct Window *win;
char *text;
SHORT x,y;
{
Move (win->RPort,x,y);
Text (win->RPort,text,strlen(text));
} /* end of print() */


VOID search_tasks () /* alle System-Tasks werden gespeichert im Feld t_nodes*/
{
register struct Task *task;

k=0;
Disable (); /* Task-Switching und Interrupts müssen verboten werden */

t_nodes[k] = (struct Task *)SysBase->ThisTask;

for (task=SysBase->TaskReady.lh_Head;task->tc_Node.ln_Succ;
     t_nodes[++k]=task,task=task->tc_Node.ln_Succ);

for (task=SysBase->TaskWait.lh_Head;task->tc_Node.ln_Succ;
     t_nodes[++k]=task,task=task->tc_Node.ln_Succ);

Enable (); /* Task-Switching und Interrupts werden wieder erlaubt */
if (k>=MAX_TASKS) k = MAX_TASKS-1;
} /*end of search_tasks() */


/* Funktion scannt, welches Task-Area angeklickt wurde */

short scan_area () 
{
short pos;
pos=MouseX/AREA_WIDTH + (MouseY-TOP)/AREA_HEIGHT*(SCREEN_WIDTH/AREA_WIDTH);
if (pos > k) pos = k;
return (pos);
} /* end of scan_area() */


/* soll prüfen, ob Task noch vorhanden */

BOOL exist_task (task)
register struct Task *task;

{
BOOL bool=FALSE;
register struct Task *t_node;
Disable (); /* Task-Switching und Interrupts verbieten */

/* Task-Adresse 'task' wird mit allen gerade vorhandenen Tasks verglichen,
der Rückgabe-Parameter bool zeigt Erfolg oder Mißerfolg an */

bool = (task == (struct Task *)SysBase->ThisTask); 

if (!bool) for (t_node=SysBase->TaskReady.lh_Head;t_node->tc_Node.ln_Succ;
   bool = (task==t_node),t_node=t_node->tc_Node.ln_Succ)
        if (bool) break;

if (!bool) for (t_node=SysBase->TaskWait.lh_Head;t_node->tc_Node.ln_Succ;
   bool = (task==t_node),t_node=t_node->tc_Node.ln_Succ)
        if (bool) break;

Enable (); /*Task-Switching und Interrupt wieder erlauben */
return (bool);
} /* end of exist_task() */
