Some interesting undocumented functions in gtk.gdk:
  flush
  x11_get_default_screen
  x11_get_server_time
  x11_grab_server
  x11_register_standard_event_type
  x11_ungrab_server
  window_foreign_new
  error_trap_push
  error_trap_pop

  event_send_client_message_for_display
    ^^ this one doesn't seem to let you set the mask, though, hence is
    useless

How click/doubleclick/drags work:
  single click looks like:
     GDK_BUTTON_PRESS, GDK_BUTTON_RELEASE
  double-click looks like:
     GDK_BUTTON_PRESS, GDK_BUTTON_RELEASE,
     GDK_BUTTON_PRESS, GDK_2BUTTON_PRESS, GDK_BUTTON_RELEASE
  (extend that for triple-clicks, if you care)
  
  drags using the gtk dnd support look like:
     GDK_BUTTON_PRESS, GTK_MOTION_NOTIFY (...),
     drag-begin emitted, drag-motion emitted (...),
     GDK_BUTTON_RELEASE
  one can also implement drag behavior without using gtk's dnd support
  directly; the critical function here is
  gtk.Widget.drag_check_threshold.
     
  docs on gtk dnd support:
    http://live.gnome.org/GnomeLove/DragNDropTutorial
    http://www.pygtk.org/pygtk2tutorial/ch-DragAndDrop.html
    http://library.gnome.org/devel/gtk/2.12/gtk-Drag-and-Drop.html

How gdk/gtk event handling works:
  gdk handles the event, then (via gdk_event_handler_set) passes it on
  to gtk.

  gtk looks up the GtkWidget associated with the GdkWindow in the
  usual get_user_data way, then (for at least PROPERTY_NOTIFY, MAP,
  UNMAP, CONFIGURE, etc., see gtkmain.c:gtk_main_do_event) calls
  gtk_widget_event on that widget, which then emits the 'event'
  signal.  Prototype: gboolean ()(GtkWidget*, GdkEvent*, void *
  user_data).  return value TRUE = stop propagation, FALSE = continue
  processing.

  gtk_widget_event does:
    if the window is no longer visible according to
      (event_window_is_still_viewable) then it discards the event.
    else, emits widget_signals[EVENT] i.e. emits the "event" signal
    if that returns false and WIDGET_REALIZED_FOR_EVENT is true, then
      it looks more carefully and emits one of the *-event signals
    then if WIDGET_REALIZED_FOR_EVENT is true, it emits the
      event-after signal.

  There are 'map', 'map-event', 'configure-event', 'client-event',
  'destroy-event', 'unmap', 'unmap-event' signals.

  "map" is emitted by gtk_widget_map, not by actual mapping.  Likewise
  for "unmap".


Easiest way to get at some events is to use gtk.gdk.event_handler_set
to filter events after they have already been moved into Python, but
before any real processing has been done on them -- passing all the
ones I'm not interested in on to gtk.main_do_event.

def func(event, user_data):
    gtk.main_do_event(event)
gtk.gdk.event_handler_set(func, None)


So: when we want to send an event, we have to go to X directly
  and when we want to read an event that GDK doesn't have (i.e.,
    *Request events), then we go to X directly
  and otherwise we catch stuff between GDK and GTK

This gives us:
  DestroyNotify -- via StructureNotify only, never SubstructureNotify
     (GDK filters)
  MapNotify
  UnmapNotify
  ConfigureNotify -- not for "child" windows (GdkWindow type field),
     not for SubstructureNotify, but yes for StructureNotify on
     foreign windows.  And not for root window, but we there are
     already nice signals for screen resize.
  PropertyNotify
  ClientMessage



