Skip to content

Screen Information

Wails provides a unified screen API that works across all platforms. Get screen information, detect multiple monitors, query screen properties (size, position, DPI), identify the primary display, and handle DPI scaling with consistent code.

// Get all screens
screens := app.Screen.GetAll()
for _, screen := range screens {
fmt.Printf("Screen: %s (%dx%d)\n",
screen.Name, screen.Width, screen.Height)
}
// Get primary screen
primary := app.Screens.GetPrimary()
fmt.Printf("Primary: %s\n", primary.Name)

That’s it! Cross-platform screen information.

screens := app.Screen.GetAll()
for _, screen := range screens {
fmt.Printf("ID: %s\n", screen.ID)
fmt.Printf("Name: %s\n", screen.Name)
fmt.Printf("Size: %dx%d\n", screen.Width, screen.Height)
fmt.Printf("Position: %d,%d\n", screen.X, screen.Y)
fmt.Printf("Scale: %.2f\n", screen.ScaleFactor)
fmt.Printf("Primary: %v\n", screen.IsPrimary)
fmt.Println("---")
}
primary := app.Screens.GetPrimary()
fmt.Printf("Primary screen: %s\n", primary.Name)
fmt.Printf("Resolution: %dx%d\n", primary.Width, primary.Height)
fmt.Printf("Scale factor: %.2f\n", primary.ScaleFactor)

Get the screen containing a window:

screen := app.Screens.GetCurrent(window)
fmt.Printf("Window is on: %s\n", screen.Name)
screen := app.Screens.GetByID("screen-id")
if screen != nil {
fmt.Printf("Found screen: %s\n", screen.Name)
}
type Screen struct {
ID string // Unique identifier
Name string // Display name
X int // X position
Y int // Y position
Width int // Width in pixels
Height int // Height in pixels
ScaleFactor float32 // DPI scale (1.0, 1.5, 2.0, etc.)
IsPrimary bool // Is this the primary screen?
}
screen := app.Screens.GetPrimary()
// Logical pixels (what you use)
logicalWidth := screen.Width
logicalHeight := screen.Height
// Physical pixels (actual display)
physicalWidth := int(float32(screen.Width) * screen.ScaleFactor)
physicalHeight := int(float32(screen.Height) * screen.ScaleFactor)
fmt.Printf("Logical: %dx%d\n", logicalWidth, logicalHeight)
fmt.Printf("Physical: %dx%d\n", physicalWidth, physicalHeight)
fmt.Printf("Scale: %.2f\n", screen.ScaleFactor)

Common scale factors:

  • 1.0 - Standard DPI (96 DPI)
  • 1.25 - 125% scaling (120 DPI)
  • 1.5 - 150% scaling (144 DPI)
  • 2.0 - 200% scaling (192 DPI) - Retina
  • 3.0 - 300% scaling (288 DPI) - 4K/5K
