;-----------------------------------------
;   Sysmets4.asm -- System Metrics Display
;   based on Charles Petzold's Sysmets4.C
;
;   Translated into assembly 18/4/99 by
;   Ron Thomas Ron_Thom@Compuserve.com	
;-----------------------------------------

.386				; 32 bit when .386 appears before .MODEL
.MODEL FLAT,STDCALL

include windows.inc

include user32.inc
include kernel32.inc
include gdi32.inc

includelib user32.lib
includelib kernel32.lib
includelib gdi32.lib

lstrlenA	PROTO	WINAPI	:LPCSTR		; Not included in latest l2inc output
lstrlen		textequ	<lstrlenA>		; but lib can't be missing as function works 

WinMain		PROTO :DWORD, :DWORD, :DWORD, :SDWORD


.DATA
include sysmets.inc	; Data for System metrics display structure (based on Sysmets.h)

ClassName db "SimpleWinClass",0
AppName   db "Get System Metrics",0

cxChar  	DWORD	?
cxCaps		DWORD	?
cyChar		DWORD	?
cxClient	DWORD	?
cyClient	DWORD	?
iMaxWidth	DWORD	?
 	  	
cyChar_byi	DD	?	
cxChar_by40	DD	?
cxCaps_by22	DD	?

szBuffer  db 10 DUP (?),0 		; Storage buffer

Fmt	  db "%5d",0			; Format control for wsprintf 

.DATA?
hInstance HINSTANCE ?
CommandLine LPSTR ?

;------------------------MACRO's-----------------------

LOWORD	MACRO 	bigword	;; Retrieves the low word from double word argument

	mov	eax,bigword
	and	eax,0FFFFh	;; Set to low word 
	ENDM

HIWORD	MACRO   bigword	;; Retrieves the high word from double word argument

	mov	ebx,bigword
	shr	ebx,16		;; Shift 16 for high word to set to high word
				
	ENDM

MAX	MACRO   word1, word2	;; Return the maximum of 2 integers in eax

	mov	ecx,word2
	mov	eax,word1
	.IF	eax < ecx
	mov	eax,word2	;; Set eax as max
	.ENDIF

	ENDM

MIN	MACRO	word1, word2	;; Return the minimum of 2 integers in eax

	mov	ecx,word2
	mov	eax,word1
	.IF	eax > ecx
	mov	eax,word2	;; Set eax as min
	.ENDIF

	ENDM
;----------------------------------------------------------------------------
.code
start:
	invoke GetModuleHandle, NULL
	mov    hInstance,eax
	invoke GetCommandLine
        invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT
	invoke ExitProcess,eax

WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:SDWORD
	LOCAL wc:WNDCLASSEX
	LOCAL msg:MSG
        LOCAL hwnd:HWND
	mov   wc.cbSize,SIZEOF WNDCLASSEX
	mov   wc.style, CS_HREDRAW or CS_VREDRAW
	mov   wc.lpfnWndProc, OFFSET WndProc
	mov   wc.cbClsExtra,NULL
	mov   wc.cbWndExtra,NULL
        push  hInstance
        pop   wc.hInstance
	mov   wc.hbrBackground,COLOR_WINDOW+1
	mov   wc.lpszMenuName,NULL
	mov   wc.lpszClassName,OFFSET ClassName
        invoke LoadIcon,NULL,IDI_APPLICATION
	mov   wc.hIcon,eax
        mov   wc.hIconSm,0
        invoke LoadCursor,NULL,IDC_ARROW
	mov   wc.hCursor,eax 
        invoke RegisterClassEx, addr wc
        INVOKE CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\
           WS_OVERLAPPEDWINDOW or WS_VSCROLL or WS_HSCROLL,CW_USEDEFAULT,\
           CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\
           hInst,NULL
        mov   hwnd,eax
        INVOKE ShowWindow, hwnd,SW_SHOWNORMAL
        INVOKE UpdateWindow, hwnd
        .WHILE TRUE
                INVOKE GetMessage, ADDR msg,NULL,0,0
                .BREAK .IF (!eax)			;Escape loop if eax not true
                INVOKE TranslateMessage, ADDR msg
                INVOKE DispatchMessage, ADDR msg
        .ENDW
        mov     eax,msg.wParam
        ret
WinMain endp

