/***************************************************************************/ /* */ /* SmartIcon, release 1.0 - An Intuition object iconifier for the Amiga */ /* Copyright (c) 1988 Gauthier H. Groult */ /* */ /* Written in January 1988 by Gauthier H. Groult */ /* 33, Boulevard Saint Denis, */ /* 92400 Courbevoie */ /* France - Europe */ /* Tel: (16) 1 47 89 09 54 */ /* email: mcvax!inria!litp!germinal!groult */ /* */ /* This source code is not in public domain, please do not distribute. */ /* The binary program is shareaware. Read docs files for details. */ /* */ /***************************************************************************/ /* */ /* SmartIcon Tech Notes. */ /* */ /* The general techniques used in the program are the following: */ /* */ /* ° The program keeps track of what as been patched trouch a linked */ /* list of "IconGadgets". The structure IconGadget is a superset */ /* of the Gadget structure. The added fields contains the information */ /* needed to reset things when cleaning up. */ /* */ /* ° The windows that are already opened when the program starts are */ /* patched by scanning the Intuition window list. */ /* */ /* ° The windows which open *after* the program as been laoded are */ /* patched trough a rom call trap to OpenWindow(). */ /* */ /* ° The windows that close *while* the program is running are */ /* un-patched trough a rom call trap to CloseWindow(). */ /* */ /* ° The icon gadgets selection for any window is detected to a rom */ /* call trap to PutMsg(). */ /* */ /* ° For every icon created, a program capable of un-iconifying the */ /* window is written to the ram disk. This program is contained in */ /* this code, under its binary form. */ /* */ /* ° The revealing program needs to have a pointer on the window it */ /* must reveal. This is given to it trough the "comment" field of */ /* the window's icon. */ /* */ /* ° The revealing program self-destroys it's ram-disk file before */ /* exiting to keep things clean. */ /* */ /* ° The windows are hidden only by resizing and moving their layers. */ /* */ #include "exec/types.h" #include "graphics/gfx.h" #include "hardware/dmabits.h" #include "hardware/custom.h" #include "hardware/blit.h" #include "graphics/gfxmacros.h" #include "graphics/copper.h" #include "graphics/view.h" #include "graphics/gels.h" #include "graphics/regions.h" #include "graphics/clip.h" #include "exec/exec.h" #include "graphics/text.h" #include "graphics/gfxbase.h" #include "graphics/layers.h" #include "graphics/clip.h" #include "intuition/intuition.h" #include "intuition/intuitionbase.h" #include "libraries/dos.h" #include "workbench/workbench.h" #include "string.h" /* #include "proto/exec.h" #include "proto/intuition.h" #include "proto/dos.h" */ #define ICONGADGET 11366 #define MAXFILELEN 13 struct IconGadget { struct IconGadget *NextIGadget; struct Gadget *IconGadget, *DepthGadget; struct Window *Window; struct MsgPort *ReelPort; ULONG Flags; }; BOOL HooksSet; ULONG IconEvent; ULONG IconBase; struct GfxBase *GfxBase; struct IntuitionBase *IntuitionBase; struct LayersBase *LayersBase; struct MsgPort *IconPort; struct IconGadget AddedGadgets; struct Task *IconTask; struct Window *theSelectedWindow; struct FileHandle *file; extern UBYTE RevealCode[]; extern USHORT RevealCodeSize; extern USHORT IconImageData[], theWindowIData[]; extern VOID SetHooks(), CleanHooks(), DisplayWindow(); struct Image IconImage = { -14,0, 51,10, 2, IconImageData, 0x0003,0x0000, NULL }; struct Gadget theIconGadget = { NULL, -36, 0, 17, 10, GADGIMAGE | GRELRIGHT, RELVERIFY, BOOLGADGET, (APTR)&IconImage, NULL, NULL, NULL, NULL, ICONGADGET, NULL }; struct Image theWindowImage = { 0,0, 55,20, 2, &theWindowIData[0], 3,0 }; struct DiskObject theWindowIcon = { WB_DISKMAGIC, WB_DISKVERSION, /* Gadget Structure */ NULL, /* Ptr to next gadget */ 0,0, /* Leftedge, Topedge */ 55,20, /* Width, Height */ GADGHBOX|GADGIMAGE, /* Flags */ RELVERIFY|GADGIMMEDIATE, /* Activation */ BOOLGADGET, /* Type */ (APTR)&theWindowImage, /* Render */ NULL, /* Select Render */ NULL, /* Text */ NULL,NULL,NULL,NULL, /* Exclude, Special, ID, UserData */ WBTOOL, /* WBObject type */ NULL, /* Default tool */ NULL, /* Tool Types */ NO_ICON_POSITION, /* Current X */ NO_ICON_POSITION, /* Current Y */ NULL,NULL,NULL, /* Drawer, ToolWindow, Stack */ }; VOID main(), Cleanup(), DepthGadgetsHNone(), Expurge(), AddIconGadget(), HideWindow(), RevealWindow(), Recover(); VOID MyOpenWindow(), MyCloseWindow(), MyPutMsg(); struct IconGadget *AllocIGadget(), *FindIGadget(); /* Main() calls the initialisation functions (the startup window and the */ /* library hooks); scans the Intuition window list to patch all depth */ /* gadgets for already opened windows; and wait forever for icon messages. */ VOID main(argc, argv) UBYTE argc, **argv; { UBYTE mystring[80], filename[MAXFILELEN+10], comment[20]; ULONG mask, IconPortMsg; struct Screen *screen; struct Window *window, *iwindow; struct IntuiMessage *message; GfxBase = (struct GfxBase *)OpenLibrary("graphics.library", 0); if (!GfxBase) Cleanup(10); IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library", 0); if (!IntuitionBase) Cleanup(11); LayersBase = (struct LayersBase *)OpenLibrary("layers.library", 0); if (!LayersBase) Cleanup(12); IconBase = OpenLibrary("icon.library",0); if (!IconBase) Cleanup(13); if (strcmp(argv[1],"-f")) DisplayWindow(); IconTask = (struct Task *)FindTask(0); IconEvent = AllocSignal(-1); if (IconEvent == -1) Cleanup(21); IconPort = (struct MsgPort *)CreatePort(0, 0); /* The message port for */ if (!IconPort) Cleanup(22); /* where icon msgs are */ IconPortMsg = 1 << IconPort->mp_SigBit; /* posted. */ SetHooks(); HooksSet = TRUE; screen = IntuitionBase->FirstScreen; /* First system screen */ while (screen) { window = screen->FirstWindow; /* First system window */ while (window) { if (window->Title && (window->Flags & WINDOWDEPTH)) { AddIconGadget(window); } window = window->NextWindow; } screen = screen->NextScreen; } for(;;) { mask = Wait(SIGBREAKF_CTRL_C | IconPortMsg | IconEvent); if (mask & SIGBREAKF_CTRL_C) Cleanup(0); if (mask & IconEvent) while (message=(struct IntuiMessage *)GetMsg(IconPort)) ReplyMsg(message); if (mask & IconEvent) { Forbid(); iwindow = theSelectedWindow; HideWindow(iwindow->WScreen, iwindow); strcpy(mystring, iwindow->Title); mystring[MAXFILELEN]=0; Expurge(mystring); sprintf(filename, "ram:%s", mystring); if (!(file = (struct FileHandle *)Open(filename, MODE_NEWFILE))) { DisplayBeep(NULL); RevealWindow(iwindow->WScreen, iwindow); } else { Write(file, RevealCode, RevealCodeSize); Close(file); sprintf(comment, "%ld", iwindow); if (!SetComment(filename, comment)) Recover(filename, iwindow); else if (!PutDiskObject(filename, &theWindowIcon)) Recover(filename, iwindow); } Permit(); } } } /* Expurge() retrieves all special caracters used by AmigaDos from */ /* a string. This is used to purge window titles, for they are used as */ /* the file name for the icon. */ VOID Expurge(text) UBYTE *text; { while (*text) { if (*text == ':' || *text == '/' || *text == ' ') strcpy(text, text+1); else text++; } } /* This function is called when attempting to iconify fails. It just */ /* undoes what as already be done. Its sole purpose as a function is */ /* to evitate a "goto". */ VOID Recover(fname, window) UBYTE *fname; struct Window *window; { DeleteFile(fname); DisplayBeep(NULL); RevealWindow(window->WScreen, window); } /* This function adds the new depth gadgets to a window. Dynamic */ /* allocation is required as we use a linked list (nodes) for the */ /* modified windows. Instead of just allocating and filling the */ /* gadget structyre, we CopyMemQuick() it for speed. */ VOID AddIconGadget(window) struct Window *window; { struct IconGadget *node; Forbid(); node = AllocIGadget(); node->Window = window; DepthGadgetsHNone(node); CopyMemQuick(&theIconGadget, node->IconGadget, sizeof(struct Gadget)); if (window->Flags & GIMMEZEROZERO) node->IconGadget->GadgetType |= GZZGADGET; AddGadget(window, node->IconGadget, 0); RefreshGList(window->FirstGadget, window, NULL, 1); if (!window->UserPort) window->UserPort = IconPort; ModifyIDCMP(window, window->IDCMPFlags); Permit(); } /* AllocIGadget(): allocates an IconGadget. IconGadgets are actually a */ /* superset of the gadget structure. The linked list of allocated */ /* IconGadgets is the only way we know about which windows have been */ /* modified. */ struct IconGadget *AllocIGadget() { struct IconGadget *node; node = (struct IconGadget *) AllocMem(sizeof(struct IconGadget), MEMF_CLEAR); node->NextIGadget = AddedGadgets.NextIGadget; AddedGadgets.NextIGadget = node; node->IconGadget = (struct Gadget *) AllocMem(sizeof(struct Gadget), MEMF_CLEAR); return(node); } /* DepthGadgetsHNone() changes the position and size of the original depth */ /* gadgets so they can match the new imagery. The name of this function */ /* doesn't discribe it's meaning, it just there for historical reasons. */ VOID DepthGadgetsHNone(node) struct IconGadget *node; { struct Gadget *gadget; struct Window *window; window = node->Window; gadget = window->FirstGadget; while (gadget) { if (!gadget->TopEdge) if (gadget->LeftEdge == -28) { node->DepthGadget = gadget; gadget->LeftEdge = -20; gadget->Width = 18; } else if (gadget->LeftEdge == -52) gadget->Width = 17; gadget = gadget->NextGadget; } } /* HideWindow(). /* This is the key to window iconification. This call removes the window */ /* from the display without releasing any of it's attributes. This is */ /* sort of a visual trick: the layer of the window is made small (one */ /* pixel), and is moved to the lower rightmost position of the display. */ /* Thus the window is invisible, but it keeps it's rastport, bitmap, */ /* gadgets, etc... The only way the trick can be seen is by checking */ /* Intuition's window list, or by using a program such as DropShadow */ /* which highlights window levels. */ /* This screws up with programs messing with layers, of course, but */ /* they are pretty rare. */ /* */ /* One good question is: does this work the same way with screens?? */ VOID HideWindow(screen, window) struct Screen *screen; struct Window *window; { struct Layer *layer; struct Layer_Info *linfo; layer = window->RPort->Layer; linfo = layer->LayerInfo; SizeLayer(linfo, layer, 1-window->Width, 1-window->Height); MoveLayer(linfo, layer, screen->Width - window->LeftEdge - 1, screen->Height - window->TopEdge - 1); ShowTitle(screen, TRUE); MakeScreen(screen); } /* RevealWindow() is the exact opposition of HideWindow(). It resizes and */ /* puts back to its original position the window's layer. The nice thing */ /* about it is that we DO NOT need to give it the position where to move */ /* to: this is described by the window itself! */ /* */ /* This function is not actually called within THIS program. It's in the */ /* code for it's a good place for it to be. It is called by the "Reveal" */ /* program which is encoded in and written to disk by this program. */ VOID RevealWindow(screen, window) struct Screen *screen; struct Window *window; { struct Layer *layer; struct Layer_Info *linfo; layer = window->RPort->Layer; linfo = layer->LayerInfo; MoveLayer(linfo, layer, window->LeftEdge - screen->Width + 1, window->TopEdge - screen->Height + 1); SizeLayer(linfo, layer, window->Width-1, window->Height-1); WindowToFront(window); } /* This where a rom call to OpenWindow() ends to. If the new window has */ /* depth gadgets, than add an icon to it. */ VOID MyOpenWindow(window) struct Window *window; { if (window && window->Title && (window->Flags & WINDOWDEPTH)) AddIconGadget(window); } /* This is where a rom call to CloseWindow() comes before actually */ /* closing the window. The jump to the rom code is done when returning */ /* from this function (see hook.asm). */ /* If the window has been patched, then deallocate what was ealier */ /* given to it. */ VOID MyCloseWindow(window) struct Window *window; { struct IconGadget *node, *next; Forbid(); if (node = FindIGadget(window)) { next = node->NextIGadget; RemoveGadget(window, next->IconGadget); FreeMem(next->IconGadget, sizeof(struct Gadget)); if (window->UserPort==IconPort) window->UserPort = NULL; node->NextIGadget = next->NextIGadget; FreeMem(next, sizeof(struct IconGadget)); } Permit(); } /* MyPutMsg(). This is another trick. */ /* How can we get the messages from the icon gadgets? The only way I */ /* figured out is by trapping all the PutMsg() calls. Thus every time */ /* there's a PutMsg(), you first come here. */ /* Now we had to signal our main() that we recognized an icon gadget */ /* message. Doing this by a PutMsg wasn't possible: as the function is */ /* trapped, using it in the trap leads to an infinite loop. So we */ /* use a signal. */ VOID MyPutMsg(port, msg) struct MsgPort *port; struct IntuiMessage *msg; { if ( msg->Class == GADGETUP && ((struct Gadget *)(msg->IAddress))->GadgetID == ICONGADGET ) { theSelectedWindow = msg->IDCMPWindow; Signal(IconTask, IconEvent); } } /* FindIGadget() returns an IconGadget given a window pointer, i.e. */ /* finds back the allocated memory from an IntuiMessage IDCMPWindow */ /* field. */ struct IconGadget *FindIGadget(window) struct Window *window; { struct IconGadget *ig; ig = &AddedGadgets; while (ig->NextIGadget) { if (ig->NextIGadget->Window == window) return(ig); ig = ig->NextIGadget; } return(NULL); } /* Cleanup() de-patches all patched windows, closes everything and leaves. */ /* Currently called only when a "Break C" is sent to the task. */ VOID Cleanup(code) UBYTE code; { struct IconGadget *node, *next; struct IntuiMessage *msg; if (code>11) DisplayBeep(NULL); if (HooksSet) CleanHooks(); Forbid(); node = AddedGadgets.NextIGadget; while (node) { next = node->NextIGadget; RemoveGadget(node->Window, node->IconGadget); node->DepthGadget->LeftEdge = -28; node->DepthGadget->Width = 26; node->DepthGadget->NextGadget->Width = 24; RefreshGList(node->DepthGadget, node->Window, NULL, 2); FreeMem(node->IconGadget, sizeof(struct Gadget)); FreeMem(node, sizeof(struct IconGadget)); node = next; } Permit(); if (IconPort) { while(msg = (struct IntuiMessage *)GetMsg(IconPort)) ReplyMsg(msg); DeletePort(IconPort); } FreeSignal(IconEvent); if (IconBase) CloseLibrary(IconBase); if (GfxBase) CloseLibrary(GfxBase); if (IntuitionBase) CloseLibrary(IntuitionBase); if (LayersBase) CloseLibrary(LayersBase); exit(code); }