From b2c69322a3c8d80b1e39a5fff0529b80df0e0105 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Niels=20M=C3=B6ller?= <nisse@lysator.liu.se>
Date: Tue, 23 Dec 1997 22:52:33 +0100
Subject: [PATCH] Opening a display works.

Rev: lib/modules/Protocols.pmod/X.pmod/Xlib.pmod:1.2
Rev: lib/modules/Protocols.pmod/X.pmod/_Xlib.pmod:1.1
Rev: lib/modules/Protocols.pmod/X.pmod/my_struct.pmod:1.1
---
 lib/modules/Protocols.pmod/X.pmod/Xlib.pmod   | 335 ++++++++++++++++--
 lib/modules/Protocols.pmod/X.pmod/_Xlib.pmod  |   6 +
 .../Protocols.pmod/X.pmod/my_struct.pmod      | 138 ++++++++
 3 files changed, 456 insertions(+), 23 deletions(-)
 create mode 100644 lib/modules/Protocols.pmod/X.pmod/_Xlib.pmod
 create mode 100644 lib/modules/Protocols.pmod/X.pmod/my_struct.pmod

diff --git a/lib/modules/Protocols.pmod/X.pmod/Xlib.pmod b/lib/modules/Protocols.pmod/X.pmod/Xlib.pmod
index 9176c3dd27..81581e0397 100644
--- a/lib/modules/Protocols.pmod/X.pmod/Xlib.pmod
+++ b/lib/modules/Protocols.pmod/X.pmod/Xlib.pmod
@@ -2,24 +2,93 @@
  *
  */
 
-object display_re = Regexp("([^:]+):([0-9]+).([0-9]+)");
-
 #define error(x) throw( ({ (x), backtrace() }) )
 
-constant XPORT 6000
-constant STATE_WAIT_FOR_CONNECT
+constant XPORT = 6000;
+
+class rec_buffer
+{
+  string buffer;
+  int expected;
+  int pad;
+
+  void create()
+  {
+    buffer = "";
+    expected = pad = 0;
+  }
+
+  void expect(int e)
+  {
+    expected = e;
+  }
+
+  int needs()
+  {
+    return pad + expected - strlen(buffer);
+  }
+  
+  void add_data(string data)
+  {
+    buffer += data;
+  }
+
+  string get_msg()
+  {
+    if (strlen(buffer) < (expected + pad))
+      return 0;
+    string res = buffer[pad..pad+expected-1];
+    buffer = buffer[pad+expected..];
+    pad = (- expected) % 4;
+    return res;
+  }
+}
 
 class Display
 {
+  import _Xlib;
+  
   inherit Stdio.File;
+  program Struct = my_struct.struct;
+  
+  constant STATE_WAIT_CONNECT = 0;
+  constant STATE_WAIT_CONNECT_DATA = 1;
+  constant STATE_WAIT_CONNECT_FAILURE = 2;
+  constant STATE_WAIT_HEADER = 3;
+  constant STATE_WAIT_REPLY = 4;
+  constant STATE_CLOSED = 4711;
+
+  constant ACTION_CONNECT = 0;
+  constant ACTION_CONNECT_FAILED = 1;
+  constant ACTION_EVENT = 2;
+  constant ACTION_REPLY = 3;
+  constant ACTION_ERROR = 4;
+  
   int screen_number;
 
   string buffer;
-
+  object received;
+  int state;
+  
   function io_error_handler;
   function error_handler;
   function close_handler;
-  function connected_callback;
+  function connect_handler;
+  function event_handler;
+  function reply_handler;
+  
+  /* Information about the X server */
+  int majorVersion, minorVersion;  /* Servers protocol version */
+  int release;
+  int ridBase, ridMask;
+  int motionBufferSize;
+  string vendor;
+  int maxRequestSize;
+  array roots;
+  array formats;
+  int imageByteOrder, bitmapBitOrder;
+  int bitmapScanlineUnit, bitmapScanlinePad;
+  int minKeyCode, maxKeyCode;
   
   void write_callback()
   {
@@ -34,7 +103,7 @@ class Display
       {
 	buffer = buffer[written..];
 	if (!strlen(buffer))
-	  buffer->set_write_callback(0);
+	  set_write_callback(0);
       }
   }
 
@@ -45,19 +114,204 @@ class Display
       set_write_callback(write_callback);
   }
 
