News:

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

Advanced centering window proc

Started by jdoe, January 19, 2006, 06:06:15 PM

Previous topic - Next topic

jdoe

Hi,

I writing a centering window proc and I have a problem with RECT structure and math operation.
Left and top from RECT can be negative numbers (defined as dword in windows.inc and as Long on MSDN).

How can I substract, add or divide with this structure. Few lines of code would be appreciated because I've search on the web and I can hardly find something for handling my problem.


Thanks



Mincho Georgiev

the shr 1(shift right) divides by 2 using bit shifting. You can do it like that:

CenterWnd PROC hwnd:HWND
      
      LOCAL p :POINT
      LOCAL rc :RECT
      
         invoke GetSystemMetrics,SM_CXSCREEN
         mov p.x,eax
         invoke GetSystemMetrics,SM_CYSCREEN
         mov p.y,eax
         invoke GetClientRect,hwnd,addr rc
         mov eax,rc.right
         shr eax,1
         mov ebx,p.x
         shr ebx,1
         sub ebx,eax
         mov p.x,ebx
         mov eax,rc.bottom
         shr eax,1
         mov ebx,p.y
         shr ebx,1
         sub ebx,eax
         mov p.y,ebx
         
         invoke SetWindowPos,hwnd,HWND_NOTOPMOST,p.x,p.y,0,0,SWP_NOSIZE
         
   ret
CenterWnd ENDP

Tedd

Use SAR instead of SHR in order to preserve the sign-bit.

-2 (FFFFFFFFFFFFFFFEh) SHR 1 = 9223372036854775807 (7FFFFFFFFFFFFFFFh)
-2 (FFFFFFFFFFFFFFFEh) SAR 1 = -1 (FFFFFFFFFFFFFFFFh)


To add/subtract/mulitply/divide with a structure, you need to take each component separately and perform the operation on each component.
No snowflake in an avalanche feels responsible.

jdoe

@shaka_zulu
Thanks for this example

@Tedd
SAR is what I was needing  :U


I have already coded a centering function with VB and now that I'm learning MASM, I translate my VB function to MASM to get use to it. My CenterWindow procedure is done and I would like to have comments from experts - what I could have done differently or better or shorter...

This procedure will be part of my library. If I look at m32lib there is no includelib and it must be include from the project directly. What is the incidence of including includelib in the asm libraries (this way you don't need to declare the needed lib in the whole project if they're only used in an asm lib). Not sure I'm clear but it's the most I can be for now as a newbie.  :lol




;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Philippe Coulombe © 2006
;; ebmoluocp@yahoo.ca
;;
;; CenterWindow, hWndChild, hWndParent
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

.586

.model flat, stdcall

option casemap:none

include \masm32\include\windows.inc

include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib


.code

