Window Management

Sometimes you'll need to interact with the Photon Window Manager to make your windows and dialogs behave the way you'd like.

This chapter discusses:


Note: Remember that PhAB's window and dialog modules are implemented as PtWindow widgets. PtWindow has many resources that are used to interact with the Window Manager.

For information about the Window Manager's regions, see the appendix on Photon architecture. For a list of related functions, see Window Manager in the Summary of Functions chapter of the Photon Library Reference.

Window-management flags

The PtWindow widget defines various types of flags:

Pt_ARG_WINDOW_RENDER_FLAGS
Which window decorations appear in the window frame.
Pt_ARG_WINDOW_MANAGED_FLAGS
How the Window Manager operates on the window.
Pt_ARG_WINDOW_NOTIFY_FLAGS
Which Window Manager events your application would like to be notified of.
Pt_ARG_WINDOW_STATE
The current state of the window.

Note: If you change the state of the window after it's realized, you'll need to let the Window Manager know. See Getting and setting the window state later in this chapter.

Window-rendering flags

The Pt_ARG_WINDOW_RENDER_FLAGS resource specifies what appears in the window's frame.


Note: In PhAB, if you turn these flags off, PhAB provides a border, title, and buttons that allow you to re-size, move, minimize, and close the window in design mode. The flags still affect how the window appears when the application is running.

To display: Set this bit: Default:
Border Ph_WM_RENDER_BORDER Yes
Resize handles Ph_WM_RENDER_RESIZE Yes
Title bar Ph_WM_RENDER_TITLE Yes
Menu button Ph_WM_RENDER_MENU Yes
Close button Ph_WM_RENDER_CLOSE
Help button (question mark) Ph_WM_RENDER_HELP
Minimize button Ph_WM_RENDER_MIN Yes
Maximize button Ph_WM_RENDER_MAX Yes
Collapse button Ph_WM_RENDER_COLLAPSE Yes
An extra line inside the standard borders Ph_WM_RENDER_INLINE

Note: Using these flags to display a decoration doesn't cause the Window Manager to do anything with it. You may need to set the window managed flags and/or notify flags.

Window-managed flags

The Pt_ARG_WINDOW_MANAGED_FLAGS resource specifies what operations you want the window manager to handle:

To let the window manager: Set this bit: Default:
Close the window Ph_WM_CLOSE Yes
Give focus Ph_WM_FOCUS Yes
Build and control the window menu Ph_WM_MENU Yes
Move the window to the front Ph_WM_TOFRONT Yes
Move the window to the back Ph_WM_TOBACK Yes
Move the window to a new console as the user switches consoles Ph_WM_CONSWITCH
Resize the window Ph_WM_RESIZE Yes
Move the window Ph_WM_MOVE Yes
Hide (i.e. minimize) the window Ph_WM_HIDE Yes
Maximize the window Ph_WM_MAX Yes
Display the window as a backdrop Ph_WM_BACKDROP
Restore the window Ph_WM_RESTORE Yes
Provide context-sensitive help Ph_WM_HELP
Make the window force-front Ph_WM_FFRONT
Collapse the window to just the title bar Ph_WM_COLLAPSE
Prevent you from cycling focus to the window by pressing Alt-Esc, Alt-Shift-Esc, or Alt-Tab Ph_WM_NO_FOCUS_LIST

By default, a selection of these flags are set, as defined by Ph_WM_APP_DEF_MANAGED in <PhWm.h>. You'd turn the management flags off if:

Window-notify flags

The Pt_ARG_WINDOW_NOTIFY_FLAGS resource specifies which window-manager operations your application should be notified of. This resource uses the same bits as Pt_ARG_WINDOW_MANAGED_FLAGS:

