;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;                                                              ;;
;; Copyright (C) KolibriOS team 2004-2010. All rights reserved. ;;
;; Copyright (C) MenuetOS 2000-2004 Ville Mikael Turjanmaa      ;;
;; Distributed under terms of the GNU General Public License    ;;
;;                                                              ;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

$Revision$

;==============================================================================
;///// public functions ///////////////////////////////////////////////////////
;==============================================================================

button.MAX_BUTTONS = 4095

struc SYS_BUTTON
{
  .pslot  dw ?
  .id_lo  dw ?
  .left   dw ?
  .width  dw ?
  .top    dw ?
  .height dw ?
  .id_hi  dw ?
          dw ?
  .sizeof:
}
virtual at 0
  SYS_BUTTON SYS_BUTTON
end virtual

align 4
;------------------------------------------------------------------------------
syscall_button: ;///// system function 8 //////////////////////////////////////
;------------------------------------------------------------------------------
;? Define/undefine GUI button object
;------------------------------------------------------------------------------
;; Define button:
;> ebx = pack[16(x), 16(width)]
;> ecx = pack[16(y), 16(height)]
;> edx = pack[8(flags), 24(button identifier)]
;>       flags bits:
;>          7 (31) = 0
;>          6 (30) = don't draw button
;>          5 (29) = don't draw button frame when pressed
;> esi = button color
; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
;; Undefine button:
;> edx = pack[8(flags), 24(button identifier)]
;>       flags bits:
;>          7 (31) = 1
;------------------------------------------------------------------------------
        ; do we actually need to undefine the button?
        test    edx, 0x80000000
        jnz     .remove_button

        ; do we have free button slots available?
        mov     edi, [BTN_ADDR]
        mov     eax, [edi]
        cmp     eax, button.MAX_BUTTONS
        jge     .exit

        ; does it have positive size? (otherwise it doesn't have sense)
        or      bx, bx
        jle     .exit
        or      cx, cx
        jle     .exit

        ; make coordinates clientbox-relative
        push    eax
        mov     eax, [current_slot]
        rol     ebx, 16
        add     bx, word[eax + APPDATA.wnd_clientbox.left]
        rol     ebx, 16
        rol     ecx, 16
        add     cx, word[eax + APPDATA.wnd_clientbox.top]
        rol     ecx, 16
        pop     eax

        ; basic checks passed, define the button
        push    ebx ecx edx
        inc     eax
        mov     [edi], ax
        shl     eax, 4
        add     edi, eax
        ; NOTE: this code doesn't rely on SYS_BUTTON struct, please revise it
        ;       if you change something
        mov     ax, [CURRENT_TASK]
        stosw
        mov     ax, dx
        stosw               ; button id number: bits 0-15
        mov     eax, ebx
        rol     eax, 16
        stosd               ; x start | x size
        mov     eax, ecx
        rol     eax, 16
        stosd               ; y start | y size
        mov     eax, edx
        shr     eax, 16
        stosw               ; button id number: bits 16-31
        pop     edx ecx ebx

        ; do we also need to draw the button?
        test    edx, 0x40000000
        jnz     .exit

        ; draw button body

        pushad

        ; calculate window-relative coordinates
        movzx   edi, cx
        shr     ebx, 16
        shr     ecx, 16
        mov     eax, [TASK_BASE]
        add     ebx, [eax - twdw + WDATA.box.left]
        add     ecx, [eax - twdw + WDATA.box.top]
        mov     eax, ebx
        shl     eax, 16
        mov     ax, bx
        add     ax, word[esp + 16]
        mov     ebx, ecx
        shl     ebx, 16
        mov     bx, cx

        ; calculate initial color
        mov     ecx, esi
        cmp     [buttontype], 0
        je      @f
        call    button._.incecx2

        ; set button height counter
    @@: mov     edx, edi

  .next_line:
        call    button._.button_dececx
        push    edi
        xor     edi, edi
        call    [draw_line]
        pop     edi
        add     ebx, 0x00010001
        dec     edx
        jnz     .next_line

        popad

        ; draw button frame

        push    ebx ecx

        ; calculate window-relative coordinates
        shr     ebx, 16
        shr     ecx, 16
        mov     eax, [TASK_BASE]
        add     ebx, [eax - twdw + WDATA.box.left]
        add     ecx, [eax - twdw + WDATA.box.top]

        ; top border
        mov     eax, ebx
        shl     eax, 16
        mov     ax, bx
        add     ax, [esp + 4]
        mov     ebx, ecx
        shl     ebx, 16
        mov     bx, cx
        push    ebx
        xor     edi, edi
        mov     ecx, esi
        call    button._.incecx
        call    [draw_line]

        ; bottom border
        movzx   edx, word[esp + 4 + 0]
        add     ebx, edx
        shl     edx, 16
        add     ebx, edx
        mov     ecx, esi
        call    button._.dececx
        call    [draw_line]

        ; left border
        pop     ebx
        push    edx
        mov     edx, eax
        shr     edx, 16
        mov     ax, dx
        mov     edx, ebx
        shr     edx, 16
        mov     bx, dx
        add     bx, [esp + 4 + 0]
        pop     edx
        mov     ecx, esi
        call    button._.incecx
        call    [draw_line]

        ; right border
        mov     dx, [esp + 4]
        add     ax, dx
        shl     edx, 16
        add     eax, edx
        add     ebx, 0x00010000
        mov     ecx, esi
        call    button._.dececx
        call    [draw_line]

        pop     ecx ebx

  .exit:
        ret

; FIXME: mutex needed
syscall_button.remove_button:
        and     edx, 0x00ffffff
        mov     edi, [BTN_ADDR]
        mov     ebx, [edi]
        inc     ebx
        imul    esi, ebx, SYS_BUTTON.sizeof
        add     esi, edi
        xor     ecx, ecx
        add     ecx, -SYS_BUTTON.sizeof
        add     esi, SYS_BUTTON.sizeof

  .next_button:
        dec     ebx
        jz      .exit

        add     ecx, SYS_BUTTON.sizeof
        add     esi, -SYS_BUTTON.sizeof

        ; does it belong to our process?
        mov     ax, [CURRENT_TASK]
        cmp     ax, [esi + SYS_BUTTON.pslot]
        jne     .next_button

        ; does the identifier match?
        mov     eax, dword[esi + SYS_BUTTON.id_hi - 2]
        mov     ax, [esi + SYS_BUTTON.id_lo]
        and     eax, 0x00ffffff
        cmp     edx, eax
        jne     .next_button

        ; okay, undefine it
        push    ebx
        mov     ebx, esi
        lea     eax, [esi + SYS_BUTTON.sizeof]
        call    memmove
        dec     dword[edi]
        add     ecx, -SYS_BUTTON.sizeof
        pop     ebx
        jmp     .next_button

  .exit:
        ret

align 4
;------------------------------------------------------------------------------
sys_button_activate_handler: ;/////////////////////////////////////////////////
;------------------------------------------------------------------------------
;? <description>
;------------------------------------------------------------------------------
;> eax = pack[8(process slot), 24(button id)]
;> ebx = pack[16(button x coord), 16(button y coord)]
;> cl = mouse button mask this system button was pressed with
;------------------------------------------------------------------------------
        call    button._.find_button
        or      eax, eax
        jz      .exit

        mov     ebx, dword[eax + SYS_BUTTON.id_hi - 2]
        call    button._.negative_button

  .exit:
        ret

align 4
;------------------------------------------------------------------------------
sys_button_deactivate_handler: ;///////////////////////////////////////////////
;------------------------------------------------------------------------------
;? <description>
;------------------------------------------------------------------------------
;> eax = pack[8(process slot), 24(button id)]
;> ebx = pack[16(button x coord), 16(button y coord)]
;> cl = mouse button mask this system button was pressed with
;------------------------------------------------------------------------------
        call    button._.find_button
        or      eax, eax
        jz      .exit

        mov     ebx, dword[eax + SYS_BUTTON.id_hi - 2]
        call    button._.negative_button

  .exit:
        ret

align 4
;------------------------------------------------------------------------------
sys_button_perform_handler: ;//////////////////////////////////////////////////
;------------------------------------------------------------------------------
;? <description>
;------------------------------------------------------------------------------
;> eax = pack[8(process slot), 24(button id)]
;> ebx = pack[16(button x coord), 16(button y coord)]
;> cl = mouse button mask this system button was pressed with
;------------------------------------------------------------------------------
        shl     eax, 8
        mov     al, cl
        movzx   ebx, byte[BTN_COUNT]
        mov     [BTN_BUFF + ebx * 4], eax
        inc     bl
        mov     [BTN_COUNT], bl
        ret

;==============================================================================
;///// private functions //////////////////////////////////////////////////////
;==============================================================================

;------------------------------------------------------------------------------
button._.find_button: ;////////////////////////////////////////////////////////
;------------------------------------------------------------------------------
;? Find system button by specified process slot, id and coordinates
;------------------------------------------------------------------------------
;> eax = pack[8(process slot), 24(button id)] or 0
;> ebx = pack[16(button x coord), 16(button y coord)]
;------------------------------------------------------------------------------
;< eax = pointer to SYS_BUTTON struct or 0
;------------------------------------------------------------------------------
        push    ecx edx esi edi

        mov     edx, eax
        shr     edx, 24
        and     eax, 0x0ffffff

        mov     edi, [BTN_ADDR]
        mov     ecx, [edi]
        imul    esi, ecx, SYS_BUTTON.sizeof
        add     esi, edi
        inc     ecx
        add     esi, SYS_BUTTON.sizeof

  .next_button:
        dec     ecx
        jz      .not_found

        add     esi, -SYS_BUTTON.sizeof

        ; does it belong to our process?
        cmp     dx, [esi + SYS_BUTTON.pslot]
        jne     .next_button

        ; does id match?
        mov     edi, dword[esi + SYS_BUTTON.id_hi - 2]
        mov     di, [esi + SYS_BUTTON.id_lo]
        and     edi, 0x0ffffff
        cmp     eax, edi
        jne     .next_button

        ; does coordinates match?
        mov     edi, dword[esi + SYS_BUTTON.left - 2]
        mov     di, [esi + SYS_BUTTON.top]
        cmp     ebx, edi
        jne     .next_button

        ; okay, return it
        mov     eax, esi
        jmp     .exit

  .not_found:
        xor     eax, eax

  .exit:
        pop     edi esi edx ecx
        ret

;------------------------------------------------------------------------------
button._.dececx: ;/////////////////////////////////////////////////////////////
;------------------------------------------------------------------------------
;? <description>
;------------------------------------------------------------------------------
        sub     cl, 0x20
        jnc     @f
        xor     cl, cl
    @@: sub     ch, 0x20
        jnc     @f
        xor     ch, ch
    @@: rol     ecx, 16
        sub     cl, 0x20
        jnc     @f
        xor     cl, cl
    @@: rol     ecx, 16
        ret

;------------------------------------------------------------------------------
button._.incecx: ;/////////////////////////////////////////////////////////////
;------------------------------------------------------------------------------
;? <description>
;------------------------------------------------------------------------------
        add     cl, 0x20
        jnc     @f
        or      cl, -1
    @@: add     ch, 0x20
        jnc     @f
        or      ch, -1
    @@: rol     ecx, 16
        add     cl, 0x20
        jnc     @f
        or      cl, -1
    @@: rol     ecx, 16
        ret

;------------------------------------------------------------------------------
button._.incecx2: ;////////////////////////////////////////////////////////////
;------------------------------------------------------------------------------
;? <description>
;------------------------------------------------------------------------------
        add     cl, 0x14
        jnc     @f
        or      cl, -1
    @@: add     ch, 0x14
        jnc     @f
        or      ch, -1
    @@: rol     ecx, 16
        add     cl, 0x14
        jnc     @f
        or      cl, -1
    @@: rol     ecx, 16
        ret

;------------------------------------------------------------------------------
button._.button_dececx: ;//////////////////////////////////////////////////////
;------------------------------------------------------------------------------
;? <description>
;------------------------------------------------------------------------------
        cmp     [buttontype], 1
        jne     .finish

        push    eax
        mov     al, 1
        cmp     edi, 20
        jg      @f
        mov     al, 2

    @@: sub     cl, al
        jnc     @f
        xor     cl, cl
    @@: sub     ch, al
        jnc     @f
        xor     ch, ch
    @@: rol     ecx, 16
        sub     cl, al
        jnc     @f
        xor     cl, cl
    @@: rol     ecx, 16

        pop     eax

  .finish:
        ret

;------------------------------------------------------------------------------
button._.negative_button: ;////////////////////////////////////////////////////
;------------------------------------------------------------------------------
;? Invert system button border
;------------------------------------------------------------------------------
        ; if requested, do not display button border on press.
        test    ebx, 0x20000000
        jnz     .exit

        pushad

        xchg    esi, eax

        movzx   ecx, [esi + SYS_BUTTON.pslot]
        shl     ecx, 5
        add     ecx, window_data

        mov     eax, dword[esi + SYS_BUTTON.left]
        mov     ebx, dword[esi + SYS_BUTTON.top]
        add     eax, [ecx + WDATA.box.left]
        add     ebx, [ecx + WDATA.box.top]
        push    eax ebx
        pop     edx ecx
        rol     eax, 16
        rol     ebx, 16
        add     ax, cx
        add     bx, dx

        mov     esi, 0x01000000
        call    draw_rectangle.forced

        popad

  .exit:
        ret