|
@@ -5,12 +5,13 @@
|
|
|
package window
|
|
package window
|
|
|
|
|
|
|
|
import (
|
|
import (
|
|
|
|
|
+ "bytes"
|
|
|
|
|
+ "fmt"
|
|
|
|
|
+ "github.com/g3n/engine/gui/assets"
|
|
|
"runtime"
|
|
"runtime"
|
|
|
|
|
|
|
|
- "bytes"
|
|
|
|
|
"github.com/g3n/engine/core"
|
|
"github.com/g3n/engine/core"
|
|
|
"github.com/g3n/engine/gls"
|
|
"github.com/g3n/engine/gls"
|
|
|
- "github.com/g3n/engine/gui/assets"
|
|
|
|
|
"github.com/go-gl/glfw/v3.2/glfw"
|
|
"github.com/go-gl/glfw/v3.2/glfw"
|
|
|
"image"
|
|
"image"
|
|
|
_ "image/png"
|
|
_ "image/png"
|
|
@@ -167,18 +168,6 @@ const (
|
|
|
MouseButtonMiddle = MouseButton(glfw.MouseButtonMiddle)
|
|
MouseButtonMiddle = MouseButton(glfw.MouseButtonMiddle)
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
-// Standard cursors for g3n. The diagonal cursors are not standard for GLFW.
|
|
|
|
|
-const (
|
|
|
|
|
- ArrowCursor = StandardCursor(glfw.ArrowCursor)
|
|
|
|
|
- IBeamCursor = StandardCursor(glfw.IBeamCursor)
|
|
|
|
|
- CrosshairCursor = StandardCursor(glfw.CrosshairCursor)
|
|
|
|
|
- HandCursor = StandardCursor(glfw.HandCursor)
|
|
|
|
|
- HResizeCursor = StandardCursor(glfw.HResizeCursor)
|
|
|
|
|
- VResizeCursor = StandardCursor(glfw.VResizeCursor)
|
|
|
|
|
- DiagResize1Cursor = StandardCursor(VResizeCursor + 1)
|
|
|
|
|
- DiagResize2Cursor = StandardCursor(VResizeCursor + 2)
|
|
|
|
|
-)
|
|
|
|
|
-
|
|
|
|
|
// Actions
|
|
// Actions
|
|
|
const (
|
|
const (
|
|
|
// Release indicates that key or mouse button was released
|
|
// Release indicates that key or mouse button was released
|
|
@@ -203,31 +192,12 @@ const (
|
|
|
CursorDisabled = CursorMode(glfw.CursorDisabled)
|
|
CursorDisabled = CursorMode(glfw.CursorDisabled)
|
|
|
)
|
|
)
|
|
|
|
|
|
|
|
-// glfwManager contains data shared by all windows
|
|
|
|
|
-type glfwManager struct {
|
|
|
|
|
- arrowCursor *glfw.Cursor // Preallocated standard arrow cursor
|
|
|
|
|
- ibeamCursor *glfw.Cursor // Preallocated standard ibeam cursor
|
|
|
|
|
- crosshairCursor *glfw.Cursor // Preallocated standard cross hair cursor
|
|
|
|
|
- handCursor *glfw.Cursor // Preallocated standard hand cursor
|
|
|
|
|
- hresizeCursor *glfw.Cursor // Preallocated standard horizontal resize cursor
|
|
|
|
|
- vresizeCursor *glfw.Cursor // Preallocated standard vertical resize cursor
|
|
|
|
|
-
|
|
|
|
|
- // Non GLFW standard cursors (but g3n standard)
|
|
|
|
|
- diag1Cursor *glfw.Cursor // Preallocated diagonal resize cursor (/)
|
|
|
|
|
- diag2Cursor *glfw.Cursor // Preallocated diagonal resize cursor (\)
|
|
|
|
|
-
|
|
|
|
|
- // User-created custom cursors
|
|
|
|
|
- customCursors map[int]*glfw.Cursor
|
|
|
|
|
- lastCursorKey int
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// glfwWindow describes one glfw window
|
|
|
|
|
-type glfwWindow struct {
|
|
|
|
|
- core.Dispatcher // Embedded event dispatcher
|
|
|
|
|
- gls *gls.GLS // Associated OpenGL State
|
|
|
|
|
- win *glfw.Window // Pointer to native glfw window
|
|
|
|
|
- mgr *glfwManager // Pointer to window manager
|
|
|
|
|
- fullScreen bool
|
|
|
|
|
|
|
+// GlfwWindow describes one glfw window
|
|
|
|
|
+type GlfwWindow struct {
|
|
|
|
|
+ *glfw.Window // Embedded GLFW window
|
|
|
|
|
+ core.Dispatcher // Embedded event dispatcher
|
|
|
|
|
+ gls *gls.GLS // Associated OpenGL State
|
|
|
|
|
+ fullscreen bool
|
|
|
lastX int
|
|
lastX int
|
|
|
lastY int
|
|
lastY int
|
|
|
lastWidth int
|
|
lastWidth int
|
|
@@ -243,30 +213,43 @@ type glfwWindow struct {
|
|
|
sizeEv SizeEvent
|
|
sizeEv SizeEvent
|
|
|
cursorEv CursorEvent
|
|
cursorEv CursorEvent
|
|
|
scrollEv ScrollEvent
|
|
scrollEv ScrollEvent
|
|
|
-}
|
|
|
|
|
|
|
|
|
|
-// glfw manager singleton
|
|
|
|
|
-var manager *glfwManager
|
|
|
|
|
|
|
+ mods ModifierKey // Current modifier keys
|
|
|
|
|
|
|
|
-// Manager returns the glfw window manager
|
|
|
|
|
-func Manager() (IWindowManager, error) {
|
|
|
|
|
|
|
+ // Cursors
|
|
|
|
|
+ cursors map[Cursor]*glfw.Cursor
|
|
|
|
|
+ lastCursorKey Cursor
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Init initializes the GlfwWindow singleton with the specified width, height, and title.
|
|
|
|
|
+func Init(width, height int, title string) error {
|
|
|
|
|
|
|
|
- if manager != nil {
|
|
|
|
|
- return manager, nil
|
|
|
|
|
|
|
+ // Panic if already created
|
|
|
|
|
+ if win != nil {
|
|
|
|
|
+ panic(fmt.Errorf("can only call window.Init() once"))
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Initialize glfw
|
|
|
|
|
- err := glfw.Init()
|
|
|
|
|
|
|
+ // OpenGL functions must be executed in the same thread where
|
|
|
|
|
+ // the context was created (by wmgr.CreateWindow())
|
|
|
|
|
+ runtime.LockOSThread()
|
|
|
|
|
+
|
|
|
|
|
+ // Create wrapper window with dispatcher
|
|
|
|
|
+ w := new(GlfwWindow)
|
|
|
|
|
+ w.Dispatcher.Initialize()
|
|
|
|
|
+ var err error
|
|
|
|
|
+
|
|
|
|
|
+ // Initialize GLFW
|
|
|
|
|
+ err = glfw.Init()
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
- return nil, err
|
|
|
|
|
|
|
+ return err
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- // Sets window hints
|
|
|
|
|
|
|
+ // Set window hints
|
|
|
glfw.WindowHint(glfw.ContextVersionMajor, 3)
|
|
glfw.WindowHint(glfw.ContextVersionMajor, 3)
|
|
|
glfw.WindowHint(glfw.ContextVersionMinor, 3)
|
|
glfw.WindowHint(glfw.ContextVersionMinor, 3)
|
|
|
glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
|
|
glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
|
|
|
glfw.WindowHint(glfw.Samples, 8)
|
|
glfw.WindowHint(glfw.Samples, 8)
|
|
|
- // Sets OpenGL forward compatible context only for OSX because it is required for OSX.
|
|
|
|
|
|
|
+ // Set OpenGL forward compatible context only for OSX because it is required for OSX.
|
|
|
// When this is set, glLineWidth(width) only accepts width=1.0 and generates an error
|
|
// When this is set, glLineWidth(width) only accepts width=1.0 and generates an error
|
|
|
// for any other values although the spec says it should ignore unsupported widths
|
|
// for any other values although the spec says it should ignore unsupported widths
|
|
|
// and generate an error only when width <= 0.
|
|
// and generate an error only when width <= 0.
|
|
@@ -274,180 +257,92 @@ func Manager() (IWindowManager, error) {
|
|
|
glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True)
|
|
glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- manager = new(glfwManager)
|
|
|
|
|
-
|
|
|
|
|
- // Preallocate GLFW standard cursors
|
|
|
|
|
- manager.arrowCursor = glfw.CreateStandardCursor(glfw.ArrowCursor)
|
|
|
|
|
- manager.ibeamCursor = glfw.CreateStandardCursor(glfw.IBeamCursor)
|
|
|
|
|
- manager.crosshairCursor = glfw.CreateStandardCursor(glfw.CrosshairCursor)
|
|
|
|
|
- manager.handCursor = glfw.CreateStandardCursor(glfw.HandCursor)
|
|
|
|
|
- manager.hresizeCursor = glfw.CreateStandardCursor(glfw.HResizeCursor)
|
|
|
|
|
- manager.vresizeCursor = glfw.CreateStandardCursor(glfw.VResizeCursor)
|
|
|
|
|
-
|
|
|
|
|
- // Preallocate g3n cursors (diagonal cursors)
|
|
|
|
|
- cursorDiag1Png := assets.MustAsset("cursors/diag1.png")
|
|
|
|
|
- cursorDiag2Png := assets.MustAsset("cursors/diag2.png")
|
|
|
|
|
-
|
|
|
|
|
- diag1Img, _, err := image.Decode(bytes.NewReader(cursorDiag1Png))
|
|
|
|
|
- diag2Img, _, err := image.Decode(bytes.NewReader(cursorDiag2Png))
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- return nil, err
|
|
|
|
|
- }
|
|
|
|
|
- manager.diag1Cursor = glfw.CreateCursor(diag1Img, 8, 8)
|
|
|
|
|
- manager.diag2Cursor = glfw.CreateCursor(diag2Img, 8, 8)
|
|
|
|
|
-
|
|
|
|
|
- // Create map for custom cursors
|
|
|
|
|
- manager.customCursors = make(map[int]*glfw.Cursor)
|
|
|
|
|
-
|
|
|
|
|
- return manager, nil
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// ScreenResolution returns the screen resolution
|
|
|
|
|
-func (m *glfwManager) ScreenResolution(p interface{}) (width, height int) {
|
|
|
|
|
-
|
|
|
|
|
- mon := glfw.GetPrimaryMonitor()
|
|
|
|
|
- vmode := mon.GetVideoMode()
|
|
|
|
|
- return vmode.Width, vmode.Height
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// PollEvents process events in the event queue
|
|
|
|
|
-func (m *glfwManager) PollEvents() {
|
|
|
|
|
-
|
|
|
|
|
- glfw.PollEvents()
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// SetSwapInterval sets the number of screen updates to wait from the time SwapBuffer()
|
|
|
|
|
-// is called before swapping the buffers and returning.
|
|
|
|
|
-func (m *glfwManager) SetSwapInterval(interval int) {
|
|
|
|
|
-
|
|
|
|
|
- glfw.SwapInterval(interval)
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// Terminate destroys any remaining window, cursors and other related objects.
|
|
|
|
|
-func (m *glfwManager) Terminate() {
|
|
|
|
|
-
|
|
|
|
|
- glfw.Terminate()
|
|
|
|
|
- manager = nil
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// CreateCursor creates a new custom cursor and returns an int handle.
|
|
|
|
|
-func (m *glfwManager) CreateCursor(imgFile string, xhot, yhot int) (int, error) {
|
|
|
|
|
-
|
|
|
|
|
- // Open image file
|
|
|
|
|
- file, err := os.Open(imgFile)
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- return 0, err
|
|
|
|
|
- }
|
|
|
|
|
- defer file.Close()
|
|
|
|
|
-
|
|
|
|
|
- // Decodes image
|
|
|
|
|
- img, _, err := image.Decode(file)
|
|
|
|
|
|
|
+ // Create window and set it as the current context.
|
|
|
|
|
+ // The window is created always as not full screen because if it is
|
|
|
|
|
+ // created as full screen it not possible to revert it to windowed mode.
|
|
|
|
|
+ // At the end of this function, the window will be set to full screen if requested.
|
|
|
|
|
+ w.Window, err = glfw.CreateWindow(width, height, title, nil, nil)
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
- return 0, err
|
|
|
|
|
|
|
+ return err
|
|
|
}
|
|
}
|
|
|
|
|
+ w.MakeContextCurrent()
|
|
|
|
|
|
|
|
- cursor := glfw.CreateCursor(img, xhot, yhot)
|
|
|
|
|
|
|
+ // Create OpenGL state
|
|
|
|
|
+ w.gls, err = gls.New()
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
- return 0, err
|
|
|
|
|
|
|
+ return err
|
|
|
}
|
|
}
|
|
|
- m.lastCursorKey += 1
|
|
|
|
|
- m.customCursors[m.lastCursorKey] = cursor
|
|
|
|
|
|
|
|
|
|
- return m.lastCursorKey, nil
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// DisposeCursor deletes the existing custom cursor with the provided int handle.
|
|
|
|
|
-func (m *glfwManager) DisposeCursor(key int) {
|
|
|
|
|
-
|
|
|
|
|
- delete(m.customCursors, key)
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// DisposeAllCursors deletes all existing custom cursors.
|
|
|
|
|
-func (m *glfwManager) DisposeAllCursors() {
|
|
|
|
|
-
|
|
|
|
|
- m.customCursors = make(map[int]*glfw.Cursor)
|
|
|
|
|
- m.lastCursorKey = 0
|
|
|
|
|
-}
|
|
|
|
|
|
|
+ // Compute and store scale
|
|
|
|
|
+ fbw, fbh := w.GetFramebufferSize()
|
|
|
|
|
+ w.scaleX = float64(fbw) / float64(width)
|
|
|
|
|
+ w.scaleY = float64(fbh) / float64(height)
|
|
|
|
|
|
|
|
-// CreateWindow creates and returns a new window with the specified width and height in screen coordinates
|
|
|
|
|
-func (m *glfwManager) CreateWindow(width, height int, title string, fullscreen bool) (IWindow, error) {
|
|
|
|
|
|
|
+ // Create map for cursors
|
|
|
|
|
+ w.cursors = make(map[Cursor]*glfw.Cursor)
|
|
|
|
|
+ w.lastCursorKey = CursorLast
|
|
|
|
|
|
|
|
- // Creates window and sets it as the current context.
|
|
|
|
|
- // The window is created always as not full screen because if it is
|
|
|
|
|
- // created as full screen it not possible to revert it to windowed mode.
|
|
|
|
|
- // At the end of this function, the window will be set to full screen if requested.
|
|
|
|
|
- win, err := glfw.CreateWindow(width, height, title, nil, nil)
|
|
|
|
|
|
|
+ // Preallocate GLFW standard cursors
|
|
|
|
|
+ w.cursors[ArrowCursor] = glfw.CreateStandardCursor(glfw.ArrowCursor)
|
|
|
|
|
+ w.cursors[IBeamCursor] = glfw.CreateStandardCursor(glfw.IBeamCursor)
|
|
|
|
|
+ w.cursors[CrosshairCursor] = glfw.CreateStandardCursor(glfw.CrosshairCursor)
|
|
|
|
|
+ w.cursors[HandCursor] = glfw.CreateStandardCursor(glfw.HandCursor)
|
|
|
|
|
+ w.cursors[HResizeCursor] = glfw.CreateStandardCursor(glfw.HResizeCursor)
|
|
|
|
|
+ w.cursors[VResizeCursor] = glfw.CreateStandardCursor(glfw.VResizeCursor)
|
|
|
|
|
+
|
|
|
|
|
+ // Preallocate extra G3N standard cursors (diagonal resize cursors)
|
|
|
|
|
+ cursorDiag1Png := assets.MustAsset("cursors/diag1.png") // [/]
|
|
|
|
|
+ cursorDiag2Png := assets.MustAsset("cursors/diag2.png") // [\]
|
|
|
|
|
+ diag1Img, _, err := image.Decode(bytes.NewReader(cursorDiag1Png))
|
|
|
|
|
+ diag2Img, _, err := image.Decode(bytes.NewReader(cursorDiag2Png))
|
|
|
if err != nil {
|
|
if err != nil {
|
|
|
- return nil, err
|
|
|
|
|
|
|
+ return err
|
|
|
}
|
|
}
|
|
|
- win.MakeContextCurrent()
|
|
|
|
|
-
|
|
|
|
|
- // Create wrapper window with dispacher
|
|
|
|
|
- w := new(glfwWindow)
|
|
|
|
|
- w.win = win
|
|
|
|
|
- w.mgr = m
|
|
|
|
|
- w.Dispatcher.Initialize()
|
|
|
|
|
-
|
|
|
|
|
- fbw, fbh := w.FramebufferSize()
|
|
|
|
|
- w.scaleX = float64(fbw) / float64(width)
|
|
|
|
|
- w.scaleY = float64(fbh) / float64(height)
|
|
|
|
|
|
|
+ w.cursors[DiagResize1Cursor] = glfw.CreateCursor(diag1Img, 8, 8) // [/]
|
|
|
|
|
+ w.cursors[DiagResize2Cursor] = glfw.CreateCursor(diag2Img, 8, 8) // [\]
|
|
|
|
|
|
|
|
// Set key callback to dispatch event
|
|
// Set key callback to dispatch event
|
|
|
- win.SetKeyCallback(func(x *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
|
|
|
|
|
-
|
|
|
|
|
- w.keyEv.W = w
|
|
|
|
|
|
|
+ w.SetKeyCallback(func(x *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
|
|
|
w.keyEv.Keycode = Key(key)
|
|
w.keyEv.Keycode = Key(key)
|
|
|
- w.keyEv.Scancode = scancode
|
|
|
|
|
w.keyEv.Action = Action(action)
|
|
w.keyEv.Action = Action(action)
|
|
|
w.keyEv.Mods = ModifierKey(mods)
|
|
w.keyEv.Mods = ModifierKey(mods)
|
|
|
|
|
+ w.mods = w.keyEv.Mods
|
|
|
if action == glfw.Press {
|
|
if action == glfw.Press {
|
|
|
|
|
+ fmt.Println("GLFW: OnKeyDown")
|
|
|
w.Dispatch(OnKeyDown, &w.keyEv)
|
|
w.Dispatch(OnKeyDown, &w.keyEv)
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- if action == glfw.Release {
|
|
|
|
|
|
|
+ } else if action == glfw.Release {
|
|
|
|
|
+ fmt.Println("GLFW: OnKeyUp")
|
|
|
w.Dispatch(OnKeyUp, &w.keyEv)
|
|
w.Dispatch(OnKeyUp, &w.keyEv)
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- if action == glfw.Repeat {
|
|
|
|
|
|
|
+ } else if action == glfw.Repeat {
|
|
|
|
|
+ fmt.Println("GLFW: OnKeyRepeat")
|
|
|
w.Dispatch(OnKeyRepeat, &w.keyEv)
|
|
w.Dispatch(OnKeyRepeat, &w.keyEv)
|
|
|
- return
|
|
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
// Set char callback
|
|
// Set char callback
|
|
|
- win.SetCharModsCallback(func(x *glfw.Window, char rune, mods glfw.ModifierKey) {
|
|
|
|
|
-
|
|
|
|
|
- w.charEv.W = w
|
|
|
|
|
|
|
+ w.SetCharModsCallback(func(x *glfw.Window, char rune, mods glfw.ModifierKey) {
|
|
|
w.charEv.Char = char
|
|
w.charEv.Char = char
|
|
|
w.charEv.Mods = ModifierKey(mods)
|
|
w.charEv.Mods = ModifierKey(mods)
|
|
|
w.Dispatch(OnChar, &w.charEv)
|
|
w.Dispatch(OnChar, &w.charEv)
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
// Set mouse button callback to dispatch event
|
|
// Set mouse button callback to dispatch event
|
|
|
- win.SetMouseButtonCallback(func(x *glfw.Window, button glfw.MouseButton, action glfw.Action, mods glfw.ModifierKey) {
|
|
|
|
|
-
|
|
|
|
|
|
|
+ w.SetMouseButtonCallback(func(x *glfw.Window, button glfw.MouseButton, action glfw.Action, mods glfw.ModifierKey) {
|
|
|
xpos, ypos := x.GetCursorPos()
|
|
xpos, ypos := x.GetCursorPos()
|
|
|
-
|
|
|
|
|
- w.mouseEv.W = w
|
|
|
|
|
w.mouseEv.Button = MouseButton(button)
|
|
w.mouseEv.Button = MouseButton(button)
|
|
|
w.mouseEv.Action = Action(action)
|
|
w.mouseEv.Action = Action(action)
|
|
|
w.mouseEv.Mods = ModifierKey(mods)
|
|
w.mouseEv.Mods = ModifierKey(mods)
|
|
|
w.mouseEv.Xpos = float32(xpos * w.scaleX)
|
|
w.mouseEv.Xpos = float32(xpos * w.scaleX)
|
|
|
w.mouseEv.Ypos = float32(ypos * w.scaleY)
|
|
w.mouseEv.Ypos = float32(ypos * w.scaleY)
|
|
|
-
|
|
|
|
|
if action == glfw.Press {
|
|
if action == glfw.Press {
|
|
|
w.Dispatch(OnMouseDown, &w.mouseEv)
|
|
w.Dispatch(OnMouseDown, &w.mouseEv)
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- if action == glfw.Release {
|
|
|
|
|
|
|
+ } else if action == glfw.Release {
|
|
|
w.Dispatch(OnMouseUp, &w.mouseEv)
|
|
w.Dispatch(OnMouseUp, &w.mouseEv)
|
|
|
- return
|
|
|
|
|
}
|
|
}
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
// Set window size callback to dispatch event
|
|
// Set window size callback to dispatch event
|
|
|
- win.SetSizeCallback(func(x *glfw.Window, width int, height int) {
|
|
|
|
|
-
|
|
|
|
|
|
|
+ w.SetSizeCallback(func(x *glfw.Window, width int, height int) {
|
|
|
fbw, fbh := x.GetFramebufferSize()
|
|
fbw, fbh := x.GetFramebufferSize()
|
|
|
w.sizeEv.W = w
|
|
w.sizeEv.W = w
|
|
|
w.sizeEv.Width = width
|
|
w.sizeEv.Width = width
|
|
@@ -458,210 +353,164 @@ func (m *glfwManager) CreateWindow(width, height int, title string, fullscreen b
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
// Set window position event callback to dispatch event
|
|
// Set window position event callback to dispatch event
|
|
|
- win.SetPosCallback(func(x *glfw.Window, xpos int, ypos int) {
|
|
|
|
|
-
|
|
|
|
|
- w.posEv.W = w
|
|
|
|
|
|
|
+ w.SetPosCallback(func(x *glfw.Window, xpos int, ypos int) {
|
|
|
w.posEv.Xpos = xpos
|
|
w.posEv.Xpos = xpos
|
|
|
w.posEv.Ypos = ypos
|
|
w.posEv.Ypos = ypos
|
|
|
w.Dispatch(OnWindowPos, &w.posEv)
|
|
w.Dispatch(OnWindowPos, &w.posEv)
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
// Set window cursor position event callback to dispatch event
|
|
// Set window cursor position event callback to dispatch event
|
|
|
- win.SetCursorPosCallback(func(x *glfw.Window, xpos float64, ypos float64) {
|
|
|
|
|
-
|
|
|
|
|
- w.cursorEv.W = w
|
|
|
|
|
|
|
+ w.SetCursorPosCallback(func(x *glfw.Window, xpos float64, ypos float64) {
|
|
|
w.cursorEv.Xpos = float32(xpos * w.scaleX)
|
|
w.cursorEv.Xpos = float32(xpos * w.scaleX)
|
|
|
w.cursorEv.Ypos = float32(ypos * w.scaleY)
|
|
w.cursorEv.Ypos = float32(ypos * w.scaleY)
|
|
|
|
|
+ w.cursorEv.Mods = w.mods
|
|
|
w.Dispatch(OnCursor, &w.cursorEv)
|
|
w.Dispatch(OnCursor, &w.cursorEv)
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
// Set mouse wheel scroll event callback to dispatch event
|
|
// Set mouse wheel scroll event callback to dispatch event
|
|
|
- win.SetScrollCallback(func(x *glfw.Window, xoff float64, yoff float64) {
|
|
|
|
|
-
|
|
|
|
|
- w.scrollEv.W = w
|
|
|
|
|
|
|
+ w.SetScrollCallback(func(x *glfw.Window, xoff float64, yoff float64) {
|
|
|
w.scrollEv.Xoffset = float32(xoff)
|
|
w.scrollEv.Xoffset = float32(xoff)
|
|
|
w.scrollEv.Yoffset = float32(yoff)
|
|
w.scrollEv.Yoffset = float32(yoff)
|
|
|
|
|
+ w.scrollEv.Mods = w.mods
|
|
|
w.Dispatch(OnScroll, &w.scrollEv)
|
|
w.Dispatch(OnScroll, &w.scrollEv)
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- // Sets full screen if requested
|
|
|
|
|
- if fullscreen {
|
|
|
|
|
- w.SetFullScreen(true)
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- // Create OpenGL state
|
|
|
|
|
- gl, err := gls.New()
|
|
|
|
|
- if err != nil {
|
|
|
|
|
- return nil, err
|
|
|
|
|
- }
|
|
|
|
|
- w.gls = gl
|
|
|
|
|
-
|
|
|
|
|
- return w, nil
|
|
|
|
|
|
|
+ win = w // Set singleton
|
|
|
|
|
+ return nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Gls returns the associated OpenGL state
|
|
|
|
|
-func (w *glfwWindow) Gls() *gls.GLS {
|
|
|
|
|
|
|
+// Gls returns the associated OpenGL state.
|
|
|
|
|
+func (w *GlfwWindow) Gls() *gls.GLS {
|
|
|
|
|
|
|
|
return w.gls
|
|
return w.gls
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Manager returns the window manager and satisfies the IWindow interface
|
|
|
|
|
-func (w *glfwWindow) Manager() IWindowManager {
|
|
|
|
|
|
|
+// Fullscreen returns whether this windows is currently fullscreen.
|
|
|
|
|
+func (w *GlfwWindow) Fullscreen() bool {
|
|
|
|
|
|
|
|
- return w.mgr
|
|
|
|
|
|
|
+ return w.fullscreen
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// MakeContextCurrent makes the OpenGL context of this window current on the calling thread
|
|
|
|
|
-func (w *glfwWindow) MakeContextCurrent() {
|
|
|
|
|
-
|
|
|
|
|
- w.win.MakeContextCurrent()
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// FullScreen returns this window full screen state for the primary monitor
|
|
|
|
|
-func (w *glfwWindow) FullScreen() bool {
|
|
|
|
|
-
|
|
|
|
|
- return w.fullScreen
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// SetFullScreen sets this window full screen state for the primary monitor
|
|
|
|
|
-func (w *glfwWindow) SetFullScreen(full bool) {
|
|
|
|
|
|
|
+// SetFullscreen sets this window as fullscreen on the primary monitor
|
|
|
|
|
+// TODO allow for fullscreen with resolutions different than the monitor's
|
|
|
|
|
+func (w *GlfwWindow) SetFullscreen(full bool) {
|
|
|
|
|
|
|
|
// If already in the desired state, nothing to do
|
|
// If already in the desired state, nothing to do
|
|
|
- if w.fullScreen == full {
|
|
|
|
|
|
|
+ if w.fullscreen == full {
|
|
|
return
|
|
return
|
|
|
}
|
|
}
|
|
|
- // Sets this window full screen for the primary monitor
|
|
|
|
|
|
|
+ // Set window fullscreen on the primary monitor
|
|
|
if full {
|
|
if full {
|
|
|
- // Get primary monitor
|
|
|
|
|
|
|
+ // Get size of primary monitor
|
|
|
mon := glfw.GetPrimaryMonitor()
|
|
mon := glfw.GetPrimaryMonitor()
|
|
|
vmode := mon.GetVideoMode()
|
|
vmode := mon.GetVideoMode()
|
|
|
width := vmode.Width
|
|
width := vmode.Width
|
|
|
height := vmode.Height
|
|
height := vmode.Height
|
|
|
- // Saves current position and size of the window
|
|
|
|
|
- w.lastX, w.lastY = w.win.GetPos()
|
|
|
|
|
- w.lastWidth, w.lastHeight = w.win.GetSize()
|
|
|
|
|
- // Sets monitor for full screen
|
|
|
|
|
- w.win.SetMonitor(mon, 0, 0, width, height, vmode.RefreshRate)
|
|
|
|
|
- w.fullScreen = true
|
|
|
|
|
|
|
+ // Set as fullscreen on the primary monitor
|
|
|
|
|
+ w.SetMonitor(mon, 0, 0, width, height, vmode.RefreshRate)
|
|
|
|
|
+ w.fullscreen = true
|
|
|
|
|
+ // Save current position and size of the window
|
|
|
|
|
+ w.lastX, w.lastY = w.GetPos()
|
|
|
|
|
+ w.lastWidth, w.lastHeight = w.GetSize()
|
|
|
} else {
|
|
} else {
|
|
|
// Restore window to previous position and size
|
|
// Restore window to previous position and size
|
|
|
- w.win.SetMonitor(nil, w.lastX, w.lastY, w.lastWidth, w.lastHeight, glfw.DontCare)
|
|
|
|
|
- w.fullScreen = false
|
|
|
|
|
|
|
+ w.SetMonitor(nil, w.lastX, w.lastY, w.lastWidth, w.lastHeight, glfw.DontCare)
|
|
|
|
|
+ w.fullscreen = false
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Destroy destroys this window and its context
|
|
// Destroy destroys this window and its context
|
|
|
-func (w *glfwWindow) Destroy() {
|
|
|
|
|
-
|
|
|
|
|
- w.win.Destroy()
|
|
|
|
|
- w.win = nil
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// SwapBuffers swaps the front and back buffers of this window.
|
|
|
|
|
-// If the swap interval is greater than zero,
|
|
|
|
|
-// the GPU driver waits the specified number of screen updates before swapping the buffers.
|
|
|
|
|
-func (w *glfwWindow) SwapBuffers() {
|
|
|
|
|
-
|
|
|
|
|
- w.win.SwapBuffers()
|
|
|
|
|
-}
|
|
|
|
|
|
|
+func (w *GlfwWindow) Destroy() {
|
|
|
|
|
|
|
|
-// FramebufferSize returns framebuffer size of this window
|
|
|
|
|
-func (w *glfwWindow) FramebufferSize() (width int, height int) {
|
|
|
|
|
-
|
|
|
|
|
- return w.win.GetFramebufferSize()
|
|
|
|
|
|
|
+ w.Window.Destroy()
|
|
|
|
|
+ glfw.Terminate()
|
|
|
|
|
+ runtime.UnlockOSThread() // Important when using the execution tracer
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// Scale returns this window's DPI scale factor (FramebufferSize / Size)
|
|
// Scale returns this window's DPI scale factor (FramebufferSize / Size)
|
|
|
-func (w *glfwWindow) Scale() (x float64, y float64) {
|
|
|
|
|
|
|
+func (w *GlfwWindow) GetScale() (x float64, y float64) {
|
|
|
|
|
|
|
|
return w.scaleX, w.scaleY
|
|
return w.scaleX, w.scaleY
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Size returns this window's size in screen coordinates
|
|
|
|
|
-func (w *glfwWindow) Size() (width int, height int) {
|
|
|
|
|
|
|
+// ScreenResolution returns the screen resolution
|
|
|
|
|
+func (w *GlfwWindow) ScreenResolution(p interface{}) (width, height int) {
|
|
|
|
|
|
|
|
- return w.win.GetSize()
|
|
|
|
|
|
|
+ mon := glfw.GetPrimaryMonitor()
|
|
|
|
|
+ vmode := mon.GetVideoMode()
|
|
|
|
|
+ return vmode.Width, vmode.Height
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// SetSize sets the size, in screen coordinates, of the client area of this window
|
|
|
|
|
-func (w *glfwWindow) SetSize(width int, height int) {
|
|
|
|
|
|
|
+// PollEvents process events in the event queue
|
|
|
|
|
+func (w *GlfwWindow) PollEvents() {
|
|
|
|
|
|
|
|
- w.win.SetSize(width, height)
|
|
|
|
|
|
|
+ glfw.PollEvents()
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// Pos returns the position, in screen coordinates, of the upper-left corner of the client area of this window
|
|
|
|
|
-func (w *glfwWindow) Pos() (xpos, ypos int) {
|
|
|
|
|
|
|
+// SetSwapInterval sets the number of screen updates to wait from the time SwapBuffer()
|
|
|
|
|
+// is called before swapping the buffers and returning.
|
|
|
|
|
+func (w *GlfwWindow) SetSwapInterval(interval int) {
|
|
|
|
|
|
|
|
- return w.win.GetPos()
|
|
|
|
|
|
|
+ glfw.SwapInterval(interval)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// SetPos sets the position, in screen coordinates, of the upper-left corner of the client area of this window.
|
|
|
|
|
-// If the window is a full screen window, this function does nothing.
|
|
|
|
|
-func (w *glfwWindow) SetPos(xpos, ypos int) {
|
|
|
|
|
|
|
+// SetCursor sets the window's cursor.
|
|
|
|
|
+func (w *GlfwWindow) SetCursor(cursor Cursor) {
|
|
|
|
|
|
|
|
- w.win.SetPos(xpos, ypos)
|
|
|
|
|
|
|
+ cur, ok := w.cursors[cursor]
|
|
|
|
|
+ if !ok {
|
|
|
|
|
+ panic("Invalid cursor")
|
|
|
|
|
+ }
|
|
|
|
|
+ w.Window.SetCursor(cur)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// SetTitle sets this window title, encoded as UTF-8
|
|
|
|
|
-func (w *glfwWindow) SetTitle(title string) {
|
|
|
|
|
-
|
|
|
|
|
- w.win.SetTitle(title)
|
|
|
|
|
-}
|
|
|
|
|
|
|
+// CreateCursor creates a new custom cursor and returns an int handle.
|
|
|
|
|
+func (w *GlfwWindow) CreateCursor(imgFile string, xhot, yhot int) (Cursor, error) {
|
|
|
|
|
|
|
|
-// ShouldClose returns the current state of this window should close flag
|
|
|
|
|
-func (w *glfwWindow) ShouldClose() bool {
|
|
|
|
|
|
|
+ // Open image file
|
|
|
|
|
+ file, err := os.Open(imgFile)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return 0, err
|
|
|
|
|
+ }
|
|
|
|
|
+ defer file.Close()
|
|
|
|
|
+ // Decode image
|
|
|
|
|
+ img, _, err := image.Decode(file)
|
|
|
|
|
+ if err != nil {
|
|
|
|
|
+ return 0, err
|
|
|
|
|
+ }
|
|
|
|
|
+ // Create and store cursor
|
|
|
|
|
+ w.lastCursorKey += 1
|
|
|
|
|
+ w.cursors[Cursor(w.lastCursorKey)] = glfw.CreateCursor(img, xhot, yhot)
|
|
|
|
|
|
|
|
- return w.win.ShouldClose()
|
|
|
|
|
|
|
+ return w.lastCursorKey, nil
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// SetShouldClose sets the state of this windows should close flag
|
|
|
|
|
-func (w *glfwWindow) SetShouldClose(v bool) {
|
|
|
|
|
-
|
|
|
|
|
- w.win.SetShouldClose(v)
|
|
|
|
|
-}
|
|
|
|
|
|
|
+// DisposeCursor deletes the existing custom cursor with the provided int handle.
|
|
|
|
|
+func (w *GlfwWindow) DisposeCursor(cursor Cursor) {
|
|
|
|
|
|
|
|
-// SetStandardCursor sets the window's cursor to a standard one
|
|
|
|
|
-func (w *glfwWindow) SetStandardCursor(cursor StandardCursor) {
|
|
|
|
|
-
|
|
|
|
|
- switch cursor {
|
|
|
|
|
- case ArrowCursor:
|
|
|
|
|
- w.win.SetCursor(w.mgr.arrowCursor)
|
|
|
|
|
- case IBeamCursor:
|
|
|
|
|
- w.win.SetCursor(w.mgr.ibeamCursor)
|
|
|
|
|
- case CrosshairCursor:
|
|
|
|
|
- w.win.SetCursor(w.mgr.crosshairCursor)
|
|
|
|
|
- case HandCursor:
|
|
|
|
|
- w.win.SetCursor(w.mgr.handCursor)
|
|
|
|
|
- case HResizeCursor:
|
|
|
|
|
- w.win.SetCursor(w.mgr.hresizeCursor)
|
|
|
|
|
- case VResizeCursor:
|
|
|
|
|
- w.win.SetCursor(w.mgr.vresizeCursor)
|
|
|
|
|
- // Non-GLFW cursors (but standard cursors for g3n)
|
|
|
|
|
- case DiagResize1Cursor:
|
|
|
|
|
- w.win.SetCursor(w.mgr.diag1Cursor)
|
|
|
|
|
- case DiagResize2Cursor:
|
|
|
|
|
- w.win.SetCursor(w.mgr.diag2Cursor)
|
|
|
|
|
- default:
|
|
|
|
|
- panic("Invalid cursor")
|
|
|
|
|
|
|
+ if cursor <= CursorLast {
|
|
|
|
|
+ panic("Can't dispose standard cursor")
|
|
|
}
|
|
}
|
|
|
|
|
+ w.cursors[cursor].Destroy()
|
|
|
|
|
+ delete(w.cursors, cursor)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// SetStandardCursor sets this window's cursor to a custom, user-created one
|
|
|
|
|
-func (w *glfwWindow) SetCustomCursor(key int) {
|
|
|
|
|
-
|
|
|
|
|
- w.win.SetCursor(w.mgr.customCursors[key])
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// SetInputMode changes specified input to specified state
|
|
|
|
|
-// Reference: http://www.glfw.org/docs/latest/group__input.html#gaa92336e173da9c8834558b54ee80563b
|
|
|
|
|
-func (w *glfwWindow) SetInputMode(mode InputMode, state int) {
|
|
|
|
|
|
|
+// DisposeAllCursors deletes all existing custom cursors.
|
|
|
|
|
+func (w *GlfwWindow) DisposeAllCustomCursors() {
|
|
|
|
|
|
|
|
- w.win.SetInputMode(glfw.InputMode(mode), state)
|
|
|
|
|
|
|
+ // Destroy and delete all custom cursors
|
|
|
|
|
+ for key := range w.cursors {
|
|
|
|
|
+ if key > CursorLast {
|
|
|
|
|
+ w.cursors[key].Destroy()
|
|
|
|
|
+ delete(w.cursors, key)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ // Set the next cursor key as the last standard cursor key + 1
|
|
|
|
|
+ w.lastCursorKey = CursorLast
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// SetCursorPos sets cursor position in window coordinates
|
|
|
|
|
-// Reference: http://www.glfw.org/docs/latest/group__input.html#ga04b03af936d906ca123c8f4ee08b39e7
|
|
|
|
|
-func (w *glfwWindow) SetCursorPos(xpos, ypos float64) {
|
|
|
|
|
-
|
|
|
|
|
- w.win.SetCursorPos(xpos, ypos)
|
|
|
|
|
-}
|
|
|
|
|
|
|
+// Center centers the window on the screen.
|
|
|
|
|
+//func (w *GlfwWindow) Center() {
|
|
|
|
|
+//
|
|
|
|
|
+// // TODO
|
|
|
|
|
+//}
|