diff -ruN vnc_unixsrc/include/rfbproto.h vnc_unixsrc-col/include/rfbproto.h
--- vnc_unixsrc/include/rfbproto.h	2002-10-29 06:18:20.000000000 -0800
+++ vnc_unixsrc-col/include/rfbproto.h	2005-02-09 09:26:42.000000000 -0800
@@ -155,12 +155,14 @@
 
 #define rfbProtocolVersionFormat "RFB %03d.%03d\n"
 #define rfbProtocolMajorVersion 3
-#define rfbProtocolMinorVersion 3
+#define rfbProtocolMinorVersion 4
 
 typedef char rfbProtocolVersionMsg[13];	/* allow extra byte for null */
 
 #define sz_rfbProtocolVersionMsg 12
 
+#define rfbVersionIsCollaborative(major,minor) (1000*major+minor >= 3004)
+
 
 /*-----------------------------------------------------------------------------
  * Authentication
@@ -214,9 +216,11 @@
 
 typedef struct {
     CARD8 shared;
+    CARD8 nameLength;
 } rfbClientInitMsg;
 
-#define sz_rfbClientInitMsg 1
+#define sz_rfbClientInitMsg 2
+#define sz_old_rfbClientInitMsg 1
 
 
 /*-----------------------------------------------------------------------------
@@ -232,10 +236,16 @@
     CARD16 framebufferHeight;
     rfbPixelFormat format;	/* the server's preferred pixel format */
     CARD32 nameLength;
+    CARD16 memberID;
+    CARD8 collaborate;
+    CARD8 cursorColorRed;
+    CARD8 cursorColorGreen;
+    CARD8 cursorColorBlue;
     /* followed by char name[nameLength] */
 } rfbServerInitMsg;
 
-#define sz_rfbServerInitMsg (8 + sz_rfbPixelFormat)
+#define sz_rfbServerInitMsg (14 + sz_rfbPixelFormat)
+#define sz_old_rfbServerInitMsg (8 + sz_rfbPixelFormat)
 
 
 /*
@@ -265,7 +275,11 @@
 #define rfbSetColourMapEntries 1
 #define rfbBell 2
 #define rfbServerCutText 3
-
+#define rfbReleaseFloorRequest 4
+#define rfbTakeFloor 5
+#define rfbUpdateMemberList 6
+#define rfbShowMemberPointer 7
+#define rfbMoveMemberPointer 8
 
 /* client -> server */
 
@@ -276,7 +290,8 @@
 #define rfbKeyEvent 4
 #define rfbPointerEvent 5
 #define rfbClientCutText 6
-
+#define rfbTakeFloorRequest 7
+#define rfbReleaseFloor 8
 
 
 
@@ -710,6 +725,19 @@
 
 
 /*-----------------------------------------------------------------------------
+ * TakeFloor - Tell client to take the floor
+ */
+
+typedef struct {
+    CARD8 type;			/* always rfbTakeFloor */
+    CARD8 allow;
+} rfbTakeFloorMsg;
+
+#define sz_rfbTakeFloorMsg 2
+
+
+
+/*-----------------------------------------------------------------------------
  * ServerCutText - the server has new text in its cut buffer.
  */
 
@@ -725,6 +753,67 @@
 
 
 /*-----------------------------------------------------------------------------
+ * ReleaseFloorRequest - Ask this client to release the floor.
+ * If force is nonzero, this is not a request.  :]
+ */
+
+typedef struct {
+    CARD8 type;			/* always rfbReleaseFloorRequest */
+    CARD8 force;
+    CARD16 memberID;
+} rfbReleaseFloorRequestMsg;
+
+#define sz_rfbReleaseFloorRequestMsg 4
+
+
+/*-----------------------------------------------------------------------------
+ * MoveMemberPointer - Tell the client to move an inactive pointer.
+ */
+
+typedef struct {
+    CARD8 type;			/* always rfbMoveMemberPointer */
+    CARD8 pad1;
+    CARD16 memberID;
+    CARD16 x;
+    CARD16 y;
+} rfbMoveMemberPointerMsg;
+
+#define sz_rfbMoveMemberPointerMsg 8
+
+
+/*-----------------------------------------------------------------------------
+ * UpdateMemberList - Add or Remove a member
+ */
+
+typedef struct {
+    CARD8 type;          /* always rfbUpdateMemberList */
+    CARD8 add;		 /* 1 for add, 0 for remove */
+    CARD8 colorRed;
+    CARD8 colorGreen;
+    CARD8 colorBlue;
+    CARD8 master;        /* if 1 then this member is a master */
+    CARD16 memberID;
+    CARD32 length;       /* 0 when add=0 */
+    /* followed by char text[length] when add=1 */
+} rfbUpdateMemberListMsg;
+
+#define sz_rfbUpdateMemberListMsg 12
+
+
+/*-----------------------------------------------------------------------------
+ * ShowMemberPointer - Tell the client to show or hide a pointer.
+ */
+
+typedef struct {
+    CARD8 type;			/* always rfbShowMemberPointer */
+    CARD8 visible;
+    CARD16 memberID;
+} rfbShowMemberPointerMsg;
+
+#define sz_rfbShowMemberPointerMsg 4
+
+
+/*-----------------------------------------------------------------------------
  * Union of all server->client messages.
  */
 
@@ -734,10 +823,16 @@
     rfbSetColourMapEntriesMsg scme;
     rfbBellMsg b;
     rfbServerCutTextMsg sct;
+    rfbReleaseFloorRequestMsg rfr;
+    rfbTakeFloorMsg tf;
+    rfbMoveMemberPointerMsg mmp;
+    rfbShowMemberPointerMsg smp;
+    rfbUpdateMemberListMsg uml;
 } rfbServerToClientMsg;
 
 
 
+
 /*****************************************************************************
  *
  * Message definitions (client -> server)
@@ -892,6 +987,33 @@
 
 
 /*-----------------------------------------------------------------------------
+ * TakeFloorRequest - Ask this server to ask the other clients to release the floor.
+ */
+
+typedef struct {
+    CARD8 type;			/* always rfbTakeFloorRequest */
+} rfbTakeFloorRequestMsg;
+
+#define sz_rfbTakeFloorRequestMsg 1
+
+
+
+/*-----------------------------------------------------------------------------
+ * ReleaseFloor - Tell the server that this client released the floor.  
+ * Optionally specify another memberID to cede control to.
+ */
+
+typedef struct {
+    CARD8 type;			/* always rfbReleaseFloor */
+    CARD8 allow;
+    CARD16 memberID;		/* if non-0, give floor to this member */
+} rfbReleaseFloorMsg;
+
+#define sz_rfbReleaseFloorMsg 4
+
+
+
+/*-----------------------------------------------------------------------------
  * Union of all client->server messages.
  */
 
@@ -904,4 +1026,6 @@
     rfbKeyEventMsg ke;
     rfbPointerEventMsg pe;
     rfbClientCutTextMsg cct;
+    rfbTakeFloorRequestMsg tfr;
+    rfbReleaseFloorMsg rf;
 } rfbClientToServerMsg;
diff -ruN vnc_unixsrc/vncserver vnc_unixsrc-col/vncserver
--- vnc_unixsrc/vncserver	2003-07-31 07:19:37.000000000 -0700
+++ vnc_unixsrc-col/vncserver	2005-02-09 09:26:42.000000000 -0800
@@ -33,7 +33,7 @@
 # Global variables.  You may want to configure some of these for your site.
 #
 
-$geometry = "1024x768";
+$geometry = "600x800";
 $depth = 24;
 $desktopName = "X";
 $vncClasses = "/usr/local/vnc/classes";
@@ -81,7 +81,7 @@
 # Check command line options
 
 &ParseOptions("-geometry",1,"-depth",1,"-pixelformat",1,"-name",1,"-kill",1,
-	      "-help",0,"-h",0,"--help",0);
+	      "-exec",1,"-help",0,"-h",0,"--help",0);
 
 &Usage() if ($opt{'-help'} || $opt{'-h'} || $opt{'--help'});
 
@@ -211,6 +211,26 @@
 
 warn "\nNew '$desktopName' desktop is $host:$displayNumber\n\n";
 
