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

uglobal
  if used fGlobalTerminate
    fGlobalTerminate dd ?
  end if
endg



body ProcessSystemEvents
  .event  XEvent
          rb 256
begin
        push    ebx ecx edx

.event_loop:
; check for quit

        get     eax, [pApplication], TApplication:MainWindow

        test    eax, eax
        jz      .continue     ; ???????????

        cmp     dword [eax], 0
        jne     .continue

        cinvoke XFlush, [hApplicationDisplay]
        xor     eax, eax
        mov     [fGlobalTerminate], 1
        stc
        pop     edx ecx ebx
        return

.continue:
        cinvoke XQLength, [hApplicationDisplay]
        test    eax, eax
        jz      .noevents

        push    edi ecx
        lea     edi, [.event]
        mov     ecx, sizeof.XEvent/4
        xor     eax, eax
        rep stosd
        pop     ecx edi

        lea     ebx, [.event]

        cinvoke  XNextEvent, [hApplicationDisplay], ebx
        stdcall  __ProcessOneSystemEvent, ebx
        jmp      .event_loop

.noevents:
        clc
        pop     edx ecx ebx
        return

endp



body WaitForSystemEvent
.event XEvent
begin
        push    eax ecx edx
        lea     eax, [.event]
        cinvoke XPeekEvent, [hApplicationDisplay], eax
        pop     edx ecx eax
        return
endp






proc __ProcessOneSystemEvent, .linux_event
begin
        pushad

        mov     ebx, [.linux_event]

        stdcall _GetWindowStruct, [ebx+XEvent.window]
        jc      .notprocessed

        test    eax, eax
        jz      .notprocessed

        mov     esi, eax
        mov     ecx, [ebx+XEvent.type]

;        cmp     ecx, [ShmCompletionEvent]
;        je      .shm_completion_event

        cmp     ecx, LASTEvent
        jae     .notprocessed

        mov     ecx, [.jump_table+4*ecx]
        jecxz   .notprocessed

        jmp     ecx


.notprocessed:
        popad
        stc
        return

.finish:
        popad
        clc
        return
;.........................................................................
;.shm_completion_event:
;
;        mov     eax, [ebx+XShmCompletionEvent.shmseg]
;        mov     ecx, [ebx+XShmCompletionEvent.offset]
;
;        OutputValue "XShmCompletionEvent: $", eax, 16, 8;
;
;;        int3
;        jmp     .finish

;.........................................................................
; seMove and seResize events.
;-------------------------------------------------------------------------
.moveresize:
        cinvoke XCheckTypedWindowEvent, [hApplicationDisplay], [ebx+XConfigureEvent.event], ConfigureNotify, ebx
        test    eax, eax
        jnz     .moveresize

        mov     eax, [ebx+XConfigureEvent.event]
        cmp     eax, [ebx+XConfigureEvent.window]
        jne     .is_move

; resize event...
        mov     eax, [esi+TWindow._width]
        mov     edx, [esi+TWindow._height]

        cmp     eax, [ebx+XConfigureEvent.width]
        jne     .resize

        cmp     edx, [ebx+XConfigureEvent.height]
        je      .is_move

.resize:
;        DebugMsg "Resize event!"
        exec    esi, TWindow:Resize, [ebx+XConfigureEvent.width], [ebx+XConfigureEvent.height]
        jmp     .finish

; move event...
.is_move:
        mov     eax, [esi+TWindow._x]
        mov     edx, [esi+TWindow._y]

        cmp     eax, [ebx+XConfigureEvent.x]
        jne     .move

        cmp     edx, [ebx+XConfigureEvent.y]
        je      .finish

.move:
;        OutputValue "Move from X=", eax, 10, -1
;        OutputValue "Move from Y=", edx, 10, -1
;        OutputValue "Move to X=", [ebx+XConfigureEvent.x], 10, -1
;        OutputValue "Move to Y=", [ebx+XConfigureEvent.y], 10, -1
        exec    esi, TWindow:Move, [ebx+XConfigureEvent.x], [ebx+XConfigureEvent.y]

        jmp     .finish

;.........................................................................
; DestroyNotify handler it invalidates the handle in TWindow structure and then destroys TWindow.
.destroy:
        test    esi, esi
        jz      .finish

        mov     [esi+TWindow.handle], 0

        destroy esi
        jmp     .finish


;.........................................................................
; Mouse event handlers

