~eliasnaur/gio-example

9b2e126e0460f044ec6071cfe61dc64717ffc80b — Chris Waldon 8 months ago 129cc07
deps,opengl: update opengl example to support wayland

This commit updates to a version of Gio with app.ViewEvent
implemented on Wayland. This required some refactorings
within the opengl example to make the code adapt to both
possible ViewEvents on Linux.

Signed-off-by: Chris Waldon <christopher.waldon.dev@gmail.com>
6 files changed, 99 insertions(+), 59 deletions(-)

M go.mod
M go.sum
M opengl/egl_linux.go
M opengl/main.go
M opengl/view_darwin.go
M opengl/view_windows.go
M go.mod => go.mod +1 -1
@@ 3,7 3,7 @@ module gioui.org/example
go 1.16

require (
	gioui.org v0.0.0-20220127143242-5ce1e982827b
	gioui.org v0.0.0-20220131180029-7204632c39d4
	gioui.org/x v0.0.0-20220105211426-ffad81642756
	github.com/go-gl/gl v0.0.0-20210315015930-ae072cafe09d
	github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210311203641-62640a716d48

M go.sum => go.sum +2 -3
@@ 34,10 34,9 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7
dmitri.shuralyov.com/gpu/mtl v0.0.0-20201218220906-28db891af037/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
gioui.org v0.0.0-20210910062418-d5d0a75a9bcb/go.mod h1:BTldRXnY5mrUrYZCdWyDwyMzyUzpfZN1cF4MMRrOt9w=
gioui.org v0.0.0-20211103131231-1d0769ae89a5/go.mod h1:yoWOxPng6WkDpsud+NRmkoftmyWn3rkKsYGEcWHpjTI=
gioui.org v0.0.0-20220105104929-8d8aeef66bef h1:vjBKFl76dewHkdOnlEt09daV4/E/oHHnMA4rV7eGH+E=
gioui.org v0.0.0-20220105104929-8d8aeef66bef/go.mod h1:yoWOxPng6WkDpsud+NRmkoftmyWn3rkKsYGEcWHpjTI=
gioui.org v0.0.0-20220127143242-5ce1e982827b h1:b2jYZFm8BStQkTV8gRgfAe6WkWw351B3yCJ1+oehwBY=
gioui.org v0.0.0-20220127143242-5ce1e982827b/go.mod h1:yoWOxPng6WkDpsud+NRmkoftmyWn3rkKsYGEcWHpjTI=
gioui.org v0.0.0-20220131180029-7204632c39d4 h1:U32jWptv5WOmkoNyVQlXHk8+4gK7xBjlR87LspNUMpg=
gioui.org v0.0.0-20220131180029-7204632c39d4/go.mod h1:yoWOxPng6WkDpsud+NRmkoftmyWn3rkKsYGEcWHpjTI=
gioui.org/cmd v0.0.0-20211103131231-1d0769ae89a5/go.mod h1:qrH3h4nt/PyIqx/XabL/eJ5cXQnQ0ERHqC3VEXx/Rmg=
gioui.org/cpu v0.0.0-20210808092351-bfe733dd3334/go.mod h1:A8M0Cn5o+vY5LTMlnRoK3O5kG+rH0kWfJjeKd9QpBmQ=
gioui.org/cpu v0.0.0-20210817075930-8d6a761490d2 h1:AGDDxsJE1RpcXTAxPG2B4jrwVUJGFDjINIPi1jtO6pc=

M opengl/egl_linux.go => opengl/egl_linux.go +27 -7
@@ 1,17 1,22 @@
// SPDX-License-Identifier: Unlicense OR MIT

//go:build linux && nowayland
// +build linux,nowayland

package main

import "gioui.org/app"
import (
	"image"
	"unsafe"

	"gioui.org/app"
)

/*
#cgo linux pkg-config: egl wayland-egl
#cgo CFLAGS: -DEGL_NO_X11
#cgo LDFLAGS: -lEGL -lGLESv2

#include <EGL/egl.h>
#include <wayland-client.h>
#include <wayland-egl.h>
#include <GLES3/gl3.h>
#define EGL_EGLEXT_PROTOTYPES
#include <EGL/eglext.h>


@@ 20,9 25,24 @@ import "gioui.org/app"
import "C"

func getDisplay(ve app.ViewEvent) C.EGLDisplay {
	return C.eglGetDisplay(C.EGLNativeDisplayType(ve.Display))
	switch ve := ve.(type) {
	case app.X11ViewEvent:
		return C.eglGetDisplay(C.EGLNativeDisplayType(ve.Display))
	case app.WaylandViewEvent:
		return C.eglGetDisplay(C.EGLNativeDisplayType(ve.Display))
	}
	panic("no display available")
}

func nativeViewFor(e app.ViewEvent) C.EGLNativeWindowType {
	return C.EGLNativeWindowType(uintptr(e.Window))
func nativeViewFor(e app.ViewEvent, size image.Point) (C.EGLNativeWindowType, func()) {
	switch e := e.(type) {
	case app.X11ViewEvent:
		return C.EGLNativeWindowType(uintptr(e.Window)), func() {}
	case app.WaylandViewEvent:
		eglWin := C.wl_egl_window_create((*C.struct_wl_surface)(e.Surface), C.int(size.X), C.int(size.Y))
		return C.EGLNativeWindowType(uintptr(unsafe.Pointer(eglWin))), func() {
			C.wl_egl_window_destroy(eglWin)
		}
	}
	panic("no native view available")
}

M opengl/main.go => opengl/main.go +62 -44
@@ 1,7 1,7 @@
// SPDX-License-Identifier: Unlicense OR MIT

//go:build darwin || windows || (linux && nowayland)
// +build darwin windows linux,nowayland
//go:build darwin || windows || linux
// +build darwin windows linux

// This program demonstrates the use of a custom OpenGL ES context with
// app.Window. It is similar to the GLFW example, but uses Gio's window


@@ 59,9 59,10 @@ import (
import "C"

type eglContext struct {
	disp C.EGLDisplay
	ctx  C.EGLContext
	surf C.EGLSurface
	disp    C.EGLDisplay
	ctx     C.EGLContext
	surf    C.EGLSurface
	cleanup func()
}

func main() {


@@ 84,49 85,59 @@ func loop(w *app.Window) error {
	var (
		ctx    *eglContext
		gioCtx gpu.GPU
		ve     app.ViewEvent
		init   bool
		size   image.Point
	)

	recreateContext := func() {
		w.Run(func() {
			if gioCtx != nil {
				gioCtx.Release()
				gioCtx = nil
			}
			if ctx != nil {
				C.eglMakeCurrent(ctx.disp, nil, nil, nil)
				ctx.Release()
				ctx = nil
			}
			c, err := createContext(ve, size)
			if err != nil {
				log.Fatal(err)
			}
			ctx = c
			// eglMakeCurrent binds a context to an operating system thread. Prevent Go from switching thread.
			runtime.LockOSThread()
			if ok := C.eglMakeCurrent(ctx.disp, ctx.surf, ctx.surf, ctx.ctx); ok != C.EGL_TRUE {
				err := fmt.Errorf("eglMakeCurrent failed (%#x)", C.eglGetError())
				log.Fatal(err)
			}
			glGetString := func(e C.GLenum) string {
				return C.GoString((*C.char)(unsafe.Pointer(C.glGetString(e))))
			}
			fmt.Printf("GL_VERSION: %s\nGL_RENDERER: %s\n", glGetString(C.GL_VERSION), glGetString(C.GL_RENDERER))
			gioCtx, err = gpu.New(gpu.OpenGL{ES: true, Shared: true})
			if err != nil {
				log.Fatal(err)
			}
		})
	}
	for e := range w.Events() {
		switch e := e.(type) {
		case app.ViewEvent:
			w.Run(func() {
				if gioCtx != nil {
					gioCtx.Release()
					gioCtx = nil
				}
				if ctx != nil {
					C.eglMakeCurrent(ctx.disp, nil, nil, nil)
					ctx.Release()
					ctx = nil
				}
				view := nativeViewFor(e)
				var nilv C.EGLNativeWindowType
				if view == nilv {
					return
				}
				c, err := createContext(e)
				if err != nil {
					log.Fatal(err)
				}
				ctx = c
				// eglMakeCurrent binds a context to an operating system thread. Prevent Go from switching thread.
				runtime.LockOSThread()
				if ok := C.eglMakeCurrent(ctx.disp, ctx.surf, ctx.surf, ctx.ctx); ok != C.EGL_TRUE {
					err := fmt.Errorf("eglMakeCurrent failed (%#x)", C.eglGetError())
					log.Fatal(err)
				}
				glGetString := func(e C.GLenum) string {
					return C.GoString((*C.char)(unsafe.Pointer(C.glGetString(e))))
				}
				fmt.Printf("GL_VERSION: %s\nGL_RENDERER: %s\n", glGetString(C.GL_VERSION), glGetString(C.GL_RENDERER))
				gioCtx, err = gpu.New(gpu.OpenGL{ES: true, Shared: true})
				if err != nil {
					log.Fatal(err)
				}
			})
			ve = e
			init = true
			if size != (image.Point{}) {
				recreateContext()
			}
		case system.DestroyEvent:
			return e.Err
		case system.FrameEvent:
			if gioCtx == nil {
			if init && size != e.Size {
				size = e.Size
				recreateContext()
			}
			if gioCtx == nil || !init {
				break
			}
			// Build ops.


@@ 212,8 223,12 @@ func drawUI(th *material.Theme, gtx layout.Context) layout.Dimensions {
	)
}

func createContext(ve app.ViewEvent) (*eglContext, error) {
	view := nativeViewFor(ve)
func createContext(ve app.ViewEvent, size image.Point) (*eglContext, error) {
	view, cleanup := nativeViewFor(ve, size)
	var nilv C.EGLNativeWindowType
	if view == nilv {
		return nil, fmt.Errorf("failed creating native view")
	}
	disp := getDisplay(ve)
	if disp == 0 {
		return nil, fmt.Errorf("eglGetPlatformDisplay failed: 0x%x", C.eglGetError())


@@ 267,7 282,7 @@ func createContext(ve app.ViewEvent) (*eglContext, error) {
	if surf == nil {
		return nil, fmt.Errorf("eglCreateWindowSurface failed (0x%x)", C.eglGetError())
	}
	return &eglContext{disp: disp, ctx: ctx, surf: surf}, nil
	return &eglContext{disp: disp, ctx: ctx, surf: surf, cleanup: cleanup}, nil
}

func (c *eglContext) Release() {


@@ 277,6 292,9 @@ func (c *eglContext) Release() {
	if c.surf != nil {
		C.eglDestroySurface(c.disp, c.surf)
	}
	if c.cleanup != nil {
		c.cleanup()
	}
	*c = eglContext{}
}


M opengl/view_darwin.go => opengl/view_darwin.go +4 -2
@@ 3,6 3,8 @@
package main

import (
	"image"

	"gioui.org/app"
)



@@ 11,6 13,6 @@ import (
*/
import "C"

func nativeViewFor(e app.ViewEvent) C.EGLNativeWindowType {
	return C.EGLNativeWindowType(e.Layer)
func nativeViewFor(e app.ViewEvent, _ image.Point) (C.EGLNativeWindowType, func()) {
	return C.EGLNativeWindowType(e.Layer), func() {}
}

M opengl/view_windows.go => opengl/view_windows.go +3 -2
@@ 3,6 3,7 @@
package main

import (
	"image"
	"unsafe"

	"gioui.org/app"


@@ 13,6 14,6 @@ import (
*/
import "C"

func nativeViewFor(e app.ViewEvent) C.EGLNativeWindowType {
	return C.EGLNativeWindowType(unsafe.Pointer(e.HWND))
func nativeViewFor(e app.ViewEvent, _ image.Point) (C.EGLNativeWindowType, func()) {
	return C.EGLNativeWindowType(unsafe.Pointer(e.HWND)), func() {}
}