Chapter 3. Basic Window Program
Every Xlib program has a similar structure. This chapter shows asimple program that puts up a window and handles events in thatwindow. You can use this simple application as a template for yourown more complex applications.
This chapter presents a simple program that demonstrates thefundamentals of programming with the X library. All clientswill use the techniques described and demonstrated here.
The basic program presented in this chapter fulfills all therequirements for a basic application outlined near the endof Chapter 2, "X Concepts," and illustrates some of the mostimportant X concepts and programming issues. You should havereadChapter 2, “X Concepts” before proceeding.
The program will perform these operations:
-
Connect the client to an X server with XOpenDisplay(),and exit gracefully if the connection could not be made.
-
Get information about the physical screen, and use it tocalculate the desired size of the window.
-
Create a window with XCreateSimpleWindow().
-
Set standard properties for the window manager.
-
Select the types of events it needs to receive.
-
Load the font to be used for printing text.
-
Create a graphics context to control the action of drawing requests.
-
Display the window with XMapWindow().
-
Loop for events.
-
Respond to the Expose event resulting from mapping thewindow (and any otherExpose event that might come alonglater) by calling routines to draw text and graphics. If thewindow is too small to perform its intended function, it willdisplay an appropriate message.
-
Receive ConfigureNotify events, indicating that thewindow has been resized by the window manager. The new windowsize is provided in the event structure.
-
Keep handling events until a KeyPress or ButtonPressevent arrives, then close the display connection and exits.
The program does not perform the following operations, which arerequired of a robust X client:
-
Allow the user to specify command line options and read the resourcedatabase.
-
Handle colors.
For more information on these topics,see Chapter 7, “Color” and Chapter 14, “A Complete Application”
If you have the sample programs (see the Preface for how toget them) and a workstation that runs X, you can try out this programby compilingbasic/basicwin.c. See the description of howto compile X programs in“Compiling and Linking X Programs” in Chapter 2
The program just displays a window with some text and graphicsdrawn into it. Figure 3-1 shows the output of the program.The one useful thing it does is tell you the size and depth of thecurrent screen.
Without further ado, let's begin to look at the code.
As usual, the code is composed of a main program and several subroutines.The main does everything described at the start of this chapter exceptcreate the GC, load the font, and draw the text and graphics. Thesetasks are done in the draw_graphics, draw_text,get_GC, and load_font, routines, which are shown with the complete codein“Complete Code for basicwin” but not described fully until Chapter 6, “Drawing Graphics and Text”You can get the general idea of what they do just by looking at them, though.
In the following sections, the code is shown and described in small pieces.In some cases, the relevant declarations of variables are shown again ineach segment of the code as well as at the top of the program (where theywould normally appear). This has been done to increase clarity when showingthe individual pieces of the program.
Example 3-1 shows the include files and declarationsfrom basicwin.c.
Example 3-1. basicwin -- include files and declarations
Let's begin with the include files. The three include files<X11/Xlib.h>, <X11/Xutil.h>, and <X11/Xos.h>are needed in virtually all Xlib programs. The <X11/Xlib.h>file contains declarations of structure types used in Xlib functions.<X11/Xlib.h> in turn includes <X11/X.h>, which sets upmany defined constants. <X11/Xutil.h> contains more structuredefinitions and defined constants for certain groups of Xlib functions.Many of the structures and constant definitions from these includefiles are described in this manual with the functions in which theyare used. Structures and constants are also presented on many ofthe reference pages in Volume Two, if the routine on that page uses astructure or defined constant as an argument or return value.Appendix , Structure Reference, of Volume Two, provides an alphabeticallisting of structures; Appendix G,Symbol Reference, of Volume Two,provides the definitions of constants.
The final include file referenced in the Example 3-1 is<X11/Xos.h>, which attempts to make programs as portableas possible by including certain files depending on the operating systemfor which the program is being compiled.This include file is not standard and isnot absolutely necessary, but it is useful.
Now let's move on to all the strange new types that appear inExample 3-1. The Window, Display,Pixmap, XSizeHints, and XEvent types usedin this program are all defined in <X11/Xlib.h>. A briefdescription of each is given here, but you will need to see thecode that uses each variable to fully understand them.
These declarations are repeated in the sections of code below in whichthey are used to avoid the need to flip back and forth.
XOpenDisplay() connects an Xlib program to a server. Thecode shown in Example 3-2 that callsXOpenDisplay()will appear in all Xlib programs.
Example 3-2. basicwin -- connecting to the server
The display_name argument to XOpenDisplay() specifieswhich server to connect to. This may be any server on the networkand could be specified on the command line in a more complete applicationthan this one. (See“Resources and User Customizability” in Chapter 2 andChapter 13, “Managing User Preferences”for a discussion of how to process command line arguments anduser-specified default values in an X program.) Whendisplay_nameis not specified by the user, it should be set to NULL, whichcausesXOpenDisplay() to connect to the server specified in theUNIX environment DISPLAY variable. You can view the current contentsof the DISPLAY environment variable by using the UNIX command:
echo $DISPLAY |
It can be changed by typing:
setenv DISPLAY display_name (C Shell) |
or:
DISPLAY=display_name; export DISPLAY (Bourne Shell) |
You must be careful to set the DISPLAY variable when you login to aremote machine to make sure that when you execute X applicationsfrom that terminal, your output will be displayed on the screen fromwhich you typed the command.
Both the DISPLAY environment variable and the display_nameargument toXOpenDisplay() have the same format. The formatishost:server.screen, in which host refers to the nameof the machine running the server;server, the server numberon that machine; andscreen, the screen number on that server.[6]
The server number can be thought of as the number of the user ona particular host. Theserver number is always zero on asingle-user workstation and may be nonzero only if a single hosthas a separate keyboard, pointer, and display for more than one user,all connected by wires (not networks) to the central host. Systemsthat run multiple X servers are rare.
The .screen part is optional and only specifies which screenis returned by theDefaultScreen() macro (more on macros in aminute). You can still use any or all of the screens controlled bythe specified server. For example, Perseus:0.1 instructs theserver you are running the program on to connect to server0on the host called Perseus and that the default screen on thatserver for this program will be screen1.[7]
The XOpenDisplay() routine returns a pointer to a structure oftypeDisplay. If the connection is successful, the structurewill be filled with information about the server and each of its screens.If the attempt to create a connection fails,XOpenDisplay() returnsNULL. The code in Example 3-2 above checks to make surethis returned pointer is notNULL before proceeding. The messageprinted when the connection fails includes the text returned by theXDisplayName() function. This function returnsdisplay_nameor, if that is NULL, the UNIX environment DISPLAY variable.XDisplayName() is necessary, since without it, there would be no wayto tell the user to what server an attempt to connect was made.
The client might not succeed in connecting to a server for a number ofreasons. Most likely, thedisplay_name variable or DISPLAYenvironment variable does not specify a valid server that is connectedvia the network to the machine on which you are running the program.Or perhaps the network is out of order. Another possibility is thatthe server and client use different versions of the X protocol.X Version 11 programs are not compatible with X Version 10 andvice versa, so that if such a connection is attempted, an errormessage such as “protocol mismatch” should be printed, since theconnection will partially succeed. All releases of X Version 11,however,are compatible since they use the same protocol.
The connection will also fail if the host you are running the clienton is not on thehost access list of the server you are tryingto display on. The host access list is a simple permission mechanism.A server reads the list of hosts as it starts up and may be connectedonly to clients running on these hosts. There are commands to add andremove hosts from the access list, but these can be called only fromclients running on the host whose list is being changed. In all thesecases, the code shown in Example 3-2 will simply print the nameof the server to which the connection failed and no further information.
In R4, a simple authorization scheme has also been implemented. If theperson operating the server has turned authorization on, Xlib must knowa secret code in order to connect to that server. Xlib gets this codefrom a file, and the server puts it there to grant access.
If Example 3-2 executes successfully past opening the display,we can begin to set up variables for use in the rest of the program.The first of these is the global variablescreen_num, set tothe return value of the DefaultScreen() macro.screen_numwill be used throughout the program to indicate which screen on theserver our operations are to affect. It is important to use theDefaultScreen() macro rather than to hardcode0 as thescreen used by the client, because even without command line parsingin the client, this allows the user to set the default screen bysetting the.screen element of the DISPLAY environment variable.
The variable screen_num can actually be any integral value between0 and the value returned by (ScreenCount)(display) - 1),inclusive. TheScreenCount) macro returns the number of screenson the connected server. Since we only intend to use one of the screens,we can be satisfied with using the default screen.
We have just described all the macros used in the context of connectingwith a display. They all get their information from theDisplaystructure returned by XOpenDisplay(). But this is not the onlyuseful information we can get from theDisplay structure. Thereare numerous other macros that supply information about the characteristicsof the server and its screens. We will describe these macros where theycome in handy in this manual. The complete set of macros that access themembers of the Display structure is listed and describedin Appendix C, Macros, of Volume Two. They tell you whether the serversupports certain features like backing store and motion history buffers,the protocol version and release and the name of the server vendor, andmuch more. TheDisplay structure also provides information abouteach screen, such as the root window dimensions and the number of planes.
The macros are provided both for convenience and because theDisplay structure is intended to be opaque; clients shouldnot access its members directly. The reason for it being opaqueis that Xlib's authors want to retain the option to change themembers in the Display structure without making existingclients obsolete.
Most clients need to know the size of the screen so that the outputcan be tailored to look the same--or to look good--on any display.There are two ways to get this information: you can access membersof the Display structure to get information about the rootwindow or you can useXGetGeometry() or XGetWindowAttributes()to get the root window's dimensions. The first method, using the macrosfor accessing information from the Display structure, works onlyfor the root window but is more efficient. The second and third methods,reading the window geometry or attributes, work for any window.
To get the dimensions of a screen in pixels, you can use the macrosDisplayWidth() andDisplayHeight(). The macrosDisplayWidthMM() and DisplayHeightMM() return the screendimensions in millimeters. These four macros get their informationlocally from theDisplay structure, so they are fast andefficient. The ratio of width in millimeters to width in pixels givesyou a measurement of the spacing between pixels horizontally, and thesame process can be used to determine the vertical pixel spacing. Thiscan be important because when you draw a circle, it will look more likean ellipse on screens that do not have the same pixel spacing in bothdirections (usually inexpensive PC servers). You can tailor your drawingto compensate for this effect.
The second and third ways to get the geometry of a window are to useXGetGeometry() or to get all the window attributes usingXGetWindowAttributes(). The difference between these two routinesis thatXGetWindowAttributes() gets much more information andactually calls XGetGeometry() itself. These methods have thedisadvantage that they get information from the server, requiring around-trip request that is subject to network delays. We show thismethod here because, for any window other than the root window, thisis the only way to get window information.
The following code fragments demonstrate the three ways of gettingroot window information.basicwin uses the macros methodbecause, in this case, we need information about the root window,and this is the most efficient way to get it.
Example 3-3 shows the macros method; Example 3-4,the XGetGeometry() method; and Example 3-5,theXGetWindowAttributes() method.
Example 3-3. Code fragment for getting display dimensions -- using macros
Display *display; int screen_num; unsigned int display_width, display_height; . . /* Open display */ screen_num = DefaultScreen(display); . . /* Display size is a member of display structure */ display_width = DisplayWidth(display, screen_num); display_height = DisplayHeight(display, screen_num); |
Example 3-4. Another way to get window size -- using XGetGeometry()
Note that the root argument of XGetGeometry() returnsthe root window at the top of the hierarchy of the window beingqueried. This happens to be useless in this case, because it isthe root window we are querying!
Example 3-5. A third way to get window size -- using XGetWindowAttributes()
The next step is to create and place windows. Actually, a window'sposition relative to its parent is determined as the window iscreated, since these coordinates are specified as arguments tothe routine that creates the window.
The basicwin application has only one window. Creating thefirst window of an application is a special case, because thatwindow is a child of the root window and, therefore, is subjectto management by the window manager. An application can suggesta position for this window, but it is very likely to be ignored.Most window managers allow the user to position the window as itappears on the screen. So most simple applications create thefirst window with its position set to (0,0).Example 3-6 shows the simplest call to create a window.
In Chapter 14, “A Complete Application” we will show you a more complete approachthat processes command line arguments to get the position of thetop-level window. When the user specifies a position, there is atechnique for making sure that the window manager will honor the position.
Example 3-6. basicwin -- creating a window
The only new thing in Example 3-6 is the use of severalnew macros in the call to create a window.
Let's talk about the RootWindow() macro. Each screen hasits own root window. To create the first of your application'swindows on a particular screen, you use the root window on thatscreen as the parent. That window can then only be used on thatscreen. The ID of the root window on a particular screen isreturned by the RootWindow() macro. The first generationof windows on a screen (known as the top-level windows) should alwaysuse this macro to specify the parent.XCreateSimpleWindow()makes a new window given arguments for specifying it parent, size,position, border width, border pixel value, and background pixelvalue. All other attributes of the window are taken from the parent,in this case the root window. If we wanted to specify any or allthe attributes instead of inheriting them from the parent, we wouldhave to useXCreateWindow() instead of XCreateSimpleWindow().
Applications do not choose pixel values, they choose colors and arereturned pixel values by a routine they call that allocates colorsor they get pixel values from the display macrosBlackPixel()andWhitePixel().[8]
This example is a monochrome application, but it will work onboth monochrome and color screens. We use theWhitePixel()macro to specify the background pixel value (in the call to createthe window) and set the foreground in the GC to be the contrastingvalue returned by BlackPixel(). The border pixel value isalso set toBlackPixel(). The background and border pixelvalues are set with the last two arguments ofXCreateSimpleWindow().The foreground pixel value is set in the get_GC routine inthe manner described in“Creating and Setting a Graphics Context” in Chapter 5
As you may recall from Chapter 2, “X Concepts” pixel values representcolors, but they will be translated by a colormap before beingdisplayed on the screen.BlackPixel() and WhitePixel()return the pixel values corresponding to two contrasting colors inthe default colormap, which might not actually be black and white.
Every application should be made to work in monochrome, because manypeople have only monochrome screens.
How to add color handling to basicwin (or any application)is described inChapter 7, “Color”
An application should create an icon design for itself, so that if awindow manager is running and the user iconifies the application, theicon will be recognizable as belonging to the particular application.Exactly how to tell the window manager about this pixmap will be describedin the next section, but first let's talk about how to create the pixmap.
The program should take two steps in creating the pixmap: it shouldfind out what sizes of icon are acceptable to the window manager andthen create a pixmap of an appropriate size. Since most currentwindow managers do not specify icon sizes, and it is difficult toknow how to respond in a reasonable way, this issue can be ignoredfor the present. Eventually, when standard window managers specifystandard icon sizes, applications would useXGetIconSizes()to determine which window manager was in operation and have a iconbitmap for each one.
Example 3-7 shows the simple process of creating a pixmap for the icon.
Example 3-7. basicwin -- creating an icon pixmap
#include "bitmaps/icon_bitmap" void main(argc, argv) int argc; char **argv; { /* Other declarations */ . . . Pixmap icon_pixmap; /* Open display, create window, etc. */ /* Might someday want to use XGetIconSizes to get the icon * sizes specified by the window manager in order to determine * which of several icon bitmap files to use, but only when * some standard window managers set these */ /* Create pixmap of depth 1 (bitmap) for icon */ icon_pixmap = XCreateBitmapFromData(display, win, icon_bitmap_bits, icon_bitmap_width, icon_bitmap_height); . . . |
An icon design can be created using the standard X applicationbitmap. You runbitmap with a filename and dimensionsas command line arguments, like so:
% bitmap icon_bitmap 40x40 |
Then you use the pointer to draw your bitmap. For more information onthe bitmap editor, see Volume Three. Normally the icon carries somesymbolic representation of the application, so use your imagination.bitmap creates an ASCII file that looks like Example 3-8.This particular bitmap is a bit small for an icon, being only 20 pixelson a side. A more typical size would be about 40 pixels on a side.
Example 3-8. Format of bitmap files
#define icon_bitmap_width 20 #define icon_bitmap_height 20 static char icon_bitmap_bits[] = { 0x60, 0x00, 0x01, 0xb0, 0x00, 0x07, 0x0c, 0x03, 0x00, 0x04, 0x04, 0x00, 0xc2, 0x18, 0x00, 0x03, 0x30, 0x00, 0x01, 0x60, 0x00, 0xf1, 0xdf, 0x00, 0xc1, 0xf0, 0x01, 0x82, 0x01, 0x00, 0x02, 0x03, 0x00, 0x02, 0x0c, 0x00, 0x02, 0x38, 0x00, 0x04, 0x60, 0x00, 0x04, 0xe0, 0x00, 0x04, 0x38, 0x00, 0x84, 0x06, 0x00, 0x14, 0x14, 0x00, 0x0c, 0x34, 0x00, 0x00, 0x00, 0x00}; |
The bitmap format shown in Example 3-8 is not used only inXCreateBitmapFromData(). It is also used by the Xlib functionsXWriteBitmapFile() andXReadBitmapFile(). An applicationcan also read from a file the data used to create a pixmap, instead ofincluding the data, but this is more complicated because it requiresprocessing of filenames.
Before mapping the window (which displays it on the screen), anapplication must set the standard properties to tell the windowmanager at least a few essential things about the application.
You may remember from Chapter 2, “X Concepts” that a property isa collection of information that is readable and writable by anyclient and is usually used to communicate between clients. Thestandard properties are part of the convention for communicationbetween each application and the window manager.
You may also remember that a property is associated with a particularwindow. The standard properties are associated with the top-levelwindow of the application. This is how the server keeps track ofthe standard properties of all the different applications and hasthem ready for the window manager to read them.
Several routines are provided that allow the application to easilyset these properties; analogous routines allow the window managerto read them. The routine designed to set all the most importantproperties for a normal application isXSetWMProperties().
The document describing the standardfor communication between the application and the window manageris called theInter-Client Communication Conventions Manual;it is reprintedin Appendix L,Interclient Communcation Conventions,of Volume Zero.More information on the conventions can be foundinChapter 12, “Interclient Communication” of this manual.
The minimum set of properties that an application must set are:
-
Window name
-
Icon name
-
Icon pixmap
-
Command name and arguments (the command line)
-
Number of arguments
-
Preferred window sizes
-
Keyboard focus model
We'll say more about each of these after you have seen the code thatsets them. Example 3-9 shows the code that sets the standardproperties.
Example 3-9. basicwin -- setting standard properties
It is important to realize that these properties are only hints.A hint is information that might or might not be used. Theremay be no window manager running, or the window manager mayignore some or all of the hints. Therefore, an applicationshould not depend on anything having been done with theinformation provided in the standard properties. For example,take the window name hint. Some window managers will use thisinformation to display a titlebar above or beside each top-levelwindow, showing the application's name. The proper and obviousthing for the application to do would be to set the window nameto be the application's name. But if the application were aneditor, it could try to set its window name to the name of thecurrent file. This plan would fall through if no window managerwere running.
The icon name and icon pixmap should both set to allow the windowmanager to use either or both. Most current window managers oftendisplay just the icon pixmap, unless no pixmap is specified, inwhich case they use the icon name. If the icon name is not set,the convention within window managers is to use the window nameas the icon name; if the window name is not specified either,then they will use the first element of the command line.
The UNIX shell command name and arguments are passed intomainin the standard fashion from the command line, asargv andargc. These can be used directly as arguments in the callto set the standard properties. This information might be used bythe session manager to restart or duplicate the application when soinstructed by the user.
And last but not least, the window size hints propertyis a structure that specifies the sizes, positions, and aspectratios preferred by the user or the program for this application.The XSizeHints structure is shown in Example 3-10.
Example 3-10. The XSizeHints structure
You might ask, “How would the user be involved in specifying thesize hints when they have to be set even before a window appears?”The answer: applications can be written to let the user specifythe position and size of the top-level window through command linearguments or the resource database.A more complete application would get these values, use them toset the size of the window.To tell the window manager that the user, not theapplication, supplied these values, the application would set theflags field toUSSize | USPosition instead ofPSize | PPosition.
All this arranges a priority for the different settings of theposition and size of a top-level window. The lowest priorityis the application itself. Next higher is the window manager,and highest of all is the user. In Example 3-9, thesymbols used to setflags are PSize andPMinSize. These indicate that the program is specifyingits desired size and its minimum useful size. The symbols used forother members ofXSizeHints are shown on the reference pagefor XSetWMProperties() in Volume Two.
Let's describe the other members ofXSizeHints. Thex, y, width, and height membersare simply the desired position and size for the window.In R4 and later, these fields should not be set.
The rest of the size hints give the window manager informationabout how to resize the window. Themin_height andmin_width fields should be set to the minimum dimensions(in pixels) required so that the application can still functionnormally. Many window managers will not allow the user to resizethe window smaller thanmin_width and min_height.max_width and max_height are analogous tomin_width andmin_height but are less criticalfor most applications.
In R4, the base_width and base_height fieldshave been added to the XSizeHints structure. They areused with thewidth_inc and height_inc fieldsto indicate to the window manager that it should resize thewindow in steps--in units of a certain number of pixelsinstead of single pixels. The window manager resizes thewindow to any multiple ofwidth_inc in width andheight_inc in height, but no smaller thanmin_widthand min_height and no bigger than max_width andmax_height. If you think about it,min_width andmin_height and base_width and base_heighthave basically the same purpose. Therefore,base_width andbase_height take priority overmin_width and min_height, so only one of these pairsshould be set.
Thexterm application provides a good example of size increments.It wants its window to be resized in multiples of the font width andheight, since it uses only constant-width fonts. This way, there areno partial characters along the edges of the window. What's more, theapplication can then interpret dimensions specified by the user inmultiples of width_inc and height_inc, instead of pixels.The user specifies dimensions in characters (24 by 80 for a standardsize terminal), which the application then translates into pixels bymultiplying them bywidth_inc and height_inc. Mostwindow managers display the dimensions of the window when the user isresizing it, and ifwidth_inc and height_inc are set,they will use multiples instead of pixels as units.
In R4, the win_gravity field has also been added totheXSizeHints structure. This field suggests to thewindow manager how the window should be placed when mapped or,more accurately, how the position for the window specified bythe user should be interpreted. Normally, when the userspecifies a position, either by clicking a pointer button toposition a window or through command line arguments, the windowmanager places the top-left corner of the application's top-levelwindow at that point. Thewin_gravity field requests thewindow manager to place a different part of the window at thatpoint. The values of this field areCenter, East,North, NorthEast, NorthWest,South,SouthEast, SouthWest, and West. Theserefer to a corner or edge of the window that should be placed atthe specified point. As mentioned, the default isNorthWest,which positions the top-left corner of the window at the specifiedpoint. Few applications need to use this feature.
The next step is to select the event types the application will require.Our simple program must receive events for three reasons: to redrawitself in case of exposure, to recalculate its contents when it isresized, and to receive a button or key press indicating that theuser is finished with the application.
The program must select these types of events specifically since,by default, it will not receive the kinds of input it needs.Example 3-11 shows the line of code that selects events.
Example 3-11. basicwin -- selecting desired event types
/* Select event types wanted */ XSelectInput(display, win, ExposureMask | KeyPressMask | ButtonPressMask | StructureNotifyMask); |
The crucial argument of XSelectInput() is the event mask.Each symbol used here selects one of more event types. Theevent mask constants are combined with a bitwise OR since theyare really setting bits in a single argument.
ExposureMask selects Expose events, which occurwhen the window is first displayed and whenever it becomes visibleafter being obscured.Expose events signal that theapplication should redraw itself.
X provides separate events for depressing and releasing bothkeyboard keys and pointer buttons and separate symbols forselecting each of these types of events.KeyPressMaskselects only KeyPress events, and ButtonPressMaskselects onlyButtonPress events. ButtonReleaseand KeyRelease events can also be selected withButtonReleaseMask andKeyReleaseMask, but theyare not needed in this application.
StructureNotifyMask selects a number of event types,specificallyCirculateNotify, ConfigureNotify,DestroyNotify, GravityNotify,MapNotify,ReparentNotify, and UnmapNotify. The only oneof these we need for our application isConfigureNotify,which informs the application of its window's new size when ithas been resized. However, there is no way to select just thisone event type. We could get away without selecting this eventtype, but any real application would use it because it allowsan increase in performance. Without this event type, on everyExpose event the application would have to useXGetGeometry() to find out its current size. This is arequest that requires a reply from the server and therefore issubject to network delays.
The rest of the event types selected by StructureNotifyMaskare described inChapter 8, “Events”
XSelectInput() actually sets theevent_mask attributeof the window. If you create the window with XCreateWindow()(as opposed toXCreateSimpleWindow()), you can select eventsat the same time by setting theevent_mask attribute in thelast two arguments of the call. This is slightly more efficientthan callingXSelectInput() separately. You can also setthis attribute through XChangeWindowAttributes() if, forsome other reason, you need to call this function anyway.
The next step in the application is to create any other serverresources that are needed. Server resources are collectionsof information managed by the server and referred to in theapplication by an ID number.Items with the types Colormap, Cursor, Font,GC, Pixmap, and Window are server resources.They should be created once and the ID kept rather than creatingand deleting them in frequently called subroutines. That is whythey are normally created inmain or in a subroutine calledonly once from main.
In this program, we have already created two resources: a windowand the icon pixmap. We still need to load a font for the textand to create a graphics context to draw both text and graphicsinto the window. These operations are done in the routinesload_font and get_GC, called just before mappingthe window. We are going to delay describing these routines untilChapters 5,The Graphics Context and 6, Drawing Graphics and Text, in order to keep this chapter tomanageable proportions. However, the complete code forbasicwin,including these functions, is listed at the end of this chapter, incase you want a sneak preview.
Finally we are ready to display the window. Note that we have doneall that preparation before mapping the window for good reason. Thewindow manager hints must be set so that the window manager can handlethe mapping properly, and events must be selected so that the firstExpose will arrive and tell the application to draw into its window.
Example 3-12 shows the code that maps the window.
You may remember from Chapter 2, “X Concepts” that in order for awindow to be visible, it must meet five conditions. These are soimportant that they bear repeating:
-
The window must be mapped withXMapWindow() or related routines.
-
All its ancestors must be mapped. This condition is always satisfiedfor the children of the root window, the top-level windows of eachapplication.
-
The window must not be obscured by visible sibling windows or theirancestors--this depends on the stacking order. When first mapped,a window appears on top of its siblings, which will be on top of allwindows if its parent is the root window.
-
The request buffer must be flushed. This topic will be described inthe next section.
-
The initial mapping of a top-level window is a special case, sincethe window's visibility may be delayed by the window manager. Forcomplicated reasons, an application must wait for the firstExpose event before assuming that its window is visibleand drawing into it.
XMapWindow() causes an X protocol request that instructs theserver to display the window on the screen. Like all other X protocolrequests, this one is queued until an event-reading routine such asXNextEvent(), a routine that queries the server (most routineswhose names containFetch, Get, or Query), ora routine such as XFlush() orXSync() is called. Theserver operates more efficiently over the network when X protocolrequests are sent in groups.
The XNextEvent() call performs the flushing frequently enoughin applications that take user input. The routines that query theserver should be called as infrequently as possible because they reduceperformance over the network. TheXFlush() command instructsthe server to process all queued output requests right away.XFlush() is generally necessary only when an application needsto draw periodically even without user input.
X programs are event-driven, which means that after setting up all theserver resources and window manager hints as described up to this point,the program performs all further actions only in response to events.The event-gathering loop is the standard way to respond to events,performing the appropriate action depending on the type of eventand the information contained in the event structure.
The event loop is normally a closed loop, in which one of the eventtypes with certain contents defined by the application indicates thatthe user wants to exit. In some existing applications such as xclock,the loop is completely closed, and therefore the only way to terminate theprogram is to find the process ID from the shell and kill it or use thewindow or session manager, but this can be inconvenient.
The choice of which events are received by the application was madeearlier when the application selected input or set theevent_maskattribute. The event loop must make sure to properly handle every eventtype selected. One of the most common debugging problems is for there tobe a difference between the events handled and those selected.
Have a look at the code in Example 3-13, beforewe describe it in more specific terms.
Example 3-13. basicwin -- processing events
Example 3-13 is framed by an infinite while loop. Justinside the top of the loop is theXNextEvent() statement,which gets an event structure from the queue Xlib maintainsfor the application and puts the pointer to it in the variablereport. You might assume that the event loop couldhave been written:
while (XNextEvent(display, &event)) { . . . } |
but this is not the case. XNextEvent() returns void; itonly returns when there is an event to return. Errors are handledthrough a separate error-handling mechanism, not through the returnedvalue. So it is necessary to write the event loop:
while (1) { XNextEvent(display, &event); . . . } |
Right after XNextEvent() is a switch statement that branchesdepending on the event type. There is one case for each of thefour types of events:ButtonPress, ConfigureNotify,Expose, and KeyPress.
The ConfigureNotify branch, in all applications, willcalculate the values of variables based on the new window size.These variable values will then be used to calculate where todraw things in theExpose branch the next time anExpose event occurs. A ConfigureNotify eventis always followed by one or moreExpose events.
Expose events occur when a window becomes visible on thescreen, after being obscured or unmapped. They occur because theX Window System does not normally save the contents of regions ofwindows obscured by other windows or not mapped. The contents ofwindows need to be redrawn when they are exposed.
The code forExpose events draws or redraws the contentsof the application's window. This code will be reached when thewindow is first mapped, and whenever a portion of the windowbecomes visible.
An application can respond to Expose events by refreshingonly the parts of the window exposed, or by refreshing the entirewindow. The former is possible because the event structure foreachExpose event carries the position and dimensions ofa single rectangular exposed area, as shown in Example 3-14.
Example 3-14. The XExposeEvent structure
typedef struct { int type; unsigned long serial;/* # of last request processed by server */ Bool send_event; /* True if this came from SendEvent request */ Display *display; /* Display the event was read from */ Window window; int x, y; int width, height; int count; /* If nonzero, at least this many more */ } XExposeEvent; |
Several Expose events can occur because of a single windowmanager operation, as shown in Figure 3-2. If windowEwere raised, four Expose events would be sent to it. Theheight andwidth members in each event structurewould correspond to the dimensions of the area where each of thewindows overlapped windowE, and the x and ymembers would specify the upper-left corner of each area relativeto the origin of windowE. All the Expose eventsgenerated by a single action are guaranteed to be contiguous inthe event queue.
Whether an application should draw the whole window or just theexposed parts depends on the complexity of the drawing in thewindow. If all of the window contents are simple for both theapplication and the server to draw, the entire window contentscan be redrawn without a performance problem. This approach workswell as long as the window is only redrawn once, even if multipleExpose events occur because of a single user action. Onetrick is to monitor thecount member of the Exposeevent structure and ignore the Expose events (do not redrawthe window) until this member is0. It might seem an evenbetter method to search the entire queue, removing allExposeevents that occurred on the window, before redrawing. But this isillegal because there may be interveningConfigureNotify eventsin the queue, and responding to an Expose event that followsaConfigureNotify event too early will result in redrawingthe wrong area or not redrawing at the right time. OnlycontiguousExpose events can be skipped.
On the other hand, if a window has any elements that can betime consuming for either the application or the server toredraw, then the application should only redraw the time-consumingelements if they are actually within the exposed areas.
The issue here is redrawing time, which has two components underthe application's control: the time the application takes toprocess the redrawing instructions, and the time it takes forthe server to actually do the redrawing. On most servers, a usermust wait for the server to complete drawing before he or she canmove the pointer or go on to other actions. Therefore, the timetaken by the server is critical, since it translates directlyinto waiting by the user. Since the system running X clients isnormally multitasking, the time taken by the application to minimizeredrawing is not as important, since the user can still do work.
There are two approaches to assisting the server in redrawing exposedregions quickly. One is to avoid redrawing items in regions that have notbeen exposed. Doing this in an application requires identifying any itemsto be drawn that do not extend into any of the exposed areas and eliminatingthese drawing requests. There are a set of routines that perform intersectingcalculations on regions that may help you implement this.
The second approach is to set the clip mask in the GC to draw only inthe exposed areas. This second approach is much simpler in code, but itdelegates the job of eliminating unnecessary drawing to the server. Manyservers may not do this elimination, because there is again a tradeoffbetween the time saved in eliminating requests and the time spent incalculating which requests to eliminate.
If you are now confused and wondering which redrawing approachto take in your application, the general rules should be as follows:
-
If the window is fast to draw, the whole window can be drawn inresponse to the lastExpose event in a contiguous series;this means drawing only when count is zero. The definitionoffast will vary from server to server, but anything thatuses the more complex features of the GC, such as wide lines orjoin styles, or that may have lots of drawing requests shouldprobably be considered slow.
-
For windows that are slow to draw, the application should avoiddrawing areas that were not exposed. If the application can figureout which slow drawing requests would draw only into areas that werenot exposed and these calculations are not time consuming in themselves,then it should eliminate these requests.
-
For windows that are slow to draw, the second best approach is to seta clip mask to allow the server to eliminate unnecessary requests.(This will work only if the server has been designed to do so.) Theapplication can combine all the areas in a contiguous series of exposeevents into a single clip mask and set this clip mask into the GC.The code for this is only slightly more complex than the approachfor the window that is fast to draw.
Since the image used by the basicwin application is simple,the application can redraw the entire window upon receiving the lastcontiguousExpose event with little performance penalty. Butwe will also show you the other approach, as if the window were morecomplex. Example 3-13 shows the first method from the listabove, and Example 3-15 shows the third method.
The second method in the list above is not shown here because itis hard to demonstrate in a way that is transferable to otherapplications. We will just describe it in a little more detailinstead. Let's say that we are writing a spreadsheet applicationand designing the exposure event handling. In the spreadsheet, itwould be easy to determine which cells were affected by the exposure,because the cells are arranged along horizontal rows and in columns.Upon getting anExpose event, the application could easilydetermine which cells overlapped the exposed area and then redrawonly those. The same could not be said of a painting program, inwhich some drawing primitives could be diagonal or drawn with weirdline styles. It would be very hard to determine whether a particularprimitive drawn in the painting program intersects with an exposedregion. In general, any application that draws most or all of itsgraphics horizontally or vertically can benefit from this technique.One example of an application written this way is xterm, andyou can look at the code for that if you can get it.xtermredraws only the characters that are in exposed areas.
Example 3-15 shows a technique that could be used for morecomplicated windows. It creates a singleRegion composedof the union of the rectangles in all the Expose events.Regions are described fully inChapter 6, “Drawing Graphics and Text” but youshould be able to understand this example anyway.
Example 3-15. Handling Expose events for complex window contents
Being able to redraw the contents of its windows is importantfor most applications, but for a few applications, it might bevery difficult or impossible. There is another method thatmight be used in such a situation. The application could drawinto a pixmap and then copy the pixmap to the window each timethe window needs redrawing. That way the complete windowcontents would always be available for redrawing the window onExpose events. The disadvantage of this approach isthat the server might not have sufficient memory to store manypixmaps in memory (especially on color displays) or it might beslow about copying the pixmap into the window. But this would bea logical way to handle exposure in an application that performsdouble-buffering.[9]On high performance graphics workstations, a feature known asa backing store might also be available to assist in redrawingwindows. When available, this feature can be turned on for anywindow that really requires it. With the backing store on,the server can maintain the contents of the window when it isobscured and even when it is unmapped and capture drawing tothe window while it is in one of these states. The one situationthat the backing store cannot fully take care of is resizing thewindow. This is because it is assumed that most applications needto recalculate the dimensions of their contents to fit a new windowsize. The application can set an attribute called bit gravity toretain part of the window during a resize, but part of the windowis still going to need redrawing if the window is resized larger.
In case you might be wondering, we have intentionally not describedthe draw_text anddraw_graphics routines here. Theyare described in Sections 6.2.7 and 6.1.3. But if youare still curious, they are included in the listing ofbasicwinat the end of this chapter.
There is often confusion about when an application is permittedto draw into its windows. You might think it would work to drawimmediately after theXMapWindow() request that displays awindow on the screen. But that will not work with most stylesof window manager. The rule is that no drawing is allowed until thefirstExpose event arrives.
The reason involves a feature of X called substructure redirection,introduced in“The Window Manager” in Chapter 2 and described more fullyin“Substructure Redirection” in Chapter 16
Another characteristic of X that often confuses newcomers isthe fact that graphics drawn may not appear on the screenimmediately. It is easy to write a program that properlyperforms a number of drawing calls but that never makesanything appear on the screen. This is a side effect of thefact that X is designed to buffer communications over a network,as was described in theoretical terms in“Buffering” in Chapter 2We will describe it in more practical terms here.
What happens is that the requests (to create windows, to mapwindows, or to draw into them) are queued up in Xlib, waitingfor something to happen that requires an immediate communicationwith the server. Xlib will not send requests of any kind to theserver until such an occurrence. The requests are saved up as apacket so they can be sent over the network more efficiently.
The queue of requests waiting to be sent to the server is calledthe request buffer. The requests are accumulated in therequest buffer until a call to:
Actually, a routine in number 2 above that gets events triggers acommunication with the server only if there is no event on Xlib'sevent queue that matches what the routine is looking for. Only ifthe routines are waiting for an event do they trigger the exchange.Any of these actions is said to flush the request buffer,which means that all requests up to this point will be acted onby the server. Novice programmers who neglect to call one ofthese routines will notice that their drawing requests have notbeen honored. They do not realize that perhapsnone oftheir X requests that require communication with the serverhave been honored.
But does it really take a lot of care to make sure that therequest buffer gets flushed? Not usually. Since X programs areevent-driven, they often call routines that get events. If anapplication handles event types that occur frequently, such aspointer or keyboard events, there is nothing to worry about. Ifthe application needs to get information from the server by makinga call containing the wordFetch, Get, or Query,no problem is likely. On the other hand, an output-only applicationthat handles onlyExpose events would certainly need to callXFlush() once in a while to make sure that its drawing washonored in a timely fashion.
The ConfigureNotify event tells the application that thewindow was resized. In this program, we pass this information tothe routines that draw, so that they can position things properly.We also see if the new size is less than the minimum useful sizethat we set as a size hint for the window manager. If it is smallerin either dimension, then we set the flagwindow_size so thatthe next time an Expose event arrives, we display the message“Too Small” instead of the usual text.
Example 3-16 shows the code that handles theConfigureNotify event.
Example 3-16. basicwin -- the ConfigureNotify event
. . . case ConfigureNotify: /* Window has been resized; change width and height to * send to place_text and place_graphics in next Expose */ width = report.xconfigure.width; height = report.xconfigure.height; if ((width < size_hints->min_width) || (height < size_hints->min_height)) window_size = TOO_SMALL; else window_size = BIG_ENOUGH; break; . . . |
Note that when the window is first mapped, the ConfigureNotifyevent appears on the queuebefore the first Exposeevent. This means that the code works even if the window managermodifies the window's size before allowing it to be displayed.The initialConfigureNotify updates the application'sknowledge of the window size, and the followingExposeevent allows the application to draw the window's contents.
If we had not selectedConfigureNotify events, thecode for Expose would have to be modified to check thedimensions in the firstExpose event, so that it knewthe correct window size. It would also have to query the serverfor the window size in response to subsequentExposeevents, because these events describe only the exposed area,not the entire window.
This program uses a key or button press to exit. This is not avery demanding use ofKeyPress and ButtonPressevents. For a description of how to use keyboard and pointerevents for more advanced purposes, seeChapter 9, “The Keyboard and Pointer”
To cleanly exit, a client should free all the memory it has allocated,particularly X resources, and then close the display connection withXCloseDisplay(). Example 3-17 shows the code thatperforms these functions inbasicwin.
Example 3-17. Closing the display connection and freeing resources
case ButtonPress: /* Trickle down into KeyPress (no break) */ case KeyPress: XUnloadFont(display, font_info->fid); XFreeGC(display, gc); XCloseDisplay(display); exit(1); |
It is good practice to use XCloseDisplay() even thoughthe connection to the server is closed automatically when aprocess exits. Otherwise, pending errors might not be reported.
Although there does not appear to be much in the way oferror-handling code in this example, the question of errorhandling has been fully considered:
-
On the XOpenDisplay() call, we check for the error return,tell the user what server the attempt was made to connect to,and exit gracefully.
-
For all other errors, we depend on the default error-handlingmechanisms. These errors might be a protocol errors caused bya programming error (all of which we hope to eliminate), a protocolerror caused by the server running out of memory (the chance ofwhich we cannot eliminate), or an IO error such as losing theconnection with the server due to network failure. For protocolerrors, the client gets an error event from the server, and Xlibinvokes an error handler function. The client is free to provideits own error handler to replace the default handler, which printsan informative message and exits. For IO errors, there is a separateerror handler function, which can be separately replaced by theapplication. But for this example, we have simply relied on thedefault handlers.
It is important to note that not all protocol errors cause theerror handler to be invoked, though this fact does not showitself inbasicwin. Some errors, such as failure to opena font, are indicated by returned values of typeStatuson the appropriate routine (in this case, XLoadFont()).The returned values are zero on failure and nonzero on success.In general, any routine that returnsStatus will needits return value tested, because it will have bypassed theerror-handling mechanism.
The basic steps that were taken in this program are as follows:
-
Open connection to server.
-
Make sure connection succeeded, print error and exit if not.
-
Get display dimensions.
-
Calculate desired size of window and create window.
-
Create pixmap for icon.
-
Initialize XSizeHint structure.
-
Set standard properties for window manager.
-
Select desired event types.
-
Map window.
-
Set up event gathering loop.
-
If event is of type Expose, draw contents of window.
-
If event is of type ConfigureNotify, recalculatedimensions of window.
-
If event is ButtonPress or KeyPress, closethe display and exit.
The order of these steps is important up to the point where thewindow is mapped. Within the event loop, the order of eventscannot be completely predicted.
Now look at the complete code for basicwin and make sureyou understand everything. Note that thedraw_graphics,draw_text, get_GC, and load_font routineshave not yet been described but will be covered in later chapters.
Example 3-18. basicwin -- in its entirety
[6]MIT's manual describes this format as host:display.screen,usingdisplay instead of server. Since most people thinkof screens and displays as virtually the same thing, their descriptionleads to confusion. The second member in the string really identifieswhich server on a particular host to connect to. Each of these serverswould support a user.
[7]Note that most servers only control a single screen. However,an X server can support multiple screens. The most common example isprobably the Apple MacX server for the Macintosh.
[8]BlackPixel() and WhitePixel() are no longer constantsas they were in X Version 10. Pixel values must not be hardcoded.
[9]Double-buffering is an animation technique that hides the drawingprocess from the viewer. In one implementation, a pixmap is drawn intoand then copied to a window when the image is complete. Anothertechnique called overlays is described in Chapter 7.
可以使用的代码:
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xos.h>
#include <X11/Xatom.h>
#include <stdio.h>
#include <stdlib.h>
#define icon_bitmap_width 20
#define icon_bitmap_height 20
static char icon_bitmap_bits[] = {
0x60, 0x00, 0x01, 0xb0, 0x00, 0x07, 0x0c, 0x03, 0x00, 0x04, 0x04, 0x00,
0xc2, 0x18, 0x00, 0x03, 0x30, 0x00, 0x01, 0x60, 0x00, 0xf1, 0xdf, 0x00,
0xc1, 0xf0, 0x01, 0x82, 0x01, 0x00, 0x02, 0x03, 0x00, 0x02, 0x0c, 0x00,
0x02, 0x38, 0x00, 0x04, 0x60, 0x00, 0x04, 0xe0, 0x00, 0x04, 0x38, 0x00,
0x84, 0x06, 0x00, 0x14, 0x14, 0x00, 0x0c, 0x34, 0x00, 0x00, 0x00, 0x00};
//#include "bitmaps/icon_bitmap"
#define BITMAPDEPTH 1
#define TOO_SMALL 0
#define BIG_ENOUGH 1
/* These are used as arguments to nearly every Xlib routine, so it
* saves routine arguments to declare them global; if there were
* additional source files, they would be declared extern there */
Display *display;
int screen_num;
/* progname is the string by which this program was invoked; this
* is global because it is needed in most application functions */
static char *progname;
void main(argc, argv)
int argc;
char **argv;
{
Window win;
unsigned int width, height; /* Window size */
int x, y; /* Window position */
unsigned int border_width = 4; /* Four pixels */
unsigned int display_width, display_height;
unsigned int icon_width, icon_height;
char *window_name = "Basic Window Program";
char *icon_name = "basicwin";
Pixmap icon_pixmap;
XSizeHints *size_hints;
XIconSize *size_list;
XWMHints *wm_hints;
XClassHint *class_hints;
XTextProperty windowName, iconName;
int count;
XEvent report;
GC gc;
XFontStruct *font_info;
char *display_name = NULL;
int window_size = 0; /* BIG_ENOUGH or TOO_SMALL to
* display contents */
progname = argv[0];
if (!(size_hints = XAllocSizeHints())) {
fprintf(stderr, "%s: failure allocating memory", progname);
exit(0);
}
if (!(wm_hints = XAllocWMHints())) {
fprintf(stderr, "%s: failure allocating memory", progname);
exit(0);
}
if (!(class_hints = XAllocClassHint())) {
fprintf(stderr, "%s: failure allocating memory", progname);
exit(0);
}
/* Connect to X server */
if ( (display=XOpenDisplay(display_name)) == NULL )
{
(void) fprintf( stderr, "%s: cannot connect to X server %s\n",
progname, XDisplayName(display_name));
exit( -1 );
}
/* Get screen size from display structure macro */
screen_num = DefaultScreen(display);
display_width = DisplayWidth(display, screen_num);
display_height = DisplayHeight(display, screen_num);
/* Note that in a real application, x and y would default
* to 0 but would be settable from the command line or
* resource database */
x = y = 0;
/* Size window with enough room for text */
width = display_width/3, height = display_height/4;
/* Create opaque window */
win = XCreateSimpleWindow(display, RootWindow(display,screen_num),
x, y, width, height, border_width, BlackPixel(display,
screen_num), WhitePixel(display,screen_num));
/* Get available icon sizes from window manager */
if (XGetIconSizes(display, RootWindow(display,screen_num),
&size_list, &count) == 0)
(void) fprintf( stderr, "%s: Window manager didn't set \
icon sizes - using default.\n", progname);
else {
;
/* A real application would search through size_list
* here to find an acceptable icon size and then
* create a pixmap of that size; this requires that
* the application have data for several sizes of icons */
}
/* Create pixmap of depth 1 (bitmap) for icon */
icon_pixmap = XCreateBitmapFromData(display, win,
icon_bitmap_bits, icon_bitmap_width,
icon_bitmap_height);
/* Set size hints for window manager; the window manager
* may override these settings */
/* Note that in a real application, if size or position
* were set by the user, the flags would be USPosition
* and USSize and these would override the window manager's
* preferences for this window */
/* x, y, width, and height hints are now taken from
* the actual settings of the window when mapped; note
* that PPosition and PSize must be specified anyway */
size_hints->flags = PPosition | PSize | PMinSize;
size_hints->min_width = 300;
size_hints->min_height = 200;
/* These calls store window_name and icon_name into
* XTextProperty structures and set their other fields
* properly */
if (XStringListToTextProperty(&window_name, 1, &windowName) == 0) {
(void) fprintf( stderr, "%s: structure allocation for \
windowName failed.\n", progname);
exit(-1);
}
if (XStringListToTextProperty(&icon_name, 1, &iconName) == 0) {
(void) fprintf( stderr, "%s: structure allocation for \
iconName failed.\n", progname);
exit(-1);
}
wm_hints->initial_state = NormalState;
wm_hints->input = True;
wm_hints->icon_pixmap = icon_pixmap;
wm_hints->flags = StateHint | IconPixmapHint | InputHint;
class_hints->res_name = progname;
class_hints->res_class = "Basicwin";
XSetWMProperties(display, win, &windowName, &iconName,
argv, argc, size_hints, wm_hints,
class_hints);
//}
/* Select event types wanted */
XSelectInput(display, win, ExposureMask | KeyPressMask |
ButtonPressMask | StructureNotifyMask);
load_font(&font_info);
/* Create GC for text and drawing */
getGC(win, &gc, font_info);
/* Display window */
XMapWindow(display, win);
/* Get events, use first to display text and graphics */
while (1) {
XNextEvent(display, &report);
switch (report.type) {
case Expose:
/* Unless this is the last contiguous expose,
* don't draw the window */
if (report.xexpose.count != 0)
break;
/* If window too small to use */
if (window_size == TOO_SMALL)
TooSmall(win, gc, font_info);
else {
/* Place text in window */
place_text(win, gc, font_info, width, height);
/* Place graphics in window */
place_graphics(win, gc, width, height);
}
break;
case ConfigureNotify:
/* Window has been resized; change width
* and height to send to place_text and
* place_graphics in next Expose */
width = report.xconfigure.width;
height = report.xconfigure.height;
if ((width < size_hints->min_width) ||
(height < size_hints->min_height))
window_size = TOO_SMALL;
else
window_size = BIG_ENOUGH;
break;
case ButtonPress:
/* Trickle down into KeyPress (no break) */
case KeyPress:
XUnloadFont(display, font_info->fid);
XFreeGC(display, gc);
XCloseDisplay(display);
exit(1);
default:
/* All events selected by StructureNotifyMask
* except ConfigureNotify are thrown away here,
* since nothing is done with them */
break;
} /* End switch */
} /* End while */
}
getGC(win, gc, font_info)
Window win;
GC *gc;
XFontStruct *font_info;
{
unsigned long valuemask = 0; /* Ignore XGCvalues and
* use defaults */
XGCValues values;
unsigned int line_width = 6;
int line_style=LineOnOffDash;
int cap_style=CapRound;
int join_style=JoinRound;
int dash_offset = 0;
static char dash_list[] = {12, 24};
int list_length = 2;
/* Create default Graphics Context */
*gc = XCreateGC(display, win, valuemask, &values);
/* Specify font */
XSetFont(display, *gc, font_info->fid);
/* Specify black foreground since default window background
* is white and default foreground is undefined */
XSetForeground(display, *gc, BlackPixel(display,screen_num));
/* Set line attributes */
XSetLineAttributes(display, *gc, line_width, line_style,
cap_style, join_style);
/* Set dashes */
XSetDashes(display, *gc, dash_offset, dash_list, list_length);
}
load_font(font_info)
XFontStruct **font_info;
{
char *fontname = "9x15";
/* Load font and get font information structure */
if ((*font_info = XLoadQueryFont(display,fontname)) == NULL)
{
(void) fprintf( stderr, "%s: Cannot open 9x15 font\n",
progname);
exit( -1 );
}
}
place_text(win, gc, font_info, win_width, win_height)
Window win;
GC gc;
XFontStruct *font_info;
unsigned int win_width, win_height;
{
char *string1 = "Hi! I'm a window, who are you?";
char *string2 = "To terminate program; Press any key";
char *string3 = "or button while in this window.";
char *string4 = "Screen Dimensions:";
int len1, len2, len3, len4;
int width1, width2, width3;
char cd_height[50], cd_width[50], cd_depth[50];
int font_height;
int initial_y_offset, x_offset;
/* Need length for both XTextWidth and XDrawString */
len1 = strlen(string1);
len2 = strlen(string2);
len3 = strlen(string3);
/* Get string widths for centering */
width1 = XTextWidth(font_info, string1, len1);
width2 = XTextWidth(font_info, string2, len2);
width3 = XTextWidth(font_info, string3, len3);
font_height = font_info->ascent + font_info->descent;
/* Output text, centered on each line */
XDrawString(display, win, gc, (win_width - width1)/2,
font_height,
string1, len1);
XDrawString(display, win, gc, (win_width - width2)/2,
(int)(win_height - (2 * font_height)),
string2, len2);
XDrawString(display, win, gc, (win_width - width3)/2,
(int)(win_height - font_height),
string3, len3);
/* Copy numbers into string variables */
(void) sprintf(cd_height, " Height - %d pixels",
DisplayHeight(display,screen_num));
(void) sprintf(cd_width, " Width - %d pixels",
DisplayWidth(display,screen_num));
(void) sprintf(cd_depth, " Depth - %d plane(s)",
DefaultDepth(display, screen_num));
/* Reuse these for same purpose */
len4 = strlen(string4);
len1 = strlen(cd_height);
len2 = strlen(cd_width);
len3 = strlen(cd_depth);
/* To center strings vertically, we place the first string
* so that the top of it is two font_heights above the center
* of the window; since the baseline of the string is what
* we need to locate for XDrawString and the baseline is
* one font_info -> ascent below the top of the character,
* the final offset of the origin up from the center of
* the window is one font_height + one descent */
initial_y_offset = win_height/2 - font_height -
font_info->descent;
x_offset = (int) win_width/4;
XDrawString(display, win, gc, x_offset, (int) initial_y_offset,
string4,len4);
XDrawString(display, win, gc, x_offset, (int) initial_y_offset +
font_height,cd_height,len1);
XDrawString(display, win, gc, x_offset, (int) initial_y_offset +
2 * font_height,cd_width,len2);
XDrawString(display, win, gc, x_offset, (int) initial_y_offset +
3 * font_height,cd_depth,len3);
}
place_graphics(win, gc, window_width, window_height)
Window win;
GC gc;
unsigned int window_width, window_height;
{
int x, y;
int width, height;
height = window_height/2;
width = 3 * window_width/4;
x = window_width/2 - width/2; /* Center */
y = window_height/2 - height/2;
XDrawRectangle(display, win, gc, x, y, width, height);
}
TooSmall(win, gc, font_info)
Window win;
GC gc;
XFontStruct *font_info;
{
char *string1 = "Too Small";
int y_offset, x_offset;
y_offset = font_info->ascent + 2;
x_offset = 2;
/* Output text, centered on each line */
XDrawString(display, win, gc, x_offset, y_offset, string1,
strlen(string1));
}