glfw.go 13 KB

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