TweetFollow Us on Twitter

Pseudo Objects
Volume Number:5
Issue Number:8
Column Tag:C Objects

Pseudo Objects

By Adam Treister, Santa Barbara, CA

Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.

Adam Treister is the president of For Your Information in Santa Barbara, CA, and, when not spending too much time writing this article, is putting the finishing touches on Commander Bond’s Briefcase, a collection of vertical market and small business calculations. He would love feedback on the article, and to have others implement more drawing features and send them to AppleLink D3474.

Evolution of the Macintosh

I recently attended one day of the Apple Worldwide Developers Conference 89, and quickly caught the thrust of Apple’s unambiguous message to developers. To quote one of the slides (and Greg Williams) “If you don’t learn object-oriented programming now, you will not be able to program the Macintosh later.” There is not a lot of room for interpretation there. When C++ becomes available, a large number of C programmers will be switching to it. With the standardization offered by a joint development between Apple and AT&T, the nice extensions C++ adds to straight C programming, the support of Object Oriented Programming and the object libraries available through MacApp and other sources, C++ seems to be destined to become a standard Macintosh language.

From what I heard, few developers were arguing with Apple’s hard line support of Object Oriented Programming. After all, the evolution of the Macintosh is looking pretty solid. The Mac is reaffirming its roots in the object oriented environment of the Xerox Star. It is striving to become a large scale operating system with virtual memory and (someday) true multi-tasking, and it is moving to a software environment that allows maximum flexibility and maintainability. And, a few years down the road, just when OS/2 is finally overcoming the obstacles of its own weight and poor design, the Macintosh, on the foundation of a (then) completely object oriented software base, will be in a position to make a seamless transition to multiprocessor architectures, and blow the blue suits out of the water.

A Gentle Transition

With these dreams as our beacon, and the dogcow cheering us onward, developers will look to C++ as a way to shift into object oriented programming without sacrificing performance. Yet there seemed to be a collective angst amongst the C programmers, that the transition to C++ will be a long and burdensome one and that the costs will be high. Personally, I don’t expect the transition to be that overwhelming. There is no complete rewrite necessary, as there is when switching between two syntactically different languages. C++ includes all of C, and the compiler will not be grading you on how object oriented your code is. The transition can be gradual, at your own speed, and within your own style. The additional features of C++ should encourage good programming style, rather than choke on any of your existing bad habits.

As part of my own transition to C++, I have written here a program in a “pseudo” object oriented style. It is meant to demonstrate that object oriented programming is as much a style of programming as a characteristic of a language. It is also meant to put my code into a style such that, when I switch to C++, I will only need to make some textual substitutions rather than to completely restructure my applications.

True Object Oriented Programming

Definitionally, an object oriented language must satisfy all of the following properties:

data abstraction

inheritance

polymorphism

dynamic binding

This program will implement the first three properties in conventional C code. I will also discuss a possible implementation of the last property, though it will not be implemented, both for the implementation problems as well as the conceptual obscurity of dynamic binding. Because of that short-coming, the occasional complete disregard for the objects when I felt I could speed up the code, and for the love of a good acronym, we cannot claim this program matches the defintion of object-oriented languages. Instead, I will call it Pseudo Object Oriented Programming.

In this code are actually two entities, which may be of interest to the reader. The first is a small shell, handling the start-up, main event loop, and shutdown responsibilities, and dispatching messages to the window objects who take over from there. This POOPShell (consisting of the first two program modules) is much the same event handling shell that I use in my commercial programs. It is almost a mini- MacApp in a few pages of source code. The second part, POOPDraw is an actual application, a MacDraw clone, which almost ranks as a real program. I hope that this program may act as the starting point for your drawing programs or drawing portions of larger programs. Although POOPDraw is only the starting point for larger applications, I think there is a vast potential for adding graphical editing capablities to a wide variety of programs, which may not ever get them if the programmer has to start from scratch. Hopefully, this program will provide a module with the necessary capabilities to let you add simple graphic editing to your applications. If this is the case (and I hope you’ll let me know if it is), then we may together verify all the good things Apple is preaching about the drop-in extensibility of object oriented programming.

First, I will give a brief description of the organization of POOPShell and POOPDraw, and then discuss how it fulfills (or does not) the criteria of object oriented programming. There are many places where I will rely on conventions or self-imposed practices to gain advantages that are inherent in true object oriented languages. But the point of this article is that the concepts of C++ and Object Oriented Programming are not really that new to most of you. You may need to learn the extra syntax of an expanded language, but the philosophy of Object Oriented Programming will probably end up looking like an elegant manifestation of all the good habits you’ve been trying to maintain all along.

Objects and the POOPShell

An object, as used in this program, is a handle, just like in any other Macintosh program. It is created like any other handle, through the standard routines of the Memory Manager. I use one of my favorite macros called _GetHandleToRecord() which takes as its argument the typedef of a structure and returns a handle to that many bytes, after checking that all went well. The function which creates the new object (poignantly called New) initializes the object to know its own identity, from that point on, the object is held be responsible for all messages that might be passed to it. To the program as a whole, all objects are of the generic type ObjectHandle, but once a message is passed to this object, it will provide the access to the private workings of its class.

The early Greeks are rarely credited for their work in Compiler Design, but if they had been into it, Archimedes would have been quoted as saying: “Give me a good jump table and I won’t have to carry this 2x4 around with me all the time.” The crux of this article is that one well-placed switch statement can turn C into a sufficiently object oriented language.

By convention, the first several fields in the structure of any object are reserved for those which are common to all objects. Only the first field is truly necessary to accomplish modularity of code, but I’ve also included a few other fields which I’ve arbitrarily chosen as a list of fields I think all objects should have. The list is cut down to the minimum for this article, but you can easily add your own standard fields by modifying the macro. All standard fields are conveniently included in all objects by condensing all of their declarations into a macro called _StdObjectFields, which all objects must include at the top of their type declaration:

