github.com/c-darwin/mobile@v0.0.0-20160313183840-ff625c46f7c9/app/app.go (about)

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