mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-11-10 04:57:26 +01:00
ebiten: add APIs to treat monitors (#2597)
This change adds these APIs: * `type MonitorType` * `func (*MonitorType) Bounds() image.Rectangle` * `func (*MonitorType) Name() string` * `func Monitor() *MonitorType` * `func SetMonitor(*MonitorType)` * `func AppendMonitors([]*MonitorType) []*MonitorType` Closes #1835
This commit is contained in:
parent
b1b4335423
commit
60b7de6a3c
127
examples/monitor/main.go
Normal file
127
examples/monitor/main.go
Normal file
@ -0,0 +1,127 @@
|
||||
// Copyright 2023 The Ebitengine Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"image/color"
|
||||
"log"
|
||||
|
||||
"golang.org/x/image/font"
|
||||
"golang.org/x/image/font/opentype"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2"
|
||||
"github.com/hajimehoshi/ebiten/v2/examples/resources/fonts"
|
||||
"github.com/hajimehoshi/ebiten/v2/inpututil"
|
||||
"github.com/hajimehoshi/ebiten/v2/text"
|
||||
)
|
||||
|
||||
var (
|
||||
mplusNormalFont font.Face
|
||||
)
|
||||
|
||||
func init() {
|
||||
tt, err := opentype.Parse(fonts.MPlus1pRegular_ttf)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
const dpi = 72
|
||||
mplusNormalFont, err = opentype.NewFace(tt, &opentype.FaceOptions{
|
||||
Size: 24,
|
||||
DPI: dpi,
|
||||
Hinting: font.HintingFull,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
const (
|
||||
screenWidth = 640
|
||||
screenHeight = 480
|
||||
)
|
||||
|
||||
type Game struct {
|
||||
monitors []*ebiten.MonitorType
|
||||
}
|
||||
|
||||
func (g *Game) Update() error {
|
||||
// Refresh monitors.
|
||||
g.monitors = ebiten.AppendMonitors(g.monitors[:0])
|
||||
|
||||
// Handle keypresses.
|
||||
if inpututil.IsKeyJustReleased(ebiten.KeyF) {
|
||||
ebiten.SetFullscreen(!ebiten.IsFullscreen())
|
||||
} else {
|
||||
for i, m := range g.monitors {
|
||||
if inpututil.IsKeyJustPressed(ebiten.KeyDigit0 + ebiten.Key(i)) {
|
||||
ebiten.SetWindowTitle(m.Name())
|
||||
ebiten.SetMonitor(m)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (g *Game) Draw(screen *ebiten.Image) {
|
||||
const x = 0
|
||||
y := 24
|
||||
text.Draw(screen, "F to toggle fullscreen\n0-9 to change monitor", mplusNormalFont, x, y, color.White)
|
||||
y += 72
|
||||
|
||||
for i, m := range g.monitors {
|
||||
text.Draw(screen, fmt.Sprintf("%d: %s %s", i, m.Name(), m.Bounds().String()), mplusNormalFont, x, y, color.White)
|
||||
y += 24
|
||||
}
|
||||
|
||||
activeMonitor := ebiten.Monitor()
|
||||
for i, m := range g.monitors {
|
||||
if m == activeMonitor {
|
||||
text.Draw(screen, fmt.Sprintf("active: %s (%d)", m.Name(), i), mplusNormalFont, x, y, color.White)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
|
||||
return screenWidth, screenHeight
|
||||
}
|
||||
|
||||
func main() {
|
||||
g := &Game{}
|
||||
|
||||
// Allow the user to pass in a monitor flag to target a specific monitor.
|
||||
var monitor int
|
||||
flag.IntVar(&monitor, "monitor", 0, "target monitor index to run the program on")
|
||||
flag.Parse()
|
||||
|
||||
// Read our monitors.
|
||||
g.monitors = ebiten.AppendMonitors(nil)
|
||||
|
||||
// Ensure the user did not supply a monitor index beyond the range of available monitors. If they did, set the monitor to the primary.
|
||||
if monitor < 0 || monitor >= len(g.monitors) {
|
||||
monitor = 0
|
||||
}
|
||||
|
||||
targetMonitor := g.monitors[monitor]
|
||||
ebiten.SetMonitor(targetMonitor)
|
||||
ebiten.SetWindowTitle(targetMonitor.Name())
|
||||
ebiten.SetWindowSize(screenWidth, screenHeight)
|
||||
|
||||
if err := ebiten.RunGame(g); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
@ -94,6 +94,10 @@ func (m *Monitor) GetVideoMode() *VidMode {
|
||||
}
|
||||
}
|
||||
|
||||
func (m *Monitor) GetName() string {
|
||||
return m.m.GetName()
|
||||
}
|
||||
|
||||
type Window struct {
|
||||
w *cglfw.Window
|
||||
|
||||
|
@ -56,6 +56,14 @@ func (m *Monitor) GetVideoMode() *VidMode {
|
||||
return (*VidMode)(v)
|
||||
}
|
||||
|
||||
func (m *Monitor) GetName() string {
|
||||
v, err := (*goglfw.Monitor)(m).GetName()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
type Window goglfw.Window
|
||||
|
||||
func (w *Window) Destroy() {
|
||||
|
@ -95,3 +95,8 @@ type RunOptions struct {
|
||||
ScreenTransparent bool
|
||||
SkipTaskbar bool
|
||||
}
|
||||
|
||||
// InitialWindowPosition returns the position for centering the given second width/height pair within the first width/height pair.
|
||||
func InitialWindowPosition(mw, mh, ww, wh int) (x, y int) {
|
||||
return (mw - ww) / 2, (mh - wh) / 3
|
||||
}
|
||||
|
@ -85,6 +85,7 @@ type userInterfaceImpl struct {
|
||||
initFullscreen bool
|
||||
initCursorMode CursorMode
|
||||
initWindowDecorated bool
|
||||
initWindowMonitor int
|
||||
initWindowPositionXInDIP int
|
||||
initWindowPositionYInDIP int
|
||||
initWindowWidthInDIP int
|
||||
@ -135,6 +136,7 @@ func init() {
|
||||
maxWindowHeightInDIP: glfw.DontCare,
|
||||
initCursorMode: CursorModeVisible,
|
||||
initWindowDecorated: true,
|
||||
initWindowMonitor: glfw.DontCare,
|
||||
initWindowPositionXInDIP: invalidPos,
|
||||
initWindowPositionYInDIP: invalidPos,
|
||||
initWindowWidthInDIP: 640,
|
||||
@ -180,13 +182,7 @@ func initialize() error {
|
||||
return errors.New("ui: no monitor was found at initialize")
|
||||
}
|
||||
|
||||
theUI.initMonitor = m
|
||||
theUI.initDeviceScaleFactor = theUI.deviceScaleFactor(m)
|
||||
// GetVideoMode must be called from the main thread, then call this here and record
|
||||
// initFullscreen{Width,Height}InDIP.
|
||||
v := m.GetVideoMode()
|
||||
theUI.initFullscreenWidthInDIP = int(theUI.dipFromGLFWMonitorPixel(float64(v.Width), m))
|
||||
theUI.initFullscreenHeightInDIP = int(theUI.dipFromGLFWMonitorPixel(float64(v.Height), m))
|
||||
theUI.setInitMonitor(m)
|
||||
|
||||
// Create system cursors. These cursors are destroyed at glfw.Terminate().
|
||||
glfwSystemCursors[CursorShapeDefault] = nil
|
||||
@ -203,12 +199,43 @@ func initialize() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type monitor struct {
|
||||
func (u *userInterfaceImpl) setInitMonitor(m *glfw.Monitor) {
|
||||
u.initMonitor = m
|
||||
u.initDeviceScaleFactor = u.deviceScaleFactor(m)
|
||||
// GetVideoMode must be called from the main thread, then call this here and record
|
||||
// initFullscreen{Width,Height}InDIP.
|
||||
v := m.GetVideoMode()
|
||||
u.initFullscreenWidthInDIP = int(u.dipFromGLFWMonitorPixel(float64(v.Width), m))
|
||||
u.initFullscreenHeightInDIP = int(u.dipFromGLFWMonitorPixel(float64(v.Height), m))
|
||||
}
|
||||
|
||||
// Monitor is a wrapper around glfw.Monitor.
|
||||
type Monitor struct {
|
||||
m *glfw.Monitor
|
||||
vm *glfw.VidMode
|
||||
// Pos of monitor in virtual coords
|
||||
x int
|
||||
y int
|
||||
x int
|
||||
y int
|
||||
width int
|
||||
height int
|
||||
id int
|
||||
name string
|
||||
}
|
||||
|
||||
// Bounds returns the monitor's bounds.
|
||||
func (m *Monitor) Bounds() image.Rectangle {
|
||||
ui := Get()
|
||||
return image.Rect(
|
||||
int(ui.dipFromGLFWMonitorPixel(float64(m.x), m.m)),
|
||||
int(ui.dipFromGLFWMonitorPixel(float64(m.y), m.m)),
|
||||
int(ui.dipFromGLFWMonitorPixel(float64(m.x+m.width), m.m)),
|
||||
int(ui.dipFromGLFWMonitorPixel(float64(m.x+m.height), m.m)),
|
||||
)
|
||||
}
|
||||
|
||||
// Name returns the monitor's name.
|
||||
func (m *Monitor) Name() string {
|
||||
return m.name
|
||||
}
|
||||
|
||||
// monitors is the monitor list cache for desktop glfw compile targets.
|
||||
@ -216,25 +243,63 @@ type monitor struct {
|
||||
// monitor config change event.
|
||||
//
|
||||
// monitors must be manipulated on the main thread.
|
||||
var monitors []*monitor
|
||||
var monitors []*Monitor
|
||||
|
||||
// AppendMonitors appends the current monitors to the passed in mons slice and returns it.
|
||||
func (u *userInterfaceImpl) AppendMonitors(mons []*Monitor) []*Monitor {
|
||||
u.m.RLock()
|
||||
defer u.m.RUnlock()
|
||||
return append(mons, monitors...)
|
||||
}
|
||||
|
||||
// Monitor returns the window's current monitor. Returns nil if there is no current monitor yet.
|
||||
func (u *userInterfaceImpl) Monitor() *Monitor {
|
||||
if !u.isRunning() {
|
||||
return nil
|
||||
}
|
||||
var monitor *Monitor
|
||||
u.mainThread.Call(func() {
|
||||
glfwMonitor := u.currentMonitor()
|
||||
if glfwMonitor == nil {
|
||||
return
|
||||
}
|
||||
for _, m := range monitors {
|
||||
if m.m == glfwMonitor {
|
||||
monitor = m
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
return monitor
|
||||
}
|
||||
|
||||
func updateMonitors() {
|
||||
monitors = nil
|
||||
ms := glfw.GetMonitors()
|
||||
for _, m := range ms {
|
||||
x, y := m.GetPos()
|
||||
monitors = append(monitors, &monitor{
|
||||
m: m,
|
||||
vm: m.GetVideoMode(),
|
||||
x: x,
|
||||
y: y,
|
||||
})
|
||||
for i, m := range ms {
|
||||
monitor := glfwMonitorToMonitor(m)
|
||||
monitor.id = i
|
||||
monitors = append(monitors, &monitor)
|
||||
}
|
||||
clearVideoModeScaleCache()
|
||||
devicescale.ClearCache()
|
||||
}
|
||||
|
||||
func ensureMonitors() []*monitor {
|
||||
func glfwMonitorToMonitor(m *glfw.Monitor) Monitor {
|
||||
x, y := m.GetPos()
|
||||
vm := m.GetVideoMode()
|
||||
return Monitor{
|
||||
m: m,
|
||||
vm: m.GetVideoMode(),
|
||||
x: x,
|
||||
y: y,
|
||||
width: vm.Width,
|
||||
height: vm.Height,
|
||||
name: m.GetName(),
|
||||
}
|
||||
}
|
||||
|
||||
func ensureMonitors() []*Monitor {
|
||||
if len(monitors) == 0 {
|
||||
updateMonitors()
|
||||
}
|
||||
@ -245,7 +310,7 @@ func ensureMonitors() []*monitor {
|
||||
// or returns nil if monitor is not found.
|
||||
//
|
||||
// getMonitorFromPosition must be called on the main thread.
|
||||
func getMonitorFromPosition(wx, wy int) *monitor {
|
||||
func getMonitorFromPosition(wx, wy int) *Monitor {
|
||||
for _, m := range ensureMonitors() {
|
||||
// TODO: Fix incorrectness in the cases of https://github.com/glfw/glfw/issues/1961.
|
||||
// See also internal/devicescale/impl_desktop.go for a maybe better way of doing this.
|
||||
@ -268,6 +333,74 @@ func (u *userInterfaceImpl) setRunning(running bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// setWindowMonitor must be called on the main thread.
|
||||
func (u *userInterfaceImpl) setWindowMonitor(monitor int) {
|
||||
if microsoftgdk.IsXbox() {
|
||||
return
|
||||
}
|
||||
|
||||
u.m.RLock()
|
||||
defer u.m.RUnlock()
|
||||
|
||||
m := monitors[monitor].m
|
||||
|
||||
// Ignore if it is the same monitor.
|
||||
if m == u.window.GetMonitor() {
|
||||
return
|
||||
}
|
||||
|
||||
// We set w, h so it can be set to the original dimensions if fullscreen.
|
||||
w, h := u.window.GetSize()
|
||||
fullscreen := u.isFullscreen()
|
||||
// This is copied from setFullscreen. They should probably use a shared function.
|
||||
if fullscreen {
|
||||
origX, origY := u.origWindowPos()
|
||||
|
||||
w = int(u.dipToGLFWPixel(float64(u.origWindowWidthInDIP), u.currentMonitor()))
|
||||
h = int(u.dipToGLFWPixel(float64(u.origWindowHeightInDIP), u.currentMonitor()))
|
||||
if u.isNativeFullscreenAvailable() {
|
||||
u.setNativeFullscreen(false)
|
||||
// Adjust the window size later (after adjusting the position).
|
||||
} else if !u.isNativeFullscreenAvailable() && u.window.GetMonitor() != nil {
|
||||
u.window.SetMonitor(nil, 0, 0, w, h, 0)
|
||||
}
|
||||
|
||||
// glfw.PollEvents is necessary for macOS to enable (*glfw.Window).SetPos and SetSize (#2296).
|
||||
// This polling causes issues on Linux and Windows when rapidly toggling fullscreen, so we only run it under macOS.
|
||||
if runtime.GOOS == "darwin" {
|
||||
glfw.PollEvents()
|
||||
}
|
||||
|
||||
if origX != invalidPos && origY != invalidPos {
|
||||
u.window.SetPos(origX, origY)
|
||||
// Dirty hack for macOS (#703). Rendering doesn't work correctly with one SetPos, but
|
||||
// work with two or more SetPos.
|
||||
if runtime.GOOS == "darwin" {
|
||||
u.window.SetPos(origX+1, origY)
|
||||
u.window.SetPos(origX, origY)
|
||||
}
|
||||
u.setOrigWindowPos(invalidPos, invalidPos)
|
||||
}
|
||||
}
|
||||
|
||||
x, y := m.GetPos()
|
||||
px, py := InitialWindowPosition(m.GetVideoMode().Width, m.GetVideoMode().Height, w, h)
|
||||
u.window.SetPos(x+px, y+py)
|
||||
|
||||
if fullscreen {
|
||||
if u.isNativeFullscreenAvailable() {
|
||||
u.setNativeFullscreen(fullscreen)
|
||||
} else {
|
||||
v := m.GetVideoMode()
|
||||
u.window.SetMonitor(m, 0, 0, v.Width, v.Height, v.RefreshRate)
|
||||
}
|
||||
|
||||
u.setOrigWindowPos(x, y)
|
||||
|
||||
u.adjustViewSizeAfterFullscreen()
|
||||
}
|
||||
}
|
||||
|
||||
func (u *userInterfaceImpl) getWindowSizeLimitsInDIP() (minw, minh, maxw, maxh int) {
|
||||
if microsoftgdk.IsXbox() {
|
||||
return glfw.DontCare, glfw.DontCare, glfw.DontCare, glfw.DontCare
|
||||
@ -381,6 +514,24 @@ func (u *userInterfaceImpl) setIconImages(iconImages []image.Image) {
|
||||
u.m.Unlock()
|
||||
}
|
||||
|
||||
func (u *userInterfaceImpl) getInitWindowMonitor() int {
|
||||
u.m.RLock()
|
||||
v := u.initWindowMonitor
|
||||
u.m.RUnlock()
|
||||
return v
|
||||
}
|
||||
|
||||
func (u *userInterfaceImpl) setInitWindowMonitor(monitor int) {
|
||||
if microsoftgdk.IsXbox() {
|
||||
return
|
||||
}
|
||||
|
||||
u.m.Lock()
|
||||
defer u.m.Unlock()
|
||||
|
||||
u.initWindowMonitor = monitor
|
||||
}
|
||||
|
||||
func (u *userInterfaceImpl) getInitWindowPositionInDIP() (int, int) {
|
||||
if microsoftgdk.IsXbox() {
|
||||
return 0, 0
|
||||
@ -670,7 +821,7 @@ func init() {
|
||||
// createWindow must be called from the main thread.
|
||||
//
|
||||
// createWindow does not set the position or size so far.
|
||||
func (u *userInterfaceImpl) createWindow(width, height int) error {
|
||||
func (u *userInterfaceImpl) createWindow(width, height int, monitor int) error {
|
||||
if u.window != nil {
|
||||
panic("ui: u.window must not exist at createWindow")
|
||||
}
|
||||
@ -680,7 +831,16 @@ func (u *userInterfaceImpl) createWindow(width, height int) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Set our target monitor if provided. This is required to prevent an initial window flash on the default monitor.
|
||||
if monitor != glfw.DontCare {
|
||||
m := monitors[monitor]
|
||||
x, y := m.m.GetPos()
|
||||
px, py := InitialWindowPosition(m.m.GetVideoMode().Width, m.m.GetVideoMode().Height, width, height)
|
||||
window.SetPos(x+px, y+py)
|
||||
}
|
||||
initializeWindowAfterCreation(window)
|
||||
|
||||
u.window = window
|
||||
|
||||
// Even just after a window creation, FramebufferSize callback might be invoked (#1847).
|
||||
@ -866,10 +1026,17 @@ func (u *userInterfaceImpl) initOnMainThread(options *RunOptions) error {
|
||||
glfw.WindowHint(glfw.Visible, glfw.True)
|
||||
}
|
||||
|
||||
// Get our target monitor.
|
||||
monitor := u.getInitWindowMonitor()
|
||||
|
||||
if monitor != glfw.DontCare {
|
||||
u.setInitMonitor(monitors[monitor].m)
|
||||
}
|
||||
|
||||
ww, wh := u.getInitWindowSizeInDIP()
|
||||
initW := int(u.dipToGLFWPixel(float64(ww), u.initMonitor))
|
||||
initH := int(u.dipToGLFWPixel(float64(wh), u.initMonitor))
|
||||
if err := u.createWindow(initW, initH); err != nil {
|
||||
if err := u.createWindow(initW, initH, monitor); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -1371,11 +1538,6 @@ func (u *userInterfaceImpl) currentMonitor() *glfw.Monitor {
|
||||
//
|
||||
// monitorFromWindow must be called on the main thread.
|
||||
func monitorFromWindow(window *glfw.Window) *glfw.Monitor {
|
||||
// GetMonitor is available only in fullscreen.
|
||||
if m := window.GetMonitor(); m != nil {
|
||||
return m
|
||||
}
|
||||
|
||||
// Getting a monitor from a window position is not reliable in general (e.g., when a window is put across
|
||||
// multiple monitors, or, before SetWindowPosition is called.).
|
||||
// Get the monitor which the current window belongs to. This requires OS API.
|
||||
|
@ -15,6 +15,7 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"image"
|
||||
"sync"
|
||||
"syscall/js"
|
||||
"time"
|
||||
@ -756,6 +757,29 @@ func (u *userInterfaceImpl) Window() Window {
|
||||
return &nullWindow{}
|
||||
}
|
||||
|
||||
type Monitor struct{}
|
||||
|
||||
var theMonitor = &Monitor{}
|
||||
|
||||
func (m *Monitor) Bounds() image.Rectangle {
|
||||
screen := window.Get("screen")
|
||||
w := screen.Get("width").Int()
|
||||
h := screen.Get("height").Int()
|
||||
return image.Rect(0, 0, w, h)
|
||||
}
|
||||
|
||||
func (m *Monitor) Name() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (u *userInterfaceImpl) AppendMonitors(mons []*Monitor) []*Monitor {
|
||||
return append(mons, theMonitor)
|
||||
}
|
||||
|
||||
func (u *userInterfaceImpl) Monitor() *Monitor {
|
||||
return theMonitor
|
||||
}
|
||||
|
||||
func (u *userInterfaceImpl) beginFrame() {
|
||||
}
|
||||
|
||||
|
@ -19,6 +19,7 @@ package ui
|
||||
import (
|
||||
stdcontext "context"
|
||||
"fmt"
|
||||
"image"
|
||||
"runtime/debug"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
@ -428,6 +429,27 @@ func (u *userInterfaceImpl) Window() Window {
|
||||
return &nullWindow{}
|
||||
}
|
||||
|
||||
type Monitor struct{}
|
||||
|
||||
var theMonitor = &Monitor{}
|
||||
|
||||
func (m *Monitor) Bounds() image.Rectangle {
|
||||
// TODO: This should return the available viewport dimensions.
|
||||
return image.Rectangle{}
|
||||
}
|
||||
|
||||
func (m *Monitor) Name() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (u *userInterfaceImpl) AppendMonitors(mons []*Monitor) []*Monitor {
|
||||
return append(mons, theMonitor)
|
||||
}
|
||||
|
||||
func (u *userInterfaceImpl) Monitor() *Monitor {
|
||||
return theMonitor
|
||||
}
|
||||
|
||||
func (u *userInterfaceImpl) UpdateInput(keys map[Key]struct{}, runes []rune, touches []TouchForInput) {
|
||||
u.updateInputState(keys, runes, touches)
|
||||
if u.fpsMode == FPSModeVsyncOffMinimum {
|
||||
|
@ -22,6 +22,7 @@ import "C"
|
||||
|
||||
import (
|
||||
stdcontext "context"
|
||||
"image"
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
@ -193,6 +194,27 @@ func (*userInterfaceImpl) Window() Window {
|
||||
return &nullWindow{}
|
||||
}
|
||||
|
||||
type Monitor struct{}
|
||||
|
||||
var theMonitor = &Monitor{}
|
||||
|
||||
func (m *Monitor) Bounds() image.Rectangle {
|
||||
// TODO: This should return the available viewport dimensions.
|
||||
return image.Rectangle{}
|
||||
}
|
||||
|
||||
func (m *Monitor) Name() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
func (u *userInterfaceImpl) AppendMonitors(mons []*Monitor) []*Monitor {
|
||||
return append(mons, theMonitor)
|
||||
}
|
||||
|
||||
func (u *userInterfaceImpl) Monitor() *Monitor {
|
||||
return theMonitor
|
||||
}
|
||||
|
||||
func (u *userInterfaceImpl) beginFrame() {
|
||||
}
|
||||
|
||||
|
@ -23,6 +23,7 @@ type Window interface {
|
||||
SetDecorated(decorated bool)
|
||||
ResizingMode() WindowResizingMode
|
||||
SetResizingMode(mode WindowResizingMode)
|
||||
SetMonitor(*Monitor)
|
||||
Position() (int, int)
|
||||
SetPosition(x, y int)
|
||||
Size() (int, int)
|
||||
@ -58,6 +59,9 @@ func (*nullWindow) ResizingMode() WindowResizingMode {
|
||||
func (*nullWindow) SetResizingMode(mode WindowResizingMode) {
|
||||
}
|
||||
|
||||
func (*nullWindow) SetMonitor(monitor *Monitor) {
|
||||
}
|
||||
|
||||
func (*nullWindow) Position() (int, int) {
|
||||
return 0, 0
|
||||
}
|
||||
|
@ -159,6 +159,19 @@ func (w *glfwWindow) Restore() {
|
||||
w.ui.mainThread.Call(w.ui.restoreWindow)
|
||||
}
|
||||
|
||||
func (w *glfwWindow) SetMonitor(monitor *Monitor) {
|
||||
if monitor == nil {
|
||||
panic("ui: monitor cannot be nil at SetMonitor")
|
||||
}
|
||||
if !w.ui.isRunning() {
|
||||
w.ui.setInitWindowMonitor(monitor.id)
|
||||
return
|
||||
}
|
||||
w.ui.mainThread.Call(func() {
|
||||
w.ui.setWindowMonitor(monitor.id)
|
||||
})
|
||||
}
|
||||
|
||||
func (w *glfwWindow) Position() (int, int) {
|
||||
if !w.ui.isRunning() {
|
||||
panic("ui: WindowPosition can't be called before the main loop starts")
|
||||
|
60
monitor.go
Normal file
60
monitor.go
Normal file
@ -0,0 +1,60 @@
|
||||
// Copyright 2023 The Ebitengine Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package ebiten
|
||||
|
||||
import (
|
||||
"image"
|
||||
|
||||
"github.com/hajimehoshi/ebiten/v2/internal/ui"
|
||||
)
|
||||
|
||||
// MonitorType represents a monitor available to the system.
|
||||
type MonitorType ui.Monitor
|
||||
|
||||
// Bounds returns the position and size of the monitor in device-independent pixels.
|
||||
func (m *MonitorType) Bounds() image.Rectangle {
|
||||
return (*ui.Monitor)(m).Bounds()
|
||||
}
|
||||
|
||||
// Name returns the monitor's name. On Linux, this reports the monitors in xrandr format.
|
||||
// On Windows, this reports "Generic PnP Monitor" for all monitors.
|
||||
func (m *MonitorType) Name() string {
|
||||
return (*ui.Monitor)(m).Name()
|
||||
}
|
||||
|
||||
// Monitor returns the current monitor.
|
||||
func Monitor() *MonitorType {
|
||||
m := ui.Get().Monitor()
|
||||
if m == nil {
|
||||
return nil
|
||||
}
|
||||
return (*MonitorType)(m)
|
||||
}
|
||||
|
||||
// SetMonitor sets the monitor that the window should be on. This can be called before or after Run.
|
||||
func SetMonitor(monitor *MonitorType) {
|
||||
ui.Get().Window().SetMonitor((*ui.Monitor)(monitor))
|
||||
}
|
||||
|
||||
// AppendMonitors returns the monitors reported by the system.
|
||||
// On desktop platforms, there will always be at least one monitor appended and the first monitor in the slice will be the primary monitor.
|
||||
// Any monitors added or removed will show up with subsequent calls to this function.
|
||||
func AppendMonitors(monitors []*MonitorType) []*MonitorType {
|
||||
// TODO: This is not an efficient operation. It would be best if we could directly pass monitors directly into `ui.AppendMonitors`.
|
||||
for _, m := range ui.Get().AppendMonitors(nil) {
|
||||
monitors = append(monitors, (*MonitorType)(m))
|
||||
}
|
||||
return monitors
|
||||
}
|
@ -166,8 +166,7 @@ var (
|
||||
func initializeWindowPositionIfNeeded(width, height int) {
|
||||
if atomic.LoadUint32(&windowPositionSetExplicitly) == 0 {
|
||||
sw, sh := ui.Get().ScreenSizeInFullscreen()
|
||||
x := (sw - width) / 2
|
||||
y := (sh - height) / 3
|
||||
x, y := ui.InitialWindowPosition(sw, sh, width, height)
|
||||
ui.Get().Window().SetPosition(x, y)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user