+
+# If the unix domain socket exists then use that (DISPLAY=:n) otherwise use
+# TCP (DISPLAY=host:n)
+
+if (-e "/tmp/.X11-unix/X$displayNumber") {
+    $ENV{DISPLAY}= ":$displayNumber";
+} else {
+    $ENV{DISPLAY}= "$host:$displayNumber";
+}
+$ENV{VNCDESKTOP}= $desktopName;
+
+
+# Run the -exec argument instead of the xstartup script if -exec was passed
+if ($opt{'-exec'}) {
+    system("$opt{'-exec'} >> " . &quotedString($desktopLog) . " 2>&1");
+    $opt{'-kill'} = ":$displayNumber";
+    &Kill();
+    exit;
+}
+
 # Create the user's xstartup script if necessary.
 
 unless (-e "$xstartup") {
@@ -226,16 +246,6 @@
 warn "Starting applications specified in $xstartup\n";
 warn "Log file is $desktopLog\n\n";
 
-# If the unix domain socket exists then use that (DISPLAY=:n) otherwise use
-# TCP (DISPLAY=host:n)
-
-if (-e "/tmp/.X11-unix/X$displayNumber") {
-    $ENV{DISPLAY}= ":$displayNumber";
-} else {
-    $ENV{DISPLAY}= "$host:$displayNumber";
-}
-$ENV{VNCDESKTOP}= $desktopName;
-
 system("$xstartup >> " . &quotedString($desktopLog) . " 2>&1 &");
 
 exit;
diff -ruN vnc_unixsrc/vncviewer/argsresources.c vnc_unixsrc-col/vncviewer/argsresources.c
--- vnc_unixsrc/vncviewer/argsresources.c	2003-07-31 07:19:37.000000000 -0700
+++ vnc_unixsrc-col/vncviewer/argsresources.c	2005-02-09 09:26:42.000000000 -0800
@@ -47,6 +47,7 @@
 
   "*desktop.baseTranslations:\
      <Key>F8: ShowPopup()\\n\
+     <Key>F9: ToggleFloorControl()\\n\
      <ButtonPress>: SendRFBEvent()\\n\
      <ButtonRelease>: SendRFBEvent()\\n\
      <Motion>: SendRFBEvent()\\n\
@@ -74,7 +75,7 @@
   "*popup.buttonForm.translations: #override\\n\
      <KeyPress>: SendRFBEvent() HidePopup()",
 
-  "*popupButtonCount: 8",
+  "*popupButtonCount: 10",
 
   "*popup*button1.label: Dismiss popup",
   "*popup*button1.translations: #override\\n\
@@ -115,6 +116,12 @@
   "*popup*button8.translations: #override\\n\
      <Btn1Down>,<Btn1Up>: SendRFBEvent(key,F8) HidePopup()",
 
+  "*popup*button9.label: Send F9",
+  "*popup*button9.translations: #override\\n\
+     <Btn1Down>,<Btn1Up>: SendRFBEvent(key,F9) HidePopup()",
+
+  "*popup*button10.label: ---- Members: ----",
+
   NULL
 };
 
@@ -220,6 +227,15 @@
 
   {"grabKeyboard", "GrabKeyboard", XtRBool, sizeof(Bool),
    XtOffsetOf(AppData, grabKeyboard), XtRImmediate, (XtPointer) False},
+
+  {"autoReleaseDelay", "AutoReleaseDelay", XtRInt, sizeof(int),
+   XtOffsetOf(AppData, autoReleaseDelay), XtRImmediate, (XtPointer) 2},
+
+  {"name", "Name", XtRString, sizeof(String),
+   XtOffsetOf(AppData, name), XtRImmediate, (XtPointer) 0},
+
+  {"showPopups", "ShowPopups", XtRBool, sizeof(Bool),
+   XtOffsetOf(AppData, showPopups), XtRImmediate, (XtPointer) True},
 };
 
 
@@ -246,7 +262,12 @@
   {"-nojpeg",        "*enableJPEG",         XrmoptionNoArg,  "False"},
   {"-nocursorshape", "*useRemoteCursor",    XrmoptionNoArg,  "False"},
   {"-x11cursor",     "*useX11Cursor",       XrmoptionNoArg,  "True"},
-
+  {"-autoreleasedelay","*autoReleaseDelay", XrmoptionSepArg, 0},
+  {"-noautorelease", "*autoReleaseDelay",   XrmoptionNoArg, "-1"},
+  {"-ard",           "*autoReleaseDelay",   XrmoptionSepArg, 0},
+  {"-nar",           "*autoReleaseDelay",   XrmoptionNoArg, "-1"},
+  {"-name",          "*name",               XrmoptionSepArg, 0},
+  {"-nopopups",      "*showPopups",         XrmoptionNoArg,  "False"},
 };
 
 int numCmdLineOptions = XtNumber(cmdLineOptions);
@@ -262,6 +283,8 @@
     {"HidePopup", HidePopup},
     {"ToggleFullScreen", ToggleFullScreen},
     {"SetFullScreenState", SetFullScreenState},
+    {"SetFloorControlState", SetFloorControlState},
+    {"ToggleFloorControl", ToggleFloorControl},
     {"SelectionFromVNC", SelectionFromVNC},
     {"SelectionToVNC", SelectionToVNC},
     {"ServerDialogDone", ServerDialogDone},
@@ -311,7 +334,7 @@
 	  "        -noraiseonbeep\n"
 	  "        -passwd <PASSWD-FILENAME>\n"
 	  "        -encodings <ENCODING-LIST> (e.g. \"tight copyrect\")\n"
-	  "        -bgr233\n"
+	  "        -bgr233  (or -bgr)\n"
 	  "        -owncmap\n"
 	  "        -truecolour\n"
 	  "        -depth <DEPTH>\n"
@@ -320,6 +343,10 @@
 	  "        -nojpeg\n"
 	  "        -nocursorshape\n"
 	  "        -x11cursor\n"
+	  "        -autoreleasedelay <SECONDS>  (or -ard)\n"
+	  "        -noautorelease  (or -nar)\n"
+	  "        -name <USERNAME>  (your username by default)\n"
+	  "        -nopopups\n"
 	  "\n"
 	  "Option names may be abbreviated, e.g. -bgr instead of -bgr233.\n"
 	  "See the manual page for more information."
@@ -406,4 +433,5 @@
     }
     vncServerPort = atoi(colonPos + 1) + portOffset;
   }
+
 }
diff -ruN vnc_unixsrc/vncviewer/collaborate.c vnc_unixsrc-col/vncviewer/collaborate.c
--- vnc_unixsrc/vncviewer/collaborate.c	1969-12-31 16:00:00.000000000 -0800
+++ vnc_unixsrc-col/vncviewer/collaborate.c	2005-02-09 09:26:42.000000000 -0800
@@ -0,0 +1,262 @@
+/*
+ *  Copyright (C) 1999 AT&T Laboratories Cambridge.  All Rights Reserved.
+ *
+ *  This is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This software is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this software; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
+ *  USA.
+ */
+
+/*
+ * collaborate.c - code to handle collaborative
+ */
+
+#include <vncviewer.h>
+#include <X11/Xaw/Toggle.h>
+
+MemberPtr floorOwner = NULL;
+char *windowTitle;
+time_t lastEventTime;
+short held_x, held_y, held_buttonMask;
+
+MemberPtr memberHead = NULL;
+time_t lastOwnerNameTime = -1;
+
+
+void AddMember(short memberID, Bool master, int colorRed, int colorGreen, int colorBlue, char *name)
+{
+    MemberPtr newMember;
+    MemberPtr m;
+
+    newMember = GetMemberFromID(memberID);
+
+    if(newMember) {
+      if (newMember->name)
+	free(newMember->name);
+    } else { 
+      newMember = malloc(sizeof(Member));
+      newMember->memberID = memberID;
+      newMember->x = -1;
+      newMember->y = -1;
+      
+      if (!memberHead)
+	memberHead = newMember;
+      else {
+	if (memberHead->memberID > newMember->memberID) {
+	  newMember->next = memberHead;
+	  memberHead = newMember;
+	} else {
+	  for (m = memberHead; m; m = m->next) {
+	    if (m->next && m->next->memberID > newMember->memberID) {
+	      newMember->next = m->next;
+	      m->next = newMember;
+	      break;
+	    }
+	    if (m->next == NULL) {
+	      m->next = newMember;
+	      newMember->next = NULL;
+	    }
+	  }
+	}
+      }
+    }
+
+    newMember->master = master;
+    newMember->name = name;
+    newMember->colorRed = colorRed;
+    newMember->colorGreen = colorGreen;
+    newMember->colorBlue = colorBlue;
+}
+
+
+void RemoveMember(short memberID)
+{
+    MemberPtr m;
+    MemberPtr old;
+
+    if (!memberHead) return;
+
+    if (memberHead->memberID == memberID) {
+      old = memberHead;
+      memberHead = memberHead->next;
+      free(old->name);
+      free(old);
+      return;
+    }
+
+    for (m = memberHead; m; m = m->next) {
+      if (m->next && m->next->memberID == memberID) {
+	old = m->next;
+	m->next = m->next->next;
+	free(old->name);
+	free(old);
+	return;
+      }
+    }
+}
+
+
+MemberPtr GetMemberFromID(short memberID)
+{
+  MemberPtr m;
+
+  if (!memberHead)
+    return NULL;
+
+  for (m = memberHead; m; m = m->next) {
+    if (m->memberID == memberID)
+      return m;
+  }
+
+  return NULL;
+}
+
+
+void ShowMemberPointer(short memberID, Bool visible)
+{
+    int i;
+    int oldx, oldy;
+    MemberPtr m = GetMemberFromID(memberID);
+    
+    if (!m) return;
+
+    if (visible) {
+      DrawMemberPointer(m, -1, -1);
+    } else {
+      oldx = m->x;
+      oldy = m->y;
+      m->x = -1;
+      m->y = -1;
+      DrawMemberPointer(m, oldx, oldy);
+    }
+}
+
+
+void MoveMemberPointer(short memberID, int x, int y)
+{
+    int oldx, oldy;
+    MemberPtr m = GetMemberFromID(memberID);
+
+    if (!m) return;
+
+    oldx = m->x;
+    oldy = m->y;
+    m->x = x;
+    m->y = y;
+
+    DrawMemberPointer(m, oldx, oldy);
+}
+
+
+Bool TakeFloorRequest()
+{
+  if (appData.collaborate)
+    return SendTakeFloorRequestEvent(0);
+
+  return FALSE;
+}
+
+
+
+Bool ReleaseFloor(Bool allow, MemberPtr m)
+{
+  if (floorOwner == appData.me && allow)
+    floorOwner = m;
+
+  if (appData.collaborate)
+    return SendReleaseFloorEvent(allow, (m) ? m->memberID : 0);
+
+  return TRUE;
+}
+
+
+Bool CheckReleaseFloor(void)
+{
+  return (appData.autoReleaseDelay != -1 && difftime(time(NULL), lastEventTime) >= appData.autoReleaseDelay);
+}
+
+
+
+void UpdateTitleWithFloorOwner()
+{
+  char *title;
+  MemberPtr m;
+
+  if (floorOwner) {
+    title = XtMalloc(strlen(windowTitle) + strlen(floorOwner->name) + 19 + 1);
+    sprintf(title, "%s -- Controlled by: %s", windowTitle, floorOwner->name);
+    XtVaSetValues(toplevel, XtNtitle, title, XtNiconName, title, NULL);
+    XtFree(title);
+
+    if (appData.showPopups) {
+      if (lastOwnerNameTime != -1)
+	ClearFloorOwner();
+
+      lastOwnerNameTime = time(NULL);
+      DrawNewFloorOwner(floorOwner->name);
+    }
+  } else {
+    XtVaSetValues(toplevel, XtNtitle, windowTitle, XtNiconName, windowTitle, NULL);
+  }
+}
+
+
+void
+GiveFloorToMember(Widget w, XtPointer client_data, XtPointer call_data)
+{
+  if (!appData.collaborate)
+    return;
+
+  if(((MemberPtr)client_data) != floorOwner || !floorOwner || floorOwner == appData.me) { 
+    if (((MemberPtr)client_data) == appData.me) {
+      ToggleFloorControl(w, NULL, NULL, NULL);
+    } else {
+      ReleaseFloor(TRUE, ((MemberPtr)client_data));
+      UpdateTitleWithFloorOwner();
+    }
+  }
+  HidePopup(w, NULL, NULL, NULL);
+}
+
+
+/*
+ * SetFloorControlState is an action which sets the "state" resource of a toggle
+ * widget to reflect whether we're in control of this Desktop.
+ */
+
+void
+SetFloorControlState(Widget w, XEvent *ev, String *params, Cardinal *num_params)
+{
+  if (floorOwner == appData.me)
+    XtVaSetValues(w, XtNstate, True, NULL);
+  else
+    XtVaSetValues(w, XtNstate, False, NULL);
+}
+
+
+/*
+ * ToggleFloorControl is an action which toggles between requesting the floor, and yielding it
+ */
+
+void
+ToggleFloorControl(Widget w, XEvent *ev, String *params, Cardinal *num_params)
+{
+  if (appData.collaborate) {
+    if (floorOwner == appData.me) {
+      ReleaseFloor(TRUE, NULL);
+      UpdateTitleWithFloorOwner();
+    } else {
+      TakeFloorRequest();
+    }
+  }
+}
diff -ruN vnc_unixsrc/vncviewer/desktop.c vnc_unixsrc-col/vncviewer/desktop.c
--- vnc_unixsrc/vncviewer/desktop.c	2003-07-30 22:01:00.000000000 -0700
+++ vnc_unixsrc-col/vncviewer/desktop.c	2005-02-11 09:04:30.000000000 -0800
@@ -28,17 +28,23 @@
 #include <X11/extensions/XShm.h>
 #endif
 
