C++ 挂起XShmPutImage事件通知
我使用XShm扩展在Linux中绘制和操作图像 为了不让屏幕闪烁,我将C++ 挂起XShmPutImage事件通知,c++,c,linux,xlib,fasm,C++,C,Linux,Xlib,Fasm,我使用XShm扩展在Linux中绘制和操作图像 为了不让屏幕闪烁,我将send_event=TRUE传递给XShmPutImage,然后在调用XScmPutImage后立即使用XIfEvent等待事件 通过这种方式,我正在进行图像绘制阻塞,以便在窗口表面显示之前不更改图像 通常一切正常。但有时,当我进行密集的图像绘制时,似乎事件从未发生,绘制过程挂起 问题出在哪里?使用XIfEvent是否适合此任务?事件如何从消息队列中消失 在某些情况下,XShmPutImage是否可能不发送事件(如果send
send_event=TRUE
传递给XShmPutImage
,然后在调用XScmPutImage后立即使用XIfEvent等待事件
通过这种方式,我正在进行图像绘制阻塞,以便在窗口表面显示之前不更改图像
通常一切正常。但有时,当我进行密集的图像绘制时,似乎事件从未发生,绘制过程挂起
问题出在哪里?使用XIfEvent是否适合此任务?事件如何从消息队列中消失
在某些情况下,XShmPutImage是否可能不发送事件(如果send_event=TRUE)或发送与ShmCompletion
不同的事件?(例如,在某些内部错误或其他方面?)
编辑:
经过进一步研究,我发现只有当窗口管理器密集地向窗口生成事件时,才会发生这种挂起。例如,当我通过拖动窗口的角来调整窗口大小时
编辑2:
我尝试了几种方法来解决这个问题,但没有成功。最后,我被迫使用一些超时,并在一段时间后取消等待。但这当然是肮脏的黑客,我想修复它无论如何
那么,如果send_event=TRUE,XShmPutImage不发送事件的原因是什么?或者该事件是否可能从消息队列中消失?
编辑3:
以下是可疑代码(FASM):
cinvoke XShmPutImage,…,真
.循环:
lea eax,[事件]
cinvoke XCheckTypedEvent、[Display]、[ShmCompletionEvent]、eax
测试eax,eax
jz.环;没有消息
注意:不管事件检查是否挂起,XShmPutImage总是返回TRUE,因此我没有在事件检查之后进行错误检查
编辑4:
由于要求,我张贴了绘图功能的全部代码。代码使用了FASM的一些宏库,但至少思想是清楚的(我希望)
请注意,此代码包含限制事件仅等待20毫秒的变通代码。如果没有这个超时,等待循环将永远挂起。按照XShm文档中的建议,通过调用XShmGetEventBase
获取XShm事件的数量
;在操作系统提供的窗口表面上绘制图像。
proc DrawImageRect、.where、.pImage、.xDst、.yDst、.xSrc、.ySrc、.width、.height
.event XEvent
rb 256
开始
普沙德
电影esi,[.pImage]
测试esi,esi
jz.出口
mov ebx,[esi+TImage.ximage]
cinvoke XCreateGC,[hApplicationDisplay],.where],0,0
电子数据交换
cinvoke XShmPutImage、.hApplicationDisplay、.where、.edi、[esi+TImage.ximage]、.xSrc、.ySrc、.xDst、.yDst、.width、.height],TRUE
stdcall GetTimestamp
lea-esi[eax+20];20毫秒超时
.循环:
lea eax,[事件]
cinvoke XCheckTypedEvent、[hApplicationDisplay]、[ShmCompletionEvent]、eax
测试eax,eax
完成
stdcall GetTimestamp
cmp-eax,esi
循环
.完成:
cinvoke XFreeGC[hApplicationDisplay],edi
.退出:
波帕德
返回
endp
下面是应用程序主事件循环的代码
过程\uu ProcessOneSystemEvent
只是将事件分派到GUI对象,并忽略它不使用的所有事件。它根本不处理shmccompletionevent
应用程序中创建的所有窗口的事件掩码为:
ExposureMask+FocusChangeMask+KeyPressMask+KeyReleaseMask+ButtonPressMask+ButtonReleaseMask+EnterWindowMask+LeaveWindowMask+PointerMotionMask+StructureNotifyMask
proc ProcessSystemEvents
.event XEvent
rb 256
开始
推ebx ecx edx
.event_循环:
; 检查是否退出
获取eax,[p应用程序],t应用程序:主窗口
测试eax,eax
继续
cmp-dword[eax],0
继续
cinvoke XFlush,[hApplicationDisplay]
异或eax,eax
mov[fGlobalTerminate],1
stc
pop edx ecx ebx
返回
.继续:
cinvoke XPending,[hApplicationDisplay]
测试eax,eax
jz.noevents
推式电子数据交换
lea edi,[事件]
mov ecx,sizeof.XEvent/4
异或eax,eax
雷普斯托德
电子数据交换
lea ebx,[事件]
cinvoke XNextEvent,[hApplicationDisplay],ebx
stdcall\uuu ProcessOneSystemEvent,ebx
jmp.event_循环
.noevents:
clc
pop edx ecx ebx
返回
endp
完整的源代码在中提供,但这是一个非常大的项目,不容易导航。讨论的源代码位于签入8453c99b1283def8
中
文件:“freshlib/graphics/images.asm”和“freshlib/graphics/Linux/images.asm”是关于图像绘制的
文件:“freshlib/gui/Main.asm”和“freshlib/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
; 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
; 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
// 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;
}