align 4
;
; Center child windows (hWndChild) into parent window or desktop
; if hWndParent is NULL. Parent don't need to be the owner.
;
; No returned value
;
CenterWindow proc hWndChild:dword, hWndParent:dword

   local rectChild:RECT         ; Child window coordonate
   local rectParent:RECT        ; Parent window coordonate
   local rectDesktop:RECT       ; Desktop coordonate (WORKAREA)
   local ddChildLeft:dword      ;;
   local ddChildTop:dword       ;; Child window new coordonate
   local ddChildWidth:dword     ;; used by MoveWindow
   local ddChildHeight:dword    ;;

   invoke GetWindowRect, hWndChild, addr rectChild
   .if eax != 0    ; 0 = no centering possible

       invoke SystemParametersInfo, SPI_GETWORKAREA, NULL, addr rectDesktop, NULL
       .if eax != 0    ; 0 = no centering possible

           invoke GetWindowRect, hWndParent, addr rectParent
           .if eax == 0    ; 0 = we take the desktop as parent (invalid or NULL hWndParent)

               mov eax, rectDesktop.left
               mov rectParent.left, eax
               mov eax, rectDesktop.top
               mov rectParent.top, eax
               mov eax, rectDesktop.right
               mov rectParent.right, eax
               mov eax, rectDesktop.bottom
               mov rectParent.bottom, eax

           .endif

           ;
           ; Get new coordonate and make sure the child window
           ; is not moved outside the desktop workarea
           ;
           mov eax, rectChild.right       ; width = right - left
           sub eax, rectChild.left
           mov ddChildWidth, eax
           mov eax, rectParent.right
           sub eax, rectParent.left
           sub eax, ddChildWidth          ; eax = Parent width - Child width...
           sar eax, 1                     ; divided by 2
           add eax, rectParent.left
           cmp eax, rectDesktop.left
           jns @F
           mov eax, rectDesktop.left
           @@:
           mov ddChildLeft, eax
           add eax, ddChildWidth          ; eax = new left coord + child width
           mov ecx, rectDesktop.right
           cmp ecx, eax
           jns @F
           sub ecx, ddChildWidth
           mov ddChildLeft, ecx

           @@:
           mov eax, rectChild.bottom      ; height = bottom - top
           sub eax, rectChild.top
           mov ddChildHeight, eax
           mov eax, rectParent.bottom
           sub eax, rectParent.top
           sub eax, ddChildHeight         ; eax = Parent height - Child height...
           sar eax, 1
           add eax, rectParent.top
           cmp eax, rectDesktop.top       ; eax (child top) must not be smaller
           jns @F                         ; than Desktop.top, if so...
           mov eax, rectDesktop.top       ; child top = Desktop.top
           @@:
           mov ddChildTop, eax
           add eax, ddChildHeight         ; eax = new top coord + child height
           mov ecx, rectDesktop.bottom
           cmp ecx, eax                   ; child window must not disapear
           jns @F                         ; outside desktop bottom, if so...
           sub ecx, ddChildHeight         ; child top = Desktop.bottom - child height
           mov ddChildTop, ecx            ;

           @@:
           ;
           ; Now we have the new coordonate - the dialog window can be moved
           ;
           invoke MoveWindow, hWndChild, ddChildLeft, ddChildTop, ddChildWidth, ddChildHeight, TRUE

       .endif

   .endif

   ret

CenterWindow endp

end




Thanks


Mincho Georgiev

Hello, again, jdoe !
I like your coding style it's very nice and clean and you put nice comments. You may call yourself a newbie ,but there's alot of people that think about theirselfs for 'good programmers' but they can learn something from you in that direction  :U
Seriously, i've just finished reading of an old source ,which was so hard written that my eyes hurts already.
Keep doin' it.
Greetings!

jdoe

Quote from: shaka_zulu on January 23, 2006, 12:23:27 AM
Hello, again, jdoe !
I like your coding style it's very nice and clean and you put nice comments. You may call yourself a newbie ,but there's alot of people that think about theirselfs for 'good programmers' but they can learn something from you in that direction  :U
Seriously, i've just finished reading of an old source ,which was so hard written that my eyes hurts already.
Keep doin' it.
Greetings!


@shaka_zulu
Nice to see I'm on the right way. I'm a newbie to MASM but I have a 7 years experience with VB and the reason I'm asking for comments about this piece of code is that I know there is good VB coding techniques and it must be the same with MASM. Maybe I want to go too fast but if I start with good advices, I won't loosing time with bad coding habits.  :boohoo:


jdoe


Hi again,

I have found how to do signed compare with .IF (here on MASM forum)....

.if sdword ptr eax > x

This is not an intuitive syntax. Without reading it I would never figure how to do signed compare with .IF directive.
I made correction on my CenterWindow proc and I think it is good enough now. So there it is...



;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;
;; Philippe Coulombe © 2006
;; ebmoluocp@yahoo.ca
;;
;; CenterWindow, hWndChild, hWndParent
;;
;; Last update 2006-01-23
;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

.586

.model flat, stdcall

option casemap:none

include \masm32\include\windows.inc

include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib


.code

