glfw.go 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  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. package window
  5. import (
  6. "runtime"
  7. "github.com/g3n/engine/core"
  8. "github.com/go-gl/glfw/v3.2/glfw"
  9. "os"
  10. "image"
  11. _ "image/png"
  12. "bytes"
  13. "github.com/g3n/engine/gui/assets"
  14. )
  15. // glfwManager contains data shared by all windows
  16. type glfwManager struct {
  17. arrowCursor *glfw.Cursor // Preallocated standard arrow cursor
  18. ibeamCursor *glfw.Cursor // Preallocated standard ibeam cursor
  19. crosshairCursor *glfw.Cursor // Preallocated standard cross hair cursor
  20. handCursor *glfw.Cursor // Preallocated standard hand cursor
  21. hresizeCursor *glfw.Cursor // Preallocated standard horizontal resize cursor
  22. vresizeCursor *glfw.Cursor // Preallocated standard vertical resize cursor
  23. // Non GLFW standard cursors (but g3n standard)
  24. diag1Cursor *glfw.Cursor // Preallocated diagonal resize cursor (/)
  25. diag2Cursor *glfw.Cursor // Preallocated diagonal resize cursor (\)
  26. // User-created custom cursors
  27. customCursors map[int]*glfw.Cursor
  28. lastCursorKey int
  29. }
  30. // glfwWindow describes one glfw window
  31. type glfwWindow struct {
  32. core.Dispatcher // Embedded event dispatcher
  33. win *glfw.Window // Pointer to native glfw window
  34. mgr *glfwManager // Pointer to window manager
  35. fullScreen bool
  36. lastX int
  37. lastY int
  38. lastWidth int
  39. lastHeight int
  40. scaleX float64
  41. scaleY float64
  42. // Events
  43. keyEv KeyEvent
  44. charEv CharEvent
  45. mouseEv MouseEvent
  46. posEv PosEvent
  47. sizeEv SizeEvent
  48. cursorEv CursorEvent
  49. scrollEv ScrollEvent
  50. }
  51. // glfw manager singleton
  52. var manager *glfwManager
  53. // Glfw returns the glfw window manager
  54. func Glfw() (IWindowManager, error) {
  55. if manager != nil {
  56. return manager, nil
  57. }
  58. // Initialize glfw
  59. err := glfw.Init()
  60. if err != nil {
  61. return nil, err
  62. }
  63. // Sets window hints
  64. glfw.WindowHint(glfw.ContextVersionMajor, 3)
  65. glfw.WindowHint(glfw.ContextVersionMinor, 3)
  66. glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
  67. glfw.WindowHint(glfw.Samples, 8)
  68. // Sets OpenGL forward compatible context only for OSX because it is required for OSX.
  69. // When this is set, glLineWidth(width) only accepts width=1.0 and generates an error
  70. // for any other values although the spec says it should ignore unsupported widths
  71. // and generate an error only when width <= 0.
  72. if runtime.GOOS == "darwin" {
  73. glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True)
  74. }
  75. manager = new(glfwManager)
  76. // Preallocate GLFW standard cursors
  77. manager.arrowCursor = glfw.CreateStandardCursor(glfw.ArrowCursor)
  78. manager.ibeamCursor = glfw.CreateStandardCursor(glfw.IBeamCursor)
  79. manager.crosshairCursor = glfw.CreateStandardCursor(glfw.CrosshairCursor)
  80. manager.handCursor = glfw.CreateStandardCursor(glfw.HandCursor)
  81. manager.hresizeCursor = glfw.CreateStandardCursor(glfw.HResizeCursor)
  82. manager.vresizeCursor = glfw.CreateStandardCursor(glfw.VResizeCursor)
  83. // Preallocate g3n cursors (diagonal cursors)
  84. cursorDiag1Png := assets.MustAsset("cursors/diag1.png")
  85. cursorDiag2Png := assets.MustAsset("cursors/diag2.png")
  86. diag1Img, _, err := image.Decode(bytes.NewReader(cursorDiag1Png))
  87. diag2Img, _, err := image.Decode(bytes.NewReader(cursorDiag2Png))
  88. if err != nil {
  89. return nil, err
  90. }
  91. manager.diag1Cursor = glfw.CreateCursor(diag1Img, 8, 8)
  92. manager.diag2Cursor = glfw.CreateCursor(diag2Img, 8, 8)
  93. // Create map for custom cursors
  94. manager.customCursors = make(map[int]*glfw.Cursor)
  95. return manager, nil
  96. }
  97. // ScreenResolution returns the screen resolution
  98. func (m *glfwManager) ScreenResolution(p interface{}) (width, height int) {
  99. mon := glfw.GetPrimaryMonitor()
  100. vmode := mon.GetVideoMode()
  101. return vmode.Width, vmode.Height
  102. }
  103. // PollEvents process events in the event queue
  104. func (m *glfwManager) PollEvents() {
  105. glfw.PollEvents()
  106. }
  107. // SetSwapInterval sets the number of screen updates to wait from the time SwapBuffer()
  108. // is called before swapping the buffers and returning.
  109. func (m *glfwManager) SetSwapInterval(interval int) {
  110. glfw.SwapInterval(interval)
  111. }
  112. // Terminate destroys any remainding window, cursors and other related objects.
  113. func (m *glfwManager) Terminate() {
  114. glfw.Terminate()
  115. manager = nil
  116. }
  117. // CreateCursor creates a new custom cursor and returns an int handle.
  118. func (m *glfwManager) CreateCursor(imgFile string, xhot, yhot int) (int, error) {
  119. // Open image file
  120. file, err := os.Open(imgFile)
  121. if err != nil {
  122. return 0, err
  123. }
  124. defer file.Close()
  125. // Decodes image
  126. img, _, err := image.Decode(file)
  127. if err != nil {
  128. return 0, err
  129. }
  130. cursor := glfw.CreateCursor(img, xhot, yhot)
  131. if err != nil {
  132. return 0, err
  133. }
  134. m.lastCursorKey += 1
  135. m.customCursors[m.lastCursorKey] = cursor
  136. return m.lastCursorKey, nil
  137. }
  138. // DisposeCursor deletes the existing custom cursor with the provided int handle.
  139. func (m *glfwManager) DisposeCursor(key int) {
  140. delete(m.customCursors, key)
  141. }
  142. // DisposeAllCursors deletes all existing custom cursors.
  143. func (m *glfwManager) DisposeAllCursors() {
  144. m.customCursors = make(map[int]*glfw.Cursor)
  145. m.lastCursorKey = 0
  146. }
  147. // CreateWindow creates and returns a new window with the specified width and height in screen coordinates
  148. func (m *glfwManager) CreateWindow(width, height int, title string, fullscreen bool) (IWindow, error) {
  149. // Creates window and sets it as the current context.
  150. // The window is created always as not full screen because if it is
  151. // created as full screen it not possible to revert it to windowed mode.
  152. // At the end of this function, the window will be set to full screen if requested.
  153. win, err := glfw.CreateWindow(width, height, title, nil, nil)
  154. if err != nil {
  155. return nil, err
  156. }
  157. win.MakeContextCurrent()
  158. // Create wrapper window with dispacher
  159. w := new(glfwWindow)
  160. w.win = win
  161. w.mgr = m
  162. w.Dispatcher.Initialize()
  163. fbw, fbh := w.FramebufferSize()
  164. w.scaleX = float64(fbw) / float64(width)
  165. w.scaleY = float64(fbh) / float64(height)
  166. // Set key callback to dispatch event
  167. win.SetKeyCallback(func(x *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
  168. w.keyEv.W = w
  169. w.keyEv.Keycode = Key(key)
  170. w.keyEv.Scancode = scancode
  171. w.keyEv.Action = Action(action)
  172. w.keyEv.Mods = ModifierKey(mods)
  173. if action == glfw.Press {
  174. w.Dispatch(OnKeyDown, &w.keyEv)
  175. return
  176. }
  177. if action == glfw.Release {
  178. w.Dispatch(OnKeyUp, &w.keyEv)
  179. return
  180. }
  181. if action == glfw.Repeat {
  182. w.Dispatch(OnKeyRepeat, &w.keyEv)
  183. return
  184. }
  185. })
  186. // Set char callback
  187. win.SetCharModsCallback(func(x *glfw.Window, char rune, mods glfw.ModifierKey) {
  188. w.charEv.W = w
  189. w.charEv.Char = char
  190. w.charEv.Mods = ModifierKey(mods)
  191. w.Dispatch(OnChar, &w.charEv)
  192. })
  193. // Set mouse button callback to dispatch event
  194. win.SetMouseButtonCallback(func(x *glfw.Window, button glfw.MouseButton, action glfw.Action, mods glfw.ModifierKey) {
  195. xpos, ypos := x.GetCursorPos()
  196. w.mouseEv.W = w
  197. w.mouseEv.Button = MouseButton(button)
  198. w.mouseEv.Action = Action(action)
  199. w.mouseEv.Mods = ModifierKey(mods)
  200. w.mouseEv.Xpos = float32(xpos * w.scaleX)
  201. w.mouseEv.Ypos = float32(ypos * w.scaleY)
  202. if action == glfw.Press {
  203. w.Dispatch(OnMouseDown, &w.mouseEv)
  204. return
  205. }
  206. if action == glfw.Release {
  207. w.Dispatch(OnMouseUp, &w.mouseEv)
  208. return
  209. }
  210. })
  211. // Set window size callback to dispatch event
  212. win.SetSizeCallback(func(x *glfw.Window, width int, height int) {
  213. fbw, fbh := x.GetFramebufferSize()
  214. w.sizeEv.W = w
  215. w.sizeEv.Width = width
  216. w.sizeEv.Height = height
  217. w.scaleX = float64(fbw) / float64(width)
  218. w.scaleY = float64(fbh) / float64(height)
  219. w.Dispatch(OnWindowSize, &w.sizeEv)
  220. })
  221. // Set window position event callback to dispatch event
  222. win.SetPosCallback(func(x *glfw.Window, xpos int, ypos int) {
  223. w.posEv.W = w
  224. w.posEv.Xpos = xpos
  225. w.posEv.Ypos = ypos
  226. w.Dispatch(OnWindowPos, &w.posEv)
  227. })
  228. // Set window cursor position event callback to dispatch event
  229. win.SetCursorPosCallback(func(x *glfw.Window, xpos float64, ypos float64) {
  230. w.cursorEv.W = w
  231. w.cursorEv.Xpos = float32(xpos * w.scaleX)
  232. w.cursorEv.Ypos = float32(ypos * w.scaleY)
  233. w.Dispatch(OnCursor, &w.cursorEv)
  234. })
  235. // Set mouse wheel scroll event callback to dispatch event
  236. win.SetScrollCallback(func(x *glfw.Window, xoff float64, yoff float64) {
  237. w.scrollEv.W = w
  238. w.scrollEv.Xoffset = float32(xoff)
  239. w.scrollEv.Yoffset = float32(yoff)
  240. w.Dispatch(OnScroll, &w.scrollEv)
  241. })
  242. // Sets full screen if requested
  243. if fullscreen {
  244. w.SetFullScreen(true)
  245. }
  246. return w, nil
  247. }
  248. // Manager returns the window manager and satisfies the IWindow interface
  249. func (w *glfwWindow) Manager() IWindowManager {
  250. return w.mgr
  251. }
  252. // MakeContextCurrent makes the OpenGL context of this window current on the calling thread
  253. func (w *glfwWindow) MakeContextCurrent() {
  254. w.win.MakeContextCurrent()
  255. }
  256. // FullScreen returns this window full screen state for the primary monitor
  257. func (w *glfwWindow) FullScreen() bool {
  258. return w.fullScreen
  259. }
  260. // SetFullScreen sets this window full screen state for the primary monitor
  261. func (w *glfwWindow) SetFullScreen(full bool) {
  262. // If already in the desired state, nothing to do
  263. if w.fullScreen == full {
  264. return
  265. }
  266. // Sets this window full screen for the primary monitor
  267. if full {
  268. // Get primary monitor
  269. mon := glfw.GetPrimaryMonitor()
  270. vmode := mon.GetVideoMode()
  271. width := vmode.Width
  272. height := vmode.Height
  273. // Saves current position and size of the window
  274. w.lastX, w.lastY = w.win.GetPos()
  275. w.lastWidth, w.lastHeight = w.win.GetSize()
  276. // Sets monitor for full screen
  277. w.win.SetMonitor(mon, 0, 0, width, height, vmode.RefreshRate)
  278. w.fullScreen = true
  279. } else {
  280. // Restore window to previous position and size
  281. w.win.SetMonitor(nil, w.lastX, w.lastY, w.lastWidth, w.lastHeight, glfw.DontCare)
  282. w.fullScreen = false
  283. }
  284. }
  285. // Destroy destroys this window and its context
  286. func (w *glfwWindow) Destroy() {
  287. w.win.Destroy()
  288. w.win = nil
  289. }
  290. // SwapBuffers swaps the front and back buffers of this window.
  291. // If the swap interval is greater than zero,
  292. // the GPU driver waits the specified number of screen updates before swapping the buffers.
  293. func (w *glfwWindow) SwapBuffers() {
  294. w.win.SwapBuffers()
  295. }
  296. // FramebufferSize returns framebuffer size of this window
  297. func (w *glfwWindow) FramebufferSize() (width int, height int) {
  298. return w.win.GetFramebufferSize()
  299. }
  300. // Scale returns this window's DPI scale factor (FramebufferSize / Size)
  301. func (w *glfwWindow) Scale() (x float64, y float64) {
  302. return w.scaleX, w.scaleY
  303. }
  304. // Size returns this window's size in screen coordinates
  305. func (w *glfwWindow) Size() (width int, height int) {
  306. return w.win.GetSize()
  307. }
  308. // SetSize sets the size, in screen coordinates, of the client area of this window
  309. func (w *glfwWindow) SetSize(width int, height int) {
  310. w.win.SetSize(width, height)
  311. }
  312. // Pos returns the position, in screen coordinates, of the upper-left corner of the client area of this window
  313. func (w *glfwWindow) Pos() (xpos, ypos int) {
  314. return w.win.GetPos()
  315. }
  316. // SetPos sets the position, in screen coordinates, of the upper-left corner of the client area of this window.
  317. // If the window is a full screen window, this function does nothing.
  318. func (w *glfwWindow) SetPos(xpos, ypos int) {
  319. w.win.SetPos(xpos, ypos)
  320. }
  321. // SetTitle sets this window title, encoded as UTF-8
  322. func (w *glfwWindow) SetTitle(title string) {
  323. w.win.SetTitle(title)
  324. }
  325. // ShouldClose returns the current state of this window should close flag
  326. func (w *glfwWindow) ShouldClose() bool {
  327. return w.win.ShouldClose()
  328. }
  329. // SetShouldClose sets the state of this windows should close flag
  330. func (w *glfwWindow) SetShouldClose(v bool) {
  331. w.win.SetShouldClose(v)
  332. }
  333. // SetStandardCursor sets the window's cursor to a standard one
  334. func (w *glfwWindow) SetStandardCursor(cursor StandardCursor) {
  335. switch cursor {
  336. case ArrowCursor:
  337. w.win.SetCursor(w.mgr.arrowCursor)
  338. case IBeamCursor:
  339. w.win.SetCursor(w.mgr.ibeamCursor)
  340. case CrosshairCursor:
  341. w.win.SetCursor(w.mgr.crosshairCursor)
  342. case HandCursor:
  343. w.win.SetCursor(w.mgr.handCursor)
  344. case HResizeCursor:
  345. w.win.SetCursor(w.mgr.hresizeCursor)
  346. case VResizeCursor:
  347. w.win.SetCursor(w.mgr.vresizeCursor)
  348. // Non-GLFW cursors (but standard cursors for g3n)
  349. case DiagResize1Cursor:
  350. w.win.SetCursor(w.mgr.diag1Cursor)
  351. case DiagResize2Cursor:
  352. w.win.SetCursor(w.mgr.diag2Cursor)
  353. default:
  354. panic("Invalid cursor")
  355. }
  356. }
  357. // SetStandardCursor sets this window's cursor to a custom, user-created one
  358. func (w *glfwWindow) SetCustomCursor(key int) {
  359. w.win.SetCursor(w.mgr.customCursors[key])
  360. }
  361. // SetInputMode changes specified input to specified state
  362. // Reference: http://www.glfw.org/docs/latest/group__input.html#gaa92336e173da9c8834558b54ee80563b
  363. func (w *glfwWindow) SetInputMode(mode InputMode, state int) {
  364. w.win.SetInputMode(glfw.InputMode(mode), state)
  365. }
  366. // SetCursorPos sets cursor position in window coordinates
  367. // Reference: http://www.glfw.org/docs/latest/group__input.html#ga04b03af936d906ca123c8f4ee08b39e7
  368. func (w *glfwWindow) SetCursorPos(xpos, ypos float64) {
  369. w.win.SetCursorPos(xpos, ypos)
  370. }