[comp.windows.x] Detecting/Compressing Multiple Exposure Events - how ?

jim@athsys.uucp (Jim Becker) (08/31/88)

	In managing window interactions and refresh there I have a
nagging problem that has recently compunded. I am looking for
enlightenment!

	When managing window events, I currently process exposure
events as full window re-renders. I would like to minimize the
rendering as much as possible, by way of only rendering the *last*
time there is an exposure event for the target window in the current
event queue.  Hence, I would like to skip the re-rendering in the
situation where there are more xexpose events further down the queue.
The "count" field seems to only apply with chained xexpose events on
regions of the window, but is not global in nature to the entire event
stream.

	All the XCheck*() calls are very nice, and would perform the
needed checking logic, but they remove the matched event from the
input queue. I would like to process all the events in order, as I
would think that processing out-of-synch with other events would cause
problems (although I have not tested this theory).

	Is there some method to determine globally if an exposure
event (or any given event/window combination) is the last such event,
with only scanning of the event queue taking place ??

	This would also be helpful for other purposes, such as
building damage lists of graphics redraw regions (that are complete
for the entire queue) before re-rendering from a database or
datastructure resource.

	If this functionality does not exist, maybe it could be
created simply for R3 by passing NULL XEvent pointers to the XCheck*()
routines. They would detect and return status, yet keep the event
queue intact.


-Jim Becker

josh@mit-vax.LCS.MIT.EDU (Joshua Marantz) (08/31/88)

This article brings up a good point.  The existing Xlib procedures for
manipulating the event queue need to be augmented.  Another way to
solve the redundant exposure problem would be to provide two Pending
routines that do not block, and do not remove matched events from the
queue.  These would complement the functionality of XCheckIfEvent and
XCheckMaskEvent.  They might be called:

	XIfPending (display, event_return, predicate, arg);
	XMaskPending (display, event_return, event_mask, arg);

Currently, I have to emulate that functionality with an XCheckIfEvent followed
by an XPutBackEvent, which has the annoying side effect of rearranging the
event queue.

The only solution I can think of that does not require a change to Xlib is
to create your own event queue and move all of X's events into it whenever
there are X events pending.  This permits you to define your own methods of
access to the event queue.

Another advantage of defining your own event queue is that you can efficiently
filter out uninteresting events, check for user interrupts (like Control-C), 
and so on.  This is more efficient than using the Xlib event manipulation
functions (even with the addition of XIfPending and XMaskPending) because
each event will be examined once by a filter that may flag it as either very
interesting (such as an interrupt character), or very uninteresting (a
redundant expose or pointer-motion event).  If you leave the events in X's
event queue, you must rescan the entire event queue each time you want to
check for an interrupt.

Note that the auxiliary event queue can utilize X's event structures, which
simplifies the implementation and elliminates extraneous data copying.

-Joshua Marnatz
Viewlogic Systems, Inc.

RWS@ZERMATT.LCS.MIT.EDU (Robert Scheifler) (08/31/88)

    Date: 30 Aug 88 17:54:08 GMT
    From: vsi1!daver!athsys!jim@AMES.ARC.NASA.GOV  (Jim Becker)

	    Is there some method to determine globally if an exposure
    event (or any given event/window combination) is the last such event,
    with only scanning of the event queue taking place ??

Yes, just think a little creatively.  XCheckIfEvent takes a predicate
and a "char *arg".  Rather than using the Bool return from XCheckIfEvent
to indicate if you have a matching event, store the outcome in the arg
instead, and always return False from the predicate (so that
XCheckIfEvent doesn't remove any events).  To get the effect of
"stopping early", your predicate will need to check the stored outcome
in the arg and do nothing when it is already True (or whatever status
you are looking for).  Yes, this means you execute a bit more code,
but somehow I doubt your event queues should be long enough for that
to really matter.

RWS@ZERMATT.LCS.MIT.EDU (Robert Scheifler) (08/31/88)

    Date: 31 Aug 88 01:30:19 GMT
    From: mit-vax!josh@bloom-beacon.mit.edu  (Joshua Marantz)

	    XIfPending (display, event_return, predicate, arg);
	    XMaskPending (display, event_return, event_mask, arg);

    Currently, I have to emulate that functionality with an XCheckIfEvent followed
    by an XPutBackEvent, which has the annoying side effect of rearranging the
    event queue.

As I pointed out in a previous posting, by using the arg value you can
avoid having to pull anything out of the queue.  In general, Xlib
functionality is rather unlikely to change if there are reasonable ways
to build "convenience" routines on top of existing functionality.  That
isn't to say that additional convenience routines aren't useful, just
that they probably won't go into Xlib.  If people feel strongly that
additional routines ought to be provided in some sort of utility library
on the MIT release, we're willing to listen (and take code that comes
with documentation).

    Another advantage of defining your own event queue is that you can efficiently
    filter out uninteresting events, check for user interrupts (like Control-C), 
    and so on.