align 4
;
; Center child window hWndChild into parent window or desktop
; if hWndParent is NULL. Parent don't need to be the owner.
;
; No returned value
;
CenterWindow proc hWndChild:dword, hWndParent:dword

   local rectChild:RECT         ; Child window coordonate
   local rectParent:RECT        ; Parent window coordonate
   local rectDesktop:RECT       ; Desktop coordonate (WORKAREA)
   local dwChildLeft:dword      ;;
   local dwChildTop:dword       ;; Child window new coordonate
   local dwChildWidth:dword     ;; used by MoveWindow
   local dwChildHeight:dword    ;;

   invoke GetWindowRect, hWndChild, addr rectChild
   .if eax != 0    ; 0 = no centering possible

       invoke SystemParametersInfo, SPI_GETWORKAREA, NULL, addr rectDesktop, NULL
       .if eax != 0    ; 0 = no centering possible

           invoke GetWindowRect, hWndParent, addr rectParent
           .if eax == 0    ; 0 = we take the desktop as parent (invalid or NULL hWndParent)

               mov eax, rectDesktop.left
               mov rectParent.left, eax
               mov eax, rectDesktop.top
               mov rectParent.top, eax
               mov eax, rectDesktop.right
               mov rectParent.right, eax
               mov eax, rectDesktop.bottom
               mov rectParent.bottom, eax

           .endif

           ;
           ; Get new coordonate and make sure the child window
           ; is not moved outside the desktop workarea
           ;
           mov eax, rectChild.right                   ; width = right - left
           sub eax, rectChild.left
           mov dwChildWidth, eax
           mov eax, rectParent.right
           sub eax, rectParent.left
           sub eax, dwChildWidth                      ; eax = Parent width - Child width...
           sar eax, 1                                 ; divided by 2
           add eax, rectParent.left                   ; eax = temporary left coord (need validation)
           .if sdword ptr eax < rectDesktop.left
               mov eax, rectDesktop.left
           .endif
           mov dwChildLeft, eax
           add eax, dwChildWidth                      ; eax = new left coord + child width
           .if sdword ptr eax > rectDesktop.right     ; if child right outside desktop workarea
               mov eax, rectDesktop.right
               sub eax, dwChildWidth                  ; right = desktop right - child width
               mov dwChildLeft, eax                   ;
           .endif

           mov eax, rectChild.bottom                  ; height = bottom - top
           sub eax, rectChild.top
           mov dwChildHeight, eax
           mov eax, rectParent.bottom
           sub eax, rectParent.top
           sub eax, dwChildHeight                     ; eax = Parent height - Child height...
           sar eax, 1
           add eax, rectParent.top
           .if sdword ptr eax < rectDesktop.top       ; eax (child top) must not be smaller, if so...
               mov eax, rectDesktop.top               ; child top = Desktop.top
           .endif
           mov dwChildTop, eax
           add eax, dwChildHeight                     ; eax = new top coord + child height
           .if sdword ptr eax > rectDesktop.bottom
               mov eax, rectDesktop.bottom            ; child is outside desktop bottom
               sub eax, dwChildHeight                 ; child top = Desktop.bottom - child height
               mov dwChildTop, eax                    ;
           .endif

           ;
           ; Now we have the new coordonate - the dialog window can be moved
           ;
           invoke MoveWindow, hWndChild, dwChildLeft, dwChildTop, dwChildWidth, dwChildHeight, TRUE

       .endif

   .endif

   ret

CenterWindow endp

end



Don't forget... I like critics.


jdoe

Quote from: jdoe on January 23, 2006, 05:28:18 PM
               mov eax, rectDesktop.left
               mov rectParent.left, eax
               mov eax, rectDesktop.top
               mov rectParent.top, eax
               mov eax, rectDesktop.right
               mov rectParent.right, eax
               mov eax, rectDesktop.bottom
               mov rectParent.bottom, eax

About this few lines...

I red this in an Iczelion's tut.

push  hInstance
pop wc.hInstance

Is there any advantage of doing it this way instead of using MOV


zooba

Some people think there is, some people think there isn't. My testing shows the push/pop style is a few cycles slower, but if speed is that important you shouldn't be doing memory-memory moves anyway,

