Hello World: 4.4 The Source File and Source Code: Code and Message Handlers

Up: GEOS SDK TechDocs | Up | Prev: 4.3 UI Objects | Next: 5 Exercises and Suggestions

One of the first things a C programmer might notice when looking at the Hello World program is that it has no main() routine. This illustrates the primary distinction between an object-oriented system and a system that emulates object-orientedness.

Applications in GEOS consist of a Process object and, optionally, other objects in the same or different threads. The Process object of an application is event-driven . This means that until it receives a message, it does nothing; when it receives a message, however, it will automatically be woken up with the instruction pointer pointing at the proper routine's entry point.

GenProcessClass , the superclass of every Process object, handles many messages that most applications may never need to know about. For example, when the program is first launched, the Process object will receive a series of messages from the UI and the kernel telling it how it should start up. It automatically responds by setting up the proper message queues and executing the proper code. These are things you, as the programmer, do not need to know about to create a working GEOS application (though they are documented in the Geodes chapter).

Throughout the program's life, then, the Process object will receive and respond to messages as they are received. Each message has at most one corresponding method; if no method exists for a message, the message is ignored.

The Hello World Process object can handle six different messages, each of which is sent by a UI object. It also uses one routine defined internally and not available for use by other objects. Of the six messages it handles, three are specific to Hello World and three are universal to all applications using a GenView object: MSG_META_EXPOSED , MSG_META_CONTENT_VIEW_WIN_OPENED , and MSG_META_CONTENT_VIEW_WIN_CLOSED are sent by the view, and MSG_HELLO_CHANGE_TO_BLUE , MSG_HELLO_CHANGE_TO_GOLD , and MSG_HELLO_REDRAW_DOCUMENT are defined specific to HelloProcessClass and are sent by the triggers.

The function HelloDrawText() is internal to Hello World and is called by the MSG_META_EXPOSED handler. It is declared before the handler to satisfy normal C constraints.

Additionally, two constants are defined to determine the document size. These constants, along with the declaration of HelloDrawText() , are shown in Constant and Routine Definition .

Code Display 2-8 Constant and Routine Definition

This display is part of hello3.goc and follows the previous display directly.

/*************************************************************************
 *			Code for HelloProcessClass
 * Now that all the UI gadgetry has been defined, we must provide the
 * methods and routines used by the application. For simplicity, all
 * messages will be handled by the HelloProcessClass object.
 *************************************************************************/
/* Define constants used by the color-setting methods. Each of these
 * is a document size parameter in points. Therefore, the document is
 * 8.5 inches wide by 11 inches tall (one point is 1/72 of an inch). */
#define HORIZ_DOC_SIZE			(72*17/2)
#define VERT_DOC_SIZE			(72*11)
/* Declare that we will use the function HelloDrawText(), and define its
 * return and parameter values. It has no return value and has one parameter:
 * a graphics state handle called "gstate." */
void HelloDrawText(GStateHandle gstate);
/* The following constants are used by HelloDrawText(). */
#define TEXT_POINT_SIZE			 48	/* point size */
#define TEXT_X_POSITION			 30	/* x position, in document coords. */
#define TEXT_Y_POSITION			100	/* y position, in document coords. */

Handling the Window Messages

As stated earlier, the winHan global variable contains the window handle of the view's window. To set the variable, Hello World must intercept and handle the message MSG_META_CONTENT_VIEW_WIN_OPENED . This message passes the window handle, which HelloProcessClass simply stuffs into its winHan variable.

When the view window is destroyed, the application must make sure it forgets its window handle. Otherwise, we could try to draw to a nonexistent window, which is an error. This will not be a problem for Hello World because the only time the view can be destroyed is when the application is being shut down. For completeness, however, Hello World handles MSG_META_CONTENT_VIEW_WIN_CLOSED and sets winHan to zero.

Both of the methods for the above messages are shown in Messages from the View .

Code Display 2-9 Messages from the View

This display is part of hello3.goc and follows the previous display directly.

/* NOTE:
 * Because these are simple methods, the structure and syntax of methods are not
 * handled here. See the handler for MSG_META_EXPOSED, later in this chapter. */
/***********************************************************************
 * MSG_META_CONTENT_VIEW_WIN_OPENED for HelloProcessClass
 ***********************************************************************
 * SYNOPSIS:		Record the handle of the view window when the view
 *		creates it. This allows us to more-easily update the
 *		document when the user changes the text color.
 * PARAMETERS:		void (	word viewWidth,
 *			word viewHeight,
 *			WindowHandle viewWin)
 * SIDE EFFECTS: winHan is set to viewWindow
 ***********************************************************************/