If you want efficient handling of "out of band" events, you can install your
own wrapper routine for the WireToEvent conversion, and trap it before it
ever even enters the queue.  See Appendix C of the Xlib manual.

sandra@utah-cs.UUCP (Sandra J Loosemore) (09/01/88)

Any clues on how to do this in CLX?  I just tried some experiments with
using an EVENT-CASE with :timeout 0 and :discard-p t to scan through the
event queue, discarding all but the first expose event on a window.  But
returning T from the clause to indicate that the event should be kept
would also exit from the EVENT-CASE, so you'd never see whether there
are more expose events in the queue or not.  I tried doing a recursive
call to EVENT-CASE to look at the remaining events, but that seemed to
end up throwing out events it shouldn't have.  I'd appreciate seeing a
bit of code that actually works.

-Sandra Loosemore
(sandra@cs.utah.edu)

Oren@home.csc.ti.COM (LaMott Oren) (09/01/88)

   Date: 31 Aug 88 18:44:11 GMT
   From: sandra@cs.utah.edu  (Sandra J Loosemore)
   Subject: Re: Detecting/Compressing Multiple Exposure Events - how ?
   
   Any clues on how to do this in CLX?  I just tried some experiments with
   using an EVENT-CASE with :timeout 0 and :discard-p t to scan through the
   event queue, discarding all but the first expose event on a window.  But
   returning T from the clause to indicate that the event should be kept
   would also exit from the EVENT-CASE, so you'd never see whether there
   are more expose events in the queue or not.  I tried doing a recursive
   call to EVENT-CASE to look at the remaining events, but that seemed to
   end up throwing out events it shouldn't have.  I'd appreciate seeing a
   bit of code that actually works.
   
   -Sandra Loosemore

Instead of discarding all but the first, discard all but the last.  That
is, when you receive an :exposure event, ignore it if there's another in
the queue using:

(event-case (display :discard-p t :force-output-p t)
  (exposure (window count)
    (unless ;; Ignore all but the last exposure event
      (event-case (window :discard-p nil :peek-p t :timeout 0)
        (exposure ((window event-window))
          (window-equal window event-window)))
      (refresh window)))
  ;; handle other events...
  )

This technique works well for :motion-notify and enter/leave-notify
event compression.

If you really want to discard all but the first exposure event, the
release 3 version of CLX contains a new form, EVENT-COND, which will
give you the control you want.

An easy way to compress exposure events is to ignore all events with a
non-zero count:
(event-case (display :discard-p t :force-output-p t)
  (exposure (window count)
    (when (zerop count) ;; Ignore all but the last exposure event
      (refresh window)))
  ;; handle other events...
  )

jim@athsys.uucp (Jim Becker) (09/02/88)

From article <19880831130506.7.RWS@KILLINGTON.LCS.MIT.EDU>, by RWS@ZERMATT.LCS.MIT.EDU (Robert Scheifler):
>     From: mit-vax!josh@bloom-beacon.mit.edu  (Joshua Marantz)
> 
>     Currently, I have to emulate that functionality with an XCheckIfEvent followed
>     by an XPutBackEvent, which has the annoying side effect of rearranging the
>     event queue.
> 
> As I pointed out in a previous posting, by using the arg value you can
> avoid having to pull anything out of the queue.  In general, Xlib
> functionality is rather unlikely to change if there are reasonable ways
> to build "convenience" routines on top of existing functionality.  That
> isn't to say that additional convenience routines aren't useful, just
> that they probably won't go into Xlib.  If people feel strongly that
> additional routines ought to be provided in some sort of utility library
> on the MIT release, we're willing to listen (and take code that comes
> with documentation).

	Here are my strong feelings, please listen !!

	There is a fairly simple modification that can be done to correct
this problem, which results in the additional functionality being added 
without increasing the already large number of Xlib calls. 

	The XCheck*() calls all take an XEvent pointer, which is used
to return the event to the application. In doing so, they take the
event out of the queue. A modification to this logic could check for a
NULL XEvent pointer being passed to the XCheck*() calls. This would
serve as a signal that the user does NOT want the event, merely to
know that one or more exist. Result being: The correct return status
would be returned and the queue would remain intact.

	The code modifications involved simply add an additional if{}
statement to the XCheck*() routines, which are pretty simple as stands.
The other alternative is to let users get at the queue itself, which I
consider to be potentially dangerous.

	Let me explain why this is such a big issue. In the current UI
paradigm that my company is using there can be hundreds of nested
windows within the layout of a single big window (which is the child
of the root).  When the single big window is mapped, deiconified or
otherwise exposed all these little guys get exposure events. If there
are multiple sets of exposes there is a lot of redundant processing.
(I believe that this is a common problem with some of the current
toolkits).  Our problem is additionally compounded by the graphics
editors having to access the database (with possible disk access) when
refreshing themselves.  Hence we have the need to contain the exposure
processing for better application throughput and responsiveness.

	The proposed solution of using XCheckIfEvent(), with continual
