; _______________________________________________________________________________________
;|                                                                                       |
;| ..::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
  .changed   dd ?
  .timer     dd ?
  .sync      dd ?

  .pImgBackup 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

        cmp     [Caret.pImgBackup], eax
        je      .finish

        stdcall DestroyImage, [Caret.pImgBackup]
        mov     [Caret.pImgBackup], eax

.finish:
        mov     [Caret.sync], eax               ; here eax should be 0
        pop     eax                             ; the old window.

.exit:
        return
endp




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

        or      eax, -1
@@:
        xchg    [Caret.sync], eax
        test    eax, eax
        jnz     .exit

        push    [Caret.visible]
        stdcall __CaretShow, 0

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

        cmp     [.width], 0
        jne     @f
        inc     [.width]
@@:
        cmp     [.height], 0
        jne     @f
        inc     [.height]
@@:

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

        mov     [Caret.changed], 1

        stdcall __CaretShow ; from the stack

.endchange:
        mov     [Caret.sync], 0

.exit:
        pop     eax
        return
endp


; returns the old mode.
proc __CaretShow, .visible
begin
;        OutputValue "Caret show = ", [.visible], 10, -1

        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
        or      eax, -1
@@:
        xchg    [Caret.sync], eax
        test    eax, eax
        jnz     .exit

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

.exit:
        return
endp




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

        or      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
.rect RECT
begin
        pushad

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

        cmp     [Caret.pWindow], 0
        je      .exit

        get     esi, [Caret.pWindow], TWindow:Canvas

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

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

        jz      .clean

        mov     ecx, [Caret.Pos.width]
        mov     edx, [Caret.Pos.height]

        mov     edi, [Caret.pImgBackup]
        test    edi, edi
        jz      .new_backup

        cmp     ecx, [edi+TImage.width]
        jne     .del_backup

        cmp     edx, [edi+TImage.height]
        je      .do_backup

.del_backup:
        stdcall DestroyImage, edi

.new_backup:

        stdcall CreateImage, ecx, edx
        mov     [Caret.pImgBackup], eax
        mov     edi, eax

        mov     [Caret.changed], 1

.do_backup:

        cmp     [Caret.changed], 0
        je      @f

        stdcall CopyImageRect, edi, 0, 0, esi, [Caret.Pos.x], [Caret.Pos.y], [Caret.Pos.width], [Caret.Pos.height]
@@:
        stdcall DrawSolidRect, esi, [Caret.Pos.x], [Caret.Pos.y], [Caret.Pos.width], [Caret.Pos.height], $ffff0000

        exec    [Caret.pWindow], TWindow:RectChangedXY, [Caret.Pos.x], [Caret.Pos.y], [Caret.Pos.width], [Caret.Pos.height], FALSE

        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]
        mov     [Caret.changed], 0

.exit:
        popad
        return

.clean:

        mov     edi, [Caret.pImgBackup]
        test    edi, edi
        jz      .exit

        stdcall CopyImageRect, esi, [Caret.OldPos.x], [Caret.OldPos.y], edi, 0, 0, [Caret.OldPos.width], [Caret.OldPos.height]
        exec    [Caret.pWindow], TWindow:RectChangedXY, [Caret.OldPos.x], [Caret.OldPos.y], [Caret.OldPos.width], [Caret.OldPos.height], FALSE

        jmp     .exit

endp




endmodule


