twitchapon-anim

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

win32.go (8942B)


      1 // Copyright 2015 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 windows
      6 
      7 package gldriver
      8 
      9 import (
     10 	"errors"
     11 	"fmt"
     12 	"runtime"
     13 	"syscall"
     14 	"unsafe"
     15 
     16 	"golang.org/x/exp/shiny/driver/internal/win32"
     17 	"golang.org/x/exp/shiny/screen"
     18 	"golang.org/x/mobile/event/key"
     19 	"golang.org/x/mobile/event/lifecycle"
     20 	"golang.org/x/mobile/event/mouse"
     21 	"golang.org/x/mobile/event/paint"
     22 	"golang.org/x/mobile/event/size"
     23 	"golang.org/x/mobile/gl"
     24 )
     25 
     26 // TODO: change this to true, after manual testing on Win32.
     27 const useLifecycler = false
     28 
     29 // TODO: change this to true, after manual testing on Win32.
     30 const handleSizeEventsAtChannelReceive = false
     31 
     32 func main(f func(screen.Screen)) error {
     33 	return win32.Main(func() { f(theScreen) })
     34 }
     35 
     36 var (
     37 	eglGetPlatformDisplayEXT = gl.LibEGL.NewProc("eglGetPlatformDisplayEXT")
     38 	eglInitialize            = gl.LibEGL.NewProc("eglInitialize")
     39 	eglChooseConfig          = gl.LibEGL.NewProc("eglChooseConfig")
     40 	eglGetError              = gl.LibEGL.NewProc("eglGetError")
     41 	eglBindAPI               = gl.LibEGL.NewProc("eglBindAPI")
     42 	eglCreateWindowSurface   = gl.LibEGL.NewProc("eglCreateWindowSurface")
     43 	eglCreateContext         = gl.LibEGL.NewProc("eglCreateContext")
     44 	eglMakeCurrent           = gl.LibEGL.NewProc("eglMakeCurrent")
     45 	eglSwapInterval          = gl.LibEGL.NewProc("eglSwapInterval")
     46 	eglDestroySurface        = gl.LibEGL.NewProc("eglDestroySurface")
     47 	eglSwapBuffers           = gl.LibEGL.NewProc("eglSwapBuffers")
     48 )
     49 
     50 type eglConfig uintptr // void*
     51 
     52 type eglInt int32
     53 
     54 var rgb888 = [...]eglInt{
     55 	_EGL_RENDERABLE_TYPE, _EGL_OPENGL_ES2_BIT,
     56 	_EGL_SURFACE_TYPE, _EGL_WINDOW_BIT,
     57 	_EGL_BLUE_SIZE, 8,
     58 	_EGL_GREEN_SIZE, 8,
     59 	_EGL_RED_SIZE, 8,
     60 	_EGL_DEPTH_SIZE, 16,
     61 	_EGL_STENCIL_SIZE, 8,
     62 	_EGL_NONE,
     63 }
     64 
     65 type ctxWin32 struct {
     66 	ctx     uintptr
     67 	display uintptr // EGLDisplay
     68 	surface uintptr // EGLSurface
     69 }
     70 
     71 func newWindow(opts *screen.NewWindowOptions) (uintptr, error) {
     72 	w, err := win32.NewWindow(opts)
     73 	if err != nil {
     74 		return 0, err
     75 	}
     76 	return uintptr(w), nil
     77 }
     78 
     79 func initWindow(w *windowImpl) {
     80 	w.glctx, w.worker = gl.NewContext()
     81 }
     82 
     83 func showWindow(w *windowImpl) {
     84 	// Show makes an initial call to sizeEvent (via win32.SizeEvent), where
     85 	// we setup the EGL surface and GL context.
     86 	win32.Show(syscall.Handle(w.id))
     87 }
     88 
     89 func closeWindow(id uintptr) {} // TODO
     90 
     91 func drawLoop(w *windowImpl) {
     92 	runtime.LockOSThread()
     93 
     94 	display := w.ctx.(ctxWin32).display
     95 	surface := w.ctx.(ctxWin32).surface
     96 	ctx := w.ctx.(ctxWin32).ctx
     97 
     98 	if ret, _, _ := eglMakeCurrent.Call(display, surface, surface, ctx); ret == 0 {
     99 		panic(fmt.Sprintf("eglMakeCurrent failed: %v", eglErr()))
    100 	}
    101 
    102 	// TODO(crawshaw): exit this goroutine on Release.
    103 	workAvailable := w.worker.WorkAvailable()
    104 	for {
    105 		select {
    106 		case <-workAvailable:
    107 			w.worker.DoWork()
    108 		case <-w.publish:
    109 		loop:
    110 			for {
    111 				select {
    112 				case <-workAvailable:
    113 					w.worker.DoWork()
    114 				default:
    115 					break loop
    116 				}
    117 			}
    118 			if ret, _, _ := eglSwapBuffers.Call(display, surface); ret == 0 {
    119 				panic(fmt.Sprintf("eglSwapBuffers failed: %v", eglErr()))
    120 			}
    121 			w.publishDone <- screen.PublishResult{}
    122 		}
    123 	}
    124 }
    125 
    126 func init() {
    127 	win32.SizeEvent = sizeEvent
    128 	win32.PaintEvent = paintEvent
    129 	win32.MouseEvent = mouseEvent
    130 	win32.KeyEvent = keyEvent
    131 	win32.LifecycleEvent = lifecycleEvent
    132 }
    133 
    134 func lifecycleEvent(hwnd syscall.Handle, to lifecycle.Stage) {
    135 	theScreen.mu.Lock()
    136 	w := theScreen.windows[uintptr(hwnd)]
    137 	theScreen.mu.Unlock()
    138 
    139 	if w.lifecycleStage == to {
    140 		return
    141 	}
    142 	w.Send(lifecycle.Event{
    143 		From:        w.lifecycleStage,
    144 		To:          to,
    145 		DrawContext: w.glctx,
    146 	})
    147 	w.lifecycleStage = to
    148 }
    149 
    150 func mouseEvent(hwnd syscall.Handle, e mouse.Event) {
    151 	theScreen.mu.Lock()
    152 	w := theScreen.windows[uintptr(hwnd)]
    153 	theScreen.mu.Unlock()
    154 
    155 	w.Send(e)
    156 }
    157 
    158 func keyEvent(hwnd syscall.Handle, e key.Event) {
    159 	theScreen.mu.Lock()
    160 	w := theScreen.windows[uintptr(hwnd)]
    161 	theScreen.mu.Unlock()
    162 
    163 	w.Send(e)
    164 }
    165 
    166 func paintEvent(hwnd syscall.Handle, e paint.Event) {
    167 	theScreen.mu.Lock()
    168 	w := theScreen.windows[uintptr(hwnd)]
    169 	theScreen.mu.Unlock()
    170 
    171 	if w.ctx == nil {
    172 		// Sometimes a paint event comes in before initial
    173 		// window size is set. Ignore it.
    174 		return
    175 	}
    176 
    177 	// TODO: the paint.Event should have External: true.
    178 	w.Send(paint.Event{})
    179 }
    180 
    181 func sizeEvent(hwnd syscall.Handle, e size.Event) {
    182 	theScreen.mu.Lock()
    183 	w := theScreen.windows[uintptr(hwnd)]
    184 	theScreen.mu.Unlock()
    185 
    186 	if w.ctx == nil {
    187 		// This is the initial size event on window creation.
    188 		// Create an EGL surface and spin up a GL context.
    189 		if err := createEGLSurface(hwnd, w); err != nil {
    190 			panic(err)
    191 		}
    192 		go drawLoop(w)
    193 	}
    194 
    195 	if !handleSizeEventsAtChannelReceive {
    196 		w.szMu.Lock()
    197 		w.sz = e
    198 		w.szMu.Unlock()
    199 	}
    200 
    201 	w.Send(e)
    202 
    203 	if handleSizeEventsAtChannelReceive {
    204 		return
    205 	}
    206 
    207 	// Screen is dirty, generate a paint event.
    208 	//
    209 	// The sizeEvent function is called on the goroutine responsible for
    210 	// calling the GL worker.DoWork. When compiling with -tags gldebug,
    211 	// these GL calls are blocking (so we can read the error message), so
    212 	// to make progress they need to happen on another goroutine.
    213 	go func() {
    214 		// TODO: this call to Viewport is not right, but is very hard to
    215 		// do correctly with our async events channel model. We want
    216 		// the call to Viewport to be made the instant before the
    217 		// paint.Event is received.
    218 		w.glctxMu.Lock()
    219 		w.glctx.Viewport(0, 0, e.WidthPx, e.HeightPx)
    220 		w.glctx.ClearColor(0, 0, 0, 1)
    221 		w.glctx.Clear(gl.COLOR_BUFFER_BIT)
    222 		w.glctxMu.Unlock()
    223 
    224 		w.Send(paint.Event{})
    225 	}()
    226 }
    227 
    228 func eglErr() error {
    229 	if ret, _, _ := eglGetError.Call(); ret != _EGL_SUCCESS {
    230 		return errors.New(eglErrString(ret))
    231 	}
    232 	return nil
    233 }
    234 
    235 func createEGLSurface(hwnd syscall.Handle, w *windowImpl) error {
    236 	var displayAttribPlatforms = [][]eglInt{
    237 		// Default
    238 		[]eglInt{
    239 			_EGL_PLATFORM_ANGLE_TYPE_ANGLE,
    240 			_EGL_PLATFORM_ANGLE_TYPE_DEFAULT_ANGLE,
    241 			_EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, _EGL_DONT_CARE,
    242 			_EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE, _EGL_DONT_CARE,
    243 			_EGL_NONE,
    244 		},
    245 		// Direct3D 11
    246 		[]eglInt{
    247 			_EGL_PLATFORM_ANGLE_TYPE_ANGLE,
    248 			_EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
    249 			_EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, _EGL_DONT_CARE,
    250 			_EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE, _EGL_DONT_CARE,
    251 			_EGL_NONE,
    252 		},
    253 		// Direct3D 9
    254 		[]eglInt{
    255 			_EGL_PLATFORM_ANGLE_TYPE_ANGLE,
    256 			_EGL_PLATFORM_ANGLE_TYPE_D3D9_ANGLE,
    257 			_EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, _EGL_DONT_CARE,
    258 			_EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE, _EGL_DONT_CARE,
    259 			_EGL_NONE,
    260 		},
    261 		// Direct3D 11 with WARP
    262 		//   https://msdn.microsoft.com/en-us/library/windows/desktop/gg615082.aspx
    263 		[]eglInt{
    264 			_EGL_PLATFORM_ANGLE_TYPE_ANGLE,
    265 			_EGL_PLATFORM_ANGLE_TYPE_D3D11_ANGLE,
    266 			_EGL_PLATFORM_ANGLE_DEVICE_TYPE_ANGLE,
    267 			_EGL_PLATFORM_ANGLE_DEVICE_TYPE_WARP_ANGLE,
    268 			_EGL_PLATFORM_ANGLE_MAX_VERSION_MAJOR_ANGLE, _EGL_DONT_CARE,
    269 			_EGL_PLATFORM_ANGLE_MAX_VERSION_MINOR_ANGLE, _EGL_DONT_CARE,
    270 			_EGL_NONE,
    271 		},
    272 	}
    273 
    274 	dc, err := win32.GetDC(hwnd)
    275 	if err != nil {
    276 		return fmt.Errorf("win32.GetDC failed: %v", err)
    277 	}
    278 
    279 	var display uintptr = _EGL_NO_DISPLAY
    280 	for i, displayAttrib := range displayAttribPlatforms {
    281 		lastTry := i == len(displayAttribPlatforms)-1
    282 
    283 		display, _, _ = eglGetPlatformDisplayEXT.Call(
    284 			_EGL_PLATFORM_ANGLE_ANGLE,
    285 			uintptr(dc),
    286 			uintptr(unsafe.Pointer(&displayAttrib[0])),
    287 		)
    288 
    289 		if display == _EGL_NO_DISPLAY {
    290 			if !lastTry {
    291 				continue
    292 			}
    293 			return fmt.Errorf("eglGetPlatformDisplayEXT failed: %v", eglErr())
    294 		}
    295 
    296 		if ret, _, _ := eglInitialize.Call(display, 0, 0); ret == 0 {
    297 			if !lastTry {
    298 				continue
    299 			}
    300 			return fmt.Errorf("eglInitialize failed: %v", eglErr())
    301 		}
    302 	}
    303 
    304 	eglBindAPI.Call(_EGL_OPENGL_ES_API)
    305 	if err := eglErr(); err != nil {
    306 		return err
    307 	}
    308 
    309 	var numConfigs eglInt
    310 	var config eglConfig
    311 	ret, _, _ := eglChooseConfig.Call(
    312 		display,
    313 		uintptr(unsafe.Pointer(&rgb888[0])),
    314 		uintptr(unsafe.Pointer(&config)),
    315 		1,
    316 		uintptr(unsafe.Pointer(&numConfigs)),
    317 	)
    318 	if ret == 0 {
    319 		return fmt.Errorf("eglChooseConfig failed: %v", eglErr())
    320 	}
    321 	if numConfigs <= 0 {
    322 		return errors.New("eglChooseConfig found no valid config")
    323 	}
    324 
    325 	surface, _, _ := eglCreateWindowSurface.Call(display, uintptr(config), uintptr(hwnd), 0, 0)
    326 	if surface == _EGL_NO_SURFACE {
    327 		return fmt.Errorf("eglCreateWindowSurface failed: %v", eglErr())
    328 	}
    329 
    330 	contextAttribs := [...]eglInt{
    331 		_EGL_CONTEXT_CLIENT_VERSION, 2,
    332 		_EGL_NONE,
    333 	}
    334 	context, _, _ := eglCreateContext.Call(
    335 		display,
    336 		uintptr(config),
    337 		_EGL_NO_CONTEXT,
    338 		uintptr(unsafe.Pointer(&contextAttribs[0])),
    339 	)
    340 	if context == _EGL_NO_CONTEXT {
    341 		return fmt.Errorf("eglCreateContext failed: %v", eglErr())
    342 	}
    343 
    344 	eglSwapInterval.Call(display, 1)
    345 
    346 	w.ctx = ctxWin32{
    347 		ctx:     context,
    348 		display: display,
    349 		surface: surface,
    350 	}
    351 
    352 	return nil
    353 }
    354 
    355 func surfaceCreate() error {
    356 	return errors.New("gldriver: surface creation not implemented on windows")
    357 }