WndProc proc uses ebx esi, hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM

	LOCAL	hdc:HDC, ps:PAINTSTRUCT, tm:TEXTMETRIC, sci:SCROLLINFO	
	LOCAL	x:DWORD, y:DWORD
	LOCAL	iVertPos:DWORD, iHorzPos:DWORD, iPaintBeg:DWORD, iPaintEnd:DWORD

	mov   eax,uMsg

	.IF eax==WM_CREATE

	  invoke  GetDC, hWnd
	  mov	hdc,eax
	  
	  invoke  GetTextMetrics, hdc, ADDR tm		
	  
	  mov	ebx,tm.tmAveCharWidth		; Get average char width
	  mov	cxChar,ebx
	  mov	al,tm.tmPitchAndFamily		; Get info about pitch and family of font
 
	  .IF tm.tmPitchAndFamily & 1		; Test if variable width font
	      mov  eax,3			; use for var width 
	  .ELSE
	      mov  eax,2			; else for fixed pitch
	  .ENDIF

	  shr	ebx,1			; cxChars/2	
	  mov	edx,0		 
	  mul	ebx			; (2 or 3)*cxChar/2
	  mov	cxCaps,eax

	  mov	eax,tm.tmHeight			; Get height (ascent+decent) of chars
	  add	eax,tm.tmExternalLeading	; Get leading between rows
	  mov	cyChar,eax

	  invoke  ReleaseDC, hWnd, hdc

;	Save the width of the three colmns

          mov	  eax,cxCaps
          mov	  ecx,22
          mov	  edx,0		
          mul	  ecx		
          mov	  cxCaps_by22,eax		; save cxCaps*22 
	 
	  mov	  eax,cxChar
	  mov	  ecx,40
	  mov	  edx,0			; 	
	  mul	  ecx			; get cxChar*40
	  add	  eax,cxCaps_by22	; cxCaps*22 + cxChar*40
	  mov	  iMaxWidth,eax

 	.ELSEIF eax==WM_SIZE

	  LOWORD   lParam
	  mov	cxClient,eax

	  HIWORD   lParam
	  mov	cyClient,ebx

;	Set vertical scroll bar range & page size

	  mov	sci.cbSize,sizeof sci
	  mov	sci.fMask,(SIF_RANGE or SIF_PAGE)
	  mov	sci.nMin,0
	  mov	sci.nMax,NUMLINES-1

	  mov	eax,cyClient
	  mov	edx,0
	  div	cyChar
	  mov	sci.nPage,eax		; Load # of visible lines of text in client area

	invoke	SetScrollInfo, hWnd, SB_VERT, ADDR sci, TRUE

;	Set horiz scroll bar range & page size

	  mov	sci.cbSize,sizeof sci			
	  mov	sci.fMask,(SIF_RANGE or SIF_PAGE);
	  mov	sci.nMin,0

	  mov	eax,iMaxWidth
	  mov	edx,0
	  div	cxChar			
	  inc	eax
	  inc	eax
	  mov	sci.nMax,eax		; 2 + iMaxWidth/cxChar

	  mov	eax,cxClient
	  mov	edx,0
	  div	cxChar			; Get cxClient/cxChar
	  mov	sci.nPage,eax

	  invoke  SetScrollInfo, hWnd, SB_HORZ, ADDR sci, TRUE

	.ELSEIF eax==WM_VSCROLL

;	Get vertical scroll bar info

	  mov	sci.cbSize,sizeof sci
	  mov	sci.fMask,SIF_ALL

	  invoke  GetScrollInfo, hWnd, SB_VERT, ADDR sci

	  mov	eax,sci.nPos		
	  mov	iVertPos,eax		; Save position for later comparison

	  LOWORD   wParam		; Get mouse notification code (eax)

	  .IF     eax==SB_TOP

		  mov	eax,sci.nMin
		  mov	sci.nPos,eax
	
	  .ELSEIF eax==SB_BOTTOM
	
		  mov	eax,sci.nMax
		  mov	sci.nPos,eax	

	  .ELSEIF eax==SB_LINEUP	; Clicked top arrow of SB  

	          dec	sci.nPos

	  .ELSEIF eax==SB_LINEDOWN	; Clicked bottom arrow of SB
   
	          inc	sci.nPos	  

	  .ELSEIF eax==SB_PAGEUP	; Clicked area between top of SB and Thumb

		  mov	eax,sci.nPage	; Subtract # lines of displayable text
		  sub	sci.nPos,eax

	  .ELSEIF eax==SB_PAGEDOWN	; Clicked area between Thumb and bottom of SB

		  mov	eax,sci.nPage	; Add # lines of displayable text
		  add	sci.nPos,eax

	  .ELSEIF eax==SB_THUMBTRACK

	          mov	eax,sci.nTrackPos ; Get current positiom of scroll bar (ebx)
	          mov	sci.nPos,eax	

	  .ENDIF

