; _______________________________________________________________________________________
;|                                                                                       |
;| ..::FreshLib::..  Free, open source. Licensed under "BSD 2-clause" license."          |
;|_______________________________________________________________________________________|
;
;  Description: Main procedure of GUI application library.
;
;  Target OS: Win32
;
;  Dependencies:
;
;  Notes: Organize the main message/event loop needed by every GUI engine.
;_________________________________________________________________________________________





body ProcessSystemEvents
.msg MSG
.msg2 MSG
begin
        push    ecx edx esi edi

.msgloop:
        lea     esi, [.msg]
        xor     eax, eax

        invoke  PeekMessageW, esi, eax, eax, eax, PM_REMOVE
        test    eax, eax
        jz      .exitok

        cmp     [.msg.message], WM_QUIT
        je      .terminate

        cmp     [.msg.message], WM_TIMER
        jne     .timer_ok

        xor     eax, eax
        stdcall __TimerProc, eax, eax, eax, eax
        jmp     .msgloop

.timer_ok:
        cmp     [.msg.message], WM_MOUSEMOVE
        jne     .move_ok

.loop:
        invoke  PeekMessageW, esi, [.msg.hwnd], WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE
        test    eax, eax
        jnz     .loop
        jmp     .processit


.move_ok:
        cmp     [.msg.message], WM_KEYDOWN
        jne     .processit

; cleaning the message queue from the keyboard autorepeat messages.

        lea     edi, [.msg2]

.loop2:
        invoke  PeekMessageW, edi, [.msg.hwnd], WM_KEYDOWN, WM_KEYDOWN, PM_NOREMOVE
        test    eax, eax
        jz      .processit

        mov     eax, [.msg2.wParam]
        mov     ecx, [.msg2.lParam]

        cmp     eax, [.msg.wParam]
        jne     .processit

        cmp     ecx, [.msg.lParam]
        jne     .processit

        invoke  PeekMessageW, edi, [.msg.hwnd], WM_KEYDOWN, WM_KEYDOWN, PM_REMOVE
        jmp     .loop2


.processit:
        stdcall __ProcessOneSystemEvent, [.msg.hwnd], [.msg.message], [.msg.wParam], [.msg.lParam]
        jnc     .msgloop

        invoke  DefWindowProcW, [.msg.hwnd], [.msg.message], [.msg.wParam], [.msg.lParam]

;        invoke  TranslateMessage, esi
;        invoke  DispatchMessageW, esi

        jmp     .msgloop


.exitok:
        clc
        pop     edi esi edx ecx
        return


.terminate:
        mov     eax, [.msg.wParam]
        stc
        pop     edi esi edx ecx
        return
endp




proc WaitForSystemEvent
begin
        push    eax ecx edx
        invoke  WaitMessage
        pop     edx ecx eax
        return
endp




;----------------------------------------------------------------------------------------------------------
; This procedure makes following:
;   1. Takes as arguments one windows message
;   2. Analizes the message and finds the target child window for it (only top level windows receive messages)
;   3. Calls the respective event handling method of the target window object.
;   4. returns CF=0 if the event was properly processed.
;   5. returns CF=1 if the event was not processed.
;
; The event can be not processed in the following cases:
;   1. The window that receives the message is not FreshLib object.
;   2. The respective event handler refuse to process the event (return CF=1)
;----------------------------------------------------------------------------------------------------------

proc __ProcessOneSystemEvent, .hwnd, .wmsg, .wparam, .lparam
begin
        pushad

        stdcall _GetWindowStruct, [.hwnd]
        test    eax, eax
        jz      .ondefault

        mov     esi, eax

        dispatch [.wmsg]

.ondefault:
        popad
        stc
        return


.ret_null:
        xor     eax, eax

.processed:
        mov     [esp+4*regEAX], eax
        popad
        clc
        return

;.........................................................................................


;oncase WM_ACTIVATE
;oncase WM_CANCELMODE
;
;        cmp     [__ActiveMenu], 0
;        je      .ondefault
;
;.hideall:
;        set     [__ActiveMenu], TWindow:Visible, FALSE
;        cmp     [__ActiveMenu], 0
;        jne     .hideall
;
;        jmp     .ret_null

;.........................................................................................


oncase WM_MOUSEWHEEL
locals
 .point POINT
endl
        movsx   ecx, word [.lparam]
        movsx   edx, word [.lparam+2]
        mov     [.point.x], ecx
        mov     [.point.y], edx

        lea     eax, [.point]
        invoke  ScreenToClient, [.hwnd], eax

        mov     ecx, [.point.y]
        shl     ecx, 16
        mov     cx, word [.point.x]

        mov     [.lparam], ecx

        movsx   ecx, word [.lparam]
        movsx   edx, word [.lparam+2]
        call    .get_mouse_target

        mov     eax, [.wparam]
        sar     eax, 16
        mov     ecx, scWheelUp
        test    eax, eax
        jns     @f
        mov     ecx, scWheelDn
        neg     eax