My personal preference is the mov eax,... / mov ...,eax syntax (though with my macros it's 'move ..., eax, ...') :U

Tedd

The reason for push-pop is to avoid messing up any registers. If you have a register to spare, then I would use it.
Or even better, if you have two..

    mov eax, rectDesktop.left
    mov ecx, rectDesktop.top
    mov rectParent.left, eax
    mov rectParent.top, ecx

    mov eax, rectDesktop.right
    mov ecx, rectDesktop.bottom
    mov rectParent.right, eax
    mov rectParent.bottom, ecx
No snowflake in an avalanche feels responsible.

Snouphruh

Quote from: shaka_zulu on January 20, 2006, 10:16:31 AM
the shr 1(shift right) divides by 2 using bit shifting. You can do it like that:

CenterWnd PROC hwnd:HWND
      
      LOCAL p :POINT
      LOCAL rc :RECT
      
         invoke GetSystemMetrics,SM_CXSCREEN
         mov p.x,eax
         invoke GetSystemMetrics,SM_CYSCREEN
         mov p.y,eax
         invoke GetClientRect,hwnd,addr rc
         mov eax,rc.right
         shr eax,1
         mov ebx,p.x
         shr ebx,1
         sub ebx,eax
         mov p.x,ebx
         mov eax,rc.bottom
         shr eax,1
         mov ebx,p.y
         shr ebx,1
         sub ebx,eax
         mov p.y,ebx
         
         invoke SetWindowPos,hwnd,HWND_NOTOPMOST,p.x,p.y,0,0,SWP_NOSIZE
         
   ret
CenterWnd ENDP

Hey, hey, hey!
Why so much?
Let it be that your application window has width = 600 and height = 400.
Look here:

                        mov esi, GetSystemMetrics
                        mov edi, 600

                        push TRUE
                        push 400
                        push edi

                        push SM_CYSCREEN
                        call esi
                        sub eax, 400
                        sar eax, 1
                        push eax

                        push SM_CXSCREEN
                        call esi
                        sub eax, edi
                        sar eax, 1
                        push eax

                        push [hWnd]
                        call MoveWindow


It will center your application window on the screen.

Mincho Georgiev

The answer is simple.
The general purpose of the accumulator/EAX/ is arithmetic operations,let's not involve the stack.
After all, you can do timing if you like, and even if the timing succeed /but i doubt/ , don't forget that the stack is involved
/UNNECESSARILY/ and not only one more register is in use, but that can make a trouble with your entire code if you made a mistake somewhere
/I'm talking in general not only in the current case/.
Remember that 'counting the lines' is not always the most important thing.

Greetings.

Mincho Georgiev

QuoteHey, hey, hey!
Why so much?
Let it be that your application window has width = 600 and height = 400.
Look here:

By the way, here is an a little bet optimized variant that i'd wrote, you can start timing now  :bdg

CenterWindow PROC hwnd:DWORD

      LOCAL rc: RECT
      LOCAL xscr :DWORD
      LOCAL yscr :DWORD
         
         invoke GetSystemMetrics,SM_CXSCREEN
         cdq
         sub eax,edx
         sar eax,1
         mov xscr,eax
         invoke GetSystemMetrics,SM_CYSCREEN
         cdq
         sub eax,edx
         sar eax,1
         mov yscr,eax
         invoke GetClientRect,hwnd,addr rc
         mov eax,rc.right
         cdq
         sub eax,edx
         sar eax,1
         mov ebx,xscr
         sub ebx,eax
         mov xscr,ebx
         mov eax,rc.bottom
         cdq
         sub eax,edx
         sar eax,1
         mov ebx,yscr
         sub ebx,eax
         mov yscr,ebx
         invoke SetWindowPos,hwnd,NULL,xscr,yscr,0,0,SWP_NOSIZE
         
ret   
CenterWindow ENDP

Mincho Georgiev

I'm just kiddin' ,Snouphruh , no affence!
Let we stop become petty and looking so mutch in details and write something real.
In one textbook that i had read years ago about algorhytms and programming in general there was a frase:
Dont optimize anything, till you get it done.

Best Regards.

Mincho.

Snouphruh

Quote from: shaka_zulu on January 31, 2006, 07:44:56 PM
I'm just kiddin' ,Snouphruh , no affence!

That's OK! We are here for the experience exchange.

Quote
In one textbook that i had read years ago about algorhytms and programming in general there was a frase:
Dont optimize anything, till you get it done.

It's a Great advice!
I always write code with no optimization, and only after that I make some ones.

I use MoveWindow function in my code above. As you know it requires 6 parameters. Thus, PUSH EAX instruction just moves the parameter on the stack for the MoveWindow function. The stack is always involved in transferring parameters for a function anyway.
Or maybe MOV [ESP - n], EAX ... SUB ESP, nn sequence of instructions is better?

And a question for another topic:
MOVQ MM0, [ESI + 8 * EBX] ; mm0 = val4  val3  val2  val1
I need MM0 register to contain first 2 values with sign-extension, i. e. mm0 = sign val2 sign val1
Something like MOVSX or CDQ instructions do.
Can you help me here?