Chapter 16. Windows

Rendering to the graphics hardware requires a window. A window is an allocated area of the screen with associated framebuffer resources.

For IRIX and Linux, the X window system manages the use of shared resources among the different windows. Windows can be requested directly from an X window server. By use of the GLX extension, OpenGL Performer-based OpenGL graphics contexts can render into X windows.

For Microsoft Windows, HWNDs and OpenGL/WGL contexts are used.

This chapter describes how to create, configure, manipulate, and communicate with a window using pfWindow in OpenGL Performer. The extended libpf object pfPipeWindow, based on pfWindow, is also mentioned in this chapter as the two objects share much functionality. Likewise, for good understanding of windowing issues, read the next chapter, Chapter 17, “pfPipeWindows and pfPipeVideoChannels”

pfWindows

The pfWindow object provides an efficient windowing interface between your application and the X Window System or the Microsoft Windows GUI. pfWindows typically create an X window (an HWND on Microsoft Windows) or can also be configured to embed your rendering area within a window created with Motif® or other windowing toolkits. libpr provides utilities to shield you from the differences between the different types of windows and guide you in your dealings with the window system. pfWindows also keep track of your graphics state: they include a pfState which is automatically initialized when you open a window and switched for you when you change windows. Simple libpr windowing support centers around the pfWindow. The libpf windowing support utilizes a pfWindow as part of a pfPipeWindow.

OpenGL Performer automatically configures and initializes your window so that it will be ready to start rendering efficiently. In the simplest case, pfWindows make creating a graphics application that can run on any SGI machine with OpenGL a snap. pfWindows do not limit your ability to configure any part or all of your windowing environment yourself; you can use the libpr pfWindows to manage your GL windows even if you create and configure the actual windows yourself.

Creating a pfWindow

A pfWindow structure is created with pfNewWin(). It can then be immediately opened with pfOpenWin(). Example 16-1 shows the most basic pfWindow operations in libpr program: to open and clear a pfWindow and swap front and back color buffers.

Example 16-1. Opening a pfWindow

int main (void)
{
     pfWindow *win;
     /* Initialize Performer */
     pfInit();
     pfInitState(NULL);
 
     /* Create and open a Window */
     win = pfNewWin(NULL);
     pfWinName(win, “Hello from OpenGL Performer”);
     pfOpenWin();
 
     /* Rendering loop */
     while (1)
     {
         /* Clear to black and max depth */
         pfClear(PFCL_COLOR | PFCL_DEPTH, NULL);
         ...
         pfSwapWinBuffers(win);
     }
}

The pfWindow in Example 16-1 will have the following configuration:

Window system interface
 

An OpenGL window using the OpenGL/X GLX interface on IRIX and Linux or an OpenGL HWND on Microsoft Windows.

Screen
 

On IRIX and Linux, the pfWindow will open a window on the screen specified by the DISPLAY environment variable or else on screen 0. On Microsoft Windows, screen 0 will always be used.

Position and size
 

The position and size will be undefined and the window will come up as a rubber-band for the user to place and stretch.

Framebuffer configuration
 

The window will be double-buffered RGBA with depth and stencil buffers allocated. The size of these buffers will depend on the available resources of the current graphics hardware platform. pfWindows will also have multisample buffers allocated if they are available on current hardware platform.

libpr state
 

A pfState will be created and initialized with all modes disabled and no attributes set.

Graphics state
 

The pfWindow will be in RGBA color mode with subpixel vertex positioning, depth testing and viewport clipping enabled. The viewing projection will be a two-dimensional one-to-one orthographic mapping from eye coordinates to window coordinates with distances to near and far clipping planes -1 and 1, respectively. The model matrix will be the current matrix and will be initialized to the identity matrix.

Typically, pfWindows go through a bit more initialization than that of Example 16-1. The pfWindow type, set with pfWinType(), is a bitmask that selects the window system interface and the type of rendering window. Table 16-1 lists the possible selectors that can be ORed together for specification of the window type.

Table 16-1. pfWinType() Tokens

PFWIN_TYPE_ Bitmask Token


Description

X

Window will be an X window on IRIX and Linux but will be an HWND on Microsoft Windows. This is the default.

STATS

