This is just an idea.

It would be really, really, nice to have an X equivalent of
screen(1) -- i.e., a way to run some X apps on a remote host, then
disconnect your local host and reattach later, and have them still
there.

VNC does part of this, but you can't make it "rootless",
i.e. have individual windows show up as individual windows.  (This is
esp. annoying to me because in my use case for this, the remote
windows are data visualizations, and sometimes I want to see them on
my little laptop screen and sometimes on my giant LCD, and picking a
fixed root window size to use for both cases sucks.)

NX does this, but it is completely #$@)#@$ing insane to deal with.
NoMachine are the last of the great 80s unix software vendors.

So here's a simple approach: Start up an Xvfb on the remote server to
be the detachable X session.  Run a special window manager on it.
This window manager enables compositing on everything, watches for
damage, and on another socket listens for connections from a special
program.  This special program attaches, gets a list of top-level
windows, and mirrors them onto a real display by opening one toplevel
for each real toplevel.  The connection between the wm and the special
program is basically forwarding events (mouse, keyboard, configure,
focus, ClientMessage), some special queries, and maybe damage from the
real X display, and receiving notifications of new windows, new
metadata on windows, cursor changes, grabs, and compressed images of
damaged areas.

The tricky parts on the window manager side are: sending the minimal
amount of data (for instance, watching how the redraw queue drains and
collapsing updates when possible); forwarding X events to clients
correctly (this needs XTest); handling grabs and override-redirect
windows properly; any necessary keyboard translation; figuring out
what cursors are wanted where.

The tricky parts for the client side are: just completeness, mostly --
grokking focus and grabs, positioning windows, enabling cursors, etc.


The trickiest thing overall, actually, may be maintaining stacking
order sync.  Before processing any mouse event, stacking order must be
accurate.  But how to achieve this?  On the client side, it's probably
not actually possible.  We get basically *nothing* to work with -- we
can work out the stacking order by hand, using a giant storm of
XQueryTree calls, but the problem with this approach is that we have
no way to get reliable notification when the stacking order has
changed.  (Well, we could select for ConfigureNotify and some other
stuff on like every window in the world, but that probably isn't
useful.)  So the only thing we actually have to work with is
_NET_CLIENT_LIST_STACKING.  We can be notified when this is changed,
but then there's a race condition -- we get the notification that it's
changed (A), the mouse sends some events, it changes again (B), we
fetch the new stacking order, and deliver the events to be interpreted
against stacking order B.  This appears to be unavoidable, though,
so... oh well.

The other really tricky bit is translating keyboard events from client
to server.  I think the approach to take is: client just packs up
events into logical form (including "nuisance" bits!) and sends them
off.  Server unpacks them into keycode/mask, then uses XQueryPointer
(!) to determine the current mask, and then issues keystrokes to
adjust the mask to what it should be, and then issues the actual
keystroke.

NOTE: MOTION_NOTIFY and BUTTON_PRESS (and BUTTON_RELEASE) all include
mask bits too, so they should all use the above logic as well.


WM-side logic:
  -- register as WM, so we will be able to rearrange children of the
     root with impunity, knowing that no other wm dare mess with us
  -- request substructure events on the root
  -- enable automatic composition of all children of the root
  -- gather up all pre-existing windows, if any
  -- start listening for slave attachments

Messages:
Slave -> Master:
  cursor move
  button press
  button release
  key press
  key release
  configure events
  focus enters window
  focus leaves window
  kill window for reals (probably a special out-of-band request from
    the PoV of the GUI)
  request window list
  "here is the current stacking"

Master -> Slave:
  window list?
  new window (automatically mapped)
    -- type, location, size, override or not, title, icon, etc.
  window changed (in any of the above properties)
  window went away (unmapped)
  window configure request (including stacking)
  current cursor changed (to ...)
  start/release active grab on keyboard or mouse
  redraw region
  
Issues:
  maintaining stacking sync is reasonably hard -- require
    a WM on the slave side that supports _NET_CLIENT_LIST (and monitor
    it)
  we may want a cache for cursors
  do we force the slave to keep a cache of window contents, so it
    never has to request them from the master?
  keyboard mapping, modifier maps
  the "frozen" state that input devices can go into
  can we really reliably detect grab/ungrab events?

Ignored, for now:
  iconified status
  shape extension
  XSync-based resize coordination as defined by EWMH (but the master
    might want to do this locally)
  passive grabs
  selection
  WarpPointer
  generic ClientMessages
  extended input events (XInput) (XTest does support these though,
    even if the version whose proto spec is easiest to find does not)
  beeps
