glfw.go 11 KB

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