ZerOS GUI Program Development Guide
Overview
GUI programs in ZerOS are graphical applications that create windows managed by GUIManager, handle user interactions through EventManager, and integrate with the system's theme system. GUI programs run in the browser-based ZerOS environment and must follow specific patterns for window lifecycle, event handling, and resource cleanup.
Key Characteristics:
- •GUI programs create windows that are managed by
GUIManager - •All event handling must go through
EventManager(mandatory requirement) - •Must use CSS theme variables for consistent styling
- •Support background mode (optional but recommended)
- •Must properly clean up resources in
__exit__()
File Location:
- •GUI programs are located in
system/service/DISK/D/application/<app-name>/<app-name>.js - •Each application should have its own directory
Program Structure
Basic Template
(function(window) {
'use strict';
const MYAPP = {
pid: null,
window: null,
windowId: null,
_kernelAPI: null,
eventHandlers: [], // Store event handler IDs for cleanup
__info__: function() {
return {
name: 'My Application',
type: 'GUI',
version: '1.0.0',
description: 'Description of my application',
author: 'Your Name',
copyright: '© 2025 ZerOS',
permissions: typeof PermissionManager !== 'undefined' ? [
PermissionManager.PERMISSION.GUI_WINDOW_CREATE,
PermissionManager.PERMISSION.EVENT_LISTENER,
// Add other required permissions
] : [],
metadata: {
allowMultipleInstances: false, // or true
category: 'system', // or 'utility', 'entertainment', etc.
showOnDesktop: true // Whether to show icon on desktop
}
};
},
__init__: async function(pid, initArgs) {
this.pid = pid;
this._kernelAPI = (initArgs && initArgs.kernelAPI) || null;
// Get GUI container
const guiContainer = (initArgs && initArgs.guiContainer) ||
document.getElementById('gui-container');
if (!guiContainer) {
if (typeof KernelLogger !== 'undefined') {
KernelLogger.warn('MYAPP', '未找到 gui-container');
}
return;
}
// Create window element
this.window = document.createElement('div');
this.window.className = 'myapp-window zos-gui-window';
this.window.dataset.pid = String(pid);
this.window.style.cssText = `
width: 600px;
height: 400px;
min-width: 400px;
min-height: 300px;
display: flex;
flex-direction: column;
overflow: hidden;
`;
// Register window with GUIManager
if (typeof GUIManager !== 'undefined') {
let icon = null;
if (typeof ApplicationAssetManager !== 'undefined') {
icon = ApplicationAssetManager.getIcon('myapp');
}
const windowInfo = GUIManager.registerWindow(pid, this.window, {
title: 'My Application',
icon: icon,
onClose: () => this._onCloseRequest()
});
if (windowInfo && windowInfo.windowId) {
this.windowId = windowInfo.windowId;
}
}
// Build UI
this._buildUI();
// Add window to container
guiContainer.appendChild(this.window);
// Register event handlers
this._registerEventHandlers();
},
__exit__: async function() {
// Clean up event handlers
if (typeof EventManager !== 'undefined') {
for (let i = 0; i < this.eventHandlers.length; i++) {
try {
EventManager.unregisterEventHandler(this.eventHandlers[i]);
} catch (e) {}
}
}
this.eventHandlers = [];
// Unregister window
if (typeof GUIManager !== 'undefined' && this.windowId) {
GUIManager.unregisterWindow(this.windowId);
} else if (this.pid && typeof GUIManager !== 'undefined') {
GUIManager.unregisterWindow(this.pid);
}
// Remove window from DOM
if (this.window && this.window.parentElement) {
this.window.parentElement.removeChild(this.window);
}
// Clear references
this.window = null;
this.windowId = null;
this._kernelAPI = null;
},
_buildUI: function() {
// Build your UI here
const content = document.createElement('div');
content.className = 'myapp-content';
content.textContent = 'Hello, World!';
this.window.appendChild(content);
},
_registerEventHandlers: function() {
// Register event handlers using EventManager
if (typeof EventManager === 'undefined') return;
const button = this.window.querySelector('.myapp-button');
if (button) {
const handlerId = EventManager.registerElementEvent(
this.pid,
button,
'click',
(e) => {
this._handleButtonClick(e);
}
);
this.eventHandlers.push(handlerId);
}
},
_handleButtonClick: function(e) {
// Handle button click
},
_onCloseRequest: function() {
// Handle close request
// Option 1: Exit immediately
this._exit();
// Option 2: Go to background (recommended for some apps)
// this._goToBackground();
},
_exit: function() {
if (this._kernelAPI && typeof this._kernelAPI.call === 'function') {
this._kernelAPI.call('Process.requestSelfTermination', []).catch(e => {
if (typeof KernelLogger !== 'undefined') {
KernelLogger.warn('MYAPP', 'requestSelfTermination 失败: ' + (e && e.message));
}
});
}
}
};
if (typeof window !== 'undefined') {
window.MYAPP = MYAPP;
} else if (typeof globalThis !== 'undefined') {
globalThis.MYAPP = MYAPP;
}
})(typeof window !== 'undefined' ? window : globalThis);
Required Methods
__info__()
Returns program metadata including name, type, version, permissions, and metadata.
Required Fields:
- •
name(string): Program name - •
type(string): Must be'GUI' - •
version(string): Version number - •
description(string): Program description - •
author(string): Author name - •
copyright(string): Copyright information
Optional Fields:
- •
permissions(Array): Required permissions (see Permissions section) - •
metadata(Object):- •
allowMultipleInstances(boolean): Whether to allow multiple instances (default:false) - •
category(string): Application category ('system','utility','entertainment', etc.) - •
showOnDesktop(boolean): Whether to show icon on desktop
- •
Example:
__info__: function() {
return {
name: 'My Application',
type: 'GUI',
version: '1.0.0',
description: 'Description of my application',
author: 'Your Name',
copyright: '© 2025 ZerOS',
permissions: typeof PermissionManager !== 'undefined' ? [
PermissionManager.PERMISSION.GUI_WINDOW_CREATE,
PermissionManager.PERMISSION.EVENT_LISTENER,
PermissionManager.PERMISSION.FILE_READ,
PermissionManager.PERMISSION.FILE_WRITE
] : [],
metadata: {
allowMultipleInstances: false,
category: 'utility',
showOnDesktop: true
}
};
}
__init__(pid, initArgs)
Initializes the GUI program. Must create the window element, register it with GUIManager, build the UI, and register event handlers.
Parameters:
- •
pid(number): Process ID assigned by ProcessManager - •
initArgs(Object): Initialization arguments- •
guiContainer(HTMLElement): Container element for GUI windows - •
kernelAPI(Object): Process-bound kernel API (recommended for kernel calls)
- •
Key Steps:
- •Store
pidandkernelAPI - •Get
guiContainerfrominitArgs.guiContainerordocument.getElementById('gui-container') - •Create window element with class
zos-gui-windowanddataset.pid - •Register window with
GUIManager.registerWindow() - •Build UI content
- •Append window to
guiContainer - •Register event handlers using
EventManager
Example:
__init__: async function(pid, initArgs) {
this.pid = pid;
this._kernelAPI = (initArgs && initArgs.kernelAPI) || null;
const guiContainer = (initArgs && initArgs.guiContainer) ||
document.getElementById('gui-container');
if (!guiContainer) {
if (typeof KernelLogger !== 'undefined') {
KernelLogger.warn('MYAPP', '未找到 gui-container');
}
return;
}
// Create window
this.window = document.createElement('div');
this.window.className = 'myapp-window zos-gui-window';
this.window.dataset.pid = String(pid);
// Register with GUIManager
if (typeof GUIManager !== 'undefined') {
const windowInfo = GUIManager.registerWindow(pid, this.window, {
title: 'My Application',
icon: ApplicationAssetManager?.getIcon('myapp'),
onClose: () => this._onCloseRequest()
});
if (windowInfo && windowInfo.windowId) {
this.windowId = windowInfo.windowId;
}
}
// Build UI
this._buildUI();
// Add to container
guiContainer.appendChild(this.window);
// Register events
this._registerEventHandlers();
}
__exit__()
Cleans up resources when the program exits. Must unregister event handlers, unregister window, and remove DOM elements.
Key Steps:
- •Unregister all event handlers using
EventManager.unregisterEventHandler() - •Unregister window using
GUIManager.unregisterWindow() - •Remove window from DOM
- •Clear all references (set to
null)
Example:
__exit__: async function() {
// Clean up event handlers
if (typeof EventManager !== 'undefined') {
for (let i = 0; i < this.eventHandlers.length; i++) {
try {
EventManager.unregisterEventHandler(this.eventHandlers[i]);
} catch (e) {}
}
}
this.eventHandlers = [];
// Unregister window
if (typeof GUIManager !== 'undefined' && this.windowId) {
GUIManager.unregisterWindow(this.windowId);
}
// Remove from DOM
if (this.window && this.window.parentElement) {
this.window.parentElement.removeChild(this.window);
}
// Clear references
this.window = null;
this.windowId = null;
this._kernelAPI = null;
}
Window Management
GUIManager API
GUIManager manages all GUI windows, providing unified window controls (minimize, maximize, close), drag and resize functionality, focus management, and z-index management.
Registering a Window
const windowInfo = GUIManager.registerWindow(pid, windowElement, {
title: 'Window Title',
icon: 'application/myapp/myapp.svg', // Optional
onClose: () => {
// Called when window is closed
// Do NOT call unregisterWindow() here
// GUIManager handles window cleanup automatically
},
onMinimize: () => {
// Optional: Called when window is minimized
},
onMaximize: (isMaximized) => {
// Optional: Called when window is maximized/restored
// isMaximized: true if maximized, false if restored
},
windowId: 'custom-window-id' // Optional: Auto-generated if not provided
});
Window Info Object:
{
windowId: string,
window: HTMLElement,
pid: number,
zIndex: number,
isFocused: boolean,
isMinimized: boolean,
isMaximized: boolean,
isMainWindow: boolean,
title: string,
icon: string|null,
createdAt: number
}
Window Operations
// Focus window GUIManager.focusWindow(this.windowId); // Minimize window GUIManager.minimizeWindow(this.windowId); // Restore window GUIManager.restoreWindow(this.windowId, true); // true = auto focus // Toggle maximize GUIManager.toggleMaximize(this.windowId); // Get window info const winInfo = GUIManager.getWindowInfo(this.windowId); // Get all windows for this PID const windows = GUIManager.getWindowsByPid(this.pid); // Unregister window (usually done in __exit__) GUIManager.unregisterWindow(this.windowId);
Window Close Flow
When a window is closed (user clicks close button or unregisterWindow is called), GUIManager executes:
- •
Calls
onClosecallback (if exists):- •Callback executes before window close animation
- •
GUIManagerclearsonClosereference to prevent recursion - •If callback already called
unregisterWindow,GUIManagerskips subsequent steps
- •
Executes close animation:
- •Uses
AnimateManagerfor smooth close animation - •Waits for animation to complete before removing element
- •Uses
- •
Unregisters window:
- •Removes window from registry
- •Cleans up event listeners (drag, resize, etc.)
- •Updates taskbar visibility
- •
Checks process termination:
- •If PID has no other windows and is not Exploit program (PID 10000), automatically calls
ProcessManager.killProgram(pid)
- •If PID has no other windows and is not Exploit program (PID 10000), automatically calls
Important:
- •
onClosecallback should only perform cleanup work - •Do NOT call
unregisterWindow()or_closeWindow()inonClose - •Window close flow is managed by
GUIManagerto ensure proper resource cleanup
Background Mode Support
GUI programs can support background mode, allowing them to continue running when the window is closed, and be restored from the system tray.
Implementing Background Mode
_onCloseRequest: function() {
// Go to background instead of exiting
this._goToBackground();
},
_goToBackground: function() {
if (!this.windowId || !this.window) return;
// Mark window as background-requested
const winInfo = typeof GUIManager !== 'undefined' ?
GUIManager.getWindowInfo(this.windowId) : null;
if (winInfo) {
winInfo._backgroundRequested = true;
}
// Hide window
if (this.window.style) {
this.window.style.display = 'none';
}
// Request background mode
if (this._kernelAPI && typeof this._kernelAPI.call === 'function') {
this._kernelAPI.call('Process.requestBackground', []).catch(e => {
if (typeof KernelLogger !== 'undefined') {
KernelLogger.warn('MYAPP', 'requestBackground 失败: ' + (e && e.message));
}
});
}
},
// Register tray click handler to restore window
_registerBackgroundTray: async function() {
if (!this._kernelAPI || typeof this._kernelAPI.call !== 'function') return;
const windowId = this.windowId;
const kernelAPI = this._kernelAPI;
try {
// Register tray click handler
await this._kernelAPI.call('Process.registerBackgroundTrayClick', [
function() {
if (typeof GUIManager === 'undefined') return;
// Show window
const winInfo = GUIManager.getWindowInfo(windowId);
if (winInfo && winInfo.window) {
winInfo.window.style.display = '';
GUIManager.focusWindow(windowId);
}
// Request foreground
if (kernelAPI && typeof kernelAPI.call === 'function') {
kernelAPI.call('Process.requestForeground', []).catch(() => {});
}
}
]);
// Register context menu (optional)
await this._kernelAPI.call('Process.registerBackgroundTrayContextMenu', [
function() {
return [
{ label: 'Restore', action: () => { /* restore */ } },
{ label: 'Exit', action: () => { /* exit */ } }
];
}
]);
} catch (e) {
if (typeof KernelLogger !== 'undefined') {
KernelLogger.warn('MYAPP', '注册后台托盘失败: ' + (e && e.message));
}
}
}
How Background Mode Works:
- •When user clicks close button,
onClosecallback is called - •Set
winInfo._backgroundRequested = trueand hide window - •Call
Process.requestBackgroundvia kernel API - •
GUIManagerdetects_backgroundRequestedand only hides window (doesn't unregister or kill process) - •Program continues running in background
- •User can click tray icon to restore window
- •Call
Process.requestForegroundto bring program back to foreground
Event Handling
EventManager API (Mandatory)
All event handling must go through EventManager. This is a mandatory requirement in ZerOS.
Why EventManager:
- •Unified event management with priority and propagation control
- •Automatic cleanup when process exits (prevents memory leaks)
- •Supports multiple programs registering the same event (executed by priority)
- •Provides unified event propagation control API
Registering Event Handlers
// Register global event handler
const handlerId = EventManager.registerEventHandler(
this.pid,
'click', // Event type
(event, eventContext) => {
// Handler function
// event: Native event object
// eventContext: Event context with stopPropagation, preventDefault, etc.
// Return values:
// - false: Prevent default behavior
// - 'stop' or 'stopPropagation': Stop event propagation
// - 'stopImmediate' or 'stopImmediatePropagation': Stop immediate propagation
},
{
priority: 100, // Lower number = higher priority (default: 100)
selector: '.my-button', // Optional: CSS selector (only trigger on matching elements)
stopPropagation: false, // Optional: Stop propagation
once: false, // Optional: Trigger only once
passive: false, // Optional: Passive listener
useCapture: false // Optional: Use capture phase
}
);
this.eventHandlers.push(handlerId);
Registering Element-Specific Events
For events that don't bubble (e.g., mouseenter, mouseleave, load, error):
// Register element-specific event
const handlerId = EventManager.registerElementEvent(
this.pid,
element, // HTMLElement
'mouseenter', // Event type
(event, eventContext) => {
// Handler function
element.style.backgroundColor = 'blue';
},
{
once: false, // Optional
passive: false // Optional
}
);
this.eventHandlers.push(handlerId);
Event Context API
EventManager.registerEventHandler(this.pid, 'click', (e, ctx) => {
// Stop event propagation
ctx.stopPropagation();
// Stop immediate propagation
ctx.stopImmediatePropagation();
// Prevent default behavior
ctx.preventDefault();
// Check if propagation was stopped
if (ctx.stopped) {
// Event propagation was stopped
}
// Check if default was prevented
if (ctx.prevented) {
// Default behavior was prevented
}
// Get current handler info
const handlerInfo = ctx.currentHandler;
});
Unregistering Event Handlers
// Unregister specific handler EventManager.unregisterEventHandler(handlerId); // Unregister all handlers for PID (usually done automatically by ProcessManager) EventManager.unregisterAllHandlersForPid(this.pid);
Note: ProcessManager automatically calls unregisterAllHandlersForPid when a process exits, so manual cleanup in __exit__ is optional but recommended for clarity.
Event Priority
Events are executed by priority (lower number = higher priority):
- •Priority 1-10: System core events (e.g., context menu, window controls)
- •Priority 11-30: Window management events (e.g., drag, resize)
- •Priority 31-50: Menu and popup events
- •Priority 51-100: Application events (default priority)
- •Priority 101+: Low priority events
Common Event Patterns
Button Click
const button = this.window.querySelector('.my-button');
if (button) {
const handlerId = EventManager.registerElementEvent(
this.pid,
button,
'click',
(e) => {
e.stopPropagation();
this._handleButtonClick();
}
);
this.eventHandlers.push(handlerId);
}
Form Submission
const form = this.window.querySelector('.my-form');
if (form) {
const handlerId = EventManager.registerEventHandler(
this.pid,
'submit',
(e, ctx) => {
ctx.preventDefault();
this._handleFormSubmit(e);
return false; // Also prevent default
},
{
selector: '.my-form',
priority: 50
}
);
this.eventHandlers.push(handlerId);
}
Keyboard Shortcuts
const handlerId = EventManager.registerEventHandler(
this.pid,
'keydown',
(e, ctx) => {
// Ctrl+S: Save
if (e.ctrlKey && e.key === 's') {
ctx.preventDefault();
this._save();
return false;
}
// Escape: Close
if (e.key === 'Escape') {
this._onCloseRequest();
return false;
}
},
{
priority: 10 // High priority
}
);
this.eventHandlers.push(handlerId);
Context Menu
const handlerId = EventManager.registerEventHandler(
this.pid,
'contextmenu',
(e, ctx) => {
ctx.preventDefault();
this._showContextMenu(e.clientX, e.clientY);
return false;
},
{
selector: '.my-content',
priority: 5 // High priority
}
);
this.eventHandlers.push(handlerId);
Theme Compatibility
Using CSS Theme Variables
Always use CSS theme variables for colors and styles to ensure compatibility with system themes:
.myapp-window {
background: var(--theme-background-elevated, rgba(37, 43, 53, 0.98));
border: 1px solid var(--theme-border, rgba(139, 92, 246, 0.3));
color: var(--theme-text, #d7e0dd);
}
.myapp-button {
background: var(--theme-primary, #8b5cf6);
color: var(--theme-text-on-primary, #ffffff);
border: 1px solid var(--theme-primary-dark, #7c3aed);
}
.myapp-button:hover {
background: var(--theme-primary-hover, #7c3aed);
}
.myapp-button:active {
background: var(--theme-primary-dark, #6d28d9);
}
.myapp-success {
color: var(--theme-success, #10b981);
}
.myapp-warning {
color: var(--theme-warning, #f59e0b);
}
.myapp-error {
color: var(--theme-error, #ef4444);
}
Available Theme Variables
Background Colors:
- •
--theme-background - •
--theme-background-secondary - •
--theme-background-tertiary - •
--theme-background-elevated
Text Colors:
- •
--theme-text - •
--theme-text-secondary - •
--theme-text-muted - •
--theme-text-on-primary
Primary Colors:
- •
--theme-primary - •
--theme-primary-light - •
--theme-primary-dark - •
--theme-primary-hover - •
--theme-secondary
Status Colors:
- •
--theme-success - •
--theme-warning - •
--theme-error - •
--theme-info
Border:
- •
--theme-border
Style Variables:
- •
--style-window-border-radius - •
--style-window-backdrop-filter - •
--style-window-box-shadow-focused
Listening to Theme Changes
__init__: async function(pid, initArgs) {
// ... initialization code ...
// Listen to theme changes
if (typeof ThemeManager !== 'undefined') {
this._themeUnsubscribe = ThemeManager.onThemeChange((themeId, theme) => {
this._updateTheme(theme);
});
}
},
__exit__: function() {
// ... cleanup code ...
// Unsubscribe from theme changes
if (this._themeUnsubscribe && typeof this._themeUnsubscribe === 'function') {
this._themeUnsubscribe();
this._themeUnsubscribe = null;
}
},
_updateTheme: function(theme) {
// Update UI based on theme
if (this.window) {
// Theme variables are automatically updated, but you can also
// manually update styles if needed
this.window.style.backgroundColor = theme.colors.backgroundElevated;
}
}
Kernel API Calls
Using Process-Bound Kernel API (Recommended)
The initArgs.kernelAPI provides a process-bound kernel API that prevents PID spoofing and is more secure:
__init__: async function(pid, initArgs) {
this.pid = pid;
this._kernelAPI = (initArgs && initArgs.kernelAPI) || null;
// Use kernel API
if (this._kernelAPI && typeof this._kernelAPI.call === 'function') {
try {
const result = await this._kernelAPI.call('FileSystem.readFile', [
'D:/path/to/file.txt'
]);
console.log('File content:', result);
} catch (e) {
if (typeof KernelLogger !== 'undefined') {
KernelLogger.error('MYAPP', '读取文件失败: ' + e.message);
}
}
}
}
Common Kernel APIs for GUI Programs
File System:
// Read file
const content = await this._kernelAPI.call('FileSystem.readFile', ['D:/path/to/file.txt']);
// Write file
await this._kernelAPI.call('FileSystem.writeFile', ['D:/path/to/file.txt', 'content']);
// List directory
const files = await this._kernelAPI.call('FileSystem.listDirectory', ['D:/path/to/dir']);
Process Management:
// Request self-termination
await this._kernelAPI.call('Process.requestSelfTermination', []);
// Request background mode
await this._kernelAPI.call('Process.requestBackground', []);
// Request foreground mode
await this._kernelAPI.call('Process.requestForeground', []);
// Register background tray click handler
await this._kernelAPI.call('Process.registerBackgroundTrayClick', [callback]);
// Register background tray context menu
await this._kernelAPI.call('Process.registerBackgroundTrayContextMenu', [menuCallback]);
Notifications:
// Create notification
await this._kernelAPI.call('Notification.createNotification', [this.pid, {
title: 'Title',
content: 'Content',
type: 'info', // 'info', 'success', 'warning', 'error', 'snapshot'
duration: 5000 // milliseconds
}]);
Storage:
// Read from LStorage
const value = await this._kernelAPI.call('LStorage.read', ['myapp.setting.key']);
// Write to LStorage
await this._kernelAPI.call('LStorage.write', ['myapp.setting.key', value]);
GUI Dialogs:
// Show alert
await this._kernelAPI.call('GUIManager.showAlert', ['Message', 'Title', 'info']);
// Show confirm
const confirmed = await this._kernelAPI.call('GUIManager.showConfirm', [
'Are you sure?',
'Confirm',
'warning'
]);
// Show prompt
const input = await this._kernelAPI.call('GUIManager.showPrompt', [
'Enter value:',
'Input',
'default value'
]);
Permissions
Required Permissions
GUI programs must declare required permissions in __info__():
permissions: typeof PermissionManager !== 'undefined' ? [
PermissionManager.PERMISSION.GUI_WINDOW_CREATE, // Required for GUI programs
PermissionManager.PERMISSION.EVENT_LISTENER, // Required for event handling
PermissionManager.PERMISSION.FILE_READ, // If reading files
PermissionManager.PERMISSION.FILE_WRITE, // If writing files
PermissionManager.PERMISSION.PROCESS_BACKGROUND, // If supporting background mode
PermissionManager.PERMISSION.NETWORK_REQUEST, // If making network requests
// ... other permissions as needed
] : []
Permission Levels
- •NORMAL: Auto-granted, no user confirmation
- •SPECIAL: Requires user confirmation
- •DANGEROUS: Requires admin approval
Common Permissions
- •
GUI_WINDOW_CREATE: Create GUI windows (required for GUI programs) - •
EVENT_LISTENER: Register event handlers (required for GUI programs) - •
FILE_READ: Read files - •
FILE_WRITE: Write files - •
PROCESS_BACKGROUND: Run in background mode - •
NETWORK_REQUEST: Make network requests - •
LSTORAGE_READ: Read from LStorage - •
LSTORAGE_WRITE: Write to LStorage - •
NOTIFICATION_CREATE: Create notifications
Best Practices
1. Window Lifecycle
- •Always register window with
GUIManagerin__init__ - •Always unregister window in
__exit__ - •Store
windowIdfor later reference - •Handle
onClosecallback properly (cleanup only, don't callunregisterWindow)
2. Event Handling
- •Always use
EventManagerfor event handling (mandatory requirement) - •Store event handler IDs in an array for cleanup
- •Unregister all event handlers in
__exit__ - •Use appropriate priorities for event handlers
- •Use
selectoroption to limit event scope when possible
3. Resource Cleanup
- •Clean up all event handlers in
__exit__ - •Unregister window from
GUIManager - •Remove window from DOM
- •Clear all references (set to
null) - •Cancel any timers (
setInterval,setTimeout) - •Unsubscribe from theme changes, language changes, etc.
4. Error Handling
- •Always wrap kernel API calls in
try-catch - •Use
KernelLoggerfor logging (never useconsole.log) - •Report errors using
Exception.report()if needed - •Handle missing dependencies gracefully
5. Theme Compatibility
- •Always use CSS theme variables for colors
- •Provide fallback values for theme variables
- •Listen to theme changes if UI needs dynamic updates
- •Test with different themes
6. Background Mode
- •Consider supporting background mode for long-running applications
- •Register tray click handler to restore window
- •Provide context menu for background processes
- •Handle window restoration properly
7. Performance
- •Avoid blocking operations in event handlers
- •Use
requestAnimationFramefor animations - •Debounce/throttle frequent events (scroll, resize, etc.)
- •Lazy load heavy content
8. Accessibility
- •Use semantic HTML elements
- •Provide keyboard shortcuts
- •Support keyboard navigation
- •Use ARIA attributes when appropriate
Common Patterns
Pattern 1: Simple Window
__init__: async function(pid, initArgs) {
this.pid = pid;
this._kernelAPI = (initArgs && initArgs.kernelAPI) || null;
const guiContainer = initArgs.guiContainer || document.getElementById('gui-container');
this.window = document.createElement('div');
this.window.className = 'myapp-window zos-gui-window';
this.window.dataset.pid = String(pid);
if (typeof GUIManager !== 'undefined') {
const windowInfo = GUIManager.registerWindow(pid, this.window, {
title: 'My App',
onClose: () => this._exit()
});
if (windowInfo) this.windowId = windowInfo.windowId;
}
this.window.innerHTML = '<div>Hello, World!</div>';
guiContainer.appendChild(this.window);
}
Pattern 2: Window with Controls
_buildUI: function() {
// Toolbar
const toolbar = document.createElement('div');
toolbar.className = 'myapp-toolbar';
const btnSave = document.createElement('button');
btnSave.textContent = 'Save';
btnSave.className = 'myapp-btn myapp-btn-save';
toolbar.appendChild(btnSave);
// Content area
const content = document.createElement('div');
content.className = 'myapp-content';
content.textContent = 'Content here';
this.window.appendChild(toolbar);
this.window.appendChild(content);
// Register button click
if (typeof EventManager !== 'undefined') {
const handlerId = EventManager.registerElementEvent(
this.pid,
btnSave,
'click',
() => this._save()
);
this.eventHandlers.push(handlerId);
}
}
Pattern 3: Form Handling
_buildForm: function() {
const form = document.createElement('form');
form.className = 'myapp-form';
const input = document.createElement('input');
input.type = 'text';
input.name = 'username';
form.appendChild(input);
const submitBtn = document.createElement('button');
submitBtn.type = 'submit';
submitBtn.textContent = 'Submit';
form.appendChild(submitBtn);
this.window.appendChild(form);
// Register form submit
if (typeof EventManager !== 'undefined') {
const handlerId = EventManager.registerEventHandler(
this.pid,
'submit',
(e, ctx) => {
ctx.preventDefault();
this._handleSubmit(new FormData(form));
return false;
},
{ selector: '.myapp-form' }
);
this.eventHandlers.push(handlerId);
}
}
Pattern 4: Loading State
_showLoading: function() {
const loading = document.createElement('div');
loading.className = 'myapp-loading';
loading.innerHTML = '<div class="spinner"></div><div>Loading...</div>';
this.window.appendChild(loading);
},
_hideLoading: function() {
const loading = this.window.querySelector('.myapp-loading');
if (loading) {
loading.remove();
}
},
_loadData: async function() {
this._showLoading();
try {
const data = await this._kernelAPI.call('FileSystem.readFile', ['D:/data.json']);
this._renderData(data);
} catch (e) {
this._showError(e.message);
} finally {
this._hideLoading();
}
}
File Location and Naming
Directory Structure
system/service/DISK/D/application/ ├── myapp/ │ ├── myapp.js # Main program file │ ├── myapp.css # Styles (optional) │ └── assets/ # Assets (optional) │ ├── icon.svg │ └── images/
Naming Conventions
- •Directory name: Lowercase, descriptive (e.g.,
myapp,filemanager) - •Main file: Same as directory name with
.jsextension (e.g.,myapp.js) - •CSS file: Same as directory name with
.cssextension (e.g.,myapp.css) - •Global object: Uppercase, same as directory name (e.g.,
MYAPP)
Testing
Manual Testing Checklist
- •
Window Creation:
- • Window appears correctly
- • Window title is correct
- • Window icon is displayed (if provided)
- • Window can be dragged
- • Window can be resized
- • Window can be minimized/maximized/closed
- •
Event Handling:
- • All buttons/links work correctly
- • Keyboard shortcuts work
- • Context menu appears (if implemented)
- • Form submission works
- •
Theme Compatibility:
- • UI adapts to theme changes
- • Colors use theme variables
- • UI looks good in different themes
- •
Background Mode:
- • Window can go to background
- • Tray icon appears
- • Clicking tray icon restores window
- • Context menu works (if implemented)
- •
Resource Cleanup:
- • No memory leaks when closing window
- • Event handlers are cleaned up
- • Timers are cleared
- • No console errors
- •
Error Handling:
- • Errors are handled gracefully
- • Error messages are displayed to user
- • Program doesn't crash on errors
Common Issues
Issue 1: Window Not Appearing
Symptoms: Window element is created but not visible.
Solutions:
- •Check if
guiContainerexists:document.getElementById('gui-container') - •Ensure window element is appended to
guiContainer - •Check CSS: window element needs
position: fixedorposition: absolute - •Verify
GUIManager.registerWindow()succeeded
Issue 2: Events Not Working
Symptoms: Click handlers, keyboard shortcuts, etc. don't work.
Solutions:
- •Ensure using
EventManager(notaddEventListener) - •Check if permissions include
EVENT_LISTENER - •Verify event handler IDs are stored for cleanup
- •Check event priority (might be blocked by higher priority handler)
Issue 3: Memory Leaks
Symptoms: Memory usage increases over time, program slows down.
Solutions:
- •Ensure all event handlers are unregistered in
__exit__ - •Clear all timers (
setInterval,setTimeout) - •Remove all DOM references (set to
null) - •Unsubscribe from theme/language change listeners
Issue 4: Theme Not Applied
Symptoms: UI doesn't match system theme.
Solutions:
- •Use CSS theme variables (
var(--theme-...)) - •Provide fallback values for theme variables
- •Listen to theme changes if UI needs dynamic updates
- •Test with different themes
Issue 5: Background Mode Not Working
Symptoms: Window closes instead of going to background.
Solutions:
- •Set
winInfo._backgroundRequested = trueinonClose - •Hide window (
window.style.display = 'none') - •Call
Process.requestBackgroundvia kernel API - •Register tray click handler with
Process.registerBackgroundTrayClick
Issue 6: Permission Denied
Symptoms: Kernel API calls fail with permission error.
Solutions:
- •Check if permission is declared in
__info__().permissions - •Verify permission level (NORMAL/SPECIAL/DANGEROUS)
- •Check if user granted permission (for SPECIAL/DANGEROUS)
- •Use process-bound kernel API (
initArgs.kernelAPI) instead of direct calls
References
API Documentation
- •GUIManager API - Window management
- •EventManager API - Event handling
- •ThemeManager API - Theme management
- •ProcessManager API - Process management
- •PermissionManager API - Permission system
Developer Guides
- •Developer Guide - General development guide
- •System Flow - System processes and flows
- •Kernel Developer Guide - Kernel module development
Example Programs
- •
system/service/DISK/D/application/servicemanager/servicemanager.js- Complex GUI application - •
system/service/DISK/D/application/hellogui/hellogui.js- Simple GUI with background mode - •
system/service/DISK/D/application/taskmanager/taskmanager.js- Task manager GUI - •
system/service/DISK/D/application/zeroide/zeroide.js- IDE GUI application