summaryrefslogtreecommitdiffstats
path: root/otherlibs/graph/open.c
diff options
context:
space:
mode:
Diffstat (limited to 'otherlibs/graph/open.c')
-rw-r--r--otherlibs/graph/open.c339
1 files changed, 339 insertions, 0 deletions
diff --git a/otherlibs/graph/open.c b/otherlibs/graph/open.c
new file mode 100644
index 000000000..4354033b9
--- /dev/null
+++ b/otherlibs/graph/open.c
@@ -0,0 +1,339 @@
+#include <fcntl.h>
+#include <signal.h>
+#include "libgraph.h"
+#include <alloc.h>
+#include <fail.h>
+#include <memory.h>
+#ifdef HAS_SETITIMER
+#include <sys/time.h>
+#endif
+
+static Bool gr_initialized = False;
+
+static int gr_error_handler(), gr_ioerror_handler();
+value gr_clear_graph();
+
+value gr_open_graph(arg)
+ value arg;
+{
+ char display_name[64], geometry_spec[64];
+ char * p, * q;
+ XSizeHints hints;
+ int ret;
+ XEvent event;
+ int x, y, w, h;
+ XWindowAttributes attributes;
+
+ if (gr_initialized) {
+ gr_clear_graph();
+ } else {
+
+ /* Parse the argument */
+ for (p = String_val(arg), q = display_name; *p != 0 && *p != ' '; p++)
+ if (q < display_name + sizeof(display_name) - 1) *q++ = *p;
+ *q = 0;
+ while (*p == ' ') p++;
+ for (q = geometry_spec; *p != 0; p++)
+ if (q < geometry_spec + sizeof(geometry_spec) - 1) *q++ = *p;
+ *q = 0;
+
+ /* Open the display */
+ grdisplay = XOpenDisplay(display_name);
+ if (grdisplay == NULL)
+ gr_fail("Cannot open display %s", XDisplayName(display_name));
+ grscreen = DefaultScreen(grdisplay);
+ grblack = BlackPixel(grdisplay, grscreen);
+ grwhite = WhitePixel(grdisplay, grscreen);
+ grcolormap = DefaultColormap(grdisplay, grscreen);
+
+ /* Set up the error handlers */
+ XSetErrorHandler(gr_error_handler);
+ XSetIOErrorHandler(gr_ioerror_handler);
+
+ /* Parse the geometry specification */
+ hints.x = 0;
+ hints.y = 0;
+ hints.width = DEFAULT_SCREEN_WIDTH;
+ hints.height = DEFAULT_SCREEN_HEIGHT;
+ hints.flags = PPosition | PSize;
+ hints.win_gravity = 0;
+
+ ret = XWMGeometry(grdisplay, grscreen, geometry_spec, "", BORDER_WIDTH,
+ &hints, &x, &y, &w, &h, &hints.win_gravity);
+ if (ret & (XValue | YValue)) {
+ hints.x = x; hints.y = y; hints.flags |= USPosition;
+ }
+ if (ret & (WidthValue | HeightValue)) {
+ hints.width = w; hints.height = h; hints.flags |= USSize;
+ }
+
+ /* Initial drawing color is black */
+ grcolor = grblack;
+
+ /* Create the on-screen window */
+ grwindow.w = hints.width;
+ grwindow.h = hints.height;
+ grwindow.win =
+ XCreateSimpleWindow(grdisplay, DefaultRootWindow(grdisplay),
+ hints.x, hints.y, hints.width, hints.height,
+ BORDER_WIDTH, grblack, grwhite);
+ XSetStandardProperties(grdisplay, grwindow.win, WINDOW_NAME, ICON_NAME,
+ None, NULL, 0, &hints);
+ grwindow.gc = XCreateGC(grdisplay, grwindow.win, 0, NULL);
+ XSetBackground(grdisplay, grwindow.gc, grwhite);
+ XSetForeground(grdisplay, grwindow.gc, grcolor);
+
+ /* Require exposure, resize and keyboard events */
+ XSelectInput(grdisplay, grwindow.win, DEFAULT_EVENT_MASK);
+
+ /* Map the window on the screen and wait for the first Expose event */
+ XMapWindow(grdisplay, grwindow.win);
+ do { XNextEvent(grdisplay, &event); } while (event.type != Expose);
+
+ /* Get the actual window dimensions */
+
+ XGetWindowAttributes(grdisplay, grwindow.win, &attributes);
+ grwindow.w = attributes.width;
+ grwindow.h = attributes.height;
+
+ /* Create the pixmap used for backing store */
+ grbstore.w = grwindow.w;
+ grbstore.h = grwindow.h;
+ grbstore.win =
+ XCreatePixmap(grdisplay, grwindow.win, grbstore.w, grbstore.h,
+ XDefaultDepth(grdisplay, grscreen));
+ grbstore.gc = XCreateGC(grdisplay, grbstore.win, 0, NULL);
+ XSetBackground(grdisplay, grbstore.gc, grwhite);
+
+ /* Clear the pixmap */
+ XSetForeground(grdisplay, grbstore.gc, grwhite);
+ XFillRectangle(grdisplay, grbstore.win, grbstore.gc,
+ 0, 0, grbstore.w, grbstore.h);
+ XSetForeground(grdisplay, grbstore.gc, grcolor);
+
+ /* The global data structures are now correctly initialized.
+ In particular, gr_sigio_handler can now handle events safely. */
+ gr_initialized = True;
+
+ /* If possible, request that system calls be restarted after
+ the EVENT_SIGNAL signal. */
+#ifdef SA_RESTART
+ { struct sigaction action;
+ sigaction(EVENT_SIGNAL, NULL, &action);
+ action.sa_flags |= SA_RESTART;
+ sigaction(EVENT_SIGNAL, &action, NULL);
+ }
+#endif
+
+#ifdef USE_ASYNC_IO
+ /* If BSD-style asynchronous I/O are supported:
+ arrange for I/O on the connection to trigger the SIGIO signal */
+ ret = fcntl(ConnectionNumber(grdisplay), F_GETFL, 0);
+ fcntl(ConnectionNumber(grdisplay), F_SETFL, ret | FASYNC);
+ fcntl(ConnectionNumber(grdisplay), F_SETOWN, getpid());
+#endif
+#ifdef USE_INTERVAL_TIMER
+ /* If BSD-style interval timers are provided, use the real-time timer
+ to poll events. */
+ { struct itimerval it;
+ it.it_interval.tv_sec = 0;
+ it.it_interval.tv_usec = 250000;
+ it.it_value.tv_sec = 0;
+ it.it_value.tv_usec = 250000;
+ setitimer(ITIMER_REAL, &it, NULL);
+ }
+#endif
+#ifdef USE_ALARM
+ /* The poor man's solution: use alarm to poll events. */
+ alarm(1);
+#endif
+ }
+ /* Position the current point at origin */
+ grx = 0;
+ gry = 0;
+ /* Reset the color cache */
+ gr_init_color_cache();
+ return Val_unit;
+}
+
+value gr_close_graph()
+{
+ if (gr_initialized) {
+#ifdef USE_INTERVAL_TIMER
+ struct itimerval it;
+ it.it_value.tv_sec = 0;
+ it.it_value.tv_usec = 0;
+ setitimer(ITIMER_REAL, &it, NULL);
+#endif
+ gr_initialized = False;
+ if (grfont != NULL) { XFreeFont(grdisplay, grfont); grfont = NULL; }
+ XFreeGC(grdisplay, grwindow.gc);
+ XDestroyWindow(grdisplay, grwindow.win);
+ XFreeGC(grdisplay, grbstore.gc);
+ XFreePixmap(grdisplay, grbstore.win);
+ XCloseDisplay(grdisplay);
+ }
+ return Val_unit;
+}
+
+value gr_clear_graph()
+{
+ gr_check_open();
+ XSetForeground(grdisplay, grwindow.gc, grwhite);
+ XFillRectangle(grdisplay, grwindow.win, grwindow.gc,
+ 0, 0, grwindow.w, grwindow.h);
+ XSetForeground(grdisplay, grwindow.gc, grcolor);
+ XSetForeground(grdisplay, grbstore.gc, grwhite);
+ XFillRectangle(grdisplay, grbstore.win, grbstore.gc,
+ 0, 0, grbstore.w, grbstore.h);
+ XSetForeground(grdisplay, grbstore.gc, grcolor);
+ XFlush(grdisplay);
+ return Val_unit;
+}
+
+value gr_size_x()
+{
+ gr_check_open();
+ return Val_int(grwindow.w);
+}
+
+value gr_size_y()
+{
+ gr_check_open();
+ return Val_int(grwindow.h);
+}
+
+/* The gr_sigio_handler is called via the signal machinery in the bytecode
+ interpreter. The signal system ensures that this function will be
+ called either between two bytecode instructions, or during a blocking
+ primitive. In either case, not in the middle of an Xlib call.
+ (There is no blocking primitives in this library, not even
+ wait_next_event, for various reasons.) */
+
+void gr_handle_simple_event();
+
+value gr_sigio_signal(unit)
+ value unit;
+{
+ return Val_int(EVENT_SIGNAL);
+}
+
+value gr_sigio_handler()
+{
+ XEvent grevent;
+
+ if (gr_initialized) {
+ while (XCheckMaskEvent(grdisplay, -1 /*all events*/, &grevent))
+ gr_handle_simple_event(&grevent);
+ }
+#ifdef USE_ALARM
+ alarm(1);
+#endif
+ return Val_unit;
+}
+
+void gr_handle_simple_event(e)
+ XEvent * e;
+{
+ switch (e->type) {
+
+ case Expose:
+ XCopyArea(grdisplay, grbstore.win, grwindow.win, grwindow.gc,
+ e->xexpose.x, e->xexpose.y + grbstore.h - grwindow.h,
+ e->xexpose.width, e->xexpose.height,
+ e->xexpose.x, e->xexpose.y);
+ XFlush(grdisplay);
+ break;
+
+ case ConfigureNotify:
+ grwindow.w = e->xconfigure.width;
+ grwindow.h = e->xconfigure.height;
+ if (grwindow.w > grbstore.w || grwindow.h > grbstore.h) {
+
+ /* Allocate a new backing store large enough to accomodate
+ both the old backing store and the current window. */
+ struct canvas newbstore;
+ newbstore.w = max(grwindow.w, grbstore.w);
+ newbstore.h = max(grwindow.h, grbstore.h);
+ newbstore.win =
+ XCreatePixmap(grdisplay, grwindow.win, newbstore.w, newbstore.h,
+ XDefaultDepth(grdisplay, grscreen));
+ newbstore.gc = XCreateGC(grdisplay, newbstore.win, 0, NULL);
+ XSetBackground(grdisplay, newbstore.gc, grwhite);
+ XSetForeground(grdisplay, newbstore.gc, grwhite);
+ XFillRectangle(grdisplay, newbstore.win, newbstore.gc,
+ 0, 0, newbstore.w, newbstore.h);
+ XSetForeground(grdisplay, newbstore.gc, grcolor);
+
+ /* Copy the old backing store into the new one */
+ XCopyArea(grdisplay, grbstore.win, newbstore.win, newbstore.gc,
+ 0, 0, grbstore.w, grbstore.h, 0, newbstore.h - grbstore.h);
+
+ /* Free the old backing store */
+ XFreeGC(grdisplay, grbstore.gc);
+ XFreePixmap(grdisplay, grbstore.win);
+
+ /* Use the new backing store */
+ grbstore = newbstore;
+ XFlush(grdisplay);
+ }
+ break;
+
+ case MappingNotify:
+ XRefreshKeyboardMapping(&(e->xmapping));
+ break;
+
+ case KeyPress:
+ { KeySym thekey;
+ char keytxt[256];
+ int nchars;
+ char * p;
+ nchars = XLookupString(&(e->xkey), keytxt, sizeof(keytxt), &thekey, 0);
+ for (p = keytxt; nchars > 0; p++, nchars--) gr_enqueue_char(*p);
+ break;
+ }
+ }
+}
+
+/* Processing of graphic errors */
+
+static value graphic_failure_exn;
+
+value gr_register_graphic_failure(exn)
+ value exn;
+{
+ graphic_failure_exn = Field(exn, 0);
+ register_global_root(&graphic_failure_exn);
+ return Val_unit;
+}
+
+void gr_fail(fmt, arg)
+ char * fmt, * arg;
+{
+ char buffer[1024];
+ sprintf(buffer, fmt, arg);
+ raise_with_string(graphic_failure_exn, buffer);
+}
+
+void gr_check_open()
+{
+ if (!gr_initialized) gr_fail("graphic screen not opened", NULL);
+}
+
+static int gr_error_handler(display, error)
+ Display * display;
+ XErrorEvent * error;
+{
+ char errmsg[512];
+ XGetErrorText(error->display, error->error_code, errmsg, sizeof(errmsg));
+ gr_fail("Xlib error: %s", errmsg);
+ return 0;
+}
+
+static int gr_ioerror_handler(display)
+ Display * display;
+{
+ gr_fail("fatal I/O error", NULL);
+ return 0;
+}
+