To be notified when: Set this bit: Default:
The window is to be closed (see below) Ph_WM_CLOSE Yes
The window is to gain/lose focus Ph_WM_FOCUS
The window menu is requested or dismissed Ph_WM_MENU
The window is to be moved to the front Ph_WM_TOFRONT
The window is to be moved to the back Ph_WM_TOBACK
The window is to switch consoles Ph_WM_CONSWITCH
The window is to be resized Ph_WM_RESIZE Yes
The window is to be moved Ph_WM_MOVE
The window is to be hidden or unhidden Ph_WM_HIDE
The window is to be maximized Ph_WM_MAX
The window is to be made into a backdrop Ph_WM_BACKDROP
The window is to be restored Ph_WM_RESTORE
The help button is pressed Ph_WM_HELP Yes
The window is to be made force-front or not force-front Ph_WM_FFRONT

The default setting is Ph_WM_RESIZE|Ph_WM_CLOSE| Ph_WM_HELP.

When the requested operations occur, the window's Pt_CB_WINDOW callback is invoked. See Notification callback below.

If you set the Ph_WM_CLOSE notify flag, your application's Pt_CB_WINDOW callback is invoked when someone wants the window to close. Your application doesn't have to close the window — it could decide to leave it open.

In contrast, the Pt_CB_WINDOW_CLOSING callback is called when a window is being unrealized, but before its region is removed. At this point, the application can't stop the window from being closed.


Note: If you've set the Ph_WM_CLOSE managed flag, the window manager is told to handle the window's closing. In this case, the Pt_CB_WINDOW_CLOSING callback is invoked, but the Pt_CB_WINDOW callback isn't.

Notification callback

When a window manager operation occurs that's listed in the window's notify flags (Pt_ARG_WINDOW_NOTIFY_FLAGS), the window's Pt_CB_WINDOW callback is invoked.

Each callback function listed in this resource is passed a PtCallbackInfo_t structure (see the Photon Widget Reference) that contains at least the following members:

reason
Pt_CB_WINDOW
reason_subtype
0 (not used).
event
A pointer to the event that caused the callback to be invoked.
cbdata
A pointer to a PhWindowEvent_t (described in the Photon Library Reference).

These callback functions should return Pt_CONTINUE.

Example: verifying window closure

Suppose you want to verify that the user really wants to exit the application when the window is closed. Here's what you need to do:

There's a significant difference between the Ph_WM_CLOSE event and the Window Closing (Pt_CB_WINDOW_CLOSING) callback.

A Pt_CB_WINDOW callback with a Ph_WM_CLOSE event is just a notification from PWM that the user has clicked on the Close button or chosen Close from the PWM menu. If the Ph_WM_CLOSE bit is unset in the Pt_ARG_WINDOW_MANAGED_FLAGS, the library takes no further action.

Window Closing is invoked when the window is about to unrealize for any reason. This includes transporting to another Photon and explicit calls to PtDestroyWidget() or PtUnrealizeWidget(). If you want to make sure in a Window Closing callback that the window is actually being destroyed, check the Pt_DESTROYED flag in Pt_ARG_FLAGS. You can also use the Pt_CB_DESTROYED callback to know when a window is marked for destruction, or Pt_CB_IS_DESTROYED to know when it is being destroyed.

Also note that calling exit() explicitly bypasses all those callbacks.

Getting and setting the window state

The Pt_ARG_WINDOW_STATE resource controls the window's state:

To do this: Set this bit:
Maximize the window Ph_WM_STATE_ISMAX
Make the window a backdrop Ph_WM_STATE_ISBACKDROP
Minimize the window Ph_WM_STATE_ISHIDDEN
Place the base window in front of the windows of all other applications Ph_WM_STATE_ISFRONT
Give keyboard focus to the window if cursor focus is disabled Ph_WM_STATE_ISFOCUS
Pass Alt key combinations to the application Ph_WM_STATE_ISALTKEY
Block the window Ph_WM_STATE_ISBLOCKED (read-only)

The default value is Ph_WM_STATE_ISFOCUS.


Note: You can get and set the state of the window at any time by using the Pt_ARG_WINDOW_STATE resource, but you might get unexpected results if the user is changing the window state at the same time.

The safest time to use this resource to set the window state is before the window is realized. For example, you could set it when creating the PtWindow widget or in the window's Pt_CB_WINDOW_OPENING callback. The setting will be in effect when the window is realized.

