Understanding How GDK Actually Works Under The Surface
GDK stands for the GNOME Drawing Kit. It is the low-level graphics abstraction layer that sits between GTK applications and the underlying window system, whether that is X11, Wayland, or other backends. Most developers never think about GDK directly because GTK wraps most of its functionality, but when things break, you will find yourself looking at GDK internals whether you like it or not. The "GDK sign" people sometimes refer to is the GDK versioning scheme and the way the library split from the original GDK which was itself derived from the GIMP toolkit. The history here is more procedural than dramatic. In the mid-1990s, the GIMP needed a portable graphics layer. They built one. That became GDK. Later when GTK was separated into its own library, GDK remained as the rendering backend dependency. The confusing part for newcomers is that the public API surface kept changing between major releases, and the internal structures became increasingly opaque as Wayland support was added around the GTK3 transition period. When I first started debugging rendering artifacts in a custom widget, I spent two days chasing what I thought was a GTK issue before realizing the problem was in how my code interfaced with the GDK draw context directly. The signs were there in the documentation, but the documentation treats GDK as somewhat of an afterthought compared to GTK itself. It is not undocumented, but it is under-explained in practical terms.
One thing most tutorials skip over is that GDK handles the heavy lifting for surface management, input event dispatching, and color space conversions before anything reaches the application layer. If you are building something custom, especially on Wayland, you need to understand the difference between GdkSurface and GtkWindow. They are related but serve different purposes. A GtkWindow manages the widget hierarchy and application logic. A GdkSurface represents the actual drawable area tied to the windowing system. Here is a practical scenario I ran into. I was working on a widget that needed direct access to the Cairo context at the GDK level for a video overlay component. The standard approach of using gtk_widget_get_drawable() does not work on Wayland because the drawable concept changed significantly. Instead you have to use the draw signal handler and request a repaint through GdkFrameClock. If you try to grab the surface context directly from the wrong callback, you will get an invalid surface error or corrupted output depending on the compositor behavior. The workaround I ended up using was to connect to the "draw" signal on the widget and manually invoke cairo operations within that handler. It is not the most elegant approach but it is reliable across both X11 and Wayland backends. You can also use gdk_window_begin_draw_frame() if you are working at a lower level, but that requires careful management of the draw context lifecycle. Forget to destroy the frame properly and you will leak resources or cause rendering glitches that are nearly impossible to trace.
Annoyingly, there is a common pitfall where developers assume that calling gdk_window_process_all_updates() will flush pending redraws immediately. It does not. On Wayland it is essentially a no-op because the compositor controls the redraw timing. This caused me significant headaches when I thought my widget was stuck in a bad state, when really it was just waiting for the compositor to present the next frame. If you are doing performance-critical rendering, consider using GdkTexture or GdkGLContext instead of repeatedly drawing into a Cairo surface. The overhead of software rendering through Cairo becomes noticeable when you are updating content at higher frame rates, and the API has improved considerably since GTK4. The tradeoff is that GL rendering requires more setup and is less portable across different hardware configurations. There is also a limitation worth noting upfront. If you need to integrate GDK directly with non-GTK toolkits or embed GTK content into other frameworks, the coupling is tight. GDK was designed to work primarily with GTK, and while it is theoretically possible to use it standalone, you lose a lot of the convenience abstractions and end up writing a lot more boilerplate code than you might expect. The official stance from the GNOME team is that GDK is meant to be used through GTK, not independently.
Get the Full Details

For people who want to dig deeper, the source code repository is the most honest documentation available. The header files in the gdk directory contain inline comments that explain design decisions better than any tutorial I have found. The API reference at docs.gnome.org is useful but assumes a baseline familiarity with how the layers interact. Reading recent commit messages for GDK specifically can also give you insight into what problems the current maintainers are trying to solve. One final observation. The GDK layer has become less visible to most developers over the years because GTK absorbed more of the complexity. This is good for casual users but bad when you actually need to troubleshoot something at that level. The skills required to debug GDK issues are not trivial, and the community of people who work with it at that depth is relatively small. If you find yourself there, you are on your own more often than not.