+#define MIN(a,b) ((a<b)?a:b)
+#define MAX(a,b) ((a>b)?a:b)
+
 GC gc;
+XFontStruct *font;
+int fontHeight;
 GC srcGC, dstGC; /* used for debugging copyrect */
 Window desktopWin;
 Cursor dotCursor;
 Widget form, viewport, desktop;
+char *lastFloorName=NULL;
 
 static Bool modifierPressed[256];
 
-static XImage *image = NULL;
+XImage *image = NULL;
 
-static Cursor CreateDotCursor();
+static Cursor CreateDotCursor(char red, char green, char blue);
 static void CopyBGR233ToScreen(CARD8 *buf, int x, int y, int width,int height);
 static void HandleBasicDesktopEvent(Widget w, XtPointer ptr, XEvent *ev,
 				    Boolean *cont);
@@ -120,7 +126,11 @@
 
   desktopWin = XtWindow(desktop);
 
-  gc = XCreateGC(dpy,desktopWin,0,NULL);
+  font = XLoadQueryFont(dpy, "fixed");
+  fontHeight = font->max_bounds.ascent + font->max_bounds.descent;
+  gcv.font = font->fid;
+
+  gc = XCreateGC(dpy, desktopWin, GCFont, &gcv);
 
   gcv.function = GXxor;
   gcv.foreground = 0x0f0f0f0f;
@@ -136,7 +146,11 @@
   valuemask = CWBackingStore;
 
   if (!appData.useX11Cursor) {
-    dotCursor = CreateDotCursor();
+    if (appData.collaborate)
+      dotCursor = CreateDotCursor(appData.me->colorRed, 
+		appData.me->colorGreen, appData.me->colorBlue);
+    else
+      dotCursor = CreateDotCursor(0,0,0);
     attr.cursor = dotCursor;    
     valuemask |= CWCursor;
   }
@@ -260,7 +274,8 @@
 	x = atoi(params[1]);
 	y = atoi(params[2]);
 	buttonMask = atoi(params[3]);
-	SendPointerEvent(x, y, buttonMask);
+	if (!appData.collaborate || floorOwner == appData.me)
+	  SendPointerEvent(x, y, buttonMask);
       } else if (*num_params == 2) {
 	switch (ev->type) {
 	case ButtonPress:
@@ -279,7 +294,8 @@
 	  return;
 	}
 	buttonMask = atoi(params[1]);
-	SendPointerEvent(x, y, buttonMask);
+	if (!appData.collaborate || floorOwner == appData.me)
+	  SendPointerEvent(x, y, buttonMask);
       } else {
 	fprintf(stderr,
 		"Invalid params: SendRFBEvent(ptr,<x>,<y>,<buttonMask>)\n"
@@ -304,6 +320,14 @@
     return;
 
   case ButtonPress:
+    if (appData.collaborate && floorOwner != appData.me) {
+      held_x = ev->xbutton.x;
+      held_y = ev->xbutton.y;
+      held_buttonMask = (((ev->xbutton.state & 0x1f00) >> 8) |
+		      (1 << (ev->xbutton.button - 1)));
+      TakeFloorRequest();
+    }
+
     SendPointerEvent(ev->xbutton.x, ev->xbutton.y,
 		     (((ev->xbutton.state & 0x1f00) >> 8) |
 		      (1 << (ev->xbutton.button - 1))));
@@ -313,6 +337,10 @@
     SendPointerEvent(ev->xbutton.x, ev->xbutton.y,
 		     (((ev->xbutton.state & 0x1f00) >> 8) &
 		      ~(1 << (ev->xbutton.button - 1))));
+
+    if (floorOwner == appData.me) 
+      lastEventTime = time(NULL);
+
     return;
 
   case KeyPress:
@@ -325,6 +353,10 @@
     }
 
     SendKeyEvent(ks, (ev->type == KeyPress));
+
+    if (!appData.collaborate || floorOwner == appData.me) 
+      lastEventTime = time(NULL);
+
     return;
 
   default:
@@ -338,7 +370,7 @@
  */
 
 static Cursor
-CreateDotCursor()
+CreateDotCursor(char red, char green, char blue)
 {
   Cursor cursor;
   Pixmap src, msk;
@@ -348,8 +380,14 @@
 
   src = XCreateBitmapFromData(dpy, DefaultRootWindow(dpy), srcBits, 5, 5);
   msk = XCreateBitmapFromData(dpy, DefaultRootWindow(dpy), mskBits, 5, 5);
-  XAllocNamedColor(dpy, DefaultColormap(dpy,DefaultScreen(dpy)), "black",
-		   &fg, &fg);
+
+  fg.red = red <<8;
+  fg.green = green <<8;
+  fg.blue = blue <<8;
+
+  if (!XAllocColor(dpy, DefaultColormap(dpy,DefaultScreen(dpy)), &fg))   
+      exit(1);
+
   XAllocNamedColor(dpy, DefaultColormap(dpy,DefaultScreen(dpy)), "white",
 		   &bg, &bg);
   cursor = XCreatePixmapCursor(dpy, src, msk, &fg, &bg, 2, 2);
@@ -468,3 +506,118 @@
     break;
   }
 }