/* 1 */

/* The field decls common to all objects */

#define _FLD1  void(*dispatch)();  
#define _FLD2  WindowPtr  port;  
#define _FLD3  short class; 
#define _FLD4  int length;
#define _FLD5  Rectbounds;
#define _FLD6  longattributes;

#define _StdObjectFields  _FLD1 _FLD2 _FLD3 \
 _FLD4 _FLD5 _FLD6

/** This macro is included in the structure definition of all objects. 
 For example, a hypothetical object declaration might appear: **/

typedef struct
{
 _StdObjectFields
 /* object specific fields go here */
} HypoObjRec, *HypoObjPtr, **HypoObjHandle;

The first field of all objects, and the one crucial to this whole implementation, is a function pointer called dispatch. Upon creation of a new object, we initialize this field to point to a function specific to this class of object. The function contains little more than a switch statement, which looks at the parameter called Message, and according to its contents, calls some other function. All of the object’s routines are declared static (which I #define into private) and are known only to this particular class of object. This establishes a single entry point to all of the methods for any given object, much as the ListManager or Packages have a single trap and a selector field, which determines the functionality within the trap call.

The ParmP parameter is a pointer to a long, which means who knows what it points to. It is a wildcard pointer, which will mean different things in each function call. If the call only has one parameter, ParmP points to it. If there are multiple parameters, ParmP is an array of pointers. Surprisingly, the entire POOPDraw only has one function with more than one parameter, and it has two parameters, and though it is risky practice to send blind pointers around, the potential errors it causes are easily recognized and not too pesky. (Yes, a hack is a hack.)

To dispatch any message, you simply call the function

/* 2 */

 Dispatch(ObjectH,message,ParmP);

Dispatch checks that the object exists, pulls out the function pointer, and invokes it:

/* 3 */

void Dispatch(ObjectH,Message,ParmP) ObjectHandle ObjectH;
int Message;
LPtr ParmP; 
{ if (ObjectH)   
(*(*ObjectH)>dispatch)(ObjectH,Message,ParmP); }

This will send execution into the object’s local ObjDispatch function:

/* 4 */

ObjDispatch(ObjectH,message,inParm,outParm)
DrawPanelHandle ObjectH;
int message;
LPtr inParm,outParm;
{
 switch (message)
 {
 case DISPOSE:   Dispose(ObjectH); break;
 case KEYDOWN:   KeyDown(ObjectH); break;
 case MOUSEDOWN: MouseDown(ObjectH);
 break;
 case UPDATE:    Update(ObjectH);
 break;

 /* more messages down here */
 }
}

This extra jump table actually adds little or no overhead to the program because it will elimitate many others that would otherwise be scattered throughout the code. For example, to draw a graphic element in a non-object oriented approach, you may include code of the form:

/* 5 */

 switch ((*ObjectH)->class)
 {
 case RECT: DrawRect(element);
 break;
 case OVAL: DrawOval(element);
 break;
 case LINE: DrawLine(element);
 break;
 }

whereas in this program, your draw routine will look like this:

/* 6 */

 Dispatch(element,DRAW,NULL);

regardless of what kind of object you are drawing.

The beauty of this approach is that when you later expand the program, for example by adding Bezier Curves as a new graphic element, you simply add all of the methods in the module with the new object, and do not have to modify the Dispose, Activate, Mousedown, Keydown, Update, etc routines of your shell to add the extra handlers. The object itself, through the function pointer in the dispatch field, will know where to call the methods it understands. For those of you who use prototypes to check parameters (and if you don’t, you should!) this also means that you need only to include the prototypes in one module, and not throughout the entire project. This will save many global recompilations, when you make changes to the parameter lists of your functions. Admittedly this wanton disregard for strongly typed pointers undermines much of the effectiveness of using prototypes, but they are still valuable within object modules.

The single exception to this modularity is the New function, which is responsible for creating all objects known to the program. Since a message can’t be dispatched to the function pointer of an object not yet created, the New function for any additional classes must be added to the appropriate application-specific module. True object oriented languages also handle new slightly different than other messages, but they do it more gracefully than I do here. But this implementation is better than allowing any piece of code to create new objects directly, because it localizes where the changes must be made, and limits recompilation to a single small module, instead of forcing a global recompile. It also centralizes the creation process, allowing for error checking (not that I ever do error-checking) of new objects to be performed all in the same place.

Toolbox objects which have a refCon field (Windows, Menus, Controls, etc.), should insert the objectHandle into their refCon field. This allows a smooth interaction between the toolbox calls and the POOPShell. The only such objects I use here are Windows. To facillitate their handling of Windows, I have yet another function WDispatch which allows the passing of a WindowPtr instead of always having to extract the refCon. This smoothes the interface with routines like FrontWindow() and FindWindow(), which are used throughout most Macintosh programs.

A possible improvement (at least in terms of execution speed) would be to implement Dispatch and WDispatch as macros instead of functions. Because they are called so oftten, this change could cut the size of the stack almost in half. But, for didactic and debugging purposes, it is valuable to be able to trace through the Dispatch function. More importantly, my attempt to implement those macros introduced some nasty interactions between the calls of the function pointer and the surrounding code.

POOPDraw

I will say very little about the application itself. It is a MacDraw-clone, which is truly the prototypical object oriented program example. Each graphic element you draw is an object, as are the window, and the view, which handles to the content region of the window. The tool palette is a cheap hack, using a single picture resource as the whole palette, instead of fifteen separate objects each with their own pictures and event handlers. It is actually implemented this way intentionally, as an example of when its easier to just cheat than to create real objects. A better job would use Thomas Fruin’s tear-off windowing techniques (MacTutor, 12/88), making TWindow objects.

In the last minute scramble, to finish a long overdue project, I am axing many expected features, which any real application must have. Major examples include printing, scrolling and the entire Edit menu. We’ll leave that stuff as exercises for the reader. I did implement a basic file I/O scheme, because that’s usually omitted from this type of article and is great code to steal if you’ve never done it or written it yourself. I thought it would be elegant code and simple to add. As it turned out, it was neither.

Lisp programmers may appreciate the inclusion of a List object, and the implementation of the Apply command, one of the nicer features of that wonderful language. Simply put, if you send a message to a List object it will dispatch the message to everyone in the list, getting a lot of work done with one call. The list structure is implemented with a dynamically resizing array, instead of the traditional doubly linked list. This makes it possible to include objects in any number of lists, with minimum overhead and maximum speed.

Fitting the definition

Now, let us re-examine the definition of Object Oriented Programming, and establish the extent to which POOPShell satisfies the textbook. I am not trying to establish the POOPShell as an example of true Object Oriented Programming, rather trying to teach something about Object Oriented Programming by showing what it is and what it isn’t. I don’t want to convince anyone to adopt my methods instead of C++, rather I want to ease the transition to that richer language through a half-way implementation. I do claim that if you shift from traditional program structure to a POOP-ier structure, the ultimate conversion to C++ might be easier.

Data Abstraction

The primary tenet of object oriented programming is Data Abstraction. This is a very fundamental concept, meant to avoid incorrect access of data. All C programmers employ it, but there are limitations to data abstraction in C which C++ will transcend.

The most significant “breakthrough experience” I had in my education into programming was when I made the transition from Basic to Pascal, and my introduction to structured programming. Here was a language and a vocabulary which embodied all of the good practices I struggled to maintain in my early programs. In many ways, Pascal enforced good programming style upon me, requiring me to declare variables, and making me be aware of which constructs were static (declared in the code) and which were dynamic (created with new statement and managed through pointers). As I became familiar with all the added rules and restrictions, I realized the extra work paid off immensely. The biggest impact was that I was no longer simply applying instructions to a machine; suddenly I was modelling a problem. The addition of complex data structures, made up of an infinitely expandable list of simpler ones united the process of problem solving with coding, and gave me a new insight into the interrelation between data structures and algorithms.

Now, I see that structured programming is only a midway station en route to object oriented programming. The elegance of structured programming still does not completely encapsulate data from accidental intrusion, it does not completely embody the correlation between the data and algorithms, even though thi is an integral part of the conceptual thrust of Pascal (as evidenced by the title of Wirth’s book: Data Structures + Algorithms = Programs).

We all are familiar with the difference between using local and global variables. Local variables are admittedly more trouble, as they require declaration in each routine (and much of compile-time debugging is devoted to declaring local variables), but the security and clarity they provide is almost universally accepted as worth the extra effort. What local variables do not provide, though, is the ability for two or more routines to access a variable, but still have it be “local” insofar as that other routines cannot change it. This is accomplished in true Object Oriented Language’s by encapsulating the data within the object and only allowing access through a method associated with the object.

C does not offer quite that clear a definition, but, by limiting the scope of declarations to a single compile-unit or module, this is sufficiently accomplished. If you follow the common sense rule that an object’s structure definition is declared in a single module and never referenced externally, then no one outside of the module can access those fields from outside the methods of the object. In this way, the C compiler will catch any accidental references to the private fields of an object. In a simple sense, the difference between data abstraction in C and C++ is not terribly significant. The latter allows more control over the level of protection, but the former offers plenty to those who are willing to obey the common sense rules of good programming style.

Inheritance

Whereas data abstraction is probably nothing new to you, it is quite likely that inheritance is a new concept. Inheritance is defined as the ability for a object to be specified as an descendent of another object, and, in the absence of overriding instructions, to assume the behaviour of the ancestor. As an example, all objects in POOPDraw have a bounds rectangle associated with them. Therefore, when I want to access the bounds rectangle of a text box, I do not have to write code which performs that function. The textbox, as a descendent of the standard object, will inherit that function, unless I specify that it should not.

If you tend to cut and paste code around to create new applications from old ones (and who doesn’t), then it won’t take you long to really benefit from the inheritance properties of C++. Once you establish an object in an old application, that piece of code, tested and debugged, will be usable in your newer applications. You call the older version the parent, and only the changes needed for newer application need to be written from scratch.

Inheritance is very simple to implement in this program. We have already discussed how all of the known messages understood by an object are implemented as case clauses within a switch statement. All inherited capabilities are accessed through the default clause. In traditional C programming, if none of the cases match, either nothing happens or, if the programmer is conscientious, an error handler is invoked. In this program, if the object does not understand a message, it will simply pass the message along to its parent class. At the topmost level, it is considered an error if a message is unrecognized, but at all lower levels, the default case is simply a call to another jump table.

Normally, the relationship of an object to a class is analagous to the that between an individual and her race. An object is an instantiation of a class. In true Object Oriented Languages, the parent of an object is an object itself, with its own data. In the POOPShell implementation, the parent is a class, but not an object. Instead, the class specifies where to look for a method if it is undefined, instead of naming an object which may know. This interferes with the implementation of dynamic binding, as discussed below, but is sufficient for providing the property of inheritance, and decreases the required message-passing overhead.

Seeing the tree structure of inheritance can be obscured by the separate tree structure associates with the dispatch of events down from the main event loop. Figure 1 shows a downward flow of execution first through “event links” and then through “inheritance links”.

Polymorphism

Polymorphism is defined as the ability for different objects to respond differently to the same message. As an example, an Oval and a Line will not execute the same code in response to a command to DRAW, but they will both understand the command.

In C, any function declaration preceded by the key word static, specifies that the function may be called only within the module where it is declared. The linker will not complain if multiple static functions with the same name exist in different modules of a program. That is to say, C is a polymorphic language.

That is not to say that C++ isn’t better. C++ incorporates overloading, which allows more than one non-static function to have the same name, and different parameters. I plan to use this feature extensively with Quickdraw, to reduce the common practice of breaking points into h and v, or transforming two points into a rectangle. But there is nothing that overloading can do that you can’t manage with mangled pointers or #define. Therefore, it can be said that C programming in general, and the POOPShell in particular, are truly polymorphous perverse.

Figure 1.

Dynamic Binding

The final criterion of true Object Oriented Programming, and the one omitted from this implementation, is dynamic binding. With dynamic binding, it is possible to change, at run-time, an object’s inheritance path or its response to a message. In my mind, this is conceptually rather than technically difficult. An example of dynamic binding in practice is the way HyperCard attaches scripts to buttons and fields. In the course of running the program, the user is able to change the behavior of buttons and fields. But, apart from environments which are user programmable and hypertextual, there aren’t many examples of dynamic binding obvious in programs I know of. So, brazen in my ignorance, I have chosen to wait to implement this feature until someone can give me a good reason to do so.

The method to implement dynamic binding is relatively simple. If we were to change the dispatch function pointer of an object to that of a different object, from that point on, the object would behave differently. So, adding dynamic binding to POOPDraw is as simple as adding a method to change the dispatching function. (You should also change the class field, so that the object does not suffer an identiy crisis, but that actually may not be necessary.)

Adding dynamic binding would require a only few substantial changes to the structure of POOPShell. In the current implementation, when the default clause of an object’s dispatching function does not recognize a message, it calls a parent function, whose name is hard-coded into the dispatch funtion. To allow dynamic binding, the object would instead contain a field naming its parent. The parent itself would be a true object, requiring a globally declared object for each class to be created at start-up. Then, in the case a message is not recognized, the object would call:

/* 7 */

 Dispatch((*ObjectH)->parent, Message, ParmP);

Dynamic binding would be achieved by giving the object the ability to change the value stored in the parent field.

One interesting advantage of this alternate method, would be that it would be possible to name not a single parent but multiple parents of an object. That is, our “language” would implement multiple inheritence, where an object can be defined as the descendent of several classes and have the abilities of all of the them. As an example, a tear-off palette might be the descendent of a window, a menu, a picture and a rectangle. It would understand the messages to any of these elements without any code unique to itself. Multiple inheritance, a gem of conceptual gadgetry, employed by only the most object oriented purists, and lost on “the rest of us”, is not implemented in the upcoming release of C++.

Objects in the Real World

This may be a good place to discuss the real world considerations of Object Oriented Programming. It is a fine line between how much is good modularity and the point when the concepts may instead burden the implementation. A good example is a rectangle. Here I don’t mean the graphical element in POOPDraw, but rather the Quickdraw rectangle, as is in the bounds field in every object. In Smalltalk, the truest object oriented language I know of, every data structure, be it a window, a rectangle or an integer, is an object. This adds substantial overhead to every operation, and bogs down the system fairly quickly. As a C programmer, I want my code to be fast. Even when the difference is only a few instructions, and may be unnoticable to the user, the code seems to read better (to me) when I know it doesn’t unnecessarily waste time or memory.

If all of the rectangles in this program were objects instead of Rects, they would each require about four times the memory as a simple static structure. Because the rectangle is a parent to just about everyone, dispatching any message to a rectangle object would mean traversing several jump tables, dereferencing all kinds of handles, pushing several activation records onto the stack, and popping them all off on the way out. The time required to perform:

/* 8 */

  Dispatch(rectangle,WIDTH,NULL,&answer); 

could easily be a hundred times as long as

/* 9 */

 rectangle.right - rectangle.left.

I am willing to expend the extra overhead to have a function Width(r) in my library, but not to have a true object. This is a personal compromise, but it is my suspicion that the transition from C to C++, will be an exercise in balancing speed and familiarity vs. elegance and modularity. Calling statically bound methods in C++ is optimized and much faster than it would be in the POOPShell, but it still takes over twice as long as calling a function. My opinion is that, in the case of simple program elements (like numerical types, strings, rectangles, etc.), objects libraries should not replace standard function libraries, especially if you consider the fact that your function libraries are already written, debugged and understood. Remember, those are the attributes which attract us to C++ in the first place. It would be counter-productive to abandon methods that work and that are fast, for the sake of conceptual purity.

To Boldly Go...

The real beauty of C++ is that it is a superset of C, and any program you have should compile and run in C++ without modification. The pressure for change all at once is minimal. But the new language adds many of the extensions C has been missing, and opens the door to a new paradigm of programming. Look forward to a broadening mental transformation, as the C vocabulary expands onto new and vast conceptual frontiers.

[Due to the size of the application Adam submitted, we are unable to publish in the journal a full listing. Save your fingers and get the source code disk if you want POOPDraw. The following is a partial listing to give you some idea of the application. -ed]

Listing:  Main.c
/******************************************************/
/* THE POOP SHELL*/
/******************************************************/
/* An Adventure in Pseudo-Object-Oriented-Programming */
/******************************************************/
/*
*   >>> File name: Main
*  >>>  Purpose: The main, start-up and shutdown routines
*  >>>  Project: PoopDraw 
*  >>>  Date:    June 5, 1989
*  >>>  By: Adam Treister
*/
/******************************************************/
/* For Your Information   1802 Hillside Rd. SB CA 93101 */
/******************************************************/
#include “PoopDrawInc”

/******************************************************/

 main   (void);
private void   WakeUp(void);
private void   Die (void);

foreign void   Twiddle  (void);
foreign void   ApplicationInit(void);
foreign void   ApplicationShutDown (void);

/******************************************************/
/* THE GLOBAL DECLARATIONS*/
/******************************************************/

MenuHandleMenus[NUMMENUS];
EventRecord Event; /* the Event Record */
long    LastMouseDown = 0;/* time of last mouse down  */
Boolean MillerTime = false; /* Quit flag */
interrno;

/* ---------------------------------------------------- */
/* MAIN */
/* The standard shit - init, run, shutdown   */
/* ---------------------------------------------------- */
main()
{
 WakeUp();
 Twiddle();
 Die();
}

/* ---------------------------------------------------- */
/* WAKE UP*/
/* This function initialises the Mac Toolbox, creates */
/* menu bar and calls application specific init routine */
/* ---------------------------------------------------- */

void WakeUp()
{
 WindowPtrwin;
 register int    i;
 extern MenuHandle Menus[];
 
 InitMacintosh();
   TurnWatchOn();

 for (i=0; i < NUMMENUS; i++ ) 
 {
 Menus[i] = GetMenu(i + AppleMenuID-1);
 InsertMenu(Menus[i], 0);
 }
 AddResMenu(Menus[0],’DRVR’);
 DrawMenuBar();
 HiliteMenu(0);
 ApplicationInit();
 TurnArrowOn();
}

/* ---------------------------------------------------- */
/* DIE  */
/* Tidys up everything before the application quits.*/
/* If there any windows open they are closed. */
/* ---------------------------------------------------- */
void Die()
{
 Str255 message,ctStr,sizStr;
 
 while (MyFrontWindow())
 {
 WDispatch(MyFrontWindow(),CLOSE,NULL);
 } 
 ApplicationShutDown();
 ExitToShell();
}
/*----------------------------------------------------*/
/* send messages to the appropriate object
/*----------------------------------------------------*/
void  Dispatch(ObjectH,Message,ParmP)
ObjectHandle ObjectH;
int Message;
LPtr ParmP;
{ 
 if (ObjectH)  
 (*(*ObjectH)->dispatch)(ObjectH,Message,ParmP); 
}
/*----------------------------------------------------*/
/* A special case of Dispatch exclusively for windows. 
/*----------------------------------------------------*/

void  WDispatch(wP,Message,ParmP)
WindowPtr wP;
int Message;
LPtr ParmP;
{ 
 ObjectHandle obj;
 obj = (ObjectHandle) GetWRefCon(wP);
 if (obj) (*(*obj)->dispatch)(obj,Message,ParmP);
}
Listing: EvtHandlers.c
/*********************************************************/
/* THE POOP SHELL*/
/*********************************************************/
/* An Adventure in Pseudo-Object-Oriented-Programming */
/********************************************************/
/*
*   >>> File name: EventHandlers
*  >>>  Purpose: main event loop (Twiddle) and other high
 level event handlers
*  >>>  Project: Briefcase
*  >>>  Date:    April 5, 1989
*  >>>  By: Adam Treister
*/
/*********************************************************/
/* For Your Information   1802 Hillside Rd. SB CA 93101 */
/*********************************************************/
#include “PoopDrawInc”

void  DoMenuCommand(long MenuSelectResult);

public void Twiddle(void);
private void     DoMouseDown(EventRecord Event);
private void     DoMouseUp(EventRecord Event);
private Boolean  ClickIsInActiveWindow (WindowPtr ClickedWindow);
private void     DoKeyDown(EventRecord Event);
private void     DoUpdate (WindowPtr wP);
private void     DoActivate (EventRecord Event);
private voidDoSuspendResume (void);

/* ---------------------------------------------------- */
/* MAIN EVENT LOOP */
/* ---------------------------------------------------- */

void Twiddle()
{
 extern EventRecord Event;
 extern Boolean MillerTime;
 register long   Sleep = 10;
 register BooleanEventPending;
 register WindowPtr  wP;

 while (!MillerTime) 
 {

 EventPending = WaitNextEvent(everyEvent, &Event,Sleep,NULL);
 if (EventPending) 
 { 
 switch (Event.what)
 {
 case mouseDown: DoMouseDown(Event); break;
 case mouseUp:   DoMouseUp(Event); break;

 case keyDown:
 case autoKey:   DoKeyDown(Event); break;
 
 case updateEvt: DoUpdate((WindowPtr)Event.message);     break;
 
 case activateEvt: DoActivate(Event);break;
 } 
 
 } /* if EventPending*/
 else
 { 
   if (OurWindow(wP = MyFrontWindow()))WDispatch(wP,IDLE,NULL);
   
}} }

Boolean DoubleClicked;  /* last mouse down was a double */
private longLastMouseUp;
private Point    LastMouseUpLoc;   /*not used for now */

/* ---------------------------------------------------- */
/* DO MOUSE DOWN */
/* This function handles mousedown event. FindWindow is */
/* called to find which window the mouse is in. Then event */
/* is “Dispatched” there. */
/* ---------------------------------------------------- */

void DoMouseDown(Event)
EventRecord Event; 
{
 
  WindowPtr WhichWindow;  /* the event window */
 register Point  MousePosition;  /* current mouse pos */
 register int    Where;   /* the result of FindWindow */
 Rect   DragRect;
 long   okay = TRUE;
 
 DoubleClicked = (Event.when - LastMouseUp < GetDblTime());
 
 MousePosition = Event.where;
 Where = FindWindow(MousePosition, &WhichWindow);

 switch (Where)
 {
 case inDesk:    break; 
 
 case inMenuBar: 
 DoMenuCommand(MenuSelect(&Event.where));
 break;
 
 case inSysWindow: SystemClick(&Event, WhichWindow);
 break;
 
 case inContent: if (ClickIsInActiveWindow(WhichWindow))       
 WDispatch(WhichWindow,MOUSEDOWN,NULL);break;
 
 case inDrag:    DragRect = screenBits.bounds;
 DragWindow(WhichWindow,MousePosition, &DragRect);
 break;
 
 case inGrow:    WDispatch(WhichWindow,GROW,NULL);             
 break;

 case inGoAway:  if (TrackGoAway(WhichWindow, MousePosition))
 if ( _OptionKeyDown(Event))
 while ((WhichWindow = MyFrontWindow()) AND okay)
 { WDispatch(WhichWindow,CLOSE,&okay);}
 else   WDispatch(WhichWindow,CLOSE,&okay);
 break;
 default:
 Oops(“\pUnknown Window Type in MouseDown Handler”, 0, TRUE);
 break;
 } /* switch (FindWindow..) */
}
/* ---------------------------------------------------- */
/* DoMouseUp*/
/* Set current time into LastMouseUp for Double Click */
/* Detection in DoMouseDown.*/
/* ---------------------------------------------------- */

void DoMouseUp(Event)
EventRecord Event;
{
 extern long LastMouseUp;
  LastMouseUp = Event.when;
}

/* ---------------------------------------------------- */
/* Boolean ClickIsInActiveWindow(ClickedWindow)*/
/* A quickie to select back windows if the’re clicked. */
/* ---------------------------------------------------- */
#define ItIsnt (!ItIs)

Boolean ClickIsInActiveWindow(ClickedWindow)
WindowPtr ClickedWindow;
{
 register BooleanItIs;
 long type;
 
 ItIs = (ClickedWindow == FrontWindow());
 if (ItIsnt)SelectWindow(ClickedWindow);
 return(ItIs);
}
 
/* ---------------------------------------------------- */
/* DO KEY DOWN   */
/* This function handles a keydown event.  If command */
/* key is down its a menu command, otherwise dispatch it */
/* ---------------------------------------------------- */

void DoKeyDown(Event)
EventRecord Event;
{
 register char   key;
 
 key = Event.message & charCodeMask;
 
 if (_CmdKeyDown(Event))  DoMenuCommand(MenuKey(key));
 else if (MyFrontWindow())WDispatch(MyFrontWindow(),KEYDOWN,NULL);
}

/* ---------------------------------------------------- */
/* DO UPDATE*/
/* ---------------------------------------------------- */
void DoUpdate(wP)
register WindowPtr wP;
{
 GrafPtrPortSave;
 
 GetPort(&PortSave);
 SetPort(wP);
 BeginUpdate(wP);
 WDispatch(wP,UPDATE,NULL);
 EndUpdate(wP);
 SetPort(PortSave);
}

/* ---------------------------------------------------- */
/* DO ACTIVATE   */
/* ---------------------------------------------------- */
void DoActivate(Event)
EventRecord Event;
{
 register WindowPtrActivateWindow; 
 register int    Message = DEACTIVATE;
 
 TurnArrowOn();
 ActivateWindow = (WindowPtr) Event.message;
 
 if (Event.modifiers & activeFlag) 
 /* Activate vs Deactivate  */
 { 
 SetPort(ActivateWindow);
 Message = ACTIVATE;
 } 
 WDispatch(ActivateWindow,Message,NULL);
}
Listing: WIND Draw.c
/******************************************************/
/* SOURCE CODE FILE*/
/******************************************************/
/*
*   >>> File name: 5.1 DrawWindow.c
*  >>>  Purpose: Methods for Rectangle Object
*  >>>  Project: PoopDraw Version 1
*  >>>  Date:    2/20/89
*  >>>  By: Adam Treister
*/
/******************************************************/
/* For Your Information  1802 Hillside Rd. SB CA 93101 */
/******************************************************/
#include “PoopDrawInc”

void DrawOutline(Rect r,int curTool);

typedef struct
{
 _StdObjectFields
 ObjectHandle    drawPanel;
 ControlHandle   vScrollBar,hScrollBar;
 Point  curOrigin; 
 ObjectHandle    doc;/* info about files and printing */
 
} WindowDataRec,*WindowDataPtr,**WindowDataHandle;

/***** Public Functions *******************************/
/* WindowPtr NewDrawWindow(void);   */
/***** Private Functions ******************************/

DrawWinDispatch(WindowDataHandle ObjectH,int message,LPtr ParmP);
WindowPtr NewDrawWindow(void);
private void Dispose(WindowPtr wP);
private void MouseDown(WindowPtr wP);
private void Grow(WindowPtr wP);
private void KeyDown(WindowPtr wP);
private void Update(WindowPtr wP);
private void Activate(WindowPtr wP);
private void DeActivate(WindowPtr wP);
private WindowDataHandle GetWinData(WindowPtr      wP);  
/***** Local Defines & Includes ***********************/
#define UntitledWindowName“\pUntitled”

/******************************************************/
DrawWinDispatch(ObjectH,message,ParmP)
WindowDataHandle ObjectH;
int message;
LPtr ParmP;
{
 WindowPtr wP;
 wP = (*ObjectH)->port;
 switch (message)
 {
 case CLOSE:
 case DISPOSE:   Dispose(wP); break;
 case MOUSEDOWN: MouseDown(wP);    break;
 case UPDATE:    Update(wP);break;
 case ACTIVATE:  Activate(wP);break;
 case DEACTIVATE:DeActivate(wP);   break;
 case GROW: Grow(wP);break;
 default: Dispatch((*ObjectH)->drawPanel,message,ParmP); 

}}

/* ---------------------------------------------- */
/* New Draw Window */
/* ---------------------------------------------- */

WindowPtr NewDrawWindow() 
{
 WindowPtrwP;    
 Rect   BoundsRect;
 WindowDataHandle WinData;
 extern Boolean DEBUG;
 
 BoundsRect = screenBits.bounds;
 BoundsRect.top += 40;
 
 WinData = _GetHandleToRecord(WindowDataRec);
 NullOutHandle(WinData);
 wP = NewWindow (NULL, &BoundsRect, UntitledWindowName,
 true, documentProc, -1L,true, (long) WinData);
 
 SetPort(wP);
 (*WinData)->port = wP;
 (*WinData)->dispatch = DrawWinDispatch;
 
 New(DRAWPANEL,WinData,&(*WinData)->drawPanel);
 return (wP);
}

/* ---------------------------------------------- */
/* Dispose Document Window*/
/* ---------------------------------------------- */
void Dispose(wP)
WindowPtr wP;    

{
 WindowDataHandleTheWindowData = GetWinData(wP);               
 Dispatch((*TheWindowData)->drawPanel,DISPOSE,NULL);
 DisposeHandle(TheWindowData);
 DisposeWindow(wP);
}

/* -------------------------------------------- */
void MouseDown(wP)
WindowPtr wP;
{
 short  PartCode;
 ControlHandle ctrlH;
 extern EventRecord  Event;
 register Point  pt;
 
 pt = Event.where;
 GlobalToLocal(&pt);
 PartCode = FindControl(pt, wP, &ctrlH);
/* if (PartCode)
 HandleScrollBars (wP,ctrlH,pt, PartCode); 
 else 
*/ {
 WindowDataHandle WinData = GetWinData(wP);
 Dispatch((*WinData)->drawPanel,MOUSEDOWN,NULL);
 }
}
/*----------------------------------------------------*/
void Grow(wP)
WindowPtr wP;
{
 WindowDataHandle  WinData; 
 long   newSize;
 Rect   oldRect,growZone;
 extern EventRecordEvent;
 int    newH,newV;
 
 WinData = GetWinData(wP);
 growZone = screenBits.bounds;
 growZone.left = 100;growZone.top = 260;
 newSize = GrowWindow(wP,Event.where,&growZone);
 if (!newSize ) return;

 newH = LoWord(newSize);  newV = HiWord(newSize);
 SizeWindow(wP,newH,newV,TRUE);
 Dispatch((*WinData)->drawPanel,RESIZE,NULL);
 ClipRect(&wP->portRect);
 EraseRect(&wP->portRect);
 InvalRect(&(wP->portRect));
}

/* ---------------------------------------------- */
/* Update */
/* ---------------------------------------------- */
void Update(wP)
WindowPtr wP;  
{
 WindowDataHandleWinData = GetWinData(wP);
 Rect r;
 
 r = wP->portRect;
 ClipRect(&r);
 DrawControls(wP); /* there are none, unless you add scrolling */
 DrawGrowIcon(wP);
 r.right -= ScrollBarWidth; r.bottom -= ScrollBarWidth;
 ClipRect(&r);
 Dispatch((*WinData)->drawPanel,UPDATE,NULL);
}
/*------------------------------------------------
*Activate 
*--------------------------------------------------*/
void Activate(wP)
WindowPtr wP;  
{
 /* activation of controls goes here */
}

/*------------------------------------------------
*Deactivate 
*--------------------------------------------------*/
void DeActivate(wP)
WindowPtr wP;  
{
 /* deactivation of controls goes here */
}

/* ---------------------------------------------- */
/* GetWindowDataHandle    */
/* This get WindowDataHandle from the refCon field of */
/* the window passed to the function.  */
/* ---------------------------------------------- */

WindowDataHandle GetWinData(wP)
WindowPtr wP;  
{
 return((WindowDataHandle) GetWRefCon(wP));
}
 }
}

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

