;----------------------------------------------------------------------------; ; winclock.ASM ; ;----------------------------------------------------------------------------; ; ; ; Sample MASM 6.1 program using the Windows API Interface. It heavily relies ; ; on the MASM 6.x high level constructs like INVOKE and .IF/.ELSE/.ENDIF. ; ; The program, although simple enough, actually does quite a bit. Double ; ; clicking on the window will bring up a popup menu with other options, ; ; including setting an alarm. ; ; ; ; Adding to this code should be simple enough. New features could include ; ; making sure that fonts are displayed with the appropiate aspect ratio. ; ; ; ; Note that this program doesn't attempt to teach how to program in Windows. ; ; For that you should consult the Windows Software Developers Kit or other ; ; reference books. ; ; ; ;----------------------------------------------------------------------------; .model small, pascal, nearstack .286 ?WINPROLOGUE = 1 NOKERNEL = 1 NOSOUND = 1 NOCOMM = 1 NODRIVERS = 1 include win.inc ; Converted from WINDOWS.H ;----------------------------------------------------------------------------; ; Prototypes & External Definitions ; ;----------------------------------------------------------------------------; WinMain PROTO PASCAL, hInstance:HANDLE, hPrevInstance:HANDLE, lpszCmdLine:LPSTR, nCmdShow:SWORD WndProc PROTO FAR PASCAL, :HWND, :WORD, :SWORD, :SDWORD Initialize PROTO, :PINT, :PINT, :PINT, :PINT Resize PROTO, :HWND SetupTimer PROTO, :HWND, :WORD extern __astart:proc ; When Windows load an app, it expects astart ; to have the necessary start-up code. We get ; astart from APPENTRY.ASM ;----------------------------------------------------------------------------; ; Numeric Equates ; ;----------------------------------------------------------------------------; INIT_FONT EQU 30t ; initial font height MAX_HEIGHT EQU 100 ; Maximum Font Height TOP_CORNER EQU 1 ; Set to 0 for Lower-Right Corner Clock IDM_DATE EQU 1t ; definitions for Menu items IDM_ALARM EQU 2t IDM_SET EQU 3t IDM_MENU EQU 4t IDM_EXIT EQU 5t IDM_ABOUT EQU 6t IDM_MINIMIZE EQU 7t IDM_ONTOP EQU 8t AMPM EQU 3t ; length of am/pm strings SCROLLCHILD EQU 30t ; identifies scroll: any number will do MAXTIME EQU 1439t ; max # of minutes TIMER_SECS EQU 1000t ; timer interval: 1000 mill = 1 second TIMER_MINS EQU 60000t ; interval for 1 minute: 1000 * 60 ICON_LEN EQU 7t ; # of chars to display icon time ;----------------------------------------------------------------------------; ; Data Segments ; ;----------------------------------------------------------------------------; .const szAppName SBYTE "WINClock",0 DateFmt SBYTE " %s %2d, %04d ",13,10,0 TimeFmt SBYTE " %d:%02d:%02d %s ",0 IconFmt SBYTE " %d:%02d ",0 AlarmFmt SBYTE " %d:%02d %s ",0 szAMPM SBYTE "am",0,"pm",0 szTooManyTimers SBYTE "Too many clocks or timers!",0 szAlarmMsg SBYTE "Remember your Appointment!",0 szMonths SBYTE "Jan",0,"Feb",0,"Mar",0,"Apr",0,"May",0,"Jun",0, "Jul",0,"Aug",0,"Sep",0,"Oct",0,"Nov",0,"Dec",0 szDateCmd SBYTE "Enable &Date", 0 szSetCmd SBYTE "S&et Alarm",0 szAlarmCmd SBYTE "Enable &Alarm", 0 szOnTopCmd SBYTE "Always on &Top",0 szMinimizeCmd SBYTE "&Minimize", 0 szExitCmd SBYTE "E&xit", 0 szAboutCmd SBYTE "About &Clock...", 0 szAboutText SBYTE "Assembler Program Using the Windows API", 0 szDisplay SBYTE "DISPLAY", 0 ; to get a handle to entire display szScrollBar SBYTE "scrollbar", 0 ; to create scrollbar 'class' .data EnableDate BYTE MF_CHECKED ; Date initially enabled EnableAlarm BYTE MF_UNCHECKED ; Alarm initially disabled AlwaysOnTop BYTE MF_CHECKED ; Window will be on topmost Iconized BYTE FALSE ; Clock initially Normal Size TestAlarm BYTE FALSE ; so that we know to test for the alarm SetAlarm BYTE FALSE ; signalling while we set the alarm AlarmTime WORD (MAXTIME/2)+1 ; initial alarm time: 12:00 pm .data? cBuffer SBYTE 40 dup (?) ; buffer to receive text for drawing hMenu HMENU ? ; handle to Popup Menu logfont LOGFONT { } ; logical font structure hWndScrol HWND ? ; handle to the scroll window TextRect RECT { } ; rectangle to draw text in ;----------------------------------------------------------------------------; ; Code Segment ; ;----------------------------------------------------------------------------; .code ;----------------------------------------------------------------------------; ; WinMain ; ;----------------------------------------------------------------------------; ; ; ; Main routine called by Windows in program start. If no previous instances, ; ; sets up a window class and registers it. Initializes the program with ; ; Initialize, creates a top window with the coordinates from Initialize, sets; ; up a child scroll bar control, and sets up the message loop. ; ; ; ;----------------------------------------------------------------------------; WinMain PROC, hInstance:HANDLE, hPrevInstance:HANDLE, lpszCmdLine:LPSTR, nCmdShow:SWORD LOCAL msg:MSG, wndclass:WNDCLASS, xStart:SWORD, yStart:SWORD, xClient:SWORD, yClient:SWORD ; Local variables: msg: message to be used in the message loop ; wndclass: temp. to store window class ; x,y Start-Client: Size of Initial Window ; ;--- Check for previous instances ; .IF (hPrevInstance == 0) lea di, wndclass ; because we use a NEARSTACK, ASSUME di:PTR WNDCLASS ; ss=ds mov ax, CS_HREDRAW OR CS_VREDRAW OR CS_DBLCLKS mov [di].style, ax mov WORD PTR [di].lpfnWndProc, LROFFSET WndProc mov WORD PTR [di].lpfnWndProc+2, SEG WndProc xor ax,ax mov [di].cbClsExtra, ax mov [di].cbWndExtra, ax mov [di].hIcon, ax ; null icon: we will draw it mov ax, hInstance mov [di].hInstance, ax INVOKE LoadCursor, NULL, IDC_ARROW mov [di].hCursor, ax INVOKE GetStockObject, WHITE_BRUSH mov [di].hbrBackground, ax xor ax, ax mov WORD PTR [di].lpszMenuName, ax mov WORD PTR [di].lpszMenuName+2, ax mov WORD PTR [di].lpszClassName, OFFSET szAppName mov WORD PTR [di].lpszClassName+2, ds INVOKE RegisterClass, di .IF (ax == 0) mov ax, FALSE jmp doRet .ENDIF ASSUME di:NOTHING .ENDIF ;--- End of IF (hPrevInstance == 0) ; ;---- Initialize ; INVOKE Initialize, ADDR xStart, ADDR yStart, ADDR xClient, ADDR yClient ; ;---- Create Top Window ; INVOKE CreateWindowEx, WS_EX_TOPMOST, ADDR szAppName, ADDR szAppName, WS_BORDER OR WS_POPUP OR WS_THICKFRAME, xStart, yStart, xClient, yClient, NULL, NULL, hInstance, NULL mov si, ax ; keep hWnd in SI, since SI doesn't ; change after function calls INVOKE ShowWindow, si, SW_SHOWNOACTIVATE INVOKE UpdateWindow, si ; ;----Create Scroll Child Window ; INVOKE CreateWindow, ADDR szScrollBar, NULL, WS_CHILD OR WS_VISIBLE OR WS_TABSTOP OR SBS_HORZ, 0, 0, 0, 0, si, SCROLLCHILD, hInstance, NULL mov hWndScrol, ax INVOKE SetScrollRange, ax, SB_CTL, 0, MAXTIME, FALSE INVOKE SetScrollPos, hWndScrol, SB_CTL, AlarmTime, FALSE INVOKE ShowScrollBar, hWndScrol, SB_CTL, TRUE INVOKE ShowWindow, hWndScrol, SW_SHOWNOACTIVATE INVOKE UpdateWindow, hWndScrol ; ;---- Message Loop ; .WHILE TRUE INVOKE GetMessage, ADDR msg, NULL, 0, 0 .BREAK .IF (ax == 0) INVOKE TranslateMessage, ADDR msg INVOKE DispatchMessage, ADDR msg .ENDW ; ;---- Return to Windows ; mov ax, msg.wParam doRet: ret WinMain ENDP ;----------------------------------------------------------------------------; ; Initialize ; ;----------------------------------------------------------------------------; ; ; ; Initializes the logfont struct, sizes the Initial Window, Makes the Menu. ; ; ; ; For the size of the initial window: We get the DC for the entire display, ; ; and calculate the size of the font into tmetric. With that, we allow for ; ; the necessary distance from the top right corner so that there's enough ; ; space for the text. ; ; ; ;----------------------------------------------------------------------------; Initialize PROC USES si di, pxStart:PINT, pyStart:PINT, pxClient:PINT, pyClient:PINT ; px,py,Start,Client will hold the dimensions of the initial ; window to create LOCAL hFont:HFONT, hDC:HDC, tmetric:TEXTMETRIC ; locals: a handle to a font, one to a device context, ; and a textmetric structure ; ;---- Initialize the logfont structure ; mov bx, OFFSET logfont ASSUME bx:PTR LOGFONT xor ax, ax mov [bx].lfHeight, INIT_FONT; Initial Font Height mov [bx].lfWidth, ax ; width's set according to hght mov [bx].lfEscapement, ax mov [bx].lfOrientation, ax mov [bx].lfWeight, FW_NORMAL mov [bx].lfItalic, al mov [bx].lfUnderline, al mov [bx].lfStrikeOut, al mov [bx].lfCharSet, ANSI_CHARSET mov [bx].lfOutPrecision, al mov [bx].lfClipPrecision, al mov [bx].lfQuality, al mov [bx].lfPitchAndFamily, DEFAULT_PITCH OR FF_SWISS mov [bx].lfFaceName, NULL ASSUME bx:NOTHING ;---- Get Initial Size for the Window Based on Font xor dx, dx INVOKE CreateIC, ADDR szDisplay, dx::dx, dx::dx, dx::dx mov hDC, ax INVOKE CreateFontIndirect, ADDR logfont mov hFont, ax INVOKE SelectObject, hDC, ax mov hFont, ax INVOKE GetTextMetrics, hDC, ADDR tmetric INVOKE SelectObject, hDC, hFont INVOKE DeleteObject, ax INVOKE DeleteDC, hDC INVOKE GetSystemMetrics, SM_CXDLGFRAME ; Set window width shl ax, 1 ; frame*2 mov bx, tmetric.tmAveCharWidth ; width of font mov cl, 4 ; to shift shl bx, cl ; width*16 add ax, bx ; frame*2 + width*16 mov si, pxClient ; address to store in mov [si], ax ; store INVOKE GetSystemMetrics, SM_CXSCREEN ; screen x-length mov si, pxClient ; window width sub ax, [si] ; start=corner-winWdth mov si, pxStart ; store result mov [si], ax INVOKE GetSystemMetrics, SM_CYDLGFRAME ; Set Height shl ax, 1 ; frame*2 mov bx, tmetric.tmHeight ; height*2 shl bx, 1 add ax, bx ; add mov si, pyClient ; store in pyClient mov [si], ax IF TOP_CORNER ; if Top Corner, xor ax, ax ; yStart=0 ELSE ; else, INVOKE GetSystemMetrics, SM_CYSCREEN mov si, pyClient ; yStart = ScreenHgth sub ax, [si] ; minus yHeight ENDIF mov si, pyStart ; set yStart mov [si], ax ; ;---- Initialize the Menu ; INVOKE CreatePopupMenu mov hMenu, ax ; Date is Initially Enabled INVOKE AppendMenu, hMenu, MF_STRING OR MF_CHECKED, IDM_DATE, ADDR szDateCmd INVOKE AppendMenu, hMenu, MF_STRING, IDM_ALARM,ADDR szAlarmCmd INVOKE AppendMenu, hMenu, MF_STRING, IDM_SET, ADDR szSetCmd INVOKE AppendMenu, hMenu, MF_SEPARATOR, 0, NULL INVOKE AppendMenu, hMenu, MF_STRING OR MF_CHECKED, IDM_ONTOP, ADDR szOnTopCmd INVOKE AppendMenu, hMenu, MF_STRING, IDM_MINIMIZE, ADDR szMinimizeCmd INVOKE AppendMenu, hMenu, MF_STRING, IDM_EXIT, ADDR szExitCmd INVOKE AppendMenu, hMenu, MF_SEPARATOR, 0, NULL INVOKE AppendMenu, hMenu, MF_STRING, IDM_ABOUT,ADDR szAboutCmd ret Initialize ENDP ;----------------------------------------------------------------------------; ; SetupTimer ; ;----------------------------------------------------------------------------; ; ; ; Setup a timer with the specified interval. If we can't set up the timer, ; ; output a message box and exit the program. ; ;----------------------------------------------------------------------------; SetupTimer PROC NEAR, hWnd:HWND, Interval:WORD ; hWnd is the Handle of the Window to associate the timer with ; Interval is the interval in milliseconds INVOKE SetTimer, hWnd, 1, Interval, NULL .IF (ax == 0) INVOKE MessageBox, hWnd,ADDR szTooManyTimers, ADDR szAppName, MB_ICONEXCLAMATION OR MB_OK mov ax, FALSE INVOKE PostQuitMessage, 0 ; Quit. .ENDIF ret SetupTimer ENDP ;----------------------------------------------------------------------------; ; AlarmSetup ; ;----------------------------------------------------------------------------; ; ; ; If we're going to set the alarm, kill the timer, show the scroll bar, and ; ; resize the fonts. Enable the Alarm after it has been re-set. ; ; ; ; If we just set up the alarm, get a new timer, ; ; check the menu to enable the alarm, hide the scrollbar, resize the fonts ; ; When creating the top window this is called to set up the initial timer. ; ; ; ;----------------------------------------------------------------------------; AlarmSetup PROC, hWnd:HWND ; hWnd is the handle of the window that received WinPaint .IF SetAlarm INVOKE KillTimer, hWnd, 1 INVOKE Resize, hWnd INVOKE ShowScrollBar, hWndScrol, SB_CTL, TRUE INVOKE SetFocus, hWndScrol INVOKE InvalidateRect, hWnd, NULL, TRUE mov EnableAlarm, MF_CHECKED mov TestAlarm, TRUE .ELSE INVOKE SetupTimer, hWnd, TIMER_SECS INVOKE CheckMenuItem, hMenu, IDM_ALARM, EnableAlarm INVOKE ShowScrollBar, hWndScrol, SB_CTL, FALSE INVOKE Resize, hWnd INVOKE InvalidateRect, hWnd, NULL, TRUE .ENDIF ret AlarmSetup ENDP ;----------------------------------------------------------------------------; ; Resize ; ;----------------------------------------------------------------------------; ; ; ; Simple resizing, without taking into account aspect ratio. ; ; If we're setting the alarm, the scroll bar height will be (client hgt)/16, ; ; the length will be (client lenght)-(2*Scroll Bar Height), and the top will ; ; will start (in Client coordinates), at one SB Height right and 2 SBHeights ; ; up from the left bottom corner. Fonts width will be length/10, height will ; ; be (client height)-(3*Scroll bar height). The Scroll bar is also displayed.; ; ; ; If we're not setting the alarm, and we're not minimized, font width will be; ; length/TIME_LEN and height will be either client height or client height/2,; ; depending on the date being enabled or not. ; ; ; ; If font height is more than MAX_HEIGHT, then height is MAX_HEIGHT, and the ; ; TextRect.top is computed so that Drawing the text on the client area will ; ; be centered. ; ; ; ; If we're minimized, width=length/ICON_LEN, height is client height. ; ; ; ; The TextRect is used so that it's not recomputed everytime the Window is ; ; repainted. ; ; ; ;----------------------------------------------------------------------------; Resize PROC, hWnd:HWND ; hWnd is the handle of the window that received WinPaint LOCAL rect:RECT ; holds a rectangle structure INVOKE SetFocus, hWnd ; take focus out of child window INVOKE GetClientRect, hWnd, ADDR rect ; get new rect.size ;---- Find desired Text Height .IF SetAlarm ;---- Calculate New TextRect allowing for Scroll Bar mov bx, rect.bottom ; SXstart is length / 8 shr bx, 3 mov cx, rect.right ; SLength=CLenght-2*SHeight sub cx, bx sub cx, bx mov dx, rect.bottom ; SYstart will be CHgt-2*SHgt sub dx, bx sub dx, bx mov TextRect.bottom, dx ; bottom = CHgt - 3*SHgt sub TextRect.bottom, bx INVOKE MoveWindow, hWndScrol, bx, dx, cx, bx, FALSE INVOKE SetScrollPos,hWndScrol,SB_CTL,AlarmTime,TRUE mov ax,TextRect.bottom ; Try to use Full Height ; for font height ;---- Else (not setting the alarm) .ELSE mov ax, rect.bottom ; Full or Half Height mov TextRect.bottom, ax .IF EnableDate && !Iconized shr ax, 1 ; height/2 .ENDIF .ENDIF ;---- Test if desired height is allowed or not ;---- If height>MAX, then height=MAX_HEIGHT. Adjust TextRect.top by ;---- substracting from the bottom the font height (or twice that if Date), ;---- dividing by two, and adding to the TextRect.top. .IF (ax > MAX_HEIGHT) mov logfont.lfHeight, MAX_HEIGHT mov ax, TextRect.bottom mov bx, MAX_HEIGHT .IF EnableDate && !Iconized shl bx, 1 .ENDIF sub ax, bx shr ax, 1 mov TextRect.top, ax .ELSE mov logfont.lfHeight, ax mov TextRect.top, 0 .ENDIF mov TextRect.left, 0 ; Left is Zero mov ax, rect.right ; Same Rect length mov TextRect.right, ax ;---- Set font width according to Iconized or not. .IF Iconized xor dx, dx mov bx, ICON_LEN div bx .ELSE shr ax, 4 ; length/16 .ENDIF mov logfont.lfWidth, ax ret Resize ENDP ;----------------------------------------------------------------------------; ; PaintAlarm ; ;----------------------------------------------------------------------------; ; ; ; Painting the alarmtime. Get the hours and minutes from Alarmtime, get AM ; ; or PM by going into szAMPM either at 0 or 3, to get the appropiate string. ; ; Then we simply draw the text into the TextRect calculated by Resize, with ; ; the font that we get from using the logfont structure. ; ; ; ;----------------------------------------------------------------------------; PaintAlarm PROC USES si di, hWnd:HWND, hDC:HDC ; hWnd is the handle of the window that received WinPaint ; hDC is the Device context of the window LOCAL nLength:SWORD, hFont:HFONT, hour:WORD, minutes:WORD, seconds:WORD ; Locals: nLength is the current length of the buffer ; hFont is a handle to a font. ;---- Get Time from AlarmTime xor dx, dx mov ax, AlarmTime mov bx, 60t div bx ; ax holds the hours mov bx, dx ; bx holds the minutes mov si, ax ; si holds the hours xor dx, dx mov di, 12 div di ; will have ax=0 or 1 (am/pm) xor dx, dx mov cx, AMPM ; AMPM = 3: 'am'+\0 mul cx mov di, ax add di,offset szAMPM ; get into di the correct ; memory address mov ax, si ; ax == tm_hour xor dx, dx mov cx, 12 div cx .IF (dx == 0) ; so we don't have 00:45 mov cx, 12 .ELSE mov cx, dx .ENDIF ;---- on using wsprintf below: since the wsprintf prototype has VARARG, ;---- we can't tell the assemble the distance of those arguments, so we ;---- have to do a specific far pointer to the data. wsprintf expects all ;---- pointers to be far pointers, no exceptions. INVOKE wsprintf, ADDR cBuffer, ADDR AlarmFmt, cx, bx, ds::di mov nLength,ax ;---- Set Font & Draw Text INVOKE CreateFontIndirect, ADDR logfont mov hFont, ax INVOKE SelectObject, hDC, ax mov hFont, ax INVOKE DrawText, hDC, ADDR cBuffer, nLength, ADDR TextRect, (DT_CENTER) INVOKE SelectObject, hDC, hFont INVOKE DeleteObject, ax ret PaintAlarm ENDP ;----------------------------------------------------------------------------; ; PaintTime ; ;----------------------------------------------------------------------------; ; ; ; Painting the time. We get a index to szMonths by multiplying the [0-11] ; ; month by 4 ('jan'+\0). We print everything to a string and store the length; ; as an index. We do this only if Date's enabled. ; ; For the time, we do the same AM/PM thing as in PaintAlarm, and print it to ; ; the same string after the Date's carriage return (included in DateFmt. ; ; Then we simply draw the text into the TextRect calculated by Resize, with ; ; the font that we get from using the logfont structure. ; ; If we're minimized, we also draw a rectangle around the time. We get a pen ; ; of width 2, black, and a hollow (transparent) brush. We save the pen ID in ; ; the stack so that we can deselect it later. ; ; Then, we check to see if we have to spring the alarm. If TestAlarm is not ; ; enabled, check the time and re-enable TestAlarm if it's NOT the AlarmTime ; ; (i.e. so that after we ring it it will ring in 24 hours). If TestAlarm is ; ; set, check if EnableAlarm is set. If it is, multiply the hours by 60, add ; ; the minutes, compare with AlarmTime. Notice that the 'mov bx, datetime' is ; ; necessary because the wsprintf and DrawText will trash ax, bx, cx. If it's ; ; alarm time, disable TestAlarm, invert the rectangle, sound a beep, revert ; ; the rect, and do a message box. This way, TestAlarm enables us to always ; ; have at least one check of the alarm time. ; ; ; ;----------------------------------------------------------------------------; PaintTime PROC USES si di, hWnd:HWND, hDC:HDC ; hWnd is the handle of the window that received WinPaint ; hDC is the Device context of the window LOCAL nLength:SWORD, hFont:HFONT, pen:WORD, hour:WORD, minutes:WORD, seconds:WORD ; Locals: nLength is the current length of the buffer ; hFont is a handle to a font. ;---- Set Date Variables .IF (EnableDate && !Iconized) mov ah, 2Ah ; function: Get Date INVOKE DOS3Call ; do the interrupt ; dh will have months ; dl will have days ; cx will have years mov al, dh ; months (1-12) xor ah, ah xor dh, dh ; day-of-month (1-31) mov si, ax ; (Month-1) * 4 dec si shl si, 2 add si, offset szMonths ;---- For note on wsprintf below, see PaintAlarm INVOKE wsprintf, ADDR cBuffer, ADDR DateFmt, ds::si, dx, cx mov nLength, ax .ELSE mov nLength, 0 .ENDIF ; of EnableDate ;---- Get Time from CPU clock xor dx, dx mov ah, 2Ch ; function: Get Time INVOKE DOS3Call ; do the interrupt ; ch will have hours ; cl will have minutes ; dh will have seconds mov al, ch ; hours (0-23) xor ah, ah mov hour, ax mov al, cl ; minutes (0-59) mov minutes, ax mov al, dh ; seconds (0-59) mov seconds, ax mov ax, hour ; divide hour/12, multiply xor dx, dx ; by 3 to get offset into mov bx, 12 ; szAMPM, then add the div bx ; szAMPM offset mov si, dx ; dx would have hours xor dx, dx mov cx, AMPM mul cx mov bx, ax add bx, offset szAMPM ; get into di the correct ; memory address .IF (si == 0) ; get hour into cx mov cx, 12 ; or 12 if hour=0 .ELSE mov cx, si .ENDIF mov si, nLength .IF !Iconized INVOKE wsprintf, ADDR cBuffer[si], ADDR TimeFmt, cx, minutes, seconds, ds::bx .ELSE INVOKE wsprintf, ADDR cBuffer[si], ADDR IconFmt, cx, minutes .ENDIF add nLength, ax ;---- Set Font, Draw the Text INVOKE CreateFontIndirect, ADDR logfont mov hFont, ax INVOKE SelectObject, hDC, ax mov hFont, ax INVOKE DrawText, hDC, ADDR cBuffer, nLength, ADDR TextRect, DT_NOCLIP OR DT_CENTER INVOKE SelectObject, hDC, hFont INVOKE DeleteObject, ax ;---- If Iconized, draw rectangle with transparent background .IF Iconized INVOKE GetStockObject, HOLLOW_BRUSH INVOKE SelectObject, hDC, ax INVOKE CreatePen, PS_SOLID OR PS_INSIDEFRAME, 2, 0 INVOKE SelectObject, hDC, ax push ax INVOKE Rectangle, hDC, TextRect.left, TextRect.top, TextRect.right, TextRect.bottom INVOKE GetStockObject, HOLLOW_BRUSH INVOKE SelectObject, hDC, ax INVOKE DeleteObject, ax pop ax INVOKE SelectObject, hDC, ax INVOKE DeleteObject, ax .ENDIF ;---- Check for Alarm xor dx, dx mov ax, hour mov cx, 60t imul cx add ax, minutes ; ax now holds the time in minutes .IF (TestAlarm) .IF (EnableAlarm && (ax == AlarmTime)) mov TestAlarm, FALSE INVOKE SetFocus, hWnd INVOKE InvertRect, hDC, ADDR TextRect INVOKE MessageBeep, MB_ICONASTERISK INVOKE InvertRect, hDC, ADDR TextRect INVOKE MessageBox, hWnd, ADDR szAlarmMsg, ADDR szAppName,MB_ICONEXCLAMATION OR MB_OK .ENDIF .ELSE ; of TestAlarm ; if TestAlarm=0, means .IF (ax != AlarmTime) ; we sounded the alarm. mov TestAlarm, TRUE ; but, if time .ENDIF ; changed, we should to .ENDIF ; check again (allows ; alarm every 24 hours) ret PaintTime ENDP ;----------------------------------------------------------------------------; ; WndProc ; ;----------------------------------------------------------------------------; ; ; ; The routine called on by Windows through WinMain's dispatch message loop. ; ; Different actions according to messages ; ; Create: Set the timer with AlarmSetup ; ; Timer: Repaint the window (Invalidate it) ; ; Paint: BeginPaint, call on the correct paint routine ; ; SetFocus: Redraw the Window Frame ; ; Resize: Resize the fonts and scroll bar, redraw the window ; ; Destroy: Close window and exit program ; ; LeftDouble or RightClick: If setting alarm, set it; otherwise pop up ; ; menu. Mouse position is in the high & low words of lParam, in ; ; client coords. Change them to Screen coords and show menu. ; ; LeftClick: Move window by tricking Windows into thinking that we've ; ; hit the Caption Bar of the window. It doesn't matter that ; ; there isn't a caption bar. ; ; Commands: Date: Toggle the switch, check the menu, resize and paint ; ; EnableAlarm: Sound the alarm or not ; ; Set: Start setting the alarm ; ; About: Display a Message Box. A dialog box is up to you ; ; AlwaysOnTop: Sets the Window Position to be Topmost or not ; ; Minimize: make Windows think we hit the 'Minimize' command ; ; from the system menu. ; ; Exit: Exit program. ; ; ScrollBar: Page Moves indicate hours, lines minutes. If thumb, the ; ; lParam low word holds the position. Check that we don't ; ; go out of range, set the scroll position, and repaint. ; ; Otherwise, call the default window procedure. ; ; ; ; Notice that most of the repaints do not erase the backgrnd, only ; ; RedrawWindow of Resize and SetFocus. This speeds up exec, ; ; and drawtext will erase over everything automatically. ; ; ; ;----------------------------------------------------------------------------; WndProc PROC FAR PASCAL, hWnd:HWND, iMessage:WORD, wParam:SWORD, lParam:SDWORD ; Windows gives us: the handle of the Window, the Message ID, ; and two parameters for the message LOCAL hDC:HDC, ps:PAINTSTRUCT, point:POINT ; locals: a handle to a device context, a paint structure, ; a point structure .IF (iMessage == WM_CREATE) INVOKE SetupTimer, hWnd, TIMER_SECS INVOKE Resize, hWnd INVOKE InvalidateRect, hWnd, NULL, FALSE .ELSEIF (iMessage == WM_TIMER) INVOKE InvalidateRect, hWnd, NULL, FALSE .ELSEIF (iMessage == WM_PAINT) INVOKE BeginPaint, hWnd, ADDR ps mov hDC, ax .IF SetAlarm INVOKE PaintAlarm, hWnd, hDC .ELSE INVOKE PaintTime, hWnd, hDC .ENDIF INVOKE EndPaint, hWnd, ADDR ps .ELSEIF (iMessage == WM_SETFOCUS) INVOKE RedrawWindow, hWnd, NULL, NULL, RDW_FRAME OR RDW_UPDATENOW .ELSEIF (iMessage == WM_SIZE) .IF ((wParam != SIZE_MINIMIZED) && Iconized) mov Iconized, FALSE INVOKE KillTimer, hWnd, 1 INVOKE SetupTimer, hWnd, TIMER_SECS .ENDIF INVOKE Resize, hWnd INVOKE RedrawWindow, hWnd, NULL, NULL, RDW_ERASE OR RDW_INVALIDATE .ELSEIF (iMessage == WM_DESTROY) INVOKE KillTimer, hWnd, 1 INVOKE PostQuitMessage, 0 .ELSEIF (iMessage==WM_LBUTTONDBLCLK)||(iMessage==WM_RBUTTONUP) .IF SetAlarm xor SetAlarm, TRUE INVOKE AlarmSetup, hWnd .ELSE mov di, word ptr lParam mov point.x, di mov di, word ptr (lParam+2) mov point.y, di INVOKE ClientToScreen, hWnd, ADDR point INVOKE TrackPopupMenu, hMenu, TPM_LEFTALIGN, point.x, point.y, 0, hWnd, NULL INVOKE InvalidateRect, hWnd, NULL, TRUE .ENDIF .ELSEIF (iMessage == WM_LBUTTONDOWN) INVOKE DefWindowProc, hWnd, WM_NCLBUTTONDOWN, HTCAPTION, lParam .ELSEIF (iMessage == WM_COMMAND) .IF (wParam == IDM_DATE) xor EnableDate, MF_CHECKED INVOKE CheckMenuItem,hMenu,wParam,EnableDate INVOKE Resize, hWnd INVOKE InvalidateRect, hWnd, NULL, FALSE .ELSEIF (wParam == IDM_ALARM) xor EnableAlarm, MF_CHECKED mov TestAlarm, TRUE INVOKE CheckMenuItem,hMenu,wParam,EnableAlarm .ELSEIF (wParam == IDM_SET) mov SetAlarm, TRUE INVOKE AlarmSetup, hWnd .ELSEIF (wParam == IDM_ABOUT) INVOKE MessageBox, hWnd, ADDR szAboutText, ADDR szAppName, MB_ICONASTERISK OR MB_OK .ELSEIF (wParam == IDM_ONTOP) xor AlwaysOnTop, MF_CHECKED INVOKE CheckMenuItem,hMenu,wParam,AlwaysOnTop .IF AlwaysOnTop mov ax, HWND_TOPMOST .ELSE mov ax, HWND_NOTOPMOST .ENDIF INVOKE SetWindowPos, hWnd, ax, 0, 0, 0, 0, SWP_NOMOVE OR SWP_NOSIZE .ELSEIF (wParam == IDM_EXIT) INVOKE KillTimer, hWnd, 1 INVOKE PostQuitMessage, 0 .ELSEIF (wParam == IDM_MINIMIZE) mov Iconized, TRUE INVOKE KillTimer, hWnd, 1 INVOKE SetupTimer, hWnd, TIMER_MINS INVOKE DefWindowProc, hWnd, WM_SYSCOMMAND, SC_ICON, NULL .ENDIF .ELSEIF (iMessage == WM_HSCROLL) .IF (wParam == SB_PAGEDOWN) add AlarmTime, 10t .ELSEIF (wParam == SB_LINEDOWN) inc AlarmTime .ELSEIF (wParam == SB_PAGEUP) sub AlarmTime, 10t .ELSEIF (wParam == SB_LINEUP) dec AlarmTime .ELSEIF (wParam == SB_TOP) mov AlarmTime, MAXTIME .ELSEIF (wParam == SB_BOTTOM) mov AlarmTime, 0t .ELSEIF (wParam == SB_THUMBPOSITION) mov ax, word ptr lParam mov AlarmTime, ax .ELSEIF (wParam == SB_THUMBTRACK) mov ax, word ptr lParam mov AlarmTime, ax .ELSEIF (wParam == SB_LINEDOWN) mov ax, word ptr lParam mov AlarmTime, ax .ENDIF .IF (AlarmTime < 0) mov AlarmTime, 0t .ELSEIF (AlarmTime > MAXTIME) mov AlarmTime, MAXTIME .ENDIF INVOKE SetScrollPos, hWndScrol, SB_CTL, AlarmTime, TRUE INVOKE InvalidateRect, hWnd, ADDR TextRect, FALSE .ELSE INVOKE DefWindowProc, hWnd, iMessage, wParam,lParam jmp doRet .ENDIF mov ax, 0 cwd doRet: ret WndProc ENDP END __astart ; so that the code of the application will ; start with the Windows start-up code