The frame clock

All GTK applications are mainloop-driven, which means that most of the time the app is idle inside a loop that just waits for something to happen and then calls out to the right place when it does. On top of this GTK has a frame clock that gives a pulse to the application. This clock beats at a steady rate, which is tied to the framerate of the output (this is synced to the monitor via the window manager/compositor). A typical refresh rate is 60 frames per second, so a new pulse happens roughly every 16 milliseconds.

The clock has several phases:

  • Events

  • Update

  • Layout

  • Paint

The phases happens in this order and we will always run each phase through before going back to the start.

The Events phase is a stretch of time between each redraw where GTK processes input events from the user and other events (like e.g. network I/O). Some events, like mouse motion are compressed so that only a single mouse motion event per clock cycle needs to be handled.

Once the Events phase is over, external events are paused and the redraw loop is run. First is the Update phase, where all animations are run to calculate the new state based on the estimated time the next frame will be visible (available via the frame clock). This often involves geometry changes which drive the next phase, Layout. If there are any changes in widget size requirements the new layout is calculated for the widget hierarchy (i.e. sizes and positions for all widgets are determined). Then comes the Paint phase, where we redraw the regions of the window that need redrawing.

If nothing requires the Update/Layout/Paint phases we will stay in the Events phase forever, as we don’t want to redraw if nothing changes. Each phase can request further processing in the following phases (e.g. the Update phase will cause there to be layout work, and layout changes cause repaints).

There are multiple ways to drive the clock, at the lowest level you can request a particular phase with gdk_frame_clock_request_phase() which will schedule a clock beat as needed so that it eventually reaches the requested phase. However, in practice most things happen at higher levels:

  • If you are doing an animation, you can use gtk_widget_add_tick_callback() which will cause a regular beating of the clock with a callback in the Update phase until you stop the tick.

  • If some state changes that causes the size of your widget to change you call gtk_widget_queue_resize() which will request a Layout phase and mark your widget as needing relayout.

  • If some state changes so you need to redraw some area of your widget you use the normal gtk_widget_queue_draw() set of functions. These will request a Paint phase and mark the region as needing redraw.

There are also a lot of implicit triggers of these from the CSS layer (which does animations, resizes and repaints as needed).

© 2005–2020 The GNOME Project
Licensed under the GNU Lesser General Public License version 2.1 or later.
https://developer.gnome.org/gtk4/4.0/frameclock.html