@@:
        mov     ebx, 120
        cdq
        div     ebx

        exec    edi, TWindow:EventScroll, scrollY, ecx, eax
        jmp     .ret_null

;.........................................................................................

oncase WM_WINDOWPOSCHANGED

locals
  .rect RECT
endl
        mov     ebx, [.lparam]

        test    [ebx+WINDOWPOS.flags], SWP_NOMOVE
        jnz     .move_ok

        exec    esi, TWindow:Move, [ebx+WINDOWPOS.x], [ebx+WINDOWPOS.y]

.move_ok:
        test    [ebx+WINDOWPOS.flags], SWP_NOSIZE
        jnz     .ret_null

        lea     eax, [.rect]
        invoke  GetClientRect, [esi+TWindow.handle], eax

        exec    esi, TWindow:Resize, [.rect.right], [.rect.bottom]
        jmp     .ret_null


;.........................................................................................

oncase WM_SYSKEYDOWN
oncase WM_SYSKEYUP
oncase WM_KEYDOWN
oncase WM_KEYUP

locals
  .keyboard rb 256
  .unicode  rw 16
  .utf8buff rb 16
  .scancode dd ?
endl

        lea     ebx, [.keyboard]
        invoke  GetKeyboardState, ebx

        mov     eax, [.lparam]
        mov     ecx, eax
        shr     eax, 16
        and     ecx, $80000000
        and     eax, $ff
        mov     [.scancode], eax

        mov     dword [.utf8buff], 0

        or      eax, ecx
        lea     edx, [.unicode]
        invoke  ToUnicode, [.wparam], eax, ebx, edx, 16, 0
        xor     ecx, ecx
        test    eax, eax
        jz      .key_translated    ; the key can not be translated.
        js      .key_translated    ; it is a dead key. What we have to do???

        lea     edx, [.utf8buff]
        xor     ecx, ecx
        mov     [edx], ecx
        mov     [edx+4], ecx
        lea     ecx, [.unicode]
        invoke  WideCharToMultiByte, CP_UTF8, 0, ecx, eax, edx, 8, 0, 0

.key_translated:
        xor     eax, eax

        cmp     [__FocusedWindow], eax
        jz      .ondefault

        test    [.keyboard+VK_CONTROL], $80
        jz      @f
        or      eax, maskCtrl
@@:
        test    [.keyboard+VK_SHIFT], $80
        jz      @f
        or      eax, maskShift
@@:
        test    [.keyboard+VK_MENU], $80
        jz      @f
        or      eax, maskAlt
@@:
        test    [.keyboard+VK_SCROLL], $01
        jz      @f
        or      eax, maskScrLk
@@:
        test    [.keyboard+VK_CAPITAL], $01
        jz      @f
        or      eax, maskCapsLock
@@:
        test    [.keyboard+VK_LBUTTON], $80
        jz      @f
        or      eax, maskBtnLeft
@@:
        test    [.keyboard+VK_RBUTTON], $80
        jz      @f
        or      eax, maskBtnRight
@@:
        test    [.keyboard+VK_MBUTTON], $80
        jz      @f
        or      eax, maskBtnMiddle
@@:

        cmp     [.wmsg], WM_KEYUP
        je      .key_up
        cmp     [.wmsg], WM_SYSKEYUP
        je      .key_up

;.key_down:

        exec    [__FocusedWindow], TWindow:EventKeyPress, dword [.utf8buff], [.scancode], eax
        jmp     .end_key_msg

.key_up:
        exec    [__FocusedWindow], TWindow:EventKeyRelease, [.scancode], eax

.end_key_msg:
        cmp     [.wmsg], WM_SYSKEYDOWN
        je      .ondefault

        cmp     [.wmsg], WM_SYSKEYUP
        je      .ondefault

        jmp     .ret_null

;.........................................................................................


oncase WM_SETFOCUS

        stdcall __SearchWindowToFocus, esi

        mov     [__FocusedWindow], eax

        test    eax, eax
        jz      .ret_null

        exec    eax, TWindow:EventFocusIn
        jmp     .ret_null


;.........................................................................................


oncase WM_KILLFOCUS

        xor     ebx, ebx
        xchg    [__FocusedWindow], ebx
        test    ebx, ebx
        jz      .ret_null

        exec    ebx, TWindow:EventFocusOut
        jmp     .ret_null

;.........................................................................................


oncase WM_MOUSEMOVE

        movsx   ecx, word [.lparam]
        movsx   edx, word [.lparam+2]

        stdcall ServeMenuMouseMove, [.hwnd], ecx, edx, 0
        jc      .ret_null

        call    .get_mouse_target

        get     eax, edi, TWindow:Enabled
        test    eax, eax
        jz      .ret_null

        cmp     edi, [__LastPointedWindow]
        je      .move_event

        cmp     [__LastPointedWindow], 0
        je      .leave_ok

        exec    [__LastPointedWindow], TWindow:EventMouseLeave

