API support
-----------

wintab:
    Can enumerate devices.  Not clear if/how more than one device
    is supported.

    Events can be via window or manually pulled from message queue.  Device
    context must be attached to a window, however.

directinput:
    Can enumerate devices and device capabilities.

    Device state cannot be queried and no events are posted; Wacom HID driver
    is only pretending to be a driver.

carbon:
    Cannot enumerate devices, or device capabilities.

    Window receives proximity and tablet events as they occur.

apple-hid
    Can enumerate devices and device capabilities.

    Cannot grab any events or query state though (Wacom driver holds
    exclusive lock).

evdev:
    Can enumerate devices and device capabilities.

    Can pull events just like joystick.  Not attached to a window.

xinput (X11):
    Can enumerate devices and device capabilities.  Requires user to have
    modified xorg.conf to include the devices (annoying, but not
    unreasonable; probably defaulted in Ubuntu etc).

    Events are posted to active window under pen (I think... there's quite
    literally no documentation on this API).

Useable APIs
------------

Windows: wintab (per-window)
OS X: carbon (no device enumeration/check though)
Linux: xinput (traditional) or evdev (exclusivity, simpler, not per-window)

Interface
---------

Separate tablet interface from input interface.  If a tablet appears on the
input interface (perhaps even more than once, e.g. directinput + wintab) then
so be it.

get_tablets() -> [Tablet, ...]

Tablet(EventDispatcher)
    open(window, exclusive) -> TabletContext

TabletContext(EventDispatcher)
    window: Window

    close()

    on_cursor_enter(cursor)
    on_cursor_leave(cursor)
    on_cursor_move(cursor, x, y, pressure, tilt)
    on_cursor_press(cursor, button, modifiers)
    on_cursor_release(cursor, button, modifiers)

Cursor
    type: str           # 'pen', 'eraser', 'puck'
    id: str             # might be bogus
    x: float
    y: float            # window coordinates
    pressure: float     # [0.0, 1.0]
    tilt: float         # [0.0, 1.0]
    orientation ...

Issues
------

Don't really like the extra TabletContext indirection, but seems necessary for
those drivers that need the tablet to be enabled for individual windows
(carbon, maybe wintab).

Platform Window classes will be secretly enhanced to pass on the tablet events
to the tablet interface driver where needed (definitely carbon, maybe wintab,
maybe xinput).

Carbon will just produce a default SystemTablet instance for get_tablets()
whose contexts never produces events if no tablet is attached.  In case you're
wondering, GTK doesn't even support tablets on OS X.

How many buttons?  Can buttons be queried? (could inspect the cursor).  What
enum to use for buttons?

If using evdev, might need clever tricks to associate events to correct window
(or drop them).  Also may need to emulate multiple cursors from 'invert'
state.  Or just use xinput instead.

What about mapping tablet space to screen/window space?  GIMP does an awful
job of this, hopefully not because of xinput.  Should there be a way to grab
the tablet "exclusive" for a window, simultaneously mapping the whole tablet
to the window (w/ aspect).  How customisable does mapping need to be (or can
be, with cross-platform compat)?

Alternatively, cursor events could be on Cursor, not TabletContext.. but I
suspect that will be annoying to set up and manage (have to attach event
handlers every time _enter event, remove on _leave event... carefully!).
With current proposal, window can be pushed onto tablet context when it's
opened to handle all its events (common case).

Don't really like "cursor" name, anything better?

Maybe don't even attempt to support multiple tablets (unlikely case, may not
even be supported by wintab and carbon)... instead have a function (on
Window?) to open a tablet context for a window.

xinput is display-dependent, so device enumeration depends on display.
Perhaps should skip the tablet device (as prev para suggests) and just
add a method on window? (or function that takes window, if want modularity).
