; _______________________________________________________________________________________
;|                                                                                       |
;| ..::FreshLib::..  Free, open source. Licensed under "Fresh artistic 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


proc ProcessSystemEvents
  .event  XEvent
begin
        push    ebx ecx edx

.event_loop:
; check for quit
        mov     eax, [Application]
        mov     eax, [eax+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 XPending, [hApplicationDisplay]
        test    eax, eax
        jz      .noevents

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

.noevents:
        clc
        pop     edx ecx ebx
        return

endp




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




;proc EventsQueued, .pWin
;begin
;        push    eax ecx edx
;        cinvoke XQLength, [hApplicationDisplay]
;        test    eax, eax
;        clc
;        jz      @f
;        stc
;@@:
;        pop     edx ecx eax
;        return
;endp




proc __ProcessOneSystemEvent, .linux_event
.event  rd 64
begin
        push    eax ebx ecx edx esi edi

        mov     ebx, [.linux_event]

        stdcall _GetWindowStruct, [ebx+XEvent.window]
        test    eax, eax
        jz      .notprocessed

        mov     esi, eax
        lea     edi, [.event]
        mov     eax, [ebx+XEvent.type]

        cmp     eax, MotionNotify
        je      .mousemove

        cmp     eax, EnterNotify
        je      .mouseenter

        cmp     eax, LeaveNotify
         je      .mouseleave

        cmp     eax, ButtonPress
        je      .mouse_btn_press

        cmp     eax, ButtonRelease
        je      .mouse_btn_release

        cmp     eax, Expose
        je      .paint_window

        cmp     eax, ClientMessage        ; event from the window manager - button close for example.
        je      .clientmessage

        cmp     eax, DestroyNotify
        je      .destroy

        cmp     eax, KeyPress
        je      .key_press

        cmp     eax, KeyRelease
        je      .key_release

        cmp     eax, MappingNotify
        je      .mapping_notify

        cmp     eax, FocusIn
        je      .focusin

        cmp     eax, FocusOut
        je      .focusout

        cmp     eax, ConfigureNotify
        je      .moveresize

.notprocessed:
        pop     edi esi edx ecx ebx eax
        stc
        return

.exec_event:
        pushad
        stdcall ExecEvent, esi, edi
        popad

.finish:
        pop     edi esi edx ecx ebx eax
        clc
        return

;.........................................................................
; Resize events
.moveresize:
locals
  .xevent XConfigureEvent
endl
        lea     eax, [.xevent]
        cinvoke XCheckTypedWindowEvent, [hApplicationDisplay], [ebx+XConfigureEvent.window], ConfigureNotify, eax
        test    eax, eax
        jz      .process

        lea     eax, [.xevent]
        cinvoke XPutBackEvent, [hApplicationDisplay], eax

        mov     eax, [.xevent.x]
        cmp     eax, [ebx+XConfigureEvent.x]
        jne     .finish

        mov     eax, [.xevent.y]
        cmp     eax, [ebx+XConfigureEvent.y]
        jne     .finish

        mov     eax, [.xevent.width]
        cmp     eax, [ebx+XConfigureEvent.width]
        jne     .finish

        mov     eax, [.xevent.height]
        cmp     eax, [ebx+XConfigureEvent.height]
        jne     .finish

.process:
        mov     [edi+TMoveResizeEvent.event], seMoveResize

        mov     eax, [ebx+XConfigureEvent.x]
        mov     ecx, [ebx+XConfigureEvent.y]
        mov     [edi+TMoveResizeEvent.newX], eax
        mov     [edi+TMoveResizeEvent.newY], ecx

        cmp     [ebx+XConfigureEvent.height], 100
        jl      @f
        DebugMsg "NewX NewY"
        OutputRegister regEAX, 10
        OutputRegister regECX, 10
@@:
        mov     eax, [ebx+XConfigureEvent.width]
        mov     ecx, [ebx+XConfigureEvent.height]
        mov     [edi+TMoveResizeEvent.newWidth], eax
        mov     [edi+TMoveResizeEvent.newHeight], ecx

        cmp     [ebx+XConfigureEvent.height], 100
        jl      @f
        DebugMsg "NewW NewH"
        OutputRegister regEAX, 10
        OutputRegister regECX, 10
@@:
        jmp     .exec_event

;.........................................................................
; Focus events
.focusout:
        mov     [edi+TFocusOutEvent.event], seFocusOut
        jmp     .setIC

.focusin:
        mov     [edi+TFocusInEvent.event], seFocusIn

.setIC:
        cinvoke XSetICValues, [hInputContext], XNFocusWindow, [esi+TWindow.handle], 0
        jmp     .exec_event

;.........................................................................
; DestroyNotify handler it invalidates the handle in TWindow structure and then destroys TWindow.
.destroy:
        mov     [esi+TWindow.handle], 0
        stdcall Destroy, esi
        jmp     .finish

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

.mouseleave:
        mov     [edi+TMouseEnterEvent.event], seMouseLeave
        jmp     .exec_event

.mouseenter:
        mov     [edi+TMouseEnterEvent.event], seMouseEnter
        jmp     .exec_event

.mousemove:
        mov     [edi+TMouseMoveEvent.event], seMouseMove
        mov     eax, [ebx+XMotionEvent.x]
        mov     ecx, [ebx+XMotionEvent.y]
        mov     [edi+TMouseMoveEvent.x], eax
        mov     [edi+TMouseMoveEvent.y], ecx
        jmp     .exec_event

.mouse_btn_press:
.mouse_btn_release:

        mov     eax, [ebx+XButtonEvent.type]
        mov     [edi+TMouseButtonEvent.event], eax      ; seMouseBtnPress=ButtonPress and seMouseBtnRelease = ButtonRelease

        mov     eax, [ebx+XButtonEvent.button]
        dec     eax

        cmp     [edi+TScrollEvent.event], seMouseBtnRelease
        je      @f

        cmp     eax, 3
        jge     .wheelscroll

@@:
        mov     [edi+TMouseButtonEvent.Button], eax

        mov     eax, [ebx+XButtonEvent.state]
        mov     [edi+TMouseButtonEvent.kbdStatus], eax

        mov     eax, [ebx+XButtonEvent.x]
        mov     ecx, [ebx+XButtonEvent.y]
        mov     [edi+TMouseButtonEvent.x], eax
        mov     [edi+TMouseButtonEvent.y], ecx

        jmp     .exec_event

.wheelscroll:
        mov     ecx, scWheelUp
        je      @f
        mov     ecx, scWheelDn
@@:
        mov     [edi+TScrollEvent.event], seScroll
        mov     [edi+TScrollEvent.ScrollBar], scrollY
        mov     [edi+TScrollEvent.ScrollCmd], ecx
        mov     [edi+TScrollEvent.Value], 1
        jmp     .exec_event

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

.paint_window:
;        cmp     [ebx+XExposeEvent.count], 0
;        jne     .finish

        lea     eax, [ebx+XExposeEvent.x]
        stdcall __PaintWindow, esi, eax

        jmp     .finish

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

locals
  .utf8buff rb 16
endl

.key_press:
        mov     ecx, seKbdKeyPress
        mov     [edi+TKeyboardEvent.event], ecx
        mov     ecx, [ebx+XKeyEvent.state]
        mov     [edi+TKeyboardEvent.kbdStatus], ecx

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

; here, .utg8buff contains the utf-8 string with the character typed on the keyboard.
        mov     eax, dword [.utf8buff]
        mov     [edi+TKeyboardEvent.key], eax

.scancode:
        mov     eax, [ebx+XKeyEvent.keycode]
        cinvoke XKeycodeToKeysym, [hApplicationDisplay], eax, 0
        mov     [edi+TKeyboardEvent.scancode], eax
        jmp     .exec_event


.key_release:
        mov     ecx, seKbdKeyRelease
        mov     eax, [ebx+XKeyEvent.state]

        mov     [edi+TKeyboardEvent.event], ecx
        mov     [edi+TKeyboardEvent.kbdStatus], eax
        mov     [edi+TKeyboardEvent.key], 0
        jmp     .scancode



; 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:
        mov     [edi+TCloseEvent.event], seCloseRequest
        mov     [edi+TCloseEvent.reason], cerFromUser
        jmp     .exec_event
endp








proc __PaintWindow, .pWindow, .ptrBounds
.event TPaintEvent
.caret dd ?
begin
        push    eax ebx ecx edx esi edi

        lea     edi, [.event]
        mov     esi, [.pWindow]

if defined Caret
        mov     [.caret], -1
        cmp     esi, [Caret.pWindow]
        jne     @f
        stdcall CaretShow, FALSE
        mov     [.caret], eax
@@:
end if
        mov     [edi+TPaintEvent.event], sePaint

        stdcall AllocateContext, [esi+TWindow.handle]
        mov     ebx, eax
        mov     [edi+TPaintEvent.context], eax

        xor     eax, eax
        mov     [edi+TPaintEvent.rect.left], eax
        mov     [edi+TPaintEvent.rect.top], eax
        mov     eax, $7fffffff
        mov     [edi+TPaintEvent.rect.right], eax
        mov     [edi+TPaintEvent.rect.bottom], eax

        mov     edx, [.ptrBounds]
        test    edx, edx
        jz      .rectok

        mov     eax, [edx+TBounds.x]
        mov     ecx, [edx+TBounds.y]

        mov     [edi+TPaintEvent.rect.left], eax
        mov     [edi+TPaintEvent.rect.top], ecx

        mov     eax, [edx+TBounds.width]
        mov     ecx, [edx+TBounds.height]
        add     eax, [edi+TPaintEvent.rect.left]
        add     ecx, [edi+TPaintEvent.rect.top]
        mov     [edi+TPaintEvent.rect.right], eax
        mov     [edi+TPaintEvent.rect.bottom], ecx

.rectok:
        pushad
        stdcall ExecEvent, esi, edi
        popad

        stdcall ReleaseContext, ebx

if defined Caret
        cmp     [.caret], -1
        je      @f
        stdcall CaretShow, [.caret]
@@:
end if
        pop     edi esi edx ecx ebx eax
        return
endp
