挂起 XShmPutImage 事件通知

Hanging of XShmPutImage event notification

我正在使用 XShm 扩展在 Linux 中绘制和操作图像。

为了避免屏幕闪烁,我将 send_event = TRUE 传递给 XShmPutImage,然后在调用 XScmPutImage 后立即使用 XIfEvent 等待事件。

通过这种方式,我正在绘制图像块,以便在图像显示在 window 表面之前不更改图像。

通常一切正常。但是有的时候,我在密集画图的时候,好像没有事件发生,画图过程挂了。

在哪里查看问题?使用 XIfEvent 是否适合这项任务?事件如何从消息队列中消失?

在某些情况下,XShmPutImage 是否可以不发送事件(如果 send_event = TRUE)或发送不同于 ShmCompletion 的事件? (例如一些内部错误或其他什么?)

编辑:

经过更多研究,我发现只有当 window 管理器向 window 集中生成事件时才会发生这种挂起。例如,当我通过拖动它的角来调整 window 的大小时。

EDIT2:

我尝试了几种方法来解决这个问题,但都没有成功。最后我被迫使用一些超时并在一段时间后取消等待。但当然这是肮脏的黑客攻击,我还是想修复它。

那么,如果 send_event=TRUE,XShmPutImage 不发送事件的原因是什么,或者这个事件是否可能从消息队列中消失?

编辑3:

这里是有问题的代码(FASM):

        cinvoke XShmPutImage, ......, TRUE

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

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

注意:无论事件检查是否挂起,XShmPutImage 总是 return 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

在应用程序中创建的所有 windows 都具有以下事件掩码: 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

完整的源代码在 repository 中可用,但这是一个非常大的项目,不便于导航。讨论的来源正在签到 8453c99b1283def8

文件:"freshlib/graphics/images.asm""freshlib/graphics/Linux/images.asm"是关于图像绘制的。

文件:"freshlib/gui/Main.asm""freshlib/gui/Linux/Main.asm" 是关于应用程序中的一般事件处理。

你的源代码将是我们可以分析的最终部分,但由于我对汇编的了解很少,我会在宏观层面给你一个答案。我仍然不知道确切的答案。

我只是遇到了太多的事件,只是它造成了这个问题,而不是事件的正常发生,这意味着你的框架 运行 虚拟内存不足,或者在前一个事件发布之前触发了另一个事件框架它的记忆。在这种情况下你可以做的很少

  1. 尝试检查是否有内存泄漏..一帧事件结束后,尝试清理内存或正确结束该帧对象,然后再触发新事件。
  2. 你也可以开发一种机制,让第二帧等待第一帧结束。在 C/C++ 中,我们使用许多同步方法来实现它,例如 Mutexselect 系统调用。如果你的设计遵循那种模式,那么你可以做到这些
  3. 如果您有权更改分配给 window 的内存,请尝试增加它。因为可以肯定的是(根据您的解释)这是一些内存问题。

回复编辑 3 看起来您正在调用某个方法 cinvoke。我不知道它是如何在内部处理 even 的。你为什么不直接在 C 中实现它。我相信无论你正在工作的目标是什么,你都会得到一些交叉编译器。

X 服务器在做什么?

如果传递给 XShmPutImage 的参数超过调用中附加到 XImage 的共享内存区域的几何形状。服务器根据先前存储的给定共享区域的限制检查 X/Y 和 width/height,如果调用参数越界,服务器将 return BadValue抑制绘图操作,抑制完成事件。

以上正是您的图书馆中正在发生的事情。方法如下:

  1. 主要事件调度程序例程是 ProcessSystemEvents。它执行 XEventNext,并根据事件类型,使用跳转 table .jump_table 分派到事件特定的处理函数。
  2. Expose 事件的事件特定函数是 .expose
  3. .expose 函数将依次使用 XExposeEvent 结构中的 X/Y 和 width/height 值调用 DrawImageRect。这是 错误的 并且是错误的真正来源,我们马上就会看到。
  4. DrawImageRect 将在对 XShmPutImage
  5. 的调用中传递这些值
  6. X 服务器XShmPutImage的处理程序将检查这些参数,如果它们超出范围,拒绝 .

这些参数被拒绝,因为它们来自曝光事件并且与 windownot[=105] 的几何形状相关=] XShmPutImage 调用中使用的附加到 XImage 的共享内存的几何结构。

具体来说,如果 window 刚刚调整大小(例如,由 window 管理员调整)并且已经放大,并且之前有一个调整大小的 ConfigureNotify 事件。现在,对于一个新的 Expose 事件,它将具有更大的 width/height,这将超过服务器知道的共享内存区域的 width/height。

It is the responsibility of the client to field the window resize events [etc.] and teardown/recreate the shared memory area with the enlarged size. This is not being done and is the source of the bug.

注意:为了完全清楚这一点,服务器只能报告错误而 出于以下几个原因对此采取任何措施:

  1. 服务器知道 window [及其调整大小]。
  2. 它知道 XImage,它的共享内存区域和大小
  3. 但它们仅在 XShmPutImage 调用期间关联 [AFAIK]
  4. 即使服务器可以关联它们,也无法调整shmarea
  5. 那是因为它无法将 shmarea 重新链接到客户端
  6. 只有客户端可以通过 XShmDetach/XShmAttach

以下是来自 c5c765bc7e 提交的相关源文件的编辑版本。它们已经被清理了一点,所以只剩下最相关的部分。一些行已被截断或换行以消除水平滚动。

我在分析文件时用 NOTENOTE/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函数]和共享内存create/destroy代码.

; 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, , 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 检查和处理 XShmPutImage 调用的 X 服务器代码。

// 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's on the
    // client side, but it's the client side bug that causes the event to be
    // suppressed

    /*
     * There's 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;
}