func centreOnScreen(window *application.WebviewWindow, screen *Screen) {
windowWidth, windowHeight := window.Size()
x := screen.X + (screen.Width-windowWidth)/2
y := screen.Y + (screen.Height-windowHeight)/2
window.SetPosition(x, y)
}
func moveToScreen(window *application.WebviewWindow, screenIndex int) {
screens := app.Screen.GetAll()
if screenIndex < 0 || screenIndex >= len(screens) {
return
}
screen := screens[screenIndex]
// Centre on target screen
centreOnScreen(window, screen)
}
// Top-left corner
func positionTopLeft(window *application.WebviewWindow, screen *Screen) {
window.SetPosition(screen.X+10, screen.Y+10)
}
// Top-right corner
func positionTopRight(window *application.WebviewWindow, screen *Screen) {
windowWidth, _ := window.Size()
window.SetPosition(screen.X+screen.Width-windowWidth-10, screen.Y+10)
}
// Bottom-right corner
func positionBottomRight(window *application.WebviewWindow, screen *Screen) {
windowWidth, windowHeight := window.Size()
window.SetPosition(
screen.X+screen.Width-windowWidth-10,
screen.Y+screen.Height-windowHeight-10,
)
}
func hasMultipleMonitors() bool {
return len(app.Screen.GetAll()) > 1
}
func getMonitorCount() int {
return len(app.Screen.GetAll())
}
func listMonitors() {
screens := app.Screen.GetAll()
fmt.Printf("Found %d monitor(s):\n", len(screens))
for i, screen := range screens {
primary := ""
if screen.IsPrimary {
primary = " (Primary)"
}
fmt.Printf("%d. %s%s\n", i+1, screen.Name, primary)
fmt.Printf(" Resolution: %dx%d\n", screen.Width, screen.Height)
fmt.Printf(" Position: %d,%d\n", screen.X, screen.Y)
fmt.Printf(" Scale: %.2fx\n", screen.ScaleFactor)
}
}
func chooseMonitor() (*Screen, error) {
screens := app.Screen.GetAll()
if len(screens) == 1 {
return screens[0], nil
}
// Show dialog to choose
var options []string
for i, screen := range screens {
primary := ""
if screen.IsPrimary {
primary = " (Primary)"
}
options = append(options,
fmt.Sprintf("%d. %s%s - %dx%d",
i+1, screen.Name, primary, screen.Width, screen.Height))
}
// Use dialog to select
// (Implementation depends on your dialog system)
return screens[0], nil
}
type MultiMonitorManager struct {
app *application.Application
windows map[int]*application.WebviewWindow
}
func NewMultiMonitorManager(app *application.Application) *MultiMonitorManager {
return &MultiMonitorManager{
app: app,
windows: make(map[int]*application.WebviewWindow),
}
}
func (m *MultiMonitorManager) CreateWindowOnScreen(screenIndex int) error {
screens := m.app.Screen.GetAll()
if screenIndex < 0 || screenIndex >= len(screens) {
return errors.New("invalid screen index")
}
screen := screens[screenIndex]
// Create window
window := m.app.Window.NewWithOptions(application.WebviewWindowOptions{
Title: fmt.Sprintf("Window on %s", screen.Name),
Width: 800,
Height: 600,
})
// Centre on screen
x := screen.X + (screen.Width-800)/2
y := screen.Y + (screen.Height-600)/2
window.SetPosition(x, y)
window.Show()
m.windows[screenIndex] = window
return nil
}
func (m *MultiMonitorManager) CreateWindowOnEachScreen() {
screens := m.app.Screen.GetAll()
for i := range screens {
m.CreateWindowOnScreen(i)
}
}
type ScreenMonitor struct {
app *application.Application
lastScreens []*Screen
changeHandler func([]*Screen)
}
func NewScreenMonitor(app *application.Application) *ScreenMonitor {
return &ScreenMonitor{
app: app,
lastScreens: app.Screen.GetAll(),
}
}
func (sm *ScreenMonitor) OnScreenChange(handler func([]*Screen)) {
sm.changeHandler = handler
}
func (sm *ScreenMonitor) Start() {
ticker := time.NewTicker(2 * time.Second)
go func() {
for range ticker.C {
sm.checkScreens()
}
}()
}
func (sm *ScreenMonitor) checkScreens() {
current := sm.app.Screen.GetAll()
if len(current) != len(sm.lastScreens) {
sm.lastScreens = current
if sm.changeHandler != nil {
sm.changeHandler(current)
}
}
}
func createDPIAwareWindow(screen *Screen) *application.WebviewWindow {
// Base size at 1.0 scale
baseWidth := 800
baseHeight := 600
// Adjust for DPI
width := int(float32(baseWidth) * screen.ScaleFactor)
height := int(float32(baseHeight) * screen.ScaleFactor)
window := app.Window.NewWithOptions(application.WebviewWindowOptions{
Title: "DPI-Aware Window",
Width: width,
Height: height,
})
// Centre on screen
x := screen.X + (screen.Width-width)/2
y := screen.Y + (screen.Height-height)/2
window.SetPosition(x, y)
return window
}
func visualiseScreenLayout() string {
screens := app.Screen.GetAll()
var layout strings.Builder
layout.WriteString("Screen Layout:\n\n")
for i, screen := range screens {
primary := ""
if screen.IsPrimary {
primary = " [PRIMARY]"
}
layout.WriteString(fmt.Sprintf("Screen %d: %s%s\n", i+1, screen.Name, primary))
layout.WriteString(fmt.Sprintf(" Position: (%d, %d)\n", screen.X, screen.Y))
layout.WriteString(fmt.Sprintf(" Size: %dx%d\n", screen.Width, screen.Height))
layout.WriteString(fmt.Sprintf(" Scale: %.2fx\n", screen.ScaleFactor))
layout.WriteString(fmt.Sprintf(" Physical: %dx%d\n",
int(float32(screen.Width)*screen.ScaleFactor),
int(float32(screen.Height)*screen.ScaleFactor)))
layout.WriteString("\n")
}
return layout.String()
}
  • Check screen count - Handle single and multiple monitors
  • Use logical pixels - Wails handles DPI automatically
  • Centre windows - Better UX than fixed positions
  • Validate positions - Ensure windows are visible
  • Handle screen changes - Monitors can be added/removed
  • Test on different DPI - 100%, 125%, 150%, 200%
  • Don’t hardcode positions - Use screen dimensions
  • Don’t assume primary screen - User might have multiple
  • Don’t ignore scale factor - Important for DPI awareness
  • Don’t position off-screen - Validate coordinates
  • Don’t forget screen changes - Laptops dock/undock
  • Don’t use physical pixels - Use logical pixels
  • Retina displays (2x scale factor)
  • Multiple displays common
  • Coordinate system: (0,0) at bottom-left
  • Spaces (virtual desktops) affect positioning
  • Various DPI scaling (100%, 125%, 150%, 200%)
  • Multiple displays common
  • Coordinate system: (0,0) at top-left
  • Per-monitor DPI awareness
  • Varies by desktop environment
  • X11 vs Wayland differences
  • DPI scaling support varies
  • Multiple displays supported

Questions? Ask in Discord or check the screen examples.