home *** CD-ROM | disk | FTP | other *** search
/ For Beginners & Professional Hackers / cd.iso / softcrc / masm / masm.610 / disk3 / samples / winclock / winclock.as$ / WINCLOCK
Encoding:
Text File  |  1992-11-12  |  46.9 KB  |  997 lines

  1. ;----------------------------------------------------------------------------;
  2. ;                               winclock.ASM                                 ;
  3. ;----------------------------------------------------------------------------;
  4. ;                                                                            ;
  5. ; Sample MASM 6.1 program using the Windows API Interface. It heavily relies ;
  6. ; on the MASM 6.x high level constructs like INVOKE and .IF/.ELSE/.ENDIF.    ;
  7. ; The program, although simple enough, actually does quite a bit. Double     ;
  8. ; clicking on the window will bring up a popup menu with other options,      ;
  9. ; including setting an alarm.                                                ;
  10. ;                                                                            ;
  11. ; Adding to this code should be simple enough. New features could include    ;
  12. ; making sure that fonts are displayed with the appropiate aspect ratio.     ;
  13. ;                                                                            ;
  14. ; Note that this program doesn't attempt to teach how to program in Windows. ;
  15. ; For that you should consult the Windows Software Developers Kit or other   ;
  16. ; reference books.                                                           ;
  17. ;                                                                            ;
  18. ;----------------------------------------------------------------------------;
  19.  
  20.                 .model  small, pascal, nearstack
  21.                 .286
  22.  
  23.                 ?WINPROLOGUE = 1
  24.                 NOKERNEL = 1
  25.                 NOSOUND = 1
  26.                 NOCOMM = 1
  27.                 NODRIVERS = 1
  28.                 include win.inc                 ; Converted from WINDOWS.H
  29.  
  30. ;----------------------------------------------------------------------------;
  31. ;                        Prototypes & External Definitions                   ;
  32. ;----------------------------------------------------------------------------;
  33.  
  34. WinMain          PROTO PASCAL, hInstance:HANDLE,  hPrevInstance:HANDLE,
  35.                         lpszCmdLine:LPSTR, nCmdShow:SWORD
  36. WndProc          PROTO FAR PASCAL,  :HWND, :WORD, :SWORD, :SDWORD
  37. Initialize       PROTO,             :PINT, :PINT, :PINT,  :PINT
  38. Resize           PROTO,             :HWND
  39. SetupTimer       PROTO,             :HWND, :WORD
  40.  
  41. extern __astart:proc            ; When Windows load an app, it expects astart
  42.                                 ; to have the necessary start-up code. We get
  43.                                 ; astart from APPENTRY.ASM
  44.  
  45. ;----------------------------------------------------------------------------;
  46. ;                             Numeric Equates                                ;
  47. ;----------------------------------------------------------------------------;
  48.  
  49. INIT_FONT        EQU    30t             ; initial font height
  50. MAX_HEIGHT       EQU    100             ; Maximum Font Height
  51. TOP_CORNER       EQU    1               ; Set to 0 for Lower-Right Corner Clock
  52.  
  53. IDM_DATE         EQU    1t              ; definitions for Menu items
  54. IDM_ALARM        EQU    2t
  55. IDM_SET          EQU    3t
  56. IDM_MENU         EQU    4t
  57. IDM_EXIT         EQU    5t
  58. IDM_ABOUT        EQU    6t
  59. IDM_MINIMIZE     EQU    7t
  60. IDM_ONTOP        EQU    8t
  61. AMPM             EQU    3t              ; length of am/pm strings
  62. SCROLLCHILD      EQU    30t             ; identifies scroll: any number will do
  63. MAXTIME          EQU    1439t           ; max # of minutes
  64. TIMER_SECS       EQU    1000t           ; timer interval: 1000 mill = 1 second
  65. TIMER_MINS       EQU    60000t          ; interval for 1 minute: 1000 * 60
  66. ICON_LEN         EQU    7t              ; # of chars to display icon time
  67.  
  68. ;----------------------------------------------------------------------------;
  69. ;                               Data Segments                                ;
  70. ;----------------------------------------------------------------------------;
  71.  
  72.                 .const          
  73.  
  74. szAppName       SBYTE   "WINClock",0                   
  75.  
  76. DateFmt         SBYTE   " %s %2d, %04d ",13,10,0
  77. TimeFmt         SBYTE   " %d:%02d:%02d %s ",0
  78. IconFmt         SBYTE   " %d:%02d ",0
  79. AlarmFmt        SBYTE   " %d:%02d %s ",0
  80. szAMPM          SBYTE   "am",0,"pm",0                   
  81. szTooManyTimers SBYTE   "Too many clocks or timers!",0
  82. szAlarmMsg      SBYTE   "Remember your Appointment!",0
  83. szMonths        SBYTE   "Jan",0,"Feb",0,"Mar",0,"Apr",0,"May",0,"Jun",0,
  84.                         "Jul",0,"Aug",0,"Sep",0,"Oct",0,"Nov",0,"Dec",0
  85. szDateCmd       SBYTE   "Enable &Date", 0
  86. szSetCmd        SBYTE   "S&et Alarm",0
  87. szAlarmCmd      SBYTE   "Enable &Alarm", 0
  88. szOnTopCmd      SBYTE   "Always on &Top",0
  89. szMinimizeCmd   SBYTE   "&Minimize", 0
  90. szExitCmd       SBYTE   "E&xit", 0
  91. szAboutCmd      SBYTE   "About &Clock...", 0
  92. szAboutText     SBYTE   "Assembler Program Using the Windows API", 0
  93. szDisplay       SBYTE   "DISPLAY", 0    ; to get a handle to entire display
  94. szScrollBar     SBYTE   "scrollbar", 0  ; to create scrollbar 'class'                
  95.  
  96.                 .data
  97.  
  98. EnableDate      BYTE    MF_CHECKED      ; Date initially enabled
  99. EnableAlarm     BYTE    MF_UNCHECKED    ; Alarm initially disabled
  100. AlwaysOnTop     BYTE    MF_CHECKED      ; Window will be on topmost
  101. Iconized        BYTE    FALSE           ; Clock initially Normal Size
  102. TestAlarm       BYTE    FALSE           ; so that we know to test for the alarm
  103. SetAlarm        BYTE    FALSE           ; signalling while we set the alarm
  104. AlarmTime       WORD    (MAXTIME/2)+1   ; initial alarm time: 12:00 pm
  105.  
  106.                 .data?
  107.  
  108. cBuffer         SBYTE   40 dup (?)      ; buffer to receive text for drawing
  109. hMenu           HMENU   ?               ; handle to Popup Menu
  110. logfont         LOGFONT { }             ; logical font structure
  111. hWndScrol       HWND    ?               ; handle to the scroll window
  112. TextRect        RECT    { }             ; rectangle to draw text in
  113.  
  114. ;----------------------------------------------------------------------------;
  115. ;                               Code Segment                                 ;
  116. ;----------------------------------------------------------------------------;
  117.  
  118.                 .code
  119.  
  120. ;----------------------------------------------------------------------------;
  121. ;                               WinMain                                      ;
  122. ;----------------------------------------------------------------------------;
  123. ;                                                                            ;
  124. ; Main routine called by Windows in program start. If no previous instances, ;
  125. ; sets up a window class and registers it. Initializes the program with      ;
  126. ; Initialize, creates a top window with the coordinates from Initialize, sets;
  127. ; up a child scroll bar control, and sets up the message loop.               ;
  128. ;                                                                            ;
  129. ;----------------------------------------------------------------------------;
  130.  
  131. WinMain         PROC,   hInstance:HANDLE,  hPrevInstance:HANDLE,
  132.                         lpszCmdLine:LPSTR, nCmdShow:SWORD
  133.                 LOCAL   msg:MSG, wndclass:WNDCLASS, xStart:SWORD,
  134.                         yStart:SWORD, xClient:SWORD, yClient:SWORD
  135.  
  136.                 ; Local variables: msg: message to be used in the message loop
  137.                 ;                  wndclass: temp. to store window class
  138.                 ;                  x,y Start-Client: Size of Initial Window
  139. ;
  140. ;--- Check for previous instances
  141. ;
  142.                 .IF (hPrevInstance == 0)
  143.  
  144.                         lea     di, wndclass    ; because we use a NEARSTACK,
  145.                         ASSUME  di:PTR WNDCLASS ; ss=ds
  146.  
  147.                         mov     ax, CS_HREDRAW OR CS_VREDRAW OR CS_DBLCLKS
  148.                         mov     [di].style, ax 
  149.                         mov     WORD PTR [di].lpfnWndProc,   LROFFSET WndProc
  150.                         mov     WORD PTR [di].lpfnWndProc+2, SEG WndProc
  151.                         xor     ax,ax
  152.                         mov     [di].cbClsExtra, ax
  153.                         mov     [di].cbWndExtra, ax
  154.  
  155.                         mov     [di].hIcon, ax  ; null icon: we will draw it
  156.  
  157.                         mov     ax, hInstance
  158.                         mov     [di].hInstance, ax
  159.  
  160.                         INVOKE  LoadCursor, NULL, IDC_ARROW
  161.                         mov     [di].hCursor, ax
  162.  
  163.                         INVOKE  GetStockObject, WHITE_BRUSH
  164.                         mov     [di].hbrBackground, ax
  165.  
  166.                         xor     ax, ax
  167.                         mov     WORD PTR [di].lpszMenuName,   ax
  168.                         mov     WORD PTR [di].lpszMenuName+2, ax
  169.  
  170.                         mov     WORD PTR [di].lpszClassName,   OFFSET szAppName
  171.                         mov     WORD PTR [di].lpszClassName+2, ds
  172.  
  173.                         INVOKE  RegisterClass, di
  174.                         .IF (ax == 0)
  175.                                 mov     ax, FALSE
  176.                                 jmp     doRet
  177.                         .ENDIF
  178.  
  179.                         ASSUME  di:NOTHING        
  180.  
  181.                 .ENDIF     ;--- End of IF (hPrevInstance == 0)
  182.  
  183. ;
  184. ;---- Initialize
  185. ;
  186.  
  187.                 INVOKE  Initialize, ADDR xStart,  ADDR yStart,
  188.                                     ADDR xClient, ADDR yClient
  189.  
  190. ;
  191. ;---- Create Top Window
  192. ;
  193.  
  194.                 INVOKE  CreateWindowEx, WS_EX_TOPMOST, ADDR szAppName,
  195.                         ADDR szAppName, WS_BORDER OR WS_POPUP OR WS_THICKFRAME,
  196.                         xStart, yStart, xClient, yClient, NULL, NULL, 
  197.                         hInstance, NULL
  198.                 mov     si, ax          ; keep hWnd in SI, since SI doesn't
  199.                                         ; change after function calls
  200.  
  201.                 INVOKE  ShowWindow,    si, SW_SHOWNOACTIVATE
  202.                 INVOKE  UpdateWindow,  si
  203.                                 
  204.                 
  205. ;
  206. ;----Create Scroll Child Window
  207. ;
  208.  
  209.                 INVOKE  CreateWindow, ADDR szScrollBar, NULL,
  210.                         WS_CHILD OR WS_VISIBLE OR WS_TABSTOP OR SBS_HORZ,
  211.                         0, 0, 0, 0, si, SCROLLCHILD, hInstance, NULL 
  212.                 mov     hWndScrol, ax
  213.  
  214.                 INVOKE  SetScrollRange, ax, SB_CTL, 0, MAXTIME, FALSE
  215.                 INVOKE  SetScrollPos,   hWndScrol, SB_CTL, AlarmTime, FALSE
  216.  
  217.                 INVOKE  ShowScrollBar,  hWndScrol, SB_CTL, TRUE
  218.                 INVOKE  ShowWindow,    hWndScrol, SW_SHOWNOACTIVATE
  219.                 INVOKE  UpdateWindow,  hWndScrol
  220.  
  221. ;
  222. ;---- Message Loop
  223. ;
  224.  
  225.                 .WHILE TRUE
  226.  
  227.                         INVOKE  GetMessage,    ADDR msg, NULL, 0, 0
  228.  
  229.                         .BREAK .IF (ax == 0)
  230.  
  231.                         INVOKE  TranslateMessage, ADDR msg
  232.                         INVOKE  DispatchMessage,  ADDR msg
  233.  
  234.                 .ENDW
  235.  
  236. ;
  237. ;---- Return to Windows
  238. ;
  239.  
  240.                 mov     ax, msg.wParam
  241. doRet:
  242.                 ret
  243.  
  244. WinMain         ENDP
  245.  
  246.  
  247. ;----------------------------------------------------------------------------;
  248. ;                                Initialize                                  ;
  249. ;----------------------------------------------------------------------------;
  250. ;                                                                            ;
  251. ; Initializes the logfont struct, sizes the Initial Window, Makes the Menu.  ;  
  252. ;                                                                            ;
  253. ; For the size of the initial window: We get the DC for the entire display,  ;
  254. ; and calculate the size of the font into tmetric. With that, we allow for   ;
  255. ; the necessary distance from the top right corner so that there's enough    ;
  256. ; space for the text.                                                        ;
  257. ;                                                                            ;
  258. ;----------------------------------------------------------------------------;
  259.  
  260. Initialize      PROC USES si di, pxStart:PINT, pyStart:PINT, 
  261.                                  pxClient:PINT, pyClient:PINT
  262.                 ; px,py,Start,Client will hold the dimensions of the initial
  263.                 ; window to create
  264.  
  265.                 LOCAL hFont:HFONT, hDC:HDC, tmetric:TEXTMETRIC
  266.                 ; locals: a handle to a font, one to a device context, 
  267.                 ;         and a textmetric structure
  268.  
  269. ;
  270. ;---- Initialize the logfont structure
  271. ;
  272.         
  273.                 mov     bx, OFFSET logfont
  274.  
  275.                 ASSUME  bx:PTR LOGFONT
  276.  
  277.                 xor     ax, ax
  278.                 mov     [bx].lfHeight, INIT_FONT; Initial Font Height
  279.                 mov     [bx].lfWidth,  ax       ; width's set according to hght
  280.                 mov     [bx].lfEscapement, ax
  281.                 mov     [bx].lfOrientation, ax
  282.                 mov     [bx].lfWeight, FW_NORMAL
  283.                 mov     [bx].lfItalic, al
  284.                 mov     [bx].lfUnderline, al
  285.                 mov     [bx].lfStrikeOut, al
  286.                 mov     [bx].lfCharSet, ANSI_CHARSET
  287.                 mov     [bx].lfOutPrecision, al
  288.                 mov     [bx].lfClipPrecision, al
  289.                 mov     [bx].lfQuality, al
  290.                 mov     [bx].lfPitchAndFamily, DEFAULT_PITCH OR FF_SWISS
  291.                 mov     [bx].lfFaceName, NULL
  292.  
  293.                 ASSUME  bx:NOTHING
  294.  
  295. ;---- Get Initial Size for the Window Based on Font
  296.  
  297.                 xor     dx, dx
  298.                 INVOKE  CreateIC, ADDR szDisplay, dx::dx, dx::dx, dx::dx
  299.                 mov     hDC, ax
  300.  
  301.                 INVOKE  CreateFontIndirect, ADDR logfont
  302.                 mov     hFont, ax
  303.  
  304.                 INVOKE  SelectObject, hDC, ax
  305.                 mov     hFont, ax
  306.  
  307.                 INVOKE  GetTextMetrics, hDC, ADDR tmetric
  308.  
  309.                 INVOKE  SelectObject, hDC, hFont
  310.  
  311.                 INVOKE  DeleteObject, ax
  312.  
  313.                 INVOKE  DeleteDC, hDC
  314.  
  315.                 INVOKE  GetSystemMetrics, SM_CXDLGFRAME ; Set window width
  316.                 shl     ax, 1                           ; frame*2       
  317.                 mov     bx, tmetric.tmAveCharWidth      ; width of font
  318.                 mov     cl, 4                           ; to shift
  319.                 shl     bx, cl                          ; width*16
  320.                 add     ax, bx                          ; frame*2 + width*16
  321.                 mov     si, pxClient                    ; address to store in
  322.                 mov     [si], ax                        ; store
  323.  
  324.                 INVOKE  GetSystemMetrics, SM_CXSCREEN   ; screen x-length
  325.                 mov     si, pxClient                    ; window width
  326.                 sub     ax, [si]                        ; start=corner-winWdth
  327.                 mov     si, pxStart                     ; store result
  328.                 mov     [si], ax
  329.  
  330.                 INVOKE  GetSystemMetrics, SM_CYDLGFRAME ; Set Height
  331.                 shl     ax, 1                           ; frame*2
  332.                 mov     bx, tmetric.tmHeight            ; height*2
  333.                 shl     bx, 1
  334.                 add     ax, bx                          ; add
  335.                 mov     si, pyClient                    ; store in pyClient
  336.                 mov     [si], ax
  337.  
  338.                 IF TOP_CORNER                           ; if Top Corner,
  339.                         xor     ax, ax                  ; yStart=0
  340.                 ELSE                                    ; else,
  341.                         INVOKE  GetSystemMetrics, SM_CYSCREEN   
  342.                         mov     si, pyClient            ; yStart = ScreenHgth
  343.                         sub     ax, [si]                ; minus yHeight
  344.                 ENDIF
  345.                 mov     si, pyStart                     ; set yStart
  346.                 mov     [si], ax
  347.  
  348.  
  349. ;
  350. ;---- Initialize the Menu
  351. ;
  352.  
  353.                 INVOKE CreatePopupMenu
  354.                 mov hMenu, ax
  355.                                                 ; Date is Initially Enabled
  356.                 INVOKE  AppendMenu, hMenu, MF_STRING OR MF_CHECKED, IDM_DATE,
  357.                                     ADDR szDateCmd
  358.                 INVOKE  AppendMenu, hMenu, MF_STRING, IDM_ALARM,ADDR szAlarmCmd
  359.                 INVOKE  AppendMenu, hMenu, MF_STRING, IDM_SET, ADDR szSetCmd
  360.                 INVOKE  AppendMenu, hMenu, MF_SEPARATOR, 0, NULL
  361.                 INVOKE  AppendMenu, hMenu, MF_STRING OR MF_CHECKED, IDM_ONTOP,
  362.                                     ADDR szOnTopCmd
  363.                 INVOKE  AppendMenu, hMenu, MF_STRING, IDM_MINIMIZE, 
  364.                                     ADDR szMinimizeCmd
  365.                 INVOKE  AppendMenu, hMenu, MF_STRING, IDM_EXIT, ADDR szExitCmd
  366.                 INVOKE  AppendMenu, hMenu, MF_SEPARATOR, 0, NULL
  367.                 INVOKE  AppendMenu, hMenu, MF_STRING, IDM_ABOUT,ADDR szAboutCmd
  368.                 ret
  369.  
  370. Initialize      ENDP
  371.  
  372. ;----------------------------------------------------------------------------;
  373. ;                                SetupTimer                                  ;
  374. ;----------------------------------------------------------------------------;
  375. ;                                                                            ;
  376. ; Setup a timer with the specified interval. If we can't set up the timer,   ;
  377. ; output a message box and exit the program.                                 ;
  378. ;----------------------------------------------------------------------------;
  379.  
  380. SetupTimer PROC NEAR, hWnd:HWND, Interval:WORD
  381.                 ; hWnd is the Handle of the Window to associate the timer with
  382.                 ; Interval is the interval in milliseconds
  383.  
  384.                 INVOKE  SetTimer, hWnd, 1, Interval, NULL
  385.                 .IF (ax == 0)
  386.                         INVOKE  MessageBox, hWnd,ADDR szTooManyTimers, 
  387.                                             ADDR szAppName,
  388.                                             MB_ICONEXCLAMATION OR MB_OK
  389.                         mov     ax, FALSE
  390.                         INVOKE PostQuitMessage, 0               ; Quit.
  391.                 .ENDIF
  392.  
  393.                 ret
  394. SetupTimer ENDP
  395.  
  396. ;----------------------------------------------------------------------------;
  397. ;                                AlarmSetup                                  ;
  398. ;----------------------------------------------------------------------------;
  399. ;                                                                            ;
  400. ; If we're going to set the alarm, kill the timer, show the scroll bar, and  ;
  401. ; resize the fonts. Enable the Alarm after it has been re-set.               ;
  402. ;                                                                            ;
  403. ; If we just set up the alarm, get a new timer,                              ;
  404. ; check the menu to enable the alarm, hide the scrollbar, resize the fonts   ;
  405. ; When creating the top window this is called to set up the initial timer.   ;
  406. ;                                                                            ;
  407. ;----------------------------------------------------------------------------;
  408.  
  409. AlarmSetup      PROC,   hWnd:HWND
  410.                 ; hWnd is the handle of the window that received WinPaint 
  411.  
  412.  
  413.                 .IF SetAlarm
  414.                         INVOKE  KillTimer, hWnd, 1
  415.                         INVOKE  Resize, hWnd
  416.                         INVOKE  ShowScrollBar, hWndScrol, SB_CTL, TRUE
  417.                         INVOKE  SetFocus, hWndScrol
  418.                         INVOKE  InvalidateRect, hWnd, NULL, TRUE
  419.                         mov     EnableAlarm, MF_CHECKED
  420.                         mov     TestAlarm, TRUE         
  421.  
  422.                 .ELSE
  423.  
  424.                         INVOKE  SetupTimer, hWnd, TIMER_SECS
  425.                         INVOKE  CheckMenuItem, hMenu, IDM_ALARM, EnableAlarm
  426.                         INVOKE  ShowScrollBar,  hWndScrol, SB_CTL, FALSE
  427.                         INVOKE  Resize,         hWnd
  428.                         INVOKE  InvalidateRect, hWnd, NULL, TRUE
  429.  
  430.                 .ENDIF          
  431.  
  432.                 ret
  433.  
  434. AlarmSetup      ENDP
  435.                 
  436.         
  437. ;----------------------------------------------------------------------------;
  438. ;                                    Resize                                  ;
  439. ;----------------------------------------------------------------------------;
  440. ;                                                                            ;
  441. ; Simple resizing, without taking into account aspect ratio.                 ;
  442. ; If we're setting the alarm, the scroll bar height will be (client hgt)/16, ;
  443. ; the length will be (client lenght)-(2*Scroll Bar Height), and the top will ;
  444. ; will start (in Client coordinates), at one SB Height right and 2 SBHeights ;
  445. ; up from the left bottom corner. Fonts width will be length/10, height will ;
  446. ; be (client height)-(3*Scroll bar height). The Scroll bar is also displayed.;
  447. ;                                                                            ;
  448. ; If we're not setting the alarm, and we're not minimized, font width will be;
  449. ; length/TIME_LEN and height will be either client height or client height/2,;
  450. ; depending on the date being enabled or not.                                ;
  451. ;                                                                            ;
  452. ; If font height is more than MAX_HEIGHT, then height is MAX_HEIGHT, and the ;
  453. ; TextRect.top is computed so that Drawing the text on the client area will  ;
  454. ; be centered.                                                               ;
  455. ;                                                                            ;
  456. ; If we're minimized, width=length/ICON_LEN, height is client height.        ;
  457. ;                                                                            ;
  458. ; The TextRect is used so that it's not recomputed everytime the Window is   ;
  459. ; repainted.                                                                 ;
  460. ;                                                                            ;
  461. ;----------------------------------------------------------------------------;
  462.  
  463. Resize          PROC,   hWnd:HWND
  464.                 ; hWnd is the handle of the window that received WinPaint 
  465.                 LOCAL   rect:RECT
  466.                 ; holds a rectangle structure
  467.  
  468.                 INVOKE  SetFocus, hWnd  ; take focus out of child window
  469.  
  470.                 INVOKE  GetClientRect, hWnd, ADDR rect ; get new rect.size
  471.                 
  472.  
  473. ;---- Find desired Text Height
  474.  
  475.                 .IF SetAlarm    
  476.  
  477. ;---- Calculate New TextRect allowing for Scroll Bar
  478.  
  479.                         mov     bx, rect.bottom   ; SXstart is length / 8
  480.                         shr     bx, 3
  481.                 
  482.                         mov     cx, rect.right    ; SLength=CLenght-2*SHeight
  483.                         sub     cx, bx 
  484.                         sub     cx, bx
  485.  
  486.                         mov     dx, rect.bottom   ; SYstart will be CHgt-2*SHgt
  487.                         sub     dx, bx
  488.                         sub     dx, bx
  489.  
  490.                         mov     TextRect.bottom, dx  ; bottom = CHgt - 3*SHgt
  491.                         sub     TextRect.bottom, bx
  492.  
  493.                         INVOKE  MoveWindow, hWndScrol, bx, dx, cx, bx, FALSE
  494.                         INVOKE  SetScrollPos,hWndScrol,SB_CTL,AlarmTime,TRUE
  495.  
  496.                         mov     ax,TextRect.bottom   ; Try to use Full Height
  497.                                                      ; for font height
  498.  
  499. ;---- Else (not setting the alarm)
  500.  
  501.                 .ELSE
  502.  
  503.                         mov     ax, rect.bottom      ; Full or Half Height
  504.                         mov     TextRect.bottom, ax
  505.                         .IF EnableDate && !Iconized
  506.                                 shr     ax, 1        ; height/2
  507.                         .ENDIF
  508.                 .ENDIF          
  509.  
  510.  
  511. ;---- Test if desired height is allowed or not
  512. ;---- If height>MAX, then height=MAX_HEIGHT. Adjust TextRect.top by
  513. ;---- substracting from the bottom the font height (or twice that if Date),
  514. ;---- dividing by two, and adding to the TextRect.top.
  515.  
  516.                 .IF (ax > MAX_HEIGHT)   
  517.                         mov     logfont.lfHeight, MAX_HEIGHT
  518.                         mov     ax, TextRect.bottom
  519.                         mov     bx, MAX_HEIGHT
  520.                         .IF EnableDate && !Iconized
  521.                                 shl     bx, 1
  522.                         .ENDIF
  523.                         sub     ax, bx
  524.                         shr     ax, 1
  525.                         mov     TextRect.top, ax
  526.                 .ELSE
  527.                         mov     logfont.lfHeight, ax
  528.                         mov     TextRect.top, 0
  529.  
  530.                 .ENDIF
  531.  
  532.                 mov     TextRect.left, 0                ; Left is Zero
  533.                 mov     ax, rect.right                  ; Same Rect length
  534.                 mov     TextRect.right, ax
  535.  
  536. ;---- Set font width according to Iconized or not.
  537.  
  538.                 .IF Iconized    
  539.                         xor     dx, dx
  540.                         mov     bx, ICON_LEN
  541.                         div     bx
  542.                 .ELSE
  543.                         shr     ax, 4        ; length/16
  544.                 .ENDIF
  545.                 mov     logfont.lfWidth, ax
  546.  
  547.                 ret
  548.  
  549. Resize          ENDP
  550.                 
  551. ;----------------------------------------------------------------------------;
  552. ;                                  PaintAlarm                                ;
  553. ;----------------------------------------------------------------------------;
  554. ;                                                                            ;
  555. ; Painting the alarmtime. Get the hours and minutes from Alarmtime, get AM   ;
  556. ; or PM by going into szAMPM either at 0 or 3, to get the appropiate string. ;
  557. ; Then we simply draw the text into the TextRect calculated by Resize, with  ;
  558. ; the font that we get from using the logfont structure.                     ;
  559. ;                                                                            ;
  560. ;----------------------------------------------------------------------------;
  561.  
  562. PaintAlarm      PROC USES si di,  hWnd:HWND, hDC:HDC
  563.                 ; hWnd is the handle of the window that received WinPaint
  564.                 ; hDC is the Device context of the window
  565.  
  566.                 LOCAL   nLength:SWORD, hFont:HFONT,
  567.                         hour:WORD, minutes:WORD, seconds:WORD
  568.                 ; Locals: nLength is the current length of the buffer
  569.                 ;         hFont is a handle to a font.  
  570.  
  571. ;---- Get Time from AlarmTime
  572.  
  573.                 xor     dx, dx
  574.                 mov     ax, AlarmTime
  575.                 mov     bx, 60t
  576.                 div     bx                      ; ax holds the hours
  577.                 mov     bx, dx                  ; bx holds the minutes
  578.  
  579.                 mov     si, ax                  ; si holds the hours
  580.                 xor     dx, dx
  581.                 mov     di, 12
  582.                 div     di                      ; will have ax=0 or 1 (am/pm)
  583.                 xor     dx, dx
  584.                 mov     cx, AMPM                ; AMPM = 3: 'am'+\0
  585.                 mul     cx
  586.                 mov     di, ax
  587.                 add     di,offset szAMPM        ; get into di the correct
  588.                                                 ; memory address        
  589.  
  590.                 mov     ax, si                  ; ax == tm_hour
  591.                 xor     dx, dx
  592.                 mov     cx, 12
  593.                 div     cx
  594.                 .IF (dx == 0)                   ; so we don't have 00:45
  595.                         mov cx, 12
  596.                 .ELSE
  597.                         mov cx, dx
  598.                 .ENDIF
  599.                 
  600. ;---- on using wsprintf below: since the wsprintf prototype has VARARG,
  601. ;---- we can't tell the assemble the distance of those arguments, so we
  602. ;---- have to do a specific far pointer to the data. wsprintf expects all
  603. ;---- pointers to be far pointers, no exceptions.
  604.  
  605.                 INVOKE  wsprintf, ADDR cBuffer, ADDR AlarmFmt,
  606.                                   cx, bx, ds::di
  607.                 mov     nLength,ax
  608.  
  609. ;---- Set Font & Draw Text
  610.  
  611.                 INVOKE  CreateFontIndirect, ADDR logfont
  612.                 mov     hFont, ax
  613.                 INVOKE  SelectObject, hDC, ax
  614.                 mov     hFont, ax
  615.                 INVOKE  DrawText, hDC, ADDR cBuffer, nLength, ADDR TextRect,
  616.                                        (DT_CENTER)
  617.                 INVOKE  SelectObject, hDC, hFont
  618.                 INVOKE  DeleteObject, ax
  619.  
  620.                 ret
  621.  
  622. PaintAlarm      ENDP
  623.  
  624. ;----------------------------------------------------------------------------;
  625. ;                                  PaintTime                                 ;
  626. ;----------------------------------------------------------------------------;
  627. ;                                                                            ;
  628. ; Painting the time. We get a index to szMonths by multiplying the [0-11]    ;
  629. ; month by 4 ('jan'+\0). We print everything to a string and store the length;
  630. ; as an index. We do this only if Date's enabled.                            ;
  631. ; For the time, we do the same AM/PM thing as in PaintAlarm, and print it to ;
  632. ; the same string after the Date's carriage return (included in DateFmt.     ;
  633. ; Then we simply draw the text into the TextRect calculated by Resize, with  ;
  634. ; the font that we get from using the logfont structure.                     ;
  635. ; If we're minimized, we also draw a rectangle around the time. We get a pen ;
  636. ; of width 2, black, and a hollow (transparent) brush. We save the pen ID in ;
  637. ; the stack so that we can deselect it later.                                ;
  638. ; Then, we check to see if we have to spring the alarm. If TestAlarm is not  ;
  639. ; enabled, check the time and re-enable TestAlarm if it's NOT the AlarmTime  ;
  640. ; (i.e. so that after we ring it it will ring in 24 hours). If TestAlarm is  ;
  641. ; set, check if EnableAlarm is set. If it is, multiply the hours by 60, add  ;
  642. ; the minutes, compare with AlarmTime. Notice that the 'mov bx, datetime' is ;
  643. ; necessary because the wsprintf and DrawText will trash ax, bx, cx. If it's ;
  644. ; alarm time, disable TestAlarm, invert the rectangle, sound a beep, revert  ;
  645. ; the rect, and do a message box. This way, TestAlarm enables us to always   ;
  646. ; have at least one check of the alarm time.                                 ;
  647. ;                                                                            ;
  648. ;----------------------------------------------------------------------------;
  649.  
  650. PaintTime       PROC USES si di,  hWnd:HWND, hDC:HDC
  651.                 ; hWnd is the handle of the window that received WinPaint
  652.                 ; hDC is the Device context of the window
  653.  
  654.                 LOCAL   nLength:SWORD, hFont:HFONT, pen:WORD,
  655.                         hour:WORD, minutes:WORD, seconds:WORD
  656.                 ; Locals: nLength is the current length of the buffer
  657.                 ;         hFont is a handle to a font.  
  658.  
  659. ;---- Set Date Variables
  660.  
  661.                 .IF (EnableDate && !Iconized) 
  662.  
  663.                        mov     ah, 2Ah               ; function: Get Date
  664.                        INVOKE  DOS3Call              ; do the interrupt
  665.                                                      ; dh will have months
  666.                                                      ; dl will have days
  667.                                                      ; cx will have years
  668.  
  669.                         mov     al, dh               ; months (1-12)
  670.                         xor     ah, ah
  671.                         xor     dh, dh               ; day-of-month (1-31)
  672.                 
  673.  
  674.                         mov     si, ax               ; (Month-1) * 4
  675.                         dec     si
  676.                         shl     si, 2
  677.                         add     si, offset szMonths
  678.  
  679. ;---- For note on wsprintf below, see PaintAlarm
  680.  
  681.                         INVOKE  wsprintf, ADDR cBuffer, ADDR DateFmt,
  682.                                   ds::si, dx, cx
  683.                         mov nLength, ax
  684.                 .ELSE
  685.                         mov nLength, 0
  686.  
  687.                 .ENDIF ; of EnableDate
  688.  
  689. ;---- Get Time from CPU clock
  690.                 xor     dx, dx
  691.  
  692.                 mov     ah, 2Ch                      ; function: Get Time
  693.                 INVOKE  DOS3Call                     ; do the interrupt
  694.                                                      ; ch will have hours
  695.                                                      ; cl will have minutes
  696.                                                      ; dh will have seconds
  697.  
  698.                 mov     al, ch                       ; hours (0-23)
  699.                 xor     ah, ah
  700.                 mov     hour, ax         
  701.                 mov     al, cl                       ; minutes (0-59)
  702.                 mov     minutes, ax          
  703.                 mov     al, dh                       ; seconds (0-59)
  704.                 mov     seconds, ax          
  705.   
  706.                 mov     ax, hour                     ; divide hour/12, multiply
  707.                 xor     dx, dx                       ; by 3 to get offset into
  708.                 mov     bx, 12                       ; szAMPM, then add the 
  709.                 div     bx                           ; szAMPM offset
  710.                 mov     si, dx                       ; dx would have hours
  711.                 xor     dx, dx
  712.                 mov     cx, AMPM
  713.                 mul     cx
  714.                 mov     bx, ax
  715.                 add     bx, offset szAMPM            ; get into di the correct
  716.                                                      ; memory address
  717.                 
  718.                 .IF (si == 0)                        ; get hour into cx
  719.                         mov  cx, 12                  ; or 12 if hour=0
  720.                 .ELSE
  721.                         mov  cx, si
  722.                 .ENDIF
  723.                 
  724.                 mov     si, nLength
  725.                 .IF !Iconized
  726.                         INVOKE  wsprintf, ADDR cBuffer[si], ADDR TimeFmt,
  727.                                           cx, minutes, seconds, ds::bx
  728.                 .ELSE
  729.                         INVOKE  wsprintf, ADDR cBuffer[si], ADDR IconFmt,
  730.                                           cx, minutes        
  731.                 .ENDIF
  732.  
  733.                 add     nLength, ax
  734.  
  735. ;---- Set Font, Draw the Text
  736.  
  737.                 INVOKE  CreateFontIndirect, ADDR logfont
  738.                 mov     hFont, ax
  739.                 INVOKE  SelectObject, hDC, ax
  740.                 mov     hFont, ax
  741.                 INVOKE  DrawText, hDC, ADDR cBuffer, nLength, ADDR TextRect,
  742.                                         DT_NOCLIP OR DT_CENTER
  743.                 INVOKE  SelectObject, hDC, hFont
  744.                 INVOKE  DeleteObject, ax
  745.  
  746. ;---- If Iconized, draw rectangle with transparent background
  747.  
  748.                 .IF Iconized
  749.  
  750.                         INVOKE  GetStockObject, HOLLOW_BRUSH
  751.                         INVOKE  SelectObject, hDC, ax
  752.  
  753.                         INVOKE  CreatePen, PS_SOLID OR PS_INSIDEFRAME, 2, 0
  754.                         INVOKE  SelectObject, hDC, ax
  755.                         push    ax
  756.  
  757.                         INVOKE  Rectangle, hDC, TextRect.left, TextRect.top,
  758.                                         TextRect.right, TextRect.bottom
  759.  
  760.                         INVOKE  GetStockObject, HOLLOW_BRUSH
  761.                         INVOKE  SelectObject, hDC, ax
  762.                         INVOKE  DeleteObject, ax
  763.  
  764.                         pop     ax
  765.                         INVOKE  SelectObject, hDC, ax
  766.                         INVOKE  DeleteObject, ax
  767.                 .ENDIF
  768.  
  769. ;---- Check for Alarm
  770.  
  771.                 xor     dx, dx
  772.                 mov     ax, hour
  773.                 mov     cx, 60t
  774.                 imul    cx
  775.                 add     ax, minutes          ; ax now holds the time in minutes
  776.  
  777.                 .IF (TestAlarm)
  778.                         .IF (EnableAlarm && (ax == AlarmTime))
  779.                                 mov     TestAlarm, FALSE
  780.                                 INVOKE  SetFocus, hWnd
  781.                                 INVOKE  InvertRect, hDC, ADDR TextRect
  782.                                 INVOKE  MessageBeep, MB_ICONASTERISK
  783.                                 INVOKE  InvertRect, hDC, ADDR TextRect
  784.                                 INVOKE  MessageBox, hWnd, ADDR szAlarmMsg,
  785.                                     ADDR szAppName,MB_ICONEXCLAMATION OR MB_OK
  786.                         .ENDIF
  787.                 .ELSE ; of TestAlarm                    ; if TestAlarm=0, means
  788.                         .IF (ax != AlarmTime)           ; we sounded the alarm. 
  789.                                 mov     TestAlarm, TRUE ; but, if time 
  790.                         .ENDIF                          ; changed, we should to
  791.                 .ENDIF                                  ; check again (allows 
  792.                                                         ; alarm every 24 hours)
  793.  
  794.                 ret
  795.  
  796. PaintTime       ENDP
  797.  
  798. ;----------------------------------------------------------------------------;
  799. ;                                  WndProc                                   ;
  800. ;----------------------------------------------------------------------------;
  801. ;                                                                            ;
  802. ; The routine called on by Windows through WinMain's dispatch message loop.  ;
  803. ; Different actions according to messages                                    ;
  804. ;       Create: Set the timer with AlarmSetup                                ;
  805. ;       Timer: Repaint the window (Invalidate it)                            ;
  806. ;       Paint: BeginPaint, call on the correct paint routine                 ;
  807. ;       SetFocus: Redraw the Window Frame                                    ;
  808. ;       Resize: Resize the fonts and scroll bar, redraw the window           ;
  809. ;       Destroy: Close window and exit program                               ;
  810. ;       LeftDouble or RightClick: If setting alarm, set it; otherwise pop up ;
  811. ;           menu.  Mouse position is in the high & low words of lParam, in   ;
  812. ;           client coords. Change them to Screen coords and show menu.       ;
  813. ;       LeftClick: Move window by tricking Windows into thinking that we've  ;
  814. ;                  hit the Caption Bar of the window. It doesn't matter that ;
  815. ;                  there isn't a caption bar.                                ;
  816. ;       Commands: Date: Toggle the switch, check the menu, resize and paint  ;
  817. ;                 EnableAlarm: Sound the alarm or not                        ;
  818. ;                 Set: Start setting the alarm                               ;
  819. ;                 About: Display a Message Box. A dialog box is up to you    ;
  820. ;                 AlwaysOnTop: Sets the Window Position to be Topmost or not ;
  821. ;                 Minimize: make Windows think we hit the 'Minimize' command ;
  822. ;                    from the system menu.                                   ;
  823. ;                 Exit: Exit program.                                        ;
  824. ;       ScrollBar: Page Moves indicate hours, lines minutes. If thumb, the   ;
  825. ;                  lParam low word holds the position. Check that we don't   ;
  826. ;                  go out of range, set the scroll position, and repaint.    ;
  827. ;       Otherwise, call the default window procedure.                        ;
  828. ;                                                                            ;
  829. ;       Notice that most of the repaints do not erase the backgrnd, only     ;
  830. ;               RedrawWindow of Resize and SetFocus. This speeds up exec,    ;
  831. ;               and drawtext will erase over everything automatically.       ; 
  832. ;                                                                            ;
  833. ;----------------------------------------------------------------------------;
  834.  
  835. WndProc         PROC FAR PASCAL, hWnd:HWND, iMessage:WORD, wParam:SWORD,
  836.                                  lParam:SDWORD
  837.                 ; Windows gives us: the handle of the Window, the Message ID,
  838.                 ; and two parameters for the message
  839.  
  840.                 LOCAL   hDC:HDC, ps:PAINTSTRUCT, point:POINT
  841.                 ; locals: a handle to a device context, a paint structure,
  842.                 ;         a point structure
  843.  
  844.                 .IF     (iMessage == WM_CREATE)
  845.                         INVOKE  SetupTimer, hWnd, TIMER_SECS
  846.                         INVOKE  Resize, hWnd
  847.                         INVOKE  InvalidateRect, hWnd, NULL, FALSE
  848.  
  849.                 .ELSEIF (iMessage == WM_TIMER)
  850.                         INVOKE  InvalidateRect, hWnd, NULL, FALSE
  851.  
  852.                 .ELSEIF (iMessage == WM_PAINT)
  853.                         INVOKE  BeginPaint, hWnd, ADDR ps
  854.                         mov     hDC, ax
  855.                         .IF SetAlarm
  856.                                 INVOKE  PaintAlarm, hWnd, hDC
  857.                         .ELSE
  858.                                 INVOKE  PaintTime, hWnd, hDC
  859.                         .ENDIF
  860.                         INVOKE  EndPaint, hWnd, ADDR ps
  861.  
  862.                 .ELSEIF (iMessage == WM_SETFOCUS)
  863.                         INVOKE  RedrawWindow, hWnd, NULL, NULL, 
  864.                                               RDW_FRAME OR RDW_UPDATENOW
  865.  
  866.                 .ELSEIF (iMessage == WM_SIZE)
  867.                         .IF ((wParam != SIZE_MINIMIZED) && Iconized)
  868.                                 mov     Iconized, FALSE
  869.                                 INVOKE  KillTimer, hWnd, 1
  870.                                 INVOKE  SetupTimer, hWnd, TIMER_SECS
  871.                         .ENDIF
  872.                         INVOKE  Resize, hWnd
  873.                         INVOKE  RedrawWindow, hWnd, NULL, NULL,
  874.                                               RDW_ERASE OR RDW_INVALIDATE
  875.                 
  876.                 .ELSEIF (iMessage == WM_DESTROY)
  877.                         INVOKE  KillTimer, hWnd, 1
  878.                         INVOKE  PostQuitMessage, 0
  879.  
  880.                 .ELSEIF (iMessage==WM_LBUTTONDBLCLK)||(iMessage==WM_RBUTTONUP)
  881.                         .IF SetAlarm
  882.                                 xor SetAlarm, TRUE
  883.                                 INVOKE  AlarmSetup, hWnd
  884.                         .ELSE
  885.                                 mov     di, word ptr lParam
  886.                                 mov     point.x, di
  887.                                 mov     di, word ptr (lParam+2)
  888.                                 mov     point.y, di
  889.                                 INVOKE  ClientToScreen, hWnd, ADDR point
  890.                                 INVOKE  TrackPopupMenu, hMenu, TPM_LEFTALIGN,
  891.                                         point.x, point.y, 0, hWnd, NULL          
  892.                                 INVOKE  InvalidateRect, hWnd, NULL, TRUE
  893.                         .ENDIF
  894.  
  895.                 .ELSEIF (iMessage == WM_LBUTTONDOWN)
  896.                         INVOKE DefWindowProc, hWnd, WM_NCLBUTTONDOWN, 
  897.                                 HTCAPTION, lParam
  898.  
  899.                 .ELSEIF (iMessage == WM_COMMAND)
  900.  
  901.                         .IF (wParam == IDM_DATE)
  902.                                 xor     EnableDate, MF_CHECKED
  903.                                 INVOKE  CheckMenuItem,hMenu,wParam,EnableDate
  904.                                 INVOKE  Resize, hWnd
  905.                                 INVOKE  InvalidateRect, hWnd, NULL, FALSE                       
  906.  
  907.                         .ELSEIF (wParam == IDM_ALARM)
  908.                                 xor     EnableAlarm, MF_CHECKED
  909.                                 mov     TestAlarm, TRUE
  910.                                 INVOKE  CheckMenuItem,hMenu,wParam,EnableAlarm
  911.  
  912.                         .ELSEIF (wParam == IDM_SET)
  913.                                 mov     SetAlarm, TRUE
  914.                                 INVOKE  AlarmSetup, hWnd
  915.  
  916.                         .ELSEIF (wParam == IDM_ABOUT)
  917.                                 INVOKE  MessageBox, hWnd, ADDR szAboutText,
  918.                                      ADDR szAppName, MB_ICONASTERISK OR MB_OK
  919.  
  920.                         .ELSEIF (wParam == IDM_ONTOP)
  921.                                 xor     AlwaysOnTop, MF_CHECKED
  922.                                 INVOKE  CheckMenuItem,hMenu,wParam,AlwaysOnTop
  923.                                 .IF AlwaysOnTop
  924.                                         mov     ax, HWND_TOPMOST
  925.                                 .ELSE
  926.                                         mov     ax, HWND_NOTOPMOST
  927.                                 .ENDIF
  928.  
  929.                                 INVOKE  SetWindowPos, hWnd, ax,
  930.                                         0, 0, 0, 0, SWP_NOMOVE OR SWP_NOSIZE
  931.                         
  932.                         .ELSEIF (wParam == IDM_EXIT)
  933.                                 INVOKE  KillTimer, hWnd, 1
  934.                                 INVOKE  PostQuitMessage, 0
  935.  
  936.                         .ELSEIF (wParam == IDM_MINIMIZE)
  937.                                 mov     Iconized, TRUE
  938.                                 INVOKE  KillTimer, hWnd, 1
  939.                                 INVOKE  SetupTimer, hWnd, TIMER_MINS
  940.                                 INVOKE  DefWindowProc, hWnd, WM_SYSCOMMAND, 
  941.                                                        SC_ICON, NULL
  942.  
  943.                         .ENDIF
  944.                 
  945.                 .ELSEIF (iMessage == WM_HSCROLL)
  946.                         .IF (wParam == SB_PAGEDOWN)
  947.                                 add     AlarmTime, 10t
  948.                         .ELSEIF (wParam == SB_LINEDOWN)
  949.                                 inc     AlarmTime
  950.                         .ELSEIF (wParam == SB_PAGEUP)
  951.                                 sub     AlarmTime, 10t
  952.                         .ELSEIF (wParam == SB_LINEUP)
  953.                                 dec     AlarmTime
  954.                         .ELSEIF (wParam == SB_TOP)
  955.                                 mov     AlarmTime, MAXTIME
  956.                         .ELSEIF (wParam == SB_BOTTOM)
  957.                                 mov     AlarmTime, 0t
  958.                         .ELSEIF (wParam == SB_THUMBPOSITION)
  959.                                 mov     ax, word ptr lParam
  960.                                 mov     AlarmTime, ax
  961.                         .ELSEIF (wParam == SB_THUMBTRACK)
  962.                                 mov     ax, word ptr lParam
  963.                                 mov     AlarmTime, ax
  964.                         .ELSEIF (wParam == SB_LINEDOWN)
  965.                                 mov     ax, word ptr lParam
  966.                                 mov     AlarmTime, ax
  967.                         .ENDIF
  968.                                 .IF (AlarmTime < 0)
  969.                                         mov     AlarmTime, 0t
  970.                                 .ELSEIF (AlarmTime > MAXTIME)
  971.                                         mov     AlarmTime, MAXTIME
  972.                                 .ENDIF
  973.                         
  974.                                 INVOKE  SetScrollPos, hWndScrol, SB_CTL,
  975.                                         AlarmTime, TRUE
  976.                                 INVOKE  InvalidateRect, hWnd,
  977.                                                         ADDR TextRect, FALSE
  978.  
  979.                 .ELSE
  980.                                 INVOKE  DefWindowProc, hWnd, iMessage,
  981.                                                        wParam,lParam
  982.                                 jmp     doRet
  983.  
  984.                 .ENDIF
  985.  
  986.                 mov ax, 0
  987.                 cwd
  988. doRet:
  989.                 ret
  990.  
  991. WndProc         ENDP
  992.  
  993.  
  994.                 END  __astart   ; so that the code of the application will
  995.                                 ; start with the Windows start-up code
  996.  
  997.