From 9a7a73b94e8237c74598448290629bedf56a436e Mon Sep 17 00:00:00 2001
From: faiface <faiface@ksp.sk>
Date: Wed, 21 Dec 2016 20:18:38 +0100
Subject: [PATCH] add input handling methods (keyboard, mouse)

---
 input.go  | 229 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 window.go |  34 +++++++-
 2 files changed, 260 insertions(+), 3 deletions(-)
 create mode 100644 input.go

diff --git a/input.go b/input.go
new file mode 100644
index 0000000..4a9be53
--- /dev/null
+++ b/input.go
@@ -0,0 +1,229 @@
+package pixel
+
+import (
+	"github.com/faiface/pixel/pixelgl"
+	"github.com/go-gl/glfw/v3.2/glfw"
+)
+
+// Pressed returns whether a button is currently pressed down.
+func (w *Window) Pressed(button Button) bool {
+	return w.currInp.buttons[button]
+}
+
+// JustPressed returns whether a button has just been pressed down.
+func (w *Window) JustPressed(button Button) bool {
+	return w.currInp.buttons[button] && !w.prevInp.buttons[button]
+}
+
+// JustReleased returns whether a button has just been released up.
+func (w *Window) JustReleased(button Button) bool {
+	return !w.currInp.buttons[button] && w.prevInp.buttons[button]
+}
+
+// MousePosition returns the current mouse position relative to the window.
+func (w *Window) MousePosition() Vec {
+	var x, y, width, height float64
+	pixelgl.Do(func() {
+		x, y = w.window.GetCursorPos()
+		wi, hi := w.window.GetSize()
+		width, height = float64(wi), float64(hi)
+	})
+
+	// transform to OpenGL coordinates
+	x = (x - width/2) / (width / 2)
+	y = (height/2 - y) / (height / 2)
+
+	return V(x, y)
+}
+
+// MouseScroll returns the scroll amount (in both axis) since the last call to Window.Update.
+func (w *Window) MouseScroll() Vec {
+	return w.currInp.scroll
+}
+
+// Button is a keyboard or mouse button. Why distinguish?
+type Button int
+
+// List of all mouse buttons.
+const (
+	MouseButton1      = Button(glfw.MouseButton1)
+	MouseButton2      = Button(glfw.MouseButton2)
+	MouseButton3      = Button(glfw.MouseButton3)
+	MouseButton4      = Button(glfw.MouseButton4)
+	MouseButton5      = Button(glfw.MouseButton5)
+	MouseButton6      = Button(glfw.MouseButton6)
+	MouseButton7      = Button(glfw.MouseButton7)
+	MouseButton8      = Button(glfw.MouseButton8)
+	MouseButtonLast   = Button(glfw.MouseButtonLast)
+	MouseButtonLeft   = Button(glfw.MouseButtonLeft)
+	MouseButtonRight  = Button(glfw.MouseButtonRight)
+	MouseButtonMiddle = Button(glfw.MouseButtonMiddle)
+)
+
+// List of all keyboard buttons.
+const (
+	KeyUnknown      = Button(glfw.KeyUnknown)
+	KeySpace        = Button(glfw.KeySpace)
+	KeyApostrophe   = Button(glfw.KeyApostrophe)
+	KeyComma        = Button(glfw.KeyComma)
+	KeyMinus        = Button(glfw.KeyMinus)
+	KeyPeriod       = Button(glfw.KeyPeriod)
+	KeySlash        = Button(glfw.KeySlash)
+	Key0            = Button(glfw.Key0)
+	Key1            = Button(glfw.Key1)
+	Key2            = Button(glfw.Key2)
+	Key3            = Button(glfw.Key3)
+	Key4            = Button(glfw.Key4)
+	Key5            = Button(glfw.Key5)
+	Key6            = Button(glfw.Key6)
+	Key7            = Button(glfw.Key7)
+	Key8            = Button(glfw.Key8)
+	Key9            = Button(glfw.Key9)
+	KeySemicolon    = Button(glfw.KeySemicolon)
+	KeyEqual        = Button(glfw.KeyEqual)
+	KeyA            = Button(glfw.KeyA)
+	KeyB            = Button(glfw.KeyB)
+	KeyC            = Button(glfw.KeyC)
+	KeyD            = Button(glfw.KeyD)
+	KeyE            = Button(glfw.KeyE)
+	KeyF            = Button(glfw.KeyF)
+	KeyG            = Button(glfw.KeyG)
+	KeyH            = Button(glfw.KeyH)
+	KeyI            = Button(glfw.KeyI)
+	KeyJ            = Button(glfw.KeyJ)
+	KeyK            = Button(glfw.KeyK)
+	KeyL            = Button(glfw.KeyL)
+	KeyM            = Button(glfw.KeyM)
+	KeyN            = Button(glfw.KeyN)
+	KeyO            = Button(glfw.KeyO)
+	KeyP            = Button(glfw.KeyP)
+	KeyQ            = Button(glfw.KeyQ)
+	KeyR            = Button(glfw.KeyR)
+	KeyS            = Button(glfw.KeyS)
+	KeyT            = Button(glfw.KeyT)
+	KeyU            = Button(glfw.KeyU)
+	KeyV            = Button(glfw.KeyV)
+	KeyW            = Button(glfw.KeyW)
+	KeyX            = Button(glfw.KeyX)
+	KeyY            = Button(glfw.KeyY)
+	KeyZ            = Button(glfw.KeyZ)
+	KeyLeftBracket  = Button(glfw.KeyLeftBracket)
+	KeyBackslash    = Button(glfw.KeyBackslash)
+	KeyRightBracket = Button(glfw.KeyRightBracket)
+	KeyGraveAccent  = Button(glfw.KeyGraveAccent)
+	KeyWorld1       = Button(glfw.KeyWorld1)
+	KeyWorld2       = Button(glfw.KeyWorld2)
+	KeyEscape       = Button(glfw.KeyEscape)
+	KeyEnter        = Button(glfw.KeyEnter)
+	KeyTab          = Button(glfw.KeyTab)
+	KeyBackspace    = Button(glfw.KeyBackspace)
+	KeyInsert       = Button(glfw.KeyInsert)
+	KeyDelete       = Button(glfw.KeyDelete)
+	KeyRight        = Button(glfw.KeyRight)
+	KeyLeft         = Button(glfw.KeyLeft)
+	KeyDown         = Button(glfw.KeyDown)
+	KeyUp           = Button(glfw.KeyUp)
+	KeyPageUp       = Button(glfw.KeyPageUp)
+	KeyPageDown     = Button(glfw.KeyPageDown)
+	KeyHome         = Button(glfw.KeyHome)
+	KeyEnd          = Button(glfw.KeyEnd)
+	KeyCapsLock     = Button(glfw.KeyCapsLock)
+	KeyScrollLock   = Button(glfw.KeyScrollLock)
+	KeyNumLock      = Button(glfw.KeyNumLock)
+	KeyPrintScreen  = Button(glfw.KeyPrintScreen)
+	KeyPause        = Button(glfw.KeyPause)
+	KeyF1           = Button(glfw.KeyF1)
+	KeyF2           = Button(glfw.KeyF2)
+	KeyF3           = Button(glfw.KeyF3)
+	KeyF4           = Button(glfw.KeyF4)
+	KeyF5           = Button(glfw.KeyF5)
+	KeyF6           = Button(glfw.KeyF6)
+	KeyF7           = Button(glfw.KeyF7)
+	KeyF8           = Button(glfw.KeyF8)
+	KeyF9           = Button(glfw.KeyF9)
+	KeyF10          = Button(glfw.KeyF10)
+	KeyF11          = Button(glfw.KeyF11)
+	KeyF12          = Button(glfw.KeyF12)
+	KeyF13          = Button(glfw.KeyF13)
+	KeyF14          = Button(glfw.KeyF14)
+	KeyF15          = Button(glfw.KeyF15)
+	KeyF16          = Button(glfw.KeyF16)
+	KeyF17          = Button(glfw.KeyF17)
+	KeyF18          = Button(glfw.KeyF18)
+	KeyF19          = Button(glfw.KeyF19)
+	KeyF20          = Button(glfw.KeyF20)
+	KeyF21          = Button(glfw.KeyF21)
+	KeyF22          = Button(glfw.KeyF22)
+	KeyF23          = Button(glfw.KeyF23)
+	KeyF24          = Button(glfw.KeyF24)
+	KeyF25          = Button(glfw.KeyF25)
+	KeyKP0          = Button(glfw.KeyKP0)
+	KeyKP1          = Button(glfw.KeyKP1)
+	KeyKP2          = Button(glfw.KeyKP2)
+	KeyKP3          = Button(glfw.KeyKP3)
+	KeyKP4          = Button(glfw.KeyKP4)
+	KeyKP5          = Button(glfw.KeyKP5)
+	KeyKP6          = Button(glfw.KeyKP6)
+	KeyKP7          = Button(glfw.KeyKP7)
+	KeyKP8          = Button(glfw.KeyKP8)
+	KeyKP9          = Button(glfw.KeyKP9)
+	KeyKPDecimal    = Button(glfw.KeyKPDecimal)
+	KeyKPDivide     = Button(glfw.KeyKPDivide)
+	KeyKPMultiply   = Button(glfw.KeyKPMultiply)
+	KeyKPSubtract   = Button(glfw.KeyKPSubtract)
+	KeyKPAdd        = Button(glfw.KeyKPAdd)
+	KeyKPEnter      = Button(glfw.KeyKPEnter)
+	KeyKPEqual      = Button(glfw.KeyKPEqual)
+	KeyLeftShift    = Button(glfw.KeyLeftShift)
+	KeyLeftControl  = Button(glfw.KeyLeftControl)
+	KeyLeftAlt      = Button(glfw.KeyLeftAlt)
+	KeyLeftSuper    = Button(glfw.KeyLeftSuper)
+	KeyRightShift   = Button(glfw.KeyRightShift)
+	KeyRightControl = Button(glfw.KeyRightControl)
+	KeyRightAlt     = Button(glfw.KeyRightAlt)
+	KeyRightSuper   = Button(glfw.KeyRightSuper)
+	KeyMenu         = Button(glfw.KeyMenu)
+	KeyLast         = Button(glfw.KeyLast)
+)
+
+func (w *Window) initInput() {
+	pixelgl.Do(func() {
+		w.window.SetMouseButtonCallback(func(_ *glfw.Window, button glfw.MouseButton, action glfw.Action, mod glfw.ModifierKey) {
+			switch action {
+			case glfw.Press:
+				w.currInp.buttons[Button(button)] = true
+			case glfw.Release:
+				w.currInp.buttons[Button(button)] = false
+			}
+		})
+
+		w.window.SetKeyCallback(func(_ *glfw.Window, key glfw.Key, scancode int, action glfw.Action, mods glfw.ModifierKey) {
+			switch action {
+			case glfw.Press:
+				w.currInp.buttons[Button(key)] = true
+			case glfw.Release:
+				w.currInp.buttons[Button(key)] = false
+			}
+		})
+
+		w.window.SetScrollCallback(func(_ *glfw.Window, xoff, yoff float64) {
+			w.currInp.scroll += V(xoff, yoff)
+		})
+	})
+}
+
+func (w *Window) updateInput() {
+	// copy temp to prev
+	w.prevInp = w.tempInp
+
+	// zero current scroll (but keep what was added in callbacks outside of this function)
+	w.currInp.scroll -= w.tempInp.scroll
+
+	// get events (usually calls callbacks, but callbacks can be called outside too)
+	pixelgl.Do(func() {
+		glfw.PollEvents()
+	})
+
+	// cache current state to temp (so that if there are callbacks outside this function, everything works)
+	w.tempInp = w.currInp
+}
diff --git a/window.go b/window.go
index 90b90f7..c800193 100644
--- a/window.go
+++ b/window.go
@@ -65,6 +65,11 @@ type Window struct {
 	restore struct {
 		xpos, ypos, width, height int
 	}
+
+	prevInp, tempInp, currInp struct {
+		buttons [KeyLast + 1]bool
+		scroll  Vec
+	}
 }
 
 // NewWindow creates a new window with it's properties specified in the provided config.
