==========================
atari.st/c.language #2390, from ianl, 6691 chars, Fri Apr  3 01:14:52 1992
There is/are comment(s) on this message.
--------------------------
TITLE: GemFast v1.8 overview (warning: long message!)
 Well, GemFast v1.8 is very near its beta release.  I've dropped a few
hints about things that'll be in it, and now I've been asked for more
details, so I decided to put together a little summary.  GemFast v1.8 has
more changes and new things than any version of GemFast since v1.0.  I put
away the metaphorical microscope I've used in working on previous versions
and added a lot of new high-level functionality.

 The changes start with appl_init(), a logical starting place.  It now
initializes a new group of global variables which are then available for
reference in your code.  I found that every GEM program I write has calls
all over the place to get the system desktop rectangle and other common
values, so I decided to just get all the common values once and make them
globally available for reference at runtime.  There are also easy-to-use
new interfaces that open and close VDI workstations without all the tedium.

 A group of new medium- and low-level object utilities have been added.
This ranges from simple things like dealing with extended object types to
complex functions that implement new types of objects you can use in dialog
boxes.  For example, there is now a thermometer-display object type,
G_THERMO, which lets you easily use this type of progress indicator in your
dialogs.  There are also several new types of buttons, including
CUA/Windows3 style buttons and rounded-corner radio buttons.

 Some miscellanious changes were made to extend the functionality of
existing AES functions.  Certain aspects of the GEM GUI make it hard to
write 'blackbox' library routines which are independant of the current
state of the main applicaton.  For example, GEM doesn't allow you to query
the current shape of the mouse cursor, so a library routine can't
temporarily change the mouse to an arrow then change it back.  Under v1.8,
the graf_mouse() call returns the old cursor shape when you set a new
shape.  Likewise, the standard wind_update() function isn't so hot: any
number of wind_update(BEG_UPDATE) calls can be cancelled by a single
END_UPDATE.  Now wind_update() works like turning the mouse on and off; if
you made 3 BEG_UPDATE calls, you'll need 3 END_UPDATE calls to actually
release control of the window semaphore.  This lets library routines aquire
and release the window and mouse semaphores without disturbing the state
of the main application.

 There are new high-level functions for blitting areas of the screen
easily, using your memory buffer or allocating one for you.  There are some
new rectangle calculation functions, such as creating a scaled copy of a
rectangle, confining a rectangle within the boundaries of another, etc.

 But by far the biggest changes are in the forms library.  The old forms
library consisted of a single function, frm_dsdial().  Now there are 25
functions, and I'm still thinking of new ones on a daily basis.  The heart
of the new forms library is the frm_dialog() function, which conducts
dialogs for you.  Basically, it does the form_dial(), objc_draw(),
form_do(), and so on.  But, it does a lot of extra stuff along the way,
including handling the following options for you:

    - Saving and restoring the screen via blits instead of redraw
      messages.
    - Dialog boxes are moveable; the user can drag them around to anywhere
      on the screen.
    - The mouse can be forced to an arrow shape during the dialog then
      restored to its prior shape.
    - The dialog can be centered within the screen, or made to pop up
      centered over the the current mouse location.
    - It supports both standard dialogs and menu dialogs, in which the
      objects highlight as the mouse crosses them, like dropdown menus do.

 Here's a summary of the new dialog functions:

   The functions which help you conduct your own dialogs are:

     frm_confine     Confines dialog to rectangular area.
     frm_defaults    Sets default options.
     frm_dialog      Conducts your standard dialog.
     frm_menu        Conducts popup menu dialog using your object tree.
     frm_eflag       Reports error made by user in a dialog.
     frm_enableblit  Enables screen save/restore via blit.
     frm_mkmoveable  Makes your dialog moveable.
     frm_desktop     Installs your custom desktop.
     frm_sizes       Calculates dialog's on-screen sizes.

   The basic dynamic dialogs are:

     frm_dsdialog    Automatic text dialog - hard to use.
     frm_dsmenu      Automatic menu dialog - hard to use.
     frm_nldialog    Automatic text dialog - easier to use.
     frm_nlmenu      Automatic menu dialog - easier to use.
     frm_printf      Formatted text dialog, like printf().
     frm_error       Formatted error reporting.
     frm_vprintf     Alternate form of frm_printf.
     frm_verror      Alternate form of frm_error.
     frm_progress    Formatted text with thermometer display.

   The quick-and-easy dynamic dialogs are:

     frm_qchoice     Formatted text and choice of 5 buttons.
     frm_qerror      Formatted error reporting.
     frm_qmenu       Automatic popup menu (like dropdown in a box).
     frm_qtext       Formatted text display.
     frm_question    Formatted yes/no question.

 The other big change in GemFast v1.8 is the documentation, it's been
