Висящее уведомление о событии XShmPutImage

Я использую расширение XShm для рисования и управления изображениями в Linux.

Чтобы не мерцать экран, я передаю send_event = TRUE в XShmPutImage, а затем жду события с XIfEvent сразу после вызова XScmPutImage.

Таким образом, я делаю блокировку изображения, чтобы не изменять изображение до тех пор, пока оно не будет отображаться на поверхности окна.

Обычно все работает нормально. Но иногда, когда у меня интенсивный рисунок рисунка, кажется, что событие никогда не приходит и процедура рисования висит.

Где искать проблему? Используется ли XIfEvent для этой задачи? Как событие может исчезнуть из очереди сообщений?

Возможно ли, что XShmPutImage не отправит событие (если send_event = TRUE) или отправить событие, отличное от ShmCompletion, при некоторых обстоятельствах? (например, при некоторой внутренней ошибке или что-то в этом роде)

EDIT:

После еще нескольких исследований я обнаружил, что такие зависания происходят только тогда, когда оконный менеджер интенсивно генерирует события в окне. Например, когда я изменяю размер окна, перетаскивая его углы.

EDIT2:

Я пробовал несколько способов решить эту проблему, но безуспешно. В конце я был вынужден использовать некоторый тайм-аут и отменить ожидание через некоторое время. Но, конечно, это грязный хак, и я все равно хочу его исправить.

Итак, что может быть причиной того, что XShmPutImage не отправляет событие, если send_event = TRUE или возможно, что это событие исчезнет из очереди сообщений?

EDIT3:

Вот сомнительный код (FASM):

        cinvoke XShmPutImage, ......, TRUE

    .loop:
        lea     eax, [.event]
        cinvoke XCheckTypedEvent, [Display], [ShmCompletionEvent], eax

        test    eax, eax
        jz      .loop      ; there is no message

NB: XShmPutImage всегда возвращает TRUE, независимо от того, зависает ли проверка события или нет, поэтому я не выполнил проверку ошибок после нее.

EDIT4:

Из-за запроса я отправляю весь код функции рисования. В коде используются некоторые макро-библиотеки FASM, но, по крайней мере, идеи ясны (надеюсь)

Обратите внимание, что этот код содержит обходной код, который ограничивает событие, ожидающее только 20 мс. Без этого таймаута цикл ожидания просто висит навсегда. Номер события XShm получен путем вызова XShmGetEventBase, как рекомендовано в документации Xshm.

; Draws the image on a OS provided window surface.
proc DrawImageRect, .where, .pImage, .xDst, .yDst, .xSrc, .ySrc, .width, .height
.event XEvent
       rb 256
begin
        pushad

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

        mov     ebx, [esi+TImage.ximage]

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


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

        stdcall GetTimestamp
        lea     esi, [eax+20]    ; 20ms timeout

.loop:
        lea     eax, [.event]
        cinvoke XCheckTypedEvent, [hApplicationDisplay], [ShmCompletionEvent], eax
        test    eax, eax
        jnz     .finish

        stdcall GetTimestamp
        cmp     eax, esi
        jb      .loop

.finish:
        cinvoke XFreeGC, [hApplicationDisplay], edi

.exit:
        popad
        return

endp

И вот код основного цикла событий приложения.

Процедура __ProcessOneSystemEvent просто отправляет события в объекты GUI и игнорирует все события, которые она не использует. Он не обрабатывает ShmCompletionEvent вообще.

Все окна, созданные в приложении, имеют маску событий: ExposureMask+FocusChangeMask+KeyPressMask+KeyReleaseMask+ButtonPressMask+ButtonReleaseMask+EnterWindowMask+LeaveWindowMask+PointerMotionMask+StructureNotifyMask

proc ProcessSystemEvents
  .event  XEvent
          rb 256
begin
        push    ebx ecx edx

.event_loop:
; check for quit

        get     eax, [pApplication], 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

        push    edi ecx
        lea     edi, [.event]
        mov     ecx, sizeof.XEvent/4
        xor     eax, eax
        rep stosd
        pop     ecx edi

        lea     ebx, [.event]

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

.noevents:
        clc
        pop     edx ecx ebx
        return

endp

Полный исходный код доступен в репозитории, но это очень большой проект, который нелегкий для навигации. Рассматриваемый источник находится в режиме регистрации 8453c99b1283def8.