You can set Pt_ARG_WINDOW_STATE after the window has been realized, basing your changes on what you think the current window state is, but it's safer to tell the window manager how you want to change the state, by calling:

PtForwardWindowEvent()
Change the state for the window associated with a given region ID
PtForwardWindowTaskEvent()
Change the state for a window associated with a given Photon connection ID

For example, to minimize a window that belongs to your application and is already open:

PhWindowEvent_t event;

memset( &event, 0, sizeof (event) );
event.event_f = Ph_WM_HIDE;
event.event_state = Ph_WM_EVSTATE_HIDE;
event.rid = PtWidgetRid( window );
PtForwardWindowEvent( &event );

In order to change the state of a window that belongs to another application, you need a connection ID (of type PhConnectId_t) for the application. If you have the region ID of a region that belongs to the application, you can call PhRegionQuery() and extract the connection ID from the owner member of the PhRegion_t structure.

If you don't have a region ID, but you know the application's process ID, you can call PhGetConnectInfo() like this to get the connection ID:

PhConnectId_t get_connect_id( pid_t pid )
{
   PhConnectInfo_t buf;
   PhConnectId_t id = 1;

   while ((id = PhGetConnectInfo(id, &buf)) != -1
          && (buf.pid != pid ||
              ND_NODE_CMP(buf.nid, ND_LOCAL_NODE)))
      ++id;

   return id;
}

Once you have the connection ID, you can minimize an open window that belongs to the other application with this code:

PhWindowEvent_t event;

memset( &event, 0, sizeof (event) );
event.event_f = Ph_WM_HIDE;
event.event_state = Ph_WM_EVSTATE_HIDE;

PtForwardWindowTaskEvent( connection_id, &event );

Note: When you call these functions, you're asking the window manager to do the specified action. If the action isn't set in the managed flags (Pt_ARG_WINDOW_MANAGED_FLAGS) for the given window, the window manager doesn't do it.

Managing multiple windows

If your application has more than one window, you'll need to take the relationships between them into account.

By definition, a child window is always in front of its parent. The child windows can move above and below siblings. For windows to be able to go behind other windows, they must be siblings. So for a window to be able to move behind the base window, that window would have to have no parent.

Window-manager functions

The following low-level functions are associated with the window manager, but you shouldn't use them in an application that uses widgets:

PhWindowChange()
Modify the attributes of a window's region
PhWindowClose()
Close a window
PhWindowOpen()
Create a window region

These functions can be called in an application that uses widgets:

PhWindowQueryVisible()
Query a visible extent
PtConsoleSwitch()
Switch to another virtual console
PtForwardWindowEvent()
Forward a window event
PtForwardWindowTaskEvent()
Forward a window event to a task
PtWindowConsoleSwitch()
Switch to the console a given window's displayed on
PtWindowGetFrameSize()
Determine the size of a window's frame

Running a standalone application

If your application is intended to run by itself, you might want to:

Modal dialogs

Sometimes, you want your program to prompt the user for information before continuing. You usually do this by popping up a dialog; if you don't want the user to be able to select any other operations before providing the information, you should use a modal dialog.

A modal dialog doesn't allow user input to go to any of the other widgets in the application. To use a modal dialog to prompt for information, you have to make sure that events are processed within the callback function.

To create a modal dialog, you have to create a new PtWindow widget, normally as a child of the main application window.

To activate the modal dialog, you have to realize the dialog widget and block all the other window widgets in the application. To block the window or windows, call one of:

PtBlockAllWindows()
Block all windows except the one with a given widget
PtBlockWindow()
Block a given window

Both of these routines return a list of blocked widgets, which you'll need when you unblock them. Instead of blocking the windows, you can make the dialog modal by calling PtMakeModal().

After the modal dialog has been activated, call PtModalBlock() to start a modal loop to process Photon events until a termination condition is met.

When the operation associated with the modal dialog is completed or aborted, you have to dismiss the dialog. To do so:

  1. Call PtModalUnblock() to stop the modal loop. You can specify the value to be returned by PtModalBlock().
  2. Destroy or unrealize the dialog itself.
  3. Call PtUnblockWindows() to unblock any window widgets that you blocked when you created the dialog. You don't need to do this if you called PtMakeModal() instead of PtBlockAllWindows() or PtBlockWindow().

