Jump to content

Aros/Developer/Zune/Beginner

From Wikibooks, open books for an open world
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

This Document is obsolete, please use Aros/Developer/Zune

Prerequisites

[edit | edit source]

Some knowledge of OO (object-oriented) programming is more than welcome when it comes to dealing with Zune or MUI for that matter. Having said that the principles and techniques used should be easy enough for even the complete beginner to grasp. Google may also help in the search for good introductory papers on this classic subject.

Knowing AROS (or AmigaOS) APIs and concepts, such as the use of taglists - and of course BOOPSI, is essential to developing with Zune. Having a copy of the official "Amiga Reference Manuals" (aka RKM - Rom Kernel Manuals) may be very useful.

Since Zune is a clone of 'MUI', all the documentation pertaining to it is also applicable to Zune. In particular, the latest available 'MUI developer kit' can be found here. In the LHA archive, 2 documents are of most interest:

  • MUIdev.guide, the MUI programmer documentation.
  • PSI.c, the sourcecode of an application demonstrating all the modern MUI practices like object-oriented design and dynamic object creation.

Additionally, this archive contains the MUI autodocs, which can also be used as reference documentations for all Zune classes.

Finally, you will need a good "build" environment to develop under. AROS favours GCC, since the whole of the main system is generated with it.

BOOPSI Primer

[edit | edit source]

Concepts

[edit | edit source]

There are four main concepts paramount to BOOPSI. They are CLASS, OBJECT, ATTRIBUTE and METHOD, and are the building blocks on which BOOPSI is implemented.

Class

[edit | edit source]

A class is defined by its name, its parent class and a dispatcher.

NAME
either a string for the public classes, so that they may be used by any program in the system, or none if its a private class used only by a single application.
PARENT CLASS
all BOOPSI classes form a hierarchy starting from the class aptly named rootclass. BOOPSI allows each subclass to implement its own versions of specific parent operations/functions, or to fall back to ones provided by its "parent". Also known as base class or super class.
DISPATCHER
This is the entry point for BOOPSI "Classes". It provides a single point of access to all operations (called methods) that a class can perform, ensuring that each operation is handled by the proper code or passed to its "super class".

The BOOPSI type for a class is ..

       Class *

.. also known as IClass.

Object

[edit | edit source]

An object is an instance of class: each object has its specific data, but all objects of the same class share the same behavior. An object has several classes if we count the parents of its true class (the most derived one) up to the rootclass.

BOOPSI type for an object is ..

       Object *

It has no field you can directly access.

Attribute

[edit | edit source]

Attributes are related to the instance (private) data of each object.

You can't access an objects internal data directly, so it provides a set of "attrbitues", you can set or get, to modify its internal state. An attribute is implemented as a Tag (ULONG value or'ed with TAG_USER).

GetAttr() and SetAttrs() are used to modify an object's attributes.

AROS also has macros SET() and GET(). To modify/read the state the object has its OM_GET or OM_SET "Method" invoked.

Attributes can be one or more of the following:

     * Initialization-settable (I) : the attribute can be given as parameter
       at the object creation.
     
     * Settable (S) : You can set this attribute at any time (or at least, not
       only creation).
     
     * Gettable (G) : You can get the value of this attribute.

Method

[edit | edit source]

A BOOPSI method is a function which receives as parameters an object, a class and a message:

     * object: the object you act on
     
     * class: the considered class for this object.
     
     * message: contains a method ID which determines the function to call within
       a dispatcher, and is followed by its parameters.

To send a message to an object, use the DoMethod() call. It will try to use the true class of the object to perform the chosen method first. If the class implements the method, it will perform the required operation. If the class DOESNT implement the method it will try the parent class, and so on up the chain until the message is handled or the rootclass is reached (in this case, the unknown message is silently discarded).

The most minimal class could consist of 1 method - OM_NEW, although its use would be extremely limited.

Examples

[edit | edit source]

Let's see basic examples of this OOP framework:

Getting an attribute

[edit | edit source]

We'll query a MUI String object for its content:

      void f(Object *string)
      {
          IPTR result;
      
          GetAttr(string, MUIA_String_Contents, &result);
          printf("String content is: %s\n", (STRPTR)result);
      }
  • Object * is the type of BOOPSI objects.
  • IPTR must be used for the type of the result, which can be an integer