Файлы: "freshlib/graphics/images.asm" и "freshlib/graphics/Linux/images.asm" относятся к графическому изображению.

Файлы: "freshlib/gui/Main.asm" и "freshlib/gui/Linux/Main.asm" относятся к общей обработке событий в приложении.

Ответ 1

Что делает сервер X?

X-сервер может и будет подавлять ShmCompletionEvent, если параметры, переданные в XShmPutImage, превышают геометрию области общей памяти, прикрепленной к XImage в вызове. Сервер проверяет X/Y и ширину/высоту на ранее сохраненные лимиты для данной общей области, и если параметры вызова являются вне пределов, сервер вернет BadValue, подавит операцию рисования и подавляет событие завершения.

Вышеупомянутое - это именно то, что происходит в вашей библиотеке. Вот как:

  • Основная процедура диспетчера событий - ProcessSystemEvents. Он выполняет XEventNext и основан на типе события, используя таблицу переходов .jump_table отправляет функцию обработчика событий.
  • Функция события для события Expose .expose
  • Функция .expose, в свою очередь, вызовет DrawImageRect, используя значения X/Y и ширины/высоты из структуры XExposeEvent. Это неверно и является истинным источником ошибки, как мы увидим на мгновение.
  • DrawImageRect будет передавать эти значения во время вызова XShmPutImage
  • Обработчик для XShmPutImage на сервере X будет проверять эти параметры и отклонять, если они находятся за пределами границ.

Параметры отклоняются, поскольку они происходят из события экспозиции и связаны с геометрией окна , а не с геометрией разделяемой памяти, прикрепленной к XImage, используемой в XShmPutImage вызов.

В частности, если окно только что изменилось (например, оконным менеджером) и было увеличено, а для изменения размера произошло предыдущее событие ConfigureNotify. Теперь, с новым событием Expose, он будет иметь большую ширину/высоту, которая будет превышать ширину/высоту области общей памяти, о которой знает сервер.

Клиент несет ответственность за изменение событий изменения размера окна [и т.д.] и разрывание/воссоздание области общей памяти с увеличенным размером. Выполняется не и является источником ошибки.

ПРИМЕЧАНИЕ.. Чтобы полностью понять это, сервер может сообщать об ошибке и ничего не делать по нескольким причинам:

  • Сервер знает о окне [и его изменении размера].
  • Он знает о XImage, его общей области памяти и размере
  • Но они связаны только во время вызова XShmPutImage [AFAIK]
  • Даже если сервер может их ассоциировать, он не может настроить shmarea
  • Это потому, что у него нет возможности перенаправить shmarea на клиентскую сторону.
  • Только клиент может сделать это через XShmDetach/XShmAttach

Ниже приведены отредактированные версии соответствующих исходных файлов из c5c765bc7e commit. Они немного почищены, поэтому остаются только самые родственные части. Некоторые линии были усечены или обернуты, чтобы исключить горизонтальную прокрутку.

Файлы были аннотированы с помощью NOTE и NOTE/BUG, которые я делал при их анализе.


gui/Main.asm Основной основной цикл верхнего уровня. Ничего не видно, чтобы увидеть здесь.

; FILE: gui/Main.asm
; _____________________________________________________________________________
;|                                                                             |
;| ..::FreshLib::.. Free, open source. Licensed under "BSD 2-clause" license." |
;|_____________________________________________________________________________|
;
;  Description: Main procedure of GUI application library.
;
;  Target OS: Any
;
;  Dependencies:
;
;  Notes: Organize the main message/event loop needed by every GUI engine.
;         This file contains only OS independent part and includes OS dependent
;         files.
;______________________________________________________________________________

module "Main library"

proc Run
begin
.mainloop:
        stdcall ProcessSystemEvents
        jc      .terminate

        mov     eax, [pApplication]
        test    eax, eax
        jz      .eventok

        get     ecx, eax, TApplication:OnIdle
        jecxz   .eventok

        stdcall ecx, eax

.eventok:
        stdcall WaitForSystemEvent
        jmp     .mainloop

.terminate:
        DebugMsg "Terminate GUI application!"
        return
endp

include '%TargetOS%/Main.asm'

endmodule

gui/Linux/Main.asm Обработчики событий

