;++
;
;   WOW v1.0
;
;   Copyright (c) 1991, Microsoft Corporation
;
;   WINMISC2.ASM
;   Win16 misc. user services
;
;   History:
;
;   Created 28-May-1991 by Jeff Parsons (jeffpar)
;   Copied from WIN31 and edited (as little as possible) for WOW16
;--

;****************************************************************************
;*									    *
;*  WINMISC2.ASM -							    *
;*									    *
;*	Random Shit							    *
;*									    *
;****************************************************************************

ifdef WOW
SEGNAME equ <TEXT>
endif
include user.inc
include vint.inc

ExternFP <GlobalHandleNorip>
ExternFP <MessageBox>
ExternFP <SysErrorBox>


ifndef WOW
ExternFP <DoBeep>
ExternFP <XCSTODS>
ExternNP <rgbKeyState>
ExternW  <hwndCapture>
ExternW  <fBeep>
ExternW  <fMessageBox>
ExternW  <hwndDragIcon>

ExternA <__WinFlags>

ATOMTABLE   STRUC
at_prime        DW  ?
at_hashTable    DW  ?
ATOMTABLE   ENDS

ATOM    STRUC
a_chain DW  ?
a_usage DW  ?
a_len   DB  ?
a_name  DB  ?
ATOM    ENDS

LocalArena  STRUC
la_prev		DW  ?	; previous arena entry (first entry points to self)
la_next		DW  ?	; next arena entry	(last entry points to self)
la_handle	DW  ?	; back link to handle table entry
LocalArena  ENDS

sBegin DATA

;
; For GetSysMetrics - don't move this stuff. DS positioning is assumed.
;
SM_CMETRICS1 equ 24
SM_CMETRICS2 equ 16

public rgwSysMet
rgwSysMet      dw (SM_CMETRICS1) DUP(0)

; These are 'variable metrics', conviently located the system metrics array.
GlobalW hwndFullScrn, 0
GlobalW iLevelCursor, 0

; These are additions since 2.0
	dw SM_CMETRICS2 DUP(0)


sEnd DATA
endif ;WOW

createSeg _%SEGNAME, %SEGNAME, WORD, PUBLIC, CODE

assumes cs,%SEGNAME
assumes ds,DATA

sBegin %SEGNAME

ifndef WOW
;*--------------------------------------------------------------------------*
;*									    *
;*  GetSystemMetrics() -						    *
;*									    *
;*--------------------------------------------------------------------------*

; int far GetSystemMetrics(iMetric)
; int iMetric;

LabelFP <PUBLIC, GetSystemMetrics>
	    
	    pop     ax			    ; Pop the FAR return
	    pop     dx
	    pop     bx			    ; BX = iMetric
	    push    dx			    ; Restore the FAR return
	    push    ax

	    xor     ax,ax
	    cmp     bx,SM_CMETRICSMAX	    ; Bigger than max?
	    jge	    gsmExit		    ; Yes, exit
	    shl     bx,1		    ; Convert to a byte index

ifndef userhimem
            mov     es,WORD PTR cs:[cstods]
else
            push    ax
            push    ds
            call    XCSTODS
            mov     es,ax
            pop     ds
            pop     ax
endif

assumes es,DATA
	    mov     ax,es:[rgwSysMet+bx]    ; Return the SysMetric value
assumes es,NOTHING
gsmExit:
	    retf


;*--------------------------------------------------------------------------*
;*									    *
;*  MessageBeep() -							    *
;*									    *
;*--------------------------------------------------------------------------*

cProc MessageBeep, <FAR, PUBLIC>

ParmW beep

cBegin
	    cmp     fBeep,0
	    je	    mbout		; No beeps today....
            mov     ax,beep
            cmp     fMessageBox,0       ; if we are in an INT24 box, let
            je      noint24             ; the sound driver know not to load
            mov     ax,-1               ; anything by passing -1.
noint24:
            push    ax
	    call    DoBeep		; Just call the sound driver guy
mbout:

cEnd


;*--------------------------------------------------------------------------*
;*									    *
;*  IsChild() - 							    *
;*									    *
;*--------------------------------------------------------------------------*