+  void default_error_handler(object me, mapping e)
+  {
+    error(sprintf("Xlib: Unhandled error %O\n", e));
+  }
+
+  string previous;  /* Partially read reply or event */
+
+  array got_data(string data)
+  {
+    received->add_data(data);
+    string msg;
+    while (msg = received->get_msg())
+      switch(state)
+	{
+	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;
+	}
+	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);
+	  
+	  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;
+	    }
+
+	  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);
+	      
+	      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);
+		  
+		  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 });
+	}
+	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;
+	      
+	      break;
+	    }
+	    case 11:  /* KeymapNotify */
+	      return ({ ACTION_EVENT,
+			  ([ "type" : "KeymapNotify",
+			   "map" : msg[1..] ]) });
+	      
+	    default:  /* Any other event */
+	      return ({ ACTION_EVENT,
+			  ([ "type" : "Unimplemented",
+			   "raw" : msg ]) });
+	      
+	    }
+	  break;
+	case STATE_WAIT_REPLY:
+	  state = STATE_WAIT_HEADER;
+	  received->expect(32);
+	  return ({ ACTION_REPLY, previous + msg });
+	}
+    return 0;
+  }
+  
   void read_callback(mixed id, string data)
   {
-    switch(state)
-      {
-      case WAIT_FOR_CONNECT:
-	
-      }
+    array a = got_data(data);
+    if (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(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]);
+	  break;
+	default:
+	  error("Xlib.Display->read_callback: internal error\n");
+	  break;
+	}
   }
 
   void close_callback(mixed id)
   {
-    if (state == WAIT_FOR_CONNECT)
-      connected_handler(this_object(), 0);
+    if (state == STATE_WAIT_CONNECT)
+      connect_handler(this_object(), 0);
     else
       if (close_handler)
 	close_handler(this_object());
@@ -68,25 +322,60 @@ class Display
   
   int open(string|void display)
   {
+    int async = !!connect_handler;
+    
     display = display || getenv("DISPLAY");
     if (!display)
       error("Xlib.pmod: $DISPLAY not set!\n");
 
-    array(string) fields display_re->split(display);
+    array(string) fields = display_re->split(display);
     if (!fields)
       error("Xlib.pmod: Invalid display name!\n");
 
-    set_nonblocking(0, 0, close_callback);
-    if (!connect(fields[0], XPORT + (int) fields[1]))
-      error("Xlib.pmod: Connection to " + display + " failed.\n");
+    string host = strlen(fields[0]) ? fields[0] : "localhost";
 
-    screen_number = (int) fields[2];
+    if (async)
+      /* Asynchronous connection */
+      set_nonblocking(0, 0, close_callback);
+    
+    if (!connect(host, XPORT + (int) fields[1]))
+      return 0;
 
+    screen_number = (int) fields[2];
+    
     buffer = "";
+    received = rec_buffer();
+    
     /* Always uses network byteorder (big endian) 
      * No authentication */
-    send(sprintf("B\0%2c%2c%2c%2c\0\0", 11, 0, 0, 0));
-    state = WAITING_FOR_CONNECT;
+    string msg = sprintf("B\0%2c%2c%2c%2c\0\0", 11, 0, 0, 0);
+    state = STATE_WAIT_CONNECT;
+    received->expect(8);
+    if (async)
+      {
+	send(msg);
+	set_nonblocking(read_callback, 0, close_callback);
+	return 1;
+      }
+    if (strlen(msg) != write(msg))
+      return 0;
+    while(1) {
+      string data = read(received->needs());
+      if (!data)
+	return 0;
+      array a = got_data(data);
+      if (!a)
+	continue;
+      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");
+	}
+    }
   }