+
+
+void
+DrawMemberPointer(MemberPtr m, int oldx, int oldy)
+{
+    if (oldx != -1) {
+#ifdef MITSHM
+      if (appData.useShm)
+	XShmPutImage(dpy, desktopWin, gc, image,
+                  MAX(oldx-2, 0), MAX(oldy - 2, 0),
+		  MAX(oldx-2, 0), MAX(oldy - 2, 0),
+		  MIN(5,si.framebufferWidth-oldx +1),
+		  MIN(5,si.framebufferHeight-oldy +1), False);
+      else
+#endif
+	XPutImage(dpy, desktopWin, gc, image, MAX(oldx-2, 0), MAX(oldy - 2, 0),
+		  MAX(oldx-2, 0), MAX(oldy - 2, 0),
+		  MIN(5,si.framebufferWidth-oldx +1),
+		  MIN(5,si.framebufferHeight-oldy +1));
+      ClearName(m->name, oldx+5, oldy-5);
+    }
+    /* Re-draw the Floor Owner's name */
+    if (floorOwner && lastOwnerNameTime != -1)
+      DrawNewFloorOwner(floorOwner->name);
+
+    if (m->x != -1) {
+      XColor color;
+
+      color.red = m->colorRed <<8;
+      color.green = m->colorGreen <<8;
+      color.blue = m->colorBlue <<8;
+
+      if (!XAllocColor(dpy, DefaultColormap(dpy,DefaultScreen(dpy)), &color))
+          exit(1);
+
+      XSetForeground(dpy, gc, WhitePixel(dpy, DefaultScreen(dpy)));
+      XFillRectangle(dpy, desktopWin, gc, MAX(m->x-2, 0), MAX(m->y - 2, 0),
+		MIN(5,si.framebufferWidth - m->x +1),
+		MIN(5,si.framebufferHeight - m->y +1));
+
+      XSetForeground(dpy, gc, color.pixel);
+      XFillRectangle(dpy, desktopWin, gc, MAX(m->x-1, 1), MAX(m->y - 1, 1),
+		MIN(3,si.framebufferWidth - m->x),
+		MIN(3,si.framebufferHeight - m->y));
+
+      DrawName(m->name, m->x+5, m->y-5);
+
+      XSetForeground(dpy, gc, BlackPixel(dpy, DefaultScreen(dpy)));
+    }
+}
+
+
+void
+DrawNewFloorOwner(char *name)
+{
+    DrawName(name, 4,4);
+    lastFloorName = name;
+}
+
+
+void
+DrawName(char *name, int x, int y)
+{
+    XColor color;
+    char *displayname;
+
+    color.red = 0xe000;
+    color.green = 0xe000;
+    color.blue = 0xe000;
+
+    if (!XAllocColor(dpy, DefaultColormap(dpy,DefaultScreen(dpy)), &color))
+        exit(1);
+
+    XSetBackground(dpy, gc, color.pixel);
+    XSetForeground(dpy, gc, BlackPixel(dpy, DefaultScreen(dpy)));
+
+    displayname = malloc(strlen(name)+3);
+    sprintf(displayname, " %s ", name);
+    XDrawImageString(dpy, desktopWin, gc, x,MAX(0,y)+font->max_bounds.ascent, displayname, strlen(displayname));
+    free(displayname);
+}
+
+
+void
+ClearFloorOwner(void)
+{
+    ClearName(lastFloorName, 4,4);
+    lastOwnerNameTime = -1;
+}
+
+
+void
+ClearName(char *name, int x, int y)
+{
+    int width;
+    char *displayname;
+
+    if (name) {
+      displayname = malloc(strlen(name)+3);
+      sprintf(displayname, " %s ", name);
+      width = XTextWidth(font, displayname, strlen(displayname)); 
+      free(displayname);
+    } else {
+      width = 0;
+    }
+
+    if (width < 1) return;
+
+#ifdef MITSHM
+      if (appData.useShm)
+	XShmPutImage(dpy, desktopWin, gc, image, x,MAX(0,y), x,MAX(0,y), width,fontHeight, False);
+      else
+#endif
+	XPutImage(dpy, desktopWin, gc, image, x,MAX(0,y), x,MAX(0,y), width,fontHeight);
+}
diff -ruN vnc_unixsrc/vncviewer/Imakefile vnc_unixsrc-col/vncviewer/Imakefile
--- vnc_unixsrc/vncviewer/Imakefile	2002-06-27 22:53:19.000000000 -0700
+++ vnc_unixsrc-col/vncviewer/Imakefile	2005-02-09 09:26:42.000000000 -0800
@@ -40,7 +40,8 @@
   shm.c \
   sockets.c \
   tunnel.c \
-  vncviewer.c
+  vncviewer.c \
+  collaborate.c
 
 OBJS = $(SRCS:.c=.o)
 
diff -ruN vnc_unixsrc/vncviewer/misc.c vnc_unixsrc-col/vncviewer/misc.c
--- vnc_unixsrc/vncviewer/misc.c	2003-01-14 23:58:32.000000000 -0800
+++ vnc_unixsrc-col/vncviewer/misc.c	2005-02-09 09:26:42.000000000 -0800
@@ -33,6 +33,7 @@
 
 Dimension dpyWidth, dpyHeight;
 Atom wmDeleteWindow, wmState;
+char *windowTitle;
 
 static Bool xloginIconified = False;
 static XErrorHandler defaultXErrorHandler;
@@ -57,6 +58,9 @@
   sprintf(title, titleFormat, desktopName);
   XtVaSetValues(toplevel, XtNtitle, title, XtNiconName, title, NULL);
 
+  windowTitle = XtMalloc( strlen(title) + 1 );
+  strcpy(windowTitle, title);
+
   XtVaSetValues(toplevel, XtNmaxWidth, si.framebufferWidth,
 		XtNmaxHeight, si.framebufferHeight, NULL);
 
diff -ruN vnc_unixsrc/vncviewer/popup.c vnc_unixsrc-col/vncviewer/popup.c
--- vnc_unixsrc/vncviewer/popup.c	2000-06-11 05:00:53.000000000 -0700
+++ vnc_unixsrc-col/vncviewer/popup.c	2005-02-09 09:26:42.000000000 -0800
@@ -27,11 +27,13 @@
 #include <X11/Xaw/Command.h>
 #include <X11/Xaw/Toggle.h>
 
-Widget popup, fullScreenToggle;
+Widget popup = NULL, fullScreenToggle;
 
 void
 ShowPopup(Widget w, XEvent *event, String *params, Cardinal *num_params)
 {
+  if (popup) XtDestroyWidget(popup);
+  CreatePopup();
   XtMoveWidget(popup, event->xbutton.x_root, event->xbutton.y_root);
   XtPopup(popup, XtGrabNone);
   XSetWMProtocols(dpy, XtWindow(popup), &wmDeleteWindow, 1);
@@ -56,9 +58,11 @@
 {
   Widget buttonForm, button, prevButton = NULL;
   int i;
-  char buttonName[12];
+  char buttonName[32];
   String buttonType;
 
+  MemberPtr m;
+
   popup = XtVaCreatePopupShell("popup", transientShellWidgetClass, toplevel,
 			       NULL);
 
@@ -90,4 +94,37 @@
     }
     prevButton = button;
   }
+
+  XtSetSensitive(prevButton, False);
+
+  if (appData.collaborate) {
+    for (m = memberHead; m; m = m->next) {
+      buttonName[0] = (m == floorOwner) ? '*' : ' ';
+      buttonName[1] = ' ';
+      if (strlen(m->name) > 29) {
+	strncpy(buttonName+2, m->name, 13);
+	buttonName[15] = buttonName[16] = buttonName[17] = '.';
+	strcpy(buttonName+18, m->name+(strlen(m->name)-13));
+      } else {
+        strcpy(buttonName+2, m->name);
+      }
+      button = XtVaCreateManagedWidget (buttonName, commandWidgetClass, 
+					buttonForm, NULL);
+      XtVaSetValues (button, XtNfromVert, prevButton, XtNleft, 
+		     XawChainLeft, XtNright, XawChainRight, NULL);
+
+      if (floorOwner == appData.me || appData.me->master) {
+	if (m == floorOwner && floorOwner != appData.me)
+          XtSetSensitive(button, False);
+	else
+	  XtAddCallback(button, XtNcallback, GiveFloorToMember, m);
+      } else {
+	if (m == appData.me)
+	  XtAddCallback(button, XtNcallback, GiveFloorToMember, m);
+	else
+	  XtSetSensitive(button, False);
+      }
+      prevButton = button;
+    }
+  }
 }
diff -ruN vnc_unixsrc/vncviewer/rfbproto.c vnc_unixsrc-col/vncviewer/rfbproto.c
--- vnc_unixsrc/vncviewer/rfbproto.c	2003-01-15 01:46:52.000000000 -0800
+++ vnc_unixsrc-col/vncviewer/rfbproto.c	2005-02-09 09:26:42.000000000 -0800
@@ -152,7 +152,9 @@
   CARD8 challenge[CHALLENGESIZE];
   char *passwd;
   int i;
+  int clientInitMsgSize;
   rfbClientInitMsg ci;
+  Bool collaborativeServer;
 
   /* if the connection is immediately closed, don't report anything, so
        that pmw's monitor can make test connections */
@@ -171,6 +173,8 @@
     return False;
   }
 
+  collaborativeServer = rfbVersionIsCollaborative(major,minor);
+
   fprintf(stderr,"VNC server supports protocol version %d.%d (viewer %d.%d)\n",
 	  major, minor, rfbProtocolMajorVersion, rfbProtocolMinorVersion);
 
@@ -262,11 +266,18 @@
     return False;
   }
 
+  if(!appData.name)
+    appData.name = getpwuid(getuid())->pw_name;
+
   ci.shared = (appData.shareDesktop ? 1 : 0);
+  ci.nameLength = strlen(appData.name);
 
-  if (!WriteExact(rfbsock, (char *)&ci, sz_rfbClientInitMsg)) return False;
+  clientInitMsgSize = (collaborativeServer) ? sz_rfbClientInitMsg : sz_old_rfbClientInitMsg;
+  if (!WriteExact(rfbsock, (char *)&ci, clientInitMsgSize)) return False;
+  if (collaborativeServer && !WriteExact(rfbsock, appData.name, ci.nameLength)) return False;
 
-  if (!ReadFromRFBServer((char *)&si, sz_rfbServerInitMsg)) return False;
+  if (!ReadFromRFBServer((char *)&si, (collaborativeServer) ? 
+      sz_rfbServerInitMsg : sz_old_rfbServerInitMsg)) return False;
 
   si.framebufferWidth = Swap16IfLE(si.framebufferWidth);
   si.framebufferHeight = Swap16IfLE(si.framebufferHeight);
@@ -294,6 +305,21 @@
   fprintf(stderr,"VNC server default format:\n");
   PrintPixelFormat(&si.format);
 
+  appData.collaborate = (collaborativeServer) ? si.collaborate : FALSE;
+
+  if (appData.collaborate){
+    appData.me = malloc(sizeof(Member));
+    appData.me->memberID = si.memberID;
+    appData.me->name = appData.name;
+    appData.me->colorRed = si.cursorColorRed;
+    appData.me->colorGreen = si.cursorColorGreen;
+    appData.me->colorBlue = si.cursorColorBlue;
+    appData.me->next = NULL;
+
+    memberHead = appData.me;
+
+    appData.useRemoteCursor = False;
+  }
   return True;
 }
 
