News:

MASM32 SDK Description, downloads and other helpful links
MASM32.com New Forum Link
masmforum WebSite

Centering a window on another window

Started by NoCforMe, April 09, 2012, 01:56:13 AM

Previous topic - Next topic

NoCforMe

This is something I do a lot in my programs, so I came up with the following routine to do this:


;============================================
; CenterWindow (winHandle, winWidth, winHeight)
;
; Centers window (by w. & h.) on another window.
;
; Returns:
; ECX = x-position for window
; EDX = y-position
;============================================

CenterWindow PROC winHandle:HWND, winWidth:DWORD, winHeight:DWORD
LOCAL gpRect:RECT

INVOKE GetWindowRect, winHandle, ADDR gpRect

; Calculate X-position of window:
MOV EAX, gpRect.right
SUB EAX, gpRect.left
SUB EAX, winWidth
MOV ECX, EAX
AND ECX, 1 SHL 31 ;Mask for sign bit.
SHR EAX, 1 ;Quick-n-dirty IDIV / 2.
OR EAX, ECX ;Replace sign bit.
ADD EAX, gpRect.left
MOV ECX, EAX ;Stash x-pos.

; Calculate Y-position of window:
MOV EAX, gpRect.bottom
SUB EAX, gpRect.top
SUB EAX, winHeight
MOV EDX, EAX
AND EDX, 1 SHL 31
SHR EAX, 1
OR EAX, EDX
ADD EAX, gpRect.top
MOV EDX, EAX
RET

CenterWindow ENDP


The handle passed in to the subroutine is that of the window you want to center the other window on; the height and width parameters are for the window you want centered. This works whether the window to be centered is larger or smaller than the other window. (To do this, I think my method of doing a "poor-man's IDIV" here is fairly clever: I use SHR 1 to divide by half, but preserve the sign bit beforehand and OR it back into the result afterwards to preserve the sign of the value.)

After calling this, you can pass the values in ECX and EDX directly to SetWindowPos() to center the window.

dedndave

to restore the sign, you want 2 bits   :P
SAR (shift arithmetic right) takes care of that for you


NoCforMe

Yeah, SAR is certainly easier, isn't it! Dunno how I missed that.

But why do you say "2 bits"? It's just the one bit, the sign bit, no? I don't care about the carry bit here.

Anyhow, here's the simplified code:


; Calculate X-position of window:
MOV EAX, gpRect.right
SUB EAX, gpRect.left
SUB EAX, winWidth
SAR EAX, 1 ;Quick-n-dirty IDIV / 2.
ADD EAX, gpRect.left
MOV ECX, EAX ;Stash x-pos.

; Calculate Y-position of window:
MOV EAX, gpRect.bottom
SUB EAX, gpRect.top
SUB EAX, winHeight
SAR EAX, 1
ADD EAX, gpRect.top
MOV EDX, EAX
RET

dedndave

2 bits - one is the sign, one is the extension

for bytes...
1abcdefg
becomes
11abcdef

and
0abcdefg
becomes
00abcdef

NoCforMe

Hmm, don't know what you mean by the "extension". Isn't that just the most significant bit of the (signed) quantity?

In any case, my original code worked fine; it was just needlessly complex.

dedndave

well - under the right (wrong) conditions, it would have returned incorrect results

let's play....

-10, in hex...

FFFF FFF6

your calculation would have yielded...

DFFF FFFB

but, -10, diviided by 2 should be somewhere near -5   :P

FFFF FFFB

you can plug those values into the str$ macro to see

        mov     eax,0FFFFFFF6h
        print   str$(eax),32
        mov     eax,0DFFFFFFBh
        print   str$(eax),32
        mov     eax,0FFFFFFFBh
        print   str$(eax),13,10

dedndave

on a different note, here's something you might consider...

;create a structure for your stuff

MyWinStruct STRUCT
  dwWidth     dd ?
  dwHeight    dd ?
  rcRect      RECT <>
;   left        dd ?
;   top         dd ?
;   right       dd ?
;   bottom      dd ?
MyWinStruct ENDS

;define it in the data area

        .DATA?

mws MyWinStruct <>


now, you can use a register to point to the base of the structure
your horizontal values are at offsets 0, 8, 16
your vertical values are at offsets 4, 12, 20

Calc    PROC

        mov     eax,offset mws
        call    Calc00
        mov     ecx,edx
        add     eax,4

Calc00: push    ecx
        mov     edx,[eax].MyWinStruct.rcRect.right
        mov     ecx,[eax].MyWinStruct.rcRect.left
        sub     edx,[eax].MyWinStruct.dwWidth
        sub     edx,ecx
        sar     edx,1
        add     edx,ecx
        pop     ecx
        ret

Calc    ENDP

you could also put the result for each in the structure, if you like
not only that, but if you pass the address of the structure in EAX, you can use it for multiple windows   :P

addressing data with a register and an index is smaller and faster than addressing it directly
"smaller and faster" may not appeal to you - how about "more efficient"

dedndave

here is what the code looks like
i always like to look at the disassembly to see where i can do better   :P
00000018 Calc    PROC

