Instantly share code, notes, and snippets.
Created
December 29, 2025 22:14
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
-
Save jstorrs/572ce2dcc9dc0ca37194424aa32c17a3 to your computer and use it in GitHub Desktop.
SensaVue Tester
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| """ | |
| Multi-monitor full-screen "color + centered text" display. | |
| - Fills ALL attached displays (spans a single borderless window across them) with a solid color. | |
| - Shows a large black "+" centered (across the whole combined desktop). | |
| - Press 1/2/3/4 to change background to Blue/Yellow/Red/Green and replace "+" with the digit. | |
| - Press any other key to exit. (Esc also exits.) | |
| """ | |
| import os | |
| import sys | |
| import pygame | |
| # Try to make multi-monitor spanning more reliable on some WMs/desktop envs. | |
| os.environ.setdefault("SDL_VIDEO_CENTERED", "0") | |
| # Background colors | |
| GREY = (160, 160, 160) | |
| BLUE = ( 0, 102, 204) | |
| YELLOW = (255, 215, 0) | |
| RED = (220, 0, 0) | |
| GREEN = ( 0, 170, 0) | |
| FG = (0, 0, 0) # black text | |
| KEY_TO_STATE = { | |
| pygame.K_1: ("1", BLUE), | |
| pygame.K_2: ("2", YELLOW), | |
| pygame.K_3: ("3", RED), | |
| pygame.K_4: ("4", GREEN), | |
| pygame.K_KP1: ("1", BLUE), | |
| pygame.K_KP2: ("2", YELLOW), | |
| pygame.K_KP3: ("3", RED), | |
| pygame.K_KP4: ("4", GREEN), | |
| } | |
| def get_virtual_desktop_rect(): | |
| """ | |
| Returns (x, y, w, h) bounding rectangle of all displays in pygame. | |
| Falls back to primary display if needed. | |
| """ | |
| # pygame 2.x supports multiple displays via pygame.display.get_num_video_displays() | |
| try: | |
| n = pygame.display.get_num_video_displays() | |
| except Exception: | |
| n = 1 | |
| if n <= 0: | |
| n = 1 | |
| min_x = min_y = 10**9 | |
| max_x = max_y = -10**9 | |
| any_ok = False | |
| for i in range(n): | |
| try: | |
| r = pygame.display.get_desktop_sizes()[i] # (w,h) per display (no position) | |
| except Exception: | |
| r = None | |
| # Pygame doesn't always provide per-display positions cross-platform. | |
| # We'll instead use the SDL "current_w/current_h" for primary and span by total widths | |
| # if positions aren't available. But we can attempt pygame.display.get_display_bounds(i) | |
| # which does include position on many systems. | |
| try: | |
| b = pygame.display.get_display_bounds(i) # Rect(x,y,w,h) | |
| x, y, w, h = b.x, b.y, b.w, b.h | |
| any_ok = True | |
| except Exception: | |
| # fallback: assume displays laid out horizontally starting at x=0 | |
| if r: | |
| w, h = r | |
| else: | |
| info = pygame.display.Info() | |
| w, h = info.current_w, info.current_h | |
| x, y = 0, 0 | |
| # We'll handle horizontal tiling below if we can't get bounds | |
| # (set any_ok False and break). | |
| any_ok = False | |
| break | |
| min_x = min(min_x, x) | |
| min_y = min(min_y, y) | |
| max_x = max(max_x, x + w) | |
| max_y = max(max_y, y + h) | |
| if any_ok: | |
| return (min_x, min_y, max_x - min_x, max_y - min_y) | |
| # Fallback: sum widths horizontally, max height | |
| try: | |
| sizes = pygame.display.get_desktop_sizes() | |
| if sizes: | |
| total_w = sum(w for (w, _h) in sizes) | |
| max_h = max(h for (_w, h) in sizes) | |
| return (0, 0, total_w, max_h) | |
| except Exception: | |
| pass | |
| info = pygame.display.Info() | |
| return (0, 0, info.current_w, info.current_h) | |
| def make_font(window_size): | |
| # Big, readable font size relative to the combined window. | |
| w, h = window_size | |
| # Aim for ~60% of the shorter dimension. | |
| px = int(min(w, h) * 0.6) | |
| px = max(24, min(px, 2000)) | |
| return pygame.font.SysFont(None, px, bold=True) | |
| def render_center(surface, font, text, color): | |
| img = font.render(text, True, color) | |
| rect = img.get_rect(center=surface.get_rect().center) | |
| surface.blit(img, rect) | |
| def main(): | |
| pygame.init() | |
| pygame.font.init() | |
| # Must set a mode before some display queries work on some platforms; | |
| # but we want bounds first, so we do minimal init and then compute. | |
| # Still, get_display_bounds often works without set_mode in pygame2. | |
| # Determine bounding rectangle of all monitors. | |
| x, y, w, h = get_virtual_desktop_rect() | |
| # Create a borderless window spanning the virtual desktop. | |
| flags = pygame.NOFRAME | |
| # FULLSCREEN may lock to a single display; using NOFRAME + size tends to span. | |
| screen = pygame.display.set_mode((w, h), flags) | |
| # Move the window to the top-left of the virtual desktop (if supported). | |
| # Works on SDL2 environments via set_window_position. | |
| try: | |
| pygame.display.get_window().set_position(x, y) | |
| except Exception: | |
| # Some pygame builds don't expose set_position; ignore. | |
| pass | |
| pygame.display.set_caption("Multi-screen Color Test") | |
| font = make_font((w, h)) | |
| text = "+" | |
| bg = GREY | |
| clock = pygame.time.Clock() | |
| running = True | |
| while running: | |
| for event in pygame.event.get(): | |
| if event.type == pygame.QUIT: | |
| running = False | |
| elif event.type == pygame.KEYDOWN: | |
| if event.key in KEY_TO_STATE: | |
| text, bg = KEY_TO_STATE[event.key] | |
| else: | |
| # Any other key exits (including ESC). | |
| running = False | |
| screen.fill(bg) | |
| render_center(screen, font, text, FG) | |
| pygame.display.flip() | |
| clock.tick(60) | |
| pygame.quit() | |
| return 0 | |
| if __name__ == "__main__": | |
| raise SystemExit(main()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment