# Aros/Developer/Docs/Libraries/Intuition/BOOPSI

* Navbar for the Aros wikibook
* Aros User
* Aros User Docs
* Aros User FAQs
* Aros User Applications
* Aros User DOS Shell
* Aros/User/AmigaLegacy
* Aros Dev Docs
* Aros Developer Docs
* Porting Software from AmigaOS/SDL
* For Zune Beginners
* Zune .MUI Classes
* For SDL Beginners
* Aros Developer BuildSystem
* Specific platforms
* Aros x86 Complete System HCL
* Aros x86 Audio/Video Support
* Aros x86 Network Support
* Aros Intel AMD x86 Installing
* Aros Storage Support IDE SATA etc
* Aros Poseidon USB Support
* x86-64 Support
* Motorola 68k Amiga Support
* Linux and FreeBSD Support
* Windows Mingw and MacOSX Support
* Android Support
* Arm Raspberry Pi Support
* PPC Power Architecture
* misc
* Aros Public License

## History

BOOPSI (Basic Object Oriented Programming System for Intuition) is an object-oriented programming system for AmigaOS. It extends the AmigaOS windowing environment (Intuition) with an object-oriented subsystem allowing a hierarchy of object classes in which every class defines a single GUI widget or interface event.
BOOPSI made it easier for developers to create their own system of widgets and create standardized graphical user interfaces. Magic User Interface (MUI) and ReAction are examples of complete widget toolkits built on BOOPSI. Both toolkits have become popular with Amiga software programmers to generate and maintain graphical user interfaces.
The object-oriented design brings advantages such as straightforward coupling of objects with other objects. For example, a programmer may link a numerical input field and a sliding control, where if the user adjusts the sliding control the numerical value in the input field changes automatically.
BOOPSI was officially introduced with AmigaOS (TM) 2.0 and was further extended in later releases. Datatypes become easier with a little understanding of BOOPSI as well.
oop.library is the other AROS object oriented software base used to build the HIDDs. (graphics HIDD, PCI HIDD, mouse HIDD etc.) In the past Staf (overseeing the ABI changes) said there were some aspects he'd probably change there (he doesn't want to freeze it for the current ABIv1 work), so its probably best to get inputs from him regarding what those concerns were, if there's any sense in unifying things with any compiler/framework support.

## Introduction

Basic Object Oriented Programming System for Intuition (BOOPSI) is a simple OS supported model for producing system wide and language independent classes for use by your programs. BOOPSI itself is made available through the intuition.library although there is no reason that it cannot be used for things other than GUIs. The system works by providing a way to make object oriented style classes available and then providing a set of functions to create objects and perform operations on these objects.
See Open Boopsi Project
Create the gadget object, and call AddGadget. Set up the position and size data in order to get it to be placed where it is needed. Calculate the new positions and size when the window is resized, etc. Intuition does have a new gadget method called GM_LAYOUT to tell a Boopsi gadget that its window's dimensions have changed. The GM_LAYOUT method uses the gpLayout structure. So you can add, remove and modify BOOPSI objects already added to a gadget object. By used the LAYOUT_AddChild, LAYOUT_AddImage, LAYOUT_RemoveChild and LAYOUT_ModifyChild tags.

### Classes

Classes in BOOPSI are extremely easy to create, maintain, and use. They are simply a function which acts as a dispatcher for each of a class's "methods." Essentially, a "message" pertaining to the method requested is sent to an object using the DoMethod function. The DoMethod function will then look at the object and determine the class that it belongs to and then passes the message onto that class's method dispatcher. The message itself is a simple structure of the form
For example, the OM_SET message looks like this:

### Dispatcher

The dispatcher is really just a single function which is called with pointers to its Class structure, the object on which the method was called, and the message that was sent to the object. The dispatcher then looks inside the message to see what method was called on the object and then acts appropriately. Here's an example of a dispatcher:

### Object

