I know that everyone is excited about GTK4, but i’d like to leave GTK3 in a somewhat better and less conflicted shape. I repeatedly promised to do so, and this topic is part of that effort, since i can’t seem to figure out how to solve a particular problem.
Short recap:
- drop layered windows in GTK3
- either drop client-side decorations OR switch to shadow-less client-side decorations (i.e. solid-csd)
- use cairo to draw directly on DC
This likely means losing selective alpha-transparency (or not; it mostly depends on what Windows WM does; nothing we can do). But one thing i’d like to keep is the synchronized window resizing. I.e. when you drag the edge of a window to change its size, window contents (as drawn by GTK) and window itself (as seen by WM) are in sync, and there’s no situations where window size increased, but GTK failed to catch up with its drawing (leaving a gap between WM frame and window content) or where window size decreased, but GTK failed to catch up again (causing WM frame to cut off a portion of existing window contents).
There are two ways to achieve that. The first one is to integrate with advanced WM API, but that requires COM, Windows 8.1-or-later, and necessitates drawing with DirectX (DC emulation on top of DirectX is just slow, though it might cut it as a last resort or something). That’s something that GTK4 will, hopefully, do. The second way is to repaint on WM_PAINT
. More specifically: Whenever GTK receives WM_PAINT
, it should immediately (and without returning from WndProc
) repaint the contents of the window, using the DC given by a call to BeginPaint()
[1]. This is different from how GTK3 currently does things. Right now:
- GTK3 (when not using layered windows) paints on a DC it gets off the window with
GetDC
, notBeginPaint
- GTK3 paints at its own time, mostly ignoring
WM_PAINT
(it takes some area info from it, i think, and a suggestion to repaint, but doesn’t really do anything, returning from the message processor function, and only repaints later, when the paint clock ticks)
This is a major shift in the way windows are drawn. I’ve managed to hack things together with some modest success, but that hack is not something that can be merged into GTK. Basically, it’s a bastardized version of the idle paint clock that issues RedrawWindow
calls (which can be made to generate WM_PAINT
internally) during the paint phase, instead of actually causing repaints. Meanwhile WM_PAINT
handler can tell whether it’s being called by a paint clock via RedrawWindow
(in which case it grabs some data that the paint clock leaks into GDK and does what the actual paint phase was supposed to do - i.e. asks GTK to redrawn the window), or whether it just received a WM_PAINT
from the OS normally (in which case it queues a paint phase on the clock, and then manually cranks a new inner event loop, until the paint clock finally does its stuff, comes to the paint phase, and then see above). This is convoluted and breaks all manners of abstraction levels between paint clock and GDK.
Suggestions?
[1] There’s an extra condition here - WM frame must be drawn; if the window has its frame removed completely, WM stops syncing its paints - probably because it doesn’t paint the frame anymore itself; this is solved by leaving a 1-pixel-thick frame around the window; works well enough with solid-csd (yet another reason to stop using normal CSD with shadows)