; FILE: gui/Linux/Main.asm
; _____________________________________________________________________________
;|                                                                             |
;| ..::FreshLib::.. Free, open source. Licensed under "BSD 2-clause" license." |
;|_____________________________________________________________________________|
;
;  Description: Main procedure of GUI application library.
;
;  Target OS: Linux
;
;  Dependencies:
;
;  Notes: Organize the main message/event loop needed by every GUI engine.
;______________________________________________________________________________

body ProcessSystemEvents
; NOTE: this is the storage for the dequeued event -- all dispatch routines
; should use it and process it
  .event  XEvent
          rb 256

begin
        push    ebx ecx edx

.event_loop:
; check for quit

        get     eax, [pApplication], 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

; NOTE: it is wasteful for the main loop to call WaitForSystemEvent, then call
; us and we do XPending on the first loop -- we already know we have at least
; one event waiting in the queue
.continue:
        cinvoke XPending, [hApplicationDisplay]
        test    eax, eax
        jz      .noevents

        push    edi ecx
        lea     edi, [.event]
        mov     ecx, sizeof.XEvent/4
        xor     eax, eax
        rep stosd
        pop     ecx edi

        lea     ebx, [.event]

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

.noevents:
        clc
        pop     edx ecx ebx
        return

endp

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

proc __ProcessOneSystemEvent, .linux_event
begin
        pushad

        mov     ebx, [.linux_event]

;        mov     eax, [ebx+XEvent.type]
;        cmp     eax, [ShmCompletionEvent]
;        je      .shm_completion

        stdcall _GetWindowStruct, [ebx+XEvent.window]
        jc      .notprocessed

        test    eax, eax
        jz      .notprocessed

        mov     esi, eax
        mov     ecx, [ebx+XEvent.type]

        cmp     ecx, LASTEvent
        jae     .notprocessed

        mov     ecx, [.jump_table+4*ecx]
        jecxz   .notprocessed

        jmp     ecx

.notprocessed:
        popad
        stc
        return

.finish:
        popad
        clc
        return

;.shm_completion:
;        DebugMsg "Put back completion event!"
;
;        int3
;        cinvoke XPutBackEvent, [hApplicationDisplay], ebx
;        jmp     .finish

;.........................................................................
; seMove and seResize events.
;-------------------------------------------------------------------------
.moveresize:

; NOTE/BUG!!!!: we must not only process a resize/move request, but we must also
; adjust the size of the shmarea attached to the XImage -- that is _not_ being
; done. (e.g.) if the window is enlarged, the shmarea must be enlarged

        cinvoke XCheckTypedWindowEvent, [hApplicationDisplay],
                [ebx+XConfigureEvent.window], ConfigureNotify, ebx
        test    eax, eax
        jnz     .moveresize

; resize event...
        mov     eax, [esi+TWindow._width]
        mov     edx, [esi+TWindow._height]
        cmp     eax, [ebx+XConfigureEvent.width]
        jne     .resize
        cmp     edx, [ebx+XConfigureEvent.height]
        je      .is_move

.resize:
        exec    esi, TWindow:EventResize, [ebx+XConfigureEvent.width],
                [ebx+XConfigureEvent.height]

; move event...
.is_move:
        mov     eax, [esi+TWindow._x]
        mov     edx, [esi+TWindow._y]
        cmp     eax, [ebx+XConfigureEvent.x]
        jne     .move
        cmp     eax, [ebx+XConfigureEvent.y]
        je      .finish

.move:
        exec    esi, TWindow:EventMove,
                [ebx+XConfigureEvent.x], [ebx+XConfigureEvent.y]

        jmp     .finish

;.........................................................................
; DestroyNotify handler it invalidates the handle in TWindow structure and
; then destroys TWindow.
.destroy:
        test    esi, esi
        jz      .finish

        mov     [esi+TWindow.handle], 0

        destroy esi
        jmp     .finish

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

.expose:
        get     edi, esi, TWindow:ImgScreen

; NOTE:BUG!!!!!
;
; if the window has been resized (e.g. enlarged), these values are wrong!
; they relate to the _window_ but _not_ the shmarea that is attached to the
; XImage
;
; however, DrawImageRect will call XShmPutImage with these values, they
; will exceed the geometry of what the X server knows about the shmarea and
; it will return BadValue and _suppress_ the completion event for XShmPutImage

        stdcall DrawImageRect, [esi+TWindow.handle], edi,
                [ebx+XExposeEvent.x],[ebx+XExposeEvent.y],
                [ebx+XExposeEvent.x], [ebx+XExposeEvent.y],
                [ebx+XExposeEvent.width], [ebx+XExposeEvent.height]

        jmp     .finish

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

