The only two things you know about "window management" by now are how to create a window (via
createWindow) and how to tell your program that you wish your window to be redisplayed (via
postRedisplay). As you may have guessed, there are a lot of other things that you can do with a window.
Before we start, it is important to keep two things in mind:
newtype Window = Window Int deriving (Eq,Ord)In other words, each window is associated to an identifier (
Int). The range of allocated identifiers starts at one. The function
IO Window, so it is always possible to keep the identifier of a created window using:
w1 <- createWindow ...
mouseFunc, etc.) are associated to a specific window. These procedures are part of the window context (let's think of this as being the collection of all window properties). Each created window has a unique associated context. State changes to a window's associated context can be done immediately after the window is created.
For example, you must declare a
keyboardFunc twice if you pretend to read keyboard inputs in two different windows. Notice that the function declared can be the same for both windows. Watch carefully the following code:
main :: IO () main = do GLUT.init Nothing w1 <- createWindow "Hello World" (return ()) [ Single, GLUT.Rgb ] (Just (WindowPosition 100 100)) (Just (WindowSize 300 250)) keyboardFunc (Just keyboard) mouseFunc (Just mouse1) w2 <- createWindow "Hello World, again!" (return ()) [ Single, GLUT.Rgb ] (Just (WindowPosition 100 100)) (Just (WindowSize 300 250)) keyboardFunc (Just keyboard) mouseFunc (Just mouse2) myInit mainLoop
In this example, two windows were created. When a keyboard key is pressed and the first window (
w1) is active, the function
keyboard is going to deal with this input. The same happens when the active window is
w2. But when a mouse button is pressed, the function that is going to deal with this input will be
mouse1, if the active window is
mouse2, if the active window is
w2. As you noticed, each window needs its own declaration of callback procedures, but the function indicated by them can be the same.
Once you understood this introduction, let's take a look on what you can do with windows:
createWindow :: String -> DisplayAction -> [DisplayMode] -> Maybe WindowPosition -> Maybe WindowSize -> IO Window
Usage and Comments: The current window is set to the newly created window. All you need to know about this function was already described in the Your First (and Simple) Program lesson. Check out it if you still have any doubts. Perhaps the only thing you don't know by now is the values of a
DisplayMode. They are:
createSubWindow :: Window -> WindowPosition -> WindowSize -> IO Window
createSubWindow (parent window) (WindowPosition posX posY) (WindowSize width height)
Comments: All values of
WindowSize are related to pixels. The current window is set to the newly created subwindow. Subwindows can be nested arbitrarily deep.
setWindow :: Window -> IO ()
setWindow (identifier of GLUT window to make the current window)
Comments: You can create a lot of windows first, then declare their callback procedures one by one, usging
setWindow to set the current window. Perhaps this will generate a "cleaner" code.
getWindow :: IO Window
Comments: If no windows exist or the previously current window was destroyed,
getWindow returns zero.
destroyWindow :: Window -> IO ()
destroyWindow (identifier of GLUT window to destroy)
Comments: Any subwindows of destroyed windows are also destroyed. If the
[identifier of GLUT window to destroy] was the current window, the current window becomes invalid (
getWindow will return zero).
postWindowRedisplay :: Window -> IO ()
postWindowRedisplay (identifier of GLUT window to redisplay)
postRedisplay function you already know forces the current window to be redisplayed.
swapBuffers :: IO ()
Comments: The contents of the back buffer of the layer in use of the current window to become the contents of the front buffer. The contents of the back buffer then become undefined. The update typically takes place during the vertical retrace of the monitor, rather than immediately after glutSwapBuffers is called. Subsequent HOpenGL commands can be issued immediately after calling
swapBuffers, but are not executed until the buffer exchange is completed. If the layer in use is not double buffered,
swapBuffers has no effect.
positionWindow :: WindowPosition -> IO ()
positionWindow (WindowPosition posX posY)
Comments: For top-level windows, the
posY parameters are pixel offsets from the screen origin. For subwindows, the
posY parameters are pixel offsets from the window's parent window origin. This function disables the full screen status of a window if previously enabled.
reshapeWindow :: WindowSize -> IO ()
reshapeWindow (WindowSize width height)
reshapeWindow requests a change in the size of the current window. Whether a reshape actually takes effect and, if so, the reshaped dimensions are reported to the program by a reshape callback (
reshapeFunc). This function disables the full screen status of a window if previously enabled.
fullScreen :: IO ()
fullScreen requests that the current window be made full screen. The exact semantics of what full screen means may vary by window system. The intent is to make the window as large as possible and disable any window decorations or borders added the window system. The window width and height are not guaranteed to be the same as the screen width and height, but that is the intent of making a window full screen. Subsequent
positionWindow requests on the window will disable the full screen status of the window. This funtion is defined to work only on top-level windows
popWindow :: IO () pushWindow :: IO ()
Comments: Both of these functions work on top-level windows and subwindows. The effect of pushing and popping windows does not take place immediately. Instead the push or pop is saved for execution upon return to the GLUT event loop. Subsequent push or pop requests on a window replace the previously saved request for that window. The effect of pushing and popping top-level windows is subject to the window system's policy for restacking windows.
hideWindow :: IO ()
hideWindow will hide the current window. The effect of showing, hiding, and iconifying windows does not take place immediately. Instead the requests are saved for execution upon return to the GLUT event loop. Subsequent show, hide, or iconification requests on a window replace the previously saved request for that window.
iconifyWindow :: IO ()
iconifyWindow will iconify a top-level window, but GLUT prohibits iconification of a subwindow. The effect of showing, hiding, and iconifying windows does not take place immediately. Instead the requests are saved for execution upon return to the GLUT event loop. Subsequent show, hide, or iconification requests on a window replace the previously saved request for that window.
showWindow :: IO ()
showWindow will show the current window (though it may still not be visible if obscured by other shown windows). The effect of showing, hiding, and iconifying windows does not take place immediately. Instead the requests are saved for execution upon return to the GLUT event loop. Subsequent show, hide, or iconification requests on a window replace the previously saved request for that window.
setWindowTitle :: String -> IO () setIconTitle :: String -> IO ()
setWindowTitle (ASCII character string for the window name to be set for the window) setIconTitle (ASCII character string for the icon name to be set for the window)
Comments: These routines should be called only when the current window is a top-level window. Upon creation of a top-level window, the window and icon names are determined by the name parameter to
createWindow. Once created,
setIconTitle can change the window and icon names respectively of top-level windows. Each call requests the window system change the title appropriately. Requests are not buffered or coalesced. The policy by which the window and icon name are displayed is window system dependent. Both function have the same effect under Windows 95/98/NT.
setCursor :: Cursor -> IO ()
setCursor (name of cursor image to change to)
Comments: The options for the
[name of cursor image to change to] are:
CursorRightArrow- arrow pointing up and to the right.
CursorLeftArrow- arrow pointing up and to the left.
CursorInfo- pointing hand.
CursorDestroy- skull & cross bones.
CursorHelp- question mark.
CursorCycle- arrows rotating in a circle.
CursorSpray- spray can.
CursorWait- wrist watch.
CursorText- insertion point cursor for text.
CursorCrosshair- simple cross-hair.
CursorUpDown- bi-directional pointing up & down.
CursorLeftRight- bi-directional pointing left & right.
CursorTopSide- arrow pointing to top side.
CursorBottomSide- arrow pointing to bottom side.
CursorLeftSide- arrow pointing to left side.
CursorRightSide- arrow pointing to right side.
CursorTopLeftCorner- arrow pointing to top-left corner.
CursorTopRightCorner- arrow pointing to top-right corner.
CursorBottomRightCorner- arrow pointing to bottom-left corner.
CursorBottomLeftCorner- arrow pointing to bottom-right corner.
CursorInherit- use parent's cursor.
CursorNone- invisible cursor.
CursorFullCrosshair- full-screen cross-hair cursor (if possible, otherwise
Comments (continuation): This function changes the cursor image of the current window. Each call requests the window system change the cursor appropriately. The cursor image when a window is created is
CursorInherit. The exact cursor images used are implementation dependent. The intent is for the image to convey the meaning of the cursor name. For a top-level window,
CursorInherit uses the default window system cursor.
warpPointer :: WindowPosition -> IO ()
warpPointer (WindowPosition posX posY)
Comments: This function warps the window system's pointer to a new location relative to the origin of the current window. The new location will be offset
posX pixels on the X axis and
posY pixels on the Y axis. These parameters may be negative. The warp is done immediately. If the pointer would be warped outside the screen's frame buffer region, the location will be clamped to the nearest screen edge.
createWindow is defined in module GLUT_Init. The other ones, in module GLUT_Window. Both of them are imported with you program already imports module GLUT.
I would like to thank The OpenGL Utility Toolkit (GLUT) Programming Interface API Version 3 Documentation, created by Mark J. Kilgard, Silicon Graphics, Inc. It was very important for the development of this lesson.