VMware Fusion 11.5.6 - Run Windows apps...
VMware Fusion and Fusion Pro - virtualization software for running Windows, Linux, and other systems on a Mac without rebooting. The latest version includes full support for Windows 10, macOS Mojave... Read more
Alfred 4.1 - Quick launcher for apps and...
Alfred is an award-winning productivity application for OS X. Alfred saves you time when you search for files online or on your Mac. Be more productive with hotkeys, keywords, and file actions at... Read more
Dashlane 6.2032.0 - Password manager and...
Dashlane is an award-winning service that revolutionizes the online experience by replacing the drudgery of everyday transactional processes with convenient, automated simplicity - in other words,... Read more
Skype 8.63.0.76 - Voice-over-internet ph...
Skype is a telecommunications app that provides HD video calls, instant messaging, calling to any phone number or landline, and Skype for Business for productive cooperation on the projects. This... Read more
Mellel 5.0.3 - The word processor for sc...
Mellel is the leading word processor for OS X and has been widely considered the industry standard for long form documents since its inception. Mellel focuses on writers and scholars for technical... Read more
A Better Finder Rename 11.20 - File, pho...
A Better Finder Rename is the most complete renaming solution available on the market today. That's why, since 1996, tens of thousands of hobbyists, professionals and businesses depend on A Better... Read more
TunnelBear 3.9.10 - Subscription-based p...
TunnelBear is a subscription-based virtual private network (VPN) service and companion app, enabling you to browse the internet privately and securely. Features Browse privately - Secure your data... Read more
Dropbox 103.4.383 - Cloud backup and syn...
Dropbox for Mac is a file hosting service that provides cloud storage, file synchronization, personal cloud, and client software. It is a modern workspace that allows you to get to all of your files... Read more
Daylite 2020.29.1 - Dynamic business org...
Daylite helps businesses organize themselves with tools such as shared calendars, contacts, tasks, projects, notes, and more. Enable easy collaboration with features such as task and project... Read more
HoudahSpot 5.1.5 - Advanced file-search...
HoudahSpot is a versatile desktop search tool. Use HoudahSpot to locate hard-to-find files and keep frequently used files within reach. HoudahSpot will immediately feel familiar. It works just the... Read more