.mousemove:

        cinvoke XCheckTypedWindowEvent, [hApplicationDisplay],
                [ebx+XConfigureEvent.window], MotionNotify, ebx
        test    eax, eax
        jnz     .mousemove

        stdcall ServeMenuMouseMove, [ebx+XMotionEvent.window],
                [ebx+XMotionEvent.x], [ebx+XMotionEvent.y],
                [ebx+XMotionEvent.state]
        jc      .finish

        cinvoke XCheckTypedWindowEvent, [hApplicationDisplay],
                [ebx+XMotionEvent.window], MotionNotify, ebx
        test    eax, eax
        jnz     .mousemove

        mov     edi, [__MouseTarget]
        test    edi, edi
        jz      .search_target_move

        stdcall __GetRelativeXY, edi, [ebx+XMotionEvent.x], [ebx+XMotionEvent.y]
        jmp     .target_move

.search_target_move:
        exec    esi, TWindow:ChildByXY, [ebx+XMotionEvent.x],
                [ebx+XMotionEvent.y], TRUE
        mov     edi, eax

.target_move:
        cmp     edi, [__LastPointedWindow]
        je      .move_event

        cmp     [__LastPointedWindow], 0
        je      .leave_ok

        exec    [__LastPointedWindow], TWindow:EventMouseLeave

.leave_ok:

        mov     [__LastPointedWindow], edi
        exec    edi, TWindow:EventMouseEnter

.move_event:
        exec    edi, TWindow:EventMouseMove, ecx, edx, [ebx+XMotionEvent.state]
        jmp     .finish

;.........................................................................
; event jump table
.jump_table dd  0                       ; event 0
            dd  0                       ; event 1
            dd  .key_press              ; KeyPress = 2
            dd  .key_release            ; KeyRelease = 3
            dd  .mouse_btn_press        ; ButtonPress = 4
            dd  .mouse_btn_release      ; ButtonRelease = 5
            dd  .mousemove              ; MotionNotify = 6
            dd  0                       ; EnterNotify = 7
            dd  0                       ; LeaveNotify = 8
            dd  .focusin                ; FocusIn = 9
            dd  .focusout               ; FocusOut = 10
            dd  0                       ; KeymapNotify = 11
            dd  .expose                 ; Expose = 12
            dd  0                       ; GraphicsExpose = 13
            dd  0                       ; NoExpose = 14
            dd  0                       ; VisibilityNotify = 15
            dd  0                       ; CreateNotify = 16
            dd  .destroy                ; DestroyNotify = 17
            dd  0                       ; UnmapNotify = 18
            dd  0                       ; MapNotify = 19
            dd  0                       ; MapRequest = 20
            dd  0                       ; ReparentNotify = 21
            dd  .moveresize             ; ConfigureNotify = 22
            dd  0                       ; ConfigureRequest = 23
            dd  0                       ; GravityNotify = 24
            dd  0                       ; ResizeRequest = 25
            dd  0                       ; CirculateNotify = 26
            dd  0                       ; CirculateRequest = 27
            dd  0                       ; PropertyNotify = 28
            dd  0                       ; SelectionClear = 29
            dd  0                       ; SelectionRequest = 30
            dd  0                       ; SelectionNotify = 31
            dd  0                       ; ColormapNotify = 32
            dd  .clientmessage          ; ClientMessage = 33
            dd  .mapping_notify         ; MappingNotify = 34

graphics/Linux/images.asm Код чертежа изображения [включая функцию DrawImageRect] и код создания/уничтожения общей памяти.

; FILE: graphics/Linux/images.asm
; _____________________________________________________________________________
;|                                                                             |
;| ..::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
  .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

        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], 1

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

        cinvoke XShmAttach, [hApplicationDisplay], ebx

        clc
        mov     [esp+4*regEAX], esi

.finish:
        popad
        return

.error_free:
        cinvoke shmctl, [ebx+XShmSegmentInfo.ShmID], IPC_RMID, 0

