(*---------------------------------------------------------------------------
    :Program.    Sleeper.mod
    :Author.     Christoph Dworzak
    :Address.    creaholic sa ; Molzgasse 10 ; 2502 BIEL ; Switzerland
    :Phone.      [(++41|0)32] 50 19 11
    :BBS/Fax.    [(++41|0)32] 50 19 15
    :BBS/ISDN.   [(++41|0)32] 50 19 19
    :Shortcut.   [CHD]
    :Version.    2.0
    :Date.       18.August 1994
    :Copyright.  by Ch. Dworzak
    :Language.   OBERON
    :Translator. Amiga Oberon Compiler V3.11d
    :Contents.   BeginIO patch of scsi.device to stop HD
    :Remark.     I am a member of:  FAB (First Amigaholics Biel)
    :Remark.     AUGS (Amiga User Group Switzerland)
    :Remark.     UseNet: dworz@amazing.link-ch1.ch
    :Remark.     SwissAmiNet: 44:8010/409.0
    :Remark.     AmigaNet: 39:110/409.0
 ---------------------------------------------------------------------------*)

MODULE Sleeper ;

(* $JOIN /txt/Sleeper.o *)


IMPORT
 e: Exec,
 d: Dos,
 kb: Keyboard,
 r: Requests,
 u: Utility,
 NoGuruRq,
 in: Intuition,
 ti: Timer,
 ic: Icon,
 gt: GadTools,
 wb: Workbench,
 sys: SYSTEM,
 scsi: SCSIDisk,
 conv: Conversions,
 sgt: SleeperGT ;



CONST
  Version    = "$VER: Sleeper 2.0 by Ch.Dworzak (18.8.94)" ;
  DefDevice  = "scsi.device" ;
  DefTimeout = 15 ;
  ColdReboot = -726 ;

TYPE
 Unit = RECORD
         timeout   : LONGINT ;
         monitored : BOOLEAN ;
         stopped   : BOOLEAN ;
         exists    : BOOLEAN ;
        END ;

VAR
 oColdReboot["oColdReboot"],
 oBeginIO["oBeginIO"]  : e.PROC ;
 MyTask["MyTask"]      : e.TaskPtr ;
 Semaphore["semaphore"]: ARRAY 8 OF e.SignalSemaphorePtr ;
 ExecBase["ExecBase"]  : e.ExecBasePtr ;
 U["unit"]             : ARRAY 8 OF e.UnitPtr ;
 watchdog["watchdog"]  : ARRAY 8 OF LONGINT ;
 InColdReboot["InColdReboot"],
 InResetHandler["InResetHandler"]
                       : LONGINT ;
 sem                   : ARRAY 8 OF e.SignalSemaphore ;
 timp, aimp, kbmp      : e.MsgPortPtr ;
 kbior                 : e.IOStdReqPtr ;
 ior                   : ARRAY 8 OF e.IOStdReqPtr ;
 timer                 : ti.TimeRequestPtr ;
 sc                    : scsi.SCSICmd ;
 flags                 : LONGSET ;
 newBeginIO, newColdReboot
                       : e.PROC ;
 scsiBase              : e.LibraryPtr ;
 quit, TimerOpen,
 iconOpen, KeyBoardOpen,
 haveResetHandler,
 iconified, windowOpen : BOOLEAN ;
 i                     : LONGINT ;
 unit                  : ARRAY 8 OF Unit ;
 ver, IconName         : e.STRING ;
 strptr                : e.STRPTR ;
 Icon                  : wb.DiskObjectPtr ;
 MyAppIcon             : wb.AppIconPtr ;
 MyMsg                 : wb.AppMessagePtr ;
 kbInt                 : e.InterruptPtr ;


PROCEDURE MyBeginIO {"MyBeginIO"} () ;

PROCEDURE MyColdReboot {"MyColdReboot"} () ;

PROCEDURE MyResetHandler {"MyResetHandler"} () ;

PROCEDURE OffGadget (ID : LONGINT ; off : BOOLEAN) ;
BEGIN
 IF off THEN
  gt.SetGadgetAttrs (sgt.SleeperGTGadgets[ID]^, sgt.SleeperGTWnd, NIL, in.gaDisabled, in.LTRUE, u.done) ;
 ELSE
  gt.SetGadgetAttrs (sgt.SleeperGTGadgets[ID]^, sgt.SleeperGTWnd, NIL, in.gaDisabled, in.LFALSE, u.done) ;
 END ;
