/* SFragMem (ShowFragmentationOfMemory)
 *
 * (c) 1992 Thies Wellpott
 *
 * compile (SAS C 5.10): LC -cfistu -rr -v -Hprecompiled
 * link: BLink FROM LIB:c.o,SFragMem.o TO SFragMem LIB LIB:amiga.lib,LIB:lcr.lib
 *	    SC SD ND DEFINE @_main=@_tinymain
 *
 *
 * History
 *
 * V0.9  6.10.02
 *   first code, working version without "Next" and "Info"
 *
 * V1.0  8.10.92
 *   "Next" and "Info" menuitems finished, Icon-ToolTypes configuration added,
 *
 * V1.01  8.10.92
 *   Menus are now really disabled when info-wd is open; better scala at the
 *   left side, adjusts itself to the window size
 */

#include <clib/alib_stdio_protos.h>

#define VERSION "1.01"
#define DATE "8.10.92"

#define NORMAL_POINTER ClearPointer(window)
#define BUSY_POINTER SetPointer(window, busy_ptr_data, 16, 16, -6, 0)

#define FREECOLOR 2
#define USEDCOLOR 3


struct config
{
   UWORD wd_x,wd_y, wd_w,wd_h;
};

/*** global variables ***/
char version[] = "\0$VER: SFragMem V"VERSION" ("DATE")";
struct WBStartup *wbmsg = NULL;

extern struct ExecBase *SysBase;
struct Library *GadToolsBase = NULL, *IconBase = NULL;
struct GfxBase *GfxBase = NULL;
struct IntuitionBase *IntuitionBase = NULL;

struct Window *window = NULL;
UWORD wd_x0, wd_y0;
struct RastPort *rp;

struct config config =
{
   0,0, 500,180
};

UWORD mem_x,mem_y, mem_w,mem_h;
struct MemHeader *memhdr;
APTR mem_start, mem_end;
ULONG mem_len;
UWORD bpp;

APTR vinfo;
struct Menu *menu;
struct NewMenu menu_data[] =
{
   {NM_TITLE, "Project",   NULL, 0, 0, NULL},
   { NM_ITEM, "About...",  "A",  0, 0, NULL},
   { NM_ITEM, NM_BARLABEL, NULL, 0, 0, NULL},
   { NM_ITEM, "Quit",      "Q",  0, 0, NULL},
   {NM_TITLE, "Memory",   NULL, 0, 0, NULL},
   { NM_ITEM, "Next",      "N",  0, 0, NULL},
   { NM_ITEM, NM_BARLABEL, NULL, 0, 0, NULL},
   { NM_ITEM, "Refresh",   "R",  0, 0, NULL},
   { NM_ITEM, NM_BARLABEL, NULL, 0, 0, NULL},
   { NM_ITEM, "Info",      "I",  0, 0, NULL},
   {  NM_END, NULL, NULL, 0, 0, NULL}
};

#define MENU_ABOUT   FULLMENUNUM(0,0,NOSUB)
#define MENU_QUIT    FULLMENUNUM(0,2,NOSUB)
#define MENU_NEXT    FULLMENUNUM(1,0,NOSUB)
#define MENU_REFRESH FULLMENUNUM(1,2,NOSUB)
#define MENU_INFO    FULLMENUNUM(1,4,NOSUB)

struct EasyStruct AboutES =
   {sizeof(struct EasyStruct), 0, "About...",
   "SFragMem V"VERSION" ("DATE")\n© 1992 Thies Wellpott\n\
This programm is freely distributable", "Ok"};


UWORD chip busy_ptr_data[] =
{
   0x0000,0x0000,	      /* OS 2.0 busy pointer */
   0x0400,0x07C0, 0x0000,0x07C0, 0x0100,0x0380, 0x0000,0x07E0,
   0x07C0,0x1FF8, 0x1FF0,0x3FEC, 0x3FF8,0x7FDE, 0x3FF8,0x7FBE,
   0x7FFC,0xFF7F, 0x7EFC,0xFFFF, 0x7FFC,0xFFFF, 0x3FF8,0x7FFE,
   0x3FF8,0x7FFE, 0x1FF0,0x3FFC, 0x07C0,0x1FF8, 0x0000,0x07E0,
   0x0000,0x0000,
};


/*** functions ***/