or a pointer. An IPTR is always written in memory, so using a smaller type would lead to memory corruption!

  • Here we query a MUI String object for its content: MUIA_String_Contents,

as any other attribute, is a ULONG (it's a Tag)

Zune applications use more often the get() and XGET() macros instead:

      get(string, MUIA_String_Contents, &result);
      
      result = XGET(string, MUIA_String_Contents);

Setting an attribute

[edit | edit source]

Let's change the content of our string:

     SetAttrs(string, MUIA_String_Contents, (IPTR)"hello", TAG_DONE);
     * Pointers parameters must be casted to IPTR to avoid warnings.
     
     * After the object parameter, a taglist is passed to SetAttrs and thus must
       end with TAG_DONE.

You'll find the set() macro useful:

     set(string, MUIA_String_Contents, (IPTR)"hello");

But it's only with SetAttrs() that you can set several attributes at once:

     SetAttrs(string,
           MUIA_Disabled, TRUE,
           MUIA_String_Contents, (IPTR)"hmmm...",
           TAG_DONE);

Calling a method

[edit | edit source]

Let's see the most called method in a Zune program, the event processing method called in your main loop:

     result = DoMethod(obj, MUIM_Application_NewInput, (IPTR)&sigs);
     * Parameters are not a taglist, and thus don't end with TAG_DONE.
     
     * You have to cast pointers to IPTR to avoid warnings.

Hello world

[edit | edit source]

Screenshot 'Hello World'

First things first! I knew you would be all excited.

Sources

[edit | edit source]

Let's study our first real life example:

      // gcc hello.c -lmui
      #include <exec/types.h>
      #include <libraries/mui.h>
      
      #include <proto/exec.h>
      #include <proto/intuition.h>
      #include <proto/muimaster.h>
      #include <clib/alib_protos.h>
      
      int main(void)
      {
          Object *wnd, *app, *but;
      
          // GUI creation
          app = ApplicationObject,
              SubWindow, wnd = WindowObject,
                  MUIA_Window_Title, "Hello world!",
                  WindowContents, VGroup,
                      Child, TextObject,
                          MUIA_Text_Contents, "\33cHello world!\nHow are you?",
                          End,
                      Child, but = SimpleButton("_Ok"),
                      End,
                  End,
              End;
      
          if (app != NULL)
          {
              ULONG sigs = 0;
      
              // Click Close gadget or hit Escape to quit
              DoMethod(wnd, MUIM_Notify, MUIA_Window_CloseRequest, TRUE,
                       (IPTR)app, 2,
                       MUIM_Application_ReturnID, MUIV_Application_ReturnID_Quit);
      
              // Click the button to quit
              DoMethod(but, MUIM_Notify, MUIA_Pressed, FALSE,
                       (IPTR)app, 2,
                       MUIM_Application_ReturnID, MUIV_Application_ReturnID_Quit);
      
              // Open the window
              set(wnd, MUIA_Window_Open, TRUE);
      
              // Check that the window opened
              if (XGET(wnd, MUIA_Window_Open))
              {
                  // Main loop
                  while((LONG)DoMethod(app, MUIM_Application_NewInput, (IPTR)&sigs)
                        != MUIV_Application_ReturnID_Quit)
                  {
                      if (sigs)
                      {
                          sigs = Wait(sigs | SIGBREAKF_CTRL_C);
                          if (sigs & SIGBREAKF_CTRL_C)
                              break;
                      }
                  }
              }
              // Destroy our application and all its objects
              MUI_DisposeObject(app);
          }
          
          return 0;
      }

Remarks

[edit | edit source]

General

[edit | edit source]

We don't manually open libraries, it's done automatically for us.

GUI creation

[edit | edit source]

We use a macro-based language to easily build our GUI. A Zune application has always 1 and only 1 Application object:

     app = ApplicationObject,

An application can have 0, 1 or more Window objects. Most often a single one:

           SubWindow, wnd = WindowObject,

Be nice, give a title to the window:

                 MUIA_Window_Title, "Hello world!",

A window must have 1 and only 1 child, usually a group. This one is vertical, that means that its children will be arranged vertically:

                 WindowContents, VGroup,

A group must have at least 1 child, here it's just a text:

                       Child, TextObject,

Zune accepts various escape codes (here, to center the text) and newlines:

                             MUIA_Text_Contents, "\33cHello world!\nHow are you? :)",

An End macro must match every xxxObject macro (here, TextObject):

                             End,

Let's add a second child to our group, a button! With a keyboard shortcut o indicated by an underscore:

                       Child, but = SimpleButton("_Ok"),

Finish the group:

                       End,

Finish the window:

                 End,

Finish the application:

           End;

So, who still needs a GUI builder? :-)