END OffGadget ;


PROCEDURE CheckGadget (ID : LONGINT ; check : BOOLEAN) ;
BEGIN
 IF check THEN
  gt.SetGadgetAttrs (sgt.SleeperGTGadgets[ID]^, sgt.SleeperGTWnd, NIL, gt.cbChecked, in.LTRUE, u.done) ;
 ELSE
  gt.SetGadgetAttrs (sgt.SleeperGTGadgets[ID]^, sgt.SleeperGTWnd, NIL, gt.cbChecked, in.LFALSE, u.done) ;
 END ;
END CheckGadget ;


PROCEDURE SetLevel (ID : LONGINT ; level : LONGINT) ;
BEGIN
 gt.SetGadgetAttrs (sgt.SleeperGTGadgets[ID]^, sgt.SleeperGTWnd, NIL, gt.slLevel, level, u.done) ;
END SetLevel ;


PROCEDURE Show (): BOOLEAN ;
BEGIN
 IF iconOpen THEN
  IF wb.RemoveAppIcon (MyAppIcon) THEN END ;
  iconOpen := FALSE ;
 END ;
 IF NOT (iconified) THEN
  IF NOT (windowOpen) THEN
   IF (sgt.SetupScreen() = 0) AND (sgt.OpenSleeperGTWindow() = 0) THEN
    windowOpen := TRUE ;
   ELSE
    RETURN (FALSE) ;
   END ;
  END ;
  FOR i := 0 TO 7 DO
   SetLevel (3*i+sgt.GDSlid0, unit[i].timeout) ;
   CheckGadget (3*i+sgt.GDStop0, unit[i].stopped) ;
   CheckGadget (3*i+sgt.GDEn0, unit[i].monitored) ;
   OffGadget (3*i+sgt.GDStop0, NOT (unit[i].monitored)) ;
   OffGadget (3*i+sgt.GDSlid0, NOT (unit[i].monitored)) ;
   OffGadget (3*i+sgt.GDEn0, NOT (unit[i].exists)) ;
  END ;
 ELSE
  FOR i := 0 TO 7 DO
   IF NOT (unit[i].exists) THEN
    IconName[i] := '.' ;
   ELSIF NOT (unit[i].monitored) THEN
    IconName[i] := '=' ;
   ELSIF NOT (unit[i].stopped) THEN
    IconName[i] := '+' ;
   ELSE
    IconName[i] := '-' ;
   END ;
  END ;
  IconName[8] := 00X ;
  Icon^.type := 0 ;
  MyAppIcon := wb.AddAppIcon (0, 0, IconName, aimp, NIL, Icon, u.done) ;
  IF MyAppIcon = NIL THEN
   RETURN (FALSE) ;
  END ;
  iconOpen := TRUE ;
  IF windowOpen THEN
   sgt.CloseSleeperGTWindow;
   sgt.CloseDownScreen;
   windowOpen := FALSE ;
  END ;
 END ;
 RETURN (TRUE) ;
END Show ;


PROCEDURE HandleSleeperGT (): BOOLEAN ;
VAR
 imsg     : in.IntuiMessagePtr ;
 Code     : INTEGER ;
 Class    : LONGSET ;
 IAddress : e.APTR ;
 Gad      : in.GadgetPtr ;
 item     : in.MenuItemPtr ;
 repaint,
 ok       : BOOLEAN ;