@@ -520,6 +546,38 @@
 
 
 /*
+ * SendTakeFloorRequestEvent.
+ */
+
+Bool
+SendTakeFloorRequestEvent(void)
+{
+  rfbTakeFloorRequestMsg tfr;
+
+  tfr.type = rfbTakeFloorRequest;
+
+  return WriteExact(rfbsock, (char *)&tfr, sz_rfbTakeFloorRequestMsg);
+}
+
+
+/*
+ * SendReleaseFloorEvent.
+ */
+
+Bool
+SendReleaseFloorEvent(Bool allow, short memberID)
+{
+  rfbReleaseFloorMsg rf;
+  
+  rf.type = rfbReleaseFloor;
+  rf.allow = allow;
+  rf.memberID = memberID;
+
+  return WriteExact(rfbsock, (char *)&rf, sz_rfbReleaseFloorMsg);
+}
+
+
+/*
  * SendClientCutText.
  */
 
@@ -692,9 +750,13 @@
 			 rect.r.w, rect.r.h);
 	}
 
-	XCopyArea(dpy, desktopWin, desktopWin, gc, cr.srcX, cr.srcY,
-		  rect.r.w, rect.r.h, rect.r.x, rect.r.y);
+	XPutImage(dpy, desktopWin, gc, image, cr.srcX, cr.srcY,
+		  rect.r.x, rect.r.y, rect.r.w, rect.r.h);
 
+	/* Copy the rect back into the off-scren image to maintain a 
+	   full representation of the screen for leter reference */
+	XGetSubImage(dpy, desktopWin, rect.r.x, rect.r.y, rect.r.w, rect.r.h,
+		     -1, ZPixmap, image, rect.r.x, rect.r.y);
 	break;
       }
 
@@ -714,6 +776,8 @@
 	    return False;
 	  break;
 	}
+	XGetSubImage(dpy, desktopWin, rect.r.x, rect.r.y, rect.r.w, rect.r.h,
+		     -1, ZPixmap, image, rect.r.x, rect.r.y);
 	break;
       }
 
@@ -733,6 +797,8 @@
 	    return False;
 	  break;
 	}
+	XGetSubImage(dpy, desktopWin, rect.r.x, rect.r.y, rect.r.w, rect.r.h,
+		     -1, ZPixmap, image, rect.r.x, rect.r.y);
 	break;
       }
 
@@ -752,6 +818,8 @@
 	    return False;
 	  break;
 	}
+	XGetSubImage(dpy, desktopWin, rect.r.x, rect.r.y, rect.r.w, rect.r.h,
+		     -1, ZPixmap, image, rect.r.x, rect.r.y);
 	break;
       }
 
@@ -771,6 +839,8 @@
 	    return False;
 	  break;
 	}
+	XGetSubImage(dpy, desktopWin, rect.r.x, rect.r.y, rect.r.w, rect.r.h,
+		     -1, ZPixmap, image, rect.r.x, rect.r.y);
 	break;
      }
 
@@ -790,6 +860,8 @@
 	    return False;
 	  break;
 	}
+	XGetSubImage(dpy, desktopWin, rect.r.x, rect.r.y, rect.r.w, rect.r.h,
+		     -1, ZPixmap, image, rect.r.x, rect.r.y);
 	break;
       }
 
@@ -801,6 +873,10 @@
 
       /* Now we may discard "soft cursor locks". */
       SoftCursorUnlockScreen();
+
+      /* Re-draw the Floor Owner's name */
+      if (lastOwnerNameTime != -1 && floorOwner)
+          DrawNewFloorOwner(floorOwner->name);
     }
 
 #ifdef MITSHM
@@ -819,6 +895,32 @@
     break;
   }
 
+  case rfbShowMemberPointer:
+  {
+    if (!ReadFromRFBServer(((char *)&msg) + 1,
+			   sz_rfbShowMemberPointerMsg - 1))
+      return False;
+
+    if (appData.collaborate) {
+	ShowMemberPointer(msg.smp.memberID, msg.smp.visible);
+    }
+
+   break;
+  }
+
+  case rfbMoveMemberPointer:
+  {
+    if (!ReadFromRFBServer(((char *)&msg) + 1,
+			   sz_rfbMoveMemberPointerMsg - 1))
+      return False;
+
+    if (appData.collaborate) {
+	MoveMemberPointer(msg.mmp.memberID, msg.mmp.x, msg.mmp.y);
+    }
+
+   break;
+  }
+
   case rfbBell:
   {
     Window toplevelWin;
@@ -833,6 +935,80 @@
     break;
   }
 
+  case rfbUpdateMemberList:
+  {
+    char *name;
+
+    if (!ReadFromRFBServer(((char *)&msg) + 1,
+			   sz_rfbUpdateMemberListMsg - 1))
+      return False;
+
+    msg.uml.length = Swap32IfLE(msg.uml.length);
+
+    name = malloc(msg.uml.length+1);
+    
+    if (!ReadFromRFBServer(name, msg.uml.length))
+      return False;
+    name[msg.uml.length] = 0;
+
+    if(msg.uml.add)
+      AddMember(msg.uml.memberID, msg.uml.master, msg.uml.colorRed, msg.uml.colorGreen, msg.uml.colorBlue, name);
+    else
+      RemoveMember(msg.uml.memberID);
+
+    break;
+  }
+
+  case rfbTakeFloor:
+  {
+    char *title;
+
+    if (!ReadFromRFBServer(((char *)&msg) + 1,
+			   sz_rfbTakeFloorMsg - 1))
+      return False;
+
+    if (appData.collaborate) {
+      if (msg.tf.allow) {
+	floorOwner = appData.me;
+	if(held_buttonMask != 0){
+	  SendPointerEvent(held_x, held_y, held_buttonMask);
+	  held_buttonMask = 0;
+	}
+	UpdateTitleWithFloorOwner();
+      } else {
+	held_buttonMask = 0;
+      }
+    }
+
+    break;
+  }
+
+  case rfbReleaseFloorRequest:
+  {
+    Bool willReleaseFloor;
+    Bool hadFloor = (floorOwner == appData.me);
+
+    if (!ReadFromRFBServer(((char *)&msg) + 1,
+			   sz_rfbReleaseFloorRequestMsg - 1))
+      return False;
+
+    if (appData.collaborate) {
+      willReleaseFloor = (hadFloor && (msg.rfr.force || CheckReleaseFloor()));
+      if (hadFloor && !msg.rfr.force) {
+	if(willReleaseFloor)
+	  ReleaseFloor(True, NULL);
+	else
+	  ReleaseFloor(False, NULL);
+      }
+
+      if ((!hadFloor || willReleaseFloor) && (!floorOwner || msg.rfr.memberID != floorOwner->memberID)) {
+	floorOwner = GetMemberFromID(msg.rfr.memberID);
+	UpdateTitleWithFloorOwner();
+      }
+    }
+    break;
+  }
+
   case rfbServerCutText:
   {
     if (!ReadFromRFBServer(((char *)&msg) + 1,
@@ -861,6 +1037,10 @@
     return False;
   }
 
+  /* Clear the new floor owner's name if the time has come to do so */
+  if (lastOwnerNameTime != -1 && difftime(time(NULL), lastOwnerNameTime) >= 2)
+    ClearFloorOwner();
+
   return True;
 }
 
diff -ruN vnc_unixsrc/vncviewer/vncviewer.c vnc_unixsrc-col/vncviewer/vncviewer.c
--- vnc_unixsrc/vncviewer/vncviewer.c	2001-09-15 22:58:13.000000000 -0700
+++ vnc_unixsrc-col/vncviewer/vncviewer.c	2005-02-09 09:26:42.000000000 -0800
@@ -89,7 +89,7 @@
   /* Create the "popup" widget - this won't actually appear on the screen until
      some user-defined event causes the "ShowPopup" action to be invoked */
 
-  CreatePopup();
+  /* CreatePopup(); */
 
   /* Find the best pixel format and X visual/colormap to use */
 
diff -ruN vnc_unixsrc/vncviewer/vncviewer.h vnc_unixsrc-col/vncviewer/vncviewer.h
--- vnc_unixsrc/vncviewer/vncviewer.h	2003-01-15 01:46:52.000000000 -0800
+++ vnc_unixsrc-col/vncviewer/vncviewer.h	2005-02-09 10:22:46.000000000 -0800
@@ -28,6 +28,7 @@
 #include <string.h>
 #include <sys/time.h>
 #include <unistd.h>
+#include <time.h>
 #include <X11/IntrinsicP.h>
 #include <X11/StringDefs.h>
 #include <X11/Shell.h>
@@ -61,6 +62,17 @@
 #define DEFAULT_VIA_CMD     \
   (DEFAULT_SSH_CMD " -f -L %L:%H:%R %G sleep 20")
 
+typedef struct member_t {
+    short memberID;
+    Bool master;
+    short x, y;
+    char colorRed;
+    char colorGreen;
+    char colorBlue;
+    char *name;
+    int  nameWidth;
+    struct member_t *next;
+} Member, *MemberPtr;
 
 /* argsresources.c */
 
@@ -71,6 +83,13 @@
   Bool grabKeyboard;
   Bool raiseOnBeep;
 
+  Bool collaborate;
+  char *name;
+  int autoReleaseDelay;
+  Bool showPopups;
+
+  MemberPtr me;
+
   String encodingsString;
 
   Bool useBGR233;
@@ -148,6 +167,7 @@
 extern GC gc;
 extern GC srcGC, dstGC;
 extern Dimension dpyWidth, dpyHeight;
+extern XImage *image;
 
 extern void DesktopInitBeforeRealization();
 extern void DesktopInitAfterRealization();
@@ -155,6 +175,13 @@
 			 Cardinal *num_params);
 extern void CopyDataToScreen(char *buf, int x, int y, int width, int height);
 extern void SynchroniseScreen();
