glfw.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  1. // Copyright 2016 The G3N 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. //go:build !wasm
  5. // +build !wasm
  6. package window
  7. import (
  8. "bytes"
  9. "fmt"
  10. "image"
  11. _ "image/png"
  12. "os"
  13. "runtime"
  14. "github.com/g3n/engine/core"
  15. "github.com/g3n/engine/gls"
  16. "github.com/g3n/engine/gui/assets"
  17. "github.com/go-gl/glfw/v3.3/glfw"
  18. )
  19. // Keycodes
  20. const (
  21. KeyUnknown = Key(glfw.KeyUnknown)
  22. KeySpace = Key(glfw.KeySpace)
  23. KeyApostrophe = Key(glfw.KeyApostrophe)
  24. KeyComma = Key(glfw.KeyComma)
  25. KeyMinus = Key(glfw.KeyMinus)
  26. KeyPeriod = Key(glfw.KeyPeriod)
  27. KeySlash = Key(glfw.KeySlash)
  28. Key0 = Key(glfw.Key0)
  29. Key1 = Key(glfw.Key1)
  30. Key2 = Key(glfw.Key2)
  31. Key3 = Key(glfw.Key3)
  32. Key4 = Key(glfw.Key4)
  33. Key5 = Key(glfw.Key5)
  34. Key6 = Key(glfw.Key6)
  35. Key7 = Key(glfw.Key7)
  36. Key8 = Key(glfw.Key8)
  37. Key9 = Key(glfw.Key9)
  38. KeySemicolon = Key(glfw.KeySemicolon)
  39. KeyEqual = Key(glfw.KeyEqual)
  40. KeyA = Key(glfw.KeyA)
  41. KeyB = Key(glfw.KeyB)
  42. KeyC = Key(glfw.KeyC)
  43. KeyD = Key(glfw.KeyD)
  44. KeyE = Key(glfw.KeyE)
  45. KeyF = Key(glfw.KeyF)
  46. KeyG = Key(glfw.KeyG)
  47. KeyH = Key(glfw.KeyH)
  48. KeyI = Key(glfw.KeyI)
  49. KeyJ = Key(glfw.KeyJ)
  50. KeyK = Key(glfw.KeyK)
  51. KeyL = Key(glfw.KeyL)
  52. KeyM = Key(glfw.KeyM)
  53. KeyN = Key(glfw.KeyN)
  54. KeyO = Key(glfw.KeyO)
  55. KeyP = Key(glfw.KeyP)
  56. KeyQ = Key(glfw.KeyQ)
  57. KeyR = Key(glfw.KeyR)
  58. KeyS = Key(glfw.KeyS)
  59. KeyT = Key(glfw.KeyT)
  60. KeyU = Key(glfw.KeyU)
  61. KeyV = Key(glfw.KeyV)
  62. KeyW = Key(glfw.KeyW)
  63. KeyX = Key(glfw.KeyX)
  64. KeyY = Key(glfw.KeyY)
  65. KeyZ = Key(glfw.KeyZ)
  66. KeyLeftBracket = Key(glfw.KeyLeftBracket)
  67. KeyBackslash = Key(glfw.KeyBackslash)
  68. KeyRightBracket = Key(glfw.KeyRightBracket)
  69. KeyGraveAccent = Key(glfw.KeyGraveAccent)
  70. KeyWorld1 = Key(glfw.KeyWorld1)
  71. KeyWorld2 = Key(glfw.KeyWorld2)
  72. KeyEscape = Key(glfw.KeyEscape)
  73. KeyEnter = Key(glfw.KeyEnter)
  74. KeyTab = Key(glfw.KeyTab)
  75. KeyBackspace = Key(glfw.KeyBackspace)
  76. KeyInsert = Key(glfw.KeyInsert)
  77. KeyDelete = Key(glfw.KeyDelete)
  78. KeyRight = Key(glfw.KeyRight)
  79. KeyLeft = Key(glfw.KeyLeft)
  80. KeyDown = Key(glfw.KeyDown)
  81. KeyUp = Key(glfw.KeyUp)
  82. KeyPageUp = Key(glfw.KeyPageUp)
  83. KeyPageDown = Key(glfw.KeyPageDown)
  84. KeyHome = Key(glfw.KeyHome)
  85. KeyEnd = Key(glfw.KeyEnd)
  86. KeyCapsLock = Key(glfw.KeyCapsLock)
  87. KeyScrollLock = Key(glfw.KeyScrollLock)
  88. KeyNumLock = Key(glfw.KeyNumLock)
  89. KeyPrintScreen = Key(glfw.KeyPrintScreen)
  90. KeyPause = Key(glfw.KeyPause)
  91. KeyF1 = Key(glfw.KeyF1)
  92. KeyF2 = Key(glfw.KeyF2)
  93. KeyF3 = Key(glfw.KeyF3)
  94. KeyF4 = Key(glfw.KeyF4)
  95. KeyF5 = Key(glfw.KeyF5)
  96. KeyF6 = Key(glfw.KeyF6)
  97. KeyF7 = Key(glfw.KeyF7)
  98. KeyF8 = Key(glfw.KeyF8)
  99. KeyF9 = Key(glfw.KeyF9)
  100. KeyF10 = Key(glfw.KeyF10)
  101. KeyF11 = Key(glfw.KeyF11)
  102. KeyF12 = Key(glfw.KeyF12)
  103. KeyF13 = Key(glfw.KeyF13)
  104. KeyF14 = Key(glfw.KeyF14)
  105. KeyF15 = Key(glfw.KeyF15)
  106. KeyF16 = Key(glfw.KeyF16)
  107. KeyF17 = Key(glfw.KeyF17)
  108. KeyF18 = Key(glfw.KeyF18)
  109. KeyF19 = Key(glfw.KeyF19)
  110. KeyF20 = Key(glfw.KeyF20)
  111. KeyF21 = Key(glfw.KeyF21)
  112. KeyF22 = Key(glfw.KeyF22)
  113. KeyF23 = Key(glfw.KeyF23)
  114. KeyF24 = Key(glfw.KeyF24)
  115. KeyF25 = Key(glfw.KeyF25)
  116. KeyKP0 = Key(glfw.KeyKP0)
  117. KeyKP1 = Key(glfw.KeyKP1)
  118. KeyKP2 = Key(glfw.KeyKP2)
  119. KeyKP3 = Key(glfw.KeyKP3)
  120. KeyKP4 = Key(glfw.KeyKP4)
  121. KeyKP5 = Key(glfw.KeyKP5)
  122. KeyKP6 = Key(glfw.KeyKP6)
  123. KeyKP7 = Key(glfw.KeyKP7)
  124. KeyKP8 = Key(glfw.KeyKP8)
  125. KeyKP9 = Key(glfw.KeyKP9)
  126. KeyKPDecimal = Key(glfw.KeyKPDecimal)
  127. KeyKPDivide = Key(glfw.KeyKPDivide)
  128. KeyKPMultiply = Key(glfw.KeyKPMultiply)
  129. KeyKPSubtract = Key(glfw.KeyKPSubtract)
  130. KeyKPAdd = Key(glfw.KeyKPAdd)
  131. KeyKPEnter = Key(glfw.KeyKPEnter)
  132. KeyKPEqual = Key(glfw.KeyKPEqual)
  133. KeyLeftShift = Key(glfw.KeyLeftShift)
  134. KeyLeftControl = Key(glfw.KeyLeftControl)
  135. KeyLeftAlt = Key(glfw.KeyLeftAlt)
  136. KeyLeftSuper = Key(glfw.KeyLeftSuper)
  137. KeyRightShift = Key(glfw.KeyRightShift)
  138. KeyRightControl = Key(glfw.KeyRightControl)
  139. KeyRightAlt = Key(glfw.KeyRightAlt)
  140. KeyRightSuper = Key(glfw.KeyRightSuper)
  141. KeyMenu = Key(glfw.KeyMenu)
  142. KeyLast = Key(glfw.KeyLast)
  143. )
  144. // Modifier keys
  145. const (
  146. ModShift = ModifierKey(glfw.ModShift)
  147. ModControl = ModifierKey(glfw.ModControl)
  148. ModAlt = ModifierKey(glfw.ModAlt)
  149. ModSuper = ModifierKey(glfw.ModSuper)
  150. )
  151. // Mouse buttons
  152. const (
  153. MouseButton1 = MouseButton(glfw.MouseButton1)
  154. MouseButton2 = MouseButton(glfw.MouseButton2)
  155. MouseButton3 = MouseButton(glfw.MouseButton3)
  156. MouseButton4 = MouseButton(glfw.MouseButton4)
  157. MouseButton5 = MouseButton(glfw.MouseButton5)
  158. MouseButton6 = MouseButton(glfw.MouseButton6)
  159. MouseButton7 = MouseButton(glfw.MouseButton7)
  160. MouseButton8 = MouseButton(glfw.MouseButton8)
  161. MouseButtonLast = MouseButton(glfw.MouseButtonLast)
  162. MouseButtonLeft = MouseButton(glfw.MouseButtonLeft)
  163. MouseButtonRight = MouseButton(glfw.MouseButtonRight)
  164. MouseButtonMiddle = MouseButton(glfw.MouseButtonMiddle)
  165. )
  166. // Input modes
  167. const (
  168. CursorInputMode = InputMode(glfw.CursorMode) // See Cursor mode values
  169. StickyKeysInputMode = InputMode(glfw.StickyKeysMode) // Value can be either 1 or 0
  170. StickyMouseButtonsInputMode = InputMode(glfw.StickyMouseButtonsMode) // Value can be either 1 or 0
  171. )
  172. // Cursor mode values
  173. const (
  174. CursorNormal = CursorMode(glfw.CursorNormal)
  175. CursorHidden = CursorMode(glfw.CursorHidden)
  176. CursorDisabled = CursorMode(glfw.CursorDisabled)
  177. )
  178. // GlfwWindow describes one glfw window
  179. type GlfwWindow struct {
  180. *glfw.Window // Embedded GLFW window
  181. core.Dispatcher // Embedded event dispatcher
  182. gls *gls.GLS // Associated OpenGL State
  183. fullscreen bool
  184. lastX int
  185. lastY int
  186. lastWidth int
  187. lastHeight int
  188. scaleX float64
  189. scaleY float64
  190. // Events
  191. keyEv KeyEvent
  192. charEv CharEvent
  193. mouseEv MouseEvent
  194. posEv PosEvent
  195. sizeEv SizeEvent
  196. cursorEv CursorEvent
  197. scrollEv ScrollEvent
  198. focusEv FocusEvent
  199. mods ModifierKey // Current modifier keys
  200. // Cursors
  201. cursors map[Cursor]*glfw.Cursor
  202. lastCursorKey Cursor
  203. }
  204. // Init initializes the GlfwWindow singleton with the specified width, height, and title.
  205. func Init(width, height int, title string) error {
  206. // Panic if already created
  207. if win != nil {
  208. panic(fmt.Errorf("can only call window.Init() once"))
  209. }
  210. // OpenGL functions must be executed in the same thread where
  211. // the context was created (by wmgr.CreateWindow())
  212. runtime.LockOSThread()
  213. // Create wrapper window with dispatcher
  214. w := new(GlfwWindow)
  215. w.Dispatcher.Initialize()
  216. var err error
  217. // Initialize GLFW
  218. err = glfw.Init()
  219. if err != nil {
  220. return err
  221. }
  222. // Set window hints
  223. glfw.WindowHint(glfw.ContextVersionMajor, 3)
  224. glfw.WindowHint(glfw.ContextVersionMinor, 3)
  225. glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
  226. glfw.WindowHint(glfw.Samples, 8)
  227. // Set OpenGL forward compatible context only for OSX because it is required for OSX.
  228. // When this is set, glLineWidth(width) only accepts width=1.0 and generates an error
  229. // for any other values although the spec says it should ignore unsupported widths
  230. // and generate an error only when width <= 0.
  231. if runtime.GOOS == "darwin" {
  232. glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True)
  233. }
  234. // Create window and set it as the current context.
  235. // The window is created always as not full screen because if it is
  236. // created as full screen it not possible to revert it to windowed mode.
  237. // At the end of this function, the window will be set to full screen if requested.
  238. w.Window, err = glfw.CreateWindow(width, height, title, nil, nil)
  239. if err != nil {
  240. return err
  241. }
  242. w.MakeContextCurrent()
  243. // Create OpenGL state
  244. w.gls, err = gls.New()
  245. if err != nil {
  246. return err
  247. }
  248. // Compute and store scale
  249. fbw, fbh := w.GetFramebufferSize()
  250. w.scaleX = float64(fbw) / float64(width)
  251. w.scaleY = float64(fbh) / float64(height)
  252. // Create map for cursors
  253. w.cursors = make(map[Cursor]*glfw.Cursor)
  254. w.lastCursorKey = CursorLast
  255. // Preallocate GLFW standard cursors
  256. w.cursors[ArrowCursor] = glfw.CreateStandardCursor(glfw.ArrowCursor)
  257. w.cursors[IBeamCursor] = glfw.CreateStandardCursor(glfw.IBeamCursor)
  258. w.cursors[CrosshairCursor] = glfw.CreateStandardCursor(glfw.CrosshairCursor)
  259. w.cursors[HandCursor] = glfw.CreateStandardCursor(glfw.HandCursor)
  260. w.cursors[HResizeCursor] = glfw.CreateStandardCursor(glfw.HResizeCursor)
  261. w.cursors[VResizeCursor] = glfw.CreateStandardCursor(glfw.VResizeCursor)
  262. // Preallocate extra G3N standard cursors (diagonal resize cursors)
  263. cursorDiag1Png := assets.MustAsset("cursors/diag1.png") // [/]
  264. cursorDiag2Png := assets.MustAsset("cursors/diag2.png") // [\]
  265. diag1Img, _, err := image.Decode(bytes.NewReader(cursorDiag1Png))
  266. diag2Img, _, err := image.Decode(bytes.NewReader(cursorDiag2Png))
  267. if err != nil {
  268. return err
  269. }
  270. w.cursors[DiagResize1Cursor] = glfw.CreateCursor(diag1Img, 8, 8) // [/]
  271. w.cursors[DiagResize2Cursor] = glfw.CreateCursor(diag2Img, 8, 8) // [\]
  272. // Set up key callback to dispatch event
  273. w.SetKeyCallback(func(x *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
  274. w.keyEv.Key = Key(key)
  275. w.keyEv.Mods = ModifierKey(mods)
  276. w.mods = w.keyEv.Mods
  277. if action == glfw.Press {
  278. w.Dispatch(OnKeyDown, &w.keyEv)
  279. } else if action == glfw.Release {
  280. w.Dispatch(OnKeyUp, &w.keyEv)
  281. } else if action == glfw.Repeat {
  282. w.Dispatch(OnKeyRepeat, &w.keyEv)
  283. }
  284. })
  285. // Set up char callback to dispatch event
  286. w.SetCharModsCallback(func(x *glfw.Window, char rune, mods glfw.ModifierKey) {
  287. w.charEv.Char = char
  288. w.charEv.Mods = ModifierKey(mods)
  289. w.Dispatch(OnChar, &w.charEv)
  290. })
  291. // Set up mouse button callback to dispatch event
  292. w.SetMouseButtonCallback(func(x *glfw.Window, button glfw.MouseButton, action glfw.Action, mods glfw.ModifierKey) {
  293. xpos, ypos := x.GetCursorPos()
  294. w.mouseEv.Button = MouseButton(button)
  295. w.mouseEv.Mods = ModifierKey(mods)
  296. w.mouseEv.Xpos = float32(xpos) //* float32(w.scaleX) TODO
  297. w.mouseEv.Ypos = float32(ypos) //* float32(w.scaleY)
  298. if action == glfw.Press {
  299. w.Dispatch(OnMouseDown, &w.mouseEv)
  300. } else if action == glfw.Release {
  301. w.Dispatch(OnMouseUp, &w.mouseEv)
  302. }
  303. })
  304. // Set up window size callback to dispatch event
  305. w.SetSizeCallback(func(x *glfw.Window, width int, height int) {
  306. fbw, fbh := x.GetFramebufferSize()
  307. w.sizeEv.Width = width
  308. w.sizeEv.Height = height
  309. w.scaleX = float64(fbw) / float64(width)
  310. w.scaleY = float64(fbh) / float64(height)
  311. w.Dispatch(OnWindowSize, &w.sizeEv)
  312. })
  313. // Set up window position callback to dispatch event
  314. w.SetPosCallback(func(x *glfw.Window, xpos int, ypos int) {
  315. w.posEv.Xpos = xpos
  316. w.posEv.Ypos = ypos
  317. w.Dispatch(OnWindowPos, &w.posEv)
  318. })
  319. // Set up window focus callback to dispatch event
  320. w.SetFocusCallback(func(x *glfw.Window, focused bool) {
  321. w.focusEv.Focused = focused
  322. w.Dispatch(OnWindowFocus, &w.focusEv)
  323. })
  324. // Set up window cursor position callback to dispatch event
  325. w.SetCursorPosCallback(func(x *glfw.Window, xpos float64, ypos float64) {
  326. w.cursorEv.Xpos = float32(xpos)
  327. w.cursorEv.Ypos = float32(ypos)
  328. w.cursorEv.Mods = w.mods
  329. w.Dispatch(OnCursor, &w.cursorEv)
  330. })
  331. // Set up mouse wheel scroll callback to dispatch event
  332. w.SetScrollCallback(func(x *glfw.Window, xoff float64, yoff float64) {
  333. w.scrollEv.Xoffset = float32(xoff)
  334. w.scrollEv.Yoffset = float32(yoff)
  335. w.scrollEv.Mods = w.mods
  336. w.Dispatch(OnScroll, &w.scrollEv)
  337. })
  338. win = w // Set singleton
  339. return nil
  340. }
  341. // Gls returns the associated OpenGL state.
  342. func (w *GlfwWindow) Gls() *gls.GLS {
  343. return w.gls
  344. }
  345. // FullScreen returns whether this windows is currently fullscreen.
  346. func (w *GlfwWindow) FullScreen() bool {
  347. return w.fullscreen
  348. }
  349. // SetFullScreen sets this window as fullscreen on the primary monitor
  350. // TODO allow for fullscreen with resolutions different than the monitor's
  351. func (w *GlfwWindow) SetFullScreen(full bool) {
  352. // If already in the desired state, nothing to do
  353. if w.fullscreen == full {
  354. return
  355. }
  356. // Set window fullscreen on the primary monitor
  357. if full {
  358. // Save current position and size of the window
  359. w.lastX, w.lastY = w.GetPos()
  360. w.lastWidth, w.lastHeight = w.GetSize()
  361. // Get size of primary monitor
  362. mon := glfw.GetPrimaryMonitor()
  363. vmode := mon.GetVideoMode()
  364. width := vmode.Width
  365. height := vmode.Height
  366. // Set as fullscreen on the primary monitor
  367. w.SetMonitor(mon, 0, 0, width, height, vmode.RefreshRate)
  368. w.fullscreen = true
  369. } else {
  370. // Restore window to previous position and size
  371. w.SetMonitor(nil, w.lastX, w.lastY, w.lastWidth, w.lastHeight, glfw.DontCare)
  372. w.fullscreen = false
  373. }
  374. }
  375. // Destroy destroys this window and its context
  376. func (w *GlfwWindow) Destroy() {
  377. w.Window.Destroy()
  378. glfw.Terminate()
  379. runtime.UnlockOSThread() // Important when using the execution tracer
  380. }
  381. // Scale returns this window's DPI scale factor (FramebufferSize / Size)
  382. func (w *GlfwWindow) GetScale() (x float64, y float64) {
  383. return w.scaleX, w.scaleY
  384. }
  385. // ScreenResolution returns the screen resolution
  386. func (w *GlfwWindow) ScreenResolution(p interface{}) (width, height int) {
  387. mon := glfw.GetPrimaryMonitor()
  388. vmode := mon.GetVideoMode()
  389. return vmode.Width, vmode.Height
  390. }
  391. // PollEvents process events in the event queue
  392. func (w *GlfwWindow) PollEvents() {
  393. glfw.PollEvents()
  394. }
  395. // SetSwapInterval sets the number of screen updates to wait from the time SwapBuffer()
  396. // is called before swapping the buffers and returning.
  397. func (w *GlfwWindow) SetSwapInterval(interval int) {
  398. glfw.SwapInterval(interval)
  399. }
  400. // SetCursor sets the window's cursor.
  401. func (w *GlfwWindow) SetCursor(cursor Cursor) {
  402. cur, ok := w.cursors[cursor]
  403. if !ok {
  404. panic("Invalid cursor")
  405. }
  406. w.Window.SetCursor(cur)
  407. }
  408. // CreateCursor creates a new custom cursor and returns an int handle.
  409. func (w *GlfwWindow) CreateCursor(imgFile string, xhot, yhot int) (Cursor, error) {
  410. // Open image file
  411. file, err := os.Open(imgFile)
  412. if err != nil {
  413. return 0, err
  414. }
  415. defer file.Close()
  416. // Decode image
  417. img, _, err := image.Decode(file)
  418. if err != nil {
  419. return 0, err
  420. }
  421. // Create and store cursor
  422. w.lastCursorKey += 1
  423. w.cursors[Cursor(w.lastCursorKey)] = glfw.CreateCursor(img, xhot, yhot)
  424. return w.lastCursorKey, nil
  425. }
  426. // DisposeCursor deletes the existing custom cursor with the provided int handle.
  427. func (w *GlfwWindow) DisposeCursor(cursor Cursor) {
  428. if cursor <= CursorLast {
  429. panic("Can't dispose standard cursor")
  430. }
  431. w.cursors[cursor].Destroy()
  432. delete(w.cursors, cursor)
  433. }
  434. // DisposeAllCursors deletes all existing custom cursors.
  435. func (w *GlfwWindow) DisposeAllCustomCursors() {
  436. // Destroy and delete all custom cursors
  437. for key := range w.cursors {
  438. if key > CursorLast {
  439. w.cursors[key].Destroy()
  440. delete(w.cursors, key)
  441. }
  442. }
  443. // Set the next cursor key as the last standard cursor key + 1
  444. w.lastCursorKey = CursorLast
  445. }
  446. // Center centers the window on the screen.
  447. //func (w *GlfwWindow) Center() {
  448. //
  449. // // TODO
  450. //}