.mousemove:
        cinvoke XCheckTypedWindowEvent, [hApplicationDisplay], [ebx+XMotionEvent.window], MotionNotify, ebx
        test    eax, eax
        jnz     .mousemove

        stdcall ServeMenuMouseMove, [ebx+XMotionEvent.window], [ebx+XMotionEvent.x], [ebx+XMotionEvent.y], [ebx+XMotionEvent.state]
        jc      .finish

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

        stdcall __GetRelativeXY, edi, [ebx+XMotionEvent.x], [ebx+XMotionEvent.y]
        jmp     .target_move

.search_target_move:
        exec    esi, TWindow:ChildByXY, [ebx+XMotionEvent.x], [ebx+XMotionEvent.y], TRUE
        mov     edi, eax

.target_move:
        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, [ebx+XMotionEvent.state]
        jmp     .finish



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



.mouse_btn_press:
        stdcall ServeMenuButtonPress, [ebx+XButtonEvent.window], [ebx+XButtonEvent.button], [ebx+XButtonEvent.state], [ebx+XButtonEvent.x], [ebx+XButtonEvent.y]
        jc      .finish

        mov     edi, [__MouseTarget]
        test    edi, edi
        jz      .search_target_press

        stdcall __GetRelativeXY, edi, [ebx+XButtonEvent.x], [ebx+XButtonEvent.y]
        jmp     .target_press

.search_target_press:
        exec    esi, TWindow:ChildByXY, [ebx+XButtonEvent.x], [ebx+XButtonEvent.y], TRUE
        mov     edi, eax

.target_press:
        get     eax, eax, TWindow:GlobalEnabled
        test    eax, eax
        jz      .finish

;        OutputValue "Mouse button:", [ebx+XButtonEvent.button], 10, -1

        cmp     [ebx+XButtonEvent.button], 4
        jge     .wheelscroll

; 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+XButtonEvent.button], [ebx+XButtonEvent.state], ecx, edx
        jmp     .finish


.wheelscroll:
        mov     ecx, [ebx+XButtonEvent.button]

        mov     edx, scrollY
        cmp     ecx, mbScrollLeft
        jb      @f

        mov     edx, scrollX
        sub     ecx, 2

@@:
        exec    edi, TWindow:EventScroll, edx, ecx, 1
        jmp     .finish



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



.mouse_btn_release:
        mov     edi, [__MouseTarget]
        test    edi, edi
        jz      .search_target_release

        stdcall __GetRelativeXY, edi, [ebx+XButtonEvent.x], [ebx+XButtonEvent.y]
        jmp     .target_release

.search_target_release:
        exec    esi, TWindow:ChildByXY, [ebx+XButtonEvent.x], [ebx+XButtonEvent.y], TRUE
        mov     edi, eax

.target_release:
        get     eax, eax, TWindow:GlobalEnabled
        test    eax, eax
        jz      .finish

        exec    edi, TWindow:EventButtonRelease, [ebx+XButtonEvent.button], [ebx+XButtonEvent.state], ecx, edx
        jmp     .finish


;.........................................................................
; Window paint event

.expose:

locals
  .rect         RECT
  .rect_cnt     dd ?
endl

        get     edi, esi, TWindow:Screen
        test    edi, edi
        jz      .finish

        mov     [.rect_cnt], 0

        stdcall CreateArray, sizeof.RECT
        mov     edx, eax

.add_rect:
        inc     [.rect_cnt]

        mov     eax, [ebx+XExposeEvent.x]
        mov     ecx, [ebx+XExposeEvent.y]

        mov     [.rect.left], eax
        mov     [.rect.top], ecx

        mov     eax, [ebx+XExposeEvent.width]
        mov     ecx, [ebx+XExposeEvent.height]

        add     eax, [.rect.left]
        add     ecx, [.rect.top]

        mov     [.rect.right], eax
        mov     [.rect.bottom], ecx

        lea     eax, [.rect]

        stdcall RectListAdd, edx, eax

        push    edx
        cinvoke XCheckTypedWindowEvent, [hApplicationDisplay], [esi+TWindow.handle], Expose, ebx
        pop     edx

        test    eax, eax
        jnz     .add_rect

;        OutputValue "Expose rectangle count:", [.rect_cnt], 10, -1

        mov     eax, [edx+TArray.count]
        mov     [.rect_cnt], eax

        push    edx
        lea     ebx, [edx+TArray.array]

.draw_loop:

        mov     eax, [ebx+RECT.right]
        mov     ecx, [ebx+RECT.bottom]
        sub     eax, [ebx+RECT.left]
        sub     ecx, [ebx+RECT.top]

        stdcall DrawImageRect, [esi+TWindow.handle], edi, [ebx+RECT.left], [ebx+RECT.top], [ebx+RECT.left], [ebx+RECT.top], eax, ecx

        add     ebx, sizeof.RECT
        dec     [.rect_cnt]
        jnz     .draw_loop

        stdcall FreeMem ; from the stack

