glfw.go 10 KB

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