; _______________________________________________________________________________________
;|                                                                                       |
;| ..::FreshLib::..  Free, open source. Licensed under "BSD 2-clause" license."          |
;|_______________________________________________________________________________________|
;
;  Description: Text caret control library.
;
;  Target OS: Any
;
;  Dependencies: timers.asm; graphics libraries.
;
;  Notes: Text caret have always only one instance.
;_________________________________________________________________________________________
module "Text caret library"

; the caret is shared resource and can exists only in one instance
struct TCaret
  .pWindow   dd ?
  .visible   dd ?
  .state     dd ?       ; 1 if the caret is displayed 0 if not.
  .Pos       TBounds
  .OldPos    TBounds
  .timer     dd ?
  .sync      dd ?
ends



uglobal
  if used CaretAttach
    Caret TCaret
  end if
endg

iglobal
  if used CaretTimes
    CaretTimes dd 200, 300
  end if
endg


if used CaretAttach

initialize InitCaretTimer
begin
        stdcall TimerCreate
        mov     [eax+TTimer.interval], 200
        mov     [eax+TTimer.Callback], __CaretTimerProc
        mov     [eax+TTimer.flags], tmfCallProc
        mov     [Caret.timer], eax
        return
endp


finalize DestroyCaretTimer
begin
        stdcall CaretAttach, 0
        mov     eax, [Caret.timer]
        and     [eax+TTimer.flags], not tmfRunning
        stdcall TimerDestroy, eax
        return
endp

end if


; Attaches the caret to the window, if the window needs caret.
proc CaretAttach, .pWindow
begin
        mov     eax,[.pWindow]
        cmp     eax, [Caret.pWindow]
        je      .exit

        mov     eax, -1
@@:
        xchg    [Caret.sync], eax
        test    eax, eax
        jnz     @b

        stdcall __DrawCaret, 0  ; hide the caret.

        mov     eax, [.pWindow]
        xchg    [Caret.pWindow], eax
        push    eax

        xor     eax, eax
        mov     [Caret.visible], eax
        mov     [Caret.Pos.x], eax
        mov     [Caret.Pos.y], eax
        mov     [Caret.Pos.height], eax
        mov     [Caret.Pos.width], eax

.finish:
        mov     [Caret.sync], 0
        pop     eax     ; the old window.
.exit:
        return
endp




proc CaretChange, .x, .y, .width, .height
begin
        push    eax

        mov     eax, -1
@@:
        xchg    [Caret.sync], eax
        test    eax, eax
        jnz     @b

        stdcall __DrawCaret, 0

        push    [.x] [.y]
        pop     [Caret.Pos.y] [Caret.Pos.x]

        cmp     [.width], 0
        je      .endchange

        push    [.width] [.height]
        pop     [Caret.Pos.height] [Caret.Pos.width]

        stdcall __DrawCaret, 1

.endchange:
        mov     [Caret.sync], 0

        pop     eax
        return
endp


; returns the old mode.
proc __CaretShow, .visible
begin
        mov     eax, [.visible]
        cmp     eax, [Caret.visible]       ; old vs new mode.
        je      .exit

        push    [Caret.visible]
        mov     [Caret.visible], eax

        push    ecx edx

        mov     ecx, [Caret.timer]
        mov     edx, [ecx+TTimer.flags]

        or      edx, tmfRunning
        test    eax, eax
        jnz     .timerok
        and     edx, not tmfRunning
.timerok:
        mov     [ecx+TTimer.flags], edx

        stdcall __DrawCaret, [Caret.visible]
        pop     edx ecx

        pop     eax
.exit:
        return
endp


proc CaretShow, .visible
begin
        mov     eax, -1
@@:
        xchg    [Caret.sync], eax
        test    eax, eax
        jnz     @b

        stdcall __CaretShow, [.visible]
        mov     [Caret.sync], 0

        return
endp



proc __CaretTimerProc, .timer
begin
        cmp     [Caret.pWindow], 0
        je      .exit

        mov     eax, -1
        xchg    [Caret.sync], eax
        test    eax, eax
        jnz     .exit                   ; don't wait for lock.

        cmp     [Caret.visible], 0
        je      @f
        mov     eax, 2
@@:
        stdcall __DrawCaret, eax

        mov     [Caret.sync], 0

.exit:
        return
endp



proc __DrawCaret, .operation    ; 0 - erase; 1-draw; 2-change
begin
        push    ecx esi

        mov     eax, [Caret.state]
        cmp     eax, [.operation]
        je      .exit

        cmp     [Caret.pWindow], 0
        je      .exit

        push    $ffffff

        xor     eax, 1
        mov     [Caret.state], eax

        mov     ecx, [Caret.timer]
        mov     eax, [Caret.state]
        mov     eax, [CaretTimes+4*eax]
        mov     [ecx+TTimer.interval], eax
        mov     [ecx+TTimer.value], 0

        test    eax, eax
        jnz     .draw
; clear
        push    [Caret.OldPos.height]
        push    [Caret.OldPos.width]
        push    [Caret.OldPos.y]
        push    [Caret.OldPos.x]
        jmp     .drawit

.draw:
        push    [Caret.Pos.height] [Caret.Pos.width] [Caret.Pos.y] [Caret.Pos.x]
        push    [Caret.Pos.height] [Caret.Pos.width] [Caret.Pos.y] [Caret.Pos.x]
        pop     [Caret.OldPos.x] [Caret.OldPos.y] [Caret.OldPos.width] [Caret.OldPos.height]

.drawit:
        mov     eax, [Caret.pWindow]
        stdcall AllocateContext, [eax+TWindow.handle]
        test    eax, eax
        jz      .exit

        mov     esi, eax

        stdcall SetDrawMode, esi, cmXor
        stdcall DrawFillRect, esi  ; arguments from the stack
        stdcall ReleaseContext, esi

.exit:
        pop     esi ecx
        return
endp




endmodule