@@ -107,6 +112,8 @@ func NewWindow(config WindowConfig) (*Window, error) {
 		return nil, errors.Wrap(err, "creating window failed")
 	}
 
+	w.initInput()
+
 	w.SetFullscreen(config.Fullscreen)
 
 	w.Do(func(pixelgl.Context) {
@@ -157,8 +164,11 @@ func (w *Window) Update() {
 				glfw.SwapInterval(1)
 			}
 			w.window.SwapBuffers()
-			glfw.PollEvents()
+		})
 
+		w.updateInput()
+
+		pixelgl.Do(func() {
 			w, h := w.window.GetSize()
 			gl.Viewport(0, 0, int32(w), int32(h))
 		})
@@ -170,6 +180,25 @@ func (w *Window) DefaultShader() *pixelgl.Shader {
 	return w.defaultShader
 }
 
+// SetClosed sets the closed flag of a window.
+//
+// This is usefull when overriding the user's attempt to close a window, or just to close a window
+// from within a program.
+func (w *Window) SetClosed(closed bool) {
+	pixelgl.Do(func() {
+		w.window.SetShouldClose(closed)
+	})
+}
+
+// Closed returns the closed flag of a window, which reports whether the window should be closed.
+//
+// The closed flag is automatically set when a user attempts to close a window.
+func (w *Window) Closed() bool {
+	return pixelgl.DoVal(func() interface{} {
+		return w.window.ShouldClose()
+	}).(bool)
+}
+
 // SetTitle changes the title of a window.
 func (w *Window) SetTitle(title string) {
 	pixelgl.Do(func() {
@@ -273,10 +302,9 @@ func (w *Window) Focus() {
 
 // Focused returns true if a window has input focus.
 func (w *Window) Focused() bool {
-	focused := pixelgl.DoVal(func() interface{} {
+	return pixelgl.DoVal(func() interface{} {
 		return w.window.GetAttrib(glfw.Focused) == glfw.True
 	}).(bool)
-	return focused
 }
 
 // Maximize puts a windowed window to a maximized state.
-- 
GitLab