An object in BOOPSI is simply a chunk of allocated memory which is divided into parts owned by the class the object is an instance of and all of its ancestors. Each class knows where its data is through a field in its Class structure which is setup by the system, so in order to get access to its data the class looks in this field and then adds its value to the object pointer. Fortunately a macro is provided to perform this function for you.
!!!!!!!!!!!!!!!ALERT!!!!!!!!!!!!!!!!! If you plan on making classes ALWAYS make sure your instance data pointer, lod here, is set otherwise you will encounter a ton of trouble. I can't tell you how many times I forgot to set it and it took me many minutes to finally figure out that that was the problem.
Classes are kept track of by the system so in order to add a new class to the system you must first use the MakeClass function to make a class structure that the system functions can use and understand. The MakeClass function simply takes some information such as the name of the class, some identifier for the parentclass, and the instance size. The dispatcher isn't passed into the function because it is added to the Class structure later. Then if you want to make the class publicly available the AddClass function must be used.
And when the class is not needed anymore it is removed by the RemoveClass and FreeClass functions. All of this work has been done in a simple Skeleton class which is implemented as a shared library that will automatically add itself to the system when it is loaded.
Objects in BOOPSI are simply allocated chunks of memory that are controlled by the class that the object is an instance of. To create a new object you use the NewObject call in intuition.library and pass the identifier for the class that the object is to be an instance of and any initial attribute settings.
Once the object has been created with NewObject you can perform a number of functions on it by calling one of its methods through DoMethod. For some system supported methods, such as OM_SET and OM_GET, functions are provided for you so that you don't need to use DoMethod.
SetGadgetAttrs is provided for any subclasses of the gadgetclass so that some special information needed by the classes can be created. Although it is not always needed when setting the attributes for a gadget, any attributes that might change the graphics of the gadgets should be set with SetGadgetAttrs.
The DoMethod function is actually just a part of the ordinary link library and is usually simple to operate although it can cause problems. The stack based DoMethod is made so that each parameter you pass to it will be interpreted as a ULONG and converted to one if it isn't, so if you need to pass data which is smaller be sure to package it appropriately or just use the tag array DoMethod. For example, the IM_DRAW method used by the imageclass classes take an X and Y parameter which are words:
Notice that although the imp_Draw structure is bigger than the structure we were passing on the stack it didn't matter since only the IM_DRAWFRAME method pays attention to the extra fields so there was no need to do the extra work.

#### MinNode

Objects in BOOPSI have the special property that there is a structure at a negative offset in each object created. This structure is put in place by the rootclass and contains a pointer to the object's class and a MinNode structure which can be used to hold objects in lists.
To use the MinNode structure in each object the rootclass provides the OM_ADDTAIL, OM_REMOVE, OM_ADDMEMBER, and OM_REMMEMBER methods. The OM_ADDTAIL and OM_REMOVE methods are implemented by the root class to do their expected function so you can use them in programs or in classes. However, the OM_*MEMBER methods aren't implemented by the rootclass but are there to provide a model for subclasses to implement. An example of the member functions might be to add items to some list view object that you've allocated.
DoMethod( listview, OM_ADDMEMBER, sometextitem );
Since the objects have the property that the rootclass data is at a negative offset it makes it easy to create base classes and make their structures publicly available so there is no need to use the SetAttrs and GetAttr functions. This is used effectively in the gadgetclass base class so that BOOPSI objects which are gadgets can be easily used as older style gadgets in Intuition.

#### Gadgets

Objects, that are instances of icclass or gadgetclass, also have the ability to talk to each other through their attributes. A target object is setup by the ICA_TARGET attribute and then whenever some attribute of class has that attribute changed it will send an OM_UPDATE message to the target object which can then act on it somehow. The target doesn't have to be an object though since you are able to set the ICA_TARGET value to be ICTARGET_IDCMP so that any OM_UPDATE messages sent from the object will be directed to the application through the IDCMP port. Unfortunately, when the target is an object there can be confusion about what an attribute identifier is supposed to mean. To fix this a special attribute called ICA_MAP is used to map an attribute ID to another one which the target object will be able to understand. For example, the propgclass uses the PGA_Top attribute for its current value while a strgclass uses the STRINGA_LongVal attribute for its current value. Both of these attributes have different identifiers so if they had each other as targets they wouldn't be able to understand what the attribute is supposed to mean.
Here is a the same example with communication with the IDCMP port.

#### Dispose

Finally, when your completely done with an object you need to dispose of it through the DisposeObject function. Its simple to use but you need to be careful of not disposing of something twice. The only real time when this might occur is if you've passed the object to some other object and it has disposed of it for you. A second possibility is if you've added your own system gadgets to a window (close, depth, etc...) then once you've closed the window the system will automatically dispose of any system gadgets in the window for you. You should also remember that the DisposeObject function is smart enough to know not to try and free a null pointer. This property allows you to make many objects that don't depend on each other without having to encase them in if's.
Note that if you do this you must take care not to use the object pointers in any of the other objects. For example, the GA_Previous attribute takes a pointer to a gadget and then modifies its NextGadget field to point to the object that is being created. If the gadget passed into this attribute doesn't exist than it will trash memory.

## Make you own Class

When making your own classes I advise you to use the Skeleton class and then proceed from there since it already has a lot of the dirty work finished for you so you can get right to writing the guts of your class.

### A Button Class

Figure the best way to go about this is to just take you through the code of a button class... Any bugs found while writing this are left as an exercise for the reader :)
They just provide a simple and quick way to cast BOOPSI style messages from Msg to the appropriate structure for that message

### Flags

Just about every class needs some flags associated with it to encode options or states.

### Protos

Instead of just packing every bit of code into dispatcher its a good idea to break it up into separate functions.

### Names

