悬挂XShmPutImage事件通知

时间:2015-11-15 14:49:34

标签: c++ c linux xlib fasm

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

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

这样,我正在阻止图像绘制,以便在窗口表面显示之前不更改图像。

通常一切正常。但有时候,当我进行密集的图像绘制时,似乎事件永远不会出现并且绘图过程会挂起。

在哪里可以找到问题所在?是否使用适合此任务的XIfEvent?如何从消息队列中消除事件?

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

编辑:

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

EDIT2:

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

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

EDIT3:

以下是可疑代码(FASM):

        cinvoke XShmPutImage, ......, TRUE

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

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

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

完整的源代码可以在repository中找到,但它是一个非常大的项目,不容易导航。讨论的来源是在办理登机手续8453c99b1283def8

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

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

2 个答案:

答案 0 :(得分:4)

X服务器在做什么?

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

以上完全您的图书馆正在发生什么。方法如下:

  1. 主要事件调度程序例程为ProcessSystemEvents。它执行XEventNext,并根据事件类型,使用跳转表.jump_table调度到特定于事件的处理函数。
  2. Expose事件的特定事件功能为.expose
  3. .expose函数将依次使用DrawImageRect结构中的X / Y和宽度/高度值调用XExposeEvent。这是错误,并且是错误的真正来源,我们将暂时看到。
  4. DrawImageRect会在调用XShmPutImage
  5. 时传递这些值
  6. X 服务器XShmPutImage的处理程序将检查这些参数,如果超出界限,则拒绝
  7. 参数被拒绝,因为它们来自曝光事件,并且与窗口的几何图形相关,而附加到{的共享内存的几何图形在XImage电话中使用{1}}。

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

      

    客户端负责对窗口调整大小事件[等]进行填充,并使用放大的大小拆除/重新创建共享内存区域。这正在完成,并且是错误的来源。

    注意:为了完全清楚这一点,服务器只能报告错误而对此做任何事情几个原因:

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

      以下是XShmDetach/XShmAttach提交的相关源文件的编辑版本。它们已被清理干净,因此只保留最密切相关的部分。某些行已被截断或包裹以消除水平滚动。

      这些文件已使用c5c765bc7eNOTE进行了注释,我在分析时就这样做了。

      gui / Main.asm 顶级通用主循环。没什么好看的。

      NOTE/BUG

      gui / Linux / 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
      

      graphics / Linux / images.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 函数]和共享内存创建/销毁代码。

      DrawImageRect

      Xext / shm.c 检查和处理; 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 电话的X服务器代码。

      XShmPutImage

答案 1 :(得分:3)

您的源代码将是我们可以分析的最终部分,但由于我非常了解Assembly,我将在宏观层面给您答案。我仍然不知道确切的答案。

我遇到太多事件的情况只是它创建了这个问题,但没有正常的事件发生,这意味着你的框架正在耗尽虚拟内存,或者在前一个事件释放内存之前触发了另一个事件框架。在这种情况下,你可以做一些事情

  1. 尝试检查是否有任何内存泄漏。在一帧事件结束后,尝试清理内存或在触发新帧之前正确结束该帧对象。
  2. 您还可以开发一种机制,使第二帧等待第一帧结束。在C / C ++中,我们使用诸如Mutexselect系统调用之类的同步方法来实现。如果您设计遵循这种模式,那么您可以执行这些
  3. 如果您有权更改分配给窗口的内存,请尝试增加它。因为有一点可以肯定(根据你的解释)这是一些记忆问题。
  4. 回复编辑3 看起来您正在调用某个方法cinvoke。内部处理均匀的方式对我来说是未知的。为什么不直接在C中实现它。我相信无论你工作的目标是什么,你都会得到一些交叉编译器。