| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516 |
- // Copyright 2016 The G3N Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- package window
- import (
- "bytes"
- "fmt"
- "github.com/g3n/engine/gui/assets"
- "runtime"
- "github.com/g3n/engine/core"
- "github.com/g3n/engine/gls"
- "github.com/go-gl/glfw/v3.2/glfw"
- "image"
- _ "image/png"
- "os"
- )
- // Keycodes
- const (
- KeyUnknown = Key(glfw.KeyUnknown)
- KeySpace = Key(glfw.KeySpace)
- KeyApostrophe = Key(glfw.KeyApostrophe)
- KeyComma = Key(glfw.KeyComma)
- KeyMinus = Key(glfw.KeyMinus)
- KeyPeriod = Key(glfw.KeyPeriod)
- KeySlash = Key(glfw.KeySlash)
- Key0 = Key(glfw.Key0)
- Key1 = Key(glfw.Key1)
- Key2 = Key(glfw.Key2)
- Key3 = Key(glfw.Key3)
- Key4 = Key(glfw.Key4)
- Key5 = Key(glfw.Key5)
- Key6 = Key(glfw.Key6)
- Key7 = Key(glfw.Key7)
- Key8 = Key(glfw.Key8)
- Key9 = Key(glfw.Key9)
- KeySemicolon = Key(glfw.KeySemicolon)
- KeyEqual = Key(glfw.KeyEqual)
- KeyA = Key(glfw.KeyA)
- KeyB = Key(glfw.KeyB)
- KeyC = Key(glfw.KeyC)
- KeyD = Key(glfw.KeyD)
- KeyE = Key(glfw.KeyE)
- KeyF = Key(glfw.KeyF)
- KeyG = Key(glfw.KeyG)
- KeyH = Key(glfw.KeyH)
- KeyI = Key(glfw.KeyI)
- KeyJ = Key(glfw.KeyJ)
- KeyK = Key(glfw.KeyK)
- KeyL = Key(glfw.KeyL)
- KeyM = Key(glfw.KeyM)
- KeyN = Key(glfw.KeyN)
- KeyO = Key(glfw.KeyO)
- KeyP = Key(glfw.KeyP)
- KeyQ = Key(glfw.KeyQ)
- KeyR = Key(glfw.KeyR)
- KeyS = Key(glfw.KeyS)
- KeyT = Key(glfw.KeyT)
- KeyU = Key(glfw.KeyU)
- KeyV = Key(glfw.KeyV)
- KeyW = Key(glfw.KeyW)
- KeyX = Key(glfw.KeyX)
- KeyY = Key(glfw.KeyY)
- KeyZ = Key(glfw.KeyZ)
- KeyLeftBracket = Key(glfw.KeyLeftBracket)
- KeyBackslash = Key(glfw.KeyBackslash)
- KeyRightBracket = Key(glfw.KeyRightBracket)
- KeyGraveAccent = Key(glfw.KeyGraveAccent)
- KeyWorld1 = Key(glfw.KeyWorld1)
- KeyWorld2 = Key(glfw.KeyWorld2)
- KeyEscape = Key(glfw.KeyEscape)
- KeyEnter = Key(glfw.KeyEnter)
- KeyTab = Key(glfw.KeyTab)
- KeyBackspace = Key(glfw.KeyBackspace)
- KeyInsert = Key(glfw.KeyInsert)
- KeyDelete = Key(glfw.KeyDelete)
- KeyRight = Key(glfw.KeyRight)
- KeyLeft = Key(glfw.KeyLeft)
- KeyDown = Key(glfw.KeyDown)
- KeyUp = Key(glfw.KeyUp)
- KeyPageUp = Key(glfw.KeyPageUp)
- KeyPageDown = Key(glfw.KeyPageDown)
- KeyHome = Key(glfw.KeyHome)
- KeyEnd = Key(glfw.KeyEnd)
- KeyCapsLock = Key(glfw.KeyCapsLock)
- KeyScrollLock = Key(glfw.KeyScrollLock)
- KeyNumLock = Key(glfw.KeyNumLock)
- KeyPrintScreen = Key(glfw.KeyPrintScreen)
- KeyPause = Key(glfw.KeyPause)
- KeyF1 = Key(glfw.KeyF1)
- KeyF2 = Key(glfw.KeyF2)
- KeyF3 = Key(glfw.KeyF3)
- KeyF4 = Key(glfw.KeyF4)
- KeyF5 = Key(glfw.KeyF5)
- KeyF6 = Key(glfw.KeyF6)
- KeyF7 = Key(glfw.KeyF7)
- KeyF8 = Key(glfw.KeyF8)
- KeyF9 = Key(glfw.KeyF9)
- KeyF10 = Key(glfw.KeyF10)
- KeyF11 = Key(glfw.KeyF11)
- KeyF12 = Key(glfw.KeyF12)
- KeyF13 = Key(glfw.KeyF13)
- KeyF14 = Key(glfw.KeyF14)
- KeyF15 = Key(glfw.KeyF15)
- KeyF16 = Key(glfw.KeyF16)
- KeyF17 = Key(glfw.KeyF17)
- KeyF18 = Key(glfw.KeyF18)
- KeyF19 = Key(glfw.KeyF19)
- KeyF20 = Key(glfw.KeyF20)
- KeyF21 = Key(glfw.KeyF21)
- KeyF22 = Key(glfw.KeyF22)
- KeyF23 = Key(glfw.KeyF23)
- KeyF24 = Key(glfw.KeyF24)
- KeyF25 = Key(glfw.KeyF25)
- KeyKP0 = Key(glfw.KeyKP0)
- KeyKP1 = Key(glfw.KeyKP1)
- KeyKP2 = Key(glfw.KeyKP2)
- KeyKP3 = Key(glfw.KeyKP3)
- KeyKP4 = Key(glfw.KeyKP4)
- KeyKP5 = Key(glfw.KeyKP5)
- KeyKP6 = Key(glfw.KeyKP6)
- KeyKP7 = Key(glfw.KeyKP7)
- KeyKP8 = Key(glfw.KeyKP8)
- KeyKP9 = Key(glfw.KeyKP9)
- KeyKPDecimal = Key(glfw.KeyKPDecimal)
- KeyKPDivide = Key(glfw.KeyKPDivide)
- KeyKPMultiply = Key(glfw.KeyKPMultiply)
- KeyKPSubtract = Key(glfw.KeyKPSubtract)
- KeyKPAdd = Key(glfw.KeyKPAdd)
- KeyKPEnter = Key(glfw.KeyKPEnter)
- KeyKPEqual = Key(glfw.KeyKPEqual)
- KeyLeftShift = Key(glfw.KeyLeftShift)
- KeyLeftControl = Key(glfw.KeyLeftControl)
- KeyLeftAlt = Key(glfw.KeyLeftAlt)
- KeyLeftSuper = Key(glfw.KeyLeftSuper)
- KeyRightShift = Key(glfw.KeyRightShift)
- KeyRightControl = Key(glfw.KeyRightControl)
- KeyRightAlt = Key(glfw.KeyRightAlt)
- KeyRightSuper = Key(glfw.KeyRightSuper)
- KeyMenu = Key(glfw.KeyMenu)
- KeyLast = Key(glfw.KeyLast)
- )
- // Modifier keys
- const (
- ModShift = ModifierKey(glfw.ModShift)
- ModControl = ModifierKey(glfw.ModControl)
- ModAlt = ModifierKey(glfw.ModAlt)
- ModSuper = ModifierKey(glfw.ModSuper)
- )
- // Mouse buttons
- const (
- MouseButton1 = MouseButton(glfw.MouseButton1)
- MouseButton2 = MouseButton(glfw.MouseButton2)
- MouseButton3 = MouseButton(glfw.MouseButton3)
- MouseButton4 = MouseButton(glfw.MouseButton4)
- MouseButton5 = MouseButton(glfw.MouseButton5)
- MouseButton6 = MouseButton(glfw.MouseButton6)
- MouseButton7 = MouseButton(glfw.MouseButton7)
- MouseButton8 = MouseButton(glfw.MouseButton8)
- MouseButtonLast = MouseButton(glfw.MouseButtonLast)
- MouseButtonLeft = MouseButton(glfw.MouseButtonLeft)
- MouseButtonRight = MouseButton(glfw.MouseButtonRight)
- MouseButtonMiddle = MouseButton(glfw.MouseButtonMiddle)
- )
- // Actions
- const (
- // Release indicates that key or mouse button was released
- Release = Action(glfw.Release)
- // Press indicates that key or mouse button was pressed
- Press = Action(glfw.Press)
- // Repeat indicates that key was held down until it repeated
- Repeat = Action(glfw.Repeat)
- )
- // Input modes
- const (
- CursorInputMode = InputMode(glfw.CursorMode) // See Cursor mode values
- StickyKeysInputMode = InputMode(glfw.StickyKeysMode) // Value can be either 1 or 0
- StickyMouseButtonsInputMode = InputMode(glfw.StickyMouseButtonsMode) // Value can be either 1 or 0
- )
- // Cursor mode values
- const (
- CursorNormal = CursorMode(glfw.CursorNormal)
- CursorHidden = CursorMode(glfw.CursorHidden)
- CursorDisabled = CursorMode(glfw.CursorDisabled)
- )
- // 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
- lastY int
- lastWidth int
- lastHeight int
- scaleX float64
- scaleY float64
- // Events
- keyEv KeyEvent
- charEv CharEvent
- mouseEv MouseEvent
- posEv PosEvent
- sizeEv SizeEvent
- cursorEv CursorEvent
- scrollEv ScrollEvent
- mods ModifierKey // Current modifier keys
- // 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 {
- // Panic if already created
- if win != nil {
- panic(fmt.Errorf("can only call window.Init() once"))
- }
- // 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 {
- return err
- }
- // Set window hints
- glfw.WindowHint(glfw.ContextVersionMajor, 3)
- glfw.WindowHint(glfw.ContextVersionMinor, 3)
- glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
- glfw.WindowHint(glfw.Samples, 8)
- // 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
- // for any other values although the spec says it should ignore unsupported widths
- // and generate an error only when width <= 0.
- if runtime.GOOS == "darwin" {
- glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True)
- }
- // 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 {
- return err
- }
- w.MakeContextCurrent()
- // Create OpenGL state
- w.gls, err = gls.New()
- if err != nil {
- return err
- }
- // Compute and store scale
- fbw, fbh := w.GetFramebufferSize()
- w.scaleX = float64(fbw) / float64(width)
- w.scaleY = float64(fbh) / float64(height)
- // Create map for cursors
- w.cursors = make(map[Cursor]*glfw.Cursor)
- w.lastCursorKey = CursorLast
- // 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 {
- return err
- }
- w.cursors[DiagResize1Cursor] = glfw.CreateCursor(diag1Img, 8, 8) // [/]
- w.cursors[DiagResize2Cursor] = glfw.CreateCursor(diag2Img, 8, 8) // [\]
- // Set key callback to dispatch event
- w.SetKeyCallback(func(x *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
- w.keyEv.Keycode = Key(key)
- w.keyEv.Action = Action(action)
- w.keyEv.Mods = ModifierKey(mods)
- w.mods = w.keyEv.Mods
- if action == glfw.Press {
- fmt.Println("GLFW: OnKeyDown")
- w.Dispatch(OnKeyDown, &w.keyEv)
- } else if action == glfw.Release {
- fmt.Println("GLFW: OnKeyUp")
- w.Dispatch(OnKeyUp, &w.keyEv)
- } else if action == glfw.Repeat {
- fmt.Println("GLFW: OnKeyRepeat")
- w.Dispatch(OnKeyRepeat, &w.keyEv)
- }
- })
- // Set char callback
- w.SetCharModsCallback(func(x *glfw.Window, char rune, mods glfw.ModifierKey) {
- w.charEv.Char = char
- w.charEv.Mods = ModifierKey(mods)
- w.Dispatch(OnChar, &w.charEv)
- })
- // Set mouse button callback to dispatch event
- w.SetMouseButtonCallback(func(x *glfw.Window, button glfw.MouseButton, action glfw.Action, mods glfw.ModifierKey) {
- xpos, ypos := x.GetCursorPos()
- w.mouseEv.Button = MouseButton(button)
- w.mouseEv.Action = Action(action)
- w.mouseEv.Mods = ModifierKey(mods)
- w.mouseEv.Xpos = float32(xpos * w.scaleX)
- w.mouseEv.Ypos = float32(ypos * w.scaleY)
- if action == glfw.Press {
- w.Dispatch(OnMouseDown, &w.mouseEv)
- } else if action == glfw.Release {
- w.Dispatch(OnMouseUp, &w.mouseEv)
- }
- })
- // Set window size callback to dispatch event
- w.SetSizeCallback(func(x *glfw.Window, width int, height int) {
- fbw, fbh := x.GetFramebufferSize()
- w.sizeEv.W = w
- w.sizeEv.Width = width
- w.sizeEv.Height = height
- w.scaleX = float64(fbw) / float64(width)
- w.scaleY = float64(fbh) / float64(height)
- w.Dispatch(OnWindowSize, &w.sizeEv)
- })
- // Set window position event callback to dispatch event
- w.SetPosCallback(func(x *glfw.Window, xpos int, ypos int) {
- w.posEv.Xpos = xpos
- w.posEv.Ypos = ypos
- w.Dispatch(OnWindowPos, &w.posEv)
- })
- // Set window cursor position event callback to dispatch event
- w.SetCursorPosCallback(func(x *glfw.Window, xpos float64, ypos float64) {
- w.cursorEv.Xpos = float32(xpos * w.scaleX)
- w.cursorEv.Ypos = float32(ypos * w.scaleY)
- w.cursorEv.Mods = w.mods
- w.Dispatch(OnCursor, &w.cursorEv)
- })
- // Set mouse wheel scroll event callback to dispatch event
- w.SetScrollCallback(func(x *glfw.Window, xoff float64, yoff float64) {
- w.scrollEv.Xoffset = float32(xoff)
- w.scrollEv.Yoffset = float32(yoff)
- w.scrollEv.Mods = w.mods
- w.Dispatch(OnScroll, &w.scrollEv)
- })
- win = w // Set singleton
- return nil
- }
- // Gls returns the associated OpenGL state.
- func (w *GlfwWindow) Gls() *gls.GLS {
- return w.gls
- }
- // Fullscreen returns whether this windows is currently fullscreen.
- func (w *GlfwWindow) Fullscreen() bool {
- return w.fullscreen
- }
- // 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 w.fullscreen == full {
- return
- }
- // Set window fullscreen on the primary monitor
- if full {
- // Get size of primary monitor
- mon := glfw.GetPrimaryMonitor()
- vmode := mon.GetVideoMode()
- width := vmode.Width
- height := vmode.Height
- // 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 {
- // Restore window to previous position and size
- w.SetMonitor(nil, w.lastX, w.lastY, w.lastWidth, w.lastHeight, glfw.DontCare)
- w.fullscreen = false
- }
- }
- // Destroy destroys this window and its context
- func (w *GlfwWindow) Destroy() {
- w.Window.Destroy()
- glfw.Terminate()
- runtime.UnlockOSThread() // Important when using the execution tracer
- }
- // Scale returns this window's DPI scale factor (FramebufferSize / Size)
- func (w *GlfwWindow) GetScale() (x float64, y float64) {
- return w.scaleX, w.scaleY
- }
- // ScreenResolution returns the screen resolution
- func (w *GlfwWindow) 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 (w *GlfwWindow) 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 (w *GlfwWindow) SetSwapInterval(interval int) {
- glfw.SwapInterval(interval)
- }
- // SetCursor sets the window's cursor.
- func (w *GlfwWindow) SetCursor(cursor Cursor) {
- cur, ok := w.cursors[cursor]
- if !ok {
- panic("Invalid cursor")
- }
- w.Window.SetCursor(cur)
- }
- // CreateCursor creates a new custom cursor and returns an int handle.
- func (w *GlfwWindow) CreateCursor(imgFile string, xhot, yhot int) (Cursor, error) {
- // 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.lastCursorKey, nil
- }
- // DisposeCursor deletes the existing custom cursor with the provided int handle.
- func (w *GlfwWindow) DisposeCursor(cursor Cursor) {
- if cursor <= CursorLast {
- panic("Can't dispose standard cursor")
- }
- w.cursors[cursor].Destroy()
- delete(w.cursors, cursor)
- }
- // DisposeAllCursors deletes all existing custom cursors.
- func (w *GlfwWindow) DisposeAllCustomCursors() {
- // 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
- }
- // Center centers the window on the screen.
- //func (w *GlfwWindow) Center() {
- //
- // // TODO
- //}
|