We can easily change our previous example of work procedures so that its progress dialog behaves as a modal dialog. We'll add a PtModalCtrl_t structure to the callback closure, for PtModalBlock() and PtModalUnblock() to use.

The done() callback is altered to call PtModalUnblock() rather than free the closure:

int done(PtWidget_t *w, void *client,
          PtCallbackInfo_t *call)
{
   CountdownClosure *closure =
      (CountdownClosure *)client;

   call = call;

   if (!closure->done) {
      PtAppRemoveWorkProc(NULL, closure->work_id);
   }
   PtDestroyWidget(closure->dialog->widget);
   free(closure->dialog);

   /* New: end the modal loop, return the counter's
      value as the response. */
   PtModalUnblock(&(closure->modal_control),
                  (void *) &(closure->value));

   return (Pt_CONTINUE);
}

All that remains at this point is to change the push_button_cb() callback function so that it blocks the window after realizing the progress dialog, starts the modal loop, and unblocks the windows and frees the closure after the dialog is dismissed.

Here's the new version of the push_button_cb() callback function:

int push_button_cb(PtWidget_t *w, void *client,
                   PtCallbackInfo_t *call)
{
   PtWidget_t   *parent = (PtWidget_t *)client;
   WorkDialog *dialog;
   PtBlockedList_t * blocked_list;
   void * response;

   w = w; call = call;

   dialog = create_working_dialog(parent);

   if (dialog)
    {
      CountdownClosure *closure =
         (CountdownClosure *)
          malloc(sizeof(CountdownClosure));

      if (closure)
        {
         PtWorkProcId_t *id;

         closure->dialog = dialog;
         closure->value = 0;
         closure->maxvalue = 200000;
         closure->done = 0;
         closure->work_id = id =
            PtAppAddWorkProc(NULL, count_cb, closure);

         PtAddCallback(dialog->ok_button, Pt_CB_ACTIVATE,
                       done, closure);
         PtRealizeWidget(dialog->widget);

         /* New: Block all the windows except the dialog,
            process events until the dialog is closed,
            and then unblock all the windows. */

         blocked_list = PtBlockAllWindows (dialog->widget,
                           Ph_CURSOR_NOINPUT, Pg_TRANSPARENT);

         response = PtModalBlock( &(closure->modal_control), 0 );
         printf ("Value reached was %d\n", *(int *)response );
         free (closure);

         PtUnblockWindows (blocked_list);

        }
    }
    return (Pt_CONTINUE);
}

Here's the new version of the whole program:

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

typedef struct workDialog {
   PtWidget_t *widget;
   PtWidget_t *label;
   PtWidget_t *ok_button;
} WorkDialog;

typedef struct countdownClosure {
   WorkDialog *dialog;
   int value;
   int maxvalue;
   int done;
   PtWorkProcId_t *work_id;

   /* New member: */
   PtModalCtrl_t modal_control;
} CountdownClosure;


WorkDialog *create_working_dialog(PtWidget_t *parent)
{
   PhDim_t      dim;
   PtArg_t      args[3];
   int          nargs;
   PtWidget_t   *window, *group;
   WorkDialog *dialog =
      (WorkDialog *)malloc(sizeof(WorkDialog));

   if (dialog)
    {
      nargs = 0;
      PtSetArg(&args[nargs], Pt_ARG_WIN_PARENT, parent, 0);
               nargs++;
      PtSetParentWidget(NULL);
      dialog->widget = window =
         PtCreateWidget(PtWindow, parent, nargs, args);

      nargs = 0;
      PtSetArg(&args[nargs], Pt_ARG_GROUP_ORIENTATION,
               Pt_GROUP_VERTICAL, 0); nargs++;
      PtSetArg(&args[nargs], Pt_ARG_GROUP_VERT_ALIGN,
               Pt_GROUP_VERT_CENTER, 0); nargs++;
      group = PtCreateWidget(PtGroup, window, nargs, args);

      nargs = 0;
      dim.w = 200;
      dim.h = 100;
      PtSetArg(&args[nargs], Pt_ARG_DIM, &dim, 0); nargs++;
      PtSetArg(&args[nargs], Pt_ARG_TEXT_STRING,
               "Counter:        ", 0); nargs++;
      dialog->label = PtCreateWidget(PtLabel, group,
                                        nargs, args);

      PtCreateWidget(PtSeparator, group, 0, NULL);

      nargs = 0;
      PtSetArg(&args[nargs], Pt_ARG_TEXT_STRING, "Stop", 0);
               nargs++;
      dialog->ok_button = PtCreateWidget(PtButton, group,
                                            1, args);
    }
   return dialog;
}

