home *** CD-ROM | disk | FTP | other *** search
/ Software of the Month Club 1995 December / SOFM_Dec1995.bin / pc / os2 / vpascal / source / rtl / wincrt.pas < prev    next >
Pascal/Delphi Source File  |  1995-10-31  |  24KB  |  836 lines

  1. {█▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀█}
  2. {█                                                       █}
  3. {█      Virtual Pascal Runtime Library.  Version 1.0.    █}
  4. {█      OS/2 Presentation Manager CRT interface unit     █}
  5. {█      ─────────────────────────────────────────────────█}
  6. {█      Copyright (C) 1995 B&M&T Corporation             █}
  7. {█      ─────────────────────────────────────────────────█}
  8. {█      Written by Vitaly Miryanov                       █}
  9. {█                                                       █}
  10. {▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀}
  11. {$S-,R-,Q-,I-,Cdecl-,OrgName-,AlignRec-}
  12.  
  13. unit WinCrt;
  14.  
  15. interface
  16.  
  17. uses Os2Def, Os2PmApi, Strings, WinDos, Use32;
  18.  
  19. var
  20.   WindowTitle: array[0..79] of Char;        { CRT window title }
  21.   InactiveTitleBuf: array[0..79] of Char;   { CRT window inactive title }
  22.  
  23. const
  24.   cw_UseDefault = Integer($8000);
  25.  
  26. const
  27.   WindowOrg: PointL =                       { CRT window origin }
  28.     (X: cw_UseDefault; Y: cw_UseDefault);
  29.   WindowSize: PointL =                      { CRT window size }
  30.     (X: cw_UseDefault; Y: cw_UseDefault);
  31.   ScreenSize: PointL = (X: 80; Y: 25);      { Screen buffer dimensions }
  32.   InactiveTitle: PChar = @InactiveTitleBuf; { Inactive window title }
  33.   Cursor: PointL = (X: 0; Y: 0);            { Cursor location }
  34.   Origin: PointL = (X: 0; Y: 0);            { Client area origin }
  35.   AutoTracking: Boolean = True;             { Track cursor on Write? }
  36.   CheckEOF: Boolean = False;                { Allow Ctrl-Z for EOF? }
  37.   CheckBreak: Boolean = True;               { Allow Ctrl-C for break? }
  38.   FontId: ULong = 1;                        { Font Id }
  39.   FontAttr: FAttrs = (                      { Font attributes }
  40.     usRecordLength:  SizeOf(FAttrs);        { Size of the record }
  41.     fsSelection:     0;                     { fattr_Sel_xxx }
  42.     lMatch:          1;
  43.     szFacename:      'System VIO';          { Fixed-pitch font }
  44.     idRegistry:      0;
  45.     usCodePage:      0;
  46.     lMaxBaselineExt: 16;                    { Font Size: 16x8 }
  47.     lAveCharWidth:   8;
  48.     fsType:          0;                     { fattr_Type_xxx }
  49.     fsFontUse:       0                      { fattr_FontUse_xxx }
  50.   );
  51.   CrtCreateFlags: ULong = fcf_TitleBar + fcf_SysMenu + fcf_SizeBorder +
  52.     fcf_MinMax + fcf_TaskList + fcf_NoByteAlign + fcf_VertScroll + fcf_HorzScroll;
  53.  
  54. procedure InitWinCrt;
  55. procedure DoneWinCrt;
  56.  
  57. procedure WriteBuf(Buffer: PChar; Count: Word);
  58. procedure WriteChar(Ch: Char);
  59.  
  60. function KeyPressed: Boolean;
  61. function ReadKey: Char;
  62. function ReadBuf(Buffer: PChar; Count: Word): Word;
  63.  
  64. procedure GotoXY(X, Y: Integer);
  65. function WhereX: Integer;
  66. function WhereY: Integer;
  67. procedure ClrScr;
  68. procedure ClrEol;
  69.  
  70. procedure CursorTo(X, Y: Integer);
  71. procedure ScrollTo(X, Y: Integer);
  72. procedure TrackCursor;
  73.  
  74. procedure AssignCrt(var F: Text);
  75.  
  76. { CRT window procedures }
  77.  
  78. function CrtWinProc(Window: HWnd; Message: ULong; Mp1, Mp2: MParam): MResult; cdecl; export;
  79. function FrameWndProc(Window: HWnd; Message: ULong; Mp1, Mp2: MParam): MResult; cdecl; export;
  80.  
  81. implementation
  82.  
  83. { Double word record }
  84.  
  85. type
  86.   LongRec = record
  87.     Lo, Hi: SmallInt;
  88.   end;
  89.  
  90. { Scroll key definition record }
  91.  
  92. type
  93.   TScrollKey = record
  94.     Key: Byte;
  95.     Ctrl: Boolean;
  96.     SBar: Byte;
  97.     Action: Byte;
  98.   end;
  99.  
  100. const
  101.   CrtWindow: HWnd = 0;                  { CRT window handle }
  102.   CrtWindowFrame: HWnd = 0;             { CRT window frame handle }
  103.   FirstLine: Integer = 0;               { First line in circular buffer }
  104.   KeyCount: Integer = 0;                { Count of keys in KeyBuffer }
  105.   Created: Boolean = False;             { CRT window created? }
  106.   Focused: Boolean = False;             { CRT window focused? }
  107.   Reading: Boolean = False;             { Reading from CRT window? }
  108.   Painting: Boolean = False;            { Handling wm_Paint? }
  109.  
  110. var
  111.   SaveExit: Pointer;                    { Saved exit procedure pointer }
  112.   ScreenBuffer: PChar;                  { Screen buffer pointer }
  113.   ClientSize: PointL;                   { Client area dimensions }
  114.   MaxWindowSize: PointL;                { Maximum window size }
  115.   Range: PointL;                        { Scroll bar ranges }
  116.   CharSize: PointL;                     { Character cell size }
  117.   CharDescent: Integer;                 { Character descent }
  118.   DC: HDC;                              { Global device context }
  119.   KeyBuffer: array[0..63] of Char;      { Keyboard type-ahead buffer }
  120.   Anchor: HAB;                          { PM anchor block }
  121.   MsgQue: HMQ;                          { PM message queue }
  122.   PS: HPS;                              { Presentation space handle }
  123.   VScrollBar: HWnd;                     { Vertical scrollbar handle }
  124.   HScrollBar: HWnd;                     { Horizontal scrollbar handle }
  125.   PR: RectL;                            { Painting rectangle }
  126.   cyClient: Integer;                    { Client window height }
  127.   OldFrameWndProc: FnWp;                { Standard frame window procedure }
  128.   DesktopSize: PointL;                  { Size of the PM Desktop }
  129.  
  130. const
  131.   CrtClassName: PChar = 'VPWinCrt';
  132.  
  133. const
  134.   sb_Top        = 8;    { PM does not have these ones }
  135.   sb_Bottom     = 9;
  136.  
  137. { Scroll keys table }
  138.  
  139. const
  140.   ScrollKeyCount = 12;
  141.   ScrollKeys: array[1..ScrollKeyCount] of TScrollKey = (
  142.     (Key: vk_Left;     Ctrl: False; SBar: sbs_Horz; Action: sb_LineUp),
  143.     (Key: vk_Right;    Ctrl: False; SBar: sbs_Horz; Action: sb_LineDown),
  144.     (Key: vk_Left;     Ctrl: True;  SBar: sbs_Horz; Action: sb_PageUp),
  145.     (Key: vk_Right;    Ctrl: True;  SBar: sbs_Horz; Action: sb_PageDown),
  146.     (Key: vk_Home;     Ctrl: False; SBar: sbs_Horz; Action: sb_Top),
  147.     (Key: vk_End;      Ctrl: False; SBar: sbs_Horz; Action: sb_Bottom),
  148.     (Key: vk_Up;       Ctrl: False; SBar: sbs_Vert; Action: sb_LineUp),
  149.     (Key: vk_Down;     Ctrl: False; SBar: sbs_Vert; Action: sb_LineDown),
  150.     (Key: vk_PageUp;   Ctrl: False; SBar: sbs_Vert; Action: sb_PageUp),
  151.     (Key: vk_PageDown; Ctrl: False; SBar: sbs_Vert; Action: sb_PageDown),
  152.     (Key: vk_Home;     Ctrl: True;  SBar: sbs_Vert; Action: sb_Top),
  153.     (Key: vk_End;      Ctrl: True;  SBar: sbs_Vert; Action: sb_Bottom));
  154.  
  155. { Return the smaller of two integer values }
  156.  
  157. function Min(X, Y: Integer): Integer;
  158. begin
  159.   if X < Y then Min := X else Min := Y;
  160. end;
  161.  
  162. { Return the larger of two integer values }
  163.  
  164. function Max(X, Y: Integer): Integer;
  165. begin
  166.   if X > Y then Max := X else Max := Y;
  167. end;
  168.  
  169. { Allocate presentation space }
  170.  
  171. procedure InitPresentationSpace;
  172. begin
  173.   if Painting then
  174.     PS := WinBeginPaint(CrtWindow, hNULL, @PR) else
  175.     PS := WinGetPS(CrtWindow);
  176.   GpiCreateLogFont(PS, nil, FontId, FontAttr);
  177.   GpiSetCharSet(PS, FontId);
  178.   GpiSetBackMix(PS, bm_OverPaint);
  179.   GpiSetColor(PS, clr_Default);
  180.   GpiSetBackColor(PS, clr_Background);
  181. end;
  182.  
  183. { Release presentation space }
  184.  
  185. procedure DonePresentationSpace;
  186. begin
  187.   GpiSetCharSet(PS, lcid_Default);
  188.   if Painting then
  189.     WinEndPaint(PS) else
  190.     WinReleasePS(PS);
  191. end;
  192.  
  193. { Calculates window parameters: character size and descent, }
  194. { maximum window size                                       }
  195.  
  196. procedure GetWindowParams;
  197. var
  198.   Metrics: FontMetrics;
  199. begin
  200.   InitPresentationSpace;
  201.   GpiQueryFontMetrics(PS, SizeOf(Metrics), Metrics);
  202.   CharSize.X := Metrics.lAveCharWidth;
  203.   CharSize.Y := Metrics.lMaxAscender + Metrics.lMaxDescender;
  204.   CharDescent := Metrics.lMaxDescender;
  205.   MaxWindowSize.X := ScreenSize.X * CharSize.X +
  206.     WinQuerySysValue(hwnd_Desktop, sv_CxVScroll) +
  207.     2 * WinQuerySysValue(hwnd_Desktop, sv_CxSizeBorder);
  208.   MaxWindowSize.Y := ScreenSize.Y * CharSize.Y +
  209.     WinQuerySysValue(hwnd_Desktop, sv_CyHScroll) +
  210.     WinQuerySysValue(hwnd_Desktop, sv_CyTitleBar) +
  211.     2 * WinQuerySysValue(hwnd_Desktop, sv_CySizeBorder);
  212.   DonePresentationSpace;
  213. end;
  214.  
  215. { Enables/Disables specified system menu item }
  216.  
  217. procedure EnableSysMenuItem(Item: ULong; Enable: Boolean);
  218. var
  219.   Value: ULong;
  220. begin
  221.   if Enable then Value := 0 else Value := mia_Disabled;
  222.   WinSendMsg(WinWindowFromID(CrtWindowFrame, fid_SysMenu),
  223.     mm_SetItemAttr, Item + 1 shl 16, mia_Disabled + Value shl 16);
  224. end;
  225.  
  226. { Show cursor }
  227.  
  228. procedure ShowCursor;
  229. begin
  230.   WinCreateCursor(CrtWindow,
  231.     (Cursor.X - Origin.X) * CharSize.X,                { X }
  232.     cyClient - (Cursor.Y - Origin.Y + 1) * CharSize.Y, { Y }
  233.     CharSize.X, 2, cursor_Solid + cursor_Flash, nil);
  234.   WinShowCursor(CrtWindow, True);
  235. end;
  236.  
  237. { Hide cursor }
  238.  
  239. procedure HideCursor;
  240. begin
  241.   WinDestroyCursor(CrtWindow);
  242. end;
  243.  
  244. { Update scroll bars }
  245.  
  246. procedure SetScrollBars;
  247. var
  248.   Swap: Swp;
  249. begin
  250.   WinQueryWindowPos(CrtWindow, Swap);
  251.   WinSendMsg(HScrollBar, sbm_SetScrollBar, Origin.X, 0 + Max(1, Range.X) shl 16);
  252.   WinSendMsg(VScrollBar, sbm_SetScrollBar, Origin.Y, 0 + Max(1, Range.Y) shl 16);
  253.   WinSendMsg(HScrollBar, sbm_SetThumbSize, Swap.cX + (ScreenSize.X * CharSize.X) shl 16, 0);
  254.   WinSendMsg(VScrollBar, sbm_SetThumbSize, Swap.cY + (ScreenSize.Y * CharSize.Y) shl 16, 0);
  255. end;
  256.  
  257. { Terminate CRT window }
  258.  
  259. procedure Terminate;
  260. begin
  261.   if Focused and Reading then HideCursor;
  262.   Halt(255);
  263. end;
  264.  
  265. { Set cursor position }
  266.  
  267. procedure CursorTo(X, Y: Integer);
  268. begin
  269.   Cursor.X := Max(0, Min(X, ScreenSize.X - 1));
  270.   Cursor.Y := Max(0, Min(Y, ScreenSize.Y - 1));
  271. end;
  272.  
  273. { Scroll window to given origin }
  274.  
  275. procedure ScrollTo(X, Y: Integer);
  276. begin
  277.   if Created then
  278.   begin
  279.     X := Max(0, Min(X, Range.X));
  280.     Y := Max(0, Min(Y, Range.Y));
  281.     if (X <> Origin.X) or (Y <> Origin.Y) then
  282.     begin
  283.       if X <> Origin.X then WinSendMsg(HScrollBar, sbm_SetPos, X, 0);
  284.       if Y <> Origin.Y then WinSendMsg(VScrollBar, sbm_SetPos, Y, 0);
  285.       WinScrollWindow(CrtWindow,
  286.         (Origin.X - X) * CharSize.X,
  287.         (Y - Origin.Y) * CharSize.Y, nil, nil, 0, nil, sw_InvalidateRgn);
  288.       Origin.X := X;
  289.       Origin.Y := Y;
  290.       WinUpdateWindow(CrtWindow);
  291.     end;
  292.   end;
  293. end;
  294.  
  295. { Scroll to make cursor visible }
  296.  
  297. procedure TrackCursor;
  298. begin
  299.   ScrollTo(Max(Cursor.X - ClientSize.X + 1, Min(Origin.X, Cursor.X)),
  300.     Max(Cursor.Y - ClientSize.Y + 1, Min(Origin.Y, Cursor.Y)));
  301. end;
  302.  
  303. { Return pointer to location in screen buffer }
  304.  
  305. function ScreenPtr(X, Y: Integer): PChar;
  306. begin
  307.   Inc(Y, FirstLine);
  308.   if Y >= ScreenSize.Y then Dec(Y, ScreenSize.Y);
  309.   ScreenPtr := @ScreenBuffer[Y * ScreenSize.X + X];
  310. end;
  311.  
  312. { Update text on cursor line }
  313.  
  314. procedure ShowText(L, R: Integer);
  315. var
  316.   P: PointL;
  317. begin
  318.   if L < R then
  319.   begin
  320.     InitPresentationSpace;
  321.     P.X := (L - Origin.X) * CharSize.X;
  322.     P.Y := cyClient - (Cursor.Y - Origin.Y + 1) * CharSize.Y + CharDescent;
  323.     GpiCharStringAt(PS, P, R - L, ScreenPtr(L, Cursor.Y));
  324.     DonePresentationSpace;
  325.   end;
  326. end;
  327.  
  328. { Write text buffer to CRT window }
  329.  
  330. procedure WriteBuf(Buffer: PChar; Count: Word);
  331. var
  332.   L, R: Integer;
  333.  
  334. procedure NewLine;
  335. begin
  336.   ShowText(L, R);
  337.   L := 0;
  338.   R := 0;
  339.   Cursor.X := 0;
  340.   Inc(Cursor.Y);
  341.   if Cursor.Y = ScreenSize.Y then
  342.   begin
  343.     Dec(Cursor.Y);
  344.     Inc(FirstLine);
  345.     if FirstLine = ScreenSize.Y then FirstLine := 0;
  346.     FillChar(ScreenPtr(0, Cursor.Y)^, ScreenSize.X, ' ');
  347.     WinScrollWindow(CrtWindow, 0, CharSize.Y, nil, nil, 0, nil, sw_InvalidateRgn);
  348.     WinUpdateWindow(CrtWindow);
  349.   end;
  350. end;
  351.  
  352. begin
  353.   InitWinCrt;
  354.   L := Cursor.X;
  355.   R := Cursor.X;
  356.   while Count > 0 do
  357.   begin
  358.     case Buffer^ of
  359.       #32..#255:
  360.         begin
  361.           ScreenPtr(Cursor.X, Cursor.Y)^ := Buffer^;
  362.           Inc(Cursor.X);
  363.           if Cursor.X > R then R := Cursor.X;
  364.           if Cursor.X = ScreenSize.X then NewLine;
  365.         end;
  366.       #13:
  367.         NewLine;
  368.       #8:
  369.         if Cursor.X > 0 then
  370.         begin
  371.           Dec(Cursor.X);
  372.           ScreenPtr(Cursor.X, Cursor.Y)^ := ' ';
  373.           if Cursor.X < L then L := Cursor.X;
  374.         end;
  375.       #7:
  376.         WinAlarm(hwnd_Desktop, wa_Note);
  377.     end;
  378.     Inc(Buffer);
  379.     Dec(Count);
  380.   end;
  381.   ShowText(L, R);
  382.   if AutoTracking then TrackCursor;
  383. end;
  384.  
  385. { Write character to CRT window }
  386.  
  387. procedure WriteChar(Ch: Char);
  388. begin
  389.   WriteBuf(@Ch, 1);
  390. end;
  391.  
  392. { Return keyboard status }
  393.  
  394. function KeyPressed: Boolean;
  395. var
  396.   M: QMsg;
  397. begin
  398.   InitWinCrt;
  399.   while WinPeekMsg(Anchor, M, 0, 0, 0, pm_Remove) do
  400.   begin
  401.     if M.Msg = wm_Quit then Terminate;
  402.     WinDispatchMsg(Anchor, M);
  403.   end;
  404.   KeyPressed := KeyCount > 0;
  405. end;
  406.  
  407. { Read key from CRT window }
  408.  
  409. function ReadKey: Char;
  410. begin
  411.   TrackCursor;
  412.   if not KeyPressed then
  413.   begin
  414.     Reading := True;
  415.     if Focused then ShowCursor;
  416.     repeat WinWaitMsg(Anchor, 0, 0) until KeyPressed;
  417.     if Focused then HideCursor;
  418.     Reading := False;
  419.   end;
  420.   ReadKey := KeyBuffer[0];
  421.   Dec(KeyCount);
  422.   Move(KeyBuffer[1], KeyBuffer[0], KeyCount);
  423. end;
  424.  
  425. { Read text buffer from CRT window }
  426.  
  427. function ReadBuf(Buffer: PChar; Count: Word): Word;
  428. var
  429.   Ch: Char;
  430.   I: Word;
  431. begin
  432.   I := 0;
  433.   repeat
  434.     Ch := ReadKey;
  435.     case Ch of
  436.       #8:
  437.         if I > 0 then
  438.         begin
  439.           Dec(I);
  440.           WriteChar(#8);
  441.         end;
  442.       #32..#255:
  443.         if I < Count - 2 then
  444.         begin
  445.           Buffer[I] := Ch;
  446.           Inc(I);
  447.           WriteChar(Ch);
  448.         end;
  449.     end;
  450.   until (Ch = #13) or (CheckEOF and (Ch = #26));
  451.   Buffer[I] := Ch;
  452.   Inc(I);
  453.   if Ch = #13 then
  454.   begin
  455.     Buffer[I] := #10;
  456.     Inc(I);
  457.     WriteChar(#13);
  458.   end;
  459.   TrackCursor;
  460.   ReadBuf := I;
  461. end;
  462.  
  463. { Set cursor position }
  464.  
  465. procedure GotoXY(X, Y: Integer);
  466. begin
  467.   CursorTo(X - 1, Y - 1);
  468. end;
  469.  
  470. { Return cursor X position }
  471.  
  472. function WhereX: Integer;
  473. begin
  474.   WhereX := Cursor.X + 1;
  475. end;
  476.  
  477. { Return cursor Y position }
  478.  
  479. function WhereY: Integer;
  480. begin
  481.   WhereY := Cursor.Y + 1;
  482. end;
  483.  
  484. { Clear screen }
  485.  
  486. procedure ClrScr;
  487. begin
  488.   InitWinCrt;
  489.   FillChar(ScreenBuffer^, ScreenSize.X * ScreenSize.Y, ' ');
  490.   Cursor.X := 0; Cursor.Y := 0;
  491.   Origin.X := 0; Origin.Y := 0;
  492.   SetScrollBars;
  493.   WinInvalidateRect(CrtWindow, nil, False);
  494.   WinUpdateWindow(CrtWindow);
  495. end;
  496.  
  497. { Clear to end of line }
  498.  
  499. procedure ClrEol;
  500. begin
  501.   InitWinCrt;
  502.   FillChar(ScreenPtr(Cursor.X, Cursor.Y)^, ScreenSize.X - Cursor.X, ' ');
  503.   ShowText(Cursor.X, ScreenSize.X);
  504. end;
  505.  
  506. { wm_Create message handler }
  507.  
  508. procedure WindowCreate;
  509. begin
  510.   Created := True;
  511.   CrtWindowFrame := WinQueryWindow(CrtWindow, qw_Parent);
  512.   GetMem(ScreenBuffer, ScreenSize.X * ScreenSize.Y);
  513.   FillChar(ScreenBuffer^, ScreenSize.X * ScreenSize.Y, ' ');
  514.   if not CheckBreak then EnableSysMenuItem(sc_Close, False);
  515.   VScrollBar := WinWindowFromID(CrtWindowFrame, fid_VertScroll);
  516.   HScrollBar := WinWindowFromID(CrtWindowFrame, fid_HorzScroll);
  517.   GetWindowParams;
  518. end;
  519.  
  520. { wm_Paint message handler }
  521.  
  522. procedure WindowPaint;
  523. var
  524.   X1, X2, Y1, Y2: Integer;
  525.   P: PointL;
  526.   R: RectL;
  527. begin
  528.   Painting := True;
  529.   InitPresentationSpace;
  530.   X1 := Max(0, PR.xLeft div CharSize.X + Origin.X);
  531.   X2 := Min(ScreenSize.X,
  532.     (PR.xRight + CharSize.X - 1) div CharSize.X + Origin.X);
  533.   Y1 := Max(0, (cyClient - PR.yTop) div CharSize.Y + Origin.Y);
  534.   Y2 := Min(ScreenSize.Y,
  535.     (cyClient - PR.yBottom + CharSize.Y - 1) div CharSize.Y + Origin.Y);
  536.   while Y1 < Y2 do
  537.   begin
  538.     P.X := (X1 - Origin.X) * CharSize.X;
  539.     P.Y := cyClient - (Y1 - Origin.Y + 1) * CharSize.Y + CharDescent;
  540.     GpiCharStringAt(PS, P, X2 - X1, ScreenPtr(X1, Y1));
  541.     Inc(Y1);
  542.   end;
  543.   R := PR;
  544.   R.yTop := P.Y - CharDescent;
  545.   if R.yTop > R.yBottom then WinFillRect(PS, R, clr_Background);
  546.   R := PR;
  547.   R.xLeft := (X2 - Origin.X) * CharSize.X;
  548.   if R.xLeft < R.xRight then WinFillRect(PS, R, clr_Background);
  549.   DonePresentationSpace;
  550.   Painting := False;
  551. end;
  552.  
  553. { wm_VScroll and wm_HScroll message handler }
  554.  
  555. procedure WindowScroll(Which, Action, Thumb: Integer);
  556. var
  557.   X, Y: Integer;
  558.  
  559. function GetNewPos(Pos, Page, Range: Integer): Integer;
  560. begin
  561.   case Action of
  562.     sb_LineUp: GetNewPos := Pos - 1;
  563.     sb_LineDown: GetNewPos := Pos + 1;
  564.     sb_PageUp: GetNewPos := Pos - Page;
  565.     sb_PageDown: GetNewPos := Pos + Page;
  566.     sb_SliderPosition: GetNewPos := Thumb;
  567.     sb_Top: GetNewPos := 0;
  568.     sb_Bottom: GetNewPos := Range;
  569.   else
  570.     GetNewPos := Pos;
  571.   end;
  572. end;
  573.  
  574. begin
  575.   X := Origin.X;
  576.   Y := Origin.Y;
  577.   case Which of
  578.     sbs_Horz: X := GetNewPos(X, ClientSize.X div 2, Range.X);
  579.     sbs_Vert: Y := GetNewPos(Y, ClientSize.Y, Range.Y);
  580.   end;
  581.   ScrollTo(X, Y);
  582. end;
  583.  
  584. { wm_Size message handler }
  585.  
  586. procedure WindowResize(X, Y: Integer);
  587. begin
  588.   if Focused and Reading then HideCursor;
  589.   cyClient := Y;
  590.   ClientSize.X := X div CharSize.X;
  591.   ClientSize.Y := Y div CharSize.Y;
  592.   Range.X := Max(0, ScreenSize.X - ClientSize.X);
  593.   Range.Y := Max(0, ScreenSize.Y - ClientSize.Y);
  594.   Origin.X := Min(Origin.X, Range.X);
  595.   Origin.Y := Min(Origin.Y, Range.Y);
  596.   SetScrollBars;
  597.   if Focused and Reading then ShowCursor;
  598. end;
  599.  
  600. { wm_Char message handler when characters are entered }
  601.  
  602. procedure WindowChar(Ch: Char);
  603. begin
  604.   if KeyCount < SizeOf(KeyBuffer) then
  605.   begin
  606.     KeyBuffer[KeyCount] := Ch;
  607.     Inc(KeyCount);
  608.   end;
  609. end;
  610.  
  611. { wm_Char message handler when non-character keys are pressed }
  612.  
  613. procedure WindowKeyDown(KeyDown: Word; CtrlDown: Boolean);
  614. var
  615.   I: Integer;
  616. begin
  617.   for I := 1 to ScrollKeyCount do
  618.     with ScrollKeys[I] do
  619.       if (Key = KeyDown) and (Ctrl = CtrlDown) then
  620.       begin
  621.         WindowScroll(SBar, Action, 0);
  622.         Exit;
  623.       end;
  624. end;
  625.  
  626. { wm_SetFocus message handler }
  627.  
  628. procedure WindowSetFocus(AFocused: Boolean);
  629. begin
  630.   Focused := AFocused;
  631.   if Reading then
  632.     if AFocused then ShowCursor else HideCursor;
  633. end;
  634.  
  635. { wm_Close message handler }
  636.  
  637. procedure WindowClose;
  638. begin
  639.   FreeMem(ScreenBuffer, ScreenSize.X * ScreenSize.Y);
  640.   Cursor.X := 0; Cursor.Y := 0;
  641.   Origin.X := 0; Origin.Y := 0;
  642.   WinPostMsg(CrtWindow, wm_Quit, 0, 0);
  643.   Created := False;
  644. end;
  645.  
  646. { CRT window procedure }
  647.  
  648. function CrtWinProc(Window: HWnd; Message: ULong; Mp1, Mp2: MParam): MResult;
  649. begin
  650.   CrtWinProc := 0;
  651.   CrtWindow := Window;
  652.   case Message of
  653.     wm_Create: WindowCreate;
  654.     wm_Paint: WindowPaint;
  655.     wm_VScroll: WindowScroll(sbs_Vert, LongRec(Mp2).Hi, LongRec(Mp2).Lo);
  656.     wm_HScroll: WindowScroll(sbs_Horz, LongRec(Mp2).Hi, LongRec(Mp2).Lo);
  657.     wm_Size: WindowResize(LongRec(Mp2).Lo, LongRec(Mp2).Hi);
  658.     wm_Char:
  659.       if (CharMsgMp1(Mp1).fs and kc_KeyUp) = 0 then
  660.       begin                                                     { Key is down }
  661.         if CheckBreak then                                      { Break enabled }
  662.           if (CharMsgMp2(Mp2).VKey = vk_Break) or               { Ctrl-Break }
  663.             (((CharMsgMp1(Mp1).fs and kc_Ctrl) <> 0) and
  664.              ((CharMsgMp2(Mp2).Chr = Ord('C')) or               { Ctrl-C }
  665.               (CharMsgMp2(Mp2).Chr = Ord('c')))) then Terminate;{ Ctrl-c }
  666.         if (CharMsgMp2(Mp2).Chr > 0) and (CharMsgMp2(Mp2).Chr <= 255) and
  667.           ((CharMsgMp1(Mp1).fs and (kc_Ctrl + kc_Alt)) = 0)
  668.           then WindowChar(Chr(CharMsgMp2(Mp2).Chr))
  669.           else WindowKeyDown(CharMsgMp2(Mp2).VKey, (CharMsgMp1(Mp1).fs and kc_Ctrl) <> 0);
  670.       end;
  671.     wm_SetFocus: WindowSetFocus(LongRec(Mp2).Lo <> 0);
  672.     wm_Close: WindowClose;
  673.   else
  674.     CrtWinProc := WinDefWindowProc(Window, Message, Mp1, Mp2);
  675.   end;
  676. end;
  677.  
  678. { CRT window frame procedure }
  679.  
  680. function FrameWndProc(Window: HWnd; Message: ULong; Mp1, Mp2: MParam): MResult;
  681. begin
  682.   FrameWndProc := OldFrameWndProc(Window, Message, Mp1, Mp2);
  683.   case Message of
  684.     wm_AdjustWindowPos:
  685.       with PSwp(Mp1)^ do
  686.       if (Fl and swp_Size) <> 0 then
  687.       begin
  688.         cX := Min(cX, MaxWindowSize.X);
  689.         cY := Min(cy, MaxWindowSize.Y);
  690.         if (Fl and swp_Maximize) <> 0 then
  691.         begin
  692.           X := (DesktopSize.X - cX) div 2;
  693.           Y := (DesktopSize.Y - cY) div 2;
  694.         end;
  695.       end;
  696.     wm_QueryTrackInfo:
  697.       with PTrackInfo(Mp2)^ do
  698.       begin
  699.         ptlMaxTrackSize.X := MaxWindowSize.X;
  700.         ptlMaxTrackSize.Y := MaxWindowSize.Y;
  701.       end;
  702.   end;
  703. end;
  704.  
  705. { Text file device driver output function }
  706.  
  707. function CrtOutput(var F: TTextRec): Integer; far;
  708. begin
  709.   if F.BufPos <> 0 then
  710.   begin
  711.     WriteBuf(PChar(F.BufPtr), F.BufPos);
  712.     F.BufPos := 0;
  713.     KeyPressed;
  714.   end;
  715.   CrtOutput := 0;
  716. end;
  717.  
  718. { Text file device driver input function }
  719.  
  720. function CrtInput(var F: TTextRec): Integer; far;
  721. begin
  722.   F.BufEnd := ReadBuf(PChar(F.BufPtr), F.BufSize);
  723.   F.BufPos := 0;
  724.   CrtInput := 0;
  725. end;
  726.  
  727. { Text file device driver close function }
  728.  
  729. function CrtClose(var F: TTextRec): Integer; far;
  730. begin
  731.   CrtClose := 0;
  732. end;
  733.  
  734. { Text file device driver open function }
  735.  
  736. function CrtOpen(var F: TTextRec): Integer; far;
  737. begin
  738.   if F.Mode = fmInput then
  739.   begin
  740.     F.InOutFunc := @CrtInput;
  741.     F.FlushFunc := nil;
  742.   end else
  743.   begin
  744.     F.Mode := fmOutput;
  745.     F.InOutFunc := @CrtOutput;
  746.     F.FlushFunc := @CrtOutput;
  747.   end;
  748.   F.CloseFunc := @CrtClose;
  749.   CrtOpen := 0;
  750. end;
  751.  
  752. { Assign text file to CRT device }
  753.  
  754. procedure AssignCrt(var F: Text);
  755. begin
  756.   with TTextRec(F) do
  757.   begin
  758.     Handle := $FFFFFFFF;
  759.     Mode := fmClosed;
  760.     BufSize := SizeOf(Buffer);
  761.     BufPtr := @Buffer;
  762.     OpenFunc := @CrtOpen;
  763.     Name[0] := #0;
  764.   end;
  765. end;
  766.  
  767. { Create CRT window if required }
  768.  
  769. procedure InitWinCrt;
  770. var
  771.   InitSize: PointL;
  772. begin
  773.   if not Created then
  774.   begin
  775.     DesktopSize.X := WinQuerySysValue(hwnd_Desktop, sv_CxScreen);
  776.     DesktopSize.Y := WinQuerySysValue(hwnd_Desktop, sv_CyScreen);
  777.     CrtWindowFrame := WinCreateStdWindow(hwnd_Desktop, 0, CrtCreateFlags,
  778.       CrtClassName, WindowTitle, 0, 0, 0, CrtWindow);
  779.     InitSize.X := (DesktopSize.X * 3) div 4;
  780.     InitSize.Y := (DesktopSize.Y * 3) div 4;
  781.     if WindowSize.X = cw_UseDefault then WindowSize := InitSize;
  782.     WindowSize.X := Min(MaxWindowSize.X, WindowSize.X);
  783.     WindowSize.Y := Min(MaxWindowSize.Y, WindowSize.Y);
  784.     if WindowOrg.X = cw_UseDefault then
  785.     begin
  786.       WindowOrg.X := (DesktopSize.X - WindowSize.X) div 2;
  787.       WindowOrg.Y := (DesktopSize.Y - WindowSize.Y) div 2;
  788.     end;
  789.     WinSetWindowPos(
  790.       CrtWindowFrame, hNULL,
  791.       WindowOrg.X, WindowOrg.Y,
  792.       WindowSize.X, WindowSize.Y,
  793.       swp_Move + swp_Size + swp_Activate + swp_Show);
  794.     Pointer(@OldFrameWndProc) := WinSubclassWindow(CrtWindowFrame, FrameWndProc);
  795.   end;
  796. end;
  797.  
  798. { Destroy CRT window if required }
  799.  
  800. procedure DoneWinCrt;
  801. begin
  802.   if Created then WinDestroyWindow(CrtWindow);
  803.   Halt(0);
  804. end;
  805.  
  806. { WinCrt unit exit procedure }
  807.  
  808. procedure ExitWinCrt; far;
  809. var
  810.   Message: QMsg;
  811. begin
  812.   ExitProc := SaveExit;
  813.   if Created and (ErrorAddr = nil) then
  814.   begin
  815.     WinSetWindowText(CrtWindowFrame, InactiveTitle);
  816.     EnableSysMenuItem(sc_Close, True);
  817.     CheckBreak := False;
  818.     while WinGetMsg(Anchor, Message, 0, 0, 0) do WinDispatchMsg(Anchor, Message);
  819.   end;
  820. end;
  821.  
  822. begin
  823.   Anchor := WinInitialize(0);
  824.   MsgQue := WinCreateMsgQueue(Anchor, 0);
  825.   if MsgQue = 0 then Halt(254);
  826.   WinRegisterClass(Anchor, CrtClassName, CrtWinProc, cs_SizeRedraw, 0);
  827.   AssignCrt(Input);
  828.   Reset(Input);
  829.   AssignCrt(Output);
  830.   Rewrite(Output);
  831.   GetArgStr(WindowTitle, 0, SizeOf(WindowTitle));
  832.   StrPCopy(InactiveTitleBuf, '(Inactive ' + ParamStr(0) + ')');
  833.   SaveExit := ExitProc;
  834.   ExitProc := @ExitWinCrt;
  835. end.
  836.