;	Set position and retrieve it; adjustments by Windows may result in a different value

	  mov	sci.fMask,SIF_POS

	  invoke  SetScrollInfo, hWnd, SB_VERT, ADDR sci, TRUE
	  invoke  GetScrollInfo, hWnd, SB_VERT, ADDR sci	

;	If position changes, scroll the window & update it

	  mov	eax,iVertPos
  
	  .IF sci.nPos != eax

	    mov	eax,iVertPos
	    sub eax,sci.nPos
	    mov	edx,0
	    mul cyChar		; Got cyChar*(iVertPos-sci.nPos)
	      
	    invoke ScrollWindow, hWnd, 0, eax, NULL, NULL
	    invoke UpdateWindow, hWnd
  
	  .ENDIF

	.ELSEIF eax==WM_HSCROLL

;	Get horizontal scroll bar info

	  mov	sci.cbSize,sizeof sci
	  mov	sci.fMask,SIF_ALL

	  invoke	GetScrollInfo, hWnd, SB_HORZ, ADDR sci

	  mov	eax,sci.nPos		
	  mov	iHorzPos,eax		; Save position for later comparison

	  LOWORD   wParam		; Get mouse notification code (eax)

	  .IF     eax==SB_LINELEFT	; Clicked left arrow

	       dec	sci.nPos
	
	  .ELSEIF eax==SB_LINERIGHT	; Clicked right arrow
	
	       inc	sci.nPos	

	  .ELSEIF eax==SB_PAGELEFT	; Clicked zone between "Thumb" and Right   

	       mov	eax,sci.nPage
	       sub	sci.nPos,eax

	  .ELSEIF eax==SB_PAGERIGHT	; Clicked zone between "Thumb" and Left

   	       mov	eax,sci.nPage
	       add	sci.nPos,eax

	  .ELSEIF eax==SB_THUMBPOSITION

	       mov	eax,sci.nTrackPos ; Get current positiom of scroll bar (ebx)
	       mov	sci.nPos,eax	

	  .ENDIF

;	Set position and retrieve it; adjustments by Windows may result in a different value

	  mov	sci.fMask,SIF_POS
	  invoke  SetScrollInfo, hWnd, SB_HORZ, ADDR sci, TRUE
	  invoke  GetScrollInfo, hWnd, SB_HORZ, ADDR sci	

;	If position changes, scroll the window 

	  mov	eax,iHorzPos
  
	  .IF sci.nPos != eax

	    mov	eax,iHorzPos
	    sub eax,sci.nPos
	    mov	edx,0
	    mul cxChar		; Got cxChar*(iHorzPos-sci.nPos)
	
            invoke ScrollWindow, hWnd, eax, 0, NULL, NULL 
  
	  .ENDIF

 	.ELSEIF eax==WM_KEYDOWN

	  mov	eax, wParam		; Get virtual Key Code 

	  .IF     eax==VK_HOME		; HOME key

	    invoke SendMessage, hWnd, WM_VSCROLL, SB_TOP, 0	; Send phoney message to Windows
								; saying there's been some 
	  .ELSEIF eax==VK_END		; END Key 		; "scroll action"

	    invoke SendMessage, hWnd, WM_VSCROLL, SB_BOTTOM, 0

  
	  .ELSEIF eax==VK_PRIOR		; Page Up

	    invoke SendMessage, hWnd, WM_VSCROLL, SB_PAGEUP, 0

	  .ELSEIF eax==VK_NEXT		; Page Down

	    invoke SendMessage, hWnd, WM_VSCROLL, SB_PAGEDOWN, 0

	  .ELSEIF eax==VK_UP		; Up arrow

	    invoke SendMessage, hWnd, WM_VSCROLL, SB_LINEUP, 0

	  .ELSEIF eax==VK_DOWN		; Down arrow

	    invoke SendMessage, hWnd, WM_VSCROLL, SB_LINEDOWN, 0

	  .ELSEIF eax==VK_LEFT		; Left arrow

	    invoke SendMessage, hWnd, WM_HSCROLL, SB_PAGEUP, 0	; Use SB_LINELEFT for single
								; char shift
	  .ELSEIF eax==VK_RIGHT		; Right arrow

	    invoke SendMessage, hWnd, WM_HSCROLL, SB_PAGEDOWN, 0; Ditto for SB_LINERIGHT

	  .ENDIF

    
        .ELSEIF eax==WM_PAINT

          invoke  BeginPaint,hWnd, ADDR ps
          mov     hdc,eax