void close_all(char *s, int err)
{
   if (window)
   {
      ClearMenuStrip(window);
      CloseWindow(window);
      FreeMenus(menu);           /* only alloc., if window opened!! */
      FreeVisualInfo(vinfo);
   }

   if (GadToolsBase)    CloseLibrary(GadToolsBase);
   if (IntuitionBase)   CloseLibrary((struct Library *)IntuitionBase);
   if (GfxBase)         CloseLibrary((struct Library *)GfxBase);

   if (s)                              /* wenn ein String angegeben ist, */
   {
      LONG out;

      /* da stdout nicht definiert ist, wird hier ein Window geöffnet,
       * auf dem der String ausgegeben wird */
      if (wbmsg)
	 out = Open("CON:20/20/260/100/SFragMem error/CLOSE/WAIT", MODE_NEWFILE);
      else
	 out = Open("*", MODE_NEWFILE);

      if (out)
      {
	 fputs(s, out);                /* diesen ausgeben */
	 fputs("!\n", out);            /* "!" + LF anhängen */
	 Close(out);
      } /* if (out) */
   } /* if (s) */

   exit(err);
}


void open_all(void)
{
   if (!(GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 33)))
      close_all("No graphics.library", 20);
   if (!(IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library", 37)))
      close_all("No intuition.library", 20);
   if (!(GadToolsBase = OpenLibrary("gadtools.library", 37)))
      close_all("No gadtools.library", 20);
}


void open_window(void)
{
   struct Screen *scr;

   if (wbmsg)
   {
      if (IconBase = OpenLibrary("icon.library", 33))
      {
	 BPTR olddir;
	 struct DiskObject *diskobj;
	 char *s;

	 olddir = CurrentDir(wbmsg->sm_ArgList->wa_Lock);
	 if (diskobj = GetDiskObject(wbmsg->sm_ArgList->wa_Name))
	 {
	    if (s = FindToolType(diskobj->do_ToolTypes, "WINDOWX"))
	       config.wd_x = atoi(s);
	    if (s = FindToolType(diskobj->do_ToolTypes, "WINDOWY"))
	       config.wd_y = atoi(s);
	    if (s = FindToolType(diskobj->do_ToolTypes, "WINDOWWIDTH"))
	       config.wd_w = atoi(s);
	    if (s = FindToolType(diskobj->do_ToolTypes, "WINDOWHEIGHT"))
	       config.wd_h = atoi(s);

	    FreeDiskObject(diskobj);
	 } /* if (GetDiskObject()) */

	 CurrentDir(olddir);
	 CloseLibrary(IconBase);
      } /* if (OpenLibrary()) */
   } /* if (wbmsg) */

   if (!(scr = LockPubScreen(NULL)))
      goto e1;
   if (!(vinfo = GetVisualInfo(scr, TAG_DONE)))
      goto e2;

   /*** Menu init. ***/
   if (!(menu = CreateMenus(menu_data, GTMN_FullMenu,TRUE, TAG_DONE)))
      goto e3;
   if (!LayoutMenus(menu, vinfo, TAG_DONE))
      goto e4;

   if (!(window = OpenWindowTags(NULL,
	    WA_Left,config.wd_x,  WA_Top,config.wd_y,
	    WA_InnerWidth,config.wd_w,	WA_InnerHeight,config.wd_h,
	    WA_Flags,WFLG_DRAGBAR | WFLG_SIZEGADGET | WFLG_DEPTHGADGET |
	       WFLG_CLOSEGADGET | WFLG_SMART_REFRESH | WFLG_ACTIVATE,
	    WA_MinWidth,190,  WA_MinHeight,50,	WA_MaxWidth,~0,  WA_MaxHeight,~0,
	    WA_Title, "SFragMem V"VERSION,  WA_AutoAdjust,TRUE)))
      goto e4;
   UnlockPubScreen(NULL, scr);
   SetMenuStrip(window, menu);
   wd_x0 = window->BorderLeft;
   wd_y0 = window->BorderTop;	 /* scr->WBorTop + scr->Font->ta_YSize + 1 */
   rp = window->RPort;

      /* space wdborder<>num, two chars, space num<>line, line, space line<>mem */
   mem_x = wd_x0 + 6 + 2*8 + 2 + 4 + 2;
   mem_y = wd_y0 + 4;
   mem_h = window->Height - mem_y - 5 - window->BorderBottom;

   return;


e4:   FreeMenus(menu);           /* bei Fehler, alle Dinge freigeben */
e3:   FreeVisualInfo(vinfo);
e2:   UnlockPubScreen(NULL, scr);
e1:   close_all("Can`t open window", 20);
}



UWORD calc_x(ULONG adr)
{
   return (mem_x + (adr - (ULONG)mem_start) / bpp % mem_w);
}

UWORD calc_y(ULONG adr)
{
   return (mem_y + (adr - (ULONG)mem_start) / bpp / mem_w);
}

void draw_free(ULONG adr, ULONG len)
{
   UWORD x1,y1,x2,y2;

   if (len > bpp / 2)
   {
      x1 = calc_x(adr);
      y1 = calc_y(adr);
      x2 = calc_x(adr + len-1);
      y2 = calc_y(adr + len-1);

      if (y1 == y2)
      {
	 Move(rp, x1,y1);
	 Draw(rp, x2,y1);
      }
      else
      {
	 Move(rp, x1,y1);
	 Draw(rp, mem_x+mem_w-1,y1);
	 if (y2 > y1+1)
	    RectFill(rp, mem_x,y1+1, mem_x+mem_w-1,y2-1);
	 Move(rp, mem_x,y2);
	 Draw(rp, x2,y2);
      }
   } /* if (len) */
}


void draw_memlist(void)
{
   struct MemChunk *chunk;

   SetAPen(rp, FREECOLOR);

   Forbid();
   chunk = memhdr->mh_First;
   do
   {
      draw_free((ULONG)chunk, chunk->mc_Bytes);
   } while (chunk = chunk->mc_Next);
   Permit();
}


void refresh_wd(void)
{
   UWORD i,a,y;
   char str[6];

   BUSY_POINTER;
   ModifyIDCMP(window, 0);

   mem_w = window->Width - mem_x - 6 - window->BorderRight;

   mem_h = window->Height - mem_y - 5 - window->BorderBottom;
   bpp = MAX((mem_len / (8 * mem_w * mem_h) + 1) * 8, 8);
   mem_h = mem_len / (mem_w * bpp);

   {
      struct Message *msg;
      ModifyIDCMP(window, IDCMP_NEWSIZE);
      SizeWindow(window, 0, mem_y + mem_h + 5 + window->BorderBottom - window->Height + 1);
      WaitPort(window->UserPort);
      while (msg = GetMsg(window->UserPort))
	 ReplyMsg(msg);
      ModifyIDCMP(window, 0);
   }

   SetAPen(rp, 0);
   RectFill(rp, wd_x0,mem_y-1, window->Width - window->BorderRight - 1,
	 window->Height - window->BorderBottom - 1);

   SetAPen(rp, USEDCOLOR);
   RectFill(rp, mem_x,mem_y, mem_x+mem_w-1,mem_y+mem_h-1);

   SetAPen(rp, 1);
   Move(rp, mem_x-1,mem_y-1);
   Draw(rp, mem_x+mem_w, mem_y-1);
   Draw(rp, mem_x+mem_w, mem_y+mem_h);
   Draw(rp, mem_x-1, mem_y+mem_h);
   Draw(rp, mem_x-1, mem_y-1);

   Move(rp, calc_x((ULONG)mem_end), mem_y+mem_h-1);
   Draw(rp, mem_x+mem_w, mem_y+mem_h-1);

   a = 1;
   while (calc_y((ULONG)mem_start + a*512*1024) - calc_y((ULONG)mem_start) < 5)
      a *= 2;

   for (i = 0; i <= (mem_len+0x420)/(512*1024); i += a)
   {
      y = calc_y((ULONG)mem_start + i * 512*1024);
      if ((i/a & 1) == 0)
      {
	 Move(rp, wd_x0 + 6, y+3);
	 sprintf(str, "%2ld", i/2);
	 Text(rp, str, 2);
      }
      Move(rp, wd_x0 + 6+2*8+1, y);
      Draw(rp, wd_x0 + 6+2*8+4, y);
   }

   draw_memlist();

   ModifyIDCMP(window, IDCMP_CLOSEWINDOW | IDCMP_MENUPICK | IDCMP_NEWSIZE);
   NORMAL_POINTER;
}


void next_memhdr(void)
{
   memhdr = memhdr->mh_Node.ln_Succ;
   if (memhdr->mh_Node.ln_Succ == NULL)
      memhdr = SysBase->MemList.lh_Head;
   mem_start = memhdr->mh_Lower;
   mem_end = memhdr->mh_Upper;
   mem_len = (ULONG)mem_end - (ULONG)mem_start;
   refresh_wd();
}


void info_window(void)
{
   struct Window *iwd;
   struct RastPort *irp;
   UWORD x0,y0;
   struct IntuiMessage *msg;
   BOOL iende = FALSE;
   char str[40];

   BUSY_POINTER;
   ModifyIDCMP(window, IDCMP_NEWSIZE);
   OffMenu(window, FULLMENUNUM(0,NOITEM,NOSUB));
   OffMenu(window, FULLMENUNUM(1,NOITEM,NOSUB));

   if (iwd = OpenWindowTags(NULL,
	 WA_Left,window->LeftEdge+wd_x0+2,  WA_Top,window->TopEdge+wd_y0,
	 WA_InnerWidth,252,  WA_InnerHeight,112,  WA_AutoAdjust,TRUE,
	 WA_Flags,WFLG_DRAGBAR | WFLG_DEPTHGADGET | WFLG_CLOSEGADGET |
	    WFLG_SMART_REFRESH | WFLG_RMBTRAP | WFLG_ACTIVATE,
	 WA_IDCMP, IDCMP_CLOSEWINDOW,
	 WA_Title,"SFM Information"))
   {
      irp = iwd->RPort;
      x0 = iwd->BorderLeft+6;
      y0 = iwd->BorderTop+4;

      SetAPen(irp, FREECOLOR);
      RectFill(irp, x0,y0, x0+9*8+6,y0+10);
      SetAPen(irp, USEDCOLOR);
      RectFill(irp, x0+90,y0, x0+90+9*8+6,y0+10);
      SetAPen(irp, 1);
      SetDrMd(irp, JAM1);
      Move(irp, x0+3,y0+2+6);
      Text(irp, "Free area", 9);
      Move(irp, x0+90+3,y0+2+6);
      Text(irp, "Used area", 9);

      sprintf(str, "%ld bytes per pixel", bpp);
      Move(irp, x0,y0+14+6);
      Text(irp, str, strlen(str));

      sprintf(str, "block length: %ld (%ld MB)", mem_len, (mem_len+0x420)/(1024*1024));
      Move(irp, x0,    y0+14+ 16+0*9+6);
      Text(irp, str, strlen(str));
      sprintf(str, "used: %ld", mem_len - memhdr->mh_Free);
      Move(irp, x0+8*8,y0+14+ 16+1*9+6);
      Text(irp, str, strlen(str));
      sprintf(str, "free: %ld", memhdr->mh_Free);
      Move(irp, x0+8*8,y0+14+ 16+2*9+6);
      Text(irp, str, strlen(str));

      sprintf(str, "Lower: $%08lx", memhdr->mh_Lower);
      Move(irp, x0,y0+48+ 14+0*9+6);
      Text(irp, str, strlen(str));
      sprintf(str, "Upper: $%08lx", memhdr->mh_Upper);
      Move(irp, x0,y0+48+ 14+1*9+6);
      Text(irp, str, strlen(str));
      sprintf(str, "Attributes: $%04lx", memhdr->mh_Attributes);
      Move(irp, x0,y0+48+ 14+2*9+6);
      Text(irp, str, strlen(str));

      Move(irp, x0,y0+80+ 18+6);
      Text(irp, "Close window to continue", 24);

      while (!iende)
      {
	 WaitPort(iwd->UserPort);
	 while (msg = (struct IntuiMessage *)GetMsg(iwd->UserPort))
	 {
	    if (msg->Class == IDCMP_CLOSEWINDOW)
	       iende = TRUE;
	    ReplyMsg((struct Message *)msg);
	 } /* while (GetMsg()) */
      } /* while (!iende) */

      CloseWindow(iwd);
   } /* if (OpenWindowTags()) */
   else
      DisplayBeep(NULL);

   OnMenu(window, FULLMENUNUM(0,NOITEM,NOSUB));
   OnMenu(window, FULLMENUNUM(1,NOITEM,NOSUB));
   ModifyIDCMP(window, IDCMP_CLOSEWINDOW | IDCMP_MENUPICK | IDCMP_NEWSIZE);
   NORMAL_POINTER;
}


void mainloop(void)
{
   BOOL ende = FALSE;
   struct IntuiMessage *msg;
   ULONG class;
   UWORD code;

   ModifyIDCMP(window, IDCMP_CLOSEWINDOW | IDCMP_MENUPICK | IDCMP_NEWSIZE);

   while (!ende)
   {
      WaitPort(window->UserPort);
      while (!ende && (msg = GT_GetIMsg(window->UserPort)))
      {
	 class = msg->Class;
	 code = msg->Code;
	 GT_ReplyIMsg(msg);

	 switch (class)
	 {
	    case IDCMP_MENUPICK:
	       while (!ende && (code != MENUNULL))
	       {
		  switch (code)
		  {
		     case MENU_ABOUT:
			EasyRequest(window, &AboutES, NULL, NULL);
			break;
		     case MENU_QUIT:
			ende = TRUE;
			break;
		     case MENU_NEXT:
			next_memhdr();
			break;
		     case MENU_REFRESH:
			refresh_wd();
			break;
		     case MENU_INFO:
			info_window();
			break;
		  } /* switch (code) */
		  code = ItemAddress(menu, code)->NextSelect;
	       } /* while (code != MENUNULL) */
	       break;
	    case IDCMP_NEWSIZE:
	       refresh_wd();
	       break;
	    case IDCMP_CLOSEWINDOW:
	       ende = TRUE;
	       break;
	 } /* switch (class) */
      } /* while (GT_GetIMsg()) */
   } /* while (!ende) */
}


void main(int argc, char *argv[])
{
   if (argc == 0)
      wbmsg = (struct WBStartup *)argv;

   open_all();
   open_window();

   memhdr = SysBase->MemList.lh_Head;
   mem_start = memhdr->mh_Lower;
   mem_end = memhdr->mh_Upper;
   mem_len = (ULONG)mem_end - (ULONG)mem_start;
   refresh_wd();

   mainloop();

   close_all(NULL, 0);
}