We need to pick a name for our gadget and for our superclass. As naming conventions go there really isn't a standard yet. Once the classerv.library is brought to its full potential there should be one.
Dispatcher... Ugh Here comes the hard part... Notice no __saveds, it can't be used here...
First you pass the message up using DoSuperMethodA, DSM for short, and this will eventually hit rootclass creating our object and then it will come back down to us after our parents have initialized their instance data to the defaults. Then below we get our instance data pointer and set any defaults. ALWAYS make sure that your instance pointer is set and DO NOT ever copy and paste the instance macro from the OM_NEW method since it uses lod = INST_DATA( cl, newobj ) where every other method should use lod = INST_DATA(cl, o), notice it should be o not newobj. NB: You should always pass the message up to your parent class, in the future some people might want to implement error handling so that any objects passed in the taglists will be disposed of if the OM_NEW fails.
Here comes GM_RENDER, it does all of the drawing for the gadget whenever intuition asks it too. Since this is the first function pertaining to gadget we'll discuss the GadgetInfo structure now.
This structure gives you a bunch of information about where your gadget is in the system as well as any information needed for drawing. It is available in all gadget method messages and the OM_SET message defined by the rootclass. Be warned though, all of the gadget methods have the pointer just after the MethodID, but the OM_SET message has it in a different place, this has caused me problems before.
SetAttrs... Doesn't get any better here The SetClassAttr function provided in the skeleton class is provided to make it easy to process all of the silly tags. Again notice, no __saveds in the function, although I think it should work with it since its always being called by the dispatcher, but who knows.
GetClassAtr... Just pure fat. This is currently of no use to us here, no reason to take it out though.
myTextLength... Ooooh Baaaad... This is prolly bad but I hate having to make a RastPort just to get a text length.
Notify... Easy to do but hard in concept The OM_NOTIFY/OM_UPDATE connection is something I will never understand. Anyways we need to send an OM_UPDATE to our target with our GA_ID as the attribute. The ti_Data of this attribute reflects whether the mouse is over the button or not.

## Creating Objects

Creating objects is relatively easy, its just a matter of finding out the class name you want to make an object out of or pass in the class pointer of the private class.
Creating many objects can also be a pain since you should check each creation to make sure it was successful before you can use it. Doing this for every creation is quite tedious so its best to create several at a time and then check them in one 'if'.

## Communication

BOOPSI gadgets are just like regular gadgets so they communicate through the IDCMP port with the same messages. They also have an extra message class called IDCMP_IDMCPUPDATE which allows gadgets to send attribute lists to the task. To set this up you need to set the ICA_TARGET attribute of the gadget to be ICTARGET_IDMCP and optionally you can map an attribute to ICSPECIAL_CODE so that the attribute's value is inserted into the Code field of the IntuiMessage. The gadget will also put the GA_ID attribute with the gadget's ID into the taglist so you can tell which gadget sent the message.
Maintained by Tim Stack(stack@cs.utah.edu) Last Changed on 27-Dec-1997, 09:51

### ObjC and ObjFW

BOOPSI and ObjC both seem to use a similar Object model. ObjC allows direct access to the instant variables, while BOOPSI seems to require a macro for that. However, even though ObjC allows direct access to instant variables, this is almost never used, but accessors are used instead (direct access of instance variables is only done inside the class, @public instance variables are almost never used due to the fact that this kills ducktyping).
The dispatch seems to be similar as well, both have a dispatch table that is used for each call. However, there is one significant difference: Selectors (this is what an entry in the dispatch table is called in ObjC and specifies the message to send to the object) seem to be sequential integers in BOOPSI that are per-class, while ObjC has integers (they can be sequential, but they don't have to - usually they are not) that are globally unique. A selector in ObjC is global for every class, if two classes have a method by the same name, they share the same selector. Selectors (like you get them with @selector for example) are a struct that contains the name and (if known to the compiler) the type encoding of the method, the runtime then registers the method and replaces the contents of the struct to point to the entry in the dispatch tables.
So, this means dispatch tables in BOOPSI are small because they only contain the methods implemented by that class, while in ObjC, they are big, because they need to have a slot for every selector that exists. The solution in ObjC is to use a sparse array (3 levels in my runtime, GNU uses 4, which makes it significantly slower (factor 2!) with no real gain, since 2^24 should be enough).
So, in order to unite them, there are two problems:
* How do we unite selectors? Do we change BOOPSI to use a sparse array? Otherwise BOOPSI would get into trouble if a BOOPSI object should be able to accept ObjC messages.
* Can we change BOOPSI objects to have a pointer to the ObjC class at index 0? That would allow the runtime to call them.
If you are really interested in briding the two, I'm sure it can be done. But it might need changes to BOOPSI. See also the CoreFoundation / Foundation bridge Apple did.

## References

NewObject NewObjectA DisposeObject
SetAttrs SetGadgetAttrs GetAttr
MakeClass AddClass RemoveClass FreeClass
AddGadget RefreshGadgets FreeGadget
ObtainGIRPort ReleaseGIRPort
DoMathod DoMethodA DoSuperMethod DoSuperMethodA
CoerceMethodA CoerceMethod
SetSuperAttrs
ULONG DoGadgetMethodA(struct Gadget *object, struct Window *win, struct Requester *req, Msg msg);