MODULE PBlanker;
(*$ StackChk:=FALSE LargeVars:=FALSE StackParms:=FALSE Volatile:=FALSE *)
(*$ RangeChk:=FALSE OverflowChk:=FALSE CaseChk:=FALSE NilChk:=FALSE *)
(* 18/19.5.91 Bernd Preusing, AMSoft *)
(* V 4.1 27.5.91 OffSprite sauberer *)
(* V 4.2 2.7.91 MouseBlanking auch bei Tastatur-Action! *)
(* V 4.3 7.7.91 Muss auch ab und zu mal ScreenToFront, falls ein
 *		Programm einen neuen Screen geoeffnet hat!
 *)

(* defaults:
  SECONDS=200
  MSECONDS=3
  CX_PRIORITY=0
  CX_POPKEY=ralt -caps f9
  CX_POPUP=YES
 *)

(* Nur die Teile zwischen vvv und ^^^ ändern! *)

FROM SYSTEM	IMPORT	ADR,ADDRESS,CAST,BITSET,LONGSET,TAG;
FROM Cio	IMPORT	sprintf;
FROM String	IMPORT	Compare;
FROM ExecSupport IMPORT	CreatePort, DeletePort;
IMPORT
   A:  Arts,
   DD:DosD,
   ED:ExecD,
   EL:ExecL,
   ID: IntuitionD,
   IL: IntuitionL,
   GrD: GraphicsD,
   GrL: GraphicsL,
   GtD:GadToolsD,
   GtL:GadToolsL,
   HW:Hardware,
   IE:InputEvent,
   CD:CommoditiesD,
   CL:CommoditiesL,
   CS:CommoditiesSupport,
   R;

CONST
  version=ADR("$VER: pblanker 37.04 (16-Jul-91) © Bernd Preusing\n\r");

  priorityToolType="CX_PRIORITY";
  popOnStartToolType="CX_POPUP";
  popKeyToolType="CX_POPKEY";

  (* vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv *)
  comName="pblanker";
  comTitle="PBlanker";
  comDescr="Screen & Mouse Blanker 37.04";
  cxDefaultPriority=0;
  defSeconds=200;
  defMSeconds=3;
  cxDefaultPopKey="ralt -caps f9";
  cxDefaultPopOnStart="YES";
  windowLeft=134;
  windowTop=64;
  windowWidth=350;
  windowHeight=60;
  windowInnerHeight=60;
  windowMinWidth=windowWidth;
  windowMaxWidth=windowWidth;
  windowMinHeight=windowHeight;
  windowMaxHeight=windowHeight;
  wFlags=ID.WindowFlagSet{ID.activate,ID.windowClose,
		ID.windowDrag,ID.windowDepth,ID.simpleRefresh};

  iFlags=ID.IDCMPFlagSet{ID.menuPick,ID.mouseButtons,ID.gadgetUp,ID.gadgetDown,
		ID.mouseMove,ID.closeWindow,ID.refreshWindow};

  popKeyID=86; (* warum? *)

  gadHide=1;
  gadDie=2;
  gadSecs=3;
  gadMSecs=4;

  menuHide=1;
  menuDie=2;

  (* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ *)

TYPE
  Str255=ARRAY [0..255] OF CHAR;
  Str255Ptr=POINTER TO Str255;