.error:
        stdcall FreeMem, esi
        stc
        jmp     .finish

endp

body DestroyImage
begin
        pushad

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

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

        cinvoke XDestroyImage, [esi+TImage.ximage]

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

.finish:
        popad
        return
endp

;if used ___CheckCompletionEvent
;___CheckCompletionEvent:
;
;virtual at esp+4
;  .display dd ?
;  .pEvent  dd ?
;  .user    dd ?
;end virtual
;
;; timeout
;        stdcall GetTimestamp
;        cmp     eax, [.user]
;        jbe     @f
;
;        DebugMsg "Timeout!"
;
;        mov     eax, 1
;        retn
;
;@@:
;        mov     eax, [.pEvent]      ;.pEvent
;        mov     eax, [eax+XEvent.type]
;
;        cmp     eax, [ShmCompletionEvent]
;        sete    al
;        movzx   eax, al
;        retn
;end if

body DrawImageRect
.event XEvent
       rb 256
begin
        pushad

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

        mov     ebx, [esi+TImage.ximage]

; NOTE: is this necessary? it seems wasteful to create and destroy a GC
; repeatedly.  Dunno, does this _have_ to be done here, _every_ time?
        cinvoke XCreateGC, [hApplicationDisplay], [.where], 0, 0
        mov     edi, eax

; NOTE/BUG: The return ShmCompletionEvent will be suppressed due to a BadValue
; if the X/Y and width/height parameters given to us by caller exceed the
; geometry/range of the shmarea attached to .ximage
;
; the routine that calls us is .expose and it _is_ giving us bad values.  it is
; passing us X/Y width/height related to an exposure event of the .where
; _window_ which we put in the call.  The X server will compare these against
; the size of the shmarea of TImage.xmage and complain if we exceed the bounds

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

; NOTE/BUG: this code should _not_ be looping on XCheckTypedEvent because it
; disrupts the normal event processing.  if we want to be "synchronous" on this
; we should loop on the main event dispatcher (ProcessSystemEvents) and let it
; dispatch to a callback we create.  we can set a "pending" flag that our [not
; yet existent] dispatch routine can clear

; THIS CODE SOMETIMES CAUSES HANGS!

        stdcall GetTimestamp
        lea     esi, [eax+20]

.loop:
        lea     eax, [.event]
        cinvoke XCheckTypedEvent, [hApplicationDisplay], [ShmCompletionEvent],
                eax
        test    eax, eax
        jnz     .finish

        stdcall GetTimestamp
        cmp     eax, esi
        jb      .loop

.finish:
        cinvoke XFreeGC, [hApplicationDisplay], edi

.exit:
        popad
        return

endp

Xext/shm.c Код сервера X, который проверяет и обрабатывает вызов XShmPutImage.

// FILE: Xext/shm.c