Latest Forum Discussions

See All

Motorball is a car football game from No...
A few years back Noodlecake Studios announced that they would be dipping in the multiplayer gaming realm with two different games. The first of those, Golf Blitz, released a while back and has proven to be very popular. Now, the second has arrived... | Read more »
SINoALICE's latest update introduce...
SINoALICE's latest update has now arrived, adding several fan-favourite characters from popular RPG series NieR. Young Nier, Kaine, and Young Emil are available in-game as part of a limited-time crossover event set to run until August 20th. [Read... | Read more »
Rocat Jumpurr is an intense roguelite pl...
Rocat Jumpurr is a roguelite platformer from developer Mousetrap Games. You might already be familiar with it if you follow the Big Indie Pitch, where it won first place during this year's Pocket Gamer Connects London competition. Following its... | Read more »
PUBG Mobile's Play As One campaign...
Back in mid-July, we reported that PUGB Mobile had teamed up with Direct Relief to help raise money for the charity's COVID-19 response project. It focused on an in-game running challenge for players, which lead to the PUBG Mobile donating $2... | Read more »
Marvel Contest of Champions' latest...
Marvel Contest of Champions' latest motion comic has arrived, and it shows off new fighters Air-Walker and Dragon Man. Both characters are set to arrive in-game this month. [Read more] | Read more »
Clash Royale: The Road to Legendary Aren...
Supercell recently celebrated its 10th anniversary and their best title, Clash Royale, is as good as it's ever been. Even for lapsed players, returning to the game is as easy as can be. If you want to join us in picking the game back up, we've put... | Read more »
Global Spy is an intriguing 2D spy sim f...
Developer Yuyosoft Innovations' Global Spy launched last month for iOS and Android, though if you missed it at the time, we're here to tell you why it's well worth a go. This one's all about international espionage, tracking down elusive spies,... | Read more »
Distract Yourself With These Great Mobil...
There’s a lot going on right now, and I don’t really feel like trying to write some kind of pithy intro for it. All I’ll say is lots of people have been coming together and helping each other in small ways, and I’m choosing to focus on that as I... | Read more »
Hyena Squad is sci-fi turn-based strateg...
Wave Light Games has just revealed its latest release, Hyena Squad, a turn-based RPG set in a space station infested by gross aliens and the living dead. The announcement was first reported on by Touch Arcade. [Read more] | Read more »
Idle Guardians: Never Die is a pixel art...
SuperPlanet has been fairly prolific with game releases so far this year with both Evil Hunter Tycoon and Lucid Adventure releasing earlier this year. Now, they've released another idle RPG called Idle Guardians: Never Die, which you can download... | Read more »

