Skip to content

Commit

Permalink
ebiten: Add {Set,}WindowSizeLimits
Browse files Browse the repository at this point in the history
Closes #1385
  • Loading branch information
hajimehoshi committed Apr 16, 2021
1 parent 5255a54 commit 9b6ba5e
Show file tree
Hide file tree
Showing 7 changed files with 170 additions and 10 deletions.
22 changes: 21 additions & 1 deletion examples/windowsize/main.go
Expand Up @@ -25,6 +25,7 @@ import (
"log"
"math"
"math/rand"
"regexp"
"strconv"
"strings"
"time"
Expand All @@ -45,6 +46,8 @@ var (
flagMaximize = flag.Bool("maximize", false, "maximize the window")
flagVsync = flag.Bool("vsync", true, "enable vsync")
flagInitFocused = flag.Bool("initfocused", true, "whether the window is focused on start")
flagMinWindowSize = flag.String("minwindowsize", "", "minimum window size (e.g., 100x200)")
flagMaxWindowSize = flag.String("maxwindowsize", "", "maximium window size (e.g., 1920x1080)")
)

func init() {
Expand Down Expand Up @@ -280,6 +283,7 @@ func (g *game) Draw(screen *ebiten.Image) {
screen.DrawImage(gophersImage, op)

wx, wy := ebiten.WindowPosition()
minw, minh, maxw, maxh := ebiten.WindowSizeLimits()
cx, cy := ebiten.CursorPosition()
tpsStr := "Uncapped"
if t := ebiten.MaxTPS(); t != ebiten.UncappedTPS {
Expand Down Expand Up @@ -319,10 +323,11 @@ func (g *game) Draw(screen *ebiten.Image) {
%s
IsFocused?: %s
Windows Position: (%d, %d)
Window size limitation: (%d, %d) - (%d, %d)
Cursor: (%d, %d)
TPS: Current: %0.2f / Max: %s
FPS: %0.2f
Device Scale Factor: %0.2f`, msgM, msgR, fg, wx, wy, cx, cy, ebiten.CurrentTPS(), tpsStr, ebiten.CurrentFPS(), ebiten.DeviceScaleFactor())
Device Scale Factor: %0.2f`, msgM, msgR, fg, wx, wy, minw, minh, maxw, maxh, cx, cy, ebiten.CurrentTPS(), tpsStr, ebiten.CurrentFPS(), ebiten.DeviceScaleFactor())
ebitenutil.DebugPrint(screen, msg)
}

Expand Down Expand Up @@ -400,6 +405,21 @@ func main() {
ebiten.SetRunnableOnUnfocused(true)
}

minw, minh, maxw, maxh := -1, -1, -1, -1
reSize := regexp.MustCompile(`^(\d+)x(\d+)$`)
if m := reSize.FindStringSubmatch(*flagMinWindowSize); m != nil {
minw, _ = strconv.Atoi(m[1])
minh, _ = strconv.Atoi(m[2])
}
if m := reSize.FindStringSubmatch(*flagMaxWindowSize); m != nil {
maxw, _ = strconv.Atoi(m[1])
maxh, _ = strconv.Atoi(m[2])
}
if minw >= 0 && minh >= 0 && maxw >= 0 && maxh >= 0 {
ebiten.SetWindowSizeLimits(minw, minh, maxw, maxh)
ebiten.SetWindowResizable(true)
}

const title = "Window Size (Ebiten Demo)"
ww := int(float64(g.width) * initScreenScale)
wh := int(float64(g.height) * initScreenScale)
Expand Down
2 changes: 2 additions & 0 deletions internal/driver/ui.go
Expand Up @@ -78,6 +78,8 @@ type Window interface {

Size() (int, int)
SetSize(width, height int)
SizeLimits() (minw, minh, maxw, maxh int)
SetSizeLimits(minw, minh, maxw, maxh int)

IsFloating() bool
SetFloating(floating bool)
Expand Down
5 changes: 3 additions & 2 deletions internal/glfw/const.go
Expand Up @@ -34,8 +34,9 @@ type (
)

const (
False = 0
True = 1
DontCare = -1
False = 0
True = 1
)

const (
Expand Down
12 changes: 8 additions & 4 deletions internal/glfw/glfw_notwindows.go
Expand Up @@ -109,10 +109,6 @@ func (w *Window) GetAttrib(attrib Hint) int {
return w.w.GetAttrib(glfw.Hint(attrib))
}

func (w *Window) SetAttrib(attrib Hint, value int) {
w.w.SetAttrib(glfw.Hint(attrib), value)
}

func (w *Window) GetCursorPos() (x, y float64) {
return w.w.GetCursorPos()
}
Expand Down Expand Up @@ -161,6 +157,10 @@ func (w *Window) Restore() {
w.w.Restore()
}

func (w *Window) SetAttrib(attrib Hint, value int) {
w.w.SetAttrib(glfw.Hint(attrib), value)
}

func (w *Window) SetCharModsCallback(cbfun CharModsCallback) (previous CharModsCallback) {
var gcb glfw.CharModsCallback
if cbfun != nil {
Expand Down Expand Up @@ -215,6 +215,10 @@ func (w *Window) SetSizeCallback(cbfun SizeCallback) (previous SizeCallback) {
return prev
}

func (w *Window) SetSizeLimits(minw, minh, maxw, maxh int) {
w.w.SetSizeLimits(minw, minh, maxw, maxh)
}

func (w *Window) SetIcon(images []image.Image) {
w.w.SetIcon(images)
}
Expand Down
72 changes: 70 additions & 2 deletions internal/uidriver/glfw/ui.go
Expand Up @@ -54,8 +54,12 @@ type UserInterface struct {

// windowWidth and windowHeight represents a window size.
// The unit is device-dependent pixels.
windowWidth int
windowHeight int
windowWidth int
windowHeight int
minWindowWidth int
minWindowHeight int
maxWindowWidth int
maxWindowHeight int

running uint32
toChangeSize bool
Expand Down Expand Up @@ -110,6 +114,10 @@ const (
var (
theUI = &UserInterface{
runnableOnUnfocused: true,
minWindowWidth: glfw.DontCare,
minWindowHeight: glfw.DontCare,
maxWindowWidth: glfw.DontCare,
maxWindowHeight: glfw.DontCare,
origPosX: invalidPos,
origPosY: invalidPos,
initVsync: true,
Expand Down Expand Up @@ -235,6 +243,25 @@ func (u *UserInterface) setRunning(running bool) {
}
}

func (u *UserInterface) getWindowSizeLimits() (minw, minh, maxw, maxh int) {
u.m.RLock()
defer u.m.RUnlock()
return u.minWindowWidth, u.minWindowHeight, u.maxWindowWidth, u.maxWindowHeight
}

func (u *UserInterface) setWindowSizeLimits(minw, minh, maxw, maxh int) bool {
u.m.RLock()
defer u.m.RUnlock()
if u.minWindowWidth == minw && u.minWindowHeight == minh && u.maxWindowWidth == maxw && u.maxWindowHeight == maxh {
return false
}
u.minWindowWidth = minw
u.minWindowHeight = minh
u.maxWindowWidth = maxw
u.maxWindowHeight = maxh
return true
}

func (u *UserInterface) getInitTitle() string {
u.m.RLock()
v := u.initTitle
Expand Down Expand Up @@ -782,6 +809,8 @@ func (u *UserInterface) init() error {
setPosition()
}

u.updateWindowSizeLimits()

// Maximizing a window requires a proper size and position. Call Maximize here (#1117).
if u.isInitWindowMaximized() {
u.window.Maximize()
Expand Down Expand Up @@ -973,8 +1002,45 @@ func (u *UserInterface) swapBuffers() {
}
}

// updateWindowSizeLimits must be called from the main thread.
func (u *UserInterface) updateWindowSizeLimits() {
minw, minh, maxw, maxh := u.getWindowSizeLimits()
if minw < 0 {
minw = glfw.DontCare
}
if minh < 0 {
minh = glfw.DontCare
}
if maxw < 0 {
maxw = glfw.DontCare
}
if maxh < 0 {
maxh = glfw.DontCare
}
u.window.SetSizeLimits(minw, minh, maxw, maxh)
}

func (u *UserInterface) adjustWindowSizeBasedOnSizeLimits(width, height int) (int, int) {
minw, minh, maxw, maxh := u.getWindowSizeLimits()
if minw >= 0 && width < minw {
width = minw
}
if minh >= 0 && height < minh {
height = minh
}
if maxw >= 0 && width > maxw {
width = maxw
}
if maxh >= 0 && height > maxh {
height = maxh
}
return width, height
}

// setWindowSize must be called from the main thread.
func (u *UserInterface) setWindowSize(width, height int, fullscreen bool) {
width, height = u.adjustWindowSizeBasedOnSizeLimits(width, height)

if u.windowWidth == width && u.windowHeight == height && u.isFullscreen() == fullscreen && u.lastDeviceScaleFactor == u.deviceScaleFactor() {
return
}
Expand Down Expand Up @@ -1045,6 +1111,8 @@ func (u *UserInterface) setWindowSize(width, height int, fullscreen bool) {
// TODO: This should return an error.
panic(fmt.Sprintf("glfw: failed to recreate window: %v", err))
}
// Reset the size limits explicitly.
u.updateWindowSizeLimits()
u.window.Show()
windowRecreated = true
}
Expand Down
46 changes: 45 additions & 1 deletion internal/uidriver/glfw/window.go
Expand Up @@ -228,7 +228,8 @@ func (w *window) setPosition(x, y int) {

func (w *window) Size() (int, int) {
if !w.ui.isRunning() {
return w.ui.getInitWindowSize()
ww, wh := w.ui.getInitWindowSize()
return w.ui.adjustWindowSizeBasedOnSizeLimits(ww, wh)
}
ww := int(w.ui.fromGLFWPixel(float64(w.ui.windowWidth)))
wh := int(w.ui.fromGLFWPixel(float64(w.ui.windowHeight)))
Expand All @@ -248,6 +249,49 @@ func (w *window) SetSize(width, height int) {
})
}

func (w *window) SizeLimits() (minw, minh, maxw, maxh int) {
minw, minh, maxw, maxh = w.ui.getWindowSizeLimits()
if minw >= 0 {
minw = int(w.ui.fromGLFWPixel(float64(minw)))
}
if minh >= 0 {
minh = int(w.ui.fromGLFWPixel(float64(minh)))
}
if maxw >= 0 {
maxw = int(w.ui.fromGLFWPixel(float64(maxw)))
}
if maxh >= 0 {
maxh = int(w.ui.fromGLFWPixel(float64(maxh)))
}
return
}

func (w *window) SetSizeLimits(minw, minh, maxw, maxh int) {
if minw >= 0 {
minw = int(w.ui.toGLFWPixel(float64(minw)))
}
if minh >= 0 {
minh = int(w.ui.toGLFWPixel(float64(minh)))
}
if maxw >= 0 {
maxw = int(w.ui.toGLFWPixel(float64(maxw)))
}
if maxh >= 0 {
maxh = int(w.ui.toGLFWPixel(float64(maxh)))
}
if !w.ui.setWindowSizeLimits(minw, minh, maxw, maxh) {
return
}
if !w.ui.isRunning() {
return
}

_ = w.ui.t.Call(func() error {
w.ui.updateWindowSizeLimits()
return nil
})
}

func (w *window) SetIcon(iconImages []image.Image) {
// The icons are actually set at (*UserInterface).loop.
w.ui.setIconImages(iconImages)
Expand Down
21 changes: 21 additions & 0 deletions window.go
Expand Up @@ -235,6 +235,27 @@ func SetWindowSize(width, height int) {
}
}

// WindowSizeLimist returns the limitation of the window size on desktops.
// A negative value indicates the size is not limited.
//
// WindowMaxSize is concurrent-safe.
func WindowSizeLimits() (minw, minh, maxw, maxh int) {
if w := uiDriver().Window(); w != nil {
return w.SizeLimits()
}
return -1, -1, -1, -1
}

// SetWindowSizeLimits sets the limitation of the window size on desktops.
// A negative value indicates the size is not limited.
//
// SetWindowMaxSize is concurrent-safe.
func SetWindowSizeLimits(minw, minh, maxw, maxh int) {
if w := uiDriver().Window(); w != nil {
w.SetSizeLimits(minw, minh, maxw, maxh)
}
}

// IsWindowFloating reports whether the window is always shown above all the other windows.
//
// IsWindowFloating returns false on browsers and mobiles.
Expand Down

0 comments on commit 9b6ba5e

Please sign in to comment.