VAR
  (*$ LongAlign:=TRUE *)
  tBuff:ARRAY[1..10] OF LONGINT;
  cxport:ED.MsgPortPtr;
  broker:CD.CxObjPtr; (* Our broker *)
  hotkeybuff:Str255; (* for showing the user the hotkey *)
  mynb:CD.NewBroker;
  cxsignal:SHORTINT;
  csignal:SHORTINT;
  (* vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv *)
  mydesiredfont:=GrD.TextAttr{name:ADR("topaz.font"),ySize:8};
  (* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ *)

  (*$ LongAlign:=TRUE *)
  window:ID.WindowPtr;
  vi: ADDRESS;
  myscreen:ID.ScreenPtr;
  SprSavePtr,
  NoSprData:RECORD
    CASE :INTEGER OF
    | 0: adr:POINTER TO ARRAY [0..5] OF CARDINAL;
    | 1: hi,lo:CARDINAL;
    END;
  END;
  topborder:INTEGER;
  font:GrD.TextFontPtr;
  glist:ID.GadgetPtr;
  menu:ID.MenuPtr;
  mydi:ID.DrawInfoPtr;
  iport:ED.MsgPortPtr;
  (*$ LongAlign:=FALSE *)
  menuattached:BOOLEAN;
  IDCMPRefresh:BOOLEAN;
  isignal:SHORTINT;
  savewindow:RECORD left,top:INTEGER END;
  windowTitle:ARRAY[0..127] OF CHAR;

VAR
  (*$ LongAlign:=TRUE *)
  timeout,
  mtimeout,
  seconds,
  mseconds:LONGINT;
  secgad,
  msecgad:ID.GadgetPtr;
  s:ID.ScreenPtr;
  (*$ LongAlign:=FALSE *)
  blankerCommand:SHORTINT;
  blanked,
  mblanked:BOOLEAN;

CONST
  blank=1;
  unblank=2;
  mblank=3;
  unmblank=4;

PROCEDURE AdaptTimes;
VAR err:BOOLEAN;
BEGIN
  err:=FALSE;
  IF seconds<=0 THEN err:=TRUE; seconds:=defSeconds END;
  IF mseconds<=0 THEN err:=TRUE; mseconds:=defMSeconds END;
  IF mseconds>=seconds THEN err:=TRUE; seconds:=mseconds+1 END;
  IF err THEN IL.DisplayBeep(NIL) END;
END AdaptTimes;

PROCEDURE shutdownWindow(); FORWARD;

(* vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv *)

PROCEDURE setupCustomGadgets(VAR gad:ID.GadgetPtr);
VAR
  ng:GtD.NewGadget;
BEGIN
  ng.topEdge:=topborder+45;
  ng.leftEdge:=10;
  ng.width:=40;
  ng.height:=12;
  ng.gadgetText:=ADR("Hide");
  ng.textAttr:=ADR(mydesiredfont);
  ng.gadgetID:=gadHide;
  ng.flags:=GtD.NewGadgetFlagSet{};
  ng.visualInfo:=vi;
  gad:=GtL.CreateGadgetA(GtD.buttonKind,gad^,ng,TAG(tBuff,0));

  ng.leftEdge:=60;
  ng.gadgetText:=ADR("Quit");
  ng.gadgetID:=gadDie;
  gad:=GtL.CreateGadgetA(GtD.buttonKind,gad^,ng,TAG(tBuff,0));

  ng.flags:=GtD.NewGadgetFlagSet{GtD.placetextRight};
  ng.topEdge:=topborder+5;
  ng.width:=60;
  ng.height:=14;
  ng.gadgetID:=gadSecs;
  ng.gadgetText:=ADR("Screen Seconds");
  gad:=GtL.CreateGadgetA(GtD.integerKind,gad^,ng,TAG(tBuff,
     GtD.gtinNumber,	seconds,
     ID.stringaJustification, ID.ActivationFlagSet{ID.stringRight},
     GtD.gtinMaxChars,	4,
     0));
  secgad:=gad;
  ng.topEdge:=topborder+20;
  ng.gadgetID:=gadMSecs;
  ng.gadgetText:=ADR("Mouse Seconds");
  gad:=GtL.CreateGadgetA(GtD.integerKind,gad^,ng,TAG(tBuff,
     GtD.gtinNumber,	mseconds,
     ID.stringaJustification, ID.ActivationFlagSet{ID.stringRight},
     GtD.gtinMaxChars,	4,
     0));
  msecgad:=gad;
END setupCustomGadgets;

