load_windows.go (3562B)
1 // Copyright 2018 The Ebiten Authors 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package glfw 16 17 import ( 18 "bytes" 19 "compress/gzip" 20 "fmt" 21 "io" 22 "io/ioutil" 23 "os" 24 "unsafe" 25 26 "golang.org/x/sys/windows" 27 ) 28 29 type dll struct { 30 d *windows.LazyDLL 31 path string 32 procs map[string]*windows.LazyProc 33 } 34 35 func (d *dll) call(name string, args ...uintptr) uintptr { 36 if d.procs == nil { 37 d.procs = map[string]*windows.LazyProc{} 38 } 39 if _, ok := d.procs[name]; !ok { 40 d.procs[name] = d.d.NewProc(name) 41 } 42 r, _, err := d.procs[name].Call(args...) 43 if err != nil && err.(windows.Errno) != 0 { 44 // It looks like there is no way to handle these errors? 45 // panic(fmt.Sprintf("glfw: calling proc error: errno: %d (%s)", err, err.Error())) 46 } 47 return r 48 } 49 50 func createTempDLL(content io.Reader) (string, error) { 51 f, err := ioutil.TempFile("", "glfw.*.dll") 52 if err != nil { 53 return "", err 54 } 55 defer f.Close() 56 57 fn := f.Name() 58 59 if _, err := io.Copy(f, content); err != nil { 60 return "", err 61 } 62 63 return fn, nil 64 } 65 66 func loadDLL() (*dll, error) { 67 f, err := gzip.NewReader(bytes.NewReader(glfwDLLCompressed)) 68 if err != nil { 69 return nil, err 70 } 71 defer f.Close() 72 73 fn, err := createTempDLL(f) 74 if err != nil { 75 return nil, err 76 } 77 78 return &dll{ 79 d: windows.NewLazyDLL(fn), 80 path: fn, 81 }, nil 82 } 83 84 func (d *dll) unload() error { 85 if err := windows.FreeLibrary(windows.Handle(d.d.Handle())); err != nil { 86 return err 87 } 88 if err := os.Remove(d.path); err != nil { 89 return err 90 } 91 return nil 92 } 93 94 func bytePtrToString(ptr *byte) string { 95 var bs []byte 96 for i := uintptr(0); ; i++ { 97 b := *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(ptr)) + i)) 98 if b == 0 { 99 break 100 } 101 bs = append(bs, b) 102 } 103 return string(bs) 104 } 105 106 type glfwError struct { 107 code ErrorCode 108 desc string 109 } 110 111 func (e *glfwError) Error() string { 112 return fmt.Sprintf("glfw: %s: %s", e.code.String(), e.desc) 113 } 114 115 var lastErr = make(chan *glfwError, 1) 116 117 func fetchError() error { 118 select { 119 case err := <-lastErr: 120 return err 121 default: 122 return nil 123 } 124 } 125 126 func panicError() { 127 if err := acceptError(); err != nil { 128 panic(err) 129 } 130 } 131 132 func flushErrors() { 133 if err := fetchError(); err != nil { 134 panic(fmt.Sprintf("glfw: uncaught error: %s", err)) 135 } 136 } 137 138 func acceptError(codes ...ErrorCode) error { 139 err := fetchError() 140 if err == nil { 141 return nil 142 } 143 for _, c := range codes { 144 if err.(*glfwError).code == c { 145 return nil 146 } 147 } 148 if err.(*glfwError).code == PlatformError { 149 // PlatformError is not handled here (See github.com/go-gl/glfw's implementation). 150 // TODO: Should we log this error? 151 return nil 152 } 153 return err 154 } 155 156 func goGLFWErrorCallback(code uintptr, desc *byte) uintptr { 157 flushErrors() 158 err := &glfwError{ 159 code: ErrorCode(code), 160 desc: bytePtrToString(desc), 161 } 162 select { 163 case lastErr <- err: 164 default: 165 panic(fmt.Sprintf("glfw: uncaught error: %s", err)) 166 } 167 return 0 168 } 169 170 var glfwDLL *dll 171 172 func init() { 173 dll, err := loadDLL() 174 if err != nil { 175 panic(err) 176 } 177 glfwDLL = dll 178 179 glfwDLL.call("glfwSetErrorCallback", windows.NewCallbackCDecl(goGLFWErrorCallback)) 180 }