From 148f150f9be5721993d70e05de9e6b352e589758 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Niels=20M=C3=B6ller?= <nisse@lysator.liu.se>
Date: Thu, 25 Dec 1997 18:57:38 +0100
Subject: [PATCH] Successfully opens a window

Rev: lib/modules/Protocols.pmod/X.pmod/Requests.pmod:1.1
Rev: lib/modules/Protocols.pmod/X.pmod/Types.pmod:1.1
Rev: lib/modules/Protocols.pmod/X.pmod/Xlib.pmod:1.3
Rev: lib/modules/Protocols.pmod/X.pmod/_Xlib.pmod:1.2
---
 .../Protocols.pmod/X.pmod/Requests.pmod       | 108 +++
 lib/modules/Protocols.pmod/X.pmod/Types.pmod  | 164 +++++
 lib/modules/Protocols.pmod/X.pmod/Xlib.pmod   | 631 +++++++++++++-----
 lib/modules/Protocols.pmod/X.pmod/_Xlib.pmod  |  84 +++
 4 files changed, 814 insertions(+), 173 deletions(-)
 create mode 100644 lib/modules/Protocols.pmod/X.pmod/Requests.pmod
 create mode 100644 lib/modules/Protocols.pmod/X.pmod/Types.pmod

diff --git a/lib/modules/Protocols.pmod/X.pmod/Requests.pmod b/lib/modules/Protocols.pmod/X.pmod/Requests.pmod
new file mode 100644
index 0000000000..c5c9285e5f
--- /dev/null
+++ b/lib/modules/Protocols.pmod/X.pmod/Requests.pmod
@@ -0,0 +1,108 @@
+/* Requests.pike
+ *
+ */
+
+#define error(x) throw( ({ (x), backtrace() }) )
+
+class request
+{
+  constant type = 0;
+  constant expect_reply = 0;
+
+  string build_value_list(mapping m, array(string) fields)
+  {
+    int mask = 0;
+    int bit = 1;
+    array v = ({ });
+
+    foreach(fields, string f)
+      {
+	if (!zero_type(m[f]))
+	  {
+	    v += ({ m[f] });
+	    mask |= bit;
+	    werror(sprintf("Request->build_value_list: field %s, mask = %x\n",
+			   f, mask));
+	  }
+	bit <<= 1;
+      }
+    return sprintf("%4c%@4c", mask, v);
+  }
+
+  string build_request(string req, void|int data)
+  {
+    if (strlen(req) % 4)
+      error("Xlib.request: internal error!\n");
+    return sprintf("%c%c%2c%s", type, data, 1 + strlen(req) / 4, req);
+  }
+
+  mixed handle_reply(string reply)
+  {
+    error("Xlib.request: unexpected reply!\n");
+    return 0;
+  }
+}
+
+class ResourceReq
+{
+  inherit request;
+  int id;
+
+  string to_string()
+  {
+    return build_request(sprintf("%4c", id));
+  }
+}
+
+class CreateWindow
+{
+  inherit request;
+  constant type = 1;
+
+  int depth;
+  
+  int wid;
+  int parent;
+
+  int x, y;
+  int width, height, borderWidth;
+
+  int c_class;
+  int visual;
+
+  mapping attributes = ([ ]);
+
+  string to_string()
+  {
+    return build_request
+      (sprintf("%4c%4c" "%2c%2c" "%2c%2c%2c" "%2c%4c" "%s",
+		      wid, parent,
+		      x, y,
+		      width, height, borderWidth,
+		      c_class, visual,
+		      build_value_list(attributes, _Xlib.window_attributes) ),
+       depth);
+  }
+}
+
+class ChangeWindowAttributes
+{
+  inherit request;
+  constant type = 2;
+
+  int window;
+  mapping attributes = ([ ]);
+
+  string to_string()
+  {
+    return build_request
+      (sprintf("%4c%s", window,
+	       build_value_list(attributes, _Xlib.window_attributes)));
+  }
+}
+
+class MapWindow
+{
+  inherit ResourceReq;
+  constant type = 8;
+}
diff --git a/lib/modules/Protocols.pmod/X.pmod/Types.pmod b/lib/modules/Protocols.pmod/X.pmod/Types.pmod
new file mode 100644
index 0000000000..2696397c72
--- /dev/null
+++ b/lib/modules/Protocols.pmod/X.pmod/Types.pmod
@@ -0,0 +1,164 @@
+/* Types.pmod
+ *
+ */
+
+class XResource
+{
+  object display;
+  int id;
+
+  void create(object d, int i)
+  {
+    display = d;
+    id = i;
+
+    display->remember_id(id, this_object());
+  }
+}
+
+class Visual
+{
+  inherit XResource;
+
+  int c_class;
+  int bitsPerRGB;
+  int colorMapEntries;
+  int redMask;
+  int greenMask;
+  int blueMask;
+}
+	      
+class Window
+{
+  inherit XResource;
+  object visual;
+  int currentInputMask;
+
+  mapping(string:function) event_callbacks = ([ ]);
+  
+  object CreateSimpleWindow_req(int x, int y, int width, int height,
+			 int border_width, int border, int background)
+  {
+    object req = Requests.CreateWindow();
+    req->depth = 0;  /* CopyFromParent */
+    req->wid = display->alloc_id();
+    req->parent = id;
+    req->x = x;
+    req->y = y;
+    req->width = width;
+    req->height = height;
+    req->borderWidth = border_width;
+    req->c_class = 0 ;  /* CopyFromParent */
+    req->visual = 0;    /* CopyFromParent */
+    req->attributes->background_pixel = background;
+    req->attributes->border_pixel = border;
+    return req;
+  }
+
+  object new(mixed ... args) /* Kludge */
+  {
+    return object_program(this_object())(@args);
+  }
+  
+  object CreateSimpleWindow(int ... args)
+  {
+    object req = CreateSimpleWindow_req(@args);
+    display->send_request(req);
+    
+    // object w = Window(display, req->wid);
+    object w = new(display, req->wid);
+    
+    w->visual = visual;
+    w->currentInputMask = 0;
+    return w;
+  }
+
+  void set_event_callback(string type, function f)
+  {
+    event_callbacks[type] = f;
+  }
+
+  function remove_event_callback(string type)
+  {
+    function f = event_callbacks[type];
+    m_delete(event_callbacks, type);
+  }
+
+  object SelectInput_req()
+  {
+    object req = Requests.ChangeWindowAttributes();
+    req->window = id;
+    req->attributes->event_mask = currentInputMask;
+    return req;
+  }
+
+  int event_mask_to_int(array(string) masks)
+  {
+    int mask = 0;
+    
+    foreach(masks, string type)
+      mask
+	|= _Xlib.event_masks[type];
+    return mask;
+  }
+
+  int SelectInput(string ... types)
+  {
+    // int old_mask = currentInputMask;
+    currentInputMask |= event_mask_to_int(types);
+    display->send_request(SelectInput_req());
+    return currentInputMask;
+  }
+
+  int DeselectInput(string ... types)
+  {
+    // int old_mask = currentInputMask;
+    currentInputMask &= ~event_mask_to_int(types);
+
+    display->send_request(SelectInput_req());
+    return currentInputMask;
+  }
+
+  object Map_req()
+  {
+    object req = Requests.MapWindow();
+    req->id = id;
+    return req;
+  }
+
+  void Map()
+  {
+    display->send_request(Map_req());
+  }
+}
+
+class Colormap
+{
+  inherit XResource;
+}
+
+class RootWindow
+{
+  inherit Window;
+
+  object defaultColorMap;
+  int whitePixel;
+  int blackPixel;
+  int pixWidth;
+  int pixHeight;
+  int mmWidth;
+  int mmHeight;
+  int minInstalledMaps;
+  int maxInstalledMaps;
+  int backingStore;
+  int saveUnders;
+  int rootDepth;
+  mapping depths;
+  
+  object new(mixed ... args) /* Kludge */
+  {
+    return Window(@args);
+  }
+}
+
+
diff --git a/lib/modules/Protocols.pmod/X.pmod/Xlib.pmod b/lib/modules/Protocols.pmod/X.pmod/Xlib.pmod
index 81581e0397..6698ae5bbb 100644
--- a/lib/modules/Protocols.pmod/X.pmod/Xlib.pmod
+++ b/lib/modules/Protocols.pmod/X.pmod/Xlib.pmod
@@ -44,11 +44,69 @@ class rec_buffer
   }
 }
 