.leave_ok:

        mov     [__LastPointedWindow], edi
        exec    edi, TWindow:EventMouseEnter

.move_event:

        exec    edi, TWindow:EventMouseMove, ecx, edx, 0
        jmp     .ret_null


;.........................................................................................


oncase WM_PAINT

locals
  .ps     PAINTSTRUCT
  .hdc    dd ?
endl

        get     edi, esi, TWindow:Screen

        lea     eax, [.ps]
        invoke  BeginPaint, [.hwnd], eax
        mov     [.hdc], eax

        test    edi, edi
        jz      .paint_ok

        mov     ecx, [.ps.rcPaint.right]
        mov     edx, [.ps.rcPaint.bottom]
        sub     ecx, [.ps.rcPaint.left]
        sub     edx, [.ps.rcPaint.top]

        stdcall DrawImageRect, [.hdc], edi, [.ps.rcPaint.left], [.ps.rcPaint.top], [.ps.rcPaint.left], [.ps.rcPaint.top], ecx, edx

.paint_ok:
        lea     eax, [.ps]
        invoke  EndPaint, [.hwnd], eax

        jmp     .ret_null


;.........................................................................................


oncase WM_CLOSE

        DebugMsg "Close request."

        get     eax, [pApplication], TApplication:MainWindow
        test    eax, eax
        jz      @f

        cmp     [eax], esi
        jne     @f

DebugMsg "Quit message posted."
        invoke  PostQuitMessage, 0

@@:
        exec    esi, TWindow:CloseRequest, cerFromUser
        jmp     .ret_null


oncase WM_DESTROY
        mov     [esi+TWindow.handle], 0
        jmp     .ondefault

;.........................................................................................


oncase WM_LBUTTONUP
oncase WM_RBUTTONUP
oncase WM_MBUTTONUP
        movsx   ecx, word [.lparam]
        movsx   edx, word [.lparam+2]
        call    .get_mouse_target

        get     eax, edi, TWindow:Enabled
        test    eax, eax
        jz      .ret_null

        mov     ebx, [.wmsg]
        sub     ebx, WM_LBUTTONDOWN
        movzx   ebx, byte [.__mouse_button_table+ebx]

        stdcall __KeyStatus, [.wparam]

        exec    edi, TWindow:EventButtonRelease, ebx, eax, ecx, edx
        jmp     .ret_null


oncase WM_LBUTTONDOWN
oncase WM_RBUTTONDOWN
oncase WM_MBUTTONDOWN

locals
  .kbdState dd ?
endl

        movsx   ecx, word [.lparam]
        movsx   edx, word [.lparam+2]

        mov     ebx, [.wmsg]
        sub     ebx, WM_LBUTTONDOWN
        movzx   ebx, byte [.__mouse_button_table+ebx]

        stdcall __KeyStatus, [.wparam]
        mov     [.kbdState], eax

        stdcall ServeMenuButtonPress, [.hwnd], ebx, eax, ecx, edx
        jc      .ret_null

        call    .get_mouse_target

        get     eax, edi, TWindow:Enabled
        test    eax, eax
        jz      .ret_null

; deal with focus

        cmp     edi, [__FocusedWindow]
        je      .focusok

        get     eax, edi, TWindow:WantFocus
        test    eax, eax
        jz      .focusok

        mov     eax, [__FocusedWindow]
        mov     [__FocusedWindow], edi


        test    eax, eax
        jz      @f
        exec    eax, TWindow:EventFocusOut
@@:

        exec    edi, TWindow:EventFocusIn

.focusok:
        exec    edi, TWindow:EventButtonPress, ebx, [.kbdState], ecx, edx
        jmp     .ret_null


; Double clicks are not supported for now...

;oncase WM_LBUTTONDBLCLK
;oncase WM_RBUTTONDBLCLK
;oncase WM_MBUTTONDBLCLK



        enddispatch


.get_mouse_target:
        mov     edi, [__MouseTarget]
        test    edi, edi
        jz      .search_target_move

        stdcall __GetRelativeXY, edi, ecx, edx
        jmp     .target_move

.search_target_move:
        exec    esi, TWindow:ChildByXY, ecx, edx, FALSE
        mov     edi, eax

.target_move:
        retn


  .__mouse_button_table db mbLeft,        mbLeft,       mbLeft
                        db mbRight,       mbRight,      mbRight
                        db mbMiddle,      mbMiddle,     mbMiddle
endp


;.........................................................................................



proc __KeyStatus, .status
begin
        xor     eax, eax
        test    [.status], MK_LBUTTON
        jz      @f
        or      eax, maskBtnLeft
@@:
        test    [.status], MK_RBUTTON
        jz      @f
        or      eax, maskBtnRight
@@:
        test    [.status], MK_MBUTTON
        jz      @f
        or      eax, maskBtnMiddle
@@:
        test    [.status], MK_SHIFT
        jz      @f
        or      eax, maskShift
@@:
        test    [.status], MK_CONTROL
        jz      @f
        or      eax, maskCtrl
@@:
        return
endp

