glfw.go 11 KB

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