glennw@crevasse.WV.TEK.COM (Glenn Widener;;61-049;;orca;) (02/07/90)
There have been several requests for a "Hello World" program updated for
R4. Here is the one I presented at my ICCCM tutorial at the MIT X
conference. Yes, it does run! It is a bit more than just a minimal Hello
World program; it demonstrates how to create client icon windows and
bitmaps, and how to handle Client Messages, using as an example a new
"client closedown" protocol that I have submitted to the Consortium for
review.
For more information, find the notes from my talk, or better yet, attend
the next edition of the ICCCM talk at Xhibition '90.
-----
/*
* Copyright 1989 by the Massachusetts Institute of Technology,
* Cambridge, Massachusetts, and Tektronix, Inc. Beaverton, Oregon.
*
* Permission to use, copy, modify, distribute, and sell this software and
* its documentation for any purpose is hereby granted without fee, provided
* that the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the names of Tektronix or M.I.T. not be used in
* advertising or publicity pertaining to distribution of the software
* without specific, written prior permission. Tektronix and M.I.T. make no
* representations about the suitability of this software for any purpose.
* It is provided "as is" without express or implied warranty.
*
* TEKTRONIX AND M.I.T. DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
* SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
* IN NO EVENT SHALL TEKTRONIX OR M.I.T. BE LIABLE FOR ANY SPECIAL, INDIRECT
* OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
* USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
* PERFORMANCE OF THIS SOFTWARE.
*
* Author: Glenn Widener, Tektronix, Inc.
* P.O. Box 1000
* Wilsonville, OR, 97070
* glennw@orca.wv.tek.com
* Based on the r3 Hello World program by David Rosenthal.
*
* NAME
* xhw_r4.c - an R4 Xlib Hello World program
*
* DESCRIPTION
* This program is an update of the Hello World supplied by
* David Rosenthal on the R3 MIT X Consortium tape.
* That program had no copyright; I have added the standard MIT
* release copyright.
*
*/
#include <stdio.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xatom.h>
#define STRING "Hello, world"
/* icon names should be as short as possible */
#define ICON_NAME "Hello!"
#define BORDER 1
#define FONT "fixed"
#define ICON_FILE "smile"
#define ARG_ICON_FILE "icon"
#define ARG_FONT "font"
#define ARG_BORDER_COLOR "bordercolor"
#define ARG_FOREGROUND "foreground"
#define ARG_BACKGROUND "background"
#define ARG_BORDER "border"
#define ARG_GEOMETRY "geometry"
#define DEFAULT_GEOMETRY ""
char *ProgramName;
XWMHints *xwmh; /* WM Hints struct */
XSizeHints *xsh; /* Size hints for window manager */
XClassHint *class_hint; /* Class name hints for window manager */
Display *dpy; /* X server connection */
void exit();
usage ()
{
fprintf (stderr,
"usage: %s [-display host:dpy] [-geometry geom]\n", ProgramName);
exit (1);
}
main(argc,argv)
int argc;
char **argv;
{
Window win; /* Window ID */
GC gc; /* GC to draw with */
char *fontName; /* Name of font for string */
XFontStruct *fontstruct; /* Font descriptor */
unsigned long fth, pad; /* Font size parameters */
unsigned long fg, bg, bd; /* Pixel values */
unsigned long bw; /* Border width */
char *tempstr; /* Temporary string */
XColor color; /* Temporary color */
Colormap cmap; /* Color map to use */
XGCValues gcv; /* Struct for creating GC */
XEvent event; /* Event received */
char *geomSpec = NULL;/* Window geometry string */
XSetWindowAttributes xswa; /* Temporary Set Window Attribute struct */
int junk; /* ignore return values */
unsigned int ujunk; /* ignore return values */
Window wjunk; /* ignore return values */
unsigned int width, height; /* of icon */
unsigned int depth; /* of root window/icon window */
char *icon_file; /* file to get icon bitmap data from */
char *displayname = NULL;
Atom ClosedownAtom; /* to ask for WM_CLOSEDOWN */
Atom WmProtocolsAtom;/* to read WM_CLOSEDOWN event */
int bitmask; /* for ParseGeometry flags from XWMGeometry */
int i;
Pixmap icon_win_pixmap;/* for icon window */
XTextProperty windowName, iconName; /* ICCCM text */
/* parse arguments for basic X args */
ProgramName = argv[0];
for (i = 1; i < argc; i++) {
char *arg = argv[i];
if (arg[0] == '-') {
switch (arg[1]) {
case 'd': /* -display host:dpy */
if (++i >= argc) usage ();
displayname = argv[i];
continue;
case 'g': /* -geometry geom */
if (++i >= argc) usage ();
geomSpec = argv[i];
continue;
default:
usage ();
/* doesn't return */
}
} else
usage ();
}
/*
* Open the display using the $DISPLAY environment variable to locate
* the X server. See Section 2.1.
*/
if ((dpy = XOpenDisplay(displayname)) == NULL) {
fprintf(stderr, "%s: can't open %s\n", argv[0],
XDisplayName(displayname));
exit(1);
}
/*
* Load the font and icon bitmap to use. See Sections 10.2 & 6.5.1
*/
if ((icon_file = XGetDefault(dpy, argv[0], ARG_ICON_FILE)) == NULL) {
icon_file = ICON_FILE;
}
if ((fontName = XGetDefault(dpy, argv[0], ARG_FONT)) == NULL) {
fontName = FONT;
}
if ((fontstruct = XLoadQueryFont(dpy, fontName)) == NULL) {
fprintf(stderr, "%s: display %s doesn't know font %s\n",
argv[0], DisplayString(dpy), fontName);
exit(1);
}
fth = fontstruct->max_bounds.ascent + fontstruct->max_bounds.descent;
/*
* Select colors for the border, the window background, and the
* foreground. We use the default colormap to allocate the colors in.
* Note that the border color will typically be overridden by the wm.
* See Sections 2.2.1, 5.1.2, & 10.4.
*/
cmap = DefaultColormap(dpy, DefaultScreen(dpy));
if ((tempstr = XGetDefault(dpy, argv[0], ARG_BORDER_COLOR)) == NULL ||
XParseColor(dpy, cmap, tempstr, &color) == 0 ||
XAllocColor(dpy, cmap, &color) == 0) {
bd = WhitePixel(dpy, DefaultScreen(dpy));
}
else {
bd = color.pixel;
}
if ((tempstr = XGetDefault(dpy, argv[0], ARG_BACKGROUND)) == NULL ||
XParseColor(dpy, cmap, tempstr, &color) == 0 ||
XAllocColor(dpy, cmap, &color) == 0) {
bg = BlackPixel(dpy, DefaultScreen(dpy));
}
else {
bg = color.pixel;
}
if ((tempstr = XGetDefault(dpy, argv[0], ARG_FOREGROUND)) == NULL ||
XParseColor(dpy, cmap, tempstr, &color) == 0 ||
XAllocColor(dpy, cmap, &color) == 0) {
fg = WhitePixel(dpy, DefaultScreen(dpy));
}
else {
fg = color.pixel;
}
/*
* Set the border width of the window, and the gap between the text
* and the edge of the window, "pad".
*/
pad = BORDER;
if ((tempstr = XGetDefault(dpy, argv[0], ARG_BORDER)) == NULL)
bw = 1;
else
bw = atoi(tempstr);
/*
* Deal with providing the window with an initial position & size.
* Fill out the XSizeHints struct to inform the window manager. See
* Sections 9.1.7 & 10.3.
*/
xsh = XAllocSizeHints();
/* Ok, ok, so technically we should be checking for out of memory... */
/*
* For a program default size, fit the window to the text and locate it in
* the center of the screen. Make the minimum size such that the text is
* always visible.
*/
xsh->flags = (PPosition | PSize | PMinSize);
xsh->height =
xsh->min_height = fth + pad * 2;
xsh->width =
xsh->min_width = XTextWidth(fontstruct, STRING, strlen(STRING)) + pad * 2;
xsh->x = (DisplayWidth(dpy, DefaultScreen(dpy)) - xsh->width) / 2;
xsh->y = (DisplayHeight(dpy, DefaultScreen(dpy)) - xsh->height) / 2;
if (geomSpec == NULL)
geomSpec = XGetDefault(dpy, argv[0], ARG_GEOMETRY);
bitmask = XWMGeometry(dpy, DefaultScreen(dpy), geomSpec,
DEFAULT_GEOMETRY, bw, xsh,
&(xsh->x), &(xsh->y),
&(xsh->width), &(xsh->height), &(xsh->win_gravity));
/* XWMGeomtry does not set anything in the size hints structure */
if (bitmask & (XValue | YValue)) {
xsh->flags |= USPosition;
}
if (bitmask & (WidthValue | HeightValue)) {
xsh->flags |= USSize;
}
/*
* Create the Window with the information in the XSizeHints, the
* border width, and the border & background pixels. See Section 3.3.
*/
win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy),
xsh->x, xsh->y, xsh->width, xsh->height,
bw, bd, bg);
/*
* Create the GC for writing the text and copying the icon pixmap. See
* Section 5.3.
*/
gcv.font = fontstruct->fid;
gcv.foreground = fg;
gcv.background = bg;
gc = XCreateGC(dpy, win, (GCFont | GCForeground | GCBackground), &gcv);
/*
* Set the standard properties for the window managers. See Section
* 9.1.13.
*/
/*
* This structure forms the WM_HINTS property of the window, letting the
* window manager know how to handle this window. It may grow, so it is
* dynamically allocated. See Section 9.1.6 of the Xlib manual.
*/
xwmh = XAllocWMHints();
xwmh->flags = (InputHint|StateHint);
xwmh->input = False;
xwmh->initial_state = NormalState;
/* unset fields are initialized to zero (they are ignored anyway) */
if (XReadBitmapFile(dpy, win, icon_file, &width, &height,
&xwmh->icon_pixmap, &junk, &junk) == BitmapSuccess) {
/*
* Create the icon window the size of the bitmap; ignore the
* WM_ICON_SIZE_HINTS and let the wm resize the window if it has to
* (the pixmap gets tiled in this case). Do this cheap, to avoid
* handling exposure; use the window background to draw the desired
* pixmap. Pixmap and window depth and visual are those of the root
* window. The background pixmap must be of the depth of the window;
* fastest to let the server generate the bits from the depth one
* bitmap. Border will be set by the WM, so usually this border
* pixel/width has no effect. Window position is irrelevant.
*/
XGetGeometry(dpy, DefaultRootWindow(dpy), &wjunk, &junk, &junk,
&ujunk, &ujunk, &ujunk, &depth);
icon_win_pixmap = XCreatePixmap(dpy, DefaultRootWindow(dpy),
width, height, depth);
XCopyPlane(dpy, xwmh->icon_pixmap, icon_win_pixmap, gc, 0, 0,
width, height, 0, 0, 1);
xswa.background_pixmap = icon_win_pixmap;
xswa.border_pixel = bd;
xwmh->icon_window = XCreateWindow(dpy, DefaultRootWindow(dpy),
0, 0, width, height, bw, CopyFromParent,
InputOutput, CopyFromParent,
(CWBackPixmap | CWBorderPixel),
&xswa);
/* server keeps a copy of the pixmap */
XFreePixmap(dpy, icon_win_pixmap);
xwmh->flags |= (IconPixmapHint|IconWindowHint|IconPositionHint);
/* for fun, center the icon on the screen (perhaps, approximately) */
xwmh->icon_x = DisplayWidth(dpy, DefaultScreen(dpy)) >> 1;
xwmh->icon_y = DisplayHeight(dpy, DefaultScreen(dpy)) >> 1;
}
else
fprintf(stderr, "%s: Can't open bitmap file '%s' for icon\n",
argv[0], icon_file);
/*
* Set the WM_CLASS property for the window managers. See Section
* 9.1.8.
*/
class_hint = XAllocClassHint();
class_hint->res_name = argv[0];
/* We set the program class name consistent with the XGetDefault code,
which is itself silly, and needs to be fixed... */
class_hint->res_class = "Program";
/*
* XSetWMProperties() uses the new XTextProperty structure. See Section
* 9.1.2.
*/
windowName.value = (unsigned char *) STRING;
iconName.value = (unsigned char *) ICON_NAME;
windowName.encoding = iconName.encoding = XA_STRING;
windowName.format = iconName.format = 8;
/* Note - nitems does not include a NULL terminator */
windowName.nitems = strlen(STRING);
iconName.nitems = strlen(ICON_NAME);
XSetWMProperties(dpy, win, &windowName, &iconName, argv, argc, xsh, xwmh,
class_hint);
/*
* We don't really have to be informed of a connection close, but doing so
* prevents dangling network resources and/or spurious error messages.
* Note - WM_CLOSEDOWN is not yet part of the ICCCM!
*/
ClosedownAtom = XInternAtom(dpy, "WM_CLOSEDOWN", False);
WmProtocolsAtom = XInternAtom(dpy, "WM_PROTOCOLS", False);
XSetWMProtocols(dpy, win, &ClosedownAtom, 1);
/*
* Ensure that the window's colormap field points to the default
* colormap, so that the window manager knows the correct colormap to
* use for the window. See Section 3.2.9. Also, set the window's Bit
* Gravity to reduce Expose events.
*/
xswa.colormap = DefaultColormap(dpy, DefaultScreen(dpy));
xswa.bit_gravity = CenterGravity;
XChangeWindowAttributes(dpy, win, (CWColormap | CWBitGravity), &xswa);
/*
* Specify the event types we're interested in - only Exposures. See
* Sections 8.5 & 8.4.5.1
*/
XSelectInput(dpy, win, ExposureMask);
/*
* Map the window to make it visible. See Section 3.5.
*/
XMapWindow(dpy, win);
/*
* Loop forever, examining each event.
*/
while (1) {
/*
* Get the next event
*/
XNextEvent(dpy, &event);
/*
* On the last of each group of Expose events, repaint the entire
* window. See Section 8.4.5.1.
*/
if (event.type == Expose && event.xexpose.count == 0) {
int x, y;
/*
* Remove any other pending Expose events from the queue to
* avoid multiple repaints. See Section 8.7.
*/
while (XCheckTypedEvent(dpy, Expose, &event));
/*
* Find out how big the window is now, so that we can center
* the text in it.
*/
if (XGetGeometry(dpy, win, &wjunk, &junk, &junk,
&width, &height, &ujunk, &ujunk) == 0)
break;
x = (width - XTextWidth(fontstruct, STRING, strlen(STRING))) / 2;
y = (height + fontstruct->max_bounds.ascent
- fontstruct->max_bounds.descent) / 2;
/*
* Fill the window with the background color, and then paint
* the centered string.
*/
XClearWindow(dpy, win);
XDrawString(dpy, win, gc, x, y, STRING, strlen(STRING));
}
else if ((event.type == ClientMessage) &&
(event.xclient.message_type == WmProtocolsAtom) &&
(event.xclient.data.l[0] == ClosedownAtom)) {
Cleanup();
}
}
Cleanup();
}
Cleanup()
{
/* just to be truly kosher */
XFree(xwmh);
XFree(class_hint);
XFree(xsh);
/* enough kosher is enough, close the display to free server resources */
XCloseDisplay(dpy);
exit(1);
}
Glenn Widener
Tektronix, Inc.
(UPS) 2660 SW Parkway
(US Mail) PO Box 1000