int done(PtWidget_t *w, void *client,
          PtCallbackInfo_t *call)
{
   CountdownClosure *closure =
      (CountdownClosure *)client;

   call = call;

   if (!closure->done) {
      PtAppRemoveWorkProc(NULL, closure->work_id);
   }
   PtDestroyWidget(closure->dialog->widget);
   free(closure->dialog);

   /* New: end the modal loop, return the counter's
      value as the response. */
   PtModalUnblock(&(closure->modal_control),
                  (void *) &(closure->value));

   return (Pt_CONTINUE);
}

int
count_cb(void *data)
{
   CountdownClosure *closure =
      (CountdownClosure *)data;
   char    buf[64];
   int     finished = 0;

   if ( closure->value++ == 0 || closure->value %
        1000 == 0 )
    {
      sprintf(buf, "Counter: %d", closure->value);
      PtSetResource( closure->dialog->label,
                     Pt_ARG_TEXT_STRING, buf, 0);
    }

   if ( closure->value == closure->maxvalue )
    {
      closure->done = finished = 1;
      PtSetResource( closure->dialog->ok_button,
                     Pt_ARG_TEXT_STRING, "Done", 0);
    }

    return finished ? Pt_END : Pt_CONTINUE;
}

int push_button_cb(PtWidget_t *w, void *client,
                   PtCallbackInfo_t *call)
{
   PtWidget_t   *parent = (PtWidget_t *)client;
   WorkDialog *dialog;
   PtBlockedList_t * blocked_list;
   void * response;

   w = w; call = call;

   dialog = create_working_dialog(parent);

   if (dialog)
    {
      CountdownClosure *closure =
         (CountdownClosure *)
          malloc(sizeof(CountdownClosure));

      if (closure)
        {
         PtWorkProcId_t *id;

         closure->dialog = dialog;
         closure->value = 0;
         closure->maxvalue = 200000;
         closure->done = 0;
         closure->work_id = id =
            PtAppAddWorkProc(NULL, count_cb, closure);

         PtAddCallback(dialog->ok_button, Pt_CB_ACTIVATE,
                       done, closure);
         PtRealizeWidget(dialog->widget);

         /* New: Block all the windows except the dialog,
            process events until the dialog is closed,
            and then unblock all the windows. */

         blocked_list = PtBlockAllWindows (dialog->widget,
                           Ph_CURSOR_NOINPUT, Pg_TRANSPARENT);

         response = PtModalBlock( &(closure->modal_control), 0 );
         printf ("Value reached was %d\n", *(int *)response );
         free (closure);

         PtUnblockWindows (blocked_list);

        }
    }
    return (Pt_CONTINUE);
}

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

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

   dim.w = 200;
   dim.h = 100;
   PtSetArg(&args[0], Pt_ARG_DIM, &dim, 0);
   if ((window = PtCreateWidget(PtWindow, Pt_NO_PARENT,
                                1, args)) == NULL)
      PtExit(EXIT_FAILURE);

   callbacks[0].data = window;
   n = 0;
   PtSetArg(&args[n++], Pt_ARG_TEXT_STRING, "Count Down...", 0);

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

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

   PtRealizeWidget(window);

   PtMainLoop();
   return (EXIT_SUCCESS);
}

If your modal dialog is self-contained and you just need to wait for it, you might find this function useful:

ApModalWait()
Process Photon events until a given widget is destroyed