glfw.go 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518
  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. lockEv LockEvent
  200. mods ModifierKey // Current modifier keys
  201. // Cursors
  202. cursors map[Cursor]*glfw.Cursor
  203. lastCursorKey Cursor
  204. }
  205. // Init initializes the GlfwWindow singleton with the specified width, height, and title.
  206. func Init(width, height int, title string) error {
  207. // Panic if already created
  208. if win != nil {
  209. panic(fmt.Errorf("can only call window.Init() once"))
  210. }
  211. // OpenGL functions must be executed in the same thread where
  212. // the context was created (by wmgr.CreateWindow())
  213. runtime.LockOSThread()
  214. // Create wrapper window with dispatcher
  215. w := new(GlfwWindow)
  216. w.Dispatcher.Initialize()
  217. var err error
  218. // Initialize GLFW
  219. err = glfw.Init()
  220. if err != nil {
  221. return err
  222. }
  223. // Set window hints
  224. glfw.WindowHint(glfw.ContextVersionMajor, 3)
  225. glfw.WindowHint(glfw.ContextVersionMinor, 3)
  226. glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
  227. glfw.WindowHint(glfw.Samples, 8)
  228. // Set OpenGL forward compatible context only for OSX because it is required for OSX.
  229. // When this is set, glLineWidth(width) only accepts width=1.0 and generates an error
  230. // for any other values although the spec says it should ignore unsupported widths
  231. // and generate an error only when width <= 0.
  232. if runtime.GOOS == "darwin" {
  233. glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True)
  234. }
  235. // Create window and set it as the current context.
  236. // The window is created always as not full screen because if it is
  237. // created as full screen it not possible to revert it to windowed mode.
  238. // At the end of this function, the window will be set to full screen if requested.
  239. w.Window, err = glfw.CreateWindow(width, height, title, nil, nil)
  240. if err != nil {
  241. return err
  242. }
  243. w.MakeContextCurrent()
  244. // Create OpenGL state
  245. w.gls, err = gls.New()
  246. if err != nil {
  247. return err
  248. }
  249. // Compute and store scale
  250. fbw, fbh := w.GetFramebufferSize()
  251. w.scaleX = float64(fbw) / float64(width)
  252. w.scaleY = float64(fbh) / float64(height)
  253. // Create map for cursors
  254. w.cursors = make(map[Cursor]*glfw.Cursor)
  255. w.lastCursorKey = CursorLast
  256. // Preallocate GLFW standard cursors
  257. w.cursors[ArrowCursor] = glfw.CreateStandardCursor(glfw.ArrowCursor)
  258. w.cursors[IBeamCursor] = glfw.CreateStandardCursor(glfw.IBeamCursor)
  259. w.cursors[CrosshairCursor] = glfw.CreateStandardCursor(glfw.CrosshairCursor)
  260. w.cursors[HandCursor] = glfw.CreateStandardCursor(glfw.HandCursor)
  261. w.cursors[HResizeCursor] = glfw.CreateStandardCursor(glfw.HResizeCursor)
  262. w.cursors[VResizeCursor] = glfw.CreateStandardCursor(glfw.VResizeCursor)
  263. // Preallocate extra G3N standard cursors (diagonal resize cursors)
  264. cursorDiag1Png := assets.MustAsset("cursors/diag1.png") // [/]
  265. cursorDiag2Png := assets.MustAsset("cursors/diag2.png") // [\]
  266. diag1Img, _, err := image.Decode(bytes.NewReader(cursorDiag1Png))
  267. diag2Img, _, err := image.Decode(bytes.NewReader(cursorDiag2Png))
  268. if err != nil {
  269. return err
  270. }
  271. w.cursors[DiagResize1Cursor] = glfw.CreateCursor(diag1Img, 8, 8) // [/]
  272. w.cursors[DiagResize2Cursor] = glfw.CreateCursor(diag2Img, 8, 8) // [\]
  273. // Set up key callback to dispatch event
  274. w.SetKeyCallback(func(x *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
  275. w.keyEv.Key = Key(key)
  276. w.keyEv.Mods = ModifierKey(mods)
  277. w.mods = w.keyEv.Mods
  278. if action == glfw.Press {
  279. w.Dispatch(OnKeyDown, &w.keyEv)
  280. } else if action == glfw.Release {
  281. w.Dispatch(OnKeyUp, &w.keyEv)
  282. } else if action == glfw.Repeat {
  283. w.Dispatch(OnKeyRepeat, &w.keyEv)
  284. }
  285. })
  286. // Set up char callback to dispatch event
  287. w.SetCharModsCallback(func(x *glfw.Window, char rune, mods glfw.ModifierKey) {
  288. w.charEv.Char = char
  289. w.charEv.Mods = ModifierKey(mods)
  290. w.Dispatch(OnChar, &w.charEv)
  291. })
  292. // Set up mouse button callback to dispatch event
  293. w.SetMouseButtonCallback(func(x *glfw.Window, button glfw.MouseButton, action glfw.Action, mods glfw.ModifierKey) {
  294. xpos, ypos := x.GetCursorPos()
  295. w.mouseEv.Button = MouseButton(button)
  296. w.mouseEv.Mods = ModifierKey(mods)
  297. w.mouseEv.Xpos = float32(xpos) //* float32(w.scaleX) TODO
  298. w.mouseEv.Ypos = float32(ypos) //* float32(w.scaleY)
  299. if action == glfw.Press {
  300. w.Dispatch(OnMouseDown, &w.mouseEv)
  301. } else if action == glfw.Release {
  302. w.Dispatch(OnMouseUp, &w.mouseEv)
  303. }
  304. })
  305. // Set up window size callback to dispatch event
  306. w.SetSizeCallback(func(x *glfw.Window, width int, height int) {
  307. fbw, fbh := x.GetFramebufferSize()
  308. w.sizeEv.Width = width
  309. w.sizeEv.Height = height
  310. w.scaleX = float64(fbw) / float64(width)
  311. w.scaleY = float64(fbh) / float64(height)
  312. w.Dispatch(OnWindowSize, &w.sizeEv)
  313. })
  314. // Set up window position callback to dispatch event
  315. w.SetPosCallback(func(x *glfw.Window, xpos int, ypos int) {
  316. w.posEv.Xpos = xpos
  317. w.posEv.Ypos = ypos
  318. w.Dispatch(OnWindowPos, &w.posEv)
  319. })
  320. // Set up window focus callback to dispatch event
  321. w.SetFocusCallback(func(x *glfw.Window, focused bool) {
  322. w.focusEv.Focused = focused
  323. w.Dispatch(OnWindowFocus, &w.focusEv)
  324. })
  325. // Set up window cursor position callback to dispatch event
  326. w.SetCursorPosCallback(func(x *glfw.Window, xpos float64, ypos float64) {
  327. w.cursorEv.Xpos = float32(xpos)
  328. w.cursorEv.Ypos = float32(ypos)
  329. w.cursorEv.Mods = w.mods
  330. w.Dispatch(OnCursor, &w.cursorEv)
  331. })
  332. // Set up mouse wheel scroll callback to dispatch event
  333. w.SetScrollCallback(func(x *glfw.Window, xoff float64, yoff float64) {
  334. w.scrollEv.Xoffset = float32(xoff)
  335. w.scrollEv.Yoffset = float32(yoff)
  336. w.scrollEv.Mods = w.mods
  337. w.Dispatch(OnScroll, &w.scrollEv)
  338. })
  339. win = w // Set singleton
  340. return nil
  341. }
  342. // Gls returns the associated OpenGL state.
  343. func (w *GlfwWindow) Gls() *gls.GLS {
  344. return w.gls
  345. }
  346. // FullScreen returns whether this windows is currently fullscreen.
  347. func (w *GlfwWindow) FullScreen() bool {
  348. return w.fullscreen
  349. }
  350. // SetFullScreen sets this window as fullscreen on the primary monitor
  351. // TODO allow for fullscreen with resolutions different than the monitor's
  352. func (w *GlfwWindow) SetFullScreen(full bool) {
  353. // If already in the desired state, nothing to do
  354. if w.fullscreen == full {
  355. return
  356. }
  357. // Set window fullscreen on the primary monitor
  358. if full {
  359. // Save current position and size of the window
  360. w.lastX, w.lastY = w.GetPos()
  361. w.lastWidth, w.lastHeight = w.GetSize()
  362. // Get size of primary monitor
  363. mon := glfw.GetPrimaryMonitor()
  364. vmode := mon.GetVideoMode()
  365. width := vmode.Width
  366. height := vmode.Height
  367. // Set as fullscreen on the primary monitor
  368. w.SetMonitor(mon, 0, 0, width, height, vmode.RefreshRate)
  369. w.fullscreen = true
  370. } else {
  371. // Restore window to previous position and size
  372. w.SetMonitor(nil, w.lastX, w.lastY, w.lastWidth, w.lastHeight, glfw.DontCare)
  373. w.fullscreen = false
  374. }
  375. }
  376. // Destroy destroys this window and its context
  377. func (w *GlfwWindow) Destroy() {
  378. w.Window.Destroy()
  379. glfw.Terminate()
  380. runtime.UnlockOSThread() // Important when using the execution tracer
  381. }
  382. // Scale returns this window's DPI scale factor (FramebufferSize / Size)
  383. func (w *GlfwWindow) GetScale() (x float64, y float64) {
  384. return w.scaleX, w.scaleY
  385. }
  386. // ScreenResolution returns the screen resolution
  387. func (w *GlfwWindow) ScreenResolution(p interface{}) (width, height int) {
  388. mon := glfw.GetPrimaryMonitor()
  389. vmode := mon.GetVideoMode()
  390. return vmode.Width, vmode.Height
  391. }
  392. // PollEvents process events in the event queue
  393. func (w *GlfwWindow) PollEvents() {
  394. glfw.PollEvents()
  395. }
  396. // SetSwapInterval sets the number of screen updates to wait from the time SwapBuffer()
  397. // is called before swapping the buffers and returning.
  398. func (w *GlfwWindow) SetSwapInterval(interval int) {
  399. glfw.SwapInterval(interval)
  400. }
  401. // SetCursor sets the window's cursor.
  402. func (w *GlfwWindow) SetCursor(cursor Cursor) {
  403. cur, ok := w.cursors[cursor]
  404. if !ok {
  405. panic("Invalid cursor")
  406. }
  407. w.Window.SetCursor(cur)
  408. }
  409. // SetCursorMode sets the window's cursor mode.
  410. func (w *GlfwWindow) SetCursorMode(mode CursorMode) {
  411. w.Window.SetInputMode(glfw.CursorMode, int(mode))
  412. w.lockEv.Locked = mode == CursorDisabled
  413. w.Dispatch(OnLockChange, &w.lockEv)
  414. }
  415. // CreateCursor creates a new custom cursor and returns an int handle.
  416. func (w *GlfwWindow) CreateCursor(imgFile string, xhot, yhot int) (Cursor, error) {
  417. // Open image file
  418. file, err := os.Open(imgFile)
  419. if err != nil {
  420. return 0, err
  421. }
  422. defer file.Close()
  423. // Decode image
  424. img, _, err := image.Decode(file)
  425. if err != nil {
  426. return 0, err
  427. }
  428. // Create and store cursor
  429. w.lastCursorKey += 1
  430. w.cursors[Cursor(w.lastCursorKey)] = glfw.CreateCursor(img, xhot, yhot)
  431. return w.lastCursorKey, nil
  432. }
  433. // DisposeCursor deletes the existing custom cursor with the provided int handle.
  434. func (w *GlfwWindow) DisposeCursor(cursor Cursor) {
  435. if cursor <= CursorLast {
  436. panic("Can't dispose standard cursor")
  437. }
  438. w.cursors[cursor].Destroy()
  439. delete(w.cursors, cursor)
  440. }
  441. // DisposeAllCursors deletes all existing custom cursors.
  442. func (w *GlfwWindow) DisposeAllCustomCursors() {
  443. // Destroy and delete all custom cursors
  444. for key := range w.cursors {
  445. if key > CursorLast {
  446. w.cursors[key].Destroy()
  447. delete(w.cursors, key)
  448. }
  449. }
  450. // Set the next cursor key as the last standard cursor key + 1
  451. w.lastCursorKey = CursorLast
  452. }
  453. // Center centers the window on the screen.
  454. //func (w *GlfwWindow) Center() {
  455. //
  456. // // TODO
  457. //}