BEGIN
 ok := TRUE ;
 repaint := FALSE ;
 LOOP
  imsg := gt.GetIMsg (sgt.SleeperGTWnd.userPort) ;
  IF imsg = NIL THEN EXIT END ;
  Class := imsg^.class ;
  Code := imsg^.code ;
  IAddress := imsg^.iAddress ;
  gt.ReplyIMsg (imsg) ;
  imsg := NIL ;

  IF in.closeWindow IN Class THEN
   ok := FALSE ;
  END;

  IF in.refreshWindow IN Class THEN
   gt.BeginRefresh (sgt.SleeperGTWnd) ;
   sgt.SleeperGTRender ;
   gt.EndRefresh (sgt.SleeperGTWnd, in.LTRUE) ;
  END ;

  IF in.gadgetUp IN Class THEN
   Gad := sys.VAL(in.GadgetPtr, IAddress) ;
   IF Gad^.gadgetID = sgt.GDiconify THEN
    iconified := TRUE ;
    repaint := TRUE ;
   ELSIF Gad^.gadgetID = sgt.GDOn THEN
    FOR i := 0 TO 7 DO
     IF unit[i].exists AND unit[i].monitored AND unit[i].stopped THEN
      watchdog[i] := 0 ;
     END ;
    END ;
   ELSIF Gad^.gadgetID = sgt.GDOff THEN
    FOR i := 0 TO 7 DO
     IF unit[i].exists AND unit[i].monitored AND NOT (unit[i].stopped) THEN
      watchdog[i] := unit[i].timeout*60 ;
     END ;
    END ;
   ELSE
    FOR i := 0 TO 7 DO
     IF Gad^.gadgetID = i*3 + sgt.GDSlid0 THEN
      unit[i].timeout := Code ;
     ELSIF Gad^.gadgetID = i*3 + sgt.GDStop0 THEN
      IF unit[i].stopped THEN
       watchdog[i] := 0 ;
      ELSE
       watchdog[i] := unit[i].timeout*60 ;
      END ;
     ELSIF Gad^.gadgetID = i*3 + sgt.GDEn0 THEN
      IF unit[i].monitored THEN
       IF unit[i].stopped THEN
        watchdog[i] := 0 ;
       END ;
      END ;
      unit[i].monitored := NOT (unit[i].monitored) ;
      repaint := TRUE ;
     END ;
    END ;
   END ;
  END ;
 END ;

 IF repaint AND NOT (Show ()) THEN
  ok := FALSE ;
 END ;

 RETURN (ok) ;
END HandleSleeperGT ;


PROCEDURE StopUnit (stop : BOOLEAN ; number : LONGINT) ;
VAR
 command: ARRAY 6 OF SHORTINT ;
BEGIN
 IF unit[number].exists THEN

  IF stop THEN
   e.ObtainSemaphore (sem[i]) ;
   unit[i].stopped := TRUE ;
  END ;

  command[0] := 1BH ; (* start/stop unit *)
  command[1] := 0 ;   (* Bit1: immediate *)
  command[2] := 0 ;
  command[3] := 0 ;
  command[4] := 1 ;   (* Bit1: start *)
  command[5] := 0 ;
  IF stop THEN
   command[4] := 0 ;
  END ;
  sc.data     := NIL ;
  sc.length   := 0 ;
  sc.command  := sys.ADR (command) ;
  sc.cmdLength:= SHORT (LEN (command)) ;
  sc.flags    := SHORTSET {scsi.read} ;
  ior[number].command := scsi.scsiCmd ;
  ior[number].length  := SIZE (sc) ;
  ior[number].data    := sys.ADR (sc);
  IF e.DoIO( ior[number] ) # 0 THEN END ;
  IF ior[number].error # 0 THEN
  END ;

  IF NOT (stop) THEN
   unit[i].stopped := FALSE ;
   watchdog[i] := 0 ;
   e.ReleaseSemaphore (sem[i]) ;
  END ;
 END ;
END StopUnit ;