Error handling

[edit | edit source]

If any of the object in the application tree can't be created, Zune destroys all the objects already created and application creation fails. If not, you have a fully working application:

     if (app != NULL)
     {
           ...

When you're done, just call MUI_DisposeObject() on your application object to destroy all the objects currently in the application, and free all the resources:

           ...
           MUI_DisposeObject(app);
     }

Notifications

[edit | edit source]

Notifications are the simplest way to react on events. The principle? We want to be notified when a certain attribute of a certain object is set to a certain value:

     DoMethod(wnd, MUIM_Notify, MUIA_Window_CloseRequest, TRUE,

Here we'll listen to the MUIA_Window_CloseRequest of our Window object and be notified whenever this attribute is set to TRUE. So what happens when a notification is triggered? A message is sent to an object, here we tell our Application to return MUIV_Application_ReturnID_Quit on the next event loop iteration:

           (IPTR)app, 2,
           MUIM_Application_ReturnID, MUIV_Application_ReturnID_Quit);

As we can specify anything we want here, we have to tell the number of extra parameters we are supplying to MUIM_Notify: here, 2 parameters.

For the button, we listen to its MUIA_Pressed attribute: it's set to FALSE whenever the button is being released (reacting when it's pressed is bad practice, you may want to release the mouse outside of the button to cancel your action - plus we want to see how it looks when it's pressed). The action is the same as the previous, send a message to the application:

     DoMethod(but, MUIM_Notify, MUIA_Pressed, FALSE,
           (IPTR)app, 2,
           MUIM_Application_ReturnID, MUIV_Application_ReturnID_Quit);

Opening the window

[edit | edit source]

Windows aren't open until you ask them to:

     set(wnd, MUIA_Window_Open, TRUE);

If all goes well, your window should be displayed at this point. But it can fail! So don't forget to check by querying the attribute, which should be TRUE:

     if (XGET(wnd, MUIA_Window_Open))

Main loop

[edit | edit source]

Let me introduce you my lil' friend, the ideal Zune event loop:

     ULONG sigs = 0;

Don't forget to initialize the signals to 0 ... The test of the loop is the MUIM_Application_NewInput method:

     ...
     while((LONG) DoMethod(app, MUIM_Application_NewInput, (IPTR)&sigs)
           != MUIV_Application_ReturnID_Quit)

It takes as input the signals of the events it has to process (result from Wait(), or 0), will modify this value to place the signals Zune is waiting for (for the next Wait()) and will return a value. This return value mechanism was historically the only way to react on events, but it was ugly and has been deprecated in favor of custom classes and object-oriented design.

The body of the loop is quite empty, we only wait for signals and handle Ctrl-C to break out of the loop:

     {
           if (sigs)
           {
                 sigs = Wait(sigs | SIGBREAKF_CTRL_C);
                 if (sigs & SIGBREAKF_CTRL_C)
                       break;
           }
     }

Conclusion

[edit | edit source]

This program gets you started with Zune, and allows you to toy with GUI design, but not more.

Notification actions

[edit | edit source]

Notification allows you to respond to events your application/gui or any other object might cause. Due to the attribute and method based nature of Zune, and a few special attributes, Most applications can be almost completely automated through the use of Notification(s).

As seen in hello.c, you use MUIM_Notify to cause an object/class to react if a certain condition happens. If you want your application to react in a specific way to events, you can use one of these schemes:

  • Set An attribute. You can automate the passing of values from gadgets, etc to other gadgets. Setting values may also trigger other notification events.
  • Invoke a method, for example:
MUIM_Application_ReturnID
you can ask your application to return an arbitrary ID on the next loop iteration, and check for the value in the loop. This is the dirty old way of doing things.
MUIM_CallHook
to call a standard Amiga callback hook: this is an average choice, not object-oriented but not that ugly either.
custom method
the method belongs to one of your custom class. It is the best solution as it supports object-oriented design in applications. It needs you to create custom classes so it may not be the easiest for beginners or people in a hurry.

Zune Examples

[edit | edit source]

See Aros/Developer/Zune/Examples