我正在使用XShm扩展来在Linux中绘制和处理图像。

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

这样,我使图像绘制处于阻塞状态,以便直到在窗口表面上显示图像之前才更改图像。

通常,一切正常。但是有时候,当我进行大量的图像绘制时,似乎事件永远不会发生,并且绘制过程会挂起。

问题出在哪里?使用XIfEvent适合此任务吗?该事件如何从消息队列中消失?

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

编辑:

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

编辑2:

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

因此,如果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始终返回TRUE,无论事件检查是否挂起,因此我没有在其后进行错误检查。

编辑4:

由于请求,我将发布绘图功能的全部代码。该代码使用了FASM的一些宏库,但至少思路很明确(我希望如此)

请注意,此代码包含解决方法代码,该方法将事件等待时间限制为仅20ms。没有此超时,等待循环将永远挂起。 XShm事件的编号是通过按照Xshm文档中的建议通过调用XShmGetEventBase来获取的。

; 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

完整的源代码可在repository中找到,但这是一个很大的项目,不便于导航。讨论的源在 checkin 8453c99b1283def8中。

文件“freshlib/graphics/images.asm” “freshlib/graphics/Linux/images.asm” 与图像绘图有关。

文件“freshlib/gui/Main.asm” “freshlib/gui/Linux/Main.asm” 与应用程序中的常规事件有关。

最佳答案

X服务器在做什么?

如果传递给ShmCompletionEvent的参数超出了调用中附加到XShmPutImage的共享内存区域的几何形状,则X服务器可以,并且抑制XImage。服务器根据给定共享区域的先前存储的限制检查X/Y和宽度/高度,如果调用参数超出范围,则服务器将返回BadValue,抑制绘图操作并抑制完成事件。

以上就是您的库中正在发生的事情。方法如下:

  • 主要事件调度程序是ProcessSystemEvents。它使用一个跳转表XEventNext调度到事件特定的处理函数,并根据事件类型执行.jump_table
  • Expose事件的事件特定功能是.expose
  • .expose函数将依次使用DrawImageRect结构中的X/Y和宽度/高度值来调用XExposeEvent。这是错误的,是错误的真实来源,我们将在稍后看到。
  • DrawImageRect将在调用XShmPutImage时传递这些值
  • X服务器中XShmPutImage的处理程序将检查这些参数,如果超出范围,拒绝

  • 这些参数被拒绝,因为它们来自暴露事件,并且与窗口的几何形状有关,而不与附加到XImage调用中使用的XShmPutImage的共享内存的几何有关。

    具体来说,如果窗口刚刚被调整大小(例如,由窗口管理器调整)并且已经被放大,并且之前存在ConfigureNotify事件用于调整大小。现在,有了新的Expose事件,它将具有更大的宽度/高度,该宽度/高度将超过服务器所知道的共享内存区域的宽度/高度。



    注意:为了完全清楚这一点,服务器仅会报告错误,由于以下几个原因,它无法对其进行任何处理:
  • 服务器知道窗口[及其大小]。
  • 它了解XImage,其共享内存区域和大小
  • 但它们仅在XShmPutImage调用[AFAIK]期间被关联
  • 即使服务器可以关联它们,也无法调整shmarea
  • 那是因为它无法将shmarea重新链接到客户端
  • 只有客户端可以通过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函数]和共享内存创建/销毁代码。
    ; 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 用于检查和处理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;
    }
    

    07-24 09:44
    查看更多