+extern Bool CheckReleaseFloor();
+extern void UpdateTitleWithFloorOwner();
+extern void DrawMemberPointer(MemberPtr m, int oldx, int oldy);
+extern void DrawNewFloorOwner(char *name);
+extern void DrawName(char *name, int x, int y);
+extern void ClearFloorOwner(void);
+extern void ClearName(char *name, int x, int y);
 
 /* dialogs.c */
 
@@ -220,6 +247,8 @@
 					 Bool incremental);
 extern Bool SendPointerEvent(int x, int y, int buttonMask);
 extern Bool SendKeyEvent(CARD32 key, Bool down);
+extern Bool SendTakeFloorRequestEvent();
+extern Bool SendReleaseFloorEvent(Bool allow, short memberID);
 extern Bool SendClientCutText(char *str, int len);
 extern Bool HandleRFBServerMessage();
 
@@ -265,3 +294,23 @@
 extern XtAppContext appContext;
 extern Display* dpy;
 extern Widget toplevel;
+
+/* collaborate.c */
+
+extern MemberPtr memberHead;
+extern MemberPtr floorOwner;
+extern char *windowTitle;
+extern time_t lastEventTime;
+extern short held_x, held_y, held_buttonMask;
+extern time_t lastOwnerNameTime;
+
+extern Bool TakeFloorRequest();
+extern Bool ReleaseFloor(Bool allow, MemberPtr m);
+extern void AddMember(short memberID, Bool master,  int colorRed, int colorGreen, int colorBlue, char *name);
+extern MemberPtr GetMemberFromID(short memberID);
+extern void RemoveMember(short memberID);
+extern void ShowMemberPointer(short memberID, Bool visible);
+extern void MoveMemberPointer(short memberID, int x, int y);
+extern void GiveFloorToMember(Widget w, XtPointer client_data, XtPointer call_data);
+extern void ToggleFloorControl(Widget w, XEvent *ev, String *params, Cardinal *num_params);
+extern void SetFloorControlState(Widget w, XEvent *ev, String *params, Cardinal *num_params);
diff -ruN vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/collaboration.c vnc_unixsrc-col/Xvnc/programs/Xserver/hw/vnc/collaboration.c
--- vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/collaboration.c	1969-12-31 16:00:00.000000000 -0800
+++ vnc_unixsrc-col/Xvnc/programs/Xserver/hw/vnc/collaboration.c	2005-02-09 09:26:42.000000000 -0800
@@ -0,0 +1,120 @@
+/*
+ * collaborate.c
+ */
+
+#include <stdio.h>
+#include "rfb.h"
+
+rfbClientPtr floorClient = NULL;
+rfbClientPtr floorRequestClient = NULL;
+Bool collaborate = FALSE;
+Bool mergeKeyboards = FALSE;
+Bool master = FALSE;
+
+void MoveMemberPointer(rfbClientPtr cl, int x, int y)
+{
+  if (cl) {
+      cl->cursorX = x;
+      cl->cursorY = y;
+      rfbSendMoveMemberPointer(cl);
+  }
+}
+
+
+void ShowMemberPointer(rfbClientPtr cl, Bool visible)
+{
+   if (cl) {
+      rfbSendShowMemberPointer(cl, visible);
+  }
+}
+
+
+void GiveFloor(rfbClientPtr cl, Bool allow)
+{
+    short newID;
+    rfbClientPtr clPtr;
+
+    if (!collaborate) return;
+
+    if (cl) {
+      rfbSendTakeFloor(cl, allow);
+      ShowMemberPointer(cl, FALSE);
+    }
+
+    if(allow) {
+      newID = (cl) ? cl->memberID : 0;
+      for (clPtr = rfbClientHead; clPtr; clPtr = clPtr->next) {
+	if (clPtr != cl && clPtr != floorClient && clPtr->isCollaborative ) 
+	  rfbSendReleaseFloorRequest(clPtr, FALSE, newID);
+      }
+
+      if (floorClient)
+	ShowMemberPointer(floorClient, TRUE);
+
+      floorClient = cl;
+    }
+    floorRequestClient = NULL;
+}
+
+
+void ReleaseFloorRequest(rfbClientPtr cl, Bool force, short memberID)
+{
+    if (!collaborate) return;
+
+    rfbSendReleaseFloorRequest(cl, force, memberID);
+}
+
+
+void UpdateMemberList(Bool add, rfbClientPtr newCl)
+{
+    rfbClientPtr clPtr;
+
+    if (!collaborate) return;
+
+    for (clPtr = rfbClientHead; clPtr; clPtr = clPtr->next) {
+        if (clPtr != newCl && clPtr->isCollaborative)
+          rfbSendUpdateMemberList(clPtr, newCl, add);
+    }
+}
+
+
+void SendMemberList(rfbClientPtr newCl)
+{
+    rfbClientPtr clPtr;
+
+    if (!collaborate || !newCl->isCollaborative) return;
+
+    for (clPtr = rfbClientHead; clPtr; clPtr = clPtr->next) {
+        rfbSendUpdateMemberList(newCl, clPtr, TRUE);
+    }
+}
+
+
+rfbClientPtr GetClientByID(short memberID)
+{
+    rfbClientPtr clPtr;
+
+    for (clPtr = rfbClientHead; clPtr; clPtr = clPtr->next) {
+        if (clPtr->memberID == memberID)
+            return clPtr;
+    }
+
+    return NULL;
+}
+
+
+void UniqueName(rfbClientPtr cl, Bool freeOld)
+{
+  char *unique;
+  rfbClientPtr clPtr;
+
+  for (clPtr = rfbClientHead; clPtr; clPtr = clPtr->next) {
+    if (clPtr != cl && strcmp(clPtr->name, cl->name) == 0) {
+      unique = (char *)xalloc(strlen(cl->name)+6);
+      sprintf(unique, "%s-%d", cl->name, cl->memberID);
+      if (freeOld) free(cl->name);
+      cl->name = unique;
+      return;
+    }
+  }
+}
diff -ruN vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/Imakefile vnc_unixsrc-col/Xvnc/programs/Xserver/hw/vnc/Imakefile
--- vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/Imakefile	2002-04-30 06:07:31.000000000 -0700
+++ vnc_unixsrc-col/Xvnc/programs/Xserver/hw/vnc/Imakefile	2005-02-09 09:26:42.000000000 -0800
@@ -3,11 +3,13 @@
 
 SRCS = init.c sockets.c kbdptr.c cmap.c draw.c cutpaste.c \
        dispcur.c sprite.c rfbserver.c translate.c httpd.c auth.c \
-       rre.c corre.c stats.c hextile.c zlib.c tight.c cursor.c
+       rre.c corre.c stats.c hextile.c zlib.c tight.c cursor.c \
+       collaboration.c
 
 OBJS = init.o sockets.o kbdptr.o cmap.o draw.o cutpaste.o \
        dispcur.o sprite.o rfbserver.o translate.o httpd.o auth.o \
-       rre.o corre.o stats.o hextile.o zlib.o tight.o cursor.o
+       rre.o corre.o stats.o hextile.o zlib.o tight.o cursor.o \
+       collaboration.o
 
 #include <vnclibs.def>
 INCLUDES = -I. -I$(XBUILDINCDIR) -I$(FONTINCSRC) -I$(XINCLUDESRC) \
diff -ruN vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/init.c vnc_unixsrc-col/Xvnc/programs/Xserver/hw/vnc/init.c
--- vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/init.c	2002-10-27 04:36:02.000000000 -0800
+++ vnc_unixsrc-col/Xvnc/programs/Xserver/hw/vnc/init.c	2005-02-09 09:26:42.000000000 -0800
@@ -371,6 +371,22 @@
 	return 1;
     }
 
+    if (strcmp(argv[i], "-collaborate") == 0) {
+	collaborate = TRUE;
+        rfbAlwaysShared = TRUE;
+	return 1;
+    }
+
+    if (strcmp(argv[i], "-mergekeyboards") == 0) {
+	mergeKeyboards = TRUE;
+	return 1;
+    }
+
+    if (strcmp(argv[i], "-master") == 0) {
+	master = TRUE;
+	return 1;
+    }
+
     if (strcmp(argv[i], "-version") == 0) {
 	ErrorF("Xvnc version %d.%d.%s\n", rfbProtocolMajorVersion,
 	       rfbProtocolMinorVersion, XVNCRELEASE);
@@ -927,6 +943,10 @@
     ErrorF("-inetd                 Xvnc is launched by inetd\n");
     ErrorF("-compatiblekbd         set META key = ALT key as in the original "
 								"VNC\n");
+    ErrorF("-collaborate           open this Xvnc session in collaborative mode\n");
+    ErrorF("-mergekeyboards        in collaborative mode, listen to all keyboards\n");
+    ErrorF("-master                in collaborative mode, the first client to\n"
+	   "                       connect becomes the master\n");
     ErrorF("-version               report Xvnc version on stderr\n");
     exit(1);
 }