completely rewritten.  It now resembles the library reference manual from a
commercial compiler or graphics library.  It includes cross references to
related functions, a complete table of contents, a quick-reference chapter
and detailed explanations of functions in separate chapters, and in general
it's easier to use.  Printed out, it's 88 pages long; there's certainly no
lack of information on how the functions really work.  ::grin::

 As usual, v1.8 will be released with full source code.  I've taken major
steps towards portability, but it's not there yet.  I hope for v1.9 to be
the true portability release, with source code that will compile on all ST
C compilers, including GNU and other 32-bit compilers.

 Well, once again a summary has become over a hundred lines of rambling on
my part, so I guess I'd better wrap this up by saying I need a few new beta
testers here on BIX.  Most of my testing was being done by folks on the
Internet, with Mike Dorman shuttling messages back and forth for me.  Now
that Mike's gone, I have no access to those folks.


--------------------------------------------------------
                  < OK TO PORT >
This information comes from the atari.st conference on
BIX (r), the BYTE Information Exchange.

For additional information about BIX, call 800-695-4775
or 617-354-4137.
---------------------------------------------------------


TITLE: GemFast and GEM programming...what's next?

 Well, GemFast v1.8 is about done, and I'm starting to think ahead.  I know
what v1.9 is going to be like:  A lot of work to make GemFast portable to
compilers other than HSC, and a few new object types for dialogs.  (I keep
wanting scrolling listboxes that popdown like the ones in CUA/Windows3.)
This is mostly tedium work, but nothing hard or innovative.  The main
limiting factor is how fast I can write the docs for things I add to the
library.  Actually, I may have to write a complete replacement form_do
handler to implement scrolling list objects, that'll be a little harder
than "tedium work".

 What's mostly on my mind now is GemFast v2.0.  I know, in a vague way,
what I want v2.0 to be all about, but I'm not sure how to go about it.  As
I've mentioned a few times lately, it's hard to write blackbox library
routines that use windows, because of the way GEM handles event processing.
I want v2.0 to fix that problem.

 So what's this blackbox stuff I'm always on about?  Let's look at a couple
examples... Suppose your application wants to allow users to view a file.
There should be a single, simple library routine you can call that will
load the text into a window with scroll bars and all.  Once your program
has called the library routine, it should be able to forget all about it;
the library routines transparently handle all window events.  Or, suppose
you want the user to be able to do a little bit of simple text editing.
You should be able to open an edit window with a single call, and the
library routines manage all interaction between the window and the user.

 GEM manages events on a per-application basis, and we need them to be
handled on a per-window basis.  Any time the main application does an event
wait, a message or event could come in that needs to be handled by a
library routine with an open window.  The application may not be aware of
what the library routines are doing, or even how many windows are open.
Does that sound a little odd?  Well, the application may know that it
created a browser object at some point, but it may not know whether that
window is still open or not.  And, for that matter, it doesn't even need to
know.  Since a browser is a read-only item, there's no reason for the
browser library routines to involve the main application in the closing of
the window.  I can go on at great length about the hows and why of
object-oriented blackbox routines, but I think I'll skip it for now, and
respond to questions anybody has about it instead of babbling gratuitously.

 There are two main issues to deal with when you want write event-driven