-
-  
+}
diff --git a/lib/modules/Protocols.pmod/X.pmod/_Xlib.pmod b/lib/modules/Protocols.pmod/X.pmod/_Xlib.pmod
new file mode 100644
index 0000000000..930875a5e8
--- /dev/null
+++ b/lib/modules/Protocols.pmod/X.pmod/_Xlib.pmod
@@ -0,0 +1,6 @@
+/* _Xlib.pmod
+ *
+ * kluge
+ */
+
+object display_re = Regexp("([^:]*):([0-9]+).([0-9]+)");
diff --git a/lib/modules/Protocols.pmod/X.pmod/my_struct.pmod b/lib/modules/Protocols.pmod/X.pmod/my_struct.pmod
new file mode 100644
index 0000000000..c05e452a27
--- /dev/null
+++ b/lib/modules/Protocols.pmod/X.pmod/my_struct.pmod
@@ -0,0 +1,138 @@
+/* my_struct.pmod
+ *
+ * This should replace ADT.struct at some time.
+ */
+
+#define error(x) throw( ({ (x), backtrace() }) )
+// #include "error.h"
+
+class struct {     
+  string buffer;
+  int index;
+  
+  void create(void|string s)
+  {
+    buffer = s || "";
+    index = 0;
+  }
+
+  /* Return data, without removing it */
+  string contents()
+  {
+    buffer = buffer[index..];
+    index = 0;
+    return buffer;
+  }
+  
+  void add_data(string s)
+  {
+    buffer += s;
+  }
+
+  string pop_data()
+  {
+    string res = buffer;
+    create();
+    return res;
+  }
+  
+  void put_uint(int i, int len)
+  {
+    if (i<0)
+      error("ADT.struct->put_uint: negative argument.\n");
+
+    add_data(sprintf("%*c", len, i));
+  }
+
+  void put_var_string(string s, int len)
+  {
+    if ( (len <= 3) && (strlen(s) >= ({ -1, 0x100, 0x10000, 0x1000000 })[len] ))
+      error("ADT.struct->put_var_String: Field overflow.\n");
+    put_uint(strlen(s), len);
+    add_data(s);
+  }
+
+  void put_bignum(object i, int|void len)
+  {
+    if (i<0)
+      error("ADT.struct->put_ubignum: negative argument.\n");
+    put_var_string(i->digits(256), len || 2);
+  }
+
+  void put_fix_string(string s)
+  {
+    add_data(s);
+  }
+
+#if 0
+  void put_fix_array(array(int) data, int item_size)
+  {
+    foreach(data, int i)
+      put_int(i, item_size);
+  }
+
+  void put_var_array(array(int) data, int item_size, int len)
+  {
+    put_int(sizeof(data), len);
+    put_fix_array(data, item_size);
+  }
+#endif
+
+  int get_uint(int len)
+  {
+    mixed i;
+    if ( (strlen(buffer) - index) < len)
+      error("ADT.struct->get_uint: no data\n");
+    sscanf(buffer, "%*" + (string) index +"s%" + (string) len + "c", i);
+    index += len;
+    return i;
+  }
+
+  string get_fix_string(int len)
+  {
+    string res;
+  
+    if ((strlen(buffer) - index) < len)
+      error("ADT.struct->get_fix_string: no data\n");
+    res = buffer[index .. index + len - 1];
+    index += len;
+    return res;
+  }
+
+  string get_var_string(int len)
+  {
+    return get_fix_string(get_uint(len));
+  }
+
+  object get_bignum(int|void len)
+  {
+    return Gmp.mpz(get_var_string(len || 2), 256);
+  }
+
+  string get_rest()
+  {
+    string s = buffer[index..];
+    create();
+    return s;
+  }
+
+#if 0
+  array(mixed) get_fix_array(int item_size, int size)
+  {
+    array(mixed) res = allocate(size);
+    for(int i = 0; i<size; i++)
+      res[i] = get_int(item_size);
+    return res;
+  }
+
+  array(mixed) get_var_array(int item_size, int len)
+  {
+    return get_fix_array(item_size, get_int(len));
+  }
+#endif
+
+  int is_empty()
+  {
+    return (index == strlen(buffer));
+  }
+}
-- 
GitLab