BEGIN
 r.Assert (e.SysBase.libNode.version >= 37, "Sorry, need OS 2.0 or better !") ;
 ExecBase := e.SysBase ;

 ver := Version ;

 kbmp := e.CreateMsgPort () ;
 r.Assert (kbmp # NIL, "Sorry, could not CreateMsgPort for KeyBoard !") ;
 kbior := e.CreateIORequest (kbmp, SIZE (kbior^)) ;
 r.Assert (kbior # NIL, "Sorry, could not CreateIORequest for KeyBoard !") ;
 r.Assert (e.OpenDevice (kb.keyboardName, 0, kbior, LONGSET{}) = 0,
           "Sorry, could not OpenDevice KeyBoard !") ;
 KeyBoardOpen := TRUE ;

 NEW (kbInt) ;
 kbInt.node.name := sys.ADR(Version) ;
 kbInt.node.pri  := 32 ;
 kbInt.code      := MyResetHandler ;

 kbior.data    := kbInt ;
 kbior.command := kb.addResetHandler ;
 IF e.DoIO (kbior) # 0 THEN END ;
 haveResetHandler := TRUE ;

 MyTask := e.FindTask(NIL) ;

 FOR i := 0 TO 7 DO
  e.InitSemaphore (sem[i]) ;
  Semaphore[i] := sys.ADR (sem[i]) ;
 END ;

 timp := e.CreateMsgPort () ;
 r.Assert (timp # NIL, "Sorry, could not CreateMsgPort for Timer !") ;
 timer := e.CreateIORequest (timp, SIZE (timer^)) ;
 r.Assert (timer # NIL, "Sorry, could not CreateIORequest for Timer !") ;
 r.Assert (e.OpenDevice (ti.timerName, ti.vBlank, timer, LONGSET{}) = 0,
           "Sorry, could not OpenDevice Timer !") ;
 TimerOpen := TRUE ;

 FOR i := 0 TO 7 DO
  unit[i].timeout   := DefTimeout ;
  unit[i].monitored := TRUE ;
  unit[i].stopped   := FALSE ;
  unit[i].exists    := TRUE ;
 END ;

 Icon := ic.GetDiskObject ("ProgDir:Sleeper") ;
 r.Assert (Icon # NIL, "Sorry, can't find my Icon !") ;

 strptr := ic.FindToolType (Icon^.toolTypes, "Device") ;

 IF strptr = NIL THEN
  strptr := sys.ADR (DefDevice) ;
 END ;

 FOR i := 0 TO 7 DO
  ior[i] := e.CreateIORequest (timp, SIZE(ior[i]^)) ;
  IF (ior[i] # NIL) AND (e.OpenDevice (strptr^, i, ior[i], LONGSET{}) = 0) THEN
   U[i] := ior[i]^.unit ;
   scsiBase := ior[i].device ;
  ELSE
   e.DeleteIORequest (ior[i]) ;
   ior[i] := NIL ;
   unit[i].monitored := FALSE ;
   unit[i].exists    := FALSE ;
  END ;
 END ;
 r.Assert (scsiBase # NIL, "Sorry, could not open Device !") ;


 FOR i := 0 TO 7 DO
  IF unit[i].exists THEN
   CASE i OF
      0 : strptr := ic.FindToolType (Icon^.toolTypes, "Unit0")
    | 1 : strptr := ic.FindToolType (Icon^.toolTypes, "Unit1")
    | 2 : strptr := ic.FindToolType (Icon^.toolTypes, "Unit2")
    | 3 : strptr := ic.FindToolType (Icon^.toolTypes, "Unit3")
    | 4 : strptr := ic.FindToolType (Icon^.toolTypes, "Unit4")
    | 5 : strptr := ic.FindToolType (Icon^.toolTypes, "Unit5")
    | 6 : strptr := ic.FindToolType (Icon^.toolTypes, "Unit6")
    | 7 : strptr := ic.FindToolType (Icon^.toolTypes, "Unit7")
   END ;
   IF (strptr = NIL) OR NOT (conv.StringToInt (strptr^, unit[i].timeout)) THEN
    unit[i].monitored := FALSE ;
    unit[i].timeout := DefTimeout ;
   ELSE
    unit[i].timeout := unit[i].timeout ;
   END ;
  END ;
 END ;

 aimp := e.CreateMsgPort () ;
 r.Assert (aimp # NIL, "Sorry, could not CreateMsgPort for AppIcon !") ;

 strptr := ic.FindToolType (Icon^.toolTypes, "Iconified") ;
 iconified  := strptr # NIL ;
 windowOpen := FALSE ;
 iconOpen   := FALSE ;
 r.Assert (Show (), "Sorry, could not open Window !") ;


 oBeginIO    := e.SetFunction (scsiBase, e.beginIO, MyBeginIO) ;

 oColdReboot := e.SetFunction (ExecBase, ColdReboot, MyColdReboot) ;

 timer.node.command := ti.addRequest ;
 timer.time.secs    := 1 ;
 timer.time.micro   := 0 ;
 flags := e.SetSignal (LONGSET {}, LONGSET {timp.sigBit}) ;
 e.SendIO (timer) ;

 quit := FALSE ;

 REPEAT

  IF InColdReboot # 0 THEN
   quit := TRUE ;
  END ;

  IF InResetHandler # 0 THEN
   quit := TRUE ;
  END ;

  IF iconified THEN
   flags := e.Wait (LONGSET {timp.sigBit, aimp.sigBit, d.ctrlC}) ;
   IF aimp.sigBit IN flags THEN
    iconified := FALSE ;
    IF NOT (Show ()) THEN
     quit := TRUE ;
    END ;
   END ;
  ELSE
   flags := e.Wait (LONGSET {timp.sigBit, sgt.SleeperGTWnd.userPort.sigBit, d.ctrlC}) ;
   IF sgt.SleeperGTWnd.userPort.sigBit IN flags THEN
    IF NOT (HandleSleeperGT ()) THEN
     quit := TRUE ;
    END ;
   END ;
  END ;

  IF d.ctrlC IN flags THEN
   quit := TRUE ;
  END ;

  IF timp.sigBit IN flags THEN
   FOR i := 0 TO 7 DO
    IF (unit[i].exists) THEN
     IF (unit[i].stopped) THEN
      IF  (watchdog[i] < (unit[i].timeout*60)) THEN
       StopUnit (FALSE, i) ;
       IF NOT (Show ()) THEN
        quit := TRUE ;
       END ;
      END ;
     ELSE
      IF (unit[i].monitored) THEN
       INC (watchdog[i]) ;
       IF watchdog[i] >= (unit[i].timeout*60) THEN
        StopUnit (TRUE, i) ;
        IF NOT (Show ()) THEN
         quit := TRUE ;
        END ;
       END ;
      END ;
     END ;
    END ;
   END ;

   IF e.WaitIO (timer) # 0 THEN END ;
   timer.node.command := ti.addRequest ;
   timer.time.secs    := 1 ;
   timer.time.micro   := 0 ;
   flags := e.SetSignal (LONGSET {}, LONGSET {timp.sigBit}) ;
   e.SendIO (timer) ;
  END ;

 UNTIL quit ;

 IF e.CheckIO (timer)=NIL THEN
  e.AbortIO (timer) ;
  IF e.WaitIO (timer) # 0 THEN END ;
 END ;

 FOR i := 0 TO 7 DO
  IF (unit[i].monitored) AND (unit[i].stopped) THEN
   StopUnit (FALSE, i) ;
  END ;
 END ;

 newBeginIO := e.SetFunction (scsiBase, e.beginIO, oBeginIO) ;
 IF newBeginIO # MyBeginIO THEN  (* trouble!!! *)
  d.PrintF ("Cannot quit, someone else has patched the Device !\n") ;
  flags := e.Wait (LONGSET {}) ;
 END ;

 newColdReboot := e.SetFunction (ExecBase, ColdReboot, oColdReboot) ;
 IF newColdReboot # MyColdReboot THEN  (* trouble!!! *)
  d.PrintF ("Someone else has patched the ColdReboot !\n") ;
  flags := e.Wait (LONGSET {}) ;
 END ;

 IF InColdReboot # 0 THEN
  e.ColdReboot () ;
 END ;

 FOR i := 0 TO 7 DO
  e.ObtainSemaphore (sem[i]) ;
  e.ReleaseSemaphore (sem[i]) ;
 END ;

CLOSE

 IF TimerOpen THEN
  e.CloseDevice (timer) ;
 END ;

 IF timer # NIL THEN
  e.DeleteIORequest (timer) ;
 END ;

 IF  timp # NIL THEN
  e.DeleteMsgPort (timp) ;
 END ;

 FOR i := 0 TO 7 DO
  IF ior[i] # NIL THEN
   e.CloseDevice (ior[i]) ;
   e.DeleteIORequest (ior[i]) ;
  END ;
 END ;

 IF InResetHandler # 0 THEN
  kbior.data    := kbInt ;
  kbior.command := kb.resetHandlerDone ;
  IF e.DoIO (kbior) # 0 THEN END ;
 END ;

 IF haveResetHandler THEN
  kbior.data    := kbInt ;
  kbior.command := kb.remResetHandler ;
  IF e.DoIO (kbior) # 0 THEN END ;
 END ;

 IF KeyBoardOpen THEN
  e.CloseDevice (kbior) ;
 END ;

 IF kbior # NIL THEN
  e.DeleteIORequest (kbior) ;
 END ;

 IF  kbmp # NIL THEN
  e.DeleteMsgPort (kbmp) ;
 END ;

 IF windowOpen THEN
  sgt.CloseSleeperGTWindow;
  sgt.CloseDownScreen;
 END ;

 IF  aimp # NIL THEN
  e.DeleteMsgPort (aimp) ;
 END ;

 IF iconOpen THEN
  IF wb.RemoveAppIcon (MyAppIcon) THEN END ;
 END ;

 IF Icon # NIL THEN
  ic.FreeDiskObject (Icon) ;
 END ;
END Sleeper.