LabelFP <PUBLIC, IsChild>
;
;ParmW hwnd
;ParmW hwndChild
;
	    pop     ax			; pop return address
	    pop     dx
	    pop     bx			; bx = hwndChild
	    pop     cx			; cx = hwnd
	    push    dx			; push return address
	    push    ax

	    push    ds
	    UserDStoDS			; es = USER's DS

	    CheckHwnd cx		; checkhwnd will zero ax if failure
	    jz	    icexit
ifdef DEBUG
	    CheckHwndNull bx 		; only do check if debug since 
	    jz	    icexit              ; we never access anything off this
                                        ; pointer
endif
	    xor     ax,ax		; Assume FALSE
icloop:
	    or	    bx,bx		; while (hwndChild == NULL &&
	    jz	    icexit
	    mov     dl,byte ptr [bx+WSTATE+WFTYPEMASK/256]
	    and     dl,LOW(WFTYPEMASK)	; TestwndChild(hwndChild))
	    cmp     dl,LOW(WFCHILD)
	    jne     icexit
	    mov     bx,[bx].wndPwndParent ; hwndChild = hwndChild->hwndParent
	    cmp     bx,cx		; if (hwnd == hwndChild)
	    jne     icloop
	    inc     al			; return(TRUE);
icexit:
	    pop     ds
	    retf

