2015-11-15 3 views
12

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

Для того, чтобы не мерцать экран, я прохожу 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 

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

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

Файлы: "freshlib/гуй/main.asm" и "freshlib/GUI/Linux/main.asm" около общих событий обработки в приложении.

+1

Это не совсем четкий вопрос. Существует ряд возможных причин, включая, но не ограничиваясь неопределенным поведением. Кроме того, это c или C++? –

+0

@iharob Ну, фактический код написан на языке ассемблера (FASM). Но IMHO, программисты C/C++ должны быть более компетентными в программировании XLib, XShm и Linux. Если вы напишете то, что не ясно, я могу отредактировать вопрос, чтобы сделать его лучше. – johnfound

+0

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

ответ

4

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

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

Вышеуказанное точно Что происходит в вашей библиотеке. Вот как:

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

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

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

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

Примечание: Просто чтобы быть совершенно ясно на этом, сервер может только отчет на ошибки и не ничего сделать по нескольким причинам:

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

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

Файлы были аннотированы 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 

графика/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'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; 
} 
+0

Отлично! Это один из лучших ответов, которые я когда-либо читал! Но (как каждый хороший ответ) возникают некоторые новые вопросы о том, как правильно программировать на X. Например, насколько дорогим является GC? Или как правильно обрабатывать ShmCompletionEvent в цикле основных событий? (Здесь вы можете увидеть прокомментированный код - оставшаяся моя неудачная попытка сделать это). Как вы думаете, стоит ли задавать отдельные вопросы по этим вопросам? – johnfound

+0

@johnfound У меня было несколько заметок об очистке/улучшении, потому что я думал, что некоторые вещи сломаны, но реализованный код работает, но может быть менее оптимальным, поэтому удаляется для сообщения. После исправления, вероятно, будет задан новый вопрос, но здесь он должен быть ограниченным/конкретным. Они могут быть менее foppish по открытым вопросам о том, как улучшить на http://codereview.stackexchange.com/ [Я работал над ответом для парня там]. Позвольте мне подумать об этом, и я опубликую здесь обновление об улучшениях и методах измерения и т. Д. –

+0

Ну, библиотека еще далека от оптимальной. :) Кстати, некоторые из ваших заметок не совсем правильные. Например, требуется вызов XPending в ProcessSystemEvents, потому что эта функция может быть вызвана из пользовательского кода, а не после WaitForSystemEvents, и в этом случае мне нужно проверить, чтобы не блокировать ожидание событий. Но, возможно, XPending является излишним, и вместо этого мне нужно использовать XQLength. – johnfound

3

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

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

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

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

+0

cinvoke - это макросы для вызова функции C. На языках высокого уровня это просто отсутствует, потому что в этих вызовах функций их имена. Например: 'cinvoke XCheckTypedEvent, [Display], [ShmCompletionEvent], eax' равно' XCheckTypedEvent (Display, ShmCompletionEvent, eax); ' – johnfound

+0

Я согласен, но' XCheckTypedEvent (Display, ShmCompletionEvent, eax) 'должен иметь некоторый код оболочки который, в свою очередь, использует 'cinvoke', но не напрямую. Этот код обертки, я думаю, настоящий трюк. В библиотеке Xorg они поддерживали некоторый дополнительный уровень предосторожности, чтобы ограничить его в рамках предписанной модели памяти, но в вашем случае чистый вызов 'cinvoke' может не обрабатывать все эти проблемы памяти. –

+0

** Для редактирования 4 ** В images.asm Что такое 'hApplicationDisplay?' –

Смежные вопросы