Programming Photon without PhAB

We strongly recommend that you use PhAB to develop Photon applications—this chapter is for those who insist on not using PhAB.

This chapter discusses the following:

Basic steps

All applications using the Photon widget library follow the same basic sequence:

  1. Include <Pt.h>, the standard header file for the widget library.
  2. Initialize the Photon widget toolkit by calling PtInit() (or PtAppInit(), which also creates the main window).
  3. Create the widgets that make up the UI by calling PtCreateWidget() . This function can make the new widgets into children of a given widget or the current container, or with no parent.
  4. Register any callback functions in the application with the appropriate widgets using PtAddCallback() or PtAddCallbacks().
  5. Realize the widgets by calling PtRealizeWidget(). This function needs to be called only once by the application.

    The realize step actually creates any Photon regions that are required and maps them to the screen. Until this step is performed, no regions exist, and nothing is displayed on the screen.

  6. Begin processing photon events by calling PtMainLoop().

    At this point, the Photon widget toolkit takes control over the application and manages the widgets. If any widgets are to call functions in your application, they must have been registered as callbacks before this.

Compiling and linking a non-PhAB application

To compile and run an application that uses the Photon widget library, you must link against the main Photon library, ph. There are both static and shared versions of this library.


Caution: The libphoton.so.1 library is for applications created with version 1.14 of the Photon microGUI only. Don't combine this library with the current libraries or header files, or your application won't run properly.

We recommend that you always link against the shared library. This lets you keep your applications smaller and allows them to inherit new features that are added to the widget library when new releases of the shared library are installed.

The Photon library includes most of the function and widget definitions. If your application uses Al (translation) or Px (extended) functions, you'll also need to link with the phexlib library. If your application uses Ap (PhAB) functions, you'll also need to link with the Ap library.

The names of the shared and static libraries are the same. By default, qcc links against the shared library; to link against the static library, specify the -Bstatic option for qcc.

For example, if we have an application called hello.c, the command to compile and link against the shared libraries is:

qcc -o hello hello.c -lph

To link against the static libraries, the command is:

qcc -o hello hello.c -Bstatic -lph -lfont

Sample application

The following example illustrates a very simple application using the widget library. The program creates a window that contains a single pushbutton.

/*
 * File: hello.c
 */
#include <Pt.h>

int main( int argc, char *argv[] )
{
   PtWidget_t *window;
   PtArg_t    args[1];

   if (PtInit(NULL) == -1)
      PtExit(EXIT_FAILURE);

   window = PtCreateWidget(PtWindow, Pt_NO_PARENT, 0, NULL);

   PtSetArg(&args[0], Pt_ARG_TEXT_STRING,
             "Press to exit", 0);
   PtCreateWidget(PtButton, window, 1, args);
   PtRealizeWidget(window);

   PtMainLoop();
   return (EXIT_SUCCESS);
}

What's going on

Although this is a simple application, a lot of work is being done by each of these calls.

PtInit()

PtInit() calls PhAttach() to attach a channel to the Photon server, and then initializes the widget libraries.

PtCreateWidget() — first call

The first call to PtCreateWidget() creates a window widget that interacts with the window manager and serves as the parent for other widgets created in the application. The arguments are:

PtCreateWidget() returns a pointer to the widget created.

For more information, see Creating widgets in the Managing Widgets in Application Code chapter. For more information about widgets and their resources, see the Photon Widget Reference.

PtSetArg()

The PtSetArg() macro sets up an argument list that's used to initialize the button's resources when it's created. For more information, see the Manipulating Resources in Application Code chapter.

PtCreateWidget() — second call

All the widgets in the application — except the top-level window — have a container widget as a parent. Container widgets may have other containers within them. Creating the widgets in the application produces a hierarchy called the widget family.

The second call to PtCreateWidget() creates a pushbutton widget as a child of the window widget, using the argument list to initialize the button's resources. You can pass Pt_DEFAULT_PARENT as the parent to make the widget the child of the most recently created container widget; in this case, the results are the same.

PtRealizeWidget()

PtRealizeWidget() displays the widget and all its descendants in the widget family. Our sample application calls PtRealizeWidget() for the top-level window, so all the widgets in the application are displayed.

When a widget is realized, it uses the values of its resources to determine how big it must be to display its contents. Before realizing a widget, you should set any of the resources that may affect its size. You may change some of the resources after the widget has been realized, but it's up to the widget to determine if it can or will resize to accommodate the change in the resource's value.

You can set resize flags that the widget uses to determine whether or not to adjust its size in response to such changes, but note that if the widget exceeds the dimensions allocated to it by its parent, it's clipped to the parent's size. There's no mechanism for the widget to negotiate with its parent to obtain more space. See the Geometry Management chapter for more information.