+class async_request
+{
+  object req;
+  function callback;
+
+  void create(object r, function f)
+  {
+    req = r;
+    callback = f;
+  }
+
+  void handle_reply(int success, string reply)
+  {
+    callback(success, (success ? req->handle_reply : req->handle_error)(reply));
+  }
+}
+
+class id_manager
+{
+  int next_id;
+  int increment;
+  mapping resources;
+  
+  void create(int base, int mask)
+  {
+    next_id = base;
+    increment = 1;
+    while(!(increment & mask))
+      increment >>=1;
+    resources = ([]);
+  }
+
+  int alloc_id()
+  {
+    int id = next_id;
+    next_id += increment;
+    return id;
+  }
+
+  mixed lookup_id(int i)
+  {
+    return resources[i];
+  }
+
+  void remember_id(int id, mixed resource)
+  {
+    resources[id] = resource;
+  }
+
+  void forget_id(int id)
+  {
+    m_delete(resources, id);
+    /* Could make id available for reallocation */
+  }
+}
+
 class Display
 {
   import _Xlib;
   
   inherit Stdio.File;
+  inherit id_manager;
+  
   program Struct = my_struct.struct;
   
   constant STATE_WAIT_CONNECT = 0;
@@ -69,12 +127,14 @@ class Display
   string buffer;
   object received;
   int state;
+  int sequence_number;
   
   function io_error_handler;
   function error_handler;
   function close_handler;
   function connect_handler;
   function event_handler;
+  function misc_event_handler;
   function reply_handler;
   
   /* Information about the X server */
@@ -89,6 +149,11 @@ class Display
   int imageByteOrder, bitmapBitOrder;
   int bitmapScanlineUnit, bitmapScanlinePad;
   int minKeyCode, maxKeyCode;
+
+  mapping pending_requests; /* Pending requests */
+  object pending_actions;   /* Actions awaiting handling */
+
+  void create() { } /* Delay initialization of id_manager */
   
   void write_callback()
   {
@@ -109,205 +174,355 @@ class Display
 
   void send(string data)
   {
+    werror(sprintf("Xlib.Display->send: '%s'\n", data));
     buffer += data;
     if (strlen(buffer))
       set_write_callback(write_callback);
   }
 
+  int flush()
+  { /* FIXME: Not thread-safe */
+    set_blocking();
+    int result = 0;
+    mixed e = catch {
+      do {
+	int written = write(buffer);
+	if (written < 0)
+	  break;
+	buffer = buffer[written..];
+      
+	if (strlen(buffer)) 
+	  break;
+	set_write_callback(0);
+	result = 1;
+      } while(0);
+    };
+    set_nonblocking();
+    if (e)
+      throw(e);
+    // werror(sprintf("flush: result = %d\n", result));
+    return result;
+  }
+  
   void default_error_handler(object me, mapping e)
   {
     error(sprintf("Xlib: Unhandled error %O\n", e));
   }
 
-  string previous;  /* Partially read reply or event */
+  void default_event_handler(object me, mapping event)
+  {
+    int wid;
+    object w;
 
-  array got_data(string data)
+    if (event->wid && (w = lookup_id(event->wid))
+	&& (w->event_callbacks[event->type]))
+      w->event_callbacks[event->type](event);
+    else
+      if (misc_event_handler)
+	misc_event_handler(event);
+      else
+	werror(sprintf("Ignored event %s\n", event->type));
+  }
+  
+  mapping reply;  /* Partially read reply or event */
+
+  array process()
   {
-    received->add_data(data);
     string msg;
     while (msg = received->get_msg())
       switch(state)
 	{
-	case STATE_WAIT_CONNECT: {
-	  int success;
-	  int len_reason;
-	  int length;
+	case STATE_WAIT_CONNECT:
+	  {
+	    int success;
+	    int len_reason;
+	    int length;
 	  
-	  sscanf(msg, "%c%c%2c%2c%2c",
-		 success, len_reason,
-		 majorVersion, minorVersion, length);
-	  if (success)
-	    {
-	      received->expect(length*4);
-	      state = STATE_WAIT_CONNECT_DATA;
-	    } else {
-	      received->expect(len_reason);
-	      state = STATE_WAIT_CONNECT_FAILURE;
-	    }
-	  break;
-	}
+	    sscanf(msg, "%c%c%2c%2c%2c",
+		   success, len_reason,
+		   majorVersion, minorVersion, length);
+	    if (success)
+	      {
+		received->expect(length*4);
+		state = STATE_WAIT_CONNECT_DATA;
+	      } else {
+		received->expect(len_reason);
+		state = STATE_WAIT_CONNECT_FAILURE;
+	      }
+	    break;
+	  }
 	case STATE_WAIT_CONNECT_FAILURE:
 	  return ({ ACTION_CONNECT_FAILED, msg });
 	  close();
 	  state = STATE_CLOSED;
 	  break;
-	case STATE_WAIT_CONNECT_DATA: {
-	  int nbytesVendor, numRoots, numFormats;
-	  object struct  = Struct(msg);
-
-	  release = struct->get_uint(4);
-	  ridBase = struct->get_uint(4);
-	  ridMask = struct->get_uint(4);
-	  motionBufferSize = struct->get_uint(4);
-	  nbytesVendor = struct->get_uint(2);
-	  maxRequestSize = struct->get_uint(2);
-	  numRoots = struct->get_uint(1);
-	  numFormats = struct->get_uint(1);
-	  imageByteOrder = struct->get_uint(1);
-	  bitmapBitOrder = struct->get_uint(1);
-	  bitmapScanlineUnit = struct->get_uint(1);
-	  bitmapScanlinePad = struct->get_uint(1);
-	  minKeyCode = struct->get_uint(1);
-	  maxKeyCode = struct->get_uint(1);
-	  /* pad2 */ struct->get_fix_string(4);
+	case STATE_WAIT_CONNECT_DATA:
+	  {
+	    int nbytesVendor, numRoots, numFormats;
+	    object struct  = Struct(msg);
+
+	    release = struct->get_uint(4);
+	    ridBase = struct->get_uint(4);
+	    ridMask = struct->get_uint(4);
+	    id_manager::create(ridBase, ridMask);
+	    
+	    motionBufferSize = struct->get_uint(4);
+	    nbytesVendor = struct->get_uint(2);
+	    maxRequestSize = struct->get_uint(2);
+	    numRoots = struct->get_uint(1);
+	    numFormats = struct->get_uint(1);
+	    imageByteOrder = struct->get_uint(1);
+	    bitmapBitOrder = struct->get_uint(1);
+	    bitmapScanlineUnit = struct->get_uint(1);
+	    bitmapScanlinePad = struct->get_uint(1);
+	    minKeyCode = struct->get_uint(1);
+	    maxKeyCode = struct->get_uint(1);
+	    /* pad2 */ struct->get_fix_string(4);
 	  
-	  vendor = struct->get_fix_string(nbytesVendor);
-	  /* pad */ struct->get_fix_string( (- nbytesVendor) % 4);
+	    vendor = struct->get_fix_string(nbytesVendor);
+	    /* pad */ struct->get_fix_string( (- nbytesVendor) % 4);
 
-	  int i;
-	  formats = allocate(numFormats);
-	  for(i=0; i<numFormats; i++)
-	    {
-	      mapping m = ([]);
-	      m->depth = struct->get_uint(1);
-	      m->bitsPerPixel = struct->get_uint(1);
-	      m->scanLinePad = struct->get_uint(1);
-	      /* pad */ struct->get_fix_string(5);
-	      formats[i] = m;
-	    }
+	    int i;
+	    formats = allocate(numFormats);
+	    for(i=0; i<numFormats; i++)
+	      {
+		mapping m = ([]);
+		m->depth = struct->get_uint(1);
+		m->bitsPerPixel = struct->get_uint(1);
+		m->scanLinePad = struct->get_uint(1);
+		/* pad */ struct->get_fix_string(5);
+		formats[i] = m;
+	      }
 
-	  roots = allocate(numRoots);
-	  for(i=0; i<numRoots; i++)
-	    {
-	      mapping m = ([]);
-	      m->windowId = struct->get_uint(4);
-	      m->defaultColorMap = struct->get_uint(4);
-	      m->whitePixel = struct->get_uint(4);
-	      m->blackPixel = struct->get_uint(4);
-	      m->currentInputMask = struct->get_uint(4);
-	      m->pixWidth = struct->get_uint(2);
-	      m->pixHeight = struct->get_uint(2);
-	      m->mmWidth = struct->get_uint(2);
-	      m->mmHeight = struct->get_uint(2);
-	      m->minInstalledMaps = struct->get_uint(2);
-	      m->maxInstalledMaps = struct->get_uint(2);
-	      m->rootVisualID = struct->get_uint(4);
-	      m->backingStore = struct->get_uint(1);
-	      m->saveUnders = struct->get_uint(1);
-	      m->rootDepth = struct->get_uint(1);
-	      int nDepths = struct->get_uint(1);
+	    roots = allocate(numRoots);
+	    for(i=0; i<numRoots; i++)
+	      {
+		int wid = struct->get_uint(4);
+		object r = Types.RootWindow(this_object(), wid);
+		int cm = struct->get_uint(4);
+		r->defaultColorMap = Types.Colormap(this_object(), cm);
+		r->whitePixel = struct->get_uint(4);
+		r->blackPixel = struct->get_uint(4);
+		r->currentInputMask = struct->get_uint(4);
+		r->pixWidth = struct->get_uint(2);
+		r->pixHeight = struct->get_uint(2);
+		r->mmWidth = struct->get_uint(2);
+		r->mmHeight = struct->get_uint(2);
+		r->minInstalledMaps = struct->get_uint(2);
+		r->maxInstalledMaps = struct->get_uint(2);
+		int rootVisualID = struct->get_uint(4);
+		r->backingStore = struct->get_uint(1);
+		r->saveUnders = struct->get_uint(1);
+		r->rootDepth = struct->get_uint(1);
+		int nDepths = struct->get_uint(1);
 	      
-	      m->depths = allocate(nDepths);
-	      for (int j=0; j<nDepths; j++)
-		{
-		  mapping d = ([]);
-		  d->depth = struct->get_uint(1);
-		  /* pad */ struct->get_fix_string(1);
-		  int nVisuals = struct->get_uint(2);
-		  /* pad */ struct->get_fix_string(4);
+		r->depths = ([ ]);
+		for (int j=0; j<nDepths; j++)
+		  {
+		    mapping d = ([]);
+		    int depth = struct->get_uint(1);
+		    /* pad */ struct->get_fix_string(1);
+		    int nVisuals = struct->get_uint(2);
+		    /* pad */ struct->get_fix_string(4);
 		  
-		  d->visuals = allocate(nVisuals);
-		  for(int k=0; k<nVisuals; k++)
-		    {
-		      mapping v = ([]);
-		      v->visualID = struct->get_uint(4);
-		      v->c_class = struct->get_uint(1);
-		      v->bitsPerRGB = struct->get_uint(1);
-		      v->colorMapEntries = struct->get_uint(2);
-		      v->redMask = struct->get_uint(4);
-		      v->greenMask = struct->get_uint(4);
-		      v->blueMask = struct->get_uint(4);
-		      /* pad */ struct->get_fix_string(4);
-		      d->visuals[k] = v;
-		    }
-		  m->depths[j] = d;
-		}
-	      roots[i] = m;
-	    }
-	  state = STATE_WAIT_HEADER;
-	  received->expect(32);
-	  return ({ ACTION_CONNECT });
-	}
+		    array visuals = allocate(nVisuals);
+		    for(int k=0; k<nVisuals; k++)
+		      {
+			int visualID = struct->get_uint(4);
+			object v = Types.Visual(this_object(), visualID);
+			v->c_class = struct->get_uint(1);
+			v->bitsPerRGB = struct->get_uint(1);
+			v->colorMapEntries = struct->get_uint(2);
+			v->redMask = struct->get_uint(4);
+			v->greenMask = struct->get_uint(4);
+			v->blueMask = struct->get_uint(4);
+			/* pad */ struct->get_fix_string(4);
+			visuals[k] = v;
+		      }
+		    r->depths[depth] = visuals;
+		  }
+		r->visual = lookup_id(rootVisualID);
+		roots[i] = r;
+	      }
+	    if (screen_number >= numRoots)
+	      werror("Xlib.Display->process: screen_number too large.\n");
+	    state = STATE_WAIT_HEADER;
+	    received->expect(32);
+	    return ({ ACTION_CONNECT });
+	  }
 	case STATE_WAIT_HEADER:
-	  received->expect(32); /* Correct for most of the cases */
-	  switch(msg[0])
-	    {
-	    case 0: { /* X_Error */
-	      mapping m = ([]);
-	      sscanf(msg, "%*c%c%2c%4c%2c%c",
-		     m->errorCode, m->sequence_number, m->resourceID,
-		     m->minorCode, m->majorCode);
-	      return ({ ACTION_ERROR, m });
-	    }
-	    case 1: { /* X_Reply */
-	      int length;
-	      sscanf(msg[2..3], "%2c", length);
-	      if (!length)
-		return ({ ACTION_REPLY, msg });
-
-	      state = STATE_WAIT_REPLY;
-	      received->expect(length*4);
-	      previous = msg;
+	  {
+	    received->expect(32); /* Correct for most of the cases */
+	    string type = _Xlib.event_types[msg[0]];
+	    switch(type)
+	      {
+	      case "Error":
+		{
+		  mapping m = ([]);
+		  sscanf(msg, "%*c%c%2c%4c%2c%c",
+			 m->errorCode, m->sequenceNumber, m->resourceID,
+			 m->minorCode, m->majorCode);
+		  return ({ ACTION_ERROR, m });
+		}
+	      case "Reply":
+		{
+		  reply = ([]);
+		  int length;
 	      
-	      break;
-	    }
-	    case 11:  /* KeymapNotify */
-	      return ({ ACTION_EVENT,
-			  ([ "type" : "KeymapNotify",
-			   "map" : msg[1..] ]) });
+		  sscanf(msg, "%*c%c%2c%4c%s",
+			 reply->data1, reply->sequenceNumber, length,
+			 reply->rest);
+		  if (!length)
+		    return ({ ACTION_REPLY, reply });
+
+		  state = STATE_WAIT_REPLY;
+		  received->expect(length*4);
 	      
-	    default:  /* Any other event */
-	      return ({ ACTION_EVENT,
-			  ([ "type" : "Unimplemented",
-			   "raw" : msg ]) });
+		  break;
+		}
+#if 0 
+	      case "KeyPress":
+	      case "KeyRelease":
+	      case "ButtonPress":
+	      case "ButtonRelease":
+	      case "MotionNotify":
+		...
+	      case "EnterNotify":
+	      case "LeaveNotify":
+		...
+	      case "FocusIn":
+	      case "FocusOut":
+		...
+#endif
+	      case "KeymapNotify":
+		return ({ ACTION_EVENT,
+			    ([ "type" : "KeymapNotify",
+			     "map" : msg[1..] ]) });
+#if 0		
+	      case "Expose":
+		...
+	      case "GraphicsExpose":
+		...
+	      case "NoExpose":
+		...
+	      case "VisibilityNotify":
+		...
+	      case "CreateNotify":
+		...
+	      case "DestroyNotify":
+		...
+	      case "UnmapNotify":
+		...
+	      case "MapNotify":
+		...
+	      case "MapRequest":
+		...
+	      case "ReparentNotify":
+		...
+	      case "ConfigureNotify":
+		...
+	      case "ConfigureRequest":
+		...
+	      case "GravityNotify":
+		...
+	      case "ResizeRequest":
+		...
+	      case "CirculateNotify":
+		...
+	      case "CirculateRequest":
+		...
+	      case "PropertyNotify":
+		...
+	      case "SelectionClear":
+		...
+	      case "SelectionRequest":
+		...
+	      case "SelectionNotify":
+		...
+	      case "ColormapNotify":
+		...
+	      case "ClientMessage":
+		...
+	      case "MappingNotify":
+#endif		
+	      default:  /* Any other event */
+		return ({ ACTION_EVENT,
+			    ([ "type" : "Unimplemented",
+			     "name" : type,
+			     "raw" : msg ]) });
 	      
-	    }
-	  break;
+	      }
+	    break;
+	  }
 	case STATE_WAIT_REPLY:
+	  reply->rest += msg;
 	  state = STATE_WAIT_HEADER;
 	  received->expect(32);
-	  return ({ ACTION_REPLY, previous + msg });
+	  return ({ ACTION_REPLY, reply });
 	}
     return 0;
   }
-  
-  void read_callback(mixed id, string data)
+
+  void handle_action(array a)
   {
-    array a = got_data(data);
-    if (a)
-      switch(a[0])
+    // werror(sprintf("Xlib.Display->handle_action: %O\n", a));
+    switch(a[0])
+      {
+      case ACTION_CONNECT:
+	connect_handler(this_object(), 1);
+	break;
+      case ACTION_CONNECT_FAILED:
+	connect_handler(this_object(), 0, a[1]);
+	break;
+      case ACTION_EVENT:
+	(event_handler || default_event_handler)(this_object(), a[1]);
+	break;
+      case ACTION_REPLY:
 	{
-	case ACTION_CONNECT:
-	  connect_handler(this_object(), 1);
-	  break;
-	case ACTION_CONNECT_FAILED:
-	  connect_handler(this_object(), 0, a[1]);
-	  break;
-	case ACTION_EVENT:
-	  event_handler(this_object(), a[1]);
-	  break;
-	case ACTION_REPLY:
-	  reply_handler(this_object(), a[1]);
-	  break;
-	case ACTION_ERROR:
-	  (error_handler || default_error_handler)(this_object(), a[1]);
+	  mixed handler = pending_requests[a[1]->sequenceNumber];
+	  if (handler)
+	    {
+	      m_delete(pending_requests, a[1]->sequenceNumber);
+	      handler->handle_reply(1, a[1]);
+	    }
+	  else
+	    reply_handler(this_object(), a[1]);
 	  break;
-	default:
-	  error("Xlib.Display->read_callback: internal error\n");
+	}
+      case ACTION_ERROR:
+	{
+	  mixed handler = pending_requests[a[1]->sequenceNumber];
+	  if (handler)
+	    {
+	      m_delete(pending_requests, a[1]->sequenceNumber);
+	      handler->handle_reply(0, a[1]);
+	    }
+	  else
+	    (error_handler || default_error_handler)(this_object(), a[1]);
 	  break;
 	}
+      default:
+	error("Xlib.Display->handle_action: internal error\n");
+	break;
+      }
+  }
+  
+  void read_callback(mixed id, string data)
+  {
+    received->add_data(data);
+    array a;
+    while(a = process())
+      handle_action(a);
   }
 
+  void process_pending_actions()
+  {
+    array a;
+    while(a = pending_actions->get())
+      handle_action(a);
+    set_read_callback(read_callback);
+  }
+	  
   void close_callback(mixed id)
   {
     if (state == STATE_WAIT_CONNECT)
@@ -335,9 +550,10 @@ class Display
     string host = strlen(fields[0]) ? fields[0] : "localhost";
 
     if (async)
-      /* Asynchronous connection */
-      set_nonblocking(0, 0, close_callback);
-    
+      { /* Asynchronous connection */
+	open_socket();
+	set_nonblocking(0, 0, close_callback);
+      }
     if (!connect(host, XPORT + (int) fields[1]))
       return 0;
 
@@ -345,37 +561,106 @@ class Display
     
     buffer = "";
     received = rec_buffer();
+    pending_requests = ([]);
+    pending_actions = ADT.queue();
+    sequence_number = 1;
     
     /* Always uses network byteorder (big endian) 
      * No authentication */
     string msg = sprintf("B\0%2c%2c%2c%2c\0\0", 11, 0, 0, 0);
     state = STATE_WAIT_CONNECT;
     received->expect(8);
+    send(msg);
     if (async)
       {
-	send(msg);
-	set_nonblocking(read_callback, 0, close_callback);
+	set_read_callback(read_callback);
 	return 1;
       }
-    if (strlen(msg) != write(msg))
+    if (!flush())
       return 0;
     while(1) {
       string data = read(received->needs());
       if (!data)
 	return 0;
-      array a = got_data(data);
-      if (!a)
-	continue;
-      switch(a[0])
+      received->add_data(data);
+      array a = process();
+      if (a)
+	switch(a[0])
+	  {
+	  case ACTION_CONNECT:
+	    set_nonblocking(read_callback, 0, close_callback);
+	    return 1;
+	  case ACTION_CONNECT_FAILED:
+	    return 0;
+	  default:
+	    error("Xlib.Display->open: Internal error!\n");
+	  }
+    }
+  }
+
+  int send_request(object req, mixed|void handler)
+  {
+    send(req->to_string());
+    return sequence_number++;
+  }
+
+  mixed blocking_request(object req)
+  {
+    mixed result = 0;
+    int done = 0;
+
+    int n = send_request(req);
+    flush();
+    set_blocking();
+
+    mixed e = catch {
+      while(!done)
 	{
-	case ACTION_CONNECT:
-	  set_nonblocking(read_callback, 0, close_callback);
-	  return 1;
-	case ACTION_CONNECT_FAILED:
-	  return 0;
-	default:
-	  error("Xlib.Display->open: Internal error!\n");
+	  string data = read(0x7fffffff, 1);
+	  if (!data)
+	    break;
+	  received->add_data(data);
+	  array a;
+	  while (!done && (a = process()))
+	    {
+	      if ((a[0] == ACTION_REPLY)
+		  && (a[1]->sequenceNumber == n))
+		{
+		  result = req->handle_reply(a[1]);
+		  done = 1;
+		  break;
+		}
+	      else if ((a[0] == ACTION_ERROR)
+		       && (a[1]->sequenceNumber == n))
+		{
+		  result = req->handle_error(a[1]);
+		  done = 1;
+		  break;
+		}
+	      /* Enqueue all other actions */
+	      pending_actions->put(a);
+	    }
 	}
-    }
+    };
+    set_nonblocking();
+    if (e)
+      throw(e);
+    if (pending_actions->size())
+      {
+	set_read_callback(0);
+	call_out(process_pending_actions, 0);
+      }
+    return result;
+  }
+  
+  void send_async_request(object req, function callback)
+  {
+    int n = send_request(req);
+    pending_requests[n] == async_request(req, callback);
+  }
+
+  object DefaultRootWindow()
+  {
+    return roots[screen_number];
   }
 }