static int
ProcShmPutImage(ClientPtr client)
{
    GCPtr pGC;
    DrawablePtr pDraw;
    long length;
    ShmDescPtr shmdesc;

    REQUEST(xShmPutImageReq);

    REQUEST_SIZE_MATCH(xShmPutImageReq);
    VALIDATE_DRAWABLE_AND_GC(stuff->drawable, pDraw, DixWriteAccess);
    VERIFY_SHMPTR(stuff->shmseg, stuff->offset, FALSE, shmdesc, client);

    // NOTE: value must be _exactly_ 0/1
    if ((stuff->sendEvent != xTrue) && (stuff->sendEvent != xFalse))
        return BadValue;

    if (stuff->format == XYBitmap) {
        if (stuff->depth != 1)
            return BadMatch;
        length = PixmapBytePad(stuff->totalWidth, 1);
    }
    else if (stuff->format == XYPixmap) {
        if (pDraw->depth != stuff->depth)
            return BadMatch;
        length = PixmapBytePad(stuff->totalWidth, 1);
        length *= stuff->depth;
    }
    else if (stuff->format == ZPixmap) {
        if (pDraw->depth != stuff->depth)
            return BadMatch;
        length = PixmapBytePad(stuff->totalWidth, stuff->depth);
    }
    else {
        client->errorValue = stuff->format;
        return BadValue;
    }

    // NOTE/BUG: The following block is the "check parameters" code.  If the
    // given drawing parameters of the request (e.g. X, Y, width, height) [or
    // combinations thereof] exceed the geometry/size of the shmarea, the
    // BadValue error is being returned here and the code to send a return
    // event will _not_ be executed.  The bug isn't really here, it on the
    // client side, but it the client side bug that causes the event to be
    // suppressed

    /*
     * There a potential integer overflow in this check:
     * VERIFY_SHMSIZE(shmdesc, stuff->offset, length * stuff->totalHeight,
     *                client);
     * the version below ought to avoid it
     */
    if (stuff->totalHeight != 0 &&
        length > (shmdesc->size - stuff->offset) / stuff->totalHeight) {
        client->errorValue = stuff->totalWidth;
        return BadValue;
    }
    if (stuff->srcX > stuff->totalWidth) {
        client->errorValue = stuff->srcX;
        return BadValue;
    }
    if (stuff->srcY > stuff->totalHeight) {
        client->errorValue = stuff->srcY;
        return BadValue;
    }
    if ((stuff->srcX + stuff->srcWidth) > stuff->totalWidth) {
        client->errorValue = stuff->srcWidth;
        return BadValue;
    }
    if ((stuff->srcY + stuff->srcHeight) > stuff->totalHeight) {
        client->errorValue = stuff->srcHeight;
        return BadValue;
    }

    // NOTE: this is where the drawing takes place
    if ((((stuff->format == ZPixmap) && (stuff->srcX == 0)) ||
         ((stuff->format != ZPixmap) &&
          (stuff->srcX < screenInfo.bitmapScanlinePad) &&
          ((stuff->format == XYBitmap) ||
           ((stuff->srcY == 0) &&
            (stuff->srcHeight == stuff->totalHeight))))) &&
        ((stuff->srcX + stuff->srcWidth) == stuff->totalWidth))
        (*pGC->ops->PutImage) (pDraw, pGC, stuff->depth,
                               stuff->dstX, stuff->dstY,
                               stuff->totalWidth, stuff->srcHeight,
                               stuff->srcX, stuff->format,
                               shmdesc->addr + stuff->offset +
                               (stuff->srcY * length));
    else
        doShmPutImage(pDraw, pGC, stuff->depth, stuff->format,
                      stuff->totalWidth, stuff->totalHeight,
                      stuff->srcX, stuff->srcY,
                      stuff->srcWidth, stuff->srcHeight,
                      stuff->dstX, stuff->dstY, shmdesc->addr + stuff->offset);

    // NOTE: this is where the return event gets sent
    if (stuff->sendEvent) {
        xShmCompletionEvent ev = {
            .type = ShmCompletionCode,
            .drawable = stuff->drawable,
            .minorEvent = X_ShmPutImage,
            .majorEvent = ShmReqCode,
            .shmseg = stuff->shmseg,
            .offset = stuff->offset
        };
        WriteEventsToClient(client, 1, (xEvent *) &ev);
    }

    return Success;
}

Ответ 2

Ваш исходный код будет конечной частью, которую мы можем проанализировать, но поскольку я понимаю Ассамблею очень мало, я дам вам ответ на макроуровне. Точный ответ мне пока неизвестен.

В случае слишком большого количества событий только эта проблема возникает, но не с обычным возникновением событий, что означает, что у вас заканчивается фреймворк виртуальной памяти, или другой кадр события запускается до того, как предыдущий выпустит свою память. В таких ситуациях вы можете сделать несколько вещей

  • Попробуйте проверить, нет ли утечки памяти или нет. После того, как один кадр события закончился, попробуйте очистить память или закончить этот объект кадра, прежде чем запускать новый.
  • Вы также можете разработать механизм, позволяющий второму кадру ждать завершения первого кадра. В C/С++ мы делаем это, используя так много методов синхронизации, как системные вызовы Mutex или select. Если вы создаете такой шаблон, тогда вы можете сделать это.
  • Если вы имеете право изменять выделенную память, предоставленную вашему окну, попробуйте увеличить ее. Потому что одна вещь наверняка (согласно вашему объяснению), что это проблема памяти.

Ответ на Редактировать 3 Похоже, вы вызываете какой-то метод cinvoke. Как это внутренне обрабатывает четное, мне неизвестно. Почему бы вам не реализовать его непосредственно в C. Я уверен, что для любой цели, с которой вы работаете, вы получите кросс-компилятор.