Talking with the OS

Since a GUI library needs to talk to some sort of display system to display information:

window := app.NewWindow()
var ops op.Ops
for e := range window.Events() {
	switch e := e.(type) {
	case system.DestroyEvent:
		// The window was closed.
		return e.Err
	case system.FrameEvent:
		// A request to draw the window state.

		// Reset the operations back to zero.
		// Draw the state into ops.
		// Update the display.

app.NewWindow chooses the appropriate “driver” depending on the environment and build context. It might choose Wayland, Win32, or Cocoa among several others.

An app.Window sends events from the display system to the window.Events() channel. The system events are listed in The input events, such as and, are also sent into that channel.


All UI libraries need a way for the program to specify what to display and how to handle events. Gio programs use operations, serialized into one or more op.Ops operation lists. Operation lists are in turn passed to the window driver through the FrameEvent.Frame function.

By convention, each operation kind is represented by a Go type with an Add method that records the operation into the Ops argument. Like any Go struct literal, zero-valued fields can be useful to represent optional values.

For example, recording an operation that sets the current color to red:

func addColorOperation(ops *op.Ops) {
	red := color.NRGBA{R: 0xFF, A: 0xFF}
	paint.ColorOp{Color: red}.Add(ops)

You might be thinking that it would be more usual to have an ops.Add(ColorOp{Color: red}) method instead of using op.ColorOp{Color: red}.Add(ops). It’s like this so that the Add method doesn’t have to take an interface-typed argument, which would often require an allocation to call. This is a key aspect of Gio’s “zero allocation” design.