PROCEDURE HandleGadget(id:INTEGER; code:CARDINAL);
BEGIN
  CASE id OF
  | gadHide:
	shutdownWindow;
  | gadDie:
	A.Terminate;
  | gadSecs,gadMSecs: (* beide, falls nicht mit RET abgeschlossen! *)
	seconds:=CAST(ID.StringInfoPtr,secgad^.specialInfo)^.longInt;
	mseconds:=CAST(ID.StringInfoPtr,msecgad^.specialInfo)^.longInt;
  |ELSE
	(* A.BreakPoint(ADR("wrong gad id!"));*)
  END;
  AdaptTimes;
END HandleGadget;

TYPE
  NmArr=ARRAY[1..4] OF GtD.NewMenu;
VAR
  mynewmenu:=NmArr{
    GtD.NewMenu{type:GtD.nmTitle,label:ADR("Project")},
    GtD.NewMenu{type:GtD.nmItem, label:ADR("Hide"), commKey:ADR("H"),userData:menuHide},
    GtD.NewMenu{type:GtD.nmItem, label:ADR("Quit"), commKey:ADR("Q"),userData:menuDie},
    GtD.NewMenu{type:GtD.nmEnd}
  };

PROCEDURE setupCustomMenu();
BEGIN
  menu:=GtL.CreateMenusA(ADR(mynewmenu),TAG(tBuff,0));
END setupCustomMenu;

PROCEDURE handleCustomMenu(code:CARDINAL);
TYPE
  ExtMenuItemPtr=POINTER TO RECORD
    i:ID.MenuItem;
    userData:LONGINT;
  END;
VAR
  item:ExtMenuItemPtr;
  terminated:BOOLEAN;
