|  |  |  | @ -15,6 +15,7 @@ FPS_CRITICAL_THRESHOLD = 0.5  # Critical threshold for triggering strict actions | 
			
		
	
		
			
				
					|  |  |  |  | ENABLE_VSYNC = os.getenv("ENABLE_VSYNC") == "1" | 
			
		
	
		
			
				
					|  |  |  |  | DEBUG_FPS = os.getenv("DEBUG_FPS") == '1' | 
			
		
	
		
			
				
					|  |  |  |  | STRICT_MODE = os.getenv("STRICT_MODE") == '1' | 
			
		
	
		
			
				
					|  |  |  |  | SCALE = float(os.getenv("SCALE", "1.0")) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | DEFAULT_TEXT_SIZE = 60 | 
			
		
	
		
			
				
					|  |  |  |  | DEFAULT_TEXT_COLOR = rl.WHITE | 
			
		
	
	
		
			
				
					|  |  |  | @ -22,6 +23,7 @@ DEFAULT_TEXT_COLOR = rl.WHITE | 
			
		
	
		
			
				
					|  |  |  |  | ASSETS_DIR = files("openpilot.selfdrive").joinpath("assets") | 
			
		
	
		
			
				
					|  |  |  |  | FONT_DIR = ASSETS_DIR.joinpath("fonts") | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | class FontWeight(IntEnum): | 
			
		
	
		
			
				
					|  |  |  |  |   THIN = 0 | 
			
		
	
		
			
				
					|  |  |  |  |   EXTRA_LIGHT = 1 | 
			
		
	
	
		
			
				
					|  |  |  | @ -39,6 +41,10 @@ class GuiApplication: | 
			
		
	
		
			
				
					|  |  |  |  |     self._fonts: dict[FontWeight, rl.Font] = {} | 
			
		
	
		
			
				
					|  |  |  |  |     self._width = width | 
			
		
	
		
			
				
					|  |  |  |  |     self._height = height | 
			
		
	
		
			
				
					|  |  |  |  |     self._scale = SCALE | 
			
		
	
		
			
				
					|  |  |  |  |     self._scaled_width = int(self._width * self._scale) | 
			
		
	
		
			
				
					|  |  |  |  |     self._scaled_height = int(self._height * self._scale) | 
			
		
	
		
			
				
					|  |  |  |  |     self._render_texture: rl.RenderTexture | None = None | 
			
		
	
		
			
				
					|  |  |  |  |     self._textures: dict[str, rl.Texture] = {} | 
			
		
	
		
			
				
					|  |  |  |  |     self._target_fps: int = DEFAULT_FPS | 
			
		
	
		
			
				
					|  |  |  |  |     self._last_fps_log_time: float = time.monotonic() | 
			
		
	
	
		
			
				
					|  |  |  | @ -62,14 +68,15 @@ class GuiApplication: | 
			
		
	
		
			
				
					|  |  |  |  |       flags |= rl.ConfigFlags.FLAG_VSYNC_HINT | 
			
		
	
		
			
				
					|  |  |  |  |     rl.set_config_flags(flags) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     rl.init_window(self._width, self._height, title) | 
			
		
	
		
			
				
					|  |  |  |  |     rl.init_window(self._scaled_width, self._scaled_height, title) | 
			
		
	
		
			
				
					|  |  |  |  |     if self._scale != 1.0: | 
			
		
	
		
			
				
					|  |  |  |  |       self._render_texture = rl.load_render_texture(self._width, self._height) | 
			
		
	
		
			
				
					|  |  |  |  |     rl.set_target_fps(fps) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     self._target_fps = fps | 
			
		
	
		
			
				
					|  |  |  |  |     self._set_styles() | 
			
		
	
		
			
				
					|  |  |  |  |     self._load_fonts() | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   def texture(self, asset_path: str, width: int, height: int, alpha_premultiply=False, keep_aspect_ratio=True): | 
			
		
	
		
			
				
					|  |  |  |  |     cache_key = f"{asset_path}_{width}_{height}_{alpha_premultiply}{keep_aspect_ratio}" | 
			
		
	
		
			
				
					|  |  |  |  |     if cache_key in self._textures: | 
			
		
	
	
		
			
				
					|  |  |  | @ -80,7 +87,7 @@ class GuiApplication: | 
			
		
	
		
			
				
					|  |  |  |  |     self._textures[cache_key] = texture_obj | 
			
		
	
		
			
				
					|  |  |  |  |     return texture_obj | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   def _load_texture_from_image(self, image_path: str, width: int, height: int, alpha_premultiply = False, keep_aspect_ratio=True): | 
			
		
	
		
			
				
					|  |  |  |  |   def _load_texture_from_image(self, image_path: str, width: int, height: int, alpha_premultiply=False, keep_aspect_ratio=True): | 
			
		
	
		
			
				
					|  |  |  |  |     """Load and resize a texture, storing it for later automatic unloading.""" | 
			
		
	
		
			
				
					|  |  |  |  |     image = rl.load_image(image_path) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  |  | @ -123,16 +130,32 @@ class GuiApplication: | 
			
		
	
		
			
				
					|  |  |  |  |       rl.unload_font(font) | 
			
		
	
		
			
				
					|  |  |  |  |     self._fonts = {} | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     if self._render_texture is not None: | 
			
		
	
		
			
				
					|  |  |  |  |       rl.unload_render_texture(self._render_texture) | 
			
		
	
		
			
				
					|  |  |  |  |       self._render_texture = None | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     rl.close_window() | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   def render(self): | 
			
		
	
		
			
				
					|  |  |  |  |     try: | 
			
		
	
		
			
				
					|  |  |  |  |       while not (self._window_close_requested or rl.window_should_close()): | 
			
		
	
		
			
				
					|  |  |  |  |         rl.begin_drawing() | 
			
		
	
		
			
				
					|  |  |  |  |         rl.clear_background(rl.BLACK) | 
			
		
	
		
			
				
					|  |  |  |  |         if self._render_texture: | 
			
		
	
		
			
				
					|  |  |  |  |           rl.begin_texture_mode(self._render_texture) | 
			
		
	
		
			
				
					|  |  |  |  |           rl.clear_background(rl.BLACK) | 
			
		
	
		
			
				
					|  |  |  |  |         else: | 
			
		
	
		
			
				
					|  |  |  |  |           rl.begin_drawing() | 
			
		
	
		
			
				
					|  |  |  |  |           rl.clear_background(rl.BLACK) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         yield | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         if self._render_texture: | 
			
		
	
		
			
				
					|  |  |  |  |           rl.end_texture_mode() | 
			
		
	
		
			
				
					|  |  |  |  |           rl.begin_drawing() | 
			
		
	
		
			
				
					|  |  |  |  |           rl.clear_background(rl.BLACK) | 
			
		
	
		
			
				
					|  |  |  |  |           src_rect = rl.Rectangle(0, 0, float(self._width), -float(self._height)) | 
			
		
	
		
			
				
					|  |  |  |  |           dst_rect = rl.Rectangle(0, 0, float(self._scaled_width), float(self._scaled_height)) | 
			
		
	
		
			
				
					|  |  |  |  |           rl.draw_texture_pro(self._render_texture.texture, src_rect, dst_rect, rl.Vector2(0, 0), 0.0, rl.WHITE) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |         if DEBUG_FPS: | 
			
		
	
		
			
				
					|  |  |  |  |           rl.draw_fps(10, 10) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
	
		
			
				
					|  |  |  | @ -141,7 +164,7 @@ class GuiApplication: | 
			
		
	
		
			
				
					|  |  |  |  |     except KeyboardInterrupt: | 
			
		
	
		
			
				
					|  |  |  |  |       pass | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   def font(self, font_weight: FontWeight=FontWeight.NORMAL): | 
			
		
	
		
			
				
					|  |  |  |  |   def font(self, font_weight: FontWeight = FontWeight.NORMAL): | 
			
		
	
		
			
				
					|  |  |  |  |     return self._fonts[font_weight] | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |   @property | 
			
		
	
	
		
			
				
					|  |  |  | @ -163,7 +186,7 @@ class GuiApplication: | 
			
		
	
		
			
				
					|  |  |  |  |       "Inter-Bold.ttf", | 
			
		
	
		
			
				
					|  |  |  |  |       "Inter-ExtraBold.ttf", | 
			
		
	
		
			
				
					|  |  |  |  |       "Inter-Black.ttf", | 
			
		
	
		
			
				
					|  |  |  |  |       ) | 
			
		
	
		
			
				
					|  |  |  |  |     ) | 
			
		
	
		
			
				
					|  |  |  |  | 
 | 
			
		
	
		
			
				
					|  |  |  |  |     for index, font_file in enumerate(font_files): | 
			
		
	
		
			
				
					|  |  |  |  |       with as_file(FONT_DIR.joinpath(font_file)) as fspath: | 
			
		
	
	
		
			
				
					|  |  |  | 
 |