glfw.go 13 KB

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