00000018  B8 00000000 R         mov     eax,offset mws
0000001D  E8 00000005 call    Calc00
00000022  8B CA         mov     ecx,edx
00000024  83 C0 04 add     eax,4

00000027  51         Calc00: push    ecx
00000028  8B 50 10 mov     edx,[eax].MyWinStruct.rcRect.right
0000002B  8B 48 08 mov     ecx,[eax].MyWinStruct.rcRect.left
0000002E  2B 10         sub     edx,[eax].MyWinStruct.dwWidth
00000030  2B D1         sub     edx,ecx
00000032  D1 FA         sar     edx,1
00000034  03 D1         add     edx,ecx
00000036  59 pop     ecx
00000037  C3 ret

00000038 Calc    ENDP

; Calculate X-position of window:
00000038  A1 00000010 R         MOV EAX, mws.rcRect.right
0000003D  2B 05 00000008 R SUB EAX, mws.rcRect.left
00000043  2B 05 00000000 R SUB EAX, mws.dwWidth
00000049  D1 F8         SAR EAX, 1 ;Quick-n-dirty IDIV / 2.
0000004B  03 05 00000008 R ADD EAX, mws.rcRect.left
00000051  8B C8         MOV ECX, EAX ;Stash x-pos.

; Calculate Y-position of window:
00000053  A1 00000014 R         MOV EAX, mws.rcRect.bottom
00000058  2B 05 0000000C R SUB EAX, mws.rcRect.top
0000005E  2B 05 00000004 R SUB EAX, mws.dwHeight
00000064  D1 F8         SAR EAX, 1
00000066  03 05 0000000C R ADD EAX, mws.rcRect.top
0000006C  8B D0         MOV EDX, EAX
0000006E  C3 RET


55 bytes of code vs 32

NoCforMe

Quote from: dedndave on April 09, 2012, 03:03:17 AM
well - under the right (wrong) conditions, it would have returned incorrect results

let's play....

-10, in hex...

FFFF FFF6

your calculation would have yielded...

DFFF FFFB

I don't see how you get that. Using my code:


MOV EAX, gpRect.right
SUB EAX, gpRect.left
SUB EAX, winWidth
MOV ECX, EAX
AND ECX, 1 SHL 31 ;Mask for sign bit.
SHR EAX, 1 ;Quick-n-dirty IDIV / 2.
OR EAX, ECX ;Replace sign bit.


let's look at the result, using your example of -10:

1. -10 = FFF6 (let's pretend it's a WORD instead of a DWORD to make things easier).
2. Mask (CX, copy of original AND 8000) = 8000
3. FFF6 SHR 1 = 7FFB (AX)
3. Result = AX OR 8000 = FFFB = -5

Q.E.D.: my method works.

But your method (SAR vs SHR) is clearly superior, and I changed my original code. I just mistakenly found a more expensive way to implement SAR is all.

dedndave

oops !
my mistake
cudda saved us a lot of typing - lol

NoCforMe

Hey, no harm, no foul.

Of course, I would never make a mistake like that ...

dedndave

brain fart on my part

when i was younger, i had a full set of registers upstairs - lol

SteveAsm

FYI guys,
when I see code that looks like this, I can only wonder how confusing that must look to noob programmers.

INVOKE GetWindowRect, winHandle, ADDR gpRect

MOV EAX, gpRect.right
SUB EAX, gpRect.left
SUB EAX, winWidth
MOV ECX, EAX
AND ECX, 1 SHL 31 ;Mask for sign bit.
SHR EAX, 1 ;Quick-n-dirty IDIV / 2.
OR EAX, ECX ;Replace sign bit.
ADD EAX, gpRect.left
MOV ECX, EAX ;Stash x-pos.


The problem appears to be the usage of TABS in your code examples.
Instead, perhaps you could use SPACES:

    INVOKE    GetWindowRect, winHandle, ADDR gpRect

    MOV    EAX, gpRect.right
    SUB    EAX, gpRect.left
    SUB    EAX, winWidth
    MOV    ECX, EAX
    AND    ECX, 1 SHL 31 ;Mask for sign bit.
    SHR    EAX, 1 ;Quick-n-dirty IDIV / 2.
    OR    EAX, ECX ;Replace sign bit.
    ADD    EAX, gpRect.left
    MOV    ECX, EAX ;Stash x-pos.


Whether or not you use TABS in your code, that is up to you.
However, when you post it for all to view, that is another thing.
It makes it very hard to read and (for some, especially new programmers) Assembly Language can be very hard to read to begin with.   :U


jj2007

Steve,

Have a look at your own post with Firefox. It looks perfectly "tabbed".

SteveAsm

Quote from: jj2007 on April 09, 2012, 03:55:55 PM
Have a look at your own post with Firefox. It looks perfectly "tabbed".

I had figured it was an issue with the forum and it still may be.
I'm not surprised.
I've seen other cases where a forum looked one way on one browser and different on another.

SourceForge is an example of that too.
I guess they all use Firefox there.
They were not aware that their forums looked bad on IE, until I informed them of a similar issue.

At any rate, this forum does not display correctly on IE.
I believe it is fixable.

You can't assume everyone uses Firefox or non-IE browsers.