library routines that are blind to the application they're embedded in:

 - Dispatching events to the library routines that are responsible for
   window the event pertains to.
 - Managing the list of events to be waited on, depending on the needs
   of all currently-open windows.

 I have a feeling the second issue is the harder of the two.  The next two
messages will address these two issues; taking the easier one first.


 I have several ideas on how to solve this problem of dispatching events to
all open windows even when the applicaton isn't responsible for (or even
aware of) all the open windows...

 Idea # 1:

 The main application can do an event wait as usual, and if the event
doesn't pertain to it, there can be a way to hand off the event to the
library routines.  To implement this, I picture a pair of new functions,
apl_register() and apl_dispatch().  When a library routine opens a window,
it calls apl_register() and passes a pointer to a routine that handles
events.  The apl_register() function maintains a list of all currently-
registered event handlers.  When the application receives an event, it
passes the XMULTI structure containing the event to apl_dispatch(), and
apl_dispatch() passes it to all registered event handlers.  Each event
handler examines the event data and acts on it if appropriate.  If a
handler completely handles an event, it clears that event bit in the
events-which-occured mask, so that other handlers don't waste time checking
to see if they need to handle the event.  When apl_dispatch() returns
control to the main application, it handles any events that are still
pending in the events mask.

 This idea has one big factor in its favor:  it requires almost no change
to existing code.  You'll already have an evnx_multi(&xm) call followed by
your event-handling code.  You just insert apl_dispatch(&xm) following the
evnx_multi() and everything works just like it always did.  In fact, since
evnx_multi() is a GemFast-defined function, not a direct GEM binding, I can
even make it happen transparently behind the scenes, by putting the
apl_dispatch() call in the evnx_multi() implementation.

 But there is also a drawback:  what if a library routine needs to do an
evnx_multi() call of its own?  It might get a message that pertains to a
window that the main application opened directly.  In the idea #1 scheme of
things, there's no way for the library routine to pass that event to the
main application.  That brings us to...

 Idea #2

 This is nearly identical to idea #1, except that the main application also
uses apl_register() to register its own event handler.  Now, the main
event loop in the application is reduced to two calls:

    for (;;) {
        evnx_multi(&xm);
        apl_dispatch(&xm);
    }

 Everything that used to be in your main event loop is moved to a separate
event handling function, and you register that function with apl_register()
before going into your event loop.  Then, events which aren't fielded by a
library routine are automatically passed to your registered event handler.
The main advantage here is that library routines can now do an event wait,
and if the event is for a main-application window, the apl_dispatch() call
will get that event routed back to the main application.

 I think this idea has a lot going for it, but it does have one major
