Your First Program

By this stage you should have a rudimentary understanding of how the Pandora Engine is structured, so it's time to look at an example program before we dive into the more technical side of the system. We're going to start with an example that goes through the process of opening a window and printing a line of text inside of it. There are two ways that we can achieve this - the first using Athene + DML, the second is to write it entirely using the Pandora Engine. Both examples are good 'shell programs' for you to use in writing your own software.

My First Script

If you're using the Pandora Engine to write programs within Athene, the most realistic option for writing a Hello World program is to use DML. While we're going to show you how to write a Hello World executable in the next part of this section, for the amount of time it takes to write the same thing in DML, a script will normally be the preferred option. Here goes:

   <dml type="program">
     <window title="Hello World" insideborder insideheight="100"
       insidewidth="180">
       <render x="[owner.leftmargin]" y="[owner.topmargin]"
          xoffset="[owner.rightmargin]" yoffset="[owner.bottommargin]"
          colour="#ffffff">
         <text align="center" string="Hello World" colour="#000000"/>
       </render>
     </window>
   </dml>

DML is designed for interface construction, which makes it ideal for simple programs like this. For more complex applications, we would typically design the entire interface in DML and then code the more complex parts using C.

For the purposes of this part of the manual we will not discuss the intricacies of DML, but you can refer to the DML Whitepaper if you need further information on how DML works.

My First Executable

Writing a Pandora based executable from scratch is a fairly straight-forward affair and does not require a great deal of startup code. The following code illustrates how a program will normally go through the process of opening a window, setting up a connection with the desktop and then interpreting system messages in a message loop. When the user clicks on the window's close gadget, the program ends. Here's the code:

   #include <pandora/system/all.h>
   #include <pandora/graphics/all.h>

   STRING ProgName      = "Hello World";
   STRING ProgAuthor    = "Rocklyte Systems";
   STRING ProgDate      = "December 2003";
   STRING ProgCopyright = "Copyright Rocklyte Systems (c) 2001-2003.  All rights reserved.";
   LONG   ProgDebug = 0;
   FLOAT  ProgKernelVersion = 3.3;

   extern struct KernelBase *KernelBase;

   ERROR msghandler(struct Message *);

   OBJECTID glWindowID;

   ERROR program(void)
   {
      struct Render *render;
      OBJECTPTR window, text;
      OBJECTID render_id;
      LONG topmargin, bottommargin, rightmargin, leftmargin;

      /*** Open a window ***/

      if (CreateObject(ID_WINDOW, NULL, &window, &glWindowID,
         FID_Title|TSTRING,      ProgName,
         FID_InsideBorder|TLONG, TRUE,
         FID_InsideWidth|TLONG,  150,
         FID_InsideHeight|TLONG, 50,
         FID_Icon|TSTRING,       "nature/flower",
         TAGEND) IS ERR_Okay) {

         /*** Get the window margins ***/

         GetFields(window, FID_LeftMargin|TLONG,   &leftmargin,
                           FID_TopMargin|TLONG,    &topmargin,
                           FID_BottomMargin|TLONG, &bottommargin,
                           FID_RightMargin|TLONG,  &rightmargin,
                           TAGEND);

         ReleaseObject(window);

         /*** Create a render area for drawing graphics ***/

         if (CreateObject(ID_RENDER, NULL, &render, &render_id,
            FID_Owner|TLONG,    glWindowID,
            FID_XCoord|TLONG,   leftmargin,
            FID_YCoord|TLONG,   topmargin,
            FID_XOffset|TLONG,  rightmargin,
            FID_YOffset|TLONG,  bottommargin,
            FID_Colour|TSTRING, "#ffffff",
            TAGEND) IS ERR_Okay) {

            /*** Create the Hello World text ***/

            CreateObject(ID_TEXT, NULL, &text, NULL,
               FID_Owner|TLONG,    render_id,
               FID_String|TSTRING, "Hello World",
               FID_Align|TLONG,    ALIGN_CENTER,
               FID_Face|TSTRING,   "Helvete:16",
               FID_Colour|TSTRING, "#000000",
               TAGEND);

            /*** Show the render object inside the window ***/

            Action(AC_Show, render, NULL);
            ReleaseObject(render);

            /*** Show our window ***/

            ActionMsg(AC_Show, glWindowID, NULL);

            /*** Set our message handler and put the task to sleep ***/

            SetResource(RES_MESSAGEHANDLER, (LARGE)&msghandler);

            ProcessMessages(NULL, MF_WAIT);
         }
      }

      return(ERR_Okay);
   }

   /* This routine will kill our program if the window that we created
   ** is closed by the user.
   */

   ERROR msghandler(struct Message *Message)
   {
      if (Message->Type IS MSGID_QUIT) return(ERR_Terminate);

      return(ERR_Failed); /* Return failure if message unrecognised */
   }

Please note that if you need exact detail on the functionality of this program, you can click on the hyperlinks to read the function documentation. Step-by-step, this is an overview of how the program works:

  1. We start by creating a window for our program to present to the user. Desktop windows are controlled by a class that is appropriately named 'Window' and referenced via the class identifier ID_WINDOW. Using the CreateObject() function, we can create a new instance of a window and define some program-specific parameters by passing a series of tags to the function. In our code we set just the basic details, such as the dimensions of the window and its title. You can learn more about window settings in the documentation for the Window Class.
  2. After retrieving the window margins, you will notice that we release the window from our program by calling ReleaseObject(). This is essential as Athene is an object based system, and certain class types such as the Window must be available to other tasks when not in use. Whenever you create an object that you know is shared (this is always true of Window and Render classes), always remember to release it. This should become second nature to you if you form the habit of doing this early.
  3. With the window initialised, we now create a Render object and place the words 'Hello World' inside of it using a Text object. The Render class forms the heart of the desktop windowing system and provides all the necessary functionality for arranging the graphical layers on the desktop and inside the windows. In this case we created a simple rectangular area that fills most of the window and paints a white background. Notice the use of the Owner field setting. This is essential for specifying where our rendered graphic will appear. If you forget this setting, the render area will not be created inside your window. You can learn more about the Render and Text classes in their relvant manuals.
  4. At this point the program interface and graphics are all set-up, so the final necessary steps are to use the Show actions to actually make the Render and Window objects visible. At this point, the program is basically complete. It is worth noting that we used the ActionMsg() function to show the window, whereas we used the standard Action() function with the render object. The reason why we did this is because we released the window earlier using ReleaseObject(), and this renders the window address invalid. In this situation you can call AccessObject() to regain access to the window address, but a simpler solution is available by calling ActionMsg() and passing it the window ID that we stored earlier.
  5. The final procedure requires that we wait for the user to close the program. We do this by waiting for the window to disappear when the user clicks the window's close gadget. You will notice the ProcessMessages() function - this will put our task to sleep while it waits for incoming messages. To simplify message management we have opted to call SetResource() and set a message handler for our task. The code we have written in the msghandler() routine will receive all incoming messages, although typically you will only ever be interested in the MSGID_QUIT message type. When the msghandler() routine receives the quit message it returns the ERR_Terminate code, which causes our task to wake from its state in ProcessMessages() and continue its program flow (thus allowing the program to exit cleanly).

That's all there is to it. We recommend reading the documentation on the Window, Render and Text classes and then modifying this program by changing the field parameters that are passed to each object. Please click here for the source code and makefile for this program. This is the only working example of an executable program in this manual, so if you want to see more, please refer to the source code that is provided with the Pandora Engine to learn more from other programs.

Next: Object Management In Depth


Copyright (c) 2000-2004 Rocklyte Systems. All rights reserved. Contact us at feedback@rocklyte.com