Price Scanner via MacPrices.net

Apple restocks refurbished 2020 13″ MacBook A...
Apple has restocked Certified Refurbished 2020 13″ MacBook Airs starting at only $849 and up to $200 off the cost of new Airs. Each MacBook features a new outer case, comes with a standard Apple one-... Read more
Apple restocks clearance 2019 13″ 2.4GHz MacB...
Apple has restocked Certified Refurbished 2019 13″ 2.4GHz 4-Core Touch Bar MacBook Pros starting at $1359 and up to $560 off original MSRP. Apple’s one-year warranty is included, shipping is free,... Read more
Apple restocks refurbished iPhone XR models s...
Apple has restocked Certified Refurbished, unlocked, iPhone XR models in the refurbished section of their online store starting at $539. Each iPhone comes with Apple’s standard one-year warranty,... Read more
Price drops! $100-$200 off clearance 27″ 5K i...
B&H Photo has dropped prices on clearance, previous-generation 27″ 5K iMacs by up to $200 off Apple’s original MSRP: – 27″ 3.0GHz 6-Core 5K iMac: $1699 $100 off original MSRP – 27″ 3.1GHz 6-Core... Read more
Woot offers Apple Watch and iPhone models fro...
Amazon-owned Woot has refurbished Apple Watch and iPhone models available from $99-$749 through August 6th. According to Woot, the items may show some wear, but they have all been fully tested and... Read more
Apple’s Phil Schiller Steps Down As SVP OF Wo...
NEWS: 08.05.20 – Former Apple senior Vice President of worldwide marketing, Phil Schiller, is stepping down from his long time role at the company in order to focus on spending more time with family... Read more
Expercom offers $320 discount on the 6-core 1...
Apple reseller Expercom has the Silver 16″ 6-core MacBook Pro on sale for a limited time for $2079 shipped. Their price is $320 off Apple’s MSRP for this model, and it’s the cheapest price currently... Read more
Apple announces Education pricing for new 202...
Purchase a new 2020 iMac or iMac Pro at Apple using Apple’s Education discount, and take up to $400 off MSRP. All teachers, students, and staff of any educational institution with a .edu email... Read more
Apple reseller Expercom offers $256 discount...
Expercom has Apple’s new 2020 10-core iMac Pro available for order and on sale for $4743 shipped. Their price is $256 off Apple’s MSRP for this new model, and it’s the cheapest price we’ve seen so... Read more
Apple releases refreshed 2020 27″ iMacs with...
Apple today released updated versions of their 27″ iMacs featuring 10th generation Intel processors, SSDs across the board, a better 5K display, and improvements to the camera, speakers, and mic.... Read more

Jobs Board

Executive Team Leader GM Sales (Assistant Man...
…(Assistant Manager General Merchandise and Operations) - Apple Valley, CaliforniaApply NowJob ID:R0000082364job family:Store Managementschedule:Full Read more
Cub Foods - *Apple* Valley - Now Hiring Par...
Cub Foods - Apple Valley - Now Hiring Part Time! United States of America, Minnesota, Apple Valley New Retail Post Date 2 days ago Requisition # 122305 Sign Up Read more
Part-time Geek Squad *Apple* Consultation P...
**770829BR** **Job Title:** Part-time Geek Squad Apple Consultation Professional-Store 384(Ithaca) **Job Category:** Store Associates **Store Number or Department:** Read more
Product Manager, *Apple* Commercial Sales -...
Product Manager, Apple Commercial Sales Austin, TX, US Requisition Number:77652 As an Apple Product Manager for the Commercial Sales team at Insight, you Read more
Cub Foods - *Apple* Valley - Now Hiring Par...
Cub Foods - Apple Valley - Now Hiring Part Time! United States of America, Minnesota, Apple Valley New Retail Post Date 1 day ago Requisition # 122305 Sign Up Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.