home *** CD-ROM | disk | FTP | other *** search
/ Monster Media 1994 #1 / monster.zip / monster / PROG_PAS / NEWCAD.ZIP / NEWCAD.PAS
Pascal/Delphi Source File  |  1994-02-23  |  42KB  |  913 lines

  1. {$IFDEF Windows}
  2.   !! ERROR: This unit is not compatible with Windows applications !!
  3. {$ENDIF}
  4.  
  5. {$F+,O-}
  6. Unit NewCAD;  { New Ctrl-Alt-Del }
  7.  
  8.  {----------------------------------------------------------------------------}
  9.  {                                                                            }
  10.  { Unit NewCAD                         Improved Ctrl-Alt-Del Handler for BP7  }
  11.  { 1.00                                DOS Real and Protected Mode Programs   }
  12.  {----------------------------------------------------------------------------}
  13.  { Copyright (c) 1994 Ray Bernard Consulting and Design. All Rights Reserved. }
  14.  { Portions copyright (c) TurboPower Software 1987 - 1993 and used under      }
  15.  { Object Professional (tm) product license.  All Rights Reserved.            }
  16.  { Portions copyright (c) Sunny Hill Software 1985, 1986 and used under       }
  17.  { license to TurboPower software.  All Rights Reserved.                      }
  18.  {----------------------------------------------------------------------------}
  19.  {                                                                            }
  20.  { NOTICE                                                                     }
  21.  {                                                                            }
  22.  { For use by registered licensees of Object Professional according to the    }
  23.  { terms of TurboPower's Object Professional license.  This file may be       }
  24.  { distributed freely to Object Professional users.                           }
  25.  {                                                                            }
  26.  { Any Object Professional users should feel free to use this unit            }
  27.  { in any applications where it is appropriate.  NOTE: There are no           }
  28.  { guarantees expressed or implied here -- use completely at your own risk.   }
  29.  {                                                                            }
  30.  {                                                                            }
  31.  { PURPOSE                                                                    }
  32.  {                                                                            }
  33.  { This unit implements an improved Ctrl-Alt-Del handler in order to provide  }
  34.  { both the end user and the application programmer with more options than    }
  35.  { the standard abandonment of the program via warm reboot.                   }
  36.  {                                                                            }
  37.  { Ctrl-Alt-Del is usually invoked by the user when desiring to halt an       }
  38.  { application due to application crash, system crash or too lengthy an       }
  39.  { application task (including being stuck in an infinite loop).  Lack of     }
  40.  { application error trapping can result in infinite loops or program stuck   }
  41.  { points, sometimes at the point of interfaces to printers or other          }
  42.  { devices.                                                                   }
  43.  {                                                                            }
  44.  { Rebooting out of a program often results in unflushed file buffers,        }
  45.  { unclosed files, and incorrect or damaged disk directory entries.  In       }
  46.  { some instances the system is unstable at the point where Ctrl-Alt-Del      }
  47.  { is pressed, and no further program execution is possible.  But sometimes   }
  48.  { (as in the case of a program infinite loop) the application could perform  }
  49.  { exit routines successfully, if it could get unstuck to do so.  This        }
  50.  { capability can come in handy for programmers who are testing software.     }
  51.  {                                                                            }
  52.  { The improved Ctrl-Alt-Del handler provides two aids to better and safer    }
  53.  { program termination instead of (or prior to) a system reboot.  First of    }
  54.  { all, the user can select program termination as an option instead of       }
  55.  { system reboot.  Program termination is accompilshed by a call to the       }
  56.  { Halt procedure, which invokes the program's normal chain of exit           }
  57.  { procedures if any have been assigned.  For programs that are stuck in      }
  58.  { an infinite loop or unable to escape from a hardware interface point,      }
  59.  { program termination is the preferred option compared to system reboot.     }
  60.  {                                                                            }
  61.  { Secondly, for unstable situation where only limited processing is          }
  62.  { available before the system freezes completely, a special program exit     }
  63.  { procedure can be assigned which will be called prior to the call to        }
  64.  { Halt.  This allows the most important routines to be given an chance       }
  65.  { to execute first, independent of the exit procedure chain. Such critical   }
  66.  { tasks as closing files or toggling serial port handshake lines may have    }
  67.  { a better chance of executing under conditions of system instability, if    }
  68.  { they are executed prior to the applications normal exit procedure chain.   }
  69.  {                                                                            }
  70.  { In addition to the usual Ctrl-Alt-Del Warm Boot, a Cold Boot option is     }
  71.  { offered which is equivalent to a system reset.                             }
  72.  {                                                                            }
  73.  { This Ctrl-Alt-Del handler also has the major advantage of preventing an    }
  74.  { instant reboot when Ctrl-Alt-Del is pressed prematurely or accidentally.   }
  75.  {                                                                            }
  76.  {                                                                            }
  77.  { DESIGN                                                                     }
  78.  {                                                                            }
  79.  { This unit installs an Interrupt Service Routine (ISR) which replaces the   }
  80.  { standard keyboard ISR.  The replacement ISR checks for Ctrl-Alt-Del and    }
  81.  { simply passes on all other key presses.  When Ctrl-Alt-Del is pressed,     }
  82.  { the UserReboot procedure is called, which offers a menu of choices for     }
  83.  { the application user.                                                      }
  84.  {                                                                            }
  85.  {                                                                            }
  86.  { MENU                                                                       }
  87.  {                                                                            }
  88.  { Here is the menu invoked by Ctrl-Alt-Del under the new handler:            }
  89.  {                                                                            }
  90.  {                   ┌───────────────────────────────┐                        }
  91.  {                   │  Ctrl-Alt-Del Reboot Request  │                        }
  92.  {                   ├───────────────────────────────┤                        }
  93.  {                   │  Perform (W)arm Boot          │                        }
  94.  {                   │  Perform (C)old Boot          │                        }
  95.  {                   ├───────────────────────────────┤                        }
  96.  {                   │  No Boot, Just (Q)uit Program │                        }
  97.  {                   │  (Esc) - (R)eturn to Program  │                        }
  98.  {                   └───────────────────────────────┘                        }
  99.  {                                                                            }
  100.  { If Ctrl-Alt-Del is pressed again while the menu is displayed, the          }
  101.  { handler simply beeps and waits for a valid response.  If any other         }
  102.  { non-menu keys are pressed, the handle just "eats" them silently            }
  103.  { and waits for a valid keypress.  Menu choices are case insensitive.        }
  104.  {                                                                            }
  105.  { If Quit program is selected, the previous keyboard handler ISR is          }
  106.  { restored upon program exit.  If a special exit procedure has been          }
  107.  { set by the procedure SetCallBeforeProgramHaltProc, it will be called       }
  108.  { before calling Halt.                                                       }
  109.  {                                                                            }
  110.  { A special halt code can be set to cause the application to return a        }
  111.  { specific exit code to the operating system upon program termination.       }
  112.  { This is useful in situations where it is desireable to report that         }
  113.  { the program was halted through the Ctrl-Alt-Del handler.                   }
  114.  {                                                                            }
  115.  { SetPgmQuitOption can be used to disable or enable the "(Q)uit Program"     }
  116.  { option, which is enabled by default.  This is especially applicable to     }
  117.  { TSRs since they don't run as a foreground task and cannot subject to       }
  118.  { Halting like a foreground application.  If the Quit Program option is      }
  119.  { disabled, a different menu display is used:                                }
  120.  {                                                                            }
  121.  {                   ┌───────────────────────────────┐                        }
  122.  {                   │  Ctrl-Alt-Del Reboot Request  │                        }
  123.  {                   ├───────────────────────────────┤                        }
  124.  {                   │  Perform (W)arm Boot          │                        }
  125.  {                   │  Perform (C)old Boot          │                        }
  126.  {                   ├───────────────────────────────┤                        }
  127.  {                   │  (Esc) - (R)eturn to the      │                        }
  128.  {                   │  Currently Executing Program  │                        }
  129.  {                   └───────────────────────────────┘                        }
  130.  {                                                                            }
  131.  {                                                                            }
  132.  { SPECIAL ACTION                                                             }
  133.  {                                                                            }
  134.  { There is also a user-assignable Special Action procedure variable.         }
  135.  { When a procedure has been assigned via SetSpecialActionProc, an            }
  136.  { additional menu item will appear:                                          }
  137.  {                                                                            }
  138.  {                   ┌───────────────────────────────┐                        }
  139.  {                   │  Ctrl-Alt-Del Reboot Request  │                        }
  140.  {                   ├───────────────────────────────┤                        }
  141.  {                   │  Perform (W)arm Boot          │                        }
  142.  {                   │  Perform (C)old Boot          │                        }
  143.  {                   │  Perform (S)pecial Action     │                        }
  144.  {                   │  No Boot, Just (Q)uit Program │                        }
  145.  {                   │  (Esc) - (R)eturn to Program  │                        }
  146.  {                   └───────────────────────────────┘                        }
  147.  {                                                                            }
  148.  { This is mainly of use to programmers who wish to interrupt execution of    }
  149.  { the program a specific points to display data, write information to        }
  150.  { disk, or perform some other specific task.  To enable or disable           }
  151.  { this Special Action on demand, SetSpecialActionProc takes a boolean        }
  152.  { parameter CheckEnvFlag to determine whether or not it should look for      }
  153.  { a DOS environment variable SPECIALACTION.  When CheckEnvFlag is True,      }
  154.  { "Perform (S)pecial Action" will only display if the environment variable   }
  155.  { is present and set to "TRUE" or "YES".  The DOS commands to set this       }
  156.  { variable at the command prompt or in a batch file are"                     }
  157.  {                                                                            }
  158.  {     SET SPECIALACTION=TRUE   or  SET SPECIALACTION=YES                     }
  159.  {                                                                            }
  160.  { depending on how you wanted it set.  This provides a means of enabling     }
  161.  { or disabling this option at run-time.                                      }
  162.  {                                                                            }
  163.  {                                                                            }
  164.  { TIMEOUT                                                                    }
  165.  {                                                                            }
  166.  { If there is no response to the menu, the menu will time out and return     }
  167.  { as though the user pressed Esc.  Use the SetMenuTimeoutTics to change      }
  168.  { the timeout from its default value of about 20 seconds.                    }
  169.  {                                                                            }
  170.  {                                                                            }
  171.  { IMPLEMENTATION                                                             }
  172.  {                                                                            }
  173.  { To install the new handler requires a single call to TakeOverKbdInterrupt. }
  174.  { RestoreKbdInterrupt is called automatically by the unit's exit procedure,  }
  175.  { but may be explicitly called by the programmer to deinstall the            }
  176.  { Ctrl-Alt-Del handler at a point prior to program exit. RestoreKbdInterrupt }
  177.  { will do nothing if the keyboard interrupt is not taken over by NEWCAD's    }
  178.  { keyboard handler at the time RestoreKbdInterrupt is called.                }
  179.  {                                                                            }
  180.  { The user menu procedure, UserReboot, is interfaced so that it may be       }
  181.  { called from an application menu as well as by Ctrl-Alt-Del.                }
  182.  {                                                                            }
  183.  { The procedure SetShowKbdTakeover is used to cause Writeln messages to      }
  184.  { be written to the screen both when the Ctrl-Alt-Del keyboard handler is    }
  185.  { installed and when the previous handler is restored.  This is for use      }
  186.  { during application testing, to provide visual evidence of the              }
  187.  { installation and deinstallation of the replacement keyboard handler.       }
  188.  {                                                                            }
  189.  { Assignments to CallBeforeProgramHaltProc and SpecialActionProc can be      }
  190.  { cleared by passing NIL as the procedure parameter.                         }
  191.  {                                                                            }
  192.  { The WarmReboot and ColdReboot procedures are taken from the OpInline       }
  193.  { Reboot procedure.  The standard OpInt ISR support is used to implement     }
  194.  { the replacement keyboard handler.                                          }
  195.  {                                                                            }
  196.  {                                                                            }
  197.  { CONFLICTS                                                                  }
  198.  {                                                                            }
  199.  { There are many TSR programs that take over one or more system interrupts.  }
  200.  { Some application programs (like XyWrite) take over the keyboard handler    }
  201.  { and trap Ctrl-Alt-Del themselves.  You should test the use of this unit    }
  202.  { in the system environment in which you expect it to be used.               }
  203.  {                                                                            }
  204.  { This Ctrl-Alt-Del handler was written for operation in DOS text mode.      }
  205.  { If you intend to use it in graphics mode, you will have to revise          }
  206.  { or eliminate the screen display procedures since they are not compatible   }
  207.  { with graphics mode.                                                        }
  208.  {                                                                            }
  209.  { The Ctrl-Alt-Del handler is not meant to be used in a multi-tasking        }
  210.  { environment such as DesqView or Windows.                                   }
  211.  {                                                                            }
  212.  {                                                                            }
  213.  { TECHNICAL INFORMATION                                                      }
  214.  {                                                                            }
  215.  { This unit was written based on information in the Object Professional      }
  216.  { user manuals and the 1991 book PC INTERRUPTS by Ralph Brown and Jim Kyle.  }
  217.  { Thanks to the Object Professional library and the excellence of its        }
  218.  { documentation, this program was written and tested in a matter of hours    }
  219.  { instead of days.  The Object Professional ISR support made that possible.  }
  220.  {                                                                            }
  221.  {                                                                            }
  222.  { THE NEWCAD UNIT                                                            }
  223.  {                                                                            }
  224.  { Additional details can be seen from the source code and comments,          }
  225.  { contained in the file NEWCAD.PAS.  A simple but effective test program     }
  226.  { (TNEWCAD.PAS) is included in a commented section of the source code file.  }
  227.  {                                                                            }
  228.  { -Ray Bernard                                                               }
  229.  {  February 22, 1994                                                         }
  230.  {----------------------------------------------------------------------------}
  231.  {                                                                            }
  232.  { VERSION HISTORY                                                            }
  233.  {                                                                            }
  234.  { Tue  02-22-1994  Ray Bernard                                               }
  235.  {      Version 1.00 released for CIS TurboPower Forum library and the        }
  236.  {      TurboPower BBS.                                                       }
  237.  {                                                                            }
  238.  { Tue  02-22-1994  Ray Bernard                                               }
  239.  {      Version 1.01 released. Support for DOS environment variable           }
  240.  {      SPECIALACTION was in documentation but left out of unit.              }
  241.  {                                                                            }
  242.  {                                                                            }
  243.  {----------------------------------------------------------------------------}
  244.  {                                                                            }
  245.  { Please send any comments or suggestions for enhancement to:                }
  246.  {                                                                            }
  247.  { Ray Bernard                                                                }
  248.  { Ray Bernard Consulting and Design                                          }
  249.  { Glendale, CA                                                               }
  250.  { CIS: 73354,3325                                                            }
  251.  { Tel: 818-507-7017                                                          }
  252.  { Fax: 818-240-3394                                                          }
  253.  {                                                                            }
  254.  {----------------------------------------------------------------------------}
  255.  
  256.  
  257. (* ---TNEWCAD.PAS:  SAMPLE TEST PROGRAM FOR NEWCAD----------------------------
  258.  
  259. {$F+}
  260. program TNewCAD;  { Test the NewCAD unit in real or protected modes.}
  261.  
  262. {$IFDEF Windows}
  263.   !! ERROR: This program is not compatible with Windows applications !!
  264. {$ENDIF}
  265.  
  266. Uses
  267.   OpCrt,
  268.   OpString,
  269.   NewCAD;
  270.  
  271. const
  272.   _Esc = 283;
  273.  
  274. var
  275.   KeyCount : Word;
  276.  
  277.  
  278. procedure PromptUser; forward;   {so it can be called by TestSpecialActionProc}
  279. {-Prompt user with instructions, increment prompt counter}
  280.  
  281.  
  282. procedure TestProgramHaltProc;
  283. {-A procedure to test the execution of the assignable program exit procedure}
  284. begin
  285.   Writeln;
  286.   Writeln('Program perform-before-halt routines would be executed here.');
  287.   Writeln('Program ended by user selection from Ctrl-Alt-Del menu.');
  288. end;
  289.  
  290.  
  291. procedure TestSpecialActionProc;
  292. {-A procedure to test the execution of the assignable Special Action procedure}
  293. begin
  294.   Writeln;
  295.   Writeln('Special Action Memory Report:');
  296.   Writeln('MaxAvail is: ',MaxAvail,' bytes.');
  297.   Writeln('MemAvail is: ',MemAvail,' bytes.');
  298.   Writeln;
  299.   PromptUser;
  300. end;
  301.  
  302.  
  303. procedure PromptUser;
  304. {-Prompt user with instructions, increment prompt counter}
  305. begin
  306.   Inc(KeyCount);
  307.   Writeln(KeyCount:3,
  308.           '. Press any key ... Ctrl-Alt-Del to test ... Esc to Quit.');
  309.   if Odd(KeyCount) then               {make the Special Action available only }
  310.     SetSpecialActionProc(Nil)         {on even numbered prompts, as a means of}
  311.   else                                {testing the Special Action feature     }
  312.     SetSpecialActionProc(TestSpecialActionProc);
  313. end;
  314.  
  315.  
  316. {-Test out a number of the program features, displaying start and end messages}
  317. begin
  318.   KeyCount := 0;
  319.   Writeln;
  320.  
  321.  {$IFDEF DPMI}                              {show which mode we're running in}
  322.   Writeln('Test of New Ctrl-Alt-Del handler in protected mode.');
  323.  {$ELSE}
  324.   Writeln('Test of New Ctrl-Alt-Del handler in real mode.');
  325.  {$ENDIF}
  326.  
  327.   SetCheckEnvFlag(False);                      {set True to test this feature}
  328.   SetShowKbdTakeover(True);        {activate display of ISR install/deinstall}
  329.   if ParamCount > 0 then           {option for testing with/without exit proc}
  330.     if StUpCase(ParamStr(1)) = 'USEEXITPROC' then
  331.       SetCallBeforeProgramHaltProc(TestProgramHaltProc)     {set pgm exit proc}
  332.     else
  333.       if StUpCase(ParamStr(1)) = 'NOQUITPGM' then
  334.         SetPgmQuitOption(False);                                {test TSR Mode}
  335.  
  336.  { This is what makes it all happen: }
  337.   TakeOverKbdInterrupt;             {install new Ctrl-Alt-Del keyboard handler}
  338.  
  339.   Writeln;
  340.   repeat                                    {give test program something to do}
  341.     PromptUser;
  342.   until ReadKeyWord = _Esc;
  343.  
  344.   Writeln;
  345.   Writeln('Normal end of program.');                {verify normal termination}
  346. end.
  347.  
  348. ---------------------------------------------------------------------------  *)
  349.  
  350.  
  351.  
  352. {***} Interface {***}
  353.  
  354. type
  355.   ProcVarType = procedure;
  356.  
  357. procedure SetCADHaltValue(B : Byte);
  358. {-Allows application to set Exit Code for Ctrl-Alt-Del initiated halt}
  359.  
  360. procedure SetCheckEnvFlag(B : Boolean);
  361. {-Set Performing Special Action to follow the SPECIALACTION DOS env variable}
  362.  
  363. procedure SetShowKbdTakeover(B : Boolean);
  364. {-For application testing, writes message when new keyboard ISR installed}
  365.  
  366. procedure SetCallBeforeProgramHaltProc(P : ProcVarType);
  367. {-Set the procedure to be called just prior to halting program}
  368.  
  369. procedure SetSpecialActionProc(P : ProcVarType);
  370. {-Set the Special Action procedure to be called by menu selection}
  371.  
  372. procedure SetPgmQuitOption(B : Boolean);
  373. {-Enable or disable the "No Boot, Just (Q)uit Program" menu item}
  374.  
  375. procedure SetMenuTimeoutTics(T : LongInt);
  376. {-Set the number of timer tics the menu will wait before timing out}
  377.  
  378. procedure TakeOverKbdInterrupt;
  379. {-Take over the keyboard interrupt to replace default Ctrl-Alt-Delete response}
  380.  
  381. procedure RestoreKbdInterrupt;
  382. {-Restore the keyboard interrupt if we took it over}
  383.  
  384. procedure UserReboot;
  385. {-Give user choices and if halting or rebooting      }
  386. { execute the appplicatio's assigned halt procedure. }
  387.  
  388. procedure WarmReboot;  { based upon OpInline's Reboot }
  389. {-Perform soft reboot equivalent to usual Ctrl-Alt-Del}
  390. {$IFNDEF Dpmi} {for real mode only; see implementation for pmode version}
  391.   inline(
  392.     $B8/$40/$00/             {mov ax,$40}
  393.     $8E/$D8/                 {mov ds,ax}
  394.     $C7/$06/$72/$00/$34/$12/ {mov word ptr [$0072],$1234}
  395.     $EA/$00/$00/$FF/$FF);    {jmp far $FFFF:$0000}
  396. {$ENDIF}
  397.  
  398. procedure ColdReboot;  { based upon OpInline's Reboot }
  399. {-Perform a reset (the one that starts the memory check)}
  400. {$IFNDEF Dpmi} {for real mode only; see implementation for pmode version}
  401.   inline(
  402.     $B8/$40/$00/             {mov ax,$40}
  403.     $8E/$D8/                 {mov ds,ax}
  404.     $C7/$06/$72/$00/$00/$00/ {mov word ptr [$0072],$0000}
  405.     $EA/$00/$00/$FF/$FF);    {jmp far $FFFF:$0000}
  406. {$ENDIF}
  407.  
  408.  
  409. {***} Implementation {***}
  410.  
  411. Uses
  412.   DOS,
  413.   OpInt,
  414.   OpCrt,
  415.   OpColor,
  416.   OpString,
  417. {$IFDEF DPMI }
  418.   DPMI,
  419. {$ENDIF}
  420.   OpInline;
  421.  
  422. const
  423.   X1 = 24;     { X and Y coordinates of Reboot window which are  }
  424.   X2 = 56;     { used for display and to set size of save buffer }
  425.   Y1 =  8;
  426.   Y2 = 16;
  427.  
  428.   BiosDataSeg = $0040;                               { Bios Data Area segment }
  429.   KbdStatusOfs = $0017;                      { offset of Keyboard Status Byte }
  430.   BiosDataTicsOfs = $006C;                        { offset of Bios Timer Tics }
  431.  
  432.   CheckEnvFlag    : Boolean = False;    { if True check DOS env SPECIALACTION }
  433.   PgmQuitOK       : Boolean = True;     { flag for enable/disable quit option }
  434.   MenuTimeoutTics : Longint = 18*20;    { default timeout of about 10 seconds }
  435.  
  436.   MenuTextAttr : Byte = WhiteOnRed;        { change these constants to change }
  437.   MenuHighAttr : Byte = YellowOnRed;       {  the menu display colors         }
  438.   MenuTextMono : Byte = LtGrayOnBlack;      { these are the monochrome values }
  439.   MenuHighMono : Byte = WhiteOnBlack;       {  to be automatically assigned   }
  440.  
  441.   ScrBufAllocated  : Boolean = False;     { has screen buffer been allocated? }
  442.   WinSaved         : Boolean = false;   { has screen below window been saved? }
  443.  
  444.   Halting   : Boolean = False;   { flag to activate pgm exit proc msg display }
  445.   HaveKbdInterrupt : Boolean = False;     { keep track of interrupt ownership }
  446.   KeyBInt          = $09;         { the keyboard interrupt we are taking over }
  447.   KbdIntIsrHandle  = 16;                    { recommended ISR handle starting }
  448.                                             { number per the OPRO manual      }
  449.  
  450. type
  451.   ScrBuf =
  452.     array[X1..X2,Y1..Y2,1..2] of Byte;   { buffer to save what's under window }
  453.  
  454.   BootRequestType =                               { user selectable responses }
  455.     (NoBoot,QuitProgram,WarmBoot,ColdBoot,SpecialAction);
  456.            
  457.   GetKeyType =
  458.     record                                        { convenient variant record }
  459.       case Flag : Byte of                         { for handling keys codes,  }
  460.         0 : (Value : Word);                       { access to word value, ch  }
  461.         1 : (Ch : Char; ScanCode : Byte);         { or scan code              }
  462.       end;
  463.  
  464. const
  465.     Scan_C = 46;                     { scan codes for keys we want to process }
  466.     Scan_Q = 16;
  467.     Scan_R = 19;
  468.     Scan_S = 31;
  469.     Scan_W = 17;
  470.     Scan_Del = 83;
  471.     Scan_Esc = 1;
  472.  
  473. var
  474.   Key          : GetKeyType;                    { see type declarations above }
  475.   BootRequest  : BootRequestType;               { see type declarations above }
  476.   InUserReboot : Boolean;                 { global flag to prevent reentrancy }
  477.  
  478.   ScrBufPtr    : ^ScrBuf;  { don't use up data segment, allocate with pointer }
  479.   BufPtr       : Pointer absolute ScrBufPtr;  { SaveWindow required parameter }
  480.  
  481.   BiosTics     : ^LongInt;      { unit init sets to Bios Data Area timer tics }
  482.  
  483.  
  484. const
  485.   CADHaltValue    : Byte = 0;        { To be be set by application if desired }
  486.   ShowKbdTakeover : Boolean = False;     { for application testing see:       }
  487.                                          { procedure TakeOverKbdInterrupt     }
  488.  
  489. var
  490.   CallBeforeProgramHaltProc : ProcVarType;
  491.    { Initialized to nil, you may assign your own procedure to it using   }
  492.    { SetCallBeforeProgramHaltProc to close files or do some required     }
  493.    { tasks before the program is halted or the system is rebooted.       }
  494.  
  495.   SpecialActionProc : ProcVarType;
  496.    { Initialized to nil, you may assign your own procedure to it using   }
  497.    { SetSpecialActionProc to perform a task via the Ctrl-Alt-Del menu    }
  498.    { selection "Perform (S)pecial Action".                               }
  499.  
  500.  
  501. {$IFDEF Dpmi}
  502. procedure WarmReboot;
  503. {-Perform soft reboot equivalent to usual Ctrl-Alt-Del}
  504. var
  505.   Regs : DPMIRegisters;
  506. begin
  507.   Word(Ptr(Seg0040, $72)^) := $1234;   { magic number for soft reboot }
  508.   FillChar(Regs, SizeOf(Regs), 0);
  509.   Regs.CS := $FFFF;
  510.   Regs.IP := $0000;
  511.   if CallFarRealModeProc(0, nil, Regs) <> 0 then ;
  512. end;
  513. {$ENDIF}
  514.  
  515.  
  516. {$IFDEF Dpmi}
  517. procedure ColdReboot;
  518. {-Perform a reset (the one that starts the memory check)}
  519. var
  520.   Regs : DPMIRegisters;
  521. begin
  522.   Word(Ptr(Seg0040, $72)^) := $0000;   { value for full reset }
  523.   FillChar(Regs, SizeOf(Regs), 0);
  524.   Regs.CS := $FFFF;
  525.   Regs.IP := $0000;
  526.   if CallFarRealModeProc(0, nil, Regs) <> 0 then ;
  527. end;
  528. {$ENDIF}
  529.  
  530.  
  531. procedure SetCADHaltValue(B : Byte);
  532. {-Allows application to set Exit Code for Ctrl-Alt-Del initiated halt}
  533. begin
  534.   CADHaltValue := B;
  535. end;
  536.  
  537.  
  538. procedure SetCheckEnvFlag(B : Boolean);
  539. {-Set Performing Special Action to follow the SPECIALACTION DOS env variable}
  540. begin
  541.   CheckEnvFlag := B;
  542. end;
  543.  
  544. procedure SetShowKbdTakeover(B : Boolean);
  545. {-For application testing, writes message when new keyboard ISR installed}
  546. begin
  547.   ShowKbdTakeover := True;
  548. end;
  549.  
  550.  
  551. procedure SetCallBeforeProgramHaltProc(P : ProcVarType);
  552. {-Set the procedure to be called just prior to halting program}
  553. begin
  554.   CallBeforeProgramHaltProc := P;
  555. end;
  556.  
  557.  
  558. procedure SetSpecialActionProc(P : ProcVarType);
  559. {-Set the Special Action procedure to be called by menu selection}
  560. begin
  561.   SpecialActionProc := P;
  562. end;
  563.  
  564.  
  565. function ProgramHaltProcInstalled : Boolean;
  566. {-Return True if procedure variable is not nil}
  567. begin
  568.   ProgramHaltProcInstalled := @CallBeforeProgramHaltProc <> nil;
  569. end;
  570.  
  571.  
  572. function SpecialActionProcInstalled : Boolean;
  573. {-Return True if procedure variable is not nil}
  574. begin
  575.   SpecialActionProcInstalled := @SpecialActionProc <> nil;
  576. end;
  577.  
  578.  
  579. procedure SetPgmQuitOption(B : Boolean);
  580. {-Enable or disable the "No Boot, Just (Q)uit Program" menu item}
  581. begin
  582.   PgmQuitOK := B;
  583. end;
  584.  
  585.  
  586. procedure SetMenuTimeoutTics(T : LongInt);
  587. {-Set the number of timer tics the menu will wait before timing out}
  588. begin
  589.   MenuTimeoutTics := T;
  590. end;
  591.  
  592.  
  593. function GetCurrentTics : LongInt;
  594. {-Return current Bios Data Area timer tics}
  595. begin
  596.   GetCurrentTics := BiosTics^;
  597. end;
  598.  
  599.  
  600. procedure PerformProgramHaltProc;
  601. {-Perform assigned program halt tasks}
  602. begin
  603.   if ProgramHaltProcInstalled then
  604.     CallBeforeProgramHaltProc;
  605. end;
  606.  
  607.  
  608. procedure PerformSpecialActionProc;
  609. {-Perform special action procedure}
  610. begin
  611.   if SpecialActionProcInstalled then
  612.     SpecialActionProc;
  613. end;
  614.  
  615.  
  616. procedure RestoreKbdInterrupt;
  617. {-Restore the keyboard interrupt if we took it over}
  618. begin
  619.   if HaveKbdInterrupt then
  620.     begin
  621.       HaveKbdInterrupt := False;
  622.       RestoreVector(KbdIntIsrHandle);
  623.       if ShowKbdTakeover then
  624.         Writeln(#10#13,'Keyboard handler has been restored.');
  625.     end
  626.   else
  627.     if ShowKbdTakeover then
  628.       Writeln(#10#13,'No need to restore Keyboard handler ... ',
  629.                       'program did not have control.');
  630. end;
  631.  
  632.  
  633. function SpecialActionOkay : Boolean;
  634. {-Return True unless CheckEnvFlag is True and DOS environment }
  635. { variable SPECIALACTION is not set to YES or TRUE.           }
  636. var
  637.   S : String;
  638. begin
  639.   SpecialActionOkay := True;
  640.   if not CheckEnvFlag then
  641.     Exit;
  642.   S := 'FALSE';
  643.   S := StUpCase(GetEnv('SPECIALACTION'));
  644.   SpecialActionOkay := (S = 'TRUE') or (S = 'YES');
  645. end;
  646.  
  647.  
  648. procedure SoundBlip;
  649. {-Blip for repeated Ctrl-Alt-Del keypresses}
  650. begin
  651.   Sound(1600);
  652.   Delay(100);
  653.   NoSound;
  654. end;
  655.  
  656.  
  657. procedure DrawWindowFrame;
  658. {-Draw the window frame to prepare for subsequent message display}
  659. begin
  660.   FastWrite('┌───────────────────────────────┐',Y1+0,X1,MenuTextAttr);
  661.   FastWrite('│  Ctrl-Alt-Del Reboot Request  │',Y1+1,X1,MenuTextAttr);
  662.   FastWrite('├───────────────────────────────┤',Y1+2,X1,MenuTextAttr);
  663.   FastWrite('│                               │',Y1+3,X1,MenuTextAttr);
  664.   FastWrite('│                               │',Y1+4,X1,MenuTextAttr);
  665.   FastWrite('├───────────────────────────────┤',Y1+5,X1,MenuTextAttr);
  666.   FastWrite('│                               │',Y1+6,X1,MenuTextAttr);
  667.   FastWrite('│                               │',Y1+7,X1,MenuTextAttr);
  668.   FastWrite('└───────────────────────────────┘',Y1+8,X1,MenuTextAttr);
  669. end;
  670.  
  671.  
  672. procedure ShowNewCADMenu;
  673. {-Add the new Ctrl-Alt-Del menu items to the window}
  674. begin
  675.   DrawWindowFrame;
  676.   FastWrite('│  Perform (W)arm Boot          │',Y1+3,X1,MenuTextAttr);
  677.   FastWrite('│  Perform (C)old Boot          │',Y1+4,X1,MenuTextAttr);
  678.   if PgmQuitOK then
  679.     begin
  680.       FastWrite('│  No Boot, Just (Q)uit Program │',Y1+6,X1,MenuTextAttr);
  681.       FastWrite('│  (Esc) - (R)eturn to Program  │',Y1+7,X1,MenuTextAttr);
  682.     end
  683.   else
  684.     begin
  685.       FastWrite('│  (Esc) - (R)eturn to the      │',Y1+6,X1,MenuTextAttr);
  686.       FastWrite('│  Currently Executing Program  │',Y1+7,X1,MenuTextAttr);
  687.     end;
  688.   if SpecialActionProcInstalled and SpecialActionOkay then
  689.     begin
  690.       FastWrite('│  Perform (S)pecial Action     │',Y1+5,X1,MenuTextAttr);
  691.       FastWrite('(S)',  Y1+5,X1+11,MenuHighAttr);
  692.     end;
  693.   FastWrite('(W)',  Y1+3,X1+11,MenuHighAttr);
  694.   FastWrite('(C)',  Y1+4,X1+11,MenuHighAttr);
  695.   if PGmQuitOK then
  696.     begin
  697.       FastWrite('(Q)',  Y1+6,X1+17,MenuHighAttr);
  698.       FastWrite('(Esc)',Y1+7,X1+ 3,MenuHighAttr);
  699.       FastWrite('(R)',  Y1+7,X1+11,MenuHighAttr);
  700.     end
  701.   else
  702.     begin
  703.       FastWrite('(Esc)',Y1+6,X1+ 3,MenuHighAttr);
  704.       FastWrite('(R)',  Y1+6,X1+11,MenuHighAttr);
  705.     end;
  706. end;
  707.  
  708.  
  709. procedure Notify(Msg : String);
  710. var
  711.   X,Y : byte;
  712. begin
  713.   DrawWindowFrame;
  714.   X := X2 - (X2-X1) div 2;                     { find middle column of window }
  715.   Y := Y1+3;                        { use 3rd line of window for display line }
  716.   if Halting and ProgramHaltProcInstalled then
  717.     begin
  718.       Dec(X,10);                                     { adjust to starting col }
  719.       FastWrite('Performing tasks then',Y,X,MenuHighAttr);
  720.       Inc(X,10);                                      { restore to middle col }
  721.       Inc(Y);                                        { increment display line }
  722.     end;
  723.   Dec(X,Length(Msg) div 2);                          { adjust to starting col }
  724.   FastWrite(Msg,Y,X,MenuHighAttr);
  725.   Delay(2000);
  726. end;
  727.  
  728.  
  729. procedure RestoreScreen;
  730. {-Restore screen area under Ctrl-Alt-Del menu window}
  731. begin
  732.   if WinSaved then
  733.     RestoreWindow(X1,Y1,X2,Y2,not ScrBufAllocated,BufPtr);
  734.   WinSaved := False;
  735. end;
  736.  
  737.  
  738. procedure UserReboot;
  739. {-Give user choices and if halting or rebooting      }
  740. { execute the appplicatio's assigned halt procedure. }
  741. var
  742.   MenuStartTics : LongInt;            { record current tics for timeout check }
  743. begin
  744.   InUserReboot := True;   { set reentrancy flag }
  745.   BootRequest  := NoBoot; { set a default response }
  746.   Halting      := False;  { set initial value }
  747.   WinSaved := SaveWindow(X1,Y1,X2,Y2,not ScrBufAllocated,BufPtr);
  748.   ShowNewCADMenu;
  749.   MenuStartTics := GetCurrentTics;
  750.  
  751.   Key.Value := 0;
  752.   repeat                                                   { get menu choices }
  753.     if GetCurrentTics < MenuStartTics then   { cheap way of handling midnight }
  754.       MenuStartTics := GetCurrentTics;
  755.     if GetCurrentTics >= MenuStartTics + MenuTimeoutTics then
  756.       Key.ScanCode := Scan_Esc;                             { set per timeout }
  757.     if KeyPressed then
  758.       Key.Value := ReadKeyWord;
  759.   until (Key.ScanCode in [Scan_Esc,Scan_C,Scan_Q,Scan_R,Scan_S,Scan_W]);
  760.  
  761.   case Key.ScanCode of
  762.     Scan_W : BootRequest := WarmBoot;
  763.     Scan_C : BootRequest := ColdBoot;
  764.     Scan_Q : BootRequest := QuitProgram;
  765.     Scan_S : BootRequest := SpecialAction;
  766.   Scan_Esc,
  767.     Scan_R : BootRequest  := NoBoot;
  768.   end; {case}
  769.  
  770.   case BootRequest of                                   { handle menu choices }
  771.          NoBoot: ;  { do nothing }
  772.        ColdBoot:  begin
  773.                     Halting := True; { set flag to display "Performing Tasks" }
  774.                     Notify('Performing Cold Boot...');
  775.                     RestoreScreen;
  776.                     PerformProgramHaltProc;
  777.                     ColdReboot;
  778.                   end;
  779.        WarmBoot:  begin
  780.                     Halting := True; { set flag to display "Performing Tasks" }
  781.                     Notify('Performing Warm Boot...');
  782.                     RestoreScreen;
  783.                     PerformProgramHaltProc;
  784.                     WarmReboot;
  785.                   end;
  786.  SpecialAction :  if SpecialActionProcInstalled and SpecialActionOkay then
  787.                     begin
  788.                       Notify('Performing Special Action...');
  789.                       RestoreScreen;        { restore prior to Special Action }
  790.                       PerformSpecialActionProc;
  791.                       { do not exit - must clear reentrancy flag }
  792.                     end;
  793.     QuitProgram:
  794.                   if PgmQuitOK then
  795.                     begin
  796.                       Halting := True;   { flag to display "Performing Tasks" }
  797.                       Notify(' Quitting Program... ');
  798.                       RestoreScreen;
  799.                       PerformProgramHaltProc;
  800.                       Halt(CADHaltValue);  { will invoke exit procedure chain }
  801.                     end;                   { see TP/BP help and manuals       }
  802.   end; {case}
  803.  
  804.   RestoreScreen; { may not have been called yet }
  805.   InUserReboot := False; { clear reentrancy flag }
  806. end;
  807.  
  808.  
  809.  
  810. procedure KbdIsr(BP : Word); Interrupt;
  811. {-Services keyboard interrupt and calls UserReboot if Ctrl-Alt-Del was }
  812. { pressed, otherwise just passes control to previous Keyboard ISR.     }
  813. var
  814.   Regs : IntRegisters absolute BP;
  815. const
  816.   KbdInPort   =   $60;    { Keyboard input port }
  817.   KbdOutPort  =   $61;    { Keyboard output port for clear/enable }
  818.   Alt_Mask    =   $08;    { bit mask for alt key status }
  819.   Ctrl_Mask   =   $04;    { bit mask for ctrl key status }
  820. var
  821.   B : byte;
  822.   KbdStatusBytePtr : ^Byte;  { point to kbd status byte in real or pmode }
  823. begin
  824.  {$IFDEF DPMI }
  825.   KbdStatusBytePtr := Ptr(BiosDataSele,KbdStatusOfs);
  826.  {$ELSE}
  827.   KbdStatusBytePtr := Ptr(BiosDataSeg,KbdStatusOfs);
  828.  {$ENDIF}
  829.   B := KbdStatusBytePtr^;
  830.   if B and Ctrl_Mask <> 0 then                 { If Ctrl key is pressed check }
  831.     if B and Alt_Mask <> 0 then                { if Alt is pressed.  If both  }
  832.       begin                                    { are pressed check to see if  }
  833.         B := Port[KbdInPort];                  { the Del key is also pressed. }
  834.         if B = Scan_Del then                   { If so handle Ctrl-Alt-Del.   }
  835.           begin
  836.             B := Port[KbdOutPort];                    { read kbd control port }
  837.             Port[KbdOutPort] := B or $80;    { set high bit to reset keyboard }
  838.             Port[KbdOutPort] := B;                       { restore port value }
  839.             SendEOI;                           { clear kbd hardware interrupt }
  840.  
  841.             if InUserReboot then            { Don't reenter UserReboot. Beep  }
  842.               SoundBlip                     { and skip a second Ctrl-Alt-Del. }
  843.             else
  844.               UserReboot;                     { call our Ctrl-Alt-Del handler }
  845.             Exit;
  846.           end;
  847.       end;
  848.  
  849.  { Chain to previous interrupt handler }
  850.   ChainInt(Regs,IsrArray[KbdIntIsrHandle].OrigAddr);
  851. end;
  852.  
  853.  
  854. procedure TakeOverKbdInterrupt;
  855. {-Take over the keyboard interrupt to replace default Ctrl-Alt-Delete response}
  856. begin
  857.   if not HaveKbdInterrupt then
  858.     begin
  859.       if InitVector(KeybInt,KbdIntIsrHandle,@KbdIsr) then
  860.         begin
  861.           HaveKbdInterrupt := True;    { set our flag for deinstall reference }
  862.           New(ScrBufPtr);               { allocate the screen save buffer now }
  863.           ScrBufAllocated := True;      { in case we're short on memory later }
  864.           if CurrentDisplay = MonoHerc then
  865.             begin
  866.               MenuTextAttr := MenuTextMono; { autodetect monochrome systems   }
  867.               MenuHighAttr := MenuHighMono; { and set menu colors accordingly }
  868.             end;
  869.           if ShowKbdTakeover then
  870.             Writeln('Ctrl-Alt-Del has been taken over '+
  871.                     'by program keyboard handler.');
  872.         end
  873.       else
  874.         if ShowKbdTakeover then
  875.           Writeln('Error: Could not take over the keyboard interrupt.')
  876.     end
  877.   else
  878.     begin
  879.       if ShowKbdTakeover then
  880.         begin
  881.           Writeln('Ctrl-Alt-Del has ALREADY been taken over '+
  882.                   'by program keyboard handler.');
  883.           Writeln('No action is being taken at this time.');
  884.         end;
  885.       Exit;
  886.     end;
  887. end;
  888.  
  889.  
  890.  
  891. var
  892.   SavedExitProc : pointer;
  893.  
  894. procedure NoCadExitProc;
  895. {-Automatically restore previous keyboard handler at program exit }
  896. begin
  897.   ExitProc := SavedExitProc;
  898.   RestoreKbdInterrupt;
  899. end;
  900.  
  901. { Put procedure to call RestoreKbdInterrupt in the exit procedure chain }
  902. begin
  903.   SavedExitProc := ExitProc;                        { save previous exit proc }
  904.   ExitProc := @NoCadExitProc;                { install new exit proc in chain }
  905.   @CallBeforeProgramHaltProc := nil;     { must initialize procedure variable }
  906.   @SpecialActionProc := nil;             { must initialize procedure variable }
  907.  {$IFDEF DPMI }
  908.   BiosTics := Ptr(BiosDataSele, BiosDataTicsOfs);
  909.  {$ELSE}
  910.   BiosTics := Ptr(BiosDataSeg, BiosDataTicsOfs);
  911.  {$ENDIF}
  912. end.
  913.