diff --git a/lib/modules/Protocols.pmod/X.pmod/_Xlib.pmod b/lib/modules/Protocols.pmod/X.pmod/_Xlib.pmod
index 930875a5e8..6d6471fac8 100644
--- a/lib/modules/Protocols.pmod/X.pmod/_Xlib.pmod
+++ b/lib/modules/Protocols.pmod/X.pmod/_Xlib.pmod
@@ -4,3 +4,87 @@
  */
 
 object display_re = Regexp("([^:]*):([0-9]+).([0-9]+)");
+
+array(string) window_attributes =
+({ "background_pixmap",
+   "background_pixel",
+   "border_pixmap",
+   "border_pixel",
+   "bit_gravity",
+   "win_gravity",
+   "backing_store",
+   "backing_bit_planes",
+   "backing_pixel",
+   "override_redirect",
+   "save_under",
+   "event_mask",
+   "do_not_propagate_mask",
+   "colormap",
+   "cursor" });
+
+mapping(string:int) event_masks =
+([
+  "KeyPress" : 1<<0,
+  "KeyRelease" : 1<<1,
+  "ButtonPress" : 1<<2,
+  "ButtonRelease" : 1<<3,
+  "EnterWindow" : 1<<4,
+  "LeaveWindow" : 1<<5,
+  "PointerMotion" : 1<<6,
+  "PointerMotionHint" : 1<<7,
+  "Button1Motion" : 1<<8,
+  "Button2Motion" : 1<<9,
+  "Button3Motion" : 1<<10,
+  "Button4Motion" : 1<<11,
+  "Button5Motion" : 1<<12,
+  "ButtonMotion" : 1<<13,
+  "KeymapState" : 1<<14,
+  "Exposure" : 1<<15,
+  "VisibilityChange" : 1<<16,
+  "StructureNotify" : 1<<17,
+  "ResizeRedirect" : 1<<18,
+  "SubstructureNotify" : 1<<19,
+  "SubstructureRedirect" : 1<<20,
+  "FocusChange" : 1<<21,
+  "PropertyChange" : 1<<22,
+  "ColormapChange" : 1<<23,
+  "OwnerGrabButton" : 1<<24
+ ]);
+
+array(string) event_types =
+({
+  "Error",
+  "Reply",
+  "KeyPress",
+  "KeyRelease",
+  "ButtonPress",
+  "ButtonRelease",
+  "MotionNotify",
+  "EnterNotify",
+  "LeaveNotify",
+  "FocusIn",
+  "FocusOut",
+  "KeymapNotify",
+  "Expose",
+  "GraphicsExpose",
+  "NoExpose",
+  "VisibilityNotify",
+  "CreateNotify",
+  "DestroyNotify",
+  "UnmapNotify",
+  "MapNotify",
+  "MapRequest",
+  "ReparentNotify",
+  "ConfigureNotify",
+  "ConfigureRequest",
+  "GravityNotify",
+  "ResizeRequest",
+  "CirculateNotify",
+  "CirculateRequest",
+  "PropertyNotify",
+  "SelectionClear",
+  "SelectionRequest",
+  "SelectionNotify",
+  "ColormapNotify",
+  "ClientMessage",
+  "MappingNotify" });
-- 
GitLab