jef@well.sf.ca.us (Jef Poskanzer) (06/19/91)
In writing scrollbars for a minimal toolkit I have come across a little puzzler. The usual event sequence for a scroll is: the user clicks, the window's contents get scrolled, a graphicsexpose gets generated for the newly visible portion, and the gexpose gets handled. Now let's say the user clicks a scroll, and then while the resulting gexpose is being handled the user clicks again. Now the event sequence is click, gexpose, click, gexpose. But what happens if the user clicks three times in rapid succession? It goes click, gexpose, click, click, gexpose, gexpose - and the second gexpose is handled *incorrectly*, since the third click has already modified the window's scrolling position. I thought at first I could solve this by doing an XCheckMaskEvent before my XNextEvent and always handling any queued expose events before any other kind. And this does indeed work correctly in the debugger. But in realtime there is a race and it has the same bug as before, the second gexpose gets handled after the third click. I also tried avoiding gexposes by doing the necessary repaints directly while handling the click event. This works perfectly until you have another window partially overlapping the scrolling window. Then the server generates gexposes anyway. Not much I can do about that. Does anyone know what I'm talking about? I looked inside Xt and it's doing something weird with expose events, but I'm not sure whether it's to solve this problem or just an optimization or something else. --- Jef Jef Poskanzer jef@well.sf.ca.us {apple, ucbvax, hplabs}!well!jef "...and now for something completely different."
bobt@boa.mitron.tek.COM (Bob Toole) (06/20/91)
> It goes click, gexpose, click, click, gexpose, gexpose - and the second > gexpose is handled *incorrectly*, ... Sorry about the post, my direct mail to you bounced. The solution to this at the Xlib level is after each XCopyPlane, wait until you get a GraphicsExpose or a NoExpose event. Better yet, don't do consecutive XCopyPlane calls unless you have had a GraphicsExpose event to repair damage or a NoExpose event to let you know there is no damage. This assumes the GC graphics-exposure flag is true. I am not sure what your toolkit is trying to do, but if you have source, you should be able to fix it. Somehow.
bobt@boa.mitron.tek.COM (Bob Toole) (06/20/91)
Oops. In my last posting I said XCopyPlane. I meant XCopyArea. Sorry.
asente@adobe.com (Paul Asente) (06/21/91)
In article <25529@well.sf.ca.us> jef@well.sf.ca.us (Jef Poskanzer) writes: [about problems with GraphicsExpose events] Handling GraphicsExpose events correctly is a little complex. When you do a CopyArea, put the change that you made into a list. When you get a GraphicsExpose, look at the entire list to figure out where the area you're exposing has been moved to. When you get a GraphicsExpose with count 0 or a NoExpose, you can remove the oldest list entry. Example: Do a CopyArea that moves everything up 10; put (10, 0) in the list. Do a CopyArea that moves everything up 20; add (20, 0) to the list. Do a CopyArea that moves everything 30 to the right; add (0, 30) to the list. Get a GraphicsExpose with count 0 indicating that bottom 10 pixels of your window need redisplay. Skip the first list entry, add the remaining ones to discover that you need to offset the area in the expose event by (20, 30) to get its current position. Remove the (10, 0) from the list since you got an event with count 0. Etc. >Does anyone know what I'm talking about? I looked inside Xt and it's >doing something weird with expose events, but I'm not sure whether it's >to solve this problem or just an optimization or something else. You have 2 options with Xt. One is not to specify XtExposeGraphicsExpose in your compress_exposure field and to specify translations for GraphicsExpose and NoExpose in your translations. This means your action procedures will get all the GraphicsExpose and NoExpose events and can handle them as above. You might want to use XtAddExposureToRegion to do your own exposure compression. The other option is to specify XtExposeGraphicsExpose in the compress_exposure field. In this case GraphicsExpose events will be dispatched to your expose procedure. If you want exposure compression, use XtExposeCompressSeries; using XtExposeCompressMultiple or XtExposeCompressMaximal will prevent you from seeing all the count=0 events and from keeping your list up to date. Also, do not use XtExposeGraphicsExposeMerged since that will merge GraphicsExpose and regular Expose events, again preventing you from seeing all count=0 events. You probably also want to specify XtExposeNoExpose so that NoExpose events also go to your expose procedure and you can handle all the list updating in one place. If you don't specify XtExposeNoExpose you need a translation for NoExpose as above. -paul asente asente@adobe.com ...decwrl!adobe!asente Ratz put a bucket of liquid in front of me. "I wanted a glass of docs, Ratz. What the hell is this?" I barked. "Motif don't fit in a glass anymore," he barked back. I looked at the liquid. It was totally opaque to me.
jef@well.sf.ca.us (Jef Poskanzer) (06/21/91)
In the referenced message, jef@well.sf.ca.us (Jef Poskanzer) wrote: }I thought at first I could solve this by doing an XCheckMaskEvent }before my XNextEvent and always handling any queued expose events }before any other kind. And this does indeed work correctly in the }debugger. But in realtime there is a race and it has the same bug as }before, the second gexpose gets handled after the third click. I ended up solving the problem by doing this plus having the clickproc do an XSync after the XCopyArea. This forces the just-generated gexpose into the input queue, where it can be handled right away. And it only slows things down if the user does a whole bunch of scrolls, which doesn't happen too often. I still wonder if there's a cleaner solution. --- Jef Jef Poskanzer jef@well.sf.ca.us {apple, ucbvax, hplabs}!well!jef WCBG: All Elvis, All The Time
pete@iris49.biosym.COM (Pete Ware) (06/21/91)
To deal with it correctly, you need to keep track of all you scrolling actions until a gexpose (or noexpose) event arrives to indicate it is finished. chuck@fid.Morgan.com (Chuck Ocheret) recently posted something called "Panhandler" to the motif mailling list that deals with this problem. To quote from his description: This problem is the result of the asynchronous nature of X. If you have an occluding window over the rectangle you are trying to pan, the your XCopyArea() calls can result in GraphicsExpose events since some areas couldn't get copied. However, you might not see those GraphicsExpose events until after you have done a bunch of pans. When the GraphicsExpose events eventually get to your client, you have already scrolled the damaged areas to new locations, so that the x, y members of the GraphicsExpose events need to be adjusted. This is what the PanHandler fixes for you. It maintains a queue of your pan requests and keeps it up to date with respect to incoming Expose, GraphicsExpose and NoExpose events. It adjusts the Expose and GraphicsExpose events accordingly so that you repair the right portions of your window. I haven't actually used the code so you on your own. --pete Pete Ware / Biosym / San Diego CA / (619) 546-5532 email: pete@biosym.com
mouse@lightning.mcrcim.mcgill.EDU (der Mouse) (06/22/91)
>> I thought at first I could solve this by doing an XCheckMaskEvent >> before my XNextEvent and always handling any queued expose events >> before any other kind. And this does indeed work correctly in the >> debugger. But in realtime there is a race and it has the same bug >> as before, the second gexpose gets handled after the third click. mterm has this same race when scrolling. The README file says - There's a race which will occasionally cause a damaged area of the window to not be repainted. The race occurs when an exposure of some sort happens and mterm scrolls at about the same time, such that the server sees the CopyArea for the scroll after the exposure, but mterm doesn't see the exposure event until after the scroll. This should be fixable, but it looks like a good deal of work, and it doesn't happen very often. (It actually happens to me often enough to be very annoying.) > I ended up solving the problem by doing this plus having the > clickproc do an XSync after the XCopyArea. This forces the > just-generated gexpose into the input queue, where it can be handled > right away. And it only slows things down if the user does a whole > bunch of scrolls, which doesn't happen too often. Are you sure this fixes it? Even with XSync(), you still have to be very careful to offset events correctly. (Using XSync narrows the window but still doesn't close it.) > I still wonder if there's a cleaner solution. I don't think so. Someday I'll get around to fixing mterm.... der Mouse old: mcgill-vision!mouse new: mouse@larry.mcrcim.mcgill.edu