| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522 |
- package imgui
- import (
- "fmt"
- "image"
- "math"
- "runtime"
- "github.com/go-gl/glfw/v3.3/glfw"
- )
- var GlfwDontCare int = glfw.DontCare
- type GLFWClipboard struct {
- window *glfw.Window
- }
- func NewGLFWClipboard(w *glfw.Window) *GLFWClipboard {
- return &GLFWClipboard{window: w}
- }
- func (c *GLFWClipboard) Text() (string, error) {
- return c.window.GetClipboardString(), nil
- }
- func (c *GLFWClipboard) SetText(text string) {
- c.window.SetClipboardString(text)
- }
- type GLFWWindowFlags uint8
- const (
- GLFWWindowFlagsNotResizable GLFWWindowFlags = 1 << iota
- GLFWWindowFlagsMaximized
- GLFWWindowFlagsFloating
- GLFWWindowFlagsFrameless
- GLFWWindowFlagsTransparent
- )
- // GLFW implements a platform based on github.com/go-gl/glfw (v3.3).
- type GLFW struct {
- imguiIO IO
- window *glfw.Window
- tps int
- time float64
- mouseJustPressed [3]bool
- mouseCursors map[int]*glfw.Cursor
- posChangeCallback func(int, int)
- sizeChangeCallback func(int, int)
- dropCallback func([]string)
- inputCallback func(key glfw.Key, mods glfw.ModifierKey, action glfw.Action)
- closeCallback func() bool
- }
- // NewGLFW attempts to initialize a GLFW context.
- func NewGLFW(io IO, title string, width, height int, flags GLFWWindowFlags) (*GLFW, error) {
- runtime.LockOSThread()
- err := glfw.Init()
- if err != nil {
- return nil, fmt.Errorf("failed to initialize glfw: %v", err)
- }
- glfw.WindowHint(glfw.ContextVersionMajor, 3)
- glfw.WindowHint(glfw.ContextVersionMinor, 3)
- glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
- glfw.WindowHint(glfw.OpenGLForwardCompatible, 1)
- glfw.WindowHint(glfw.ScaleToMonitor, glfw.True)
- glfw.WindowHint(glfw.Visible, glfw.False)
- if flags&GLFWWindowFlagsNotResizable != 0 {
- glfw.WindowHint(glfw.Resizable, glfw.False)
- }
- if flags&GLFWWindowFlagsMaximized != 0 {
- glfw.WindowHint(glfw.Maximized, glfw.True)
- }
- if flags&GLFWWindowFlagsFloating != 0 {
- glfw.WindowHint(glfw.Floating, glfw.True)
- }
- if flags&GLFWWindowFlagsFrameless != 0 {
- glfw.WindowHint(glfw.Decorated, glfw.False)
- }
- if flags&GLFWWindowFlagsTransparent != 0 {
- glfw.WindowHint(glfw.TransparentFramebuffer, glfw.True)
- }
- window, err := glfw.CreateWindow(width, height, title, nil, nil)
- if err != nil {
- glfw.Terminate()
- return nil, fmt.Errorf("failed to create window: %v", err)
- }
- window.MakeContextCurrent()
- glfw.SwapInterval(1)
- platform := &GLFW{
- imguiIO: io,
- window: window,
- tps: 60,
- }
- platform.setKeyMapping()
- platform.installCallbacks()
- // Create mosue cursors
- platform.mouseCursors = make(map[int]*glfw.Cursor)
- platform.mouseCursors[MouseCursorArrow] = glfw.CreateStandardCursor(glfw.ArrowCursor)
- platform.mouseCursors[MouseCursorTextInput] = glfw.CreateStandardCursor(glfw.IBeamCursor)
- platform.mouseCursors[MouseCursorResizeAll] = glfw.CreateStandardCursor(glfw.CrosshairCursor)
- platform.mouseCursors[MouseCursorHand] = glfw.CreateStandardCursor(glfw.HandCursor)
- platform.mouseCursors[MouseCursorResizeEW] = glfw.CreateStandardCursor(glfw.HResizeCursor)
- platform.mouseCursors[MouseCursorResizeNS] = glfw.CreateStandardCursor(glfw.VResizeCursor)
- io.SetClipboard(NewGLFWClipboard(window))
- if flags&GLFWWindowFlagsMaximized == 0 {
- // Center window to monitor
- platform.centerWindow()
- }
- platform.window.Show()
- return platform, nil
- }
- // Dispose cleans up the resources.
- func (platform *GLFW) Dispose() {
- platform.window.Destroy()
- glfw.Terminate()
- }
- func (platform *GLFW) GetContentScale() float32 {
- x, _ := platform.window.GetContentScale()
- // Do not scale on MacOS
- if runtime.GOOS == "darwin" {
- x = 1
- }
- return x
- }
- func (platform *GLFW) GetWindow() *glfw.Window {
- return platform.window
- }
- func (platform *GLFW) GetPos() (x, y int) {
- return platform.window.GetPos()
- }
- func (platform *GLFW) centerWindow() {
- monitor := platform.getBestMonitor()
- if monitor == nil {
- return
- }
- mode := monitor.GetVideoMode()
- if mode == nil {
- return
- }
- monitorX, monitorY := monitor.GetPos()
- windowWidth, windowHeight := platform.window.GetSize()
- platform.window.SetPos(monitorX+(mode.Width-windowWidth)/2, monitorY+(mode.Height-windowHeight)/2)
- }
- func (platform *GLFW) getBestMonitor() *glfw.Monitor {
- monitors := glfw.GetMonitors()
- if len(monitors) == 0 {
- return nil
- }
- width, height := platform.window.GetSize()
- x, y := platform.window.GetPos()
- var bestMonitor *glfw.Monitor
- var bestArea int
- for _, m := range monitors {
- monitorX, monitorY := m.GetPos()
- mode := m.GetVideoMode()
- if mode == nil {
- continue
- }
- areaMinX := int(math.Max(float64(x), float64(monitorX)))
- areaMinY := int(math.Max(float64(y), float64(monitorY)))
- areaMaxX := int(math.Min(float64(x+width), float64(monitorX+mode.Width)))
- areaMaxY := int(math.Min(float64(y+height), float64(monitorY+mode.Height)))
- area := (areaMaxX - areaMinX) * (areaMaxY - areaMinY)
- if area > bestArea {
- bestArea = area
- bestMonitor = m
- }
- }
- return bestMonitor
- }
- // ShouldStop returns true if the window is to be closed.
- func (platform *GLFW) ShouldStop() bool {
- return platform.window.ShouldClose()
- }
- // SetShouldStop sets whether window should be closed
- func (platform *GLFW) SetShouldStop(v bool) {
- platform.window.SetShouldClose(v)
- }
- func (platform *GLFW) WaitForEvent() {
- if platform.imguiIO.GetConfigFlags()&ConfigFlagEnablePowerSavingMode == 0 {
- return
- }
- windowIsHidden := platform.window.GetAttrib(glfw.Visible) == glfw.False || platform.window.GetAttrib(glfw.Iconified) == glfw.True
- waitingTime := math.Inf(0)
- if !windowIsHidden {
- waitingTime = GetEventWaitingTime()
- }
- if waitingTime > 0 {
- if math.IsInf(waitingTime, 0) {
- glfw.WaitEvents()
- } else {
- glfw.WaitEventsTimeout(waitingTime)
- }
- }
- }
- // ProcessEvents handles all pending window events.
- func (platform *GLFW) ProcessEvents() {
- platform.WaitForEvent()
- glfw.PollEvents()
- }
- // DisplaySize returns the dimension of the display.
- func (platform *GLFW) DisplaySize() [2]float32 {
- w, h := platform.window.GetSize()
- return [2]float32{float32(w), float32(h)}
- }
- // FramebufferSize returns the dimension of the framebuffer.
- func (platform *GLFW) FramebufferSize() [2]float32 {
- w, h := platform.window.GetFramebufferSize()
- return [2]float32{float32(w), float32(h)}
- }
- // NewFrame marks the begin of a render pass. It forwards all current state to imgui IO.
- func (platform *GLFW) NewFrame() {
- // Setup display size (every frame to accommodate for window resizing)
- displaySize := platform.DisplaySize()
- platform.imguiIO.SetDisplaySize(Vec2{X: displaySize[0], Y: displaySize[1]})
- // Setup time step
- currentTime := glfw.GetTime()
- if platform.time > 0 {
- platform.imguiIO.SetDeltaTime(float32(currentTime - platform.time))
- }
- platform.time = currentTime
- // Setup inputs
- if platform.window.GetAttrib(glfw.Focused) != 0 {
- x, y := platform.window.GetCursorPos()
- platform.imguiIO.SetMousePosition(Vec2{X: float32(x), Y: float32(y)})
- } else {
- platform.imguiIO.SetMousePosition(Vec2{X: -math.MaxFloat32, Y: -math.MaxFloat32})
- }
- for i := 0; i < len(platform.mouseJustPressed); i++ {
- down := platform.mouseJustPressed[i] || (platform.window.GetMouseButton(glfwButtonIDByIndex[i]) == glfw.Press)
- platform.imguiIO.SetMouseButtonDown(i, down)
- platform.mouseJustPressed[i] = false
- }
- platform.updateMouseCursor()
- }
- // PostRender performs a buffer swap.
- func (platform *GLFW) PostRender() {
- platform.window.SwapBuffers()
- }
- func (platform *GLFW) SetPosChangeCallback(cb func(int, int)) {
- platform.posChangeCallback = cb
- }
- func (platform *GLFW) SetSizeChangeCallback(cb func(int, int)) {
- platform.sizeChangeCallback = cb
- }
- func (platform *GLFW) Update() {
- glfw.PostEmptyEvent()
- }
- func (platform *GLFW) SetDropCallback(cb func(names []string)) {
- platform.dropCallback = cb
- }
- func (platform *GLFW) SetInputCallback(cb func(key glfw.Key, mods glfw.ModifierKey, action glfw.Action)) {
- platform.inputCallback = cb
- }
- func (platform *GLFW) SetCloseCallback(cb func() bool) {
- platform.closeCallback = cb
- }
- func (platform *GLFW) updateMouseCursor() {
- io := platform.imguiIO
- if (io.GetConfigFlags()&ConfigFlagNoMouseCursorChange) == 1 || platform.window.GetInputMode(glfw.CursorMode) == glfw.CursorDisabled {
- return
- }
- cursor := MouseCursor()
- if cursor == MouseCursorNone || io.GetMouseDrawCursor() {
- platform.window.SetInputMode(glfw.CursorMode, glfw.CursorHidden)
- } else {
- gCursor := platform.mouseCursors[MouseCursorArrow]
- if c, ok := platform.mouseCursors[cursor]; ok {
- gCursor = c
- }
- platform.window.SetCursor(gCursor)
- platform.window.SetInputMode(glfw.CursorMode, glfw.CursorNormal)
- }
- }
- func (platform *GLFW) setKeyMapping() {
- // Keyboard mapping. ImGui will use those indices to peek into the io.KeysDown[] array.
- platform.imguiIO.KeyMap(KeyTab, int(glfw.KeyTab))
- platform.imguiIO.KeyMap(KeyLeftArrow, int(glfw.KeyLeft))
- platform.imguiIO.KeyMap(KeyRightArrow, int(glfw.KeyRight))
- platform.imguiIO.KeyMap(KeyUpArrow, int(glfw.KeyUp))
- platform.imguiIO.KeyMap(KeyDownArrow, int(glfw.KeyDown))
- platform.imguiIO.KeyMap(KeyPageUp, int(glfw.KeyPageUp))
- platform.imguiIO.KeyMap(KeyPageDown, int(glfw.KeyPageDown))
- platform.imguiIO.KeyMap(KeyHome, int(glfw.KeyHome))
- platform.imguiIO.KeyMap(KeyEnd, int(glfw.KeyEnd))
- platform.imguiIO.KeyMap(KeyInsert, int(glfw.KeyInsert))
- platform.imguiIO.KeyMap(KeyDelete, int(glfw.KeyDelete))
- platform.imguiIO.KeyMap(KeyBackspace, int(glfw.KeyBackspace))
- platform.imguiIO.KeyMap(KeySpace, int(glfw.KeySpace))
- platform.imguiIO.KeyMap(KeyEnter, int(glfw.KeyEnter))
- platform.imguiIO.KeyMap(KeyEscape, int(glfw.KeyEscape))
- platform.imguiIO.KeyMap(KeyA, int(glfw.KeyA))
- platform.imguiIO.KeyMap(KeyC, int(glfw.KeyC))
- platform.imguiIO.KeyMap(KeyV, int(glfw.KeyV))
- platform.imguiIO.KeyMap(KeyX, int(glfw.KeyX))
- platform.imguiIO.KeyMap(KeyY, int(glfw.KeyY))
- platform.imguiIO.KeyMap(KeyZ, int(glfw.KeyZ))
- }
- func (platform *GLFW) installCallbacks() {
- platform.window.SetMouseButtonCallback(platform.mouseButtonChange)
- platform.window.SetScrollCallback(platform.mouseScrollChange)
- platform.window.SetKeyCallback(platform.keyChange)
- platform.window.SetCharCallback(platform.charChange)
- platform.window.SetSizeCallback(platform.sizeChange)
- platform.window.SetDropCallback(platform.onDrop)
- platform.window.SetPosCallback(platform.posChange)
- platform.window.SetCloseCallback(platform.onClose)
- platform.window.SetFocusCallback(platform.onFocus)
- }
- var glfwButtonIndexByID = map[glfw.MouseButton]int{
- glfw.MouseButton1: 0,
- glfw.MouseButton2: 1,
- glfw.MouseButton3: 2,
- }
- var glfwButtonIDByIndex = map[int]glfw.MouseButton{
- 0: glfw.MouseButton1,
- 1: glfw.MouseButton2,
- 2: glfw.MouseButton3,
- }
- func (platform *GLFW) onFocus(window *glfw.Window, focused bool) {
- platform.imguiIO.AddFocusEvent(focused)
- }
- func (platform *GLFW) onClose(window *glfw.Window) {
- if platform.closeCallback != nil {
- shouldClose := platform.closeCallback()
- window.SetShouldClose(shouldClose)
- }
- }
- func (platform *GLFW) onDrop(window *glfw.Window, names []string) {
- window.Focus()
- if platform.dropCallback != nil {
- platform.dropCallback(names)
- }
- }
- func (platform *GLFW) posChange(window *glfw.Window, x, y int) {
- platform.imguiIO.SetFrameCountSinceLastInput(0)
- // Notfy pos changed and redraw.
- if platform.posChangeCallback != nil {
- platform.posChangeCallback(x, y)
- }
- }
- func (platform *GLFW) sizeChange(window *glfw.Window, width, height int) {
- platform.imguiIO.SetFrameCountSinceLastInput(0)
- // Notify size changed and redraw.
- if platform.sizeChangeCallback != nil {
- platform.sizeChangeCallback(width, height)
- }
- }
- func (platform *GLFW) mouseButtonChange(window *glfw.Window, rawButton glfw.MouseButton, action glfw.Action, mods glfw.ModifierKey) {
- platform.imguiIO.SetFrameCountSinceLastInput(0)
- buttonIndex, known := glfwButtonIndexByID[rawButton]
- if known && (action == glfw.Press) {
- platform.mouseJustPressed[buttonIndex] = true
- }
- }
- func (platform *GLFW) mouseScrollChange(window *glfw.Window, x, y float64) {
- platform.imguiIO.SetFrameCountSinceLastInput(0)
- platform.imguiIO.AddMouseWheelDelta(float32(x), float32(y))
- }
- func (platform *GLFW) keyChange(window *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
- platform.imguiIO.SetFrameCountSinceLastInput(0)
- if action == glfw.Press {
- platform.imguiIO.KeyPress(int(key))
- }
- if action == glfw.Release {
- platform.imguiIO.KeyRelease(int(key))
- }
- // Modifiers are not reliable across systems
- platform.imguiIO.KeyCtrl(int(glfw.KeyLeftControl), int(glfw.KeyRightControl))
- platform.imguiIO.KeyShift(int(glfw.KeyLeftShift), int(glfw.KeyRightShift))
- platform.imguiIO.KeyAlt(int(glfw.KeyLeftAlt), int(glfw.KeyRightAlt))
- platform.imguiIO.KeySuper(int(glfw.KeyLeftSuper), int(glfw.KeyRightSuper))
- if platform.inputCallback != nil {
- platform.inputCallback(key, mods, action)
- }
- }
- func (platform *GLFW) charChange(window *glfw.Window, char rune) {
- platform.imguiIO.SetFrameCountSinceLastInput(0)
- platform.imguiIO.AddInputCharacters(string(char))
- }
- func (platform *GLFW) GetClipboard() string {
- return platform.window.GetClipboardString()
- }
- func (platform *GLFW) SetClipboard(content string) {
- platform.window.SetClipboardString(content)
- }
- func (platform *GLFW) GetTPS() int {
- return platform.tps
- }
- func (platform *GLFW) SetTPS(tps int) {
- platform.tps = tps
- }
- // SetIcon sets the icon of the specified window. If passed an array of candidate images,
- // those of or closest to the sizes desired by the system are selected. If no images are
- // specified, the window reverts to its default icon.
- //
- // The image is ideally provided in the form of *image.NRGBA.
- // The pixels are 32-bit, little-endian, non-premultiplied RGBA, i.e. eight
- // bits per channel with the red channel first. They are arranged canonically
- // as packed sequential rows, starting from the top-left corner. If the image
- // type is not *image.NRGBA, it will be converted to it.
- //
- // The desired image sizes varies depending on platform and system settings. The selected
- // images will be rescaled as needed. Good sizes include 16x16, 32x32 and 48x48.
- func (platform *GLFW) SetIcon(icons []image.Image) {
- platform.window.SetIcon(icons)
- }
- // SetSizeLimits sets the size limits of the client area of the specified window.
- // If the window is full screen or not resizable, this function does nothing.
- //
- // The size limits are applied immediately and may cause the window to be resized.
- // To specify only a minimum size or only a maximum one, set the other pair to GLFW_DONT_CARE.
- // To disable size limits for a window, set them all to GLFW_DONT_CARE.
- func (platform *GLFW) SetSizeLimits(minw, minh, maxw, maxh int) {
- platform.window.SetSizeLimits(minw, minh, maxw, maxh)
- }
- // SetTitle sets the title of window.
- func (platform *GLFW) SetTitle(title string) {
- platform.window.SetTitle(title)
- }
- // IsMinimized checks whether window is minimized.
- func (platform *GLFW) IsMinimized() bool {
- return glfw.True == platform.window.GetAttrib(glfw.Iconified)
- }
- // IsVisible checks whether window is visible.
- func (platform *GLFW) IsVisible() bool {
- return glfw.True == platform.window.GetAttrib(glfw.Visible)
- }
|