Esempio n. 1
0
func showWindow(w *windowImpl) {
	w.glctxMu.Lock()
	w.glctx, w.worker = gl.NewContext()
	w.glctxMu.Unlock()

	C.doShowWindow(C.uintptr_t(w.id))
}
Esempio n. 2
0
// loop is the primary drawing loop.
//
// After Cocoa has captured the initial OS thread for processing Cocoa
// events in runApp, it starts loop on another goroutine. It is locked
// to an OS thread for its OpenGL context.
//
// Two Cocoa threads deliver draw signals to loop. The primary source of
// draw events is the CVDisplayLink timer, which is tied to the display
// vsync. Secondary draw events come from [NSView drawRect:] when the
// window is resized.
func loop(ctx C.GLintptr) {
	runtime.LockOSThread()
	C.makeCurrentContext(ctx)
	var worker gl.Worker
	glctx, worker = gl.NewContext()

	workAvailable := worker.WorkAvailable()
	for {
		select {
		case <-workAvailable:
			worker.DoWork()
		case <-draw:
		loop1:
			for {
				select {
				case <-workAvailable:
					worker.DoWork()
				case <-theApp.publish:
					C.CGLFlushDrawable(C.CGLGetCurrentContext())
					theApp.publishResult <- PublishResult{}
					break loop1
				}
			}
			drawDone <- struct{}{}
		}
	}
}
Esempio n. 3
0
//export drawgl
func drawgl(ctx uintptr) {
	if glctx == nil {
		C.setContext(unsafe.Pointer(ctx))

		glctx, worker = gl.NewContext()
		workAvailable = worker.WorkAvailable()

		// TODO(crawshaw): not just on process start.
		theApp.sendLifecycle(lifecycle.StageFocused)
	}

	// TODO(crawshaw): don't send a paint.Event unconditionally. Only send one
	// if the window actually needs redrawing.
	theApp.eventsIn <- paint.Event{}

	for {
		select {
		case <-workAvailable:
			worker.DoWork()
		case <-theApp.publish:
			theApp.publishResult <- PublishResult{}
			return
		}
	}
}
Esempio n. 4
0
func showWindow(w *windowImpl) {
	w.glctxMu.Lock()
	w.glctx, w.worker = gl.NewContext()
	w.glctxMu.Unlock()

	// Show makes an initial call to sizeEvent (via win32.SizeEvent), where
	// we setup the EGL surface and GL context.
	win32.Show(syscall.Handle(w.id))
}
Esempio n. 5
0
func (this *Window) Init() error {
	wm := TheApp.GetDefaultDisplay().GetWindowManager()
	wm.AddWindow(this)

	this.mGL, this.mGLWorker = gl.NewContext()

	TheApp.RegisterEventCallback(this)

	this._createWindow()

	return nil
}
Esempio n. 6
0
func main(f func(screen.Screen)) error {
	if gl.Version() == "GL_ES_2_0" {
		return errors.New("gldriver: ES 3 required on X11")
	}
	C.startDriver()
	glctx, worker = gl.NewContext()

	closec := make(chan struct{})
	go func() {
		f(theScreen)
		close(closec)
	}()

	// heartbeat is a channel that, at regular intervals, directs the select
	// below to also consider X11 events, not just Go events (channel
	// communications).
	//
	// TODO: select instead of poll. Note that knowing whether to call
	// C.processEvents needs to select on a file descriptor, and the other
	// cases below select on Go channels.
	heartbeat := time.NewTicker(time.Second / 60)
	workAvailable := worker.WorkAvailable()

	for {
		select {
		case <-closec:
			return nil
		case ctx := <-glcontextc:
			// TODO: do we need to synchronize with seeing a size event for
			// this window's context before or after calling makeCurrent?
			// Otherwise, are we racing with the gl.Viewport call? I've
			// occasionally seen a stale viewport, if the window manager sets
			// the window width and height to something other than that
			// requested by XCreateWindow, but it's not easily reproducible.
			C.makeCurrent(C.uintptr_t(ctx))
		case w := <-publishc:
			C.swapBuffers(C.uintptr_t(w.ctx.(uintptr)))
			w.publishDone <- screen.PublishResult{}
		case req := <-uic:
			ret := req.f()
			if req.retc != nil {
				req.retc <- ret
			}
		case <-heartbeat.C:
			C.processEvents()
		case <-workAvailable:
			worker.DoWork()
		}
	}
}
Esempio n. 7
0
func main(f func(App)) {
	runtime.LockOSThread()

	var worker gl.Worker
	glctx, worker = gl.NewContext()
	workAvailable := worker.WorkAvailable()

	C.createWindow()

	// TODO: send lifecycle events when e.g. the X11 window is iconified or moved off-screen.
	theApp.sendLifecycle(lifecycle.StageFocused)

	// TODO: translate X11 expose events to shiny paint events, instead of
	// sending this synthetic paint event as a hack.
	theApp.eventsIn <- paint.Event{}

	donec := make(chan struct{})
	go func() {
		f(theApp)
		close(donec)
	}()

	// TODO: can we get the actual vsync signal?
	ticker := time.NewTicker(time.Second / 60)
	defer ticker.Stop()
	var tc <-chan time.Time

	for {
		select {
		case <-donec:
			return
		case <-workAvailable:
			worker.DoWork()
		case <-theApp.publish:
			C.swapBuffers()
			tc = ticker.C
		case <-tc:
			tc = nil
			theApp.publishResult <- PublishResult{}
		}
		C.processEvents()
	}
}
Esempio n. 8
0
// NewContext creates an OpenGL ES context with a dedicated processing thread.
func NewContext() (gl.Context, error) {
	glctx, worker := gl.NewContext()

	errCh := make(chan error)
	workAvailable := worker.WorkAvailable()
	go func() {
		runtime.LockOSThread()
		err := surfaceCreate()
		errCh <- err
		if err != nil {
			return
		}

		for range workAvailable {
			worker.DoWork()
		}
	}()
	if err := <-errCh; err != nil {
		return nil, err
	}
	return glctx, nil
}
Esempio n. 9
0
func init() {
	theApp.eventsIn = pump(theApp.eventsOut)
	theApp.glctx, theApp.worker = gl.NewContext()
}
Esempio n. 10
0
func mainUI(vm, jniEnv, ctx uintptr) error {
	var worker gl.Worker
	glctx, worker = gl.NewContext()
	workAvailable := worker.WorkAvailable()

	donec := make(chan struct{})
	go func() {
		mainUserFn(theApp)
		close(donec)
	}()

	var pixelsPerPt float32
	var orientation size.Orientation

	// Android can send a windowRedrawNeeded event any time, including
	// in the middle of a paint cycle. The redraw event may have changed
	// the size of the screen, so any partial painting is now invalidated.
	// We must also not return to Android (via sending on windowRedrawDone)
	// until a complete paint with the new configuration is complete.
	//
	// When a windowRedrawNeeded request comes in, we increment redrawGen
	// (Gen is short for generation number), and do not make a paint cycle
	// visible on <-endPaint unless Generation agrees. If possible,
	// windowRedrawDone is signalled, allowing onNativeWindowRedrawNeeded
	// to return.
	//
	// TODO: is this still needed?
	var redrawGen uint32

	for {
		select {
		case <-donec:
			return nil
		case cfg := <-windowConfigChange:
			pixelsPerPt = cfg.pixelsPerPt
			orientation = cfg.orientation
		case w := <-windowRedrawNeeded:
			if C.surface == nil {
				if errStr := C.createEGLSurface(w); errStr != nil {
					return fmt.Errorf("%s (%s)", C.GoString(errStr), eglGetError())
				}
			}
			theApp.sendLifecycle(lifecycle.StageFocused)
			widthPx := int(C.ANativeWindow_getWidth(w))
			heightPx := int(C.ANativeWindow_getHeight(w))
			theApp.eventsIn <- size.Event{
				WidthPx:     widthPx,
				HeightPx:    heightPx,
				WidthPt:     geom.Pt(float32(widthPx) / pixelsPerPt),
				HeightPt:    geom.Pt(float32(heightPx) / pixelsPerPt),
				PixelsPerPt: pixelsPerPt,
				Orientation: orientation,
			}
			redrawGen++
			theApp.eventsIn <- paint.Event{redrawGen}
		case <-windowDestroyed:
			if C.surface != nil {
				if errStr := C.destroyEGLSurface(); errStr != nil {
					return fmt.Errorf("%s (%s)", C.GoString(errStr), eglGetError())
				}
			}
			C.surface = nil
			theApp.sendLifecycle(lifecycle.StageAlive)
		case <-workAvailable:
			worker.DoWork()
		case <-theApp.publish:
			// TODO: compare a generation number to redrawGen for stale paints?
			if C.surface != nil {
				// eglSwapBuffers blocks until vsync.
				if C.eglSwapBuffers(C.display, C.surface) == C.EGL_FALSE {
					log.Printf("app: failed to swap buffers (%s)", eglGetError())
				}
			}
			select {
			case windowRedrawDone <- struct{}{}:
			default:
			}
			theApp.publishResult <- PublishResult{}
		}
	}
}
Esempio n. 11
0
func NewContext() (*Context, error) {
	c := &Context{}
	c.gl, c.worker = mgl.NewContext()
	return c, nil
}