@method HelloProcessClass, MSG_META_CONTENT_VIEW_WIN_OPENED {
	/* Get the window handle of the View. We need this handle in order to
	 * force a window invalidation, causing the View to send a MSG_META_EXPOSED
	 * to the Process object and thereby forcing a redraw of the window. */
    winHan = viewWindow;
}
/***********************************************************************
 * MSG_META_CONTENT_VIEW_WIN_CLOSED for HelloProcessClass
 ***********************************************************************
 * SYNOPSIS:		Take note that the view is now closed, so we don't
 *		try and draw to it or invalidate it any more (at
 *		least until it reopens)
 * PARAMETERS:		void (WindowHandle viewWindow)
 * SIDE EFFECTS:		winHan is set to 0
 *
 ***********************************************************************/
@method HelloProcessClass, MSG_META_CONTENT_VIEW_WIN_CLOSED {
	/* Set our window handle variable to zero. */
    winHan = 0;
}

Handling MSG_META_EXPOSED

As discussed in our HelloView object declaration (see Hello World's Primary and View Objects ), the view will send a MSG_META_EXPOSED to the Hello World Process object. Receipt of this message indicates that part of the scrollable view window has become invalid and must be redrawn.

Therefore, the class of the Hello World Process object ( HelloProcessClass ) must know how to draw the document in response to this message. Note that this message did not have to be defined specifically in the earlier definition of HelloProcessClass --this is because the message is already defined for MetaClass , the superclass of all GEOS classes.

MSG_META_EXPOSED Handler shows the method that handles MSG_META_EXPOSED for HelloProcessClass . Notice that the method calls the HelloDrawText() routine rather than drawing the text directly. While this may appear inefficient at first (and is for such a small, simple application), there are two main reasons why this is done:

First, it takes advantage of the GEOS single imaging model. The method simply creates the proper GState for drawing to the view window, then calls the drawing routine. A similar message for printing (i.e. when the user clicks on a "Print" trigger, a print message may be sent to the Process object) can use the same drawing routine--its handler would simply set up a GState for drawing to a Spool file and then call the drawing routine. Thus, one function is used for two purposes, cutting down code size.

Second, it allows more modularity in the testing of your code. If you need to make sure, for example, that the message is being received and handled, but you don't (yet) care if the drawing is done properly, you can set up HelloDrawText() as a dummy function. This would allow you to ensure the message is handled properly without having to debug all the drawing code.

Code Display 2-10 MSG_META_EXPOSED Handler

This display is part of hello3.goc and follows the previous display directly.

/***********************************************************************
 * MSG_META_EXPOSED for HelloProcessClass
 ***********************************************************************
 * SYNOPSIS:		Redraw the recently-exposed portion of the View
 * PARAMETERS:		void (WindowHandle win)
 * SIDE EFFECTS:		The invalid region of the window is cleared out
 *
 * STRATEGY:		This message is sent by the windowing system when a
 *		portion of the GenView has become invalid, either
 *		because a window that was obscuring it has been moved,
 *		or because some called GrInvalRect.
 *
 *		We redraw the entire document, after telling the
 *		graphics system we're drawing to the invalid portion
 *		of the window.
 *
 ***********************************************************************/
/* The method is declared with the goc keyword @method. This is followed by
 * the name of the class that knows how to handle the message (in this case,
 * the class is HelloProcessClass). Finally, the name of the message that
 * invokes this method is specified. Other items may also be specified (such
 * as a routine name that can be used instead of a message), but these are not
 * required. */
@method HelloProcessClass, MSG_META_EXPOSED {
    /* The local variable gstate will hold a GState handle. We will do
     * our drawing to this gstate. */
    GStateHandle gstate;
    /* Get a new, default graphics state that we can use while drawing.
     * We must allocate a new graphics state for the View window using
     * the kernel routine GrCreateState(). We pass the window handle of
     * the View window, which we received in a parameter called "win". */
    gstate = GrCreateState(win);
    /* Next, start a window update. This tells the windowing system that
     * we are in the process of drawing to this window. This is very
     * important--it ensures the window will be in a consistent state while
     * we're drawing. Specifically, it locks in the invalidated region to
     * which we'll be drawing; this makes sure that other threads drawing
     * to this window will not have any effect on our GState. A window
     * update is started by calling the kernel routine GrBeginUpdate()
     * with the GState handle. */
    GrBeginUpdate(gstate);
    /* If we had background graphics to draw, we could call the appropriate
     * graphics routines now. But we don't. */
    /* Draw our text into the window (pass the GState). This is done here
     * by calling the function HelloDrawText(), which knows how to draw
     * the appropriate document. (See below.) */
    HelloDrawText(gstate);				/* Special Hello World routine (below). */
    /* Now end the window update (unlock the GState and its window)
     * with the routine GrEndUpdate(), and free the GState handle by calling
     * the kernel routine GrDestroyState(). */
    GrEndUpdate(gstate);				/* Signal that we are done with 
				 * the window update. */
    GrDestroyState(gstate);				/* Destroy the temporary GState. */
}
/***********************************************************************
 * MSG_HELLO_REDRAW_DOCUMENT for HelloProcessClass
 ***********************************************************************
 * SYNOPSIS:		Force the document to be redrawn by marking the
 *		entire document area in the view as invalid.
 * PARAMETERS:		void (void)
 * SIDE EFFECTS:		Any drawing to the document area between this
 *		message and the MSG_META_EXPOSED that it generates
 *		will not show up, as the entire window will be invalid.
 *
 ***********************************************************************/
@method HelloProcessClass, MSG_HELLO_REDRAW_DOCUMENT
{
    /* Now create a temporary GState to use for window invalidation
     * if the window handle is valid, then redraw the window. */
    if (winHan != 0) {
	GStateHandle gstate = GrCreateState(winHan);
	/* Call GrInvalRect using the GState. Invalidate the entire
	 * document. This will cause the View to redraw itself and send
	 * a MSG_META_EXPOSED to the Process object. */
	GrInvalRect(gstate, 0, 0, HORIZ_DOC_SIZE, VERT_DOC_SIZE);
	GrDestroyState(gstate);				/* Free the GState. */
    }
}
/***********************************************************************
 * HelloDrawText
 ***********************************************************************
 * SYNOPSIS:		Draw a single line of text onto the document. Note
 *		that it has no concept of the screen or the view --
 *		it is given a graphics state and draws through it.
 * CALLED BY:		(INTERNAL) HelloProcess::MSG_META_EXPOSED
 * RETURN:		nothing
 * SIDE EFFECTS:		attributes in the gstate are altered
 *
 * STRATEGY:		We separate the drawing from the exposure handler
 *		so the same routine can be used for both window
 *		refresh and for printing.
 *
 *		Set the font, point size and color, then draw the text.
 *
 ***********************************************************************/
/* Functions are declared as they would be in C. Parameters are defined using
 * the ANSI calling convention: The type of the parameter is given, followed
 * by the parameter name. Multiple parameters are separated by commas. This
 * function has a single parameter. */
void HelloDrawText(GStateHandle gstate) {
    /* First change some of the default GState values such as the font
     * and point size. This is done with the routine GrSetFont(). */
    GrSetFont(gstate, FID_DTC_URW_ROMAN, MakeWWFixed(TEXT_POINT_SIZE));
    /* Set the text color to the value in helloTextColor by calling the
     * graphics routine GrSetTextColor(). */
    GrSetTextColor(gstate, CF_INDEX, helloTextColor, 0, 0);
    /* Draw the text onto the document by using the GrDrawText() routine. */
    GrDrawText(gstate, TEXT_X_POSITION, TEXT_Y_POSITION,
			"Welcome to GEOS!", 0);
}

Handling Messages from the Triggers

When the user clicks on one of the two triggers in the Color dialog box, the pressed trigger sends off a message to the Hello World Process object. The Blue trigger sends MSG_HELLO_CHANGE_TO_BLUE , and the Gold trigger sends MSG_HELLO_CHANGE_TO_GOLD . The Process object must be able to handle both of these messages.

The methods that handle these messages are similar. Each sets the global variable helloTextColor , and each forces the view window to redraw itself (by sending MSG_HELLO_REDRAW_DOCUMENT to the Process) so the color is changed on the screen as well as in our variable. Handlers for MSG_HELLO_ shows the code and comments of both these methods.

Code Display 2-11 Handlers for MSG_HELLO_...

This display is part of hello3.goc and follows the previous display directly.

/***********************************************************************
 * MSG_HELLO_CHANGE_TO_BLUE for HelloProcessClass
 ***********************************************************************
 * SYNOPSIS:		Set the text color to dark blue and redraw the text.
 * PARAMETERS:		void (void)
 * SIDE EFFECTS:		helloTextColor is set to C_BLUE
 *
 ***********************************************************************/
@method HelloProcessClass, MSG_HELLO_CHANGE_TO_BLUE {
    helloTextColor = C_BLUE;				/* Set the helloTextColor variable to blue. */
    @call self::MSG_HELLO_REDRAW_DOCUMENT();
}
/***********************************************************************
 * MSG_HELLO_CHANGE_TO_GOLD for HelloProcessClass
 ***********************************************************************
 * SYNOPSIS:		Set the text color to yellow and redraw the text.
 * PARAMETERS:		void (void)
 * SIDE EFFECTS:		helloTextColor is set to C_YELLOW
 *
 ***********************************************************************/
@method HelloProcessClass, MSG_HELLO_CHANGE_TO_GOLD
{
    helloTextColor = C_YELLOW; /* Set the helloTextColor variable to gold. */
    @call self::MSG_HELLO_REDRAW_DOCUMENT();
}

Up: GEOS SDK TechDocs | Up | Prev: 4.3 UI Objects | Next: 5 Exercises and Suggestions