/* Unix: Header file are in /usr/openwin/share/include/X11 Link with -lX11 PuTTY: Category -> Connection -> SSH -> Tunnels -> Enable X11 forwarding Mac OS X Xcode: Create a "Command Line Utility" -> "C++ Tool" project. Project -> Add to Project... command-shift-G /usr/X11R6/lib/X11.dylib Groups & Files -> Executables -> progname and double-click on it. Executable "progname" Info -> General, Set the working directory to Launch X11.app. In the xterm window, cd to that directory (on my Mac it was /Users/itslogin/Documents/Debug), ./prog Windows: http://www.objectcentral.com/objectcentral/downloads.htm */ #include #include /* for malloc, free, exit, EXIT_FAILURE */ #include /* for strlen */ #include /* for isprint */ #include /* for LONG_MAX */ #include /* for errno, EOVERFLOW */ #include #include #include /* for struct timeval, gettimeofday */ #include "term.h" /* for term_ functions */ static Display *display = NULL; static int screen = 0; static Window window; static GC gc; /* Variables set by term_construct, used by term_put, term_puts. */ static XFontStruct *font_struct = NULL; static int xoffset = 0; /* in pixels */ static int yoffset = 0; /* in pixels */ static int xfactor = 0; /* in pixels */ static int yfactor = 0; /* in pixels */ /* Number of columns and rows of characters. */ static unsigned xmax = 0; static unsigned ymax = 0; /* Variable used by term_get and term_put */ static char *p; #define A(x, y) (p[(y) * xmax + (x)]) /* Make sure other term_ functions are called only between term_construct and term_destruct. */ static int calls = 0; static void check_calls(const char *name) { if (calls != 1) { calls = 1; term_destruct(); fprintf(stderr, "%s: must have prior call to term_construct\n", name); exit(EXIT_FAILURE); } } static void check_xy(const char *name, unsigned x, unsigned y) { if (x >= xmax || y >= ymax) { term_destruct(); fprintf(stderr, "%s: location (%u, %u) is off screen (%u, %u)\n", name, x, y, xmax, ymax); exit(EXIT_FAILURE); } } void term_construct() { const unsigned border_width = 1; /* in pixels */ int display_width, display_height; /* in pixels */ unsigned window_width, window_height; /* in pixels */ const int x0 = 0, y0 = 0; /* upper left corner of window, in pixels */ int depth; unsigned long black_pixel, white_pixel; size_t x, y; XSizeHints size_hints; XEvent event; if (calls != 0) { fprintf(stderr, "term_construct has already been called.\n"); exit(EXIT_FAILURE); } ++calls; display = XOpenDisplay(NULL); if (display == NULL) { fprintf(stderr, "term_construct could not open display \"%s\"\n", XDisplayName(NULL)); exit(EXIT_FAILURE); } printf("Connected to X Window server.\n"); printf("ServerVendor == \"%s\"\n", ServerVendor(display)); printf("VendorRelease == %d\n", VendorRelease(display)); printf("ProtocolVersion == %d\n", ProtocolVersion(display)); printf("ProtocolRevision == %d\n", ProtocolRevision(display)); screen = DefaultScreen(display); depth = DefaultDepth(display, screen); printf("depth == %d\n", depth); display_width = DisplayWidth (display, screen); display_height = DisplayHeight(display, screen); if ((font_struct = XLoadQueryFont(display, "9x15")) == NULL && (font_struct = XLoadQueryFont(display, "fixed")) == NULL) { fprintf(stderr, "term_construct could not get XFontStruct\n"); exit(EXIT_FAILURE); } xoffset = border_width + 1; yoffset = border_width + 1 + font_struct->ascent; xfactor = XTextWidth(font_struct, "A", 1); yfactor = font_struct->ascent + font_struct->descent; /* Window will be 2/3 of display dimensions. */ xmax = (display_width * 2 / 3 - 2 * (border_width + 1)) / xfactor; ymax = (display_height * 2 / 3 - 2 * (border_width + 1)) / yfactor; printf("Window dimensions: %d x %d pixels, %u x %u characters\n", window_width = xmax * xfactor + 2 * (border_width + 1), window_height = ymax * yfactor + 2 * (border_width + 1), xmax, ymax ); window = XCreateSimpleWindow( display, RootWindow(display, screen), x0, y0, window_width, window_height, border_width, black_pixel = BlackPixel(display, screen), white_pixel = WhitePixel(display, screen) ); if (window == 0) { fprintf(stderr, "term_construct could not create window\n"); exit(EXIT_FAILURE); } XSetWindowBorder(display, window, black_pixel); size_hints.flags = PPosition | PSize | PMinSize | PMaxSize; size_hints.x = x0; size_hints.y = y0; size_hints.width = size_hints.min_width = size_hints.max_width = window_width; size_hints.height = size_hints.min_height = size_hints.max_height = window_height; XSetNormalHints(display, window, &size_hints); if ((gc = XCreateGC(display, window, (unsigned long)0, NULL)) == NULL) { fprintf(stderr, "term_construct could not create graphics context\n"); exit(EXIT_FAILURE); } XSetForeground(display, gc, black_pixel); XSetBackground(display, gc, white_pixel); XSetFont(display, gc, font_struct->fid); XSelectInput(display, window, KeyPressMask | ExposureMask); XMapWindow(display, window); XFlush(display); if ((p = malloc(xmax * ymax)) == NULL) { fprintf(stderr, "term_construct could not allocate %u x %u " "== %u bytes", xmax, ymax, xmax * ymax); exit(EXIT_FAILURE); } for (x = 0; x < xmax; ++x) { for (y = 0; y < ymax; ++y) { A(x, y) = ' '; } } /* Can't draw before first Expose event. */ do { XNextEvent(display, &event); } while (event.type != Expose); } void term_destruct() { check_calls("term_destruct"); --calls; free(p); XFreeGC(display, gc); XFreeFont(display, font_struct); XDestroyWindow(display, window); XCloseDisplay(display); } unsigned term_xmax() { check_calls("term_xmax"); return xmax; } unsigned term_ymax() { check_calls("term_ymax"); return ymax; } static void check_c(const char *name, unsigned x, unsigned y, char c) { if (!isprint((unsigned char)c)) { term_destruct(); fprintf(stderr, "%s: unprintable char, decimal %u " "at location (%u, %u)\n", name, (unsigned char)c, x, y); exit(EXIT_FAILURE); } } void term_put(unsigned x, unsigned y, char c) { const char s[] = {c, '\0'}; check_calls("term_put"); check_xy("term_put", x, y); check_c("term_put", x, y, c); term_puts(x, y, s); } void term_puts(unsigned x, unsigned y, const char *s) { const size_t len = strlen(s); size_t i; unsigned x1 = x, y1 = y; check_calls("term_puts"); for (i = 0; i < len; ++i) { check_xy("term_puts", x1, y1); check_c("term_puts", x1, y1, s[i]); A(x1, y1) = s[i]; if (++x1 >= xmax) { x1 = 0; ++y1; } } XDrawImageString(display, window, gc, x * xfactor + xoffset, y * yfactor + yoffset, s, len); XFlush(display); x1 = x; y1 = y; for (i = 0; i < len; ++i) { A(x1, y1) = s[i]; if (++x1 >= xmax) { x1 = 0; ++y1; } } } char term_get(unsigned x, unsigned y) { char c; check_calls("term_get"); check_xy("term_get", x, y); check_c("term_get", x, y, c = A(x, y)); return c; } char term_key() { XEvent event; char buffer[128]; KeySym symbol; size_t y; unsigned new_xmax, new_ymax; check_calls("term_key"); for (XFlush(display); XPending(display) > 0;) { XNextEvent(display, &event); switch (event.type) { case KeyPress: if (XLookupString((XKeyEvent *)&event, buffer, sizeof buffer - 1, &symbol, NULL) > 0) { return buffer[0]; } break; case Expose: if (((const XExposeEvent *)&event)->count == 0) { for (y = 0; y < ymax; ++y) { XDrawImageString(display, window, gc, xoffset, y * yfactor + yoffset, p + y * xmax, xmax); } } XFlush(display); break; default: break; } } return '\0'; } /* Get the current time in seconds and milliseconds. */ static void gettime(long *secs, long *mils) { struct timeval tv; if (gettimeofday(&tv, NULL) != 0) { if (errno == EOVERFLOW) { fprintf(stderr, "term_wait: gettimeofday >= long\n"); } else { fprintf(stderr, "term_wait could not gettimeofday\n"); } term_destruct(); exit(EXIT_FAILURE); } *secs = tv.tv_sec; *mils = tv.tv_usec / 1000; /* 1 microsecond == 1000 milliseconds */ } void term_wait(int milliseconds) { ldiv_t d; long ssec, smil; /* start */ long csec, cmil; /* current */ check_calls("term_wait"); if (milliseconds < 0) { fprintf(stderr, "term_wait: can't wait %d milliseconds\n", milliseconds); term_destruct(); exit(EXIT_FAILURE); } d = ldiv(milliseconds, 1000); /* d.quot is seconds to wait */ gettime(&ssec, &smil); do { gettime(&csec, &cmil); csec -= ssec; /* set csec to how many seconds waited so far */ if ((cmil -= smil) < 0) { --csec; cmil += 1000; } } while (csec < d.quot || csec == d.quot && cmil < d.rem); } void term_beep() { check_calls("term_beep"); XBell(display, 100); }