diff -ruN vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/rfb.h vnc_unixsrc-col/Xvnc/programs/Xserver/hw/vnc/rfb.h
--- vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/rfb.h	2003-01-15 02:44:05.000000000 -0800
+++ vnc_unixsrc-col/Xvnc/programs/Xserver/hw/vnc/rfb.h	2005-02-09 09:26:42.000000000 -0800
@@ -127,6 +127,7 @@
 
     int sock;
     char *host;
+    char *name;
 				/* Possible client states: */
     enum {
 	RFB_PROTOCOL_VERSION,	/* establishing protocol version */
@@ -145,6 +146,8 @@
     int preferredEncoding;
     int correMaxWidth, correMaxHeight;
 
+    Bool isCollaborative;
+
     /* The following member is only used during VNC authentication */
 
     CARD8 authChallenge[CHALLENGESIZE];
@@ -240,7 +243,12 @@
     Bool cursorWasChanged;         /* cursor shape update should be sent */
     Bool cursorWasMoved;           /* cursor position update should be sent */
 
+    short memberID;
+    Bool master;
     int cursorX, cursorY;          /* client's cursor position */
+    int pointerColorRed;
+    int pointerColorGreen;
+    int pointerColorBlue;
 
     struct rfbClientRec *next;
 
@@ -300,6 +308,15 @@
 		   (((l) & 0x0000ff00) << 8)  | \
 		   ((l) << 24))
 
+static const int pointer_colors[8][3] = 
+                    { {0xff, 0x00, 0x00},  /* red */
+                      {0x00, 0xff, 0x00},  /* green */
+                      {0x00, 0x00, 0xff},  /* blue */
+                      {0xff, 0xff, 0x00},  /* yellow */
+                      {0xff, 0x00, 0xff},  /* magenta */
+                      {0x00, 0xff, 0xff},  /* cyan */
+                      {0x80, 0x80, 0x80},  /* gray */
+                      {0x00, 0x00, 0x00} }; /* black */
 
 /* init.c */
 
@@ -430,8 +447,12 @@
 extern Bool rfbSendSetColourMapEntries(rfbClientPtr cl, int firstColour,
 				       int nColours);
 extern void rfbSendBell();
+extern void rfbSendTakeFloor(rfbClientPtr cl, Bool allow);
+extern void rfbSendReleaseFloorRequest(rfbClientPtr cl, Bool force, short memberID);
 extern void rfbSendServerCutText(char *str, int len);
-
+extern void rfbSendMoveMemberPointer(rfbClientPtr movedCl);
+extern void rfbSendShowMemberPointer(rfbClientPtr movedCl, Bool visible);
+extern void rfbSendUpdateMemberList(rfbClientPtr cl, rfbClientPtr newCL, Bool add);
 
 /* translate.c */
 
@@ -522,3 +543,21 @@
 
 extern void rfbResetStats(rfbClientPtr cl);
 extern void rfbPrintStats(rfbClientPtr cl);
+
+
+/* collaborate.c */
+
+extern rfbClientPtr floorClient;
+extern rfbClientPtr floorRequestClient;
+extern Bool collaborate;
+extern Bool mergeKeyboards;
+extern Bool master;
+
+extern void GiveFloor(rfbClientPtr cl, Bool allow);
+extern void ReleaseFloorRequest(rfbClientPtr cl, Bool force, short memberID);
+extern void MoveMemberPointer(rfbClientPtr cl, int x, int y);
+extern void ShowMemberPointer(rfbClientPtr cl, Bool visible);
+extern void UpdateMemberList(Bool add, rfbClientPtr newCl);
+extern void SendMemberList(rfbClientPtr cl);
+extern rfbClientPtr GetClientByID(short memberID);
+extern void UniqueName(rfbClientPtr cl, Bool freeOld);
diff -ruN vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/rfbserver.c vnc_unixsrc-col/Xvnc/programs/Xserver/hw/vnc/rfbserver.c
--- vnc_unixsrc/Xvnc/programs/Xserver/hw/vnc/rfbserver.c	2003-01-15 02:44:05.000000000 -0800
+++ vnc_unixsrc-col/Xvnc/programs/Xserver/hw/vnc/rfbserver.c	2005-02-09 09:26:42.000000000 -0800
@@ -54,6 +54,8 @@
 Bool rfbDontDisconnect = FALSE;
 Bool rfbViewOnly = FALSE; /* run server in view only mode - Ehud Karni SW */
 
+int maxPointerID = 0;
+
 static rfbClientPtr rfbNewClient(int sock);
 static void rfbProcessClientProtocolVersion(rfbClientPtr cl);
 static void rfbProcessClientNormalMessage(rfbClientPtr cl);
@@ -192,6 +194,12 @@
 
     cl->zlibCompressLevel = 5;
 
+    cl->memberID = ++maxPointerID;
+    cl->cursorX = cl->cursorY = 0;
+    cl->pointerColorRed = pointer_colors[(maxPointerID-1) % 8][0];
+    cl->pointerColorGreen = pointer_colors[(maxPointerID-1) % 8][1];
+    cl->pointerColorBlue = pointer_colors[(maxPointerID-1) % 8][2];
+
     sprintf(pv,rfbProtocolVersionFormat,rfbProtocolMajorVersion,
 	    rfbProtocolMinorVersion);
 
@@ -243,6 +251,12 @@
     if (pointerClient == cl)
 	pointerClient = NULL;
 
+    if (floorClient == cl) 
+        GiveFloor(NULL, TRUE);
+
+    if (floorRequestClient == cl)
+        floorRequestClient = NULL;
+
 #ifdef CORBA
     destroyConnection(cl);
 #endif
@@ -260,6 +274,11 @@
 
     if (cl->translateLookupTable) free(cl->translateLookupTable);
 
+    if(collaborate) {
+        ShowMemberPointer(cl, FALSE);
+        UpdateMemberList(FALSE, cl);
+    }
+
     xfree(cl);
 }
 
@@ -356,6 +375,8 @@
 	rfbLog("Ignoring minor version mismatch\n");
     }
 
+    cl->isCollaborative = rfbVersionIsCollaborative(major, minor);
+
     rfbAuthNewClient(cl);
 }
 
@@ -398,10 +419,13 @@
     char buf[256];
     rfbServerInitMsg *si = (rfbServerInitMsg *)buf;
     struct passwd *user;
-    int len, n;
+    int len, n, clientInitMsgSize, serverInitMsgSize;
     rfbClientPtr otherCl, nextCl;
 
-    if ((n = ReadExact(cl->sock, (char *)&ci,sz_rfbClientInitMsg)) <= 0) {
+    clientInitMsgSize = (cl->isCollaborative) ? sz_rfbClientInitMsg : sz_old_rfbClientInitMsg;
+    serverInitMsgSize = (cl->isCollaborative) ? sz_rfbServerInitMsg : sz_old_rfbServerInitMsg;
+
+    if ((n = ReadExact(cl->sock, (char *)&ci,clientInitMsgSize)) <= 0) {
 	if (n == 0)
 	    rfbLog("rfbProcessClientInitMessage: client gone\n");
 	else
@@ -410,12 +434,38 @@
 	return;
     }
 
+    cl->name = NULL;
+
+    if (cl->isCollaborative) {
+      cl->name = (char *)xalloc(ci.nameLength + 1);
+      if ((n = ReadExact(cl->sock, cl->name, ci.nameLength)) <= 0) {
+	if (n == 0)
+	  rfbLog("rfbProcessClientInitMessage: client gone\n");
+	else
+	  rfbLogPerror("rfbProcessClientInitMessage: read");
+	rfbCloseSock(cl->sock);
+	return;
+      }
+      cl->name[ci.nameLength] = 0;
+      UniqueName(cl, TRUE);
+    }
+
+    if(collaborate && !cl->name) {
+      cl->name = "anonymous";      
+      UniqueName(cl, FALSE);
+    }
+
     si->framebufferWidth = Swap16IfLE(rfbScreen.width);
     si->framebufferHeight = Swap16IfLE(rfbScreen.height);
     si->format = rfbServerFormat;
     si->format.redMax = Swap16IfLE(si->format.redMax);
     si->format.greenMax = Swap16IfLE(si->format.greenMax);
     si->format.blueMax = Swap16IfLE(si->format.blueMax);
+    si->collaborate = collaborate;
+    si->memberID = cl->memberID;
+    si->cursorColorRed = cl->pointerColorRed;
+    si->cursorColorGreen = cl->pointerColorGreen;
+    si->cursorColorBlue = cl->pointerColorBlue;
 
     user = getpwuid(getuid());
 
@@ -423,16 +473,16 @@
 	desktopName[128] = 0;
 
     if (user) {
-	sprintf(buf + sz_rfbServerInitMsg, "%s's %s desktop (%s:%s)",
-		user->pw_name, desktopName, rfbThisHost, display);
+	sprintf(buf + serverInitMsgSize, "%s's %s desktop (%s:%s)",
+	    user->pw_name, desktopName, rfbThisHost, display);
     } else {
-	sprintf(buf + sz_rfbServerInitMsg, "%s desktop (%s:%s)",
-		desktopName, rfbThisHost, display);
+	sprintf(buf + serverInitMsgSize, "%s desktop (%s:%s)",
+            desktopName, rfbThisHost, display);
     }
-    len = strlen(buf + sz_rfbServerInitMsg);
+    len = strlen(buf + serverInitMsgSize);
     si->nameLength = Swap32IfLE(len);
 
-    if (WriteExact(cl->sock, buf, sz_rfbServerInitMsg + len) < 0) {
+    if (WriteExact(cl->sock, buf, serverInitMsgSize + len) < 0) {
 	rfbLogPerror("rfbProcessClientInitMessage: write");
 	rfbCloseSock(cl->sock);
 	return;
@@ -463,8 +513,34 @@
 	    }
 	}
     }