negative return status for each event in the queue, causes the entire
queue to be scanned for each curious query (each window!). It also
returns it's real status by "side effect", something I group with
"goto"s as far as cleanliness. If there are two hundred windows, with
a few sets of exposure events, you are doing a lot of work to look at
the entire queue for each window. Possibly, in this case, it is faster
not to try to compress the events!! (Our application currently STILL 
has over SIX hundred windows to contend with..uggh!).

	I believe that this would only cause minor mods in the five
XCheck*() routines, and the additional logic is trivial. I understand
that you guys have a lot to do to get R3 frozen, but this may be a
worthwhile change. If there are no other changes in these routines since
R2, I can make the changes and post them to xbugs. Ten minutes of fixes!

	One way or another, this will be a _very_ useful addition down
the road. Once people get more advanced in their use of X they will
want to "Check", as opposed to "Check & Get", the event queue.

	Enough leaning on on you busy guys -- thanks one way or the other!!


-Jim Becker

RWS@ZERMATT.LCS.MIT.EDU (Robert Scheifler) (09/02/88)

    Date: 1 Sep 88 21:36:01 GMT
    From: vsi1!daver!athsys!jim@AMES.ARC.NASA.GOV  (Jim Becker)

	    I believe that this would only cause minor mods in the five
    XCheck*() routines, and the additional logic is trivial. I understand
    that you guys have a lot to do to get R3 frozen, but this may be a
    worthwhile change. If there are no other changes in these routines since
    R2, I can make the changes and post them to xbugs. Ten minutes of fixes!

People need to understand that the number of minutes it takes to fix the
code is somewhat irrelevant.  What matters is that there is a standard
definition of the Xlib interface, and it doesn't willy nilly get
changed.  There is a process that has to get followed.  And, at this
particular point, stability of interfaces is rather important, to
vendors and to developers.  There are many products in-progress out
there, being built to comply with current interfaces.  Those products
are on varying schedules, none under the control of MIT.  Many are close
to being "out", and a number of companies already have products out.
You can't just change an interface and expect that all vendors will be
able to instantly make the change.  For some, it make a year to fold it
into their release cycle.  Those are realities that we have to live
with.

This isn't to say we'll ignore your suggestion, just that expecting it
as part of Xlib in R3 is not realistic.

josh@mit-vax.LCS.MIT.EDU (Joshua Marantz) (09/07/88)

JDM> Currently, I have to emulate that functionality with an XCheckIfEvent
JDM> followed by an XPutBackEvent, which has the annoying side effect of
JDM> rearranging the event queue.

RWS> As I pointed out in a previous posting, by using the arg value you can
RWS> avoid having to pull anything out of the queue...
RWS> ...  If people feel strongly that
RWS> additional routines ought to be provided in some sort of utility library
RWS> on the MIT release, we're willing to listen (and take code that comes
RWS> with documentation).

JB> [Paraphrased]
JB> By modifying the XCheck*() routines to accept a NULL event pointer and
JB> take it to mean that the event should not be removed from the queue, my
JB> problems can be solved efficiently without breaking anyone else's code
JB> and with minimal coding work from the X Consortium.  The arg modification
JB> proposed by RWS is functional but potentially inefficient and arguably
JB> unaesthetic.

If Jim Becker's change were implemented for R3, it might present a backwards
compatibility problem:  if I write my code for an Xlib that accepts NULL
events in XCheckIfEvent, then my code won't work correctly on a vendor's
library, since they typically lag the MIT version by a few months.  This
may not be so important to some people, but it would be to others.

I like RWS's other suggestion -- using a custom WireToEvent routine.
As described on page 243 of the MIT, Xlib manual, in the extensions
section, the routine XESetWireToEvent will allow you to define a
routine to intercept events before they are placed on the queue.  This
routine could update your own data structure saying which events are
pending.  It could then call the default WireToEvent routine, which is
returned by XESetWireToEvent.  This is a nice little design, since
several independent modules can intercept events, process them, and
then call the previous handler.

My only problem with it is that the manual says:

XLib> You can replace a core event conversion routine with one of your own,
XLib> though this is not encouraged.  It would, however, allow you to
XLib> intercept a core event and modify it before being enqueued or
XLib> otherwise extended.

Should I take this to mean that I am treading on thin ice in terms of
portability by using this routine?  Or am I OK so long as I call the
default handler and return its status after doing my own filtering?

Also, the documentation is confusing as to whether my routine is
supposed to return an "int" or a "Status", since XESetWireToEvent
returns an integer procedure pointer.  I suppose they are ultimately
the same thing, but I haven't checked yet.

-Joshua Marantz
Viewlogic Systems, Inc.

jg@jumbo.dec.com (Jim Gettys) (09/07/88)

Event conversion routines, as in all X protocol extensions, must
be carefully written if they are to be portable, particularly to machines
like a Cray.  This is why there is a warning in the Xlib manual.
					- Jim