BEGIN
  terminated:=FALSE;
  WHILE (code#ID.menuNull) & ~terminated DO
    item:=CAST(ExtMenuItemPtr,IL.ItemAddress(menu,code));
    CASE item^.userData OF
    | menuHide:
	shutdownWindow;
	terminated:=TRUE;
    | menuDie:
	A.Terminate;
    | ELSE (* strange! *)
        RETURN;
	(* A.BreakPoint(ADR("illegal menucode")); *)
    END;
    code:=item^.i.nextSelect;
  END;
END handleCustomMenu;

PROCEDURE refreshWindow();
VAR
BEGIN
  IF window#NIL THEN
    IF IDCMPRefresh THEN
      GtL.GTBeginRefresh(window)
    END;
    (* Own refresh here *)
    IF IDCMPRefresh THEN
      GtL.GTEndRefresh(window,TRUE);
    END;
    IF topborder+windowInnerHeight>window^.height THEN
      IL.RefreshWindowFrame(window)
    END;
  END;
END refreshWindow;


(*$ LoadA4:=TRUE Pardealloc:=FALSE *)
PROCEDURE BlankerAction(co:CD.CxObjPtr; cxm:CD.CxMsgPtr);
VAR
  ie{R.A3}:IE.InputEventPtr;
(*$ NilChk:=FALSE RangeChk:=FALSE OverflowChk:=FALSE StackChk:=FALSE *)
BEGIN
  ie:=CL.CxMsgData(cxm);
  (*
   * timer: hochzaehlen
   * mouse: unblank,unmblank
   * #mouse: mtimer hoch, mblank
   *)
  IF ie^.class=IE.timer THEN
    INC(timeout);
    INC(mtimeout);
    (* Bei Screenwechsel durch Tastatur oder Neu-Öffnen muß trotzdem
       periodisch geblankt werden. Eine Abrage
       "IF ~mblanked" ist also nicht korrekt!
       Ich moechte nicht in in OpenWindow,Screen patchen!!
    *)
    IF timeout>=seconds*10 THEN
      blankerCommand:=blank;
      EL.Signal(A.thisTask,LONGSET{csignal});
      blanked:=TRUE;
      mblanked:=TRUE;
      timeout:=0;
      mtimeout:=0;
    ELSIF mtimeout>=mseconds*10 THEN
      blankerCommand:=mblank;
      EL.Signal(A.thisTask,LONGSET{csignal});
      mblanked:=TRUE;
      mtimeout:=0;
    END;
  ELSE (* anderes Event *)
    IF blanked THEN
      blankerCommand:=unblank;
      EL.Signal(A.thisTask,LONGSET{csignal});
      blanked:=FALSE;
      mblanked:=FALSE;
      mtimeout:=0;
    ELSIF mblanked & (ie^.class=IE.rawmouse) THEN
      blankerCommand:=unmblank;
      EL.Signal(A.thisTask,LONGSET{csignal});
      mblanked:=FALSE;
      mtimeout:=0;
    END;
    timeout:=0;
    IF ie^.class#IE.rawkey THEN
      mtimeout:=0;
    END;
  END;
END BlankerAction;
(*$ POP NilChk POP RangeChk POP OverflowChk POP StackChk *)

PROCEDURE MBlank;
VAR ci{8}:GrD.CopinitPtr;
BEGIN
  ci:=GrL.graphicsBase^.copinit;
  IF SprSavePtr.adr=NIL THEN
    SprSavePtr.hi:=ci^.sprstrtup[1];
    SprSavePtr.lo:=ci^.sprstrtup[3];
  END;
  ci^.sprstrtup[1]:=NoSprData.hi;
  ci^.sprstrtup[3]:=NoSprData.lo;
END MBlank;

PROCEDURE UnMBlank;
VAR ci{8}:GrD.CopinitPtr;
BEGIN
  IF SprSavePtr.adr#NIL THEN
    ci:=GrL.graphicsBase^.copinit;
    ci^.sprstrtup[1]:=SprSavePtr.hi;
    ci^.sprstrtup[3]:=SprSavePtr.lo;
    SprSavePtr.adr:=NIL;
  END;
END UnMBlank;

PROCEDURE Blank;
VAR
  tBuff:ARRAY[1..10] OF LONGINT;
BEGIN
  IF s=NIL THEN
    s:=IL.OpenScreenTagList(NIL,TAG(tBuff,
      ID.saDepth,	1,
      ID.saWidth,	320,
      ID.saHeight,	20,
      ID.saQuiet,	TRUE,
      0));
    IF s#NIL THEN
      GrL.SetRGB4(ADR(s^.viewPort),0,0,0,0);
    END;
  ELSE
    IF s#IL.intuitionBase^.firstScreen THEN
      IL.ScreenToFront(s)
    END;
  END;
  MBlank;
END Blank;

PROCEDURE UnBlank();
BEGIN
  IF s#NIL THEN
    IL.CloseScreen(s);
    s:=NIL;
  END;
  UnMBlank;
END UnBlank;

PROCEDURE setupCustomCx():BOOLEAN;
VAR
  objectlist:CD.CxObjPtr;
BEGIN
  mseconds:=CS.ArgInt(ADR("MSECONDS"),defMSeconds);
  seconds:=CS.ArgInt(ADR("SECONDS"),defSeconds);
  AdaptTimes;
  objectlist:=CS.CxCustom(BlankerAction,0);
  IF CL.CxObjError(objectlist)#CD.CoErrorSet{} THEN
    CL.DeleteCxObjAll(objectlist);
    RETURN FALSE
  END;
  CL.AttachCxObj(broker,objectlist);
  RETURN TRUE; (* Error wird gleich gecheckt! *)
END setupCustomCx;

PROCEDURE handleCustomSignal;
BEGIN
  IF blankerCommand=unblank THEN
    UnBlank
  ELSIF blankerCommand=mblank THEN
    MBlank;
  ELSIF blankerCommand=blank THEN
    Blank
  ELSIF blankerCommand=unmblank THEN
    UnMBlank;
  END;
END handleCustomSignal;

(* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ *)

PROCEDURE getNewWindow():ID.WindowPtr;
VAR
  nw:ID.NewWindow;
  tBuff:ARRAY[0..6] OF LONGINT;
BEGIN
  sprintf(windowTitle,"%s: HotKey=%s",TAG(tBuff,ADR(comTitle),ADR(hotkeybuff)));
  nw.leftEdge:=savewindow.left;
  nw.topEdge:=savewindow.top;
  nw.width:=windowWidth;
  nw.height:=windowHeight;
  nw.detailPen:=-1;
  nw.blockPen:=-1;
  nw.idcmpFlags:=iFlags;
  nw.flags:=wFlags;
  nw.firstGadget:=NIL;
  nw.checkMark:=NIL;
  nw.title:=ADR(windowTitle);
  nw.screen:=NIL;
  nw.bitMap:=NIL;
  nw.minWidth:=windowMinWidth;
  nw.minHeight:=windowMinHeight;
  nw.maxWidth:=windowMaxWidth;
  nw.maxHeight:=windowMaxHeight;
  nw.type:=ID.ScreenFlagSet{ID.wbenchScreen};
  RETURN IL.OpenWindowTagList(ADR(nw),TAG(tBuff,
	ID.waInnerHeight, windowInnerHeight,
	ID.waAutoAdjust,  TRUE,
	ID.waPubScreen,   myscreen,
	0));
END getNewWindow;

PROCEDURE removeGadgets;
VAR
BEGIN
  IF glist#NIL THEN
    GtL.FreeGadgets(glist);
    glist:=NIL;
  END;
  IF font#NIL THEN
    GrL.CloseFont(font);
    font:=NIL
  END;
END removeGadgets;

PROCEDURE addGadgets(w:ID.WindowPtr):BOOLEAN;
VAR
  gad:ID.GadgetPtr;
BEGIN
  IF font=NIL THEN
    font:=GrL.OpenFont(ADR(mydesiredfont));
    IF font=NIL THEN RETURN FALSE END;
  END;
  gad:=GtL.CreateContext(glist);
  setupCustomGadgets(gad);
  IF gad=NIL THEN
    IF glist#NIL THEN
      GtL.FreeGadgets(glist);
      glist:=NIL;
    END;
    IF font#NIL THEN
      GrL.CloseFont(font);
      font:=NIL;
    END;
    RETURN FALSE
  END;
  IF IL.AddGList(window,glist,-1,-1,NIL)=0 THEN END;
  IL.RefreshGList(window^.firstGadget,window,NIL,-1);
  GtL.GTRefreshWindow(window,NIL);
  RETURN TRUE;
END addGadgets;

PROCEDURE setupWindow;
VAR
  tagBuff:LONGINT;
BEGIN
  IF window#NIL THEN
    IL.WindowToFront(window);
    RETURN
  END;
  tagBuff:=0;
  IF myscreen=NIL THEN
    myscreen:=IL.LockPubScreen(NIL);
    IF myscreen=NIL THEN RETURN END;
  END;
  IF mydi=NIL THEN
    mydi:=IL.GetScreenDrawInfo(myscreen);
    IF mydi=NIL THEN RETURN END;
  END;
  topborder:=myscreen^.wBorTop+INTEGER(myscreen^.font^.ySize)+1;
  IF vi=NIL THEN
    vi:=GtL.GetVisualInfoA(myscreen,ADR(tagBuff));
    IF vi=NIL THEN RETURN END;
  END;
  window:=getNewWindow();
  IF window=NIL THEN RETURN END;
  iport:=window^.userPort;
  isignal:=iport^.sigBit;
  IF ~addGadgets(window) THEN
    shutdownWindow;
    RETURN;
  END;
  setupCustomMenu;
  IF menu#NIL THEN
    IF ~GtL.LayoutMenusA(menu,vi,ADR(tagBuff)) THEN END;
  END;
  menuattached:=IL.SetMenuStrip(window,menu);
  refreshWindow;
END setupWindow;

PROCEDURE shutdownWindow();
VAR
BEGIN
  IF window=NIL THEN
    RETURN
  END;
  savewindow.left:=window^.leftEdge;
  savewindow.top:=window^.topEdge;
  IF menuattached THEN
    IL.ClearMenuStrip(window);
    menuattached:=FALSE;
  END;
  IF menu#NIL THEN
    GtL.FreeMenus(menu);
    menu:=NIL
  END;
  IL.CloseWindow(window);
  window:=NIL;
  iport:=NIL;
  isignal:=cxsignal; (* Achtung bei Wait() sonst Trap #14 ! *)

  removeGadgets;

  IF vi#NIL THEN
    GtL.FreeVisualInfo(vi);
    vi:=NIL;
  END;
  IF mydi#NIL THEN
    IL.FreeScreenDrawInfo(myscreen,mydi);
    mydi:=NIL
  END;
  IF myscreen#NIL THEN
    IL.UnlockPubScreen(NIL,myscreen);
    myscreen:=NIL
  END;
END shutdownWindow;

PROCEDURE handleIMsg(i:ID.IntuiMessagePtr);
VAR
  class:ID.IDCMPFlagSet;
  code:CARDINAL;
  gaddress:ID.GadgetPtr;
BEGIN
  class:=i^.class;
  code:=i^.code;
  gaddress:=CAST(ID.GadgetPtr,i^.iAddress);
  GtL.GTReplyIMsg(i);

  IF class=ID.IDCMPFlagSet{ID.closeWindow} THEN
    shutdownWindow;
  ELSIF class=ID.IDCMPFlagSet{ID.refreshWindow} THEN
    IDCMPRefresh:=TRUE;
    refreshWindow;
    IDCMPRefresh:=FALSE;
  ELSIF class=ID.IDCMPFlagSet{ID.menuPick} THEN
    handleCustomMenu(code);
  ELSIF class=ID.IDCMPFlagSet{ID.gadgetUp} THEN
    HandleGadget(gaddress^.gadgetID,code);
  END;
END handleIMsg;

PROCEDURE handleCxMsg(msg:ED.MessagePtr);
VAR
  msgid:LONGINT;
  msgtype:LONGCARD;
BEGIN
  msgid:=CL.CxMsgID(CAST(CD.CxMsgPtr,msg));
  msgtype:=CL.CxMsgType(CAST(CD.CxMsgPtr,msg));
  EL.ReplyMsg(msg);
  IF msgtype=CD.cxmIevent THEN
    IF msgid=popKeyID THEN
      setupWindow;
    END;
  ELSIF msgtype=CD.cxmCommand THEN
    IF msgid=CD.cxcmdDisable THEN
      IF CL.ActivateCxObj(broker,FALSE) THEN END;
    ELSIF msgid=CD.cxcmdEnable THEN
      IF CL.ActivateCxObj(broker,TRUE) THEN END;
    ELSIF (msgid=CD.cxcmdAppear) OR (msgid=CD.cxcmdUnique) THEN
      setupWindow;
    ELSIF msgid=CD.cxcmdDisappear THEN
      shutdownWindow;
    ELSIF msgid=CD.cxcmdKill THEN
      A.Terminate
    END;
  END; (* else nix? *)
END handleCxMsg;

PROCEDURE shutdownCx();
VAR
  msg:ED.MessagePtr;
BEGIN
  IF cxport#NIL THEN
    CL.DeleteCxObjAll(broker);
    LOOP
      msg:=EL.GetMsg(cxport);
      IF msg=NIL THEN EXIT END;
      EL.ReplyMsg(cxport);
    END;
    DeletePort(cxport);

    cxport:=NIL;
    cxsignal:=-1;
    CAST(ADDRESS,broker):=NIL;
  END;
END shutdownCx;


PROCEDURE setupCx():BOOLEAN;
VAR
  error:LONGCARD;
  str:Str255Ptr;
  coerr:CD.CoErrorSet;
BEGIN
  shutdownCx;
  cxport:=CreatePort(mynb.name,0);
  IF cxport=NIL THEN
    RETURN FALSE
  END;
  cxsignal:=cxport^.sigBit;
  mynb.pri:=CS.ArgInt(ADR(priorityToolType),cxDefaultPriority);
  mynb.port:=cxport;

  (*INCL(mynb.flags,CD.cofShowHide);*)
  mynb.flags:=CD.NewBrokerFlagSet{CD.cofShowHide};

  broker:=CL.CxBroker(mynb,error);
  IF broker=NIL THEN
    shutdownCx;
    RETURN FALSE
  END;

  str:=CS.ArgString(ADR(popKeyToolType),ADR(cxDefaultPopKey));
  CL.AttachCxObj(broker,CS.HotKey(str,cxport,popKeyID));
  hotkeybuff:=str^;

  IF ~setupCustomCx() THEN
    shutdownCx;
    RETURN FALSE
  END;

  coerr:=CL.CxObjError(broker);
  IF coerr#CD.CoErrorSet{} THEN
    shutdownCx;
    RETURN FALSE
  END;

  IF CL.ActivateCxObj(broker,TRUE) THEN END;
  RETURN TRUE;
END setupCx;

PROCEDURE main();
VAR
  sigrcvd:LONGSET;
  msg:ADDRESS;
  str:Str255Ptr;
BEGIN
  NoSprData.adr:=EL.AllocMem(12,ED.MemReqSet{ED.public,ED.chip,ED.memClear});
  IF NoSprData.adr=NIL THEN
    A.Terminate
  END;
  NoSprData.adr^[0]:=0FE00H;
  NoSprData.adr^[1]:=0FF00H;
  csignal:=EL.AllocSignal(-1);
  IF csignal=-1 THEN
    A.Terminate
  END;
  IF ~setupCx() THEN
    A.Terminate;
  END;
  str:=CS.ArgString(ADR(popOnStartToolType),ADR(cxDefaultPopOnStart));
  IF Compare(str^,"YES")=0 THEN
    setupWindow;
  END;
  LOOP (* exit by terminate *)
    sigrcvd:=EL.Wait(LONGSET{DD.ctrlE,isignal,cxsignal,csignal});
    IF DD.ctrlE IN sigrcvd THEN
      A.Terminate
    ELSIF csignal IN sigrcvd THEN
      handleCustomSignal
    END;
    LOOP
      IF cxport=NIL THEN EXIT END;
      msg:=EL.GetMsg(cxport);
      IF msg=NIL THEN EXIT END;
      handleCxMsg(msg);
    END;
    LOOP
      IF iport=NIL THEN EXIT END;
      msg:=GtL.GTGetIMsg(iport);
      IF msg=NIL THEN EXIT END;
      handleIMsg(msg);
    END;
  END; (* LOOP *)

END main;

PROCEDURE terminate();
BEGIN
  UnBlank;
  shutdownCx;
  shutdownWindow;
  IF csignal#-1 THEN
    EL.FreeSignal(csignal);
    csignal:=-1
  END;
  IF NoSprData.adr#NIL THEN
    EL.FreeMem(NoSprData.adr,12);
    NoSprData.adr:=NIL
  END;
END terminate;


BEGIN
  savewindow.left:=windowLeft;
  savewindow.top:=windowTop;
  csignal:=-1;
  mynb.version:=CD.nbVersion;
  mynb.name:=ADR(comName);
  mynb.title:=ADR(comTitle);
  mynb.descr:=ADR(comDescr);
  mynb.unique:=CD.nbuUnique+CD.nbuNotify;
  (* Rest bleibt NIL oder 0 *)
  main;


CLOSE
  terminate;


END PBlanker.