-}
 
+    if (!master) cl->master = FALSE;
+
+    if(collaborate) {
+
+      if (master) {
+	cl->master = TRUE;
+	for (otherCl = rfbClientHead; otherCl; otherCl = otherCl->next) {
+	  if (otherCl != cl && otherCl->master) {
+	    cl->master = FALSE;
+	    break;
+	  }
+	}
+      }
+
+      SendMemberList(cl);
+      UpdateMemberList(TRUE, cl);
+      ShowMemberPointer(cl,TRUE);
+
+      if (cl->isCollaborative) {
+        if (floorClient) {
+          ReleaseFloorRequest(cl, FALSE, floorClient->memberID);
+        } else {
+          GiveFloor(cl, TRUE);
+	}
+      }
+    }
+}
 
 /*
  * rfbProcessClientNormalMessage is called when the client has sent a normal
@@ -744,9 +820,9 @@
 	if (!isKeyboardEnabled(cl))
 	    return;
 #endif
-	if (!rfbViewOnly && !cl->viewOnly) {
+	if (!rfbViewOnly && !cl->viewOnly && (!collaborate || cl == floorClient || mergeKeyboards)) 
 	    KbdAddEvent(msg.ke.down, (KeySym)Swap32IfLE(msg.ke.key), cl);
-	}
+
 	return;
 
 
@@ -777,14 +853,79 @@
 	else
 	    pointerClient = cl;
 
-	if (!rfbViewOnly && !cl->viewOnly) {
-	    cl->cursorX = (int)Swap16IfLE(msg.pe.x);
-            cl->cursorY = (int)Swap16IfLE(msg.pe.y);
+	cl->cursorX = (int)Swap16IfLE(msg.pe.x);
+	cl->cursorY = (int)Swap16IfLE(msg.pe.y);
+	
+	if (!rfbViewOnly && !cl->viewOnly)
+	  if (!collaborate || cl == floorClient)
 	    PtrAddEvent(msg.pe.buttonMask, cl->cursorX, cl->cursorY, cl);
-	}
+	  else
+	    MoveMemberPointer(cl, cl->cursorX, cl->cursorY);
+
 	return;
 
 
+    case rfbTakeFloorRequest:
+       {
+	 if (!collaborate) return;
+
+	 if ((n = ReadExact(cl->sock, ((char *)&msg) + 1,
+			    sz_rfbTakeFloorRequestMsg - 1)) <= 0) {
+	   if (n != 0)
+	     rfbLogPerror("rfbProcessClientNormalMessage: read");
+	   rfbCloseSock(cl->sock);
+	   return;
+	 }
+
+	 if (!pointerClient && !floorRequestClient) {
+	   floorRequestClient = cl;
+
+	   if (floorClient == NULL || cl->master) {
+             if (floorClient)
+	       ReleaseFloorRequest(floorClient, TRUE, cl->memberID);
+	     GiveFloor(floorRequestClient, TRUE);
+	   } else {
+	       if (floorClient)
+		 ReleaseFloorRequest(floorClient, FALSE, cl->memberID);
+	   }
+	 } else {
+	   if (floorRequestClient != cl)
+	     GiveFloor(cl, FALSE);
+	 }
+	 return;
+       }
+
+    case rfbReleaseFloor:
+      {
+	rfbClientPtr newCl;
+
+        if (!collaborate) return;
+	
+        if ((n = ReadExact(cl->sock, ((char *)&msg) + 1,
+	  		   sz_rfbReleaseFloorMsg - 1)) <= 0) {
+	  if (n != 0)
+	    rfbLogPerror("rfbProcessClientNormalMessage: read");
+	  rfbCloseSock(cl->sock);
+	  return;
+        }
+	
+        if (floorRequestClient) {
+  	  if(msg.rf.memberID > 0 && floorRequestClient->memberID != msg.rf.memberID && (cl == floorClient || cl->master) && (newCl = GetClientByID(msg.rf.memberID)) != NULL) {
+	    GiveFloor(floorRequestClient, FALSE);
+	    GiveFloor(newCl, TRUE);
+          } else {
+	    GiveFloor(floorRequestClient, msg.rf.allow);
+          }
+	} else {
+          if(msg.rf.memberID > 0 && (cl == floorClient || cl->master)) {
+	    if ((newCl = GetClientByID(msg.rf.memberID)) != NULL)
+		GiveFloor(newCl, TRUE);
+          } else
+	    GiveFloor(NULL, TRUE);
+	}
+	
+	return;
+      }
     case rfbClientCutText:
 
 	if ((n = ReadExact(cl->sock, ((char *)&msg) + 1,
@@ -1378,6 +1519,147 @@
 
 
 /*
+ * rfbSendTakeFloor tells this client to Take the Floor, and tells the other 
+ * clients that this client has control now.  
+ * Accepts a NULL client to take control away from everyone.
+ */
+
+void
+rfbSendTakeFloor(rfbClientPtr cl, Bool allow)
+{
+    rfbTakeFloorMsg tf;
+
+    if (cl && cl->isCollaborative) {
+      tf.type = rfbTakeFloor;
+      tf.allow = allow;
+      if (WriteExact(cl->sock, (char *)&tf, sz_rfbTakeFloorMsg) < 0) {
+	rfbLogPerror("rfbSendTakeFloor: write");
+	rfbCloseSock(cl->sock);
+      }
+    }
+}
+
+
+/*
+ * rfbSendReleaseFloorRequest tells this client to release the floor,
+ *    and sends the name of the new floor owner.
+ */
+
+void
+rfbSendReleaseFloorRequest(rfbClientPtr cl, Bool force, short memberID)
+{
+    rfbReleaseFloorRequestMsg rfr;
+    
+    if (cl->isCollaborative) {
+      rfr.type = rfbReleaseFloorRequest;
+      rfr.force = force;
+      rfr.memberID = memberID;
+      if (WriteExact(cl->sock, (char *)&rfr,
+		     sz_rfbReleaseFloorRequestMsg) < 0) {
+	rfbLogPerror("rfbSendReleaseFloorRequest: write");
+	rfbCloseSock(cl->sock);
+	return;
+      }
+    }
+}
+
+
+/*
+ * rfbSendMoveMemberPointer sends the position of a non-active pointer
+ * to a client.
+ */
+
+void
+rfbSendMoveMemberPointer(rfbClientPtr movedCl)
+{
+    rfbClientPtr cl, nextCl;
+    rfbMoveMemberPointerMsg mmp;
+
+    mmp.type = rfbMoveMemberPointer;
+    mmp.memberID = movedCl->memberID;
+    mmp.x = movedCl->cursorX;
+    mmp.y = movedCl->cursorY;
+
+    for (cl = rfbClientHead; cl; cl = nextCl) {
+	nextCl = cl->next;
+	if (cl != movedCl && cl->isCollaborative) {
+	    if (WriteExact(cl->sock, (char *)&mmp, sz_rfbMoveMemberPointerMsg) < 0) {
+	        rfbLogPerror("rfbMoveMemberPointer: write");
+	        rfbCloseSock(cl->sock);
+	    }
+        }
+    }
+}
+
+
+/*
+ * rfbSendUpdateMemberList sends (to cl)  a description of a new client 
+ * (newCl), or the ID of a client that's going away.
+ */
+
+void
+rfbSendUpdateMemberList(rfbClientPtr cl, rfbClientPtr newCl, Bool add)
+{
+    rfbUpdateMemberListMsg uml;
+    rfbClientPtr clPtr;
+
+    uml.type = rfbUpdateMemberList;
+    uml.memberID = newCl->memberID;
+    uml.master = (newCl->master) ? 1 : 0;
+    uml.add = add;
+
+    if (add) {
+        uml.colorRed = newCl->pointerColorRed;
+        uml.colorGreen = newCl->pointerColorGreen;
+        uml.colorBlue = newCl->pointerColorBlue;
+        uml.length = Swap32IfLE(strlen(newCl->name));
+    } else {
+        uml.length = 0;
+    }
+
+    if (cl->isCollaborative) {
+        if (WriteExact(cl->sock, (char *)&uml, sz_rfbUpdateMemberListMsg) < 0) {
+            rfbLogPerror("rfbUpdateMemberList: write");
+            rfbCloseSock(cl->sock);
+        }
+	if (add)
+	    if (WriteExact(cl->sock, newCl->name, strlen(newCl->name)) < 0) {
+	        rfbLogPerror("rfbSendUpdateMemberList: write");
+		rfbCloseSock(cl->sock);
+	    }	    
+    }
+}
+
+
+
+/*
+ * rfbSendShowMemberPointer sends the visibility and color of a non-active
+ * pointer to all clients.
+ */
+
+void
+rfbSendShowMemberPointer(rfbClientPtr movedCl, Bool visible)
+{
+    rfbClientPtr cl, nextCl;
+    rfbShowMemberPointerMsg smp;
+
+    smp.type = rfbShowMemberPointer;
+    smp.memberID = movedCl->memberID;
+    smp.visible = visible;
+
+    for (cl = rfbClientHead; cl; cl = nextCl) {
+        nextCl = cl->next;
+        if (cl != movedCl && cl->isCollaborative) {
+	  if (WriteExact(cl->sock, (char *)&smp, sz_rfbShowMemberPointerMsg) < 0) {
+                rfbLogPerror("rfbShowMemberPointer: write");
+                rfbCloseSock(cl->sock);
+            }
+        }
+    }
+}
+
+
+/*
  * rfbSendServerCutText sends a ServerCutText message to all the clients.
  */
 
@@ -1477,3 +1759,4 @@
 	rfbDisconnectUDPSock();
     }
 }
+
