[plt-scheme] windows drscheme + cygwin Xlib mzscheme extension

From: Ron Stanonik (stanonik at Cogsci.ucsd.edu)
Date: Fri Jun 6 13:33:37 EDT 2003

We converted an Xlib application (a zoomable surface) into a mzscheme
extension.  That is, mzscheme draws on the surface and gets callbacks
when XEvents (eg, MotionNotify, ButtonPress, etc) occur.  The X event
loop is hooked into mzscheme's event loop, so that mzscheme is in control.
Something like

  while(1) {
    scheme_block_until
    XNextEvent
  }

The application runs in a scheme thread in an eventspace.

This works in linux and Mac OSX (XDarwin).  Now we'd like to run in
the Windows world.  We compiled the Xlib mzscheme extension using
CygWin+XFree86.  We then load-extension it into the Windows version
of DrScheme.

We get an odd event behaviour.  DrScheme only gets callbacks from the
extension (due to Xevents) when DrScheme has the focus.  In particular,
if DrScheme has the focus, then it receives MotionNotify callbacks
from motion in the Xlib window, but if we click in the Xlib window,
then the Xlib window gets the focus and DrScheme no longer gets callbacks.
But when we click in the DrScheme window, then DrScheme gets the callbacks
queued up from any motion that occurred while the Xlib window had the focus.
Whew!  Hope I haven't lost you.

It's as though scheme_block_until doesn't check the file descriptor
from the Cygwin+XFree86 server or doesn't return, until Drscheme has the focus.
I assume the Cygwin XFree86 server is sending the MotionNotify events
because it runs in a separate Windows process.

I'm appending the simplest example X application, which reproduces the problem.
To compile, I copy lib and include from the Windows PLT directory into a
convenient cygwin directory.  To test, I start DrScheme from a cygwin window.
I get the same behaviour, if I start Drscheme from Windows Desktop.

We're running the latest DrScheme and CygWin.

Thanks for any suggestions.

Ron
stanonik at cogsci.ucsd.edu

============ simple.c ===============
#include "escheme.h"
#include <stdio.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <sys/time.h>

static int debug = 0;
static int fd;
static fd_set fdset[3];

static int
ready(Scheme_Object *data)
{
	fd_set *fdset;
	int n = 0;
	struct timeval timeout;

	if (debug) fprintf(stderr, "ready fd = %d\n", fd);
	fdset = (fd_set *)data;
	FD_ZERO(&fdset[0]);
	FD_ZERO(&fdset[1]);
	FD_ZERO(&fdset[2]);
	FD_SET(fd, &fdset[0]);
	n = fd + 1;
	timeout.tv_sec = timeout.tv_usec = 0;
	n = select(n, &fdset[0], &fdset[1], &fdset[2], &timeout);
	if (debug) fprintf(stderr, "ready n = %d\n", n);
	return n;
}

static void
wakeup(Scheme_Object *data, void *fds)
{
	void *fdset[3];

	if (debug) fprintf(stderr, "wakeup fd = %d\n", fd);
	fdset[0] = MZ_GET_FDSET(fds, 0);
	fdset[0] = MZ_GET_FDSET(fds, 1);
	fdset[0] = MZ_GET_FDSET(fds, 2);
	MZ_FD_SET(fd, (fd_set *)fdset[0]);
}

Scheme_Object *
sch_simple(int argc, Scheme_Object **argv)
{
        Display *display;
        Window window;
        XEvent event, nextevent;
        int screen;
        unsigned long foreground, background;
	int move = 0, n;

        /* connect to the X server */
        display = XOpenDisplay("127.0.0.1:0");

        if (display == NULL) {
                scheme_warning("cannot connect to server");
                return scheme_false;
        }
	fd = ConnectionNumber(display);
	fprintf(stderr, "fd = %d\n", fd);

        /* get default screen */
        screen = DefaultScreen (display);

        /* get black and white representation on current screen */
        background = WhitePixel (display, screen);
        foreground = BlackPixel (display, screen);

        /* Create window at (100,50), width 350, height 250, border width
           2, in default root  */
        window = XCreateSimpleWindow (display,
                DefaultRootWindow(display), 100, 50, 350, 250, 2,
                foreground, background);

        if (window == NULL) {
                scheme_warning("cannot open window");
                return scheme_false;
        }

	XSelectInput(display, window,
		PointerMotionMask |
		ButtonPressMask |
		ButtonReleaseMask);

        /* pop this window up on the screen */
        XMapRaised (display, window);

	/* flush X request queue to server */
        XFlush (display);
        while (1) {
		n = scheme_block_until(ready, wakeup, (Scheme_Object *)fdset, 0);
		fprintf(stderr, "XNextEvent\n");
                XNextEvent(display, &event);
		switch (event.type) {
			case MotionNotify:
				fprintf(stderr, "motion %d\n", move++);
				while (1) {
					if (QLength(display) == 0)
						break;
					XPeekEvent(display, &nextevent);
					if (nextevent.type != MotionNotify)
						break;
					XNextEvent(display, &event);
				        fprintf(stderr, "motion %d\n", move++);
				}
				break;
			case ButtonPress:
				fprintf(stderr, "press\n");
				break;
			case ButtonRelease:
				fprintf(stderr, "release\n");
				break;
		}
        }

	XDestroyWindow(display, window);
	XCloseDisplay (display);

        return scheme_true;
}

Scheme_Object *scheme_reload(Scheme_Env *env)
{
  scheme_add_global("simple",
    scheme_make_prim_w_arity(sch_simple, "simple", 0, 0), env);
  return scheme_make_string("Hello Simple!");
}

Scheme_Object *scheme_initialize(Scheme_Env *env)
{
  return scheme_reload(env);
}

Scheme_Object *scheme_module_name()
{
  /* This extension doesn't define a module: */
  return scheme_false;
}

============ makefile ============
PLT=/home/hollan/plt

CFLAGS=-I$(PLT)/include -I/usr/X11R6/include
CC=gcc

simple.so: simple.o
	gcc -shared -o simple.so simple.o $(PLT)/lib/gcc/mzdyn.o -L/usr/X11R6/lib -lX11

clean:
	-rm -f *.o *.so

============= simple.ss ===============
(load-extension "simple.so")

(parameterize
  ((current-eventspace (make-eventspace)))
  (thread
    (lambda()
      (simple)
      (display "bye"))))



Posted on the users mailing list.