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