[comp.windows.x] A Call for Callforwards...

bzs@BU-CS.BU.EDU (Barry Shein) (06/26/89)

This all relates to the Xt Toolkit though might be applicable to other
toolkits.

The Xt Toolkit supports "Callback" functions: Functions defined in a
user application which are called by a widget typically (though not
necessarily) as a result of some XEvent. An example is a callback
which is invoked when a commandwidget is pressed so the application
can perform whatever corresponds to that user action.

In implementing Xt widgets I have run across a need for something
similar which I shall refer to as "Callforward" functions: A function
called by the widget for the sole purpose of getting information from
the application that the widget can not (or should not) generate for
itself.

An Illustrative Example:

Consider a scrollbar widget with at least the functions incrementUnit
and decrementUnit. In a text application this would correspond to
moving the scroll region one line of text up or down. Currently there
would be three ways to specify how much motion is needed to scroll one
line of text (this is needed to move the slider an appropriate
amount):

	1. A default value.
	2. A value set by the application (eg. XtincrementValue)
	3. By relieving the scrollwidget entirely of this and setting
	the slider position on every callback.

The first two work fine so long as all lines are the same height (ie.
a constant value.) The last works for anything but is not the way we
expect to use scrollbar widgets, more like hand-to-hand combat.

A Callforward is simply an application-supplied function which the
widget calls (if it exists) and expects a return value from.

In this case the scrollbar widget calls the callforward passing the
widget, a value indicating how much to scroll, typically one unit in
this example, (this can be genericized to provide a uniform interface)
and, possibly, user data (a closure.)

The definition is almost identical to that of a Callback but the
return type is changed from void to some generic type (eg. caddr_t).

Thus, the logic for scrolling (forward) becomes:

	IF CALL_FORWARD_EXISTS THEN
		MoveSlider(CallCallforward(w,name,amount,data))
	ELSE
		MoveSlider(incrementValue)

The major differences between a callback and a callforward are that
the latter returns a value and I think the ability to have a list of
them (as with callbacks) is a bad idea (I don't think this community
is ready for multiple-value-returns in C.)

Other exemplary uses of callforwards:

	1. Querying the font to use at a particular x/y position
	2. Boolean requests to check legality of a particular
	action, such as whether or not to allow a grip to be moved
	any further.
	3. Advice on setting dimensions within a resize request
	(eg. an application might want to round down or up to
	contain an entire object to avoid, eg, chopping a spread sheet
	cell.)
	4. Advice within GeometryRequests.

I am sure there are many other uses.

Comments?

	-Barry Shein

Software Tool & Die, Purveyors to the Trade
1330 Beacon Street, Brookline, MA 02146, (617) 739-0202
Internet: bzs@skuld.std.com
UUCP:     encore!xylogics!skuld!bzs or uunet!skuld!bzs

asente@decwrl.dec.com (Paul Asente) (06/26/89)

In article <8906260010.AA16100@bu-cs.BU.EDU> bzs@BU-CS.BU.EDU (Barry Shein) writes:
>In implementing Xt widgets I have run across a need for something
>similar which I shall refer to as "Callforward" functions: A function
>called by the widget for the sole purpose of getting information from
>the application that the widget can not (or should not) generate for
>itself.

You can do this now, using the call_data parameter.  There's nothing to
prevent a callback function from filling in some fields in call_data
before returning and having the widget implementation look at the data
that was passed back.

	-paul asente
	    asente@decwrl.dec.com	decwrl!asente

swick@ATHENA.MIT.EDU (Ralph R. Swick) (06/29/89)

> A Callforward is simply an application-supplied function which the
> widget calls (if it exists) and expects a return value from.
> ...
> The definition is almost identical to that of a Callback but the
> return type is changed from void to some generic type (eg. caddr_t).

I certainly agree with you that widgets could be calling into
application code to retrieve information, not just to provide it,
and that this would make certain APIs less clumsy.

But we don't need to add a new (procedure) datatype to support this
in Xt; the call_data argument in XtCallbackProc is defined by the
widget to be whatever it needs, including a pointer to a return
value from the callback.

The syntax you propose for callforwards has a syntactic advantage for
single-valued procedures but doesn't actually extend the bag o' tricks.

bzs@BU-CS.BU.EDU (Barry Shein) (06/30/89)

Ok, two votes for just use "call_data" (Paul Asente also suggested
this.)

What about the semantics of multiple callbacks supported by
CallCallbacks, does this gibe with passing data back? How is it
coordinated so the call_data isn't stomped on multiple times (ok, the
programmer should just be careful etc, but there is a little bit of
gee why do anything since the user can always write code to simulate
it eventually?)

But I do agree that it might need some more compelling arguments to
bring it from the level of conceptually useful (I think merely
discussing it probably gave some people a useful "aha") to the level
of let's add yet another feature, my sympathy is with you on that.

It also has conceptual roots in CL:

	(member item list :test #'func)

approach to passing the function to ask when the time comes.

In OOP parlance I can't think of a parallel off-hand tho there
probably should be one, basically it's setting an instance variable to
a function pointer tho I'd be hard pressed to find any discourse on
that thought in that sort of literature.

Ah well, something to puncutate the cries for an IBM/PC port of X...

	-Barry Shein

Software Tool & Die, Purveyors to the Trade
1330 Beacon Street, Brookline, MA 02146, (617) 739-0202
Internet: bzs@skuld.std.com
UUCP:     encore!xylogics!skuld!bzs or uunet!skuld!bzs

asente@decwrl.dec.com (Paul Asente) (06/30/89)

In article <8906292347.AA04479@bu-cs.BU.EDU> bzs@BU-CS.BU.EDU (Barry Shein) writes:
>
>Ok, two votes for just use "call_data" (Paul Asente also suggested
>this.)
>
>What about the semantics of multiple callbacks supported by
>CallCallbacks, does this gibe with passing data back? How is it
>coordinated so the call_data isn't stomped on multiple times (ok, the
>programmer should just be careful etc, but there is a little bit of
>gee why do anything since the user can always write code to simulate
>it eventually?)

I would suggest passing in the call_data the address of a data structure
something like

struct {
	Boolean data_supplied;
	<whatever else is needed>
};

The widget would set data_supplied to FALSE before executing the
callbacks.  Each callback checks data_supplied before filling in the rest
of the data and sets data_supplied to TRUE once the data is there.  This
allows having multiple callbacks, each of which might have the information
needed to fill in the data at different times during program execution.
This organization could well lead to more modular programs.

	-paul asente
	    asente@decwrl.dec.com	decwrl!asente

swick@ATHENA.MIT.EDU (Ralph R. Swick) (06/30/89)

     Date: Thu, 29 Jun 89 19:47:42 EDT
     From: bzs@bu-cs.bu.edu (Barry Shein)

     What about the semantics of multiple callbacks supported by
     CallCallbacks, does this gibe with passing data back? How is it
     coordinated so the call_data isn't stomped on multiple times (ok, the
     programmer should just be careful

Ah, the canonical flexibility vs. potential for error trade-off.
Paul's example illustrates well why you might not wish to enforce
single-entry callback lists universally, but I certainly understand
the desire to have the library (dynamically) verify any constraints
rather than the application or debugger.

If we were starting over, the most useful improvement over Paul's
suggestion (of passing a flag around with the data) I can think of
would be to use the function return value from the callback to
stop processing the remainder of the list immediately, thereby
avoiding even the procedure call overhead.  But then we'd have
programmers making errors the other way :-)