This uses the idle-inhibit protocol from wayland-protocols, to attach an
inhibitor to the GdkSurface. The inhibit function can be called as many
times as the user wants, but the uninhibit function MUST be called as
many times to unset the idle inhibition.
This has been tested on Sway.
Not all compositors support _NET_WM_FRAME_DRAWN. In cases
where the compositor doesn't support _NET_WM_FRAME_DRAWN we don't
need to do all the fancy damage tracking and fence watching.
Furthermore, if the compositor doesn't support _NET_WM_FRAME_DRAWN,
it's possible that one frame will start before the previous frame has
made it through the pipeline, leading to a blown assertion.
This commit side-steps the unnecessary code and associated assertion
when _NET_WM_FRAME_DRAWN isn't supported.
Fixes: https://gitlab.gnome.org/GNOME/gtk/-/issues/2927
If we create an implicit grab on a surface, leave the surface, and
release the button, we would get 2 XI_Leave events, one with mode
XINotifyNormal when the pointer leaves the surface, and another with
mode XINotifyUngrab when the button is released.
Meanwhile, the upper layers rely on crossing events being paired,
and particularly in no crossing event being sent until the implicit
grab is dismissed (either by releasing it, or via more pervasive
grabs).
Ignoring the set of XINotifyNormal events while an implicit grab
is active adapts the X11 backend to this behavior. If the grab were
released or taken away by another grab, a crossing event with one
of the other XINotify*Grab/XINotify*Ungrab will be generated.
Fixes: https://gitlab.gnome.org/GNOME/gtk/-/issues/2879
Since commit 972134abe4 a frame getting
drawn has three states (with the vendor nvidia driver at least):
1. drawn by gtk waiting on the GPU
2. drawn by GPU waiting on the compositor
3. drawn by compositor
Those three states are encoded in two flags: frame_pending and
frame_still_painting.
frame_pending means step 1 is done, but step 2 and 3 are still
in progress. frame_still_painting means step 2 is still in progress.
After step 1 is finished the surface is frozen until step 3 is finished.
When the compositor notifies gtk it's done with step 3, with a
_NET_WM_FRAME_DRAWN client message, the toolkit thaws the surface to
allow the next frame to proceed.
The compositor sometimes sends gtk a _NET_WM_FRAME_DRAWN client message
between steps 1 and 2. This message should be ignored because it's not
a reply to the current frame.
Unfortunately, gtk currently assumes if it gets a _NET_WM_FRAME_DRAWN
client message while waiting for step 2 that it's actually at step 3,
and proceeds to draw a new frame while the existing frame is still
pending, leading to a blown assertion.
This commit addresses the problem by ignoring _NET_WM_FRAME_DRAWN
client messages from the compositor unless actually expecting one.
Fixes: #2902
Since commit 972134abe4 we now call
glClientWaitSync for the vendor nvidia driver, to know when a frame
is ready for the compositor to process.
If a surface is hidden while a frame is still being rendered by the GPU,
the surface will never produce the damage event the code relies on to
trigger the call to glClientWaitSync. This leaves the fence dangling,
and the next time the surface is shown, it will start a fresh frame
and blow an assertion since the fence from the last frame is still
hanging around.
This commit ensures a frame gets fully wrapped up before hiding a
surface.
Include docstrings and format the list of supported
values better.
Also, add the same warning we have for GTK_DEBUG when
the environment variable is ignored.
This kind of transient state sets the expectative that events update
devices, while it's more accurate to say that devices generate events.
It does not make to expose this function anymore.
Crossing events are now detached from widget state, all tricky consequences
from getting multiple crossing events are now somewhat moot. Resort to sending
all generated crossing events, and drop this barely (ever?) used API.
Commit a0f6ff101e made sure that a
context was bound before calling glClientWaitSync, but it doesn't
check that the context shares objects with the context that created
the fence.
This commit does a little more validation before deciding the current
context is good enough.
Since commit 972134abe4 we now call
glClientWaitSync for the vendor nvidia driver, to know when a frame
is ready for the compositor to process.
glClientWaitSync can be called regardless of which context is currently
bound, but if no context is bound at all, it returns 0 without
doing anything.
This commit checks for that edge case, and ensures a context gets
made current in the event no context is already current, before calling
glClientWaitSync.
When given a 0 timeout, glClientWaitSync is only supposed to return one
of three possible values:
- GL_ALREADY_SIGNALED - fence fired
- GL_WAIT_FAILED - there was an error
- GL_TIMEOUT_EXPIRED - fence hasn't fired yet
In addition, it can also return GL_CONDITION_SATISFIED if a non-zero
timeout is passed, and the fence fires while waiting on the timeout.
Since commit 972134abe4 we now call
glClientWaitSync (with a 0 timeout), but one user is reporting it's
returning some value that's not one of the above four.
This commit changes the g_assert to a g_error so we can see what
value is getting returned.
May help with https://gitlab.gnome.org/GNOME/gtk/-/issues/2858
Fix scheduling of the frame clock when we don't receive "frame drawn"
messages from the compositor.
If we received "frame drawn" events recently, then the "smooth frame
time" would be in sync with the vsync time. When we don't receive frame
drawn events, the "smooth frame time" is simply incremented by constant
multiples of the refresh interval. In both cases we can use this smooth
time as the basis for scheduling the next clock cycle.
By only using the "smooth frame time" as a basis we also benefit from
more consistent scheduling cadence. If, for example, we got "frame
drawn" events, then didn't receive them for a few frames, we would still
be in sync when we start receiving these events again.
When an animation is started while the application is idle, that often
happens as a result of some external event. This can be an input event,
an expired timer, data arriving over the network etc. The result is that
the first animation clock cycle could be scheduled at some random time,
as opposed to follow up cycles which are usually scheduled right after a
vsync.
Since the frame time we report to the application is correlated to the
time when the frame clock was scheduled to run, this can result in
uneven times reported in the first few animation frames. In order to fix
that, we measure the phase of the first clock cycle - i.e. the offset
between the first cycle and the preceding vsync. Once we start receiving
"frame drawn" signals, the cadence of the frame clock scheduling becomes
tied to the vsync. In order to maintain the regularity of the reported
frame times, we adjust subsequent reported frame times with the
aforementioned phase.
Use better matching format modifiers/specifiers, initialise some things
which in theory wont be written to because of getters using g_return_if_fail(),
a cast, and gsize as input for malloc because gsize!=glong on 64bit Windows.
GTK 4.0 was currently using GL_EXT_framebuffer_object, which is
deprecated as the ARB version has been merged into OpenGL 3.0 as well as
OpenGL ES 2.0, and provides laxer requirements.
This is a port of !2076 for 4.x.
Scroll events can have history too, so make a
getter that works for both. This drops the
gdk_scroll_event_get_history getter that was
added a few commits earlier, since we now
store scroll history in the same way as
motion history.
Update the docs, and all callers.
There is really no need to store 128 doubles as axes,
ever. We can do just fine with 10. At the same time,
add a GdkAxisFlags member, so we can interpret the
values without having to go chasing the right device
for this information.
Only return one accumulated scroll event per frame.
Compress them by adding up the deltas.
Still missing: a way to capture history, like
we do for motion events.
Fixes: #2800
With the vendor provided Nvidia driver there is a small window of time
after drawing to a GL surface before the updates to that surface
can be used by the compositor.
Drawing is already coordinated with the compositor through the frame
synchronization protocol detailed here:
https://fishsoup.net/misc/wm-spec-synchronization.html
Unfortunately, at the moment, GdkX11Surface tells the compositor the
frame is ready immediately after drawing to the surface, not later,
when it's consumable by the compositor.
This commit defers announcing the frame as ready until it's consumable
by the compositor. It does this by listening for the X server to announce
damage events associated with the frame drawing. It tries to find the
right damage event by waiting until fence placed at buffer swap time
signals.
This commit moves some of the end frame sync counter handling
code to subroutines.
It's a minor readability win, but the main motivation is to
make it easier in a subsequent commit to defer updating the
sync counter until a more appropriate time.
commit 14bf58ec5d dropped support
for using the DAMAGE extension since there was no code that
needed it.
We're going to need it again, however, to address an NVidia
vendor driver issue.
This commit does the plumbing to add it back.
When the application does not receive "frame drawn" signals we schedule
the clock to run more or less at intervals equal to the last known
refresh interval. In order to minimize clock skew we have to aim for
exact intervals.
We try to step the frame clock in whole refresh_interval steps, but to
avoid drift and rounding issues we additionally try to converge it to
be synced to the physical vblank (actually the time we get the
frame-drawn message from the compositor, but these are tied together).
However, the convergence to vsync only really makes sense if the new
frame_time actually is tied to the vsync. It may very well be that
some other kind of event (say a network or mouse event) triggered
the redraw, and not a vsync presentation.
We used to assume that all frames that are close in time (< 4 frames
apart) were regular and thus tied to the vsync, but there is really no
guarantee of that. Even non regular times could be rapid.
This commit changes the code to only do the convergence-to-real-time
if the cause of the clock cycle was a thaw (i.e. last frame drawn and
animating). Paint cycles for any other kind of reason are always
scheduled an integer number of frames after the last cycle that was
caused by a thaw.
When we get to a paint cycle we now know if this was caused by a
thaw, which typically means last frame was drawn, or some other event.
In the first case the time of the cycle is tied to the vblank in some
sense, and in the others it is essentially random. We can use this
information to compute better frame times. (Will be done in later
commits.)
The included fribidi header is not used in gdkkeys-wayland.c and already
included in gdk.c which causes linker issues due to the header defining
a global variable.
Scroll events do not have a position, so they shouldn't implement the
GdkEventClass.get_position() virtual function; nor they should have an x
and y fields that never get updated.
When we run the frameclock RUN_FLUSH_IDLE idle before the paint,
then gdk_frame_clock_flush_idle() sets
```
priv->phase = GDK_FRAME_CLOCK_PHASE_BEFORE_PAINT
```
at the end if there is a paint comming.
But, before doing the paint cycle it may handle other X events, and
during that time the phase is set to BEFORE_PAINT. This means that the
current check on whether we're inside a paint is wrong:
```
if (priv->phase != GDK_FRAME_CLOCK_PHASE_NONE &&
priv->phase != GDK_FRAME_CLOCK_PHASE_FLUSH_EVENTS)
return priv->smoothed_frame_time_base;
```
This caused us to sometimes use this smoothed_frame_time_base even
though we previously reported a later value during PHASE_NONE, thus
being non-monotonic.
We can't just additionally check for the BEGIN_PAINT phase though,
becasue if we are in the paint loop actually doing that phase we
should use the time base. Instead we check for `!(BEFORE_PAINT &&
in_paint_idle)`.
We require a C compiler supporting C99 now. The main purpose of
these fallbacks was for MSVC. From what I can see this is now all supported
by MSVC 2015+ anyway.
The only other change this includes is to replace isnanf() with the
(type infering) C99 isnan() macro, because MSVC doesn't provide isnanf().
Without a way to create events, there is no point
in allowing gdk_display_put_event to be used from
the outside. And little good can come out of using
the other apis, so just make them all private.
A call to frame gdk_frame_clock_get_frame_time() outside of the paint
cycle could report an un-error-corrected frame time, and later a
corrected value could be earlier than the previously reported value.
We now always store the latest reported time so we can ensure
monotonicity.
In commit c6901a8b, the frame clock reported time was changed from
simply reporting the time we ran the frame clock cycle to reporting a
smoothed value that increased by the frame interval each time it was
called.
However, this change caused some problems, such as:
https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/1415https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/1416https://gitlab.gnome.org/GNOME/gtk/-/merge_requests/1482
I think a lot of this is caused by the fact that we just overwrote the
old frame time with the smoothed, monotonous timestamp, breaking
some things that relied on knowing the actual time something happened.
This is a new approach to doing the smoothing that is more explicit.
The "frame_time" we store is the actual time we ran the update cycle,
and then we separately compute and store the derived smoothed time and
its period, allowing us to easily return a smoothed time at any time
by rounding the time difference to an integer number of frames.
The initial frame_time can be somewhat arbitrary, as it depends on the
first cycle which is not driven by the frame clock. But follow-up
cycles are typically tied to the the compositor sending the drawn
signal. It may happen that the initial frame is exactly in the middle
between two frames where jitter causes us to randomly round in
different directions when rounding to nearest frame. To fix this we
additionally do a quadratic convergence towards the "real" time,
during presentation driven clock cycles (i.e. when the frame times are
small).
On my X11 + nvidia setup gnome-shell doesn't report presentation times.
However it does report refresh rate. We were mostly using this in our
calculation except when computing predicted presentation time, were
it fell back on the default 60Hz.
Don't call into the backends when the input region
or shadow width don't actually change. This avoid
distracting calls in debug logs, and just generally
is the right thing to do.
This is not used anymore now that surfaces are always toplevel in the
semantics of GdkWindow where child windows were available. We can drop
that and simplify the vfunc just a bit more.
Fixes#2765
On X11, shortcuts inhibition is emulated using a grab on the keyboard.
So if another widget ungrabs the keyboard behind our back (for example
when a popup window is dismissed) that effectively disables the effects
of the shortcut inhibition on the surface and we need to update the
shortcut inhibition status accordingly.
Check for "grab-broken" events on the surface and clear existing
shortcuts inhibition for the matching seat, so that the client can be
notified and may decide to re-enable shortcut inhibition if desired.
We pass the GdkEvent as a pointer, because the autogenerated marshallers
don't know how to handle GTypeInstance-derived classes.
Since the GValue box that we use in the marshaller passes the GdkEvent
instance as is, we also need to acquire a reference before invoking the
closure, and release it afterwards, to ensure that the GdkEvent instance
survices the invocation.
Just because we take a ref on a surface does not
guarantee that it is still usable a second later.
Check if its been destroyed in the meantime.
This is breaking the template tests in ci, since
there is no client behind the Broadway server.
The assumption is that the source device in events
is a slave device, so create pointer and keyboard
devices and use them in events.
This fixes the seat test on Broadway.
GDK backends are expected to keep a references on
their surfaces as long as they are associated with
external resources, and drop it in destroy().
This showed up as criticals in the shortcuts test
which manually creates and destroys surfaces.
If the tablet gets removed/freed while there are pad events in flight,
we leave a dangling pointer from the pad to the tablet, which may
lead to invalid reads/writes when handling the pad event(s).
If you run weston with the headless backend, you get a Wayland
display with no seat, which is just fine by the protocol.
gdk_display_get_default_seat() returns NULL in this case. Various
widgets assume that we always have a seat with a keyboard and a
pointer, since that is what X guarantees. Make things survive
without that, so we can run the testsuite under a headless
Wayland compositor.
In the gtk-demo drag-and-drop demo i can't drag anything, all I get
is:
(gtk4-demo:358993): Gdk-CRITICAL **: 09:36:19.617: Surface 0x7e1bb0 has not been mapped in GdkSeatGrabPrepareFunc
This is because GdkX11Drag.ipc_surface is not considered mapped, even
though we called gdk_x11_surface_show() on it, because the
GDK_SURFACE_STATE_WITHDRAWN flag is still set.
I added calls to gdk_synthesize_surface_state() to match what
e.g. show_popup() and gdk_x11_toplevel_present() does.
Tracking of those broke sometime along the gdk cleanups, so we
started missing some GDK_GRAB_BROKEN events from being emitted
(eg. after a button press/implicit grab triggers an active grab).
Implicit grabs are only added if there's no prior grab (either
implicit through other button presses, or explicit), in order to
keep accounting correct, make those prevail.
Conditionally check whether the Vulkan headers version defines
VK_RESULT_RANGE_SIZE, and avoid using it for version >=140. The
following comming in Vulkan-Headers has removed the enum value:
0c5351f5e9 (diff-4febd94c0666d59030d8b1dd20c72403)
When I removed the 0-termination for the entries for
each keyval, I remove the code initializing the key,
but accidentally left the code that adds it to the
array, so gdk_keymap_get_cached_entries_for_keyval
returns one extra, uninitialized value at the end
of the array. Stop doing that.
We currently calling gdk_display_map_keyval up to
once per key event per shortcut trigger, and that function
does an expensive loop over the entire keymap and
allocates an array. Avoid this by caching the entries
in a single array, and have a lookup table for finding
the entries for a keyval.
To do this, change the GdkKeymap.get_entries_for_keyval
signature, and change the ::keys-changed signal to be
RUN_FIRST, since we want to clear the cache in the class
handler before running signal handlers. These changes are
possible now, since keymaps are no longer public API.
In the absence of icon themes (such as in a freshly
created toolbox container), we should not just fall back
to "no cursor", since that makes it hard even to close
the application. Fall back to an included default cursor
of last resort.
Provide a fallback cursor of last resort. Otherwise,
we end up with no visible cursor if there is no
cursor theme installed, wihch is less than helpful.
If the wl_surface receiving touch events is destroyed, we will get no
wl_touch.up event to remove the touchpoint from our internal accounting.
Check for this, and drop touchpoints happening in surfaces that do
disappear during operation.
The api to configure surfaces is now GdkToplevelLayout
and GdkPopupLayout. Unfortunately, there's still quite
a bit of internal use of GdkGeometry that will take some
time to clean up, so move it go gdkinternals.h for now.
The api to configure surfaces is now GdkToplevelLayout
and GdkPopupLayout. Unfortunately, there's still quite
a bit of internal use of GdkGeometry that will take some
time to clean up, so move it go gdkinternals.h for now.
We don't create a grabbing popup if it's not the top most one, as that
is a protocol violation, and complain if anything attempts to do it.
What we didn't do is handle this gracefully in the code that tries to
create said popup.
Fix this by dropping the attempt to show the popup on the floor, instead
of setting various state making it look like it succeeded. This won't
actually fix anything, but it'll result in a bit more accurate warnings
logged, as the state more correctly corresponds to the reality.
When we autohide a popup surface with a grab, hide all other auto hiding
popups up the popup chain. The end result is that when you click outside
a menu with submenus open, the whole menu chain is dismissed.
GdkEvent has been a "I-can't-believe-this-is-not-OOP" type for ages,
using a union of sub-types. This has always been problematic when it
comes to implementing accessor functions: either you get generic API
that takes a GdkEvent and uses a massive switch() to determine which
event types have the data you're looking for; or you create namespaced
accessors, but break language bindings horribly, as boxed types cannot
have derived types.
The recent conversion of GskRenderNode (which had similar issues) to
GTypeInstance, and the fact that GdkEvent is now a completely opaque
type, provide us with the chance of moving GdkEvent to GTypeInstance,
and have sub-types for GdkEvent.
The change from boxed type to GTypeInstance is pretty small, all things
considered, but ends up cascading to a larger commit, as we still have
backends and code in GTK trying to access GdkEvent structures directly.
Additionally, the naming of the public getter functions requires
renaming all the data structures to conform to the namespace/type-name
pattern.
For the X11 backend, keep a list of monitors for which the surface
intersects the monitor area.
Whenever the X11 surface is configured, check against the list of
monitors to determine whether it enters a new monitor or if it left a
monitor, to emit the corresponding ::enter/leave-monitor signals just
like a Wayland compositor would.
As monitors can be added, removed or reconfigured at any time, redo
those checks whenever any of these events occur.
Stop rewriting key and focus events on the GDK side.
Instead deliver them as they are, and propagate them
from the root on the gtk side, in gtkmain.c. And
stop complaining about focus events on popups - we
can just ignore them if we have no use for them.
A toplevel will only ever be transient-for to another toplevel, and only
a toplevel will ever be transient-for, so move the field into the
GdkWaylandToplevel, and make it a pointer to another GdkWaylandToplevel.
We them up there, so that code higher up compared to where they are
defined now can make use of them. Also add a few macros for type
checking and casting.
The third version of xdg-shell introduces support for explicit popup
repositioning. If available, make use of this to implement popup
repositioning.
Note that this does *NOT* include atomic parent-child state
synchronization. For that,
https://gitlab.freedesktop.org/wayland/wayland-protocols/issues/13 will
be needed.
This currently uses my own fork of wayland-protocols which adds meson
support, so that we can use it as a subproject. Eventually when
wayland-protocols' meson support lands upstream, we should change it to
point there.
Silence some meson warnings while at it to make CI happy.
This also bumps the glib requirement, since g_warning_once() is used.
We don't need all of them, only the ones that contain public API. This
allows us to reduce the chance of a stray symbol getting incorrectly
added to the introspection data.
This removes the use of the context menu and shift group
intents in gdkevents.c. If it turns out to be important,
we need to introduce vfuncs for gdk_event_triggers_context_menu
and gdk_event_matches.
Add all of the keyboard translation results in the key event,
so we can translate the keyboard state at the time the event
is created, and avoid doing state translation at match time.
We actually need to carry two sets of translation results,
since we ignore CapsLock when matching accelerators, in
gdk_event_matches().
At the same time, drop the scancode field - it is only ever
set on win32, and is basically unused in GTK.
Update all callers.
The win32 backend is using GDK_MOD2_MASK for AltGr,
so define GDK_MOD2_MASK locally to keep this working,
but remove any mention of GDK_MOD3_MASK,...,GDK_MOD5_MASK.
With C compilers defaulting to -fcommon, this isn't an issue, but
upcoming compilers (GCC 10 and Clang 11) will default to -fno-common,
ending up with duplicate definitions of these variables.
With the removal of `gdk_seat_grab` we do not need to keep the
definitions of `GdkGrabStatus` and `GdkGrabOwnership` public.
Move those definitions to become internal only.
When we reconfigure, `configure_file()` is called again, and
`*.gresource.xml` files are regenerated, which causes many (all?)
binaries to be relinked. Now we only write those out if the contents
actually changed (or if the output didn't already exist).
This is exactly what Meson already does with `configure_file()` when
`command:` is not used.
While we're at it, also do the same for `gen-c-array.py` and
`gentypefuncs.py` for completeness. Now even if the input to those
changes, re-building of those custom targets may not result in
relinking if the outputted C files have the same contents.
When we `Alt+Tab` away from a GTK application, it loses keyboard focus.
If we don't clear the modifiers, events from other devices that we
receive while unfocused will assume `Alt` is still pressed. This results
in e.g. Firefox navigating through the history instead of scrolling the
page when using the mouse wheel on it.
We don't get any information about modifiers while we are missing
keyboard focus, so assuming no modifiers are active is the best we can
do.
The shell sends us a modifier update immediately before we regain
keyboard focus, so the state shouldn't get out of sync.
Fixes https://gitlab.gnome.org/GNOME/gtk/-/issues/2112
Win32 backend doesn't have support for inhibit shortcuts, yet it needs
support the standard set of GdkToplevel properties.
Add support for the "inhibit-list" object property to GdkToplevel on
win32.
Broadway doesn't have support for inhibit shortcuts, yet it needs to
support the standard set of GdkToplevel properties.
Add support for the "inhibit-list" object property to GdkToplevel on
Broadway.
On X11, there is no such equivalent to the inhibit shortcut protocol
found on Wayland.
To implement the inhibit_system_shortcuts API on X11, we emulate the
same behavior using grabs on the keyboard.
To avoid keeping active grabs on the keyboard that would affect other
X11 applications even when the surface isn't focused, the X11
implementation takes care of releasing the grabs as soon as the toplevel
loses focus.
With the current implementation, we use a `wl_seat` as the key for our
internal has table where we store the Wayland shortcuts inhibitors.
There is however no technical reason for this, and we could use a
GdkSeat instead, which will ease the implementation of the GdkToplevel
shortcut inhibition API.
With the removal of grabs from the public API, we need a replacement API
to let applications bypass system keyboard shortcuts.
A typical use case for this API is remote desktop or virtual machine
viewers which need to inhibit the default system keyboard shortcuts so
that the remote session or virtual host gets those instead of the local
environment.
Close: https://gitlab.gnome.org/GNOME/gtk/issues/982
GTK defines various types that are meant to be derivable only within GTK
itself, and "final" from the perspective of consumers of the GTK API.
The existing macros defined by GObject, such as G_DECLARE_FINAL_TYPE and
G_DECLARE_DERIVABLE_TYPE, lack this functionality.
While we wait for GObject to get this kind of macro, we should define
our own.
Without this, the back buffers of the wrong size
keep being used, causing flickery misdraws, as
seen when expanding the expander in the popover
in widget-factory.
surface->x/y (and various x,y arguments) should be in the parent
coordinates, so treat it as such. We also keep track of the root coords
as these are needed for popup positioning.
Also, drop the isTemp property server side and the weird initial
placement at (100, 100) in the daemon. We now fully control window
placement from the client instead. If this is not we want we should do
a serious design for that but until then lets do the simplest thing.
In firefox, onload will trigger when the image is loaded, but at
that point it may not be decoded yet so showing it will sometimers
trigger flashes. We use the new decode() feature instead which ensures
both that the image is loaded *and* decoded, thus fixing the flashes.
These fixes were done blindly, to make the ci pass,
and will need review by somebody with access to an
actual win32 system to make sure the surface subtypes
are implemented properly.