If a Photon region is required to display the widget correctly, it's created each time the widget is realized. A region is required under any of the following conditions:

You can unrealize a widget by calling PtUnrealizeWidget(). This affects the visibility of the widget and its descendants, but not the rest of the widget family hierarchy. You can redisplay the widget later by calling PtRealizeWidget().

You can prevent a widget and its descendants from being realized when the widget's ancestor is realized. To do this, set Pt_DELAY_REALIZE in the widget's Pt_ARG_FLAGS resource. If you set this flag, it's your responsibility to call PtRealizeWidget() on the widget when you want it to appear.

PtMainLoop()

Calling PtMainLoop() transfers control of the application to the Photon widget library.

The widget library waits for Photon events and passes them on to the widgets to handle them. Application code is executed only when callback functions that the application has registered with a widget are invoked as a result of some event.

Connecting application code to widgets

If you compile, link, and run the sample application, you'll see that a window appears with a button in it. If you push the button, nothing happens because no application code has been associated with it.

The Photon widget library is designed so that the UI code can be kept distinctly separate from the application code. The UI is composed of the code to create and manipulate the widget family hierarchy, and must call the application code in response to particular events or user actions. The connection between the application code and the UI that allows it to use the application code is the single point where these two parts have intimate knowledge of each other.

Connections are made between the UI and the application code using callbacks and event handlers.

A callback is a special type of widget resource that allows the application to take advantage of existing widget features. Using a callback, the application can register a function to be called by the widget library later in response to a particular occurrence within the widget.

Event handlers (raw and filter callbacks) are normally used to add capabilities to a widget. For example, you could add behavior to a button press inside a widget that has no callbacks associated with button-press events.

Callbacks

A callback resource is used to notify the application that a specific action has occurred for a widget (e.g. you've selected a button). Every callback resource represents some user action that we thought your application might be interested in.

As with all resources, a widget has its callback resources defined by its widget class, and it inherits the callback resources defined by all the ancestors of its class. This means that a widget may have several user actions that it can notify the application about.

The value of a callback resource is a callback list. Each element of the list is an application function to be called in response to the behavior and client data associated with the callback. Client data is a pointer to any arbitrary data that your application may need to provide to the callback function for it to work correctly.

For information about callbacks, see Callbacks in the Managing Widgets in Application Code chapter.

Event handling

When we create a widget class, we can't possibly anticipate all of your application's needs. Your application may want to be notified of some occurrence on a widget that doesn't have an associated callback resource. In such cases, your application can define event-handling functions.

For information about event handlers, see Event handlers in the Managing Widgets in Application Code chapter.

Complete sample application

We can now use our newly acquired knowledge of resources and callbacks to create a more functional version of the sample application given earlier.

Using resources, we can give the pushbutton widget the same dimensions as the window, and specify which font to use for the label's text. We can also define the callback to be executed when the pushbutton is pressed. We'll make the callback function display a simple message and exit.

Here's the complete source code for our sample program with these changes:

#include <stdio.h>
#include <stdlib.h>
#include <Pt.h>

int main( int argc, char *argv[] )
{
    PtArg_t args[3];
    int n;
    PtWidget_t *window;
    int push_button_cb( PtWidget_t *, void *,
                        PtCallbackInfo_t *);
    PtCallback_t callbacks[] = {{push_button_cb, NULL}};
    char Helvetica14[MAX_FONT_TAG];

    if (PtInit(NULL) == -1)
       PtExit(EXIT_FAILURE);

    window = PtCreateWidget(PtWindow, Pt_NO_PARENT, 0, NULL);

    n = 0;
    PtSetArg(&args[n++], Pt_ARG_TEXT_STRING,
             "Press to exit", 0);

    /* Use 14-point, bold Helvetica if it's available. */

    if(PfGenerateFontName("Helvetica", 0, 14,
                          Helvetica14) == NULL) {
        perror("Unable to generate font name");
    } else {
        PtSetArg(&args[n++], Pt_ARG_TEXT_FONT, Helvetica14, 0);
    }
    PtSetArg(&args[n++], Pt_CB_ACTIVATE, callbacks,
        sizeof(callbacks)/sizeof(callbacks[0]));
    PtCreateWidget(PtButton, window, n, args);

    PtRealizeWidget(window);
    PtMainLoop();
    return (EXIT_SUCCESS);
}

int
push_button_cb(PtWidget_t *w, void *data,
               PtCallbackInfo_t *cbinfo)
{
    printf( "I was pushed\n" );
    PtExit( EXIT_SUCCESS );

    /* This line won't be reached, but it keeps
       the compiler happy. */

    return( Pt_CONTINUE );
}