How focus works in gtk/gdk:
  toplevel window keeps track of the subwindow that should have focus

  actual SetInputFocus request generated only by
  _gdk_x11_set_input_focus which is called only by gdk_window_focus
  (gtkwindow-x11.c, which sets the focus to the given window, or uses
  the WM to do so) and in response to a WM_TAKE_FOCUS message
  (gdkevents-x11.c, which ignores WM_TAKE_FOCUS to child windows, and
  actually sends focus to the magic focus_window, which is a secret
  window that gdk toplevel windows create

  gdk_window_focus is effectively called only by
  gtk_window_present_with_time

  I guess in practice what happens is that gdk toplevel windows have a
  magic hidden window that they direct focus at, and then they emulate
  something like X's focus model internally -- in fact all keyboard
  events etc. go to the hidden window, but they are then redirected to
  the widgets inside the app, and the app also generates fake focus
  notification events, etc.  The focus window is created as a 1x1
  child window of the toplevel located at -1x-1.

  input_only toplevels don't get a focus window.

  in particular, the focus window just gets key presses, key releases,
  and focus change info.  the actual window hierarchy is used for
  mouse stuff.

  the focus window is stuck into the X->GDK window lookup table as an
  alias for the actual toplevel -- what this means is that if one asks
  for the GdkWindow corresponding to the focus window's XID, then one
  actually gets back the toplevel window.  And in particular, X events
  delivered to the focus window will end up being processed by GDK as
  if they were sent to the toplevel.

  (how the heck do XEmbed plugs handle this?  Please tell me they
  don't send all keyboard events via synthesized messages or
  something...?

  ...it looks like maybe they do.)


Options:
  -- actually give focus to child, don't worry, our gtk will never
     notice because focus is being transferred to a child window?
     ...but actually I don't think that's true, because it actually
     looks for focus events on the focus window?
     does that matter, though?  focus actually only seems to determine
     how the widget is drawn...

the right way to do it is probably, subclass GtkWindow and make a
version that does not track has-toplevel-focus in the default way, but
by our own custom thing.  then actually pass X focus around, if the
tray is supposed to have toplevel focus then give it focus for real
(using WM_TAKE_FOCUS, I suppose) whenever some widget that is not a
client window is focused, when a client window is focused give it to
the client window.

Not sure what advantage this has over simply never focusing the tray
itself, since, well, is it really necessary to do standard keyboard
control there?  Should be using global keybindings anyway...

Note, Widget.is_focus tells if we are the focused widget within the
toplevel, Widget.flags & GTK_HAS_FOCUS means... basically the same
thing?  the "has-focus" property is supposed to mirror the
GTK_HAS_FOCUS flag.  Ah-hah, gtkwindow.c:do_focus_change sets or
unsets focused widget GTK_HAS_FOCUS flag directly.  And
do_focus_change is called for example when the toplevel gets or loses
focus.  So HAS_FOCUS/has-focus really is the "I really truly have
focus right now" bit.


gtk.Window.do_focus_in_event:
  -- ignores anything that happens when it is not visible
  -- calls _gtk_window_set_has_toplevel_focus (TRUE)
  -- calls _gtk_window_set_is_active (TRUE)
  -- returns false
gtk.Window.do_focus_out_event:
  -- ditto, but with (FALSE)s
_gtk_window_set_is_active:
  if new setting != old setting, update setting, call
  window_update_has_focus, and notify for "is-active"
_gtk_window_set_has_toplevel_focus:
  actually... identical to _gtk_window_set_is_active except for
  twiddling the has_toplevel_focus flag/notifying "has-toplevel-focus"
window_update_has_focus is semi-complex.

The easiest way to do this would be to catch the real
focus-{in,out}-events, and generate fake ones (call parent do_*
methods) when needed.

Racing is nasty, if we want to allow focus in non-client widgetry,
because to get the focus to non-client widgetry, we need to get gdk
internals to issue focus change stuff to the tray toplevel.  The only
way to get it to do this is to get a WM_TAKE_FOCUS event in there
somewhere.  We cannot even queue a fake event to do this, because the
relevant bit of gdk works before X->GDK event translation.  So we need
to send a message to the X server, which at some point will get back
and cause us to issue a focus change request.  It does do the
timestamp on that focus change correctly.  Also it is sort of stupid
to send the timestamp first to the main window and then to a client,
if a client is getting it -- this is also a small race with keyboard
entry.  


Focus-stealing prevention: I guess this means detecting whenever focus
leaves a client we think should be focused, and sending it back?
Select for focus events on client toplevels, when one of them gets a
focus-out, check to see if we think it should currently be focused,
and if so, unconditionally send the focus back?  The question is, how
race-y is this.


-------
Keybindings:

This is another of those places where X is insane.

So: we have *Keysyms* which represent what we usually think of as keys
("space bar", "lowercase g", "num lock", etc.), and *Keycodes* which
are just arbitrary integers in the [8, 255] range.  There are also
what the user thinks of as modifiers -- shift, alt, super, numlock --
let's call these *user modifiers* -- and there are *X modifiers*,
which are a bit-mask whose bits are Shift, Lock, Control, Mod1, ...,
Mod5.

There is an arbitrary many-to-many mapping between Keysyms and
Keycodes, and it can change at any time.  (Which causes a
MappingNotify event to all clients.)  There is also a semi-arbitrary
mapping between user modifiers and X modifiers (which can also change
at any time, and causes a MappingNotify event).  Suppose, for
instance, that we would like to grab the key combination "<meta>n".
The proper way to do this is:
  -- Look up the keycode corresponding to the keyval "n"
  -- Look up the keycode corresponding to the keyvals "Meta_L" and
     "Meta_R".
  -- Look up the keycodes corresponding to all X modifiers.
  -- Check to see which X modifiers are bound to keycodes that are
     equivalent to Meta_L or Meta_R.
  -- Now we know the X keycode and the X modifiers meant by
     "<meta>n".
  -- Also look up the keycodes corresponding to all nuisance modifiers
     (like scroll lock and num lock).  Then compare them to the X
     modifiers as well, to figure out what the X nuisance modifiers
     are.
  -- Now bind the X keycode and X modifiers meant by <meta>n, with and
     without all combinations of nuisance modifiers.
  -- Redo all of the above if the keymap changes.  (To ungrab in that
     case, just use AnyKey/AnyModifier, to avoid the horror of trying
     to figure out what we grabbed before in the previous universe.)

Of course, life is not actually that simple -- there are also things
like "<meta>plus", which on most keyboards is of course equivalent to
"<meta><shift>equals".  GDK has a function that will take a keypress
with X modifiers and tell you that it was really <meta>plus and not
<meta><shift>equals, but it doesn't have any way in general to work
out what keypresses will trigger such behavior and generally things
are a mess.  For our purposes, let's assume:
  -- WM hotkeys are used spatially, so they should not be affected by
     caps lock or a language-switch key
  -- People might want to use shift itself as a modifier, and it
     probably makes more sense to treat it as a modifier than as a
     thing-that-changes-what-other-keys-mean.
So, let's say that when we get a key name mentioned, that just means
"whichever key(s) have this keyval on them somewhere", i.e. it's a way
of referring to a physical hardware object, and then people have to
specify modifiers as well (and for those we do do the weird lookup
stuff).

Nuisance modifiers are:
  -- lock (this means capslock or shiftlock)
  -- whatever Mod<n> that scroll lock is attached to
  -- whatever Mod<n> that num lock is attached to
  -- shift, but only sort of -- we won't treat it as one, see above.

The GdkKeymap::keys-changed signal is emitted for *all* MappingNotify
events, whether they refer to keys or modifiers or mouse buttons.
