Documentation Index
Fetch the complete documentation index at: https://cloud-docs.mentra.glass/llms.txt
Use this file to discover all available pages before exploring further.
Overview
DisplayManager is responsible for managing what appears on the smart glasses display. It handles display requests from apps, implements throttling to prevent display flicker, manages boot screens, handles onboarding flows, and enforces display priorities between foreground and background apps.
File: packages/cloud/src/services/layout/DisplayManager6.1.ts
Key Features
- Display Throttling: 300ms minimum between updates to prevent flicker
- Boot Screen Management: Shows loading screens while apps start
- App Prioritization: Manages display conflicts between apps
- Background Lock System: Allows background apps temporary display control
- Onboarding Support: Shows first-time user instructions
- Queue Management: Handles display requests during boot and throttling
Architecture
State Management
Display State
interface DisplayState {
currentDisplay: ActiveDisplay | null; // Currently showing
coreAppDisplay: ActiveDisplay | null; // Core app's queued display
backgroundLock: { // Background app lock
packageName: string;
expiresAt: Date;
lastActiveTime: number;
} | null;
savedDisplayBeforeBoot: ActiveDisplay | null; // Saved during boot
}
Throttling System
private throttledRequests: Map<string, ThrottledRequest> = new Map();
interface ThrottledRequest {
activeDisplay: ActiveDisplay;
timestamp: number;
}
Display Request Processing
Main Entry Point
handleDisplayRequest(displayRequest: DisplayRequest): boolean {
// Check if app is booting
if (this.bootingApps.has(displayRequest.packageName)) {
this.queueBootDisplay(displayRequest);
return true;
}
// Handle dashboard requests (always allowed)
if (displayRequest.packageName === SYSTEM_DASHBOARD_PACKAGE_NAME) {
return this.showDisplay(this.createActiveDisplay(displayRequest));
}
// Handle core app display
if (displayRequest.packageName === this.mainApp) {
const activeDisplay = this.createActiveDisplay(displayRequest);
this.displayState.coreAppDisplay = activeDisplay;
// Check if blocked by background app
const blockedByBackgroundApp =
this.displayState.backgroundLock &&
this.displayState.backgroundLock?.packageName !== this.mainApp &&
this.displayState.currentDisplay?.displayRequest.packageName ===
this.displayState.backgroundLock?.packageName;
if (!blockedByBackgroundApp) {
return this.showDisplay(activeDisplay);
}
return false; // Blocked
}
// Handle background app display
const canDisplay = this.canBackgroundAppDisplay(displayRequest.packageName);
if (canDisplay) {
return this.showDisplay(this.createActiveDisplay(displayRequest));
}
return false;
}
Throttling Implementation
Show Display with Throttling
private showDisplay(activeDisplay: ActiveDisplay): boolean {
const displayRequest = activeDisplay.displayRequest;
// Block during onboarding (except onboarding displays)
if (this.onboardingActive && Date.now() < this.onboardingEndTime) {
if (!(displayRequest.layout?.layoutType === LayoutType.REFERENCE_CARD &&
displayRequest.layout.title === 'Welcome')) {
return false;
}
}
// Check throttle
if (Date.now() - this.lastDisplayTime < this.THROTTLE_DELAY &&
!displayRequest.forceDisplay) {
this.enqueueThrottledDisplay(activeDisplay);
return true; // Accepted but queued
}
// Send immediately
const success = this.sendToWebSocket(displayRequest, this.userSession?.websocket);
if (success) {
this.displayState.currentDisplay = activeDisplay;
this.lastDisplayTime = Date.now();
// Set expiry timeout if duration specified
if (activeDisplay.expiresAt) {
const timeUntilExpiry = activeDisplay.expiresAt.getTime() - Date.now();
setTimeout(() => {
if (this.displayState.currentDisplay === activeDisplay) {
this.showNextDisplay('duration_expired');
}
}, timeUntilExpiry);
}
}
return success;
}
Throttle Queue Processing
private enqueueThrottledDisplay(activeDisplay: ActiveDisplay): void {
const packageName = activeDisplay.displayRequest.packageName;
// Add to queue (overwrites previous request from same app)
this.throttledRequests.set(packageName, {
activeDisplay,
timestamp: Date.now()
});
// Schedule display after throttle delay
setTimeout(() => {
const currentRequest = this.throttledRequests.get(packageName);
if (currentRequest?.activeDisplay === activeDisplay) {
this.sendToWebSocket(activeDisplay.displayRequest, this.userSession?.websocket);
this.displayState.currentDisplay = activeDisplay;
this.lastDisplayTime = Date.now();
this.throttledRequests.delete(packageName);
}
}, this.THROTTLE_DELAY);
}
Boot Screen Management
App Start Handler
public handleAppStart(packageName: string): void {
const app = this.userSession.installedApps.get(packageName);
// Set main app if standard type
if (app?.appType === 'standard') {
this.mainApp = packageName;
}
// Skip boot screen for dashboard
if (packageName === SYSTEM_DASHBOARD_PACKAGE_NAME) {
return;
}
// Save current display if valid
if (this.displayState.currentDisplay) {
const currentPackage = this.displayState.currentDisplay.displayRequest.packageName;
const displayIsValid = this.hasRemainingDuration(this.displayState.currentDisplay);
if (this.userSession.runningApps.has(currentPackage) && displayIsValid) {
this.displayState.savedDisplayBeforeBoot = this.displayState.currentDisplay;
}
}
// Show boot screen
this.bootingApps.add(packageName);
this.updateBootScreen();
// Complete boot after duration
setTimeout(async () => {
this.bootingApps.delete(packageName);
if (this.bootingApps.size === 0) {
this.clearDisplay('main');
this.processBootQueue();
// Check onboarding
if (packageName !== SYSTEM_DASHBOARD_PACKAGE_NAME) {
await this.checkAndShowOnboarding(packageName);
}
}
}, this.BOOT_DURATION);
}
Boot Screen Display
private updateBootScreen(): void {
const bootingAppNames = Array.from(this.bootingApps).join(', ');
const bootRequest: DisplayRequest = {
type: AppToCloudMessageType.DISPLAY_REQUEST,
view: ViewType.MAIN,
packageName: 'system.boot',
layout: {
layoutType: LayoutType.TEXT_WALL,
text: `Loading ${bootingAppNames}...`
},
timestamp: new Date(),
forceDisplay: true
};
this.sendToWebSocket(bootRequest, this.userSession?.websocket);
}
Background Lock System
Lock Request
requestBackgroundLock(packageName: string, durationMs: number = 10000): boolean {
if (this.displayState.backgroundLock) {
const now = Date.now();
// Check if existing lock is expired or inactive
if (this.displayState.backgroundLock.expiresAt.getTime() > now &&
now - this.displayState.backgroundLock.lastActiveTime < this.LOCK_INACTIVE_TIMEOUT) {
return false; // Active lock exists
}
}
// Grant lock
this.displayState.backgroundLock = {
packageName,
expiresAt: new Date(Date.now() + durationMs),
lastActiveTime: Date.now()
};
return true;
}
Lock Validation
private canBackgroundAppDisplay(packageName: string): boolean {
if (!this.displayState.backgroundLock) {
return false;
}
const now = Date.now();
// Check lock ownership and validity
if (this.displayState.backgroundLock.packageName !== packageName) {
return false;
}
if (this.displayState.backgroundLock.expiresAt.getTime() <= now) {
this.displayState.backgroundLock = null;
return false;
}
// Check if lock holder has been inactive
if (now - this.displayState.backgroundLock.lastActiveTime > this.LOCK_INACTIVE_TIMEOUT) {
this.displayState.backgroundLock = null;
return false;
}
return true;
}
Display Priority Logic
Show Next Display
private showNextDisplay(reason: string): void {
// Boot screen takes precedence
if (this.bootingApps.size > 0) {
this.updateBootScreen();
return;
}
// Check throttled requests first
if (this.throttledRequests.size > 0) {
const oldestRequest = this.findOldestThrottledRequest();
if (oldestRequest) {
this.showThrottledDisplay(oldestRequest);
return;
}
}
// Try saved display from before boot
if (reason === 'boot_complete' && this.displayState.savedDisplayBeforeBoot) {
if (this.isDisplayStillValid(this.displayState.savedDisplayBeforeBoot)) {
this.restoreSavedDisplay();
return;
}
}
// Show core app display if available
if (this.displayState.coreAppDisplay &&
this.isDisplayStillValid(this.displayState.coreAppDisplay)) {
this.showDisplay(this.displayState.coreAppDisplay);
return;
}
// Clear display
this.clearDisplay('main');
}
Onboarding Support
Check and Show Onboarding
private async checkAndShowOnboarding(packageName: string): Promise<void> {
const userEmail = this.userSession.userId;
try {
const onboardingStatus = await this.getOnboardingStatus(userEmail, packageName);
if (!onboardingStatus) {
const instructions = await this.getOnboardingInstructions(packageName);
if (instructions) {
const onboardingDisplay: DisplayRequest = {
type: AppToCloudMessageType.DISPLAY_REQUEST,
view: ViewType.MAIN,
packageName,
layout: {
layoutType: LayoutType.TEXT_WALL,
text: instructions
},
timestamp: new Date(),
durationMs: 15000
};
this.onboardingActive = true;
this.onboardingEndTime = Date.now() + 5000;
this.sendDisplay(onboardingDisplay);
await this.completeOnboarding(userEmail, packageName);
}
}
} catch (err) {
this.logger.error({ err }, `Error handling onboarding for ${packageName}`);
}
}
Configuration
Timing Constants
private readonly THROTTLE_DELAY = 300; // 300ms between displays
private readonly BOOT_DURATION = 1500; // 1.5s boot screen
private readonly LOCK_TIMEOUT = 10000; // 10s background lock
private readonly LOCK_INACTIVE_TIMEOUT = 2000; // 2s inactivity timeout
Lifecycle Management
Disposal
dispose(): void {
// Clear all timeouts and intervals
this.throttledRequests.clear();
this.bootDisplayQueue.clear();
this.bootingApps.clear();
// Reset state
this.displayState = {
currentDisplay: null,
coreAppDisplay: null,
backgroundLock: null,
savedDisplayBeforeBoot: null
};
this.logger.info(`DisplayManager disposed`);
}
Best Practices
- Always respect throttling unless
forceDisplay is set
- Save displays during boot for restoration afterward
- Validate display ownership before showing saved displays
- Handle onboarding gracefully without blocking other displays
- Clean up expired locks to prevent display blocking
- Use appropriate display types (TEXT_WALL, REFERENCE_CARD, etc.)
Integration Points
- AppManager: Notifies of app start/stop for boot screens
- DashboardManager: Special handling for system dashboard
- WebSocket: Sends display updates to glasses
- Onboarding Service: Checks and completes onboarding