; _______________________________________________________________________________________
;|                                                                                       |
;| ..::FreshLib::..  Free, open source. Licensed under "BSD 2-clause" license."          |
;|_______________________________________________________________________________________|
;
;  Description: Memory based images manipulation library.
;
;  Target OS: Linux
;
;  Dependencies: memory.asm
;
;  Notes:
;_________________________________________________________________________________________

uses libX11, xshm



struct TImage
  .width   dd ?  ; width in pixels.
  .height  dd ?  ; height in pixels.
  .pPixels dd ?  ; pointer to the pixel memory.

; os dependent data

  .lock    TMutex

  .ximage  dd ?
  .shminfo XShmSegmentInfo
ends



body CreateImage
begin
        pushad

        stdcall GetMem, sizeof.TImage
        jc      .finish
        mov     esi, eax

        xor     eax, eax
        inc     eax

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

        cmp     ecx, 0
        cmovle  ecx, eax

        cmp     edx, 0
        cmovle  edx, eax

        mov     [esi+TImage.width], ecx
        mov     [esi+TImage.height], edx

        lea     eax, [4*ecx]
        imul    eax, edx

        cmp     [flagSharedMemory], 0
        je      .fall_back

        cinvoke shmget, IPC_PRIVATE, eax, IPC_CREAT or 777o
        test    eax, eax
        js      .error

        mov     [esi+TImage.shminfo.ShmID], eax

        cinvoke shmat, eax, 0, 0
        cmp     eax, -1
        je      .error_free

        mov     [esi+TImage.shminfo.Addr], eax
        mov     [esi+TImage.pPixels], eax
        mov     [esi+TImage.shminfo.fReadOnly], 0

        lea     ebx, [esi+TImage.shminfo]
        cinvoke XShmCreateImage, [hApplicationDisplay], 0, $20, ZPixmap, eax, ebx, [esi+TImage.width], [esi+TImage.height]
        mov     [esi+TImage.ximage], eax


;     XShmAttach will return the shared segment ID that later will be returned in the
; XShmCompletionEvent.shmseg field on completion events.
;
;     This value is a server side ID and can not be easy associated with the client side image
; in order to provide some locking mechanism.
;
;     The only way I know is to collect these IDs in some array (hash table, etc.) and later to
; search in it in order to unlock the image.

        cinvoke XShmAttach, [hApplicationDisplay], ebx

;        OutputValue "Create image SegID2: $", [esi+TImage.shminfo.SegID], 16, 8

.exit_ok:
        mov     [esp+4*regEAX], esi

        lea     esi, [esi+TImage.lock]
        stdcall MutexCreate, 0, esi
        stdcall MutexRelease, esi

        clc

.finish:
        popad
        return

.error_free:
;        OutputValue "Error create image ShmID: $", [esi+TImage.shminfo.ShmID], 16, 8
        cinvoke shmctl, [esi+TImage.shminfo.ShmID], IPC_RMID, 0

.error:
        stdcall FreeMem, esi
        stc
        jmp     .finish


.fall_back:
        stdcall GetMem, eax
        mov     [esi+TImage.pPixels], eax

        cinvoke XCreateImage, [hApplicationDisplay], 0, $20, ZPixmap, 0, eax, [esi+TImage.width], [esi+TImage.height], 32, 0
        test    eax, eax
        jz      .error

        mov     [esi+TImage.ximage], eax
        jmp     .exit_ok
endp




body DestroyImage
begin
        pushad

        mov     esi, [.ptrImage]
        test    esi, esi
        jz      .finish

.lock_wait:
        stdcall LockImg, esi
        jc      .lock_wait

        cmp     [flagSharedMemory], 0
        je      .fall_back

;        OutputValue "Destroy image SegID: $", [esi+TImage.shminfo.SegID], 16, 8

        lea     eax, [esi+TImage.shminfo]
        cinvoke XShmDetach, [hApplicationDisplay], eax

        cinvoke XSync, [hApplicationDisplay], FALSE

        cinvoke XDestroyImage, [esi+TImage.ximage]

        cinvoke shmdt, [esi+TImage.shminfo.Addr]
        cinvoke shmctl, [esi+TImage.shminfo.ShmID], IPC_RMID, 0

.exit_ok:
        lea     edi, [esi+TImage.lock]
        stdcall MutexDestroy, edi
        stdcall FreeMem, esi

.finish:
        popad
        return


.fall_back:
        mov     eax, [esi+TImage.ximage]
        stdcall FreeMem, [eax+XImage.pData]
        mov     [eax+XImage.pData], 0

        cinvoke XDestroyImage, eax
        jmp     .exit_ok

endp




if used ___CheckCompletionEvent

___CheckCompletionEvent:

virtual at esp+4
  .display dd ?
  .pEvent  dd ?
  .user    dd ?
end virtual

; timeout
        mov     eax, [.pEvent]
        mov     eax, [eax+XEvent.type]
        cmp     eax, [ShmCompletionEvent]
        sete    al
        movzx   eax, al
        retn
end if





body DrawImageRect
.event XEvent
       rb 256
;.start dd ?
begin
        pushad

;if defined options.DebugMode & options.DebugMode
;        stdcall GetFineTimestamp
;        mov     [.start], eax
;end if

        mov     esi, [.pImage]
        test    esi, esi
        jz      .exit

        stdcall LockImg, esi
        jc      .exit

;        OutputValue "Draw image pixels: $", [esi+TImage.pPixels], 16, 8
;        OutputValue "Draw image SegID: $", [esi+TImage.shminfo.SegID], 16, 8

        mov     ebx, [esi+TImage.ximage]

        mov     ecx, [esi+TImage.width]
        mov     edx, [esi+TImage.height]

        sub     ecx, [.xSrc]
        jl      .exit

        sub     edx, [.ySrc]
        jl      .exit

        cmp     ecx, [.width]
        jl      .exit

        cmp     edx, [.height]
        jl      .exit

        cinvoke XCreateGC, [hApplicationDisplay], [.where], 0, 0
        mov     edi, eax

        cmp     [flagSharedMemory], 0
        jne     .draw_shm

        cinvoke XPutImage, [hApplicationDisplay], [.where], edi, [esi+TImage.ximage], [.xSrc], [.ySrc], [.xDst], [.yDst], [.width], [.height]
        jmp     .exit_ok

.draw_shm:

; For now I will keep the event processing disabled, but if later some flickering start to appear, it can be
; reenabled again.

        cinvoke XShmPutImage, [hApplicationDisplay], [.where], edi, [esi+TImage.ximage], [.xSrc], [.ySrc], [.xDst], [.yDst], [.width], [.height], 0     ;TRUE

;        lea     eax, [.event]
;        cinvoke XIfEvent, [hApplicationDisplay], eax, ___CheckCompletionEvent, eax

.exit_ok:
        cinvoke XFreeGC, [hApplicationDisplay], edi
        stdcall UnlockImg, esi

.exit:

;if defined options.DebugMode & options.DebugMode
;        stdcall GetFineTimestamp
;        sub     eax, [.start]
;
;        OutputValue "Image draw time [us]:", eax, 10, -1
;;        OutputValue "Image pointer:", [.pImage], 16, 8
;;        OutputRect  [.xDst], [.yDst], [.width], [.height]
;
;end if

        popad
        return

endp




