--- title: Screen Information description: Get information about displays and monitors sidebar: order: 1 --- import { Card, CardGrid } from "@astrojs/starlight/components"; ## 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. ## Quick Start ```go // 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. ## Getting Screen Information ### All Screens ```go 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 Screen ```go 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) ``` ### Current Screen Get the screen containing a window: ```go screen := app.Screens.GetCurrent(window) fmt.Printf("Window is on: %s\n", screen.Name) ``` ### Screen by ID ```go screen := app.Screens.GetByID("screen-id") if screen != nil { fmt.Printf("Found screen: %s\n", screen.Name) } ``` ## Screen Properties ### Screen Structure ```go 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? } ``` ### Physical vs Logical Pixels ```go 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 ## Window Positioning ### Centre on Screen ```go 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) } ``` ### Position on Specific Screen ```go 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) } ``` ### Position Relative to Screen ```go // 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, ) } ``` ## Multi-Monitor Support ### Detect Multiple Monitors ```go func hasMultipleMonitors() bool { return len(app.Screen.GetAll()) > 1 } func getMonitorCount() int { return len(app.Screen.GetAll()) } ``` ### List All Monitors ```go 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) } } ``` ### Choose Monitor ```go 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 } ``` ## Complete Examples ### Multi-Monitor Window Manager ```go 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) } } ``` ### Screen Change Detection ```go 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) } } } ``` ### DPI-Aware Window Sizing ```go 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 } ``` ### Screen Layout Visualiser ```go 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() } ``` ## Best Practices ### ✅ Do - **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 - **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 ## Platform Differences ### macOS - Retina displays (2x scale factor) - Multiple displays common - Coordinate system: (0,0) at bottom-left - Spaces (virtual desktops) affect positioning ### Windows - Various DPI scaling (100%, 125%, 150%, 200%) - Multiple displays common - Coordinate system: (0,0) at top-left - Per-monitor DPI awareness ### Linux - Varies by desktop environment - X11 vs Wayland differences - DPI scaling support varies - Multiple displays supported ## Next Steps Learn about window management. [Learn More →](/features/windows/basics) Configure window appearance. [Learn More →](/features/windows/options) Multi-window patterns. [Learn More →](/features/windows/multiple) Call Go functions from JavaScript. [Learn More →](/features/bindings/methods) --- **Questions?** Ask in [Discord](https://discord.gg/JDdSxwjhGf) or check the [screen examples](https://github.com/wailsapp/wails/tree/v3-alpha/v3/examples).