Window will have framebuffer resources to accommodate hardware statistics modes. This type cannot be combined with PFWIN_TYPE_OVERLAY or PFWIN_TYPE_NOPORT.

OVERLAY

Window will have only overlay planes for rendering. This type cannot be combined with PFWIN_TYPE_STATS or PFWIN_TYPE_NOPORT.

NOPORT

Window will have a graphics context but no physical window or graphics or framebuffer rendering resources and will not be placed on the screen. This token can not be used in combination with any other type token.

PBUFFER

The pfWindow drawable will be created as a pbuffer and will not be visible.

UNMANAGED

Other than select upon open, no unrequested window management operations are done automatically on the pfWindow.

The selection of screen can be done explicitly with pfWinScreen(), or implicitly by opening a connection to the window system using pfOpenScreen() with the desired screen as the default screen. A window system connection can communicate with any screen on the system; the default screen only determines the screen for windows that do not have a screen explicitly set for them. Only one window system connection should be opened for a process. See “Communicating with the Window System” later in this section for details on efficient interaction with the window system.

The position and/or size, is set with  pfWinOriginSize(). If the x and y components of the origin are (-1), the window will open with position undefined for the user to place. If the x or y components of the size are (-1), the window will open with both position and size undefined (the default) for the user to place and stretch. On IRIX and Linux, the X window manager may override negative origins and place the window at (0,0). If the window is already opened when pfWinOriginSize() is called, the window will be reconfigured to the specified origin and size upon the next pfSelectWin(). Similarly, pfWinFullScreen() causes a window to open as full screen or to become full screen upon the next call to pfSelectWin(). A full screen window will have its border automatically removed so that the drawing area truly gets the full rendering surface. The routines for querying the position and size work a bit differently than the pattern established by the rest of libpr get and set pairs of routines. This is because a user may change the origin or size independently of the program and under certain conditions, querying the true current X window size and origin can be expensive. pfGetWinOrigin() and pfGetWinSize() will always be fast and returns the last explicitly set origin and size, such as by pfOpenWin(), pfWinOriginSize(), or pfWinFullScreen(). If the window origin or size has been changed, but not through a pfWindow routine, the values returned by pfGetWinOrigin() and pfGetWinSize() may not be correct. pfGetWinCurOriginSize() returns an accurate size and origin relative to the pfWindow parent. For X windows, note that it requires an expensive query to the X server and should not be done in real-time situations. pfGetWinCurScreenOriginSize() returns the size and the screen-relative origin of the pfWindow. As with pfGetWinCurOriginSize(), this command will be quite expensive and is not recommended accept for rare use or initialization purposes.

pfPipeWindows, discussed in Chapter 17, “pfPipeWindows and pfPipeVideoChannels”, take advantage of the multiprocessed libpf environment to always be able to return an accurate window size and origin relative to the window parent. However, even for pfPipeWindows, getting a screen-relative origin can be an expensive operation.

Write programs that are window-relative and do not depend on knowing the current exact location of a window relative to its parent or screen.

Configuring the Framebuffer of a pfWindow

OpenGL Performer provides a default framebuffer configurations for the current graphics hardware platform for the standard window types: normal rendering, statistics (stats), and overlay. You may want to define your own framebuffer configuration, such as single-buffered, stereo, etc. You can use utilities in libpr to help you with this task, or create your own framebuffer configuration structure with X utilities, or even create the window yourself and apply it to the pfWindow. pfOpenWin() respects any specified framebuffer configuration. Additionally, pfOpenWin() uses any window or graphics context that is assigned to it and only creates what is undefined.

pfWinFBConfigAttrs() can be used to specify an array of framebuffer attribute tokens listed in Table 16-2. The tokens correspond to OpenGL/X tokens. Note that if an attribute array is specified, the tokens modify configuration with no attributes set, not the default OpenGL Performer framebuffer configuration.

Table 16-2. pfWinFBConfigAttrs() Tokens

PFFB_ Token

Value

Description

BUFFER_SIZE

integer > 0

The size of the color index buffer.

LEVEL

integer > 0

The color plane level:
normal color planes have level = 0

overlay color planes have level > 0

underlay color planes have level < 0

There may be only one or no levels for overlay and underlay color planes on some graphics hardware configurations.

RGBA

Boolean: true if present