drawback:  your event handler routine has to be reentrant, and that imposes
some restrictions on your coding style.  (Consider that you could be
processing a menu event which opens a dialog box.  The dialog handler
(which you know little or nothing about) could do an event call and call
apl_dispatch(), which could result in re-entering your event handler while
it's still handling the original menu event.)  Personally, I consider this
to be No Problem, because I tend to write routines that are reentrant out
of habit, but I'm not sure how it might affect the coding style of others.
Especially, if you aren't used to working with reentrancy issues, you can
fall into subtle traps.

 Idea #3

 My third idea is by far the most ambitious, it comes down to basically
redefining the standard GEM programming idiom from scratch.  In fact, this
is probably a GemFast v3.0 kind of thing, but I want to get folks thinking
about it.  Here, we tie events and windows very closely together.  When you
open a window, you do it via a call to a library routine, not a direct
wind_create() call.  You provide a library of callback functions which is
attached to the window object the library creates.  A central dispatcher
function in the library invokes the callback functions as needed.  The
library also provides a set of default functions that handle the routine
window events such as sizing and moving, so a lot of applications will have
only a drawfunction actually coded, and the library supplies the rest of
the standard window functionality.  It should be possible to build up
classes of window objects for text-in-windows, dialog-in-windows,
pictures-in-windows, and so on, by having libraries of routines that
implement standard drawing functions and scrolling functions.  When you
call PictureWindowCreate(picbuffer, NEO_FORMAT), that routine will call the
basic WindowCreate(), and then will supply the proper callback functions
for drawing and scrolling a NEO type picture.  If your application is a
paint program (and not just a picture viewer), then after calling the
PictureWindowCreate() function (which returns you a pointer to a Window
object structure), you can just plug in a pointer to a callback function to
handle button events, and that's your hook to provide the drawing
functionality, but the library routines still handle all the other mundane
window tasks.  In this scheme of things, menu and timer events would be
tied to a special "window handle" of 0, so that your main program can
handle events which aren't specific to a single window.

 I think my immediate goal is to implement a generic event dispatching
system that is compatible with idea #3.  That is, I want to provide a
foundation which #3 can be built on, without forcing the world to relearn
everything they know about GEM programming right away.

 I do know that if the state of available software in the ST world is to
improve, then building GEM applications has to become much easier than it
currently is.  The two best ways I know of to make it easier are to move
towards object-oriented programming (not C++ mind you, just OO design), and
then to begin coding libraries of pre-built objects which you incorporate
into your applications with just a simple function call or two.

 Now we come to the difficult issue...how to manage which events to wait
for when blackbox library routines have windows open.

 Let's suppose you have an ideal application.  It has a menu bar, and an
event handler to process menu selections.  It conducts a few custom
dialogs.  And, it relies heavily on libraries of prewritten windowed
functions so that it doesn't have to do any window management itself.
From the main application's point of view, it needs to worry only about
message events so that it can process message selections.  But, when
windows get opened by the library routines it calls, those windows may need
to respond to mouse events, keyboard events, and so on.

 In the prior message, I posted 3 ideas for handling the dispatching of
events to all registered event handlers.  Two of those ideas have the main
application doing the event wait, and thus only the main application can
set the mask of events to wait for in the evnx_multi() call.  The third
idea has windows and events tied closely together, and managing the events
to wait for actually becomes pretty simple:  each window object has an
event set associated with it.  The event set for the top window is used as
input for the evnx_multi() calls.  (It's a bit more complicated than that,
but not much.)

 In the other two scenarios, we have a bit of a problem:  how can a library
routine which opens a window communicate its events-oriented needs to the
main application?  That is, if the application doesn't care about keyboard
events, but it opens an editor window, how is it to know that it then must
begin waiting for keyboard events too?  Another problem is how to (or even
whether to) multiplex conflicting events.  For example, what do you do when
the main application and a library window routine both want to use MU_M2
events?

 As usual, I have ideas, but no firm answers.  The first idea I have is to
make a few rules that prevent conflicting events.  Looking at each type of
event, I propose this:

    MU_BUTTON   - A wait for a single or double click, with an mbstate of
                  button-down, can be used by main and library routines. A
                  wait for button-up can only be done modally, by the
                  owner of the current top window.
    MU_MESAG    - Can be used by main and library routines.  In addition,
                  this is a required event at all times except when
                  running modally.
    MU_KEYBD    - Can be used by main and library routines.
    MU_TIMER    - Reserved for main routines when non-modal.
    MU_M1       - Reserved for main routines when non-modal.
    MU_M2       - Reserved for library routines; owner of top window only.

 I've made a couple references to "running modally".  By that, I mean that
the library routine or main application has done a wind_update() call to
lock out window changes or system mouse handling.  When an event handler
detects its window is on top, it can "go modal" and then safely do most
anything it wants, event-wise, because another window can't be topped
while it's modal.

 So, any number of routines can wait for button-down concurrently, because
the event will only pertain to one window.  When a button-down event
occurs, every event handler can check to see if its window is on top, and
ignore it if not.  Also, any windows that don't care about button events
can just flat-out ignore the event even when their window is topped.  But,
nobody can wait for button-up unless they go modal, because it would
impossible to have one window waiting for button-down and another waiting
for button-up.  The same sort of logic applies to keyboard events.

 Timer events and mouse rectangle events are problematic, primarily because
they are independant of the current top window.  Logic to multiplex
conflicting timer events is beyond the scope of the rather simplistic
dispatchers I'm proposing.  So, it seems easier (for now at least), to just
say:  library routines have to live without timer events, except when
running modally.  Since two rectangle events are available, it makes sense
to reserve one to be totally under the main application's control, and the
other to be shared among the library routines.  When sharing the M2 event,
some sort of logic is needed so that the owner of the current top window
can dictate the rectangle to watch.  This smacks of dispatching idea #3, so
for now I just want to reserve the event for future implementation.

 Message events are the simple one: the main application alway includes
messages in the set of events it waits for.  This shouldn't be any problem,
since all useful applications pretty much need message events anyway.

 Okay, now we have some (perhaps flawed?) rules, but if the main
application is going to wait for events on behalf of library routines it
knows little about, then there has to be a way for library routines to tell
the main application what they need.  I can think of several schemes to do
this, and some pros and cons for each scheme.  But, what is done to solve
this problem is going to depend heavily on which of the 3 dispatching
solutions is in use.

 It is at this point in my thinking that I usually begin to think that
dispatching idea #3 really has a lot going for it, and I think that idea #1
can be trivially rejected as being too inflexible for growth.  Since #3 has
been locked out as too complex to do right now, that leaves #2.  Actually,
a varient of #2 is probably what we need.

 First, let's modify the main loop concept in dispatching idea #2 a bit:
instead of the main application calling evnx_multi() then passing the
XMULTI along to apl_dispatch(), let's eliminate the evnx_multi()
altogether.  The main application registers its event handler using
apl_register(), and it passes a pointer to its XMULTI at that time.  The
main application can change the contents of its XMULTI structure any time
it wants to, subject to the rules above (ie, don't touch MU_M2-related
data, and so on.)  It also passes a flag to apl_register() that says "I am
the main application."  Then it calls apl_run() just once, and that
function never returns.

 When a library routine registers an event handler, it also passes an
XMULTI that says what events it wants, and a flag that says it is NOT the
main application.  The apl_run() routine merges the events from all the
library routines that are registered into the main application's XMULTI,
then it calls evnx_multi(), and uses apl_dispatch() to send the events out
to the main application and other registered handlers.

 Well, I think that's enough babbling for now.  It should be clear to all
and sundry that I have given this matter some thought, but probably not
enough thought.  But, my poor little brain becomes overheated when it works
on these problems for too long, and I'm to the point where I really need
more input.  I need to know whether other folks have considered these
issues.  Have any solutions, partial or complete, been cooked up?  What
problems do you foresee that I haven't event touched on here?  What do we
do next?

--------------------------------------------------------
                  < OK TO PORT >
This information comes from the atari.st conference on
BIX (r), the BYTE Information Exchange.

For additional information about BIX, call 800-695-4775
or 617-354-4137.
---------------------------------------------------------



The preceeding text is a compilation of 3 messages from 
BIX/atari.st/tech, messages 4307 thru 4309, by ianl (Ian Lepore)
and was re-posted here with his express consent.  please feel free to
re-post this anywhere as long as the text remains unaltered.  Ian is
also working on a program to "demo" some of the new features and I'll
be posting that here as well as soon as he gives me permission to do
so.  BTW, if anyone out there would be interested in a conference with
Ian as a "guest", please e-mail me and/or the moderators here so that 
we can determine if it'd be worth the time and effort to arrange something.

Mike Douglass

