twitchapon-anim

Basic Twitchapon Receiver/Visuals
git clone git://bsandro.tech/twitchapon-anim
Log | Files | Refs | README | LICENSE

app.go (5903B)


      1 // Copyright 2014 The Go Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style
      3 // license that can be found in the LICENSE file.
      4 
      5 // +build linux darwin windows
      6 
      7 package app
      8 
      9 import (
     10 	"golang.org/x/mobile/event/lifecycle"
     11 	"golang.org/x/mobile/event/size"
     12 	"golang.org/x/mobile/gl"
     13 	_ "golang.org/x/mobile/internal/mobileinit"
     14 )
     15 
     16 // Main is called by the main.main function to run the mobile application.
     17 //
     18 // It calls f on the App, in a separate goroutine, as some OS-specific
     19 // libraries require being on 'the main thread'.
     20 func Main(f func(App)) {
     21 	main(f)
     22 }
     23 
     24 // App is how a GUI mobile application interacts with the OS.
     25 type App interface {
     26 	// Events returns the events channel. It carries events from the system to
     27 	// the app. The type of such events include:
     28 	//  - lifecycle.Event
     29 	//  - mouse.Event
     30 	//  - paint.Event
     31 	//  - size.Event
     32 	//  - touch.Event
     33 	// from the golang.org/x/mobile/event/etc packages. Other packages may
     34 	// define other event types that are carried on this channel.
     35 	Events() <-chan interface{}
     36 
     37 	// Send sends an event on the events channel. It does not block.
     38 	Send(event interface{})
     39 
     40 	// Publish flushes any pending drawing commands, such as OpenGL calls, and
     41 	// swaps the back buffer to the screen.
     42 	Publish() PublishResult
     43 
     44 	// TODO: replace filters (and the Events channel) with a NextEvent method?
     45 
     46 	// Filter calls each registered event filter function in sequence.
     47 	Filter(event interface{}) interface{}
     48 
     49 	// RegisterFilter registers a event filter function to be called by Filter. The
     50 	// function can return a different event, or return nil to consume the event,
     51 	// but the function can also return its argument unchanged, where its purpose
     52 	// is to trigger a side effect rather than modify the event.
     53 	RegisterFilter(f func(interface{}) interface{})
     54 }
     55 
     56 // PublishResult is the result of an App.Publish call.
     57 type PublishResult struct {
     58 	// BackBufferPreserved is whether the contents of the back buffer was
     59 	// preserved. If false, the contents are undefined.
     60 	BackBufferPreserved bool
     61 }
     62 
     63 var theApp = &app{
     64 	eventsOut:      make(chan interface{}),
     65 	lifecycleStage: lifecycle.StageDead,
     66 	publish:        make(chan struct{}),
     67 	publishResult:  make(chan PublishResult),
     68 }
     69 
     70 func init() {
     71 	theApp.eventsIn = pump(theApp.eventsOut)
     72 	theApp.glctx, theApp.worker = gl.NewContext()
     73 }
     74 
     75 func (a *app) sendLifecycle(to lifecycle.Stage) {
     76 	if a.lifecycleStage == to {
     77 		return
     78 	}
     79 	a.eventsIn <- lifecycle.Event{
     80 		From:        a.lifecycleStage,
     81 		To:          to,
     82 		DrawContext: a.glctx,
     83 	}
     84 	a.lifecycleStage = to
     85 }
     86 
     87 type app struct {
     88 	filters []func(interface{}) interface{}
     89 
     90 	eventsOut      chan interface{}
     91 	eventsIn       chan interface{}
     92 	lifecycleStage lifecycle.Stage
     93 	publish        chan struct{}
     94 	publishResult  chan PublishResult
     95 
     96 	glctx  gl.Context
     97 	worker gl.Worker
     98 }
     99 
    100 func (a *app) Events() <-chan interface{} {
    101 	return a.eventsOut
    102 }
    103 
    104 func (a *app) Send(event interface{}) {
    105 	a.eventsIn <- event
    106 }
    107 
    108 func (a *app) Publish() PublishResult {
    109 	// gl.Flush is a lightweight (on modern GL drivers) blocking call
    110 	// that ensures all GL functions pending in the gl package have
    111 	// been passed onto the GL driver before the app package attempts
    112 	// to swap the screen buffer.
    113 	//
    114 	// This enforces that the final receive (for this paint cycle) on
    115 	// gl.WorkAvailable happens before the send on endPaint.
    116 	a.glctx.Flush()
    117 	a.publish <- struct{}{}
    118 	return <-a.publishResult
    119 }
    120 
    121 func (a *app) Filter(event interface{}) interface{} {
    122 	for _, f := range a.filters {
    123 		event = f(event)
    124 	}
    125 	return event
    126 }
    127 
    128 func (a *app) RegisterFilter(f func(interface{}) interface{}) {
    129 	a.filters = append(a.filters, f)
    130 }
    131 
    132 type stopPumping struct{}
    133 
    134 // pump returns a channel src such that sending on src will eventually send on
    135 // dst, in order, but that src will always be ready to send/receive soon, even
    136 // if dst currently isn't. It is effectively an infinitely buffered channel.
    137 //
    138 // In particular, goroutine A sending on src will not deadlock even if goroutine
    139 // B that's responsible for receiving on dst is currently blocked trying to
    140 // send to A on a separate channel.
    141 //
    142 // Send a stopPumping on the src channel to close the dst channel after all queued
    143 // events are sent on dst. After that, other goroutines can still send to src,
    144 // so that such sends won't block forever, but such events will be ignored.
    145 func pump(dst chan interface{}) (src chan interface{}) {
    146 	src = make(chan interface{})
    147 	go func() {
    148 		// initialSize is the initial size of the circular buffer. It must be a
    149 		// power of 2.
    150 		const initialSize = 16
    151 		i, j, buf, mask := 0, 0, make([]interface{}, initialSize), initialSize-1
    152 
    153 		srcActive := true
    154 		for {
    155 			maybeDst := dst
    156 			if i == j {
    157 				maybeDst = nil
    158 			}
    159 			if maybeDst == nil && !srcActive {
    160 				// Pump is stopped and empty.
    161 				break
    162 			}
    163 
    164 			select {
    165 			case maybeDst <- buf[i&mask]:
    166 				buf[i&mask] = nil
    167 				i++
    168 
    169 			case e := <-src:
    170 				if _, ok := e.(stopPumping); ok {
    171 					srcActive = false
    172 					continue
    173 				}
    174 
    175 				if !srcActive {
    176 					continue
    177 				}
    178 
    179 				// Allocate a bigger buffer if necessary.
    180 				if i+len(buf) == j {
    181 					b := make([]interface{}, 2*len(buf))
    182 					n := copy(b, buf[j&mask:])
    183 					copy(b[n:], buf[:j&mask])
    184 					i, j = 0, len(buf)
    185 					buf, mask = b, len(b)-1
    186 				}
    187 
    188 				buf[j&mask] = e
    189 				j++
    190 			}
    191 		}
    192 
    193 		close(dst)
    194 		// Block forever.
    195 		for range src {
    196 		}
    197 	}()
    198 	return src
    199 }
    200 
    201 // TODO: do this for all build targets, not just linux (x11 and Android)? If
    202 // so, should package gl instead of this package call RegisterFilter??
    203 //
    204 // TODO: does Android need this?? It seems to work without it (Nexus 7,
    205 // KitKat). If only x11 needs this, should we move this to x11.go??
    206 func (a *app) registerGLViewportFilter() {
    207 	a.RegisterFilter(func(e interface{}) interface{} {
    208 		if e, ok := e.(size.Event); ok {
    209 			a.glctx.Viewport(0, 0, e.WidthPx, e.HeightPx)
    210 		}
    211 		return e
    212 	})
    213 }