Use RGBA color planes (instead of color index).

DOUBLEBUFFER

Boolean: true if present

Use double-buffered color buffers.

STEREO

Boolean: true if present

Allocate left and right stereo color buffers (allocates back left and back right if DOUBLEBUFFER is specified.

AUX_BUFFER

integer > 0

Number of additional color buffers to allocate.

RED_SIZE
GREEN_SIZE
BLUE_SIZE
ALPHA_SIZE

integer > 0

Minimum number of bits color for components R, G, and B will all be the same and be the maximum specified. Alpha may be different.

DEPTH_SIZE

integer > 0

Number of bits in the depth buffer.

STENCIL

integer > 0

Number of bits allocated for stencil. One is used by pfDecal rendering and three or four are used by the hardware fill statistics in pfStats.

ACCUM_RED_SIZE
ACCUM_GREEN_SIZE
ACCUM_BLUE_SIZE
ACCUM_ALPHA_SIZE

integer > 0

Number of bits per RGBA component for the accumulation color buffer.

USE_GL

Boolean: true if present

Exists for historical reasons. Has no effect.

If you desire more control over the exact framebuffer configuration of your pfWindow, you have several options. For OpenGL/X windows you can provide the appropriate framebuffer description for the current GL operation to the pfWindow using pfWinFBConfig(). X uses visuals to describe available framebuffer configurations. XVisualInfo pointer with XGetVisualInfo() returns a list of all visuals on the system and you can search through them to find the appropriate configuration. On IRIX-based systems, OpenGL/X also uses GLXFBConfigSGIX to describe framebuffer configurations. You can select either the visual or the GLXFBConfigSGIX for your window and set it on the pfWindow with pfWinFBConfig(). pfGetWinFBConfig() always returns the corresponding X visual.

libpr also offers utilities for creating framebuffer configurations (pfFBConfig) independently of a pfWindow. pfChooseFBConfig() takes an attribute array of tokens from Table 16-2 and will return a pfFBConfig structure that can be used with your pfWindows, or with X Windows created outside of libpr, such as with Motif. You may get back a framebuffer configuration that is better than the one you requested. OpenGL Performer will give you back the maximum framebuffer configuration that meets your request that will not add any serious performance degradations. There are specific machine-dependent instances where, for performance reasons, we do limit the framebuffer configuration. See the man page for pfChooseWinFBConfig() for the specific details. The libpfutil utility pfuChooseFBConfig() in /usr/share/Performer/src/lib/libpfutil/xwin.c (IRIX and Linux) and %PFROOT%\Src\lib\libpfutil\xwin.c (Microsoft Windows) provides a limiting framebuffer configuration selector, complete with source code.

You can use pfQuerySys() to query particular framebuffer resources in the current hardware configuration and then use pfQueryWin() to query your resulting framebuffer configuration.

pfWindows and GL Windows


Note: This is an advanced topic.

For IRIX and Linux, libpr allows you to use X window handles and OpenGL/X graphics contexts to create your own windows and to set them on the pfWindow. These handles can be assigned to the pfWindow with pfWinWSDrawable() or pfWinGLCxt(). For Microsoft Windows, you can do the same, but you must use HWNDs (instead of X window handles) and OpenGL/WGL graphics contexts.

pfOpenWin() will automatically call pfInitGfx() and will automatically create a new pfState for your window. If you have your own window management and do not call pfOpenWin(), then you should definitely call pfInitGfx() to initialize the window's graphics state for OpenGL Performer rendering. You will also need to call pfNewState() to create a pfState for OpenGL Performer's state management.

For X windows, OpenGL Performer maintains two windows and a graphics context. The top level X window is placed on the screen and is the one that you should use in your application for selecting X events. This top level window is very lightweight and has minimal resources allocated to it. OpenGL Performer then maintains a separate X window that is a child of the parent X window and is the one that is attached to the graphics context. This allows you to select different framebuffer resources for the same drawing area by just selecting a different graphics window and graphics context pair for the parent X window. pfWindows directly support this functionality and this is discussed in the next section, “Manipulating a pfWindow”. Finally, with OpenGL, you may choose to draw to a different X Drawable than a window. X windows are created with the X function XCreateWindow(). OpenGL graphics contexts are created with glXCreateContext(). The parent X Window can be set with pfWinWSWindow(), the graphics window or X Drawable is set with pfWinWSDrawable() and can be an X window, pbuffer, or pixmap. The graphics context is set with pfWinGLCxt(). OpenGL Performer defines the following window-system-independent types defined in Table 16-3. If you create your own window but want to use pfQueryWin(), you must also provide the framebuffer configuration information with pfWinFBConfig(). pfQueryWin() uses the internally stored visual.

Table 16-3. Window System Types

pfWS Type

X Type

pfWindow Set/Get Routine

pfWSWindow

X Window for IRIX and Linux
HWND for Microsoft Windows

pfWinWSWindow()
pfGetWinWSWindow()

pfWSDrawable

Drawable (window, pbuffer, pixmap) for IRIX and Linux

HANDLE (to window or buffer) for Microsoft Windows

pfWinWSDrawable()
pfGetWinWSDrawable()

pfGLContext

OpenGL: GLXContext for IRIX and Linux

HGLRC for Microsoft Windows

pfWinGLCxt()
pfGetWinGLCxt()

pfFBConfig

XVisualInfo* or GLXFBConfigSGIX* for IRIX and Linux

int (pixel format ID) for Microsoft Windows

pfWinFBConfig()
pfGetWinFBConfig()

pfWSConnection

Display* for IRIX and Linux only

pfGetCurWSConnection()


Manipulating a pfWindow

Windows are opened with pfOpenWin() and closed with pfCloseWin(). When a window is closed, its graphics context is deleted. If you have multiple windows, you select the window to draw to with pfSelectWin(). Multiple windows can be made more efficient using share groups configured with pfWinShare() to share hardware resources. Multiple windows can be made to have swapbuffers execute simultaneously through window swap groups created with  pfAttachWinSwapGroup(). There are also some additional modes on pfWindows to control their behavior under various operations. This section goes through the basics of these important features.

There are some modes you can set that can effect the general look and behavior of your window and alternate configuration windows. These boolean modes can be individually set and changed at any time with pfWinMode() and the tokens in Table 16-4.

Table 16-4. pfWinMode() Tokens

PFWIN_ Token

Description

NOBORDER

Window will be without normal window system border

HAS_OVERLAY

Overlay alternate configuration window will be managed by the pfWindow. pfOpenWin() will automatically create an overlay window if one has not already been set.
pfWinIndex(win, PFWIN_OVERLAY_WIN) will also automatically create and open an overlay window if one has not already been set.

HAS_STATS

Statistics alternate configuration window will be managed by the pfWindow. pfOpenWin() will automatically create a statistics window if one has not already been set.
pfWinIndex(win, PFWIN_OVERLAY_WIN) will also automatically create and open a statistics window if one has not already been set and if the current window cannot support statistics.

AUTO_RESIZE

The graphics window and active alternate configuration windows are automatically resized to match the parent pfWinWSWindow(). This mode is enabled by default.

ORIGIN_LL

The origin of the pfWindow, for placement purposes, will be the lower-left corner. X and the Microsoft Windows GUI use the upper left corner as the origin. This mode is enabled by default.

EXIT

The application will receive a DeleteWindow message upon selection of the “Exit” from the window system menu on the window border.


Alternate Framebuffer Configuration Windows

OpenGL Performer supports multiple framebuffer configurations for the same drawing area with alternate configuration windows. An OpenGL Performer alternate configuration window has the same window parent ( pfWinWSWindow()) but may have a different drawable and graphics context. There are standard alternate configuration windows for overlay and statistics windows that can be automatically created upon demand.

An alternate configuration window is created as a full pfWindow and is an alternate configuration window by virtue of being given to a base window in a pfList of alternate configuration windows, or being directly assigned as one of the standard alternate configuration windows with either of pfWinOverlayWin() or pfWinStatsWin(). A pfWindow may be an alternate configuration window of only one base window at a time; alternate configuration windows may not be instanced between base windows. The sharing of window attributes between alternate configuration windows, such as the parent X window and GL objects (for OpenGL windows), must be set with pfWinShare() on the base window and applied to the alternate configuration windows with pfAttachWin(). You select the desired alternate configuration window to draw into with pfWinIndex() and provide an index into your alternate configuration window list or one of the standard indices ( PFWIN_GFX_WIN, PFWIN_OVERLAY_WIN, or PFWIN_STATS_WIN). PFWIN_GFX_WIN is the default window index and selects the base window. If the alternate configuration window has not been opened, it will be opened automatically upon being selected for rendering. Example 16-2 demonstrates creating a pfWindow using the default overlay window. The graphics drawable and graphics context of an alternate configuration window of a pfWindow can be closed with pfCloseWinGL(). This can be called on the base window, in which case the active alternate configuration window's GL window and context will be closed, or it can be called on the alternate configuration window pfWindow directly. The main parent window will remain on the screen and a new alternate configuration window can be applied to it or pfOpenWin() can be called to create a new graphics window and context.

Window Share Groups

Multiple windows on a screen will require duplicate processing and resources unless they are set up as share groups. A pfWindow is attached to the group of another with pfAttachWin(groupWin, attachee). The attributes to be shared are set with pfWinShare() on any of the windows in the group. The full list of attributes are in the man page for pfWindow (and similarly for pfPipeWindow) but most notably are PFWIN_SHARE_GL_CXT for using the same graphics context across multiple windows, PFWIN_SHARE_STATE for sharing full state information, and PFWIN_SHARE_GL_OBJS for sharing display lists and textures across windows. In particular, sharing GL objects is important if a display list (such as for fonts) are to be created for one context and used in multiple contexts. When default alternate configuration windows are automatically created (overlay and stats) they are configured to share GL objects with the base window. A libpf pfPipeWindow example of window share groups is in /usr/share/Performer/src/pguide/libpf/C/multiwin.c for IRIX and Linux and in %PFROOT%\Src\pguide\libpf\C\multiwin.c for Microsoft Windows.

Synchronization of Buffer Swap for Multiple Windows

On IRIX systems, double-buffered pfWindows in window swap groups will have simultaneous hardware execution of the buffer swap. There is a similar mechanism for pfPipeWindows activated through pfChannel share groups sharing PFCHAN_SWAPBUFFERS_HW that is discussed in Chapter 2, “Setting Up the Display Environment”.

A window swap group is created by attaching windows with pfAttachWinSwapGroup(groupWin, attachee). There is no global list maintained for the swap group and their status so you cannot get back a list of windows in the group. However, pfWinInSwapGroup() returns 1 if the specified window as been synchronized to a swap group and 0 otherwise. This synchronization configuration will actually take place upon a call to pfSelectWin() for the window. Windows of separate screens can be attached but this also requires a BNC cable (of any Ohms) to be attached to the swap ready connectors of the graphics pipelines. Detach from swap groups is not supported. GLX barriers are used for multi-pipeline synchronization. If necessary, you can have a pfWindow explicitly join a specific barrier group with pfWinSwapBarrier(). When windows of multiple screens are attached, the video vertical retrace of those screens should also be syncrhonized with genlock(7).

Communicating with the Window System

You can communicate with a local or remote window server by means of a window system connection, a pfWSConnection (in X, also known as a Display connection). You can use your pfWSConnection for selecting X events for your window, as is demonstrated in Example 16-4.

libpr offers several utilities for creating a connection to a window server. A given connection can communicate with any screen managed by that window server so usually a process only needs one connection. A process should not share the connection of another process, so you will need a connection per process. Typically, there is exactly one window server on a machine but that is not required. libpr maintains a pfWSConnection for the current process. By default, this connection obeys the setting of the DISPLAY environment variable which can point to a window server on a local or a remote machine. The current connection can be requested with pfGetCurWSConnection() and can be set with pfSelectWSConnection(). Whenever possible, use this connection to limit the total number of open connections. pfOpenScreen() is a convenient mechanism for opening a connection with a specified default screen. pfOpenWSConnection() allows you to specify the exact name specifying the desired target for the connection. Both pfOpenScreen() and pfOpenWSConnection() allow you to specify if you would like the new connection to automatically be made the current libpr pfWSConnection; this is recommended.

More pfWindow Examples

Example 16-2 demonstrates the creation of a window with a default overlay window.

Example 16-2. Using the Default Overlay Window

int main (void)
{
    pfWindow *win, *over;
    /* Initialize Performer */
    pfInit();
    pfInitState(NULL);
 
    /* Initialize the window. */
     win = pfNewWin(NULL);
     pfWinOriginSize(win, 100, 100, 500, 500);
     pfWinName(win, “OpenGL Performer”);
     pfWinType(win, PFWIN_TYPE_X);
     pfWinMode(win, PFWIN_HAS_OVERLAY, 1);
     pfOpenWin(win);
     /* First select and draw into the overlay window */
     pfWinIndex(win, PFWIN_OVERLAY_WIN);
     /* Select causes the index to be applied */
     pfSelectWin(win); 
     ...
     /* Then select the main gfx window */
     pfWinIndex(win, PFWIN_GFX_WIN);
     pfSelectWin(win); 
     ...
}

Example 16-3 demonstrates creating a custom overlay window and is taken from the sample program /usr/share/Performer/src/pguide/libpr/C/winfbconfig.c for IRIX and Linux and %PFROOT%\Src\pguide\libpr\C\winfbconfig.c for Microsoft Windows.

Example 16-3. Creating a Custom Overlay Window

static int OverlayAttrs[] = {
 PFFB_LEVEL, 1, /* Level 1 indicates overlay visual */
 PFFB_BUFFER_SIZE, 8, 
 None,
};
 
int main (void)
{
    pfWindow *win, *over;
    /* Initialize Performer */
    pfInit();
    pfInitState(NULL);
 
    /* Initialize the window. */
     win = pfNewWin(NULL);
     pfWinOriginSize(win, 100, 100, 500, 500);
     pfWinName(win, “OpenGL Performer”);
     pfWinType(win, PFWIN_TYPE_X);
     pfWinMode(win, PFWIN_HAS_OVERLAY, 1);
 
     over = pfNewWin(NULL);
     pfWinName(over, “OpenGL Performer Overlay”);
     pfWinType(over, PFWIN_TYPE_X | PFWIN_TYPE_OVERLAY);
     /* See if we can get the desired overlay visual */
     if (!(pfChooseWinFBConfig(over, OverlayAttrs)))
         pfNotify(PFNFY_NOTICE, PFNFY_PRINT,
            “pfChooseWinFBConfig failed for OVERLAY win”);
     pfOpenWin(win);
     /* First select and draw into the overlay window */
     pfWinIndex(win, PFWIN_OVERLAY_WIN);
     /* Select causes the index to be applied */
     pfSelectWin(win); 
     ...
     /* Then select the main gfx window */
     pfWinIndex(win, PFWIN_GFX_WIN);
     pfSelectWin(win); 
     ...
}

Example 16-4 demonstrates the selection of X input events on a pfWindow. This example is taken from /usr/share/Performer/src/pguide/libpr/C/hlcube.c for IRIX and Linux and from %PFROOT%\Src\pguide\libpr\C\hlcube.c for Microsoft Windows.. See the /usr/share/Performer/src/pguide/libpf/C/complex.c sample program for a detailed example of using either standard or forked X input on pfWindows for IRIX and Linux and sample program %PFROOT%\Src\pguide\libpf\C\complex.c for Microsoft Windows.

Example 16-4. pfWindows and X Input

    pfWSConnection Dsp;
 
void main (void)
{
    pfWindow *win;
    pfWSWindow xwin;
 
    /* Initialize Performer */
    pfInit();
    pfInitState(NULL);
 
    /* Initialize the window. */
     win = pfNewWin(NULL);
     pfWinOriginSize(win, 100, 100, 500, 500);
     pfWinName(win, “OpenGL Performer”);
     pfWinType(win, PFWIN_TYPE_X);
     pfOpenWin(win);
     ...
     /* set up X input event handling on pfWindow */
     Dsp = pfGetCurWSConnection();
     xwin = pfGetWinWSWindow(win);
     XSelectInput(Dsp, xwin, KeyPressMask );
     XMapWindow(Dsp, xwin);
     XSync(Dsp,FALSE);
     ...
     do_events(win);
}
static void 
do_events(pfWindow *win)
{
    while (1) {
    while (XPending(dsp))
    {
         XEvent event;
         XNextEvent(Dsp, &event);
         switch (event.type) 
         {
         case KeyPress:
          ....
          }
     }
}