;	Get Vert scroll bar info

	  mov	sci.cbSize, sizeof sci
	  mov	sci.fMask, SIF_POS

	  invoke  GetScrollInfo, hWnd, SB_VERT, ADDR sci

	  mov	eax,sci.nPos
	  mov	iVertPos,eax

;	Get Horiz scroll bar info

	  invoke  GetScrollInfo, hWnd, SB_HORZ, ADDR sci

	  mov	eax,sci.nPos
	  mov	iHorzPos,eax

;	Find painting limits

	  mov	eax,iVertPos
	  add	eax,ps.rcPaint.top
	  mov	edx,0
	  div	cyChar		; Got (iVertPos + rc.Paint.top)/cyChar
 	  MAX	0, eax

	  mov	iPaintBeg,eax	; 

	  mov	eax,iVertPos
	  add	eax,ps.rcPaint.bottom
	  mov	edx,0
	  div	cyChar		; Get (iVertPos + rc.Paint.bottom)/cyChar
	  MIN	NUMLINES-1, eax
	  mov	iPaintEnd,eax   ; 
 	 
	  mov	  ebx,0			; offset index		  
	  mov	  esi,iPaintBeg		; Set start position
	   	
	  .WHILE  esi <= iPaintEnd	 

	    mov	  eax,1			
	    sub	  eax,iHorzPos
	    mov	  edx,0
	    mul	  cxChar
	    mov	  x,eax			; x = cxChar*(1 - iHorzPos) 
	    push  x			; save copy
		    
	    mov	  eax,esi		; Get index
	    sub	  eax,iVertPos
	    mov	  edx,0
	    mul	  cyChar
	    mov	  y,eax			; y = cyChar*(i - iVertPos)  		

	    invoke  lstrlen, ADDR sysmetrics.szLabel[ebx]	; get string length
	    invoke  TextOut, hdc, x, y, ADDR sysmetrics.szLabel[ebx] ,eax 
;			                           addr               length 
	    mov	  eax,cxCaps
	    mov	  ecx,22
	    mov	  edx,0			 
	    mul	  ecx			
	    add	  x,eax			; x + cxCaps*22

	    invoke  lstrlen, ADDR sysmetrics.szDesc[ebx]	
	    invoke  TextOut, hdc, x, y, ADDR sysmetrics.szDesc[ebx], eax

	    invoke  SetTextAlign, hdc, (TA_RIGHT or TA_TOP)   ; Top right corner    

	    mov	  eax,cxChar
	    mov	  ecx,40
	    mov	  edx,0			; 	
	    mul	  ecx			; cxChar*40
	    add	  eax,cxCaps_by22	; cxCaps*22 + cxChar*40
	    pop	  x			; Get original x value
	    add	  x,eax			; x + (cxCaps*22 + cxChar*40) 

	    invoke  GetSystemMetrics, sysmetrics.iIndex[ebx]  ; Return with System metric in eax
	    invoke  wsprintf, ADDR szBuffer, ADDR Fmt, eax    ; Return with # chars in eax 
	    invoke  TextOut, hdc, x, y, ADDR szBuffer, eax ; Print the buffer

	    add	  ebx, SIZEOF items	; bump to next line of metrics
	    inc	  esi

	    invoke  SetTextAlign, hdc, (TA_LEFT or TA_TOP)    ; Top left corner  	 

	  .ENDW

           invoke EndPaint, hWnd, ADDR ps

        .ELSEIF eax==WM_DESTROY

	      invoke PostQuitMessage, NULL 
	.ELSE
              invoke DefWindowProc, hWnd,uMsg,wParam,lParam
              ret
	.ENDIF
        xor    eax,eax
	ret

WndProc endp

        end start