;        stdcall DrawImageRect, [esi+TWindow.handle], edi, [ebx+XExposeEvent.x], [ebx+XExposeEvent.y], [ebx+XExposeEvent.x], [ebx+XExposeEvent.y], [ebx+XExposeEvent.width], [ebx+XExposeEvent.height]
        jmp     .finish


;.........................................................................
; Keyboard events.

locals
  .utf8buff rb 16
endl

.key_press:

        cmp     [__FocusedWindow], 0
        je      .finish

        mov     dword [.utf8buff], 0
        lea     eax, [.utf8buff]
        cinvoke Xutf8LookupString, [hInputContext], ebx, eax, 16, 0, 0

        mov     eax, [ebx+XKeyEvent.keycode]
        cinvoke XKeycodeToKeysym, [hApplicationDisplay], eax, 0

        exec    [__FocusedWindow], TWindow:EventKeyPress, dword [.utf8buff], eax, [ebx+XKeyEvent.state]
        jmp     .finish



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



.key_release:

        cmp     [__FocusedWindow], 0
        je      .finish

        mov     eax, [ebx+XKeyEvent.keycode]
        cinvoke XKeycodeToKeysym, [hApplicationDisplay], eax, 0

        exec    [__FocusedWindow], TWindow:EventKeyRelease, eax, [ebx+XKeyEvent.state]
        jmp     .finish



; refreshes keyboard mapping information, stored locally in XLib.
.mapping_notify:
        cinvoke XRefreshKeyboardMapping, ebx
        jmp     .finish


;.........................................................................
; This event is sent from the window manager, when the user click on the close button of the
; window or close it some other way. So, the window have to decide whether to close or not.

.clientmessage:
        mov     eax, dword [ebx+XClientMessageEvent.data]
        cmp     eax, [atomWMDelete]
        jne     .finish

; The window is closed by click on close button or by pressing Alt+F4 or by other WM method
;.close_from_wm:

        exec    esi, TWindow:CloseRequest, cerFromUser
        jmp     .finish


; Focus handling events.

.focusin:
        call    .hide_menu

        stdcall __SearchWindowToFocus, esi

        mov     [__FocusedWindow], eax

        test    eax, eax
        jz      .finish

        exec    eax, TWindow:EventFocusIn
        jmp     .finish


.focusout:
        call    .hide_menu

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

        exec    ebx, TWindow:EventFocusOut
        jmp     .finish



.hideall:
        DebugMsg "Hide one menu"
        set     [__ActiveMenu], TWindow:Visible, FALSE

.hide_menu:
        cmp     [__ActiveMenu], 0
        jne     .hideall

        retn



.jump_table dd  0                   ; event 0
            dd  0                   ; event 1
            dd  .key_press              ; KeyPress = 2
            dd  .key_release            ; KeyRelease = 3
            dd  .mouse_btn_press        ; ButtonPress = 4
            dd  .mouse_btn_release      ; ButtonRelease = 5
            dd  .mousemove              ; MotionNotify = 6
            dd  0                       ; EnterNotify = 7
            dd  0                       ; LeaveNotify = 8
            dd  .focusin                ; FocusIn = 9
            dd  .focusout               ; FocusOut = 10
            dd  0                       ; KeymapNotify = 11
            dd  .expose                 ; Expose = 12
            dd  0                       ; GraphicsExpose = 13
            dd  0                       ; NoExpose = 14
            dd  0                       ; VisibilityNotify = 15
            dd  0                       ; CreateNotify = 16
            dd  .destroy                ; DestroyNotify = 17
            dd  0                       ; UnmapNotify = 18
            dd  0                       ; MapNotify = 19
            dd  0                       ; MapRequest = 20
            dd  0                       ; ReparentNotify = 21
            dd  .moveresize             ; ConfigureNotify = 22
            dd  0                       ; ConfigureRequest = 23
            dd  0                       ; GravityNotify = 24
            dd  0                       ; ResizeRequest = 25
            dd  0                       ; CirculateNotify = 26
            dd  0                       ; CirculateRequest = 27
            dd  0                       ; PropertyNotify = 28
            dd  0                       ; SelectionClear = 29
            dd  0                       ; SelectionRequest = 30
            dd  0                       ; SelectionNotify = 31
            dd  0                       ; ColormapNotify = 32
            dd  .clientmessage          ; ClientMessage = 33
            dd  .mapping_notify         ; MappingNotify = 34
endp