; BOOL IsDescendant(hwndParent, hwndChild);
;
; Internal version of IsChild that is a bit faster and ignores the
; WFCHILD business.  MUST be called with DS == USER DS.
;
; Returns TRUE if hwndChild == hwndParent (IsChild doesn't)
;
;   while (hwndChild != NULL)
;   {
;	if (hwndParent == hwndChild)
;	    return TRUE;
;	hwndChild = hwndChild->hwndParent;
;   }
;
LabelFP     <PUBLIC, IsDescendant>
	    pop     ax			; pop off return address
	    pop     dx
	    pop     bx			; bx = hwndChild
	    pop     cx			; cx = hwndParent
	    push    dx			; replace return address
	    push    ax

	    xor     ax,ax		; assume FALSE
idloop:
	    or	    bx,bx		; if at end, return FALSE
	    jz	    idexit
	    cmp     bx,cx		; hwndChild == hwndParent?
	    mov     bx,[bx].wndPwndParent ; hwndChild = hwndChild->hwndParent
	    jnz     idloop		; keep looping if we didn't find it...

	    inc     al			; ax = TRUE
idexit:
	    retf

;--------------------------------------------------------------------------
;
; IsWindowVisible() -
;
;--------------------------------------------------------------------------
;
; BOOL FAR PASCAL IsWindowVisible(register HWND hwnd)
; {
;     if (!CheckHwnd(hwnd))
;	  return(FALSE);
;
;     if (hwnd == hwndDragIcon)
;	  return(TRUE);
;
;     for ( ; hwnd != NULL; hwnd = hwnd->hwndParent)
;     {
;	  if (!TestWF(hwnd, WFVISIBLE))
;	      return FALSE;
;     }
;     return TRUE;
; }
;
LabelFP <PUBLIC, IsWindowVisible>
;ParmW hwnd
	    pop     ax			; pop return address
	    pop     dx
	    pop     bx			; bx = hwnd
	    push    dx			; push return address
	    push    ax

	    push    ds
	    UserDStoDS			; es = USER's DS

	    CheckHwnd bx		; checkhwnd will zero ax if failure
	    jz	    ivwexit

	    mov     ax,TRUE		; assume TRUE

; Check if this is the iconic window being moved around with a mouse */
; If so, return a TRUE, though, strictly speaking, it is hidden.     */
; This helps the Tracer guys from going crazy!			     */
; Fix for Bug #57 -- SANKAR -- 08-08-89 --			     */
;
	    cmp     bx,hwndDragIcon	; hwnd == hwndDragIcon?
	    jz	    ivwexit		; yes: return TRUE.
ivwloop:
	    or	    bx,bx		; while (hwndChild == NULL &&
	    jz	    ivwexit
	    TSTWF   bx,WFVISIBLE
	    mov     bx,[bx].wndPwndParent ; hwndChild = hwndChild->hwndParent
	    jnz     ivwloop		; if visible bit set, keep looping

	    xor     ax,ax		; visible bit clear: return FALSE
ivwexit:
	    pop     ds
	    retf


;=======================================================================
;
; Return whether or not a given window can be drawn in or not.
;
; BOOL FAR IsVisible(HWND hwnd, BOOL fClient)
; {
;     HWND hwndT;
;
;     for (hwndT = hwnd; hwndT != NULL; hwndT = hwndT->hwndParent)
;     {
;	  // Invisible windows are always invisible
;	  //
;	  if (!TestWF(hwndT, WFVISIBLE))
;	      return FALSE;
;
;	  if (TestWF(hwndT, WFICONIC))
;	  {
;	      // Children of icons are always invisible.
;	      //
;	      if (hwndT != hwnd)
;		  return FALSE;
;
;	      // Client areas with class icons are always invisible.
;	      //
;	      if (fClient && hwndT->pcls->hIcon)
;		  return FALSE;
;	  }
;     }
;     return TRUE;
; }
;
LabelFP <PUBLIC, IsVisible>
	pop	ax
	pop	dx
	pop	cx	    ; cx = fClient
	pop	bx	    ; bx = hwnd
	push	dx
	push	ax

	mov	dx,bx	    ; hwnd = dx, bx = hwndT
	xor	ax,ax	    ; assume FALSE return
	jmps	iv100	    ; fall into loop...
ivloop:
	mov	bx,[bx].wndPwndParent ; hwndChild = hwndChild->hwndParent
iv100:
	or	bx,bx
	jz	ivtrue	    ; Reached the top: return TRUE

	TSTWF	bx,WFVISIBLE ; if not visible, get out of here.
	jz	ivfalse

	TSTWF	bx,WFMINIMIZED ; if not minimized, keep looping
	jz	ivloop

	cmp	bx,dx	    ; if (hwnd != hwndT)
	jnz	ivfalse     ;	return FALSE

	jcxz	ivloop	    ; if fClient == FALSE, keep going.

	mov	bx,[bx].wndPcls
	mov	bx,[bx].uclshIcon
	or	bx,bx
	jnz	ivfalse
	mov	bx,dx	    ; resume enumeration at bx
	jmps	ivloop	    ; keep looping...
ivtrue:
	inc	al	    ; ax = TRUE
ivfalse:
	retf

;*--------------------------------------------------------------------------*
;*									    *
;*  GetMenu() - 							    *
;*									    *
;*--------------------------------------------------------------------------*

cProc GetMenu, <FAR, PUBLIC>,<si>

ParmW hwnd

cBegin
	    mov     si,hwnd
	    CheckHwnd si
	    jz	    gmexit
	    mov     ax,[si].wndhMenu
gmexit:

cEnd
endif ;WOW

;*--------------------------------------------------------------------------*
;*									    *
;*  SwapHandle() -							    *
;*									    *
;*--------------------------------------------------------------------------*

; Takes a far pointer to a word on the stack and converts it from a handle
; into a segment address or vice-versa.  Note that this function is a NO OP 
; in protect mode.

ifndef PMODE
cProc SwapHandle, <PUBLIC, FAR, NODATA, ATOMIC>

ParmD lpHandle

cBegin
ifndef WOW
            mov     ax,__WinFlags
            test    ax,1
            jnz     sh200            ; SwapHandle is a no op in pmode.
endif
	    ; Save the parameter.
	    mov     bx,off_lpHandle
	    push    bx

	    ; Get the handle/segment
	    mov     ax,word ptr ss:[bx]+2
	    push    ax			    ; Save it

	    ; Call GlobalHandleNorip which puts the proper handle in AX
	    ;  and the corresponding segment address in DX.
	    push    ax
	    call    GlobalHandleNorip

	    ; Restore the original word.
	    pop     bx

	    ; If DX==CS then we know we've converted a handle into a segment.
	    ;	This prevents problems with the FFFE segment.
	    mov     cx,cs
	    cmp     dx,cx
	    je	    sh50

	    ; Was the original word a segment address?
	    test    bl,1
	    jnz     sh100		    ; Yes, AX = handle, DX = segment

sh50:	    xchg    ax,dx		    ; Nope, AX = segment, DX = handle

	    ; Restore the pointer to the original word.
sh100:	    pop     bx

	    ; Skip if zero.
	    or	    ax,ax
	    jz	    sh200

	    ; Move the result into the original word pointed to.
	    mov     word ptr ss:[bx]+2,ax
sh200:
cEnd
endif ;PMODE

ifndef WOW
;*--------------------------------------------------------------------------*
;*									    *
;*  SwapMouseButton() - 						    *
;*									    *
;*--------------------------------------------------------------------------*

; BOOL SwapMouseButton(fSwap)
; BOOL fSwap;

LabelFP <PUBLIC, SwapMouseButton>
            mov     ax,_INTDS
            mov     es,ax
assumes es,INTDS
	    mov     ax,es:fSwapButtons	; Return fSwapButtons' old value
	    pop     cx			; Pop off the FAR return
	    pop     dx
	    pop     es:[fSwapButtons]	; fSwapButtons = fSwap

            mov     bx,es:[fSwapButtons]
assumes es,NOTHING

            mov     es,WORD PTR cs:[cstods] ; Get user's ds
assumes es,DATA
            mov     es:[rgwSysMet+SM_SWAPBUTTON*2],bx
assumes es,NOTHING

	    push    dx			; Restore the FAR return
	    push    cx
	    retf
assumes es,NOTHING

endif; Not WOW


;*--------------------------------------------------------------------------*
;*									    *
;*  SetDivZero() -							    *
;*									    *
;*--------------------------------------------------------------------------*

LabelFP <PUBLIC, SetDivZero>
	    push    ds
	    push    cs			; Set DS == CS
	    pop     ds
ifndef userhimem
            mov     dx,Offset DivideByZero
else
            push    ds
            mov	    ax, _INTDS
            mov     ds,ax
assumes ds,INTDS
            mov     ax,fffedelta
            pop     ds
assumes ds,DATA
            add     ax,Offset DivideByZero
            mov     dx,ax
endif
sdzvector:
	    mov     ax,2500h		; Use DOS to set interrupt zero
	    int     21h
	    pop     ds
	    retf



;*--------------------------------------------------------------------------*
;*									    *
;*  DivideByZero() -							    *
;*									    *
;*--------------------------------------------------------------------------*

LabelFP <PUBLIC, DivideByZero>
            FSTI
ifdef DEBUG
            pusha
            push    es
endif

	    ; Put up the system modal message box.
            mov     cx,_INTDS
ifdef WOW
            ; Put up the SysErrorBox Directly
            push    cx
            lea     ax,szDivZero
            push    ax

            push    cx
            lea     ax,szSysError
            push    ax

ifdef DEBUGlater

            push    SEB_CLOSE
            push    0
            push    SEB_CANCEL
            call    SysErrorBox
            cmp     ax,SEB_BTN1
            jz      DBZ_Terminate

            pop     es
            popa
            DebugErr DBF_FATAL, "Divide by zero or divide overflow error: break and trace till IRET"
            iret

DBZ_Terminate:
            pop     es
            popa
else ; FREE Build
            push    0                       ; no button 1
            push    SEB_CLOSE               ; only allow close
            push    0                       ; no button 3
            call    SysErrorBox
endif; FREE Build
	    mov     ax,4C00h		    ; Abort the task with a 0
            int     21h

else; Not WOW
	    xor     ax,ax
	    push    ax			    ; NULL hwnd
	    lea     ax,szDivZero
	    push    cx
	    push    ax			    ; Message Text
	    lea     ax,szSysError
	    push    cx
	    push    ax			    ; Caption Text
ifdef DEBUG
	    mov     ax,MB_SYSTEMMODAL OR MB_ICONHAND OR MB_OKCANCEL
else
	    mov     ax,MB_SYSTEMMODAL OR MB_ICONHAND
endif
	    push    ax
            call    MessageBox
ifdef DEBUG
            cmp     ax,1                 ; If OK Button clicked, terminate app
            jz      DBZ_Terminate

            pop     es
            popa
            DebugErr DBF_FATAL, "Divide by zero or divide overflow error: break and trace till IRET"
            iret

DBZ_Terminate:
            pop     es
            popa
endif
endif; Not WOW
	    mov     ax,4C00h		    ; Abort the task with a 0
            int     21h

ifndef WOW

;-------------------------------------------------------------------------
;
;   word FAR PASCAL GetUserLocalObjType(pObj)
;     Given a near pointer to an object in USER's local heap, this function
;     determines the type of the object and returns it;
;   It finds out if the given object is a non-tagged belonging to the atom
;   table; If not, it looks at the tag and returns the object type.
;
;   WARNING:  Because this function determines the type of the object by
;   the process of elimination, the results will be unpredictable if the 
;   input in incorrect. i.e., no validation is done on the input value;
;   To validate if the input value is indeed an object in USER's heap would
;   warant a walk down the heap; This will be very costly, if done for
;   every call; Apps like HeapWalker are expected to walk down the USER's
;   local heap and make calls to this function for every object thay come
;   accross; So, a validation done here is duplication of effort and affect
;   the performance unnecessarily
;
;
;-------------------------------------------------------------------------

ifndef DEBUG
; The following is in the RETAIL version of USER
LabelFP	<PUBLIC, GetUserLocalObjType>
	xor   ax, ax	; Return Unknown struct type
	retf  2		; Compensate for the WORD parameter
else
; The following is in the DEBUG version of USER

cProc	GetUserLocalObjType, <PUBLIC, FAR>, <si, di>

ParmW	pObj	; Near pointer to an OBJ in USER's heap

cBegin
	; Now DS register is pointing to USER's DS
	;
	; Check if the object is a moveable object
	mov	bx, pObj
	mov	ax, [bx].la_prev
	test	ax, 01   ; is it a free object
	jz	FoundFreeObj
	test	ax, 02	 ; Is it a moveable object
	jz      FoundFixedObj
	; Now, it is a moveable obj; So, we have the tags
	mov	al, byte ptr [bx + SIZE LocalArena]
	xor     ah, ah
	jmps	FoundObjType

FoundFreeObj:
	mov	ax, ST_FREE
	jmps	FoundObjType

FoundFixedObj:
	; Assume that the object belongs to atom table
	mov	ax, ST_ATOMS

	; Check if this object is the atom table itself
	add	bx, SIZE LocalArena - 2
	cmp	bx, ds:[8]	; pAtomTable  is at this offset.
	je	FoundObjType

	; Check if this is possibly an atom string. If so, the first word
	; stored in this object is a ptr to the next string or NULL;
	; Check if the last two bits are zero; If they are not zero, then
	; this can not be an atom; If they are zero, this may or may not be
	; an atom;

	mov	cx, [bx]
	and	cx, 03h
	jnz	NotAnAtom

	; Now walk down the atom table and check each entry against the 
	; given object

	mov	dx, bx	; save the near pointer to the object
	mov	bx, ds:[8]	; Get the pointer to the atom table pAtomTable
	mov	cx, [bx].at_prime	; Get the number of entries
	; Skip to the first entry in the atom table
	errnz   <at_hashtable - 2>
AtomLoop2:
	errnz	<SIZE at_hashtable - 2>
	add	bx, 2			; 
	errnz   <a_chain>
	mov	si, [bx]	; Pointer to the next string
AtomLoop:
	or	si, si
	jz	NextBucket	; Goto NextBucket

	;Check the new atom matches the given object
	cmp	si, dx
	jz	FoundObjType	; AX already has ST_ATOMS in it
	mov	si, [si].a_chain
	jmps	AtomLoop

NextBucket:
	loop	AtomLoop2
	mov	bx, dx 	       ; Make bx point to the first byte of the object

NotAnAtom:
	; bx points to the tag byte of the object
	xor	ah, ah
	mov	al, byte ptr [bx]
FoundObjType:
	; ax already contains the proper return value 
cEnd
endif

endif ;WOW

sEnd %SEGNAME

end
