10388 lines
780 KiB
HTML
10388 lines
780 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<title>Welcome to DVX</title>
|
|
<style>
|
|
body { font-family: sans-serif; margin: 0; padding: 0; display: flex; }
|
|
nav { width: 250px; min-width: 250px; background: #f0f0f0; padding: 16px;
|
|
border-right: 1px solid #ccc; height: 100vh; overflow-y: auto;
|
|
position: sticky; top: 0; box-sizing: border-box; }
|
|
nav ul { list-style: none; padding-left: 16px; margin: 4px 0; }
|
|
nav > ul { padding-left: 0; }
|
|
nav a { text-decoration: none; color: #0066cc; }
|
|
nav a:hover { text-decoration: underline; }
|
|
main { flex: 1; padding: 24px 32px; max-width: 800px; }
|
|
h1 { border-bottom: 2px solid #333; padding-bottom: 4px; }
|
|
h2 { border-bottom: 1px solid #999; padding-bottom: 2px; margin-top: 32px; }
|
|
h3 { margin-top: 24px; }
|
|
pre { background: #f8f8f8; border: 1px solid #ddd; padding: 8px;
|
|
overflow-x: auto; font-size: 14px; }
|
|
blockquote { background: #fffde7; border-left: 4px solid #ffc107;
|
|
padding: 8px 12px; margin: 12px 0; }
|
|
hr { border: none; border-top: 1px solid #ccc; margin: 24px 0; }
|
|
img { max-width: 100%; }
|
|
.topic { margin-bottom: 48px; }
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<nav>
|
|
<h3>Contents</h3>
|
|
<ul>
|
|
<li><a href="#user.welcome">Welcome to DVX</a></li>
|
|
<li><a href="#user.start">Getting Started</a></li>
|
|
<li><a href="#user.input">Using the Mouse and Keyboard</a></li>
|
|
<li><a href="#user.windows">Working with Windows</a></li>
|
|
<li><a href="#user.menus">Menus and Dialog Boxes</a>
|
|
<ul>
|
|
<li><a href="#user.controls">Buttons, Lists and Other Controls</a></li>
|
|
<li><a href="#user.filedlg">Opening and Saving Files</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#user.clipboard">Copying and Pasting</a></li>
|
|
<li><a href="#user.printing">Printing</a></li>
|
|
<li><a href="#user.programs">Running Programs</a>
|
|
<ul>
|
|
<li><a href="#user.progman">The Program Manager</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#user.settings">Changing Settings</a></li>
|
|
<li><a href="#user.taskmgr">Task Manager</a></li>
|
|
<li><a href="#user.help">Getting Help</a></li>
|
|
<li><a href="#user.keys">Keyboard Shortcuts</a></li>
|
|
<li><a href="#user.trouble">Troubleshooting</a></li>
|
|
<li><a href="#user.developers">For Developers</a></li>
|
|
<li><strong>Technical Reference (for Developers)</strong>
|
|
<ul>
|
|
<li><a href="#sys.welcome">Developer Overview</a></li>
|
|
<li><strong>Architecture</strong>
|
|
<ul>
|
|
<li><a href="#arch.overview">System Overview</a>
|
|
<ul>
|
|
<li><a href="#arch.layers">Five-Layer Architecture</a></li>
|
|
<li><a href="#arch.pipeline">Display Pipeline</a></li>
|
|
<li><a href="#arch.windows">Window System</a></li>
|
|
<li><a href="#arch.widgets">Widget System</a></li>
|
|
<li><a href="#arch.dialogs">Modal Dialogs</a></li>
|
|
<li><a href="#arch.resources">Resource System</a></li>
|
|
<li><a href="#arch.prefs">Preferences System</a></li>
|
|
<li><a href="#arch.memtrack">Per-App Memory Tracking</a></li>
|
|
<li><a href="#arch.dxe">DXE Module System</a></li>
|
|
<li><a href="#arch.events">Event Model</a></li>
|
|
<li><a href="#arch.fonts">Font System</a></li>
|
|
<li><a href="#arch.colors">Color System</a></li>
|
|
<li><a href="#arch.platform">Platform Layer</a></li>
|
|
<li><a href="#arch.build">Build System</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#api.overview">DVX GUI API Reference</a>
|
|
<ul>
|
|
<li><a href="#api.types">dvxTypes.h -- Shared Type Definitions</a></li>
|
|
<li><a href="#api.cursor">dvxCur.h -- Cursor Definitions</a></li>
|
|
<li><a href="#api.font">dvxFont.h -- Bitmap Font Data</a></li>
|
|
<li><a href="#api.palette">dvxPal.h -- 8-bit Palette</a></li>
|
|
<li><a href="#api.video">dvxVideo.h -- Layer 1: Video Backend</a></li>
|
|
<li><a href="#api.draw">dvxDraw.h -- Layer 2: Drawing Primitives</a></li>
|
|
<li><a href="#api.comp">dvxComp.h -- Layer 3: Compositor</a></li>
|
|
<li><a href="#api.wm">dvxWm.h -- Layer 4: Window Manager</a></li>
|
|
<li><a href="#api.app">dvxApp.h -- Layer 5: Application API</a></li>
|
|
<li><a href="#api.driver">dvxDrv.h -- Driver Modules</a></li>
|
|
<li><a href="#api.widget">dvxWgt.h -- Widget System</a></li>
|
|
<li><a href="#api.dialog">dvxDlg.h -- Modal Dialogs</a></li>
|
|
<li><a href="#api.resource">dvxRes.h -- Resource System</a></li>
|
|
<li><a href="#api.prefs">dvxPrefs.h -- Preferences System</a></li>
|
|
<li><a href="#api.mem">dvxMem.h -- Memory Tracking</a></li>
|
|
<li><a href="#api.widgetplugin">dvxWgtP.h -- Widget Plugin API</a></li>
|
|
<li><a href="#api.platform">platform/dvxPlat.h -- Platform Layer</a></li>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
</li>
|
|
<li><strong>Libraries</strong>
|
|
<ul>
|
|
<li><a href="#lib.audio">Sound Library</a></li>
|
|
<li><a href="#lib.basrt">BASIC Runtime Library</a>
|
|
<ul>
|
|
<li><a href="#lib.basrt.values">Value System</a></li>
|
|
<li><a href="#lib.basrt.vm">Virtual Machine</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#lib.conn">Connection Library</a></li>
|
|
<li><a href="#lib.dvxnet">Networking Library</a>
|
|
<ul>
|
|
<li><a href="#lib.dvxnet.api">The Socket API</a></li>
|
|
<li><a href="#lib.dvxnet.waiting">Waiting and Multitasking</a></li>
|
|
<li><a href="#lib.dvxnet.config">Settings and Status</a></li>
|
|
<li><a href="#lib.dvxnet.inside">How It Works</a></li>
|
|
<li><a href="#lib.dvxnet.basic">BASIC Functions</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#shell.overview">DVX Shell Library</a>
|
|
<ul>
|
|
<li><a href="#shell.types">Types and Constants</a></li>
|
|
<li><a href="#shell.lifecycle">Lifecycle API</a></li>
|
|
<li><a href="#shell.query">Query API</a></li>
|
|
<li><a href="#shell.startup">Starting Up</a></li>
|
|
<li><a href="#shell.config">Configuration API</a></li>
|
|
<li><a href="#shell.desktop">Desktop Callbacks</a></li>
|
|
<li><a href="#shell.info">System Information</a></li>
|
|
<li><a href="#shell.programs">Installed Programs</a></li>
|
|
<li><a href="#shell.run">Running Programs and Documents</a></li>
|
|
<li><a href="#shell.assoc">File Associations</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#sql.overview">dvxSql -- SQL Database Interface</a>
|
|
<ul>
|
|
<li><a href="#sql.db">Database Operations</a></li>
|
|
<li><a href="#sql.cursor">Cursor Operations</a></li>
|
|
<li><a href="#sql.utility">Utility Functions</a></li>
|
|
<li><a href="#sql.example">Example Usage</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#lib.fileops">File Operations Library</a></li>
|
|
<li><a href="#lib.game">Game Helpers Library</a></li>
|
|
<li><a href="#lib.joystick">Joystick Library</a></li>
|
|
<li><a href="#libtasks">libtasks -- Cooperative Task Switching</a></li>
|
|
<li><a href="#lib.listhelp">List Helper Library</a></li>
|
|
<li><a href="#lib.music">Music Library</a></li>
|
|
<li><a href="#lib.print">Printing Library</a></li>
|
|
<li><a href="#lib.serial">Serial Stack</a>
|
|
<ul>
|
|
<li><a href="#lib.serial.rs232">RS-232 UART Driver</a></li>
|
|
<li><a href="#lib.serial.packet">Packet Transport</a></li>
|
|
<li><a href="#lib.serial.security">Security (DH, XTEA, SHA-256)</a></li>
|
|
<li><a href="#lib.serial.seclink">Secure Link</a></li>
|
|
<li><a href="#lib.serial.example">End-to-End Example</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#lib.taskmgr">Task Manager</a></li>
|
|
<li><a href="#lib.texthelp">Text Help Library</a>
|
|
<ul>
|
|
<li><a href="#lib.texthelp.cursorblink">wgtUpdateCursorBlink</a></li>
|
|
<li><a href="#lib.texthelp.clearother">clearOtherSelections</a></li>
|
|
<li><a href="#lib.texthelp.iswordchar">isWordChar</a></li>
|
|
<li><a href="#lib.texthelp.wordend">wordEnd</a></li>
|
|
<li><a href="#lib.texthelp.wordstart">wordStart</a></li>
|
|
<li><a href="#lib.texthelp.wordboundaryleft">wordBoundaryLeft</a></li>
|
|
<li><a href="#lib.texthelp.wordboundaryright">wordBoundaryRight</a></li>
|
|
<li><a href="#lib.texthelp.saveundo">textEditSaveUndo</a></li>
|
|
<li><a href="#lib.texthelp.undoswap">textEditUndoSwap</a></li>
|
|
<li><a href="#lib.texthelp.single">TextEditSingleT</a></li>
|
|
<li><a href="#lib.texthelp.dragupdate">widgetTextEditDragUpdateLine</a></li>
|
|
<li><a href="#lib.texthelp.mouseclick">widgetTextEditMouseClick</a></li>
|
|
<li><a href="#lib.texthelp.onkey">widgetTextEditOnKey</a></li>
|
|
<li><a href="#lib.texthelp.paintline">widgetTextEditPaintLine</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="#lib.xfer">File Transfer Library</a>
|
|
<ul>
|
|
<li><a href="#lib.xfer.api">C API</a></li>
|
|
<li><a href="#lib.xfer.protocols">Protocol Details</a></li>
|
|
<li><a href="#lib.xfer.basic">BASIC Functions</a></li>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
</li>
|
|
<li><strong>Writing Games for DVX</strong>
|
|
<ul>
|
|
<li><a href="#games.intro">Writing Games for DVX</a>
|
|
<ul>
|
|
<li><a href="#games.input">Keyboard, Mouse and Joysticks</a></li>
|
|
<li><a href="#games.timing">Timing and Frame Pacing</a></li>
|
|
<li><a href="#games.screen">The Screen: Framebuffers and Palettes</a></li>
|
|
<li><a href="#games.sound">Sound, Music and MIDI</a></li>
|
|
<li><a href="#games.files">Data Files and Saved Games</a></li>
|
|
<li><a href="#games.packaging">Packaging and Ending</a></li>
|
|
<li><a href="#games.basic">Games in DVX BASIC</a></li>
|
|
<li><a href="#games.porting">Porting a Game Library to DVX</a></li>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
</li>
|
|
<li><strong>SDK Quick Start</strong>
|
|
<ul>
|
|
<li><a href="#sdk.quickstart">SDK Quick Start</a>
|
|
<ul>
|
|
<li><a href="#sdk.firstapp">Your First DVX Program</a></li>
|
|
<li><a href="#sdk.firstwidget">Your First Widget</a></li>
|
|
<li><a href="#sdk.firstlib">Your First Library</a></li>
|
|
<li><a href="#sdk.firstbasic">Your First BASIC Program</a></li>
|
|
<li><a href="#sdk.debug">Debugging: the Log and Crash Reports</a></li>
|
|
<li><a href="#sdk.abi">Binary Interface Rules</a></li>
|
|
<li><a href="#sdk.packaging">Packaging and Installing</a></li>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
</li>
|
|
<li><strong>Widgets</strong>
|
|
<ul>
|
|
<li><a href="#widget.base">Widget System</a></li>
|
|
<li><a href="#widget.ansiterm">AnsiTerm</a></li>
|
|
<li><a href="#widget.box">Layout Containers</a></li>
|
|
<li><a href="#widget.box">Box (VBox / HBox / Frame)</a></li>
|
|
<li><a href="#widget.button">Button</a></li>
|
|
<li><a href="#widget.canvas">Canvas</a></li>
|
|
<li><a href="#widget.checkbox">Checkbox</a></li>
|
|
<li><a href="#widget.combobox">ComboBox</a></li>
|
|
<li><a href="#widget.datactrl">Data Widgets</a></li>
|
|
<li><a href="#widget.datactrl">DataCtrl</a></li>
|
|
<li><a href="#widget.dbgrid">DbGrid</a></li>
|
|
<li><a href="#widget.dropdown">Dropdown</a></li>
|
|
<li><a href="#widget.imagebutton">ImageButton</a></li>
|
|
<li><a href="#widget.image">Display Widgets</a></li>
|
|
<li><a href="#widget.image">Image</a></li>
|
|
<li><a href="#widget.label">Label</a></li>
|
|
<li><a href="#widget.listbox">ListBox</a></li>
|
|
<li><a href="#widget.listview">ListView</a></li>
|
|
<li><a href="#widget.progressbar">ProgressBar</a></li>
|
|
<li><a href="#widget.radio">Radio Button</a></li>
|
|
<li><a href="#widget.scrollpane">ScrollPane</a></li>
|
|
<li><a href="#widget.separator">Separator</a></li>
|
|
<li><a href="#widget.slider">Slider</a></li>
|
|
<li><a href="#widget.spacer">Spacer</a></li>
|
|
<li><a href="#widget.spinner">Spinner</a></li>
|
|
<li><a href="#widget.splitter">Splitter</a></li>
|
|
<li><a href="#widget.statusbar">StatusBar</a></li>
|
|
<li><a href="#widget.tabcontrol">TabControl</a></li>
|
|
<li><a href="#widget.textinput">Input Widgets</a></li>
|
|
<li><a href="#widget.textinput">TextInput / TextArea</a></li>
|
|
<li><a href="#widget.timer">Non-Visual Widgets</a></li>
|
|
<li><a href="#widget.timer">Timer</a></li>
|
|
<li><a href="#widget.toolbar">Container Widgets</a></li>
|
|
<li><a href="#widget.toolbar">Toolbar</a></li>
|
|
<li><a href="#widget.treeview">TreeView</a></li>
|
|
<li><a href="#widget.wrapbox">WrapBox</a></li>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
</li>
|
|
</ul>
|
|
<h3>Index</h3>
|
|
<ul>
|
|
<li><a href="#sdk.firstlib">.dep</a></li>
|
|
<li><a href="#shell.startup">/RUN=</a></li>
|
|
<li><a href="#shell.startup">/SAFE</a></li>
|
|
<li><a href="#games.screen">8-bit desktop</a></li>
|
|
<li><a href="#api.palette">8-bit mode</a></li>
|
|
<li><a href="#sdk.abi">ABI</a></li>
|
|
<li><a href="#arch.dxe">ABI stamp</a></li>
|
|
<li><a href="#api.types">ABI version</a></li>
|
|
<li><a href="#games.packaging">abort</a></li>
|
|
<li><a href="#user.programs">About</a></li>
|
|
<li><a href="#api.types">AccelEntryT</a></li>
|
|
<li><a href="#arch.events">Accelerator Tables</a></li>
|
|
<li><a href="#api.draw">accelParse</a></li>
|
|
<li><a href="#api.types">AccelTableT</a></li>
|
|
<li><a href="#lib.dvxnet.api">accept</a></li>
|
|
<li><a href="#user.windows">Alt+Tab</a></li>
|
|
<li><a href="#widget.ansiterm">AnsiTerm</a></li>
|
|
<li><a href="#widget.ansiterm">AnsiTermExApiT</a></li>
|
|
<li><a href="#api.overview">API Reference</a></li>
|
|
<li><a href="#api.app">AppContextT</a></li>
|
|
<li><a href="#shell.types">AppDescriptorT</a></li>
|
|
<li><a href="#games.files">appDir</a></li>
|
|
<li><a href="#user.programs">Applications</a></li>
|
|
<li><a href="#sdk.firstapp">appMain</a></li>
|
|
<li><a href="#shell.types">AppStateE</a></li>
|
|
<li><a href="#arch.overview">Architecture</a></li>
|
|
<li><a href="#user.windows">Arranging Windows</a></li>
|
|
<li><a href="#arch.platform">Assembly</a></li>
|
|
<li><a href="#shell.assoc">ASSOC.INI</a></li>
|
|
<li><a href="#lib.audio">audio</a></li>
|
|
<li><a href="#lib.audio">audio.h</a></li>
|
|
<li><a href="#lib.audio">audioClose</a></li>
|
|
<li><a href="#lib.audio">audioDeskPlay</a></li>
|
|
<li><a href="#lib.audio">audioDeskPlayFile</a></li>
|
|
<li><a href="#lib.audio">audioDriverNames</a></li>
|
|
<li><a href="#lib.audio">AudioDriverT</a></li>
|
|
<li><a href="#lib.audio">audioDrv.h</a></li>
|
|
<li><a href="#lib.audio">audioFmAlloc</a></li>
|
|
<li><a href="#lib.audio">audioFmRawAcquire</a></li>
|
|
<li><a href="#lib.audio">audioFmWrite</a></li>
|
|
<li><a href="#lib.audio">audioGetInfo</a></li>
|
|
<li><a href="#lib.audio">AudioInfoT</a></li>
|
|
<li><a href="#lib.audio">audioMidiAcquire</a></li>
|
|
<li><a href="#lib.audio">audioNotesAdd</a></li>
|
|
<li><a href="#lib.audio">audioNotesOpen</a></li>
|
|
<li><a href="#lib.audio">AudioNotesT</a></li>
|
|
<li><a href="#lib.audio">audioOpen</a></li>
|
|
<li><a href="#lib.audio">audioPlay</a></li>
|
|
<li><a href="#lib.audio">audioPlayFile</a></li>
|
|
<li><a href="#games.sound">audioPump</a></li>
|
|
<li><a href="#lib.audio">audioSampleLoad</a></li>
|
|
<li><a href="#lib.audio">audioStreamOpen</a></li>
|
|
<li><a href="#lib.audio">audioStreamWrite</a></li>
|
|
<li><a href="#lib.audio">AudioSynthDriverT</a></li>
|
|
<li><a href="#lib.audio">audioTone</a></li>
|
|
<li><a href="#user.help">Back</a></li>
|
|
<li><a href="#arch.pipeline">Backbuffer</a></li>
|
|
<li><a href="#lib.basrt.values">basArrayNew</a></li>
|
|
<li><a href="#lib.basrt">BasArrayT</a></li>
|
|
<li><a href="#lib.basrt.values">BasArrayT</a></li>
|
|
<li><a href="#sdk.firstbasic">bascomp</a></li>
|
|
<li><a href="#lib.basrt">BASIC Runtime</a></li>
|
|
<li><a href="#games.intro">basicgame template</a></li>
|
|
<li><a href="#lib.basrt">BasModuleT</a></li>
|
|
<li><a href="#lib.basrt.vm">BasModuleT</a></li>
|
|
<li><a href="#lib.basrt.values">basStringNew</a></li>
|
|
<li><a href="#lib.basrt">BasStringT</a></li>
|
|
<li><a href="#lib.basrt.values">BasStringT</a></li>
|
|
<li><a href="#lib.basrt.values">BasUdtT</a></li>
|
|
<li><a href="#lib.basrt.values">basValCopy</a></li>
|
|
<li><a href="#lib.basrt.values">basValRelease</a></li>
|
|
<li><a href="#lib.basrt">BasValueT</a></li>
|
|
<li><a href="#lib.basrt.values">BasValueT</a></li>
|
|
<li><a href="#lib.basrt.vm">basVmCallSub</a></li>
|
|
<li><a href="#lib.basrt.vm">basVmCreate</a></li>
|
|
<li><a href="#lib.basrt.vm">basVmDestroy</a></li>
|
|
<li><a href="#lib.basrt.vm">BasVmResultE</a></li>
|
|
<li><a href="#lib.basrt.vm">basVmRun</a></li>
|
|
<li><a href="#lib.basrt.vm">basVmStep</a></li>
|
|
<li><a href="#lib.basrt">BasVmT</a></li>
|
|
<li><a href="#lib.basrt.vm">BasVmT</a></li>
|
|
<li><a href="#lib.serial.example">BBS Client</a></li>
|
|
<li><a href="#arch.colors">Bevel</a></li>
|
|
<li><a href="#api.types">BevelStyleT</a></li>
|
|
<li><a href="#sdk.abi">Binary Interface Rules</a></li>
|
|
<li><a href="#arch.fonts">Bitmap Font</a></li>
|
|
<li><a href="#arch.fonts">BitmapFontT</a></li>
|
|
<li><a href="#api.types">BitmapFontT</a></li>
|
|
<li><a href="#api.font">BitmapFontT</a></li>
|
|
<li><a href="#arch.pipeline">BlitOpsT</a></li>
|
|
<li><a href="#api.types">BlitOpsT</a></li>
|
|
<li><a href="#arch.dxe">Boot Sequence</a></li>
|
|
<li><a href="#lib.dvxnet">BSD sockets</a></li>
|
|
<li><a href="#arch.build">Build</a></li>
|
|
<li><a href="#widget.button">Button</a></li>
|
|
<li><a href="#user.controls">Buttons</a></li>
|
|
<li><a href="#widget.canvas">Canvas</a></li>
|
|
<li><a href="#user.windows">Cascade</a></li>
|
|
<li><a href="#user.controls">Check Boxes</a></li>
|
|
<li><a href="#widget.checkbox">Checkbox</a></li>
|
|
<li><a href="#arch.windows">Chrome</a></li>
|
|
<li><a href="#lib.texthelp.clearother">clearOtherSelections</a></li>
|
|
<li><a href="#user.input">Clicking</a></li>
|
|
<li><a href="#user.clipboard">Clipboard</a></li>
|
|
<li><a href="#lib.texthelp.onkey">Clipboard</a></li>
|
|
<li><a href="#user.windows">Closing Windows</a></li>
|
|
<li><a href="#api.types">ColorIdE</a></li>
|
|
<li><a href="#user.settings">Colors</a></li>
|
|
<li><a href="#arch.colors">Colors</a></li>
|
|
<li><a href="#arch.colors">ColorSchemeT</a></li>
|
|
<li><a href="#api.types">ColorSchemeT</a></li>
|
|
<li><a href="#lib.serial.rs232">COM Port</a></li>
|
|
<li><a href="#widget.combobox">ComboBox</a></li>
|
|
<li><a href="#widget.base">Common Events</a></li>
|
|
<li><a href="#widget.base">Common Properties</a></li>
|
|
<li><a href="#arch.pipeline">Compositing</a></li>
|
|
<li><a href="#games.files">configDir</a></li>
|
|
<li><a href="#lib.conn">conn</a></li>
|
|
<li><a href="#lib.conn">ConnConfigT</a></li>
|
|
<li><a href="#lib.dvxnet.api">connect</a></li>
|
|
<li><a href="#lib.conn">Connection</a></li>
|
|
<li><a href="#lib.conn">connSetWindowSize</a></li>
|
|
<li><a href="#games.screen">contentBuf</a></li>
|
|
<li><a href="#user.controls">Controls</a></li>
|
|
<li><a href="#arch.events">Cooperative</a></li>
|
|
<li><a href="#lib.dvxnet.waiting">Cooperative multitasking</a></li>
|
|
<li><a href="#libtasks">Cooperative Multitasking</a></li>
|
|
<li><a href="#user.clipboard">Copy and Paste</a></li>
|
|
<li><a href="#arch.fonts">CP437</a></li>
|
|
<li><a href="#api.font">CP437</a></li>
|
|
<li><a href="#arch.dxe">Crash Recovery</a></li>
|
|
<li><a href="#sdk.debug">Crash Report</a></li>
|
|
<li><a href="#lib.xfer.protocols">CRC</a></li>
|
|
<li><a href="#lib.serial.packet">CRC-16</a></li>
|
|
<li><a href="#api.platform">Critical Error Handler</a></li>
|
|
<li><a href="#arch.build">Cross-Compilation</a></li>
|
|
<li><a href="#user.taskmgr">Ctrl+Esc</a></li>
|
|
<li><a href="#arch.events">Cursor</a></li>
|
|
<li><a href="#lib.texthelp.cursorblink">Cursor Blink</a></li>
|
|
<li><a href="#api.cursor">Cursor Shapes</a></li>
|
|
<li><a href="#api.cursor">CURSOR_ARROW</a></li>
|
|
<li><a href="#api.cursor">CURSOR_BUSY</a></li>
|
|
<li><a href="#api.types">CursorT</a></li>
|
|
<li><a href="#api.dialog">Custom Dialogs</a></li>
|
|
<li><a href="#user.settings">Customizing</a></li>
|
|
<li><a href="#user.clipboard">Cut, Copy, Paste</a></li>
|
|
<li><a href="#games.files">Data files</a></li>
|
|
<li><a href="#sql.overview">Database</a></li>
|
|
<li><a href="#widget.datactrl">Database Binding</a></li>
|
|
<li><a href="#widget.dbgrid">Database Grid</a></li>
|
|
<li><a href="#widget.datactrl">DataCtrl</a></li>
|
|
<li><a href="#widget.datactrl">dataCtrlAddNew</a></li>
|
|
<li><a href="#widget.datactrl">dataCtrlDelete</a></li>
|
|
<li><a href="#widget.datactrl">dataCtrlGetField</a></li>
|
|
<li><a href="#widget.datactrl">dataCtrlMoveFirst</a></li>
|
|
<li><a href="#widget.datactrl">dataCtrlMoveNext</a></li>
|
|
<li><a href="#widget.datactrl">dataCtrlRefresh</a></li>
|
|
<li><a href="#widget.datactrl">dataCtrlSetField</a></li>
|
|
<li><a href="#widget.datactrl">dataCtrlUpdate</a></li>
|
|
<li><a href="#widget.dbgrid">DbGrid</a></li>
|
|
<li><a href="#widget.dbgrid">dbGridGetSelectedRow</a></li>
|
|
<li><a href="#widget.dbgrid">dbGridRefresh</a></li>
|
|
<li><a href="#widget.dbgrid">dbGridSetColumnHeader</a></li>
|
|
<li><a href="#widget.dbgrid">dbGridSetColumnVisible</a></li>
|
|
<li><a href="#widget.dbgrid">dbGridSetColumnWidth</a></li>
|
|
<li><a href="#sdk.debug">Debugging</a></li>
|
|
<li><a href="#user.start">Desktop</a></li>
|
|
<li><a href="#user.settings">Desktop Picture</a></li>
|
|
<li><a href="#lib.audio">Desktop sounds</a></li>
|
|
<li><a href="#sys.welcome">Developer Overview</a></li>
|
|
<li><a href="#user.developers">Developers</a></li>
|
|
<li><a href="#lib.conn">Dialer</a></li>
|
|
<li><a href="#user.menus">Dialog Boxes</a></li>
|
|
<li><a href="#arch.dialogs">Dialogs</a></li>
|
|
<li><a href="#user.trouble">Different Version of DVX</a></li>
|
|
<li><a href="#lib.serial">Diffie-Hellman</a></li>
|
|
<li><a href="#lib.serial.security">Diffie-Hellman</a></li>
|
|
<li><a href="#arch.pipeline">Dirty Rects</a></li>
|
|
<li><a href="#api.comp">dirtyListAdd</a></li>
|
|
<li><a href="#api.comp">dirtyListClear</a></li>
|
|
<li><a href="#api.comp">dirtyListInit</a></li>
|
|
<li><a href="#api.comp">dirtyListMerge</a></li>
|
|
<li><a href="#arch.pipeline">DirtyListT</a></li>
|
|
<li><a href="#api.types">DirtyListT</a></li>
|
|
<li><a href="#arch.pipeline">Display Pipeline</a></li>
|
|
<li><a href="#arch.pipeline">DisplayT</a></li>
|
|
<li><a href="#api.types">DisplayT</a></li>
|
|
<li><a href="#arch.overview">DJGPP</a></li>
|
|
<li><a href="#arch.pipeline">Double Buffer</a></li>
|
|
<li><a href="#user.input">Double-click</a></li>
|
|
<li><a href="#arch.events">Double-Click</a></li>
|
|
<li><a href="#arch.overview">DPMI</a></li>
|
|
<li><a href="#lib.texthelp.dragupdate">Drag Select</a></li>
|
|
<li><a href="#user.input">Dragging</a></li>
|
|
<li><a href="#api.draw">drawArrowGlyph</a></li>
|
|
<li><a href="#api.draw">drawBevel</a></li>
|
|
<li><a href="#api.draw">drawChar</a></li>
|
|
<li><a href="#api.draw">drawFocusRect</a></li>
|
|
<li><a href="#api.draw">drawHLine</a></li>
|
|
<li><a href="#api.draw">drawInit</a></li>
|
|
<li><a href="#api.draw">drawMaskedBitmap</a></li>
|
|
<li><a href="#api.draw">drawScrollbarArrow</a></li>
|
|
<li><a href="#api.draw">drawTermRow</a></li>
|
|
<li><a href="#api.draw">drawText</a></li>
|
|
<li><a href="#api.draw">drawTextAccel</a></li>
|
|
<li><a href="#api.draw">drawTextN</a></li>
|
|
<li><a href="#api.draw">drawVLine</a></li>
|
|
<li><a href="#lib.serial.security">DRBG</a></li>
|
|
<li><a href="#api.driver">Driver Modules</a></li>
|
|
<li><a href="#user.filedlg">Drives</a></li>
|
|
<li><a href="#user.controls">Drop-down Lists</a></li>
|
|
<li><a href="#widget.dropdown">Dropdown</a></li>
|
|
<li><a href="#user.welcome">DVX</a></li>
|
|
<li><a href="#arch.overview">DVX</a></li>
|
|
<li><a href="#api.overview">DVX API</a></li>
|
|
<li><a href="#shell.overview">DVX Shell</a></li>
|
|
<li><a href="#user.trouble">DVX.LOG</a></li>
|
|
<li><a href="#sdk.debug">DVX.LOG</a></li>
|
|
<li><a href="#api.types">DVX_ABI_STAMP</a></li>
|
|
<li><a href="#sdk.firstlib">DVX_ABI_STAMP</a></li>
|
|
<li><a href="#arch.dxe">DVX_ABI_VERSION</a></li>
|
|
<li><a href="#api.types">DVX_ABI_VERSION</a></li>
|
|
<li><a href="#sdk.abi">DVX_ABI_VERSION</a></li>
|
|
<li><a href="#api.dialog">DVX_BUTTON_BOX_MAX</a></li>
|
|
<li><a href="#api.prefs">DVX_CONFIG_DIR</a></li>
|
|
<li><a href="#api.driver">DVX_DRIVER_HDR</a></li>
|
|
<li><a href="#api.app">DVX_HOTKEY_NONE</a></li>
|
|
<li><a href="#api.app">DVX_HUNG_APP_MS</a></li>
|
|
<li><a href="#api.app">dvxAddAccel</a></li>
|
|
<li><a href="#arch.layers">dvxApp</a></li>
|
|
<li><a href="#api.app">dvxApp.h</a></li>
|
|
<li><a href="#api.app">dvxAppHungForget</a></li>
|
|
<li><a href="#api.app">dvxAppIsHung</a></li>
|
|
<li><a href="#api.app">dvxApplyColorScheme</a></li>
|
|
<li><a href="#api.app">dvxBeep</a></li>
|
|
<li><a href="#api.dialog">dvxButtonBox</a></li>
|
|
<li><a href="#api.mem">dvxCalloc</a></li>
|
|
<li><a href="#api.app">dvxCascadeWindows</a></li>
|
|
<li><a href="#api.app">dvxChangeVideoMode</a></li>
|
|
<li><a href="#api.dialog">dvxChoiceDialog</a></li>
|
|
<li><a href="#api.app">dvxClipboardClear</a></li>
|
|
<li><a href="#api.app">dvxClipboardCopy</a></li>
|
|
<li><a href="#api.app">dvxClipboardGet</a></li>
|
|
<li><a href="#api.app">dvxColorLabel</a></li>
|
|
<li><a href="#api.app">dvxColorName</a></li>
|
|
<li><a href="#arch.layers">dvxComp</a></li>
|
|
<li><a href="#api.comp">dvxComp.h</a></li>
|
|
<li><a href="#api.app">dvxContentDisplay</a></li>
|
|
<li><a href="#api.app">dvxCreateAccelTable</a></li>
|
|
<li><a href="#api.app">dvxCreateWindow</a></li>
|
|
<li><a href="#api.app">dvxCreateWindowCentered</a></li>
|
|
<li><a href="#api.cursor">dvxCur.h</a></li>
|
|
<li><a href="#api.app">dvxDestroyWindow</a></li>
|
|
<li><a href="#api.dialog">dvxDialogAddField</a></li>
|
|
<li><a href="#api.dialog">dvxDialogAddOkCancel</a></li>
|
|
<li><a href="#api.dialog">dvxDialogCreate</a></li>
|
|
<li><a href="#api.dialog">dvxDialogEnd</a></li>
|
|
<li><a href="#api.dialog">dvxDialogRun</a></li>
|
|
<li><a href="#api.dialog">dvxDlg.h</a></li>
|
|
<li><a href="#arch.layers">dvxDraw</a></li>
|
|
<li><a href="#api.draw">dvxDraw.h</a></li>
|
|
<li><a href="#api.driver">DvxDriverClassT</a></li>
|
|
<li><a href="#api.driver">DvxDriverHeaderT</a></li>
|
|
<li><a href="#api.driver">dvxDriverKeep</a></li>
|
|
<li><a href="#api.driver">DvxDriverModT</a></li>
|
|
<li><a href="#api.driver">dvxDriversFree</a></li>
|
|
<li><a href="#api.driver">dvxDriversLoad</a></li>
|
|
<li><a href="#api.driver">dvxDriverUnload</a></li>
|
|
<li><a href="#api.driver">dvxDrv.h</a></li>
|
|
<li><a href="#api.app">DvxEntropyHookT</a></li>
|
|
<li><a href="#api.dialog">dvxErrorBox</a></li>
|
|
<li><a href="#api.dialog">dvxFileDialog</a></li>
|
|
<li><a href="#api.app">dvxFitWindow</a></li>
|
|
<li><a href="#api.app">dvxFitWindowH</a></li>
|
|
<li><a href="#api.app">dvxFitWindowW</a></li>
|
|
<li><a href="#api.font">dvxFont.h</a></li>
|
|
<li><a href="#arch.memtrack">dvxFree</a></li>
|
|
<li><a href="#api.mem">dvxFree</a></li>
|
|
<li><a href="#api.app">dvxFreeAccelTable</a></li>
|
|
<li><a href="#api.app">dvxFreeImage</a></li>
|
|
<li><a href="#api.app">dvxGetBlitOps</a></li>
|
|
<li><a href="#api.app">dvxGetColor</a></li>
|
|
<li><a href="#api.app">dvxGetColors</a></li>
|
|
<li><a href="#api.app">dvxGetDisplay</a></li>
|
|
<li><a href="#api.app">dvxGetFont</a></li>
|
|
<li><a href="#api.app">dvxGetVideoModes</a></li>
|
|
<li><a href="#api.app">dvxHideWindow</a></li>
|
|
<li><a href="#api.app">DvxHotkeyFnT</a></li>
|
|
<li><a href="#api.app">dvxImageInfo</a></li>
|
|
<li><a href="#api.dialog">dvxInfoBox</a></li>
|
|
<li><a href="#api.app">dvxInit</a></li>
|
|
<li><a href="#api.dialog">dvxInputBox</a></li>
|
|
<li><a href="#api.dialog">dvxIntInputBox</a></li>
|
|
<li><a href="#api.app">dvxInvalidateRect</a></li>
|
|
<li><a href="#api.app">dvxInvalidateWindow</a></li>
|
|
<li><a href="#api.app">dvxLoadImage</a></li>
|
|
<li><a href="#api.app">dvxLoadImageAlpha</a></li>
|
|
<li><a href="#api.app">dvxLoadImageFromMemory</a></li>
|
|
<li><a href="#api.app">dvxLoadTheme</a></li>
|
|
<li><a href="#api.platform">dvxLog</a></li>
|
|
<li><a href="#sdk.debug">dvxLog</a></li>
|
|
<li><a href="#arch.memtrack">dvxMalloc</a></li>
|
|
<li><a href="#api.mem">dvxMalloc</a></li>
|
|
<li><a href="#api.app">dvxMaximizeWindow</a></li>
|
|
<li><a href="#api.mem">dvxMem.h</a></li>
|
|
<li><a href="#api.mem">dvxMemGetAppUsage</a></li>
|
|
<li><a href="#api.mem">dvxMemResetApp</a></li>
|
|
<li><a href="#api.dialog">dvxMessageBox</a></li>
|
|
<li><a href="#api.app">dvxMinimizeWindow</a></li>
|
|
<li><a href="#lib.dvxnet">dvxnet</a></li>
|
|
<li><a href="#lib.dvxnet.config">dvxNet.h</a></li>
|
|
<li><a href="#api.palette">dvxPal.h</a></li>
|
|
<li><a href="#arch.platform">dvxPlat.h</a></li>
|
|
<li><a href="#api.platform">dvxPlat.h</a></li>
|
|
<li><a href="#api.app">dvxPlaySound</a></li>
|
|
<li><a href="#api.app">dvxPlaySoundWait</a></li>
|
|
<li><a href="#api.prefs">dvxPrefs.h</a></li>
|
|
<li><a href="#api.dialog">dvxPromptSave</a></li>
|
|
<li><a href="#api.app">dvxQuit</a></li>
|
|
<li><a href="#api.app">dvxRaiseWindow</a></li>
|
|
<li><a href="#api.mem">dvxRealloc</a></li>
|
|
<li><a href="#api.app">dvxRegisterHotkey</a></li>
|
|
<li><a href="#api.app">dvxRequestWindowClose</a></li>
|
|
<li><a href="#api.resource">dvxRes.h</a></li>
|
|
<li><a href="#api.resource">dvxResAppend</a></li>
|
|
<li><a href="#api.resource">dvxResClose</a></li>
|
|
<li><a href="#api.app">dvxResetColorScheme</a></li>
|
|
<li><a href="#api.resource">dvxResFind</a></li>
|
|
<li><a href="#arch.resources">DvxResHandleT</a></li>
|
|
<li><a href="#api.resource">DvxResHandleT</a></li>
|
|
<li><a href="#api.app">dvxResizeWindow</a></li>
|
|
<li><a href="#api.app">dvxResLoadData</a></li>
|
|
<li><a href="#api.app">dvxResLoadIcon</a></li>
|
|
<li><a href="#api.app">dvxResLoadText</a></li>
|
|
<li><a href="#api.resource">dvxResOpen</a></li>
|
|
<li><a href="#api.resource">dvxResRead</a></li>
|
|
<li><a href="#api.resource">dvxResRemove</a></li>
|
|
<li><a href="#api.app">dvxRestoreWindow</a></li>
|
|
<li><a href="#api.app">dvxRun</a></li>
|
|
<li><a href="#api.app">dvxSaveImage</a></li>
|
|
<li><a href="#api.app">dvxSaveTheme</a></li>
|
|
<li><a href="#api.app">dvxScreenshot</a></li>
|
|
<li><a href="#api.app">dvxSetBusy</a></li>
|
|
<li><a href="#api.app">dvxSetColor</a></li>
|
|
<li><a href="#api.app">dvxSetInputEntropyHook</a></li>
|
|
<li><a href="#api.app">dvxSetMouseConfig</a></li>
|
|
<li><a href="#api.app">dvxSetSoundHook</a></li>
|
|
<li><a href="#api.app">dvxSetTitle</a></li>
|
|
<li><a href="#api.app">dvxSetWallpaper</a></li>
|
|
<li><a href="#api.app">dvxSetWallpaperMode</a></li>
|
|
<li><a href="#api.app">dvxSetWindowIcon</a></li>
|
|
<li><a href="#api.app">dvxShowWindow</a></li>
|
|
<li><a href="#api.app">dvxShutdown</a></li>
|
|
<li><a href="#api.app">DvxSoundE</a></li>
|
|
<li><a href="#api.app">DvxSoundHookT</a></li>
|
|
<li><a href="#sql.overview">dvxSql</a></li>
|
|
<li><a href="#sql.db">dvxSqlAffectedRows</a></li>
|
|
<li><a href="#sql.db">dvxSqlClose</a></li>
|
|
<li><a href="#sql.cursor">dvxSqlEof</a></li>
|
|
<li><a href="#sql.db">dvxSqlError</a></li>
|
|
<li><a href="#sql.utility">dvxSqlEscape</a></li>
|
|
<li><a href="#sql.db">dvxSqlExec</a></li>
|
|
<li><a href="#sql.cursor">dvxSqlFieldByName</a></li>
|
|
<li><a href="#sql.cursor">dvxSqlFieldCount</a></li>
|
|
<li><a href="#sql.cursor">dvxSqlFieldDbl</a></li>
|
|
<li><a href="#sql.cursor">dvxSqlFieldInt</a></li>
|
|
<li><a href="#sql.cursor">dvxSqlFieldName</a></li>
|
|
<li><a href="#sql.cursor">dvxSqlFieldText</a></li>
|
|
<li><a href="#sql.cursor">dvxSqlFreeResult</a></li>
|
|
<li><a href="#sql.cursor">dvxSqlNext</a></li>
|
|
<li><a href="#sql.db">dvxSqlOpen</a></li>
|
|
<li><a href="#sql.cursor">dvxSqlQuery</a></li>
|
|
<li><a href="#api.mem">dvxStrdup</a></li>
|
|
<li><a href="#api.app">dvxTextHash</a></li>
|
|
<li><a href="#api.app">dvxTileWindows</a></li>
|
|
<li><a href="#api.app">dvxTileWindowsH</a></li>
|
|
<li><a href="#api.app">dvxTileWindowsV</a></li>
|
|
<li><a href="#api.types">dvxTypes.h</a></li>
|
|
<li><a href="#api.app">dvxUnregisterAppHotkeys</a></li>
|
|
<li><a href="#api.app">dvxUnregisterHotkey</a></li>
|
|
<li><a href="#api.app">dvxUpdate</a></li>
|
|
<li><a href="#arch.layers">dvxVideo</a></li>
|
|
<li><a href="#api.video">dvxVideo.h</a></li>
|
|
<li><a href="#api.widget">dvxWgt.h</a></li>
|
|
<li><a href="#api.widgetplugin">dvxWgtP.h</a></li>
|
|
<li><a href="#api.app">dvxWindowScreenshot</a></li>
|
|
<li><a href="#arch.layers">dvxWm</a></li>
|
|
<li><a href="#api.wm">dvxWm.h</a></li>
|
|
<li><a href="#arch.dxe">DXE</a></li>
|
|
<li><a href="#shell.overview">DXE Application Loading</a></li>
|
|
<li><a href="#arch.resources">DXE Resources</a></li>
|
|
<li><a href="#arch.dxe">DXE3</a></li>
|
|
<li><a href="#arch.build">dxe3gen</a></li>
|
|
<li><a href="#shell.types">DxeAppContextT</a></li>
|
|
<li><a href="#arch.dxe">Dynamic Loading</a></li>
|
|
<li><a href="#lib.dvxnet.api">EINPROGRESS</a></li>
|
|
<li><a href="#user.trouble">Empty Floppy Drive</a></li>
|
|
<li><a href="#user.taskmgr">End Task</a></li>
|
|
<li><a href="#arch.events">Event Dispatch</a></li>
|
|
<li><a href="#arch.events">Events</a></li>
|
|
<li><a href="#lib.dvxnet.api">EWOULDBLOCK</a></li>
|
|
<li><a href="#games.packaging">exit</a></li>
|
|
<li><a href="#user.progman">Exit DVX</a></li>
|
|
<li><a href="#shell.lifecycle">Exit DVX</a></li>
|
|
<li><a href="#user.start">Exiting DVX</a></li>
|
|
<li><a href="#user.help">F1</a></li>
|
|
<li><a href="#shell.assoc">File Associations</a></li>
|
|
<li><a href="#arch.dialogs">File Dialog</a></li>
|
|
<li><a href="#lib.xfer">File Transfer</a></li>
|
|
<li><a href="#lib.fileops">fileops</a></li>
|
|
<li><a href="#lib.fileops">fileOpsJobClearAttr</a></li>
|
|
<li><a href="#lib.fileops">fileOpsJobCreate</a></li>
|
|
<li><a href="#lib.fileops">fileOpsJobRun</a></li>
|
|
<li><a href="#lib.fileops">fileOpsJobStep</a></li>
|
|
<li><a href="#lib.fileops">FileOpsJobT</a></li>
|
|
<li><a href="#lib.fileops">fileOpsListDir</a></li>
|
|
<li><a href="#user.filedlg">Files</a></li>
|
|
<li><a href="#arch.widgets">Flexbox</a></li>
|
|
<li><a href="#api.comp">flushRect</a></li>
|
|
<li><a href="#user.filedlg">Folders</a></li>
|
|
<li><a href="#api.font">Font</a></li>
|
|
<li><a href="#arch.fonts">Fonts</a></li>
|
|
<li><a href="#user.help">Forward</a></li>
|
|
<li><a href="#widget.box">Frame</a></li>
|
|
<li><a href="#games.timing">Frame Pacing</a></li>
|
|
<li><a href="#games.screen">Framebuffer</a></li>
|
|
<li><a href="#lib.game">FRAMEWAIT</a></li>
|
|
<li><a href="#games.basic">FRAMEWAIT</a></li>
|
|
<li><a href="#lib.game">game</a></li>
|
|
<li><a href="#games.input">Game Input</a></li>
|
|
<li><a href="#games.porting">Game library</a></li>
|
|
<li><a href="#games.intro">Game loop</a></li>
|
|
<li><a href="#games.packaging">Game packaging</a></li>
|
|
<li><a href="#lib.joystick">game port</a></li>
|
|
<li><a href="#games.intro">Game template</a></li>
|
|
<li><a href="#lib.game">game.bas</a></li>
|
|
<li><a href="#lib.game">game.h</a></li>
|
|
<li><a href="#lib.game">gameFrameInit</a></li>
|
|
<li><a href="#lib.game">gameFrameNext</a></li>
|
|
<li><a href="#lib.game">GameFramePacerT</a></li>
|
|
<li><a href="#lib.game">gameFrameWait</a></li>
|
|
<li><a href="#games.timing">gameFrameWait</a></li>
|
|
<li><a href="#games.input">gameInput</a></li>
|
|
<li><a href="#lib.game">GameKeyDown</a></li>
|
|
<li><a href="#games.basic">GameKeyDown</a></li>
|
|
<li><a href="#lib.game">gameKeyIsDown</a></li>
|
|
<li><a href="#lib.game">gamePalCacheInit</a></li>
|
|
<li><a href="#lib.game">gamePalCacheLookup</a></li>
|
|
<li><a href="#lib.game">gamePalCacheSync</a></li>
|
|
<li><a href="#lib.game">GamePalCacheT</a></li>
|
|
<li><a href="#games.screen">GamePalCacheT</a></li>
|
|
<li><a href="#lib.game">gamePalNearest</a></li>
|
|
<li><a href="#games.screen">gamePalNearest</a></li>
|
|
<li><a href="#games.intro">Games</a></li>
|
|
<li><a href="#lib.game">gameTimerAdd</a></li>
|
|
<li><a href="#lib.music">General MIDI</a></li>
|
|
<li><a href="#lib.music">GENMIDI</a></li>
|
|
<li><a href="#lib.dvxnet.api">getaddrinfo</a></li>
|
|
<li><a href="#user.start">Getting Started</a></li>
|
|
<li><a href="#lib.serial.packet">Go-Back-N</a></li>
|
|
<li><a href="#games.porting">HAL</a></li>
|
|
<li><a href="#widget.box">HBox</a></li>
|
|
<li><a href="#lib.serial">HDLC</a></li>
|
|
<li><a href="#lib.serial.packet">HDLC</a></li>
|
|
<li><a href="#games.input">Held keys</a></li>
|
|
<li><a href="#user.help">Help</a></li>
|
|
<li><a href="#user.menus">Help Menu</a></li>
|
|
<li><a href="#arch.windows">Hit Testing</a></li>
|
|
<li><a href="#lib.serial.security">HKDF</a></li>
|
|
<li><a href="#lib.serial.security">HMAC</a></li>
|
|
<li><a href="#user.keys">Hot Keys</a></li>
|
|
<li><a href="#api.app">Hotkeys</a></li>
|
|
<li><a href="#widget.image">Image</a></li>
|
|
<li><a href="#widget.imagebutton">ImageButton</a></li>
|
|
<li><a href="#sdk.quickstart">include/net</a></li>
|
|
<li><a href="#user.help">Index</a></li>
|
|
<li><a href="#arch.prefs">INI files</a></li>
|
|
<li><a href="#arch.events">Input</a></li>
|
|
<li><a href="#arch.dialogs">Input Box</a></li>
|
|
<li><a href="#user.trouble">Installing DVX</a></li>
|
|
<li><a href="#arch.platform">INT 16h</a></li>
|
|
<li><a href="#lib.print">INT 17h</a></li>
|
|
<li><a href="#api.platform">INT 24h</a></li>
|
|
<li><a href="#arch.platform">INT 33h</a></li>
|
|
<li><a href="#user.welcome">Introduction</a></li>
|
|
<li><a href="#arch.build">ISO</a></li>
|
|
<li><a href="#lib.texthelp.iswordchar">isWordChar</a></li>
|
|
<li><a href="#lib.music">IT</a></li>
|
|
<li><a href="#lib.joystick">JOY_DRIVER</a></li>
|
|
<li><a href="#lib.joystick">JoyAxisCalT</a></li>
|
|
<li><a href="#lib.joystick">joyAxisScale</a></li>
|
|
<li><a href="#lib.joystick">joyBasStick</a></li>
|
|
<li><a href="#lib.joystick">joyButtonPressed</a></li>
|
|
<li><a href="#lib.joystick">joyButtonWentDown</a></li>
|
|
<li><a href="#lib.joystick">joyClose</a></li>
|
|
<li><a href="#lib.joystick">joyCount</a></li>
|
|
<li><a href="#lib.joystick">joyDeadZoneApply</a></li>
|
|
<li><a href="#lib.joystick">JoyDriverT</a></li>
|
|
<li><a href="#lib.joystick">JoyEventT</a></li>
|
|
<li><a href="#lib.joystick">joyGetCalibration</a></li>
|
|
<li><a href="#lib.joystick">joyGetDeadZone</a></li>
|
|
<li><a href="#lib.joystick">joyGetInfo</a></li>
|
|
<li><a href="#lib.joystick">joyGetRaw</a></li>
|
|
<li><a href="#lib.joystick">JoyInfoT</a></li>
|
|
<li><a href="#lib.joystick">joyOpen</a></li>
|
|
<li><a href="#lib.joystick">joyOpenShared</a></li>
|
|
<li><a href="#lib.joystick">joyPump</a></li>
|
|
<li><a href="#lib.joystick">JoyRawT</a></li>
|
|
<li><a href="#lib.joystick">joyRead</a></li>
|
|
<li><a href="#lib.joystick">joyRescan</a></li>
|
|
<li><a href="#lib.joystick">joySetCalibration</a></li>
|
|
<li><a href="#lib.joystick">joySetDeadZone</a></li>
|
|
<li><a href="#lib.joystick">joySetEventFn</a></li>
|
|
<li><a href="#lib.joystick">joySettingGet</a></li>
|
|
<li><a href="#lib.joystick">joySettingSet</a></li>
|
|
<li><a href="#lib.joystick">joySettingsLoad</a></li>
|
|
<li><a href="#lib.joystick">joySettingsStore</a></li>
|
|
<li><a href="#lib.joystick">JoyStateT</a></li>
|
|
<li><a href="#lib.joystick">joystick</a></li>
|
|
<li><a href="#lib.joystick">joystick.bas</a></li>
|
|
<li><a href="#lib.joystick">joystick.h</a></li>
|
|
<li><a href="#lib.texthelp.onkey">Key Handling</a></li>
|
|
<li><a href="#user.input">Keyboard</a></li>
|
|
<li><a href="#arch.events">Keyboard</a></li>
|
|
<li><a href="#user.keys">Keyboard Shortcuts</a></li>
|
|
<li><a href="#widget.label">Label</a></li>
|
|
<li><a href="#arch.layers">Layers</a></li>
|
|
<li><a href="#arch.widgets">Layout</a></li>
|
|
<li><a href="#widget.box">Layout</a></li>
|
|
<li><a href="#arch.widgets">Layout Engine</a></li>
|
|
<li><a href="#user.start">Leaving DVX</a></li>
|
|
<li><a href="#arch.pipeline">LFB</a></li>
|
|
<li><a href="#api.video">LFB</a></li>
|
|
<li><a href="#libtasks">libtasks</a></li>
|
|
<li><a href="#lib.music">libxmp-lite</a></li>
|
|
<li><a href="#arch.pipeline">Linear Framebuffer</a></li>
|
|
<li><a href="#user.controls">List Boxes</a></li>
|
|
<li><a href="#widget.listbox">ListBox</a></li>
|
|
<li><a href="#lib.listhelp">listhelp</a></li>
|
|
<li><a href="#lib.listhelp">ListItemsT</a></li>
|
|
<li><a href="#lib.listhelp">ListPopupT</a></li>
|
|
<li><a href="#lib.listhelp">listReorderMove</a></li>
|
|
<li><a href="#lib.listhelp">ListSelT</a></li>
|
|
<li><a href="#widget.listview">ListView</a></li>
|
|
<li><a href="#widget.listview">ListViewColT</a></li>
|
|
<li><a href="#widget.listview">ListViewSortE</a></li>
|
|
<li><a href="#user.trouble">Log File</a></li>
|
|
<li><a href="#lib.dvxnet">lwIP</a></li>
|
|
<li><a href="#sdk.packaging">make package</a></li>
|
|
<li><a href="#arch.build">Makefile</a></li>
|
|
<li><a href="#widget.datactrl">Master-Detail</a></li>
|
|
<li><a href="#user.windows">Maximize</a></li>
|
|
<li><a href="#user.trouble">Memory</a></li>
|
|
<li><a href="#arch.memtrack">Memory Tracking</a></li>
|
|
<li><a href="#arch.dxe">Memory Tracking</a></li>
|
|
<li><a href="#user.menus">Menu Bar</a></li>
|
|
<li><a href="#arch.windows">Menu System</a></li>
|
|
<li><a href="#api.types">MenuBarT</a></li>
|
|
<li><a href="#api.types">MenuItemT</a></li>
|
|
<li><a href="#user.menus">Menus</a></li>
|
|
<li><a href="#arch.windows">Menus</a></li>
|
|
<li><a href="#api.types">MenuT</a></li>
|
|
<li><a href="#arch.dialogs">Message Box</a></li>
|
|
<li><a href="#user.menus">Message Boxes</a></li>
|
|
<li><a href="#games.sound">MIDI</a></li>
|
|
<li><a href="#lib.music">MIDI</a></li>
|
|
<li><a href="#user.windows">Minimize</a></li>
|
|
<li><a href="#arch.windows">Minimized Windows</a></li>
|
|
<li><a href="#arch.build">mkcd.sh</a></li>
|
|
<li><a href="#lib.music">MOD</a></li>
|
|
<li><a href="#lib.conn">Modem</a></li>
|
|
<li><a href="#arch.dxe">Modules</a></li>
|
|
<li><a href="#user.input">Mouse</a></li>
|
|
<li><a href="#arch.events">Mouse</a></li>
|
|
<li><a href="#lib.texthelp.mouseclick">Mouse Click</a></li>
|
|
<li><a href="#user.trouble">Mouse Does Not Move</a></li>
|
|
<li><a href="#user.settings">Mouse Settings</a></li>
|
|
<li><a href="#arch.events">Mouse Wheel</a></li>
|
|
<li><a href="#user.windows">Moving Windows</a></li>
|
|
<li><a href="#lib.audio">MPU-401</a></li>
|
|
<li><a href="#lib.music">MT-32</a></li>
|
|
<li><a href="#lib.texthelp.mouseclick">Multi-Click</a></li>
|
|
<li><a href="#games.sound">Music</a></li>
|
|
<li><a href="#lib.music">music</a></li>
|
|
<li><a href="#lib.music">Music macro language</a></li>
|
|
<li><a href="#lib.music">music.bas</a></li>
|
|
<li><a href="#lib.music">music.h</a></li>
|
|
<li><a href="#lib.music">musicBasPlay</a></li>
|
|
<li><a href="#lib.music">MusicInfoT</a></li>
|
|
<li><a href="#lib.music">musicLoad</a></li>
|
|
<li><a href="#lib.music">MusicLoad</a></li>
|
|
<li><a href="#lib.music">musicMidiNoteOn</a></li>
|
|
<li><a href="#lib.music">musicMidiOpen</a></li>
|
|
<li><a href="#lib.music">musicMmlOpen</a></li>
|
|
<li><a href="#lib.music">musicMmlPlay</a></li>
|
|
<li><a href="#lib.music">MusicMmlT</a></li>
|
|
<li><a href="#lib.music">musicPlay</a></li>
|
|
<li><a href="#lib.music">MusicPlay</a></li>
|
|
<li><a href="#lib.music">musicStop</a></li>
|
|
<li><a href="#sdk.quickstart">NET = 1</a></li>
|
|
<li><a href="#lib.dvxnet.basic">net.bas</a></li>
|
|
<li><a href="#lib.dvxnet.config">NetConfigT</a></li>
|
|
<li><a href="#lib.dvxnet.config">netGetStatus</a></li>
|
|
<li><a href="#lib.dvxnet.waiting">netPoll</a></li>
|
|
<li><a href="#lib.dvxnet.config">netRestart</a></li>
|
|
<li><a href="#lib.dvxnet.config">netStart</a></li>
|
|
<li><a href="#lib.dvxnet.config">NetStatusT</a></li>
|
|
<li><a href="#lib.dvxnet">Networking</a></li>
|
|
<li><a href="#sdk.quickstart">newProject.sh</a></li>
|
|
<li><a href="#user.trouble">No Sound</a></li>
|
|
<li><a href="#user.taskmgr">Not Responding</a></li>
|
|
<li><a href="#api.app">Not responding</a></li>
|
|
<li><a href="#user.menus">OK and Cancel</a></li>
|
|
<li><a href="#widget.base">onChange</a></li>
|
|
<li><a href="#widget.base">onClick</a></li>
|
|
<li><a href="#widget.base">onKeyDown</a></li>
|
|
<li><a href="#widget.base">onKeyPress</a></li>
|
|
<li><a href="#widget.base">onMouseDown</a></li>
|
|
<li><a href="#games.input">onRawKey</a></li>
|
|
<li><a href="#lib.music">OP2</a></li>
|
|
<li><a href="#user.filedlg">Open Dialog</a></li>
|
|
<li><a href="#lib.audio">OPL</a></li>
|
|
<li><a href="#user.controls">Option Buttons</a></li>
|
|
<li><a href="#user.trouble">Out of Memory</a></li>
|
|
<li><a href="#sdk.packaging">Packaging</a></li>
|
|
<li><a href="#api.video">packColor</a></li>
|
|
<li><a href="#lib.serial.packet">packet</a></li>
|
|
<li><a href="#lib.dvxnet">Packet driver</a></li>
|
|
<li><a href="#user.printing">Page Setup</a></li>
|
|
<li><a href="#api.palette">Palette</a></li>
|
|
<li><a href="#games.screen">Palette</a></li>
|
|
<li><a href="#lib.audio">PC speaker</a></li>
|
|
<li><a href="#arch.colors">Pixel Format</a></li>
|
|
<li><a href="#arch.colors">PixelFormatT</a></li>
|
|
<li><a href="#api.types">PixelFormatT</a></li>
|
|
<li><a href="#lib.serial.packet">pktClose</a></li>
|
|
<li><a href="#lib.dvxnet.inside">pktDrv</a></li>
|
|
<li><a href="#lib.serial.packet">pktOpen</a></li>
|
|
<li><a href="#lib.serial.packet">pktOpenStream</a></li>
|
|
<li><a href="#lib.serial.packet">pktPoll</a></li>
|
|
<li><a href="#lib.serial.packet">pktSend</a></li>
|
|
<li><a href="#lib.serial.packet">PktStreamT</a></li>
|
|
<li><a href="#arch.platform">Platform Layer</a></li>
|
|
<li><a href="#api.platform">Platform Layer</a></li>
|
|
<li><a href="#api.platform">platformAppExit</a></li>
|
|
<li><a href="#api.platform">platformBootDrive</a></li>
|
|
<li><a href="#api.platform">platformClock</a></li>
|
|
<li><a href="#api.platform">platformDirIsWritable</a></li>
|
|
<li><a href="#api.platform">platformDiskSpace</a></li>
|
|
<li><a href="#api.platform">platformDriveList</a></li>
|
|
<li><a href="#api.platform">PlatformDriveT</a></li>
|
|
<li><a href="#api.platform">platformEntropyStamp</a></li>
|
|
<li><a href="#api.platform">platformFilterMatch</a></li>
|
|
<li><a href="#api.platform">platformFlushRect</a></li>
|
|
<li><a href="#api.platform">platformInjectScan</a></li>
|
|
<li><a href="#api.platform">platformIrqRestore</a></li>
|
|
<li><a href="#api.platform">platformIrqSave</a></li>
|
|
<li><a href="#api.platform">platformIsReadOnly</a></li>
|
|
<li><a href="#api.platform">platformKeyboardRead</a></li>
|
|
<li><a href="#api.platform">platformKeyEventRead</a></li>
|
|
<li><a href="#api.platform">PlatformKeyEventT</a></li>
|
|
<li><a href="#api.platform">platformKeyHookInit</a></li>
|
|
<li><a href="#api.platform">platformKeyIsDown</a></li>
|
|
<li><a href="#api.platform">platformKeyStateClear</a></li>
|
|
<li><a href="#api.platform">platformKeyStateGet</a></li>
|
|
<li><a href="#api.platform">platformLockRegion</a></li>
|
|
<li><a href="#api.platform">platformLogMemory</a></li>
|
|
<li><a href="#api.platform">platformMicros</a></li>
|
|
<li><a href="#games.timing">platformMicros</a></li>
|
|
<li><a href="#api.platform">platformMousePoll</a></li>
|
|
<li><a href="#api.platform">platformNormalizeFKeyScan</a></li>
|
|
<li><a href="#api.platform">platformPathIsAbsolute</a></li>
|
|
<li><a href="#api.platform">platformPathSibling</a></li>
|
|
<li><a href="#api.platform">platformReplaceFile</a></li>
|
|
<li><a href="#api.platform">platformSetAppExitHook</a></li>
|
|
<li><a href="#api.platform">platformSetLogMemory</a></li>
|
|
<li><a href="#api.platform">platformSetReadOnly</a></li>
|
|
<li><a href="#api.platform">platformShutdownHookAdd</a></li>
|
|
<li><a href="#api.platform">platformShutdownHookRemove</a></li>
|
|
<li><a href="#api.platform">platformSplashNearestColor</a></li>
|
|
<li><a href="#api.platform">platformTempDir</a></li>
|
|
<li><a href="#api.platform">platformTimerAdd</a></li>
|
|
<li><a href="#api.platform">platformTimerAddFast</a></li>
|
|
<li><a href="#api.platform">PlatformTimerFnT</a></li>
|
|
<li><a href="#api.platform">platformTimerRemove</a></li>
|
|
<li><a href="#api.platform">platformTimerRemoveOwner</a></li>
|
|
<li><a href="#api.platform">platformTimerStats</a></li>
|
|
<li><a href="#api.platform">PlatformTimerStatsT</a></li>
|
|
<li><a href="#api.platform">platformUnlockRegion</a></li>
|
|
<li><a href="#api.platform">platformVideoInit</a></li>
|
|
<li><a href="#lib.audio">PlayTone</a></li>
|
|
<li><a href="#lib.audio">PlayWave</a></li>
|
|
<li><a href="#lib.dvxnet.api">poll</a></li>
|
|
<li><a href="#arch.events">Polling</a></li>
|
|
<li><a href="#games.porting">Porting</a></li>
|
|
<li><a href="#arch.prefs">Preferences</a></li>
|
|
<li><a href="#api.prefs">Preferences</a></li>
|
|
<li><a href="#api.prefs">prefsAccelChoices</a></li>
|
|
<li><a href="#api.prefs">prefsChoiceIndex</a></li>
|
|
<li><a href="#api.prefs">prefsChoiceIndexOfValue</a></li>
|
|
<li><a href="#api.prefs">prefsChoiceLabels</a></li>
|
|
<li><a href="#api.prefs">PrefsChoiceT</a></li>
|
|
<li><a href="#api.prefs">prefsGetBool</a></li>
|
|
<li><a href="#api.prefs">prefsGetChoice</a></li>
|
|
<li><a href="#api.prefs">prefsGetInt</a></li>
|
|
<li><a href="#api.prefs">prefsGetString</a></li>
|
|
<li><a href="#api.prefs">prefsGetVideoMode</a></li>
|
|
<li><a href="#arch.prefs">PrefsHandleT</a></li>
|
|
<li><a href="#api.prefs">PrefsHandleT</a></li>
|
|
<li><a href="#api.prefs">prefsKeyName</a></li>
|
|
<li><a href="#api.prefs">prefsLoad</a></li>
|
|
<li><a href="#api.prefs">prefsSave</a></li>
|
|
<li><a href="#api.prefs">prefsSetBool</a></li>
|
|
<li><a href="#api.prefs">prefsSetInt</a></li>
|
|
<li><a href="#api.prefs">prefsSetString</a></li>
|
|
<li><a href="#api.prefs">prefsSetVideoMode</a></li>
|
|
<li><a href="#api.prefs">prefsWallpaperChoices</a></li>
|
|
<li><a href="#api.prefs">prefsWheelDirChoices</a></li>
|
|
<li><a href="#user.printing">Print</a></li>
|
|
<li><a href="#lib.print">print</a></li>
|
|
<li><a href="#lib.print">printDialog</a></li>
|
|
<li><a href="#lib.print">PrintDriverT</a></li>
|
|
<li><a href="#user.printing">Printer</a></li>
|
|
<li><a href="#user.printing">Printing</a></li>
|
|
<li><a href="#lib.print">printJobOpen</a></li>
|
|
<li><a href="#lib.print">PrintJobT</a></li>
|
|
<li><a href="#lib.print">printJobWrite</a></li>
|
|
<li><a href="#lib.print">printPageSetupDialog</a></li>
|
|
<li><a href="#lib.print">printRasterJob</a></li>
|
|
<li><a href="#lib.print">printTextJob</a></li>
|
|
<li><a href="#lib.print">printUiText</a></li>
|
|
<li><a href="#user.trouble">Problems</a></li>
|
|
<li><a href="#user.progman">Program Groups</a></li>
|
|
<li><a href="#user.programs">Program Help</a></li>
|
|
<li><a href="#user.progman">Program Manager</a></li>
|
|
<li><a href="#user.developers">Programming</a></li>
|
|
<li><a href="#user.programs">Programs</a></li>
|
|
<li><a href="#widget.progressbar">ProgressBar</a></li>
|
|
<li><a href="#widget.radio">Radio</a></li>
|
|
<li><a href="#user.controls">Radio Buttons</a></li>
|
|
<li><a href="#widget.radio">RadioGroup</a></li>
|
|
<li><a href="#games.input">RawKeyEventT</a></li>
|
|
<li><a href="#user.trouble">Read-only Disk</a></li>
|
|
<li><a href="#api.platform">Read-only Media</a></li>
|
|
<li><a href="#lib.dvxnet.inside">Receive ring</a></li>
|
|
<li><a href="#api.draw">rectCopy</a></li>
|
|
<li><a href="#api.draw">rectCopyGrayscale</a></li>
|
|
<li><a href="#api.draw">rectFill</a></li>
|
|
<li><a href="#api.comp">rectIntersect</a></li>
|
|
<li><a href="#api.types">RectT</a></li>
|
|
<li><a href="#arch.platform">rep movsd</a></li>
|
|
<li><a href="#arch.platform">rep stosl</a></li>
|
|
<li><a href="#games.input">Reserved keys</a></li>
|
|
<li><a href="#api.video">resetClipRect</a></li>
|
|
<li><a href="#user.windows">Resizing Windows</a></li>
|
|
<li><a href="#arch.resources">Resources</a></li>
|
|
<li><a href="#api.resource">Resources</a></li>
|
|
<li><a href="#games.files">Resources</a></li>
|
|
<li><a href="#user.windows">Restore</a></li>
|
|
<li><a href="#user.input">Right-click</a></li>
|
|
<li><a href="#lib.serial">RS-232</a></li>
|
|
<li><a href="#lib.serial.rs232">rs232</a></li>
|
|
<li><a href="#lib.serial.rs232">rs232Close</a></li>
|
|
<li><a href="#lib.serial.rs232">rs232GetDcd</a></li>
|
|
<li><a href="#lib.serial.rs232">rs232Open</a></li>
|
|
<li><a href="#lib.serial.rs232">rs232Read</a></li>
|
|
<li><a href="#lib.serial.rs232">rs232SetBreak</a></li>
|
|
<li><a href="#lib.serial.rs232">rs232Write</a></li>
|
|
<li><a href="#user.progman">Run</a></li>
|
|
<li><a href="#shell.run">Run dialog</a></li>
|
|
<li><a href="#user.programs">Running Programs</a></li>
|
|
<li><a href="#lib.music">S3M</a></li>
|
|
<li><a href="#user.trouble">Safe Video Mode</a></li>
|
|
<li><a href="#shell.startup">Safe Video Mode</a></li>
|
|
<li><a href="#user.filedlg">Save As</a></li>
|
|
<li><a href="#user.filedlg">Save Dialog</a></li>
|
|
<li><a href="#games.files">Saved games</a></li>
|
|
<li><a href="#user.trouble">Screen Problems</a></li>
|
|
<li><a href="#user.settings">Screen Resolution</a></li>
|
|
<li><a href="#user.windows">Screenshot</a></li>
|
|
<li><a href="#user.controls">Scroll Bars</a></li>
|
|
<li><a href="#user.input">Scroll Wheel</a></li>
|
|
<li><a href="#api.types">ScrollbarT</a></li>
|
|
<li><a href="#widget.scrollpane">ScrollPane</a></li>
|
|
<li><a href="#sdk.quickstart">SDK</a></li>
|
|
<li><a href="#sdk.quickstart">SDK Quick Start</a></li>
|
|
<li><a href="#sdk.quickstart">sdk.mk</a></li>
|
|
<li><a href="#user.help">Search</a></li>
|
|
<li><a href="#lib.serial.security">secCipherCreate</a></li>
|
|
<li><a href="#lib.serial.security">secDhCreate</a></li>
|
|
<li><a href="#lib.serial">secLink</a></li>
|
|
<li><a href="#lib.serial.seclink">secLink</a></li>
|
|
<li><a href="#lib.serial.seclink">secLinkClose</a></li>
|
|
<li><a href="#lib.serial.seclink">secLinkHandshake</a></li>
|
|
<li><a href="#lib.serial.seclink">secLinkHandshakeStep</a></li>
|
|
<li><a href="#lib.serial.seclink">secLinkOpen</a></li>
|
|
<li><a href="#lib.serial.seclink">secLinkOpenStream</a></li>
|
|
<li><a href="#lib.serial.seclink">secLinkPoll</a></li>
|
|
<li><a href="#lib.serial.seclink">secLinkSend</a></li>
|
|
<li><a href="#lib.serial.seclink">secLinkSetChannelPlain</a></li>
|
|
<li><a href="#lib.serial.seclink">secLinkSetPassphrase</a></li>
|
|
<li><a href="#lib.serial.security">secRngAddEntropy</a></li>
|
|
<li><a href="#lib.serial.security">secRngBytes</a></li>
|
|
<li><a href="#lib.serial.security">secRngLoadSeed</a></li>
|
|
<li><a href="#lib.serial.security">Security</a></li>
|
|
<li><a href="#lib.dvxnet.api">select</a></li>
|
|
<li><a href="#lib.texthelp.clearother">Selection</a></li>
|
|
<li><a href="#lib.texthelp.paintline">Selection Highlight</a></li>
|
|
<li><a href="#widget.separator">Separator</a></li>
|
|
<li><a href="#lib.serial">Serial</a></li>
|
|
<li><a href="#lib.serial.example">Serial Example</a></li>
|
|
<li><a href="#api.video">setClipRect</a></li>
|
|
<li><a href="#user.settings">Settings</a></li>
|
|
<li><a href="#user.trouble">Setup</a></li>
|
|
<li><a href="#lib.serial.security">SHA-256</a></li>
|
|
<li><a href="#lib.texthelp">Shared Library</a></li>
|
|
<li><a href="#shell.types">SHELL_APP_DESCRIPTOR</a></li>
|
|
<li><a href="#sdk.firstapp">SHELL_APP_DESCRIPTOR</a></li>
|
|
<li><a href="#shell.types">SHELL_APP_NAME_MAX</a></li>
|
|
<li><a href="#shell.types">SHELL_DESKTOP_APP</a></li>
|
|
<li><a href="#shell.startup">SHELL_INI_WELCOME</a></li>
|
|
<li><a href="#shell.types">SHELL_STACK_DEFAULT</a></li>
|
|
<li><a href="#shell.overview">shellApp.h</a></li>
|
|
<li><a href="#shell.desktop">ShellAppCleanupFnT</a></li>
|
|
<li><a href="#shell.lifecycle">shellAppCodeForgetTask</a></li>
|
|
<li><a href="#shell.query">shellAppFindByPath</a></li>
|
|
<li><a href="#shell.lifecycle">shellAppInit</a></li>
|
|
<li><a href="#shell.query">shellAppSlotCount</a></li>
|
|
<li><a href="#shell.types">ShellAppT</a></li>
|
|
<li><a href="#shell.assoc">shellAssocCommand</a></li>
|
|
<li><a href="#shell.assoc">shellAssocCount</a></li>
|
|
<li><a href="#shell.assoc">shellAssocGet</a></li>
|
|
<li><a href="#shell.assoc">shellAssocLookup</a></li>
|
|
<li><a href="#shell.assoc">shellAssocNormalize</a></li>
|
|
<li><a href="#shell.assoc">shellAssocOpenDocument</a></li>
|
|
<li><a href="#shell.assoc">shellAssocRemove</a></li>
|
|
<li><a href="#shell.assoc">shellAssocSet</a></li>
|
|
<li><a href="#shell.lifecycle">shellCloseAllApps</a></li>
|
|
<li><a href="#shell.config">shellConfigPath</a></li>
|
|
<li><a href="#shell.desktop">shellCtrlEscFn</a></li>
|
|
<li><a href="#shell.desktop">shellDesktopUpdate</a></li>
|
|
<li><a href="#shell.config">shellEnsureConfigDir</a></li>
|
|
<li><a href="#shell.lifecycle">shellForceKillApp</a></li>
|
|
<li><a href="#shell.query">shellGetApp</a></li>
|
|
<li><a href="#shell.info">shellGetSystemInfo</a></li>
|
|
<li><a href="#shell.info">shellInfoInit</a></li>
|
|
<li><a href="#shell.programs">shellIsProgram</a></li>
|
|
<li><a href="#shell.lifecycle">shellLoadApp</a></li>
|
|
<li><a href="#shell.lifecycle">shellLoadAppWithArgs</a></li>
|
|
<li><a href="#shell.lifecycle">shellOpenHelp</a></li>
|
|
<li><a href="#shell.query">shellPathsMatch</a></li>
|
|
<li><a href="#shell.programs">shellProgramIcon</a></li>
|
|
<li><a href="#shell.programs">shellProgramInfo</a></li>
|
|
<li><a href="#shell.programs">shellProgramListFree</a></li>
|
|
<li><a href="#shell.programs">shellProgramScan</a></li>
|
|
<li><a href="#shell.programs">ShellProgramT</a></li>
|
|
<li><a href="#shell.lifecycle">shellReapApp</a></li>
|
|
<li><a href="#shell.lifecycle">shellReapApps</a></li>
|
|
<li><a href="#shell.desktop">shellRegisterAppCleanup</a></li>
|
|
<li><a href="#shell.desktop">shellRegisterDesktopUpdate</a></li>
|
|
<li><a href="#shell.desktop">shellRegisterIdle</a></li>
|
|
<li><a href="#shell.lifecycle">shellRequestExit</a></li>
|
|
<li><a href="#shell.run">shellResolveArgs</a></li>
|
|
<li><a href="#shell.run">shellRunCommand</a></li>
|
|
<li><a href="#shell.run">shellRunDialog</a></li>
|
|
<li><a href="#shell.lifecycle">shellRunExitRequest</a></li>
|
|
<li><a href="#shell.query">shellRunningAppCount</a></li>
|
|
<li><a href="#shell.run">shellRunProgram</a></li>
|
|
<li><a href="#shell.run">shellSplitCommand</a></li>
|
|
<li><a href="#shell.lifecycle">shellTaskDepthForget</a></li>
|
|
<li><a href="#shell.lifecycle">shellTaskDepthShare</a></li>
|
|
<li><a href="#lib.taskmgr">shellTaskMgrOpen</a></li>
|
|
<li><a href="#lib.taskmgr">shellTaskMgrRefresh</a></li>
|
|
<li><a href="#shell.lifecycle">shellTerminateAllApps</a></li>
|
|
<li><a href="#shell.desktop">shellUnregisterAppCleanup</a></li>
|
|
<li><a href="#shell.desktop">shellUnregisterDesktopUpdate</a></li>
|
|
<li><a href="#shell.desktop">shellUnregisterIdle</a></li>
|
|
<li><a href="#user.trouble">Shift Key at Start</a></li>
|
|
<li><a href="#user.keys">Shortcut Keys</a></li>
|
|
<li><a href="#widget.slider">Slider</a></li>
|
|
<li><a href="#user.controls">Sliders</a></li>
|
|
<li><a href="#lib.dvxnet.api">socket</a></li>
|
|
<li><a href="#lib.dvxnet">Sockets</a></li>
|
|
<li><a href="#lib.audio">sound</a></li>
|
|
<li><a href="#games.sound">Sound</a></li>
|
|
<li><a href="#lib.audio">Sound Blaster</a></li>
|
|
<li><a href="#lib.audio">sound.bas</a></li>
|
|
<li><a href="#lib.audio">SoundInfo$</a></li>
|
|
<li><a href="#user.settings">Sounds</a></li>
|
|
<li><a href="#widget.spacer">Spacer</a></li>
|
|
<li><a href="#widget.spinner">Spinner</a></li>
|
|
<li><a href="#widget.splitter">Splitter</a></li>
|
|
<li><a href="#sql.overview">SQL</a></li>
|
|
<li><a href="#sql.example">SQL Example</a></li>
|
|
<li><a href="#sql.overview">SQLite</a></li>
|
|
<li><a href="#user.start">Starting DVX</a></li>
|
|
<li><a href="#user.programs">Starting Programs</a></li>
|
|
<li><a href="#user.progman">Startup Group</a></li>
|
|
<li><a href="#widget.statusbar">StatusBar</a></li>
|
|
<li><a href="#lib.joystick">STICK</a></li>
|
|
<li><a href="#games.basic">STICK</a></li>
|
|
<li><a href="#lib.joystick">STRIG</a></li>
|
|
<li><a href="#games.basic">STRIG</a></li>
|
|
<li><a href="#user.taskmgr">Stuck Program</a></li>
|
|
<li><a href="#arch.windows">Submenus</a></li>
|
|
<li><a href="#user.taskmgr">Switch To</a></li>
|
|
<li><a href="#user.programs">Switching Programs</a></li>
|
|
<li><a href="#user.windows">System Menu</a></li>
|
|
<li><a href="#widget.tabcontrol">TabControl</a></li>
|
|
<li><a href="#widget.tabcontrol">TabPage</a></li>
|
|
<li><a href="#user.controls">Tabs</a></li>
|
|
<li><a href="#user.taskmgr">Task Manager</a></li>
|
|
<li><a href="#lib.taskmgr">Task Manager</a></li>
|
|
<li><a href="#libtasks">Task Switching</a></li>
|
|
<li><a href="#libtasks">taskswitch.h</a></li>
|
|
<li><a href="#lib.dvxnet">TCP/IP</a></li>
|
|
<li><a href="#user.developers">Technical Reference</a></li>
|
|
<li><a href="#sys.welcome">Technical Reference</a></li>
|
|
<li><a href="#lib.conn">Telnet</a></li>
|
|
<li><a href="#sdk.quickstart">Templates</a></li>
|
|
<li><a href="#widget.ansiterm">Terminal</a></li>
|
|
<li><a href="#user.controls">Text Boxes</a></li>
|
|
<li><a href="#lib.texthelp">Text Editing</a></li>
|
|
<li><a href="#lib.texthelp.onkey">Text Editing</a></li>
|
|
<li><a href="#arch.fonts">Text Rendering</a></li>
|
|
<li><a href="#lib.texthelp.paintline">Text Rendering</a></li>
|
|
<li><a href="#widget.textinput">TextArea</a></li>
|
|
<li><a href="#lib.texthelp.undoswap">textEditCopyText</a></li>
|
|
<li><a href="#lib.texthelp.single">textEditDefaultSyntaxRgb</a></li>
|
|
<li><a href="#lib.texthelp.single">textEditKeyEdits</a></li>
|
|
<li><a href="#lib.texthelp.undoswap">textEditMoveCursor</a></li>
|
|
<li><a href="#lib.texthelp.saveundo">textEditSaveUndo</a></li>
|
|
<li><a href="#lib.texthelp.single">textEditScrollToCursor</a></li>
|
|
<li><a href="#lib.texthelp.single">textEditSingleClearSelection</a></li>
|
|
<li><a href="#lib.texthelp.single">textEditSingleFree</a></li>
|
|
<li><a href="#lib.texthelp.single">textEditSingleInit</a></li>
|
|
<li><a href="#lib.texthelp.single">textEditSingleSetText</a></li>
|
|
<li><a href="#lib.texthelp.single">TextEditSingleT</a></li>
|
|
<li><a href="#lib.texthelp.undoswap">textEditUndoSwap</a></li>
|
|
<li><a href="#lib.texthelp.single">textEditVisibleChars</a></li>
|
|
<li><a href="#lib.texthelp">textHelp</a></li>
|
|
<li><a href="#widget.textinput">TextInput</a></li>
|
|
<li><a href="#api.draw">textWidth</a></li>
|
|
<li><a href="#api.draw">textWidthAccel</a></li>
|
|
<li><a href="#user.settings">Themes</a></li>
|
|
<li><a href="#arch.colors">Theming</a></li>
|
|
<li><a href="#user.windows">Tile</a></li>
|
|
<li><a href="#widget.timer">Timer</a></li>
|
|
<li><a href="#api.platform">Timer Service</a></li>
|
|
<li><a href="#games.timing">Timer Service</a></li>
|
|
<li><a href="#widget.toolbar">Toolbar</a></li>
|
|
<li><a href="#widget.treeview">TreeItem</a></li>
|
|
<li><a href="#widget.treeview">TreeView</a></li>
|
|
<li><a href="#user.trouble">Troubleshooting</a></li>
|
|
<li><a href="#lib.dvxnet.waiting">tsYield</a></li>
|
|
<li><a href="#lib.serial.rs232">UART</a></li>
|
|
<li><a href="#user.controls">Undo</a></li>
|
|
<li><a href="#lib.texthelp.saveundo">Undo</a></li>
|
|
<li><a href="#lib.texthelp.undoswap">Undo</a></li>
|
|
<li><a href="#lib.texthelp.onkey">Undo</a></li>
|
|
<li><a href="#user.trouble">Uninstalling DVX</a></li>
|
|
<li><a href="#user.trouble">Upgrading DVX</a></li>
|
|
<li><a href="#user.welcome">User Guide</a></li>
|
|
<li><a href="#arch.platform">VBE</a></li>
|
|
<li><a href="#widget.box">VBox</a></li>
|
|
<li><a href="#sdk.packaging">VENDOR</a></li>
|
|
<li><a href="#arch.platform">VESA</a></li>
|
|
<li><a href="#api.video">VESA</a></li>
|
|
<li><a href="#user.settings">Video Mode</a></li>
|
|
<li><a href="#api.video">videoInit</a></li>
|
|
<li><a href="#api.types">VideoModeInfoT</a></li>
|
|
<li><a href="#api.video">videoShutdown</a></li>
|
|
<li><a href="#user.settings">Volume</a></li>
|
|
<li><a href="#widget.ansiterm">VT100</a></li>
|
|
<li><a href="#user.settings">Wallpaper</a></li>
|
|
<li><a href="#user.welcome">Welcome</a></li>
|
|
<li><a href="#shell.startup">Welcome Program</a></li>
|
|
<li><a href="#api.widget">WGT_API_HEADER</a></li>
|
|
<li><a href="#sdk.firstwidget">WGT_API_HEADER</a></li>
|
|
<li><a href="#widget.ansiterm">wgtAnsiTerm</a></li>
|
|
<li><a href="#widget.ansiterm">wgtAnsiTermClear</a></li>
|
|
<li><a href="#widget.ansiterm">wgtAnsiTermCopy</a></li>
|
|
<li><a href="#widget.ansiterm">wgtAnsiTermPoll</a></li>
|
|
<li><a href="#widget.ansiterm">wgtAnsiTermRepaint</a></li>
|
|
<li><a href="#widget.ansiterm">wgtAnsiTermSetComm</a></li>
|
|
<li><a href="#widget.ansiterm">wgtAnsiTermSetPalette</a></li>
|
|
<li><a href="#widget.ansiterm">wgtAnsiTermSetScrollback</a></li>
|
|
<li><a href="#widget.ansiterm">wgtAnsiTermWrite</a></li>
|
|
<li><a href="#api.widget">WgtApiHeaderT</a></li>
|
|
<li><a href="#api.widget">WgtApiPrefixT</a></li>
|
|
<li><a href="#widget.button">wgtButton</a></li>
|
|
<li><a href="#widget.canvas">wgtCanvas</a></li>
|
|
<li><a href="#widget.canvas">wgtCanvasClear</a></li>
|
|
<li><a href="#widget.canvas">wgtCanvasDrawLine</a></li>
|
|
<li><a href="#widget.canvas">wgtCanvasDrawRect</a></li>
|
|
<li><a href="#widget.canvas">wgtCanvasDrawText</a></li>
|
|
<li><a href="#widget.canvas">wgtCanvasFillCircle</a></li>
|
|
<li><a href="#widget.canvas">wgtCanvasFillRect</a></li>
|
|
<li><a href="#widget.canvas">wgtCanvasGetPixel</a></li>
|
|
<li><a href="#widget.canvas">wgtCanvasLoad</a></li>
|
|
<li><a href="#widget.canvas">wgtCanvasResize</a></li>
|
|
<li><a href="#widget.canvas">wgtCanvasSave</a></li>
|
|
<li><a href="#widget.canvas">wgtCanvasSetMouseCallback</a></li>
|
|
<li><a href="#widget.canvas">wgtCanvasSetPenColor</a></li>
|
|
<li><a href="#widget.canvas">wgtCanvasSetPenSize</a></li>
|
|
<li><a href="#widget.canvas">wgtCanvasSetPixel</a></li>
|
|
<li><a href="#api.widget">wgtChars</a></li>
|
|
<li><a href="#widget.base">wgtChars</a></li>
|
|
<li><a href="#widget.checkbox">wgtCheckbox</a></li>
|
|
<li><a href="#widget.checkbox">wgtCheckboxIsChecked</a></li>
|
|
<li><a href="#widget.checkbox">wgtCheckboxSetChecked</a></li>
|
|
<li><a href="#widget.combobox">wgtComboBox</a></li>
|
|
<li><a href="#widget.combobox">wgtComboBoxAddItem</a></li>
|
|
<li><a href="#widget.combobox">wgtComboBoxClear</a></li>
|
|
<li><a href="#widget.combobox">wgtComboBoxGetSelected</a></li>
|
|
<li><a href="#widget.combobox">wgtComboBoxRemoveItem</a></li>
|
|
<li><a href="#widget.combobox">wgtComboBoxSetItems</a></li>
|
|
<li><a href="#widget.combobox">wgtComboBoxSetSelected</a></li>
|
|
<li><a href="#widget.datactrl">wgtDataCtrl</a></li>
|
|
<li><a href="#widget.dbgrid">wgtDbGrid</a></li>
|
|
<li><a href="#api.widget">wgtDestroy</a></li>
|
|
<li><a href="#widget.base">wgtDestroy</a></li>
|
|
<li><a href="#widget.dropdown">wgtDropdown</a></li>
|
|
<li><a href="#widget.dropdown">wgtDropdownAddItem</a></li>
|
|
<li><a href="#widget.dropdown">wgtDropdownClear</a></li>
|
|
<li><a href="#widget.dropdown">wgtDropdownGetSelected</a></li>
|
|
<li><a href="#widget.dropdown">wgtDropdownRemoveItem</a></li>
|
|
<li><a href="#widget.dropdown">wgtDropdownSetItems</a></li>
|
|
<li><a href="#widget.dropdown">wgtDropdownSetSelected</a></li>
|
|
<li><a href="#api.widget">wgtFind</a></li>
|
|
<li><a href="#widget.base">wgtFind</a></li>
|
|
<li><a href="#api.widget">wgtFindByBasName</a></li>
|
|
<li><a href="#widget.box">wgtFrame</a></li>
|
|
<li><a href="#api.widget">wgtGetApi</a></li>
|
|
<li><a href="#api.widget">wgtGetContext</a></li>
|
|
<li><a href="#widget.base">wgtGetContext</a></li>
|
|
<li><a href="#api.widget">wgtGetFocused</a></li>
|
|
<li><a href="#api.widget">wgtGetIface</a></li>
|
|
<li><a href="#api.widget">wgtGetText</a></li>
|
|
<li><a href="#widget.base">wgtGetText</a></li>
|
|
<li><a href="#widget.box">wgtHBox</a></li>
|
|
<li><a href="#widget.separator">wgtHSeparator</a></li>
|
|
<li><a href="#arch.widgets">WgtIfaceT</a></li>
|
|
<li><a href="#widget.image">wgtImage</a></li>
|
|
<li><a href="#widget.imagebutton">wgtImageButton</a></li>
|
|
<li><a href="#widget.imagebutton">wgtImageButtonFromFile</a></li>
|
|
<li><a href="#widget.imagebutton">wgtImageButtonLoadFile</a></li>
|
|
<li><a href="#widget.imagebutton">wgtImageButtonSetData</a></li>
|
|
<li><a href="#widget.image">wgtImageFromFile</a></li>
|
|
<li><a href="#widget.image">wgtImageLoadFile</a></li>
|
|
<li><a href="#widget.image">wgtImageSetData</a></li>
|
|
<li><a href="#widget.image">wgtImageSetTransparent</a></li>
|
|
<li><a href="#api.widget">wgtInitWindow</a></li>
|
|
<li><a href="#widget.base">wgtInitWindow</a></li>
|
|
<li><a href="#api.widget">wgtInvalidate</a></li>
|
|
<li><a href="#widget.base">wgtInvalidate</a></li>
|
|
<li><a href="#api.widget">wgtInvalidatePaint</a></li>
|
|
<li><a href="#widget.label">wgtLabel</a></li>
|
|
<li><a href="#widget.label">wgtLabelGetAlign</a></li>
|
|
<li><a href="#widget.label">wgtLabelSetAlign</a></li>
|
|
<li><a href="#api.widget">wgtLayout</a></li>
|
|
<li><a href="#widget.listbox">wgtListBox</a></li>
|
|
<li><a href="#widget.listbox">wgtListBoxAddItem</a></li>
|
|
<li><a href="#widget.listbox">wgtListBoxClear</a></li>
|
|
<li><a href="#widget.listbox">wgtListBoxClearSelection</a></li>
|
|
<li><a href="#widget.listbox">wgtListBoxGetSelected</a></li>
|
|
<li><a href="#widget.listbox">wgtListBoxIsItemSelected</a></li>
|
|
<li><a href="#widget.listbox">wgtListBoxRemoveItem</a></li>
|
|
<li><a href="#widget.listbox">wgtListBoxSelectAll</a></li>
|
|
<li><a href="#widget.listbox">wgtListBoxSetMultiSelect</a></li>
|
|
<li><a href="#widget.listbox">wgtListBoxSetReorderable</a></li>
|
|
<li><a href="#widget.listbox">wgtListBoxSetSelected</a></li>
|
|
<li><a href="#widget.listview">wgtListView</a></li>
|
|
<li><a href="#widget.listview">wgtListViewClearSelection</a></li>
|
|
<li><a href="#widget.listview">wgtListViewGetSelected</a></li>
|
|
<li><a href="#widget.listview">wgtListViewSelectAll</a></li>
|
|
<li><a href="#widget.listview">wgtListViewSetColumns</a></li>
|
|
<li><a href="#widget.listview">wgtListViewSetData</a></li>
|
|
<li><a href="#widget.listview">wgtListViewSetHeaderClickCallback</a></li>
|
|
<li><a href="#widget.listview">wgtListViewSetMultiSelect</a></li>
|
|
<li><a href="#widget.listview">wgtListViewSetReorderable</a></li>
|
|
<li><a href="#widget.listview">wgtListViewSetSelected</a></li>
|
|
<li><a href="#widget.listview">wgtListViewSetSort</a></li>
|
|
<li><a href="#widget.textinput">wgtMaskedInput</a></li>
|
|
<li><a href="#api.widget">wgtPaint</a></li>
|
|
<li><a href="#widget.textinput">wgtPasswordInput</a></li>
|
|
<li><a href="#api.widget">wgtPercent</a></li>
|
|
<li><a href="#widget.base">wgtPercent</a></li>
|
|
<li><a href="#api.widget">wgtPixels</a></li>
|
|
<li><a href="#widget.base">wgtPixels</a></li>
|
|
<li><a href="#widget.progressbar">wgtProgressBar</a></li>
|
|
<li><a href="#widget.progressbar">wgtProgressBarGetValue</a></li>
|
|
<li><a href="#widget.progressbar">wgtProgressBarSetValue</a></li>
|
|
<li><a href="#widget.progressbar">wgtProgressBarV</a></li>
|
|
<li><a href="#widget.radio">wgtRadio</a></li>
|
|
<li><a href="#widget.radio">wgtRadioGetIndex</a></li>
|
|
<li><a href="#widget.radio">wgtRadioGroup</a></li>
|
|
<li><a href="#widget.radio">wgtRadioGroupGetSelected</a></li>
|
|
<li><a href="#widget.radio">wgtRadioGroupSetSelected</a></li>
|
|
<li><a href="#widget.radio">wgtRadioIsSelected</a></li>
|
|
<li><a href="#widget.radio">wgtRadioSetSelected</a></li>
|
|
<li><a href="#sdk.firstwidget">wgtRegister</a></li>
|
|
<li><a href="#api.widget">wgtRegisterApi</a></li>
|
|
<li><a href="#api.widget">wgtRegisterClass</a></li>
|
|
<li><a href="#api.widget">wgtRegisterIface</a></li>
|
|
<li><a href="#api.widget">wgtResolveSize</a></li>
|
|
<li><a href="#widget.scrollpane">wgtScrollPane</a></li>
|
|
<li><a href="#widget.scrollpane">wgtScrollPaneScrollToChild</a></li>
|
|
<li><a href="#widget.scrollpane">wgtScrollPaneSetNoBorder</a></li>
|
|
<li><a href="#widget.separator">wgtSeparator</a></li>
|
|
<li><a href="#api.widget">wgtSetDebugLayout</a></li>
|
|
<li><a href="#api.widget">wgtSetEnabled</a></li>
|
|
<li><a href="#widget.base">wgtSetEnabled</a></li>
|
|
<li><a href="#api.widget">wgtSetFocused</a></li>
|
|
<li><a href="#widget.base">wgtSetFocused</a></li>
|
|
<li><a href="#api.widget">wgtSetName</a></li>
|
|
<li><a href="#api.widget">wgtSetReadOnly</a></li>
|
|
<li><a href="#api.widget">wgtSetText</a></li>
|
|
<li><a href="#widget.base">wgtSetText</a></li>
|
|
<li><a href="#api.widget">wgtSetTooltip</a></li>
|
|
<li><a href="#api.widget">wgtSetVisible</a></li>
|
|
<li><a href="#widget.base">wgtSetVisible</a></li>
|
|
<li><a href="#widget.slider">wgtSlider</a></li>
|
|
<li><a href="#widget.slider">wgtSliderGetValue</a></li>
|
|
<li><a href="#widget.slider">wgtSliderSetValue</a></li>
|
|
<li><a href="#widget.spacer">wgtSpacer</a></li>
|
|
<li><a href="#widget.spinner">wgtSpinner</a></li>
|
|
<li><a href="#widget.spinner">wgtSpinnerGetRealValue</a></li>
|
|
<li><a href="#widget.spinner">wgtSpinnerGetValue</a></li>
|
|
<li><a href="#widget.spinner">wgtSpinnerSetDecimals</a></li>
|
|
<li><a href="#widget.spinner">wgtSpinnerSetRange</a></li>
|
|
<li><a href="#widget.spinner">wgtSpinnerSetRealMode</a></li>
|
|
<li><a href="#widget.spinner">wgtSpinnerSetRealRange</a></li>
|
|
<li><a href="#widget.spinner">wgtSpinnerSetRealStep</a></li>
|
|
<li><a href="#widget.spinner">wgtSpinnerSetRealValue</a></li>
|
|
<li><a href="#widget.spinner">wgtSpinnerSetStep</a></li>
|
|
<li><a href="#widget.spinner">wgtSpinnerSetValue</a></li>
|
|
<li><a href="#widget.splitter">wgtSplitter</a></li>
|
|
<li><a href="#widget.splitter">wgtSplitterGetPos</a></li>
|
|
<li><a href="#widget.splitter">wgtSplitterSetPos</a></li>
|
|
<li><a href="#widget.statusbar">wgtStatusBar</a></li>
|
|
<li><a href="#widget.tabcontrol">wgtTabControl</a></li>
|
|
<li><a href="#widget.tabcontrol">wgtTabControlGetActive</a></li>
|
|
<li><a href="#widget.tabcontrol">wgtTabControlSetActive</a></li>
|
|
<li><a href="#widget.tabcontrol">wgtTabPage</a></li>
|
|
<li><a href="#widget.textinput">wgtTextArea</a></li>
|
|
<li><a href="#widget.textinput">wgtTextAreaCursorToEnd</a></li>
|
|
<li><a href="#widget.textinput">wgtTextAreaFindNext</a></li>
|
|
<li><a href="#widget.textinput">wgtTextAreaGoToLine</a></li>
|
|
<li><a href="#widget.textinput">wgtTextAreaReplaceAll</a></li>
|
|
<li><a href="#widget.textinput">wgtTextAreaReplaceSelection</a></li>
|
|
<li><a href="#widget.textinput">wgtTextAreaSetAutoIndent</a></li>
|
|
<li><a href="#widget.textinput">wgtTextAreaSetColorize</a></li>
|
|
<li><a href="#widget.textinput">wgtTextAreaSetCursor</a></li>
|
|
<li><a href="#widget.textinput">wgtTextAreaSetShowLineNumbers</a></li>
|
|
<li><a href="#widget.textinput">wgtTextInput</a></li>
|
|
<li><a href="#widget.timer">wgtTimer</a></li>
|
|
<li><a href="#widget.timer">wgtTimerIsRunning</a></li>
|
|
<li><a href="#widget.timer">wgtTimerSetInterval</a></li>
|
|
<li><a href="#widget.timer">wgtTimerStart</a></li>
|
|
<li><a href="#widget.timer">wgtTimerStop</a></li>
|
|
<li><a href="#widget.toolbar">wgtToolbar</a></li>
|
|
<li><a href="#widget.treeview">wgtTreeItem</a></li>
|
|
<li><a href="#widget.treeview">wgtTreeItemIsExpanded</a></li>
|
|
<li><a href="#widget.treeview">wgtTreeItemIsSelected</a></li>
|
|
<li><a href="#widget.treeview">wgtTreeItemSetExpanded</a></li>
|
|
<li><a href="#widget.treeview">wgtTreeItemSetSelected</a></li>
|
|
<li><a href="#widget.treeview">wgtTreeView</a></li>
|
|
<li><a href="#widget.treeview">wgtTreeViewGetSelected</a></li>
|
|
<li><a href="#widget.treeview">wgtTreeViewSetMultiSelect</a></li>
|
|
<li><a href="#widget.treeview">wgtTreeViewSetReorderable</a></li>
|
|
<li><a href="#widget.treeview">wgtTreeViewSetSelected</a></li>
|
|
<li><a href="#api.widget">wgtUnregisterAppClasses</a></li>
|
|
<li><a href="#api.widget">wgtUnregisterClass</a></li>
|
|
<li><a href="#lib.texthelp.cursorblink">wgtUpdateCursorBlink</a></li>
|
|
<li><a href="#widget.timer">wgtUpdateTimers</a></li>
|
|
<li><a href="#widget.box">wgtVBox</a></li>
|
|
<li><a href="#widget.separator">wgtVSeparator</a></li>
|
|
<li><a href="#widget.wrapbox">wgtWrapBox</a></li>
|
|
<li><a href="#arch.widgets">Widget API</a></li>
|
|
<li><a href="#widget.base">Widget Base</a></li>
|
|
<li><a href="#api.widgetplugin">widgetAddChild</a></li>
|
|
<li><a href="#api.widgetplugin">widgetAlloc</a></li>
|
|
<li><a href="#api.widgetplugin">widgetCallbackTarget</a></li>
|
|
<li><a href="#arch.widgets">WidgetClassT</a></li>
|
|
<li><a href="#api.widget">WidgetClassT</a></li>
|
|
<li><a href="#api.widgetplugin">widgetClosePopup</a></li>
|
|
<li><a href="#api.widgetplugin">widgetDestroyChildren</a></li>
|
|
<li><a href="#lib.listhelp">widgetDrawDropdownArrow</a></li>
|
|
<li><a href="#api.widgetplugin">widgetDrawScrollbarH</a></li>
|
|
<li><a href="#api.widgetplugin">widgetDrawScrollbarV</a></li>
|
|
<li><a href="#lib.listhelp">widgetDropdownPopupRect</a></li>
|
|
<li><a href="#api.widgetplugin">widgetFindByAccel</a></li>
|
|
<li><a href="#api.widgetplugin">widgetFindNextFocusable</a></li>
|
|
<li><a href="#api.widgetplugin">widgetFindPrevFocusable</a></li>
|
|
<li><a href="#api.widgetplugin">widgetHitTest</a></li>
|
|
<li><a href="#api.widgetplugin">widgetIsEnabledChain</a></li>
|
|
<li><a href="#lib.listhelp">widgetMaxItemLen</a></li>
|
|
<li><a href="#lib.listhelp">widgetNavigateIndex</a></li>
|
|
<li><a href="#lib.listhelp">widgetPaintPopupList</a></li>
|
|
<li><a href="#arch.widgets">Widgets</a></li>
|
|
<li><a href="#api.widgetplugin">widgetScrollbarApplyHit</a></li>
|
|
<li><a href="#arch.widgets">WidgetT</a></li>
|
|
<li><a href="#api.widget">WidgetT</a></li>
|
|
<li><a href="#widget.base">WidgetT</a></li>
|
|
<li><a href="#lib.texthelp.dragupdate">widgetTextEditDragUpdateLine</a></li>
|
|
<li><a href="#lib.texthelp.mouseclick">widgetTextEditMouseClick</a></li>
|
|
<li><a href="#lib.texthelp.onkey">widgetTextEditOnKey</a></li>
|
|
<li><a href="#lib.texthelp.paintline">widgetTextEditPaintLine</a></li>
|
|
<li><a href="#arch.windows">Window</a></li>
|
|
<li><a href="#user.windows">Window Menu</a></li>
|
|
<li><a href="#arch.windows">Window Stack</a></li>
|
|
<li><a href="#user.windows">Windows</a></li>
|
|
<li><a href="#api.types">WindowStackT</a></li>
|
|
<li><a href="#arch.windows">WindowT</a></li>
|
|
<li><a href="#api.types">WindowT</a></li>
|
|
<li><a href="#api.wm">wmAddMenu</a></li>
|
|
<li><a href="#api.wm">wmAddMenuBar</a></li>
|
|
<li><a href="#api.wm">wmAddMenuItem</a></li>
|
|
<li><a href="#api.wm">wmAddSubMenu</a></li>
|
|
<li><a href="#api.wm">wmClampWindowPos</a></li>
|
|
<li><a href="#api.wm">wmCreateMenu</a></li>
|
|
<li><a href="#api.wm">wmCreateWindow</a></li>
|
|
<li><a href="#api.wm">wmDestroyWindow</a></li>
|
|
<li><a href="#api.wm">wmDragBegin</a></li>
|
|
<li><a href="#api.wm">wmDrawChrome</a></li>
|
|
<li><a href="#api.wm">wmHitTest</a></li>
|
|
<li><a href="#api.wm">wmInit</a></li>
|
|
<li><a href="#api.wm">wmMaximize</a></li>
|
|
<li><a href="#api.wm">wmMinimize</a></li>
|
|
<li><a href="#api.wm">wmMinWindowSize</a></li>
|
|
<li><a href="#api.wm">wmRaiseWindow</a></li>
|
|
<li><a href="#api.wm">wmReallocContentBuf</a></li>
|
|
<li><a href="#api.wm">wmResizeBegin</a></li>
|
|
<li><a href="#api.wm">wmRestore</a></li>
|
|
<li><a href="#api.wm">wmSetFocus</a></li>
|
|
<li><a href="#api.wm">wmSetIconData</a></li>
|
|
<li><a href="#api.wm">wmUpdateContentRect</a></li>
|
|
<li><a href="#lib.texthelp.wordboundaryleft">Word Boundary</a></li>
|
|
<li><a href="#lib.texthelp.wordboundaryright">Word Boundary</a></li>
|
|
<li><a href="#lib.texthelp.mouseclick">Word Select</a></li>
|
|
<li><a href="#lib.texthelp.wordboundaryleft">wordBoundaryLeft</a></li>
|
|
<li><a href="#lib.texthelp.wordboundaryright">wordBoundaryRight</a></li>
|
|
<li><a href="#lib.texthelp.wordend">wordEnd</a></li>
|
|
<li><a href="#lib.texthelp.wordstart">wordStart</a></li>
|
|
<li><a href="#widget.wrapbox">WrapBox</a></li>
|
|
<li><a href="#lib.xfer">xfer</a></li>
|
|
<li><a href="#lib.xfer.basic">xfer.bas</a></li>
|
|
<li><a href="#lib.xfer.api">xferAnswer</a></li>
|
|
<li><a href="#lib.xfer.api">xferCancel</a></li>
|
|
<li><a href="#lib.xfer.api">xferCreate</a></li>
|
|
<li><a href="#lib.xfer.api">xferDetectFeed</a></li>
|
|
<li><a href="#lib.xfer.api">XferIoT</a></li>
|
|
<li><a href="#lib.xfer.api">XferOptionsT</a></li>
|
|
<li><a href="#lib.xfer.api">xferPoll</a></li>
|
|
<li><a href="#lib.xfer.basic">XferPoll</a></li>
|
|
<li><a href="#lib.xfer.api">xferProgress</a></li>
|
|
<li><a href="#lib.xfer.api">XferProgressT</a></li>
|
|
<li><a href="#lib.xfer.basic">XferReceive</a></li>
|
|
<li><a href="#lib.xfer.basic">XferSendFile</a></li>
|
|
<li><a href="#lib.xfer.api">xferStartReceive</a></li>
|
|
<li><a href="#lib.xfer.api">xferStartSend</a></li>
|
|
<li><a href="#lib.music">XM</a></li>
|
|
<li><a href="#lib.xfer">XMODEM</a></li>
|
|
<li><a href="#lib.serial">XTEA</a></li>
|
|
<li><a href="#lib.serial.security">XTEA</a></li>
|
|
<li><a href="#lib.serial.security">XTEA-CTR</a></li>
|
|
<li><a href="#lib.xfer">YMODEM</a></li>
|
|
<li><a href="#sdk.firstbasic">Your First BASIC Program</a></li>
|
|
<li><a href="#sdk.firstapp">Your First DVX Program</a></li>
|
|
<li><a href="#sdk.firstlib">Your First Library</a></li>
|
|
<li><a href="#sdk.firstwidget">Your First Widget</a></li>
|
|
<li><a href="#arch.windows">Z-Order</a></li>
|
|
<li><a href="#lib.xfer.protocols">ZDLE</a></li>
|
|
<li><a href="#lib.xfer">ZMODEM</a></li>
|
|
<li><a href="#lib.xfer.protocols">ZRPOS</a></li>
|
|
</ul>
|
|
</nav>
|
|
<main>
|
|
<div class="topic" id="user.welcome">
|
|
<h1>Welcome to DVX</h1>
|
|
<p style="text-align:center"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAB0CAYAAAD0DOulAAAAAXNSR0IB2cksfwAAAARnQU1BAACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+oEDgIyCfl8ewwAAAyBSURBVHja7Z15jJ1VGcZ/352ZO53OdDojAwVEKAhlK5TFgmUJBMGwSCgJW4OINIZKKhqICwoxkIDIEgRNClGBoLK4gxKwGKBA2UIQEazI0kJVKC0tdoW20xn/OIO0w70zvdN7v/c953ue5P2ry33Pec77fO/Z3gPQL4vShPri14Zc9kbcb32G/XZHPRogMZEgClAC7jbk70ogi6i/MuAHhv111wBnEkCZBLGOsOTshoj66cZUxrfEIn2bB5wmbdssHGE8rTtQH4ohbQMwRQIoq9X6gK8BH5PGDYuJwHojnpYBoxz3TduYMWOsPhDrgN1TSvll+dtS4E5p3LB40JCjBUCTwz5pARYa9su9qa15yOzsNuBo6dyQeNaQnzedZetbA0sM++PpFBd9Zba2QRo3JJqA+Yb8HO+oL0427IeXqdOOrwRQton19PScKp0bEq3A84YcnemgD6Ybj9Mkd3NkvuwYaV1VfN2YG8uP1DTjtp8vAZTltUs8WVpXFTOwOx4zD2g2arfl8szZKTdQ5syyLNvQ09OzrbSuKj5vyM9cg/Y+leq0VwIoq2a3S+eqogdYbMjN7Bzb+rBhO98CxkgAZVZ2Pz7PoXnAaGC5ITdP5NBGy+M/75LjQfCSxrNQAccC96kbKmINMAF43+j39wV2beD/vxewh0XDsiyjq6vr3Lz7VhmPrJKtaXCgxY6bjfnZrwFtmmzcppuKtMsj82+9gDZFqiQshJJMlrv2O9exPbtiWwTiNozKgSnQZUPZ8q6urq2kd1XxI0Nu/lrHdrxk2I5rLQlUkMtUW3DkOMyYm6vr0IbrDf3fABwsAZR5PyT9TWldVew+sHBvxc91I5w+ZsAsQ7/fA3axJk8BLssz20gV+xhzU44w/vfyQJwCW1aLjZPWVc2mHjPk5W2guwZ/e4BFhv4+5IU0rfEIteAdYDywWl1RES8Cexv99mpgB+C/w/y9bkK9QavK08/TmGM8NUMHoYVa0QP8Xd1QFQcAzxn9djthN3eoWzwtwD+txK+zs/MZnBXd0LROpp3htOJq+hB+zdC4yZ8oIbGPU3t7e39nZ+cRoqgqzsH2YPHJFXw6HduTBNMkgEJKGfoG4HOiqCqmGvNzzgeOjB07dqaVH1mW9ZdKpROKHGBC2ssUenO4Op405OVhJ+Oo8MElpC2Cc7CrWOwdLYQjKlbcPEeoKG1Z28/t2MjrGEymOKibCHrOdA4RRRXRDfyLsEtbJKwAdiTUT3QJHYMR6oUpwB/VDRXxLrB/Adu9j2fx0xRYU+FG2G9EUVXcSnGOSd2kgJIAFlEAVwHbiaaquKYA4nd5LGRoDTBOAfSOXmASYfFd+CjWk+6m0VrsrtjVDK0BCo1AM7ouNxT2IJSCSg2rgd1iclgZoDLAPD6yWgb5KHYBXkusTeOBN2IbnILQSKiGYGXMB76VUHvOj038Ns4otAkivhppl4q2qnia+Dc9HldASQAlgEObbopURkvkIvhY7NxKAMVXHvY6Ee0OGuC+SHmNGloDFPLCToR7od3qior4eWwf4paWllOUUSgDVAaosVAvfBbbGoK11PY7UgGlQS8BHFl9uLNFY1Wcpo+YBFBIWAAH7ERRWXVZaqFj3haQ2LleCaD4sqomfYLorIh2QgUZb5wtBUan9rURBKuxd4G6oSJWEzaNFjry6TVCbb81EkBBqA8+AzzK0M84FhUr8FVf8R4SfQtaU2DxZWodHR1/E61VcbkDji5JeRoiCKZYtWrVPuqFqniEcOzECr2E2x7KKJQBKgNssEkIN8XhjrhJ8r0XlcOKUwBTxTpgApFWFUmc76eAQ42zUU2BhaRRLpfLr6gbAPits4/dp4E7NaXSFLgIfE0iHHewmm5dVXCO73C8THGrAkoCWAS+XjAOtJ8WdOnkAfyv1f5eASUBTJ2vEjDXgQgWBm1tbXuVSqUYNquWAFsroCSAReDrCcNAezelQBsGOxOuB8ayY7+OcDNEASUBTJqvo40DcwWJ3T8djHK5PA54n/iOLb0HdCmgJICp83W6caC9mbIINjU1LSDes5uPKqAkgMnzlWXZucaBliouIP4D7DMVUBLAovBlWT5rWmJcfps4KkBvToXoCxRQEsDU+erBvk7dVxLh8coEhG+wXaaAkgCmztcowgtvloE2NnIO24FXEhTAeUBbTEToLnCcAuiBL8sP28vARGB9hPy1EgqdbpPo+HwLGE84JuMeugssjBTfM/ztCcCTEfZZRrhhs03C42I74HlNqTQFLgJfs9DOcC04PsFpbzU7RgElAUydrwy4SyK4WTiMuG561GPX/iAFlAQwdb4mGgfaOmCyc85OKpDwDT4ec5z3NQltgsQngN74mgg8C5QN++VQwr3lonLmXWdcQpsgQj3wIrCbpQOtra1zu7u72531SzMwR8ODB3D88p+mwJoC14OvFuAh4ynXr5xx9WBBp76VbLYCSigCX382DrQbnfDk5Y7vRcB3nfjyZQWUkDpfGbDIONBmGXO0PeE5SesNiAs38uk7DgSwFxingBJS58v6vNvbQIcRPzsAKx2IzdoKvvU68Gu5NxGUAEoAG8HXmcaBtnIgE8sTE/FR3WUZlW+bbD8gQB4ywT09BJN2gYVG4XZsjz90AAty/s1p2B/5WEEoVb+4wp+9OfBnq4x9bAJOUUYhFIWvItwU+SE+Nhou3gxfr3Di67UKKCF1vlqB+caB1ugagpc5EZTbavDZy9vDlyighNT5asJ+R3R6owhpbm72ICT31Dj9zoD7Hfi9klAfUQElJM+XdbDVGyVCbT/rdt2yhWu11v6/brV2qk0QIU983/j3/1LHMd8xELifcNCvv9iCf/szB/7vBLyKUTVpZYDKAPPiKwOuc5Bt1CPQluHjrN+kOrRlMqGqjnV7FiughCLwFftU+DR81Pbbr46cfAof5wNPUkAJqfO1+0D2YhZspVLp6BH6Pg0fu6eXNoCXq5207RQFlJA6X+OwvzJ2QITZaz/wjwby4uW1uo8roITU+bI+i7Yuy7JDIhPAeYSnSRuFtoENCet2Pq6AEqyCL0/cbRloo0aN2tz2znaSGeVxXKTZSVvvlQAKAHR0dKT80NCjxoH2yyGEpUwote9BEGbmyMmFTto8h1BsVwKo7C/pl9asA+1PVfya4EQIzjDg5AtO2j5eAlhgZFlWhKcmpzoItKMG+bQfPspbnWc4/L7qoP29hFJjEkBlfknzdSyw3jjQtneW+S1p9BRwGLQCS530xSfrnlzkNOD1LObIhM8s6SxouyHU0xvrxJf3CcdBlhn70QP8B9tnTyEUbzihnryUchzUMmXNm5txvGz4+51OOHibcM94mQNO3iEUUn3H2I/jgKvq+R+qGILgLVtfR7gpsqDgPDzkQHAGC/IjDvz4BnCWsjNZ0plnd3f3EVmW9RWUg1lORTkDbnbQP4sIZxUlgLKkp94XFbD/b4ggO73RQT+9AYyuh6Jrl1bwNAUejCsIb9oWAfOAvSPx9VUasCtbI5YSNmi0BihsOcrlske3Lh5Y9ykCpkbk64kOfOgCTtUUWJb6zvNuGJfPysEOilCwD3HSd1/UFFhIcfq7MbYCXuPDM3opYQrwVKS+Hwk87MCPbQk71ZoCC1uE0U79Wkq4nZEaXiC8VRIrngRecuDH7JH8IwmgMBhrHPu2mPCKWSqYA+xLOPsYK9YCexIq5lhiEuHsZM3Q2pcstmWQ+xLo76cT/Hg+66Bff1frmo/WAIUPxkIsaCIULogV/cDBwDOJjaHDCbdFLMfSWsJNojc0BRZSFD8IL7LFXGDjrATFD+AxYLqxD62EM4r7awosS73owlH4eJ6yFptO+jjPSV/vKwGUJV11prOz88C2trZY+noqxcEZDvr7D8PNFLQGWNQ5b5bR39+fUp1G7+N4CeGa25KCDLFtCVf7uo39+DEwQ2uAwqZq8aH4bZNIk+Y59u3fhMKmSwo0xBYB2wFvGftxLnCNMkDhw69eqURfX19qVbrLhJsiOzjzqw8Yg+/zlY1EB7DSgR9thArbygCLPO0FUhQ/CIeJJzv068oCix/AqqEysBwxH2ivOBuSacMjIVzsqL9v0mf3/7jFAR8rCWuTEkAJX9I4D/vnLK9Dj4ENXm6b5SAWviQBlAAWATPU5y7hIR5mSgAlekXA/Qb9voY0q9bUC3sPbEZYx8eZH6SlCpZ0phjCphhLOIbRltPvrQd2JryhK1THjoQd+2ZDH5YC47QLHLfgbWzCR7F8IBvLYxe2n3D/VOI3PBYCPzH2YStg7v8AFSZzItvMm/sAAAAASUVORK5CYII=" alt="help.png"></p>
|
|
<p>DVX is a graphical desktop for DOS computers. Instead of typing commands at a prompt, you work with windows, menus, icons and a mouse, much like Windows 3.1.</p>
|
|
<p>With DVX you can run several programs at the same time, each in its own window. You can move and resize those windows, switch between them, and copy text from one program to another.</p>
|
|
<h2>About This Guide</h2>
|
|
<p>This guide explains the things that work the same way everywhere in DVX: starting programs, using windows, menus and dialog boxes, opening and saving files, and getting out of trouble.</p>
|
|
<p>Every program also has its own help, which explains that program's commands. Open a program and press F1, or choose Contents from its Help menu.</p>
|
|
<h2>Where to Go Next</h2>
|
|
<p>If you are new to DVX, read these pages in order:</p>
|
|
<p><a href="#user.start">Getting Started</a></p>
|
|
<p><a href="#user.input">Using the Mouse and Keyboard</a></p>
|
|
<p><a href="#user.windows">Working with Windows</a></p>
|
|
<p><a href="#user.menus">Menus and Dialog Boxes</a></p>
|
|
<p><a href="#user.programs">Running Programs</a></p>
|
|
<blockquote><strong>Tip:</strong> Press F1 at any time to open help. Use the list on the left side of the help window to jump to any page, or click the Search button to look for a word.</blockquote>
|
|
</div>
|
|
<div class="topic" id="user.start">
|
|
<h1>Getting Started</h1>
|
|
<h2>What You Need</h2>
|
|
<ul>
|
|
<li>A PC running DOS with a 386 processor or better. A 486 or Pentium is recommended.</li>
|
|
<li>A video card that supports VESA 2.0 graphics. Most video cards made since the mid 1990s do.</li>
|
|
<li>At least 4 MB of memory. 8 MB or more is recommended.</li>
|
|
<li>A mouse, with its DOS mouse driver loaded before you start DVX. A scroll wheel works if your mouse driver supports it.</li>
|
|
</ul>
|
|
<h2>Starting DVX</h2>
|
|
<p>At the DOS prompt, change to the folder where DVX is installed, type DVX and press Enter. You can also start DVX from any folder by typing its full path, for example C:\DVX\DVX.</p>
|
|
<p>While DVX loads you see a title screen with a bar that fills from left to right. Sometimes the bar fills a second time. That happens after programs or parts of DVX were added or removed, while DVX brings its help files up to date. It only takes a moment.</p>
|
|
<p>When loading is done, the screen changes to the DVX desktop and the Program Manager window opens.</p>
|
|
<h2>The Desktop and the Program Manager</h2>
|
|
<p>The desktop is the background behind all the windows. It can show a solid color or a picture.</p>
|
|
<p>The Program Manager is your home base. Its window shows your programs as icons, sorted into groups you can arrange as you like. Double-click an icon to start that program. The Installed Programs group always lists every program installed in DVX: newly installed programs appear there without any setup.</p>
|
|
<p><a href="#user.programs">Running Programs</a></p>
|
|
<p><a href="#user.progman">The Program Manager</a></p>
|
|
<h2>Leaving DVX</h2>
|
|
<p>Choose Exit DVX from the Program Manager's File menu, or close the Program Manager window. DVX asks "Are you sure you want to exit DVX?". Click Yes to return to DOS.</p>
|
|
<p>Before it exits, DVX closes each running program the same way you would by closing its windows. A program with unsaved work asks whether you want to save it first:</p>
|
|
<ul>
|
|
<li>Yes saves your work, and No discards it. Either way the program closes and DVX goes on to the next program.</li>
|
|
<li>Cancel keeps that program open and stops the exit. DVX keeps running, so you can finish or save your work and choose Exit DVX again. Programs that had already closed stay closed.</li>
|
|
</ul>
|
|
<blockquote><strong>Tip:</strong> If a program does not respond when DVX asks it to close, DVX tells you so and asks whether to end it. Click Yes to end the program and continue exiting (anything it had not saved is lost), or No to stay in DVX.</blockquote>
|
|
<p>If a dialog box is open when you choose Exit DVX, DVX brings it to the front and does not exit. Answer or close the dialog box first.</p>
|
|
<p><a href="#user.input">Next: Using the Mouse and Keyboard</a></p>
|
|
</div>
|
|
<div class="topic" id="user.input">
|
|
<h1>Using the Mouse and Keyboard</h1>
|
|
<h2>The Mouse</h2>
|
|
<p>Moving the mouse moves the pointer on the screen. These are the basic mouse actions:</p>
|
|
<pre> Action How to do it
|
|
------ ------------
|
|
Point Move the pointer over something.
|
|
Click Press and release the left mouse button once.
|
|
Double-click Click the left button twice, quickly.
|
|
Drag Hold the left button down while you move the mouse, then let go.
|
|
Right-click Press and release the right mouse button. Some programs show a short menu of choices.</pre>
|
|
<p>If you hold the pointer still over some buttons and icons, a small tip appears that tells you what they do.</p>
|
|
<p>The speed of the pointer, the double-click speed and the scroll wheel can be changed. See Changing Settings.</p>
|
|
<h2>The Scroll Wheel</h2>
|
|
<p>If your mouse has a scroll wheel, turning it scrolls the window you are working in (the active window), not the window under the pointer.</p>
|
|
<h2>The Keyboard</h2>
|
|
<p>Most things you can do with the mouse you can also do with the keyboard:</p>
|
|
<ul>
|
|
<li>Tab moves to the next button, box or list in a window. Shift+Tab moves back.</li>
|
|
<li>Enter or the Space bar presses the button that is highlighted.</li>
|
|
<li>F10 opens the menu bar of the active window. Alt plus an underlined letter opens that menu directly.</li>
|
|
<li>Alt+Tab switches to the next window.</li>
|
|
<li>F1 opens help.</li>
|
|
</ul>
|
|
<p>Letters that are underlined in menus, buttons and labels are shortcuts. Hold Alt and press the letter to choose that item.</p>
|
|
<p><a href="#user.keys">See the full list of keyboard shortcuts</a></p>
|
|
<p><a href="#user.settings">Changing Settings</a></p>
|
|
</div>
|
|
<div class="topic" id="user.windows">
|
|
<h1>Working with Windows</h1>
|
|
<p>Every program shows its work in one or more windows. Only one window is active at a time: the one you are typing into. The title bar of the active window is drawn in a different color from the others, so you can always tell which one it is.</p>
|
|
<h2>Parts of a Window</h2>
|
|
<ul>
|
|
<li>The title bar across the top shows the window's name.</li>
|
|
<li>The Close box is the small button at the left end of the title bar. It shows a short horizontal bar.</li>
|
|
<li>The Minimize button (a small filled square) and the Maximize button (a larger box outline) are at the right end of the title bar. Windows that cannot change size have no Maximize button.</li>
|
|
<li>Many windows have a menu bar under the title bar.</li>
|
|
<li>Scroll bars appear along the right and bottom edges when there is more to see than fits in the window.</li>
|
|
</ul>
|
|
<h2>Making a Window Active</h2>
|
|
<p>Click anywhere in the window. It comes to the front and becomes active. You can also press Alt+Tab to go to the next window, or Shift+Alt+Tab to go the other way.</p>
|
|
<h2>Moving a Window</h2>
|
|
<p>Drag the title bar. DVX always keeps part of the window on the screen so you cannot lose it.</p>
|
|
<h2>Changing a Window's Size</h2>
|
|
<p>Point at any edge or corner of the window, then drag. Dragging a corner changes the width and the height together. The small notches near each corner show where the corners begin. Some windows, such as dialog boxes, have a fixed size.</p>
|
|
<h2>Maximizing and Restoring</h2>
|
|
<p>Click the Maximize button to make the window fill the whole screen. Click the same button again (it now shows a smaller box) to put the window back to its old size. Double-clicking the title bar does the same thing.</p>
|
|
<h2>Minimizing</h2>
|
|
<p>Click the Minimize button to put the window away. It becomes a small square icon along the bottom of the screen, showing the program's picture or a tiny copy of the window. The program keeps running.</p>
|
|
<p>To bring the window back, double-click its icon. You can also use the Task Manager (Ctrl+Esc) and click Switch To. Icons can be hidden behind other windows; move or minimize those windows to reach them.</p>
|
|
<h2>Closing a Window</h2>
|
|
<p>Any of these closes a window:</p>
|
|
<ul>
|
|
<li>Double-click the Close box at the left end of the title bar.</li>
|
|
<li>Press Alt+F4.</li>
|
|
<li>Click the Close box once to open the window menu, then choose Close.</li>
|
|
<li>Choose Exit or Close from the program's File menu, if it has one.</li>
|
|
</ul>
|
|
<p>Closing a program's last window usually ends the program.</p>
|
|
<h2>The Window Menu</h2>
|
|
<p>Every window has a window menu. Open it by clicking the Close box once, or by pressing Alt+Space. It contains:</p>
|
|
<pre> Item What it does
|
|
---- ------------
|
|
Restore Puts a maximized window back to its old size.
|
|
Move Move the window with the arrow keys. Press Enter when done, or Esc to put it back.
|
|
Size Resize the window with the arrow keys. Right and Down make it bigger, Left and Up make it smaller. Press Enter when done, or Esc to cancel.
|
|
Minimize Turns the window into an icon.
|
|
Maximize Makes the window fill the screen.
|
|
Screenshot... Saves a picture of the whole screen to a file.
|
|
Window Shot... Saves a picture of just this window's contents to a file.
|
|
Close Closes the window.</pre>
|
|
<p>Items that do not apply right now are grayed out.</p>
|
|
<h2>Arranging Windows</h2>
|
|
<p>The Program Manager's Window menu can arrange all open windows for you:</p>
|
|
<ul>
|
|
<li>Cascade stacks the windows in an overlapping staircase so every title bar shows.</li>
|
|
<li>Tile arranges the windows side by side in a grid.</li>
|
|
<li>Tile Horizontally stacks the windows one above the other.</li>
|
|
<li>Tile Vertically places the windows next to each other from left to right.</li>
|
|
</ul>
|
|
<p>Minimized windows are left where they are.</p>
|
|
<h2>Saving a Picture of the Screen</h2>
|
|
<p>Press Ctrl+F12 to save a picture of the whole screen, or Ctrl+Shift+F12 to save a picture of the active window. DVX asks where to save it. Name the file ending in .png or .bmp to choose the picture format.</p>
|
|
</div>
|
|
<div class="topic" id="user.menus">
|
|
<h1>Menus and Dialog Boxes</h1>
|
|
<h2>Using Menus</h2>
|
|
<p>Many windows have a menu bar under the title bar, with words such as File, Edit and Help. Each word opens a menu of commands.</p>
|
|
<p>With the mouse, click a menu name to open it, then click a command. While a menu is open, move the pointer along the menu bar to look at the other menus. Click anywhere outside the menu to close it without choosing anything.</p>
|
|
<p>With the keyboard, press F10 to open the first menu, or hold Alt and press the underlined letter of a menu name. Then:</p>
|
|
<ul>
|
|
<li>Up and Down move through the commands.</li>
|
|
<li>Left and Right move to the neighboring menus.</li>
|
|
<li>Enter chooses the highlighted command. You can also press the command's underlined letter.</li>
|
|
<li>Esc closes the menu.</li>
|
|
</ul>
|
|
<p>Some commands show extra signs:</p>
|
|
<ul>
|
|
<li>Three dots (...) after a command mean it opens a dialog box where you give more details.</li>
|
|
<li>A key name at the right edge, such as Ctrl+S, is a shortcut. Press those keys to run the command without opening the menu.</li>
|
|
<li>A check mark means an option is turned on. Choose it again to turn it off.</li>
|
|
<li>An arrow means the command opens another, smaller menu.</li>
|
|
<li>Gray commands cannot be used right now.</li>
|
|
</ul>
|
|
<p>Some programs also show a short menu when you right-click.</p>
|
|
<h2>Menus Most Programs Share</h2>
|
|
<p>Many programs use the same menu names for the same kinds of commands:</p>
|
|
<pre> Menu Usual commands
|
|
---- --------------
|
|
File New, Open..., Save, Save As..., Page Setup..., Print... and Exit.
|
|
Edit Cut, Copy, Paste and Select All.
|
|
Help Contents opens the program's own help. About shows the program's name and copyright.</pre>
|
|
<h2>Dialog Boxes</h2>
|
|
<p>A dialog box is a small window that asks you a question or asks for details. While a dialog box is open you must answer it before you can use the rest of the program.</p>
|
|
<ul>
|
|
<li>Enter presses the main button, usually OK, Open, Save or Yes.</li>
|
|
<li>Esc presses Cancel (or No), and closes the box without making changes.</li>
|
|
<li>Closing a dialog box with its Close box counts as Cancel.</li>
|
|
<li>Tab moves between the parts of the dialog box.</li>
|
|
</ul>
|
|
<p>Message boxes show a short message with a picture: an "i" for information, a "!" for a warning, an "X" for an error, or a "?" for a question. Click one of the buttons to answer.</p>
|
|
<h2>Saving Changes</h2>
|
|
<p>When you close a program, or start a new file, while you have unsaved work, most programs ask "Save changes?":</p>
|
|
<ul>
|
|
<li>Yes saves your work first.</li>
|
|
<li>No throws the changes away.</li>
|
|
<li>Cancel goes back to your work without doing anything.</li>
|
|
</ul>
|
|
<p><a href="#user.controls">Next: Buttons, Lists and Other Controls</a></p>
|
|
<p><a href="#user.filedlg">Opening and Saving Files</a></p>
|
|
</div>
|
|
<div class="topic" id="user.controls">
|
|
<h1>Buttons, Lists and Other Controls</h1>
|
|
<p>Windows and dialog boxes are built from a small set of controls. They all work the same way in every program.</p>
|
|
<h2>Buttons</h2>
|
|
<p>Click a button to use it. When a button is highlighted, Enter or the Space bar presses it too.</p>
|
|
<h2>Check Boxes</h2>
|
|
<p>A check box turns one option on or off. Click it, or press the Space bar when it is highlighted. A check mark means the option is on.</p>
|
|
<h2>Option Buttons</h2>
|
|
<p>Option buttons come in groups, and only one in a group can be chosen. Click the one you want, or use the arrow keys to move through the group.</p>
|
|
<h2>Lists</h2>
|
|
<p>A list shows several items. Click an item to select it, and double-click it (or press Enter) to open or use it. Up, Down, Page Up, Page Down, Home and End move through the list. Typing a letter jumps to the next item that starts with that letter.</p>
|
|
<p>Some lists let you select more than one item. Hold Ctrl while clicking to add or remove single items, or hold Shift while clicking to select everything between two items.</p>
|
|
<p>Some lists have column headings. Click a heading to sort the list by that column; click it again to sort the other way. Drag the line between two headings to make a column wider or narrower.</p>
|
|
<h2>Drop-down Lists</h2>
|
|
<p>A drop-down list shows one choice with an arrow button beside it. Click it to see all the choices, then click the one you want. With the keyboard, Up and Down change the choice, and Enter, the Space bar, F4 or Alt+Down opens the list. In an open list, press Enter, F4 or Alt+Down to take the highlighted choice.</p>
|
|
<p>Some drop-down lists also let you type your own entry in the box.</p>
|
|
<h2>Text Boxes</h2>
|
|
<p>Click in a text box and type. These keys help you edit:</p>
|
|
<pre> Key What it does
|
|
--- ------------
|
|
Left, Right Move one character.
|
|
Ctrl+Left, Ctrl+Right Move one word.
|
|
Home, End Go to the start or end of the line.
|
|
Shift + a moving key Select text as you move.
|
|
Backspace, Del Delete text.
|
|
Ctrl+A Select everything.
|
|
Ctrl+Z Undo the last change. Press it again to redo.</pre>
|
|
<p>You can also drag across text with the mouse to select it. Double-click selects a word, and a triple-click selects the whole line.</p>
|
|
<p>Cut, Copy and Paste work in every text box; see Copying and Pasting.</p>
|
|
<p><a href="#user.clipboard">Copying and Pasting</a></p>
|
|
<h2>Number Boxes</h2>
|
|
<p>A number box has small up and down arrows. Click the arrows, press Up or Down, or type a number.</p>
|
|
<h2>Sliders</h2>
|
|
<p>Drag the slider's knob, or click it and use the arrow keys. Home and End jump to the lowest and highest values.</p>
|
|
<h2>Tabs</h2>
|
|
<p>Some windows have several pages, with tabs along the top like file folders. Click a tab to show its page, or use Left and Right when the tabs are highlighted.</p>
|
|
<h2>Trees</h2>
|
|
<p>A tree shows items inside other items, like folders. Click the small + box to open an item and the - box to close it. With the keyboard, Right opens an item and Left closes it.</p>
|
|
<h2>Scroll Bars</h2>
|
|
<p>A scroll bar lets you see the part of a window that does not fit. Click the arrow at either end to move a little, click the empty track to move a whole page, or drag the box in the middle. The mouse wheel also scrolls.</p>
|
|
</div>
|
|
<div class="topic" id="user.filedlg">
|
|
<h1>Opening and Saving Files</h1>
|
|
<p>When a program opens or saves a file, it shows the same Open or Save As dialog box.</p>
|
|
<h2>Parts of the Dialog Box</h2>
|
|
<ul>
|
|
<li>Path shows the folder you are looking at. Type a different folder or drive here (for example D: or C:\DOCS) and press Enter to go there.</li>
|
|
<li>The list shows the folders in square brackets, such as [DOCS], followed by the files. The [..] entry takes you up one folder.</li>
|
|
<li>Filter, when present, chooses which kinds of files the list shows, for example only text files.</li>
|
|
<li>File name is where you type or check the name of the file.</li>
|
|
</ul>
|
|
<h2>Opening a File</h2>
|
|
<p>Double-click a folder to look inside it. Click a file to put its name in the File name box, then click Open, or just double-click the file. If you type the name of a file that does not exist, the dialog asks whether you want to create it.</p>
|
|
<h2>Saving a File</h2>
|
|
<p>Go to the folder you want, type a name in the File name box and click Save. If you leave off the ending (such as .txt), the dialog adds the one from the Filter box. If a file with that name already exists, DVX asks whether to replace it.</p>
|
|
<blockquote><strong>Tip:</strong> There is no separate drive list. To change drives, type the drive letter and a colon, such as A:, in the Path box and press Enter.</blockquote>
|
|
</div>
|
|
<div class="topic" id="user.clipboard">
|
|
<h1>Copying and Pasting</h1>
|
|
<p>The clipboard is a holding place for text. You put text on it with Cut or Copy, and take it back out with Paste. There is one clipboard shared by every program, so you can copy text in one program and paste it into another.</p>
|
|
<pre> Key Command What it does
|
|
--- ------- ------------
|
|
Ctrl+X Cut Removes the selected text and puts it on the clipboard.
|
|
Ctrl+C Copy Puts a copy of the selected text on the clipboard.
|
|
Ctrl+V Paste Puts the clipboard text where the cursor is. If text is selected, the pasted text replaces it.</pre>
|
|
<p>These keys work in every text box. Many programs also have the same commands in their Edit menu, and some show them when you right-click.</p>
|
|
<h2>Copying Text Between Programs</h2>
|
|
<ul>
|
|
<li>Select the text in the first program, by dragging across it or with Shift and the arrow keys.</li>
|
|
<li>Press Ctrl+C.</li>
|
|
<li>Switch to the other program (click its window or press Alt+Tab).</li>
|
|
<li>Click where the text should go, and press Ctrl+V.</li>
|
|
</ul>
|
|
<p>The clipboard keeps its text until you cut or copy something else, so you can paste the same text many times.</p>
|
|
</div>
|
|
<div class="topic" id="user.printing">
|
|
<h1>Printing</h1>
|
|
<p>Programs that can print have Print... in their File menu, and programs that print text also have Page Setup.... Every program prints to the same printer, so you only tell DVX once which printer you have and where it is plugged in (see Changing Settings). You can also send printouts into a file instead of a printer.</p>
|
|
<h2>The Print Dialog</h2>
|
|
<p>File > Print... opens the Print dialog. Choose where the printout goes (a printer port such as LPT1, or a file), how many copies you want and, for a document of several pages, whether to print all of it or only some pages. Then choose Print.</p>
|
|
<p>While a printout is going to the printer, a small Printing window shows it is busy. You can keep working in other programs, and Cancel stops the printout.</p>
|
|
<h2>When the Printer Has a Problem</h2>
|
|
<p>If the printer is out of paper, switched off, off line or not answering, a message says what is wrong. Fix the problem and choose Retry to carry on, or Cancel to stop printing. Nothing waits forever.</p>
|
|
<h2>Page Setup</h2>
|
|
<p>File > Page Setup... chooses how text is laid out on paper: the margins, how many lines fit on a page, how wide a line is, and a header and footer printed on every page (for example the file name and the page number). Your choices are kept for next time and used by every program that prints text.</p>
|
|
<h2>Pictures</h2>
|
|
<p>Pictures are printed in black and white, as large as fits on the page. Printing pictures needs your printer's model to be set; with the setting for text only, a program tells you so.</p>
|
|
</div>
|
|
<div class="topic" id="user.programs">
|
|
<h1>Running Programs</h1>
|
|
<h2>Finding and Starting a Program</h2>
|
|
<p>Every program installed in DVX appears as an icon in the Program Manager, in its Installed Programs group, and you can put icons for the programs you use most into groups of your own. Select an icon to see a short description of the program in the Program Manager's status bar.</p>
|
|
<p>Double-click an icon to start that program, or select it with the arrow keys and press Enter. The program opens in a new window. While it is loading, the mouse pointer shows an hourglass. The Program Manager's Search box finds a program in every group as you type its name.</p>
|
|
<p>To start a program file that has no icon, choose Run... from the Program Manager's File menu and type or pick the file.</p>
|
|
<h2>Opening Documents</h2>
|
|
<p>A document can be opened the same way as a program: DVX starts the program set up for its kind of file (the letters after the dot in its name, such as TXT) and gives it the document, which the program opens right away. Which program opens which kind of file is set in the settings program (see Changing Settings).</p>
|
|
<h2>Running Several Programs</h2>
|
|
<p>You can run many programs at the same time, each in its own window. They all keep running while you work in another one.</p>
|
|
<p>Most programs can only run once at a time. If you start a program that is already running, DVX tells you so; find its window instead with Alt+Tab or the Task Manager. Some programs can be opened more than once, each copy in its own window.</p>
|
|
<h2>Switching Between Programs</h2>
|
|
<ul>
|
|
<li>Click anywhere in the program's window, or</li>
|
|
<li>Press Alt+Tab until its window comes to the front, or</li>
|
|
<li>Press Ctrl+Esc to open the Task Manager, pick the program and click Switch To.</li>
|
|
</ul>
|
|
<h2>Closing a Program</h2>
|
|
<p>Close the program's main window, or choose Exit from its File menu. A program that has unsaved work usually asks whether you want to save it first. See Working with Windows for all the ways to close a window.</p>
|
|
<h2>Help for Each Program</h2>
|
|
<p>Every program has its own help, which explains its menus and commands. To open it:</p>
|
|
<ul>
|
|
<li>Press F1 while you are working in the program, or</li>
|
|
<li>Choose Contents from the program's Help menu.</li>
|
|
</ul>
|
|
<p>The Help menu usually also has an About command, which shows the program's name and copyright.</p>
|
|
<p>If a program has no help of its own, F1 opens this guide instead.</p>
|
|
<p><a href="#user.help">Getting Help</a></p>
|
|
<p><a href="#user.taskmgr">Task Manager</a></p>
|
|
</div>
|
|
<div class="topic" id="user.progman">
|
|
<h1>The Program Manager</h1>
|
|
<p>The Program Manager opens when DVX starts and stays open the whole time DVX runs. It is where you find and start your programs:</p>
|
|
<ul>
|
|
<li>The group list on the left holds your program groups. Installed Programs, the first group, always shows every installed program. Programs in the Startup group start by themselves each time DVX starts.</li>
|
|
<li>The icons show the programs in the selected group. Double-click one to start it.</li>
|
|
<li>The Search box finds programs in every group.</li>
|
|
<li>The status bar describes the selected program and says how many programs are running.</li>
|
|
</ul>
|
|
<p>You can make your own groups, add, move and copy icons, give an icon a shortcut key that starts it from any program, and choose large icons, small icons or a list. Everything you arrange is remembered the next time you start DVX. The Program Manager's own help explains all of this: press F1 while it is the active window, or choose Contents from its Help menu.</p>
|
|
<blockquote><strong>Warning:</strong> Closing the Program Manager window leaves DVX, after asking you first.</blockquote>
|
|
</div>
|
|
<div class="topic" id="user.settings">
|
|
<h1>Changing Settings</h1>
|
|
<p>The mouse, the screen colors, the desktop picture, the screen resolution, which program opens each kind of document, the network, the printer, joysticks and the sounds are changed with the settings program, which has an icon in the Installed Programs group of the Program Manager. Start it like any other program. Its own help, opened with F1 or its Help menu, explains every setting.</p>
|
|
<p>Settings you keep are saved, and DVX uses them again the next time it starts.</p>
|
|
<p>DVX plays a short sound when it starts and when it ends, and when a message appears (a different one for errors, warnings, questions and information). Each sound can be changed or turned off, and the volume set, in the settings program.</p>
|
|
<blockquote><strong>Tip:</strong> If a new screen resolution leaves the screen unreadable, just wait: DVX goes back to the old one after 15 seconds unless you confirm the change.</blockquote>
|
|
</div>
|
|
<div class="topic" id="user.taskmgr">
|
|
<h1>Task Manager</h1>
|
|
<p>The Task Manager shows every program that is running and lets you switch to one or stop one. Press Ctrl+Esc from anywhere to open it, or choose Task Manager from the Program Manager's Help menu.</p>
|
|
<p>The list shows each program's name, its window title, its file, how much memory it is using, and its status. The line at the bottom shows how many programs are running and how much memory is in use.</p>
|
|
<pre> Button What it does
|
|
------ ------------
|
|
Switch To Brings the selected program's window to the front, even if it was minimized. Double-clicking a program does the same.
|
|
End Task Stops the selected program immediately.
|
|
Run... Starts a program you type or pick, or opens a document in its program.</pre>
|
|
<h2>When a Program Stops Responding</h2>
|
|
<p>If a program stops reacting to the mouse and keyboard, press Ctrl+Esc. After a few seconds the Task Manager shows its status as Not responding (a program that is only waiting for you to answer one of its own messages is not counted). Select the program and click End Task. The other programs keep running, and the selection moves to the program next to the one you ended.</p>
|
|
<blockquote><strong>Warning:</strong> End Task does not ask first, and it does not give the program a chance to save. Any unsaved work in that program is lost.</blockquote>
|
|
<p>If the whole screen is frozen and even the mouse pointer does not move, DVX cannot recover. Restart the computer.</p>
|
|
<h2>When a Program Crashes</h2>
|
|
<p>If a program makes a serious mistake, DVX stops it and shows an Application Error message saying the program "has caused a fault and will be terminated". Click OK. The rest of DVX and your other programs keep running, but unsaved work in the stopped program is lost.</p>
|
|
</div>
|
|
<div class="topic" id="user.help">
|
|
<h1>Getting Help</h1>
|
|
<p>Press F1 at any time. F1 opens the help of the program you are working in; a program with no help of its own opens this guide instead. To come back to this guide from anywhere, choose DVX Help from the Program Manager's Help menu.</p>
|
|
<p>Help opens in the DVX Help window. If DVX Help is already open, F1 shows the new page in the same window.</p>
|
|
<h2>Finding Your Way Around</h2>
|
|
<ul>
|
|
<li>The list on the left is the table of contents. Click a page name to read it. Click the small + box beside a name to see the pages under it.</li>
|
|
<li>Underlined words in a page are links. Click one to jump to that page. With the keyboard, press Tab to reach a link and Enter to follow it.</li>
|
|
<li>The < Back and Fwd > buttons retrace your steps, like pages in a book. Alt+Left and Alt+Right do the same.</li>
|
|
<li>The Index button shows an alphabetical list of subjects. Pick one to go to its page.</li>
|
|
<li>The Search button (or Ctrl+F) finds every page containing the words you type. Parts of words work too, and upper and lower case do not matter.</li>
|
|
<li>Esc closes the help window.</li>
|
|
</ul>
|
|
<p>Press F1 in the DVX Help window itself to learn more about using it.</p>
|
|
</div>
|
|
<div class="topic" id="user.keys">
|
|
<h1>Keyboard Shortcuts</h1>
|
|
<p>These keys work in every program:</p>
|
|
<pre> Key What it does
|
|
--- ------------
|
|
F1 Open help for the program you are using.
|
|
Alt+Tab Switch to the next window.
|
|
Shift+Alt+Tab Switch to the previous window.
|
|
Ctrl+Esc Open the Task Manager.
|
|
Alt+F4 Close the active window.
|
|
Alt+Space Open the window menu of the active window.
|
|
F10 Open the menu bar of the active window.
|
|
Alt+letter Open the menu, or press the button, whose underlined letter you typed.
|
|
Tab, Shift+Tab Move to the next or previous control in a window.
|
|
Enter Press the highlighted button, or the main button of a dialog box.
|
|
Esc Close a menu or drop-down list, or cancel a dialog box.
|
|
F4, Alt+Down Open a drop-down list, or take the highlighted choice.
|
|
Ctrl+F12 Save a picture of the whole screen.
|
|
Ctrl+Shift+F12 Save a picture of the active window.</pre>
|
|
<h2>Editing Text</h2>
|
|
<pre> Key What it does
|
|
--- ------------
|
|
Ctrl+X Cut
|
|
Ctrl+C Copy
|
|
Ctrl+V Paste
|
|
Ctrl+A Select everything
|
|
Ctrl+Z Undo (press again to redo)</pre>
|
|
<h2>In DVX Help</h2>
|
|
<pre> Key What it does
|
|
--- ------------
|
|
Alt+Left Back
|
|
Alt+Right Forward
|
|
Ctrl+F Search
|
|
Esc Close the help window</pre>
|
|
<p>Programs can have their own shortcut keys. Look for them at the right edge of each program's menus, and in each program's help. A shortcut key given to a program icon works everywhere, whichever window is in front (but not while a menu or a message is open).</p>
|
|
</div>
|
|
<div class="topic" id="user.trouble">
|
|
<h1>Troubleshooting</h1>
|
|
<h2>The mouse pointer does not move</h2>
|
|
<p>DVX uses your DOS mouse driver. Load the mouse driver (usually from AUTOEXEC.BAT, or by typing its name at the DOS prompt) before you start DVX. If the scroll wheel does nothing, your mouse driver may not support wheels; everything else still works. If the pointer is too fast or too slow, change the mouse settings (see Changing Settings).</p>
|
|
<h2>DVX will not start, or the screen stays blank</h2>
|
|
<p>DVX needs a video card with VESA 2.0 graphics. Some older cards need a VESA driver program loaded first; check the card's manual.</p>
|
|
<p>If the screen goes wrong right after you picked a new screen resolution, wait 15 seconds and DVX puts the old one back.</p>
|
|
<h2>Safe video mode</h2>
|
|
<p>If the screen resolution DVX is set to use does not work on your computer (for example after changing video cards), DVX starts in safe video mode instead: 640x480, which every video card can show. A message tells you so and asks whether to use this mode from now on. Answer Yes to keep it, or No to try the old resolution again at the next start.</p>
|
|
<p>If DVX starts but the screen cannot be read (the monitor cannot show the saved resolution), hold down a Shift key while DVX starts. DVX then starts in safe video mode and asks the same question. Typing DVX /SAFE at the DOS prompt does the same.</p>
|
|
<p>You can pick another resolution at any time with the settings program (see Changing Settings).</p>
|
|
<h2>There is no sound</h2>
|
|
<p>DVX finds the sound hardware by itself. A Sound Blaster needs its BLASTER setting in AUTOEXEC.BAT, for example SET BLASTER=A220 I5 D1 H5 (the card's manual gives the numbers). Without a sound card DVX uses the PC speaker, which plays tones and beeps but not recorded sounds. The settings program shows the sound hardware in use, lets you pick it yourself and sets the volume; if a sound is set to none there, nothing is heard for it. Without any sound hardware DVX and its programs work the same, silently.</p>
|
|
<h2>An empty floppy or CD-ROM drive</h2>
|
|
<p>When a program tries to read a drive that has no disk in it, or to write to a write-protected disk, the program simply reports that it could not read or write the file. DVX never stops at the DOS "Abort, Retry, Fail" question. Put the disk in and try again.</p>
|
|
<h2>Installing, upgrading and removing DVX</h2>
|
|
<p>DVX is installed with SETUP, from the DVX CD or from Disk 1 of the DVX floppy disks: change to that drive and type SETUP. Setup shows the licence, asks for the folder to install DVX in, checks that there is room and unpacks DVX there, asking for each floppy disk in turn. It can also make DVX start when the computer starts (it shows the line it adds to AUTOEXEC.BAT and keeps a copy of the old file as AUTOEXEC.DVX). Setup never writes to the CD or the floppy disks.</p>
|
|
<p>To upgrade or repair DVX, run Setup again and choose the same folder: answer Yes to keep your settings. To remove DVX, run SETUP in the DVX folder (for example C:\DVX\SETUP) and choose Uninstall. Files you added to the folder yourself are not removed, and your settings are only removed if you ask.</p>
|
|
<h2>Running DVX from a CD-ROM or a write-protected disk</h2>
|
|
<p>DVX also runs straight from a disk it cannot write to. It then tells you once that changed settings last only until you exit DVX, and the help files are used as they are. To keep your settings, install DVX on your hard disk with Setup.</p>
|
|
<h2>A program is already running</h2>
|
|
<p>Most programs can only be open once. Use Alt+Tab or the Task Manager (Ctrl+Esc) to find its window. If you cannot see it, it may be minimized to an icon at the bottom of the screen.</p>
|
|
<h2>"This program was built for a different version of DVX"</h2>
|
|
<p>The program was made for an older or newer version of DVX and cannot run on this one. Get a copy of the program made for your version of DVX. The same thing can happen to an add-on (a file in the LIBS or WIDGETS folder): DVX then stops while starting and names the file. Remove that file or replace it with one made for your version, then start DVX again.</p>
|
|
<h2>A program stopped responding or crashed</h2>
|
|
<p>Press Ctrl+Esc and use End Task. See the Task Manager page for details.</p>
|
|
<h2>Running low on memory</h2>
|
|
<p>Each running program uses memory. If DVX reports that it is out of memory, close programs you are not using. The Task Manager shows how much memory each program uses, and System Information in the Program Manager's Help menu shows how much memory your computer has free. A lower screen resolution or fewer colors also frees memory.</p>
|
|
<h2>A window is lost off the screen or behind others</h2>
|
|
<p>Press Alt+Tab until it comes to the front, or use the Program Manager's Window menu and choose Cascade to bring every window into view.</p>
|
|
<h2>DVX will not exit</h2>
|
|
<p>When you exit DVX, each program is closed as if you had closed it yourself. If you answer Cancel when a program asks to save its work, DVX stays running. Save or close that program, then choose Exit DVX again.</p>
|
|
<h2>Reporting a problem</h2>
|
|
<p>DVX writes a record of what happened to the file DVX.LOG, in the same folder as DVX.EXE. If a program crashes, this file contains details that help the program's author find the problem. The file is started fresh each time DVX starts, so copy it before starting DVX again.</p>
|
|
<p><a href="#user.settings">Changing Settings</a></p>
|
|
<p><a href="#user.taskmgr">Task Manager</a></p>
|
|
</div>
|
|
<div class="topic" id="user.developers">
|
|
<h1>For Developers</h1>
|
|
<p>The rest of this help file is a technical reference for programmers who write programs, controls or add-ons for DVX in C. It is listed under Technical Reference (for Developers) in the table of contents.</p>
|
|
<p><a href="#sys.welcome">Start with the Developer Overview</a></p>
|
|
<p>Programs can also be written in BASIC, if the BASIC programming environment is installed. It is easier to learn and has its own help: open it and press F1.</p>
|
|
</div>
|
|
<div class="topic" id="sys.welcome">
|
|
<h1>Developer Overview</h1>
|
|
<p style="text-align:center"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAUAAAAB0CAYAAAD0DOulAAAAAXNSR0IB2cksfwAAAARnQU1BAACxjwv8YQUAAAAgY0hSTQAAeiYAAICEAAD6AAAAgOgAAHUwAADqYAAAOpgAABdwnLpRPAAAAAZiS0dEAAAAAAAA+UO7fwAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+oEDgIyCfl8ewwAAAyBSURBVHja7Z15jJ1VGcZ/352ZO53OdDojAwVEKAhlK5TFgmUJBMGwSCgJW4OINIZKKhqICwoxkIDIEgRNClGBoLK4gxKwGKBA2UIQEazI0kJVKC0tdoW20xn/OIO0w70zvdN7v/c953ue5P2ry33Pec77fO/Z3gPQL4vShPri14Zc9kbcb32G/XZHPRogMZEgClAC7jbk70ogi6i/MuAHhv111wBnEkCZBLGOsOTshoj66cZUxrfEIn2bB5wmbdssHGE8rTtQH4ohbQMwRQIoq9X6gK8BH5PGDYuJwHojnpYBoxz3TduYMWOsPhDrgN1TSvll+dtS4E5p3LB40JCjBUCTwz5pARYa9su9qa15yOzsNuBo6dyQeNaQnzedZetbA0sM++PpFBd9Zba2QRo3JJqA+Yb8HO+oL0427IeXqdOOrwRQton19PScKp0bEq3A84YcnemgD6Ybj9Mkd3NkvuwYaV1VfN2YG8uP1DTjtp8vAZTltUs8WVpXFTOwOx4zD2g2arfl8szZKTdQ5syyLNvQ09OzrbSuKj5vyM9cg/Y+leq0VwIoq2a3S+eqogdYbMjN7Bzb+rBhO98CxkgAZVZ2Pz7PoXnAaGC5ITdP5NBGy+M/75LjQfCSxrNQAccC96kbKmINMAF43+j39wV2beD/vxewh0XDsiyjq6vr3Lz7VhmPrJKtaXCgxY6bjfnZrwFtmmzcppuKtMsj82+9gDZFqiQshJJMlrv2O9exPbtiWwTiNozKgSnQZUPZ8q6urq2kd1XxI0Nu/lrHdrxk2I5rLQlUkMtUW3DkOMyYm6vr0IbrDf3fABwsAZR5PyT9TWldVew+sHBvxc91I5w+ZsAsQ7/fA3axJk8BLssz20gV+xhzU44w/vfyQJwCW1aLjZPWVc2mHjPk5W2guwZ/e4BFhv4+5IU0rfEIteAdYDywWl1RES8Cexv99mpgB+C/w/y9bkK9QavK08/TmGM8NUMHoYVa0QP8Xd1QFQcAzxn9djthN3eoWzwtwD+txK+zs/MZnBXd0LROpp3htOJq+hB+zdC4yZ8oIbGPU3t7e39nZ+cRoqgqzsH2YPHJFXw6HduTBNMkgEJKGfoG4HOiqCqmGvNzzgeOjB07dqaVH1mW9ZdKpROKHGBC2ssUenO4Op405OVhJ+Oo8MElpC2Cc7CrWOwdLYQjKlbcPEeoKG1Z28/t2MjrGEymOKibCHrOdA4RRRXRDfyLsEtbJKwAdiTUT3QJHYMR6oUpwB/VDRXxLrB/Adu9j2fx0xRYU+FG2G9EUVXcSnGOSd2kgJIAFlEAVwHbiaaquKYA4nd5LGRoDTBOAfSOXmASYfFd+CjWk+6m0VrsrtjVDK0BCo1AM7ouNxT2IJSCSg2rgd1iclgZoDLAPD6yWgb5KHYBXkusTeOBN2IbnILQSKiGYGXMB76VUHvOj038Ns4otAkivhppl4q2qnia+Dc9HldASQAlgEObbopURkvkIvhY7NxKAMVXHvY6Ee0OGuC+SHmNGloDFPLCToR7od3qior4eWwf4paWllOUUSgDVAaosVAvfBbbGoK11PY7UgGlQS8BHFl9uLNFY1Wcpo+YBFBIWAAH7ERRWXVZaqFj3haQ2LleCaD4sqomfYLorIh2QgUZb5wtBUan9rURBKuxd4G6oSJWEzaNFjry6TVCbb81EkBBqA8+AzzK0M84FhUr8FVf8R4SfQtaU2DxZWodHR1/E61VcbkDji5JeRoiCKZYtWrVPuqFqniEcOzECr2E2x7KKJQBKgNssEkIN8XhjrhJ8r0XlcOKUwBTxTpgApFWFUmc76eAQ42zUU2BhaRRLpfLr6gbAPits4/dp4E7NaXSFLgIfE0iHHewmm5dVXCO73C8THGrAkoCWAS+XjAOtJ8WdOnkAfyv1f5eASUBTJ2vEjDXgQgWBm1tbXuVSqUYNquWAFsroCSAReDrCcNAezelQBsGOxOuB8ayY7+OcDNEASUBTJqvo40DcwWJ3T8djHK5PA54n/iOLb0HdCmgJICp83W6caC9mbIINjU1LSDes5uPKqAkgMnzlWXZucaBliouIP4D7DMVUBLAovBlWT5rWmJcfps4KkBvToXoCxRQEsDU+erBvk7dVxLh8coEhG+wXaaAkgCmztcowgtvloE2NnIO24FXEhTAeUBbTEToLnCcAuiBL8sP28vARGB9hPy1EgqdbpPo+HwLGE84JuMeugssjBTfM/ztCcCTEfZZRrhhs03C42I74HlNqTQFLgJfs9DOcC04PsFpbzU7RgElAUydrwy4SyK4WTiMuG561GPX/iAFlAQwdb4mGgfaOmCyc85OKpDwDT4ec5z3NQltgsQngN74mgg8C5QN++VQwr3lonLmXWdcQpsgQj3wIrCbpQOtra1zu7u72531SzMwR8ODB3D88p+mwJoC14OvFuAh4ynXr5xx9WBBp76VbLYCSigCX382DrQbnfDk5Y7vRcB3nfjyZQWUkDpfGbDIONBmGXO0PeE5SesNiAs38uk7DgSwFxingBJS58v6vNvbQIcRPzsAKx2IzdoKvvU68Gu5NxGUAEoAG8HXmcaBtnIgE8sTE/FR3WUZlW+bbD8gQB4ywT09BJN2gYVG4XZsjz90AAty/s1p2B/5WEEoVb+4wp+9OfBnq4x9bAJOUUYhFIWvItwU+SE+Nhou3gxfr3Di67UKKCF1vlqB+caB1ugagpc5EZTbavDZy9vDlyighNT5asJ+R3R6owhpbm72ICT31Dj9zoD7Hfi9klAfUQElJM+XdbDVGyVCbT/rdt2yhWu11v6/brV2qk0QIU983/j3/1LHMd8xELifcNCvv9iCf/szB/7vBLyKUTVpZYDKAPPiKwOuc5Bt1CPQluHjrN+kOrRlMqGqjnV7FiughCLwFftU+DR81Pbbr46cfAof5wNPUkAJqfO1+0D2YhZspVLp6BH6Pg0fu6eXNoCXq5207RQFlJA6X+OwvzJ2QITZaz/wjwby4uW1uo8roITU+bI+i7Yuy7JDIhPAeYSnSRuFtoENCet2Pq6AEqyCL0/cbRloo0aN2tz2znaSGeVxXKTZSVvvlQAKAHR0dKT80NCjxoH2yyGEpUwote9BEGbmyMmFTto8h1BsVwKo7C/pl9asA+1PVfya4EQIzjDg5AtO2j5eAlhgZFlWhKcmpzoItKMG+bQfPspbnWc4/L7qoP29hFJjEkBlfknzdSyw3jjQtneW+S1p9BRwGLQCS530xSfrnlzkNOD1LObIhM8s6SxouyHU0xvrxJf3CcdBlhn70QP8B9tnTyEUbzihnryUchzUMmXNm5txvGz4+51OOHibcM94mQNO3iEUUn3H2I/jgKvq+R+qGILgLVtfR7gpsqDgPDzkQHAGC/IjDvz4BnCWsjNZ0plnd3f3EVmW9RWUg1lORTkDbnbQP4sIZxUlgLKkp94XFbD/b4ggO73RQT+9AYyuh6Jrl1bwNAUejCsIb9oWAfOAvSPx9VUasCtbI5YSNmi0BihsOcrlske3Lh5Y9ykCpkbk64kOfOgCTtUUWJb6zvNuGJfPysEOilCwD3HSd1/UFFhIcfq7MbYCXuPDM3opYQrwVKS+Hwk87MCPbQk71ZoCC1uE0U79Wkq4nZEaXiC8VRIrngRecuDH7JH8IwmgMBhrHPu2mPCKWSqYA+xLOPsYK9YCexIq5lhiEuHsZM3Q2pcstmWQ+xLo76cT/Hg+66Bff1frmo/WAIUPxkIsaCIULogV/cDBwDOJjaHDCbdFLMfSWsJNojc0BRZSFD8IL7LFXGDjrATFD+AxYLqxD62EM4r7awosS73owlH4eJ6yFptO+jjPSV/vKwGUJV11prOz88C2trZY+noqxcEZDvr7D8PNFLQGWNQ5b5bR39+fUp1G7+N4CeGa25KCDLFtCVf7uo39+DEwQ2uAwqZq8aH4bZNIk+Y59u3fhMKmSwo0xBYB2wFvGftxLnCNMkDhw69eqURfX19qVbrLhJsiOzjzqw8Yg+/zlY1EB7DSgR9thArbygCLPO0FUhQ/CIeJJzv068oCix/AqqEysBwxH2ivOBuSacMjIVzsqL9v0mf3/7jFAR8rCWuTEkAJX9I4D/vnLK9Dj4ENXm6b5SAWviQBlAAWATPU5y7hIR5mSgAlekXA/Qb9voY0q9bUC3sPbEZYx8eZH6SlCpZ0phjCphhLOIbRltPvrQd2JryhK1THjoQd+2ZDH5YC47QLHLfgbWzCR7F8IBvLYxe2n3D/VOI3PBYCPzH2YStg7v8AFSZzItvMm/sAAAAASUVORK5CYII=" alt="help.png"></p>
|
|
<blockquote><strong>Note:</strong> This part of the help is written for programmers. If you just want to use DVX, start with the user guide instead.</blockquote>
|
|
<p><a href="#user.welcome">Welcome to DVX (the user guide)</a></p>
|
|
<p>DVX (DOS Visual eXecutive) is a graphical user interface environment for DOS, designed for 486-class hardware and above. This help file covers the system architecture, the core libdvx API, the widget toolkit, and the platform abstraction layer.</p>
|
|
<h2>What Is libdvx?</h2>
|
|
<p>libdvx is the heart of DVX -- a DXE3 library that provides:</p>
|
|
<ul>
|
|
<li>A VESA VBE 2.0+ video backend (LFB only, no bank switching).</li>
|
|
<li>Optimised 2D drawing primitives (rectangle fill, bevels, text, bitmap blits, cursors).</li>
|
|
<li>A dirty-rectangle compositor that minimises LFB traffic.</li>
|
|
<li>A window manager with Motif-style chrome, drag/resize, menus, and scrollbars.</li>
|
|
<li>A retained-mode widget toolkit with automatic layout.</li>
|
|
<li>Modal dialog helpers, an INI preferences system, a DXE resource system, and per-app memory tracking.</li>
|
|
</ul>
|
|
<p>Applications written in C link against libdvx (and the widget DXEs they need) to build native DVX programs. Applications written in DVX BASIC compile to bytecode that runs on top of libdvx via the form runtime.</p>
|
|
<h2>Target Audience</h2>
|
|
<p>This document is aimed at developers writing native C code for DVX:</p>
|
|
<ul>
|
|
<li>System-level contributors maintaining libdvx itself.</li>
|
|
<li>Widget authors writing new .wgt DXE modules.</li>
|
|
<li>Application authors writing .app DXE modules.</li>
|
|
<li>Tool authors (e.g. the BASIC compiler/runtime) that sit on top of libdvx.</li>
|
|
</ul>
|
|
<p>All examples and signatures assume the DJGPP cross-compiler toolchain.</p>
|
|
<h2>What's Covered</h2>
|
|
<ul>
|
|
<li>Architecture -- Five-layer model, display pipeline, event model, build system.</li>
|
|
<li>API Reference -- Every public function, struct, enum, and constant documented with parameters and return values.</li>
|
|
</ul>
|
|
<p>Use the table of contents on the left to navigate. The API reference is organised by header file; each function has a one-line summary, parameter table, and (where useful) a working example.</p>
|
|
<h2>Conventions</h2>
|
|
<ul>
|
|
<li>Types end in a capital T (e.g. WindowT, DisplayT, BlitOpsT).</li>
|
|
<li>Enum types end in a capital E (e.g. ColorIdE, WallpaperModeE, ScrollbarOrientE).</li>
|
|
<li>Public functions use camelCase and prefixes that identify the subsystem: dvx* (application), wm* (window manager), wgt* (widget), prefs* (preferences), draw* / rect* (drawing), dirtyList* / flush* (compositor), video* / packColor / setClipRect (video), platform* (platform layer).</li>
|
|
<li>Constants use SCREAMING_SNAKE_CASE (e.g. HIT_CONTENT, MB_OK, CURSOR_ARROW).</li>
|
|
<li>Every header uses stdint.h types (int32_t, uint8_t) and stdbool.h types (bool).</li>
|
|
</ul>
|
|
<h2>Getting Started</h2>
|
|
<p>To write a program, widget or library right away, start with the SDK Quick Start: it creates a working project from a template and walks through building, running, debugging and installing it.</p>
|
|
<p><a href="#sdk.quickstart">SDK Quick Start</a></p>
|
|
<p>To understand how DVX works, read these topics in order:</p>
|
|
<ul>
|
|
<li>Architecture Overview -- The big picture, five-layer model, and the design philosophy.</li>
|
|
<li>Display Pipeline -- How the backbuffer, dirty list, and compositor work together.</li>
|
|
<li>Event Model -- How input becomes window/widget callbacks.</li>
|
|
<li>Widget System -- The retained-mode toolkit layered on the window manager.</li>
|
|
<li>DXE Module System -- How apps and widgets are loaded dynamically.</li>
|
|
</ul>
|
|
<p>Then dip into the API Reference to find specific functions. The public entry point for any application is dvxInit / dvxRun in dvxApp.h.</p>
|
|
<h2>License</h2>
|
|
<p>DVX is distributed under the MIT License (see the copyright notice at the top of every source file). Third-party code (stb_image, stb_ds, stb_image_write in thirdparty/) is used under its own permissive license.</p>
|
|
</div>
|
|
<div class="topic" id="arch.overview">
|
|
<h1>DVX Architecture Overview</h1>
|
|
<p>DOS Visual eXecutive -- A Windowing GUI for DJGPP/DPMI</p>
|
|
<p>DVX (DOS Visual eXecutive) is a complete windowing GUI compositor targeting DJGPP/DPMI on DOS. It provides overlapping windows with Motif-style chrome, a retained-mode widget toolkit, cooperative multitasking of DXE-loaded applications, and a dirty-rectangle compositor optimized for 486/Pentium hardware.</p>
|
|
<h2>Key Design Constraints</h2>
|
|
<ul>
|
|
<li>VESA VBE 2.0+ LFB only -- no bank switching. If the hardware cannot provide a linear framebuffer, initialization fails.</li>
|
|
<li>486 baseline -- all hot paths are written to be fast on a 486, with Pentium-specific paths where the gain is significant.</li>
|
|
<li>Single-tasking cooperative model -- applications yield the CPU via tsYield(); there is no preemptive scheduler.</li>
|
|
<li>A single emulator is the trusted reference platform for testing; any bugs observed there are treated as DVX bugs.</li>
|
|
<li>No external font or cursor files -- all bitmaps are compiled in as static const data.</li>
|
|
</ul>
|
|
<p>The runtime environment consists of a bootstrap loader (dvx.exe) that loads core DXE libraries, widget plugins, and the shell, which in turn loads and manages DXE application modules.</p>
|
|
<h2>Contents</h2>
|
|
<p><a href="#arch.layers">Five-Layer Architecture</a></p>
|
|
<p><a href="#arch.pipeline">Display Pipeline</a></p>
|
|
<p><a href="#arch.windows">Window System</a></p>
|
|
<p><a href="#arch.widgets">Widget System</a></p>
|
|
<p><a href="#arch.dialogs">Modal Dialogs</a></p>
|
|
<p><a href="#arch.resources">Resource System</a></p>
|
|
<p><a href="#arch.prefs">Preferences System</a></p>
|
|
<p><a href="#arch.memtrack">Per-App Memory Tracking</a></p>
|
|
<p><a href="#arch.dxe">DXE Module System</a></p>
|
|
<p><a href="#arch.events">Event Model</a></p>
|
|
<p><a href="#arch.fonts">Font System</a></p>
|
|
<p><a href="#arch.colors">Color System</a></p>
|
|
<p><a href="#arch.platform">Platform Layer</a></p>
|
|
<p><a href="#arch.build">Build System</a></p>
|
|
</div>
|
|
<div class="topic" id="arch.layers">
|
|
<h1>Five-Layer Architecture</h1>
|
|
<p>DVX is organized into five layers, each implemented as a single .h/.c pair. Every header includes dvxTypes.h (the shared type definitions) to avoid circular dependencies. The layers are strictly stacked: each layer depends only on the layers below it.</p>
|
|
<pre><code> Applications (DXE .app modules)
|
|
==================================================
|
|
| |
|
|
| +------------------------------------------+ |
|
|
| | Layer 5: dvxApp (Application API) | | dvxApp.h / dvxApp.c
|
|
| | Event loop, window creation, public API | |
|
|
| +------------------------------------------+ |
|
|
| | Layer 4: dvxWm (Window Manager) | | dvxWm.h / dvxWm.c
|
|
| | Window stack, chrome, drag, resize | |
|
|
| +------------------------------------------+ |
|
|
| | Layer 3: dvxComp (Compositor) | | dvxComp.h / dvxComp.c
|
|
| | Dirty rect tracking, merge, LFB flush | |
|
|
| +------------------------------------------+ |
|
|
| | Layer 2: dvxDraw (Drawing Primitives) | | dvxDraw.h / dvxDraw.c
|
|
| | Rects, bevels, text, blits, cursors | |
|
|
| +------------------------------------------+ |
|
|
| | Layer 1: dvxVideo (Video Backend) | | dvxVideo.h / dvxVideo.c
|
|
| | VESA VBE, LFB mapping, pixel format | |
|
|
| +------------------------------------------+ |
|
|
| |
|
|
| +------------------------------------------+ |
|
|
| | Platform Layer (dvxPlat.h) | | dvxPlatformDos.c
|
|
| | OS-specific: video, input, asm spans | |
|
|
| +------------------------------------------+ |
|
|
| |
|
|
| +------------------------------------------+ |
|
|
| | Shared Types (dvxTypes.h) | |
|
|
| | DisplayT, WindowT, RectT, ColorSchemeT | |
|
|
| +------------------------------------------+ |
|
|
==================================================</code></pre>
|
|
<h2>Layer Summary</h2>
|
|
<pre> Layer Header Responsibility
|
|
----- ------ --------------
|
|
1 - Video dvxVideo.h VESA VBE mode negotiation, LFB mapping via DPMI, backbuffer allocation, packColor() (RGB to native pixel format), display-wide clip rectangle.
|
|
2 - Draw dvxDraw.h All 2D drawing: rectFill, rectCopy, drawBevel, drawText/drawTextN, drawMaskedBitmap (cursor), drawTermRow (batch terminal row). Stateless beyond clip rect. Dispatches hot inner loops through BlitOpsT function pointers.
|
|
3 - Compositor dvxComp.h Dirty rectangle tracking (dirtyListAdd), pairwise merge of overlapping rects (dirtyListMerge), and flushRect to copy dirty regions from backBuf to LFB.
|
|
4 - Window Manager dvxWm.h Window lifecycle, Z-order stack, chrome drawing (title bars, bevels, close/minimize/maximize gadgets), hit testing, drag/resize, menu bars, scrollbars, system menu, keyboard move/resize, minimized icon bar.
|
|
5 - Application dvxApp.h Public API aggregating all layers into AppContextT. Provides dvxInit/dvxShutdown, dvxRun/dvxUpdate, window creation helpers, image loading, clipboard, accelerator tables, theme management, wallpaper, video mode switching, screenshot capture.</pre>
|
|
</div>
|
|
<div class="topic" id="arch.pipeline">
|
|
<h1>Display Pipeline</h1>
|
|
<p>The double-buffer strategy is the single most important performance decision in DVX. All drawing goes to a system RAM backbuffer (DisplayT.backBuf); only dirty rectangles are flushed to the linear framebuffer (DisplayT.lfb) in video memory.</p>
|
|
<p>This matters because writes to video memory over the PCI bus are 10-50x slower than writes to main RAM on 486/Pentium hardware for random-access patterns.</p>
|
|
<h2>Per-Frame Compositing Pipeline</h2>
|
|
<pre><code> 1. Input poll (mouse, keyboard)
|
|
|
|
|
2. Event dispatch (focus window callbacks)
|
|
|
|
|
3. Layers call dirtyListAdd() for changed regions
|
|
|
|
|
4. dirtyListMerge() consolidates overlapping rects
|
|
|
|
|
5. For each merged dirty rect:
|
|
a. Clip and redraw desktop background (or wallpaper)
|
|
b. For each window (back-to-front, painter's algorithm):
|
|
- wmDrawChrome() -- frame, title bar, gadgets, menu bar
|
|
- wmDrawContent() -- blit per-window content buffer
|
|
- wmDrawScrollbars()
|
|
c. Draw minimized window icons
|
|
d. Draw popup menus / tooltips (overlay pass)
|
|
e. Draw software mouse cursor
|
|
|
|
|
6. flushRect() -- copy each dirty rect from backBuf to LFB
|
|
|
|
|
7. Yield (platformYield)</code></pre>
|
|
<h2>Key Data Structures</h2>
|
|
<p>DisplayT -- Central display context: width, height, pitch, pixel format, LFB pointer, backbuffer pointer, palette, clip rectangle. Passed by pointer through every layer -- no globals.</p>
|
|
<p>BlitOpsT -- Vtable of span fill/copy function pointers resolved at init time for the active pixel depth. On DOS these dispatch to hand-written rep stosl / rep movsd asm inner loops.</p>
|
|
<p>DirtyListT -- Fixed-capacity dynamic array of RectT. Linear scanning for merge candidates is cache-friendly at typical sizes (under 128 rects). If the list fills up, the compositor merges aggressively or falls back to full-screen repaint.</p>
|
|
<h2>Why This Works on a 486</h2>
|
|
<ul>
|
|
<li>A full 640x480x32bpp frame is 1.2 MB -- far too much to flush every frame over a slow PCI bus.</li>
|
|
<li>A typical dirty region during normal interaction (typing, menu open) is a few KB.</li>
|
|
<li>Merging overlapping dirty rects into larger rects reduces per-rect overhead and improves bus utilization.</li>
|
|
<li>Per-window content buffers persist across frames, so windows don't repaint on expose -- only when their own content changes.</li>
|
|
</ul>
|
|
</div>
|
|
<div class="topic" id="arch.windows">
|
|
<h1>Window System</h1>
|
|
<h2>WindowT Structure</h2>
|
|
<p>Each WindowT is the central object of the window manager. Key fields:</p>
|
|
<pre> Field Group Purpose
|
|
----------- -------
|
|
Geometry (x, y, w, h) Outer frame rectangle (including chrome).
|
|
Content area (contentX/Y/W/H) Computed from frame minus chrome. Where application content lives.
|
|
Content buffer (contentBuf, contentPitch) Per-window backbuffer in native pixel format. Persists across frames.
|
|
Chrome state (menuBar, vScroll, hScroll) Optional menu bar and scrollbars. Affect content area computation.
|
|
Widget tree (widgetRoot) Root of the retained-mode widget tree (NULL if using raw callbacks).
|
|
Callbacks onPaint, onKey, onKeyUp, onMouse, onResize, onClose, onMenu, onScroll, onFocus, onBlur, onCursorQuery.</pre>
|
|
<h2>Window Stack (Z-Order)</h2>
|
|
<p>WindowStackT is an array of WindowT* ordered front-to-back: index count-1 is the topmost window. This allows:</p>
|
|
<ul>
|
|
<li>Back-to-front iteration for painting (painter's algorithm).</li>
|
|
<li>Front-to-back iteration for hit testing (first hit wins).</li>
|
|
<li>Reordering by pointer swap (no copying of large WindowT structs).</li>
|
|
</ul>
|
|
<p>Only one drag/resize/scroll operation can be active system-wide at a time (single mouse), so that state lives on the stack, not on individual windows.</p>
|
|
<h2>Chrome Layout</h2>
|
|
<pre><code> +-------------------------------------------+
|
|
| 4px outer border (raised bevel) |
|
|
| +-------------------------------------+ |
|
|
| | [X] Title Bar Text [_] [^] [X] | | 20px title height
|
|
| +-------------------------------------+ |
|
|
| | 2px inner border | |
|
|
| +-------------------------------------+ |
|
|
| | Menu Bar (optional, 20px) | |
|
|
| +-------------------------------------+ |
|
|
| | | |
|
|
| | Content Area | |
|
|
| | | |
|
|
| | | S | | S = vertical scrollbar
|
|
| | | B | | (16px wide)
|
|
| +-------------------------------------+ |
|
|
| | Horizontal Scrollbar (optional) | | 16px tall
|
|
| +-------------------------------------+ |
|
|
| 4px outer border |
|
|
+-------------------------------------------+</code></pre>
|
|
<p>Chrome constants are compile-time defines:</p>
|
|
<pre><code> CHROME_BORDER_WIDTH = 4px
|
|
CHROME_TITLE_HEIGHT = 20px
|
|
CHROME_INNER_BORDER = 2px
|
|
CHROME_MENU_HEIGHT = 20px
|
|
SCROLLBAR_WIDTH = 16px</code></pre>
|
|
<h2>Hit Test Regions</h2>
|
|
<p>wmHitTest() iterates the stack front-to-back and returns a hit-part identifier: HIT_CONTENT, HIT_TITLE, HIT_CLOSE, HIT_RESIZE, HIT_MENU, HIT_VSCROLL, HIT_HSCROLL, HIT_MINIMIZE, HIT_MAXIMIZE. Resize edge detection returns a bitmask of RESIZE_LEFT, RESIZE_RIGHT, RESIZE_TOP, RESIZE_BOTTOM (corners combine two edges).</p>
|
|
<h2>Menu System</h2>
|
|
<p>Menus use fixed-size inline char buffers for labels (MAX_MENU_LABEL = 32, no heap strings). Both menus per bar and items per menu are stored in dynamic arrays that grow on demand. Supports cascading submenus via MenuItemT.subMenu pointer. Item types: normal, checkbox, radio. Separators are non-interactive items. The popup state (PopupStateT) tracks a stack of parent frames for cascading submenu nesting.</p>
|
|
<h2>Minimized Windows</h2>
|
|
<p>Minimized windows display as 64x64 icons at the bottom of the screen with beveled borders, similar to a classic desktop icon bar. Icons show a scaled-down preview of the window's content buffer, refreshed one per frame in a round-robin fashion to amortize the scaling cost.</p>
|
|
</div>
|
|
<div class="topic" id="arch.widgets">
|
|
<h1>Widget System</h1>
|
|
<p>The widget system (dvxWgt.h) is a retained-mode toolkit layered on top of the window manager. Widgets form a tree rooted at a per-window VBox container.</p>
|
|
<h2>WidgetT Base Structure</h2>
|
|
<p>Every widget shares the same WidgetT struct. The type field is a runtime-assigned integer ID. The wclass pointer references the widget's WidgetClassT vtable. Widget-specific private data is stored in w->data (opaque void*).</p>
|
|
<p>Tree linkage: parent, firstChild, lastChild, nextSibling. No prevSibling -- this halves pointer overhead and removal is still O(n) for typical tree depths of 5-10.</p>
|
|
<h2>Layout Engine</h2>
|
|
<p>Two-pass flexbox-like algorithm:</p>
|
|
<ul>
|
|
<li>Bottom-up (calcMinSize) -- compute minimum sizes for every widget, starting from leaves.</li>
|
|
<li>Top-down (layout) -- allocate space within available bounds, distributing extra space according to weight values (0 = fixed, 100 = normal stretch).</li>
|
|
</ul>
|
|
<p>Size hints use a tagged encoding: the top 2 bits of an int32_t select the unit (pixels, character widths, or percentage of parent), the low 30 bits hold the value. Macros: wgtPixels(v), wgtChars(v), wgtPercent(v).</p>
|
|
<h2>Widget Class Dispatch (WidgetClassT)</h2>
|
|
<p>Each widget type provides a WidgetClassT with a handlers[] array indexed by stable method IDs. Method IDs are never reordered or reused -- new methods append at the end. A change to the slots that would break a compiled widget bumps DVX_ABI_VERSION instead, and the loader then refuses widgets built for the old number (see Binary Interface Version).</p>
|
|
<p>Methods include: PAINT, PAINT_OVERLAY, CALC_MIN_SIZE, LAYOUT, ON_MOUSE, ON_KEY, ON_ACCEL_ACTIVATE, DESTROY, GET_TEXT, SET_TEXT, POLL, ON_BLUR, ON_WHEEL, and more (23 defined, IDs 0-22, room for 32). A widget class whose version is not WGT_CLASS_VERSION is rejected by wgtRegisterClass.</p>
|
|
<h3>Class Flags</h3>
|
|
<pre> Flag Meaning
|
|
---- -------
|
|
WCLASS_FOCUSABLE Can receive keyboard focus (Tab navigation)
|
|
WCLASS_HORIZ_CONTAINER Lays out children horizontally (HBox)
|
|
WCLASS_PAINTS_CHILDREN Widget handles child rendering itself
|
|
WCLASS_SCROLLABLE Mouse wheel sends Up/Down arrow keys
|
|
WCLASS_SCROLL_CONTAINER ScrollPane -- scrolling viewport
|
|
WCLASS_NEEDS_POLL Needs periodic polling (e.g. AnsiTerm comms)
|
|
WCLASS_SWALLOWS_TAB Tab key goes to widget, not focus navigation
|
|
WCLASS_PRESS_RELEASE Click = press + release (buttons)</pre>
|
|
<h2>Available Widget Types</h2>
|
|
<p>Each widget is a separate .wgt DXE module. 29 widget types are included:</p>
|
|
<pre> Widget Description
|
|
------ -----------
|
|
Box (VBox/HBox) Vertical and horizontal layout containers
|
|
Button Clickable push button with label
|
|
Canvas Raw drawing surface for custom painting
|
|
Checkbox Boolean toggle with checkmark
|
|
ComboBox Text input with dropdown list
|
|
DataCtrl Data-bound control for database operations
|
|
DbGrid Database grid (tabular data display)
|
|
Dropdown Dropdown selection list
|
|
Image Static image display
|
|
ImageButton Button with bitmap icon
|
|
Label Static text label
|
|
ListBox Scrollable selection list
|
|
ListView Multi-column list with headers and sorting
|
|
ProgressBar Determinate progress indicator
|
|
Radio Radio button (mutual exclusion group)
|
|
ScrollPane Scrollable viewport container
|
|
Separator Visual divider line
|
|
Slider Value selection via draggable thumb
|
|
Spacer Empty space for layout
|
|
Spinner Numeric input with up/down arrows
|
|
Splitter Resizable split pane
|
|
StatusBar Window status bar with sections
|
|
TabControl Tabbed page container
|
|
Terminal (AnsiTerm) ANSI terminal emulator widget
|
|
TextInput Single-line text entry field
|
|
Timer Periodic timer events
|
|
Toolbar Toolbar with icon buttons
|
|
TreeView Hierarchical tree display
|
|
WrapBox Flow layout (wrapping horizontal container)</pre>
|
|
<h2>Widget API Registry</h2>
|
|
<p>Each widget DXE registers a small API struct under a name during wgtRegister(). Callers retrieve it via wgtGetApi("button", sizeof(ButtonApiT)) and cast to the widget-specific API type. Every API struct starts with a WgtApiHeaderT (the provider's DVX_ABI_VERSION and struct size), so wgtGetApi refuses a table built for another ABI or shorter than the caller expects. Per-widget headers provide typed accessors so callers avoid manual casts. Adding a new widget requires zero changes to the core.</p>
|
|
<h2>Widget Interface Descriptors (WgtIfaceT)</h2>
|
|
<p>Each widget can register an interface descriptor that describes its BASIC-facing properties, methods, and events. These descriptors are used by the form runtime and IDE for generic dispatch and property panel enumeration. Properties have typed getters/setters (WGT_IFACE_STRING, WGT_IFACE_INT, WGT_IFACE_BOOL, WGT_IFACE_ENUM).</p>
|
|
</div>
|
|
<div class="topic" id="arch.dialogs">
|
|
<h1>Modal Dialogs (dvxDlg.h)</h1>
|
|
<p>Pre-built modal dialog boxes block the caller and run their own event loop via dvxUpdate() until the user dismisses them. Each dialog sets ctx->modalWindow to prevent input from reaching other windows while open.</p>
|
|
<h2>Dialog Types</h2>
|
|
<pre> Function Purpose
|
|
-------- -------
|
|
dvxMessageBox Generic message box with button/icon flags (MB_OK, MB_YESNO, MB_ICONERROR, etc.)
|
|
dvxErrorBox Shortcut for MB_OK + MB_ICONERROR
|
|
dvxInfoBox Shortcut for MB_OK + MB_ICONINFO
|
|
dvxFileDialog File open/save dialog with directory navigation and filter dropdown
|
|
dvxInputBox Single-line text input with OK/Cancel
|
|
dvxIntInputBox Integer input with spinner, clamp range, and step
|
|
dvxChoiceDialog List selection with OK/Cancel
|
|
dvxPromptSave Canonical "Save changes?" Yes/No/Cancel dialog</pre>
|
|
<h2>Flag Encoding</h2>
|
|
<p>dvxMessageBox flags split into two nibbles: button configuration (low) and icon type (high). This matches the Win16 MessageBox() convention so OR'd flags read naturally:</p>
|
|
<pre><code>dvxMessageBox(ctx, "Confirm", "Delete file?", MB_YESNO | MB_ICONQUESTION);</code></pre>
|
|
<p>Return values are ID_OK / ID_CANCEL / ID_YES / ID_NO / ID_RETRY.</p>
|
|
</div>
|
|
<div class="topic" id="arch.resources">
|
|
<h1>Resource System (dvxRes.h)</h1>
|
|
<p>Resources are appended to DXE3 files (.app, .wgt, .lib) after the normal DXE content. The DXE loader never reads past the DXE header, so the appended data is invisible to dlopen. Every DXE can carry its own icons, text strings, and binary data without separate resource files.</p>
|
|
<h2>On-Disk Layout</h2>
|
|
<pre><code>[DXE3 content] -- untouched, loaded by dlopen
|
|
[resource data entries] -- sequential, variable length
|
|
[resource directory] -- fixed-size entries (48 bytes each)
|
|
[footer] -- magic + directory offset + count (16 bytes)</code></pre>
|
|
<p>Readers start from the end: seek to EOF - sizeof(footer), verify the magic (DVX_RES_MAGIC = "DVXR"), then seek to the directory. Entries are sorted at open time so lookups are O(log n) via bsearch.</p>
|
|
<h2>Types</h2>
|
|
<pre> Type ID Value Payload
|
|
------- ----- -------
|
|
DVX_RES_ICON 1 Image data (BMP or PNG, typically 16x16 or 32x32 icons)
|
|
DVX_RES_TEXT 2 Null-terminated string (localisable UI text, author, copyright)
|
|
DVX_RES_BINARY 3 Arbitrary data (app-specific)</pre>
|
|
<h2>Runtime API</h2>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
dvxResOpen Open a handle by reading the footer and directory
|
|
dvxResRead Look up a resource by name and read its data into a malloc'd buffer
|
|
dvxResFind Look up a resource's directory entry (no data copy)
|
|
dvxResClose Release the handle
|
|
dvxResAppend Append a new resource to a DXE file
|
|
dvxResRemove Remove a resource by name</pre>
|
|
<p>The dvxApp.h wrappers (dvxResLoadIcon, dvxResLoadText, dvxResLoadData) combine open + read + close for the common case.</p>
|
|
</div>
|
|
<div class="topic" id="arch.prefs">
|
|
<h1>Preferences System (dvxPrefs.h)</h1>
|
|
<p>Handle-based API over classic INI files. Multiple files can be open at once. Each prefsLoad / prefsCreate returns a PrefsHandleT opaque pointer passed to subsequent calls and freed with prefsClose. The system INI lives at DVX_INI_PATH = "CONFIG\\DVX.INI".</p>
|
|
<h2>Usage Pattern</h2>
|
|
<pre><code>PrefsHandleT *h = prefsLoad(DVX_INI_PATH);
|
|
int32_t w = prefsGetInt(h, "video", "width", 640);
|
|
bool sound = prefsGetBool(h, "audio", "enable", true);
|
|
const char *bg = prefsGetString(h, "desktop", "wallpaper", "");
|
|
|
|
prefsSetInt(h, "video", "width", 800);
|
|
prefsSave(h);
|
|
prefsClose(h);</code></pre>
|
|
<p>Boolean getters recognise "true"/"yes"/"1" and "false"/"no"/"0" (case-insensitive). If the file does not exist, prefsLoad still returns a valid empty handle with the path captured so prefsSave can write a new file.</p>
|
|
</div>
|
|
<div class="topic" id="arch.memtrack">
|
|
<h1>Per-App Memory Tracking (dvxMem.h)</h1>
|
|
<p>All allocations route through dvxMalloc/dvxFree wrappers that prepend a 16-byte header recording the owning app ID and allocation size. The Task Manager displays per-app memory usage, and leaks are detected at app termination.</p>
|
|
<h2>Transparent Interception</h2>
|
|
<p>DXE code does NOT need to include dvxMem.h. The DXE export table maps malloc / free / calloc / realloc / strdup to the dvxXxx wrappers, so standard C code is tracked automatically.</p>
|
|
<p>Explicit use (e.g. in the Task Manager) can include dvxMem.h to call:</p>
|
|
<pre> Function Purpose
|
|
-------- -------
|
|
dvxMemGetAppUsage Query current bytes allocated for an app
|
|
dvxMemResetApp Free every tracked allocation charged to an app</pre>
|
|
<p>The dvxMemAppIdPtr pointer is set by the shell to &ctx->currentAppId so the allocator always knows which app to charge.</p>
|
|
</div>
|
|
<div class="topic" id="arch.dxe">
|
|
<h1>DXE Module System</h1>
|
|
<p>DVX uses DJGPP's DXE3 (Dynamic eXtension) format for all loadable modules. DXE3 supports RTLD_GLOBAL symbol sharing -- symbols exported by one module are visible to all subsequently loaded modules. This is critical: widget DXEs call core API functions (e.g. rectFill, wgtInvalidate) that are exported by the core library DXE.</p>
|
|
<h2>Module Types</h2>
|
|
<pre> Extension Directory Purpose Examples
|
|
--------- --------- ------- --------
|
|
.lib LIBS/ Core libraries loaded first. Provide infrastructure APIs. Each exports an ABI stamp. libtasks.lib, libdvx.lib, dvxshell.lib
|
|
.wgt WIDGETS/ Widget type plugins. Each exports wgtRegister() and an ABI stamp. button.wgt, listview.wgt, terminal.wgt
|
|
.app APPS/*/ Application modules. Each exports appDescriptor (which records its ABI) and appMain(). Loaded on demand by the shell. progman.app, notepad.app, cpanel.app</pre>
|
|
<h2>Binary Interface Version</h2>
|
|
<p>DVX_ABI_VERSION (dvxTypes.h) is one number for the binary interface between all modules. Each .lib and .wgt exports <baseName>AbiVersion (written by DVX_ABI_STAMP(baseName) in its main source file); each app's AppDescriptorT starts with abiVersion (SHELL_APP_DESCRIPTOR fills it); each widget API struct starts with a WgtApiHeaderT; each widget class carries WGT_CLASS_VERSION, which is the same number. The loader checks every .lib and .wgt right after loading it and stops the boot with a message naming a module whose stamp is missing or different; the shell refuses a mismatched app with a message box; wgtGetApi and wgtRegisterClass refuse mismatched tables and classes. Details always go to DVX.LOG. The bump rule and the size-table test that enforces it are described with DVX_ABI_VERSION in the API reference (dvxTypes.h).</p>
|
|
<h2>Boot Sequence</h2>
|
|
<pre><code> dvx.exe (loader)
|
|
|
|
|
+-- Enter VGA mode 13h, display splash screen with progress bar
|
|
|
|
|
+-- Scan LIBS/ for *.lib, WIDGETS/ for *.wgt
|
|
|
|
|
+-- Read .dep files for each module (dependency base names)
|
|
|
|
|
+-- Topological sort: load modules in dependency order
|
|
| - dlopen() with RTLD_GLOBAL
|
|
| - ABI stamp checked; a mismatch stops the boot
|
|
| - Each .wgt that exports wgtRegister() has it called
|
|
|
|
|
+-- Find and call shellMain() (exported by dvxshell.lib)
|
|
|
|
|
+-- dvxInit() -- video mode, input, font, colors, cursors
|
|
|
|
|
+-- Load desktop app (progman.app)
|
|
|
|
|
+-- Main loop:
|
|
dvxUpdate() -> tsYield() -> shellReapApps()</code></pre>
|
|
<h2>Application Lifecycle</h2>
|
|
<p>Two kinds of DXE apps:</p>
|
|
<h3>Callback-only (hasMainLoop = false)</h3>
|
|
<p>appMain() creates windows, registers callbacks, and returns. The app lives through GUI callbacks driven by the shell's main loop. Lifecycle ends when the last window is closed. No extra task stack needed -- simpler and cheaper.</p>
|
|
<h3>Main-loop (hasMainLoop = true)</h3>
|
|
<p>A dedicated cooperative task is created. appMain() runs in that task with its own loop, calling tsYield() to share CPU. Needed for apps with continuous work (terminal emulators, games). Lifecycle ends when appMain() returns.</p>
|
|
<h2>Crash Recovery</h2>
|
|
<p>The platform layer installs signal handlers for SIGSEGV, SIGFPE, SIGILL. On crash, the handler logs platform-specific diagnostics (register dump on DJGPP), then longjmps back to the shell's main loop. The crashed app is killed; other apps and the shell survive. This provides Windows 3.1-style fault tolerance.</p>
|
|
<h2>Per-App Memory Tracking</h2>
|
|
<p>All allocations route through dvxMalloc/dvxFree wrappers that prepend a 16-byte header recording the owning app ID and allocation size. The Task Manager displays per-app memory usage, and leaks are detected at app termination.</p>
|
|
</div>
|
|
<div class="topic" id="arch.events">
|
|
<h1>Event Model</h1>
|
|
<p>DVX uses a cooperative polling model. The main loop (dvxRun / dvxUpdate) runs this cycle each frame:</p>
|
|
<ul>
|
|
<li>Poll mouse -- platformMousePoll() returns position and button bitmask. Compare with previous frame for press/release edge detection.</li>
|
|
<li>Poll keyboard -- platformKeyboardRead() returns ASCII + scancode. Non-blocking; returns false if buffer is empty. Shift/Ctrl/Alt+F-keys arrive as the plain F-key plus modifier flags. If an input entropy hook is installed (dvxSetInputEntropyHook), input events are timestamped and fed to it here.</li>
|
|
<li>Dispatch to focused window -- the event loop fires window callbacks (onKey, onMouse, etc.) on the focused window. If the window has a widget tree, the widget system's installed handlers dispatch to individual widgets.</li>
|
|
<li>Tooltips, widget polling (WCLASS_NEEDS_POLL), cursor blink and widget timers.</li>
|
|
<li>Deferred paint flush -- every window whose paintNeeded was raised by wgtInvalidate / wgtInvalidatePaint during the frame is painted once (the highest requested level wins). Windows queued for destruction are skipped.</li>
|
|
<li>Deferred destroy flush -- windows destroyed from inside callbacks are torn down once the dispatch depth is back to zero.</li>
|
|
<li>Compositor pass -- merge dirty rects, composite, flush to LFB.</li>
|
|
<li>Yield -- platformYield() or idle callback.</li>
|
|
</ul>
|
|
<h2>Event Dispatch Chain</h2>
|
|
<pre><code> Mouse/Keyboard Input
|
|
|
|
|
Global handlers (Ctrl+Esc, modal filter)
|
|
|
|
|
System-wide hotkeys (dvxRegisterHotkey)
|
|
|
|
|
Menu keys (Alt+letter), accelerator table check (focused window)
|
|
|
|
|
Window callback (onMouse / onKey)
|
|
|
|
|
[If widget tree installed:]
|
|
|
|
|
widgetOnMouse / widgetOnKey
|
|
|
|
|
Widget hit test (widgetHitTest)
|
|
|
|
|
wclsOnMouse / wclsOnKey (vtable dispatch)
|
|
|
|
|
Universal callbacks (onClick, onChange, etc.)</code></pre>
|
|
<p>Mouse events use two hit tests. widgetHitTest finds the widget whose class handles the press; it stops at a WCLASS_NO_HIT_RECURSE container (Splitter, ScrollPane), so the container's ON_MOUSE handler still gets the press (divider drag, scrollbar clicks, forwarding into its children). widgetCallbackTarget continues through non-focusable NO_HIT_RECURSE containers into the child whose paintClip (the visible part when it was last painted) contains the point. The universal callbacks (onClick, onDblClick, onMouseDown/Up/Move), context menus, tooltips and the cursor shape use that target, so a control inside a Splitter pane or ScrollPane viewport gets its own events only where it is visible; on the container's own chrome the container is the target.</p>
|
|
<h2>Accelerator Tables</h2>
|
|
<p>Per-window accelerator tables map key + modifier combinations to command IDs. The runtime normalizes key/modifier at registration time with the same rule it applies to every keystroke (letters uppercased, Ctrl+letter and Alt+letter mapped to the letter, Ctrl/Alt always significant, Shift significant except for a printable character typed without Ctrl/Alt), so matching at dispatch time is two integer comparisons per entry. Matched accelerators fire the window's onMenu callback with the command ID, unifying the menu and hotkey code paths.</p>
|
|
<h2>Mouse Wheel</h2>
|
|
<p>The wheel goes to the focused window. It is first offered to a WGT_METHOD_ON_WHEEL handler on the focused widget and then on each ancestor (delta in notches, positive = down, reverse setting applied); a handler that returns true consumes it. Otherwise a WCLASS_SCROLLABLE focused widget, or its nearest scroll-container ancestor, receives Up/Down arrow keys (wheelStep per notch), and failing that the window's own scrollbar moves. The focused widget's onScroll callback fires in every case.</p>
|
|
<h2>Mouse Cursor</h2>
|
|
<p>Software-rendered cursor using the classic AND/XOR mask approach. Seven cursor shapes are compiled in: arrow, horizontal resize, vertical resize, NW-SE diagonal resize, NE-SW diagonal resize, busy (hourglass), and crosshair. The cursor is painted into the backbuffer on top of the composited frame and the affected region is flushed to the LFB each frame.</p>
|
|
<h2>Double-Click Detection</h2>
|
|
<p>Timestamp-based: two clicks on the same target (title bar, minimized icon, close gadget) within the configurable double-click interval trigger the double-click action. Separate tracking for each target type.</p>
|
|
</div>
|
|
<div class="topic" id="arch.fonts">
|
|
<h1>Font System</h1>
|
|
<p>DVX uses fixed-width 8-pixel-wide bitmap fonts only. One size is provided: 8x16, matching the standard VGA ROM font and CP437 encoding (256 glyphs).</p>
|
|
<h2>BitmapFontT</h2>
|
|
<pre><code> typedef struct {
|
|
int32_t charWidth; // fixed width per glyph (always 8)
|
|
int32_t charHeight; // 16
|
|
int32_t firstChar; // typically 0
|
|
int32_t numChars; // typically 256
|
|
const uint8_t *glyphData; // packed 1bpp, charHeight bytes per glyph
|
|
} BitmapFontT;</code></pre>
|
|
<p>Design rationale:</p>
|
|
<ul>
|
|
<li>Character positions are pure multiplication (x = col * 8).</li>
|
|
<li>Glyph lookup is a single array index.</li>
|
|
<li>Each scanline of a glyph is exactly one byte (1bpp at 8 pixels wide).</li>
|
|
<li>No glyph-width tables, kerning, or per-character positioning needed.</li>
|
|
<li>8-pixel width aligns with byte boundaries -- no bit shifting in per-scanline rendering.</li>
|
|
</ul>
|
|
<h2>Text Rendering Functions</h2>
|
|
<p>drawChar() -- Renders a single character. Supports opaque (background fill) and transparent modes.</p>
|
|
<p>drawTextN() -- Optimized batch rendering for a known character count. Clips once for the entire run, fills background in a single rectFill, then overlays glyph foreground pixels. Significantly faster than per-character rendering for long runs.</p>
|
|
<p>drawTermRow() -- Renders an 80-column terminal row in a single pass, with per-cell foreground/background from a 16-color palette, blink attribute support, and cursor rendering. Exists because per-character terminal rendering is unacceptably slow on target hardware.</p>
|
|
<p>drawTextAccel() -- Renders text with & accelerator markers. The character after & is underlined to indicate the keyboard shortcut.</p>
|
|
<h2>Performance Optimization</h2>
|
|
<p>AppContextT stores a fixed-point 16.16 reciprocal of font.charHeight (charHeightRecip) so that dividing by charHeight (the pixel-to-row mapping for popup menu and system menu hover/click) becomes a multiply+shift instead of an integer divide, which costs 40+ cycles on a 486.</p>
|
|
</div>
|
|
<div class="topic" id="arch.colors">
|
|
<h1>Color System</h1>
|
|
<h2>Pixel Format</h2>
|
|
<p>PixelFormatT describes the active VESA mode's pixel encoding. Populated once from the VBE mode info block. Stores shift, mask, and bit count for each channel so packColor() can convert RGB to native format with shift-and-mask arithmetic -- no per-pixel computation.</p>
|
|
<p>Supported depths:</p>
|
|
<pre> Depth Bytes/Pixel Notes
|
|
----- ----------- -----
|
|
8 bpp 1 Palette mode. Nearest-index via 6x6x6 color cube + grey ramp.
|
|
15 bpp 2 5-5-5 RGB (1 bit unused).
|
|
16 bpp 2 5-6-5 RGB.
|
|
32 bpp 4 8-8-8 RGB (8 bits unused).</pre>
|
|
<h2>ColorSchemeT -- Theming</h2>
|
|
<p>All 20 UI colors are pre-packed into display pixel format at init time. Every color is a uint32_t that can be written directly to the framebuffer with zero per-pixel conversion. The scheme must be regenerated on video mode change, but mode changes require re-init anyway.</p>
|
|
<p>Color roles mirror classic Motif/Windows 3.x conventions:</p>
|
|
<ul>
|
|
<li>desktop -- desktop background</li>
|
|
<li>windowFace, windowHighlight, windowShadow -- window chrome bevel triplet</li>
|
|
<li>activeTitleBg/Fg, inactiveTitleBg/Fg -- focused vs. unfocused title bar</li>
|
|
<li>contentBg/Fg -- window content area</li>
|
|
<li>menuBg/Fg, menuHighlightBg/Fg -- menus</li>
|
|
<li>buttonFace -- button background</li>
|
|
<li>scrollbarBg/Fg/Trough -- scrollbar components</li>
|
|
<li>cursorFg/Bg -- mouse cursor colors</li>
|
|
</ul>
|
|
<p>Source RGB values are kept in AppContextT.colorRgb[] for theme save/load. Themes are stored as INI files with a [colors] section. The API provides dvxLoadTheme(), dvxSaveTheme(), dvxSetColor(), and dvxResetColorScheme().</p>
|
|
<h2>Bevel Styles</h2>
|
|
<p>Bevels are the defining visual element of the Motif aesthetic. Convenience macros create bevel style descriptors by swapping highlight and shadow colors:</p>
|
|
<pre><code> BEVEL_RAISED(colorScheme, borderWidth) -- raised 3D look
|
|
BEVEL_SUNKEN(colorScheme, face, borderWidth) -- sunken/inset look
|
|
BEVEL_TROUGH(colorScheme) -- 1px scrollbar trough
|
|
BEVEL_SB_BUTTON(colorScheme) -- scrollbar button</code></pre>
|
|
</div>
|
|
<div class="topic" id="arch.platform">
|
|
<h1>Platform Layer</h1>
|
|
<p>All OS-specific and CPU-specific code is isolated behind platform/dvxPlat.h. To port DVX, implement a new dvxPlatformXxx.c against this header.</p>
|
|
<h2>Implementations</h2>
|
|
<pre> File Target Details
|
|
---- ------ -------
|
|
dvxPlatformDos.c DJGPP/DPMI Real VESA VBE, INT 33h mouse, INT 16h keyboard, rep movsd/rep stosl asm spans, DPMI physical memory mapping for LFB, INT 9 hook for key-up, CuteMouse Wheel API.
|
|
dvxPlatformHost.c Linux (test) Host test backend: in-memory framebuffer, scripted mouse/keyboard queues, fake clock. Used only by the host test suites.
|
|
dvxPlatformUtil.c Shared Code common to both backends (paths, globbing, F-key normalization, string helpers).</pre>
|
|
<h2>Abstraction Areas</h2>
|
|
<h3>Video</h3>
|
|
<p>platformVideoInit() -- mode probe and framebuffer setup. platformVideoShutdown() -- restore previous mode. platformVideoEnumModes() -- enumerate available modes.</p>
|
|
<h3>Framebuffer Flush</h3>
|
|
<p>platformFlushRect() -- copy dirty rect from backBuf to LFB. On DOS, each scanline uses rep movsd for near-optimal aligned 32-bit writes over the PCI bus.</p>
|
|
<h3>Optimized Memory Spans</h3>
|
|
<p>Six functions: platformSpanFill8/16/32() and platformSpanCopy8/16/32(). Called once per scanline of every rectangle fill, blit, and text draw. On DOS these use inline assembly for critical inner loops.</p>
|
|
<h3>Mouse Input</h3>
|
|
<p>Polling model. platformMousePoll() returns position and button bitmask. Wheel support via CuteMouse API. platformMouseInit() can be called again after a video-mode change; it re-applies the speed, acceleration and wheel settings after the driver reset.</p>
|
|
<h3>Keyboard Input</h3>
|
|
<p>platformKeyboardRead() -- non-blocking key read; Shift/Ctrl/Alt+F1..F12 are reported as the plain F-key scancode (platformNormalizeFKeyScan). The keyboard hook (a chain on INT 9 on DOS; its code and data are pinned with platformLockRegion) decodes every key going down and up into a held-key table and a queue of raw events (platformKeyEventRead, platformKeyIsDown), which feed onKeyUp and game input windows. platformAltScanToChar() -- scancode-to-ASCII lookup for Alt+key combinations.</p>
|
|
<h3>Timing</h3>
|
|
<p>platformClock() / platformClockHz() -- monotonic UI tick counter. platformMicros() -- high-resolution monotonic clock (PIT, with the TSC between interrupts when there is one). The timer service owns the timer interrupt: callbacks at up to 1000 Hz (platformTimerAdd) while the BIOS keeps its 18.2 Hz tick. platformEntropyStamp() -- fine-grained timestamp (TSC, or BIOS tick + PIT counter) for input-timing entropy.</p>
|
|
<h3>Crash Recovery</h3>
|
|
<p>platformInstallCrashHandler() -- signal handlers + longjmp for fault tolerance. platformOutOfMemory() -- stb_ds allocation-failure hook; logs and raises SIGSEGV so it takes the same recovery path. platformAppExit() / platformAppAbort() -- exit() and abort() as loaded modules see them: the shell's hook ends only the calling program, by the same recovery path, without a fault message.</p>
|
|
<h3>DXE Support</h3>
|
|
<p>platformRegisterDxeExports() -- register C runtime and platform symbols for DXE resolution. platformRegisterSymOverrides() -- register function pointer overrides for module loader (SDK API for embedders and test harnesses; DVX itself does not call it).</p>
|
|
</div>
|
|
<div class="topic" id="arch.build">
|
|
<h1>Build System</h1>
|
|
<h2>Cross-Compilation</h2>
|
|
<p>DVX is cross-compiled from Linux using a DJGPP cross-compiler (i586-pc-msdosdjgpp-gcc). The top-level Makefile orchestrates building all subsystems in dependency order.</p>
|
|
<pre><code> make -- build everything
|
|
./mkcd.sh -- build + create ISO for the target emulator</code></pre>
|
|
<h2>Build Targets</h2>
|
|
<pre><code> all: core tasks loader texthelp listhelp tools widgets shell taskmgr serial sql apps</code></pre>
|
|
<pre> Target Output Description
|
|
------ ------ -----------
|
|
core bin/libs/libdvx.lib GUI core library (draw, comp, wm, app, widget infrastructure)
|
|
tasks bin/libs/libtasks.lib Cooperative task switcher
|
|
loader bin/dvx.exe Bootstrap loader (the DOS executable)
|
|
widgets bin/widgets/*.wgt 29 widget type plugins
|
|
shell bin/libs/dvxshell.lib DVX Shell (app management, desktop)
|
|
taskmgr bin/libs/taskmgr.lib Task Manager (loaded as a separate DXE)
|
|
texthelp shared library Shared text editing helpers (clipboard, word boundaries)
|
|
listhelp shared library Shared dropdown/list helpers
|
|
apps bin/apps/*/*.app Application modules (progman, notepad, clock, etc.)
|
|
tools bin/dvxres Resource compiler (runs on Linux, builds resource sections into DXEs)
|
|
serial serial DXE libs UART driver, HDLC packets, security, seclink
|
|
sql SQL DXE lib SQLite integration</pre>
|
|
<h2>DXE3 Build Process</h2>
|
|
<p>Each DXE module is compiled to an object file with GCC, then linked with dxe3gen:</p>
|
|
<pre><code> # Compile
|
|
i586-pc-msdosdjgpp-gcc -O2 -march=i486 -mtune=i586 -c -o widget.o widget.c
|
|
|
|
# Link as DXE (exports all non-static symbols)
|
|
dxe3gen -o widget.wgt -U widget.o
|
|
|
|
# Optionally append resources
|
|
dvxres build widget.wgt widget.res</code></pre>
|
|
<p>The -E flag specifies exported symbols (prefixed with underscore per DJGPP convention). -U marks unresolved symbols as OK (they'll be resolved at load time from previously loaded DXEs).</p>
|
|
<h2>Deployment (mkcd.sh)</h2>
|
|
<ul>
|
|
<li>Runs make all.</li>
|
|
<li>Verifies critical outputs exist (dvx.exe, libtasks.lib, libdvx.lib, dvxshell.lib).</li>
|
|
<li>Counts widget modules.</li>
|
|
<li>Creates an ISO 9660 image from bin/ using mkisofs: -iso-level 1 (strict 8.3 filenames for DOS), -J (Joliet extensions for long names), -V DVX (volume label).</li>
|
|
<li>Places the ISO at the target emulator's CD-ROM mount path.</li>
|
|
</ul>
|
|
<h2>Compiler Flags</h2>
|
|
<pre><code> -O2 Optimization level 2
|
|
-march=i486 486 instruction set baseline
|
|
-mtune=i586 Optimize scheduling for Pentium
|
|
-Wall -Wextra Full warnings</code></pre>
|
|
<h2>Directory Layout</h2>
|
|
<pre><code> dvxgui/
|
|
+-- core/ Core library sources (dvxVideo, dvxDraw, dvxComp, dvxWm, dvxApp, widget infra)
|
|
| +-- platform/ Platform abstraction (dvxPlat.h, dvxPlatformDos.c, dvxPlatformUtil.c)
|
|
| +-- thirdparty/ stb_image, stb_ds, stb_image_write
|
|
+-- loader/ Bootstrap loader (dvx.exe)
|
|
+-- tasks/ Cooperative task switcher (libtasks.lib)
|
|
+-- shell/ DVX Shell (dvxshell.lib)
|
|
+-- widgets/ Widget DXE modules (*.wgt), each in its own subdirectory
|
|
| +-- box/ VBox/HBox layout containers
|
|
| +-- button/ Push button
|
|
| +-- textInput/ Text entry field
|
|
| +-- listView/ Multi-column list
|
|
| +-- ... (29 widget types total)
|
|
+-- texthelp/ Shared text editing helpers
|
|
+-- listhelp/ Shared dropdown/list helpers
|
|
+-- apps/ Application DXE modules (*.app)
|
|
| +-- progman/ Program Manager (desktop)
|
|
| +-- notepad/ Text editor
|
|
| +-- cpanel/ Control Panel
|
|
| +-- imgview/ Image viewer
|
|
| +-- clock/ Clock
|
|
| +-- dvxdemo/ Demo / showcase app
|
|
| +-- dvxbasic/ DVX BASIC compiler and VM
|
|
+-- tools/ Build tools (dvxres resource compiler)
|
|
+-- rs232/ ISR-driven UART driver
|
|
+-- packet/ HDLC framing, CRC-16, sliding window
|
|
+-- security/ DH key exchange, XTEA cipher, DRBG RNG
|
|
+-- seclink/ Encrypted channel wrapper
|
|
+-- serial/ Combined serial stack DXE
|
|
+-- proxy/ Linux proxy (emulator <-> secLink <-> telnet)
|
|
+-- sql/ SQLite integration
|
|
+-- bin/ Build output (dvx.exe, libs/, widgets/, apps/, config/)
|
|
+-- obj/ Intermediate object files
|
|
+-- docs/ Documentation</code></pre>
|
|
</div>
|
|
<div class="topic" id="api.overview">
|
|
<h1>DVX GUI API Reference</h1>
|
|
<p>DOS Visual eXecutive -- Complete public API documentation covering every public function, type, and constant in the libdvx headers.</p>
|
|
<p>The DVX GUI is built as a five-layer architecture. Each layer is defined in its own header file. Additional headers cover dialogs, resources, preferences, memory tracking, the platform abstraction, and the widget system.</p>
|
|
<h2>Headers</h2>
|
|
<ul>
|
|
<li>dvxTypes.h -- Shared type definitions and constants</li>
|
|
<li>dvxCur.h -- Embedded mouse cursor bitmaps</li>
|
|
<li>dvxFont.h -- Embedded 8x16 CP437 bitmap font</li>
|
|
<li>dvxPal.h -- 8-bit mode 256-color palette</li>
|
|
<li>dvxVideo.h -- Layer 1: VESA VBE video backend</li>
|
|
<li>dvxDraw.h -- Layer 2: 2D drawing primitives</li>
|
|
<li>dvxComp.h -- Layer 3: Dirty rectangle compositor</li>
|
|
<li>dvxWm.h -- Layer 4: Window manager</li>
|
|
<li>dvxApp.h -- Layer 5: Application API</li>
|
|
<li>dvxDlg.h -- Modal dialog boxes</li>
|
|
<li>dvxRes.h -- DXE-embedded resource system</li>
|
|
<li>dvxPrefs.h -- INI preferences system</li>
|
|
<li>dvxMem.h -- Per-app memory tracking</li>
|
|
<li>dvxDrv.h -- Driver modules</li>
|
|
<li>dvxWgt.h -- Widget system public API</li>
|
|
<li>dvxWgtP.h -- Widget plugin API</li>
|
|
<li>platform/dvxPlat.h -- Platform abstraction layer</li>
|
|
</ul>
|
|
<p><a href="#api.types">dvxTypes.h -- Shared Type Definitions</a></p>
|
|
<p><a href="#api.cursor">dvxCur.h -- Cursor Definitions</a></p>
|
|
<p><a href="#api.font">dvxFont.h -- Bitmap Font Data</a></p>
|
|
<p><a href="#api.palette">dvxPal.h -- 8-bit Palette</a></p>
|
|
<p><a href="#api.video">dvxVideo.h -- Layer 1: Video Backend</a></p>
|
|
<p><a href="#api.draw">dvxDraw.h -- Layer 2: Drawing Primitives</a></p>
|
|
<p><a href="#api.comp">dvxComp.h -- Layer 3: Compositor</a></p>
|
|
<p><a href="#api.wm">dvxWm.h -- Layer 4: Window Manager</a></p>
|
|
<p><a href="#api.app">dvxApp.h -- Layer 5: Application API</a></p>
|
|
<p><a href="#api.dialog">dvxDlg.h -- Modal Dialogs</a></p>
|
|
<p><a href="#api.resource">dvxRes.h -- Resource System</a></p>
|
|
<p><a href="#api.prefs">dvxPrefs.h -- Preferences</a></p>
|
|
<p><a href="#api.mem">dvxMem.h -- Memory Tracking</a></p>
|
|
<p><a href="#api.driver">dvxDrv.h -- Driver Modules</a></p>
|
|
<p><a href="#api.widget">dvxWgt.h -- Widget System</a></p>
|
|
<p><a href="#api.widgetplugin">dvxWgtP.h -- Widget Plugin API</a></p>
|
|
<p><a href="#api.platform">platform/dvxPlat.h -- Platform Layer</a></p>
|
|
</div>
|
|
<div class="topic" id="api.types">
|
|
<h1>dvxTypes.h -- Shared Type Definitions</h1>
|
|
<p>Central type definitions shared across all five layers of the DVX GUI stack. Every header includes this file. Contains no function definitions -- only structs, enums, typedefs, and compile-time constants.</p>
|
|
<h2>Binary Interface Version</h2>
|
|
<p>DVX_ABI_VERSION is one number for the whole binary interface between dvx.exe, libraries (.lib), widgets (.wgt) and apps (.app). Every module records the value it was compiled with, and a module whose value differs from the running system's is refused instead of being allowed to call the wrong functions:</p>
|
|
<pre> Module / table Where the value is recorded Checked by
|
|
-------------- --------------------------- ----------
|
|
.lib, .wgt DVX_ABI_STAMP(baseName) exports <baseName>AbiVersion The loader, right after loading the module. A missing or different stamp stops the boot with a message naming the file (details in DVX.LOG).
|
|
.app AppDescriptorT.abiVersion, set by SHELL_APP_DESCRIPTOR The shell, before appMain. The user sees "This program was built for a different version of DVX and cannot run."; DVX.LOG has both versions.
|
|
Widget API WgtApiHeaderT.abiVersion, set by WGT_API_HEADER wgtGetApi (which also checks the table's size).
|
|
Widget class WidgetClassT.version (WGT_CLASS_VERSION is DVX_ABI_VERSION) wgtRegisterClass.
|
|
Driver (.drv) DvxDriverHeaderT.abiVersion, set by DVX_DRIVER_HDR dvxDriversLoad, when the library of its device class loads it (a mismatched driver is skipped and logged).</pre>
|
|
<p>Every library and widget module writes the stamp once at file scope in its main source file; the argument is the module file's base name (libdvx for LIBDVX.LIB, box for BOX.WGT):</p>
|
|
<pre><code>#include "dvxTypes.h"
|
|
|
|
DVX_ABI_STAMP(mylib);</code></pre>
|
|
<p>Bump rule: increment DVX_ABI_VERSION in the same change that alters anything an already compiled module depends on -- the size, member order or member types of a public struct or widget API struct in an SDK header; a WGT_METHOD_* slot or the meaning of a WidgetClassT slot; the parameters, return type or calling contract of a public or exported function (or its removal); the value of a public constant modules compile in. Adding a function, a constant, or a struct no existing struct embeds needs no bump. After a bump, run make -C src/test/abi update to regenerate the committed size table (src/test/abi/abiSizes.txt). The abi host test fails when a public struct changes size without a bump, or when the table was not regenerated.</p>
|
|
<h2>Core Structures</h2>
|
|
<h3>PixelFormatT</h3>
|
|
<p>Describes the pixel encoding for the active VESA video mode. Populated once at startup from the VBE mode info block, then treated as read-only.</p>
|
|
<pre> Field Description
|
|
----- -----------
|
|
int32_t bitsPerPixel 8, 15, 16, or 32
|
|
int32_t bytesPerPixel 1, 2, 2, or 4
|
|
uint32_t redMask, greenMask, blueMask Bitmasks for each color channel
|
|
int32_t redShift, greenShift, blueShift Bit position of each color field
|
|
int32_t redBits, greenBits, blueBits Number of bits per channel</pre>
|
|
<h3>DisplayT</h3>
|
|
<p>Single display context passed by pointer through every layer. All drawing targets the backBuf; only dirty rects are flushed to lfb.</p>
|
|
<pre> Field Description
|
|
----- -----------
|
|
int32_t width, height Screen dimensions in pixels
|
|
int32_t pitch Bytes per scanline
|
|
PixelFormatT format Active pixel format
|
|
uint8_t *lfb Mapped linear framebuffer (VESA LFB)
|
|
uint8_t *backBuf System RAM backbuffer
|
|
uint8_t *palette 768 bytes for 8-bit mode, NULL otherwise
|
|
int32_t clipX, clipY, clipW, clipH Current clip rectangle</pre>
|
|
<h3>RectT</h3>
|
|
<p>Rectangle in origin + extent form. Used throughout the compositor, window manager, and widget layout engine.</p>
|
|
<pre> Field Description
|
|
----- -----------
|
|
int32_t x, y Top-left corner
|
|
int32_t w, h Width and height</pre>
|
|
<h3>BlitOpsT</h3>
|
|
<p>Vtable for hot-path span operations. Resolved at init time based on pixel depth. On DOS, these dispatch to hand-written asm (rep stosl / rep movsd).</p>
|
|
<pre> Field Description
|
|
----- -----------
|
|
SpanFillFnT spanFill Fill a horizontal span with a solid color
|
|
SpanCopyFnT spanCopy Copy a horizontal span between buffers
|
|
int32_t bytesPerPixel Bytes per pixel for the active mode
|
|
int32_t pitch Bytes per scanline</pre>
|
|
<h3>BevelStyleT</h3>
|
|
<p>Bevel drawing parameters. Swapping highlight/shadow flips raised vs. sunken appearance.</p>
|
|
<pre> Field Description
|
|
----- -----------
|
|
uint32_t highlight Lighter color (top/left edges)
|
|
uint32_t shadow Darker color (bottom/right edges)
|
|
uint32_t face Interior fill color (0 = no fill)
|
|
int32_t width Border thickness in pixels (typically 2)</pre>
|
|
<h3>BitmapFontT</h3>
|
|
<p>Fixed-width 8-pixel-wide bitmap font descriptor. One size provided: 8x16 (standard VGA ROM font, CP437 encoding).</p>
|
|
<pre> Field Description
|
|
----- -----------
|
|
int32_t charWidth Fixed width per glyph (always 8)
|
|
int32_t charHeight Glyph height (16 in the bundled font)
|
|
int32_t firstChar ASCII code of first glyph (typically 0)
|
|
int32_t numChars Number of glyphs (typically 256)
|
|
const uint8_t *glyphData Packed 1bpp data, charHeight bytes per glyph</pre>
|
|
<h3>ColorSchemeT</h3>
|
|
<p>All UI colors pre-packed into display pixel format at init time. Theme support is achieved by swapping this struct.</p>
|
|
<pre> Field Description
|
|
----- -----------
|
|
uint32_t desktop Desktop background color
|
|
uint32_t windowFace Window body / chrome face
|
|
uint32_t windowHighlight Bevel highlight (top/left edge)
|
|
uint32_t windowShadow Bevel shadow (bottom/right edge)
|
|
uint32_t activeTitleBg, activeTitleFg Focused window title bar
|
|
uint32_t inactiveTitleBg, inactiveTitleFg Unfocused window title bar
|
|
uint32_t contentBg, contentFg Window content area default colors
|
|
uint32_t menuBg, menuFg Menu bar and popup background/text
|
|
uint32_t menuHighlightBg, menuHighlightFg Menu item highlight
|
|
uint32_t buttonFace Button face color
|
|
uint32_t scrollbarBg, scrollbarFg, scrollbarTrough Scrollbar element colors
|
|
uint32_t cursorFg, cursorBg Mouse cursor colors</pre>
|
|
<h3>ColorIdE</h3>
|
|
<p>Enum for addressing individual colors in ColorSchemeT. Order matches struct field order.</p>
|
|
<p>Values: ColorDesktopE, ColorWindowFaceE, ColorWindowHighlightE, ColorWindowShadowE, ColorActiveTitleBgE, ColorActiveTitleFgE, ColorInactiveTitleBgE, ColorInactiveTitleFgE, ColorContentBgE, ColorContentFgE, ColorMenuBgE, ColorMenuFgE, ColorMenuHighlightBgE, ColorMenuHighlightFgE, ColorButtonFaceE, ColorScrollbarBgE, ColorScrollbarFgE, ColorScrollbarTroughE, ColorCursorFgE, ColorCursorBgE, ColorCountE.</p>
|
|
<h3>DirtyListT</h3>
|
|
<p>List of dirty rectangles. The rect array is preallocated to a soft cap (128) by dirtyListInit; at the cap dirtyListAdd merges or collapses rects instead of growing, so the array is only (re)allocated when the initial allocation failed.</p>
|
|
<pre> Field Description
|
|
----- -----------
|
|
RectT *rects Dynamic array of dirty rectangles
|
|
int32_t count Current number of dirty rects
|
|
int32_t cap Allocated capacity</pre>
|
|
<h3>WindowT</h3>
|
|
<p>Central window object. Each window owns a persistent content backbuffer and receives events through callback function pointers.</p>
|
|
<pre> Field Description
|
|
----- -----------
|
|
int32_t id Unique window identifier
|
|
int32_t appId Shell app ID (0 = shell itself)
|
|
int32_t x, y, w, h Outer frame position and dimensions
|
|
int32_t contentX, contentY, contentW, contentH Content area inset from frame
|
|
char title[MAX_TITLE_LEN] Window title text (max 128 chars)
|
|
bool visible, focused, minimized, maximized, resizable, modal Window state flags
|
|
bool iconNeedsRefresh true when contentBuf changed (for minimized icon refresh)
|
|
bool gameInput Game input: see below
|
|
uint8_t paintNeeded PAINT_NONE / PAINT_PARTIAL / PAINT_FULL
|
|
int32_t maxW, maxH Maximum dimensions
|
|
int32_t preMaxX, preMaxY, preMaxW, preMaxH Saved geometry before maximize
|
|
uint8_t *contentBuf Per-window content backbuffer
|
|
int32_t contentPitch Content buffer bytes per row
|
|
uint8_t *iconData Icon pixel data, NULL if none
|
|
int32_t iconW, iconH, iconPitch Icon image dimensions and pitch
|
|
MenuBarT *menuBar Menu bar (NULL if no menus)
|
|
ScrollbarT *vScroll, *hScroll Scrollbars (NULL if not present)
|
|
struct WidgetT *widgetRoot Widget tree root (NULL if none)
|
|
MenuT *contextMenu Right-click context menu
|
|
AccelTableT *accelTable Keyboard accelerator table
|
|
void *userData Application-defined data pointer</pre>
|
|
<p>Callbacks:</p>
|
|
<pre> Callback Description
|
|
-------- -----------
|
|
onPaint(WindowT *win, RectT *dirtyArea) Content repaint requested
|
|
onKey(WindowT *win, int32_t key, int32_t mod) Key press
|
|
onKeyUp(WindowT *win, int32_t scancode, int32_t mod) Key release
|
|
onMouse(WindowT *win, int32_t x, int32_t y, int32_t buttons) Mouse event (content-relative)
|
|
onResize(WindowT *win, int32_t newW, int32_t newH) Window resized
|
|
onClose(WindowT *win) Close requested
|
|
onMenu(WindowT *win, int32_t menuId) Menu item or accelerator activated
|
|
onScroll(WindowT *win, ScrollbarOrientE orient, int32_t value) Scrollbar value changed
|
|
int32_t onCursorQuery(WindowT *win, int32_t x, int32_t y) Return CURSOR_* for hit position
|
|
onFocus(WindowT *win) Window gained focus
|
|
onBlur(WindowT *win) Window lost focus
|
|
onRawKey(WindowT *win, const RawKeyEventT *evt) Key pressed or released (gameInput windows)</pre>
|
|
<p>Game input: while a window with gameInput set has the keyboard focus, DVX keeps only Alt+Tab, Ctrl+Esc and Alt+F4 for itself. Every other keystroke, F1, F10, Alt+letter, Ctrl+F12, accelerators and hotkeys included, goes straight to onKey (except while a menu opened with the mouse, or the keyboard move/size mode, is taking keys). onRawKey then receives each key going down and up (RawKeyEventT: set-1 scancode, extended flag, repeat flag, modifiers from the held-key table), after the frame's onKey calls. When the focus moves away the window gets a release for every key still held (with no modifiers), and the held-key table starts over for the next window. onKeyUp keeps working as for any window. Mouse input is unchanged. The game library (game.h) adds gameKeyIsDown, frame pacing and timers.</p>
|
|
<h3>WindowStackT</h3>
|
|
<p>Z-ordered window stack (front-to-back: index count-1 is topmost). Owns system-wide drag/resize/scroll interaction state.</p>
|
|
<pre> Field Description
|
|
----- -----------
|
|
WindowT **windows Dynamic array of window pointers
|
|
int32_t count, cap Current count and allocated capacity
|
|
int32_t focusedIdx Stack index of focused window
|
|
int32_t dragWindow, dragOffX, dragOffY Active drag state
|
|
int32_t dragStartX, dragStartY Mouse position at drag begin (for deadzone)
|
|
bool dragActive True once mouse moved past deadzone
|
|
int32_t resizeWindow, resizeEdge Active resize state
|
|
int32_t scrollWindow, scrollOrient, scrollDragOff Active scroll drag state</pre>
|
|
<h3>MenuT / MenuItemT / MenuBarT</h3>
|
|
<p>Menu system types. Fixed-size label buffers (MAX_MENU_LABEL = 32). Cascading submenus supported via MenuItemT.subMenu pointer.</p>
|
|
<pre> Field Description
|
|
----- -----------
|
|
MenuItemT.label Item text (supports & accelerator markers)
|
|
MenuItemT.id Application-defined command ID
|
|
MenuItemT.type MenuItemNormalE, MenuItemCheckE, or MenuItemRadioE
|
|
MenuItemT.separator true = horizontal divider line
|
|
MenuItemT.enabled, checked Item state
|
|
MenuItemT.accelKey Lowercase accelerator character (0 = none)
|
|
MenuItemT.subMenu Child menu for cascading (NULL if leaf)
|
|
MenuT.label Menu bar label (e.g. "File")
|
|
MenuT.items Dynamic array of MenuItemT
|
|
MenuT.itemCount, itemCap Current count and allocated capacity
|
|
MenuT.barX, barW Computed position/width on the menu bar
|
|
MenuT.accelKey Lowercase accelerator character (0 = none)
|
|
MenuBarT.menus Dynamic array of MenuT
|
|
MenuBarT.menuCount, menuCap Current count and allocated capacity
|
|
MenuBarT.activeIdx Open popup index (-1 = none)
|
|
MenuBarT.positionsDirty True when barX/barW need recomputation</pre>
|
|
<h3>ScrollbarT</h3>
|
|
<p>Window-level scrollbar state. Managed by the WM layer, drawn after content.</p>
|
|
<pre> Field Description
|
|
----- -----------
|
|
ScrollbarOrientE orient ScrollbarVerticalE or ScrollbarHorizontalE
|
|
int32_t min, max Scroll range
|
|
int32_t value Current position
|
|
int32_t pageSize Visible portion (for proportional thumb sizing)
|
|
int32_t x, y, length Computed screen position and track length</pre>
|
|
<h3>AccelTableT / AccelEntryT</h3>
|
|
<p>Per-window keyboard accelerator table. Entries are matched against keystrokes in the event loop and fire onMenu(cmdId) on match. Only the normalized form is stored (see dvxAddAccel), so matching is two integer comparisons per entry.</p>
|
|
<pre> Field Description
|
|
----- -----------
|
|
AccelEntryT.cmdId Command ID passed to onMenu
|
|
AccelEntryT.normKey Normalized key (see dvxAddAccel)
|
|
AccelEntryT.normMods Normalized ACCEL_* modifiers (see dvxAddAccel)
|
|
AccelTableT.entries Dynamic array of AccelEntryT
|
|
AccelTableT.count, cap Current count and allocated capacity</pre>
|
|
<h3>VideoModeInfoT</h3>
|
|
<p>Describes an available video mode (enumerated at init).</p>
|
|
<pre> Field Description
|
|
----- -----------
|
|
int32_t w, h Resolution
|
|
int32_t bpp Bits per pixel</pre>
|
|
<h3>CursorT</h3>
|
|
<p>Software-rendered 16x16 cursor using AND/XOR mask encoding.</p>
|
|
<pre> Field Description
|
|
----- -----------
|
|
int32_t width, height Cursor dimensions (always 16x16)
|
|
int32_t hotX, hotY Hot spot coordinates
|
|
const uint16_t *andMask AND mask (0 = draw pixel, 1 = transparent)
|
|
const uint16_t *xorData XOR data (0 = black, 1 = white where AND = 0)</pre>
|
|
<h2>Bevel Convenience Macros</h2>
|
|
<pre> Macro Description
|
|
----- -----------
|
|
BEVEL_RAISED(cs, bw) Raised bevel style from ColorSchemeT ptr and border width
|
|
BEVEL_SUNKEN(cs, face, bw) Sunken bevel style with explicit face color
|
|
BEVEL_TROUGH(cs) 1px sunken trough (for scrollbar tracks)
|
|
BEVEL_SB_BUTTON(cs) 1px raised scrollbar button</pre>
|
|
<h2>Chrome Constants</h2>
|
|
<pre> Define Value Description
|
|
------ ----- -----------
|
|
CHROME_BORDER_WIDTH 4 Outer frame border width
|
|
CHROME_TITLE_HEIGHT 20 Title bar height
|
|
CHROME_TITLE_PAD 4 Title text padding
|
|
CHROME_INNER_BORDER 2 Inner chrome border
|
|
CHROME_MENU_HEIGHT 20 Menu bar height
|
|
CHROME_TOTAL_TOP 26 Total inset from top of frame to content
|
|
CHROME_TOTAL_SIDE 6 Total inset from side of frame to content
|
|
CHROME_TOTAL_BOTTOM 6 Total inset from bottom of frame to content</pre>
|
|
<h2>Hit Test Constants</h2>
|
|
<pre> Define Value Description
|
|
------ ----- -----------
|
|
HIT_CONTENT 0 Content area
|
|
HIT_TITLE 1 Title bar
|
|
HIT_CLOSE 2 Close gadget
|
|
HIT_RESIZE 3 Resize border
|
|
HIT_MENU 4 Menu bar
|
|
HIT_VSCROLL 5 Vertical scrollbar
|
|
HIT_HSCROLL 6 Horizontal scrollbar
|
|
HIT_MINIMIZE 7 Minimize gadget
|
|
HIT_MAXIMIZE 8 Maximize gadget
|
|
HIT_NONE -1 No window hit (desktop)</pre>
|
|
<h2>Mouse Button Flags</h2>
|
|
<pre> Define Value Description
|
|
------ ----- -----------
|
|
MOUSE_LEFT 1 Left mouse button
|
|
MOUSE_RIGHT 2 Right mouse button
|
|
MOUSE_MIDDLE 4 Middle mouse button</pre>
|
|
<h2>Accelerator Modifier Flags</h2>
|
|
<p>The keyboard modifier flags are the BIOS shift-state bits. KEY_MOD_* (dvxTypes.h) is the single definition; ACCEL_* are aliases of the same values for accelerator tables.</p>
|
|
<pre> Define Value Description
|
|
------ ----- -----------
|
|
KEY_MOD_SHIFT 0x03 Either Shift key (BIOS shift state bits)
|
|
KEY_MOD_CTRL 0x04 Ctrl key
|
|
KEY_MOD_ALT 0x08 Alt key
|
|
ACCEL_SHIFT 0x03 Alias of KEY_MOD_SHIFT
|
|
ACCEL_CTRL 0x04 Alias of KEY_MOD_CTRL
|
|
ACCEL_ALT 0x08 Alias of KEY_MOD_ALT</pre>
|
|
<h2>Extended Key Codes</h2>
|
|
<p>Non-ASCII keys are delivered as (scancode | KEY_EXT_FLAG). The platform layer reports Shift/Ctrl/Alt+F1..F12 as the plain F-key code with the modifier in the shift-state flags (e.g. Alt+F4 arrives as KEY_F4 with KEY_MOD_ALT).</p>
|
|
<pre> Define Description
|
|
------ -----------
|
|
KEY_EXT_FLAG 0x100, marks an extended (scancode) key
|
|
KEY_F1 .. KEY_F10 Function keys (KEY_SCAN_F1..F10 = 0x3B..0x44, | KEY_EXT_FLAG)
|
|
KEY_F11, KEY_F12 0x85 | KEY_EXT_FLAG, 0x86 | KEY_EXT_FLAG
|
|
KEY_INSERT Insert key
|
|
KEY_DELETE Delete key
|
|
KEY_HOME Home key
|
|
KEY_END End key
|
|
KEY_PGUP Page Up key
|
|
KEY_PGDN Page Down key
|
|
KEY_UP, KEY_DOWN Arrow keys
|
|
KEY_LEFT, KEY_RIGHT Arrow keys
|
|
KEY_CTRL_LEFT Ctrl+Left (0x73 | KEY_EXT_FLAG)
|
|
KEY_CTRL_RIGHT Ctrl+Right (0x74 | KEY_EXT_FLAG)
|
|
KEY_CTRL_END Ctrl+End (0x75 | KEY_EXT_FLAG)
|
|
KEY_CTRL_HOME Ctrl+Home (0x77 | KEY_EXT_FLAG)</pre>
|
|
<h3>Raw Scancodes</h3>
|
|
<p>KEY_SCAN_* are the raw BIOS scancodes (without KEY_EXT_FLAG): KEY_SCAN_ESC, KEY_SCAN_TAB, KEY_SCAN_SPACE, KEY_SCAN_F1 .. KEY_SCAN_F12, KEY_SCAN_HOME, KEY_SCAN_END, KEY_SCAN_PGUP, KEY_SCAN_PGDN, KEY_SCAN_INSERT, KEY_SCAN_DELETE, KEY_SCAN_UP, KEY_SCAN_DOWN, KEY_SCAN_LEFT, KEY_SCAN_RIGHT, KEY_SCAN_CTRL_LEFT, KEY_SCAN_CTRL_RIGHT, KEY_SCAN_CTRL_END, KEY_SCAN_CTRL_HOME, KEY_SCAN_ALT_TAB.</p>
|
|
<h3>Raw Key Codes</h3>
|
|
<p>A raw key code names one physical key: its set-1 make code (1-127), plus KEY_RAW_EXT (0x80) for keys sent behind an 0xE0 prefix (grey arrows and navigation keys, right Ctrl and Alt, keypad Enter). KEY_RAW_CODE(scancode, extended) builds one. The code is the same whatever modifier is held; letters, digits and punctuation use their KEY_SCAN_* / BIOS scancode. Used by the held-key table (platformKeyIsDown, gameKeyIsDown) and RawKeyEventT.</p>
|
|
<pre> Define Value Key
|
|
------ ----- ---
|
|
KEY_RAW_LSHIFT, KEY_RAW_RSHIFT 0x2A, 0x36 Left / right Shift
|
|
KEY_RAW_LCTRL, KEY_RAW_RCTRL 0x1D, 0x9D Left / right Ctrl
|
|
KEY_RAW_LALT, KEY_RAW_RALT 0x38, 0xB8 Left / right Alt
|
|
KEY_RAW_F11, KEY_RAW_F12 0x57, 0x58 F11, F12
|
|
KEY_RAW_UP, KEY_RAW_DOWN 0xC8, 0xD0 Grey arrows (the keypad's are 0x48, 0x50)
|
|
KEY_RAW_LEFT, KEY_RAW_RIGHT 0xCB, 0xCD
|
|
KEY_RAW_KP_ENTER 0x9C Keypad Enter
|
|
KEY_RAW_PAUSE 0xC5 Pause (a press and a release at once)
|
|
KEY_RAW_COUNT 256 Size of the held-key snapshot</pre>
|
|
<p>RawKeyEventT: uint8_t scancode (the make code), bool extended, bool down, bool repeat (a typematic repeat of a key already down), int32_t modifiers (KEY_MOD_* from the held-key table after the event, with KEY_MOD_LSHIFT 0x02 and KEY_MOD_RSHIFT 0x01 apart).</p>
|
|
<h3>ASCII Key Codes</h3>
|
|
<pre> Define Value Description
|
|
------ ----- -----------
|
|
KEY_ESCAPE 0x1B Escape
|
|
KEY_ENTER 0x0D Enter / Return
|
|
KEY_TAB 0x09 Tab
|
|
KEY_SPACE 0x20 Space
|
|
KEY_BACKSPACE 0x08 Backspace
|
|
KEY_ASCII_DEL 0x7F ASCII DEL (not KEY_DELETE)
|
|
KEY_ASCII_PRINT_FIRST 0x20 First printable character
|
|
KEY_ASCII_PRINT_LAST 0x7E Last printable character
|
|
KEY_CTRL_A 0x01 Ctrl+A (BIOS reports Ctrl+letter as 0x01..0x1A)
|
|
KEY_CTRL_C 0x03 Ctrl+C
|
|
KEY_CTRL_V 0x16 Ctrl+V
|
|
KEY_CTRL_X 0x18 Ctrl+X
|
|
KEY_CTRL_Z 0x1A Ctrl+Z</pre>
|
|
<h2>Resize Edge Flags</h2>
|
|
<pre> Define Value Description
|
|
------ ----- -----------
|
|
RESIZE_NONE 0 No resize edge
|
|
RESIZE_LEFT 1 Left edge
|
|
RESIZE_RIGHT 2 Right edge
|
|
RESIZE_TOP 4 Top edge
|
|
RESIZE_BOTTOM 8 Bottom edge (combinable via OR for corners)</pre>
|
|
<h2>Utility Macros</h2>
|
|
<pre> Macro Description
|
|
----- -----------
|
|
DVX_MIN(a, b) Return the smaller of two values
|
|
DVX_MAX(a, b) Return the larger of two values</pre>
|
|
</div>
|
|
<div class="topic" id="api.cursor">
|
|
<h1>dvxCur.h -- Cursor Definitions</h1>
|
|
<p>Embedded 16x16 mouse cursor bitmaps compiled as static const data. No external cursor files. Uses the standard AND/XOR mask encoding from the IBM VGA hardware cursor spec. The AND mask selects transparency (1 = transparent, 0 = opaque) and the XOR data selects black vs. white for opaque pixels.</p>
|
|
<h2>Cursor Shape IDs</h2>
|
|
<pre> Define Value Description
|
|
------ ----- -----------
|
|
CURSOR_ARROW 0 Standard arrow (hot spot at tip)
|
|
CURSOR_RESIZE_H 1 Horizontal resize (left/right arrows)
|
|
CURSOR_RESIZE_V 2 Vertical resize (up/down arrows)
|
|
CURSOR_RESIZE_DIAG_NWSE 3 NW-SE diagonal resize
|
|
CURSOR_RESIZE_DIAG_NESW 4 NE-SW diagonal resize
|
|
CURSOR_BUSY 5 Hourglass (wait)
|
|
CURSOR_CROSSHAIR 6 Crosshair for placement
|
|
CURSOR_COUNT 7 Total number of cursor shapes</pre>
|
|
<h2>Data</h2>
|
|
<h3>dvxCursors[CURSOR_COUNT]</h3>
|
|
<p>Static const array of CursorT structs, indexed by CURSOR_xxx constants. Each entry includes the AND mask, XOR data (both as 16-element uint16_t arrays), dimensions (16x16), and hot spot coordinates.</p>
|
|
<h3>dvxCursor</h3>
|
|
<p>Legacy alias for backward compatibility with code that predates multi-cursor support. Equivalent to dvxCursors[CURSOR_ARROW].</p>
|
|
</div>
|
|
<div class="topic" id="api.font">
|
|
<h1>dvxFont.h -- Embedded Bitmap Font Data</h1>
|
|
<p>Raw glyph bitmaps for the standard 8x16 VGA ROM font covering the full IBM Code Page 437 character set (256 glyphs). Compiled in as a static const array, avoiding a real-mode INT 10h call at startup.</p>
|
|
<p>Glyph format: 1 bit per pixel, 8 pixels wide, MSB = leftmost pixel. Each glyph occupies 16 consecutive bytes (one byte per scanline). The 8-pixel width means per-scanline rendering never needs bit shifting across byte boundaries.</p>
|
|
<p>CP437 covers:</p>
|
|
<ul>
|
|
<li>0-31 -- Smiley, card suits, arrows, notes (classic extended ASCII glyphs)</li>
|
|
<li>32-126 -- Printable ASCII</li>
|
|
<li>127 -- House glyph</li>
|
|
<li>128-175 -- Accented Latin letters, currency, fractions</li>
|
|
<li>176-223 -- Box-drawing characters (essential for chrome gadgets)</li>
|
|
<li>224-254 -- Greek letters, math symbols, super/subscripts</li>
|
|
<li>255 -- Non-breaking space</li>
|
|
</ul>
|
|
<h2>Data</h2>
|
|
<h3>font8x16</h3>
|
|
<p>const uint8_t array of FONT_NUM_CHARS * FONT_CHAR_HEIGHT = 4096 bytes containing the packed 1bpp glyph bitmaps, in ASCII code order. It has external linkage with a single definition in the core library (the translation unit that defines DVX_FONT_IMPL before including dvxFont.h); every other includer sees only the extern declaration.</p>
|
|
<h3>dvxFont8x16</h3>
|
|
<p>const BitmapFontT struct describing the 8x16 font, ready to pass to drawText/drawTextN/drawChar. Use this as the default font for all text rendering. Declared extern like font8x16.</p>
|
|
<h2>Constants</h2>
|
|
<pre> Define Value Description
|
|
------ ----- -----------
|
|
FONT_NUM_CHARS 256 Glyph count (full 8-bit character range)
|
|
FONT_CHAR_WIDTH 8 Fixed glyph width in pixels (defined in dvxTypes.h)
|
|
FONT_CHAR_HEIGHT 16 Glyph height in pixels (defined in dvxTypes.h)</pre>
|
|
</div>
|
|
<div class="topic" id="api.palette">
|
|
<h1>dvxPal.h -- 256-Color Palette for 8-bit Mode</h1>
|
|
<p>Defines the 256-color VGA/VESA palette used in 8-bit video modes. Layout:</p>
|
|
<pre> Range Purpose
|
|
----- -------
|
|
0-215 6x6x6 color cube -- uniformly distributed RGB (6 levels per channel: 0, 51, 102, 153, 204, 255). Index = r*36 + g*6 + b.
|
|
216-231 16-step grey ramp for smooth gradients
|
|
232-239 Dedicated UI chrome colors (highlight, shadow, title bar, desktop)
|
|
240-255 Reserved (black)</pre>
|
|
<p>The cube layout enables O(1) color lookup: snapping an RGB triplet to the nearest cube vertex is integer division by 51. The grey ramp and chrome slots hold colors that need exact values not available in the cube.</p>
|
|
<h2>Layout Constants</h2>
|
|
<pre> Define Value Description
|
|
------ ----- -----------
|
|
PAL_ENTRIES 256 Palette size
|
|
PAL_CHANNELS 3 Bytes per entry (R, G, B)
|
|
PAL_CUBE_LEVELS 6 Levels per channel in the color cube
|
|
PAL_CUBE_STEP 51 Distance between cube levels (255 / 5)
|
|
PAL_CUBE_ROUND 25 Rounding bias when snapping to a cube level
|
|
PAL_CUBE_COUNT 216 Number of cube entries (indices 0-215)
|
|
PAL_GREY_FIRST 216 First grey-ramp index
|
|
PAL_GREY_COUNT 16 Grey-ramp entries
|
|
PAL_GREY_STEP 17 Distance between grey levels (255 / 15)
|
|
PAL_CHROME_FIRST 232 First chrome color index
|
|
PAL_RESERVED_FIRST 240 First reserved (black) index</pre>
|
|
<h2>Chrome Color Indices</h2>
|
|
<pre> Define Value Description
|
|
------ ----- -----------
|
|
PAL_CHROME_HIGHLIGHT 232 White (bevel highlight)
|
|
PAL_CHROME_SHADOW 233 Dark grey (bevel shadow)
|
|
PAL_CHROME_ACTIVE_BG 234 Navy (focused title bar)
|
|
PAL_CHROME_INACTIVE_BG 235 Grey (unfocused title bar)
|
|
PAL_CHROME_DESKTOP 236 Steel blue (desktop)
|
|
PAL_CHROME_SELECTION 237 Navy (selection highlight)
|
|
PAL_CHROME_TEXT 238 Black (text)
|
|
PAL_CHROME_WHITE 239 Bright white</pre>
|
|
<h2>Inline Functions</h2>
|
|
<h3>dvxPalSet</h3>
|
|
<pre><code>static inline void dvxPalSet(uint8_t *pal, int32_t idx, uint8_t r, uint8_t g, uint8_t b);</code></pre>
|
|
<p>Store one RGB entry at index idx of a PAL_ENTRIES * PAL_CHANNELS palette buffer.</p>
|
|
<h3>dvxGeneratePalette</h3>
|
|
<pre><code>static inline void dvxGeneratePalette(uint8_t *pal);</code></pre>
|
|
<p>Populate a 768-byte (256 * 3) RGB buffer with the default DVX 8-bit palette. Called once at init in 8-bit mode to program the DAC.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
pal Output RGB buffer (768 bytes)</pre>
|
|
<h3>dvxNearestPalEntry</h3>
|
|
<pre><code>static inline uint8_t dvxNearestPalEntry(const uint8_t *pal, uint8_t r, uint8_t g, uint8_t b);</code></pre>
|
|
<p>Find the nearest palette entry for an RGB color using minimum Euclidean distance. Two-phase: snap to color cube vertex (O(1)), then scan grey ramp and chrome entries (216-239) for a closer match. Called by packColor() in 8-bit mode.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
pal Active 768-byte RGB palette
|
|
r, g, b Color components (0-255)</pre>
|
|
<p>Returns: Palette index (0-239) of the nearest match.</p>
|
|
</div>
|
|
<div class="topic" id="api.video">
|
|
<h1>dvxVideo.h -- Layer 1: VESA VBE Video Backend</h1>
|
|
<p>The lowest layer. Responsible for VESA VBE mode negotiation, LFB mapping via DPMI, system RAM backbuffer allocation, pixel format discovery, and color packing. LFB-only design: bank switching is deliberately unsupported.</p>
|
|
<h2>videoInit</h2>
|
|
<pre><code>int32_t videoInit(DisplayT *d, int32_t requestedW, int32_t requestedH, int32_t preferredBpp);</code></pre>
|
|
<p>Probe VBE for a mode matching the requested resolution and depth, enable it, map the LFB into DPMI linear address space, and allocate a system RAM backbuffer. preferredBpp is a hint; the closest available depth is used if an exact match is not found.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
d Display context to initialize
|
|
requestedW/H Desired screen resolution
|
|
preferredBpp Preferred bits per pixel (8, 15, 16, or 32)</pre>
|
|
<p>Returns: 0 on success, negative on failure.</p>
|
|
<h2>videoShutdown</h2>
|
|
<pre><code>void videoShutdown(DisplayT *d);</code></pre>
|
|
<p>Restore VGA text mode (mode 3), unmap the LFB, and free the backbuffer. Safe to call even if videoInit() failed.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
d Display context to shut down</pre>
|
|
<h2>packColor</h2>
|
|
<pre><code>uint32_t packColor(const DisplayT *d, uint8_t r, uint8_t g, uint8_t b);</code></pre>
|
|
<p>Pack an RGB triplet into the display's native pixel format. For direct-color modes (15/16/32 bpp), returns a packed pixel value using shift/mask fields. For 8-bit mode, returns the nearest palette index via Euclidean distance in RGB space.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
d Display context (provides pixel format)
|
|
r, g, b Color components (0-255)</pre>
|
|
<p>Returns: Native pixel value suitable for direct framebuffer write.</p>
|
|
<h2>unpackColor</h2>
|
|
<pre><code>void unpackColor(const DisplayT *d, uint32_t color, uint8_t *r, uint8_t *g, uint8_t *b);</code></pre>
|
|
<p>Reverse of packColor -- decode a native pixel value to 8-bit RGB components. For direct-color modes, reverses the shift/mask arithmetic. For 8-bit mode, looks up the palette entry at the given index.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
d Display context
|
|
color Packed native pixel value
|
|
r, g, b Output: color components (0-255)</pre>
|
|
<h2>setClipRect</h2>
|
|
<pre><code>void setClipRect(DisplayT *d, int32_t x, int32_t y, int32_t w, int32_t h);</code></pre>
|
|
<p>Set the clip rectangle on the display. All subsequent draw operations clip to this rectangle. The caller must save and restore the clip rect around scoped operations.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
d Display context
|
|
x, y, w, h Clip rectangle in screen coordinates</pre>
|
|
<h2>resetClipRect</h2>
|
|
<pre><code>void resetClipRect(DisplayT *d);</code></pre>
|
|
<p>Reset the clip rectangle to the full display dimensions.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
d Display context</pre>
|
|
</div>
|
|
<div class="topic" id="api.draw">
|
|
<h1>dvxDraw.h -- Layer 2: Drawing Primitives</h1>
|
|
<p>All 2D drawing operations: rectangle fills, bitmap blits, text rendering, bevels, lines, and cursor rendering. Every function draws into the display's backbuffer and clips to the current clip rectangle. This layer is stateless beyond the clip rect on DisplayT.</p>
|
|
<h2>drawInit</h2>
|
|
<pre><code>void drawInit(BlitOpsT *ops, const DisplayT *d);</code></pre>
|
|
<p>Populate a BlitOpsT with the correct span functions for the display's pixel depth (1, 2 or 4 bytes per pixel). Must be called once after videoInit(). For any other pixel size the span functions are left NULL so misuse fails loudly instead of drawing with the wrong depth.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ops BlitOpsT to populate
|
|
d Initialized display context</pre>
|
|
<h2>rectFill</h2>
|
|
<pre><code>void rectFill(DisplayT *d, const BlitOpsT *ops, int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color);</code></pre>
|
|
<p>Fill a rectangle with a solid color. Clips to the display clip rect. Workhorse for backgrounds, window fills, and clear operations.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
d Display context
|
|
ops Blit operations vtable
|
|
x, y, w, h Rectangle to fill
|
|
color Packed pixel color</pre>
|
|
<h2>rectCopy</h2>
|
|
<pre><code>void rectCopy(DisplayT *d, const BlitOpsT *ops, int32_t dstX, int32_t dstY, const uint8_t *srcBuf, int32_t srcPitch, int32_t srcX, int32_t srcY, int32_t w, int32_t h);</code></pre>
|
|
<p>Copy a rectangle from an arbitrary source buffer into the backbuffer. Used to blit per-window content buffers during compositing. Rows are copied with ops->spanCopy (rep movsd on DOS); when both pitches equal the row size the whole block is one span. Source and destination must not overlap.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
d Display context
|
|
ops Blit operations vtable
|
|
dstX, dstY Destination position in backbuffer
|
|
srcBuf Source pixel buffer
|
|
srcPitch Source buffer bytes per row
|
|
srcX, srcY Origin within source buffer
|
|
w, h Rectangle dimensions to copy</pre>
|
|
<h2>rectCopyGrayscale</h2>
|
|
<pre><code>void rectCopyGrayscale(DisplayT *d, const BlitOpsT *ops, int32_t dstX, int32_t dstY, const uint8_t *srcBuf, int32_t srcPitch, int32_t srcX, int32_t srcY, int32_t w, int32_t h);</code></pre>
|
|
<p>Copy a rectangle with grayscale conversion. Each pixel's RGB is converted to luminance (0.299R + 0.587G + 0.114B) for a disabled/grayed appearance. In 8-bit mode the gray is mapped back to the nearest palette entry (cached per index); on an 8-bit display without a palette the indices are copied unchanged.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
d Display context
|
|
ops Blit operations vtable
|
|
dstX, dstY Destination position
|
|
srcBuf, srcPitch Source buffer and pitch
|
|
srcX, srcY Source origin
|
|
w, h Rectangle dimensions</pre>
|
|
<h2>rectCopyTransparent</h2>
|
|
<pre><code>void rectCopyTransparent(DisplayT *d, const BlitOpsT *ops, int32_t dstX, int32_t dstY, const uint8_t *srcBuf, int32_t srcPitch, int32_t srcX, int32_t srcY, int32_t w, int32_t h, uint32_t keyColor);</code></pre>
|
|
<p>Copy with color-key transparency. Pixels matching keyColor are skipped, letting the destination show through. Used to draw image buttons and icons with transparent backgrounds.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
d Display context
|
|
ops Blit operations vtable
|
|
dstX, dstY Destination position
|
|
srcBuf, srcPitch Source buffer and pitch
|
|
srcX, srcY Source origin
|
|
w, h Rectangle dimensions
|
|
keyColor Packed color treated as transparent</pre>
|
|
<h2>drawBevel</h2>
|
|
<pre><code>void drawBevel(DisplayT *d, const BlitOpsT *ops, int32_t x, int32_t y, int32_t w, int32_t h, const BevelStyleT *style);</code></pre>
|
|
<p>Draw a beveled frame. Top/left edges in highlight color, bottom/right in shadow. Interior filled with the face color unless face is BEVEL_NO_FILL (0 is packed black and fills normally).</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
d Display context
|
|
ops Blit operations vtable
|
|
x, y, w, h Outer bevel rectangle
|
|
style Bevel colors and width</pre>
|
|
<h2>drawChar</h2>
|
|
<pre><code>int32_t drawChar(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, int32_t x, int32_t y, char ch, uint32_t fg, uint32_t bg, bool opaque);</code></pre>
|
|
<p>Draw a single character glyph. When opaque is true, the background fills the entire cell; when false, only foreground pixels are drawn (transparent background).</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
d Display context
|
|
ops Blit operations vtable
|
|
font Bitmap font
|
|
x, y Character position
|
|
ch Character to draw
|
|
fg, bg Foreground and background packed colors
|
|
opaque true = fill background, false = transparent</pre>
|
|
<p>Returns: Advance width (always charWidth).</p>
|
|
<h2>drawText</h2>
|
|
<pre><code>void drawText(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, int32_t x, int32_t y, const char *text, uint32_t fg, uint32_t bg, bool opaque);</code></pre>
|
|
<p>Draw a null-terminated string. Equivalent to drawTextN over the whole string (one background span per scanline, character-range clipping).</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
d Display context
|
|
ops Blit operations vtable
|
|
font Bitmap font
|
|
x, y Start position
|
|
text Null-terminated string
|
|
fg, bg Foreground and background packed colors
|
|
opaque true = fill background, false = transparent</pre>
|
|
<h2>drawTextN</h2>
|
|
<pre><code>void drawTextN(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, int32_t x, int32_t y, const char *text, int32_t count, uint32_t fg, uint32_t bg, bool opaque);</code></pre>
|
|
<p>Optimized batch text rendering for a known character count. Computes clip bounds once, fills background in a single rectFill, then overlays glyph foreground pixels. Significantly faster than per-character drawChar for long runs (terminal rows, list items).</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
d Display context
|
|
ops Blit operations vtable
|
|
font Bitmap font
|
|
x, y Start position
|
|
text Character buffer (not required to be null-terminated)
|
|
count Number of characters to render
|
|
fg, bg Foreground and background packed colors
|
|
opaque true = fill background, false = transparent</pre>
|
|
<h2>textWidth</h2>
|
|
<pre><code>int32_t textWidth(const BitmapFontT *font, const char *text);</code></pre>
|
|
<p>Return the pixel width of a null-terminated string (strlen(text) * charWidth).</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
font Bitmap font
|
|
text Null-terminated string</pre>
|
|
<p>Returns: Width in pixels.</p>
|
|
<h2>accelParse</h2>
|
|
<pre><code>char accelParse(const char *text);</code></pre>
|
|
<p>Scan text for an & prefix and return the following character as a lowercase accelerator key. "&File" returns 'f', "E&xit" returns 'x'.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
text Text with optional & accelerator marker</pre>
|
|
<p>Returns: Lowercase accelerator character, or 0 if none.</p>
|
|
<h2>drawTextAccel</h2>
|
|
<pre><code>void drawTextAccel(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, int32_t x, int32_t y, const char *text, uint32_t fg, uint32_t bg, bool opaque);</code></pre>
|
|
<p>Draw text with & accelerator markers. The character after & is drawn underlined to indicate the keyboard shortcut. && produces a literal &. Used for menu items and button labels.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
d Display context
|
|
ops Blit operations vtable
|
|
font Bitmap font
|
|
x, y Start position
|
|
text Text with & markers
|
|
fg, bg Foreground and background packed colors
|
|
opaque true = fill background, false = transparent</pre>
|
|
<h2>textWidthAccel</h2>
|
|
<pre><code>int32_t textWidthAccel(const BitmapFontT *font, const char *text);</code></pre>
|
|
<p>Measure text width excluding & markers (so "&File" measures as 4 chars).</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
font Bitmap font
|
|
text Text with optional & markers</pre>
|
|
<p>Returns: Width in pixels.</p>
|
|
<h2>drawMaskedBitmap</h2>
|
|
<pre><code>void drawMaskedBitmap(DisplayT *d, const BlitOpsT *ops, int32_t x, int32_t y, int32_t w, int32_t h, const uint16_t *andMask, const uint16_t *xorData, uint32_t fgColor, uint32_t bgColor);</code></pre>
|
|
<p>Draw a 1-bit AND/XOR masked bitmap. Used for software-rendered mouse cursors.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
d Display context
|
|
ops Blit operations vtable
|
|
x, y Screen position
|
|
w, h Bitmap dimensions
|
|
andMask AND transparency mask (one uint16_t per row)
|
|
xorData XOR color data
|
|
fgColor, bgColor Cursor foreground and background packed colors</pre>
|
|
<h2>drawTermRow</h2>
|
|
<pre><code>void drawTermRow(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, int32_t x, int32_t y, int32_t cols, const uint8_t *lineData, const uint32_t *palette, bool blinkVisible, int32_t cursorCol);</code></pre>
|
|
<p>Render an entire row of terminal character cells (ch/attr byte pairs) in a single pass. Colors looked up from a 16-color palette. The attribute byte uses the CGA layout described by the TERM_ATTR_* constants.</p>
|
|
<pre> Define Value Description
|
|
------ ----- -----------
|
|
TERM_ATTR_FG_MASK 0x0F Foreground palette index (low nibble)
|
|
TERM_ATTR_BG_SHIFT 4 Shift of the background index
|
|
TERM_ATTR_BG_MASK 0x07 Background palette index (bits 4-6, after shifting)
|
|
TERM_ATTR_BLINK 0x80 Blink attribute</pre>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
d Display context
|
|
ops Blit operations vtable
|
|
font Bitmap font
|
|
x, y Row start position
|
|
cols Number of columns
|
|
lineData Packed ch/attr byte pairs (2 bytes per cell)
|
|
palette 16-entry packed color palette
|
|
blinkVisible false = hide blinking characters
|
|
cursorCol Column for inverted text cursor (-1 = none)</pre>
|
|
<h2>drawArrowGlyph</h2>
|
|
<pre><code>void drawArrowGlyph(DisplayT *d, const BlitOpsT *ops, int32_t cx, int32_t cy, int32_t rows, ScrollArrowDirE dir, uint32_t color);</code></pre>
|
|
<p>Draw a solid triangle of rows rows (base 2 * rows - 1 pixels) pointing in direction dir (ScrollArrowUpE/DownE/LeftE/RightE), centered on (cx, cy). The shared direction glyph for scrollbar arrows, spinners, tab scroll buttons, navigation bars and sort indicators.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
d Display context
|
|
ops Blit operations vtable
|
|
cx, cy Center of the glyph
|
|
rows Number of rows (tip to base)
|
|
dir Direction the tip points
|
|
color Fill color</pre>
|
|
<h2>drawScrollbarArrow</h2>
|
|
<pre><code>void drawScrollbarArrow(DisplayT *d, const BlitOpsT *ops, const ColorSchemeT *colors, int32_t x, int32_t y, int32_t size, ScrollArrowDirE dir);</code></pre>
|
|
<p>Draw the arrow glyph of a scrollbar button whose top-left is (x, y) and side is size, using drawArrowGlyph centered in the button with colors->scrollbarFg.</p>
|
|
<h2>drawFocusRect</h2>
|
|
<pre><code>void drawFocusRect(DisplayT *d, const BlitOpsT *ops, int32_t x, int32_t y, int32_t w, int32_t h, uint32_t color);</code></pre>
|
|
<p>Draw a 1px dotted rectangle (alternating pixels). Used for keyboard focus indicators, matching the Windows 3.x focus rectangle convention.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
d Display context
|
|
ops Blit operations vtable
|
|
x, y, w, h Focus rectangle bounds
|
|
color Dot color (packed)</pre>
|
|
<h2>drawHLine</h2>
|
|
<pre><code>void drawHLine(DisplayT *d, const BlitOpsT *ops, int32_t x, int32_t y, int32_t w, uint32_t color);</code></pre>
|
|
<p>Draw a horizontal line (1px tall).</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
d Display context
|
|
ops Blit operations vtable
|
|
x, y Start position
|
|
w Width in pixels
|
|
color Packed pixel color</pre>
|
|
<h2>drawVLine</h2>
|
|
<pre><code>void drawVLine(DisplayT *d, const BlitOpsT *ops, int32_t x, int32_t y, int32_t h, uint32_t color);</code></pre>
|
|
<p>Draw a vertical line (1px wide).</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
d Display context
|
|
ops Blit operations vtable
|
|
x, y Start position
|
|
h Height in pixels
|
|
color Packed pixel color</pre>
|
|
</div>
|
|
<div class="topic" id="api.comp">
|
|
<h1>dvxComp.h -- Layer 3: Dirty Rectangle Compositor</h1>
|
|
<p>Tracks changed screen regions and ensures only dirty regions are redrawn and flushed to video memory. The compositing pipeline: mark dirty, merge overlapping rects, redraw desktop + windows (back-to-front, painter's algorithm), flush to LFB.</p>
|
|
<h2>dirtyListInit</h2>
|
|
<pre><code>void dirtyListInit(DirtyListT *dl);</code></pre>
|
|
<p>One-time initialization: empty the list and preallocate its rect array to the soft cap (128) so adding rects never allocates. Calling it again keeps the existing array.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
dl Dirty list to initialize</pre>
|
|
<h2>dirtyListAdd</h2>
|
|
<pre><code>void dirtyListAdd(DirtyListT *dl, int32_t x, int32_t y, int32_t w, int32_t h);</code></pre>
|
|
<p>Enqueue a dirty rectangle (empty rects are ignored). At the soft cap the list is merged first; if it is still full, every rect (including the new one) is collapsed into one bounding rect. A rect is never dropped while the list has any storage: if the array cannot be allocated it is unioned into the first entry.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
dl Dirty list
|
|
x, y, w, h Dirty rectangle in screen coordinates</pre>
|
|
<h2>dirtyListMerge</h2>
|
|
<pre><code>void dirtyListMerge(DirtyListT *dl);</code></pre>
|
|
<p>Consolidate the dirty list by merging overlapping and adjacent rects. Uses iterative pairwise merge: if combining two rects does not increase total area beyond a threshold, they are merged. Reduces compositor passes and LFB flush operations.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
dl Dirty list to merge</pre>
|
|
<h2>dirtyListClear</h2>
|
|
<pre><code>void dirtyListClear(DirtyListT *dl);</code></pre>
|
|
<p>Reset the dirty list to empty (sets count to 0).</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
dl Dirty list to clear</pre>
|
|
<h2>flushRect</h2>
|
|
<pre><code>void flushRect(DisplayT *d, const RectT *r);</code></pre>
|
|
<p>Copy a rectangle from the system RAM backbuffer to the LFB (video memory). This is the only place the real framebuffer is written. Uses platform-specific fast copy (rep movsd on DOS) for each scanline.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
d Display context
|
|
r Rectangle to flush</pre>
|
|
<h2>rectIntersect</h2>
|
|
<pre><code>bool rectIntersect(const RectT *a, const RectT *b, RectT *result);</code></pre>
|
|
<p>Compute the intersection of two rectangles.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
a, b Input rectangles
|
|
result Output: intersection rectangle (valid only when return is true)</pre>
|
|
<p>Returns: true if the rectangles overlap, false if disjoint.</p>
|
|
</div>
|
|
<div class="topic" id="api.wm">
|
|
<h1>dvxWm.h -- Layer 4: Window Manager</h1>
|
|
<p>Manages the window lifecycle, Z-order stack, chrome drawing, hit testing, and interactive operations (drag, resize, scroll). The WM owns window geometry and chrome; content is owned by the application via callbacks or the widget system.</p>
|
|
<h2>Initialization</h2>
|
|
<h3>wmInit</h3>
|
|
<pre><code>void wmInit(WindowStackT *stack);</code></pre>
|
|
<p>Zero the window stack. Must be called before any other WM function.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
stack Window stack to initialize</pre>
|
|
<h2>Window Lifecycle</h2>
|
|
<h3>wmCreateWindow</h3>
|
|
<pre><code>WindowT *wmCreateWindow(WindowStackT *stack, DisplayT *d, const char *title, int32_t x, int32_t y, int32_t w, int32_t h, bool resizable);</code></pre>
|
|
<p>Allocate a new window, initialize its geometry and content buffer, and push it to the top of the Z-order stack. Returns with all callbacks NULL; the caller should set onPaint/onKey/etc. before the next event loop iteration.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
stack Window stack
|
|
d Display context
|
|
title Window title text
|
|
x, y Initial position
|
|
w, h Initial outer frame dimensions
|
|
resizable true = allow user resize</pre>
|
|
<p>Returns: Pointer to new WindowT, or NULL on failure.</p>
|
|
<h3>wmDestroyWindow</h3>
|
|
<pre><code>void wmDestroyWindow(WindowStackT *stack, WindowT *win);</code></pre>
|
|
<p>Free the window's content buffer and all attached resources (menu bar, scrollbars, widget tree), remove it from the stack, and dirty the vacated region.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
stack Window stack
|
|
win Window to destroy</pre>
|
|
<h2>Z-Order and Focus</h2>
|
|
<h3>wmRaiseWindow</h3>
|
|
<pre><code>void wmRaiseWindow(WindowStackT *stack, DirtyListT *dl, int32_t idx);</code></pre>
|
|
<p>Move window at stack index idx to the top of the Z-order. Dirties both old and new top positions so overlapping windows get repainted.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
stack Window stack
|
|
dl Dirty list for repaint marking
|
|
idx Stack index of window to raise</pre>
|
|
<h3>wmSetFocus</h3>
|
|
<pre><code>void wmSetFocus(WindowStackT *stack, DirtyListT *dl, int32_t idx);</code></pre>
|
|
<p>Transfer keyboard focus to the window at stack index idx. Unfocuses the previously focused window and dirties both title bars.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
stack Window stack
|
|
dl Dirty list
|
|
idx Stack index of window to focus</pre>
|
|
<h2>Geometry</h2>
|
|
<h3>wmUpdateContentRect</h3>
|
|
<pre><code>void wmUpdateContentRect(WindowT *win);</code></pre>
|
|
<p>Recompute contentX/Y/W/H from the window's outer frame dimensions, accounting for chrome borders, title bar, menu bar, and scrollbars. Must be called after any change to frame size or chrome configuration.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
win Window to update</pre>
|
|
<h3>wmReallocContentBuf</h3>
|
|
<pre><code>int32_t wmReallocContentBuf(WindowT *win, const DisplayT *d);</code></pre>
|
|
<p>Reallocate the per-window content backbuffer to match current contentW/H. Old buffer contents are lost; caller should trigger a full repaint via onPaint afterward.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
win Window to reallocate
|
|
d Display context (for bytes-per-pixel)</pre>
|
|
<p>Returns: 0 on success, -1 on allocation failure.</p>
|
|
<h3>wmMinWindowSize</h3>
|
|
<pre><code>void wmMinWindowSize(const WindowT *win, int32_t *minW, int32_t *minH);</code></pre>
|
|
<p>Get the minimum window size. Accounts for chrome, gadgets, and menu bar.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
win Window
|
|
minW, minH Output: minimum width and height</pre>
|
|
<h3>wmClampWindowPos</h3>
|
|
<pre><code>void wmClampWindowPos(const WindowT *win, int32_t *x, int32_t *y, int32_t screenW, int32_t screenH);</code></pre>
|
|
<p>Clamp a candidate window position so the window's title bar stays reachable on a screenW x screenH screen. Shared by mouse dragging (wmDragMove) and keyboard move.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
win Window being moved
|
|
x, y In/out: candidate position, clamped in place
|
|
screenW, screenH Screen size</pre>
|
|
<h2>Menu Bar</h2>
|
|
<h3>wmAddMenuBar</h3>
|
|
<pre><code>MenuBarT *wmAddMenuBar(WindowT *win);</code></pre>
|
|
<p>Allocate and attach a menu bar to a window. Adjusts content area to make room (CHROME_MENU_HEIGHT pixels). One menu bar per window.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
win Window to add menu bar to</pre>
|
|
<p>Returns: Pointer to the new MenuBarT.</p>
|
|
<h3>wmDestroyMenuBar</h3>
|
|
<pre><code>void wmDestroyMenuBar(WindowT *win, const DisplayT *d);</code></pre>
|
|
<p>Free the menu bar and reclaim the content area. The content buffer is reallocated to fit the grown content area.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
win Window to remove menu bar from
|
|
d Display context; pass NULL only during window teardown</pre>
|
|
<h3>wmAddMenu</h3>
|
|
<pre><code>MenuT *wmAddMenu(MenuBarT *bar, const char *label);</code></pre>
|
|
<p>Append a dropdown menu to the menu bar. The label supports & accelerator markers (e.g. "&File").</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
bar Menu bar
|
|
label Menu label text</pre>
|
|
<p>Returns: Pointer to the new MenuT to populate with items.</p>
|
|
<h3>wmAddMenuItem</h3>
|
|
<pre><code>void wmAddMenuItem(MenuT *menu, const char *label, int32_t id);</code></pre>
|
|
<p>Append a clickable item to a menu. The id is passed to the window's onMenu callback when selected.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
menu Menu to append to
|
|
label Item label (supports & markers)
|
|
id Application-defined command ID</pre>
|
|
<h3>wmAddMenuCheckItem</h3>
|
|
<pre><code>void wmAddMenuCheckItem(MenuT *menu, const char *label, int32_t id, bool checked);</code></pre>
|
|
<p>Add a checkbox-style menu item. Check state toggles on click; rendered with a checkmark glyph.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
menu Menu to append to
|
|
label Item label
|
|
id Command ID
|
|
checked Initial checked state</pre>
|
|
<h3>wmAddMenuRadioItem</h3>
|
|
<pre><code>void wmAddMenuRadioItem(MenuT *menu, const char *label, int32_t id, bool checked);</code></pre>
|
|
<p>Add a radio-style menu item. Radio groups are defined implicitly by consecutive radio items; selecting one unchecks the others in the group.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
menu Menu to append to
|
|
label Item label
|
|
id Command ID
|
|
checked Initial checked state</pre>
|
|
<h3>wmAddMenuSeparator</h3>
|
|
<pre><code>void wmAddMenuSeparator(MenuT *menu);</code></pre>
|
|
<p>Insert a horizontal separator line. Separators are not interactive.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
menu Menu to append separator to</pre>
|
|
<h3>wmRemoveMenuItem</h3>
|
|
<pre><code>bool wmRemoveMenuItem(MenuT *menu, int32_t id);</code></pre>
|
|
<p>Remove a menu item by command ID. Searches the menu's items array and shifts remaining items down to close the gap.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
menu Menu to remove item from
|
|
id Command ID to remove</pre>
|
|
<p>Returns: true if a matching item was found and removed.</p>
|
|
<h3>wmClearMenuItems</h3>
|
|
<pre><code>void wmClearMenuItems(MenuT *menu);</code></pre>
|
|
<p>Remove all items from a menu while preserving the menu itself on the menu bar. Use when rebuilding a menu's contents dynamically (e.g. a recent-files submenu).</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
menu Menu to empty</pre>
|
|
<h3>wmMenuItemIsChecked</h3>
|
|
<pre><code>bool wmMenuItemIsChecked(MenuBarT *bar, int32_t id);</code></pre>
|
|
<p>Query the checked state of a menu item by command ID. Searches all menus in the bar.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
bar Menu bar
|
|
id Command ID to query</pre>
|
|
<p>Returns: true if checked.</p>
|
|
<h3>wmMenuItemSetChecked</h3>
|
|
<pre><code>void wmMenuItemSetChecked(MenuBarT *bar, int32_t id, bool checked);</code></pre>
|
|
<p>Set the checked state of a menu item by command ID. For radio items, setting checked=true also unchecks other radio items in the same group.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
bar Menu bar
|
|
id Command ID
|
|
checked New checked state</pre>
|
|
<h3>wmMenuItemSetEnabled</h3>
|
|
<pre><code>void wmMenuItemSetEnabled(MenuBarT *bar, int32_t id, bool enabled);</code></pre>
|
|
<p>Enable or disable a menu item by command ID.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
bar Menu bar
|
|
id Command ID
|
|
enabled true = enabled, false = grayed out</pre>
|
|
<h3>wmAddSubMenu</h3>
|
|
<pre><code>MenuT *wmAddSubMenu(MenuT *parentMenu, const char *label);</code></pre>
|
|
<p>Create a cascading submenu attached to a parent menu. The child MenuT is heap-allocated and freed when the parent window is destroyed.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
parentMenu Parent menu to attach submenu to
|
|
label Submenu label text</pre>
|
|
<p>Returns: Pointer to the child MenuT, or NULL on allocation failure.</p>
|
|
<h3>wmCreateMenu</h3>
|
|
<pre><code>MenuT *wmCreateMenu(void);</code></pre>
|
|
<p>Allocate a heap-resident MenuT for use as a context menu (right-click). Unlike menu bar menus, context menus are standalone allocations. Free with wmFreeMenu().</p>
|
|
<p>Returns: Pointer to the new MenuT.</p>
|
|
<h3>wmFreeMenu</h3>
|
|
<pre><code>void wmFreeMenu(MenuT *menu);</code></pre>
|
|
<p>Free a standalone menu allocated with wmCreateMenu(). Also frees any heap-allocated submenu children recursively.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
menu Menu to free</pre>
|
|
<h2>Scrollbars</h2>
|
|
<h3>wmAddVScrollbar</h3>
|
|
<pre><code>ScrollbarT *wmAddVScrollbar(WindowT *win, int32_t min, int32_t max, int32_t pageSize);</code></pre>
|
|
<p>Attach a vertical scrollbar to the right edge of the window's content area. Shrinks contentW by SCROLLBAR_WIDTH pixels. If the window already has a vertical scrollbar, that ScrollbarT is reused (same pointer) and reset to the new range with value = min. A negative pageSize is treated as 0.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
win Window
|
|
min, max Scroll value range
|
|
pageSize Visible portion (controls thumb size)</pre>
|
|
<p>Returns: Pointer to the window's ScrollbarT, or NULL on allocation failure.</p>
|
|
<h3>wmAddHScrollbar</h3>
|
|
<pre><code>ScrollbarT *wmAddHScrollbar(WindowT *win, int32_t min, int32_t max, int32_t pageSize);</code></pre>
|
|
<p>Attach a horizontal scrollbar to the bottom edge. Shrinks contentH by SCROLLBAR_WIDTH pixels. If the window already has a horizontal scrollbar, that ScrollbarT is reused and reset. A negative pageSize is treated as 0.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
win Window
|
|
min, max Scroll value range
|
|
pageSize Visible portion</pre>
|
|
<p>Returns: Pointer to the window's ScrollbarT, or NULL on allocation failure.</p>
|
|
<h2>Drawing</h2>
|
|
<h3>wmDrawChrome</h3>
|
|
<pre><code>void wmDrawChrome(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors, WindowT *win, const RectT *clipTo);</code></pre>
|
|
<p>Draw the window frame: outer bevel, title bar with text, close/minimize/maximize gadgets, and menu bar if present. Drawing is clipped to the intersection with clipTo. When the window has a content buffer, the content well (content area and scrollbar strips) is not painted: follow with wmDrawContent and wmDrawScrollbars, as the compositor does.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
d Display context
|
|
ops Blit operations vtable
|
|
font Bitmap font for title text
|
|
colors Color scheme
|
|
win Window to draw chrome for
|
|
clipTo Dirty rectangle to clip drawing to</pre>
|
|
<h3>wmDrawContent</h3>
|
|
<pre><code>void wmDrawContent(DisplayT *d, const BlitOpsT *ops, WindowT *win, const RectT *clipTo);</code></pre>
|
|
<p>Blit the window's content backbuffer into the display backbuffer, clipped to the dirty rect. Pure copy operation (no drawing); rows are copied with ops->spanCopy.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
d Display context
|
|
ops Blit operations vtable
|
|
win Window
|
|
clipTo Dirty rectangle</pre>
|
|
<h3>wmDrawScrollbars</h3>
|
|
<pre><code>void wmDrawScrollbars(DisplayT *d, const BlitOpsT *ops, const ColorSchemeT *colors, WindowT *win, const RectT *clipTo);</code></pre>
|
|
<p>Draw scrollbars (track, arrows, proportional thumb) for a window. Drawn after content so scrollbars overlay the content area edge.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
d Display context
|
|
ops Blit operations vtable
|
|
colors Color scheme
|
|
win Window
|
|
clipTo Dirty rectangle</pre>
|
|
<h3>wmDrawMinimizedIcons</h3>
|
|
<pre><code>void wmDrawMinimizedIcons(DisplayT *d, const BlitOpsT *ops, const ColorSchemeT *colors, const WindowStackT *stack, const RectT *clipTo);</code></pre>
|
|
<p>Draw icons for all minimized windows along the bottom of the screen. Each icon shows a scaled preview of the window's content with a beveled border.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
d Display context
|
|
ops Blit operations vtable
|
|
colors Color scheme
|
|
stack Window stack
|
|
clipTo Dirty rectangle</pre>
|
|
<h3>wmDrawVScrollbarAt</h3>
|
|
<pre><code>void wmDrawVScrollbarAt(DisplayT *d, const BlitOpsT *ops, const ColorSchemeT *colors, int32_t x, int32_t y, int32_t h, int32_t scrollPos, int32_t visibleItems, int32_t totalItems);</code></pre>
|
|
<p>Draw a standalone vertical scrollbar at the given screen coordinates, without a ScrollbarT struct. Used by popup lists (dropdown, combobox) that need a scrollbar inline within a popup overlay. Thumb size and position are computed from scrollPos / visibleItems / totalItems.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
d Display context
|
|
ops Blit operations vtable
|
|
colors Color scheme
|
|
x, y Screen position (top of scrollbar)
|
|
h Track height in pixels
|
|
scrollPos Current top visible item index
|
|
visibleItems Visible item count (for thumb size)
|
|
totalItems Total item count</pre>
|
|
<h3>wmVScrollbarAtClick</h3>
|
|
<pre><code>int32_t wmVScrollbarAtClick(int32_t h, int32_t relY, int32_t scrollPos, int32_t visibleItems, int32_t totalItems);</code></pre>
|
|
<p>Apply a click on a scrollbar drawn by wmDrawVScrollbarAt with the same h and scroll state; relY is the click's offset from the top of the bar. Returns the new scrollPos: the arrow buttons step one item, the trough steps a page (visibleItems) toward the click, and the thumb leaves the position unchanged. The result is clamped to 0 .. totalItems - visibleItems. The thumb comes from the same geometry the paint uses, so a click always matches what is drawn.</p>
|
|
<h2>Hit Testing</h2>
|
|
<h3>wmHitTest</h3>
|
|
<pre><code>int32_t wmHitTest(const WindowStackT *stack, int32_t mx, int32_t my, int32_t *hitPart);</code></pre>
|
|
<p>Determine which window and chrome region is under the given screen coordinates. Iterates front-to-back (highest Z first) so the topmost window wins.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
stack Window stack
|
|
mx, my Screen coordinates
|
|
hitPart Output: HIT_CONTENT, HIT_TITLE, HIT_CLOSE, etc.</pre>
|
|
<p>Returns: Stack index of hit window, or -1 for desktop.</p>
|
|
<h3>wmResizeEdgeHit</h3>
|
|
<pre><code>int32_t wmResizeEdgeHit(const WindowT *win, int32_t mx, int32_t my);</code></pre>
|
|
<p>Determine which edge(s) of a window's border zone are targeted for resize.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
win Window
|
|
mx, my Screen coordinates</pre>
|
|
<p>Returns: Bitmask of RESIZE_LEFT / RESIZE_RIGHT / RESIZE_TOP / RESIZE_BOTTOM.</p>
|
|
<h3>wmMinimizedIconHit</h3>
|
|
<pre><code>int32_t wmMinimizedIconHit(const WindowStackT *stack, const DisplayT *d, int32_t mx, int32_t my);</code></pre>
|
|
<p>Hit-test minimized icons at the bottom of the screen.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
stack Window stack
|
|
d Display context
|
|
mx, my Screen coordinates</pre>
|
|
<p>Returns: Stack index of the minimized window, or -1.</p>
|
|
<h2>Drag and Resize</h2>
|
|
<h3>wmDragBegin</h3>
|
|
<pre><code>void wmDragBegin(WindowStackT *stack, int32_t idx, int32_t mouseX, int32_t mouseY);</code></pre>
|
|
<p>Begin a window drag operation. Records the mouse offset from the window origin.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
stack Window stack
|
|
idx Stack index of window to drag
|
|
mouseX/Y Current mouse position</pre>
|
|
<h3>wmDragMove</h3>
|
|
<pre><code>void wmDragMove(WindowStackT *stack, DirtyListT *dl, int32_t mouseX, int32_t mouseY, int32_t screenW, int32_t screenH);</code></pre>
|
|
<p>Update window position during an active drag. Dirties both old and new positions.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
stack Window stack
|
|
dl Dirty list
|
|
mouseX/Y Current mouse position
|
|
screenW/H Screen dimensions (for clamping)</pre>
|
|
<h3>wmDragEnd</h3>
|
|
<pre><code>void wmDragEnd(WindowStackT *stack);</code></pre>
|
|
<p>End the current drag operation. Clears dragWindow state.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
stack Window stack</pre>
|
|
<h3>wmResizeBegin</h3>
|
|
<pre><code>void wmResizeBegin(WindowStackT *stack, int32_t idx, int32_t edge, int32_t mouseX, int32_t mouseY);</code></pre>
|
|
<p>Begin a window resize operation. Records which edge(s) are being dragged.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
stack Window stack
|
|
idx Stack index
|
|
edge Bitmask of RESIZE_xxx flags
|
|
mouseX/Y Current mouse position</pre>
|
|
<h3>wmResizeMove</h3>
|
|
<pre><code>void wmResizeMove(WindowStackT *stack, DirtyListT *dl, const DisplayT *d, int32_t *mouseX, int32_t *mouseY);</code></pre>
|
|
<p>Update window dimensions during an active resize. Enforces MIN_WINDOW_W/H and maxW/maxH constraints. Reallocates content buffer and calls onResize if size changed. mouseX/mouseY are in/out: clamped on return for cursor warping.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
stack Window stack
|
|
dl Dirty list
|
|
d Display context
|
|
mouseX/Y In/out: mouse position (clamped on return)</pre>
|
|
<h3>wmResizeEnd</h3>
|
|
<pre><code>void wmResizeEnd(WindowStackT *stack);</code></pre>
|
|
<p>End the current resize operation. Clears resizeWindow state.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
stack Window stack</pre>
|
|
<h2>Scrollbar Interaction</h2>
|
|
<h3>wmScrollbarClick</h3>
|
|
<pre><code>void wmScrollbarClick(WindowStackT *stack, DirtyListT *dl, int32_t idx, ScrollbarOrientE orient, int32_t mx, int32_t my);</code></pre>
|
|
<p>Handle an initial click on a scrollbar. Determines what was hit (arrows, trough, or thumb) and either adjusts the value immediately or begins a thumb drag.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
stack Window stack
|
|
dl Dirty list
|
|
idx Stack index of window
|
|
orient ScrollbarVerticalE or ScrollbarHorizontalE
|
|
mx, my Click screen coordinates</pre>
|
|
<h3>wmScrollbarDrag</h3>
|
|
<pre><code>void wmScrollbarDrag(WindowStackT *stack, DirtyListT *dl, int32_t mx, int32_t my);</code></pre>
|
|
<p>Update the scroll value during an active thumb drag. Maps mouse position along the track to a proportional scroll value.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
stack Window stack
|
|
dl Dirty list
|
|
mx, my Current mouse position</pre>
|
|
<h3>wmScrollbarEnd</h3>
|
|
<pre><code>void wmScrollbarEnd(WindowStackT *stack);</code></pre>
|
|
<p>End an active scrollbar thumb drag.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
stack Window stack</pre>
|
|
<h2>Minimize / Maximize / Restore</h2>
|
|
<h3>wmMaximize</h3>
|
|
<pre><code>void wmMaximize(DirtyListT *dl, const DisplayT *d, WindowT *win);</code></pre>
|
|
<p>Maximize a window. Saves current geometry, then expands to screen or maxW/maxH bounds.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
dl Dirty list
|
|
d Display context
|
|
win Window to maximize</pre>
|
|
<h3>wmMinimize</h3>
|
|
<pre><code>void wmMinimize(WindowStackT *stack, DirtyListT *dl, WindowT *win);</code></pre>
|
|
<p>Minimize a window. Hides the window and shows an icon at the bottom of the screen.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
stack Window stack
|
|
dl Dirty list
|
|
win Window to minimize</pre>
|
|
<h3>wmRestore</h3>
|
|
<pre><code>void wmRestore(DirtyListT *dl, const DisplayT *d, WindowT *win);</code></pre>
|
|
<p>Restore a maximized window to its pre-maximize geometry.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
dl Dirty list
|
|
d Display context
|
|
win Maximized window to restore</pre>
|
|
<h3>wmRestoreMinimized</h3>
|
|
<pre><code>void wmRestoreMinimized(WindowStackT *stack, DirtyListT *dl, const DisplayT *d, WindowT *win);</code></pre>
|
|
<p>Restore a minimized window (show it again, remove the icon) and raise it to the top. It does NOT change focus, so no onBlur/onFocus fires here; applications use dvxRestoreWindow, which also focuses the window with correct callback attribution.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
stack Window stack
|
|
dl Dirty list
|
|
d Display context
|
|
win Minimized window to restore</pre>
|
|
<h2>Minimized Icon Layout</h2>
|
|
<h3>wmMinimizedIconPos</h3>
|
|
<pre><code>void wmMinimizedIconPos(const DisplayT *d, int32_t index, int32_t *x, int32_t *y);</code></pre>
|
|
<p>Compute the screen position of a minimized icon by ordinal index. Icons wrap into rows from bottom to top when the screen fills up.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
d Display context
|
|
index Ordinal index of the minimized icon
|
|
x, y Output: screen position</pre>
|
|
<h3>wmMinimizedIconRect</h3>
|
|
<pre><code>void wmMinimizedIconRect(const WindowStackT *stack, const DisplayT *d, int32_t *y, int32_t *h);</code></pre>
|
|
<p>Compute the screen rect covering all minimized icon rows. Used to dirty the icon area when windows are minimized or restored.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
stack Window stack
|
|
d Display context
|
|
y, h Output: vertical extent of icon area</pre>
|
|
<h2>Miscellaneous</h2>
|
|
<h3>wmSetTitle</h3>
|
|
<pre><code>void wmSetTitle(WindowT *win, DirtyListT *dl, const char *title);</code></pre>
|
|
<p>Set the window title and dirty the title bar for repaint.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
win Window
|
|
dl Dirty list
|
|
title New title text</pre>
|
|
<h3>wmSetIconData</h3>
|
|
<pre><code>void wmSetIconData(WindowT *win, uint8_t *pixels, int32_t w, int32_t h, int32_t pitch);</code></pre>
|
|
<p>Install an icon that is already in display pixel format (for example from dvxLoadImage). Takes ownership of the malloc'd pixel buffer; it is freed with the window or when the next icon replaces it.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
win Window
|
|
pixels Display-format pixels (ownership transfers to the window)
|
|
w, h Icon size in pixels
|
|
pitch Bytes per row</pre>
|
|
</div>
|
|
<div class="topic" id="api.app">
|
|
<h1>dvxApp.h -- Layer 5: Application API</h1>
|
|
<p>The topmost layer and the public-facing API. Aggregates all lower layers into a single AppContextT. Applications interact exclusively through dvx*() functions and window callbacks. The event loop follows a cooperative model: poll, dispatch, composite, yield.</p>
|
|
<h2>AppContextT</h2>
|
|
<p>Single monolithic context that owns all GUI state. Contains the display, window stack, dirty list, blit ops, font, color scheme, popup state, cursor state, mouse/keyboard state, tooltip state, wallpaper buffer, video mode list, and various configuration fields. Allocated on the caller's stack or statically.</p>
|
|
<p>Fields applications and the shell commonly read (all others are internal state):</p>
|
|
<pre> Field Description
|
|
----- -----------
|
|
DisplayT display Screen display context (use dvxGetDisplay)
|
|
WindowStackT stack Window stack (z-order, focus, drag state)
|
|
int32_t mouseX, mouseY Current mouse position
|
|
int32_t mouseButtons Current MOUSE_* button bitmask
|
|
int32_t mouseWheel Wheel delta this frame (positive = down)
|
|
int32_t keyModifiers Current BIOS shift state (KEY_MOD_*)
|
|
int32_t keyFocusWinId Window the held-key table was last cleared for (0 = none; internal)
|
|
int32_t wheelDirection 1 = normal, -1 = reversed
|
|
int32_t wheelStep Lines per wheel notch (1-10, default 3)
|
|
PlatformTicksT dblClickTicks Double-click interval in platformClock ticks
|
|
uint32_t charHeightRecip 16.16 reciprocal of font.charHeight (popup row mapping)
|
|
WindowT *modalWindow If non-NULL, only this window receives input
|
|
int32_t dispatchDepth Callback nesting depth; destroys are deferred while > 0
|
|
uint32_t windowsCreated Windows created by dvxCreateWindow so far (the shell compares it before and after a close request to see whether the app showed a prompt)
|
|
int32_t currentAppId App charged for allocations and callbacks (0 = shell)
|
|
bool busy Busy cursor shown and input blocked
|
|
idleCallback / idleCtx Called instead of yielding when a frame had no events
|
|
onCtrlEsc, onF1, onTitleChange Shell hooks (with their ctx pointers)
|
|
VideoModeInfoT *videoModes stb_ds array of settable modes
|
|
char wallpaperPath[] Current wallpaper source path (empty = none)
|
|
WallpaperModeE wallpaperMode Stretch, tile or center</pre>
|
|
<p>The mouse speed and acceleration settings are not stored in AppContextT: dvxSetMouseConfig programs them into the platform layer, which re-applies them after every mouse driver reset.</p>
|
|
<h2>Initialization and Shutdown</h2>
|
|
<h3>dvxInit</h3>
|
|
<pre><code>int32_t dvxInit(AppContextT *ctx, int32_t requestedW, int32_t requestedH, int32_t preferredBpp);</code></pre>
|
|
<p>Initialize the entire GUI stack: video mode, input devices, font, color scheme, cursor shapes, and internal state. Single entry point for starting a DVX application.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context to initialize
|
|
requestedW/H Desired screen resolution
|
|
preferredBpp Preferred bits per pixel</pre>
|
|
<p>Returns: 0 on success, negative on failure.</p>
|
|
<h3>dvxShutdown</h3>
|
|
<pre><code>void dvxShutdown(AppContextT *ctx);</code></pre>
|
|
<p>Tear down the GUI stack in reverse order: destroy all windows, restore text mode, release input devices. Safe to call after a failed dvxInit().</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context</pre>
|
|
<h3>dvxChangeVideoMode</h3>
|
|
<pre><code>int32_t dvxChangeVideoMode(AppContextT *ctx, int32_t requestedW, int32_t requestedH, int32_t preferredBpp);</code></pre>
|
|
<p>Switch to a new video mode live. Reallocates the backbuffer, all window content buffers, repacks colors, rescales wallpaper, and repositions off-screen windows.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
requestedW/H New resolution
|
|
preferredBpp New bits per pixel</pre>
|
|
<p>Returns: 0 on success, -1 on failure (old mode restored).</p>
|
|
<h2>Event Loop</h2>
|
|
<h3>dvxRun</h3>
|
|
<pre><code>void dvxRun(AppContextT *ctx);</code></pre>
|
|
<p>Enter the main event loop. Polls input, dispatches events, composites dirty regions, and yields on each iteration. Returns when ctx->running becomes false.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context</pre>
|
|
<h3>dvxUpdate</h3>
|
|
<pre><code>bool dvxUpdate(AppContextT *ctx);</code></pre>
|
|
<p>Process exactly one frame of the event loop. For applications that integrate the GUI into their own main loop (e.g. polling serial ports between frames).</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context</pre>
|
|
<p>Returns: false when the GUI wants to exit.</p>
|
|
<h3>dvxQuit</h3>
|
|
<pre><code>void dvxQuit(AppContextT *ctx);</code></pre>
|
|
<p>Request exit from the main event loop (sets ctx->running = false).</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context</pre>
|
|
<h2>Window Management</h2>
|
|
<p>Every dvx* function that takes a WindowT pointer accepts NULL as a no-op (functions that return a status return failure).</p>
|
|
<h3>dvxCreateWindow</h3>
|
|
<pre><code>WindowT *dvxCreateWindow(AppContextT *ctx, const char *title, int32_t x, int32_t y, int32_t w, int32_t h, bool resizable);</code></pre>
|
|
<p>Create a window at an explicit screen position. The window is raised to the top, focused, and its entire region is dirtied.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
title Window title
|
|
x, y Screen position
|
|
w, h Outer frame dimensions
|
|
resizable true = allow user resize</pre>
|
|
<p>Returns: Pointer to new WindowT.</p>
|
|
<h3>dvxCreateWindowCentered</h3>
|
|
<pre><code>WindowT *dvxCreateWindowCentered(AppContextT *ctx, const char *title, int32_t w, int32_t h, bool resizable);</code></pre>
|
|
<p>Convenience wrapper that centers the window on screen.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
title Window title
|
|
w, h Outer frame dimensions
|
|
resizable true = allow user resize</pre>
|
|
<p>Returns: Pointer to new WindowT.</p>
|
|
<h3>dvxDestroyWindow</h3>
|
|
<pre><code>void dvxDestroyWindow(AppContextT *ctx, WindowT *win);</code></pre>
|
|
<p>Destroy a window, free all its resources, and dirty its former region.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
win Window to destroy</pre>
|
|
<h3>dvxRaiseWindow</h3>
|
|
<pre><code>void dvxRaiseWindow(AppContextT *ctx, WindowT *win);</code></pre>
|
|
<p>Raise a window to the top of the Z-order and give it focus.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
win Window to raise</pre>
|
|
<h3>dvxRequestWindowClose</h3>
|
|
<pre><code>void dvxRequestWindowClose(AppContextT *ctx, WindowT *win);</code></pre>
|
|
<p>Ask a window to close exactly as its close box, Alt+F4 and the system menu's Close do. The window's onClose runs (under the window's app ID) and decides -- a save prompt may keep the window open; a window without onClose is destroyed. A clean frame is composited first so an open menu is erased before onClose shows a dialog. When no dispatch is in progress the window may already be freed on return, so look it up again by its id rather than using the pointer. The shell's Exit DVX sequence uses this to ask each app to close.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
win Window to ask to close</pre>
|
|
<h3>dvxRestoreWindow</h3>
|
|
<pre><code>void dvxRestoreWindow(AppContextT *ctx, WindowT *win);</code></pre>
|
|
<p>Restore a minimized window (or un-maximize a maximized one), then raise and focus it. Focus-change callbacks (onBlur of the old window, onFocus of the restored one) run under their own app's ID. No-op for NULL or a window pending destruction.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
win Window to restore</pre>
|
|
<h3>dvxFitWindow</h3>
|
|
<pre><code>void dvxFitWindow(AppContextT *ctx, WindowT *win);</code></pre>
|
|
<p>Resize a window to exactly fit its widget tree's computed minimum size (plus chrome), never smaller than the window manager's minimum window size. Used for dialog boxes and fixed-layout windows.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
win Window to fit</pre>
|
|
<h3>dvxFitWindowW</h3>
|
|
<pre><code>void dvxFitWindowW(AppContextT *ctx, WindowT *win);</code></pre>
|
|
<p>Resize window width only to fit widget tree's minimum width (plus chrome), never below the window manager's minimum width.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
win Window to fit</pre>
|
|
<h3>dvxFitWindowH</h3>
|
|
<pre><code>void dvxFitWindowH(AppContextT *ctx, WindowT *win);</code></pre>
|
|
<p>Resize window height only to fit widget tree's minimum height (plus chrome), never below the window manager's minimum height.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
win Window to fit</pre>
|
|
<h3>dvxResizeWindow</h3>
|
|
<pre><code>void dvxResizeWindow(AppContextT *ctx, WindowT *win, int32_t newW, int32_t newH);</code></pre>
|
|
<p>Programmatically resize a window to the specified outer dimensions.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
win Window to resize
|
|
newW, newH New outer frame dimensions</pre>
|
|
<h3>dvxMinimizeWindow</h3>
|
|
<pre><code>void dvxMinimizeWindow(AppContextT *ctx, WindowT *win);</code></pre>
|
|
<p>Minimize a window (show as icon at bottom of screen).</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
win Window to minimize</pre>
|
|
<h3>dvxMaximizeWindow</h3>
|
|
<pre><code>void dvxMaximizeWindow(AppContextT *ctx, WindowT *win);</code></pre>
|
|
<p>Maximize a window (expand to fill screen or maxW/maxH).</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
win Window to maximize</pre>
|
|
<h3>dvxHideWindow</h3>
|
|
<pre><code>void dvxHideWindow(AppContextT *ctx, WindowT *win);</code></pre>
|
|
<p>Hide a window without destroying it. Marks the exposed region dirty.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
win Window to hide</pre>
|
|
<h3>dvxShowWindow</h3>
|
|
<pre><code>void dvxShowWindow(AppContextT *ctx, WindowT *win);</code></pre>
|
|
<p>Show a previously hidden window. Marks its region dirty for repaint.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
win Window to show</pre>
|
|
<h2>Invalidation</h2>
|
|
<h3>dvxInvalidateRect</h3>
|
|
<pre><code>void dvxInvalidateRect(AppContextT *ctx, WindowT *win, int32_t x, int32_t y, int32_t w, int32_t h);</code></pre>
|
|
<p>Mark a sub-region of a window's content area as needing repaint. Coordinates are relative to the content area, not the screen. Triggers onPaint during the next composite pass.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
win Window
|
|
x, y, w, h Dirty rectangle in content-relative coordinates</pre>
|
|
<h3>dvxInvalidateWindow</h3>
|
|
<pre><code>void dvxInvalidateWindow(AppContextT *ctx, WindowT *win);</code></pre>
|
|
<p>Mark the entire window content area as dirty.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
win Window to invalidate</pre>
|
|
<h2>Window Properties</h2>
|
|
<h3>dvxSetTitle</h3>
|
|
<pre><code>void dvxSetTitle(AppContextT *ctx, WindowT *win, const char *title);</code></pre>
|
|
<p>Set a window's title text and dirty the title bar.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
win Window
|
|
title New title text</pre>
|
|
<h3>dvxSetWindowIcon</h3>
|
|
<pre><code>int32_t dvxSetWindowIcon(AppContextT *ctx, WindowT *win, const char *path);</code></pre>
|
|
<p>Load an icon for a window from an image file. The image is converted to the display format with the same (dithered) conversion as dvxLoadImage and installed with wmSetIconData.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
win Window
|
|
path Image file path</pre>
|
|
<p>Returns: 0 on success, -1 on failure.</p>
|
|
<h3>dvxSetBusy</h3>
|
|
<pre><code>void dvxSetBusy(AppContextT *ctx, bool busy);</code></pre>
|
|
<p>Set or clear busy state. While busy, the hourglass cursor is shown and input is blocked.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
busy true = show hourglass, false = normal</pre>
|
|
<h2>Accessors</h2>
|
|
<h3>dvxGetFont</h3>
|
|
<pre><code>const BitmapFontT *dvxGetFont(const AppContextT *ctx);</code></pre>
|
|
<p>Get a pointer to the default font.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context</pre>
|
|
<p>Returns: Pointer to the active BitmapFontT.</p>
|
|
<h3>dvxGetColors</h3>
|
|
<pre><code>const ColorSchemeT *dvxGetColors(const AppContextT *ctx);</code></pre>
|
|
<p>Get a pointer to the current color scheme.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context</pre>
|
|
<p>Returns: Pointer to the active ColorSchemeT.</p>
|
|
<h3>dvxGetDisplay</h3>
|
|
<pre><code>DisplayT *dvxGetDisplay(AppContextT *ctx);</code></pre>
|
|
<p>Get a pointer to the display context.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context</pre>
|
|
<p>Returns: Pointer to the DisplayT.</p>
|
|
<h3>dvxGetBlitOps</h3>
|
|
<pre><code>const BlitOpsT *dvxGetBlitOps(const AppContextT *ctx);</code></pre>
|
|
<p>Get a pointer to the blit operations vtable.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context</pre>
|
|
<p>Returns: Pointer to the active BlitOpsT.</p>
|
|
<h3>dvxContentDisplay</h3>
|
|
<pre><code>void dvxContentDisplay(const AppContextT *ctx, const WindowT *win, DisplayT *out);</code></pre>
|
|
<p>Fill *out with a copy of the display redirected at the window's content buffer: backBuf and lfb point at win->contentBuf, width/height/pitch are the content size and pitch, and the clip rect covers the whole buffer. Pixel format fields come from the real display. Use it to draw straight into a window's content buffer with the normal drawing primitives, for example from an onPaint handler that does not use widgets. Because lfb is redirected too, nothing drawn through the copy can reach the screen.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
win Window whose content buffer is the target
|
|
out Output: display context for drawing into the buffer</pre>
|
|
<pre><code>static void onPaint(WindowT *win, RectT *dirty) {
|
|
DisplayT cd;
|
|
|
|
dvxContentDisplay(ctx, win, &cd);
|
|
rectFill(&cd, dvxGetBlitOps(ctx), 0, 0, win->contentW, win->contentH, dvxGetColors(ctx)->contentBg);
|
|
}</code></pre>
|
|
<h3>dvxGetVideoModes</h3>
|
|
<pre><code>const VideoModeInfoT *dvxGetVideoModes(const AppContextT *ctx, int32_t *count);</code></pre>
|
|
<p>Return the list of available video modes enumerated at init time.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
count Output: number of mode entries</pre>
|
|
<p>Returns: Pointer to the VideoModeInfoT array.</p>
|
|
<h2>Color Scheme</h2>
|
|
<h3>dvxSetColor</h3>
|
|
<pre><code>void dvxSetColor(AppContextT *ctx, ColorIdE id, uint8_t r, uint8_t g, uint8_t b);</code></pre>
|
|
<p>Set a single color by ID. Repacks to native pixel format and invalidates the entire screen.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
id Color ID (ColorIdE)
|
|
r, g, b RGB values (0-255)</pre>
|
|
<h3>dvxGetColor</h3>
|
|
<pre><code>void dvxGetColor(const AppContextT *ctx, ColorIdE id, uint8_t *r, uint8_t *g, uint8_t *b);</code></pre>
|
|
<p>Get a color's RGB values by ID.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
id Color ID (ColorIdE)
|
|
r, g, b Output: RGB values</pre>
|
|
<h3>dvxApplyColorScheme</h3>
|
|
<pre><code>void dvxApplyColorScheme(AppContextT *ctx);</code></pre>
|
|
<p>Apply all colors from ctx->colorRgb[] at once (repack + full repaint).</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context</pre>
|
|
<h3>dvxResetColorScheme</h3>
|
|
<pre><code>void dvxResetColorScheme(AppContextT *ctx);</code></pre>
|
|
<p>Reset all colors to the built-in defaults and repaint.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context</pre>
|
|
<h3>dvxLoadTheme</h3>
|
|
<pre><code>bool dvxLoadTheme(AppContextT *ctx, const char *filename);</code></pre>
|
|
<p>Load a theme file (INI format with [colors] section) and apply it. Each color is a "name = r,g,b" key in [colors]; keys outside that section, unknown names and malformed values are ignored, and channels are clamped to 0..255.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
filename Path to theme INI file</pre>
|
|
<p>Returns: true on success.</p>
|
|
<h3>dvxSaveTheme</h3>
|
|
<pre><code>bool dvxSaveTheme(const AppContextT *ctx, const char *filename);</code></pre>
|
|
<p>Save the current color scheme to a theme file. When the file already exists its comments and other lines are kept and only the [colors] keys are updated. The new copy is written to a temporary file first and swapped in only when every write succeeded, so a failed save leaves the old theme intact.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
filename Output file path</pre>
|
|
<p>Returns: true on success.</p>
|
|
<h3>dvxColorName</h3>
|
|
<pre><code>const char *dvxColorName(ColorIdE id);</code></pre>
|
|
<p>Return the INI key name for a color ID (e.g. "desktop", "windowFace").</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
id Color ID</pre>
|
|
<p>Returns: Static string.</p>
|
|
<h3>dvxColorLabel</h3>
|
|
<pre><code>const char *dvxColorLabel(ColorIdE id);</code></pre>
|
|
<p>Return a human-readable display label (e.g. "Desktop", "Cursor Color").</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
id Color ID</pre>
|
|
<p>Returns: Static string.</p>
|
|
<h2>Wallpaper</h2>
|
|
<h3>dvxSetWallpaper</h3>
|
|
<pre><code>bool dvxSetWallpaper(AppContextT *ctx, const char *path);</code></pre>
|
|
<p>Load and apply a wallpaper image using the current wallpaperMode (stretch/tile/center). Pass NULL to clear the wallpaper. If the image cannot be loaded, the current wallpaper and its saved path are kept and false is returned.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
path Image file path, or NULL to clear</pre>
|
|
<p>Returns: true on success.</p>
|
|
<h3>dvxSetWallpaperMode</h3>
|
|
<pre><code>void dvxSetWallpaperMode(AppContextT *ctx, WallpaperModeE mode);</code></pre>
|
|
<p>Change the wallpaper display mode and re-render. No effect if no wallpaper is loaded.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
mode WallpaperStretchE, WallpaperTileE, or WallpaperCenterE</pre>
|
|
<h2>Mouse Configuration</h2>
|
|
<h3>dvxSetMouseConfig</h3>
|
|
<pre><code>void dvxSetMouseConfig(AppContextT *ctx, int32_t wheelDir, int32_t dblClickMs, int32_t accelThreshold, int32_t mickeyRatio, int32_t wheelStep);</code></pre>
|
|
<p>Configure mouse behavior. Updates the in-memory AppContextT fields and programs any INT 33h settings that need driver-side adjustment.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
wheelDir 1 = normal, -1 = reversed
|
|
dblClickMs Double-click speed in milliseconds (e.g. 500)
|
|
accelThreshold Double-speed threshold in mickeys/sec (0 = don't change)
|
|
mickeyRatio Mickeys per 8 pixels (0 = don't change, 8 = default speed)
|
|
wheelStep Lines to scroll per wheel notch (1-10, default 3)</pre>
|
|
<p>The speed, acceleration and wheel settings are re-applied by the platform layer after every mouse driver reset, so they survive a live video-mode change.</p>
|
|
<h2>Input Entropy</h2>
|
|
<h3>dvxSetInputEntropyHook</h3>
|
|
<pre><code>typedef void (*DvxEntropyHookT)(const uint8_t *data, int32_t len);
|
|
|
|
void dvxSetInputEntropyHook(DvxEntropyHookT fn);</code></pre>
|
|
<p>Install a sink for input-timing entropy (for example a random-number generator's add-entropy routine). While a hook is set, every keyboard event and every mouse change (movement, button, wheel) is recorded as a 32-bit platformEntropyStamp value followed by the event bytes (scancode, ASCII and shift state; or position, buttons and wheel delta). Records are batched and handed to fn once 64 bytes have accumulated, so the cost per event stays negligible. An idle mouse is not recorded. Changing or clearing the hook discards any pending partial batch. The core library has no link-time dependency on any consumer; the shell resolves one at run time.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
fn Entropy sink, or NULL to disable</pre>
|
|
<h2>Desktop Sounds</h2>
|
|
<p>The sounds DVX plays for its events. Which file each event plays (or none) is set on the Control Panel's Sounds page and kept in DVX.INI [sounds]; the sound library plays them (see its developer help). Without the sound library, or without sound hardware, nothing is heard and nothing else changes.</p>
|
|
<pre><code>typedef enum {
|
|
SoundDefaultE, // the default beep (dvxBeep)
|
|
SoundStartE, // DVX started
|
|
SoundExitE, // DVX is ending
|
|
SoundErrorE,
|
|
SoundWarningE,
|
|
SoundQuestionE,
|
|
SoundInfoE,
|
|
SoundWindowOpenE,
|
|
SoundWindowCloseE,
|
|
SoundCountE
|
|
} DvxSoundE;</code></pre>
|
|
<ul>
|
|
<li>dvxMessageBox (and the dialogs built on it: dvxErrorBox, dvxInfoBox, dvxPromptSave, ...) plays the event of its icon: MB_ICONERROR SoundErrorE, MB_ICONWARNING SoundWarningE, MB_ICONQUESTION SoundQuestionE, MB_ICONINFO SoundInfoE, no icon SoundDefaultE.</li>
|
|
<li>A window created with dvxCreateWindow plays SoundWindowOpenE at the next dvxUpdate, unless it has become modal by then (dialogs are quiet); destroying a window that is not modal plays SoundWindowCloseE.</li>
|
|
<li>The shell plays SoundStartE once the desktop program is up, and SoundExitE (waiting for it) when DVX ends.</li>
|
|
</ul>
|
|
<h3>dvxPlaySound / dvxPlaySoundWait / dvxBeep</h3>
|
|
<pre><code>void dvxPlaySound(DvxSoundE event);
|
|
void dvxPlaySoundWait(DvxSoundE event);
|
|
void dvxBeep(void);</code></pre>
|
|
<p>Play the sound of an event. dvxPlaySound returns at once while the sound plays on; dvxPlaySoundWait returns once it has played (at most a few seconds); dvxBeep is dvxPlaySound(SoundDefaultE). An event without a sound is silent.</p>
|
|
<h3>dvxSetSoundHook</h3>
|
|
<pre><code>typedef void (*DvxSoundHookT)(DvxSoundE event, bool wait);
|
|
|
|
void dvxSetSoundHook(DvxSoundHookT fn);</code></pre>
|
|
<p>The function that plays the sounds, set by the sound library when it loads (NULL: silent). Programs do not call it.</p>
|
|
<h2>Accelerators</h2>
|
|
<h3>dvxCreateAccelTable</h3>
|
|
<pre><code>AccelTableT *dvxCreateAccelTable(void);</code></pre>
|
|
<p>Allocate a new accelerator table. Attach to a window via win->accelTable.</p>
|
|
<p>Returns: Pointer to new AccelTableT.</p>
|
|
<h3>dvxFreeAccelTable</h3>
|
|
<pre><code>void dvxFreeAccelTable(AccelTableT *table);</code></pre>
|
|
<p>Free an accelerator table and its entries.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
table Table to free</pre>
|
|
<h3>dvxAddAccel</h3>
|
|
<pre><code>void dvxAddAccel(AccelTableT *table, int32_t key, int32_t modifiers, int32_t cmdId);</code></pre>
|
|
<p>Register a keyboard shortcut. On match, fires the window's onMenu callback with cmdId.</p>
|
|
<p>The key and modifiers are normalized at registration with the same rule applied to every keystroke:</p>
|
|
<ul>
|
|
<li>Ctrl+letter control codes map back to the letter, and Alt+letter/digit scancodes map to the character, so ('S', ACCEL_CTRL) and ('S', ACCEL_ALT) both match.</li>
|
|
<li>Letters are uppercased ('s' and 'S' are the same key).</li>
|
|
<li>Ctrl and Alt are always significant. Shift is significant for extended keys (F-keys, arrows, ...) and for any Ctrl/Alt combination, so Ctrl+S and Ctrl+Shift+S are different accelerators, as are F5 and Shift+F5.</li>
|
|
<li>A printable character typed without Ctrl/Alt matches regardless of Shift, because the character already encodes Shift ('+', 'A').</li>
|
|
</ul>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
table Accelerator table (NULL is a no-op)
|
|
key ASCII character or KEY_Fxx / KEY_* constant
|
|
modifiers Bitmask of ACCEL_CTRL / ACCEL_SHIFT / ACCEL_ALT
|
|
cmdId Command ID passed to onMenu</pre>
|
|
<h2>System-Wide Hotkeys</h2>
|
|
<p>A hotkey is a keystroke that works whichever window has the focus (or when none has): its callback runs instead of anything the focused window would do with the keys, including its menu keys and accelerators. Program Manager's item shortcut keys are hotkeys. Hotkeys are not checked while a menu or a modal dialog is open, and keys the system handles first (Alt+Tab, Alt+F4, Ctrl+F12, Ctrl+Esc, F1, F10, Alt+Space) never reach them.</p>
|
|
<p>A hotkey belongs to the app that was current when it was registered (ctx->currentAppId, set by the shell whenever app code runs). When an app exits, crashes or is ended from the Task Manager, the shell removes its hotkeys with dvxUnregisterAppHotkeys, so a callback never outlives the code it points into. The callback also runs with currentAppId set to that app.</p>
|
|
<h3>dvxRegisterHotkey</h3>
|
|
<pre><code>typedef void (*DvxHotkeyFnT)(void *userCtx);
|
|
|
|
int32_t dvxRegisterHotkey(AppContextT *ctx, int32_t key, int32_t modifiers, DvxHotkeyFnT fn, void *userCtx);</code></pre>
|
|
<p>Register a system-wide hotkey. key and modifiers are given and normalized exactly as for dvxAddAccel. A key needs Ctrl or Alt unless it is a function or navigation key (KEY_EXT_FLAG set), so a hotkey cannot swallow typing.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
key ASCII character or KEY_Fxx / KEY_* constant
|
|
modifiers Bitmask of ACCEL_CTRL / ACCEL_SHIFT / ACCEL_ALT
|
|
fn Called with userCtx when the keystroke is pressed
|
|
userCtx Passed to fn</pre>
|
|
<p>Returns: the hotkey's id (greater than zero), or DVX_HOTKEY_NONE when refused: NULL ctx or fn, a character key without Ctrl or Alt, or a keystroke that is already a hotkey (first come, first served).</p>
|
|
<h3>dvxUnregisterHotkey</h3>
|
|
<pre><code>bool dvxUnregisterHotkey(AppContextT *ctx, int32_t hotkeyId);</code></pre>
|
|
<p>Remove one hotkey (a callback may remove its own). Returns false when ctx has no hotkey with that id.</p>
|
|
<h3>dvxUnregisterAppHotkeys</h3>
|
|
<pre><code>void dvxUnregisterAppHotkeys(int32_t appId);</code></pre>
|
|
<p>Remove every hotkey registered while appId was the current app. The shell calls it when an app goes away; apps do not need to.</p>
|
|
<h2>Hung Apps</h2>
|
|
<h3>dvxAppIsHung</h3>
|
|
<pre><code>#define DVX_HUNG_APP_MS 5000
|
|
|
|
bool dvxAppIsHung(const AppContextT *ctx, int32_t appId);</code></pre>
|
|
<p>True when appId has been inside one of its window callbacks (an event handler that has not returned to the event loop, such as a button handler in an endless loop) for DVX_HUNG_APP_MS or longer. An app with a modal dialog of its own open is waiting for the user, not hung. The Task Manager shows such an app as "Not responding". Every window callback dvxUpdate dispatches is booked per app (nested calls count once, from the outermost); widget Timer callbacks are not window callbacks and are not tracked.</p>
|
|
<h3>dvxAppHungForget</h3>
|
|
<pre><code>void dvxAppHungForget(int32_t appId);</code></pre>
|
|
<p>Drop appId's callback bookkeeping. The shell calls it when an app goes away (even from inside one of its callbacks, when End Task stops it); apps do not need to.</p>
|
|
<h2>Window Arrangement</h2>
|
|
<h3>dvxCascadeWindows</h3>
|
|
<pre><code>void dvxCascadeWindows(AppContextT *ctx);</code></pre>
|
|
<p>Cascade all visible, non-minimized windows. Each is offset diagonally by the title bar height; the diagonal wraps back to the top-left corner when the next window would leave the screen.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context</pre>
|
|
<h3>dvxTileWindows</h3>
|
|
<pre><code>void dvxTileWindows(AppContextT *ctx);</code></pre>
|
|
<p>Arrange visible windows in an NxM grid filling the screen. Windows are never placed off screen: when there are more windows than fit at the minimum window size, the extra tiles overlap.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context</pre>
|
|
<h3>dvxTileWindowsH</h3>
|
|
<pre><code>void dvxTileWindowsH(AppContextT *ctx);</code></pre>
|
|
<p>Tile windows horizontally (side by side, equal width, full height).</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context</pre>
|
|
<h3>dvxTileWindowsV</h3>
|
|
<pre><code>void dvxTileWindowsV(AppContextT *ctx);</code></pre>
|
|
<p>Tile windows vertically (stacked, full width, equal height).</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context</pre>
|
|
<h2>Image I/O</h2>
|
|
<h3>dvxLoadImage</h3>
|
|
<pre><code>uint8_t *dvxLoadImage(const AppContextT *ctx, const char *path, int32_t *outW, int32_t *outH, int32_t *outPitch);</code></pre>
|
|
<p>Load an image file (BMP, PNG, JPEG, GIF) and convert to the display's native pixel format. Caller must free with dvxFreeImage().</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
path Image file path
|
|
outW, outH Output: image dimensions
|
|
outPitch Output: row pitch in bytes</pre>
|
|
<p>Returns: Pixel buffer, or NULL on failure.</p>
|
|
<h3>dvxLoadImageFromMemory</h3>
|
|
<pre><code>uint8_t *dvxLoadImageFromMemory(const AppContextT *ctx, const uint8_t *data, int32_t dataLen, int32_t *outW, int32_t *outH, int32_t *outPitch);</code></pre>
|
|
<p>Load an image from a memory buffer (typically resource data). Same output format as dvxLoadImage(). Caller must free with dvxFreeImage().</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
data Image data buffer
|
|
dataLen Buffer size in bytes
|
|
outW, outH Output: image dimensions
|
|
outPitch Output: row pitch in bytes</pre>
|
|
<p>Returns: Pixel buffer, or NULL on failure.</p>
|
|
<h3>dvxLoadImageAlpha</h3>
|
|
<pre><code>uint8_t *dvxLoadImageAlpha(const AppContextT *ctx, const uint8_t *data, int32_t dataLen, int32_t *outW, int32_t *outH, int32_t *outPitch, bool *outHasAlpha, uint32_t *outKeyColor);</code></pre>
|
|
<p>Load an image from memory with alpha transparency. Pixels with alpha < 128 are replaced with a packed magenta key color. If any transparent pixels are found, *outHasAlpha is set to true and *outKeyColor receives the packed key color for use with rectCopyTransparent. Caller must free with dvxFreeImage.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
data Image data buffer
|
|
dataLen Buffer size in bytes
|
|
outW, outH Output: image dimensions
|
|
outPitch Output: row pitch
|
|
outHasAlpha Output: true if any transparent pixels found
|
|
outKeyColor Output: packed key color (valid when outHasAlpha is true)</pre>
|
|
<p>Returns: Pixel buffer, or NULL on failure.</p>
|
|
<h3>dvxFreeImage</h3>
|
|
<pre><code>void dvxFreeImage(uint8_t *data);</code></pre>
|
|
<p>Free a pixel buffer returned by dvxLoadImage() or dvxLoadImageFromMemory().</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
data Buffer to free</pre>
|
|
<h3>dvxImageInfo</h3>
|
|
<pre><code>bool dvxImageInfo(const char *path, int32_t *outW, int32_t *outH);</code></pre>
|
|
<p>Query image dimensions without decoding the full file.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
path Image file path
|
|
outW, outH Output: image dimensions</pre>
|
|
<p>Returns: true on success.</p>
|
|
<h3>dvxSaveImage</h3>
|
|
<pre><code>int32_t dvxSaveImage(const AppContextT *ctx, const uint8_t *data, int32_t w, int32_t h, int32_t pitch, const char *path);</code></pre>
|
|
<p>Save native-format pixel data to an image file: BMP when path ends in .bmp (case-insensitive), PNG otherwise.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
data Pixel data in display native format
|
|
w, h Image dimensions
|
|
pitch Row pitch in bytes
|
|
path Output file path</pre>
|
|
<p>Returns: 0 on success, -1 on failure.</p>
|
|
<h2>Screenshots</h2>
|
|
<h3>dvxScreenshot</h3>
|
|
<pre><code>int32_t dvxScreenshot(AppContextT *ctx, const char *path);</code></pre>
|
|
<p>Save the entire screen (backbuffer contents) to an image file (BMP for a .bmp path, PNG otherwise). Converts from native pixel format to RGB (15/16-bit channels are expanded to full intensity). Shows the busy cursor while writing and restores the caller's busy state afterwards.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
path Output file path</pre>
|
|
<p>Returns: 0 on success, -1 on failure.</p>
|
|
<h3>dvxWindowScreenshot</h3>
|
|
<pre><code>int32_t dvxWindowScreenshot(AppContextT *ctx, WindowT *win, const char *path);</code></pre>
|
|
<p>Save a window's content to an image file (BMP for a .bmp path, PNG otherwise). A repaint still pending from wgtInvalidate is run first, so the image shows the current state. Restores the caller's busy state afterwards.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
win Window
|
|
path Output file path</pre>
|
|
<p>Returns: 0 on success, -1 on failure.</p>
|
|
<h2>Clipboard</h2>
|
|
<h3>dvxClipboardClear</h3>
|
|
<pre><code>void dvxClipboardClear(void);</code></pre>
|
|
<p>Empty the clipboard; dvxClipboardGet then returns NULL.</p>
|
|
<h3>dvxClipboardCopy</h3>
|
|
<pre><code>void dvxClipboardCopy(const char *text, int32_t len);</code></pre>
|
|
<p>Copy text to the process-wide clipboard buffer. Simple static buffer (not inter-process). Copying nothing (NULL text or a len of 0 or less) empties the clipboard, like dvxClipboardClear.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
text Text to copy
|
|
len Length in bytes</pre>
|
|
<h3>dvxClipboardGet</h3>
|
|
<pre><code>const char *dvxClipboardGet(int32_t *outLen);</code></pre>
|
|
<p>Retrieve the current clipboard contents. Returns a pointer to the internal buffer (valid until the next dvxClipboardCopy), or NULL if empty.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
outLen Output: length of clipboard text</pre>
|
|
<p>Returns: Clipboard text, or NULL.</p>
|
|
<h2>Resource Loading</h2>
|
|
<h3>dvxResLoadIcon</h3>
|
|
<pre><code>uint8_t *dvxResLoadIcon(AppContextT *ctx, const char *dxePath, const char *resName, int32_t *outW, int32_t *outH, int32_t *outPitch);</code></pre>
|
|
<p>Load an icon/image resource from a DXE file and decode to native pixel format. Caller must free with dvxFreeImage().</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
dxePath Path to DXE file
|
|
resName Resource name within the DXE
|
|
outW, outH Output: image dimensions
|
|
outPitch Output: row pitch</pre>
|
|
<p>Returns: Pixel buffer, or NULL if not found.</p>
|
|
<h3>dvxResLoadText</h3>
|
|
<pre><code>bool dvxResLoadText(const char *dxePath, const char *resName, char *buf, int32_t bufSize);</code></pre>
|
|
<p>Load a text resource from a DXE file into a caller-provided buffer. Null-terminated and truncated to fit bufSize.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
dxePath Path to DXE file
|
|
resName Resource name
|
|
buf Output buffer
|
|
bufSize Buffer capacity</pre>
|
|
<p>Returns: true on success.</p>
|
|
<h3>dvxResLoadData</h3>
|
|
<pre><code>void *dvxResLoadData(const char *dxePath, const char *resName, uint32_t *outSize);</code></pre>
|
|
<p>Load a raw binary resource from a DXE file. Returns a malloc'd buffer that the caller must free.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
dxePath Path to DXE file
|
|
resName Resource name
|
|
outSize Output: data size in bytes</pre>
|
|
<p>Returns: Data buffer, or NULL if not found.</p>
|
|
<h2>Utilities</h2>
|
|
<h3>dvxTextHash</h3>
|
|
<pre><code>uint32_t dvxTextHash(const char *text);</code></pre>
|
|
<p>Compute a djb2-xor hash for cheap dirty detection. Compare at save time with the current hash to detect changes without a shadow copy. Not cryptographic.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
text Null-terminated string to hash</pre>
|
|
<p>Returns: 32-bit hash value.</p>
|
|
</div>
|
|
<div class="topic" id="api.driver">
|
|
<h1>dvxDrv.h -- Driver Modules</h1>
|
|
<p>One model for the hardware drivers of every device class (sound, joysticks, ...). A driver is a module of its own, a file <name>.drv in its class's folder under bin/libs (built like a library, but the loader does not load it). The library of the device class loads its drivers only when a program first needs that kind of device, probes them, keeps the ones it uses and unloads the rest, so drivers for hardware that is not there, or for a device class nobody uses, never stay in memory. Nothing lists drivers: adding one is dropping its file into the folder.</p>
|
|
<p>Each driver exports one descriptor under a name its class chooses (audioDriver, joyDriver, ...). The descriptor is the class's own struct and starts with a DvxDriverHeaderT, written with DVX_DRIVER_HDR (the classes wrap it in their own macro, such as AUDIO_DRIVER or JOY_DRIVER).</p>
|
|
<h2>Types</h2>
|
|
<pre> Type Members
|
|
---- -------
|
|
DvxDriverHeaderT abiVersion (DVX_ABI_VERSION the driver was built for), size (sizeof the whole descriptor), name (short name, lower case, no blanks; DVX.INI settings use it), priority (higher is tried first).
|
|
DvxDriverModT handle (NULL once kept out of the list or unloaded), desc (the descriptor), kind (index of the descriptor symbol found).
|
|
DvxDriverClassT symbols (descriptor symbol of each kind, as dlsym wants it: DVX_SYM("name")), minSizes (smallest descriptor accepted per kind), kinds, valid (the class's own check of a descriptor, such as its functions being present; NULL for none).</pre>
|
|
<p>DVX_DRIVER_HDR(type, name, priority) fills the header: { DVX_ABI_VERSION, sizeof(type), name, priority }.</p>
|
|
<h2>Functions</h2>
|
|
<h3>dvxDriversLoad</h3>
|
|
<pre><code>DvxDriverModT *dvxDriversLoad(const char *dir, const DvxDriverClassT *cls);</code></pre>
|
|
<p>Loads every *.drv in dir (relative to the DVX directory), looks each descriptor up and checks it: the ABI version, the size, a name, and the class's valid function. The valid ones come back as an stb_ds array sorted by priority (highest first, by name on a tie); a module that does not load, has no valid descriptor, or repeats a name a higher priority driver already has is logged to DVX.LOG and unloaded. NULL when there are none.</p>
|
|
<h3>dvxDriverKeep</h3>
|
|
<pre><code>DvxDriverModT dvxDriverKeep(DvxDriverModT *mods, int32_t index);</code></pre>
|
|
<p>Takes mods[index] out of the list (so dvxDriversFree leaves it loaded) and returns it; unload it with dvxDriverUnload when done.</p>
|
|
<h3>dvxDriversFree</h3>
|
|
<pre><code>void dvxDriversFree(DvxDriverModT *mods);</code></pre>
|
|
<p>Unloads every module still in the list and frees the array.</p>
|
|
<h3>dvxDriverUnload</h3>
|
|
<pre><code>void dvxDriverUnload(DvxDriverModT *mod);</code></pre>
|
|
<p>Unloads one module (nothing when it is not loaded) and clears its handle and descriptor.</p>
|
|
<h2>Writing a Device Class</h2>
|
|
<p>A library that has drivers puts them in drivers/<name>/ in its source folder; its Makefile sets OBJDIR and DRVDIR and writes $(foreach d,$(DVX_DRIVERS),$(eval $(call DVX_DRIVER_RULE,$d))) (common.mk), which builds each folder into $(DRVDIR)/<name>.drv. The library defines its descriptor struct (starting with DvxDriverHeaderT), a macro that writes it, and a DvxDriverClassT; it calls dvxDriversLoad when it first needs devices, dvxDriverKeep for the drivers it uses and dvxDriversFree for the rest. Driver code resolves the library's functions when it loads, since the libraries are loaded for everyone to use.</p>
|
|
</div>
|
|
<div class="topic" id="api.widget">
|
|
<h1>dvxWgt.h -- Widget System</h1>
|
|
<p>Retained-mode widget toolkit layered on the DVX window manager. Widgets form a tree (parent-child) rooted at a per-window VBox container. Layout is automatic: measure minimum sizes bottom-up, then allocate space top-down with flexbox-like weighted distribution. Widget types are registered dynamically at runtime via DXE plugins.</p>
|
|
<h2>WidgetT Structure</h2>
|
|
<p>Core widget structure. Generic across all widget types; type-specific data lives in the void *data pointer managed by each widget's DXE.</p>
|
|
<pre> Field Description
|
|
----- -----------
|
|
int32_t type Widget type ID (assigned by wgtRegisterClass)
|
|
const WidgetClassT *wclass Vtable for this widget type
|
|
char name[MAX_WIDGET_NAME] Widget name for lookup via wgtFind
|
|
parent, firstChild, lastChild, nextSibling Tree linkage pointers
|
|
WindowT *window Owning window
|
|
int32_t x, y, w, h Computed geometry (relative to content area)
|
|
int32_t calcMinW, calcMinH Computed minimum size (from layout pass)
|
|
int32_t minW, minH, maxW, maxH, prefW, prefH Size hints (tagged sizes)
|
|
int32_t weight Extra-space distribution weight (0=fixed, 100=normal)
|
|
WidgetAlignE align Main-axis alignment for children
|
|
int32_t spacing, padding Tagged sizes for child spacing and padding
|
|
uint32_t fgColor, bgColor Custom colors (0 = use scheme defaults)
|
|
bool visible, enabled, readOnly State flags
|
|
bool swallowTab Tab key goes to widget, not focus navigation
|
|
bool paintDirty Needs repaint (set by wgtInvalidatePaint)
|
|
bool childDirty A descendant needs repaint (for WCLASS_PAINTS_CHILDREN)
|
|
bool pressed WCLASS_PRESS_RELEASE: set while button is pressed
|
|
char accelKey Accelerator character (0 = none)
|
|
int32_t contentOffX, contentOffY Content offset for mouse event coordinates
|
|
void *userData, *data Application data and widget-private data
|
|
const char *tooltip Tooltip text (NULL = none)
|
|
MenuT *contextMenu Right-click menu (NULL = none)
|
|
uint32_t serial Unique per allocation, never reused while the process runs (tells "the same widget" from a new widget at a recycled address)
|
|
RectT paintClip Display clip when last painted (content-buffer coords, w = 0 until first paint); used by mouse dispatch inside clipping containers</pre>
|
|
<p>Universal Callbacks:</p>
|
|
<pre> Callback Description
|
|
-------- -----------
|
|
onClick(WidgetT *w) Widget clicked
|
|
onDblClick(WidgetT *w) Widget double-clicked
|
|
onChange(WidgetT *w) Value changed
|
|
onFocus(WidgetT *w) Widget gained focus
|
|
onBlur(WidgetT *w) Widget lost focus
|
|
onKeyPress(WidgetT *w, int32_t keyAscii) ASCII key press
|
|
onKeyDown(WidgetT *w, int32_t keyCode, int32_t shift) Key down
|
|
onKeyUp(WidgetT *w, int32_t keyCode, int32_t shift) Key up
|
|
onMouseDown(WidgetT *w, int32_t button, int32_t x, int32_t y) Mouse button pressed
|
|
onMouseUp(WidgetT *w, int32_t button, int32_t x, int32_t y) Mouse button released
|
|
onMouseMove(WidgetT *w, int32_t button, int32_t x, int32_t y) Mouse moved
|
|
onScroll(WidgetT *w, int32_t delta) Mouse wheel
|
|
onValidate(WidgetT *w) Return false to cancel a write</pre>
|
|
<h2>Size Specification Macros</h2>
|
|
<pre> Macro Description
|
|
----- -----------
|
|
wgtPixels(v) Size in pixels
|
|
wgtChars(v) Size in character widths (multiplied by charWidth at layout)
|
|
wgtPercent(v) Size as percentage of parent dimension</pre>
|
|
<h2>Widget Class Flags</h2>
|
|
<pre> Flag Description
|
|
---- -----------
|
|
WCLASS_FOCUSABLE Can receive keyboard focus (Tab navigation)
|
|
WCLASS_HORIZ_CONTAINER Lays out children horizontally (vs. vertical)
|
|
WCLASS_PAINTS_CHILDREN Widget handles child rendering itself
|
|
WCLASS_NO_HIT_RECURSE Class hit testing stops here (the container's ON_MOUSE gets the press); universal callbacks still reach visible children of a non-focusable container
|
|
WCLASS_FOCUS_FORWARD Accel hit forwards focus to next focusable sibling
|
|
WCLASS_HAS_POPUP Has dropdown popup overlay
|
|
WCLASS_SCROLLABLE Mouse wheel sends Up/Down arrow keys
|
|
WCLASS_SCROLL_CONTAINER Scroll container (ScrollPane)
|
|
WCLASS_NEEDS_POLL Needs periodic polling
|
|
WCLASS_SWALLOWS_TAB Tab key goes to widget, not focus navigation
|
|
WCLASS_RELAYOUT_ON_SCROLL Full relayout on scrollbar drag
|
|
WCLASS_PRESS_RELEASE Click = press + release (Button, ImageButton)
|
|
WCLASS_ACCEL_WHEN_HIDDEN Accelerator matching works even when invisible</pre>
|
|
<h2>Window Integration</h2>
|
|
<h3>wgtInitWindow</h3>
|
|
<pre><code>WidgetT *wgtInitWindow(AppContextT *ctx, WindowT *win);</code></pre>
|
|
<p>Initialize the widget system for a window. Creates a root VBox container that fills the content area, and installs callback handlers (onPaint, onMouse, onKey, onResize) for widget-based event dispatch. The root widget's userData is set to the AppContextT pointer (the window's userData is left for the application). Calling it again on a window that already has a widget root returns that root.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
win Window to initialize</pre>
|
|
<p>Returns: Root VBox widget (add children to this).</p>
|
|
<h2>Widget Operations</h2>
|
|
<h3>wgtGetContext</h3>
|
|
<pre><code>AppContextT *wgtGetContext(const WidgetT *w);</code></pre>
|
|
<p>Retrieve the AppContextT stored in the root widget's userData: in O(1) through the widget's window when it is attached, otherwise by walking up to the root of its tree. Lets any widget access the full application context.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
w Any widget in the tree</pre>
|
|
<p>Returns: Pointer to the AppContextT.</p>
|
|
<h3>wgtInvalidate</h3>
|
|
<pre><code>void wgtInvalidate(WidgetT *w);</code></pre>
|
|
<p>Re-measure and re-lay out the widget's whole window immediately (adding or removing window scrollbars as needed, so widget geometry can be read right away) and schedule a full repaint. The paint happens once per frame in the deferred paint flush of dvxUpdate, so several invalidations in one event handler cost one paint. Use after structural changes (adding/removing children, text changes that affect size).</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
w Widget to invalidate</pre>
|
|
<h3>wgtInvalidatePaint</h3>
|
|
<pre><code>void wgtInvalidatePaint(WidgetT *w);</code></pre>
|
|
<p>Mark a widget as needing repaint only, without re-layout. Use for visual-only changes (checkbox toggle, selection highlight, cursor blink).</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
w Widget to repaint</pre>
|
|
<h3>wgtSetText</h3>
|
|
<pre><code>void wgtSetText(WidgetT *w, const char *text);</code></pre>
|
|
<p>Set widget text content (dispatches to the widget class's SET_TEXT handler).</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
w Widget
|
|
text New text</pre>
|
|
<h3>wgtGetText</h3>
|
|
<pre><code>const char *wgtGetText(const WidgetT *w);</code></pre>
|
|
<p>Get the widget's current text content.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
w Widget</pre>
|
|
<p>Returns: Text string (empty string if no handler).</p>
|
|
<h3>wgtSetEnabled</h3>
|
|
<pre><code>void wgtSetEnabled(WidgetT *w, bool enabled);</code></pre>
|
|
<p>Enable or disable a widget. Disabled widgets are grayed out and do not receive input; disabling a container also blocks mouse and keyboard input to every widget inside it (see widgetIsEnabledChain). Setting the current value is a no-op.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
w Widget
|
|
enabled true = enabled, false = disabled</pre>
|
|
<h3>wgtSetReadOnly</h3>
|
|
<pre><code>void wgtSetReadOnly(WidgetT *w, bool readOnly);</code></pre>
|
|
<p>Set read-only mode. Allows scrolling and selection but blocks editing.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
w Widget
|
|
readOnly true = read-only</pre>
|
|
<h3>wgtSetFocused</h3>
|
|
<pre><code>void wgtSetFocused(WidgetT *w);</code></pre>
|
|
<p>Set keyboard focus to a widget.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
w Widget to focus</pre>
|
|
<h3>wgtGetFocused</h3>
|
|
<pre><code>WidgetT *wgtGetFocused(void);</code></pre>
|
|
<p>Get the currently focused widget.</p>
|
|
<p>Returns: Focused widget, or NULL.</p>
|
|
<h3>wgtSetVisible</h3>
|
|
<pre><code>void wgtSetVisible(WidgetT *w, bool visible);</code></pre>
|
|
<p>Show or hide a widget. Hiding a subtree that holds keyboard focus drops the focus. Setting the current value is a no-op.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
w Widget
|
|
visible true = visible, false = hidden</pre>
|
|
<h3>wgtSetName</h3>
|
|
<pre><code>void wgtSetName(WidgetT *w, const char *name);</code></pre>
|
|
<p>Set a widget's name for lookup via wgtFind().</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
w Widget
|
|
name Name string (max MAX_WIDGET_NAME chars)</pre>
|
|
<h3>wgtFind</h3>
|
|
<pre><code>WidgetT *wgtFind(WidgetT *root, const char *name);</code></pre>
|
|
<p>Find a widget by name. Searches the subtree rooted at root.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
root Root of subtree to search
|
|
name Widget name to find</pre>
|
|
<p>Returns: Matching widget, or NULL.</p>
|
|
<h3>wgtDestroy</h3>
|
|
<pre><code>void wgtDestroy(WidgetT *w);</code></pre>
|
|
<p>Destroy a widget and all its children. Removes from parent's child list.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
w Widget to destroy</pre>
|
|
<h3>wgtSetTooltip</h3>
|
|
<pre><code>void wgtSetTooltip(WidgetT *w, const char *text);</code></pre>
|
|
<p>Set tooltip text for a widget. Pass NULL to remove. Caller owns the string and it must outlive the widget.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
w Widget
|
|
text Tooltip text, or NULL</pre>
|
|
<h3>widgetOnResize</h3>
|
|
<pre><code>void widgetOnResize(WindowT *win, int32_t newW, int32_t newH);</code></pre>
|
|
<p>Default window resize handler installed by wgtInitWindow(). Re-evaluates scrollbars and relayouts the widget tree. Call from custom onResize handlers to chain to the widget system.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
win Window being resized
|
|
newW, newH New content dimensions</pre>
|
|
<h2>Layout</h2>
|
|
<h3>wgtResolveSize</h3>
|
|
<pre><code>int32_t wgtResolveSize(int32_t taggedSize, int32_t parentSize, int32_t charWidth);</code></pre>
|
|
<p>Decode a tagged size value (WGT_SIZE_PIXELS/CHARS/PERCENT) into a concrete pixel count. Returns 0 for a raw 0 input (meaning "auto").</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
taggedSize Tagged size value
|
|
parentSize Parent dimension (for PERCENT mode)
|
|
charWidth Font character width (for CHARS mode)</pre>
|
|
<p>Returns: Size in pixels.</p>
|
|
<h3>wgtLayout</h3>
|
|
<pre><code>void wgtLayout(WidgetT *root, int32_t availW, int32_t availH, const BitmapFontT *font);</code></pre>
|
|
<p>Execute the full two-pass layout algorithm on a tree at an explicit size. Pass 1 (bottom-up): compute minimum sizes. Pass 2 (top-down): allocate space with weighted distribution; a weighted child capped by maxW/maxH stops at its cap and the space it cannot take goes to the other weighted children. Window-managed trees are laid out by wgtInvalidate and the paint path; this is for laying out a tree on demand.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
root Root widget
|
|
availW/H Available space
|
|
font Bitmap font (for character-based sizing)</pre>
|
|
<h3>wgtPaint</h3>
|
|
<pre><code>void wgtPaint(WidgetT *root, DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors, bool fullRepaint);</code></pre>
|
|
<p>Paint the widget tree by depth-first traversal (every widget, or only dirty ones) using the display's current clip rect. Popup overlays are not painted here; the window paint handler paints them afterwards so they are on top.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
root Root widget
|
|
d Display context
|
|
ops Blit operations vtable
|
|
font Bitmap font
|
|
colors Color scheme
|
|
fullRepaint true = repaint every widget; false = only paintDirty widgets</pre>
|
|
<h2>Debug</h2>
|
|
<h3>wgtSetDebugLayout</h3>
|
|
<pre><code>void wgtSetDebugLayout(AppContextT *ctx, bool enabled);</code></pre>
|
|
<p>Draw colored borders around layout containers for debugging.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
enabled true = draw debug borders</pre>
|
|
<h2>Dynamic Widget Registration</h2>
|
|
<h3>wgtRegisterClass</h3>
|
|
<pre><code>int32_t wgtRegisterClass(const WidgetClassT *wclass);</code></pre>
|
|
<p>Register a new widget class at runtime. Stores the class in the first free widgetClassTable slot (or appends one). The WidgetClassT must remain valid until wgtUnregisterClass is called for it (typically static const in a DXE). A class whose version is not WGT_CLASS_VERSION is rejected (logged, returns -1).</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
wclass Widget class vtable to register</pre>
|
|
<p>Returns: Assigned type ID, or -1 if wclass is NULL or its version does not match.</p>
|
|
<h3>wgtUnregisterClass</h3>
|
|
<pre><code>void wgtUnregisterClass(int32_t classId);</code></pre>
|
|
<p>Release a class registered with wgtRegisterClass, typically from an app's shutdown before its module is unloaded. Every widget of the class must already be destroyed. The slot is cleared: creating a widget with the freed ID fails (returns NULL), and a later wgtRegisterClass may reuse the ID. Out-of-range or already-free IDs are ignored.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
classId Type ID returned by wgtRegisterClass</pre>
|
|
<h3>wgtUnregisterAppClasses</h3>
|
|
<pre><code>void wgtUnregisterAppClasses(int32_t appId);</code></pre>
|
|
<p>Release every class, API (wgtRegisterApi) and interface (wgtRegisterIface) registered while appId was the current app (AppContextT.currentAppId at registration time), since all of them point into the app's module. The shell calls it whenever it unloads an app (normal exit or End Task), once the app's windows and widgets are gone. appId 0 (boot-time widget modules) is ignored.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
appId App whose classes are released</pre>
|
|
<h3>wgtRegisterApi</h3>
|
|
<pre><code>void wgtRegisterApi(const char *name, const void *api);</code></pre>
|
|
<p>Register a widget API struct under a name. Each widget DXE registers its API during initialization. Callers retrieve it via wgtGetApi() and cast to the widget-specific type.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
name Widget type name (e.g. "button", "listbox")
|
|
api Pointer to the widget's API struct</pre>
|
|
<p>Every API struct starts with a WgtApiHeaderT member named header, which the provider fills with WGT_API_HEADER(its API type):</p>
|
|
<pre><code>typedef struct {
|
|
WgtApiHeaderT header;
|
|
WidgetT *(*create)(WidgetT *parent, const char *text);
|
|
} ButtonApiT;
|
|
|
|
static const ButtonApiT sApi = {
|
|
.header = WGT_API_HEADER(ButtonApiT),
|
|
.create = wgtButton
|
|
};</code></pre>
|
|
<pre> Type / Macro Description
|
|
------------ -----------
|
|
WgtApiHeaderT { uint32_t abiVersion; uint32_t size; }: the DVX_ABI_VERSION the provider was built with and the sizeof its API struct.
|
|
WGT_API_HEADER(apiType) Initializer for the header member: { DVX_ABI_VERSION, sizeof(apiType) }.
|
|
WgtApiPrefixT { WgtApiHeaderT header; WgtApiFnT create; }: the part every API struct shares. Generic callers that do not know the widget's own API type use it.</pre>
|
|
<blockquote><strong>Warning:</strong> The create function must be the first field after the header. BASIC's form runtime and the designer instantiate widgets through WgtApiPrefixT.create (see createWidgetByIface), cast to the signature WgtIfaceT.createSig names, so whichever field comes first is what gets invoked. If the struct has multiple constructor variants, the one matching createSig must come first; put helper constructors, index accessors, and other entry points after it.</blockquote>
|
|
<h3>wgtGetApi</h3>
|
|
<pre><code>const void *wgtGetApi(const char *name, uint32_t minSize);</code></pre>
|
|
<p>Retrieve a registered widget API struct by name, checked against the caller's view of it. A struct built for another DVX_ABI_VERSION, or smaller than minSize (an older, shorter table), is refused and the refusal logged.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
name Widget type name
|
|
minSize sizeof the API type the caller will use: sizeof(ButtonApiT) for a typed accessor, sizeof(WgtApiPrefixT) when only create is called</pre>
|
|
<p>Returns: Pointer to the API struct, or NULL if not found or refused. Per-widget headers wrap the call in a typed accessor (dvxButtonApi() and friends).</p>
|
|
<h2>Widget Interface Descriptors</h2>
|
|
<h3>wgtRegisterIface</h3>
|
|
<pre><code>void wgtRegisterIface(const char *name, const WgtIfaceT *iface);</code></pre>
|
|
<p>Register an interface descriptor for a widget type. Used by the BASIC form runtime and IDE for generic property/method dispatch.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
name Widget type name
|
|
iface Interface descriptor</pre>
|
|
<h3>wgtGetIface</h3>
|
|
<pre><code>const WgtIfaceT *wgtGetIface(const char *name);</code></pre>
|
|
<p>Retrieve an interface descriptor by widget type name.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
name Widget type name</pre>
|
|
<p>Returns: Interface descriptor, or NULL.</p>
|
|
<h3>wgtFindByBasName</h3>
|
|
<pre><code>const char *wgtFindByBasName(const char *basName);</code></pre>
|
|
<p>Find a widget type name by its VB-style name (e.g. "CommandButton" -> "button"). Case-insensitive search.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
basName VB-style widget name</pre>
|
|
<p>Returns: Internal type name, or NULL.</p>
|
|
<h3>wgtIfaceCount</h3>
|
|
<pre><code>int32_t wgtIfaceCount(void);</code></pre>
|
|
<p>Return the number of registered widget interfaces.</p>
|
|
<p>Returns: Count of registered interfaces.</p>
|
|
<h3>wgtIfaceAt</h3>
|
|
<pre><code>const WgtIfaceT *wgtIfaceAt(int32_t idx, const char **outName);</code></pre>
|
|
<p>Get a registered widget interface by index.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
idx Index (0-based)
|
|
outName Output: type name</pre>
|
|
<p>Returns: Interface descriptor.</p>
|
|
<h3>wgtIfaceGetPath</h3>
|
|
<pre><code>const char *wgtIfaceGetPath(const char *name);</code></pre>
|
|
<p>Get the .wgt DXE file path for a registered widget.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
name Widget type name</pre>
|
|
<p>Returns: File path string.</p>
|
|
<h3>wgtIfaceSetPath</h3>
|
|
<pre><code>void wgtIfaceSetPath(const char *name, const char *path);</code></pre>
|
|
<p>Set the .wgt DXE file path for a registered widget (called by the loader).</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
name Widget type name
|
|
path DXE file path</pre>
|
|
<h3>wgtIfaceGetPathIndex</h3>
|
|
<pre><code>int32_t wgtIfaceGetPathIndex(const char *name);</code></pre>
|
|
<p>Get the 1-based index of this widget within its .wgt file. Used to construct suffixed resource names (e.g. "name-2", "icon16-2").</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
name Widget type name</pre>
|
|
<p>Returns: 1-based index within the DXE file.</p>
|
|
<h2>Typed Dispatch Helpers</h2>
|
|
<p>The following inline functions provide type-safe dispatch through the WidgetClassT handler table. Each checks for a non-NULL handler before calling.</p>
|
|
<pre> Function Method ID Description
|
|
-------- --------- -----------
|
|
wclsHas(w, methodId) -- Check if handler exists
|
|
wclsPaint(w, d, ops, font, colors) WGT_METHOD_PAINT Paint the widget
|
|
wclsPaintOverlay(w, d, ops, font, colors) WGT_METHOD_PAINT_OVERLAY Paint overlay (popups)
|
|
wclsCalcMinSize(w, font) WGT_METHOD_CALC_MIN_SIZE Compute minimum size
|
|
wclsLayout(w, font) WGT_METHOD_LAYOUT Layout children
|
|
wclsGetLayoutMetrics(w, font, ...) WGT_METHOD_GET_LAYOUT_METRICS Get pad, gap, extraTop, borderW
|
|
wclsOnMouse(w, root, vx, vy) WGT_METHOD_ON_MOUSE Handle mouse event
|
|
wclsOnKey(w, key, mod) WGT_METHOD_ON_KEY Handle key event
|
|
wclsOnAccelActivate(w, root) WGT_METHOD_ON_ACCEL_ACTIVATE Handle accelerator
|
|
wclsDestroy(w) WGT_METHOD_DESTROY Destroy widget data
|
|
wclsOnChildChanged(parent, child) WGT_METHOD_ON_CHILD_CHANGED Notify parent of change
|
|
wclsGetText(w) WGT_METHOD_GET_TEXT Get widget text
|
|
wclsSetText(w, text) WGT_METHOD_SET_TEXT Set widget text
|
|
wclsClearSelection(w) WGT_METHOD_CLEAR_SELECTION Clear text selection
|
|
wclsClosePopup(w) WGT_METHOD_CLOSE_POPUP Close dropdown popup
|
|
wclsGetPopupRect(w, font, ...) WGT_METHOD_GET_POPUP_RECT Get popup screen rect
|
|
wclsOnDragUpdate(w, root, x, y) WGT_METHOD_ON_DRAG_UPDATE Update during drag
|
|
wclsOnDragEnd(w, root, x, y) WGT_METHOD_ON_DRAG_END End drag operation
|
|
wclsGetCursorShape(w, vx, vy) WGT_METHOD_GET_CURSOR_SHAPE Get cursor for position
|
|
wclsPoll(w, win) WGT_METHOD_POLL Periodic polling
|
|
wclsQuickRepaint(w, outY, outH) WGT_METHOD_QUICK_REPAINT Fast partial repaint
|
|
wclsScrollChildIntoView(parent, child) WGT_METHOD_SCROLL_CHILD_INTO_VIEW Scroll child visible
|
|
wclsOnBlur(w) WGT_METHOD_ON_BLUR Widget lost keyboard focus
|
|
wclsOnWheel(w, delta) WGT_METHOD_ON_WHEEL Mouse wheel; true = consumed</pre>
|
|
<p>The mouse wheel is offered first to an ON_WHEEL handler on the focused widget, then on each ancestor, with delta in notches (positive = down, the user's reverse-wheel setting already applied; the lines-per-notch preference is AppContextT.wheelStep). Returning true consumes the event. Otherwise a WCLASS_SCROLLABLE focused widget (or a scroll-container ancestor) receives Up/Down arrow keys, and failing that the window scrollbar moves. The widget's onScroll callback fires in every case.</p>
|
|
<h2>Method IDs</h2>
|
|
<p>Fixed constants used to index into WidgetClassT.handlers[]. Method IDs are stable ABI -- new methods are appended at the next sequential ID, never reordered or reused.</p>
|
|
<pre> ID Symbol Signature
|
|
-- ------ ---------
|
|
0 WGT_METHOD_PAINT void (w, d, ops, font, colors)
|
|
1 WGT_METHOD_PAINT_OVERLAY void (w, d, ops, font, colors)
|
|
2 WGT_METHOD_CALC_MIN_SIZE void (w, font)
|
|
3 WGT_METHOD_LAYOUT void (w, font)
|
|
4 WGT_METHOD_GET_LAYOUT_METRICS void (w, font, pad, gap, extraTop, borderW)
|
|
5 WGT_METHOD_ON_MOUSE void (w, root, vx, vy)
|
|
6 WGT_METHOD_ON_KEY void (w, key, mod)
|
|
7 WGT_METHOD_ON_ACCEL_ACTIVATE void (w, root)
|
|
8 WGT_METHOD_DESTROY void (w)
|
|
9 WGT_METHOD_ON_CHILD_CHANGED void (parent, child)
|
|
10 WGT_METHOD_GET_TEXT const char *(w)
|
|
11 WGT_METHOD_SET_TEXT void (w, text)
|
|
12 WGT_METHOD_CLEAR_SELECTION bool (w)
|
|
13 WGT_METHOD_CLOSE_POPUP void (w)
|
|
14 WGT_METHOD_GET_POPUP_RECT void (w, font, contentH, popX, popY, popW, popH)
|
|
15 WGT_METHOD_ON_DRAG_UPDATE void (w, root, x, y)
|
|
16 WGT_METHOD_ON_DRAG_END void (w, root, x, y)
|
|
17 WGT_METHOD_GET_CURSOR_SHAPE int32_t (w, vx, vy)
|
|
18 WGT_METHOD_POLL void (w, win)
|
|
19 WGT_METHOD_QUICK_REPAINT int32_t (w, outY, outH)
|
|
20 WGT_METHOD_SCROLL_CHILD_INTO_VIEW void (parent, child)
|
|
21 WGT_METHOD_ON_BLUR void (w)
|
|
22 WGT_METHOD_ON_WHEEL bool (w, delta)
|
|
-- WGT_METHOD_MAX = 32 (room for future methods)</pre>
|
|
<p>WGT_CLASS_VERSION is DVX_ABI_VERSION (see Binary Interface Version). The framework rejects WidgetClassT structs whose version does not match.</p>
|
|
<h2>Widget Class Dispatch Table</h2>
|
|
<h3>WidgetClassT</h3>
|
|
<pre><code>typedef struct WidgetClassT {
|
|
uint32_t version;
|
|
uint32_t flags;
|
|
void *handlers[WGT_METHOD_MAX];
|
|
} WidgetClassT;</code></pre>
|
|
<pre> Field Description
|
|
----- -----------
|
|
version Must equal WGT_CLASS_VERSION at registration
|
|
flags Bitmask of WCLASS_xxx flags
|
|
handlers Array of 32 function pointers indexed by WGT_METHOD_xxx</pre>
|
|
<h2>Size Encoding Constants</h2>
|
|
<pre> Define Value Description
|
|
------ ----- -----------
|
|
WGT_SIZE_TYPE_MASK 0xC0000000 Top 2 bits: unit type selector
|
|
WGT_SIZE_VAL_MASK 0x3FFFFFFF Bottom 30 bits: numeric value
|
|
WGT_SIZE_PIXELS 0x00000000 Unit tag: pixels
|
|
WGT_SIZE_CHARS 0x40000000 Unit tag: character widths
|
|
WGT_SIZE_PERCENT 0x80000000 Unit tag: percentage of parent
|
|
WGT_WEIGHT_FILL 100 Default "flexible" weight value</pre>
|
|
<h2>Widget Interface Descriptors</h2>
|
|
<h3>WgtPropDescT</h3>
|
|
<p>Describes a BASIC-exposed property on a widget type. Used by the form runtime and IDE for generic get/set dispatch.</p>
|
|
<pre> Field Description
|
|
----- -----------
|
|
name BASIC property name (e.g. "Caption", "Value")
|
|
type Data type: WGT_IFACE_STRING, WGT_IFACE_INT, WGT_IFACE_BOOL, WGT_IFACE_FLOAT, or WGT_IFACE_ENUM
|
|
getFn Getter function pointer (NULL if write-only)
|
|
setFn Setter function pointer (NULL if read-only)
|
|
enumNames For WGT_IFACE_ENUM: NULL-terminated array of value names</pre>
|
|
<h3>WgtMethodDescT</h3>
|
|
<p>Describes an invokable method on a widget type.</p>
|
|
<pre> Field Description
|
|
----- -----------
|
|
name BASIC method name (e.g. "Clear", "SetFocus")
|
|
sig Calling convention ID (WGT_SIG_xxx)
|
|
fn Function pointer</pre>
|
|
<p>Calling convention IDs (method sig field). IDs are stable ABI; new ones are appended.</p>
|
|
<pre> Define ID Handler signature
|
|
------ -- -----------------
|
|
WGT_SIG_VOID 0 void fn(WidgetT *)
|
|
WGT_SIG_INT 1 void fn(WidgetT *, int32_t)
|
|
WGT_SIG_BOOL 2 void fn(WidgetT *, bool)
|
|
WGT_SIG_STR 3 void fn(WidgetT *, const char *)
|
|
WGT_SIG_INT_INT 4 void fn(WidgetT *, int32_t, int32_t)
|
|
WGT_SIG_INT_BOOL 5 void fn(WidgetT *, int32_t, bool)
|
|
WGT_SIG_RET_INT 6 int32_t fn(const WidgetT *)
|
|
WGT_SIG_RET_BOOL 7 bool fn(const WidgetT *)
|
|
WGT_SIG_RET_BOOL_INT 8 bool fn(const WidgetT *, int32_t)
|
|
WGT_SIG_INT3 9 void fn(WidgetT *, int32_t, int32_t, int32_t)
|
|
WGT_SIG_INT4 10 void fn(WidgetT *, int32_t, int32_t, int32_t, int32_t)
|
|
WGT_SIG_RET_INT_INT_INT 11 int32_t fn(const WidgetT *, int32_t, int32_t)
|
|
WGT_SIG_INT_INT_STR 12 void fn(WidgetT *, int32_t, int32_t, const char *)
|
|
WGT_SIG_INT5 13 void fn(WidgetT *, int32_t, int32_t, int32_t, int32_t, int32_t)
|
|
WGT_SIG_RET_STR_INT 14 const char *fn(const WidgetT *, int32_t)
|
|
WGT_SIG_RET_STR_INT_INT 15 const char *fn(const WidgetT *, int32_t, int32_t)
|
|
WGT_SIG_STR_BOOL_BOOL 16 bool fn(WidgetT *, const char *, bool, bool)
|
|
WGT_SIG_STR_STR_BOOL 17 int32_t fn(WidgetT *, const char *, const char *, bool)
|
|
WGT_SIG_RET_STR 18 const char *fn(const WidgetT *)
|
|
WGT_SIG_STR_INT 19 void fn(WidgetT *, const char *, int32_t)
|
|
WGT_SIG_INT_STR 20 void fn(WidgetT *, int32_t, const char *)
|
|
WGT_SIG_STR_STR 21 void fn(WidgetT *, const char *, const char *)
|
|
WGT_SIG_STR_BOOL 22 void fn(WidgetT *, const char *, bool)
|
|
WGT_SIG_RET_INT_STR 23 int32_t fn(WidgetT *, const char *) -- status-returning string method (e.g. Load/Save)</pre>
|
|
<h3>WgtEventDescT</h3>
|
|
<p>Describes an event beyond the standard common set (Click, DblClick, Change, GotFocus, LostFocus).</p>
|
|
<pre> Field Description
|
|
----- -----------
|
|
name Event name (e.g. "Change", "Timer")</pre>
|
|
<h3>WgtIfaceT</h3>
|
|
<p>Top-level interface descriptor registered by each widget DXE.</p>
|
|
<pre> Field Description
|
|
----- -----------
|
|
basName VB-style name (e.g. "CommandButton"), or NULL
|
|
props Array of WgtPropDescT
|
|
propCount Length of props array
|
|
methods Array of WgtMethodDescT
|
|
methodCount Length of methods array
|
|
events Array of WgtEventDescT (extras beyond common set)
|
|
eventCount Length of events array
|
|
createSig How to call the widget's create() function (WgtCreateSigE)
|
|
createArgs Default numeric args for design-time instantiation
|
|
isContainer true = widget can hold child widgets
|
|
defaultEvent Default event name for form designer
|
|
namePrefix Auto-name prefix (NULL = derive from basName)</pre>
|
|
<h2>Property Data Type Constants</h2>
|
|
<pre> Define Value Description
|
|
------ ----- -----------
|
|
WGT_IFACE_STRING 0 const char *
|
|
WGT_IFACE_INT 1 int32_t
|
|
WGT_IFACE_BOOL 2 bool
|
|
WGT_IFACE_FLOAT 3 float
|
|
WGT_IFACE_ENUM 4 int32_t with named values</pre>
|
|
<h2>Create Function Signature Constants</h2>
|
|
<pre> Define Value Form
|
|
------ ----- ----
|
|
WGT_CREATE_PARENT 0 fn(parent)
|
|
WGT_CREATE_PARENT_TEXT 1 fn(parent, const char *text)
|
|
WGT_CREATE_PARENT_INT 2 fn(parent, int32_t)
|
|
WGT_CREATE_PARENT_INT_INT 3 fn(parent, int32_t, int32_t)
|
|
WGT_CREATE_PARENT_INT_INT_INT 4 fn(parent, int32_t, int32_t, int32_t)
|
|
WGT_CREATE_PARENT_INT_BOOL 5 fn(parent, int32_t, bool)
|
|
WGT_CREATE_PARENT_BOOL 6 fn(parent, bool)
|
|
WGT_CREATE_PARENT_DATA 7 fn(parent, data, w, h, pitch) -- not auto-creatable</pre>
|
|
<p>WGT_MAX_CREATE_ARGS is 3.</p>
|
|
</div>
|
|
<div class="topic" id="api.dialog">
|
|
<h1>dvxDlg.h -- Modal Dialogs</h1>
|
|
<p>Pre-built modal dialog boxes that block the caller and run their own event loop via dvxUpdate() until the user dismisses them. Modal dialogs set ctx->modalWindow to prevent input from reaching other windows for the dialog's lifetime.</p>
|
|
<h2>Message Box Flags</h2>
|
|
<p>Button configurations (low nibble):</p>
|
|
<pre> Define Value Description
|
|
------ ----- -----------
|
|
MB_OK 0x0000 OK button only (default)
|
|
MB_OKCANCEL 0x0001 OK and Cancel
|
|
MB_YESNO 0x0002 Yes and No
|
|
MB_YESNOCANCEL 0x0003 Yes, No, and Cancel
|
|
MB_RETRYCANCEL 0x0004 Retry and Cancel</pre>
|
|
<p>Icon types (high nibble, OR with button flags):</p>
|
|
<pre> Define Value Description
|
|
------ ----- -----------
|
|
MB_ICONINFO 0x0010 Information icon
|
|
MB_ICONWARNING 0x0020 Warning icon
|
|
MB_ICONERROR 0x0030 Error icon
|
|
MB_ICONQUESTION 0x0040 Question mark icon</pre>
|
|
<p>Return values:</p>
|
|
<pre> Define Value Button pressed
|
|
------ ----- --------------
|
|
ID_OK 1 OK
|
|
ID_CANCEL 2 Cancel or dialog closed
|
|
ID_YES 3 Yes
|
|
ID_NO 4 No
|
|
ID_RETRY 5 Retry</pre>
|
|
<h2>dvxMessageBox</h2>
|
|
<pre><code>int32_t dvxMessageBox(AppContextT *ctx, const char *title, const char *message, int32_t flags);</code></pre>
|
|
<p>Display a modal message box. Blocks the caller by running dvxUpdate() in a loop until a button is pressed or the dialog is closed. The window is automatically destroyed on return.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
title Window title (NULL falls back to a generic default)
|
|
message Body text
|
|
flags MB_xxx button + icon flags, OR'd together</pre>
|
|
<p>Returns: ID_xxx of the button that was pressed.</p>
|
|
<pre><code>// Example: yes/no confirmation
|
|
int32_t r = dvxMessageBox(ctx, "Save", "Save changes before closing?", MB_YESNOCANCEL | MB_ICONQUESTION);
|
|
if (r == ID_YES) { saveFile(); }</code></pre>
|
|
<h2>dvxButtonBox</h2>
|
|
<pre><code>int32_t dvxButtonBox(AppContextT *ctx, const char *title, const char *message, int32_t iconFlags, const char * const *labels, int32_t count);</code></pre>
|
|
<p>A message box with the caller's own buttons, for a question the MB_xxx sets do not cover (for example Yes / No / Yes to All / Cancel). It looks and behaves like dvxMessageBox: Enter presses the first button, and Escape or closing the box chooses the last one, so make the last button the one that changes nothing.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
title Window title
|
|
message Body text (word-wrapped; may contain '\n')
|
|
iconFlags One MB_ICONxxx value, or 0 for none
|
|
labels Button labels; '&' marks the accelerator
|
|
count Number of buttons, 1 to DVX_BUTTON_BOX_MAX (6)</pre>
|
|
<p>Returns: the index in labels of the button chosen, or -1 for bad arguments.</p>
|
|
<pre><code>static const char *const labels[] = { "&Retry", "&Skip", "&Cancel" };
|
|
int32_t pick = dvxButtonBox(ctx, "Copy", "The disk is full.", MB_ICONERROR, labels, 3);</code></pre>
|
|
<h2>dvxErrorBox</h2>
|
|
<pre><code>int32_t dvxErrorBox(AppContextT *ctx, const char *title, const char *message);</code></pre>
|
|
<p>Convenience wrapper for MB_OK | MB_ICONERROR. Title defaults to "Error" when NULL.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
title Dialog title, or NULL for "Error"
|
|
message Error message</pre>
|
|
<p>Returns: ID_OK.</p>
|
|
<h2>dvxInfoBox</h2>
|
|
<pre><code>int32_t dvxInfoBox(AppContextT *ctx, const char *title, const char *message);</code></pre>
|
|
<p>Convenience wrapper for MB_OK | MB_ICONINFO.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
title Dialog title
|
|
message Informational message</pre>
|
|
<p>Returns: ID_OK.</p>
|
|
<h2>File Dialog</h2>
|
|
<h3>FileFilterT</h3>
|
|
<p>Describes a filter entry in the file dialog's format dropdown. The glob pattern is extracted from inside the parentheses. Patterns are case-insensitive, may be a semicolon-separated list, and support '*' and '?' anywhere ("readme*", "a*.txt", "*.*" matches every file); blank entries in a list are ignored. A label without parentheses is itself the pattern when it contains a wildcard; otherwise it matches every file.</p>
|
|
<pre> Field Description
|
|
----- -----------
|
|
label e.g. "Text Files (*.txt)" or "Images (*.bmp;*.png;*.jpg;*.gif)"</pre>
|
|
<h3>File Dialog Flags</h3>
|
|
<pre> Define Value Description
|
|
------ ----- -----------
|
|
FD_OPEN 0x0000 File open dialog (default)
|
|
FD_SAVE 0x0001 File save dialog (with overwrite prompt)</pre>
|
|
<h3>dvxFileDialog</h3>
|
|
<pre><code>bool dvxFileDialog(AppContextT *ctx, const char *title, int32_t flags, const char *initialDir, const FileFilterT *filters, int32_t filterCount, char *outPath, int32_t outPathSize);</code></pre>
|
|
<p>Display a modal file open/save dialog. Shows a directory listing with parent/drive-letter navigation, a filename input, and an optional filter dropdown. The path box always shows the current folder. Typing "D:" or "D:folder" in the path box changes drive/folder; the file name box also accepts a relative or absolute path ("sub\x.txt", "C:\X\A.TXT") and navigates to its folder. A folder that does not exist shows an "Invalid Path" error. The dialog closes at once if the app that owns it is ended.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
title Window title
|
|
flags FD_OPEN or FD_SAVE
|
|
initialDir Starting directory (NULL = current working directory)
|
|
filters Array of FileFilterT, or NULL
|
|
filterCount Number of filter entries
|
|
outPath Output buffer for selected path
|
|
outPathSize Capacity of outPath buffer</pre>
|
|
<p>Returns: true if the user selected a file, false if cancelled or closed.</p>
|
|
<h2>Input Boxes</h2>
|
|
<h3>dvxInputBox</h3>
|
|
<pre><code>bool dvxInputBox(AppContextT *ctx, const char *title, const char *prompt, const char *defaultText, char *outBuf, int32_t outBufSize);</code></pre>
|
|
<p>Display a modal input box with a prompt label, text field, and OK/Cancel buttons. The prompt is word-wrapped to the dialog width and may contain '\n'. The field accepts at most outBufSize - 1 characters (capped at 4096), so the caller receives exactly what was typed. An outBufSize below 2 (no room for any input) returns false without showing the dialog.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
title Window title
|
|
prompt Prompt label above the input field
|
|
defaultText Initial text (NULL = empty)
|
|
outBuf Output buffer
|
|
outBufSize Capacity of outBuf</pre>
|
|
<p>Returns: true if OK was pressed, false if cancelled.</p>
|
|
<h3>dvxIntInputBox</h3>
|
|
<pre><code>bool dvxIntInputBox(AppContextT *ctx, const char *title, const char *prompt, int32_t defaultVal, int32_t minVal, int32_t maxVal, int32_t step, int32_t *outVal);</code></pre>
|
|
<p>Display a modal integer input box with a spinner (up/down arrows) for clamped numeric input.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
title Window title
|
|
prompt Prompt label
|
|
defaultVal Initial value
|
|
minVal Minimum allowed value
|
|
maxVal Maximum allowed value
|
|
step Increment per arrow click
|
|
outVal Output: entered value</pre>
|
|
<p>Returns: true if OK pressed, false if cancelled.</p>
|
|
<h2>Choice Dialog</h2>
|
|
<h3>dvxChoiceDialog</h3>
|
|
<pre><code>bool dvxChoiceDialog(AppContextT *ctx, const char *title, const char *prompt, const char **items, int32_t itemCount, int32_t defaultIdx, int32_t *outIdx);</code></pre>
|
|
<p>Display a modal dialog with a listbox of choices. The user picks one item and clicks OK, or cancels.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
title Window title
|
|
prompt Prompt label above the list
|
|
items Array of choice strings
|
|
itemCount Number of items
|
|
defaultIdx Initially highlighted item (-1 = none)
|
|
outIdx Output: selected index</pre>
|
|
<p>Returns: true if a choice was made, false if cancelled.</p>
|
|
<h2>Custom Dialogs</h2>
|
|
<p>Any dialog built from widgets, with the behaviour of the boxes above: modal, Enter chooses OK (unless a button has the focus), Escape or closing the window cancels, a nested dialog (a Browse button, an error box) works, and the dialog ends cleanly if its program is ended meanwhile.</p>
|
|
<pre><code>typedef bool (*DvxDialogOkFnT)(WindowT *dlg, void *userCtx);
|
|
|
|
WindowT *dvxDialogCreate(AppContextT *ctx, const char *title, int32_t contentW);
|
|
WidgetT *dvxDialogAddField(WindowT *dlg, const char *label, int32_t labelW, int32_t maxLen, const char *value);
|
|
void dvxDialogAddOkCancel(WindowT *dlg, const char *okLabel, DvxDialogOkFnT onOk, void *userCtx);
|
|
void dvxDialogEnd(WindowT *dlg, bool accepted);
|
|
bool dvxDialogRun(AppContextT *ctx, WindowT *dlg);</code></pre>
|
|
<p>dvxDialogCreate makes an empty dialog contentW pixels wide (NULL on failure); add widgets under dlg->widgetRoot, or labelled text boxes with dvxDialogAddField. dvxDialogAddOkCancel adds the right-aligned OK (labelled okLabel, "&OK" when NULL) and Cancel buttons after them. dvxDialogRun fits the height to the widgets, centers the dialog, runs it and destroys it; it returns true when it ended with OK. Every created dialog must be run.</p>
|
|
<p>dvxDialogAddField adds a row: a label labelW pixels wide and a text box of up to maxLen characters holding value, filling the rest of the row. It returns the text box (NULL on failure); the box's parent is the row, so a Browse... button can be added after it with wgtButton(box->parent, ...). Give every field's label the same labelW so the boxes line up.</p>
|
|
<p>onOk (may be NULL) runs when OK is chosen, while the widgets still exist: copy their values out and return true to close the dialog, or tell the user what is wrong and return false to keep it open. It is never called for Cancel.</p>
|
|
<p>dvxDialogEnd ends a running dialog from any of the caller's own widget callbacks, such as a double-click in a list or an extra button: accepted true acts like OK (onOk runs first and can keep the dialog open), false like Cancel. Use it only on a dialog made by dvxDialogCreate.</p>
|
|
<pre><code>static bool onOk(WindowT *dlg, void *userCtx) {
|
|
(void)dlg;
|
|
return dvxCopyTrimmed((char *)userCtx, NAME_MAX, wgtGetText(sName)) > 0;
|
|
}
|
|
|
|
WindowT *dlg = dvxDialogCreate(ctx, "Name", 240);
|
|
sName = dvxDialogAddField(dlg, "&Name:", 60, NAME_MAX - 1, "");
|
|
dvxDialogAddOkCancel(dlg, NULL, onOk, name);
|
|
if (dvxDialogRun(ctx, dlg)) {
|
|
// name holds the text
|
|
}</code></pre>
|
|
<h2>Save Prompt</h2>
|
|
<p>Return codes for dvxPromptSave:</p>
|
|
<pre> Define Value Action
|
|
------ ----- ------
|
|
DVX_SAVE_YES 1 Save, then proceed
|
|
DVX_SAVE_NO 2 Discard, then proceed
|
|
DVX_SAVE_CANCEL 3 Abort the operation</pre>
|
|
<h3>dvxPromptSave</h3>
|
|
<pre><code>int32_t dvxPromptSave(AppContextT *ctx, const char *title);</code></pre>
|
|
<p>Display a canonical "Save changes?" dialog with Yes/No/Cancel buttons and an appropriate question icon.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context
|
|
title Document name for message context</pre>
|
|
<p>Returns: DVX_SAVE_YES, DVX_SAVE_NO, or DVX_SAVE_CANCEL.</p>
|
|
</div>
|
|
<div class="topic" id="api.resource">
|
|
<h1>dvxRes.h -- DXE-Embedded Resource System</h1>
|
|
<p>Resources are appended to DXE3 files (.app, .wgt, .lib) after the normal DXE content. The DXE loader never reads past its own header-specified sections, so the appended resource block is invisible to dlopen. This lets each DXE carry its own icons, localized text, help content, and binary data without separate resource files.</p>
|
|
<h2>File Layout</h2>
|
|
<pre><code>[DXE3 content] -- untouched, loaded by dlopen
|
|
[resource data entries] -- sequential, variable length
|
|
[resource directory] -- fixed-size entries (48 bytes each)
|
|
[footer] -- magic + directory offset + entry count (16 bytes)</code></pre>
|
|
<p>Reading starts from the end: seek to EOF - sizeof(footer), verify the magic, then seek to the directory.</p>
|
|
<h2>Constants</h2>
|
|
<pre> Define Value Description
|
|
------ ----- -----------
|
|
DVX_RES_ICON 1 Image data (BMP, PNG, etc.)
|
|
DVX_RES_TEXT 2 Null-terminated string
|
|
DVX_RES_BINARY 3 Arbitrary binary data (app-specific)
|
|
DVX_RES_MAGIC 0x52585644 Footer signature ("DVXR" little-endian)
|
|
DVX_RES_NAME_MAX 32 Max resource name length including null</pre>
|
|
<h2>Structures</h2>
|
|
<h3>DvxResDirEntryT</h3>
|
|
<p>Directory entry describing one resource (48 bytes on disk).</p>
|
|
<pre> Field Description
|
|
----- -----------
|
|
name Resource name (up to 32 chars, null-terminated)
|
|
type DVX_RES_ICON, DVX_RES_TEXT, or DVX_RES_BINARY
|
|
offset Absolute file offset of the data
|
|
size Data size in bytes
|
|
reserved Padding (must be zero)</pre>
|
|
<h3>DvxResFooterT</h3>
|
|
<p>Footer at the very end of a DXE file with appended resources (16 bytes).</p>
|
|
<pre> Field Description
|
|
----- -----------
|
|
magic DVX_RES_MAGIC
|
|
dirOffset Absolute file offset of the resource directory
|
|
entryCount Number of directory entries
|
|
reserved Padding (must be zero)</pre>
|
|
<h3>DvxResHandleT</h3>
|
|
<p>Opaque runtime handle representing an open resource block.</p>
|
|
<pre> Field Description
|
|
----- -----------
|
|
path Source file path
|
|
entries Directory entries (sorted by name for bsearch)
|
|
entryCount Number of entries</pre>
|
|
<h2>Runtime API</h2>
|
|
<h3>dvxResOpen</h3>
|
|
<pre><code>DvxResHandleT *dvxResOpen(const char *path);</code></pre>
|
|
<p>Open a resource handle by reading the footer and directory. Returns NULL if the file has no resource block or cannot be read. Entries are sorted on open for O(log n) name lookup via bsearch.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
path Path to the DXE (or any file) with appended resources</pre>
|
|
<p>Returns: Handle pointer, or NULL.</p>
|
|
<h3>dvxResRead</h3>
|
|
<pre><code>void *dvxResRead(DvxResHandleT *h, const char *name, uint32_t *outSize);</code></pre>
|
|
<p>Find a resource by name and read its data into a freshly malloc'd buffer. Caller must free the returned buffer.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
h Resource handle
|
|
name Resource name
|
|
outSize Output: data size in bytes</pre>
|
|
<p>Returns: Data buffer, or NULL if not found or I/O failed.</p>
|
|
<h3>dvxResFind</h3>
|
|
<pre><code>const DvxResDirEntryT *dvxResFind(DvxResHandleT *h, const char *name);</code></pre>
|
|
<p>Look up a resource's directory entry by name. The returned pointer is valid until dvxResClose. A name of DVX_RES_NAME_MAX (32) or more characters never matches (names are not truncated to a prefix).</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
h Resource handle
|
|
name Resource name</pre>
|
|
<p>Returns: Pointer to directory entry, or NULL if not found.</p>
|
|
<h3>dvxResClose</h3>
|
|
<pre><code>void dvxResClose(DvxResHandleT *h);</code></pre>
|
|
<p>Close a resource handle and free its memory.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
h Handle to close</pre>
|
|
<h3>dvxResAppend</h3>
|
|
<pre><code>int32_t dvxResAppend(const char *path, const char *name, uint32_t type, const void *data, uint32_t dataSize);</code></pre>
|
|
<p>Append a resource to an existing DXE file. Preserves existing resources and adds the new one. Thin wrapper over the resource writer in tools/dvxResWrite.h.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
path Target DXE path
|
|
name Resource name (up to DVX_RES_NAME_MAX - 1 chars)
|
|
type DVX_RES_ICON, DVX_RES_TEXT, or DVX_RES_BINARY
|
|
data Resource payload
|
|
dataSize Payload size in bytes</pre>
|
|
<p>Returns: 0 on success, -1 on failure.</p>
|
|
<h3>dvxResRemove</h3>
|
|
<pre><code>int32_t dvxResRemove(const char *path, const char *name);</code></pre>
|
|
<p>Remove a resource from a DXE file by name. The resource block is rewritten through a temporary file that is swapped in only when every write succeeded, so a failed write leaves the original file intact. If this was the last resource, the result is the bare DXE content with no resource block.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
path Target DXE path
|
|
name Resource name to remove (32 or more characters never matches)</pre>
|
|
<p>Returns: 0 on success, -1 if not found, the name is too long, or I/O failed.</p>
|
|
<h2>See Also</h2>
|
|
<p><a href="#api.app">dvxResLoadIcon / dvxResLoadText / dvxResLoadData -- higher-level helpers on top of this API</a></p>
|
|
</div>
|
|
<div class="topic" id="api.prefs">
|
|
<h1>dvxPrefs.h -- INI Preferences System</h1>
|
|
<p>Handle-based API over classic INI files. Each prefsLoad/prefsCreate returns a PrefsHandleT that must be passed to all subsequent calls and freed with prefsClose when done. Multiple INI files can be open simultaneously.</p>
|
|
<h2>Constants</h2>
|
|
<pre> Define Value Description
|
|
------ ----- -----------
|
|
DVX_CONFIG_DIR "CONFIG" Settings folder, relative to the DVX folder
|
|
DVX_INI_PATH DVX_CONFIG_DIR DVX_PATH_SEP "DVX.INI" Canonical system INI path</pre>
|
|
<h2>Types</h2>
|
|
<h3>PrefsHandleT</h3>
|
|
<p>Opaque handle representing an in-memory INI file. Fields are not exposed.</p>
|
|
<h2>Lifecycle</h2>
|
|
<h3>prefsCreate</h3>
|
|
<pre><code>PrefsHandleT *prefsCreate(void);</code></pre>
|
|
<p>Create an empty preferences handle with no associated file. Use this when building an INI from scratch before calling prefsSaveAs.</p>
|
|
<p>Returns: New handle, or NULL on allocation failure.</p>
|
|
<h3>prefsLoad</h3>
|
|
<pre><code>PrefsHandleT *prefsLoad(const char *filename);</code></pre>
|
|
<p>Load an INI file into a new handle. If the file does not exist but a backup copy left by an interrupted save does (same name with the extension replaced by ~ and its first two characters, e.g. DVX.~IN), the backup is read instead, so the next save restores the file. Otherwise a missing file gives a valid empty handle. Either way the path is stored so prefsSave can write the file later.</p>
|
|
<p>File format rules:</p>
|
|
<ul>
|
|
<li>Lines of any length are read without truncation; LF and CR LF endings are accepted.</li>
|
|
<li>"[name]" starts a section; "key = value" is a key (blanks around key and value are trimmed). Keys before the first section belong to section "".</li>
|
|
<li>Section and key names match case-insensitively. With duplicates, the first one wins.</li>
|
|
<li>Blank lines, ';' and '#' comments and lines that are neither a section nor a key are kept.</li>
|
|
<li>On save, every line not changed through the API (comments, unknown lines, untouched keys and headers) keeps its content; changed or new keys are written as "key = value". Every line is written with CR LF endings, so a file with LF-only endings is rewritten in full.</li>
|
|
</ul>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
filename Path to INI file</pre>
|
|
<p>Returns: Handle, or NULL for a NULL filename or on allocation failure.</p>
|
|
<h3>prefsSave</h3>
|
|
<pre><code>bool prefsSave(PrefsHandleT *h);</code></pre>
|
|
<p>Save the in-memory state back to the file that was loaded (or captured by prefsLoad for a non-existent file). Returns false and writes nothing when the file existed but could not be read by prefsLoad, so an unreadable file is never replaced by defaults. Uses the same crash-safe write as prefsSaveAs.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
h Preferences handle</pre>
|
|
<p>Returns: true on success.</p>
|
|
<h3>prefsSaveAs</h3>
|
|
<pre><code>bool prefsSaveAs(PrefsHandleT *h, const char *filename);</code></pre>
|
|
<p>Save to a specific file, overriding the handle's stored path. The data is written to a sibling file whose extension is replaced by $ and the first two characters of the original extension (8.3-safe, e.g. DVX.INI -> DVX.$IN) and swapped in only after every write succeeded; the previous file is parked as DVX.~IN during the swap and then removed. A failed save (disk full, write error) returns false and leaves the previous file intact. When DVX runs from read-only media (platformIsReadOnly), a file that cannot be written is kept in memory instead (the save returns true) and prefsLoad of the same path reads that copy until DVX exits.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
h Preferences handle
|
|
filename Output file path</pre>
|
|
<p>Returns: true on success.</p>
|
|
<h3>prefsClose</h3>
|
|
<pre><code>void prefsClose(PrefsHandleT *h);</code></pre>
|
|
<p>Release all memory held by the handle. Does not save -- call prefsSave first if needed.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
h Handle to close</pre>
|
|
<h2>Getters</h2>
|
|
<h3>prefsGetString</h3>
|
|
<pre><code>const char *prefsGetString(PrefsHandleT *h, const char *section, const char *key, const char *defaultVal);</code></pre>
|
|
<p>Retrieve a string value from [section]/key. The returned pointer is valid until the key is modified or the handle is closed.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
h Preferences handle
|
|
section Section name (without brackets)
|
|
key Key name
|
|
defaultVal Returned if the key is not present</pre>
|
|
<p>Returns: Key value or defaultVal.</p>
|
|
<h3>prefsGetInt</h3>
|
|
<pre><code>int32_t prefsGetInt(PrefsHandleT *h, const char *section, const char *key, int32_t defaultVal);</code></pre>
|
|
<p>Retrieve an integer value. Supports decimal and negative numbers.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
h Preferences handle
|
|
section Section name
|
|
key Key name
|
|
defaultVal Returned if the key is absent or non-numeric</pre>
|
|
<p>Returns: Parsed int, or defaultVal.</p>
|
|
<h3>prefsGetBool</h3>
|
|
<pre><code>bool prefsGetBool(PrefsHandleT *h, const char *section, const char *key, bool defaultVal);</code></pre>
|
|
<p>Retrieve a boolean value. The whole value must be "true", "yes" or "1" (true) or "false", "no" or "0" (false), in any case; anything else (e.g. "t", "10", "normal") returns defaultVal.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
h Preferences handle
|
|
section Section name
|
|
key Key name
|
|
defaultVal Returned if the key is absent or unrecognised</pre>
|
|
<p>Returns: Parsed bool, or defaultVal.</p>
|
|
<h3>prefsGetVideoMode / prefsSetVideoMode</h3>
|
|
<pre><code>void prefsGetVideoMode(PrefsHandleT *h, int32_t *width, int32_t *height, int32_t *bpp);
|
|
void prefsSetVideoMode(PrefsHandleT *h, int32_t width, int32_t height, int32_t bpp);</code></pre>
|
|
<p>Read or store the [video] display mode DVX starts in (keys width, height and bpp). Missing keys read as DVX_DEFAULT_VIDEO_W/H/BPP (640x480, 16 bpp). The one place those key names live: the shell (start-up and safe video mode), the Control Panel and Setup all go through these two functions.</p>
|
|
<h2>Setters</h2>
|
|
<h3>prefsSetString</h3>
|
|
<pre><code>void prefsSetString(PrefsHandleT *h, const char *section, const char *key, const char *value);</code></pre>
|
|
<p>Set a string value. Creates the section and key if they do not already exist; a new key is placed after the section's last key (not after trailing comments that belong to the next section). A NULL value is stored as an empty string.</p>
|
|
<h3>prefsSetInt</h3>
|
|
<pre><code>void prefsSetInt(PrefsHandleT *h, const char *section, const char *key, int32_t value);</code></pre>
|
|
<p>Set an integer value (stored in decimal).</p>
|
|
<h3>prefsSetBool</h3>
|
|
<pre><code>void prefsSetBool(PrefsHandleT *h, const char *section, const char *key, bool value);</code></pre>
|
|
<p>Set a boolean value (stored as "true"/"false").</p>
|
|
<h3>prefsRemove</h3>
|
|
<pre><code>void prefsRemove(PrefsHandleT *h, const char *section, const char *key);</code></pre>
|
|
<p>Remove a key from a section. No-op if the key does not exist.</p>
|
|
<h3>prefsKeyName</h3>
|
|
<pre><code>const char *prefsKeyName(PrefsHandleT *h, const char *section, int32_t index);</code></pre>
|
|
<p>Name of the index-th distinct key (0-based, file order) in a section, or NULL past the last one (and for a NULL handle or section or a negative index), so a section with keys not known in advance can be walked. A key repeated in the section counts once: the first, which the getters read. The pointer is valid until the key is removed or prefsClose.</p>
|
|
<pre><code>for (int32_t i = 0; (key = prefsKeyName(h, "associations", i)) != NULL; i++) {
|
|
printf("%s = %s\n", key, prefsGetString(h, "associations", key, ""));
|
|
}</code></pre>
|
|
<h2>Shared Preference Vocabularies</h2>
|
|
<p>Enumerated preferences are stored in DVX.INI by name. One table per preference is the single source of truth for the label the Control Panel shows, the spelling written to the INI and the value applied.</p>
|
|
<pre><code>typedef struct {
|
|
const char *label; // user-facing text (dropdown item)
|
|
const char *iniName; // spelling stored in the INI
|
|
int32_t value; // value applied
|
|
} PrefsChoiceT;
|
|
|
|
extern const PrefsChoiceT prefsAccelChoices[PREFS_ACCEL_CHOICE_COUNT];
|
|
extern const PrefsChoiceT prefsWallpaperChoices[PREFS_WALLPAPER_CHOICE_COUNT];
|
|
extern const PrefsChoiceT prefsWheelDirChoices[PREFS_WHEEL_DIR_CHOICE_COUNT];</code></pre>
|
|
<pre> Table INI key Entries (iniName: value) Default
|
|
----- ------- ------------------------ -------
|
|
prefsAccelChoices [mouse] acceleration off, low, medium, high: MOUSE_ACCEL_* threshold MOUSE_ACCEL_DEFAULT ("medium")
|
|
prefsWallpaperChoices [desktop] mode stretch, tile, center: WallpaperModeE (indexed by it) WALLPAPER_MODE_DEFAULT ("stretch")
|
|
prefsWheelDirChoices [mouse] wheel normal: 1, reversed: -1 (MOUSE_WHEEL_DIR_*) MOUSE_WHEEL_DIR_DEFAULT ("normal")</pre>
|
|
<p>The entry counts are PREFS_ACCEL_CHOICE_COUNT (4), PREFS_WALLPAPER_CHOICE_COUNT (3) and PREFS_WHEEL_DIR_CHOICE_COUNT (2). MOUSE_WHEEL_DIR_NORMAL (1) and MOUSE_WHEEL_DIR_REVERSED (-1) in dvxTypes.h are also the wheelDir values for dvxSetMouseConfig.</p>
|
|
<p>Labels are the capitalized names ("Off", "Stretch", "Reversed", ...). The accel table is in dropdown order. The shell reads [mouse] wheel, [mouse] acceleration and [desktop] mode through prefsGetChoice at startup, and the Control Panel builds its dropdowns and writes the INI from the same tables.</p>
|
|
<h3>prefsChoiceIndex</h3>
|
|
<pre><code>int32_t prefsChoiceIndex(const PrefsChoiceT *choices, int32_t count, const char *name);</code></pre>
|
|
<p>Return the index of the choice whose iniName equals name (case-insensitive), or -1 when there is none (also for a NULL name).</p>
|
|
<h3>prefsChoiceIndexOfValue</h3>
|
|
<pre><code>int32_t prefsChoiceIndexOfValue(const PrefsChoiceT *choices, int32_t count, int32_t value);</code></pre>
|
|
<p>Return the index of the choice whose value equals value, or -1 when there is none. A dropdown built from a choice table selects this index for the current setting.</p>
|
|
<h3>prefsChoiceLabels</h3>
|
|
<pre><code>void prefsChoiceLabels(const PrefsChoiceT *choices, int32_t count, const char **labels);</code></pre>
|
|
<p>Fill labels[0..count-1] with the choices' labels, for wgtDropdownSetItems. No strings are copied: the pointers are the table's own.</p>
|
|
<h3>prefsGetChoice</h3>
|
|
<pre><code>int32_t prefsGetChoice(PrefsHandleT *h, const char *section, const char *key, const PrefsChoiceT *choices, int32_t count, const char *defaultName);</code></pre>
|
|
<p>Return the index of the choice named by the key's value; the index of defaultName when the key is missing or holds an unknown name. Use choices[index].value for the value to apply.</p>
|
|
</div>
|
|
<div class="topic" id="api.mem">
|
|
<h1>dvxMem.h -- Per-App Memory Tracking</h1>
|
|
<p>Wraps malloc/free/calloc/realloc/strdup with a 16-byte header that records the owning app ID and allocation size. The Task Manager displays per-app memory usage, and leaks are detected when an app is terminated.</p>
|
|
<p>DXE code does NOT need to include this header for tracking to work. The DXE export table maps malloc/free/calloc/realloc/strdup to these wrappers transparently. Call sites that need the explicit dvxXxx names (e.g. dvxMemGetAppUsage) or the dvxMemAppIdPtr declaration should include dvxMem.h directly.</p>
|
|
<h2>Globals</h2>
|
|
<h3>dvxMemAppIdPtr</h3>
|
|
<pre><code>extern int32_t *dvxMemAppIdPtr;</code></pre>
|
|
<p>Pointer to the current owning app ID. The shell points this at its currentAppId field during init so every allocation is charged to the running app. When dvxMemAppIdPtr is NULL or points to zero, allocations are charged to the shell.</p>
|
|
<h2>Allocation Wrappers</h2>
|
|
<h3>dvxMalloc</h3>
|
|
<pre><code>void *dvxMalloc(size_t size);</code></pre>
|
|
<p>Tracked malloc. Returns a pointer to user memory (header is hidden in front of it).</p>
|
|
<h3>dvxCalloc</h3>
|
|
<pre><code>void *dvxCalloc(size_t nmemb, size_t size);</code></pre>
|
|
<p>Tracked calloc.</p>
|
|
<h3>dvxRealloc</h3>
|
|
<pre><code>void *dvxRealloc(void *ptr, size_t size);</code></pre>
|
|
<p>Tracked realloc. Works correctly with tracked pointers only -- the magic header identifies them.</p>
|
|
<h3>dvxFree</h3>
|
|
<pre><code>void dvxFree(void *ptr);</code></pre>
|
|
<p>Tracked free. Falls through to the real free() if the pointer's header magic does not match, so mixing tracked and untracked pointers is safe.</p>
|
|
<h3>dvxStrdup</h3>
|
|
<pre><code>char *dvxStrdup(const char *s);</code></pre>
|
|
<p>Tracked strdup.</p>
|
|
<h2>Accounting</h2>
|
|
<h3>dvxMemGetAppUsage</h3>
|
|
<pre><code>uint32_t dvxMemGetAppUsage(int32_t appId);</code></pre>
|
|
<p>Return the total bytes currently charged to the given app ID.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
appId App ID to query</pre>
|
|
<p>Returns: Bytes allocated for this app.</p>
|
|
<h3>dvxMemResetApp</h3>
|
|
<pre><code>void dvxMemResetApp(int32_t appId);</code></pre>
|
|
<p>Free every tracked allocation charged to the given app. Called by the shell after an app exits or crashes to reclaim its memory.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
appId App ID to flush</pre>
|
|
</div>
|
|
<div class="topic" id="api.widgetplugin">
|
|
<h1>dvxWgtP.h -- Widget Plugin API</h1>
|
|
<p>Included by widget .c files (DXE modules) to access core infrastructure for tree manipulation, focus management, scrollbar drawing, layout helpers, and shared interaction state. This header is NOT intended for application code -- use dvxWgt.h for the public widget API.</p>
|
|
<h2>Validation Macros</h2>
|
|
<h3>VALIDATE_WIDGET / VALIDATE_WIDGET_VOID</h3>
|
|
<pre><code>VALIDATE_WIDGET(w, wtype, retval); // returns retval on mismatch
|
|
VALIDATE_WIDGET_VOID(w, wtype); // returns void on mismatch</code></pre>
|
|
<p>Bail out of a widget method if the widget pointer is NULL or its type does not match the expected wtype. Use at the top of every widget-specific API function.</p>
|
|
<h2>Core Constants</h2>
|
|
<pre> Define Value Description
|
|
------ ----- -----------
|
|
DEFAULT_SPACING 4 Default pixel spacing between VBox/HBox children
|
|
DEFAULT_PADDING 4 Default internal padding for containers
|
|
SB_MIN_THUMB 14 Minimum scrollbar thumb size in pixels
|
|
WGT_SB_W 14 Widget-internal scrollbar width</pre>
|
|
<p>The KEY_MOD_* modifier bits are defined in dvxTypes.h (see Accelerator Modifier Flags).</p>
|
|
<h2>Inline Helpers</h2>
|
|
<h3>clampInt</h3>
|
|
<pre><code>static inline int32_t clampInt(int32_t val, int32_t lo, int32_t hi);</code></pre>
|
|
<p>Clamp val into the [lo, hi] range.</p>
|
|
<h3>drawTextEmbossed</h3>
|
|
<pre><code>static inline void drawTextEmbossed(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, int32_t x, int32_t y, const char *text, const ColorSchemeT *colors);</code></pre>
|
|
<p>Draw text twice -- once offset by (1,1) in highlight color, then overlaid in shadow color at (x,y) -- for a classic Motif-style embossed look used on disabled labels.</p>
|
|
<h3>drawTextAccelEmbossed</h3>
|
|
<pre><code>void drawTextAccelEmbossed(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, int32_t x, int32_t y, const char *text, const ColorSchemeT *colors);</code></pre>
|
|
<p>Embossed text with & accelerator markers (declared in dvxDraw.h). Same offset logic as drawTextEmbossed, but processes & markers. drawWidgetTextAccel uses it for disabled widgets.</p>
|
|
<h2>Shared Interaction State</h2>
|
|
<p>Extern variables defined in widgetCore.c (sClosedPopup in widgetEvent.c, sKeyPressedBtn and sCursorBlinkFn in dvxApp.c). These hold system-wide widget state: only one focused widget, one open popup, one pressed button at a time.</p>
|
|
<pre> Variable Type Purpose
|
|
-------- ---- -------
|
|
sCursorBlinkOn bool Current state of the text cursor blink
|
|
sDblClickTicks clock_t Double-click interval in clock() ticks
|
|
sDebugLayout bool When true, draw colored debug borders around containers
|
|
sClosedPopup WidgetT* Widget whose popup just closed (suppresses re-open)
|
|
sFocusedWidget WidgetT* Currently focused widget
|
|
sKeyPressedBtn WidgetT* Button being pressed via keyboard (for release tracking)
|
|
sOpenPopup WidgetT* Currently open popup (dropdown/combo list)
|
|
sDragWidget WidgetT* Widget being dragged (any drag type)
|
|
sPollWidgets WidgetT** Dynamic array of poll widgets (stb_ds)
|
|
sCursorBlinkFn fn ptr Callback invoked when the blink flips (repaints focused text)</pre>
|
|
<h2>Core Widget Functions</h2>
|
|
<h3>widgetAlloc</h3>
|
|
<pre><code>WidgetT *widgetAlloc(WidgetT *parent, int32_t type);</code></pre>
|
|
<p>Allocate a new WidgetT, initialize its fields to defaults, link it as the last child of parent (if non-NULL), and assign its class pointer from widgetClassTable[type].</p>
|
|
<h3>widgetAddChild</h3>
|
|
<pre><code>void widgetAddChild(WidgetT *parent, WidgetT *child);</code></pre>
|
|
<p>Append child to parent's child list. Updates parent's firstChild/lastChild and child's nextSibling pointers.</p>
|
|
<h3>widgetRemoveChild</h3>
|
|
<pre><code>void widgetRemoveChild(WidgetT *parent, WidgetT *child);</code></pre>
|
|
<p>Unlink child from parent's child list. Does not destroy the child.</p>
|
|
<h3>widgetDestroyChildren</h3>
|
|
<pre><code>void widgetDestroyChildren(WidgetT *w);</code></pre>
|
|
<p>Recursively destroy all descendants of w, calling each one's wclsDestroy handler.</p>
|
|
<h2>Focus Management</h2>
|
|
<h3>widgetFindNextFocusable</h3>
|
|
<pre><code>WidgetT *widgetFindNextFocusable(WidgetT *root, WidgetT *after);</code></pre>
|
|
<p>Find the next focusable widget in tab order after the given widget. Wraps to the beginning at the end of the tree.</p>
|
|
<h3>widgetFindPrevFocusable</h3>
|
|
<pre><code>WidgetT *widgetFindPrevFocusable(WidgetT *root, WidgetT *before);</code></pre>
|
|
<p>Find the previous focusable widget in tab order. Wraps to the end at the beginning of the tree.</p>
|
|
<h3>widgetFindByAccel</h3>
|
|
<pre><code>WidgetT *widgetFindByAccel(WidgetT *root, char key);</code></pre>
|
|
<p>Find a focusable widget whose accelKey (lowercase) matches the given character. Used for Alt+letter hotkeys on buttons and labels.</p>
|
|
<h2>Hit Testing</h2>
|
|
<h3>widgetHitTest</h3>
|
|
<pre><code>WidgetT *widgetHitTest(WidgetT *w, int32_t x, int32_t y);</code></pre>
|
|
<p>Find the deepest widget at (x, y) within w's subtree. Respects WCLASS_NO_HIT_RECURSE: the search stops at such a container, whose class ON_MOUSE handler receives the press. Hit testing ignores the enabled flag; event dispatch checks widgetIsEnabledChain on the result.</p>
|
|
<h3>widgetCallbackTarget</h3>
|
|
<pre><code>WidgetT *widgetCallbackTarget(WidgetT *root, int32_t x, int32_t y);</code></pre>
|
|
<p>Find the widget the universal callbacks (onClick, onDblClick, onMouseDown/Up/Move), context menus, tooltips and the cursor shape go to. Starts from widgetHitTest; while the result is a non-focusable WCLASS_NO_HIT_RECURSE container (Splitter, ScrollPane), it continues into the child whose paintClip (the part actually visible when last painted) contains the point. Returns the container itself when the point is on its own chrome (divider, scrollbars) or no child is visible there.</p>
|
|
<h3>widgetClosePopup</h3>
|
|
<pre><code>void widgetClosePopup(void);</code></pre>
|
|
<p>Close the open dropdown/combobox popup, if any: dirty its overlay rect and fully invalidate its window so the overlay pixels are erased. The single close path for click-outside, clicks in another window, Escape and focus changes.</p>
|
|
<h3>widgetIsEnabledChain</h3>
|
|
<pre><code>bool widgetIsEnabledChain(const WidgetT *w);</code></pre>
|
|
<p>True when w and every ancestor are enabled. Disabling a container disables input to its whole subtree: mouse and keyboard dispatch use this test.</p>
|
|
<h2>Utility Queries</h2>
|
|
<h3>widgetCountVisibleChildren</h3>
|
|
<pre><code>int32_t widgetCountVisibleChildren(const WidgetT *w);</code></pre>
|
|
<p>Count children whose visible flag is true.</p>
|
|
<h3>widgetFrameBorderWidth</h3>
|
|
<pre><code>int32_t widgetFrameBorderWidth(const WidgetT *w);</code></pre>
|
|
<p>Return the effective border width for a container widget (used to inset child layout).</p>
|
|
<h3>widgetIsFocusable</h3>
|
|
<pre><code>bool widgetIsFocusable(int32_t type);</code></pre>
|
|
<p>True if the widget class with this type ID has the WCLASS_FOCUSABLE flag.</p>
|
|
<h3>widgetIsHorizContainer</h3>
|
|
<pre><code>bool widgetIsHorizContainer(int32_t type);</code></pre>
|
|
<p>True if the widget class lays out children horizontally (WCLASS_HORIZ_CONTAINER).</p>
|
|
<h3>multiClickDetect</h3>
|
|
<pre><code>int32_t multiClickDetect(int32_t vx, int32_t vy);</code></pre>
|
|
<p>Track consecutive clicks at roughly the same position within sDblClickTicks. Returns the current click count (1 for single, 2 for double, etc.).</p>
|
|
<h2>Clipboard</h2>
|
|
<h3>clipboardCopy</h3>
|
|
<pre><code>void clipboardCopy(const char *text, int32_t len);</code></pre>
|
|
<p>Copy text to the process-wide clipboard buffer. Same underlying storage as dvxClipboardCopy.</p>
|
|
<h3>clipboardGet</h3>
|
|
<pre><code>const char *clipboardGet(int32_t *outLen);</code></pre>
|
|
<p>Retrieve clipboard text.</p>
|
|
<h2>Scrollbar Helpers</h2>
|
|
<h3>ScrollHitE</h3>
|
|
<p>Scrollbar hit-test result.</p>
|
|
<pre> Value Description
|
|
----- -----------
|
|
ScrollHitNoneE Click fell outside the scrollbar
|
|
ScrollHitArrowDecE Up/left arrow
|
|
ScrollHitArrowIncE Down/right arrow
|
|
ScrollHitPageDecE Trough above/before the thumb
|
|
ScrollHitPageIncE Trough below/after the thumb
|
|
ScrollHitThumbE The draggable thumb</pre>
|
|
<h3>widgetScrollbarThumb</h3>
|
|
<pre><code>void widgetScrollbarThumb(int32_t trackLen, int32_t totalSize, int32_t visibleSize, int32_t scrollPos, int32_t *thumbPos, int32_t *thumbSize);</code></pre>
|
|
<p>Compute thumb position and size for a scrollbar with the given geometry. Enforces SB_MIN_THUMB.</p>
|
|
<h3>widgetDrawScrollbarV</h3>
|
|
<pre><code>void widgetDrawScrollbarV(DisplayT *d, const BlitOpsT *ops, const ColorSchemeT *colors, int32_t sbX, int32_t sbY, int32_t sbH, int32_t totalSize, int32_t visibleSize, int32_t scrollPos);</code></pre>
|
|
<p>Draw a vertical scrollbar (WGT_SB_W wide) inside a widget.</p>
|
|
<h3>widgetDrawScrollbarH</h3>
|
|
<pre><code>void widgetDrawScrollbarH(DisplayT *d, const BlitOpsT *ops, const ColorSchemeT *colors, int32_t sbX, int32_t sbY, int32_t sbW, int32_t totalSize, int32_t visibleSize, int32_t scrollPos);</code></pre>
|
|
<p>Draw a horizontal scrollbar (WGT_SB_W high) inside a widget.</p>
|
|
<h3>widgetScrollbarHitTest</h3>
|
|
<pre><code>ScrollHitE widgetScrollbarHitTest(int32_t sbLen, int32_t relPos, int32_t totalSize, int32_t visibleSize, int32_t scrollPos);</code></pre>
|
|
<p>Classify a click position along a scrollbar into ScrollHitE. sbLen is the scrollbar length, relPos is the click position along the bar.</p>
|
|
<h3>widgetScrollbarApplyHit</h3>
|
|
<pre><code>bool widgetScrollbarApplyHit(int32_t sbLen, int32_t relPos, int32_t total, int32_t visible, int32_t lineStep, int32_t pageStep, int32_t *pPos, int32_t *outDragOff);</code></pre>
|
|
<p>Apply a click on a widget scrollbar to the scroll position *pPos: an arrow moves it by lineStep, the trough by pageStep, and the result is clamped to [0, total - visible]. A click on the thumb leaves *pPos unchanged and returns true with *outDragOff set to the grab offset inside the thumb; the caller then starts its thumb drag (see widgetScrollbarThumbDragScroll). The one arrow/page/thumb dispatch shared by every scrollbar-owning widget.</p>
|
|
<h3>widgetScrollbarThumbDragScroll</h3>
|
|
<pre><code>int32_t widgetScrollbarThumbDragScroll(int32_t trackLen, int32_t total, int32_t visible, int32_t relMouse, int32_t maxScroll);</code></pre>
|
|
<p>Map a dragged thumb position to a scroll position in [0, maxScroll]. relMouse is where the thumb's leading edge should be, relative to the start of a track of trackLen pixels (the mouse position minus the grab offset). Returns 0 when maxScroll <= 0.</p>
|
|
<h2>Layout Functions</h2>
|
|
<h3>widgetCalcMinSizeBox / widgetCalcMinSizeTree</h3>
|
|
<pre><code>void widgetCalcMinSizeBox(WidgetT *w, const BitmapFontT *font);
|
|
void widgetCalcMinSizeTree(WidgetT *w, const BitmapFontT *font);</code></pre>
|
|
<p>Generic box-container min-size calculation (bottom-up layout pass). widgetCalcMinSizeBox handles a single VBox/HBox; widgetCalcMinSizeTree recurses through the whole subtree.</p>
|
|
<h3>widgetLayoutBox / widgetLayoutChildren</h3>
|
|
<pre><code>void widgetLayoutBox(WidgetT *w, const BitmapFontT *font);
|
|
void widgetLayoutChildren(WidgetT *w, const BitmapFontT *font);</code></pre>
|
|
<p>Generic box-container layout (top-down pass). widgetLayoutBox lays out a single container; widgetLayoutChildren processes each child's layout handler.</p>
|
|
<h2>Event Dispatch</h2>
|
|
<h3>widgetOnPaint / widgetOnMouse / widgetOnKey / etc.</h3>
|
|
<pre><code>void widgetOnPaint(WindowT *win, RectT *dirtyArea);
|
|
void widgetOnMouse(WindowT *win, int32_t x, int32_t y, int32_t buttons);
|
|
void widgetOnKey(WindowT *win, int32_t key, int32_t mod);
|
|
void widgetOnKeyUp(WindowT *win, int32_t scancode, int32_t mod);
|
|
void widgetOnResize(WindowT *win, int32_t newW, int32_t newH);
|
|
void widgetOnScroll(WindowT *win, ScrollbarOrientE orient, int32_t value);
|
|
void widgetOnFocus(WindowT *win);
|
|
void widgetOnBlur(WindowT *win);</code></pre>
|
|
<p>Window-level event handlers that dispatch to individual widgets in the tree. wgtInitWindow installs these as the window's onPaint/onMouse/onKey/etc. callbacks.</p>
|
|
<h3>widgetManageScrollbars</h3>
|
|
<pre><code>void widgetManageScrollbars(WindowT *win, AppContextT *ctx);</code></pre>
|
|
<p>Add, remove, or resize window scrollbars based on the widget tree's current min-size vs. the window content area.</p>
|
|
<h2>Paint Helpers</h2>
|
|
<h3>widgetPaintOne</h3>
|
|
<pre><code>void widgetPaintOne(WidgetT *w, DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors);</code></pre>
|
|
<p>Paint a single widget (sets clip rect, calls the widget's PAINT handler). Does not recurse.</p>
|
|
<h3>widgetPaintOverlays</h3>
|
|
<pre><code>void widgetPaintOverlays(WidgetT *root, DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors);</code></pre>
|
|
<p>Paint the overlay pass: dropdown popups and tooltips drawn on top of the main tree.</p>
|
|
<h2>Pressable Button Helpers</h2>
|
|
<p>Shared state machine for WCLASS_PRESS_RELEASE widgets (Button, ImageButton). The w->pressed flag tracks the visual state; these helpers fire the widget's onClick callback on release-within-bounds.</p>
|
|
<h3>widgetPressableOnMouse / widgetPressableOnKey / widgetPressableOnDragUpdate / widgetPressableOnDragEnd / widgetPressableOnAccelActivate</h3>
|
|
<pre><code>void widgetPressableOnMouse(WidgetT *w, WidgetT *root, int32_t vx, int32_t vy);
|
|
void widgetPressableOnKey(WidgetT *w, int32_t key, int32_t mod);
|
|
void widgetPressableOnDragUpdate(WidgetT *w, WidgetT *root, int32_t x, int32_t y);
|
|
void widgetPressableOnDragEnd(WidgetT *w, WidgetT *root, int32_t x, int32_t y);
|
|
void widgetPressableOnAccelActivate(WidgetT *w, WidgetT *root);</code></pre>
|
|
<p>Register these directly as the widget class's handlers for WGT_METHOD_ON_MOUSE, WGT_METHOD_ON_KEY, WGT_METHOD_ON_DRAG_UPDATE, WGT_METHOD_ON_DRAG_END, and WGT_METHOD_ON_ACCEL_ACTIVATE.</p>
|
|
<h2>Generic Text Handlers</h2>
|
|
<p>For widgets whose data struct has a (const char *) as its first field and the WCLASS_HAS_TEXT flag set, these implement GET_TEXT / SET_TEXT directly.</p>
|
|
<h3>widgetTextGet / widgetTextSet</h3>
|
|
<pre><code>const char *widgetTextGet(const WidgetT *w);
|
|
void widgetTextSet(WidgetT *w, const char *text);</code></pre>
|
|
<p>Register these directly as the widget class's handlers for WGT_METHOD_GET_TEXT and WGT_METHOD_SET_TEXT. The text is strdup'd on set and freed automatically on destroy.</p>
|
|
<h2>Class Table</h2>
|
|
<h3>widgetClassTable</h3>
|
|
<pre><code>extern const WidgetClassT **widgetClassTable;</code></pre>
|
|
<p>stb_ds dynamic array. Each widget DXE appends its class pointer via wgtRegisterClass() during wgtRegister(). Indexed by the runtime type ID assigned at registration.</p>
|
|
</div>
|
|
<div class="topic" id="api.platform">
|
|
<h1>platform/dvxPlat.h -- Platform Abstraction Layer</h1>
|
|
<p>All OS-specific and CPU-specific code is isolated behind this interface. Port DVX to a new platform by implementing a new dvxPlatformXxx.c against this header. The target implementation is dvxPlatformDos.c for DJGPP/DPMI; dvxPlatformHost.c is a Linux backend used only by the host test suites (fake clock, in-memory framebuffer, scripted input). Code shared by both lives in dvxPlatformUtil.c.</p>
|
|
<p>Platform functions must be stateless (or manage their own internal state) and must not reference AppContextT or any layer above dvxTypes.h.</p>
|
|
<h2>Constants</h2>
|
|
<pre> Define Value Description
|
|
------ ----- -----------
|
|
DVX_MAX_PATH 260 Maximum file path length
|
|
PLATFORM_SYSINFO_MAX 4096 Maximum size of formatted system info text
|
|
DVX_PATH_SEP "\\" Native directory separator on DOS</pre>
|
|
<h2>Types</h2>
|
|
<h3>PlatformKeyEventT</h3>
|
|
<p>Keyboard event with ASCII and scancode separated. Extended keys (arrows, F-keys) have ascii == 0 and a non-zero scancode.</p>
|
|
<pre> Field Description
|
|
----- -----------
|
|
ascii ASCII value, 0 for extended/function keys
|
|
scancode PC scancode (e.g. 0x48 = Up, 0x50 = Down)</pre>
|
|
<h3>PlatformLogFnT</h3>
|
|
<pre><code>typedef void (*PlatformLogFnT)(const char *fmt, ...);</code></pre>
|
|
<p>Log function signature used by the crash handler.</p>
|
|
<h3>PlatformSymOverrideT</h3>
|
|
<p>Name/function-pointer pair for registering symbol overrides with the DXE loader.</p>
|
|
<pre> Field Description
|
|
----- -----------
|
|
name Symbol name as seen by DXEs
|
|
func Function pointer to use instead of the default</pre>
|
|
<h2>Lifecycle</h2>
|
|
<h3>dvxLog</h3>
|
|
<pre><code>void dvxLog(const char *fmt, ...);</code></pre>
|
|
<p>Append a formatted line to dvx.log. Implemented in dvx.exe and exported to every DXE via the loader.</p>
|
|
<h3>platformInit</h3>
|
|
<pre><code>void platformInit(void);</code></pre>
|
|
<p>One-time platform initialization (a second call does nothing new; the loader and dvxInit both call it). On DOS this disables Ctrl+C and Ctrl+Break at every level (the DJGPP runtime check, SIGINT, and DOS break checking via INT 21h AH=33h) so the user cannot kill the GUI from inside a graphics mode, and points the real-mode INT 24h (critical error) vector at a stub in DOS memory that answers Fail. From then on a drive that is not ready, a write-protected disk or a removed disk makes the DOS call return an error (errno) instead of waiting at "Abort, Retry, Fail" on the hidden text screen, so no DVX code needs a guard of its own. The handler is never removed: DOS restores the vector from the PSP when DVX exits. It does not install crash handlers (see platformInstallCrashHandler).</p>
|
|
<h3>platformYield</h3>
|
|
<pre><code>void platformYield(void);</code></pre>
|
|
<p>Cooperative CPU yield, used between frames when the event loop has nothing to do. On DOS this calls __dpmi_yield() to be friendly to multitaskers.</p>
|
|
<h2>Timing</h2>
|
|
<h3>platformClock / platformClockHz</h3>
|
|
<pre><code>typedef uint32_t PlatformTicksT;
|
|
|
|
PlatformTicksT platformClock(void);
|
|
PlatformTicksT platformClockHz(void);
|
|
#define PLATFORM_MS_TO_TICKS(ms)</code></pre>
|
|
<p>Monotonic tick counter for UI timing (double-click, tooltip delay, cursor blink, timers), advancing platformClockHz() ticks per second. PlatformTicksT is unsigned so "now - then" is wrap-safe: always compare intervals by subtraction, never raw tick values. PLATFORM_MS_TO_TICKS converts milliseconds to ticks at the platform rate.</p>
|
|
<h3>platformEntropyStamp</h3>
|
|
<pre><code>uint64_t platformEntropyStamp(void);</code></pre>
|
|
<p>Fine-grained, free-running timestamp for input-timing entropy. It is not a clock: units and wrap point are backend-specific. On DOS it returns the CPU time-stamp counter (RDTSC) when CPUID reports one, otherwise the BIOS tick count shifted left 16 bits with the live PIT channel 0 counter (about 0.84 us resolution) in the low 16 bits. Used by dvxSetInputEntropyHook.</p>
|
|
<h3>platformMicros</h3>
|
|
<pre><code>uint64_t platformMicros(void);</code></pre>
|
|
<p>Monotonic microseconds, the high-resolution clock for games and measurements. On DOS it counts from the start of the timer service (dvxInit): the PIT input clocks the timer interrupt has counted plus the part of the running period, read from the latched channel 0 count (about 0.84 us steps), or on a CPU with a time-stamp counter the TSC cycles since the last interrupt at the rate measured over the interrupt period before it, so the clock never drifts from the PIT and a TSC whose rate changes (power saving, an emulator) does no harm. The host backend returns its fake clock. Not for use inside a timer callback.</p>
|
|
<h3>Timer Service</h3>
|
|
<pre><code>#define PLATFORM_PIT_HZ 1193182
|
|
#define PLATFORM_PIT_BIOS_DIVISOR 65536
|
|
#define PLATFORM_TIMER_MAX_HZ 1000
|
|
#define PLATFORM_TIMER_FAST_MAX_HZ 30000
|
|
#define PLATFORM_TIMER_SLOTS 16
|
|
|
|
typedef void (*PlatformTimerFnT)(void *userData);
|
|
|
|
void platformTimerInit(void);
|
|
void platformTimerShutdown(void);
|
|
int32_t platformTimerAdd(uint32_t hz, PlatformTimerFnT fn, void *userData, int32_t ownerAppId);
|
|
int32_t platformTimerAddFast(uint32_t hz, PlatformTimerFnT fn, void *userData, int32_t ownerAppId);
|
|
void platformTimerRemove(int32_t timerId);
|
|
void platformTimerRemoveOwner(int32_t ownerAppId);
|
|
void platformTimerStats(PlatformTimerStatsT *out);</code></pre>
|
|
<p>One timer interrupt for everything that must run at a steady rate however busy the programs are (audio refill, joystick polling). dvxInit starts the service; dvxShutdown stops it. On DOS it owns PIT channel 0 (rate generator mode) and INT 8: the interrupt runs as fast as the fastest registered timer needs (at most PLATFORM_TIMER_MAX_HZ; the BIOS 18.2 Hz when there is none) and exactly one interrupt per 65536 PIT clocks is passed on to the BIOS handler, so the BIOS tick count, the DOS time of day, clock() and the floppy motor timeout keep their normal rate. A rate change takes effect at once (the PIT is restarted and the part of the period already run is counted). Shutdown restores the BIOS square-wave mode and the old vector; it also runs at exit() and from the crash handler when a crash is not recovered.</p>
|
|
<p>platformTimerAdd calls fn(userData) hz times a second, 1 to PLATFORM_TIMER_MAX_HZ; exact on average, while single intervals vary by up to one interrupt period. It returns a timer id, or -1 (bad arguments, all PLATFORM_TIMER_SLOTS in use, service not running). ownerAppId is recorded for platformTimerRemoveOwner, which the shell calls when a program ends; use 0 for a library that removes its own timers. gameTimerAdd (game.lib) registers one for the calling program. After platformTimerRemove returns, the callback is not running and will not run again.</p>
|
|
<p>platformTimerAddFast is the same for a device that must be fed at an audio rate (the PC speaker's digital sound): hz may be up to PLATFORM_TIMER_FAST_MAX_HZ. The whole timer interrupt then runs that often, so the callback must be very short (a few port writes); ask for PLATFORM_PIT_HZ / divisor to be called on every interrupt.</p>
|
|
<blockquote><strong>Warning:</strong> The callback runs INSIDE THE TIMER INTERRUPT, with interrupts disabled, on a small stack. Keep it to a few dozen microseconds; do not call the C library, DVX or DOS; do not use floating point; and pin its code and every byte it touches with platformLockRegion (a page fault there is fatal on DOS). Set flags and counters, fill buffers, write I/O ports.</blockquote>
|
|
<p>PlatformTimerStatsT reports rateHz (the interrupt rate, rounded), divisor (the PIT reload value), interrupts (since the service started) and biosTicks (of those, passed on to the BIOS).</p>
|
|
<h3>Interrupt-Safe Updates and Shutdown Hooks</h3>
|
|
<pre><code>uint32_t platformIrqSave(void);
|
|
void platformIrqRestore(uint32_t state);
|
|
|
|
#define PLATFORM_SHUTDOWN_HOOKS 8
|
|
typedef void (*PlatformShutdownFnT)(void);
|
|
|
|
bool platformShutdownHookAdd(PlatformShutdownFnT fn);
|
|
void platformShutdownHookRemove(PlatformShutdownFnT fn);</code></pre>
|
|
<p>platformIrqSave disables interrupts and returns the previous state for platformIrqRestore; bracket the few instructions that change data an interrupt handler reads (nothing happens on the host).</p>
|
|
<p>A shutdown hook runs when DVX ends without its normal shutdown (exit() or a crash with no recovery point), before the timer service and the keyboard hook are given back, so a module that drives hardware from an interrupt (a sound card playing by DMA) can stop it. Each hook runs once. A module removes its hook before it is unloaded; at most PLATFORM_SHUTDOWN_HOOKS exist at once (Add returns false when full; adding one already present does nothing).</p>
|
|
<h2>Video</h2>
|
|
<h3>platformVideoInit / platformVideoShutdown</h3>
|
|
<pre><code>int32_t platformVideoInit(DisplayT *d, int32_t requestedW, int32_t requestedH, int32_t preferredBpp);
|
|
void platformVideoShutdown(DisplayT *d);</code></pre>
|
|
<p>Low-level video mode setup and teardown. Called by videoInit / videoShutdown.</p>
|
|
<h3>platformVideoEnumModes</h3>
|
|
<pre><code>void platformVideoEnumModes(void (*cb)(int32_t w, int32_t h, int32_t bpp, void *userData), void *userData);</code></pre>
|
|
<p>Enumerate the graphics modes platformVideoInit can actually set (on DOS: VBE modes with a linear framebuffer at 8, 15, 16 or 32 bpp; 24 bpp is not offered). cb is invoked once per distinct width/height/depth. Feeds the video-mode list offered to the user; platformVideoInit selects its own best match independently.</p>
|
|
<h3>platformVideoSetPalette</h3>
|
|
<pre><code>void platformVideoSetPalette(const uint8_t *pal, int32_t firstEntry, int32_t count);</code></pre>
|
|
<p>Program the VGA/VESA DAC palette registers (8-bit mode only). pal is the full 256-entry RGB table (3 bytes per entry); entries firstEntry..firstEntry+count-1 are written, clamped to the 256-entry table.</p>
|
|
<h3>platformVideoFreeBuffers</h3>
|
|
<pre><code>void platformVideoFreeBuffers(DisplayT *d);</code></pre>
|
|
<p>Free the backbuffer and palette without restoring text mode. Used when switching between graphics modes live.</p>
|
|
<h2>Framebuffer Flush</h2>
|
|
<h3>platformFlushRect</h3>
|
|
<pre><code>void platformFlushRect(const DisplayT *d, const RectT *r);</code></pre>
|
|
<p>Copy a rectangle from the system RAM backbuffer to the LFB. The single critical path where PCI bus write speed matters. On DOS, each scanline uses rep movsd for aligned 32-bit writes.</p>
|
|
<h2>Span Operations</h2>
|
|
<h3>platformSpanFill8/16/32</h3>
|
|
<pre><code>void platformSpanFill8(uint8_t *dst, uint32_t color, int32_t count);
|
|
void platformSpanFill16(uint8_t *dst, uint32_t color, int32_t count);
|
|
void platformSpanFill32(uint8_t *dst, uint32_t color, int32_t count);</code></pre>
|
|
<p>Fill count pixels at dst with color. Three depth variants because the fill instruction differs by size. On DOS these use inline rep stosl / rep stosw / rep stosb asm.</p>
|
|
<h3>platformSpanCopy8/16/32</h3>
|
|
<pre><code>void platformSpanCopy8(uint8_t *dst, const uint8_t *src, int32_t count);
|
|
void platformSpanCopy16(uint8_t *dst, const uint8_t *src, int32_t count);
|
|
void platformSpanCopy32(uint8_t *dst, const uint8_t *src, int32_t count);</code></pre>
|
|
<p>Copy count pixels from src to dst. On DOS these use rep movsd.</p>
|
|
<h2>Mouse Input</h2>
|
|
<h3>platformMouseInit</h3>
|
|
<pre><code>void platformMouseInit(int32_t screenW, int32_t screenH);</code></pre>
|
|
<p>Initialize the mouse driver and constrain movement to the screen bounds. On DOS this resets the INT 33h driver, sets the X/Y range and centers the cursor. Safe to call again after a video-mode change: the speed, acceleration and wheel settings applied earlier are re-applied after the driver reset.</p>
|
|
<h3>platformMousePoll</h3>
|
|
<pre><code>void platformMousePoll(int32_t *mx, int32_t *my, int32_t *buttons);</code></pre>
|
|
<p>Poll current mouse state. Buttons is a bitmask: bit 0 = left, bit 1 = right, bit 2 = middle. MOUSE_BUTTON_MASK (MOUSE_LEFT | MOUSE_RIGHT | MOUSE_MIDDLE, in dvxPlat.h) covers every bit it can report.</p>
|
|
<h3>platformMouseWheelInit / platformMouseWheelPoll</h3>
|
|
<pre><code>bool platformMouseWheelInit(void);
|
|
int32_t platformMouseWheelPoll(void);</code></pre>
|
|
<p>Detect and activate the CuteMouse Wheel API. platformMouseWheelPoll returns the accumulated delta (positive = scroll down) since the last poll.</p>
|
|
<h3>platformMouseSetAccel / platformMouseSetMickeys</h3>
|
|
<pre><code>void platformMouseSetAccel(int32_t threshold);
|
|
void platformMouseSetMickeys(int32_t horizMickeys, int32_t vertMickeys);</code></pre>
|
|
<p>Set the double-speed threshold and mickey-to-pixel ratio. See dvxSetMouseConfig for a higher-level interface.</p>
|
|
<h3>platformMouseWarp</h3>
|
|
<pre><code>void platformMouseWarp(int32_t x, int32_t y);</code></pre>
|
|
<p>Move the mouse cursor to an absolute screen position. Used to clamp the cursor to window edges during resize.</p>
|
|
<h2>Keyboard Input</h2>
|
|
<h3>platformKeyboardGetModifiers</h3>
|
|
<pre><code>int32_t platformKeyboardGetModifiers(void);</code></pre>
|
|
<p>Return the current modifier key state in BIOS shift-state format.</p>
|
|
<h3>platformKeyboardRead</h3>
|
|
<pre><code>bool platformKeyboardRead(PlatformKeyEventT *evt);</code></pre>
|
|
<p>Non-blocking read of the next key from the keyboard buffer. Returns true if a key was available. On DOS this uses INT 16h AH=11h/10h; extended keys have their ASCII byte zeroed, and Shift/Ctrl/Alt+F1..F12 are reported as the plain F-key scancode (see platformNormalizeFKeyScan) with the modifier available from platformKeyboardGetModifiers.</p>
|
|
<h3>platformNormalizeFKeyScan</h3>
|
|
<pre><code>int32_t platformNormalizeFKeyScan(int32_t scancode);</code></pre>
|
|
<p>Map the BIOS scancodes for Shift/Ctrl/Alt+F1..F12 (Shift F1-F10 0x54-0x5D, Ctrl 0x5E-0x67, Alt 0x68-0x71, Shift/Ctrl/Alt F11-F12 0x87-0x8C) onto the plain F-key scancodes (F1-F10 0x3B-0x44, F11 0x85, F12 0x86). Any other scancode is returned unchanged. Applied by every backend's platformKeyboardRead.</p>
|
|
<h3>Raw Keys and the Held-Key Table</h3>
|
|
<pre><code>#define PLATFORM_KEY_EVENT_QUEUE 64
|
|
|
|
void platformKeyHookInit(void);
|
|
void platformKeyHookShutdown(void);
|
|
bool platformKeyEventRead(RawKeyEventT *evt);
|
|
bool platformKeyIsDown(int32_t rawCode);
|
|
void platformKeyStateGet(uint8_t out[KEY_RAW_COUNT]);
|
|
void platformKeyStateClear(void);
|
|
void platformInjectScan(uint8_t scan);</code></pre>
|
|
<p>The keyboard hook (on DOS a chain on INT 9 that reads port 60h before the BIOS handler stores the key for platformKeyboardRead) decodes every set-1 byte: 0xE0 prefixes, the fake Shift codes the keyboard wraps around grey keys (dropped), the Pause burst (one press and one release) and keyboard replies such as ACK and overrun (dropped). Each key going down or up updates the held-key table and is queued as a RawKeyEventT (at most PLATFORM_KEY_EVENT_QUEUE; newer events are dropped when the queue is full, the table still changes). Keys are named by raw code, KEY_RAW_CODE(scancode, extended) (see dvxTypes.h): Left and Right Shift, Ctrl and Alt are keys of their own, and the code does not change with modifiers. dvxInit installs the hook and dvxShutdown removes it; the event loop reads the queue (onKeyUp, onRawKey) and clears the table whenever the keyboard focus moves. platformKeyStateGet copies the table (1 = down). platformInjectScan feeds one byte to the decoder as if the keyboard had sent it (the test script's KEYDOWN / KEYUP use it).</p>
|
|
<h3>platformAltScanToChar</h3>
|
|
<pre><code>char platformAltScanToChar(int32_t scancode);</code></pre>
|
|
<p>Translate an Alt+key scancode to its corresponding ASCII character. Returns 0 for non-printable scancodes.</p>
|
|
<h2>System Information</h2>
|
|
<h3>platformGetSystemInfo</h3>
|
|
<pre><code>const char *platformGetSystemInfo(const DisplayT *display);</code></pre>
|
|
<p>Return a pre-formatted text string with hardware/OS information (CPU, clock, memory, video, mouse, disks). Valid for the lifetime of the process.</p>
|
|
<h3>platformGetMemoryInfo</h3>
|
|
<pre><code>bool platformGetMemoryInfo(uint32_t *totalKb, uint32_t *freeKb);</code></pre>
|
|
<p>Query total and free physical memory.</p>
|
|
<h2>File System</h2>
|
|
<h3>platformValidateFilename</h3>
|
|
<pre><code>const char *platformValidateFilename(const char *name);</code></pre>
|
|
<p>Validate a filename against platform-specific rules (8.3 on DOS). Returns NULL if valid, or a human-readable error string.</p>
|
|
<h3>platformMkdirRecursive</h3>
|
|
<pre><code>int32_t platformMkdirRecursive(const char *path);</code></pre>
|
|
<p>Create a directory and all parent directories (like mkdir -p). Existing directories are not an error. Returns 0 on success, -1 on failure (including a path of DVX_MAX_PATH characters or more).</p>
|
|
<h3>platformChdir</h3>
|
|
<pre><code>int32_t platformChdir(const char *path);</code></pre>
|
|
<p>Change the working directory, including drive letter on DOS (switches drive first when the path starts with "X:"). If the chdir fails the previous drive is restored. A drive prefix that is not a letter is rejected. Returns 0 on success, -1 on failure.</p>
|
|
<h3>platformPathDirEnd</h3>
|
|
<pre><code>char *platformPathDirEnd(const char *path);</code></pre>
|
|
<p>Return a pointer to the last directory separator in path, or NULL. On DOS accepts both "/" and "\\".</p>
|
|
<h3>platformPathIsAbsolute</h3>
|
|
<pre><code>bool platformPathIsAbsolute(const char *path);</code></pre>
|
|
<p>True for a path rooted at a separator ('/' or '\\') or qualified with a drive letter ("C:..."). Both separators count on every platform. A NULL path is not absolute.</p>
|
|
<h3>platformPathBaseName</h3>
|
|
<pre><code>const char *platformPathBaseName(const char *path);</code></pre>
|
|
<p>Return a pointer to the leaf (basename) portion of path.</p>
|
|
<h3>platformCopyFile</h3>
|
|
<pre><code>bool platformCopyFile(const char *srcPath, const char *dstPath);</code></pre>
|
|
<p>Byte-for-byte file copy. Returns false on any open, read, or write failure; a partially written destination is removed so no truncated copy is left behind.</p>
|
|
<h3>platformPathSibling</h3>
|
|
<pre><code>#define PLATFORM_TMP_EXT ".$"
|
|
#define PLATFORM_BAK_EXT ".~"
|
|
#define PLATFORM_SIBLING_EXT_MAX 3
|
|
|
|
bool platformPathSibling(char *out, int32_t outSize, const char *path, const char *ext);</code></pre>
|
|
<p>Write path with its file-name extension replaced by ext into out, followed by as many leading characters of the original extension as fit in PLATFORM_SIBLING_EXT_MAX characters (a full-length ext such as ".BAK" replaces the extension outright; a name with no extension gets ext appended). Replacing rather than appending keeps an 8.3 name valid on DOS without long file names, and keeping part of the original extension gives files that differ only in extension distinct scratch names: CONFIG\DVX.INI becomes CONFIG\DVX.$IN (temp) and CONFIG\DVX.~IN (backup), FOO.APP and FOO.HLP become FOO.$AP and FOO.$HL. Returns false when the result does not fit in outSize or would name path itself (compared case-insensitively).</p>
|
|
<h3>platformReplaceFile</h3>
|
|
<pre><code>bool platformReplaceFile(const char *tmpPath, const char *path, const char *bakPath);</code></pre>
|
|
<p>Crash-safe swap of a fully written tmpPath in for path. The original is renamed aside to bakPath first (DOS rename cannot overwrite) and removed only once the replacement is in place; if the swap fails the original is restored and tmpPath removed. A directory at path is never moved aside: the call fails and removes tmpPath. A leftover bakPath is removed only while path exists; with path missing it may be the only copy left by a crash between the two renames, so it is kept (prefsLoad reads it back). The preferences system, the resource writer and the help compiler all save this way, with the PLATFORM_TMP_EXT and PLATFORM_BAK_EXT siblings from platformPathSibling.</p>
|
|
<h3>platformReadFile</h3>
|
|
<pre><code>char *platformReadFile(const char *path, int32_t *outLen);</code></pre>
|
|
<p>Slurp a whole file into a freshly malloc'd, NUL-terminated buffer. Caller frees. Binary-safe: the NUL is past the end of the reported length.</p>
|
|
<h3>platformGlobMatch</h3>
|
|
<pre><code>bool platformGlobMatch(const char *pattern, const char *name);</code></pre>
|
|
<p>Simple glob pattern match. Case-insensitive. Supports * (zero or more chars) and ? (single char). Iterative (a mismatch lets the last * take one more character), so patterns with many stars stay fast.</p>
|
|
<h3>platformFilterMatch</h3>
|
|
<pre><code>#define PLATFORM_FILTER_ALL "*.*"
|
|
bool platformFilterMatch(const char *name, const char *filter);</code></pre>
|
|
<p>True when name matches filter: platformGlobMatch patterns separated by ';', ',' or blanks ("*.txt;*.bat", "*.bmp, *.jpg"). A pattern equal to PLATFORM_FILTER_ALL matches every file, with or without an extension, as on DOS; a NULL or blank filter matches everything too. The file dialog (dvxFileDialog) and the file operations library (fileOpsListDir) both use it, so a filter behaves the same everywhere.</p>
|
|
<h3>platformDriveList / platformDiskSpace / platformBootDrive</h3>
|
|
<pre><code>#define PLATFORM_DRIVE_MAX 26
|
|
#define PLATFORM_DRIVE_ROOT_MAX 4
|
|
|
|
typedef enum {
|
|
PlatformDriveFixedE,
|
|
PlatformDriveRemovableE,
|
|
PlatformDriveRemoteE // network drive or CD-ROM
|
|
} PlatformDriveTypeE;
|
|
|
|
typedef struct {
|
|
char root[PLATFORM_DRIVE_ROOT_MAX]; // "C:\"
|
|
PlatformDriveTypeE type;
|
|
} PlatformDriveT;
|
|
|
|
int32_t platformDriveList(PlatformDriveT *out, int32_t max);
|
|
bool platformDiskSpace(const char *path, uint64_t *totalBytes, uint64_t *freeBytes);
|
|
char platformBootDrive(void);</code></pre>
|
|
<p>platformDriveList fills out[] with the drives that exist, in letter order, and returns the count. On DOS it uses IOCTL calls only (INT 21h AX=4409h remote, 440Eh logical drive map, 4408h removable), never the media, so empty floppy and CD-ROM drives are listed at once; the second letter of a shared floppy drive (B: on a one-floppy system) is left out because using it makes DOS ask for a disk swap. The host backend reports one drive, "/". System Information and the file operations library both use it.</p>
|
|
<p>platformDiskSpace reports the size and free space of the disk holding path (DOS: INT 21h AH=36h on the path's drive letter, or the current drive; host: statvfs of the nearest existing folder of path, so a folder about to be created reports its disk). False when unknown.</p>
|
|
<p>platformBootDrive returns the letter of the drive DOS started from (INT 21h AX=3305h, DOS 4.0+), whose root holds AUTOEXEC.BAT, or 0 where there is none (older DOS, and every platform but DOS).</p>
|
|
<h3>Read-only media: platformSetReadOnly / platformIsReadOnly / platformDirIsWritable / platformTempDir / platformSetLogMemory / platformLogMemory</h3>
|
|
<pre><code>#define PLATFORM_LOG_MEM_MAX 16384
|
|
|
|
void platformSetReadOnly(bool readOnly);
|
|
bool platformIsReadOnly(void);
|
|
bool platformDirIsWritable(const char *dir);
|
|
const char *platformTempDir(void);
|
|
void platformSetLogMemory(void);
|
|
const char *platformLogMemory(void);</code></pre>
|
|
<p>The loader tests at startup whether the DVX folder can be written to (platformDirIsWritable creates and removes a scratch file in dir) and records the answer with platformSetReadOnly. When DVX runs from a CD-ROM or a write-protected disk (platformIsReadOnly is true): the log goes to DVX.LOG in the TEMP folder (platformTempDir: the TEMP or else TMP variable, NULL when neither is set) or, without one, to memory; the boot-time help rebuild is skipped (its output and counts could not be saved); prefsSave and prefsSaveAs keep a file they cannot write in memory, and prefsLoad reads it back until DVX exits; the shell says so once at startup; and a program that allows several copies runs only once when there is no TEMP folder (a second copy is a temporary copy of the module). platformSetLogMemory sends dvxLog to a buffer holding the latest PLATFORM_LOG_MEM_MAX bytes (the oldest whole lines are dropped), which platformLogMemory returns; platformSetLogPath goes back to a file. SETUP.EXE logs to memory unless /LOG= is given, so it never writes to its own disk.</p>
|
|
<h3>platformLineEnding / platformStripLineEndings</h3>
|
|
<pre><code>const char *platformLineEnding(void);
|
|
int32_t platformStripLineEndings(char *buf, int32_t len);</code></pre>
|
|
<p>platformLineEnding returns the native line ending string ("\r\n" on DOS). platformStripLineEndings removes every '\r' from buf[0..len) in place and NUL-terminates the result, so buf must hold at least len + 1 bytes. Returns the new length.</p>
|
|
<h2>String Helpers</h2>
|
|
<h3>dvxSkipWs</h3>
|
|
<pre><code>const char *dvxSkipWs(const char *s);</code></pre>
|
|
<p>Advance past leading spaces and tabs. Returns s unchanged if NULL or already non-whitespace.</p>
|
|
<h3>dvxTrimRight</h3>
|
|
<pre><code>int32_t dvxTrimRight(char *buf);</code></pre>
|
|
<p>Strip trailing spaces, tabs, carriage returns, and newlines from buf in place. Returns the new length.</p>
|
|
<h3>dvxCopyTrimmed</h3>
|
|
<pre><code>int32_t dvxCopyTrimmed(char *dst, int32_t dstSize, const char *src);</code></pre>
|
|
<p>Copy src into dst without its leading spaces and tabs or its trailing whitespace, truncated to fit dstSize. A NULL src gives an empty string. Returns the length of the result. The usual way to read a trimmed value out of a text box: dvxCopyTrimmed(name, sizeof(name), wgtGetText(input)).</p>
|
|
<h3>dvxNextArg</h3>
|
|
<pre><code>#define DVX_ARG_QUOTE '"'
|
|
|
|
const char *dvxNextArg(const char *p, char *out, int32_t outSize, bool *quoted);</code></pre>
|
|
<p>Copy the next argument of the command line p into out and return the text after it, or NULL when only blanks are left (or p is NULL). Arguments are separated by blanks and tabs; one that starts with a double quote runs to the closing quote (or the end), keeps its blanks and loses the quotes, and *quoted is set. The copy is truncated to fit outSize. This is the one rule for splitting command lines: the shell's Run command, program launch arguments and the BASIC COMMAND$ function all use it. Walk every argument with:</p>
|
|
<pre><code>const char *p = args;
|
|
bool quoted;
|
|
char arg[DVX_MAX_PATH];
|
|
|
|
while ((p = dvxNextArg(p, arg, sizeof(arg), &quoted)) != NULL) {
|
|
// use arg
|
|
}</code></pre>
|
|
<h2>DXE Module Support</h2>
|
|
<h3>platformRegisterDxeExports</h3>
|
|
<pre><code>void platformRegisterDxeExports(void);</code></pre>
|
|
<p>Register platform and C runtime symbols with the dynamic module loader so DXE modules can resolve them at load time.</p>
|
|
<h3>platformRegisterSymOverrides</h3>
|
|
<pre><code>void platformRegisterSymOverrides(const PlatformSymOverrideT *entries);</code></pre>
|
|
<p>Register function pointer overrides for subsequently loaded modules. entries is terminated by {NULL, NULL}. On DOS this wraps dlregsym(). Nothing in DVX itself calls it: it is SDK API for embedders and test harnesses that need to substitute a symbol (e.g. a stub) before loading a module.</p>
|
|
<h2>Interrupt-Safe Memory</h2>
|
|
<h3>platformLockRegion / platformUnlockRegion</h3>
|
|
<pre><code>int32_t platformLockRegion(const void *addr, uint32_t size, bool isCode);
|
|
int32_t platformUnlockRegion(const void *addr, uint32_t size, bool isCode);</code></pre>
|
|
<p>Pin (or unpin) a code (isCode = true) or data range into physical memory so an interrupt handler can touch it without faulting under a paging DPMI host. On DOS this converts the address to linear using the code or data segment base and calls the DPMI lock/unlock linear region services. Shared by the key-up INT 9 hook and the serial driver's ISR. Returns 0 on success, -1 on failure; a no-op returning 0 on hosts without paging concerns.</p>
|
|
<h2>Crash Recovery</h2>
|
|
<h3>platformInstallCrashHandler</h3>
|
|
<pre><code>void platformInstallCrashHandler(jmp_buf *recoveryBuf, volatile int *crashSignal, PlatformLogFnT logFn);</code></pre>
|
|
<p>Install signal handlers for SIGSEGV, SIGFPE, SIGILL. On crash, logs platform-specific diagnostics via logFn, stores the signal in *crashSignal, then longjmps to recoveryBuf. Without a recovery buffer the timer service and the keyboard hook are removed and the signal's default action runs.</p>
|
|
<h3>platformAppExit / platformAppAbort / platformSetAppExitHook</h3>
|
|
<pre><code>#define PLATFORM_ABORT_STATUS 3
|
|
typedef void (*PlatformAppExitFnT)(int status);
|
|
|
|
void platformSetAppExitHook(PlatformAppExitFnT fn);
|
|
void platformAppExit(int status);
|
|
void platformAppAbort(void);</code></pre>
|
|
<p>What exit() and abort() mean to loaded modules. The DXE export table maps them to platformAppExit and platformAppAbort (the host build links every app with the same substitution, HOST_APP_LDFLAGS in common.mk). Both call the hook first; the shell's hook ends only the program that is current, the way End Task does (its task, or its callback running on the shell task, is abandoned through the crash-recovery path and the program is removed without its appShutdown and without a fault message; the log says "'name' called exit (status n); ending it"). abort() reports status PLATFORM_ABORT_STATUS. With no program current, or no hook, they end the process like the C library functions. Handlers a program registered with atexit are not run.</p>
|
|
<h3>platformLogCrashDetail</h3>
|
|
<pre><code>void platformLogCrashDetail(int sig, PlatformLogFnT logFn);</code></pre>
|
|
<p>Log platform-specific crash diagnostics. Called internally from the crash handler and exposed for manual invocation.</p>
|
|
<h3>platformOutOfMemory</h3>
|
|
<pre><code>void platformOutOfMemory(size_t bytes);</code></pre>
|
|
<p>Out-of-memory hook for the stb_ds dynamic arrays and hash maps (arrput, hmput, shput and friends have no error return). stb_ds_wrap.h points STBDS_OUT_OF_MEMORY at it wherever the stb_ds implementation is compiled. It logs "out of memory (N bytes) in dynamic array" via dvxLog and raises SIGSEGV, so under platformInstallCrashHandler a failed growth takes the normal crash-recovery path: the shell terminates the app that was current (its task, or its callback running on the shell task), and a failure with no app current is logged and the shell returns to its main loop. With no handler installed (the loader, the tools, the BASIC compiler) the process terminates. It never returns. The stb_ds patch that adds the hook is recorded in thirdparty/stb_ds.PATCHES.md.</p>
|
|
<h2>VGA Splash Screen</h2>
|
|
<h3>platformSplashInit / platformSplashShutdown</h3>
|
|
<pre><code>void platformSplashInit(void);
|
|
void platformSplashShutdown(void);</code></pre>
|
|
<p>Enter/leave VGA mode 13h (SPLASH_W x SPLASH_H x 256) used for the boot splash.</p>
|
|
<pre> Define Value Description
|
|
------ ----- -----------
|
|
SPLASH_W, SPLASH_H 320, 200 Splash screen size
|
|
SPLASH_BPP 8 Bits per pixel
|
|
SPLASH_PAL_ENTRIES 256 Palette entries
|
|
SPLASH_PAL_CHANNELS 3 Bytes per palette entry (R, G, B)
|
|
SPLASH_PAL_BYTES 768 Palette size in the file
|
|
SPLASH_PIXEL_BYTES 64000 Pixel data size in the file
|
|
SPLASH_DAC_SHIFT 2 8-bit RGB to 6-bit DAC value shift</pre>
|
|
<h3>platformSplashLoadRaw</h3>
|
|
<pre><code>bool platformSplashLoadRaw(const char *path);</code></pre>
|
|
<p>Load and display a raw splash file (SPLASH.RAW, written by the bmp2raw tool): SPLASH_PAL_BYTES of palette (6-bit DAC values, R, G, B per entry) followed by SPLASH_PIXEL_BYTES of 8-bit pixels, top-to-bottom scanlines.</p>
|
|
<h3>platformSplashFillRect</h3>
|
|
<pre><code>void platformSplashFillRect(int32_t x, int32_t y, int32_t w, int32_t h, uint8_t color);</code></pre>
|
|
<p>Fill a rectangle on the mode 13h screen with a palette index. Used to draw the progress bar during module loading.</p>
|
|
<h3>platformSplashNearestColor</h3>
|
|
<pre><code>uint8_t platformSplashNearestColor(uint8_t r, uint8_t g, uint8_t b);</code></pre>
|
|
<p>Palette index of the loaded splash image closest to an 8-bit RGB color, so overlays drawn on the splash (the progress bar) do not depend on the image's palette layout. Returns 0 when no splash palette is loaded (always 0 on the host test backend).</p>
|
|
</div>
|
|
<div class="topic" id="lib.audio">
|
|
<h1>Sound Library</h1>
|
|
<p>Sound for DVX programs through whatever sound hardware the computer has: sampled sound from WAV and VOC files, tones and noise, and streams a program feeds itself (music, speech). Every program opens its own audio context; any number of programs play at the same time through one mixer, and everything a program opened is stopped and released when it ends, normally, killed or crashed.</p>
|
|
<p>Header: audio/audio.h (programs), audio/audioDrv.h (sound drivers)</p>
|
|
<p>Loaded as: bin/libs/kpunch/audio/audio.lib, drivers in bin/libs/kpunch/audio/drivers/*.drv</p>
|
|
<h2>Contexts and the Device</h2>
|
|
<pre><code>AudioCtxT *audio = audioOpen(); // NULL: no driver could start
|
|
|
|
int32_t voice = audioPlayFile(audio, "BOOM.WAV", AUDIO_VOLUME_MAX, AUDIO_PAN_CENTER, false);
|
|
|
|
if (voice < 0) {
|
|
dvxLog("no boom: %s", audioErrorText(voice));
|
|
}
|
|
...
|
|
audioClose(audio); // or let the program end</code></pre>
|
|
<p>The first audioOpen starts the sound device and the last audioClose stops it. In between, after AUDIO_IDLE_CLOSE_SEC (3) seconds with nothing playing, the device's output is closed so it costs no CPU time (desktop sounds would otherwise keep it mixing silence), and the next sound opens it again with the same driver before it starts, so nothing of it is lost; audioGetInfo still reports the device. Functions returning int32_t give AUDIO_OK or a voice id (above 0) on success and a negative AUDIO_ERR_* code on failure; audioErrorText says what a code means. audioGetInfo describes the device: the driver's name and description, the mixing rate it achieved, its output bits and channels, the voice limit, and whether it plays sampled sound (hasDac), has a hardware synthesizer (hasSynth) or plays tones on a hardware tone generator (hasTone, the PC speaker). audioGetStats counts mixed blocks and frames, stream underruns, clipped samples, dropped voices and the voices playing.</p>
|
|
<p>None of the functions may be called from an interrupt (a timer-service callback).</p>
|
|
<h2>Samples and Voices</h2>
|
|
<p>audioSampleLoad (file), audioSampleLoadMem (memory) and audioSampleCreate (raw PCM in an AudioFormatT: rate, 8 bits unsigned or 16 bits signed, 1 or 2 channels) make an AudioSampleT; audioSampleFree frees it and stops the voices playing it. audioPlay(sample, volume, pan, loop) plays it on a new voice; audioPlayFile loads a file and plays it, freeing the sample when the voice ends.</p>
|
|
<pre> Setting Range
|
|
------- -----
|
|
volume 0 .. AUDIO_VOLUME_MAX (256)
|
|
pan AUDIO_PAN_LEFT (-256) .. AUDIO_PAN_RIGHT (256); centre plays at full volume on both sides and panning turns the other side down
|
|
pitch AUDIO_PITCH_MIN (64) .. AUDIO_PITCH_MAX (1024), AUDIO_PITCH_NORMAL (256) = recorded speed</pre>
|
|
<p>audioVoiceSetVolume, audioVoiceSetPan, audioVoiceSetPitch, audioVoiceStop and audioVoiceIsPlaying take the context and the voice id; a voice id of another context, or of a voice that has ended, gives AUDIO_ERR_VOICE. audioStopAll stops every sample and tone of a context.</p>
|
|
<p>audioSetEndFn(ctx, fn, userData) asks for a call when a voice of ctx ends, with the reason: AUDIO_END_FINISHED (played out), AUDIO_END_STOPPED (stopped, or its sample or stream went away) or AUDIO_END_DROPPED (the voice limit made room for a louder sound). The call comes from audioPump or the shell's idle time, as the program that owns the context, never from an interrupt. The mixer runs up to one buffer (about 50 ms) ahead of what is heard, so a notification can come that much before the sound has finished playing.</p>
|
|
<h2>Tones</h2>
|
|
<p>audioTone(ctx, wave, hz, volume, ms) plays a square (AUDIO_WAVE_SQUARE), triangle (AUDIO_WAVE_TRIANGLE) or noise (AUDIO_WAVE_NOISE) tone of hz (AUDIO_TONE_MIN_HZ 20 to AUDIO_TONE_MAX_HZ 20000) for ms milliseconds, or until stopped when ms is 0. For noise, hz is how fast the noise changes: higher sounds brighter. audioVoiceSetFrequency changes a playing tone. When the computer has an FM chip (and DVX.INI [audio] fmTones is not false), a square or triangle tone plays on a free FM voice (a square as a carrier modulated at twice its pitch, which gives odd harmonics like a square wave; a triangle as a sine) at up to the chip's highest note (about 6,200 Hz); when every FM voice is taken it is mixed like any sound. Its voice id, volume, pan, pitch and end notification work the same either way. On a device with a DAC tones are mixed like any sound; on the PC speaker in its tones mode the loudest square or triangle tone playing is sounded by the timer chip.</p>
|
|
<h2>Note Queues</h2>
|
|
<pre><code>AudioNotesT *tune = NULL;
|
|
|
|
audioNotesOpen(audio, AUDIO_WAVE_SQUARE, AUDIO_VOLUME_MAX, &tune);
|
|
audioNotesAdd(tune, 440 * AUDIO_MILLIHZ_PER_HZ, 250000); // A for 1/4 s
|
|
audioNotesAdd(tune, 0, 50000); // a rest</code></pre>
|
|
<p>A note queue plays tones and rests one after the other, each lasting exactly as long as asked (its length is counted in output frames from the time queued since the queue last went quiet, so many short notes do not drift). audioNotesAdd(notes, milliHz, us) appends a tone of milliHz thousandths of a hertz (AUDIO_TONE_MIN_HZ to AUDIO_TONE_MAX_HZ, or 0 for a rest) lasting us microseconds; up to AUDIO_NOTES_MAX entries may wait (AUDIO_ERR_BUSY beyond). audioNotesQueued says how many entries have not finished (and how many of them are tones), audioNotesSpace how many more fit, audioNotesClear silences the queue at once. The queue plays on its own voice (audioNotesVoice, for volume and pan) that the voice limit never drops, in the mixer or on the PC speaker's tone generator (never on the FM chip); it is silent while empty. music.lib plays QBasic's PLAY and SOUND through note queues.</p>
|
|
<h2>Streams</h2>
|
|
<pre><code>AudioFormatT fmt = { 22050, 16, 2 };
|
|
AudioStreamT *stream = NULL;
|
|
|
|
audioStreamOpen(audio, &fmt, 250, fillMusic, &player, &stream);</code></pre>
|
|
<p>audioStreamOpen(ctx, fmt, bufferMs, fill, userData, &stream) opens a stream of any rate from AUDIO_RATE_MIN (4000) to AUDIO_RATE_MAX (48000), 8 or 16 bits, mono or stereo, with a buffer of bufferMs milliseconds (0: 250). It plays at once on its own voice (audioStreamVoice), silence until samples arrive. Without fill the program writes samples with audioStreamWrite, which takes as many frames as fit and returns how many; audioStreamSpace and audioStreamQueued tell how much room and how much sound are left. With fill, the library asks for samples whenever the buffer has room: fill(stream, buf, frames, userData) writes up to frames frames in the stream's format and returns how many. Fill functions run from audioPump and the shell's idle time; a program that keeps the desktop busy every frame should call audioPump once a frame. A stream that runs dry plays silence and counts underruns. audioStreamClose stops and frees it.</p>
|
|
<h2>FM Synthesizer</h2>
|
|
<pre><code>int32_t v = audioFmAlloc(audio); // AUDIO_ERR_NO_VOICE: all taken
|
|
|
|
audioFmWrite(audio, v, AUDIO_FM_CAR_LEVEL, 0);
|
|
audioFmWrite(audio, v, AUDIO_FM_FNUM_LOW, fnum & 0xFF);
|
|
audioFmWrite(audio, v, AUDIO_FM_KEY_BLOCK, AUDIO_FM_KEY_ON | (block << AUDIO_FM_BLOCK_SHIFT) | (fnum >> AUDIO_FM_FNUM_HI_SHIFT));
|
|
audioFmFree(audio, v); // key off, back to everyone</code></pre>
|
|
<p>An OPL2 (9 voices) or OPL3 (18 voices, each playing left, right or both) found by the opl driver is shared by every program: audioFmAlloc(ctx) takes a two-operator voice (the least recently given back first, so a released note's tail is cut last) and returns its id, AUDIO_ERR_NO_VOICE when all are taken, AUDIO_ERR_BUSY while a program has the whole chip, AUDIO_ERR_NO_SYNTH without a chip. audioFmWrite(ctx, voice, reg, value) sets a register of the voice's channel, numbered as for the first channel of an OPL2 (AUDIO_FM_MOD_CHAR 20h, AUDIO_FM_CAR_CHAR 23h, levels 40h/43h, attack 60h/63h, sustain 80h/83h, waveforms E0h/E3h, AUDIO_FM_FNUM_LOW A0h, AUDIO_FM_KEY_BLOCK B0h, AUDIO_FM_FEEDBACK C0h); the library adds the channel's offsets and bank. On an OPL3 a feedback value without an output bit (AUDIO_FM_OUT_LEFT, AUDIO_FM_OUT_RIGHT) plays on both sides. Frequencies: hz = fnum x AUDIO_FM_CHIP_HZ (49,716) x 2^block / 2^20. audioFmFree keys the voice off and gives it back; closing the context gives back all of its voices. audioFmGetInfo tells the chip, its voices and how many are free.</p>
|
|
<p>audioFmRawAcquire(ctx) gives one program the whole chip: every voice the others hold is keyed off and taken back (their tones go on in the mixer), new voices are refused, and audioFmRawWrite(ctx, reg, value) sets any register (bank x AUDIO_FM_BANK_REGS + index). A second program asking gets AUDIO_ERR_BUSY. audioFmRawRelease (or closing the context) silences the chip, sets it up again and gives its voices back to everyone.</p>
|
|
<p>audioFmAlloc, audioFmFree, audioFmWrite and audioFmRawWrite may also be called from a timer callback (platformTimerAdd); music.lib plays MIDI that way.</p>
|
|
<h2>MIDI Port</h2>
|
|
<p>With DVX.INI [audio] midi = mpu401 the mpu401 driver opens an MPU-401 (or compatible, such as a Sound Blaster 16's) in UART mode, for an external synthesizer such as a Roland MT-32 or SC-55. The port belongs to one program at a time: audioMidiAcquire(ctx) (AUDIO_ERR_BUSY when another has it, AUDIO_ERR_NO_SYNTH without a port), audioMidiPut(ctx, byte) sends a byte and returns AUDIO_ERR_BUSY when the port cannot take one yet (send it again later; it may be called from a timer callback), audioMidiRelease gives it back (closing the context does too). audioMidiGetInfo tells whether there is a port and whether it is in use. music.lib uses the port for MIDI music.</p>
|
|
<h2>Volume and the Voice Limit</h2>
|
|
<p>audioSetMasterVolume sets the volume of all sound (DVX.INI [audio] volume, in percent, sets it when the device starts); audioSetVolume sets the volume of everything one context plays. At most the voice limit (DVX.INI [audio] voices, 1 to AUDIO_VOICES_MAX, default 16) plays at once. Starting another sample or tone then stops the quietest one playing (it ends with AUDIO_END_DROPPED); when the new sound would itself be the quietest it is not started (AUDIO_ERR_NO_VOICE). Streams are never dropped.</p>
|
|
<h2>Files</h2>
|
|
<p>WAV: uncompressed PCM, 8-bit unsigned or 16-bit signed, mono or stereo, 4000 to 48000 Hz; WAVE_FORMAT_EXTENSIBLE with the PCM sub-format is accepted, other chunks are skipped, and a data chunk that claims more bytes than the file holds plays what is there. VOC (Creative Voice File, header checksum checked): sound blocks with 8-bit unsigned samples, continuation, silence, extended (rate and stereo for the next sound block) and new-format blocks with 8-bit or 16-bit PCM; markers and text are skipped, and a repeated section plays once. Compressed sound (ADPCM, 24-bit, floating point) gives AUDIO_ERR_UNSUPPORTED, a damaged file AUDIO_ERR_CORRUPT, a file that is neither AUDIO_ERR_FORMAT.</p>
|
|
<h2>The Mixer</h2>
|
|
<p>All sound is mixed in 16-bit integers at the rate the device achieved (the mixer never assumes the rate it asked for: a Sound Blaster Pro asked for 22,050 Hz plays at 22,222 Hz), and every source is resampled to it with linear interpolation. Gains combine volume, pan, context volume and master volume; the sum is clipped to 16 bits. The mixer runs from the sound card's own interrupt (the Sound Blaster, at the end of each half buffer of about 23 ms), or from the timer service for devices without one, so sound keeps playing however busy the programs are. It uses no floating point and touches only memory pinned for interrupts (sample and stream buffers are pinned when they are made).</p>
|
|
<h2>Settings</h2>
|
|
<p>DVX.INI section [audio]:</p>
|
|
<pre> Key Meaning
|
|
--- -------
|
|
driver auto (default: find the hardware), or a driver name: sb, speaker, null
|
|
rate mixing rate asked for, 4000 to 48000 (default 22050)
|
|
bits 0 (the device's best), 8 or 16
|
|
channels 0 (the device's best), 1 or 2
|
|
voices voices mixed at once, 1 to 32 (default 16)
|
|
volume master volume in percent (default 100)
|
|
speakerPcm true: sampled sound on the PC speaker (see below); default false
|
|
fmTones false: tones always in the mixer, never on FM voices; default true
|
|
oplPort the FM chip's port, hex (default 388)
|
|
midi where MIDI music plays: auto (default: the FM chip, else music.lib's software synthesizer), mpu401, fm, software
|
|
mpuPort the MPU-401's port, hex (default 330); the port is only looked for with midi = mpu401
|
|
mpuMode what is on the MPU-401 (music.lib): gm (default), mt32 or raw
|
|
midiBank the OP2 instrument bank for FM MIDI (music.lib), a path from the DVX folder</pre>
|
|
<h2>Desktop Sounds</h2>
|
|
<p>The library plays the sounds DVX makes for its events (dvxPlaySound in dvxApp.h): when it loads it sets DVX's sound hook to audioDeskPlay(event, wait). The file of each event is read from DVX.INI [sounds] at the first sound: the key is audioDeskKey(event) and the value a WAV or VOC file (a path from the DVX folder), empty or missing for none; audioDeskReload makes the next sound read the section again (the Control Panel calls it after saving). The sounds play on a context of the library's own, so no program's end cuts one short, at full volume under the master volume. When no sound device starts, the sounds stay silent without trying again until the next reload. With wait the call returns once the sound has played (at most 5 seconds); after the exit sound further events are quiet (DVX is ending). audioDeskLabel(event) names an event for people, audioDeskPlayFile(path) plays any file the same way (to try one out). The default set, made by DVX's own sound generator at build time, is in AUDIO_SOUNDS_DIR (config/sounds).</p>
|
|
<pre> Key Event
|
|
--- -----
|
|
default the default beep (dvxBeep; a message box without an icon)
|
|
startup DVX started
|
|
exit DVX is ending
|
|
error a message box with MB_ICONERROR
|
|
warning MB_ICONWARNING
|
|
question MB_ICONQUESTION
|
|
information MB_ICONINFO
|
|
windowOpen a window opened (not a dialog)
|
|
windowClose a window closed (not a dialog)</pre>
|
|
<h2>Drivers</h2>
|
|
<pre> Driver Device
|
|
------ ------
|
|
sb Sound Blaster family by DSP version: 1.x (8-bit single-cycle), 1.5 / 2.0 (8-bit auto-init, high-speed above 23,000 Hz on 2.0), Pro and Pro 2 (8-bit stereo at 11,025 or 22,050 Hz), 16 (8- or 16-bit, mono or stereo, any rate from 5,000 to 44,100 Hz). The port, IRQ and DMA channels come from the BLASTER variable (e.g. SET BLASTER=A220 I5 D1 H5); without it an SB16 is found at 220h or 240h and asked for its IRQ and DMA. If no interrupt arrives from the card within half a second (a wrong IRQ), the next driver is tried.
|
|
speaker The PC speaker. By default tones only: the timer chip plays the loudest square or triangle tone as a square wave, with no interrupts and no CPU cost; sampled sound and noise are not heard. With speakerPcm = true it plays everything by pulse-width modulation: the timer interrupt runs at 22,096 Hz and each interrupt sets the next pulse (about 5.7 bits). That costs a lot of CPU time: 22,096 interrupts a second plus the mixing. A 486DX2-66 spends a large share of its time on it (estimated 10 to 15 percent for the interrupts alone on plain DOS, much more under a memory manager or Windows) and can lose interrupts, which slows the sound and the DVX clock down. Meant for Pentium-class machines.
|
|
null No sound device: plays nothing, but voices still run their course and end, so programs behave the same.
|
|
opl (synthesizer) The OPL2 or OPL3 FM chip of an AdLib or Sound Blaster at 388h (oplPort), found by the AdLib timer test; the status register tells an OPL2 from an OPL3. Register writes are timed by status reads (3.3 and 23 us on an OPL2) with interrupts off around each index/data pair. Closing zeroes every register.
|
|
mpu401 (synthesizer) An MPU-401 MIDI port in UART mode at mpuPort, only with midi = mpu401: reset (FFh, answered FEh; tried twice, since a port left in UART mode takes the first FFh as data), then UART mode (3Fh). Bytes are sent when the port says it is ready (a short wait, else the caller sends later); input the port has (active sensing) is read and dropped. Closing sends All Notes Off on every channel and resets the port.</pre>
|
|
<p>audioDriverNames(names, max) lists the short names of the sound drivers installed, the one tried first first (for a settings screen).</p>
|
|
<p>Sound drivers are tried in this order: the one DVX.INI names, then from the highest priority down (Sound Blaster, PC speaker, null). The first that starts is used. Synthesizer drivers are opened with the sound device, beside it: of each kind (FM chip, MIDI port) the first that finds its hardware is kept, and they close with the device. The sound device's description and hasSynth in AudioInfoT tell whether there is a synthesizer.</p>
|
|
<h2>Writing a Driver</h2>
|
|
<p>A driver is a driver module (see dvxDrv.h in the API reference): <name>.drv in bin/libs/kpunch/audio/drivers, built by the audio Makefile from each drivers/<name>/ folder, that exports one AudioDriverT named audioDriver, written with AUDIO_DRIVER(name, AUDIO_PRIORITY_*, .open = ..., .play = ..., .close = ...). The descriptor carries DVX_ABI_VERSION; a driver built for another version is refused. audio.lib loads the drivers when the first program opens sound, keeps the one that starts (and the synthesizers that find their hardware) and unloads the rest. open detects the hardware and fills AudioDrvInfoT (description, achieved rate, bits, channels, hasDac, hasSynth, a hardware tone function) without making a sound; play starts output; close stops it and releases everything, after which the mixer is not called again. After a few seconds of silence audio.lib calls close and later open and play again on the same driver, so open must work more than once and must not make a sound (the driver's module stays loaded in between). While playing, the driver calls audioMixRender(buffer, frames) for each block of output from its own interrupt, or calls audioClockStart to have the timer service drive the mixer (the output is thrown away) when it only plays tones or nothing. Code and data an interrupt touches must be pinned: audioLockSpan pins a module's listed functions and data (a module's code comes before its data, so one span covers them), and the library is built without hot/cold function splitting and without loops turned into library calls. Interrupt code must not call the C library, DVX or DOS and must not use floating point. audio.lib stops the device from a shutdown hook if DVX ends without closing it.</p>
|
|
<p>A synthesizer driver exports an AudioSynthDriverT named audioSynth instead, written with AUDIO_SYNTH_DRIVER(name, priority, .kind = AUDIO_SYNTH_FM or AUDIO_SYNTH_MIDI, .open = ..., .close = ..., .write = ... or .put = ...). open detects the hardware, fills AudioSynthDrvInfoT (description; for FM the voices and whether it is stereo) and leaves it silent; close silences it. write(reg, value) sets an FM register (bank x AUDIO_FM_BANK_REGS + index) and must keep the index/data pair together (audioIrqOff / audioIrqRestore); put(byte) sends a MIDI byte, returning false when the port is not ready. Both run at interrupt time too (timer callbacks), so they follow the rules above and are pinned by the driver.</p>
|
|
<h2>BASIC</h2>
|
|
<p>src/include/basic/sound.bas (DECLARE LIBRARY "audio"): PlayWave(fileName), PlayWaveLoop(fileName), PlayTone(frequency, milliseconds, volume), PlayNoise(frequency, milliseconds, volume) return a voice number (0 on failure: SoundError$ says why); StopWave(voice), StopAllSounds, SoundPlaying(voice), SoundVolume(percent), SoundInfo$ (the device, e.g. "Sound Blaster 16 (DSP 4.05) at 220h, 22050 Hz, 16-bit stereo"). Volumes are percent. Each BASIC program plays through its own context, closed when it ends.</p>
|
|
</div>
|
|
<div class="topic" id="lib.basrt">
|
|
<h1>BASIC Runtime Library</h1>
|
|
<p>Stack-based p-code virtual machine and value system for DVX BASIC. Embeddable: the host provides I/O and UI callbacks. No DVX GUI dependencies in the core runtime.</p>
|
|
<p>Headers: apps/dvxbasic/runtime/vm.h, apps/dvxbasic/runtime/values.h</p>
|
|
<p><a href="#lib.basrt.values">Value System (values.h)</a></p>
|
|
<p><a href="#lib.basrt.vm">Virtual Machine (vm.h)</a></p>
|
|
</div>
|
|
<div class="topic" id="lib.basrt.values">
|
|
<h1>Value System</h1>
|
|
<p>Tagged union value type for the VM evaluation stack, variables, and array elements. Strings, arrays, and UDT instances are reference-counted for automatic memory management without a garbage collector.</p>
|
|
<p>Header: apps/dvxbasic/runtime/values.h</p>
|
|
<h2>Type Tags</h2>
|
|
<pre> Constant Value C Union Field Description
|
|
-------- ----- ------------- -----------
|
|
BAS_TYPE_INTEGER 0 intVal 16-bit signed integer.
|
|
BAS_TYPE_LONG 1 longVal 32-bit signed integer.
|
|
BAS_TYPE_SINGLE 2 sngVal 32-bit float.
|
|
BAS_TYPE_DOUBLE 3 dblVal 64-bit float.
|
|
BAS_TYPE_STRING 4 strVal Reference-counted dynamic string.
|
|
BAS_TYPE_BOOLEAN 5 boolVal True (-1) or False (0).
|
|
BAS_TYPE_ARRAY 6 arrVal Reference-counted array.
|
|
BAS_TYPE_UDT 7 udtVal Reference-counted user-defined type.
|
|
BAS_TYPE_OBJECT 8 objVal Opaque host pointer (form, control).
|
|
BAS_TYPE_REF 9 refVal ByRef pointer to a BasValueT slot.</pre>
|
|
<h2>BasValueT</h2>
|
|
<p>Tagged union holding any BASIC value.</p>
|
|
<pre><code>struct BasValueTag {
|
|
uint8_t type; // BAS_TYPE_*
|
|
union {
|
|
int16_t intVal;
|
|
int32_t longVal;
|
|
float sngVal;
|
|
double dblVal;
|
|
BasStringT *strVal;
|
|
int16_t boolVal;
|
|
BasArrayT *arrVal;
|
|
BasUdtT *udtVal;
|
|
void *objVal;
|
|
BasValueT *refVal;
|
|
};
|
|
};</code></pre>
|
|
<h2>Value Constructors</h2>
|
|
<pre><code>BasValueT basValInteger(int16_t v);
|
|
BasValueT basValLong(int32_t v);
|
|
BasValueT basValSingle(float v);
|
|
BasValueT basValDouble(double v);
|
|
BasValueT basValString(BasStringT *s);
|
|
BasValueT basValStringFromC(const char *text);
|
|
BasValueT basValBool(bool v);
|
|
BasValueT basValObject(void *obj);</code></pre>
|
|
<p>Each returns a BasValueT with the appropriate type tag set. basValString increments the string's reference count.</p>
|
|
<h2>Value Lifetime</h2>
|
|
<pre><code>BasValueT basValCopy(BasValueT v);
|
|
void basValRelease(BasValueT *v);</code></pre>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
basValCopy Copy a value. Increments reference count for strings, arrays, and UDTs.
|
|
basValRelease Release a value. Decrements reference count and frees if it reaches zero.</pre>
|
|
<h2>Type Conversion</h2>
|
|
<pre><code>BasValueT basValToInteger(BasValueT v);
|
|
BasValueT basValToLong(BasValueT v);
|
|
BasValueT basValToSingle(BasValueT v);
|
|
BasValueT basValToDouble(BasValueT v);
|
|
BasValueT basValToString(BasValueT v);
|
|
BasValueT basValToBool(BasValueT v);</code></pre>
|
|
<p>Each returns a new value of the target type. The original is not released; the caller manages both lifetimes.</p>
|
|
<h2>Value Utilities</h2>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
basValToNumber(v) Convert any numeric value to double.
|
|
basValFormatString(v) Return a new ref-counted string representation of v.
|
|
basValIsTruthy(v) True if non-zero number or non-empty string.
|
|
basValCompare(a, b) Compare two values. Returns -1, 0, or 1.
|
|
basValCompareCI(a, b) Case-insensitive comparison (OPTION COMPARE TEXT).
|
|
basValPromoteType(a, b) Determine common type for binary ops (e.g. Integer + Single -> Single).</pre>
|
|
<h2>BasStringT</h2>
|
|
<p>Reference-counted string with flexible array member for inline storage.</p>
|
|
<pre><code>typedef struct {
|
|
int32_t refCount;
|
|
int32_t len;
|
|
int32_t cap;
|
|
char data[];
|
|
} BasStringT;</code></pre>
|
|
<h3>String Functions</h3>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
basStringNew(text, len) Allocate from a C string. refCount starts at 1.
|
|
basStringAlloc(cap) Allocate an empty string with given capacity.
|
|
basStringRef(s) Increment reference count. Returns s.
|
|
basStringUnref(s) Decrement reference count. Frees when it reaches zero.
|
|
basStringConcat(a, b) Concatenate two strings. Returns a new string (refCount 1).
|
|
basStringSub(s, start, len) Extract a substring. Returns a new string (refCount 1).
|
|
basStringCompare(a, b) Compare. Returns <0, 0, >0 (like strcmp).
|
|
basStringCompareCI(a, b) Case-insensitive compare.</pre>
|
|
<p>The global basEmptyString is a singleton that is never freed.</p>
|
|
<h2>BasArrayT</h2>
|
|
<p>Reference-counted multi-dimensional array (up to BAS_ARRAY_MAX_DIMS = 8 dimensions).</p>
|
|
<pre><code>typedef struct {
|
|
int32_t refCount;
|
|
uint8_t elementType;
|
|
int32_t dims;
|
|
int32_t lbound[BAS_ARRAY_MAX_DIMS];
|
|
int32_t ubound[BAS_ARRAY_MAX_DIMS];
|
|
int32_t totalElements;
|
|
BasValueT *elements;
|
|
} BasArrayT;</code></pre>
|
|
<h3>Array Functions</h3>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
basArrayNew(dims, lbounds, ubounds, type) Allocate an array. refCount starts at 1.
|
|
basArrayFree(arr) Free all elements and release the array.
|
|
basArrayRef(arr) Increment reference count.
|
|
basArrayUnref(arr) Decrement reference count. Frees at zero.
|
|
basArrayIndex(arr, indices, ndims) Compute flat index from multi-dimensional indices. Returns -1 if out of bounds.</pre>
|
|
<h2>BasUdtT</h2>
|
|
<p>Reference-counted user-defined type instance.</p>
|
|
<pre><code>typedef struct {
|
|
int32_t refCount;
|
|
int32_t typeId;
|
|
int32_t fieldCount;
|
|
BasValueT *fields;
|
|
} BasUdtT;</code></pre>
|
|
<h3>UDT Functions</h3>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
basUdtNew(typeId, fieldCount) Allocate a UDT instance. refCount starts at 1.
|
|
basUdtFree(udt) Free all fields and release.
|
|
basUdtRef(udt) Increment reference count.
|
|
basUdtUnref(udt) Decrement reference count. Frees at zero.</pre>
|
|
</div>
|
|
<div class="topic" id="lib.basrt.vm">
|
|
<h1>Virtual Machine</h1>
|
|
<p>Stack-based p-code interpreter. Executes compiled BASIC bytecode modules. The host provides I/O, UI, SQL, and external library callbacks. The VM has no DVX dependencies; it can be embedded in any C program.</p>
|
|
<p>Header: apps/dvxbasic/runtime/vm.h</p>
|
|
<h2>VM Limits</h2>
|
|
<pre> Constant Value Description
|
|
-------- ----- -----------
|
|
BAS_VM_STACK_SIZE 256 Evaluation stack depth.
|
|
BAS_VM_CALL_STACK_SIZE 64 Maximum call nesting depth.
|
|
BAS_VM_MAX_GLOBALS 512 Global variable slots.
|
|
BAS_VM_MAX_LOCALS 64 Local variables per stack frame.
|
|
BAS_VM_MAX_FOR_DEPTH 32 Maximum nested FOR loop depth.
|
|
BAS_VM_MAX_FILES 16 Open file channels (1-based).</pre>
|
|
<h2>BasVmResultE</h2>
|
|
<p>Result codes returned by basVmRun and basVmStep.</p>
|
|
<pre> Code Value Description
|
|
---- ----- -----------
|
|
BAS_VM_OK 0 Program completed normally.
|
|
BAS_VM_HALTED 1 HALT instruction reached.
|
|
BAS_VM_ERROR 2 Runtime error.
|
|
BAS_VM_STACK_OVERFLOW 3 Evaluation stack overflow.
|
|
BAS_VM_STACK_UNDERFLOW 4 Evaluation stack underflow.
|
|
BAS_VM_CALL_OVERFLOW 5 Call stack overflow.
|
|
BAS_VM_DIV_BY_ZERO 6 Division by zero.
|
|
BAS_VM_TYPE_MISMATCH 7 Type mismatch in operation.
|
|
BAS_VM_OUT_OF_MEMORY 8 Memory allocation failed.
|
|
BAS_VM_BAD_OPCODE 9 Unknown opcode encountered.
|
|
BAS_VM_FILE_ERROR 10 File I/O error.
|
|
BAS_VM_SUBSCRIPT_RANGE 11 Array subscript out of range.
|
|
BAS_VM_STEP_LIMIT 12 Step limit reached (not an error).
|
|
BAS_VM_BREAKPOINT 13 Breakpoint or step completed (not an error).</pre>
|
|
<h2>Lifecycle</h2>
|
|
<pre><code>BasVmT *basVmCreate(void);
|
|
void basVmDestroy(BasVmT *vm);
|
|
void basVmLoadModule(BasVmT *vm, BasModuleT *module);
|
|
void basVmReset(BasVmT *vm);</code></pre>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
basVmCreate Allocate and initialize a new VM instance.
|
|
basVmDestroy Destroy the VM and free all resources.
|
|
basVmLoadModule Load a compiled module (BasModuleT) into the VM.
|
|
basVmReset Reset to initial state (clear stack, globals, PC).</pre>
|
|
<h2>Execution</h2>
|
|
<pre><code>BasVmResultE basVmRun(BasVmT *vm);
|
|
BasVmResultE basVmStep(BasVmT *vm);
|
|
void basVmSetStepLimit(BasVmT *vm, int32_t limit);</code></pre>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
basVmRun Execute the loaded module until it ends, halts, yields, errors, or hits a breakpoint/step limit.
|
|
basVmStep Execute a single instruction and return. Useful for debugger stepping.
|
|
basVmSetStepLimit Set maximum instructions per basVmRun call. 0 = unlimited (default). Returns BAS_VM_STEP_LIMIT when reached.</pre>
|
|
<h2>I/O Callbacks</h2>
|
|
<p>The host provides these callbacks for PRINT, INPUT, and DoEvents statements.</p>
|
|
<pre><code>void basVmSetPrintCallback(BasVmT *vm, BasPrintFnT fn, void *ctx);
|
|
void basVmSetInputCallback(BasVmT *vm, BasInputFnT fn, void *ctx);
|
|
void basVmSetDoEventsCallback(BasVmT *vm, BasDoEventsFnT fn, void *ctx);</code></pre>
|
|
<h3>Callback Types</h3>
|
|
<pre><code>typedef void (*BasPrintFnT)(void *ctx, const char *text, bool newline);
|
|
typedef bool (*BasInputFnT)(void *ctx, const char *prompt,
|
|
char *buf, int32_t bufSize);
|
|
typedef bool (*BasDoEventsFnT)(void *ctx);</code></pre>
|
|
<pre> Type Description
|
|
---- -----------
|
|
BasPrintFnT Called for PRINT output. text is null-terminated. newline indicates line advance.
|
|
BasInputFnT Called for INPUT. Fill buf (up to bufSize-1 chars). Return true on success, false on cancel.
|
|
BasDoEventsFnT Called for DoEvents. Process pending events and return. Return false to stop the program.</pre>
|
|
<h2>UI Callbacks</h2>
|
|
<p>For form and control integration. The VM resolves all UI operations through these callbacks, keeping it independent of any specific GUI toolkit.</p>
|
|
<pre><code>void basVmSetUiCallbacks(BasVmT *vm, const BasUiCallbacksT *ui);</code></pre>
|
|
<h3>BasUiCallbacksT</h3>
|
|
<pre> Field Signature Description
|
|
----- --------- -----------
|
|
getProp BasValueT (*)(ctx, ctrlRef, propName) Get a control property value.
|
|
setProp void (*)(ctx, ctrlRef, propName, value) Set a control property.
|
|
callMethod BasValueT (*)(ctx, ctrlRef, methodName, args, argc) Call a method on a control.
|
|
createCtrl void *(*)(ctx, formRef, typeName, ctrlName) Create a control on a form.
|
|
findCtrl void *(*)(ctx, formRef, ctrlName) Find a control by name.
|
|
findCtrlIdx void *(*)(ctx, formRef, ctrlName, index) Find a control array element.
|
|
loadForm void *(*)(ctx, formName) Load a form by name.
|
|
unloadForm void (*)(ctx, formRef) Unload a form.
|
|
showForm void (*)(ctx, formRef, modal) Show a form (modal or modeless).
|
|
hideForm void (*)(ctx, formRef) Hide a form (keep in memory).
|
|
msgBox int32_t (*)(ctx, message, flags) Display a message box.
|
|
inputBox BasStringT *(*)(ctx, prompt, title, defaultText) Display an input box.
|
|
ctx void * User pointer passed to all callbacks.</pre>
|
|
<h2>SQL Callbacks</h2>
|
|
<p>For database integration.</p>
|
|
<pre><code>void basVmSetSqlCallbacks(BasVmT *vm, const BasSqlCallbacksT *sql);</code></pre>
|
|
<h3>BasSqlCallbacksT</h3>
|
|
<pre> Field Signature Description
|
|
----- --------- -----------
|
|
sqlOpen int32_t (*)(path) Open a database. Returns handle.
|
|
sqlClose void (*)(db) Close a database.
|
|
sqlExec bool (*)(db, sql) Execute a non-query SQL statement.
|
|
sqlError const char *(*)(db) Get last error message.
|
|
sqlQuery int32_t (*)(db, sql) Execute a query. Returns result set handle.
|
|
sqlNext bool (*)(rs) Advance to next row.
|
|
sqlEof bool (*)(rs) Check if at end of result set.
|
|
sqlFieldCount int32_t (*)(rs) Get number of columns.
|
|
sqlFieldName const char *(*)(rs, col) Get column name by index.
|
|
sqlFieldText const char *(*)(rs, col) Get column value as text by index.
|
|
sqlFieldByName const char *(*)(rs, name) Get column value as text by name.
|
|
sqlFieldInt int32_t (*)(rs, col) Get column value as integer.
|
|
sqlFieldDbl double (*)(rs, col) Get column value as double.
|
|
sqlFreeResult void (*)(rs) Free a result set.
|
|
sqlAffectedRows int32_t (*)(db) Get number of rows affected by last statement.</pre>
|
|
<h2>External Library Callbacks</h2>
|
|
<p>For DECLARE LIBRARY support. The VM resolves external functions at runtime via the host.</p>
|
|
<pre><code>void basVmSetExternCallbacks(BasVmT *vm,
|
|
const BasExternCallbacksT *ext);</code></pre>
|
|
<h3>BasExternCallbacksT</h3>
|
|
<p>The compiler converts every argument to its declared parameter type, so callExtern receives values whose type is the declared type. The stock basExternCall pushes cdecl stack words by that type: STRING as a char pointer, SINGLE as one 4-byte float, DOUBLE as 8 bytes, everything else as one int32_t. DOUBLE and SINGLE returns are read from the x87 ST(0) register; INTEGER returns keep only the low 16 bits.</p>
|
|
<pre> Field Signature Description
|
|
----- --------- -----------
|
|
resolveExtern void *(*)(ctx, libName, funcName) Resolve a native function by library and symbol name. Cached after first call.
|
|
callExtern BasValueT (*)(ctx, funcPtr, libName, funcName, args, argc, retType) Call a resolved native function, marshalling arguments and return value.
|
|
ctx void * User pointer passed to both callbacks.</pre>
|
|
<h2>Form Context</h2>
|
|
<p>Set the active form context during event dispatch.</p>
|
|
<pre><code>void basVmSetCurrentForm(BasVmT *vm, void *formRef);
|
|
void basVmSetCurrentFormVars(BasVmT *vm,
|
|
BasValueT *vars, int32_t count);</code></pre>
|
|
<h2>Stack Access</h2>
|
|
<p>Push and pop values on the evaluation stack for host integration.</p>
|
|
<pre><code>bool basVmPush(BasVmT *vm, BasValueT val);
|
|
bool basVmPop(BasVmT *vm, BasValueT *val);</code></pre>
|
|
<p>Both return true on success, false on stack overflow/underflow.</p>
|
|
<h2>Error Reporting</h2>
|
|
<pre><code>const char *basVmGetError(const BasVmT *vm);</code></pre>
|
|
<p>Returns the current error message string. Valid after basVmRun returns BAS_VM_ERROR.</p>
|
|
<h2>Sub/Function Calls from Host</h2>
|
|
<p>Call a SUB or FUNCTION by its code address from host code.</p>
|
|
<pre><code>bool basVmCallSub(BasVmT *vm, int32_t codeAddr);
|
|
bool basVmCallSubWithArgs(BasVmT *vm, int32_t codeAddr,
|
|
const BasValueT *args, int32_t argCount);
|
|
bool basVmCallSubWithArgsOut(BasVmT *vm, int32_t codeAddr,
|
|
const BasValueT *args, int32_t argCount,
|
|
BasValueT *outArgs, int32_t outCount);</code></pre>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
basVmCallSub Call a SUB with no arguments.
|
|
basVmCallSubWithArgs Call a SUB with arguments pushed onto the stack frame.
|
|
basVmCallSubWithArgsOut Call a SUB and read back modified argument values after it returns.</pre>
|
|
<p>All three push a call frame, execute until the SUB returns, then restore the previous execution state. Return true on normal completion, false on error or if the VM is not idle.</p>
|
|
<h2>Debugger API</h2>
|
|
<pre><code>void basVmSetBreakpoints(BasVmT *vm,
|
|
int32_t *lines, int32_t count);
|
|
void basVmStepInto(BasVmT *vm);
|
|
void basVmStepOver(BasVmT *vm);
|
|
void basVmStepOut(BasVmT *vm);
|
|
void basVmRunToCursor(BasVmT *vm, int32_t line);
|
|
int32_t basVmGetCurrentLine(const BasVmT *vm);</code></pre>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
basVmSetBreakpoints Set the breakpoint list (sorted array of source line numbers, host-owned memory).
|
|
basVmStepInto Break at the next OP_LINE instruction.
|
|
basVmStepOver Break when call depth returns to the current level.
|
|
basVmStepOut Break when call depth drops below the current level.
|
|
basVmRunToCursor Break when execution reaches the specified source line.
|
|
basVmGetCurrentLine Get the current source line number (from the last OP_LINE instruction).</pre>
|
|
<p>The breakpoint callback notifies the host when a breakpoint fires during nested sub calls:</p>
|
|
<pre><code>typedef void (*BasBreakpointFnT)(void *ctx, int32_t line);</code></pre>
|
|
<h2>BasModuleT</h2>
|
|
<p>Compiled module produced by the BASIC compiler and loaded into the VM.</p>
|
|
<pre><code>typedef struct {
|
|
uint8_t *code;
|
|
int32_t codeLen;
|
|
BasStringT **constants;
|
|
int32_t constCount;
|
|
int32_t globalCount;
|
|
int32_t entryPoint;
|
|
BasValueT *dataPool;
|
|
int32_t dataCount;
|
|
BasProcEntryT *procs;
|
|
int32_t procCount;
|
|
BasFormVarInfoT *formVarInfo;
|
|
int32_t formVarInfoCount;
|
|
BasDebugVarT *debugVars;
|
|
int32_t debugVarCount;
|
|
BasDebugUdtDefT *debugUdtDefs;
|
|
int32_t debugUdtDefCount;
|
|
} BasModuleT;</code></pre>
|
|
<pre> Field Description
|
|
----- -----------
|
|
code P-code bytecode array.
|
|
codeLen Length of bytecode in bytes.
|
|
constants String constant pool.
|
|
constCount Number of string constants.
|
|
globalCount Number of global variable slots needed.
|
|
entryPoint PC of the first instruction (module-level code).
|
|
dataPool DATA statement value pool (for READ).
|
|
dataCount Number of values in the data pool.
|
|
procs Procedure table (SUBs and FUNCTIONs).
|
|
procCount Number of procedures.
|
|
formVarInfo Per-form variable counts and init code addresses.
|
|
formVarInfoCount Number of forms with form-scoped variables.
|
|
debugVars Variable names and metadata for the debugger.
|
|
debugVarCount Number of debug variable entries.
|
|
debugUdtDefs UDT type definitions for the debugger.
|
|
debugUdtDefCount Number of debug UDT definitions.</pre>
|
|
</div>
|
|
<div class="topic" id="lib.conn">
|
|
<h1>Connection Library</h1>
|
|
<p>One interface for byte-stream connections: a serial port, a modem with an AT command dialer, the secure link to the DVX host proxy (over a COM port or over the network), and Telnet. Every call except the host name lookup is non-blocking: connOpen starts the connection (dialing, TCP connect, key exchange) and connPoll moves it along, fills the receive buffer and sends what connWrite queued. Call connPoll as often as you can (every frame). The lookup of a host name waits for the answer, but other programs keep running meanwhile; a dotted address needs no lookup.</p>
|
|
<p>Loaded as: bin/libs/kpunch/conn/conn.lib (needs the serial and networking libraries)</p>
|
|
<p>Header: conn/conn.h</p>
|
|
<h2>Connection Types</h2>
|
|
<pre> Type Behaviour
|
|
---- ---------
|
|
ConnSerialE A COM port with the given speed, data bits, parity, stop bits and flow control (none, XON/XOFF, RTS/CTS). Open at once.
|
|
ConnModemE The COM port, then the init string (waits for OK), then dial prefix + phone number. CONNECT opens the connection; BUSY, NO ANSWER and NO CARRIER dial again after redialDelay seconds, up to redialCount more times; NO DIAL TONE and ERROR end it. An empty phone number opens the port for typing AT commands. When the carrier (DCD) has been seen and drops, the connection ends. Closing drops DTR, which makes the modem hang up. Everything the modem says is also delivered as received data.
|
|
ConnSecureE The secure link (secLink): the key exchange runs in the background, authenticated when a passphrase is set (both sides must use the same one, or none). Only encrypted, authenticated data on channel 0 is delivered. With host empty it runs on the COM port; with host set it runs over a TCP connection to host:tcpPort (the proxy's listening port, 2323 by default), through secLinkOpenStream.
|
|
ConnTelnetE TCP to host:tcpPort with Telnet (RFC 854). Negotiated: the host may ECHO and SUPPRESS-GO-AHEAD; BINARY both ways; TERMINAL-TYPE answered "ANSI"; NAWS sends the size given to connSetWindowSize (80 x 25 until then, sent again on every change). Other options are refused, and an option is only answered when its state changes. 0xFF is doubled on the way out and undoubled on the way in; outside binary mode a lone CR goes out as CR NUL and CR NUL comes in as CR.</pre>
|
|
<p>The Telnet protocol itself is a separate filter (connTelnet.c) that only moves bytes, so it is tested byte for byte without a network.</p>
|
|
<h2>ConnConfigT</h2>
|
|
<pre> Field Meaning
|
|
----- -------
|
|
type ConnTypeE.
|
|
comPort RS232_COM1 .. RS232_COM4.
|
|
bps, dataBits, parity Line settings (parity 'N', 'E', 'O', 'M' or 'S').
|
|
stopBits, flow 1 or 2 stop bits; RS232_HANDSHAKE_* flow control.
|
|
initString, dialPrefix Modem: sent before dialing (empty = none); dial command such as ATDT.
|
|
phone Modem: the number (empty = no dialing).
|
|
redialCount, redialDelay Modem: extra attempts and the seconds between them.
|
|
passphrase Secure link: the shared secret (empty = none).
|
|
host, tcpPort Telnet; secure link over the network (host set) or over the COM port (host empty). A name or a dotted address.</pre>
|
|
<h2>Functions</h2>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
ConnT *connCreate(void) Allocate a closed connection.
|
|
void connDestroy(c) Close and free.
|
|
void connConfigDefaults(cfg) COM1, 9600 8N1, no flow control, ATZ, ATDT.
|
|
int32_t connOpen(c, cfg, nowMs) Start connecting (cfg is copied). CONN_OK, CONN_ERR_PARAM, CONN_ERR_PORT or CONN_ERR_HOST (the host was not found or refused at once); connStatus explains. Later failures (refused, no answer within 30 seconds, connection lost, host closed it) end the connection with the reason in connStatus.
|
|
void connPoll(c, nowMs) Dial, key exchange, receive, send, timers.
|
|
int32_t connRead(c, buf, max) Take received bytes (0 when none).
|
|
int32_t connWrite(c, data, len) Queue bytes to send; returns the count accepted (0 while not open or when the 8 KB send buffer is full).
|
|
int32_t connPending(c) Bytes queued (here and in the layer below) not yet sent.
|
|
bool connSendBreak(c, nowMs) Hold a break for 300 ms (serial and modem).
|
|
void connClose(c) Close; a modem hangs up.
|
|
ConnStateE connState(c) ConnClosedE, ConnOpeningE (dialing, key exchange) or ConnOpenE.
|
|
const char *connStatus(c) What is happening, or why the connection ended, in plain words.
|
|
uint32_t connStatusSerial(c) Changes whenever the status text changes.
|
|
void connSetWindowSize(c, cols, rows) The terminal size for Telnet's NAWS (sent now if the host asked for it).
|
|
bool connTypeAvailable(type) True for every ConnTypeE value.
|
|
bool connCanBreak(type) True for serial and modem.
|
|
const char *connErrorText(err) Text for a connOpen result.</pre>
|
|
<h2>Example</h2>
|
|
<pre><code>ConnT *c = connCreate();
|
|
ConnConfigT cfg;
|
|
|
|
connConfigDefaults(&cfg);
|
|
cfg.type = ConnModemE;
|
|
snprintf(cfg.phone, sizeof(cfg.phone), "555-0100");
|
|
connOpen(c, &cfg, nowMs());
|
|
|
|
// every frame:
|
|
connPoll(c, nowMs());
|
|
n = connRead(c, buf, sizeof(buf));</code></pre>
|
|
<p>A Telnet session is the same with cfg.type = ConnTelnetE, cfg.host = "bbs.example.org" and cfg.tcpPort = 23.</p>
|
|
<p>A file transfer runs over a connection by giving the transfer library connRead, connWrite and connPending as its XferIoT callbacks (ZMODEM over Telnet works: 0xFF bytes are escaped by the Telnet filter).</p>
|
|
</div>
|
|
<div class="topic" id="lib.dvxnet">
|
|
<h1>Networking Library</h1>
|
|
<p>TCP/IP for DVX programs with the standard BSD socket API: a C programmer who knows sockets writes DVX network code the usual way. IPv4 today; the types and constants for IPv6 (AF_INET6, sockaddr_in6, sockaddr_storage) are defined so code can be written for both.</p>
|
|
<p>Loaded as: bin/libs/kpunch/dvxnet/dvxnet.lib (needs the shell, the task switcher and the core library)</p>
|
|
<p>Headers: dvxnet/dvxNet.h (settings and status), and the standard headers in dvxnet/include: sys/socket.h, netinet/in.h, netinet/tcp.h, arpa/inet.h, netdb.h, sys/select.h, poll.h, sys/ioctl.h and errno.h (the C library's plus the network codes). Put the include directory on the compiler's include path ahead of the C library's. In the SDK they are in include/net, and a project Makefile that sets NET = 1 gets them (see SDK Quick Start).</p>
|
|
<p><a href="#lib.dvxnet.api">The socket API</a></p>
|
|
<p><a href="#lib.dvxnet.waiting">Waiting, polling and cooperative multitasking</a></p>
|
|
<p><a href="#lib.dvxnet.config">Settings and status (dvxNet.h)</a></p>
|
|
<p><a href="#lib.dvxnet.inside">How it works</a></p>
|
|
<p><a href="#lib.dvxnet.basic">Networking from BASIC</a></p>
|
|
<h2>Hardware</h2>
|
|
<p>The network card is reached through a DOS packet driver (FTP Software's specification) loaded before DVX, for example NE2000.COM 0x60 3 0x300 for an NE2000 at I/O 300h, IRQ 3. dvxnet finds it on software interrupts 60h to 80h (or the one set in DVX.INI) and registers for IPv4 and ARP frames. Any card with a packet driver works, and so do SLIP/PPP links through the usual packet-driver shims.</p>
|
|
<p>Without a packet driver the library still runs: 127.0.0.1 works (programs can talk to each other), and connections elsewhere fail with ENETUNREACH.</p>
|
|
<h2>Example</h2>
|
|
<pre><code>#include <arpa/inet.h>
|
|
#include <netdb.h>
|
|
#include <sys/socket.h>
|
|
#include <unistd.h>
|
|
|
|
struct addrinfo hints = { 0 };
|
|
struct addrinfo *res;
|
|
int fd;
|
|
|
|
hints.ai_family = AF_INET;
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
|
|
if (getaddrinfo("bbs.example.org", "telnet", &hints, &res) == 0) {
|
|
fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
|
|
|
if (connect(fd, res->ai_addr, res->ai_addrlen) == 0) {
|
|
send(fd, "hello\r\n", 7, 0);
|
|
}
|
|
|
|
close(fd);
|
|
freeaddrinfo(res);
|
|
}</code></pre>
|
|
<h2>Licence of the TCP/IP stack</h2>
|
|
<p>The protocol engine is lwIP 2.2.1, unmodified, under the 3-clause BSD licence:</p>
|
|
<pre><code>Copyright (c) 2001, 2002 Swedish Institute of Computer Science.
|
|
All rights reserved.
|
|
|
|
Redistribution and use in source and binary forms, with or without
|
|
modification, are permitted provided that the following conditions are met:
|
|
|
|
1. Redistributions of source code must retain the above copyright notice,
|
|
this list of conditions and the following disclaimer.
|
|
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
this list of conditions and the following disclaimer in the documentation
|
|
and/or other materials provided with the distribution.
|
|
3. The name of the author may not be used to endorse or promote products
|
|
derived from this software without specific prior written permission.
|
|
|
|
THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
|
|
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
|
EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
|
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
|
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
|
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
|
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</code></pre>
|
|
</div>
|
|
<div class="topic" id="lib.dvxnet.api">
|
|
<h1>The Socket API</h1>
|
|
<p>Standard names, arguments and meanings. A socket is an ordinary file descriptor: read(), write() and close() work on it, as do fcntl(F_GETFL/F_SETFL, O_NONBLOCK), ioctl(FIONBIO, FIONREAD), select() and poll().</p>
|
|
<pre> Function Notes
|
|
-------- -----
|
|
socket(AF_INET, SOCK_STREAM or SOCK_DGRAM, 0) Starts the network with the saved settings the first time. EAFNOSUPPORT for other families (AF_INET6 included, for now).
|
|
bind, listen, accept listen's backlog is capped at SOMAXCONN (16). An unbound socket listens on a free port. Accepted sockets are blocking (O_NONBLOCK is not inherited, as on Linux).
|
|
connect Blocking: waits for the connection (SO_SNDTIMEO limits the wait: EINPROGRESS). Non-blocking: EINPROGRESS, then poll/select for writable and read SO_ERROR (ECONNREFUSED, ETIMEDOUT, ...). UDP: sets the default peer; AF_UNSPEC removes it.
|
|
send, recv, sendto, recvfrom Flags MSG_PEEK, MSG_DONTWAIT, MSG_WAITALL (TCP), MSG_NOSIGNAL (accepted; there are no signals). MSG_OOB: EOPNOTSUPP. A blocking TCP send returns when everything is queued. recv returns 0 at the end of the stream. UDP messages longer than the buffer are cut; up to 8192 bytes are sent.
|
|
shutdown(SHUT_RD, SHUT_WR, SHUT_RDWR) As usual; the other side reads the end of the stream after SHUT_WR.
|
|
close An orderly close in the background; unread received data makes it a reset (as on BSD).
|
|
setsockopt / getsockopt SO_REUSEADDR, SO_KEEPALIVE, SO_BROADCAST, SO_RCVTIMEO, SO_SNDTIMEO, TCP_NODELAY; SO_ERROR, SO_TYPE, SO_ACCEPTCONN, SO_RCVBUF, SO_SNDBUF (read only: 5840 for TCP, 8192 for UDP); SO_LINGER is accepted and reads as off. Others: ENOPROTOOPT.
|
|
getsockname, getpeername IPv4 addresses.
|
|
select, poll Sockets report real readiness (POLLIN, POLLOUT, POLLERR, POLLHUP); other descriptors always count as ready.
|
|
getaddrinfo, freeaddrinfo, gai_strerror AF_INET or AF_UNSPEC; names, dotted addresses, "localhost"; service numbers or the common names (telnet, http, ftp, smtp, domain, echo, ...). AI_PASSIVE, AI_CANONNAME, AI_NUMERICHOST, AI_NUMERICSERV.
|
|
gethostbyname, gethostbyaddr, h_errno gethostbyaddr does no reverse lookup: the name is the dotted address.
|
|
getnameinfo Numeric host and service; NI_NAMEREQD gives EAI_NONAME.
|
|
gethostname The computer name from the network settings.
|
|
inet_pton, inet_ntop AF_INET (AF_INET6: EAFNOSUPPORT).
|
|
inet_addr, inet_aton, inet_ntoa BSD forms (a.b.c.d, a.b.c, a.b, a; decimal, octal or hex).
|
|
htonl, htons, ntohl, ntohs Inline in <netinet/in.h>; also real functions for code built without optimisation.</pre>
|
|
<h2>Error Codes</h2>
|
|
<p>errno.h adds the network codes: EWOULDBLOCK (the same as EAGAIN), EINPROGRESS, EALREADY, ENOTSOCK, EDESTADDRREQ, EMSGSIZE, EPROTOTYPE, ENOPROTOOPT, EPROTONOSUPPORT, ESOCKTNOSUPPORT, EOPNOTSUPP (ENOTSUP), EPFNOSUPPORT, EAFNOSUPPORT, EADDRINUSE, EADDRNOTAVAIL, ENETDOWN, ENETUNREACH, ENETRESET, ECONNABORTED, ECONNRESET, ENOBUFS, EISCONN, ENOTCONN, ESHUTDOWN, ETIMEDOUT, ECONNREFUSED, EHOSTDOWN, EHOSTUNREACH. The C library's strerror() does not know them; netErrorText(errno) gives plain words for them.</p>
|
|
<h2>Descriptors</h2>
|
|
<p>Sockets are real descriptors below FD_SETSIZE (256), at most 32 at a time. dup() and dup2() refuse socket descriptors (EOPNOTSUPP), lseek gives ESPIPE.</p>
|
|
</div>
|
|
<div class="topic" id="lib.dvxnet.waiting">
|
|
<h1>Waiting, Polling and Cooperative Multitasking</h1>
|
|
<p>DVX runs programs cooperatively, so a call that has to wait never stops the system: blocking connect, accept, recv, send (with a full buffer), select, poll and name lookups wait by running the network and then letting the other tasks run (tsYield), again and again, until they can return. Other programs, and other connections, keep working meanwhile.</p>
|
|
<p>The network also runs from the shell's idle time, so connections progress (and data arrives) while a program is busy with other things; every socket call runs it too. A program that polls non-blocking sockets in its own loop needs nothing else.</p>
|
|
<p>Right after the network starts, DHCP needs a moment to get an address. connect, sendto and name lookups wait for it (up to 20 seconds) instead of failing, so a program started together with DVX can connect at once.</p>
|
|
<h2>Sockets Belong to Programs</h2>
|
|
<p>Each socket belongs to the program that created it (accept gives the new socket to the program that accepts). When a program ends, normally or because it was ended or crashed, the shell closes every socket it still had open, and drops its unfinished name lookups.</p>
|
|
</div>
|
|
<div class="topic" id="lib.dvxnet.config">
|
|
<h1>Settings and Status (dvxNet.h)</h1>
|
|
<p>The settings live in DVX.INI, section [network]: dhcp (true/false), address, netmask, gateway, dns1, dns2, hostname and vector (auto or 0x60-0x80). The Control Panel's Network page edits them.</p>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
int32_t netStart(void) Start with the saved settings unless running: NET_OK, NET_ERR_NO_DRIVER (the stack runs anyway, for 127.0.0.1), NET_ERR_CONFIG or NET_ERR_NO_MEMORY. Socket calls do this themselves.
|
|
void netStop(void) Close every socket and stop (the packet driver is released).
|
|
int32_t netRestart(void) Stop, then start with the saved settings (after they changed).
|
|
void netPoll(void) Take received frames, run the timers, send. Called from the shell's idle time and by every socket call.
|
|
void netGetStatus(NetStatusT *status) State (NetStoppedE, NetNoDriverE, NetConfiguringE, NetUpE), plain-words message, addresses, name servers, host name, packet driver name, interrupt and hardware address, packet and byte counters, frames lost while DVX was busy, open sockets.
|
|
const char *netStateText(int32_t state) Plain words for a state.
|
|
const char *netErrorText(int32_t err) Plain words for an errno value, network codes included.
|
|
void netConfigDefaults(NetConfigT *cfg) DHCP, find the driver, host name DVX.
|
|
void netConfigLoad(NetConfigT *cfg) Read DVX.INI [network].
|
|
void netConfigFromPrefs(h, cfg) Read [network] from an open preferences handle.
|
|
void netConfigToPrefs(h, cfg) Store cfg as [network] in an open handle (the caller saves).
|
|
const char *netConfigProblem(cfg) NULL when usable, else why not in plain words (bad address or mask, name, interrupt).
|
|
bool netParseAddress(text, &addr) Strict dotted IPv4 (four decimal parts 0-255) to network byte order.
|
|
void netCloseAppSockets(int32_t appId) Close every socket of that app (the shell calls this when an app ends).</pre>
|
|
<p>Name servers set in the settings are used even with DHCP; left empty, the DHCP server's are used.</p>
|
|
</div>
|
|
<div class="topic" id="lib.dvxnet.inside">
|
|
<h1>How It Works</h1>
|
|
<pre> Part Source Job
|
|
---- ------ ---
|
|
BSD layer netSock.c, netResolve.c sockaddr and errno handling, descriptors, blocking and timeouts, select/poll, lookups.
|
|
Protocol glue netProto.c One object per socket over lwIP's raw TCP/UDP API: received data stays queued until read (the TCP window reopens only for bytes read), sends copy into lwIP, errors are kept for SO_ERROR.
|
|
Stack netStack.c lwIP start/stop, the Ethernet interface, DHCP, DNS, polling, counters.
|
|
Driver pktDrvDos.c, pktRing.c Packet driver calls through DPMI; a real-mode callback receives frames at interrupt time into a 16-slot ring in conventional memory; the stack takes them out later.
|
|
DOS glue netDos.c Descriptors through DJGPP File System Extensions, fcntl for sockets, the shell hooks (idle poll, app cleanup), orderly shutdown.
|
|
Settings dvxNet.c DVX.INI, status, errors in plain words.</pre>
|
|
<p>lwIP runs without threads (NO_SYS), configured only by port/lwipopts.h: TCP MSS 1460 with a 4-segment window, 16 TCP connections, 48 KB of stack memory, DHCP, DNS (2 servers), ARP, ICMP (ping answers), IP fragments, loopback. Nothing runs lwIP at interrupt time.</p>
|
|
<p>The receive ring holds 16 frames. A burst larger than that while DVX is busy loses frames (counted as "lost" in the status); TCP sends them again.</p>
|
|
<p>On the host the same sources are built into the "net" test suite: a fake packet driver passes frames through the real ring to a second copy of the stack in another process, which plays DHCP, DNS and echo server.</p>
|
|
</div>
|
|
<div class="topic" id="lib.dvxnet.basic">
|
|
<h1>Networking from BASIC</h1>
|
|
<p>net.bas declares simple functions over the socket API (netBas.c): HostLookup$, NetAddress$, NetError$, TcpConnect, TcpListen, TcpAccept, TcpSend, TcpPending, TcpRecv$, TcpConnected, TcpClose, UdpOpen, UdpSend, UdpRecv$, UdpFrom$, UdpClose. A handle is the socket's descriptor + 1, so 0 means failure. Only TcpConnect (up to 20 seconds) and TcpSend wait; everything else returns at once, so a program polls from a Timer. Strings end at a zero byte, so the functions carry text. See the BASIC language reference, Include Libraries.</p>
|
|
</div>
|
|
<div class="topic" id="shell.overview">
|
|
<h1>DVX Shell Library</h1>
|
|
<p>The DVX shell library manages the lifecycle of DXE applications: loading, launching, tracking, and reaping. It provides the bridge between the DVX GUI compositor and dynamically loaded DXE3 application modules.</p>
|
|
<p>Header: dvxshell/shellApp.h</p>
|
|
<p>Loaded as: bin/libs/dvxshell.lib</p>
|
|
<h2>App Model</h2>
|
|
<p>The shell supports two kinds of DXE apps:</p>
|
|
<ul>
|
|
<li>Callback-only (hasMainLoop = false) -- appMain() runs in the shell's task 0, creates windows, registers event callbacks, and returns immediately. The app lives through GUI callbacks. Lifecycle ends when the last window is closed. Simpler and cheaper (no extra stack/task).</li>
|
|
<li>Main-loop (hasMainLoop = true) -- A dedicated cooperative task is created. appMain() runs in that task and can do its own polling loop, calling tsYield() to share CPU. Lifecycle ends when appMain() returns or the task is killed. Needed for terminal emulators, games, or long computations.</li>
|
|
</ul>
|
|
<p>Both types use the same DXE interface: an exported appDescriptor and appMain function.</p>
|
|
<h2>DXE Interface</h2>
|
|
<p>Every .app DXE module must export these symbols (COFF convention uses leading underscore):</p>
|
|
<pre> Symbol Type Required
|
|
------ ---- --------
|
|
_appDescriptor AppDescriptorT Yes
|
|
_appMain int32_t (*)(DxeAppContextT *) Yes
|
|
_appShutdown void (*)(void) No</pre>
|
|
<p>The shell resolves appDescriptor at dlopen time; if missing, the load fails with an error dialog. If its abiVersion is not the shell's DVX_ABI_VERSION the app was built against other headers: the load fails with "This program was built for a different version of DVX and cannot run." and both numbers go to DVX.LOG. appMain is called immediately for callback-only apps or from the new task for main-loop apps. appShutdown, if present, is called during graceful reap (but not during force kill from crashed apps).</p>
|
|
<h2>Callback-Only vs Main-Loop Apps</h2>
|
|
<p>The app descriptor's hasMainLoop flag selects between two very different lifecycles:</p>
|
|
<ul>
|
|
<li>hasMainLoop = false (callback-only). The shell calls appMain directly on task 0 at dlopen time. The function creates windows, registers event callbacks, and returns. After that the app has no executing thread of its own -- it exists purely through GUI callbacks dispatched by dvxUpdate. The shell reaps the app automatically when its last window closes. Best for modal tools, dialogs, and event-driven utilities.</li>
|
|
<li>hasMainLoop = true (main-loop). The shell creates a dedicated cooperative task (via tsCreate) with the descriptor's stackSize (or TS_DEFAULT_STACK_SIZE) and priority. appMain runs in that task and can do its own work loop, calling tsYield or any GUI function that yields. The app terminates when appMain returns (the wrapper sets AppStateTerminatingE) or when forced via shellForceKillApp. Best for terminal emulators, games, and any app with continuous background work.</li>
|
|
</ul>
|
|
<p>Both app types use the same export interface; only the descriptor's flags differ. Apps cannot switch modes at runtime.</p>
|
|
<h2>Icon Conventions</h2>
|
|
<p>Shell-level UI (Program Manager, Task Manager) displays app icons at 16x16 and 32x32. Icons are not handled by the shell itself; each app embeds its own icons via the DVX resource system (DVX_RES_ICON):</p>
|
|
<ul>
|
|
<li>16x16 BMP for toolbar entries and list rows</li>
|
|
<li>32x32 BMP for desktop shortcuts and Program Manager tiles</li>
|
|
</ul>
|
|
<p>The Program Manager reads the app's 32x32 icon resource when building shortcut tiles. Apps without an icon resource fall back to a default shell-provided bitmap.</p>
|
|
<h2>State Machine</h2>
|
|
<p>App slots progress through four states:</p>
|
|
<pre><code>Free -> Loaded -> Running -> Terminating -> Free</code></pre>
|
|
<p>LoadedE is transient (only during shellLoadApp before the entry point is called). TerminatingE means the app's task has exited, or a force kill was deferred (killPending) because the app's code was still running, but cleanup has not yet occurred. The shell's main loop reaps terminating apps each frame via shellReapApps().</p>
|
|
<h2>Contents</h2>
|
|
<p><a href="#shell.types">Types and Constants</a></p>
|
|
<p><a href="#shell.lifecycle">Lifecycle API</a></p>
|
|
<p><a href="#shell.query">Query API</a></p>
|
|
<p><a href="#shell.startup">Starting Up</a></p>
|
|
<p><a href="#shell.config">Configuration API</a></p>
|
|
<p><a href="#shell.desktop">Desktop Callbacks</a></p>
|
|
<p><a href="#shell.info">System Information</a></p>
|
|
<p><a href="#shell.programs">Installed Programs</a></p>
|
|
<p><a href="#shell.run">Running Programs and Documents</a></p>
|
|
<p><a href="#shell.assoc">File Associations</a></p>
|
|
</div>
|
|
<div class="topic" id="shell.types">
|
|
<h1>Types and Constants</h1>
|
|
<h2>Constants</h2>
|
|
<pre> Constant Value Description
|
|
-------- ----- -----------
|
|
SHELL_APP_NAME_MAX 64 Maximum length of an app name string.
|
|
SHELL_STACK_DEFAULT 0 Use in AppDescriptorT.stackSize to get the default task stack size.
|
|
SHELL_DESKTOP_APP "apps/kpunch/progman/progman.app" Default desktop application path.
|
|
SHELL_INI_SECTION "shell" DVX.INI section of the shell's own keys.
|
|
SHELL_INI_DESKTOP "desktop" [shell] key: the desktop app (SHELL_DESKTOP_APP when missing).
|
|
SHELL_INI_WELCOME "welcome" [shell] key: a program started after the desktop while the key exists (see Starting Up).
|
|
SHELL_HELP_TOPIC_MAX 128 Capacity of DxeAppContextT.helpTopic.
|
|
DVX_HELP_VIEWER_APP_LITERAL "APPS\KPUNCH\DVXHELP\DVXHELP.APP" Path of the help viewer app.
|
|
DVX_SYSTEM_HELP_FILE_LITERAL "APPS\KPUNCH\PROGMAN\DVXHELP.HLP" Path of the system help file (F1 fallback, Program Manager's Help menu).</pre>
|
|
<h3>SHELL_APP_CALL</h3>
|
|
<pre><code>SHELL_APP_CALL(app, call);</code></pre>
|
|
<p>Brackets a direct shell call into an app's code (appMain of a callback-only app, appShutdown, onHelpQuery, onNewArgs). It counts the call on the calling task (shellAppCallEnter / shellAppCallLeave), so a force kill issued from inside that call is deferred until the call has returned.</p>
|
|
<h2>AppDescriptorT</h2>
|
|
<p>Exported by every DXE app as a global named appDescriptor. The shell reads it at load time to determine how to launch the app. Declare it with SHELL_APP_DESCRIPTOR, which names the global and fills abiVersion:</p>
|
|
<pre><code>SHELL_APP_DESCRIPTOR(
|
|
.name = "Clock",
|
|
.hasMainLoop = true,
|
|
.multiInstance = true,
|
|
.stackSize = SHELL_STACK_DEFAULT,
|
|
.priority = TS_PRIORITY_LOW
|
|
);</code></pre>
|
|
<pre> Field Type Description
|
|
----- ---- -----------
|
|
abiVersion uint32_t DVX_ABI_VERSION the app was built with (always the first field). The shell refuses the app when it differs from its own.
|
|
name[SHELL_APP_NAME_MAX] char[] Display name of the application.
|
|
hasMainLoop bool false = callback-only, true = dedicated task.
|
|
multiInstance bool true = allow multiple simultaneous instances via temp copy.
|
|
stackSize int32_t SHELL_STACK_DEFAULT or explicit byte count for the task stack.
|
|
priority int32_t Task priority, TS_PRIORITY_LOW..TS_PRIORITY_HIGH (0..10). Any other value makes the launch fail.</pre>
|
|
<h2>DxeAppContextT</h2>
|
|
<p>Passed as the sole argument to appMain(). Gives the app access to the shell's GUI context and its own identity. Heap-allocated by shellLoadApp so its address is stable across reallocations of the internal app slot table (apps may safely cache the pointer in their static globals).</p>
|
|
<pre> Field Type Description
|
|
----- ---- -----------
|
|
shellCtx AppContextT * The shell's GUI context (for creating windows, drawing, etc.).
|
|
appId int32_t This app's unique ID (slot index, 1-based).
|
|
appPath[DVX_MAX_PATH] char[] Full path to the .app DXE file.
|
|
appDir[DVX_MAX_PATH] char[] Directory containing the .app file (for loading resources).
|
|
configDir[DVX_MAX_PATH] char[] Writable config directory (e.g. CONFIG/APPS/KPUNCH/DVXBASIC/).
|
|
args[1024] char[] Launch arguments (empty string if none): blank-separated, one with blanks in double quotes. Walk them with dvxNextArg. A document opened through its file type arrives here (see File Associations); BASIC programs read these arguments with COMMAND$.
|
|
helpFile[DVX_MAX_PATH] char[] Help file path (for F1 context help). Before appMain the shell sets it to <app>.hlp in the .app's directory when that file exists (else empty); appMain may change it.
|
|
helpTopic[128] char[] Current help topic ID (updated by the app at runtime). Size is SHELL_HELP_TOPIC_MAX.
|
|
onHelpQuery void (*)(void *ctx) Optional callback the shell fires on F1 so the app can refresh helpTopic from context.
|
|
helpQueryCtx void * Opaque context pointer passed to onHelpQuery.
|
|
onNewArgs void (*)(void *ctx, const char *args) Optional. Set by an app that can take new launch arguments while running. F1 hands its "helpfile [topic]" arguments to a running help viewer that sets this, and raises it, instead of loading another copy.
|
|
newArgsCtx void * Opaque context pointer passed to onNewArgs.</pre>
|
|
<p>The appDir field is derived from the .app file path at load time so apps can find their own resources via relative paths. This is necessary because the working directory is shared by all apps in DOS. The config directory is mirrored from the app path: an app at APPS/KPUNCH/DVXBASIC/dvxbasic.app gets CONFIG/APPS/KPUNCH/DVXBASIC/. A full path inside the DVX directory (for example one picked with the Task Manager's Run... dialog) is mirrored the same way. An app outside the DVX directory, or loaded by a bare file name, gets CONFIG/APPS/<file name without extension>/.</p>
|
|
<h2>AppStateE</h2>
|
|
<pre> Value Description
|
|
----- -----------
|
|
AppStateFreeE Slot is available for reuse.
|
|
AppStateLoadedE DXE loaded, entry point not yet called (transient).
|
|
AppStateRunningE Entry point called, app is active.
|
|
AppStateTerminatingE Shutdown in progress, awaiting reap.</pre>
|
|
<h2>ShellAppT</h2>
|
|
<p>Per-app slot in the shell's app table. Slot 0 is reserved for the shell itself; apps use slots 1 and above.</p>
|
|
<pre> Field Type Description
|
|
----- ---- -----------
|
|
appId int32_t Unique ID (slot index, 1-based; 0 = shell).
|
|
name[SHELL_APP_NAME_MAX] char[] Display name from AppDescriptorT.
|
|
path[DVX_MAX_PATH] char[] Original DXE file path.
|
|
tempPath[DVX_MAX_PATH] char[] Temp copy path for multi-instance (empty if not a copy).
|
|
dxeHandle void * dlopen() handle for the DXE module.
|
|
state AppStateE Current lifecycle state.
|
|
hasMainLoop bool Whether this app has a dedicated task.
|
|
mainTaskId uint32_t Task ID if hasMainLoop, else 0.
|
|
entryFn int32_t (*)(DxeAppContextT *) Pointer to appMain.
|
|
shutdownFn void (*)(void) Pointer to appShutdown (may be NULL).
|
|
dxeCtx DxeAppContextT * Heap-allocated context (address stable across realloc).
|
|
taskCodeDepth int32_t * Per task id: SHELL_APP_CALL calls into this app still open on that task's stack.
|
|
killPending bool A force kill was deferred; shellReapApps finishes it.</pre>
|
|
</div>
|
|
<div class="topic" id="shell.lifecycle">
|
|
<h1>App Lifecycle API</h1>
|
|
<h2>shellAppInit</h2>
|
|
<pre><code>void shellAppInit(void);</code></pre>
|
|
<p>Initialize the app slot table. Seeds slot 0 (reserved for the shell). Must be called once at startup before any other shell API function.</p>
|
|
<h2>shellLoadApp</h2>
|
|
<pre><code>int32_t shellLoadApp(AppContextT *ctx, const char *path);</code></pre>
|
|
<p>Load and start an app from a DXE file. Returns the app ID (>= 1) on success, or -1 on error.</p>
|
|
<p>For callback-only apps, appMain() runs synchronously and returns before shellLoadApp returns. For main-loop apps, a cooperative task is created and the app begins running on the next tsYield().</p>
|
|
<p>If multiInstance is false in the app's descriptor and the same DXE is already loaded, the call fails with an error dialog. If multiInstance is true, the DXE is copied to a temp file so dlopen gets an independent code and data image.</p>
|
|
<h2>shellLoadAppWithArgs</h2>
|
|
<pre><code>int32_t shellLoadAppWithArgs(AppContextT *ctx, const char *path, const char *args);</code></pre>
|
|
<p>Load and run an app with arguments. The args string is copied into DxeAppContextT.args before appMain() is called. Otherwise identical to shellLoadApp().</p>
|
|
<h2>shellReapApps</h2>
|
|
<pre><code>bool shellReapApps(AppContextT *ctx);</code></pre>
|
|
<p>Scan for and reap finished apps. Call once per frame from the main loop.</p>
|
|
<p>Returns true if any apps were reaped, so the caller can trigger a desktop refresh. For main-loop apps, termination is detected by the AppStateTerminatingE state (set when appMain returns). For callback-only apps, termination is detected when no windows remain for that app.</p>
|
|
<h2>shellReapApp</h2>
|
|
<pre><code>void shellReapApp(AppContextT *ctx, ShellAppT *app);</code></pre>
|
|
<p>Gracefully shut down a single app. Calls the app's shutdownFn (if present), destroys all windows belonging to the app, kills its task (if any), releases any widget classes and system-wide hotkeys the app registered and did not unregister (wgtUnregisterAppClasses, dvxUnregisterAppHotkeys), closes the DXE handle, and frees the context. The slot returns to AppStateFreeE.</p>
|
|
<h2>shellForceKillApp</h2>
|
|
<pre><code>void shellForceKillApp(AppContextT *ctx, ShellAppT *app);</code></pre>
|
|
<p>Forcibly kill an app. Used by the Task Manager "End Task" or as part of crash recovery. The app's shutdownFn is NOT called: a force kill targets a hung or faulting app whose own code cannot be trusted to run. Instead the shell drops every idle and desktop-update callback the app registered, destroys all of the app's windows, kills its task (for main-loop apps), and finally closes the DXE handle.</p>
|
|
<p>Cleanup order: callbacks and windows first (removes them from the compositor), then the task (frees its stack; any dispatch frames still open on it are dropped from the context's dispatch depth), then the widget classes and system-wide hotkeys the app registered (its shutdownFn never got the chance to unregister them), then the DXE handle (unmaps the code). Closing the DXE before destroying windows would cause callbacks into unmapped code.</p>
|
|
<p>If the app's code may still be running underneath the call -- End Task clicked in a dialog the app has open, a kill issued from the app's own task, or from inside a shell call into the app -- the unload is deferred: the app's windows are hidden and queued for destruction, the slot is marked Terminating, and the shell finishes the kill (still without shutdownFn) at the next safe point of its main loop, once that code has returned. Event-handler frames on the app's own task do not hold the kill back, since they end with the task: an app stuck in an endless event handler is killed at once (or at the next main-loop pass).</p>
|
|
<h2>shellOpenHelp</h2>
|
|
<pre><code>void shellOpenHelp(AppContextT *ctx, const char *args);</code></pre>
|
|
<p>Show help. args is "helpfile [topicId]"; empty or NULL args open the system help. A running help viewer that accepts new arguments (onNewArgs) is given them and raised; otherwise a new viewer is loaded. F1 and application Help menus both use it, so a Help > Contents command can pass ctx->helpFile as is.</p>
|
|
<h2>shellTaskDepthForget / shellTaskDepthShare / shellAppCodeForgetTask</h2>
|
|
<pre><code>void shellTaskDepthForget(uint32_t taskId);
|
|
int32_t shellTaskDepthShare(uint32_t taskId);
|
|
void shellAppCodeForgetTask(uint32_t taskId);</code></pre>
|
|
<p>The context's dispatch depth counts event-dispatch frames on every task's stack. shellTaskDepthShare returns one task's share. When a task is killed its frames can never unwind: shellTaskDepthForget removes its share, and (through shellAppCodeForgetTask) the SHELL_APP_CALL calls it held. Crash recovery does the same for the crashed task and the shell's own task.</p>
|
|
<h2>shellTerminateAllApps</h2>
|
|
<pre><code>void shellTerminateAllApps(AppContextT *ctx);</code></pre>
|
|
<p>Force-kill all running apps. Called during shell shutdown, after the main loop has exited, so every active app is released immediately (never deferred) and no shutdownFn runs. By then Exit DVX (below) has already given every app except the desktop the chance to close itself; what is left is the desktop app and apps that had no visible window to close.</p>
|
|
<h2>Exit DVX: shellRequestExit / shellRunExitRequest / shellCloseAllApps</h2>
|
|
<pre><code>void shellRequestExit(void);
|
|
void shellRunExitRequest(AppContextT *ctx, int32_t keepAppId);
|
|
bool shellCloseAllApps(AppContextT *ctx, int32_t keepAppId);</code></pre>
|
|
<p>Exiting DVX never closes an app without first asking it to close, the way the user would. The desktop app's Exit command (Program Manager's File > Exit DVX and its window's close box, after its own "Are you sure?" confirmation) calls shellRequestExit, which only records the request. The shell's main loop calls shellRunExitRequest after every frame; when a request is pending it clears it, runs shellCloseAllApps for every app except the desktop, and calls dvxQuit only if that returns true. Otherwise DVX keeps running.</p>
|
|
<p>shellCloseAllApps asks the running apps to close one at a time, in app-slot order, exactly as if the user had closed their windows: for each app, its oldest visible window is raised and dvxRequestWindowClose is called on it inside a dispatch frame, so the window's onClose runs just as it does for the close box or Alt+F4. A C app's onClose shows its own save prompt; a BASIC app's form runs QueryUnload and Unload. This repeats with the app's next visible window until it has none left or the app has ended. Windows that hide on close (tool palettes) count as closed, and hidden windows are left alone. Apps that close are reaped normally (their shutdownFn runs). Between requests the shell keeps pumping frames (dvxUpdate, a task yield, shellReapApps), so main-loop apps can react.</p>
|
|
<p>A window that is still open after a request is judged as follows:</p>
|
|
<ul>
|
|
<li>If the app created a window while handling the request (ctx->windowsCreated changed -- its save prompt) and no dialog box is still open, the user chose to keep it (Cancel). shellCloseAllApps returns false at once: that app and every app not yet asked keep running untouched. Apps already closed stay closed.</li>
|
|
<li>If the app showed nothing and still has the window after 5 seconds, it is not responding. The shell asks "'name' is not responding to the request to close. End it and continue exiting DVX?". Yes ends it with shellForceKillApp (no shutdownFn) and the exit continues; No returns false.</li>
|
|
</ul>
|
|
<p>shellCloseAllApps also returns false, without asking any app, when a dialog box is already open (ctx->modalWindow): the dialog is raised so it can be answered first. It pumps the event loop, so call it only from the shell's top level, never from inside a callback. An app ended from the Task Manager while the sequence runs simply counts as closed.</p>
|
|
</div>
|
|
<div class="topic" id="shell.query">
|
|
<h1>Query API</h1>
|
|
<h2>shellGetApp</h2>
|
|
<pre><code>ShellAppT *shellGetApp(int32_t appId);</code></pre>
|
|
<p>Look up an app slot by ID. Returns a pointer to the ShellAppT, or NULL if the ID is out of range or the slot is free. Valid app IDs are 1 through shellAppSlotCount() - 1.</p>
|
|
<h2>shellAppSlotCount</h2>
|
|
<pre><code>int32_t shellAppSlotCount(void);</code></pre>
|
|
<p>Return the total number of app slots (including slot 0). Use as the iteration bound when scanning all slots. Slot 0 is the shell itself; app slots start at 1.</p>
|
|
<h2>shellRunningAppCount</h2>
|
|
<pre><code>int32_t shellRunningAppCount(void);</code></pre>
|
|
<p>Count running apps, not counting the shell itself. Includes apps in both AppStateLoadedE and AppStateRunningE states.</p>
|
|
<h2>shellAppFindByPath</h2>
|
|
<pre><code>ShellAppT *shellAppFindByPath(const char *path);</code></pre>
|
|
<p>Return an active app slot loaded from path, or NULL. Paths are compared with shellPathsMatch.</p>
|
|
<h2>shellPathsMatch</h2>
|
|
<pre><code>bool shellPathsMatch(const char *a, const char *b);</code></pre>
|
|
<p>True when two paths name the same file under DOS rules: case-insensitive, with / and \ the same separator.</p>
|
|
</div>
|
|
<div class="topic" id="shell.startup">
|
|
<h1>Starting Up</h1>
|
|
<p>shellMain brings DVX up in this order: preferences (DVX.INI), the task system, the GUI in the saved display mode, the desktop app, then the start-up programs.</p>
|
|
<h2>Safe Video Mode</h2>
|
|
<p>The GUI starts in the [video] mode of DVX.INI (prefsGetVideoMode). If that mode cannot be set (dvxInit fails), or a Shift key is held down while DVX starts (read with platformKeyboardGetModifiers just before the mode is set), or DVX was started with /SAFE, the shell starts in the built-in default mode instead (DVX_DEFAULT_VIDEO_W/H/BPP: 640x480, 16 bpp or the closest mode the card has). Before the desktop loads, a message box says so and asks whether to use that mode from now on: Yes writes it to DVX.INI (prefsSetVideoMode, prefsSave); No leaves DVX.INI alone, so the saved mode is tried again next time. If the default mode fails too, DVX does not start.</p>
|
|
<h2>Start-up Programs</h2>
|
|
<p>After the desktop app has loaded:</p>
|
|
<ul>
|
|
<li>When the DVX folder cannot be written to (platformIsReadOnly: the loader found it read-only, as on a CD-ROM), a message says once that changed settings last until DVX exits and that the help is used as it is.</li>
|
|
<li>The program named by DVX.INI [shell] welcome (SHELL_INI_WELCOME), if the key is there. The shipped DVX.INI names the Welcome window, which removes the key when the user ticks "Don't show this again", so the key itself is the "first start" flag.</li>
|
|
<li>The program given with /RUN=path on the DVX command line (for example DVX /RUN=APPS\KPUNCH\NOTEPAD\NOTEPAD.APP).</li>
|
|
</ul>
|
|
<p>Both are started with shellRunCommand, so a missing program is reported to the user.</p>
|
|
</div>
|
|
<div class="topic" id="shell.config">
|
|
<h1>Configuration API</h1>
|
|
<p>Each app has a per-app configuration directory derived from its DXE path, mirrored under CONFIG/. For example, an app at APPS/KPUNCH/DVXBASIC/dvxbasic.app gets the config directory CONFIG/APPS/KPUNCH/DVXBASIC/.</p>
|
|
<h2>shellEnsureConfigDir</h2>
|
|
<pre><code>int32_t shellEnsureConfigDir(const DxeAppContextT *ctx);</code></pre>
|
|
<p>Ensure the app's config directory exists, creating all parent directories as needed. Returns 0 on success, -1 on failure.</p>
|
|
<p>Call this before writing any config files. The directory path comes from ctx->configDir.</p>
|
|
<h2>shellConfigPath</h2>
|
|
<pre><code>void shellConfigPath(const DxeAppContextT *ctx, const char *filename, char *outPath, int32_t outSize);</code></pre>
|
|
<p>Build a full path to a file in the app's config directory by joining ctx->configDir with the given filename.</p>
|
|
<pre><code>// Example:
|
|
char path[DVX_MAX_PATH];
|
|
shellConfigPath(ctx, "settings.ini", path, sizeof(path));
|
|
// -> "CONFIG/APPS/KPUNCH/PROGMAN/settings.ini"</code></pre>
|
|
<h2>Random Seed File</h2>
|
|
<p>When the serial library is loaded, the shell feeds keyboard and mouse event timings to its random-number generator (dvxSetInputEntropyHook) and carries generator state across runs in CONFIG/SECLINK.RND: the file is read and immediately rewritten at startup, and rewritten again at shutdown. The library is found at run time; without it no seed file is written.</p>
|
|
</div>
|
|
<div class="topic" id="shell.desktop">
|
|
<h1>Desktop Callbacks</h1>
|
|
<p>The shell wires several AppContextT event hooks (onCtrlEsc, onF1, onTitleChange, idleCallback) during startup and exposes them to other DXEs via function-pointer extern and registration API. Desktop managers register a callback to refresh their display when apps come and go. The Task Manager DXE registers its Ctrl+Esc handler via the shellCtrlEscFn extern pointer (see Task Manager documentation).</p>
|
|
<h2>Shell Event Hooks</h2>
|
|
<pre> AppContextT field Handler Purpose
|
|
----------------- ------- -------
|
|
idleCallback shellIdleDispatch Runs every registered idle handler (see shellRegisterIdle), then yields to cooperative tasks, when dvxUpdate has no input events and no dirty rects.
|
|
onCtrlEsc ctrlEscHandler Invokes shellCtrlEscFn if the Task Manager DXE is loaded.
|
|
onF1 f1HelpHandler Opens the focused app's helpFile/helpTopic (or the system help) in the help viewer, reusing a running viewer that sets onNewArgs.
|
|
onTitleChange titleChangeHandler Runs shellDesktopUpdate so Program Manager window lists refresh.</pre>
|
|
<h2>shellRegisterDesktopUpdate</h2>
|
|
<pre><code>void shellRegisterDesktopUpdate(void (*updateFn)(void));</code></pre>
|
|
<p>Register a callback for app state change notifications. Multiple callbacks are supported; registering the same callback again has no effect. Apps typically call this during appMain() to receive notifications. A callback may register or unregister callbacks (including itself) while notifications are being delivered.</p>
|
|
<h2>shellUnregisterDesktopUpdate</h2>
|
|
<pre><code>void shellUnregisterDesktopUpdate(void (*updateFn)(void));</code></pre>
|
|
<p>Remove a previously registered callback. Call this before app shutdown to avoid dangling function pointers.</p>
|
|
<h2>shellRegisterIdle</h2>
|
|
<pre><code>void shellRegisterIdle(void (*fn)(void *ctx), void *ctx);</code></pre>
|
|
<p>Register an idle handler invoked every idle frame by the shell main loop, regardless of which app is foreground. Background pollers (serial and secLink) register here so their connections keep pumping while another app is in front. Registration is idempotent on the (fn, ctx) pair. The registering module MUST unregister before it is unloaded -- app DXEs do this from their _appShutdown hook on a graceful exit. On a force kill (which does not run _appShutdown) the shell drops the app's registrations itself.</p>
|
|
<h2>shellUnregisterIdle</h2>
|
|
<pre><code>void shellUnregisterIdle(void (*fn)(void *ctx), void *ctx);</code></pre>
|
|
<p>Remove a previously registered idle handler. No-op if the (fn, ctx) pair is absent, so a double-unregister is harmless.</p>
|
|
<h2>shellRegisterAppCleanup</h2>
|
|
<pre><code>typedef void (*ShellAppCleanupFnT)(int32_t appId);
|
|
|
|
void shellRegisterAppCleanup(ShellAppCleanupFnT fn);
|
|
void shellUnregisterAppCleanup(ShellAppCleanupFnT fn);</code></pre>
|
|
<p>Register a function the shell calls with an app's id whenever that app ends -- normally, force-killed or after a crash -- once its windows are gone, next to the shell's own release of the app's widget classes and hotkeys. A library uses it to release what the app left open with it: the networking library closes the app's sockets this way. For library code that stays loaded; registering the same function again has no effect, and unregistering one that is not registered does nothing.</p>
|
|
<h2>shellDesktopUpdate</h2>
|
|
<pre><code>void shellDesktopUpdate(void);</code></pre>
|
|
<p>Notify all registered desktop callbacks that app state has changed. Called internally by the shell after loading or reaping an app. Can also be called by apps that need to trigger a desktop refresh.</p>
|
|
<h2>shellCtrlEscFn</h2>
|
|
<pre><code>extern void (*shellCtrlEscFn)(AppContextT *ctx);</code></pre>
|
|
<p>Function pointer set by the taskmgr DXE's constructor. The shell calls this when Ctrl+Esc is pressed. NULL if the task manager is not loaded.</p>
|
|
</div>
|
|
<div class="topic" id="shell.info">
|
|
<h1>System Information</h1>
|
|
<p>Header: dvxshell/shellInf.h</p>
|
|
<p>Thin wrapper around the platform layer's hardware detection. Gathers system information at startup, logs it, and caches the result for display in dialogs.</p>
|
|
<h2>shellInfoInit</h2>
|
|
<pre><code>void shellInfoInit(AppContextT *ctx);</code></pre>
|
|
<p>Gather all hardware information via the platform layer, log each line to DVX.LOG, and store the result for later retrieval. Call once after dvxInit().</p>
|
|
<h2>shellGetSystemInfo</h2>
|
|
<pre><code>const char *shellGetSystemInfo(void);</code></pre>
|
|
<p>Return the formatted system information text. The returned pointer is valid for the lifetime of the process (static buffer in the platform layer).</p>
|
|
</div>
|
|
<div class="topic" id="shell.programs">
|
|
<h1>Installed Programs</h1>
|
|
<p>Header: dvxshell/shellProg.h</p>
|
|
<p>Finds the installed programs (every .app file under APPS, SHELL_PROG_DIR) and reads what they are called and what they look like, for launchers, file managers and setup programs. A program's display name and description are its "name" and "description" text resources (the capitalized file name when it has no name resource); its picture is its "icon32" resource.</p>
|
|
<pre> Constant Value Meaning
|
|
-------- ----- -------
|
|
SHELL_PROG_DIR "apps" Folder, relative to the DVX root, holding every program.
|
|
SHELL_PROG_EXT ".app" A program's file extension.
|
|
SHELL_PROG_DESC_MAX 128 ShellProgramT.description capacity.
|
|
SHELL_PROG_ICON 32 Large icon edge in pixels.
|
|
SHELL_PROG_ICON_SMALL 16 Small icon edge in pixels.</pre>
|
|
<pre><code>typedef struct {
|
|
char path[DVX_MAX_PATH];
|
|
char name[SHELL_APP_NAME_MAX];
|
|
char description[SHELL_PROG_DESC_MAX];
|
|
} ShellProgramT;</code></pre>
|
|
<h2>shellProgramScan</h2>
|
|
<pre><code>int32_t shellProgramScan(const char *dir, const char *skipPath, bool readInfo, ShellProgressFnT progress, void *progressCtx, ShellProgramT **outList);</code></pre>
|
|
<p>Every program under dir (NULL for SHELL_PROG_DIR) and its subfolders, except skipPath (compared with shellPathsMatch; NULL for none), in the order found. With readInfo each entry is filled by shellProgramInfo; without it only path is set, which is fast (no program file is opened) and enough to notice programs being added or removed. progress, when not NULL, is called with progressCtx after each program (for example to keep the screen responsive). Returns the count; *outList is an array of that many entries, NULL when there are none. Free it with shellProgramListFree.</p>
|
|
<h2>shellProgramInfo</h2>
|
|
<pre><code>void shellProgramInfo(const char *path, ShellProgramT *out);</code></pre>
|
|
<p>Fill out with the program's path, display name and description. Always succeeds: without resources (or for a file that is not a program) the name is the file name without its extension, first letter capitalized, and the description is empty.</p>
|
|
<h2>shellProgramIcon</h2>
|
|
<pre><code>uint8_t *shellProgramIcon(AppContextT *ctx, const char *path, int32_t edge, int32_t *outPitch);</code></pre>
|
|
<p>A picture edge x edge pixels in display format, for drawing with rectCopy: a program's icon32 resource, or any image file dvxLoadImage reads (BMP, PNG, JPEG, GIF). Other sizes are scaled (nearest pixel). Returns NULL when there is no picture. Free with dvxFreeImage.</p>
|
|
<h2>shellProgramListFree / shellIsProgram</h2>
|
|
<pre><code>void shellProgramListFree(ShellProgramT *list);
|
|
bool shellIsProgram(const char *path);</code></pre>
|
|
<p>shellProgramListFree frees a list from shellProgramScan (NULL is fine). shellIsProgram is true when path has the .app extension (any case).</p>
|
|
</div>
|
|
<div class="topic" id="shell.run">
|
|
<h1>Running Programs and Documents</h1>
|
|
<p>Header: dvxshell/shellRun.h</p>
|
|
<p>One way for every program to start another (Program Manager's items and Run..., the Task Manager's Run..., File Manager, setup programs). A command line is a program, in quotes if it contains blanks, followed by its arguments; it is split with dvxNextArg, the same rule a program uses to read its own arguments. A document in place of a program opens in the program associated with its file type (see File Associations).</p>
|
|
<h2>shellRunProgram</h2>
|
|
<pre><code>int32_t shellRunProgram(AppContextT *ctx, const char *program, const char *args, const char *folder);</code></pre>
|
|
<p>Start program with args. A .app is loaded with shellLoadAppWithArgs, its arguments first passed through shellResolveArgs(folder, ...). Anything else is a document: it is looked for in folder first (when relative), must exist, and is opened with shellAssocCommand's program and arguments. Problems are shown to the user in an error box (a missing document, a document type with no association; load errors come from shellLoadApp as usual). Returns the new application id, or -1.</p>
|
|
<h2>shellRunCommand</h2>
|
|
<pre><code>int32_t shellRunCommand(AppContextT *ctx, const char *command);</code></pre>
|
|
<p>shellSplitCommand, then shellRunProgram with no working folder. Returns the application id or -1 (also for an empty command).</p>
|
|
<h2>shellRunDialog</h2>
|
|
<pre><code>bool shellRunDialog(AppContextT *ctx, const char *const *history, int32_t historyCount, char *outCommand, int32_t outSize);</code></pre>
|
|
<p>The standard Run dialog: an Open box listing history (newest first, the first one preselected) with a Browse... button. Returns true with the trimmed, non-empty command in outCommand when the user chose OK; OK with an empty box does nothing. The caller runs the command (shellRunCommand) and keeps its own history. SHELL_RUN_CMD_MAX (512) is a suitable outCommand size.</p>
|
|
<h2>shellSplitCommand</h2>
|
|
<pre><code>bool shellSplitCommand(const char *command, char *program, int32_t programSize, char *args, int32_t argsSize);</code></pre>
|
|
<p>Split a command line into the program (quotes removed) and the rest, trimmed. False, with both outputs empty, when there is no program.</p>
|
|
<h2>shellResolveArgs</h2>
|
|
<pre><code>void shellResolveArgs(const char *folder, const char *args, char *out, int32_t outSize);</code></pre>
|
|
<p>Copy args to out with blanks normalized, replacing every relative argument that names an existing file inside folder by folder\name, so a program finds the files an item's arguments name no matter where it runs. Quoted arguments keep their quotes. With an empty or NULL folder the arguments are only normalized. DVX programs share one current folder, so this is how a program item's working folder takes effect.</p>
|
|
</div>
|
|
<div class="topic" id="shell.assoc">
|
|
<h1>File Associations</h1>
|
|
<p>Header: dvxshell/shellAssoc.h</p>
|
|
<p>Which program opens each kind of document. One store serves every program: CONFIG\ASSOC.INI (SHELL_ASSOC_INI_PATH), section [associations]. Each key is an extension without the dot, upper case; each value is a program path relative to the DVX root, optionally followed by ,arguments where %1 stands for the document:</p>
|
|
<pre><code>[associations]
|
|
TXT = apps/kpunch/notepad/notepad.app
|
|
FRM = apps/kpunch/dvxbasic/dvxbasic.app,%1</code></pre>
|
|
<p>DVX ships a default ASSOC.INI; users change it on the Control Panel's File Types page, and a setup program registers its own types with shellAssocSet. The store is read once through dvxPrefs and kept; every change is saved at once with the crash-safe rewrite, and a failed save drops the cached copy so the next call rereads the file. Extension arguments may have a leading dot and any case.</p>
|
|
<h2>Looking Up</h2>
|
|
<pre><code>bool shellAssocLookup(const char *extension, char *programPath, int32_t programPathSize);
|
|
int32_t shellAssocCount(void);
|
|
bool shellAssocGet(int32_t index, char *extension, int32_t extensionSize, char *programPath, int32_t programPathSize);</code></pre>
|
|
<p>shellAssocLookup gives the stored value (program plus any ,arguments) for an extension; false when there is none (or its value is empty). shellAssocCount and shellAssocGet walk every association in file order (index 0 to count-1); the extension comes back as written in the file.</p>
|
|
<h2>Opening a Document</h2>
|
|
<pre><code>bool shellAssocCommand(const char *documentPath, char *program, int32_t programSize, char *args, int32_t argsSize);
|
|
bool shellAssocOpenDocument(AppContextT *ctx, const char *documentPath);</code></pre>
|
|
<p>shellAssocCommand builds the program and argument string that open a document: the value is split at the first comma, every %1 in the arguments becomes documentPath, and without arguments documentPath alone is passed (in double quotes when it contains a blank, so dvxNextArg and COMMAND$(1) still return it whole). A program that opens documents takes the first argument of its DxeAppContextT.args (dvxNextArg) as the file to open when it starts; a BASIC program uses COMMAND$(1). shellAssocOpenDocument does that and starts the program; false when there is no association or the program does not start. It shows nothing to the user (shellRunProgram does, for a document it cannot open).</p>
|
|
<h2>Changing</h2>
|
|
<pre><code>bool shellAssocSet(const char *extension, const char *programPath);
|
|
bool shellAssocRemove(const char *extension);
|
|
bool shellAssocNormalize(const char *extension, char *out);</code></pre>
|
|
<p>shellAssocSet adds or replaces an association (programPath may carry ,arguments) and saves; false for a bad extension, an empty program, or a failed save. shellAssocRemove removes one and saves; false when it did not exist or the save failed. shellAssocNormalize writes the extension as the store keys it (SHELL_ASSOC_EXT_MAX bytes: no dot, upper case) and is false for an empty extension, one longer than eight characters, or anything but letters and digits.</p>
|
|
</div>
|
|
<div class="topic" id="sql.overview">
|
|
<h2>dvxSql -- SQL Database Interface</h2>
|
|
<p>High-level wrapper around SQLite3 for DVX applications. Manages database connections and result set cursors via integer handles so BASIC code never touches raw pointers. All handles are 1-based; 0 indicates an error or invalid handle.</p>
|
|
<p>Header: sql/dvxSql.h</p>
|
|
<h3>SQLite Wrapping</h3>
|
|
<p>dvxSql wraps the bundled SQLite 3.53.4 amalgamation (sql/thirdparty/sqlite3/sqlite3.c and sqlite3.h; origin, checksum and build options in sql/thirdparty/sqlite3/DVX-VERSION.md). It manages two internal dynamic tables keyed by 1-based handles:</p>
|
|
<ul>
|
|
<li>Database table: each slot holds a sqlite3 * plus a per-database error string and affected-row count.</li>
|
|
<li>Cursor table: each slot holds a sqlite3_stmt *, the owning database handle, and EOF tracking.</li>
|
|
</ul>
|
|
<p>Growing either table doubles its capacity. Closed slots are recycled by subsequent dvxSqlOpen or dvxSqlQuery calls, keeping handle values stable for the caller.</p>
|
|
<h3>SQLite Build and File Layer</h3>
|
|
<p>SQLite is compiled with the options listed in sql/thirdparty/sqlite3/sqliteOpts.mk (the host test build uses the same list). The ones that change what programs see:</p>
|
|
<ul>
|
|
<li>Double-quoted text is always an identifier (SQLITE_DQS=0): write string literals in single quotes. SELECT "abc" fails with "no such column: abc".</li>
|
|
<li>The JSON functions, WAL journal mode, loadable extensions, shared cache, UTF-16 APIs and deprecated APIs are left out. PRAGMA journal_mode = WAL leaves the database in rollback-journal (DELETE) mode.</li>
|
|
<li>Temporary tables, sorts and statement journals are kept in memory (PRAGMA temp_store = FILE switches a connection to temporary files).</li>
|
|
<li>New databases use 1 KB pages; each connection caches up to 512 KB. Existing databases keep their own page size.</li>
|
|
<li>LIKE never matches BLOB values.</li>
|
|
</ul>
|
|
<p>The DOS port is SQLite's SQLITE_OS_OTHER build with a small DVX file layer, the "dvx" VFS in sql/dvxSqlVfs.c (the stock unix layer does not build under DJGPP). It uses the POSIX file calls, so the host tests run the same code:</p>
|
|
<ul>
|
|
<li>Files are opened in binary mode. A file that cannot be opened for writing (read-only attribute or medium) opens read-only; writes then fail with "attempt to write a readonly database".</li>
|
|
<li>Journals keep 8.3 names: DATA.DB uses DATA.NAL (the last three letters of "-journal"), and an extensionless DATA uses DATA.NAL too. Two databases that differ only in extension (DATA.DB and DATA.DAT) share the journal name, so keep them in different directories.</li>
|
|
<li>Paths are made absolute and canonical (relative names join the current directory, backslashes become forward slashes, "." and ".." are resolved), so every spelling of one file is the same database.</li>
|
|
<li>Locking is in-process: DOS is single user, but two connections inside DVX (two programs, or a Data control and BASIC code) can share a file. Any number may read; one may write. A write while another connection is writing, or a commit while another connection has a query open, fails with "database is locked"; retry after the other side finishes (dvxSqlFreeResult releases a query's read lock). Nothing is locked at the DOS level, so do not share a database with another program outside DVX.</li>
|
|
<li>Temporary files, when used, are SQLxxxxx.TMP in the directory named by TMPDIR, TEMP or TMP (else the current directory) and are deleted when closed.</li>
|
|
<li>Randomness comes from the serial library's generator (secRngBytes) when that library is loaded, else from platformEntropyStamp mixed with the time of day.</li>
|
|
</ul>
|
|
<p>Databases written by the SQLite 3.5.6 that earlier DVX releases bundled open, read and write unchanged. Files created now use the current file format, which SQLite 3.5.6 cannot read.</p>
|
|
<h3>Handle Model</h3>
|
|
<p>Database and cursor handles are int32_t values. A successful open or query returns a handle greater than zero. Handle 0 is reserved as the invalid/error sentinel. Closing a database automatically finalizes all cursors that belong to it, so callers do not need to track cursor lifetimes per-database.</p>
|
|
<h3>Connection Lifecycle</h3>
|
|
<ul>
|
|
<li>dvxSqlOpen allocates a handle and calls sqlite3_open. The database file is created if it does not exist.</li>
|
|
<li>Use dvxSqlExec for DDL and non-query DML. On success, dvxSqlAffectedRows returns the row count for the last call on this handle.</li>
|
|
<li>Use dvxSqlQuery to obtain a cursor over a SELECT. Iterate with dvxSqlNext and read columns with dvxSqlFieldText, dvxSqlFieldInt, dvxSqlFieldDbl, or dvxSqlFieldByName.</li>
|
|
<li>Call dvxSqlFreeResult when the cursor is no longer needed. Call dvxSqlClose when the database is no longer needed; any cursors still open on that database are finalized automatically.</li>
|
|
</ul>
|
|
<h3>Common Patterns</h3>
|
|
<ul>
|
|
<li>Parameterize user input with dvxSqlEscape before interpolating into SQL strings.</li>
|
|
<li>Check dvxSqlError(db) for the last error on a handle; its message is stable until the next operation on the same database.</li>
|
|
<li>dvxSqlFieldByName matches column names case-insensitively, suitable for most real-world schemas.</li>
|
|
</ul>
|
|
<p><a href="#sql.db">Database Operations</a></p>
|
|
<p><a href="#sql.cursor">Cursor Operations</a></p>
|
|
<p><a href="#sql.utility">Utility Functions</a></p>
|
|
<p><a href="#sql.example">Example</a></p>
|
|
</div>
|
|
<div class="topic" id="sql.db">
|
|
<h2>Database Operations</h2>
|
|
<h3>dvxSqlOpen</h3>
|
|
<pre><code>int32_t dvxSqlOpen(const char *path);</code></pre>
|
|
<p>Open a SQLite database file. Creates the file if it does not exist.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
path Path to the database file.</pre>
|
|
<p>Returns a database handle greater than 0 on success, or 0 on error (null path, open failure, or no free slots). After a failed open, dvxSqlError(0) returns the reason (for example "unable to open database file (No such file or directory)").</p>
|
|
<h3>dvxSqlClose</h3>
|
|
<pre><code>void dvxSqlClose(int32_t db);</code></pre>
|
|
<p>Close a database and free all associated resources. Any open cursors belonging to this database are automatically finalized.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
db Database handle returned by dvxSqlOpen.</pre>
|
|
<p>Safe to call with an invalid handle (no-op).</p>
|
|
<h3>dvxSqlExec</h3>
|
|
<pre><code>bool dvxSqlExec(int32_t db, const char *sql);</code></pre>
|
|
<p>Execute one or more SQL statements that return no result rows. Suitable for DDL (CREATE TABLE, DROP TABLE, etc.) and DML (INSERT, UPDATE, DELETE).</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
db Database handle.
|
|
sql SQL statement(s) to execute.</pre>
|
|
<p>Returns true on success, false on error. On failure, the error message is available via dvxSqlError. On success, the affected row count is available via dvxSqlAffectedRows.</p>
|
|
<h3>dvxSqlError</h3>
|
|
<pre><code>const char *dvxSqlError(int32_t db);</code></pre>
|
|
<p>Return the last error message for a database handle. The returned string is stored internally and valid until the next operation on the same handle.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
db Database handle.</pre>
|
|
<p>Returns the error string, or "Invalid database handle" if the handle is invalid. Messages are SQLite's own (for example "no such table: x" or "database is locked"); when a file could not be opened, read or written, the operating system's reason follows in parentheses. Handle 0 (what a failed dvxSqlOpen returns) reports why the most recent open failed; after a successful open it is invalid again.</p>
|
|
<h3>dvxSqlAffectedRows</h3>
|
|
<pre><code>int32_t dvxSqlAffectedRows(int32_t db);</code></pre>
|
|
<p>Return the number of rows inserted, updated, or deleted by the last dvxSqlExec call on this handle.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
db Database handle.</pre>
|
|
<p>Returns the row count, or 0 if the handle is invalid.</p>
|
|
</div>
|
|
<div class="topic" id="sql.cursor">
|
|
<h2>Cursor Operations</h2>
|
|
<p>Result set cursors are created by dvxSqlQuery and must be freed with dvxSqlFreeResult when no longer needed. A new cursor is positioned before the first row; call dvxSqlNext to advance to the first row before reading field values.</p>
|
|
<h3>dvxSqlQuery</h3>
|
|
<pre><code>int32_t dvxSqlQuery(int32_t db, const char *sql);</code></pre>
|
|
<p>Execute a SELECT query and return a cursor handle for iterating the results. The cursor is positioned before the first row.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
db Database handle.
|
|
sql SQL SELECT statement.</pre>
|
|
<p>Returns a cursor handle greater than 0 on success, or 0 on error (invalid handle, null SQL, SQL that is empty or only a comment, SQL syntax error, or no free cursor slots). On failure, the error message is available via dvxSqlError on the database handle.</p>
|
|
<h3>dvxSqlNext</h3>
|
|
<pre><code>bool dvxSqlNext(int32_t rs);</code></pre>
|
|
<p>Advance the cursor to the next row.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
rs Cursor handle returned by dvxSqlQuery.</pre>
|
|
<p>Returns true if a row is now available for reading, false if the cursor has reached the end or the handle is invalid.</p>
|
|
<h3>dvxSqlEof</h3>
|
|
<pre><code>bool dvxSqlEof(int32_t rs);</code></pre>
|
|
<p>Test whether the cursor is past the last row.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
rs Cursor handle.</pre>
|
|
<p>Returns true if the cursor is exhausted or the handle is invalid, false otherwise.</p>
|
|
<h3>dvxSqlFieldCount</h3>
|
|
<pre><code>int32_t dvxSqlFieldCount(int32_t rs);</code></pre>
|
|
<p>Return the number of columns in the result set.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
rs Cursor handle.</pre>
|
|
<p>Returns the column count, or 0 if the handle is invalid.</p>
|
|
<h3>dvxSqlFieldName</h3>
|
|
<pre><code>const char *dvxSqlFieldName(int32_t rs, int32_t col);</code></pre>
|
|
<p>Return the name of a column by index.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
rs Cursor handle.
|
|
col Column index (0-based).</pre>
|
|
<p>Returns the column name, or "" if the handle or index is invalid.</p>
|
|
<h3>dvxSqlFieldText</h3>
|
|
<pre><code>const char *dvxSqlFieldText(int32_t rs, int32_t col);</code></pre>
|
|
<p>Return the value of a column as a string.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
rs Cursor handle.
|
|
col Column index (0-based).</pre>
|
|
<p>Returns the text value, or "" if the handle or index is invalid or the value is NULL.</p>
|
|
<h3>dvxSqlFieldByName</h3>
|
|
<pre><code>const char *dvxSqlFieldByName(int32_t rs, const char *name);</code></pre>
|
|
<p>Return the value of a column identified by name as a string. The name match is case-insensitive.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
rs Cursor handle.
|
|
name Column name (case-insensitive).</pre>
|
|
<p>Returns the text value, or "" if the handle is invalid, the name is NULL, or no column with that name exists.</p>
|
|
<h3>dvxSqlFieldInt</h3>
|
|
<pre><code>int32_t dvxSqlFieldInt(int32_t rs, int32_t col);</code></pre>
|
|
<p>Return the value of a column as a 32-bit integer.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
rs Cursor handle.
|
|
col Column index (0-based).</pre>
|
|
<p>Returns the integer value, or 0 if the handle or index is invalid.</p>
|
|
<h3>dvxSqlFieldDbl</h3>
|
|
<pre><code>double dvxSqlFieldDbl(int32_t rs, int32_t col);</code></pre>
|
|
<p>Return the value of a column as a double-precision floating point number.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
rs Cursor handle.
|
|
col Column index (0-based).</pre>
|
|
<p>Returns the double value, or 0.0 if the handle or index is invalid.</p>
|
|
<h3>dvxSqlFreeResult</h3>
|
|
<pre><code>void dvxSqlFreeResult(int32_t rs);</code></pre>
|
|
<p>Close a result set cursor and free its resources. Must be called for every cursor returned by dvxSqlQuery.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
rs Cursor handle.</pre>
|
|
<p>Safe to call with an invalid handle (no-op).</p>
|
|
</div>
|
|
<div class="topic" id="sql.utility">
|
|
<h2>Utility Functions</h2>
|
|
<h3>dvxSqlEscape</h3>
|
|
<pre><code>int32_t dvxSqlEscape(const char *src, char *dst, int32_t dstSize);</code></pre>
|
|
<p>Escape a string for safe inclusion in SQL string literals. Doubles single quotes so that O'Brien becomes O''Brien.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
src Source string to escape.
|
|
dst Destination buffer for the escaped string.
|
|
dstSize Size of the destination buffer in bytes.</pre>
|
|
<p>Returns the length of the escaped string (not including the null terminator), or -1 if the buffer was too small or any parameter is NULL/invalid.</p>
|
|
</div>
|
|
<div class="topic" id="sql.example">
|
|
<h2>Example Usage</h2>
|
|
<h3>Creating a Table and Inserting Data</h3>
|
|
<pre><code>int32_t db = dvxSqlOpen("mydata.db");
|
|
if (!db) {
|
|
// handle error
|
|
}
|
|
|
|
dvxSqlExec(db, "CREATE TABLE IF NOT EXISTS contacts ("
|
|
"id INTEGER PRIMARY KEY, "
|
|
"name TEXT, "
|
|
"phone TEXT)");
|
|
|
|
dvxSqlExec(db, "INSERT INTO contacts (name, phone) "
|
|
"VALUES ('Alice', '555-0100')");</code></pre>
|
|
<h3>Querying and Iterating Results</h3>
|
|
<pre><code>int32_t rs = dvxSqlQuery(db, "SELECT id, name, phone FROM contacts");
|
|
if (!rs) {
|
|
printf("Error: %s\n", dvxSqlError(db));
|
|
}
|
|
|
|
while (dvxSqlNext(rs)) {
|
|
int32_t id = dvxSqlFieldInt(rs, 0);
|
|
const char *name = dvxSqlFieldText(rs, 1);
|
|
const char *phone = dvxSqlFieldText(rs, 2);
|
|
printf("%d: %s -- %s\n", id, name, phone);
|
|
}
|
|
|
|
dvxSqlFreeResult(rs);
|
|
dvxSqlClose(db);</code></pre>
|
|
<h3>Escaping User Input</h3>
|
|
<pre><code>char escaped[512];
|
|
dvxSqlEscape(userInput, escaped, sizeof(escaped));
|
|
|
|
char sql[1024];
|
|
snprintf(sql, sizeof(sql),
|
|
"INSERT INTO notes (text) VALUES ('%s')", escaped);
|
|
dvxSqlExec(db, sql);</code></pre>
|
|
</div>
|
|
<div class="topic" id="lib.fileops">
|
|
<h1>File Operations Library</h1>
|
|
<p>Attributes, folder listing, path helpers and recursive copy/move/delete jobs with progress, Cancel and conflict handling. The File Manager and Setup are user interfaces over this library; any program can use it. Drives, free space and wildcard filters come from the platform layer (dvxPlat.h): platformDriveList, platformDiskSpace and platformFilterMatch.</p>
|
|
<p>Header: fileops/fileOps.h</p>
|
|
<p>Loaded as: bin/libs/kpunch/fileops/fileops.lib</p>
|
|
<h2>DOS Disk Errors</h2>
|
|
<p>On DOS, touching a drive with no disk (or a write-protected or removed one) raises a critical error, which would normally wait at an "Abort, Retry, Fail" prompt on the hidden text screen. DVX installs a critical-error handler at startup (platformInit) that answers Fail, so every call here, and your own disk code, simply gets an error (errno) instead. platformDriveList never touches the media at all (IOCTL calls only), so listing drives costs nothing even with empty drives.</p>
|
|
<h2>Attributes and Dates</h2>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
fileOpsGetAttr(path, &attr) FILEOPS_ATTR_READONLY/HIDDEN/SYSTEM/ARCHIVE bits. Elsewhere than DOS: read-only = no write permission, hidden = a dot-file.
|
|
fileOpsSetAttr(path, attr) Set the FILEOPS_ATTR_EDITABLE bits (elsewhere than DOS only read-only applies).
|
|
fileOpsSetTime(path, mtime) Set a file's modification time.</pre>
|
|
<h2>Listing, Names and Paths</h2>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
fileOpsListDir(dir, filter, &entries, &count) Read a folder into an FileOpsEntryT array (name, size, mtime, isDir). Files not matching filter (platformFilterMatch: wildcards * and ?, several patterns separated by ';', ',' or blanks; "*.*" and an empty filter match everything) are left out; folders always stay. Free with fileOpsListFree.
|
|
fileOpsListSort(entries, count, col, descending) Folders first, then by FileOpsSortNameE/SizeE/TypeE/DateE; ties by name.
|
|
fileOpsExtension(name) Extension without the dot, "" when none.
|
|
fileOpsTypeName(entry, out, size) "Folder", "Program" (.APP), "TXT File", "File".
|
|
fileOpsFormatSize(bytes, out, size) "1,234,567".
|
|
fileOpsFormatDate(t, out, size) "YYYY-MM-DD HH:MM" in local time.
|
|
fileOpsTreeSize(path, &bytes, &files, &folders) Total size of a file or of a folder and everything in it.
|
|
fileOpsMakeDir(dir, name, err, errSize) Create a folder; a reason in err on failure.
|
|
fileOpsRename(dir, oldName, newName, err, errSize) Rename within a folder; refuses a name already in use.
|
|
fileOpsValidateName(name, err, errSize) Check a new name (8.3 rules and device names on DOS).
|
|
fileOpsPathJoin(out, size, dir, name) dir + separator + name; false when it does not fit.
|
|
fileOpsPathParent(path, out, size) Parent folder; false at a root.
|
|
fileOpsPathIsRoot(path) True for "C:\", "C:" or "/".
|
|
fileOpsPathIsInside(path, dir) True when path is dir or below it.
|
|
fileOpsPathSame(a, b) Same place (separators equal, DOS ignores case, a trailing separator ignored).
|
|
fileOpsPathNormalize(path, out, size) Canonical absolute form of an existing folder.</pre>
|
|
<h2>Jobs</h2>
|
|
<p>A job copies, moves or deletes any number of files and folders (recursively):</p>
|
|
<pre><code>FileOpsJobT *job = fileOpsJobCreate(FileOpsCopyE, "C:\\BACKUP");
|
|
|
|
fileOpsJobAddSource(job, "C:\\DOCS\\LETTER.TXT");
|
|
fileOpsJobAddSource(job, "C:\\DOCS\\PHOTOS");
|
|
|
|
for (;;) {
|
|
FileOpsStepE step = fileOpsJobStep(job);
|
|
|
|
if (step == FileOpsStepDoneE || step == FileOpsStepCancelledE) {
|
|
break;
|
|
}
|
|
|
|
if (step == FileOpsStepAskE) {
|
|
// fileOpsJobMessage(job): "... already exists" or "... is read-only"
|
|
fileOpsJobAnswer(job, FileOpsAnswerAllE);
|
|
} else if (step == FileOpsStepErrorE) {
|
|
fileOpsJobAnswer(job, FileOpsAnswerSkipE);
|
|
}
|
|
|
|
// show progress, tsYield(), honour a Cancel button here
|
|
}
|
|
|
|
fileOpsJobFree(job);</code></pre>
|
|
<p>Each fileOpsJobStep does one bounded piece of work: read one folder, copy one FILEOPS_COPY_CHUNK of one file, or delete one item. A cooperative program yields between steps.</p>
|
|
<pre> Step result Meaning
|
|
----------- -------
|
|
FileOpsStepBusyE More work to do.
|
|
FileOpsStepAskE A destination file exists, or a file to delete is read-only. Answer Yes, No, All or Cancel.
|
|
FileOpsStepErrorE Something failed; fileOpsJobMessage says why. Answer Retry, Skip or Cancel.
|
|
FileOpsStepDoneE Finished.
|
|
FileOpsStepCancelledE Stopped by Cancel.</pre>
|
|
<p>Until it is answered, the job keeps returning the same question. What the job does:</p>
|
|
<ul>
|
|
<li>Scan: each folder is read once and its files are counted, so fileOpsJobProgress (FileOpsProgressT: files and bytes done and total, percent, current path) is accurate while the transfer runs.</li>
|
|
<li>Copy: folders are created before their contents, files are copied in chunks and keep their date and attributes (plus Archive). A read-only destination is made writable before it is replaced.</li>
|
|
<li>Move: a top-level item whose destination does not exist yet is renamed in one go. Otherwise each file is copied and then its source removed, and the emptied source folders are removed at the end. A folder that still holds skipped items is kept.</li>
|
|
<li>Delete: files first, deepest first, then folders; read-only files ask first.</li>
|
|
<li>Copying or moving a folder into itself, or a file onto itself, is an error; moving an item to where it already is does nothing.</li>
|
|
<li>fileOpsJobCancel makes the next step stop, removing a partly copied file; fileOpsJobFree does the same for a job that was not finished.</li>
|
|
</ul>
|
|
<p>fileOpsJobClearAttr(job, attr), called before the first step, clears those attribute bits on every file a copy or move writes: Setup passes FILEOPS_ATTR_READONLY, because every file on a CD-ROM is read-only and the installed copies must not be.</p>
|
|
<p>fileOpsJobRun(job, callbacks) runs a job to the end instead: progress(job, ctx) is called after every step (return false to cancel) and ask(job, kind, message, ctx) answers questions. With no callbacks, existing files are kept (No) and failing items are skipped.</p>
|
|
<h2>BASIC</h2>
|
|
<p>src/include/basic/fileops.bas declares BASIC wrappers: FileOpsCopyTree, FileOpsMoveTree, FileOpsDeleteTree (a whole folder, without prompting), FileOpsDriveList$ and FileOpsFreeKb. Single files are handled by the built-in FILECOPY, KILL, NAME, MKDIR, RMDIR, GETATTR and SETATTR statements.</p>
|
|
</div>
|
|
<div class="topic" id="lib.game">
|
|
<h1>Game Helpers Library</h1>
|
|
<p>Frame pacing, held keys, program-owned timers and nearest-colour lookup for programs that run like games in a DVX window. It builds on the core's game input windows, held-key table, timer service and high-resolution clock (see Writing Games for DVX).</p>
|
|
<p>Header: game/game.h</p>
|
|
<p>Loaded as: bin/libs/kpunch/game/game.lib</p>
|
|
<h2>Frame Pacing</h2>
|
|
<pre><code>GameFramePacerT pacer;
|
|
|
|
gameFrameInit(&pacer, 70);
|
|
|
|
while (running) {
|
|
uint32_t missed = gameFrameWait(&pacer); // 0 when on time
|
|
|
|
updateGame(missed + 1); // catch the game logic up
|
|
drawFrame();
|
|
}</code></pre>
|
|
<p>gameFrameInit(p, hz) starts pacing at hz frames a second (1 to GAME_FRAME_MAX_HZ, 1000; out of range is clamped) with frame 0 beginning now. gameFrameWait waits until the next frame begins and returns the number of frames that were missed. While it waits it gives the time to the other programs (tsYield), so call it from a program with its own main loop (hasMainLoop), never from a window callback. Frame n begins at start + n / hz on platformMicros, so the pace does not drift however long it runs. When the frame after the next one has already begun (the caller was busy), the frames in between are skipped, not caught up: gameFrameWait returns at once with how many, and GameFramePacerT.missed keeps the total. A frame that starts late but within its own time counts as on time.</p>
|
|
<p>gameFrameNext(p, &missed) does the same without the waiting: it moves on to the next frame (skipping and counting missed ones the same way) and returns the platformMicros time that frame begins, for a caller that waits its own way. The BASIC runtime uses it for the FRAMEWAIT statement, keeping the desktop running while it waits.</p>
|
|
<h2>Held Keys</h2>
|
|
<p>gameKeyIsDown(win, rawCode) is true while the key is held down and win has the keyboard focus. Keys are named by raw code (KEY_RAW_CODE, KEY_RAW_* in dvxTypes.h). The held-key table starts over whenever the focus moves, so a key pressed while another window had the focus is not seen until it repeats.</p>
|
|
<h2>Timers</h2>
|
|
<p>gameTimerAdd(ctx, hz, fn, userData) is platformTimerAdd for the calling program (ctx->currentAppId): the timer is removed when the program ends, however it ends. fn runs inside the timer interrupt; read the rules at the Timer Service in the platform reference before writing one. Remove it with platformTimerRemove.</p>
|
|
<h2>Colours on a 256-Colour Desktop</h2>
|
|
<p>On an 8-bit desktop every window shares the desktop palette (dvxGetDisplay(ctx)->palette; NULL at 15 bits and more). A game with its own colours draws the nearest desktop entries.</p>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
gamePalNearest(pal, r, g, b) The entry of the 256-entry palette pal (R, G, B bytes) closest to the colour: smallest squared RGB distance, lowest index on a tie. An exact search of all 256 entries.
|
|
gamePalCacheInit(c, pal) Record pal in the cache c and empty it. GamePalCacheT is about 37 KB: allocate it.
|
|
gamePalCacheLookup(c, r, g, b) The entry nearest to the centre of the colour's 15-bit cell (5 bits per channel), searched once per cell and then remembered, so every colour in a cell gets the same answer.
|
|
gamePalCacheSync(c, pal) Start over (and return true) when pal differs from the recorded palette; call it once a frame if the palette can change.</pre>
|
|
<h2>BASIC</h2>
|
|
<p>src/include/basic/game.bas declares GameKeyDown(keyCode), True while the key is held and one of the calling program's windows has the focus, with GAMEKEY_* constants for common keys. The BASIC runtime gives the library the GUI context (gameBasSetContext) when a program starts. Frame pacing from BASIC comes with the BASIC game statements later.</p>
|
|
</div>
|
|
<div class="topic" id="lib.joystick">
|
|
<h1>Joystick Library</h1>
|
|
<p>Game controllers for DVX programs behind one API, whatever the hardware: the devices the drivers found, axes calibrated to -32768..32767 with a dead zone round the centre, buttons, hats, read when the program wants (polled) or delivered as events. A program's handles close when it ends, however it ends. Calibration and the other settings live in DVX.INI [joystick]; the Control Panel's Game Controllers page sets them.</p>
|
|
<p>Header: joystick/joystick.h</p>
|
|
<p>Loaded as: bin/libs/kpunch/joystick/joystick.lib, drivers in bin/libs/kpunch/joystick/drivers/*.drv (joyport.drv, joykeys.drv), loaded only when a program first asks for devices.</p>
|
|
<h2>Reading a Joystick</h2>
|
|
<pre><code>if (joyCount() > 0) {
|
|
int32_t stick = joyOpen(ctx, 0); // device 0, owned by the calling program
|
|
JoyStateT st;
|
|
|
|
if (joyRead(stick, &st) && st.connected) {
|
|
shipX += st.axes[0] / 4096; // -8..7
|
|
firing = (st.buttons & 1) != 0;
|
|
}
|
|
}</code></pre>
|
|
<p>joyCount() is the number of devices (the first call of any joystick function finds them); joyGetInfo(device, &info) describes one: name, id (the driver's name and unit number, "gameport0", which prefixes the device's DVX.INI keys), axisCount, buttonCount, hatCount, axisPresent (bit n set when axis n answers; a missing axis reads 0) and caps (JOY_CAP_CALIBRATE for analog axes, JOY_CAP_KEYBOARD for the keyboard joystick).</p>
|
|
<p>joyOpen(ctx, device) returns a handle owned by ctx->currentAppId, or -1. joyOpenShared(ctx, device) returns the calling program's shared handle on the device, opening it the first time (for code with no place to keep a handle; the BASIC functions use it). joyClose closes a handle; a program's handles are closed for it when it ends.</p>
|
|
<p>joyRead(handle, &st) fills a JoyStateT: axes[] (JOY_AXIS_MIN..JOY_AXIS_MAX, 0 inside the dead zone), hats[] (JOY_HAT_CENTERED or degrees clockwise from up), buttons (bit n = button n) and connected (false when the device stopped answering; everything then reads neutral). A button that went down and up again since the handle last looked reads as down once, so a quick tap between two reads is not lost. joyButtonPressed(handle, button) asks the same about one button. joyButtonWentDown(handle, button) only asks whether the button went down since the last time this function asked about it (a press counter of its own, apart from joyRead's).</p>
|
|
<p>The keyboard joystick (JOY_CAP_KEYBOARD) reads neutral for a handle whose program has no window with the focus, so keys typed into another program do not steer the game.</p>
|
|
<h2>Events</h2>
|
|
<p>joySetEventFn(handle, fn, userData) delivers the device's changes from now on: JoyEventAxisE when an axis moved by at least JOY_EVENT_AXIS_STEP or reached the centre or an end, JoyEventButtonDownE / JoyEventButtonUpE (a tap between two polls gives both), JoyEventHatE. fn runs in program context as the handle's program (ctx->currentAppId is its id), so DVX calls are fine. Events are delivered by joyPump, which the shell calls on every idle frame; a program with its own main loop that repaints every frame (so the shell is never idle) calls joyPump itself or reads with joyRead. joyPump is not reentrant: an event function that runs a modal dialog gets its remaining events after it returns. A NULL fn stops the events.</p>
|
|
<h2>Polling and CPU Cost</h2>
|
|
<p>While at least one handle is open, a timer-service callback marks a poll due [joystick] pollHz times a second (JOY_POLL_HZ_DEFAULT 60, JOY_POLL_HZ_MIN 10 to JOY_POLL_HZ_MAX 200) and runs the drivers' tick hooks (the game port latches button presses there). The poll itself runs in program context, the next time a program reads (joyRead, joyButtonPressed, joyGetRaw) or the shell is idle, so however often a program reads there are at most pollHz polls a second. With no handle open nothing is polled and the timer is removed.</p>
|
|
<p>A game port poll waits for the slowest connected axis to run out: about 0.6 ms with the stick centred, up to about 1.2 ms at an end. At 60 polls a second that is about 4 to 7 percent of the processor's time on any machine, whatever its speed, while a program has a joystick open; the timer interrupt's part (one port read) is negligible. The keyboard joystick costs nothing measurable.</p>
|
|
<h2>Calibration and Settings</h2>
|
|
<p>joyGetRaw(device, &raw) returns the last raw poll (driver units: microseconds for the game port). joyGetCalibration / joySetCalibration get and set an axis's JoyAxisCalT (min, center, max in raw units; min < center < max): min..center maps to -32768..0, center..max to 0..32767, clamped. A NULL calibration forgets it. An axis never calibrated ranges automatically: centred on the reading taken when the device was found, starting at center / 8 .. center * 2 and widening as the stick moves. joyGetDeadZone / joySetDeadZone get and set the device's dead zone in percent (0..JOY_DEADZONE_MAX, default JOY_DEADZONE_DEFAULT 8). joyAxisScale(cal, raw) and joyDeadZoneApply(value, percent) are the two steps as plain functions.</p>
|
|
<p>Settings changed through the library take effect at once and reach DVX.INI only through joySettingsStore(prefs) (the caller saves the file); joySettingsLoad(prefs) replaces them all, e.g. to undo. joySettingGet / joySettingSet read and write single values. The [joystick] keys:</p>
|
|
<pre> Key Value
|
|
--- -----
|
|
pollHz Polls a second (10 to 200, default 60).
|
|
fallback auto (default): the keyboard joystick only when no other device is found; always; never.
|
|
gameport.mode twoSticks (default) or fourAxes: one joystick using all four axes and buttons (flight sticks with a throttle or rudder).
|
|
keyboard.up ... keyboard.right, keyboard.button1 ... keyboard.button4 Key names (joyKeyName: Up, Down, Left, Right, LCtrl, RCtrl, LShift, RShift, LAlt, RAlt, Space, Enter, Tab, Backspace, Insert, Delete, Home, End, PgUp, PgDn, Keypad0 to Keypad9, A to Z, 0 to 9). Defaults: the arrows, Left Ctrl, Space, Left Shift, Z.
|
|
<id>.axis<n> min,center,max: axis n of the device with that id, e.g. gameport0.axis0 = 40,560,1150.
|
|
<id>.deadZone The device's dead zone in percent.</pre>
|
|
<p>joyRescan() looks for devices again (a joystick plugged in later, a changed gameport.mode); it works only while no handle is open and returns -1 otherwise.</p>
|
|
<h2>The Game Port Driver</h2>
|
|
<p>joyport.drv drives the standard PC game port at I/O port 201h. Writing to the port starts four one-shot timers, one per axis; each runs for a time proportional to its axis's resistance (about 24 us plus 11 us per kilohm). The driver times them against the platform's microsecond clock (platformMicros) with interrupts left enabled, so the port never holds up the timer, the sound card or the mouse: each sample reads the port and the clock, and an axis whose fall can only be placed in a window wider than JOYPORT_GAP_US (40 us; an interrupt ran in between) keeps its previous value for that poll. A measurement gives up after JOYPORT_TIMEOUT_US (3 ms). At the probe an axis that does not run out, or runs out in under 10 us (some cards read 0 for an unconnected axis), is missing. At run time an axis that times out three polls in a row is left out of the measurement, so an unplugged stick does not cost the timeout every poll, and is tried again every 60 polls; a stick all of whose axes time out reads as not connected. Buttons A1, A2, B1, B2 are read at every poll and sampled by the timer interrupt, so short presses are latched.</p>
|
|
<p>In the default mode the port makes up to two joysticks (A with axes 0-1 and buttons 0-1, B with axes 2-3 and buttons 2-3; either may be missing); in the four-axis mode one joystick with four axes and four buttons.</p>
|
|
<h2>The Keyboard Joystick</h2>
|
|
<p>joykeys.drv is a fallback driver: a joystick made of keys (two axes: full deflection while a key is held, the centre with both or neither; four buttons), read from the held-key table, so games work without joystick hardware. It is listed only when no other device is found, unless [joystick] fallback says always or never.</p>
|
|
<h2>Writing a Driver</h2>
|
|
<p>A driver is a driver module (see dvxDrv.h in the API reference): a file <name>.drv in bin/libs/kpunch/joystick/drivers, built by the joystick Makefile from each drivers/<name>/ folder, that exports one JoyDriverT named joyDriver, written with JOY_DRIVER(name, priority, .flags = ..., .probe = ..., .info = ..., .poll = ..., .tick = ..., .settingsChanged = ...). Nothing lists drivers and nothing loads them at startup: the first time a program asks for devices (the GUI and the timer service are running by then), and at every joyRescan, the library loads them all, probes them from the highest priority down (JOY_PRIORITY_PORT, ..., JOY_PRIORITY_KEYBOARD), probes the fallback drivers only when the fallback setting asks for them, and unloads every driver that found no device. The descriptor carries DVX_ABI_VERSION; a driver built for another version, one without probe, info or poll, or one whose name a higher priority driver already has is skipped and logged.</p>
|
|
<pre> Member Meaning
|
|
------ -------
|
|
hdr.name Lower case, no blanks ("gameport"); the prefix of the device ids and of the driver's own [joystick] keys.
|
|
hdr.priority Probing order (JOY_PRIORITY_*); devices are numbered in that order.
|
|
flags JOY_DRIVER_FALLBACK for a stand-in like the keyboard joystick.
|
|
probe() Find the units; return how many. Called right after the driver is loaded.
|
|
info(unit, out) Describe one unit (name, counts, axisPresent, caps); the library fills in id.
|
|
poll(raw, units) Read every unit now, in program context: raw[0..units-1]. Set axisValid for each axis measured (the others keep their last value), buttons held now, pressed for buttons that went down since the last poll, hats (JOY_HAT_CENTERED when not pushed) and connected. Axes are raw units with JOY_CAP_CALIBRATE, else JOY_AXIS_MIN..JOY_AXIS_MAX.
|
|
tick() NULL, or called INSIDE THE TIMER INTERRUPT at the poll rate while a handle is open. The timer service's rules apply; the driver pins the code and data it touches with platformLockRegion and releases them with platformUnlockRegion when its module is unloaded (a destructor), so rescans do not pile up locked memory.
|
|
settingsChanged() NULL, or re-read the driver's settings (joySettingGet) after they changed.</pre>
|
|
<p>Up to JOY_MAX_DRIVERS drivers in use get their tick called. A driver is loaded again at every probe, so it must not rely on state kept from an earlier load; set its hardware up in probe (or a constructor).</p>
|
|
<h2>BASIC</h2>
|
|
<p>src/include/basic/joystick.bas declares JoyCount, JoyName$, JoyAxes, JoyButtons, JoyX, JoyY, JoyAxis, JoyButton and JoyHat. Joystick, axis, button and hat numbers start at 1; each function reads through the calling program's shared handle.</p>
|
|
<p>The BASIC runtime calls joyBasStick(n) and joyBasStrig(n) for QBasic's STICK and STRIG. Joystick A is device 0 (axes 0-1, buttons 0 and 1); joystick B is device 1, or axes 2-3 and buttons 2-3 of device 0 when it is the only device and has more than two axes. STICK(0) reads A's X and remembers A's Y and B's X and Y for STICK(1) to STICK(3) (per program, forgotten when it ends); positions are 1 to 200 ((value - JOY_AXIS_MIN) x 199 / 65535 + 1), 0 without that joystick. Even STRIG numbers use joyButtonWentDown (0 A button 1, 2 B button 1, 4 A button 2, 6 B button 2), odd ones the state joyRead reports; True is -1.</p>
|
|
</div>
|
|
<div class="topic" id="games.intro">
|
|
<h1>Writing Games for DVX</h1>
|
|
<p>A game in DVX is a program with its own main loop (hasMainLoop = true in its SHELL_APP_DESCRIPTOR). DVX gives it its own task: appMain runs the game loop itself, draws into the game's window, and waits for each frame on the high-resolution clock while the other programs keep running. Nothing in DVX has to stop for a game: the desktop, other programs, the Task Manager (Ctrl+Esc) and help all stay available.</p>
|
|
<h2>Starting From a Template</h2>
|
|
<pre><code> bin/sdk/newProject.sh game myGame a C game
|
|
bin/sdk/newProject.sh basicgame myGame a DVX BASIC game</code></pre>
|
|
<p>The C template is a small but complete game, and every part of this guide is shown in it: a game input window, a 320x200 screen of 8-bit colour numbers drawn scaled into the window on any desktop colour depth, frame pacing, the arrow keys and a joystick, a sound effect and music carried inside the .app as resources, tones for the end of a round, the best score saved in the program's settings folder, its own help file (F1) and icon. The BASIC template does the same in a few dozen lines with FRAMEWAIT, GameKeyDown, STICK, STRIG and PLAY.</p>
|
|
<h2>The Shape of a Game</h2>
|
|
<pre><code>int32_t appMain(DxeAppContextT *ctx) {
|
|
GameFramePacerT pacer;
|
|
|
|
createGameWindow(ctx); // gameInput = true, onClose sets quit
|
|
gameFrameInit(&pacer, 60);
|
|
|
|
while (!quit) {
|
|
uint32_t missed = gameFrameWait(&pacer); // yields until the frame is due
|
|
|
|
audioPump(); // sound end calls and streams
|
|
musicPump();
|
|
update(missed + 1); // move by the frames that passed
|
|
draw(); // into the game's own framebuffer
|
|
present(); // changed parts -> window, dvxInvalidateRect
|
|
}
|
|
|
|
cleanUp(); // or just return: DVX releases the rest
|
|
dvxDestroyWindow(ctx->shellCtx, win);
|
|
return 0;
|
|
}</code></pre>
|
|
<p>The sections that follow cover each part.</p>
|
|
<p><a href="#games.input">Keyboard, Mouse and Joysticks</a></p>
|
|
<p><a href="#games.timing">Timing and Frame Pacing</a></p>
|
|
<p><a href="#games.screen">The Screen: Framebuffers and Palettes</a></p>
|
|
<p><a href="#games.sound">Sound, Music and MIDI</a></p>
|
|
<p><a href="#games.files">Data Files and Saved Games</a></p>
|
|
<p><a href="#games.packaging">Packaging and Ending</a></p>
|
|
<p><a href="#games.basic">Games in DVX BASIC</a></p>
|
|
<p><a href="#games.porting">Porting a Game Library to DVX</a></p>
|
|
</div>
|
|
<div class="topic" id="games.input">
|
|
<h1>Keyboard, Mouse and Joysticks</h1>
|
|
<h2>Game Input Windows</h2>
|
|
<p>Set gameInput on the game's window and give it an onRawKey callback if it wants every key going down and up:</p>
|
|
<pre><code>win->gameInput = true;
|
|
win->onKey = gameKey; // typed keys: every key but three
|
|
win->onRawKey = gameRawKey; // each key going down and up</code></pre>
|
|
<p>While the window has the focus, DVX keeps only three keys, so the user can always switch away, open the Task Manager or close the game:</p>
|
|
<pre> Key Always does
|
|
--- -----------
|
|
Alt+Tab Switch to the next window
|
|
Ctrl+Esc Open the Task Manager
|
|
Alt+F4 Close the window (its onClose)</pre>
|
|
<p>Every other key, F1, F10, Alt+letter and the accelerators included, goes to onKey instead of opening help or menus. The game decides what F1 does (the template opens its help with shellOpenHelp); its menus still work with the mouse. onRawKey gets a RawKeyEventT for every key going down or up: the set-1 scancode with an extended flag for the grey keys, down or up, whether it is a typematic repeat, and the modifiers. Shift, Ctrl and Alt are keys of their own there, left and right apart.</p>
|
|
<h2>Held Keys</h2>
|
|
<p>For "is it held right now" use gameKeyIsDown(win, rawCode) with a raw key code (KEY_RAW_UP, KEY_RAW_LCTRL, or KEY_RAW_CODE(scan, extended) for any key); it is true only while win has the focus. platformKeyIsDown and platformKeyStateGet read the table itself. When the focus moves away, the game gets a release for every key still held and the table starts over, so no key stays stuck down. A key already held when the window gets the focus is seen once it repeats.</p>
|
|
<h2>Typed Characters and the Mouse</h2>
|
|
<p>onKey gets typed characters (with Shift and the keyboard layout applied) as it does in any window; keep them in the game's own queue if the game reads text. onMouse gets the pointer in content-area pixels: divide by the scale the game draws at, and clamp.</p>
|
|
<h2>Joysticks</h2>
|
|
<p>joyCount says how many joysticks there are; joyOpen(ctx, device) gives the program a handle (closed for it when it ends) and joyRead fills a JoyStateT: axes calibrated to -32768..32767 with a dead zone, buttons (a press between two reads is not lost), hats. Read it once a frame. Programs that wait for input can take events instead (joySetEventFn). With no joystick connected the keyboard joystick stands in (the arrow keys and four buttons, chosen in the Control Panel), answering only while the program's window has the focus; so a game that reads both the keys and the joystick should let the keys win rather than add the two up. Polling a game port joystick costs about 4 to 7 percent of the processor while a program has one open. See the Joystick Library.</p>
|
|
</div>
|
|
<div class="topic" id="games.timing">
|
|
<h1>Timing and Frame Pacing</h1>
|
|
<p>platformMicros() is a monotonic microsecond clock from the PIT, the timer chip every PC has (with the CPU's time-stamp counter between timer interrupts when there is one). Use it for any game time: elapsed milliseconds are platformMicros() / 1000.</p>
|
|
<h2>Frame Pacing</h2>
|
|
<p>gameFrameInit(&pacer, hz) starts a frame clock (1 to GAME_FRAME_MAX_HZ frames a second); gameFrameWait(&pacer) once a frame yields to the other programs until the next frame is due and returns how many frames were missed. Frame n starts at start + n / hz, so the rate never drifts; frames that already ended while the game was busy are skipped, not caught up (pacer.missed counts them). Move by missed + 1 frames' worth (with a limit, so a long stall does not teleport the player) and the game runs at the same speed on a slow machine. pacer.frame is a frame counter.</p>
|
|
<p>There is no vertical retrace wait: a game window is composited with the rest of the desktop, and the frame rate is a virtual one. 60 or 70 frames a second both work; pick what the game was designed for.</p>
|
|
<h2>The Timer Service</h2>
|
|
<p>For work that must happen at a steady rate however busy the programs are (a music player, a sample-accurate sequencer), the timer service calls a function up to 1000 times a second from the timer interrupt: gameTimerAdd(ctx, hz, fn, userData), removed when the program ends (or platformTimerAdd). DVX speeds the timer interrupt up only as far as the fastest timer needs and keeps the DOS clock at its normal rate. The callback runs at interrupt time: no C library, no DVX calls, no floating point, and its code and data pinned with platformLockRegion. See Timer Service in the platform reference.</p>
|
|
</div>
|
|
<div class="topic" id="games.screen">
|
|
<h1>The Screen: Framebuffers and Palettes</h1>
|
|
<p>A window's content buffer (win->contentBuf, win->contentPitch bytes per row, contentW x contentH pixels) is in the desktop's own pixel format: 8, 15, 16 or 32 bits per pixel, as dvxGetDisplay(ctx)->format says. A game that must look the same everywhere draws into a framebuffer of its own (for example 320x200 bytes, each a colour number into a 256-colour game palette) and converts only what changed into the content buffer each frame.</p>
|
|
<h2>The Colour Lookup Table</h2>
|
|
<p>Keep one desktop pixel value per game colour, and rebuild it when the game palette changes or the desktop's pixel format or palette does (the user can change the video mode while the game runs):</p>
|
|
<pre><code>const DisplayT *d = dvxGetDisplay(ctx);
|
|
|
|
for (int32_t i = 0; i < 256; i++) {
|
|
const uint8_t *c = &gamePal[i * 3];
|
|
|
|
lut[i] = d->palette ? gamePalNearest(d->palette, c[0], c[1], c[2]) // 8-bit desktop
|
|
: packColor(d, c[0], c[1], c[2]); // 15, 16, 32 bits
|
|
}</code></pre>
|
|
<pre> Desktop Pixel size Game colours
|
|
------- ---------- ------------
|
|
32-bit 4 bytes Exact.
|
|
16-bit 2 bytes 5-6-5 bits of red, green, blue: very close.
|
|
15-bit 2 bytes 5 bits each: very close.
|
|
8-bit 1 byte The nearest of the desktop's 256 colours.</pre>
|
|
<p>On an 8-bit desktop all windows share the one desktop palette: a 6x6x6 colour cube, a 16-step grey ramp and the chrome colours (see dvxPal.h). A game cannot change it; gamePalNearest finds the closest entry, and a GamePalCacheT remembers the answer per 15-bit colour when there are many colours to map (gamePalCacheSync starts over if the desktop palette changed). Gradients come out banded; design for it, or ask players to use a 15-bit or better desktop for the best look.</p>
|
|
<p>Palette animation (colour cycling, fades) works at every depth: change the game palette entries, look up just those entries again, and redraw the pixels that use them. Games whose hardware picks a palette per scan line keep one small table per palette and pick the table per row.</p>
|
|
<h2>Presenting a Frame</h2>
|
|
<ul>
|
|
<li>Keep a list of the framebuffer rectangles (or rows) that changed this frame.</li>
|
|
<li>For each, expand the colour numbers through the table into contentBuf, repeating each pixel scale times across and each row scale times down, then call dvxInvalidateRect(ctx, win, x * scale, y * scale, w * scale, h * scale) so DVX puts it on the screen.</li>
|
|
<li>Give the window an onPaint that converts the whole framebuffer (without invalidating): DVX calls it when the window is first shown and after a video mode change, when contentBuf has been made again.</li>
|
|
<li>Clip to contentW and contentH, and do nothing while contentBuf is NULL.</li>
|
|
</ul>
|
|
<p>Size the window so its content area is exactly the framebuffer times a whole scale that fits the screen (the template computes the scale from the display size and the chrome constants, then corrects with dvxResizeWindow after adding the menu bar). Copying only what changed matters on slower machines: a full 320x200 frame at 2x on a 16-bit desktop is 256 KB of pixels to convert and composite.</p>
|
|
</div>
|
|
<div class="topic" id="games.sound">
|
|
<h1>Sound, Music and MIDI</h1>
|
|
<p>Games play sound on whatever the computer has: a Sound Blaster family card, an FM synthesizer chip, a MIDI port, the PC speaker, or nothing. Nothing fails because of missing hardware: audioOpen returns NULL (or the device plays tones only), and the game carries on silently. Several programs can play at once, and everything a program opened is stopped and released when it ends.</p>
|
|
<h2>Sound Effects and Tones (audio.h)</h2>
|
|
<ul>
|
|
<li>audioOpen() gives the program an audio context (the first one opened starts the sound device).</li>
|
|
<li>audioSampleLoad / audioSampleLoadMem load a WAV or VOC file (from memory: a resource, say); audioSampleCreate makes a sample from raw PCM (8-bit unsigned or 16-bit signed, mono or stereo, any rate from 4000 to 48000 Hz).</li>
|
|
<li>audioPlay(sample, volume, pan, loop) plays it on a voice; the voice id sets volume, pan and pitch while it plays (audioVoiceSet...), audioVoiceStop stops it, and audioSetEndFn tells when it ends.</li>
|
|
<li>audioTone plays a square, triangle or noise tone of a frequency and length; audioNotesOpen gives a queue of notes and rests played back to back, each to the exact sample (jingles, chip-style tunes).</li>
|
|
<li>audioStreamOpen takes the game's own sound at any rate: written with audioStreamWrite, or asked for by a fill function.</li>
|
|
<li>audioSetVolume sets the program's volume; the master volume belongs to the user (Control Panel).</li>
|
|
</ul>
|
|
<h2>Music (music.h)</h2>
|
|
<p>musicLoad or musicLoadMem load a tracker module (MOD, S3M, XM, IT) or a Standard MIDI File; musicPlay(music, loop) plays it, with pause, resume, stop, volume, position and an end notification. MIDI plays on the best synthesizer there is: an external one on the MPU-401 port when the user chose it, else the FM chip with a General MIDI bank, else DVX's own FM software synthesizer; modules play on any card that plays sampled sound. musicMidiOpen gives live MIDI (note on and off, program change, controllers, pitch bend), and musicMmlPlay plays tunes written in QBasic's music macro language.</p>
|
|
<h2>The FM Chip and the MIDI Port</h2>
|
|
<p>For music drivers of its own, a game can allocate two-operator FM voices shared with the other programs (audioFmAlloc, audioFmWrite with the AUDIO_FM_* register numbers), take the whole FM chip for itself (audioFmRawAcquire; refused with AUDIO_ERR_BUSY while another program has it), or send bytes to the MIDI port (audioMidiAcquire, audioMidiPut). Give them back as soon as the game does not need them.</p>
|
|
<h2>Keeping Sound Fed</h2>
|
|
<p>Sampled sound is mixed from the sound card's own interrupt, so it keeps playing however busy the game is. End notifications, pull streams, modules and the software synthesizer are made in the desktop's idle time; a game that redraws every frame should call audioPump() and musicPump() once a frame so they keep up. Modules use floating point: on a computer without a maths coprocessor they are slow.</p>
|
|
<p>See the Sound Library, the Music Library and the Joystick Library for every function.</p>
|
|
</div>
|
|
<div class="topic" id="games.files">
|
|
<h1>Data Files and Saved Games</h1>
|
|
<p>DOS has one current directory for everything, so a program never relies on it. appMain gets a DxeAppContextT with the paths it needs:</p>
|
|
<pre> Field What it is Use it for
|
|
----- ---------- ----------
|
|
appPath the program's .app file dvxResOpen: resources inside the .app
|
|
appDir the folder the .app is in read-only game data installed beside it
|
|
configDir the program's own settings folder saved games, high scores, options
|
|
helpFile the program's .hlp file shellOpenHelp (F1)</pre>
|
|
<p>configDir is CONFIG plus the folder the .app is in: a game in APPS\ACME\MYGAME keeps its files in CONFIG\APPS\ACME\MYGAME.</p>
|
|
<ul>
|
|
<li>Small data (sounds, music, a title picture) can travel inside the .app as resources: list them in NAME.res as binary (or icon, text) and read them with dvxResOpen(ctx->appPath) and dvxResRead. The template carries its sound effect and music this way.</li>
|
|
<li>Bigger data goes in files beside the .app (the DATA variable in the Makefile packages them there): build paths from appDir, never from the current directory.</li>
|
|
<li>Saved games go in configDir, which DVX keeps apart from the program files so a reinstall never overwrites them. Create it with shellEnsureConfigDir(ctx) before the first write and build names with shellConfigPath(ctx, "SAVE1.DAT", path, size). For settings and scores, prefsLoad and prefsSaveAs read and write INI files safely (a failed save leaves the old file intact).</li>
|
|
<li>Use 8.3 file names (8 characters, a dot, 3 characters).</li>
|
|
<li>platformDiskSpace tells how much room is free before a big save.</li>
|
|
<li>dvxLog writes a line to DVX.LOG, the place to look when something goes wrong.</li>
|
|
</ul>
|
|
</div>
|
|
<div class="topic" id="games.packaging">
|
|
<h1>Packaging and Ending</h1>
|
|
<h2>Packaging</h2>
|
|
<p>make package lays out pkg/APPS/<VENDOR>/<NAME>/ with NAME.app, NAME.hlp and the files listed in DATA; zip the contents of pkg/ and the player unpacks it into the DVX directory (make install DVX_DIR=... does it for you). The .app carries its icon, name, author and description for Program Manager (NAME.res). Every game should have its own help (NAME.dhs, compiled to NAME.hlp), since F1 in a game input window goes to the game: open the help from the game's F1 and from Help > Contents. A game built on a static library of its own links it in with the LIBS variable (LIBS = port/libgame.a).</p>
|
|
<h2>Ending</h2>
|
|
<p>Close the game's window and return from appMain. Every sound, music, joystick handle and timer the program opened is released when it ends, however it ends, so a crash or End Task leaves nothing playing. A game (or any program) that calls exit() or abort() ends only itself, like End Task: its windows close, its timers are removed and DVX keeps running. Handlers registered with atexit are not run; clean up before calling exit(), or return from appMain. malloc and free work as usual; DVX counts each program's memory (the Task Manager shows it).</p>
|
|
</div>
|
|
<div class="topic" id="games.basic">
|
|
<h1>Games in DVX BASIC</h1>
|
|
<p>newProject.sh basicgame NAME starts a BASIC game. A Timer fires once and runs the game loop; FRAMEWAIT rate waits for the next frame while the desktop and the program's forms keep running, and the loop ends when the form closes:</p>
|
|
<pre><code>Sub tmrStart_Timer ()
|
|
tmrStart.Enabled = False
|
|
Do
|
|
MovePlayer ' GameKeyDown, STICK(0), STICK(1), STRIG(1)
|
|
DrawScreen ' a PictureBox: Clear, FillRect, FillCircle
|
|
FRAMEWAIT 30
|
|
Loop Until quitting ' set in the form's Unload event
|
|
End Sub</code></pre>
|
|
<p>GameKeyDown (game.bas) tells whether a key is held; QBasic's STICK and STRIG read joysticks (the keyboard joystick when there is none); BEEP, SOUND and PLAY make sound, PLAY "MB ..." in the background so the game does not wait. sound.bas plays WAV files and tones, music.bas plays modules and MIDI files, and joystick.bas reads joysticks in full. See Sound, Joysticks and Frames and Include Libraries in the BASIC reference.</p>
|
|
</div>
|
|
<div class="topic" id="games.porting">
|
|
<h1>Porting a Game Library to DVX</h1>
|
|
<p>Portable game libraries for DOS-era machines usually keep everything machine-specific behind a small hardware layer: start and stop, show a frame, set the palette, read input, wait for the next frame, play sound, and find files. A DVX port replaces that layer. The rest of the library, including drawing code, sprite compilers that write x86 code into memory, and asset formats built for DOS, works unchanged, because a DVX program is 32-bit protected-mode x86 code in the same flat memory as a DOS program built with the same compiler.</p>
|
|
<h2>The Model: a Window, a Task, a Virtual Frame Rate</h2>
|
|
<p>The game becomes a DVX program with its own main loop: appMain calls the game's main() (compile the game with -Dmain=gameMain, say, and call gameMain from appMain). The library's start-up creates a game input window whose content area is the game's screen at a whole scale; its frame wait yields until the next frame on the high-resolution clock. The game shares the screen with the desktop, so there is no mode change, no direct palette hardware and no vertical retrace; the port converts the library's back buffer into the window each frame.</p>
|
|
<h2>Mapping a Hardware Layer to DVX</h2>
|
|
<pre> Hardware layer DVX
|
|
-------------- ---
|
|
Start-up SHELL_APP_DESCRIPTOR (hasMainLoop), dvxCreateWindowCentered, gameInput
|
|
Shut-down dvxDestroyWindow, return from appMain
|
|
Error text, debug log dvxLog (DVX.LOG)
|
|
Present the back buffer colour table -> win->contentBuf, dvxInvalidateRect; onPaint
|
|
Palette packColor, gamePalNearest, GamePalCacheT
|
|
Palette per scan line one colour table per palette, chosen per row
|
|
Wait for vertical blank gameFrameInit, gameFrameWait
|
|
Frame counter, frame rate GameFramePacerT.frame, the rate given to gameFrameInit
|
|
Milliseconds platformMicros() / 1000
|
|
Held keys onRawKey, gameKeyIsDown, platformKeyStateGet
|
|
Typed characters onKey
|
|
Mouse onMouse
|
|
Joystick joyCount, joyOpen, joyRead
|
|
Periodic interrupt callback gameTimerAdd, platformLockRegion
|
|
Critical section platformIrqSave, platformIrqRestore
|
|
Audio start and stop audioOpen, audioClose
|
|
Sound effect slots audioSampleCreate, audioSampleLoadMem, audioPlay, audioVoiceSetPan, audioVoiceStop
|
|
Streamed sound audioStreamOpen, audioStreamWrite
|
|
Tracker modules musicLoadMem, musicPlay, musicStop, musicIsPlaying
|
|
Tone and noise voices audioTone, audioVoiceSetFrequency, audioVoiceSetVolume, audioNotesOpen, audioFmAlloc
|
|
Per-frame audio work audioPump, musicPump
|
|
Read-only data folder DxeAppContextT.appDir, dvxResOpen, dvxResRead
|
|
Save folder DxeAppContextT.configDir, shellEnsureConfigDir, shellConfigPath
|
|
Disk free platformDiskSpace
|
|
Memory malloc, free
|
|
Serial port the serial library (rs232Open)
|
|
exit() ends only the game</pre>
|
|
<h2>Notes on the Mapping</h2>
|
|
<ul>
|
|
<li>Start-up: make the window not resizable, with its content area the game's screen at a whole scale that fits the display; give it onClose and onPaint. audioOpen returning NULL (no sound device) is not an error.</li>
|
|
<li>Shut-down: close what the port opened and return. Anything left (sounds, music, joystick handles, timers) is released when the program ends.</li>
|
|
<li>Present: expand the rows or word bands that changed through the colour table into the content buffer, scaled, and invalidate them; onPaint does the whole screen. See The Screen: Framebuffers and Palettes.</li>
|
|
<li>Palette: scale 12-bit or 18-bit colour to 0..255 per channel before looking it up. Never touch the VGA palette registers: other programs share the screen.</li>
|
|
<li>Vertical blank: there is none in a window. The frame rate is a virtual one: use the rate the library reports (60 or 70 frames a second); truncate the frame counter to the library's width.</li>
|
|
<li>Keyboard: raw codes are set-1 scancodes with KEY_RAW_EXT for the keys sent behind 0xE0, so a DOS port's scancode table maps directly. Never hook the keyboard interrupt.</li>
|
|
<li>Mouse: divide content-area pixels by the scale and clamp.</li>
|
|
<li>Joystick: axes are -32768..32767 after calibration and dead zone; divide down to the library's range, or compare with a threshold for a digital stick. Buttons are bits. Never read the game port directly; calibration lives in the Control Panel.</li>
|
|
<li>Timer callbacks run inside the timer interrupt: no C library, no DVX calls, no floating point, code and data pinned. Never reprogram the PIT or hook the timer interrupt.</li>
|
|
<li>Sound effects: DVX's 8-bit samples are unsigned; flip the top bit of signed 8-bit data or convert it to 16-bit. Stream fill functions run in program time, not in an interrupt.</li>
|
|
<li>Files: the library's fixed "DATA" and save folders become run-time paths under appDir and configDir; use remove() to delete a save.</li>
|
|
<li>Serial: one owner per COM port, so the port uses DVX's serial library rather than programming the UART.</li>
|
|
<li>exit() ends only the game, like End Task; atexit handlers do not run.</li>
|
|
</ul>
|
|
<h2>Things to Decide</h2>
|
|
<ul>
|
|
<li>Shared library or static: built as a DVX library (.lib, loaded when DVX starts) the port is shared by every game, but its global state (the stage, the key table) means one game at a time. Linked into each game's .app (the LIBS variable) every game has its own copy, several can run at once, and nothing is loaded while no game runs.</li>
|
|
<li>Paths: libraries that open "DATA/..." or write saves to a fixed folder need a run-time root (appDir and configDir) instead of compile-time paths.</li>
|
|
<li>Speed: every frame costs a conversion and a composite. Present only what changed; a 1x scale on a 486 and larger scales on faster machines.</li>
|
|
<li>Tearing: there is no vertical retrace in a window. Games that depend on raster timing need rework.</li>
|
|
<li>The 256-colour desktop: nearest colours only; say so in the game's help if it needs 15-bit colour or better.</li>
|
|
</ul>
|
|
<h2>Checking a Port</h2>
|
|
<p>The library's own drawing tests (hashes of the back buffer after each drawing call) should give the same answers as on DOS, since the drawing code is the same. Test in a 256-colour and a 16-bit desktop, with and without a sound card and a joystick, and with other programs running; End Task and exit() must leave DVX, the sound and the timer as they were.</p>
|
|
</div>
|
|
<div class="topic" id="sdk.quickstart">
|
|
<h1>SDK Quick Start</h1>
|
|
<p>The DVX SDK holds everything needed to build DVX modules outside the DVX source tree: the public headers, the host tools, a shared set of build rules, and a template for each kind of module. A DVX build writes it to bin/sdk.</p>
|
|
<pre> Path Contents
|
|
---- --------
|
|
newProject.sh Creates a project from a template
|
|
sdk.mk Build rules every project Makefile includes
|
|
bin/ dvxres (resources), dvxhlpc (help compiler), bascomp (BASIC compiler), dxeGen.sh (runs dxe3gen)
|
|
include/ Public C headers, one directory per library, plus include/basic for BASIC declaration files
|
|
templates/ The project templates
|
|
samples/basic/ Example BASIC projects</pre>
|
|
<h2>Requirements</h2>
|
|
<ul>
|
|
<li>A Linux host (the tools in bin/ are Linux programs; on DVX itself they are SYSTEM/DVXRES.EXE, SYSTEM/DVXHLPC.EXE and SYSTEM/BASCOMP.EXE).</li>
|
|
<li>The DJGPP cross compiler (i586-pc-msdosdjgpp-gcc and dxe3gen). Projects find it through DJGPP_PREFIX, from the environment or the make command line. Without it they use toolchains/djgpp of the DVX source tree the SDK sits in (SDK_DIR/../../toolchains/djgpp), which ./installToolchain.sh sets up.</li>
|
|
</ul>
|
|
<h2>Creating a Project</h2>
|
|
<pre><code> bin/sdk/newProject.sh KIND NAME [DIR]</code></pre>
|
|
<pre> KIND Creates
|
|
---- -------
|
|
app A C program (NAME.app)
|
|
widget A C widget (NAME.wgt) usable from C programs and DVX BASIC
|
|
library A C library of shared functions (NAME.lib) with BASIC declarations
|
|
basic A DVX BASIC program (NAME.dbp, built to NAME.app)
|
|
game A C game (NAME.app): a 320x200 screen in a window, frame pacing, keyboard and joystick, sound effects and music
|
|
basicgame A DVX BASIC game (NAME.dbp): a FRAMEWAIT loop, GameKeyDown, STICK, STRIG and PLAY</pre>
|
|
<p>The two game templates are explained in Writing Games for DVX.</p>
|
|
<p>NAME is a letter followed by letters and digits, 8 characters at most, because it becomes a DOS file name. Files are named NAME with its first letter in lower case (camelCase, such as myApp); DVX shows it in upper case. The script refuses DOS device names (CON, PRN, AUX, NUL, COM1 and so on), the name of a widget DVX already has, and an existing directory. The project goes in DIR, or a new directory of that name in the current one.</p>
|
|
<p>The template's names become yours everywhere: file names, identifiers in each case style (tplName, TplName, tplname, TPLNAME), help topic ids and the ABI stamp. The Makefile refers to the SDK by a relative path, so a project and the SDK can move together.</p>
|
|
<h2>Building</h2>
|
|
<p>Every project Makefile only names the SDK, the kind and the module; the rules come from sdk.mk:</p>
|
|
<pre> Command Does
|
|
------- ----
|
|
make Builds the module with its resources, and NAME.hlp from the help source
|
|
make package Lays out pkg/ exactly as the files go into the DVX directory
|
|
make install DVX_DIR=/path/to/dvx Packages, then copies pkg/ into a DVX installation
|
|
make clean Removes everything the build made</pre>
|
|
<p>Every .c file in the project directory is compiled with warnings as errors (-Wall -Wextra -Werror) and every SDK include directory on the path. Add your own flags with CFLAGS, for example CFLAGS=-I../myLib to use a library's header. VENDOR (default local) is the vendor directory the module installs under.</p>
|
|
<h2>Programs That Use the Network</h2>
|
|
<p>The dvxnet library provides TCP/IP through the standard socket calls (socket, connect, send, recv, getaddrinfo, select and so on). Its headers are in include/net: dvxNet.h, plus sys/socket.h, netinet/in.h, netinet/tcp.h, arpa/inet.h, netdb.h, poll.h, sys/select.h, sys/ioctl.h and an errno.h that adds the socket error codes to the C library's. Because they stand in for headers of the same name, include/net is not on every module's path; a module that uses sockets adds one line to its Makefile, before the include of sdk.mk:</p>
|
|
<pre><code> NET = 1</code></pre>
|
|
<p>sdk.mk then puts include/net ahead of the C library's headers, and the program uses the ordinary names:</p>
|
|
<pre><code> #include <sys/socket.h>
|
|
#include <netdb.h>
|
|
|
|
struct addrinfo hints = {0};
|
|
struct addrinfo *res = NULL;
|
|
|
|
hints.ai_socktype = SOCK_STREAM;
|
|
|
|
if (getaddrinfo("example.com", "80", &hints, &res) == 0) {
|
|
int fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
|
|
...
|
|
}</code></pre>
|
|
<p>Nothing else is needed: DVX loads dvxnet.lib when it starts and connects the calls when it loads the program. Close a socket with close(), and free the getaddrinfo result with freeaddrinfo.</p>
|
|
<p><a href="#lib.dvxnet">The dvxnet library</a></p>
|
|
<h2>The Guides</h2>
|
|
<p><a href="#sdk.firstapp">Your First DVX Program</a></p>
|
|
<p><a href="#sdk.firstwidget">Your First Widget</a></p>
|
|
<p><a href="#sdk.firstlib">Your First Library</a></p>
|
|
<p><a href="#sdk.firstbasic">Your First BASIC Program</a></p>
|
|
<p><a href="#sdk.debug">Debugging: the Log and Crash Reports</a></p>
|
|
<p><a href="#sdk.abi">Binary Interface Rules</a></p>
|
|
<p><a href="#sdk.packaging">Packaging and Installing</a></p>
|
|
<p><a href="#games.intro">Writing Games for DVX</a></p>
|
|
</div>
|
|
<div class="topic" id="sdk.firstapp">
|
|
<h1>Your First DVX Program</h1>
|
|
<h2>Create and Build</h2>
|
|
<pre><code> bin/sdk/newProject.sh app hello
|
|
cd hello
|
|
make</code></pre>
|
|
<p>The build makes hello.app and hello.hlp. The project holds:</p>
|
|
<pre> File Purpose
|
|
---- -------
|
|
hello.c The program
|
|
hello.res Resources appended to hello.app: its icon, name, author, description and a greeting text
|
|
icon32.bmp The 32x32 icon Program Manager shows
|
|
hello.dhs The program's help, compiled to hello.hlp
|
|
Makefile Names the SDK, KIND = app and NAME = hello</pre>
|
|
<h2>How the Program Works</h2>
|
|
<p>SHELL_APP_DESCRIPTOR(...) declares the exported appDescriptor: the program's name, whether it has its own main loop, whether several copies may run, its task stack size and priority. It also records the binary interface version the program was built for.</p>
|
|
<p>DVX calls appMain(DxeAppContextT *ctx) once. The template is a callback-only program (hasMainLoop = false): appMain creates a window with a File and Help menu, a label and a button, and returns 0. From then on DVX calls the window's onClose and onMenu callbacks and the button's onClick. Destroying the program's last window ends it. A program that needs to keep working between events sets hasMainLoop = true, loops in appMain and calls tsYield() often (see the Clock program's source for an example).</p>
|
|
<p>ctx gives the program its paths: appPath (the .app file, used to read its own resources with dvxResOpen and dvxResRead), appDir, configDir (a writable directory for its settings) and helpFile (preset to hello.hlp beside hello.app).</p>
|
|
<h2>Run</h2>
|
|
<pre><code> make install DVX_DIR=/path/to/dvx</code></pre>
|
|
<p>This copies hello.app and hello.hlp to APPS/LOCAL/HELLO/ in the DVX directory. Start DVX and run the program from Program Manager (File > Run), or add an icon for it. After a change, rebuild and install again; close the running copy first.</p>
|
|
<h2>Help</h2>
|
|
<p>F1 and Help > Contents open hello.hlp. Edit hello.dhs and rebuild; the compiler stops on an error with the file and line. The format is described in the Help Viewer's own help (Help Source Format).</p>
|
|
<h2>Next</h2>
|
|
<p><a href="#sdk.debug">Debugging: the Log and Crash Reports</a></p>
|
|
<p><a href="#sdk.abi">Binary Interface Rules</a></p>
|
|
<p><a href="#sdk.packaging">Packaging and Installing</a></p>
|
|
</div>
|
|
<div class="topic" id="sdk.firstwidget">
|
|
<h1>Your First Widget</h1>
|
|
<h2>Create and Build</h2>
|
|
<pre><code> bin/sdk/newProject.sh widget badge
|
|
cd badge
|
|
make</code></pre>
|
|
<p>The build makes badge.wgt and badge.hlp (a preview of both help sources compiled together). The project holds:</p>
|
|
<pre> File Purpose
|
|
---- -------
|
|
badge.c The widget: class, API table, BASIC interface, wgtRegister
|
|
badge.h The public API: BadgeApiT, the dvxBadgeApi() accessor and the wgtBadge... macros
|
|
badge.res Resources: icon24 (the DVX BASIC toolbox icon) and name (its tool tip)
|
|
icon24.bmp The 24x24 toolbox icon
|
|
badge.dhs Developer help, added to the system help
|
|
badge.bhs BASIC help, added to the DVX BASIC help
|
|
Makefile KIND = widget</pre>
|
|
<h2>How the Widget Works</h2>
|
|
<p>DVX loads every widget when it starts and calls its wgtRegister once. The template registers three things:</p>
|
|
<ul>
|
|
<li>A widget class (WidgetClassT): the paint and minimum-size handlers, and the shared text handlers (the widget's data starts with its text, so widgetAllocWithText creates it and WCLASS_HAS_TEXT frees it).</li>
|
|
<li>An API table (BadgeApiT) under the name "badge". It starts with WgtApiHeaderT, filled by WGT_API_HEADER. Programs reach it through the macros in badge.h, which call wgtGetApi("badge", sizeof(BadgeApiT)).</li>
|
|
<li>A BASIC interface (WgtIfaceT): the control name Badge, a Style property, a Clear method and Click as the default event. The form runtime adds the common properties (Caption, Left, Top, Width, Height, Visible, Enabled...) and events itself.</li>
|
|
</ul>
|
|
<p>DVX_ABI_STAMP(badge) at file scope records the binary interface version. Its argument must be the module's file name in lower case.</p>
|
|
<h2>Run</h2>
|
|
<p>Install it (make install DVX_DIR=/path/to/dvx puts badge.wgt and its help in WIDGETS/LOCAL/BADGE/) and restart DVX. The widget is then:</p>
|
|
<ul>
|
|
<li>In the DVX BASIC toolbox: draw a Badge on a form, set its Style in the property panel.</li>
|
|
<li>Usable from a C program: add CFLAGS=-I../badge to the program's make command (or Makefile), include badge.h and call wgtBadge(parent, "Text"). wgtGetApi returns NULL when the widget is not installed, so a program that can run without it checks dvxBadgeApi() before using the macros.</li>
|
|
</ul>
|
|
<blockquote><strong>Warning:</strong> DVX stops at start-up with a message naming a widget or library it cannot load or that was built for a different version. Remove that module's directory to start DVX again.</blockquote>
|
|
<h2>Help</h2>
|
|
<p>badge.dhs and badge.bhs are installed as BADGE.DHS and BADGE.BHS beside the widget. When DVX starts and finds a different set of widgets or libraries, it rebuilds the system help and the DVX BASIC help with them, so your pages appear there. The make build compiles both into badge.hlp only to check them.</p>
|
|
<h2>Next</h2>
|
|
<p><a href="#sdk.debug">Debugging: the Log and Crash Reports</a></p>
|
|
<p><a href="#sdk.abi">Binary Interface Rules</a></p>
|
|
<p><a href="#sdk.packaging">Packaging and Installing</a></p>
|
|
</div>
|
|
<div class="topic" id="sdk.firstlib">
|
|
<h1>Your First Library</h1>
|
|
<p>Anything several programs could share belongs in a library: file operations, protocols, device access. Programs then stay thin user interfaces over it.</p>
|
|
<h2>Create and Build</h2>
|
|
<pre><code> bin/sdk/newProject.sh library mathx
|
|
cd mathx
|
|
make</code></pre>
|
|
<p>The build makes mathx.lib and mathx.hlp (a preview of the help). The project holds:</p>
|
|
<pre> File Purpose
|
|
---- -------
|
|
mathx.c The functions and DVX_ABI_STAMP(mathx)
|
|
mathx.h The public header programs include; MATHX_VERSION
|
|
mathx.dep Libraries that must load first, one per line (the template lists libdvx)
|
|
mathx.bas DECLARE LIBRARY declarations for BASIC programs
|
|
mathx.res Resources identifying the module (name, author, description, icon)
|
|
icon32.bmp The icon in the resources
|
|
mathx.dhs Developer help, added to the system help
|
|
Makefile KIND = library</pre>
|
|
<h2>How the Library Works</h2>
|
|
<p>DVX loads every library when it starts, before any widget or program, in the order the .dep files require. Every function that is not static is exported: a program, widget or library loaded later calls it directly, and DVX connects the call when it loads that module. A library that calls another library's functions lists it in its .dep file.</p>
|
|
<p>BASIC programs add mathx.bas to their project. DECLARE LIBRARY looks up each function by exactly the name it declares, without a trailing $ (a $ marks a function returning a string), so mathxGreet$ calls the C function mathxGreet. Keep the declarations and the C functions in step; strings are passed as const char * and returned as const char *.</p>
|
|
<h2>Run</h2>
|
|
<p>Install it (LIBS/LOCAL/MATHX/ gets mathx.lib, mathx.dep and MATHX.DHS) and restart DVX. Build programs that use it with CFLAGS=-I../mathx.</p>
|
|
<h2>Help</h2>
|
|
<p>mathx.dhs goes into the system help, under Technical Reference > Libraries, when DVX next starts with the library installed.</p>
|
|
<h2>Next</h2>
|
|
<p><a href="#sdk.debug">Debugging: the Log and Crash Reports</a></p>
|
|
<p><a href="#sdk.abi">Binary Interface Rules</a></p>
|
|
<p><a href="#sdk.packaging">Packaging and Installing</a></p>
|
|
</div>
|
|
<div class="topic" id="sdk.firstbasic">
|
|
<h1>Your First BASIC Program</h1>
|
|
<h2>Create and Build</h2>
|
|
<pre><code> bin/sdk/newProject.sh basic greeter
|
|
cd greeter
|
|
make</code></pre>
|
|
<p>The build compiles the project with bascomp into greeter.app and the help into greeter.hlp. The project holds:</p>
|
|
<pre> File Purpose
|
|
---- -------
|
|
greeter.dbp The project: name, author, version, icon, modules, forms, startup form
|
|
greeter.frm The main form (controls and menus) and its code
|
|
icon32.bmp The program's icon (the project's Icon entry)
|
|
greeter.dhs The program's help, compiled to greeter.hlp
|
|
Makefile KIND = basic</pre>
|
|
<p>The project lists the SDK's include/basic/help.bas, which declares HelpView; Help > Contents calls HelpView "greeter.hlp", which opens the file beside the program. The project settings in the .dbp take the place of a C program's resource file.</p>
|
|
<h2>Run and Debug</h2>
|
|
<p>make install puts greeter.app and greeter.hlp in APPS/LOCAL/GREETER/. You can also copy the project directory to the DVX machine and open greeter.dbp in DVX BASIC (File > Open Project...): Run > Run (F5) runs it, Run > Debug sets it going under the debugger with breakpoints (F9), stepping and the locals, watch and call stack windows, and File > Make Executable... builds the .app there. The DVX BASIC help describes the language and the IDE.</p>
|
|
<h2>Next</h2>
|
|
<p><a href="#sdk.packaging">Packaging and Installing</a></p>
|
|
</div>
|
|
<div class="topic" id="sdk.debug">
|
|
<h1>Debugging: the Log and Crash Reports</h1>
|
|
<h2>The Log</h2>
|
|
<p>dvxLog(fmt, ...) (dvxPlat.h) appends one printf-style line to DVX.LOG in the DVX directory. The file is opened and closed for every line, so everything written before a crash is kept. DVX logs there too: each module it loads, why a module was refused, each program started (Shell: loaded 'Name' ... entry=0x...), and each help rebuild.</p>
|
|
<h2>Crash Reports</h2>
|
|
<p>When a program faults, DVX ends that program with the message "'Name' has caused a fault and will be terminated." and keeps running. DVX.LOG then holds the report:</p>
|
|
<pre><code> === CRASH ===
|
|
Signal: 11 (SIGSEGV (page fault))
|
|
EIP: 0x... CS: 0x...
|
|
EAX: ... (all registers)
|
|
App name: Hello
|
|
App path: APPS\LOCAL\HELLO\hello.app</code></pre>
|
|
<p>To find the faulting code of a program, take its entry= address from the Shell: loaded line: it is where appMain is. EIP minus that address is the fault's distance from appMain. Then:</p>
|
|
<pre><code> i586-pc-msdosdjgpp-nm -n hello.o appMain's offset in .text
|
|
i586-pc-msdosdjgpp-objdump -d hello.o the code at appMain's offset plus the distance</code></pre>
|
|
<p>This works directly when the program is one .c file (as the templates are). Build with CFLAGS=-g and use objdump -d -S to see the source lines.</p>
|
|
<h2>Other Tips</h2>
|
|
<ul>
|
|
<li>A module DVX refuses is named in DVX.LOG with the reason (missing symbol, different binary interface version, missing ABI stamp).</li>
|
|
<li>Memory a program allocates is counted against it and freed when it ends (Task Manager shows each program's use).</li>
|
|
<li>DVX BASIC programs are best debugged in the DVX BASIC IDE (Run > Debug).</li>
|
|
</ul>
|
|
</div>
|
|
<div class="topic" id="sdk.abi">
|
|
<h1>Binary Interface Rules</h1>
|
|
<p>DVX_ABI_VERSION (dvxTypes.h) is one number for the binary interface between DVX and every module. Each module records the number it was built with, and DVX refuses a module whose number differs, so a module built against another release's headers cannot call the wrong functions.</p>
|
|
<pre> Module Records it with If it differs
|
|
------ --------------- -------------
|
|
Program SHELL_APP_DESCRIPTOR(...) "This program was built for a different version of DVX and cannot run."
|
|
Widget DVX_ABI_STAMP(name); WGT_API_HEADER in its API DVX stops at start-up naming the module
|
|
Library DVX_ABI_STAMP(name) DVX stops at start-up naming the module</pre>
|
|
<ul>
|
|
<li>The DVX_ABI_STAMP argument is the module's file name in lower case, without the extension. Renaming the file without changing the stamp makes DVX report it as having no stamp.</li>
|
|
<li>A widget's API table starts with WgtApiHeaderT (.header = WGT_API_HEADER(YourApiT)). Callers pass the size of the table they were built with to wgtGetApi, which refuses a shorter table; so only ever add members at the end of your table.</li>
|
|
<li>A library's exported functions are its interface: never change one's parameters or return type. Add a new function instead, and raise your own version constant (the template's NAME_VERSION) so callers can check for it.</li>
|
|
<li>When DVX's number changes, rebuild all your modules against the new SDK.</li>
|
|
</ul>
|
|
<p>The DVX side of these rules (what bumps the number) is under Binary Interface Version in the API reference.</p>
|
|
</div>
|
|
<div class="topic" id="sdk.packaging">
|
|
<h1>Packaging and Installing</h1>
|
|
<p>make package lays out pkg/ exactly as the files go into the DVX directory; make install DVX_DIR=... copies it there. VENDOR (default local) names the vendor directory; use a short name of your own (8 characters at most).</p>
|
|
<pre> Kind Directory Files
|
|
---- --------- -----
|
|
Program APPS/<VENDOR>/<NAME>/ NAME.app, NAME.hlp, the DATA files (if any)
|
|
Widget WIDGETS/<VENDOR>/<NAME>/ NAME.wgt, NAME.DHS, NAME.BHS, NAME.dep (if any)
|
|
Library LIBS/<VENDOR>/<NAME>/ NAME.lib, NAME.DHS, NAME.dep (if any)</pre>
|
|
<ul>
|
|
<li>Programs carry their compiled help. Widgets and libraries carry help sources, which DVX compiles into its system help when the set of installed modules changes.</li>
|
|
<li>.dep files are written with DOS line endings.</li>
|
|
<li>A program's data files (levels, graphics, sounds it loads at run time) go in the Makefile's DATA variable, for example DATA = levels sounds; the package puts them beside NAME.app, where the program finds them through appDir in its DxeAppContextT. Small ones can instead travel inside the .app as binary resources (see the .res file).</li>
|
|
<li>To ship a module, zip the contents of pkg/; the user unpacks it into the DVX directory. A library's header and .bas file go to the developers who use it, not into the DVX directory.</li>
|
|
<li>Removing a module is deleting its directory.</li>
|
|
</ul>
|
|
</div>
|
|
<div class="topic" id="libtasks">
|
|
<h1>libtasks -- Cooperative Task Switching</h1>
|
|
<p>Credit-based cooperative (non-preemptive) multitasking library for DOS protected mode (DJGPP/DPMI). Each task receives (priority + 1) credits per scheduling round. Tasks run round-robin, consuming one credit per turn. When all credits are exhausted, every ready task is refilled. Higher-priority tasks run proportionally more often but never starve lower ones.</p>
|
|
<p>Header: libtasks/taskSwch.h</p>
|
|
<p>Loaded as: bin/libs/libtasks.lib</p>
|
|
<h2>Why Cooperative</h2>
|
|
<p>DOS is single-threaded with no kernel scheduler. DPMI provides no preemption primitives. The DVX GUI event model is inherently single-threaded: one compositor, one input queue, one window stack. Cooperative switching lets each task yield at safe points, avoiding synchronization primitives entirely.</p>
|
|
<h2>Scheduling Semantics</h2>
|
|
<p>Task storage is a stb_ds dynamic array. Terminated slots are recycled by tsCreate so the array does not grow unboundedly. The scheduler performs a linear scan for the next Ready task whose credit count is positive; when no task has credits left, every Ready task is refilled with (priority + 1) credits and the scan restarts.</p>
|
|
<pre> Priority Credits per round Approximate share
|
|
-------- ----------------- -----------------
|
|
TS_PRIORITY_LOW 1 ~4% (1 of 25 possible credits)
|
|
TS_PRIORITY_NORMAL 6 ~22%
|
|
TS_PRIORITY_HIGH 11 ~41%</pre>
|
|
<p>Actual share depends on the mix of priorities currently running. What the algorithm guarantees is that no ready task ever goes unscheduled -- even a priority-0 task always earns at least one turn per round.</p>
|
|
<h3>Stack Allocation</h3>
|
|
<p>Task 0 uses the caller's stack (no separate allocation). Every other task gets a heap-allocated stack of the size passed to tsCreate (or TS_DEFAULT_STACK_SIZE if 0 is passed; sizes below TS_MIN_STACK_SIZE are raised to it). Stacks are freed at task termination: at once by tsKill, or on the next scheduler entry after tsExit.</p>
|
|
<h2>Error Codes</h2>
|
|
<pre> Constant Value Description
|
|
-------- ----- -----------
|
|
TS_OK 0 Success
|
|
TS_ERR_INIT -1 Task system not initialized
|
|
TS_ERR_PARAM -2 Invalid parameter (bad task ID, priority, etc.)
|
|
TS_ERR_NOMEM -4 Memory allocation failed
|
|
TS_ERR_STATE -5 Invalid state transition</pre>
|
|
<h2>Constants</h2>
|
|
<pre> Constant Value Description
|
|
-------- ----- -----------
|
|
TS_DEFAULT_STACK_SIZE 32768 Default stack size per task (32 KB)
|
|
TS_MIN_STACK_SIZE 4096 Smallest stack tsCreate allocates (4 KB)
|
|
TS_NAME_MAX 32 Maximum task name length including NUL
|
|
TS_PRIORITY_LOW 0 Low priority (1 credit per round)
|
|
TS_PRIORITY_NORMAL 5 Normal priority (6 credits per round)
|
|
TS_PRIORITY_HIGH 10 High priority (11 credits per round)</pre>
|
|
<h2>Types</h2>
|
|
<h3>TaskStateE</h3>
|
|
<p>Task scheduling state. Only Ready tasks participate in scheduling.</p>
|
|
<pre> Value Description
|
|
----- -----------
|
|
TaskStateReady Eligible for scheduling
|
|
TaskStateRunning Currently executing (cosmetic; marks active task)
|
|
TaskStatePaused Skipped by scheduler until explicitly resumed
|
|
TaskStateTerminated Slot available for reuse</pre>
|
|
<h3>TaskEntryT</h3>
|
|
<pre><code>typedef void (*TaskEntryT)(void *arg);</code></pre>
|
|
<p>Task entry point function signature. The void* argument lets the caller pass arbitrary context (e.g. a shell app descriptor).</p>
|
|
<h2>tsInit</h2>
|
|
<pre><code>int32_t tsInit(void);</code></pre>
|
|
<p>Initialize the task system. The calling context becomes task 0 (the main task). Task 0 is special: it cannot be killed or paused, and tsRecoverToMain() always returns control here. No separate stack is allocated for task 0; it uses the process stack directly.</p>
|
|
<p>Returns: TS_OK on success, TS_ERR_STATE if already initialized.</p>
|
|
<h2>tsShutdown</h2>
|
|
<pre><code>void tsShutdown(void);</code></pre>
|
|
<p>Shut down the task system and free all task stacks and internal storage. Safe to call even if tsInit() was never called.</p>
|
|
<h2>tsCreate</h2>
|
|
<pre><code>int32_t tsCreate(const char *name, TaskEntryT entry, void *arg, uint32_t stackSize, int32_t priority);</code></pre>
|
|
<p>Create a new task. Terminated task slots are recycled to avoid unbounded array growth.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
name Task name (truncated to TS_NAME_MAX - 1 characters)
|
|
entry Task entry point function
|
|
arg Opaque argument passed to entry
|
|
stackSize Stack size in bytes (pass 0 for TS_DEFAULT_STACK_SIZE; raised to TS_MIN_STACK_SIZE if smaller)
|
|
priority Scheduling priority (TS_PRIORITY_LOW..TS_PRIORITY_HIGH, i.e. 0..10)</pre>
|
|
<p>Returns: Task ID (>= 0) on success, or a negative error code (TS_ERR_INIT if not initialized, TS_ERR_PARAM if entry is NULL or priority is out of range, TS_ERR_NOMEM if stack allocation failed).</p>
|
|
<h2>tsYield</h2>
|
|
<pre><code>void tsYield(void);</code></pre>
|
|
<p>Yield CPU to the next eligible ready task using credit-based round-robin. This is the sole mechanism for task switching. Every task must call this (or a GUI function that calls it) periodically, or it will monopolize the CPU.</p>
|
|
<h2>tsPause</h2>
|
|
<pre><code>int32_t tsPause(uint32_t taskId);</code></pre>
|
|
<p>Pause a task, removing it from scheduling. Cannot pause the main task (ID 0). If a task pauses itself, an implicit yield occurs.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
taskId ID of the task to pause</pre>
|
|
<p>Returns: TS_OK on success, TS_ERR_INIT if not initialized, TS_ERR_PARAM on invalid ID, or TS_ERR_STATE if the task is the main task or not in a pausable state (not Running or Ready).</p>
|
|
<h2>tsResume</h2>
|
|
<pre><code>int32_t tsResume(uint32_t taskId);</code></pre>
|
|
<p>Resume a paused task. Credits are refilled so the task gets a fair share of CPU time immediately rather than waiting for the next scheduling round.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
taskId ID of the task to resume</pre>
|
|
<p>Returns: TS_OK on success, TS_ERR_INIT if not initialized, TS_ERR_PARAM on invalid ID, TS_ERR_STATE if the task is not paused.</p>
|
|
<h2>tsSetPriority</h2>
|
|
<pre><code>int32_t tsSetPriority(uint32_t taskId, int32_t priority);</code></pre>
|
|
<p>Set a task's scheduling priority. Also refills credits so the change takes effect immediately.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
taskId ID of the task to modify
|
|
priority New priority level (TS_PRIORITY_LOW..TS_PRIORITY_HIGH, i.e. 0..10)</pre>
|
|
<p>Returns: TS_OK on success, TS_ERR_INIT if not initialized, TS_ERR_PARAM on invalid ID or out-of-range priority, or TS_ERR_STATE if the task is terminated.</p>
|
|
<h2>tsGetPriority</h2>
|
|
<pre><code>int32_t tsGetPriority(uint32_t taskId);</code></pre>
|
|
<p>Get a task's current scheduling priority.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
taskId ID of the task to query</pre>
|
|
<p>Returns: Priority value (0..10) on success, TS_ERR_INIT if not initialized, TS_ERR_PARAM on invalid ID.</p>
|
|
<h2>tsGetState</h2>
|
|
<pre><code>TaskStateE tsGetState(uint32_t taskId);</code></pre>
|
|
<p>Get a task's current scheduling state.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
taskId ID of the task to query</pre>
|
|
<p>Returns: TaskStateE value. Returns TaskStateTerminated for invalid IDs.</p>
|
|
<h2>tsCurrentId</h2>
|
|
<pre><code>uint32_t tsCurrentId(void);</code></pre>
|
|
<p>Get the ID of the currently executing task. Always valid while the task system is initialized.</p>
|
|
<p>Returns: Current task ID.</p>
|
|
<h2>tsGetName</h2>
|
|
<pre><code>const char *tsGetName(uint32_t taskId);</code></pre>
|
|
<p>Get a task's name string.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
taskId ID of the task to query</pre>
|
|
<p>Returns: Pointer to the task's name, or NULL on invalid ID. The pointer remains valid until the task slot is reused.</p>
|
|
<h2>tsExit</h2>
|
|
<pre><code>void tsExit(void);</code></pre>
|
|
<p>Terminate the calling task. Must not be called from the main task (ID 0). The slot is marked for reuse immediately; the stack, which is still in use until the switch, is freed on the next entry into the scheduler. This function never returns; it performs an internal context switch to the next ready task.</p>
|
|
<h2>tsKill</h2>
|
|
<pre><code>int32_t tsKill(uint32_t taskId);</code></pre>
|
|
<p>Forcibly terminate another task. Cannot kill the main task (ID 0) or the currently running task (use tsExit() for self-termination). Safe because cooperative scheduling guarantees the target is suspended at a yield point.</p>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
taskId ID of the task to terminate</pre>
|
|
<p>Returns: TS_OK on success, TS_ERR_INIT if not initialized, TS_ERR_PARAM on invalid ID, or TS_ERR_STATE on illegal target (main task, self, or already terminated).</p>
|
|
<h2>tsRecoverToMain</h2>
|
|
<pre><code>void tsRecoverToMain(void);</code></pre>
|
|
<p>Crash recovery: force the scheduler back to the main task (ID 0). Call after longjmp from a signal handler that fired in a non-main task. The crashed task is NOT cleaned up by this call; call tsKill() afterward to free its resources. This exists because longjmp unwinds the crashed task's stack but the scheduler state still points to it.</p>
|
|
<h2>tsActiveCount</h2>
|
|
<pre><code>uint32_t tsActiveCount(void);</code></pre>
|
|
<p>Get the number of non-terminated tasks currently in the system.</p>
|
|
<p>Returns: Count of tasks in Ready, Running, or Paused state.</p>
|
|
</div>
|
|
<div class="topic" id="lib.listhelp">
|
|
<h1>List Helper Library</h1>
|
|
<p>Shared helper routines for dropdown and list-based widget DXEs (ListBox, Dropdown, ComboBox, ListView). Provides the owned-string item list, the single/multi-select model, drag-reorder helpers, the dropdown popup (state, geometry, painting, hit testing and keyboard handling), dropdown arrow rendering, item measurement, keyboard navigation, type-ahead search, and popup scrollbar hit testing (with the same thumb geometry the window manager paints).</p>
|
|
<p>Header: listhelp/listHelp.h</p>
|
|
<p>Loaded as: bin/libs/listhelp.lib</p>
|
|
<h2>Constants</h2>
|
|
<pre> Constant Value Description
|
|
-------- ----- -----------
|
|
DROPDOWN_BTN_WIDTH 16 Width of the dropdown arrow button in pixels.
|
|
DROPDOWN_MAX_VISIBLE 8 Maximum number of items visible in a popup list before scrolling.
|
|
DROPDOWN_MIN_CHARS 8 Minimum text-area width of a dropdown/combobox, in characters.
|
|
DROPDOWN_BEVEL_W 2 Bevel width of the dropdown/combobox text area and drop button.
|
|
DROPDOWN_ARROW_ROWS 4 Rows in the drop-arrow triangle.
|
|
POPUP_BORDER 2 Popup list border width; items start this far inside the popup.
|
|
POPUP_SCROLLBAR_W SCROLLBAR_WIDTH Popup vertical scrollbar width (mirrors the core window-manager scrollbar width).
|
|
LIST_REORDER_LINE_H 2 Thickness of the drag-reorder insertion line.</pre>
|
|
<h2>ListItemsT (owned-string item list)</h2>
|
|
<p>Item list shared by ListBox, Dropdown and ComboBox. items points either at a caller-owned array (isOwned false) or at owned, an stb_ds array of strdup'd strings (isOwned true). maxLen caches the widest item in characters and is maintained incrementally. NULL entries in a caller's array read as empty strings.</p>
|
|
<pre><code>typedef struct {
|
|
const char **items;
|
|
char **owned;
|
|
int32_t count;
|
|
int32_t maxLen;
|
|
bool isOwned;
|
|
} ListItemsT;
|
|
|
|
bool listItemsAdd(ListItemsT *li, const char *text);
|
|
void listItemsAdopt(ListItemsT *li);
|
|
bool listItemsClear(ListItemsT *li);
|
|
void listItemsFree(ListItemsT *li);
|
|
const char *listItemsGet(const ListItemsT *li, int32_t idx);
|
|
bool listItemsRemove(ListItemsT *li, int32_t idx);
|
|
bool listItemsSetExternal(ListItemsT *li, const char **items, int32_t count);</code></pre>
|
|
<p>listItemsAdd and listItemsRemove first adopt (deep-copy) a caller's array, so they extend or shorten it without modifying it. listItemsSetExternal keeps the previously owned strings until the next adopt, clear or free, because the new array may point into them. The mutators return true when maxLen changed, i.e. when the widget's minimum width changed and it needs a relayout rather than just a repaint. listItemsGet returns "" for an index out of range.</p>
|
|
<p>widgetAdoptOwnedStrings(owned, src, count) builds the deep copy that adoption uses (complete before the old strings are freed, so src may alias them) and widgetFreeOwnedStrings(owned) frees every string of an owned array and empties it. ListView uses both for its row-major cell copy.</p>
|
|
<h2>ListSelT (selection model)</h2>
|
|
<p>Selection state shared by ListBox and ListView. cursor is the current (keyboard) item and anchor the start of Shift ranges; bits holds one byte per item in multi-select mode. Every index is a data index; functions that take a display index also take an optional display-to-data order map (NULL = identity) so a sorted view selects what the user sees as a contiguous range.</p>
|
|
<pre><code>typedef struct {
|
|
uint8_t *bits;
|
|
int32_t cursor;
|
|
int32_t anchor;
|
|
bool multi;
|
|
} ListSelT;</code></pre>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
listSelInit(s) / listSelFree(s) Initialize (no cursor, no anchor) / free the bits.
|
|
listSelReset(s, count) Rebuild the bits from scratch: all clear except the cursor item.
|
|
listSelInsert(s, newCount, idx) An item was inserted at idx: existing selections are kept, cursor/anchor shift.
|
|
listSelRemove(s, oldCount, idx) The item at idx was removed: bits close the gap, cursor/anchor shift; a removed cursor/anchor becomes -1.
|
|
listSelReorder(s, from, dest) Drag-reorder moved an item: its bit moves with it and it becomes the cursor.
|
|
listSelClick(s, count, order, disp, shift, ctrl) Mouse selection: plain, Ctrl (toggle) or Shift (range from the anchor).
|
|
listSelMove(s, count, order, disp, shift) Keyboard cursor move; Shift selects the range from the anchor.
|
|
listSelKey(s, count, key, mod) Multi-select Ctrl+A (ListSelKeyAllE) and Space (ListSelKeyToggleE); ListSelKeyNoneE otherwise.
|
|
listSelSetCursor(s, count, idx) Programmatic selection: idx becomes the only selected item.
|
|
listSelSetAll / listSelSetItem Set every bit / one bit; return false when there are no selection bits (not multi-select, or an empty list) or idx is out of range.
|
|
listSelIsSelected(s, count, idx) Bit state in multi-select mode, idx == cursor otherwise.
|
|
listDisplayRowOf(order, count, dataIdx) Display row of a data index (-1 if none).
|
|
listIndexAfterRemove(idx, removed) Where an index ends up after a removal (-1 if it was removed).
|
|
listIndexAfterMove(idx, from, dest) Where an index ends up after an item moved.</pre>
|
|
<h2>Drag-Reorder Helpers</h2>
|
|
<pre><code>void listReorderCancel(WidgetT *w, int32_t *pDragIdx, int32_t *pDropIdx);
|
|
int32_t listReorderDest(int32_t from, int32_t to, int32_t count);
|
|
void listReorderMove(void *base, size_t elemSize, int32_t from, int32_t dest);
|
|
void listReorderTrack(int32_t relY, int32_t rowH, int32_t visibleRows, int32_t count, int32_t *pScrollPos, int32_t *pDropIdx);
|
|
void listReorderPaint(DisplayT *d, const BlitOpsT *ops, int32_t x, int32_t top, int32_t w, int32_t h, int32_t rowH, int32_t scrollPos, int32_t dropIdx, uint32_t color);</code></pre>
|
|
<p>listReorderCancel abandons an item drag when the list changes under it (an item is removed or the items are replaced): it resets both indices and, when w is dragging an item, ends the drag so the rest of the mouse motion is ignored. listReorderDest maps a drag from item from to insertion point to (0..count) to the item's final index, or -1 when the drop is a no-op. listReorderMove moves one element of any size in place (a ListView row is colCount pointers). listReorderTrack updates the drop index from the pointer position and auto-scrolls one row when the pointer is on the first or last visible row. listReorderPaint draws the insertion line.</p>
|
|
<h2>ListPopupT (dropdown popup)</h2>
|
|
<p>Popup state shared by Dropdown and ComboBox: the item list plus the selected item, the hovered item, the popup scroll position and whether it is open.</p>
|
|
<pre><code>typedef struct {
|
|
ListItemsT items;
|
|
int32_t selectedIdx;
|
|
int32_t hoverIdx;
|
|
int32_t scrollPos;
|
|
bool open;
|
|
} ListPopupT;</code></pre>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
listPopupInit(p) No selection, no hover, closed.
|
|
listPopupAddItem / listPopupRemoveItem Item list edits; the selection and hover stay on the same item (-1 when it is removed).
|
|
listPopupClearItems / listPopupSetItems Replace the items; an out-of-range selection becomes -1.
|
|
listPopupOpen(w, p) Open with the selection hovered and scrolled into view (keyboard, mouse and accelerator all use it).
|
|
listPopupClose(w, p) WGT_METHOD_CLOSE_POPUP body.
|
|
listPopupCommit(p) Close and make the hovered item the selection; false when nothing valid is hovered.
|
|
listPopupNavKey(p, key) Up/Down move the hover item, a printable key jumps to the next matching item.
|
|
listPopupOnMouse(w, p, vx, vy) Click while open: ListPopupHitScrollE (scrollbar), ListPopupHitItemE (item selected, closed) or ListPopupHitNoneE (closed).
|
|
listPopupItemAt(p, font, popY, vy) Item under vy, or -1 on the border or past the last item.
|
|
listPopupPaint(w, p, ...) Overlay paint; uses the window content height, the same source as the hit test.
|
|
listPopupDrawFrame(d, ops, colors, w, bg, down) Sunken text area plus the drop button and arrow.
|
|
listPopupCalcMinSize(w, font, p) Minimum size from the widest item.
|
|
listPopupEnsureVisible(p) Scroll the hover item into view.
|
|
listPopupVisibleCount(itemCount) Rows the popup shows (1..DROPDOWN_MAX_VISIBLE).</pre>
|
|
<h2>widgetDrawDropdownArrow</h2>
|
|
<p>Draw the triangular dropdown arrow glyph centered at a given position.</p>
|
|
<pre><code>void widgetDrawDropdownArrow(DisplayT *d, const BlitOpsT *ops, int32_t centerX, int32_t centerY, uint32_t color);</code></pre>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
d Display context.
|
|
ops Blit operations vtable for the active pixel depth.
|
|
centerX Horizontal center of the arrow in backbuffer coordinates.
|
|
centerY Vertical center of the arrow in backbuffer coordinates.
|
|
color Pre-packed pixel color for the arrow.</pre>
|
|
<h2>widgetMaxItemLen</h2>
|
|
<p>Scan an array of strings and return the length (in characters) of the longest item.</p>
|
|
<pre><code>int32_t widgetMaxItemLen(const char **items, int32_t count);</code></pre>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
items Array of null-terminated string pointers (NULL entries count as empty).
|
|
count Number of items in the array.</pre>
|
|
<p>Returns the character length of the longest item, or 0 if count is zero.</p>
|
|
<h2>widgetNavigateIndex</h2>
|
|
<p>Compute a new selected index from a navigation key press. Handles Up, Down, Home, End, Page Up, and Page Down.</p>
|
|
<pre><code>int32_t widgetNavigateIndex(int32_t key, int32_t current, int32_t count, int32_t pageSize);</code></pre>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
key Keyboard scancode (Up, Down, Home, End, PgUp, PgDn).
|
|
current Currently selected index.
|
|
count Total number of items in the list.
|
|
pageSize Number of visible items (used for PgUp/PgDn step size).</pre>
|
|
<p>Returns the new index, clamped to [0, count-1]. Returns -1 for unrecognized keys so callers can detect whether the key was consumed.</p>
|
|
<h2>widgetDropdownPopupRect</h2>
|
|
<p>Calculate the screen rectangle for a dropdown popup list, positioning it below the owning widget and clamping to screen bounds.</p>
|
|
<pre><code>void widgetDropdownPopupRect(const WidgetT *w, const BitmapFontT *font, int32_t contentH, int32_t itemCount, int32_t *popX, int32_t *popY, int32_t *popW, int32_t *popH);</code></pre>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
w The widget that owns the popup (Dropdown, ComboBox, etc.).
|
|
font Bitmap font used for item text (determines row height).
|
|
contentH Height of the widget's content area in pixels.
|
|
itemCount Total number of items in the list.
|
|
popX Output: popup X position (screen coordinates).
|
|
popY Output: popup Y position (screen coordinates).
|
|
popW Output: popup width in pixels.
|
|
popH Output: popup height in pixels.</pre>
|
|
<p>The popup is sized to show up to DROPDOWN_MAX_VISIBLE items.</p>
|
|
<h2>widgetPaintPopupList</h2>
|
|
<p>Render a popup item list with highlight, scrolling, and beveled border. When itemCount exceeds DROPDOWN_MAX_VISIBLE, draws a vertical scrollbar on the right edge sized according to scrollPos.</p>
|
|
<pre><code>void widgetPaintPopupList(DisplayT *d, const BlitOpsT *ops, const BitmapFontT *font, const ColorSchemeT *colors, int32_t popX, int32_t popY, int32_t popW, int32_t popH, const char **items, int32_t itemCount, int32_t hoverIdx, int32_t scrollPos);</code></pre>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
d Display context.
|
|
ops Blit operations vtable.
|
|
font Bitmap font for rendering item text.
|
|
colors Color scheme for background, text, and highlight colors.
|
|
popX Popup X position (from widgetDropdownPopupRect).
|
|
popY Popup Y position.
|
|
popW Popup width.
|
|
popH Popup height.
|
|
items Array of null-terminated item strings.
|
|
itemCount Total number of items.
|
|
hoverIdx Index of the highlighted (hovered/selected) item, or -1 for none.
|
|
scrollPos Index of the first visible item (scroll offset).</pre>
|
|
<h2>widgetTypeAheadMatch</h2>
|
|
<p>True when text starts with ch (case-insensitive). NULL text never matches. Used by widgetTypeAheadSearch and by ListView's first-column type-ahead.</p>
|
|
<pre><code>bool widgetTypeAheadMatch(char ch, const char *text);</code></pre>
|
|
<h2>widgetTypeAheadSearch</h2>
|
|
<p>Case-insensitive type-ahead search. Scans items forward from currentIdx + 1, wrapping around, for the next item whose first character matches ch. Used by list and dropdown widgets to jump to an item based on a single keystroke.</p>
|
|
<pre><code>int32_t widgetTypeAheadSearch(char ch, const char **items, int32_t itemCount, int32_t currentIdx);</code></pre>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ch Character to match (case-insensitive).
|
|
items Array of null-terminated item strings.
|
|
itemCount Number of items in the array.
|
|
currentIdx Currently selected index (search starts at currentIdx + 1 and wraps).</pre>
|
|
<p>Returns the index of the next matching item, or -1 if no match is found or itemCount is not positive.</p>
|
|
<h2>widgetPopupScrollbarClick</h2>
|
|
<p>Hit-test a popup scrollbar click. Tests whether the (x, y) coordinate falls on the popup's vertical scrollbar track; if so, updates scrollPos for up/down arrow clicks or page-up/page-down clicks in the trough. No-op on the thumb itself (callers handle drag separately).</p>
|
|
<pre><code>bool widgetPopupScrollbarClick(int32_t x, int32_t y, int32_t popX, int32_t popY, int32_t popW, int32_t popH, int32_t itemCount, int32_t visibleItems, int32_t *scrollPos);</code></pre>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
x Click X in the popup's coordinate space.
|
|
y Click Y in the popup's coordinate space.
|
|
popX Popup X origin.
|
|
popY Popup Y origin.
|
|
popW Popup width.
|
|
popH Popup height.
|
|
itemCount Total number of items.
|
|
visibleItems Number of visible rows in the popup.
|
|
scrollPos [in/out] Current scroll offset; updated in place when the click hits the scrollbar.</pre>
|
|
<p>Returns true if the click landed on the scrollbar (even if scrollPos was not changed, for example clicking the thumb), false if the click was in the item list area. When itemCount is less than or equal to DROPDOWN_MAX_VISIBLE, always returns false (there is no scrollbar).</p>
|
|
</div>
|
|
<div class="topic" id="lib.music">
|
|
<h1>Music Library</h1>
|
|
<p>Music for DVX programs on whatever sound hardware the computer has: tracker modules (MOD, S3M, XM, IT), Standard MIDI Files (type 0 and 1), and live MIDI a program sends itself. Every music belongs to the program that loaded it and is stopped and freed when the program ends, however it ends; any number of programs may play music at the same time.</p>
|
|
<p>Header: music/music.h. Loaded as: bin/libs/kpunch/music/music.lib (needs audio.lib); the General MIDI instrument bank is bin/libs/kpunch/music/genmidi.op2.</p>
|
|
<h2>Playing a File</h2>
|
|
<pre><code>MusicT *tune = NULL;
|
|
int32_t rc = musicLoad("THEME.MID", &tune); // or musicLoadMem(data, size, &tune)
|
|
|
|
if (rc != MUSIC_OK) {
|
|
dvxLog("%s", musicErrorText(rc));
|
|
} else {
|
|
musicSetEndFn(tune, onTuneEnd, NULL);
|
|
musicPlay(tune, false); // true: loop until stopped
|
|
}</code></pre>
|
|
<p>musicLoad and musicLoadMem tell the kind by the contents: a MIDI file (or a RIFF RMID file around one) by its header, anything else is offered to the module player. Errors: MUSIC_ERR_OPEN (unreadable file), MUSIC_ERR_FORMAT (not music DVX plays, or a type 2 MIDI file), MUSIC_ERR_CORRUPT, MUSIC_ERR_NO_MEMORY, MUSIC_ERR_NO_DEVICE (no sound device could start), MUSIC_ERR_BANK (MIDI with no usable instrument bank and no MPU-401 port). Nothing plays until musicPlay(music, loop), which plays from the current position (the start after a load or a stop; a music that played to its end starts over). musicPause / musicResume, musicStop (back to the start), musicIsPlaying, musicSetVolume (0..MUSIC_VOLUME_MAX, per music; the master volume applies too - on the FM chip and the MPU-401 port as it was when the music started or its volume was last set), musicGetPosition / musicSetPosition (milliseconds), musicGetInfo (kind, where it plays, a description of the device, title, length, position, state) and musicFree complete the set. musicSetEndFn(music, fn, userData) asks for a call when a music that does not loop plays to its end; it comes in the shell's idle time as the program that owns the music, never from an interrupt.</p>
|
|
<p>Setting the position of a MIDI file sends the instruments and controllers set before that point (no notes). A module goes to the start of the pattern that holds the time.</p>
|
|
<h2>Where Music Plays</h2>
|
|
<p>Modules are rendered by libxmp-lite into a stream of the sound device (16-bit stereo at the device's rate, 400 ms of buffer, linear interpolation), so they play on any device that plays sampled sound.</p>
|
|
<p>MIDI goes to the best synthesizer there is, decided at the first musicPlay (or musicMidiOpen), with DVX.INI [audio] midi:</p>
|
|
<pre> midi MIDI plays on
|
|
---- -------------
|
|
auto the FM chip (OPL2 / OPL3) when there is one and no program has taken the whole chip; else the software synthesizer
|
|
mpu401 the MPU-401 port (mpuPort, default 330h) when it was found and no other music is using it; else as auto
|
|
fm as auto
|
|
software the software synthesizer</pre>
|
|
<p>On the FM chip and the MPU-401 port the song is played by the timer service (500 times a second), so it keeps time however busy the programs are. On the FM chip every note takes a voice from audio.lib's shared pool; when none is free the note plays on the software synthesizer instead, which then runs beside the chip (its stream is always open for that). The software synthesizer runs in the stream's fill function (the shell's idle time), 64 frames between event checks, 150 ms ahead of what is heard; a program that keeps the desktop busy every frame should call audioPump once a frame, as for any stream.</p>
|
|
<h2>General MIDI on FM</h2>
|
|
<p>Instruments come from an OP2 bank (the DMX "GENMIDI" format: 128 melodic instruments and 47 percussion instruments for notes 35 to 81, each one or two two-operator voices, with a note offset, a fixed note for percussion and a detune for the second voice). DVX ships Freedoom's GENMIDI bank (see Licences below); DVX.INI [audio] midiBank names another OP2 file instead (a path from the DVX folder), for example a bank made with an OPL editor, or another freely licensed bank such as DMXOPL. The bank is read once, at the first MIDI music that needs it.</p>
|
|
<p>What is played: note on / off, velocity, program change, channel volume (CC7), expression (CC11), pan (CC10: left, both or right on an OPL3 and the software synthesizer), sustain pedal (CC64), pitch bend with the bend range from RPN 0 (default 2 semitones), all notes off (CC123, CC120), reset controllers (CC121), and General MIDI System On (a reset). Channel 10 plays percussion. Loudness is velocity x volume x expression x the music's volume on a 40 log10 curve, added as attenuation to the voice's level. OP2 banks are tuned for DMX, which sounds note n where MIDI has note n + 12, so notes are played an octave up from the bank's offsets. Not played: modulation (CC1), aftertouch, the FM chip's own rhythm mode, four-operator voices.</p>
|
|
<h2>The Software Synthesizer</h2>
|
|
<p>When there is no FM chip (or it is busy), MIDI plays on DVX's own software FM synthesizer: an integer two-operator FM chip modelled on the OPL2 (the same log-sine and exponential tables, waveforms 0 to 3, feedback, the chip's envelope rates and key scaling, key scale level, tremolo and vibrato) with 24 voices, rendering at the device's rate into a stream. It is driven with the same bank and the same register values as the chip, so music sounds alike on both. The host tests compare it with emu8950, an accurate OPL2 emulator, for several patches: pitch, loudness over time and harmonics agree closely. Cost on a 486: roughly 1 to 2 percent of a 486DX2-66 per sounding voice at 22,050 Hz.</p>
|
|
<h2>The MPU-401 Port</h2>
|
|
<p>With DVX.INI [audio] midi = mpu401, MIDI goes out of the MPU-401 port (audio.lib's mpu401 driver) to an external synthesizer, one music at a time (a second MIDI music meanwhile plays on the FM chip or the software synthesizer). Bytes are queued and sent by the timer as fast as the port takes them, with a 40 ms pause after every system exclusive message (first-generation MT-32s lose data without it). The music's volume scales every channel volume (CC7) sent. DVX.INI [audio] mpuMode says what is on the port:</p>
|
|
<pre> mpuMode Treatment
|
|
------- ---------
|
|
gm A General MIDI synthesizer (Roland SC-55 and the like): the music as it is, after a General MIDI System On message.
|
|
mt32 A Roland MT-32 (or CM-32L, LAPC-I) playing General MIDI music: the melodic channels move to the MT-32's eight parts (MIDI channels 2 to 9, in the order they are first used; a ninth channel shares the last part), percussion stays on channel 10, programs go through a General MIDI to MT-32 timbre table (our own nearest-sound choice), pan is mirrored (the MT-32 pans the other way), the bend range is set to 2 semitones by system exclusive messages at the start (and RPN 0 changes it the same way, since the MT-32 has no RPNs), and what it does not understand (other RPNs and NRPNs, bank select, reverb and chorus depth, aftertouch, system exclusive messages that are not Roland's) is left out.
|
|
raw Anything: the music exactly as it is, nothing added (for music written for an MT-32, with its own set-up messages).</pre>
|
|
<p>Stopping, pausing or freeing a music sends sustain off and all notes off on every channel the synthesizer hears.</p>
|
|
<h2>Live MIDI</h2>
|
|
<pre><code>MusicT *keys = NULL;
|
|
|
|
musicMidiOpen(&keys); // plays at once
|
|
musicMidiProgram(keys, 0, 73); // flute on channel 0
|
|
musicMidiNoteOn(keys, 0, 60, 100); // middle C
|
|
musicMidiNoteOff(keys, 0, 60);</code></pre>
|
|
<p>musicMidiOpen opens a music with no file that plays at once, on the same synthesizer a MIDI file would use. musicMidiNoteOn, musicMidiNoteOff, musicMidiProgram, musicMidiControl and musicMidiPitchBend (-8192..8191) send channel messages (channels 0 to 15, data 0 to 127) to it, or to a MIDI file that is playing (for dynamic game scores); MUSIC_ERR_ARG for a module or a value out of range.</p>
|
|
<h2>Modules</h2>
|
|
<p>MOD (ProTracker and compatibles), S3M, XM and IT through libxmp-lite 4.7.3 (thirdparty/libxmp-lite, unmodified; formats only, no depackers or ProWizard). Stereo separation is 50 percent (softer than the Amiga's hard left and right). The whole module is held in memory. libxmp works out pitches with floating point, so modules play in the shell's idle time, never at interrupt time; on a 486SX (no maths coprocessor) that goes through floating point emulation and is slow. Rough cost on a 486DX2-66 at 22,050 Hz: 5 percent for a 4-channel MOD, 20 to 25 percent for a 16-channel XM.</p>
|
|
<h2>Music Macros</h2>
|
|
<pre><code>MusicMmlT *tune = NULL;
|
|
|
|
musicMmlOpen(&tune);
|
|
musicMmlPlay(tune, "MB T140 O3 L8 CDEC CDEC EFG4 EFG4");</code></pre>
|
|
<p>A macro queue plays tunes written in QBasic's music macro language (the commands are listed in music.h and in the BASIC language reference: notes A-G with sharps and flats, N, O, > and <, L, P, dots, T, MN / ML / MS, MF / MB). musicMmlPlay parses the whole string first; a mistake anywhere returns MUSIC_ERR_MACRO and queues nothing. The settings last from one string to the next. musicMmlTone queues a square-wave tone of milliHz thousandths of a hertz (0: silence) for us microseconds (QBasic's SOUND). musicMmlQueued counts the notes not yet played, musicMmlStop silences the queue, musicMmlBusy says whether a caller that follows QBasic should still wait (foreground mode: until everything has played; background: while more than MUSIC_MML_BACKGROUND_MAX notes wait) and moves the queue along. A queue belongs to the program that opened it and is freed when that program ends.</p>
|
|
<p>Timing is QBasic's: a whole note is four beats of 60 / T seconds, each dot adds half again, and a note sounds for 7/8 (MN), all (ML) or 3/4 (MS) of its length. Octave 3 starts at middle C; octave 3's A is 440 Hz. The notes play as square waves on an audio.lib note queue, each to the exact sample (on the PC speaker as tones). DVX's own I command (I1 to I128) plays the notes that follow on that General MIDI instrument: each run of such notes becomes a small MIDI file that plays like any MIDI music (on the synthesizer DVX.INI [audio] midi chooses); I0 returns to tones. Runs play one after the other; when the instruments cannot play (no instrument bank, no device) the run plays as tones.</p>
|
|
<h2>Several Programs at Once</h2>
|
|
<p>Every music plays through one audio context of the library's own; audio.lib mixes all streams, so modules and software MIDI of any number of programs play together. FM voices are shared with every program's tones and FM music; the MPU-401 port serves one music. When a program ends, its musics stop and are freed and their FM voices and the port are given back; the others go on.</p>
|
|
<h2>BASIC</h2>
|
|
<p>src/include/basic/music.bas (DECLARE LIBRARY "music"): MusicLoad(fileName) and MusicMidiOpen() return a music number (0 on failure: MusicError$ says why); MusicPlay(music, loopIt), MusicStop, MusicPause, MusicResume, MusicPlaying, MusicVolume(music, percent), MusicPosition, MusicLength, MusicSeek(music, ms), MusicFree; live MIDI MusicNoteOn(music, channel, note, velocity), MusicNoteOff, MusicProgram, MusicControl, MusicPitchBend with channels 1 to 16 (10 for drums). A number only works for the program that got it.</p>
|
|
<p>The BASIC runtime itself calls musicBasBeep, musicBasSound(hz, ticks), musicBasPlay(macro), musicBasPlayCount and musicBasBusy for the statements BEEP, SOUND, PLAY and the PLAY(n) function; each BASIC program has its own macro queue. SOUND's duration is in clock ticks (18.2 a second) and 0 stops the queue; BEEP is 800 Hz for a quarter second.</p>
|
|
<h2>Licences</h2>
|
|
<p>libxmp-lite (Extended Module Player Lite), (C) 1996-2026 Claudio Matsuoka and Hipolito Carraro Jr, MIT licence:</p>
|
|
<p>Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</p>
|
|
<p>The GENMIDI instrument bank is from Freedoom 0.13.0, Copyright (C) 2001-2024 Contributors to the Freedoom project. All rights reserved. BSD 3-clause licence:</p>
|
|
<p>Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the Freedoom project nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</p>
|
|
</div>
|
|
<div class="topic" id="lib.print">
|
|
<h1>Printing Library</h1>
|
|
<p>Printer ports and print jobs, text laid out on pages, pictures for dot-matrix and laser printers, the printer settings and the common Print and Page Setup dialogs. Notepad, Image Viewer, Icon Editor, the Control Panel's Printer page and BASIC's LPRINT all print through it; any program can.</p>
|
|
<p>Header: print/print.h</p>
|
|
<p>Loaded as: bin/libs/kpunch/print/print.lib</p>
|
|
<h2>Settings</h2>
|
|
<p>The printer is chosen once for everything, in DVX.INI [printer] (the Control Panel's Printer page and the Page Setup dialog edit it): where output goes (PrintDestE: LPT1, LPT2, LPT3 or a file), the printer model (PrintModelE: text only, Epson 9-pin, Epson 24-pin, HP LaserJet) and the page setup (PrintPageSetupT).</p>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
printSettingsLoad(&s) Read DVX.INI into a PrintSettingsT (defaults for anything missing; a page setup that cannot work is replaced by the defaults as a whole).
|
|
printSettingsSave(&s) Write it back; false when the file could not be saved.
|
|
printSettingsRead(h, &s) / Write(h, &s) The same on a preferences handle you already have open.
|
|
printSettingsDefaults(&s) LPT1, text only, PRINTER.PRN, 80 columns, margins 3 lines / 5 columns, tab 8, wrap, header "&f", footer "Page &p".
|
|
printPageSetupProblem(&ps) NULL when the page setup can be used, else the reason (a static buffer).
|
|
printPageLines(&ps, model) Lines per page used: the set value, or the model's usual length (66; 60 for a laser printer).
|
|
printDestChoices[] / printModelChoices[] The labels and INI spellings (PrefsChoiceT), indexed by PrintDestE / PrintModelE.</pre>
|
|
<h2>Jobs and Printer Problems</h2>
|
|
<p>A job sends bytes to a printer port or into a file. On DOS a port is driven through the BIOS printer service (INT 17h); a byte is sent only when a status check says the printer is ready, so the BIOS never waits by itself. While the printer is busy the job calls the idle hook; a printer busy for PRINT_BUSY_TIMEOUT_MS (10 seconds), out of paper, off line or reporting an error is a problem, and so is a file that cannot be written (disk full). A problem goes to the ask hook: Retry tries again, Cancel ends the job and every later write fails at once.</p>
|
|
<pre><code>PrintHooksT hooks = { myAsk, myIdle, NULL, myState };
|
|
char err[PRINT_MSG_MAX];
|
|
PrintJobT *job = printJobOpen(PrintDestLpt1E, NULL, &hooks, err, sizeof(err));
|
|
|
|
if (!job) {
|
|
// err: "This computer has no printer port LPT1."
|
|
} else {
|
|
printJobPuts(job, "Hello, printer\r\n\f");
|
|
printJobClose(job); // true when everything was sent
|
|
}</code></pre>
|
|
<pre> Hook Called
|
|
---- ------
|
|
ask(ctx, message) For a problem; return PrintAnswerRetryE or PrintAnswerCancelE. NULL cancels.
|
|
idle(ctx) While the printer is busy, and after every PRINT_YIELD_BYTES bytes (4096 into a file). Return false to cancel. NULL calls tsYield, so other programs keep running.
|
|
finished(ctx, closing) When a write that called idle returns, and (closing true) when the job is freed.</pre>
|
|
<p>printJobWrite returns once every byte was taken, or false when the job is cancelled. printJobBytes gives the count sent, printJobCancelled whether it was cancelled. printStatusProblem(status, afterWrite, where, out, size) turns BIOS status bits (PRINT_STATUS_*) into the message the user sees. printSetPortOps replaces the port services (present / status / write) with your own, as the tests do to play a printer that is busy or out of paper; NULL restores the real ones. Elsewhere than DOS the real ports always exist, are always ready and append to lpt1.prn .. lpt3.prn in the working directory.</p>
|
|
<h2>Text</h2>
|
|
<p>printTextJob(job, model, text, len, &pageSetup, &info) prints plain text on pages. Lines end with LF or CR LF; a form feed in the text starts a new page. Each printed line starts with marginLeft blanks and ends with CR LF, tabs are expanded to tabWidth, other control characters print as blanks, and a line longer than the text width is wrapped at the last blank before it (a word longer than a whole line is split) or, with wrap off, cut off. Every page ends with a form feed. For the Epson and LaserJet models the printer's reset command comes first (and, for the LaserJet, last).</p>
|
|
<p>A page is marginTop lines with the header in their middle line, the text, then marginBottom lines with the footer in their middle line. Header and footer codes: &f file name (PrintTextInfoT.title), &p page number, &n number of pages, &d date, &t time (PrintTextInfoT.date / time, or today and now), && an ampersand; &l, &c and &r put what follows at the left, centre or right (text before any of them is centred). printFormatHeader expands a header for one page.</p>
|
|
<p>PrintTextInfoT also chooses a page range (firstPage, lastPage; 0 = all) and the number of copies. printTextPageCount returns the number of pages text takes.</p>
|
|
<h2>Pictures and Printer Drivers</h2>
|
|
<p>printRasterJob(job, driver, rgb, w, h, pitch) prints a picture of R, G, B bytes on one page: scaled to the largest size that fits the printable area (8 by 10 inches) keeping its shape, and dithered to black and white with a 4x4 ordered pattern. The driver gets one band of dot rows at a time, so only one band is ever in memory. printDriver(model) returns the driver of a model (NULL for text only).</p>
|
|
<pre> Model Driver
|
|
----- ------
|
|
Epson 9-pin (ESC/P) 120 x 72 dots per inch (ESC * 1), 8-row bands, paper fed with ESC J; blank bands only feed paper, and each pass stops after its last black dot.
|
|
Epson 24-pin (ESC/P) 180 x 180 dots per inch (ESC * 39, 3 bytes per column), 24-row bands.
|
|
HP LaserJet (PCL) 150 dots per inch raster graphics, one uncompressed row transfer per dot row (every LaserJet understands it), trailing white bytes left out.</pre>
|
|
<p>A PrintDriverT holds the resolution, the printable size in dots, the band height, the text reset strings and usual page length, and begin / band / end functions that write the printer's commands; a new printer family is one more driver.</p>
|
|
<h2>Dialogs and Ready-Made Printing</h2>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
printDialog(ctx, title, pageCount, &choice) The common Print dialog: print to a port or a file, copies and, when pageCount > 0, all pages or a range. Starts from the settings; true with the PrintChoiceT filled when the user chose Print.
|
|
printPageSetupDialog(ctx) The Page Setup dialog; saves the page setup to DVX.INI.
|
|
printUiText(ctx, text, title, askFirst) Print text with the page setup on the printer in the settings (the Print dialog first when askFirst).
|
|
printUiImageFile(ctx, path, title, askFirst) Print a picture file (BMP, PNG, JPEG, GIF).
|
|
printUiWindow(ctx, win, askFirst) Print a window's contents as a picture.
|
|
printUiOpenPort(ctx, port) A job with the user interface hooks on LPTn (0-based) or PRINT_PORT_DEFAULT.</pre>
|
|
<p>Jobs opened by these ask about printer problems with a Retry / Cancel message box. While a write waits, a modal Printing window with a Cancel button appears (at once for a document, after half a second for other output) and the whole desktop keeps running under it. Problems are told to the user; the functions return true only when everything was printed. Pictures need a printer model that prints them; on a text-only printer the user is told to choose the model in the Control Panel.</p>
|
|
<h2>BASIC</h2>
|
|
<p>src/include/basic/print.bas declares PrintTextDialog, PrintText, PrintPictureDialog, PrintPicture, PrintPageSetup and PrintCanPrintPictures. The BASIC runtime gives the library the GUI context (printBasSetContext) when a program starts, and runs LPRINT, LPRINT USING and OPEN "LPT1:" / "PRN" through printUiOpenPort, printJobWrite and printJobClose. See the BASIC language reference (LPRINT, File I/O, Include Libraries).</p>
|
|
</div>
|
|
<div class="topic" id="lib.serial">
|
|
<h1>Serial Stack</h1>
|
|
<p>The DVX serial/networking stack provides reliable, optionally encrypted communication over RS-232 serial ports. It is composed of four layers, each building on the one below:</p>
|
|
<ul>
|
|
<li>rs232 -- ISR-driven UART driver with ring buffers and flow control</li>
|
|
<li>packet -- HDLC framing, CRC-16, Go-Back-N ARQ (reliable delivery)</li>
|
|
<li>security -- 1024-bit Diffie-Hellman key exchange, XTEA-CTR cipher, SHA-256/HMAC/HKDF, SHA-256 DRBG</li>
|
|
<li>secLink -- Convenience wrapper: authenticated handshake, channel multiplexing, per-packet encryption and MAC</li>
|
|
</ul>
|
|
<p>Loaded as: bin/libs/kpunch/serial/serial.lib</p>
|
|
<h2>Layered Architecture</h2>
|
|
<pre><code>+--------------------------------------------------+
|
|
| Application |
|
|
| |
|
|
| secLinkSend() -- send on channel, opt. encrypt |
|
|
| secLinkPoll() -- receive + dispatch |
|
|
+--------------------------------------------------+
|
|
| secLink (handshake, channels, cipher + MAC) |
|
|
+--------------------------------------------------+
|
|
| packet (HDLC framing, CRC-16, Go-Back-N ARQ) |
|
|
+--------------------------------------------------+
|
|
| security -- DH, XTEA-CTR, SHA-256/HMAC, DRBG |
|
|
| (used by secLink during handshake) |
|
|
+--------------------------------------------------+
|
|
| rs232 (ISR-driven UART, 2048-byte rings) |
|
|
+--------------------------------------------------+
|
|
| UART hardware (8250 / 16450 / 16550 / 16550A) |
|
|
+--------------------------------------------------+</code></pre>
|
|
<p>The security layer is invoked by secLink during the Diffie-Hellman handshake and for per-packet cipher operations. It has no runtime coupling to rs232 or packet, so it can be used standalone for cryptographic operations independent of the serial stack.</p>
|
|
<p><a href="#lib.serial.rs232">Layer 1: RS-232 UART Driver</a></p>
|
|
<p><a href="#lib.serial.packet">Layer 2: Packet Transport</a></p>
|
|
<p><a href="#lib.serial.security">Layer 3: Security (DH, XTEA, SHA-256)</a></p>
|
|
<p><a href="#lib.serial.seclink">Layer 4: Secure Link</a></p>
|
|
<p><a href="#lib.serial.example">End-to-End Example</a></p>
|
|
</div>
|
|
<div class="topic" id="lib.serial.rs232">
|
|
<h1>RS-232 UART Driver</h1>
|
|
<p>ISR-driven serial port driver supporting up to 4 simultaneous COM ports. A shared ISR drains UART FIFOs into per-port ring buffers (2048 bytes, power-of-2 for fast index wrapping). Flow control (XON/XOFF, RTS/CTS, DTR/DSR) operates within the ISR using watermark thresholds. All ISR data structures are DPMI-locked to prevent page faults. XON/XOFF bytes are queued for the transmit interrupt instead of being written over a byte still in the transmit register, and every cached register (IER, MCR) is changed with interrupts off so the ISR and the application never lose each other's updates.</p>
|
|
<p>Header: rs232/rs232.h</p>
|
|
<h2>Port Constants</h2>
|
|
<pre> Constant Value Description
|
|
-------- ----- -----------
|
|
RS232_COM1 0 First serial port.
|
|
RS232_COM2 1 Second serial port.
|
|
RS232_COM3 2 Third serial port.
|
|
RS232_COM4 3 Fourth serial port.
|
|
RS232_NUM_PORTS 4 Total number of supported COM ports.</pre>
|
|
<h2>Handshake Modes</h2>
|
|
<pre> Constant Value Description
|
|
-------- ----- -----------
|
|
RS232_HANDSHAKE_NONE 0 No flow control.
|
|
RS232_HANDSHAKE_XONXOFF 1 Software flow control (XON/XOFF characters).
|
|
RS232_HANDSHAKE_RTSCTS 2 Hardware flow control via RTS/CTS lines.
|
|
RS232_HANDSHAKE_DTRDSR 3 Hardware flow control via DTR/DSR lines.</pre>
|
|
<h2>UART Types</h2>
|
|
<p>Detected automatically by probing the scratch register and FIFO capability.</p>
|
|
<pre> Constant Value Description
|
|
-------- ----- -----------
|
|
RS232_UART_UNKNOWN 0 Detection failed.
|
|
RS232_UART_8250 1 Original IBM PC UART. No FIFO.
|
|
RS232_UART_16450 2 Has scratch register, no FIFO.
|
|
RS232_UART_16550 3 Has FIFO but buggy (rare).
|
|
RS232_UART_16550A 4 Working 16-byte FIFO. Most common in 486+ hardware.</pre>
|
|
<h2>Error Codes</h2>
|
|
<pre> Constant Value Description
|
|
-------- ----- -----------
|
|
RS232_SUCCESS 0 Operation succeeded.
|
|
RS232_ERR_UNKNOWN -1 Unknown error.
|
|
RS232_ERR_NOT_OPEN -2 Port is not open.
|
|
RS232_ERR_ALREADY_OPEN -3 Port is already open.
|
|
RS232_ERR_NO_UART -4 No UART detected at port address.
|
|
RS232_ERR_INVALID_PORT -5 Invalid port number.
|
|
RS232_ERR_INVALID_BASE -6 Invalid base I/O address.
|
|
RS232_ERR_INVALID_IRQ -7 Invalid IRQ number.
|
|
RS232_ERR_INVALID_BPS -8 Invalid baud rate.
|
|
RS232_ERR_INVALID_DATA -9 Invalid data bits value.
|
|
RS232_ERR_INVALID_PARITY -10 Invalid parity character.
|
|
RS232_ERR_INVALID_STOP -11 Invalid stop bits value.
|
|
RS232_ERR_INVALID_HANDSHAKE -12 Invalid handshake mode.
|
|
RS232_ERR_INVALID_FIFO -13 Invalid FIFO threshold.
|
|
RS232_ERR_NULL_PTR -14 NULL pointer argument.
|
|
RS232_ERR_IRQ_NOT_FOUND -15 Could not detect IRQ for port.
|
|
RS232_ERR_LOCK_MEM -16 DPMI memory lock failed.</pre>
|
|
<h2>rs232Open</h2>
|
|
<p>Open a COM port with the specified line parameters.</p>
|
|
<pre><code>int32_t rs232Open(int32_t com, int32_t bps, int32_t dataBits, char parity, int32_t stopBits, int32_t handshake);</code></pre>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
com Port index (RS232_COM1..RS232_COM4).
|
|
bps Baud rate (e.g. 9600, 19200, 38400, 57600, 115200).
|
|
dataBits Data bits per character (5, 6, 7, or 8).
|
|
parity Parity mode: 'N' (none), 'O' (odd), 'E' (even), 'M' (mark), 'S' (space).
|
|
stopBits Stop bits (1 or 2).
|
|
handshake Flow control mode (RS232_HANDSHAKE_*).</pre>
|
|
<p>Returns RS232_SUCCESS or a negative error code. The UART type is probed once here (RS232_ERR_NO_UART when nothing answers at the port address); only a 16550A gets its FIFO enabled and is fed 16 bytes per transmit interrupt, every other UART one byte.</p>
|
|
<h2>rs232Close</h2>
|
|
<p>Close a COM port and release its ISR resources.</p>
|
|
<pre><code>int32_t rs232Close(int32_t com);</code></pre>
|
|
<h2>rs232Read</h2>
|
|
<p>Non-blocking read from the receive ring buffer.</p>
|
|
<pre><code>int32_t rs232Read(int32_t com, char *data, int32_t len);</code></pre>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
com Port index.
|
|
data Buffer to receive data.
|
|
len Maximum bytes to read.</pre>
|
|
<p>Returns the number of bytes actually read (may be less than len).</p>
|
|
<h2>rs232Write</h2>
|
|
<p>Blocking polled write directly to UART transmit holding register. Bypasses the TX ring buffer. Polls LSR_TX_HOLD_EMPTY before writing each byte (the check and the write happen with interrupts off, so a flow-control byte from the ISR is never overwritten). Use for small, immediate writes where order matters (the packet layer uses this for frame transmission).</p>
|
|
<pre><code>int32_t rs232Write(int32_t com, const char *data, int32_t len);</code></pre>
|
|
<p>Returns the number of bytes actually written (may be less than len while flow control holds the line), or a negative error code (RS232_ERR_INVALID_PORT, RS232_ERR_NOT_OPEN, RS232_ERR_NULL_PTR).</p>
|
|
<h2>rs232WriteBuf</h2>
|
|
<p>Non-blocking write to the transmit ring buffer. The ISR drains buffered data to the UART asynchronously.</p>
|
|
<pre><code>int32_t rs232WriteBuf(int32_t com, const char *data, int32_t len);</code></pre>
|
|
<p>Returns the number of bytes actually queued (may be less than len if the buffer is full).</p>
|
|
<h2>rs232Set</h2>
|
|
<p>Change all line parameters on an open port. Internally calls rs232SetBps, rs232SetData, rs232SetParity, rs232SetStop, and rs232SetHandshake in sequence. Returns RS232_SUCCESS on success or the first negative error code encountered.</p>
|
|
<pre><code>int32_t rs232Set(int32_t com, int32_t bps, int32_t dataBits, char parity, int32_t stopBits, int32_t handshake);</code></pre>
|
|
<h2>Configuration Getters</h2>
|
|
<pre> Function Returns
|
|
-------- -------
|
|
rs232GetBase(com) I/O base address.
|
|
rs232GetBps(com) Current baud rate (int32_t).
|
|
rs232GetData(com) Data bits setting (5-8).
|
|
rs232GetParity(com) Parity character: 'n', 'o', 'e', 'm', or 's' (lowercase).
|
|
rs232GetStop(com) Stop bits setting (1 or 2).
|
|
rs232GetHandshake(com) Handshake mode (RS232_HANDSHAKE_*).
|
|
rs232GetIrq(com) IRQ number.
|
|
rs232GetUartType(com) UART type probed when the port was opened (RS232_UART_*).</pre>
|
|
<p>rs232GetParity returns a lowercase character on success. Callers comparing against uppercase constants must use either both cases or tolower().</p>
|
|
<h2>Status Getters</h2>
|
|
<pre> Function Returns
|
|
-------- -------
|
|
rs232GetRxBuffered(com) Bytes waiting in the receive ring buffer.
|
|
rs232GetTxBuffered(com) Bytes waiting in the transmit ring buffer.
|
|
rs232GetCts(com) CTS line state (0 or 1).
|
|
rs232GetDcd(com) DCD (carrier detect) line state (0 or 1), as last reported by the modem status interrupt.
|
|
rs232GetDsr(com) DSR line state (0 or 1).
|
|
rs232GetDtr(com) DTR line state (0 or 1).
|
|
rs232GetRts(com) RTS line state (0 or 1).
|
|
rs232GetLsr(com) Line status register value.
|
|
rs232GetMcr(com) Modem control register value.
|
|
rs232GetMsr(com) Modem status register value.</pre>
|
|
<h2>Configuration Setters</h2>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
rs232SetBase(com, base) Set I/O base address (before open).
|
|
rs232SetBps(com, bps) Change baud rate.
|
|
rs232SetData(com, dataBits) Change data bits.
|
|
rs232SetParity(com, parity) Change parity mode.
|
|
rs232SetStop(com, stopBits) Change stop bits.
|
|
rs232SetHandshake(com, handshake) Change flow control mode. Releases any throttle the old mode applied (raises RTS/DTR or sends XON) and starts the transmit side from the live CTS/DSR line.
|
|
rs232SetIrq(com, irq) Move an open port to another IRQ (for a wrong auto-detection). Interrupt enables and DTR/RTS are kept.
|
|
rs232SetMcr(com, mcr) Set modem control register directly.
|
|
rs232SetDtr(com, dtr) Set DTR line state.
|
|
rs232SetBreak(com, on) Hold the transmit line in the break (spacing) state (on = true) or release it. The caller times the break, typically 250-500 ms.
|
|
rs232SetRts(com, rts) Set RTS line state.
|
|
rs232SetFifoThreshold(com, thr) Set FIFO trigger level (1, 4, 8, or 14 bytes) or 0 to turn the FIFO off. Levels other than 0 return RS232_ERR_INVALID_FIFO unless the UART is a 16550A.</pre>
|
|
<h2>Buffer Management</h2>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
rs232ClearRxBuffer(com) Discard all data in the receive ring buffer.
|
|
rs232ClearTxBuffer(com) Discard all data in the transmit ring buffer.</pre>
|
|
</div>
|
|
<div class="topic" id="lib.serial.packet">
|
|
<h1>Packet Transport</h1>
|
|
<p>Reliable, ordered packet delivery over an RS-232 serial link. Uses HDLC-style byte-stuffed framing for frame delimiting, CRC-16-CCITT for error detection, and Go-Back-N ARQ with a configurable sliding window for retransmission.</p>
|
|
<p>Header: packet/packet.h</p>
|
|
<h2>Constants</h2>
|
|
<pre> Constant Value Description
|
|
-------- ----- -----------
|
|
PKT_MAX_PAYLOAD 255 Maximum bytes per packet payload.
|
|
PKT_DEFAULT_WINDOW 4 Default sliding window size (unacknowledged frames in flight).
|
|
PKT_MAX_WINDOW 8 Maximum sliding window size.</pre>
|
|
<h2>Error Codes</h2>
|
|
<pre> Constant Value Description
|
|
-------- ----- -----------
|
|
PKT_SUCCESS 0 Operation succeeded.
|
|
PKT_ERR_INVALID_PARAM -1 Invalid parameter (NULL handle, bad data pointer, length out of range).
|
|
PKT_ERR_TX_FULL -2 Transmit window is full (non-blocking send only).
|
|
PKT_ERR_DISCONNECTED -3 Serial port returned a negative read (disconnect/error), or the connection was closed from its own callback.
|
|
PKT_ERR_TIMEOUT -4 A blocking send got no acknowledgement for PKT_SEND_TIMEOUT_MS (15000 ms).</pre>
|
|
<h2>PktRecvCallbackT</h2>
|
|
<p>Callback type for received packets.</p>
|
|
<pre><code>typedef void (*PktRecvCallbackT)(void *ctx,
|
|
const uint8_t *data, int len);</code></pre>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx User context pointer (passed to pktOpen).
|
|
data Payload bytes (valid only during the callback).
|
|
len Payload length.</pre>
|
|
<h2>pktOpen</h2>
|
|
<p>Open a packetized connection over an already-open COM port. Only one packet connection may exist on a given COM port at a time; a second pktOpen call on the same port returns NULL to avoid silent framing corruption.</p>
|
|
<pre><code>PktConnT *pktOpen(int com, int windowSize,
|
|
PktRecvCallbackT callback, void *callbackCtx);</code></pre>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
com COM port index (RS232_COM1..RS232_COM4). Must already be open via rs232Open.
|
|
windowSize Sliding window size (1..PKT_MAX_WINDOW). Pass 0 for PKT_DEFAULT_WINDOW. Values greater than PKT_MAX_WINDOW are clamped.
|
|
callback Called when a complete, CRC-verified, in-order packet is received. The data pointer is valid only during the callback.
|
|
callbackCtx User pointer passed to the callback.</pre>
|
|
<p>Returns an opaque PktConnT handle, or NULL on failure (invalid port, duplicate open, or allocation failure).</p>
|
|
<h2>pktOpenStream</h2>
|
|
<p>Open a packetized connection over any byte stream instead of a COM port -- a TCP socket, for example. The stream is described by two non-blocking calls and a context pointer, copied into the connection. Any number of stream connections may be open.</p>
|
|
<pre><code>typedef struct {
|
|
int32_t (*read)(void *ctx, uint8_t *buf, int32_t len);
|
|
int32_t (*write)(void *ctx, const uint8_t *data, int32_t len);
|
|
void *ctx;
|
|
} PktStreamT;
|
|
|
|
PktConnT *pktOpenStream(const PktStreamT *stream, int windowSize, PktRecvCallbackT callback, void *callbackCtx);</code></pre>
|
|
<pre> Member Description
|
|
------ -----------
|
|
read Up to len bytes into buf: the count, 0 when nothing has arrived, negative when the stream is gone (pktPoll then reports PKT_ERR_DISCONNECTED).
|
|
write Bytes taken from data (0 = none right now; a frame is retried until it is all out or nothing was taken for a while), negative when the stream is gone.
|
|
ctx Passed to both.</pre>
|
|
<p>Returns the handle, or NULL for a NULL stream or call, or on allocation failure. pktClose never closes the stream.</p>
|
|
<h2>pktClose</h2>
|
|
<p>Close a packetized connection and release its registry slot. Does not close the underlying COM port. The caller is responsible for calling rs232Close separately when no longer needed. May be called from inside the receive callback: the connection is then freed when the outermost pktPoll returns (which reports PKT_ERR_DISCONNECTED).</p>
|
|
<pre><code>void pktClose(PktConnT *conn);</code></pre>
|
|
<h2>pktSend</h2>
|
|
<p>Send a data packet. The payload is copied into an internal retransmit slot before transmission, so the caller may reuse the supplied buffer immediately.</p>
|
|
<pre><code>int pktSend(PktConnT *conn, const uint8_t *data,
|
|
int len, bool block);</code></pre>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
conn Connection handle from pktOpen.
|
|
data Payload bytes (1..PKT_MAX_PAYLOAD).
|
|
len Payload length.
|
|
block If true, wait for transmit window space via pktWaitSpace. If false, return PKT_ERR_TX_FULL immediately when the window is full.</pre>
|
|
<p>Returns PKT_SUCCESS on success, PKT_ERR_INVALID_PARAM for NULL/out-of-range input, PKT_ERR_TX_FULL for a non-blocking call with a full window, PKT_ERR_DISCONNECTED if the serial port drops during a blocking wait, or PKT_ERR_TIMEOUT if the peer stops acknowledging. A frame the port accepts only partially (flow control) is completed before the call returns.</p>
|
|
<h2>pktPoll</h2>
|
|
<p>Poll for incoming data, process received frames, and handle retransmits. Must be called frequently in the main loop. The callback is invoked synchronously for each complete, CRC-verified, in-order data frame and may call pktSend or pktPoll again: received bytes are consumed from a per-connection buffer, so a nested call continues the same byte stream in order. After a gap in the sequence only one NAK is sent until the missing frame arrives (go-back-N resends the whole window on one NAK).</p>
|
|
<pre><code>int pktPoll(PktConnT *conn);</code></pre>
|
|
<p>Returns the number of valid data packets delivered to the callback this call, or PKT_ERR_INVALID_PARAM if conn is NULL, or PKT_ERR_DISCONNECTED if the serial layer returned a negative read.</p>
|
|
<h2>pktReset</h2>
|
|
<p>Reset the local connection state (sequence numbers, transmit slots, RX state machine) and send a RST frame to the remote side. The remote's RST handler clears its own state (discarding its unacknowledged frames), runs its reset callback, and answers with a RST reply. A RST reply that answers no reset of ours is ignored. Useful for recovering from a desynchronized connection.</p>
|
|
<pre><code>int pktReset(PktConnT *conn);</code></pre>
|
|
<p>Returns PKT_SUCCESS on success or PKT_ERR_INVALID_PARAM if conn is NULL.</p>
|
|
<h2>pktCanSend</h2>
|
|
<p>Check whether there is room in the transmit window for another packet. Returns false when conn is NULL or the transmit window is full.</p>
|
|
<pre><code>bool pktCanSend(PktConnT *conn);</code></pre>
|
|
<h2>pktGetPending</h2>
|
|
<p>Get the number of unacknowledged packets currently in the transmit window. Ranges from 0 (all sent packets acknowledged) to windowSize (window full).</p>
|
|
<pre><code>int pktGetPending(PktConnT *conn);</code></pre>
|
|
<p>Returns the pending count on success, or PKT_ERR_INVALID_PARAM if conn is NULL.</p>
|
|
<h2>pktSetResetCallback</h2>
|
|
<p>Register a callback run when the remote side resets the connection (its unacknowledged frames are gone at that point, so an upper layer with per-frame state must resynchronize). The callback receives the ctx passed to pktOpen. NULL removes it.</p>
|
|
<pre><code>typedef void (*PktResetCallbackT)(void *ctx);
|
|
void pktSetResetCallback(PktConnT *conn, PktResetCallbackT callback);</code></pre>
|
|
<h2>pktWaitSpace</h2>
|
|
<p>Poll until the transmit window has room. The wait restarts whenever an acknowledgement frees a slot, so only a peer that stops acknowledging times out.</p>
|
|
<pre><code>int pktWaitSpace(PktConnT *conn);</code></pre>
|
|
<p>Returns PKT_SUCCESS, PKT_ERR_INVALID_PARAM, PKT_ERR_DISCONNECTED, or PKT_ERR_TIMEOUT after PKT_SEND_TIMEOUT_MS without progress.</p>
|
|
</div>
|
|
<div class="topic" id="lib.serial.security">
|
|
<h1>Security (DH, XTEA, SHA-256)</h1>
|
|
<p>Cryptographic primitives for the serial stack: 1024-bit Diffie-Hellman key exchange (RFC 2409 Group 2), XTEA block cipher in CTR mode, SHA-256, HMAC-SHA256 and HKDF-SHA256, and a SHA-256 based random number generator.</p>
|
|
<p>XTEA requires no lookup tables and compiles to approximately 20 instructions per round, making it suitable for 486-class hardware where AES S-box tables would thrash the 8KB cache. SHA-256 needs only 32-bit arithmetic and a 256-byte constant table.</p>
|
|
<p>Headers: security/security.h, security/secHash.h</p>
|
|
<h2>Constants</h2>
|
|
<pre> Constant Value Description
|
|
-------- ----- -----------
|
|
SEC_DH_KEY_SIZE 128 Size of a DH public key or shared secret in bytes (1024 bits).
|
|
SEC_XTEA_KEY_SIZE 16 Size of an XTEA key in bytes (128 bits).
|
|
SEC_RNG_SEED_SIZE 32 Bytes stored in a seed file.
|
|
SEC_SHA256_SIZE 32 SHA-256 digest size.
|
|
SEC_SHA256_BLOCK_SIZE 64 SHA-256 block size.
|
|
SEC_HKDF_MAX_OUT 8160 Largest HKDF output (255 digests).</pre>
|
|
<h2>Error Codes</h2>
|
|
<pre> Constant Value Description
|
|
-------- ----- -----------
|
|
SEC_SUCCESS 0 Operation succeeded.
|
|
SEC_ERR_PARAM -1 Invalid parameter.
|
|
SEC_ERR_NOT_READY -2 DH context not ready (keys not generated or secret not computed).
|
|
SEC_ERR_IO -3 Seed file could not be read or written.</pre>
|
|
<h2>RNG Functions</h2>
|
|
<p>A SHA-256 hash DRBG over a 32-byte entropy pool. secRngAddEntropy hashes new bytes into the pool and never discards what is already there, so it is safe to call at any time, before or after the first output. Each output block is a hash of the pool and a counter; after every request the pool is re-hashed so earlier output cannot be recomputed from a later state.</p>
|
|
<p>The first secRngBytes call gathers hardware entropy automatically. On DOS that is 256 PIT samples, each taken after a busy wait sized by the previous sample, plus the full time stamp counter on CPUs that have one (Pentium and later) and the BIOS tick count. On a CPU without a time stamp counter the samples depend mostly on the PIT phase and time of day, which an observer can narrow down, so applications should feed user-input timings to secRngAddEntropy and keep a seed file: call secRngLoadSeed at startup and secRngSaveSeed at shutdown. Host builds read /dev/urandom.</p>
|
|
<p>Under the DVX shell this is automatic when the serial library is installed: the shell loads the seed file CONFIG\SECLINK.RND at startup, feeds the timing of every key press and mouse event to secRngAddEntropy, and saves a fresh seed at shutdown. Applications only need their own calls when they run without the shell.</p>
|
|
<pre><code>void secRngAddEntropy(const uint8_t *data, int32_t len);
|
|
void secRngBytes(uint8_t *buf, int32_t len);
|
|
int32_t secRngGatherEntropy(uint8_t *buf, int32_t len);
|
|
int secRngLoadSeed(const char *path);
|
|
int secRngSaveSeed(const char *path);
|
|
void secSecureZero(void *ptr, int len);</code></pre>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
secRngAddEntropy Hash bytes into the pool (key timings, mouse positions, anything unpredictable).
|
|
secRngBytes Generate pseudorandom bytes.
|
|
secRngGatherEntropy Fill buf with condensed hardware entropy (one fresh gather per 32 bytes). Returns the byte count (0 if a host build cannot read its random device).
|
|
secRngLoadSeed Mix a seed file into the pool, then immediately rewrite it with fresh output so a seed is never used twice. SEC_SUCCESS or SEC_ERR_IO.
|
|
secRngSaveSeed Write SEC_RNG_SEED_SIZE bytes of fresh output for the next run. SEC_SUCCESS or SEC_ERR_IO.
|
|
secSecureZero Zero memory in a way the compiler cannot optimize away (for key material).</pre>
|
|
<h2>Hash Functions</h2>
|
|
<pre><code>void secSha256(const uint8_t *data, int32_t len, uint8_t digest[32]);
|
|
void secSha256Init(SecSha256T *ctx);
|
|
void secSha256Update(SecSha256T *ctx, const uint8_t *data, int32_t len);
|
|
void secSha256Final(SecSha256T *ctx, uint8_t digest[32]);
|
|
void secHmacSha256(const uint8_t *key, int32_t keyLen, const uint8_t *data, int32_t len, uint8_t mac[32]);
|
|
void secHmacInit(SecHmacT *h, const uint8_t *key, int32_t keyLen);
|
|
void secHmacUpdate(SecHmacT *h, const uint8_t *data, int32_t len);
|
|
void secHmacFinal(SecHmacT *h, uint8_t mac[32]);
|
|
int secHkdf(const uint8_t *salt, int32_t saltLen, const uint8_t *ikm, int32_t ikmLen, const uint8_t *info, int32_t infoLen, uint8_t *okm, int32_t okmLen);
|
|
bool secConstTimeEqual(const void *a, const void *b, int32_t len);</code></pre>
|
|
<p>SHA-256 follows FIPS 180-4, HMAC RFC 2104 and HKDF RFC 5869 (extract and expand; a NULL or empty salt means 32 zero bytes). secHkdf returns SEC_ERR_PARAM for okmLen outside 1..SEC_HKDF_MAX_OUT. secConstTimeEqual compares without revealing where the inputs differ; use it for every MAC check.</p>
|
|
<h2>Diffie-Hellman Key Exchange</h2>
|
|
<p>1024-bit DH with 256-bit private exponents using the RFC 2409 Group 2 safe prime. The modular exponentiation is a Montgomery ladder with branch-free operand swaps, so its timing does not depend on the private key.</p>
|
|
<pre><code>SecDhT *secDhCreate(void);
|
|
int secDhGenerateKeys(SecDhT *dh);
|
|
int secDhGetPublicKey(SecDhT *dh, uint8_t *buf, int *len);
|
|
int secDhComputeSecret(SecDhT *dh, const uint8_t *remotePub, int len);
|
|
int secDhGetSecret(SecDhT *dh, uint8_t *buf, int *len);
|
|
void secDhDestroy(SecDhT *dh);</code></pre>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
secDhCreate Allocate a new DH context.
|
|
secDhGenerateKeys Generate a private exponent and public key from the DRBG.
|
|
secDhGetPublicKey Copy the local public key into buf. *len receives the key size.
|
|
secDhComputeSecret Compute the shared secret from the remote public key. Keys outside [2, p-2] are rejected with SEC_ERR_PARAM (0, 1 and p-1 would force a trivially known secret).
|
|
secDhGetSecret Copy the shared secret (SEC_DH_KEY_SIZE big-endian bytes) into buf. It is input for a key derivation function such as secHkdf, never a key by itself.
|
|
secDhDestroy Securely zero and free the DH context.</pre>
|
|
<p>Typical usage order: secDhCreate, secDhGenerateKeys, exchange public keys, secDhComputeSecret, secDhGetSecret, secHkdf, secDhDestroy.</p>
|
|
<h2>XTEA-CTR Cipher</h2>
|
|
<p>XTEA in counter mode. Encrypt and decrypt are the same operation (XOR with keystream). The counter must never repeat with the same key.</p>
|
|
<pre><code>SecCipherT *secCipherCreate(const uint8_t *key);
|
|
void secCipherCrypt(SecCipherT *c, uint8_t *data, int len);
|
|
void secCipherSetCounter(SecCipherT *c, uint64_t counter);
|
|
void secCipherDestroy(SecCipherT *c);</code></pre>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
secCipherCreate Allocate a cipher context with a 128-bit key. Returns NULL on allocation failure or NULL key. Initial counter is zero.
|
|
secCipherCrypt Encrypt or decrypt data in place. The 64-bit counter increments by one every 8 bytes (one XTEA block).
|
|
secCipherSetCounter Position the 64-bit block counter (secLink derives it from the frame sequence number).
|
|
secCipherDestroy Securely zero the cipher context (key and counter state) and free its memory.</pre>
|
|
<p>secCipherCreate returns NULL if the key pointer is NULL or allocation fails. secCipherCrypt is a no-op when any of c/data is NULL or len <= 0.</p>
|
|
</div>
|
|
<div class="topic" id="lib.serial.seclink">
|
|
<h1>Secure Link</h1>
|
|
<p>Top-level API composing all three lower layers (rs232, packet, security) into a single interface. Provides an authenticated key exchange, channel multiplexing (0-127) and per-packet encryption with integrity protection over a reliable serial link.</p>
|
|
<p>Header: seclink/secLink.h</p>
|
|
<h2>Protocol (version 2)</h2>
|
|
<p>Every packet starts with a type byte:</p>
|
|
<pre> Type Layout Purpose
|
|
---- ------ -------
|
|
HELLO type, version, flags, DH public key (128 bytes) Starts a key exchange. Flag bit 0: passphrase in use.
|
|
CONFIRM type, HMAC-SHA256 (32 bytes) Proves both sides derived the same keys.
|
|
PLAIN type, channel, payload Clear application data.
|
|
CIPHER type, channel, ciphertext, MAC (8 bytes) Encrypted, authenticated application data.</pre>
|
|
<p>Handshake packets are never delivered to the application, and application data is never taken as key material. A HELLO is kept whenever it arrives, so the side that starts first is never missed.</p>
|
|
<p>Key derivation: HKDF-SHA256 with salt = SHA-256(passphrase) (32 zero bytes without one), input = the DH shared secret, and info = "DVX secLink v2" followed by the lower then the higher public key. The output supplies an XTEA key and an HMAC key for each direction plus a confirmation key; the side with the lower public key sends with the "low" keys. Directions never share a key.</p>
|
|
<p>Confirmation: each side sends HMAC(confirmation key, SHA-256(lower HELLO + higher HELLO) + role byte). A different passphrase, a passphrase on only one side, or a modified HELLO makes the check fail on both sides with SECLINK_ERR_AUTH. A side that has a passphrase never accepts a peer without one.</p>
|
|
<p>Encrypted frames: encrypt-then-MAC. Each direction numbers its encrypted frames with an implicit 64-bit sequence number that is never sent. The MAC is the first 8 bytes of HMAC-SHA256 over type, channel, big-endian sequence number and ciphertext, and the CTR counter for a frame starts at sequence * 32, so every frame has its own keystream range. Tampered, replayed or injected frames fail the MAC, are dropped and counted (secLinkGetDropped); later frames still verify.</p>
|
|
<p>Without a passphrase the link is encrypted and integrity-protected but not authenticated: an active attacker in the middle could run two separate exchanges. Use the same passphrase on both sides to rule that out.</p>
|
|
<p>Clear frames are accepted only until the first session is established. From then on, until secLinkClose, a clear frame is delivered only on a channel marked plaintext with secLinkSetChannelPlain -- even after the session was dropped by a packet-layer reset or a failed re-key, which someone on the line can cause; so neither can turn a protected channel into a clear one. A re-key HELLO on a running session whose protocol version or passphrase flag cannot match is dropped (and counted) without ending the session.</p>
|
|
<h2>Constants</h2>
|
|
<pre> Constant Value Description
|
|
-------- ----- -----------
|
|
SECLINK_PROTOCOL_VERSION 2 Version carried in HELLO; a mismatch fails the handshake.
|
|
SECLINK_FRAME_HDR_SIZE 2 Type byte plus channel byte.
|
|
SECLINK_MAC_SIZE 8 Truncated MAC on encrypted frames.
|
|
SECLINK_MAX_PAYLOAD 245 Maximum payload per send (PKT_MAX_PAYLOAD minus header and MAC), clear or encrypted.
|
|
SECLINK_MAX_CHANNEL 127 Highest valid channel number.
|
|
SECLINK_NUM_CHANNELS 128 Array size needed to hold one slot per channel (SECLINK_MAX_CHANNEL + 1).
|
|
SECLINK_CHAN_BUF_SIZE 4096 Recommended per-channel receive buffer size for callers that buffer inbound data between the receive callback and an application read.</pre>
|
|
<h2>Error Codes</h2>
|
|
<pre> Constant Value Description
|
|
-------- ----- -----------
|
|
SECLINK_SUCCESS 0 Operation succeeded.
|
|
SECLINK_ERR_PARAM -1 Invalid parameter (NULL pointer, bad channel, bad length).
|
|
SECLINK_ERR_SERIAL -2 Serial port closed or disconnected (secLinkPoll).
|
|
SECLINK_ERR_ALLOC -3 Memory allocation failed.
|
|
SECLINK_ERR_HANDSHAKE -4 Handshake failed: invalid public key, protocol version mismatch, reflected HELLO, dead line, or the peer reset mid-exchange.
|
|
SECLINK_ERR_NOT_READY -5 No session keys yet (encrypted send attempted).
|
|
SECLINK_ERR_SEND -6 Packet send failed, the peer stopped acknowledging, or a non-blocking send found the window full.
|
|
SECLINK_ERR_TIMEOUT -7 The peer did not complete the handshake within 30 seconds (the exchange stays pending).
|
|
SECLINK_ERR_AUTH -8 Passphrase mismatch, passphrase on only one side, or tampering.
|
|
SECLINK_PENDING 1 secLinkHandshakeStep only: the exchange is still running.</pre>
|
|
<h2>SecLinkRecvT</h2>
|
|
<p>Receive callback type. Delivers plaintext with the channel number. encrypted is true when the frame arrived encrypted and its MAC verified, false for a clear frame.</p>
|
|
<pre><code>typedef void (*SecLinkRecvT)(void *ctx, const uint8_t *data, int len, uint8_t channel, bool encrypted);</code></pre>
|
|
<h2>secLinkOpen</h2>
|
|
<p>Open a secure serial link. Opens the COM port via rs232Open and creates a packet layer (default window size) with an internal receive dispatcher. The handshake must be performed separately via secLinkHandshake before encrypted data can be sent.</p>
|
|
<pre><code>SecLinkT *secLinkOpen(int com, int32_t bps, int dataBits, char parity, int stopBits, int handshake, SecLinkRecvT callback, void *ctx);</code></pre>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
com COM port index (RS232_COM1..RS232_COM4).
|
|
bps Baud rate.
|
|
dataBits Data bits per character (5-8).
|
|
parity Parity mode ('N', 'O', 'E', 'M', 'S' or lowercase).
|
|
stopBits Stop bits (1 or 2).
|
|
handshake Flow control mode (RS232_HANDSHAKE_*).
|
|
callback Called when a packet is received, after verification and decryption. The data pointer is valid only during the callback.
|
|
ctx User pointer passed to the callback.</pre>
|
|
<p>Returns an opaque SecLinkT handle, or NULL on failure (allocation failure, rs232Open error, or pktOpen error). On failure all partially initialized resources are cleaned up.</p>
|
|
<h2>secLinkOpenStream</h2>
|
|
<p>Open a secure link over a byte stream (see pktOpenStream) instead of a COM port, e.g. a TCP connection to the host proxy's listening port. Everything else -- handshake, passphrase, channels, sending and polling -- is the same as for a COM port link. secLinkClose does not close the stream; the caller closes it afterwards.</p>
|
|
<pre><code>SecLinkT *secLinkOpenStream(const PktStreamT *stream, SecLinkRecvT callback, void *ctx);</code></pre>
|
|
<h2>secLinkClose</h2>
|
|
<p>Close the link and free all associated resources: securely destroy the ciphers and key material, destroy any remaining DH context, close the packet layer and the COM port, and free the link. Safe to call with a NULL link (no-op). Safe to call from inside the receive callback: the close then happens when the outermost secLink call returns, and no further packets are delivered.</p>
|
|
<pre><code>void secLinkClose(SecLinkT *link);</code></pre>
|
|
<h2>secLinkSetPassphrase</h2>
|
|
<p>Set the shared passphrase for the next handshake. NULL or "" clears it. Only SHA-256 of the text is kept. Set it before calling secLinkHandshake; both sides must use the same passphrase, or both none. An exchange already in progress (for example one left pending by a timed-out secLinkHandshake) was started with the old setting, so it is abandoned: the next secLinkHandshake starts a fresh one.</p>
|
|
<pre><code>int secLinkSetPassphrase(SecLinkT *link, const char *pass);</code></pre>
|
|
<p>Returns SECLINK_SUCCESS or SECLINK_ERR_PARAM for a NULL link.</p>
|
|
<h2>secLinkHandshake</h2>
|
|
<p>Run the authenticated DH key exchange. Blocks, polling the packet layer, until both CONFIRM messages have been checked, or until 30 seconds pass.</p>
|
|
<pre><code>int secLinkHandshake(SecLinkT *link);</code></pre>
|
|
<p>Behavior:</p>
|
|
<ul>
|
|
<li>A fresh DH key pair is generated from the DRBG and our HELLO is sent. If the peer's HELLO already arrived (it started first), the keys are derived at once.</li>
|
|
<li>The DH private key is destroyed as soon as the session keys are derived (forward secrecy).</li>
|
|
<li>After SECLINK_ERR_TIMEOUT the exchange stays pending: it still completes during secLinkPoll if the peer shows up, and calling secLinkHandshake again resumes the wait without sending a new key.</li>
|
|
<li>On a ready link it negotiates fresh session keys. A peer that is already ready (or has an exchange pending) answers automatically from secLinkPoll.</li>
|
|
<li>Encrypted traffic under the previous keys that was sent before the new HELLO is still accepted; encrypted sends are refused (SECLINK_ERR_NOT_READY) until the new exchange completes.</li>
|
|
</ul>
|
|
<p>Returns SECLINK_SUCCESS, SECLINK_ERR_PARAM, SECLINK_ERR_ALLOC, SECLINK_ERR_HANDSHAKE, SECLINK_ERR_TIMEOUT, or SECLINK_ERR_AUTH. When the peer confirms (or rejects) and hangs up in the same poll -- a proxy exits as soon as it sees a passphrase mismatch -- the exchange's outcome is reported, not the disconnect.</p>
|
|
<h2>secLinkHandshakeStep</h2>
|
|
<p>Non-blocking handshake for callers that run their own event loop.</p>
|
|
<pre><code>int secLinkHandshakeStep(SecLinkT *link);</code></pre>
|
|
<p>Starts an exchange on an idle link (never handshaken, or dropped by a peer reset), polls once and returns SECLINK_SUCCESS once the link is ready, SECLINK_PENDING (1) while the exchange is still running, or the error that ended it (the same codes as secLinkHandshake, without SECLINK_ERR_TIMEOUT). A failed exchange keeps returning its error; secLinkHandshake starts a new one.</p>
|
|
<h2>secLinkSend</h2>
|
|
<p>Send data on a channel, optionally encrypted and MACed.</p>
|
|
<pre><code>int secLinkSend(SecLinkT *link, const uint8_t *data, int len, uint8_t channel, bool encrypt, bool block);</code></pre>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
link Connection handle from secLinkOpen.
|
|
data Plaintext payload.
|
|
len Payload length. Must be in 1..SECLINK_MAX_PAYLOAD.
|
|
channel Logical channel number (0..SECLINK_MAX_CHANNEL).
|
|
encrypt If true, encrypt and MAC before sending (requires a completed handshake).
|
|
block If true, wait (pumping the receive callback) until the transmit window has space. If false, return SECLINK_ERR_SEND immediately when the window is full.</pre>
|
|
<p>Returns SECLINK_SUCCESS on success or a negative error code (SECLINK_ERR_PARAM, SECLINK_ERR_NOT_READY, SECLINK_ERR_SEND).</p>
|
|
<p>A frame takes its sequence number only once window space is guaranteed, and is queued without pumping in between, so frames always reach the wire in sequence order. The receive callback may itself send (blocking or not), including encrypted.</p>
|
|
<p>Once session keys exist the receiver drops clear frames unless it marked the channel plaintext with secLinkSetChannelPlain.</p>
|
|
<h2>secLinkSendBuf</h2>
|
|
<p>Send an arbitrarily large buffer by splitting into SECLINK_MAX_PAYLOAD chunks. Always blocks until all data is sent.</p>
|
|
<pre><code>int secLinkSendBuf(SecLinkT *link, const uint8_t *data, int len, uint8_t channel, bool encrypt);</code></pre>
|
|
<p>Returns SECLINK_SUCCESS or the first error encountered.</p>
|
|
<h2>secLinkSetChannelPlain</h2>
|
|
<p>Allow (plain = true) or refuse (plain = false, the default) clear frames on a channel once session keys exist. Before the first handshake clear frames are always accepted.</p>
|
|
<pre><code>int secLinkSetChannelPlain(SecLinkT *link, uint8_t channel, bool plain);</code></pre>
|
|
<p>Returns SECLINK_SUCCESS or SECLINK_ERR_PARAM for a bad link or channel.</p>
|
|
<h2>secLinkPoll</h2>
|
|
<p>Poll for incoming data. Drains the serial port, processes frames and retransmits, completes pending handshake steps, verifies and decrypts data frames and forwards them to the callback.</p>
|
|
<pre><code>int secLinkPoll(SecLinkT *link);</code></pre>
|
|
<p>Returns the number of packets delivered by the packet layer this call, SECLINK_ERR_PARAM if link is NULL, or SECLINK_ERR_SERIAL when the port is closed or disconnected. Must be called frequently in the main loop.</p>
|
|
<p>If the remote packet layer resets, the session keys are dropped (the link is no longer ready) and a new handshake is needed. Clear frames stay refused on protected channels (see Protocol).</p>
|
|
<h2>secLinkIsReady</h2>
|
|
<p>Check whether the handshake is complete and the link is ready for encrypted traffic. Cleartext sends do not require the link to be ready.</p>
|
|
<pre><code>bool secLinkIsReady(SecLinkT *link);</code></pre>
|
|
<h2>secLinkGetDropped</h2>
|
|
<p>Number of received frames dropped since open: bad MAC (tampered, replayed or injected), clear frames on a protected channel, encrypted frames without session keys, and malformed frames.</p>
|
|
<pre><code>int32_t secLinkGetDropped(SecLinkT *link);</code></pre>
|
|
<p>Returns the count or SECLINK_ERR_PARAM when link is NULL.</p>
|
|
<h2>secLinkGetPending</h2>
|
|
<p>Get the number of unacknowledged packets in the transmit window. Delegates directly to pktGetPending on the underlying packet connection. Useful for non-blocking send loops to decide when to throttle.</p>
|
|
<pre><code>int secLinkGetPending(SecLinkT *link);</code></pre>
|
|
<p>Returns the pending count on success or SECLINK_ERR_PARAM when link is NULL.</p>
|
|
</div>
|
|
<div class="topic" id="lib.serial.example">
|
|
<h1>End-to-End Example</h1>
|
|
<p>Below is a complete secure BBS client that opens COM1, runs the authenticated key exchange, and then shuttles terminal traffic over a single encrypted channel. It demonstrates the typical serial stack usage: load the RNG seed file, open the link, set the passphrase, run the handshake, then enter a poll+send loop.</p>
|
|
<pre><code>#include "rs232/rs232.h"
|
|
#include "packet/packet.h"
|
|
#include "security/security.h"
|
|
#include "seclink/secLink.h"
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#define CHAN_TERMINAL 0
|
|
|
|
#define SEED_FILE "SECLINK.RND"
|
|
|
|
static void onPacket(void *ctx, const uint8_t *data, int len, uint8_t channel, bool encrypted) {
|
|
(void)ctx;
|
|
if (channel == CHAN_TERMINAL && encrypted) {
|
|
fwrite(data, 1, len, stdout);
|
|
fflush(stdout);
|
|
}
|
|
}
|
|
|
|
|
|
int main(void) {
|
|
// Carry entropy over from the last run (hardware entropy is
|
|
// gathered automatically; feed key timings with secRngAddEntropy).
|
|
secRngLoadSeed(SEED_FILE);
|
|
|
|
// Open the link at 115200 8N1 with RTS/CTS flow control.
|
|
SecLinkT *link = secLinkOpen(RS232_COM1, 115200, 8, 'N', 1, RS232_HANDSHAKE_RTSCTS, onPacket, NULL);
|
|
if (!link) {
|
|
fprintf(stderr, "secLinkOpen failed\n");
|
|
return 1;
|
|
}
|
|
|
|
// Authenticated key exchange. The proxy must use the same passphrase.
|
|
secLinkSetPassphrase(link, "shared secret words");
|
|
if (secLinkHandshake(link) != SECLINK_SUCCESS) {
|
|
fprintf(stderr, "Handshake failed\n");
|
|
secLinkClose(link);
|
|
return 1;
|
|
}
|
|
|
|
// Send an encrypted banner and then relay keyboard input.
|
|
const char *hello = "Hello, secure BBS!\r\n";
|
|
secLinkSend(link, (const uint8_t *)hello, (int)strlen(hello), CHAN_TERMINAL, true, true);
|
|
|
|
while (!feof(stdin)) {
|
|
// Deliver any incoming packets to onPacket().
|
|
secLinkPoll(link);
|
|
|
|
// If the user typed something, forward it encrypted.
|
|
int ch = getchar();
|
|
if (ch == EOF) {
|
|
break;
|
|
}
|
|
uint8_t byte = (uint8_t)ch;
|
|
secLinkSend(link, &byte, 1, CHAN_TERMINAL, true, true);
|
|
}
|
|
|
|
secLinkClose(link);
|
|
secRngSaveSeed(SEED_FILE);
|
|
return 0;
|
|
}</code></pre>
|
|
<p>Notes:</p>
|
|
<ul>
|
|
<li>secLinkOpen internally calls rs232Open and pktOpen. secLinkClose tears them down in reverse order.</li>
|
|
<li>The DRBG gathers hardware entropy on first use. On a CPU without a time stamp counter that alone is guessable; the seed file and user-input timings (secRngAddEntropy) are what make the DH keys unpredictable.</li>
|
|
<li>secLinkPoll must be called frequently to drain the RX ring buffer, process ACKs, and dispatch received packets to the callback.</li>
|
|
<li>For bulk transfers larger than SECLINK_MAX_PAYLOAD (245 bytes), use secLinkSendBuf which splits the data into chunks automatically.</li>
|
|
</ul>
|
|
</div>
|
|
<div class="topic" id="lib.taskmgr">
|
|
<h1>Task Manager</h1>
|
|
<p>System-level task manager for DVX. Displays all running applications with a six-column ListView (Name, Title, File, Type, Memory, Status) and exposes Switch To, End Task, and Run... operations. Always accessible via Ctrl+Esc regardless of which application is focused. Persists independently of the desktop app (Program Manager).</p>
|
|
<p>Header: taskmgr/shellTaskMgr.h</p>
|
|
<p>Loaded as: bin/libs/taskmgr.lib</p>
|
|
<h2>Integration</h2>
|
|
<p>The task manager is a shell-level library, not a regular DXE app. At DXE load time a GCC constructor function registers the Task Manager with the shell by setting the shellCtrlEscFn function pointer (declared extern in dvxshell/shellApp.h). The shell invokes shellCtrlEscFn on every Ctrl+Esc keystroke; if taskmgr.lib is not loaded the pointer stays NULL and Ctrl+Esc does nothing.</p>
|
|
<p>The Task Manager owns its window on behalf of the shell itself (appId = 0), not any running app, which is why it survives when the desktop app terminates. It registers shellTaskMgrRefresh with shellRegisterDesktopUpdate so the list refreshes live as apps load, reap, or crash.</p>
|
|
<h2>Integration with libtasks</h2>
|
|
<p>The Task Manager reads from the shell's app slot table (shellGetApp, shellAppSlotCount) rather than from the libtasks scheduler directly; the list and the "Applications: N" status count both show the running apps. Each ShellAppT slot carries its cooperative task ID when hasMainLoop is true; End Task calls shellForceKillApp, which in turn calls tsKill on that task ID (for main-loop apps) after destroying the app's windows. If the app is still inside one of its own dialogs, the unload is finished once that dialog's code has returned.</p>
|
|
<h2>Features</h2>
|
|
<ul>
|
|
<li>Six-column ListView showing Name, Title, File, Type (Task or Callback), Memory (from dvxMemGetAppUsage), and Status for every running app. Status is Running, or Not responding when dvxAppIsHung reports the app has been inside one of its window callbacks for DVX_HUNG_APP_MS (5 seconds) without a modal dialog of its own open; the open window re-checks every second (a shell idle handler) and refreshes when a status changes.</li>
|
|
<li>Switch To button (Alt+S): raises and focuses the selected app's topmost visible window, restoring it if minimized. Also triggered by double-clicking a row.</li>
|
|
<li>End Task button (Alt+E): force-kills the selected app via shellForceKillApp. Every refresh keeps the selection on the same app; when that app is gone the selection moves to the row that took its place (or the new last row).</li>
|
|
<li>Run... button (Alt+R): the shell's Run dialog (shellRunDialog, no history): a program with arguments, or a document, which opens in its associated program (shellRunCommand).</li>
|
|
<li>Status bar showing running app count plus total and used system memory.</li>
|
|
<li>Single-instance: reopening the Task Manager while it is already visible raises and focuses the existing window rather than creating a new one.</li>
|
|
</ul>
|
|
<h2>shellTaskMgrOpen</h2>
|
|
<p>Open the Task Manager window, or raise it to the front if already open.</p>
|
|
<pre><code>void shellTaskMgrOpen(AppContextT *ctx);</code></pre>
|
|
<pre> Parameter Description
|
|
--------- -----------
|
|
ctx Application context (from dvxInit). Required for window creation, stack manipulation, and registering the desktop-update callback.</pre>
|
|
<p>Called by the shell's global Ctrl+Esc handler via the shellCtrlEscFn hook.</p>
|
|
<h2>shellTaskMgrRefresh</h2>
|
|
<p>Rebuild the task list display from the current shell app slot table and refresh the status bar. Registered with shellRegisterDesktopUpdate at open time so it is invoked whenever the shell detects app state changes.</p>
|
|
<pre><code>void shellTaskMgrRefresh(void);</code></pre>
|
|
<p>Takes no parameters. Safe to call even if the Task Manager window is not currently open (the call is a no-op in that case).</p>
|
|
</div>
|
|
<div class="topic" id="lib.texthelp">
|
|
<h1>Text Help Library</h1>
|
|
<p>Shared text editing infrastructure library for DVX widget DXEs. Provides cursor blink management, cross-widget selection clearing, word boundary logic, and a complete single-line text editing engine. Used by TextInput, Spinner, ComboBox, AnsiTerm, and other widgets that need text editing behavior.</p>
|
|
<p>Header: texthelp/textHelp.h</p>
|
|
<p>Loaded as: bin/libs/texthelp.lib</p>
|
|
<h2>How Widgets Use This Library</h2>
|
|
<p>Widget DXEs that implement text editing delegate to textHelp for the four high-cost behaviors:</p>
|
|
<ul>
|
|
<li>Cursor blink. The library tracks a 250 ms blink timer in a static global and the focused widget reads sCursorBlinkOn (exposed via libdvx) when repainting.</li>
|
|
<li>Selection clearing. When a widget gains focus it calls clearOtherSelections(self) so only one widget ever has an active text selection.</li>
|
|
<li>Word boundaries. isWordChar, wordStart/wordEnd, and wordBoundaryLeft/wordBoundaryRight implement the logic for double-click word selection and Ctrl+Left/Right navigation in a uniform way.</li>
|
|
<li>Single-line editing engine. A single-line widget (TextInput, Spinner, ComboBox) embeds a TextEditSingleT (buffer, cursor, scroll offset, selection, undo slot) and hands it to widgetTextEditOnKey, widgetTextEditMouseClick and widgetTextEditDragUpdateLine. widgetTextEditPaintLine draws a line from an explicit buffer so a widget can paint substitute text (password bullets).</li>
|
|
</ul>
|
|
<p>The multi-line engine (TextArea) works the same way with a TextEditMultiT.</p>
|
|
<h2>Constants</h2>
|
|
<pre> Constant Value Description
|
|
-------- ----- -----------
|
|
TEXT_INPUT_PAD 3 Pixel padding inside text editing fields.
|
|
TEXT_EDIT_UNDO_NONE -1 Undo length meaning "no snapshot". Set it at creation and whenever the text is replaced wholesale.
|
|
TEXT_EDIT_DEFAULT_MAX_LEN 256 Capacity in characters of a single-line editor created with maxLen <= 0; also the BASIC default for TextBox and ComboBox.
|
|
TEXT_CURSOR_BLINK_MS 250 Caret blink half-period in milliseconds.</pre>
|
|
</div>
|
|
<div class="topic" id="lib.texthelp.cursorblink">
|
|
<h2>wgtUpdateCursorBlink</h2>
|
|
<p>Advances the cursor blink timer. When the blink interval (TEXT_CURSOR_BLINK_MS, 250 ms) elapses, toggles the global sCursorBlinkOn flag and invalidates the currently focused widget for repaint. Registered automatically at library load time via a constructor function that sets the sCursorBlinkFn pointer.</p>
|
|
<pre><code>void wgtUpdateCursorBlink(void);</code></pre>
|
|
<p>Takes no arguments and returns nothing. Called by the core event loop.</p>
|
|
</div>
|
|
<div class="topic" id="lib.texthelp.clearother">
|
|
<h2>clearOtherSelections</h2>
|
|
<p>Clears the text selection on whichever widget previously had one, unless that widget is the one passed as the argument. Tracks the last selected widget in a static pointer for O(1) lookup instead of walking the widget tree. Validates that the previous widget's window is still in the window stack before touching it, so stale pointers from closed windows are handled safely. If the previous selection was in a different window, that window gets a full repaint to clear the stale highlight.</p>
|
|
<pre><code>void clearOtherSelections(WidgetT *except);</code></pre>
|
|
<pre> Parameter Type Description
|
|
--------- ---- -----------
|
|
except WidgetT * The widget that now owns the selection. Its selection is preserved.</pre>
|
|
</div>
|
|
<div class="topic" id="lib.texthelp.iswordchar">
|
|
<h2>isWordChar</h2>
|
|
<p>Returns true if the character is alphanumeric or underscore. Used by word boundary functions to define what constitutes a "word" for double-click selection and Ctrl+arrow navigation.</p>
|
|
<pre><code>bool isWordChar(char c);</code></pre>
|
|
<pre> Parameter Type Description
|
|
--------- ---- -----------
|
|
c char The character to test.</pre>
|
|
<p>Returns true if c is alphanumeric (a-z, A-Z, 0-9) or underscore.</p>
|
|
</div>
|
|
<div class="topic" id="lib.texthelp.wordend">
|
|
<h2>wordEnd</h2>
|
|
<p>Scans forward from the given position, returning the index of the first non-word character. Used to find the right boundary of a word for double-click word selection.</p>
|
|
<pre><code>int32_t wordEnd(const char *buf, int32_t len, int32_t pos);</code></pre>
|
|
<pre> Parameter Type Description
|
|
--------- ---- -----------
|
|
buf const char * The text buffer to scan.
|
|
len int32_t Length of the text in the buffer.
|
|
pos int32_t Starting position (character index).</pre>
|
|
<p>Returns the index one past the last word character.</p>
|
|
</div>
|
|
<div class="topic" id="lib.texthelp.wordstart">
|
|
<h2>wordStart</h2>
|
|
<p>Scans backward from the given position, returning the index of the first character of the current word. Used to find the left boundary of a word for double-click word selection.</p>
|
|
<pre><code>int32_t wordStart(const char *buf, int32_t pos);</code></pre>
|
|
<pre> Parameter Type Description
|
|
--------- ---- -----------
|
|
buf const char * The text buffer to scan.
|
|
pos int32_t Starting position (character index).</pre>
|
|
<p>Returns the index of the first character of the word containing pos.</p>
|
|
</div>
|
|
<div class="topic" id="lib.texthelp.wordboundaryleft">
|
|
<h2>wordBoundaryLeft</h2>
|
|
<p>Finds the left word boundary for Ctrl+Left navigation. Skips non-word characters first, then skips word characters, leaving the cursor at the start of the previous word.</p>
|
|
<pre><code>int32_t wordBoundaryLeft(const char *buf, int32_t pos);</code></pre>
|
|
<pre> Parameter Type Description
|
|
--------- ---- -----------
|
|
buf const char * The text buffer to scan.
|
|
pos int32_t Starting position (character index).</pre>
|
|
<p>Returns the index of the previous word boundary. Returns 0 if pos is already at or before the start of the buffer.</p>
|
|
</div>
|
|
<div class="topic" id="lib.texthelp.wordboundaryright">
|
|
<h2>wordBoundaryRight</h2>
|
|
<p>Finds the right word boundary for Ctrl+Right navigation. Skips word characters first, then skips non-word characters, leaving the cursor at the start of the next word.</p>
|
|
<pre><code>int32_t wordBoundaryRight(const char *buf, int32_t len, int32_t pos);</code></pre>
|
|
<pre> Parameter Type Description
|
|
--------- ---- -----------
|
|
buf const char * The text buffer to scan.
|
|
len int32_t Length of the text buffer.
|
|
pos int32_t Starting position (character index).</pre>
|
|
<p>Returns the index of the next word boundary. Returns len if pos is already at or past the end of the buffer.</p>
|
|
</div>
|
|
<div class="topic" id="lib.texthelp.saveundo">
|
|
<h2>textEditSaveUndo</h2>
|
|
<p>Capture a snapshot of the current buffer contents and cursor position into an undo buffer. Called by the single-line engine before every destructive operation (insert, delete, paste, cut). Truncates to bufSize - 1 characters so the null terminator always fits.</p>
|
|
<pre><code>void textEditSaveUndo(char *buf, int32_t len,
|
|
int32_t cursor, char *undoBuf,
|
|
int32_t *pUndoLen, int32_t *pUndoCursor,
|
|
int32_t bufSize);</code></pre>
|
|
<pre> Parameter Type Description
|
|
--------- ---- -----------
|
|
buf char * Source buffer to snapshot.
|
|
len int32_t Current length of source contents.
|
|
cursor int32_t Current cursor position to save.
|
|
undoBuf char * Destination undo buffer. If NULL the call is a no-op.
|
|
pUndoLen int32_t * [out] Length of saved undo contents.
|
|
pUndoCursor int32_t * [out] Saved cursor position.
|
|
bufSize int32_t Size of the destination undo buffer including the null terminator.</pre>
|
|
</div>
|
|
<div class="topic" id="lib.texthelp.undoswap">
|
|
<h2>textEditUndoSwap</h2>
|
|
<p>Exchange the live buffer with the undo snapshot (single-slot undo; a second swap is redo). buf and undoBuf must both be bufSize bytes. Returns false and changes nothing when there is no snapshot (*pUndoLen is TEXT_EDIT_UNDO_NONE). On success *pCursor is the restored cursor, clamped to the restored length, and the undo slot holds the previous text and cursor.</p>
|
|
<pre><code>bool textEditUndoSwap(char *buf, int32_t bufSize,
|
|
int32_t *pLen, int32_t *pCursor, char *undoBuf,
|
|
int32_t *pUndoLen, int32_t *pUndoCursor);</code></pre>
|
|
<p>Two related helpers:</p>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
textEditCopyText int32_t textEditCopyText(char *buf, int32_t bufSize, const char *text). Copy text into buf (truncated to bufSize - 1), terminate it, and return the length. Writes only the copied bytes.
|
|
textEditMoveCursor void textEditMoveCursor(int32_t newPos, bool shift, int32_t *pCursor, int32_t *pSelStart, int32_t *pSelEnd). Move a single-line cursor; with shift extend the selection (anchoring at the old cursor), otherwise clear it. The selection pointers may be NULL.</pre>
|
|
</div>
|
|
<div class="topic" id="lib.texthelp.single">
|
|
<h2>TextEditSingleT</h2>
|
|
<p>Per-editor state of a single-line field. selStart/selEnd are -1 when nothing is selected; undoLen is TEXT_EDIT_UNDO_NONE until the first edit takes a snapshot.</p>
|
|
<pre><code>typedef struct TextEditSingleT {
|
|
char *buf;
|
|
char *undoBuf;
|
|
int32_t bufSize;
|
|
int32_t len;
|
|
int32_t cursorPos;
|
|
int32_t scrollOff;
|
|
int32_t selStart;
|
|
int32_t selEnd;
|
|
int32_t undoLen;
|
|
int32_t undoCursor;
|
|
} TextEditSingleT;</code></pre>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
bool textEditSingleInit(TextEditSingleT *te, int32_t maxLen) Allocate buf and undoBuf for maxLen characters plus the terminator (TEXT_EDIT_DEFAULT_MAX_LEN when maxLen <= 0): empty text, no selection, no undo snapshot. Returns false (both pointers NULL) when allocation fails.
|
|
void textEditSingleFree(TextEditSingleT *te) Free both buffers.
|
|
void textEditSingleSetText(TextEditSingleT *te, const char *text) Replace the text (NULL = empty, truncated to the buffer): cursor at the end, view scrolled home, no selection, undo slot emptied.
|
|
bool textEditSingleClearSelection(TextEditSingleT *te) Drop the selection. Returns true when a non-empty selection was cleared.
|
|
bool textEditKeyEdits(int32_t key) True for keys that modify the text (printable characters, Backspace, Delete, Ctrl+V, Ctrl+X, Ctrl+Z). Read-only editors drop these before calling the engine.
|
|
int32_t textEditVisibleChars(int32_t fieldWidth, const BitmapFontT *font) Whole columns visible in a field fieldWidth pixels wide, inside the text padding.
|
|
void textEditScrollToCursor(int32_t cursor, int32_t visibleChars, int32_t *pScrollOff) Adjust a horizontal scroll offset so the cursor is inside the visible columns.
|
|
uint32_t textEditDefaultSyntaxRgb(uint8_t idx) Default syntax color (0x00RRGGBB) for a TEXT_SYNTAX_* index; 0 for TEXT_SYNTAX_DEFAULT, which uses the caller's foreground.</pre>
|
|
<p>The key handler, the drag update and the widgets' paint code all use textEditVisibleChars, so they agree on the visible width.</p>
|
|
</div>
|
|
<div class="topic" id="lib.texthelp.dragupdate">
|
|
<h2>widgetTextEditDragUpdateLine</h2>
|
|
<p>Called during mouse drag to extend the selection in a single-line text field. Converts a viewport-relative pixel x coordinate to a character position and updates the cursor and selection end accordingly. Auto-scrolls the text when the mouse moves past the left or right visible edge.</p>
|
|
<pre><code>void widgetTextEditDragUpdateLine(TextEditSingleT *te, int32_t vx, int32_t leftEdge, int32_t maxChars, const BitmapFontT *font);</code></pre>
|
|
<pre> Parameter Type Description
|
|
--------- ---- -----------
|
|
te TextEditSingleT * [in/out] Editor state: cursorPos, scrollOff and selEnd are updated.
|
|
vx int32_t Mouse x position in viewport coordinates.
|
|
leftEdge int32_t Pixel x of the left edge of the text area.
|
|
maxChars int32_t Maximum visible characters in the field.
|
|
font const BitmapFontT * Bitmap font used for character width.</pre>
|
|
</div>
|
|
<div class="topic" id="lib.texthelp.mouseclick">
|
|
<h2>widgetTextEditMouseClick</h2>
|
|
<p>Handles mouse click events for single-line text widgets. Converts pixel coordinates to a character position using the font's character width. Detects multi-click sequences via the core multiClickDetect() function:</p>
|
|
<ul>
|
|
<li>Single click -- places the cursor and begins a potential drag selection.</li>
|
|
<li>Double click -- selects the word under the cursor (if wordSelect is true), or selects all text.</li>
|
|
<li>Triple click -- selects all text in the field.</li>
|
|
</ul>
|
|
<pre><code>void widgetTextEditMouseClick(WidgetT *w, TextEditSingleT *te, int32_t vx, int32_t vy, int32_t textLeftX, const BitmapFontT *font, bool wordSelect, bool dragSelect);</code></pre>
|
|
<pre> Parameter Type Description
|
|
--------- ---- -----------
|
|
w WidgetT * The widget receiving the click.
|
|
te TextEditSingleT * [in/out] Editor state: cursorPos, selStart and selEnd are set.
|
|
vx int32_t Mouse x in viewport coordinates.
|
|
vy int32_t Mouse y in viewport coordinates.
|
|
textLeftX int32_t Pixel x of the left edge of the text area.
|
|
font const BitmapFontT * Bitmap font for character width calculation.
|
|
wordSelect bool If true, double-click selects word; if false, selects all.
|
|
dragSelect bool If true, single-click begins drag selection.</pre>
|
|
</div>
|
|
<div class="topic" id="lib.texthelp.onkey">
|
|
<h2>widgetTextEditOnKey</h2>
|
|
<p>The core single-line text editing engine. Processes a single keystroke and updates the editor's buffer, cursor, scroll offset, selection, and undo state. Shared by TextInput, Spinner and ComboBox.</p>
|
|
<pre><code>void widgetTextEditOnKey(WidgetT *w, int32_t key, int32_t mod, TextEditSingleT *te, int32_t fieldWidth);</code></pre>
|
|
<pre> Parameter Type Description
|
|
--------- ---- -----------
|
|
w WidgetT * The widget receiving the key event.
|
|
key int32_t Key code (ASCII for printable, control code or scan|0x100 for extended).
|
|
mod int32_t Modifier flags (KEY_MOD_SHIFT, etc.).
|
|
te TextEditSingleT * [in/out] Editor state. Ctrl+Z does nothing while undoLen is TEXT_EDIT_UNDO_NONE.
|
|
fieldWidth int32_t Field width in pixels including the text padding (subtracted internally). 0 = use widget width.</pre>
|
|
<h3>Supported Keys</h3>
|
|
<pre> Key Action
|
|
--- ------
|
|
Printable Insert character at cursor (replaces selection if active).
|
|
Backspace Delete character before cursor or delete selection.
|
|
Delete Delete character after cursor or delete selection.
|
|
Left Move cursor left. With Shift, extend selection.
|
|
Right Move cursor right. With Shift, extend selection.
|
|
Ctrl+Left Move cursor to previous word boundary. With Shift, extend selection.
|
|
Ctrl+Right Move cursor to next word boundary. With Shift, extend selection.
|
|
Home Move cursor to start. With Shift, extend selection.
|
|
End Move cursor to end. With Shift, extend selection.
|
|
Ctrl+A Select all text.
|
|
Ctrl+C Copy selection to clipboard.
|
|
Ctrl+V Paste from clipboard (newlines stripped).
|
|
Ctrl+X Cut selection to clipboard.
|
|
Ctrl+Z Undo (swaps current and undo buffers).</pre>
|
|
<p>Fires w->onChange after any mutation (insert, delete, paste, cut, undo). Automatically adjusts scroll offset to keep cursor visible after every operation.</p>
|
|
</div>
|
|
<div class="topic" id="lib.texthelp.paintline">
|
|
<h2>widgetTextEditPaintLine</h2>
|
|
<p>Renders a single line of text with optional selection highlighting and a blinking cursor. Splits the visible text into up to three segments (pre-selection, selection, post-selection) and draws each with appropriate colors. The selection highlight uses the menu highlight colors from the color scheme. The cursor is drawn as a one-pixel vertical line when sCursorBlinkOn is true.</p>
|
|
<pre><code>void widgetTextEditPaintLine(
|
|
DisplayT *d, const BlitOpsT *ops,
|
|
const BitmapFontT *font,
|
|
const ColorSchemeT *colors,
|
|
int32_t textX, int32_t textY,
|
|
const char *buf, int32_t visLen,
|
|
int32_t scrollOff, int32_t cursorPos,
|
|
int32_t selStart, int32_t selEnd,
|
|
uint32_t fg, uint32_t bg,
|
|
bool showCursor,
|
|
int32_t cursorMinX, int32_t cursorMaxX);</code></pre>
|
|
<pre> Parameter Type Description
|
|
--------- ---- -----------
|
|
d DisplayT * Display context for drawing.
|
|
ops const BlitOpsT * Blit operations table for the current pixel format.
|
|
font const BitmapFontT * Bitmap font for text rendering.
|
|
colors const ColorSchemeT * Color scheme (used for selection highlight colors).
|
|
textX int32_t Pixel x of the start of visible text.
|
|
textY int32_t Pixel y of the text baseline.
|
|
buf const char * Visible portion of the text buffer (buf + scrollOff).
|
|
visLen int32_t Number of visible characters to draw.
|
|
scrollOff int32_t Scroll offset (used to map selection to visible range).
|
|
cursorPos int32_t Absolute cursor position in the full buffer.
|
|
selStart int32_t Selection start (-1 if no selection).
|
|
selEnd int32_t Selection end (-1 if no selection).
|
|
fg uint32_t Foreground (text) color.
|
|
bg uint32_t Background color.
|
|
showCursor bool Whether to draw the cursor (false when widget lacks focus).
|
|
cursorMinX int32_t Left clipping bound for cursor drawing.
|
|
cursorMaxX int32_t Right clipping bound for cursor drawing.</pre>
|
|
</div>
|
|
<div class="topic" id="lib.xfer">
|
|
<h1>File Transfer Library</h1>
|
|
<p>XMODEM, XMODEM-1K, YMODEM and ZMODEM senders and receivers that work over any byte stream: a serial port, the secure link, and later a network connection. The engines are non-blocking state machines: start a transfer with read and write callbacks, then call xferPoll with the current time in milliseconds as often as you can (every frame). A poll hands queued output to the transport, consumes the input that has arrived and runs the timeouts. Nothing blocks and nothing touches the screen, so a program stays responsive and draws its own progress.</p>
|
|
<p>Loaded as: bin/libs/kpunch/xfer/xfer.lib (needs the serial library only for the BASIC functions)</p>
|
|
<p>Header: xfer/xfer.h</p>
|
|
<p><a href="#lib.xfer.api">C API</a></p>
|
|
<p><a href="#lib.xfer.protocols">Protocol details</a></p>
|
|
<p><a href="#lib.xfer.basic">Using transfers from BASIC</a></p>
|
|
<h2>Example</h2>
|
|
<pre><code>static int32_t ioRead(void *ctx, uint8_t *buf, int32_t max) { return connRead((ConnT *)ctx, buf, max); }
|
|
static int32_t ioWrite(void *ctx, const uint8_t *d, int32_t len) { return connWrite((ConnT *)ctx, d, len); }
|
|
|
|
XferT *x = xferCreate();
|
|
XferIoT io = { conn, ioRead, ioWrite, NULL };
|
|
XferOptionsT opt;
|
|
|
|
xferOptionsDefaults(&opt);
|
|
opt.exists = XferExistRenameE;
|
|
xferStartReceive(x, XferZmodemE, &io, &opt, "DOWNLOAD", nowMs());
|
|
|
|
// every frame:
|
|
if (xferPoll(x, nowMs()) != XferRunningE) {
|
|
printf("%s\n", xferProgress(x)->message);
|
|
}</code></pre>
|
|
</div>
|
|
<div class="topic" id="lib.xfer.api">
|
|
<h1>File Transfer C API</h1>
|
|
<h2>Types</h2>
|
|
<pre> Type Description
|
|
---- -----------
|
|
XferT Opaque transfer. One XferT runs one transfer at a time and can be started again when it has ended.
|
|
XferIoT The transport: ctx, read (never blocks; returns the bytes available, 0 when none), write (returns the bytes accepted, may be fewer), pending (optional: bytes accepted but not yet sent; a ZMODEM sender keeps it small so it reacts quickly to an error report on a slow line).
|
|
XferOptionsT exists (what happens to an incoming file that already exists), rxWindow (ZMODEM receiving: bytes the sender may stream before it waits; 0 = no limit), escapeCtl (ZMODEM: escape every control character), resume (ZMODEM sending: ask the receiver to continue partial files).
|
|
XferProgressT Status, protocol, direction, file index and count, file size and bytes done, batch size and bytes done, resume offset, retries, current file name, and the outcome message. While the status is XferAskingE, askLocalSize, askRemoteSize and askCanResume describe the question.
|
|
XferDetectT State of the ZMODEM auto-start detector (zero it, or call xferDetectReset).</pre>
|
|
<h2>Enumerations</h2>
|
|
<pre> Enumeration Values
|
|
----------- ------
|
|
XferProtoE XferXmodemE (128-byte blocks, CRC or checksum), XferXmodem1kE (1K blocks), XferYmodemE (batch with names and sizes; block 0 carries every file-information field: size, date 0 = unknown, mode 0 = a DOS file, serial 0, files and bytes left, so receivers such as lrz never read unset fields), XferZmodemE (batch, streaming, CRC-32, resume).
|
|
XferStatusE XferIdleE, XferRunningE, XferAskingE (waiting for xferAnswer), XferDoneE, XferFailedE, XferCancelledE.
|
|
XferExistE XferExistAskE, XferExistRenameE (store as NAME~1.EXT), XferExistResumeE (ZMODEM: continue a shorter file; otherwise rename), XferExistOverwriteE, XferExistSkipE (ZMODEM skips the file; YMODEM receives and discards it).</pre>
|
|
<h2>Functions</h2>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
XferT *xferCreate(void) Allocate an idle transfer (NULL when out of memory).
|
|
void xferDestroy(x) Close any open file and free the transfer.
|
|
void xferOptionsDefaults(opt) Ask when a file exists, no window limit, no extra escaping, no resume request.
|
|
bool xferStartSend(x, proto, io, opt, paths, count, now) Send count files (XMODEM sends the first). False, with the reason in the message, when a file cannot be opened. opt may be NULL.
|
|
bool xferStartReceive(x, proto, io, opt, target, now) XMODEM: target is the file to create. YMODEM and ZMODEM: target is the folder for the files.
|
|
XferStatusE xferPoll(x, now) Move the transfer along. now is a millisecond clock (wrapping is fine).
|
|
void xferCancel(x) Send the cancel sequence to the other side and end with XferCancelledE.
|
|
void xferAnswer(x, action, now) Answer XferAskingE: XferExistResumeE, RenameE, OverwriteE or SkipE.
|
|
const XferProgressT *xferProgress(x) Progress and outcome.
|
|
bool xferDetectFeed(d, byte) Feed received terminal bytes; true when a ZMODEM sender's start frame (ZRQINIT) has arrived, so a terminal can start a ZMODEM receive by itself.
|
|
void xferSafeName(name, out) Reduce a sender's file name to a safe 8.3 name: no path, no drive, no DOS device name (CON, PRN, COM1 and so on get a leading _), only portable characters. out holds XFER_NAME_MAX bytes.
|
|
uint16_t xferCrc16(data, len) CRC-16/XMODEM (polynomial 0x1021).
|
|
uint32_t xferCrc32(data, len) CRC-32 (IEEE 802.3), as ZMODEM uses it.</pre>
|
|
<h2>Files</h2>
|
|
<p>Received names are made safe with xferSafeName and stored in the receive folder. An existing file is never replaced silently: XferOptionsT.exists decides, and with XferExistAskE the transfer pauses in XferAskingE (the other side waits) until xferAnswer. A failed or cancelled XMODEM or YMODEM download deletes its partial file. A ZMODEM download keeps it, so the same file can be continued later: the receiver continues a shorter file of the same name when the sender asks for it (resume) or when exists is XferExistResumeE.</p>
|
|
</div>
|
|
<div class="topic" id="lib.xfer.protocols">
|
|
<h1>Protocol Details</h1>
|
|
<h2>XMODEM and XMODEM-1K</h2>
|
|
<p>Packets are SOH (128 bytes) or STX (1024 bytes), the block number and its complement, the data, and a CRC-16 or a one-byte checksum. The receiver asks for CRC mode with 'C' every 3 seconds and falls back to checksums (NAK) after three tries, unless the sender has already started. Every packet is answered with ACK or NAK; two CANs cancel. After 10 failed tries of one packet the transfer gives up and cancels the other side. A damaged packet is refused only once the line has been quiet for a second, so the rest of it cannot be mistaken for the next packet. An EOT counts only when the line stays quiet after it (a stray 0x04 inside a damaged packet does not end the file early). A sender whose 1K block fails three times sends that block and the rest of the file in 128-byte blocks. XMODEM pads the last block with 0x1A (SUB); the receiver keeps the padding because the protocol does not carry the file size.</p>
|
|
<h2>YMODEM</h2>
|
|
<p>A batch of files, always with CRC and 1K blocks (a tail of at most 128 bytes goes in a 128-byte block). Block 0 carries the file name and size before each file, and an empty block 0 ends the batch. The receiver refuses the first EOT of each file and accepts the second, and cuts the padding using the size.</p>
|
|
<h2>ZMODEM</h2>
|
|
<p>Headers are sent in hex (to start and answer) or binary with CRC-16 or CRC-32; data travels in subpackets with a CRC over the data and the frame end. ZDLE escaping protects ZDLE, DLE, XON and XOFF (with and without the high bit) and a CR after '@'; with escapeCtl every control character is escaped (the sender announces it with ZSINIT; the receiver asks for it in ZRINIT). Five CANs in a row cancel.</p>
|
|
<p>The sender streams: it keeps at most 8 KB unacknowledged, asks for a progress acknowledgement every 4 KB, honours the receiver's buffer size (a receiver without flow control should set rxWindow to what its hardware buffer holds, so a slow frame never loses data), and builds a subpacket only when the transport has sent nearly everything it was given (XferIoT.pending). On an error the receiver sends ZRPOS with the last good position; the sender goes back there and halves its subpacket size (down to 64 bytes), then doubles it again after eight clean subpackets. A sender that gets no acknowledgement for the window goes back to the last acknowledged position. File names travel in the ZFILE subpacket with the size and the files and bytes left in the batch, which the receiver shows as batch progress. Commands from the other side (ZCOMMAND) are never run.</p>
|
|
<p>Interoperability: the engines are tested against lrz and lsz (lrzsz 0.12.20) in both directions for every protocol, including escaping and resume.</p>
|
|
</div>
|
|
<div class="topic" id="lib.xfer.basic">
|
|
<h1>File Transfers from BASIC</h1>
|
|
<p>Add xfer.bas (and comm.bas, for SerOpen) to a BASIC project to send and receive files over a serial port. Start a transfer, then call XferPoll often, for example from a Timer, until it no longer returns XFER_RUNNING. While it runs, do not read the port yourself and keep it detached from any Terminal control. Received files never replace existing ones: the new file gets a new name.</p>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
XferSendFile(com, proto, path) Send one file over COM port com (1-4). Returns a handle, or 0.
|
|
XferReceive(com, proto, target) Receive: XMODEM into the file target; YMODEM and ZMODEM into the folder target. Returns a handle, or 0.
|
|
XferPoll(handle) Move the transfer along. Returns XFER_RUNNING, XFER_DONE, XFER_FAILED or XFER_CANCELLED.
|
|
XferBytes(handle), XferSize(handle) Bytes of the current file done, and its size (-1 when unknown).
|
|
XferFile$(handle) The file being transferred, as stored on this computer.
|
|
XferMessage$(handle) How the transfer ended, or why it failed.
|
|
XferCancel handle Stop a running transfer (the other side is told).
|
|
XferClose handle Free the handle.</pre>
|
|
<p>Protocols: XFER_XMODEM, XFER_XMODEM1K, XFER_YMODEM, XFER_ZMODEM. At most four transfers run at once.</p>
|
|
<pre><code>Dim h As Long
|
|
If SerOpen(1, 115200, 8, "N", 1, SER_HANDSHAKE_RTSCTS) Then
|
|
h = XferReceive(1, XFER_ZMODEM, "C:\DOWNLOAD")
|
|
End If
|
|
|
|
Sub Timer1_Timer ()
|
|
If h <> 0 Then
|
|
If XferPoll(h) <> XFER_RUNNING Then
|
|
Print XferFile$(h); ": "; XferMessage$(h)
|
|
XferClose h
|
|
h = 0
|
|
End If
|
|
End If
|
|
End Sub</code></pre>
|
|
</div>
|
|
<div class="topic" id="widget.base">
|
|
<h1>DVX Widget System</h1>
|
|
<p>Complete reference for the DVX GUI widget toolkit. All widgets are implemented as dynamically loaded DXE modules. They are created via convenience macros that wrap the per-widget API function tables. The base WidgetT structure is defined in libdvx/dvxWgt.h; individual widget headers live in widgets/.</p>
|
|
<p>Individual widgets are documented in their own sections. See the table of contents for the full list.</p>
|
|
<h2>Base WidgetT (Common Properties, Events, and Operations)</h2>
|
|
<p>Every widget inherits from the WidgetT structure defined in libdvx/dvxWgt.h. The fields and callbacks listed here are available on all widget types.</p>
|
|
<h3>Common Properties</h3>
|
|
<pre> Field Type Description
|
|
----- ---- -----------
|
|
name char[32] Widget name for lookup via wgtFind().
|
|
x, y, w, h int32_t Computed geometry relative to the window content area (set by layout).
|
|
minW, minH int32_t (tagged) Minimum size hints. Use wgtPixels(), wgtChars(), or wgtPercent(). 0 = auto.
|
|
maxW, maxH int32_t (tagged) Maximum size constraints. 0 = no limit.
|
|
prefW, prefH int32_t (tagged) Preferred size. 0 = auto.
|
|
weight int32_t Extra-space distribution weight. 0 = fixed, 100 = normal. A widget with weight=200 gets twice the extra space of one with weight=100.
|
|
align WidgetAlignE Main-axis alignment for children: AlignStartE, AlignCenterE, AlignEndE.
|
|
spacing int32_t (tagged) Spacing between children (containers only). 0 = default.
|
|
padding int32_t (tagged) Internal padding (containers only). 0 = default.
|
|
fgColor uint32_t Foreground color override. 0 = use color scheme default.
|
|
bgColor uint32_t Background color override. 0 = use color scheme default.
|
|
visible bool Visibility state.
|
|
enabled bool Enabled state. Disabled widgets are grayed out and ignore input.
|
|
readOnly bool Read-only mode: allows scrolling/selection but blocks editing.
|
|
swallowTab bool When true, Tab key goes to the widget instead of navigating focus.
|
|
accelKey char Lowercase accelerator character. 0 if none.
|
|
tooltip const char * Tooltip text. NULL = none. Caller owns the string.
|
|
contextMenu MenuT * Right-click context menu. NULL = none. Caller owns.
|
|
userData void * Application-defined user data pointer.</pre>
|
|
<h3>Size Specification Macros</h3>
|
|
<pre> Macro Description
|
|
----- -----------
|
|
wgtPixels(v) Size in pixels.
|
|
wgtChars(v) Size in character widths (multiplied by font charWidth).
|
|
wgtPercent(v) Size as a percentage of parent dimension.</pre>
|
|
<h3>Common Events (Callbacks)</h3>
|
|
<p>These callback function pointers are available on every WidgetT. Set them directly on the widget struct.</p>
|
|
<pre> Callback Signature Description
|
|
-------- --------- -----------
|
|
onClick void (*)(WidgetT *w) Fires on mouse click / activation.
|
|
onDblClick void (*)(WidgetT *w) Fires on double-click.
|
|
onChange void (*)(WidgetT *w) Fires when the widget's value changes (text, selection, check state, etc.).
|
|
onFocus void (*)(WidgetT *w) Fires when the widget receives keyboard focus.
|
|
onBlur void (*)(WidgetT *w) Fires when the widget loses keyboard focus.
|
|
onKeyPress void (*)(WidgetT *w, int32_t keyAscii) Fires on a printable key press (ASCII value).
|
|
onKeyDown void (*)(WidgetT *w, int32_t keyCode, int32_t shift) Fires on key down (scan code + shift state).
|
|
onKeyUp void (*)(WidgetT *w, int32_t keyCode, int32_t shift) Fires on key up.
|
|
onMouseDown void (*)(WidgetT *w, int32_t button, int32_t x, int32_t y) Fires on mouse button press.
|
|
onMouseUp void (*)(WidgetT *w, int32_t button, int32_t x, int32_t y) Fires on mouse button release.
|
|
onMouseMove void (*)(WidgetT *w, int32_t button, int32_t x, int32_t y) Fires on mouse movement over the widget.
|
|
onScroll void (*)(WidgetT *w, int32_t delta) Fires on mouse wheel scroll.
|
|
onValidate bool (*)(WidgetT *w) Validation callback. Return false to cancel a pending write.</pre>
|
|
<h3>Common Operations</h3>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
WidgetT *wgtInitWindow(AppContextT *ctx, WindowT *win) Initialize widgets for a window. Returns the root VBox container.
|
|
AppContextT *wgtGetContext(const WidgetT *w) Walk up from any widget to retrieve the AppContextT.
|
|
void wgtInvalidate(WidgetT *w) Mark widget for re-layout and repaint. Propagates to ancestors.
|
|
void wgtInvalidatePaint(WidgetT *w) Mark widget for repaint only (no layout recalculation).
|
|
void wgtSetText(WidgetT *w, const char *text) Set widget text (label, button, textinput, etc.).
|
|
const char *wgtGetText(const WidgetT *w) Get widget text.
|
|
void wgtSetEnabled(WidgetT *w, bool enabled) Enable or disable a widget.
|
|
void wgtSetReadOnly(WidgetT *w, bool readOnly) Set read-only mode.
|
|
void wgtSetFocused(WidgetT *w) Set keyboard focus to a widget.
|
|
WidgetT *wgtGetFocused(void) Get the currently focused widget.
|
|
void wgtSetVisible(WidgetT *w, bool visible) Show or hide a widget.
|
|
void wgtSetName(WidgetT *w, const char *name) Set widget name for lookup.
|
|
WidgetT *wgtFind(WidgetT *root, const char *name) Find a widget by name in the subtree.
|
|
void wgtDestroy(WidgetT *w) Destroy a widget and all its children.
|
|
void wgtSetTooltip(WidgetT *w, const char *text) Set tooltip text. Pass NULL to remove.</pre>
|
|
</div>
|
|
<div class="topic" id="widget.ansiterm">
|
|
<h2>AnsiTerm</h2>
|
|
<p>A VT100/ANSI-compatible terminal emulator widget designed for connecting to BBS systems over the serial link. Uses a traditional text-mode cell buffer (character + attribute byte pairs) with the CP437 character set and 16-color CGA palette. Supports cursor movement, screen/line erase, scrolling regions, SGR colors, DEC private modes, and a configurable scrollback buffer. Communication is abstracted through read/write function pointers, allowing the terminal to work with raw serial ports, the secLink encrypted channel, or any other byte-oriented transport.</p>
|
|
<p>The widget renders through two paint paths: a full paint used by the normal widget pipeline, and a fast incremental repaint (wgtAnsiTermRepaint) that pushes dirty rows directly to the window content buffer for low-latency serial echo.</p>
|
|
<p>Header: widgets/ansiTerm.h</p>
|
|
<h3>Creation</h3>
|
|
<pre><code>WidgetT *term = wgtAnsiTerm(parent, 80, 25);</code></pre>
|
|
<h3>API Functions</h3>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
WidgetT *wgtAnsiTerm(parent, cols, rows) Create an ANSI terminal widget with the given column/row dimensions.
|
|
void wgtAnsiTermWrite(w, data, len) Write raw bytes into the terminal's ANSI parser. data is a const uint8_t * buffer.
|
|
void wgtAnsiTermClear(w) Clear the terminal screen and reset the cursor to the home position.
|
|
void wgtAnsiTermSetComm(w, ctx, readFn, writeFn) Attach a communication channel. readFn and writeFn are I/O callbacks; ctx is passed as their first argument.
|
|
void wgtAnsiTermSetScrollback(w, maxLines) Set the maximum number of scrollback lines.
|
|
int32_t wgtAnsiTermPoll(w) Poll the communication channel for incoming data and feed it into the parser. Returns number of bytes consumed.
|
|
int32_t wgtAnsiTermRepaint(w, outY, outH) Fast repaint path that renders dirty rows directly into the window's content buffer, bypassing the widget pipeline. Returns the number of rows repainted and the dirty region via outY/outH.</pre>
|
|
<h3>API Struct (wgtRegisterApi "ansiterm")</h3>
|
|
<p>The sApi struct exposes these function pointers:</p>
|
|
<pre> Slot Function
|
|
---- --------
|
|
header WGT_API_HEADER (ABI version and size, checked by wgtGetApi)
|
|
create wgtAnsiTerm
|
|
write wgtAnsiTermWrite
|
|
clear wgtAnsiTermClear
|
|
setComm wgtAnsiTermSetComm
|
|
setScrollback wgtAnsiTermSetScrollback
|
|
poll wgtAnsiTermPoll
|
|
repaint wgtAnsiTermRepaint</pre>
|
|
<h3>Extension API Struct (wgtRegisterApi "ansitermex")</h3>
|
|
<p>Features added after the original table live in a second table so AnsiTermApiT keeps its layout for modules already built against it. dvxAnsiTermExApi() returns it (NULL when the loaded widget is older).</p>
|
|
<pre> Slot Function Description
|
|
---- -------- -----------
|
|
header WGT_API_HEADER ABI version and size, checked by wgtGetApi.
|
|
copySelection bool wgtAnsiTermCopy(w) Copy the selection to the clipboard and clear it (a menu's Copy). Returns false when nothing is selected.
|
|
setPalette void wgtAnsiTermSetPalette(w, rgb) Replace the 16 attribute colors with ANSITERM_PALETTE_BYTES bytes of red, green, blue triplets (a color scheme). NULL restores the standard CGA colors.</pre>
|
|
<h3>Properties (BASIC Interface)</h3>
|
|
<pre> Property Type Access Description
|
|
-------- ---- ------ -----------
|
|
Cols Integer Read-only Number of columns.
|
|
Rows Integer Read-only Number of rows.
|
|
Scrollback Integer Read/Write Maximum number of scrollback lines. Setting it discards the existing history.</pre>
|
|
<h3>Methods (BASIC Interface)</h3>
|
|
<pre> Method Description
|
|
------ -----------
|
|
Clear Clear the terminal screen.
|
|
Poll Process any pending bytes on the attached communication channel.
|
|
Write Write a string into the terminal (with ANSI escape processing).</pre>
|
|
<h3>Events</h3>
|
|
<p>AnsiTerm uses the common events only. No widget-specific events are defined.</p>
|
|
<h3>Keyboard Shortcuts</h3>
|
|
<p>Within the terminal widget:</p>
|
|
<pre> Key Action
|
|
--- ------
|
|
Ctrl+C Copy selection to the clipboard (when a selection exists).
|
|
Ctrl+V Send the clipboard contents to the attached write function.
|
|
Arrows Send VT100 cursor escape sequences.
|
|
Home/End Send VT100 Home/End sequences.
|
|
PgUp/PgDn/Del Send the corresponding VT100 escape sequences.
|
|
Shift+PgUp Page back through the scrollback history.
|
|
Shift+PgDn Page forward through the scrollback history.</pre>
|
|
<p>Any key sent to the host returns the view to the live screen. The mouse wheel scrolls through the scrollback history locally; it does not send keys to the host. Clearing the screen (ESC[2J, form feed, reset or Clear) saves the screen into the scrollback only up to its last non-blank row.</p>
|
|
</div>
|
|
<div class="topic" id="widget.box">
|
|
<h2>Box (VBox / HBox / Frame)</h2>
|
|
<p>Container widgets that arrange their children in a vertical column (VBox), horizontal row (HBox), or a titled group box (Frame). These are the primary layout building blocks. Children are laid out using a weight-based algorithm with configurable spacing, padding, and alignment.</p>
|
|
<p>Frame is a labelled grouping container with a Motif-style beveled border. Its title text sits centered vertically on the top border line with a small background-filled gap behind the title, giving the classic Windows 3.1 / Motif group box appearance. Internally, Frame behaves like a VBox for layout purposes.</p>
|
|
<p>The widget DXE registers three separate interface entries ("vbox", "hbox", "frame") so the form designer can create each type independently.</p>
|
|
<p>Header: widgets/box.h</p>
|
|
<h3>Creation</h3>
|
|
<pre><code>WidgetT *row = wgtHBox(parent);
|
|
WidgetT *col = wgtVBox(parent);
|
|
WidgetT *group = wgtFrame(parent, "Options");</code></pre>
|
|
<h3>API Functions</h3>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
WidgetT *wgtVBox(parent) Create a vertical box container. Children stack top-to-bottom.
|
|
WidgetT *wgtHBox(parent) Create a horizontal box container. Children stack left-to-right.
|
|
WidgetT *wgtFrame(parent, title) Create a titled group box. Children stack vertically inside the bordered frame. The title string may include a '&' prefix for an accelerator key.</pre>
|
|
<h3>API Struct (wgtRegisterApi "box")</h3>
|
|
<pre> Slot Function
|
|
---- --------
|
|
header WGT_API_HEADER (ABI version and size, checked by wgtGetApi)
|
|
vBox wgtVBox
|
|
hBox wgtHBox
|
|
frame wgtFrame</pre>
|
|
<p>The designer also registers per-type APIs: "vbox", "hbox", and "frame" each expose a single create slot.</p>
|
|
<h3>Properties</h3>
|
|
<p>Box containers use the common WidgetT fields for layout control. There are no widget-specific properties registered with the interface system.</p>
|
|
<pre> Field Description
|
|
----- -----------
|
|
align Main-axis alignment of children. HBox: Start=left, Center=center, End=right. VBox: Start=top, Center=center, End=bottom.
|
|
spacing Gap between children (tagged size). Default 4 pixels for VBox/HBox, 0 for Frame.
|
|
padding Internal padding around children (tagged size). Default 4 pixels.
|
|
weight Controls how the box itself stretches within its parent.</pre>
|
|
<p>Frame text is managed via the standard wgtSetText() / wgtGetText() interface (the widget has WCLASS_HAS_TEXT). BASIC code can set its title via the generic "Caption" or "Text" property.</p>
|
|
<h3>Events</h3>
|
|
<p>Containers use the common events only. No widget-specific events or methods are registered.</p>
|
|
<h3>Default Event</h3>
|
|
<p>"Click" on all three types.</p>
|
|
</div>
|
|
<div class="topic" id="widget.button">
|
|
<h2>Button</h2>
|
|
<p>A push button with a text label. Fires onClick when pressed and released. Uses a two-phase press model: the button visually depresses on mouse-down and fires onClick only when the mouse is released while still inside the button bounds -- dragging the mouse off cancels the press. Supports accelerator keys via '&' prefix in the text (e.g. "&OK" underlines 'O' and binds Alt+O).</p>
|
|
<p>Disabled buttons use the classic "embossed" rendering (highlight text offset by +1,+1 and shadow text at 0,0) for a chiseled appearance.</p>
|
|
<p>Header: widgets/button.h</p>
|
|
<h3>Creation</h3>
|
|
<pre><code>WidgetT *btn = wgtButton(parent, "&OK");
|
|
btn->onClick = onOkClicked;</code></pre>
|
|
<h3>API Functions</h3>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
WidgetT *wgtButton(parent, text) Create a push button with the given label text. Pass NULL for no text. Text is copied into the widget.</pre>
|
|
<h3>API Struct (wgtRegisterApi "button")</h3>
|
|
<pre> Slot Function
|
|
---- --------
|
|
header WGT_API_HEADER (ABI version and size, checked by wgtGetApi)
|
|
create wgtButton</pre>
|
|
<h3>Properties</h3>
|
|
<p>Uses common WidgetT properties. Label text is managed via wgtSetText() / wgtGetText(). Set accelKey for keyboard shortcut (automatically parsed from '&' prefix in the text). The button is focusable (WCLASS_FOCUSABLE) and draws a focus rectangle when it holds keyboard focus.</p>
|
|
<p>No widget-specific properties are registered with the interface system. BASIC code sets the label via the generic "Caption" or "Text" property.</p>
|
|
<h3>Methods</h3>
|
|
<p>No widget-specific methods.</p>
|
|
<h3>Events</h3>
|
|
<pre> Callback Description
|
|
-------- -----------
|
|
onClick Fires when the button is clicked (mouse press and release inside, or Space/Enter when focused).</pre>
|
|
<h3>Default Event</h3>
|
|
<p>"Click" (VB basName: CommandButton, namePrefix: Command).</p>
|
|
</div>
|
|
<div class="topic" id="widget.canvas">
|
|
<h2>Canvas</h2>
|
|
<p>A freeform drawing surface with a fixed-size pixel buffer stored in the display's native pixel format. Provides drawing primitives (lines, rectangles, circles, text, individual pixels; coordinates may lie anywhere, drawing is clipped to the buffer) and supports loading BMP/PNG/JPEG/GIF files and saving BMP or PNG files (via dvxLoadImage/dvxSaveImage). Mouse interaction is available via a callback. The buffer is rendered to screen as a straight blit with no per-pixel conversion.</p>
|
|
<p>Canvas coordinates are independent of widget position: (0,0) is the top-left of the canvas content, not the widget. The widget frames the buffer with a 2-pixel sunken bevel.</p>
|
|
<p>Header: widgets/canvas.h</p>
|
|
<h3>Creation</h3>
|
|
<pre><code>WidgetT *cv = wgtCanvas(parent, 320, 200);</code></pre>
|
|
<h3>API Functions</h3>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
WidgetT *wgtCanvas(parent, w, h) Create a canvas with the given pixel dimensions. Buffer is initialized to white.
|
|
void wgtCanvasClear(w, color) Fill the entire canvas with a packed color.
|
|
void wgtCanvasSetPenColor(w, color) Set the drawing pen color (packed display color).
|
|
void wgtCanvasSetPenSize(w, size) Set the drawing pen size in pixels (affects DrawLine dot thickness). Clamped to 255; sizes below 1 are ignored.
|
|
void wgtCanvasDrawLine(w, x0, y0, x1, y1) Draw a line using Bresenham's algorithm with the current pen color and size.
|
|
void wgtCanvasDrawRect(w, x, y, width, height) Draw a 1-pixel rectangle outline using the current pen color.
|
|
void wgtCanvasFillRect(w, x, y, width, height) Fill a rectangle using the current pen color.
|
|
void wgtCanvasFillCircle(w, cx, cy, radius) Fill a circle using the current pen color (integer sqrt; no FPU needed).
|
|
void wgtCanvasSetPixel(w, x, y, color) Set a single pixel to the given packed color.
|
|
uint32_t wgtCanvasGetPixel(w, x, y) Read a single pixel. Returns packed color.
|
|
void wgtCanvasDrawText(w, x, y, text) Draw text at the given canvas coordinates using the current pen color.
|
|
void wgtCanvasSetMouseCallback(w, cb) Set a mouse callback. Signature: void (*cb)(WidgetT *w, int32_t cx, int32_t cy, bool drag). cx/cy are canvas-relative, drag=true on mouse-move during press.
|
|
int32_t wgtCanvasSave(w, path) Save the canvas buffer to an image file: BMP when path ends in .bmp, PNG otherwise. Returns 0 on success, -1 on error.
|
|
int32_t wgtCanvasLoad(w, path) Load an image file into the canvas (resizes buffer to match). Returns 0 on success, -1 on error.
|
|
void wgtCanvasResize(w, newW, newH) Resize the canvas buffer and relayout. The old content is discarded; the new buffer is white.</pre>
|
|
<h3>API Struct (wgtRegisterApi "canvas")</h3>
|
|
<pre> Slot Function
|
|
---- --------
|
|
header WGT_API_HEADER (ABI version and size, checked by wgtGetApi)
|
|
create wgtCanvas
|
|
clear wgtCanvasClear
|
|
setPenColor wgtCanvasSetPenColor
|
|
setPenSize wgtCanvasSetPenSize
|
|
setMouseCallback wgtCanvasSetMouseCallback
|
|
save wgtCanvasSave
|
|
load wgtCanvasLoad
|
|
drawLine wgtCanvasDrawLine
|
|
drawRect wgtCanvasDrawRect
|
|
fillRect wgtCanvasFillRect
|
|
fillCircle wgtCanvasFillCircle
|
|
setPixel wgtCanvasSetPixel
|
|
getPixel wgtCanvasGetPixel
|
|
drawText wgtCanvasDrawText
|
|
resize wgtCanvasResize</pre>
|
|
<h3>Properties</h3>
|
|
<p>Drawing state (pen color, pen size) is managed by the API functions above. The BASIC interface registers two read-only properties:</p>
|
|
<pre> Property Type Access Description
|
|
-------- ---- ------ -----------
|
|
ImageHeight Integer Read-only Height of the canvas buffer in pixels (0 when there is no buffer).
|
|
ImageWidth Integer Read-only Width of the canvas buffer in pixels (0 when there is no buffer).</pre>
|
|
<h3>Methods (BASIC Interface)</h3>
|
|
<pre> Method Description
|
|
------ -----------
|
|
Clear color% Fill the entire canvas with a 0x00RRGGBB color.
|
|
DrawLine x0%, y0%, x1%, y1%, color% Draw a line between two points.
|
|
DrawRect x%, y%, w%, h%, color% Draw a rectangle outline.
|
|
DrawText x%, y%, text$ Draw text at the given position.
|
|
FillCircle cx%, cy%, radius%, color% Fill a circle.
|
|
FillRect x%, y%, w%, h%, color% Fill a rectangle.
|
|
GetPixel(x%, y%) Returns the 0x00RRGGBB color at a pixel.
|
|
Load(path$) Load a BMP, PNG, JPEG or GIF image (the canvas takes its size). Returns 0 on success, -1 on error (WGT_SIG_RET_INT_STR).
|
|
Resize w%, h% Resize the canvas (the drawing is cleared to white).
|
|
Save(path$) Save the canvas: BMP when path$ ends in .bmp, PNG otherwise. Returns 0 on success, -1 on error (WGT_SIG_RET_INT_STR).
|
|
SetPenColor color% Set the drawing color (0x00RRGGBB).
|
|
SetPenSize size% Set the pen size in pixels (1 to 255).
|
|
SetPixel x%, y%, color% Set a single pixel.</pre>
|
|
<h3>Events</h3>
|
|
<pre> Callback Description
|
|
-------- -----------
|
|
onClick Fires when the canvas is clicked.
|
|
Mouse callback (via wgtCanvasSetMouseCallback) Fires on mouse down and drag with canvas-local coordinates.</pre>
|
|
<h3>Default Event</h3>
|
|
<p>"Click" (VB basName: PictureBox, namePrefix: Picture).</p>
|
|
</div>
|
|
<div class="topic" id="widget.checkbox">
|
|
<h2>Checkbox</h2>
|
|
<p>A toggle control with a text label. Clicking toggles between checked and unchecked states. Fires onChange each time the state flips. Supports accelerator keys (via '&' in the text) and keyboard toggle via Space or Enter when focused.</p>
|
|
<p>The check mark is drawn as an "X" pattern rather than a traditional checkmark glyph. The focus rectangle wraps the label text (not the box), matching the Windows 3.1 convention.</p>
|
|
<p>Header: widgets/checkbox.h</p>
|
|
<h3>Creation</h3>
|
|
<pre><code>WidgetT *cb = wgtCheckbox(parent, "Enable &logging");</code></pre>
|
|
<h3>API Functions</h3>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
WidgetT *wgtCheckbox(parent, text) Create a checkbox with the given label text.
|
|
bool wgtCheckboxIsChecked(w) Returns true if the checkbox is checked.
|
|
void wgtCheckboxSetChecked(w, checked) Set the checked state programmatically. Triggers a repaint but does NOT fire onChange.</pre>
|
|
<h3>API Struct (wgtRegisterApi "checkbox")</h3>
|
|
<pre> Slot Function
|
|
---- --------
|
|
header WGT_API_HEADER (ABI version and size, checked by wgtGetApi)
|
|
create wgtCheckbox
|
|
isChecked wgtCheckboxIsChecked
|
|
setChecked wgtCheckboxSetChecked</pre>
|
|
<h3>Properties (BASIC Interface)</h3>
|
|
<pre> Property Type Access Description
|
|
-------- ---- ------ -----------
|
|
Value Boolean Read/Write Whether the checkbox is checked.</pre>
|
|
<p>Label text is managed via the standard wgtSetText() / wgtGetText() interface. BASIC code uses the generic "Caption" or "Text" property.</p>
|
|
<h3>Methods</h3>
|
|
<p>No widget-specific methods.</p>
|
|
<h3>Events</h3>
|
|
<pre> Callback Description
|
|
-------- -----------
|
|
onClick Fires when clicked (after the toggle has already applied).
|
|
onChange Fires when the checked state changes (mouse click or keyboard toggle).</pre>
|
|
<h3>Default Event</h3>
|
|
<p>"Click" (VB basName: CheckBox).</p>
|
|
</div>
|
|
<div class="topic" id="widget.combobox">
|
|
<h2>ComboBox</h2>
|
|
<p>A combination of a single-line text input and a drop-down list. The user can type a value or select one from the list. When the user picks a list item, its text is copied into the edit buffer. Supports full text editing (cursor movement, selection, clipboard, undo) via the texthelp library and a popup overlay list via the listhelp library. Setting the text or picking a list item clears the undo history. Keyboard: Down, F4 or Alt+Down/Alt+Up opens the list; while it is open Down/Up move the highlight and Enter, F4 or Alt+Down/Alt+Up pick the item and close it (Esc closes without picking).</p>
|
|
<p>A read-only combo box (wgtSetReadOnly) ignores the editing keys (typing, Backspace, Delete, paste, cut, undo) but still allows cursor movement, selection, copy and picking an item from the list.</p>
|
|
<p>Depends on "texthelp" and "listhelp" helper libraries (declared in combobox.dep).</p>
|
|
<p>Header: widgets/comboBox.h</p>
|
|
<h3>Creation</h3>
|
|
<pre><code>WidgetT *cb = wgtComboBox(parent, 128);
|
|
wgtComboBoxAddItem(cb, "Arial");
|
|
wgtComboBoxAddItem(cb, "Courier");
|
|
wgtComboBoxAddItem(cb, "Times");</code></pre>
|
|
<h3>API Functions</h3>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
WidgetT *wgtComboBox(parent, maxLen) Create a combo box. maxLen is the maximum editable text length (0 => default 256).
|
|
void wgtComboBoxSetItems(w, items, count) Set the dropdown items from a const char ** array. Items are not copied -- caller owns them.
|
|
int32_t wgtComboBoxGetSelected(w) Get the index of the last selected item (-1 if the text was typed freely).
|
|
void wgtComboBoxSetSelected(w, idx) Select an item by index. Copies its text into the edit buffer.
|
|
void wgtComboBoxAddItem(w, text) Append an item to the owned list (strdup'd).
|
|
void wgtComboBoxRemoveItem(w, idx) Remove the item at idx. The selection stays on the same item; removing the selected item leaves no selection (the edit text is kept).
|
|
void wgtComboBoxClear(w) Remove all owned items and reset the selection.
|
|
const char *wgtComboBoxGetItem(w, idx) Get the text of an item by index.
|
|
int32_t wgtComboBoxGetItemCount(w) Get the total number of items.</pre>
|
|
<h3>API Struct (wgtRegisterApi "combobox")</h3>
|
|
<pre> Slot Function
|
|
---- --------
|
|
header WGT_API_HEADER (ABI version and size, checked by wgtGetApi)
|
|
create wgtComboBox
|
|
setItems wgtComboBoxSetItems
|
|
getSelected wgtComboBoxGetSelected
|
|
setSelected wgtComboBoxSetSelected
|
|
addItem wgtComboBoxAddItem
|
|
removeItem wgtComboBoxRemoveItem
|
|
clear wgtComboBoxClear
|
|
getItem wgtComboBoxGetItem
|
|
getItemCount wgtComboBoxGetItemCount</pre>
|
|
<h3>Properties (BASIC Interface)</h3>
|
|
<pre> Property Type Access Description
|
|
-------- ---- ------ -----------
|
|
ListIndex Integer Read/Write Index of the currently selected item (-1 if none).</pre>
|
|
<p>Editable text is accessible via the generic "Text" property. "ListCount" is available through the ListCount method.</p>
|
|
<h3>Methods (BASIC Interface)</h3>
|
|
<pre> Method Description
|
|
------ -----------
|
|
AddItem text$ Append an item to the dropdown list.
|
|
Clear Remove all items and clear the selection.
|
|
List(index%) Return the text of the item at the given index.
|
|
ListCount() Return the total number of items.
|
|
RemoveItem index% Remove the item at the given index. The selection stays on the same item; removing the selected item leaves no selection (the text is kept).</pre>
|
|
<h3>Events</h3>
|
|
<pre> Callback Description
|
|
-------- -----------
|
|
onChange Fires when the selection or text changes.</pre>
|
|
<h3>Default Event</h3>
|
|
<p>"Click" (VB basName: ComboBox).</p>
|
|
</div>
|
|
<div class="topic" id="widget.datactrl">
|
|
<h2>DataCtrl</h2>
|
|
<p>A VB3-style Data control for database binding. Displays a visible navigation bar (|< < > >|) that connects to a SQLite database via dvxSql* functions. Reads all rows from the RecordSource query into an in-memory cache for bidirectional navigation. Fires a Reposition event each time the current row changes so bound controls can update. Supports master-detail linking between Data controls (via MasterSource / MasterField / DetailField), INSERT via AddNew, and DELETE via Delete. UPDATE and DELETE identify the row by the KeyColumn value it had when it was loaded, so editing the key column changes the key instead of writing to another row; an UPDATE writes only the changed columns and fails (the row stays unsaved) when no database row matches. A key the quoted text match cannot find (an integer in a column without a declared type, a REAL, or NULL) is matched by its text form instead, as long as that names exactly one row. Setting a field to the value it already has is not an edit. With a read-only source, pending edits are dropped when the cursor moves. Refresh saves a pending edit first; if that save fails or is cancelled the edit is discarded. With a SELECT RecordSource the control is read-only: Update and Delete do nothing, and a master-detail filter is applied to the query as a subquery.</p>
|
|
<p>The widget depends on the "dvxsql" library DXE; the navigation logic caches the full result set so there is no round-trip for MoveNext/MovePrev.</p>
|
|
<p>Header: widgets/dataCtrl.h</p>
|
|
<h3>Creation</h3>
|
|
<pre><code>WidgetT *data = wgtDataCtrl(parent);</code></pre>
|
|
<p>Set properties (DatabaseName, RecordSource, KeyColumn, etc.) and then call dataCtrlRefresh to populate.</p>
|
|
<h3>API Functions</h3>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
WidgetT *dataCtrlCreate(parent) Create a Data control. Exposed as create in the API struct.
|
|
void dataCtrlRefresh(w) Re-execute the RecordSource query and rebuild the row cache.
|
|
void dataCtrlMoveFirst(w) Move the cursor to the first row.
|
|
void dataCtrlMovePrev(w) Move the cursor to the previous row.
|
|
void dataCtrlMoveNext(w) Move the cursor to the next row.
|
|
void dataCtrlMoveLast(w) Move the cursor to the last row.
|
|
const char *dataCtrlGetField(w, colName) Get the value of a column in the current row (case-insensitive).
|
|
void dataCtrlSetField(w, colName, value) Set the value of a column in the current row. Marks the row dirty unless the value is unchanged.
|
|
void dataCtrlUpdate(w) Flush pending changes (INSERT or UPDATE) to the database.
|
|
void dataCtrlAddNew(w) Append a blank row and move the cursor to it. The row is dirty and new until Update is called.
|
|
void dataCtrlDelete(w) Delete the current row from the cache and the database.
|
|
void dataCtrlSetMasterValue(w, val) Set the master-detail filter value. Normally managed by the form runtime.
|
|
int32_t dataCtrlGetRowCount(w) Get the total number of cached rows.
|
|
int32_t dataCtrlGetColCount(w) Get the number of columns in the result set.
|
|
const char *dataCtrlGetColName(w, col) Get the name of a column by index.
|
|
const char *dataCtrlGetCellText(w, row, col) Get the text of a specific cell.
|
|
void dataCtrlSetCurrentRow(w, row) Set the current row by index. Auto-saves the previous row if dirty.
|
|
int32_t dataCtrlGetCurrentRow(w) Get the current row index (-1 if there are no rows).
|
|
uint32_t dataCtrlGetGeneration(w) A counter that changes whenever the cached rows or cell values change (Refresh, AddNew, Delete, SetField) but not on cursor moves. Bound views use it to rebuild derived state only when needed.</pre>
|
|
<h3>API Struct (wgtRegisterApi "data")</h3>
|
|
<pre> Slot Function
|
|
---- --------
|
|
header WGT_API_HEADER (ABI version and size, checked by wgtGetApi)
|
|
create dataCtrlCreate
|
|
refresh dataCtrlRefresh
|
|
moveFirst dataCtrlMoveFirst
|
|
movePrev dataCtrlMovePrev
|
|
moveNext dataCtrlMoveNext
|
|
moveLast dataCtrlMoveLast
|
|
getField dataCtrlGetField
|
|
setField dataCtrlSetField
|
|
updateRow dataCtrlUpdate (same as update; kept so later slots keep their positions)
|
|
update dataCtrlUpdate
|
|
addNew dataCtrlAddNew
|
|
delete dataCtrlDelete
|
|
setMasterValue dataCtrlSetMasterValue
|
|
getRowCount dataCtrlGetRowCount
|
|
getColCount dataCtrlGetColCount
|
|
getColName dataCtrlGetColName
|
|
getCellText dataCtrlGetCellText
|
|
setCurrentRow dataCtrlSetCurrentRow
|
|
getCurrentRow dataCtrlGetCurrentRow
|
|
getGeneration dataCtrlGetGeneration</pre>
|
|
<h3>Properties (BASIC Interface)</h3>
|
|
<pre> Property Type Access Description
|
|
-------- ---- ------ -----------
|
|
DatabaseName String Read/Write Path to the SQLite database file.
|
|
RecordSource String Read/Write SQL query or table name for the result set.
|
|
KeyColumn String Read/Write Primary key column name (used for UPDATE/DELETE).
|
|
MasterSource String Read/Write Name of the master Data control for master-detail linking.
|
|
MasterField String Read/Write Column in the master control to read for the filter value.
|
|
DetailField String Read/Write Column in this table to filter by the master value.
|
|
Caption String Read/Write Text displayed on the navigator bar.
|
|
BOF Boolean Read-only True when the cursor is at the first row (or no rows).
|
|
EOF Boolean Read-only True when the cursor is past the last row (or no rows).</pre>
|
|
<h3>Methods (BASIC Interface)</h3>
|
|
<pre> Method Description
|
|
------ -----------
|
|
AddNew Append a blank row and move to it.
|
|
Delete Delete the current row.
|
|
MoveFirst Move the cursor to the first row.
|
|
MoveLast Move the cursor to the last row.
|
|
MoveNext Move the cursor to the next row.
|
|
MovePrevious Move the cursor to the previous row.
|
|
Refresh Save a pending edit, then re-execute the query and rebuild the cache.
|
|
Update Write pending changes to the database.</pre>
|
|
<h3>Events</h3>
|
|
<pre> Event Description
|
|
----- -----------
|
|
Reposition Fires when the current row changes. Default event.
|
|
Validate Fires before writing. Return false to cancel the write.</pre>
|
|
<p>While the Validate handler runs, the moves (MoveFirst/MoveLast/MoveNext/MovePrevious, dataCtrlSetCurrentRow), AddNew, Refresh and Update on the same control are refused and return without doing anything: each would save, and therefore validate, the same row again. Delete is still allowed; Update re-checks the row after the handler returns.</p>
|
|
</div>
|
|
<div class="topic" id="widget.dbgrid">
|
|
<h2>DbGrid</h2>
|
|
<p>A database grid widget that displays all records from a Data control in a scrollable, sortable table. Columns auto-populate from the Data control's column names and can be hidden, resized (by dragging a column border), and renamed by the application. Clicking a column header sorts the display ascending/descending. The highlighted row is always the Data control's current row: selecting a row moves the Data control's cursor, and the highlight is read from the cursor whenever the grid paints. The Data control does not repaint the grid itself, so a C application that moves the cursor elsewhere (the Data control's own buttons or API) calls dbGridRefresh (or wgtInvalidatePaint on the grid) from the Data control's onChange; the form runtime does this for BASIC programs. The grid reads directly from the Data control's cached rows, so there is no separate copy of the data. Re-binding the same Data control (as the form runtime does on every Reposition) keeps the sort order and column settings while the column set is unchanged (the sort order and automatic widths are rebuilt when the Data control's rows or values changed), and a width set by dragging a column border is kept like one set with dbGridSetColumnWidth. Cell and header text is clipped to its column.</p>
|
|
<p>The grid supports alternating row shading, vertical and horizontal scrollbars, keyboard navigation (arrows, Page Up/Down, Home, End), and double-click activation (fires onDblClick). The mouse wheel scrolls the view without moving the record cursor.</p>
|
|
<p>Header: widgets/dbGrid.h</p>
|
|
<h3>Creation</h3>
|
|
<pre><code>WidgetT *grid = wgtDbGrid(parent);
|
|
dbGridSetDataWidget(grid, dataCtrl);</code></pre>
|
|
<h3>API Functions</h3>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
WidgetT *dbGridCreate(parent) Create a database grid widget. Exposed as create in the API struct.
|
|
void dbGridSetDataWidget(w, dataWidget) Bind the grid to a Data control. Auto-populates columns from the Data control's column names.
|
|
void dbGridRefresh(w) Re-read the Data control's state and repaint the grid. The sort index and automatic column widths are rebuilt only when the Data control's rows or values changed since the last build; a cursor move just repaints.
|
|
void dbGridSetColumnVisible(w, fieldName, visible) Show or hide a column by field name.
|
|
void dbGridSetColumnHeader(w, fieldName, header) Set a display header for a column (overrides the field name).
|
|
void dbGridSetColumnWidth(w, fieldName, width) Set a column's width (tagged size, 0 = auto).
|
|
int32_t dbGridGetSelectedRow(w) Get the selected data row: the bound Data control's current row (-1 if unbound or no rows).
|
|
void dbGridSetSelectedRow(w, row) Move the bound Data control's cursor to a data row (clamped to the row range). A negative row is ignored.</pre>
|
|
<h3>API Struct (wgtRegisterApi "dbgrid")</h3>
|
|
<pre> Slot Function
|
|
---- --------
|
|
header WGT_API_HEADER (ABI version and size, checked by wgtGetApi)
|
|
create dbGridCreate
|
|
setDataWidget dbGridSetDataWidget
|
|
refresh dbGridRefresh
|
|
setColumnVisible dbGridSetColumnVisible
|
|
setColumnHeader dbGridSetColumnHeader
|
|
setColumnWidth dbGridSetColumnWidth
|
|
getSelectedRow dbGridGetSelectedRow
|
|
setSelectedRow dbGridSetSelectedRow</pre>
|
|
<h3>Properties (BASIC Interface)</h3>
|
|
<pre> Property Type Access Description
|
|
-------- ---- ------ -----------
|
|
GridLines Boolean Read/Write Whether to draw grid lines between cells (default: true).
|
|
SelectedRow Integer Read/Write The bound Data control's current row (-1 if none). Setting it moves the Data cursor.</pre>
|
|
<h3>Methods (BASIC Interface)</h3>
|
|
<pre> Method Description
|
|
------ -----------
|
|
Refresh Re-read the Data control and repaint.</pre>
|
|
<h3>Events</h3>
|
|
<pre> Event Description
|
|
----- -----------
|
|
Click Fires when a row is clicked (selection change).
|
|
DblClick Fires when a row is double-clicked. Default event.</pre>
|
|
<h3>Default Event</h3>
|
|
<p>"DblClick" (VB basName: DBGrid, namePrefix: DBGrid).</p>
|
|
</div>
|
|
<div class="topic" id="widget.dropdown">
|
|
<h2>Dropdown</h2>
|
|
<p>A drop-down list that displays a single selected item and expands to a popup list when clicked. Unlike ComboBox, the user cannot type free text -- only list items can be selected. Keyboard: while closed, Down/Up change the selection and type-ahead picks the next item starting with a printable key; Space, Enter, F4 or Alt+Down/Alt+Up open the list. While open, Down/Up move the highlight and Enter, Space, F4 or Alt+Down/Alt+Up select it and close the list (Esc closes without selecting). Ctrl and Alt chords never change the selection. The popup overlay is rendered above all other widgets via the widget paint overlay mechanism.</p>
|
|
<p>Depends on the "listhelp" helper library (declared in dropdown.dep).</p>
|
|
<p>Header: widgets/dropdown.h</p>
|
|
<h3>Creation</h3>
|
|
<pre><code>WidgetT *dd = wgtDropdown(parent);
|
|
wgtDropdownAddItem(dd, "Red");
|
|
wgtDropdownAddItem(dd, "Green");
|
|
wgtDropdownAddItem(dd, "Blue");</code></pre>
|
|
<h3>API Functions</h3>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
WidgetT *wgtDropdown(parent) Create a dropdown list.
|
|
void wgtDropdownSetItems(w, items, count) Set the list from a caller-owned const char ** array.
|
|
int32_t wgtDropdownGetSelected(w) Get the index of the selected item (-1 if none).
|
|
void wgtDropdownSetSelected(w, idx) Set the selected item by index.
|
|
void wgtDropdownAddItem(w, text) Append an owned item (strdup'd).
|
|
void wgtDropdownRemoveItem(w, idx) Remove the item at idx. The selection stays on the same item; removing the selected item leaves no selection.
|
|
void wgtDropdownClear(w) Remove all owned items.
|
|
const char *wgtDropdownGetItem(w, idx) Get the text of an item by index.
|
|
int32_t wgtDropdownGetItemCount(w) Get the total number of items.</pre>
|
|
<h3>API Struct (wgtRegisterApi "dropdown")</h3>
|
|
<pre> Slot Function
|
|
---- --------
|
|
header WGT_API_HEADER (ABI version and size, checked by wgtGetApi)
|
|
create wgtDropdown
|
|
setItems wgtDropdownSetItems
|
|
getSelected wgtDropdownGetSelected
|
|
setSelected wgtDropdownSetSelected
|
|
addItem wgtDropdownAddItem
|
|
removeItem wgtDropdownRemoveItem
|
|
clear wgtDropdownClear
|
|
getItem wgtDropdownGetItem
|
|
getItemCount wgtDropdownGetItemCount</pre>
|
|
<h3>Properties (BASIC Interface)</h3>
|
|
<pre> Property Type Access Description
|
|
-------- ---- ------ -----------
|
|
ListIndex Integer Read/Write Index of the currently selected item (-1 if none).</pre>
|
|
<h3>Methods (BASIC Interface)</h3>
|
|
<pre> Method Description
|
|
------ -----------
|
|
AddItem text$ Append an item to the drop-down list.
|
|
Clear Remove all items.
|
|
List(index%) Return the text of the item at the given index.
|
|
ListCount() Return the total number of items.
|
|
RemoveItem index% Remove the item at the given index. The selection stays on the same item; removing the selected item leaves no selection.</pre>
|
|
<h3>Events</h3>
|
|
<pre> Callback Description
|
|
-------- -----------
|
|
onChange Fires when the selected item changes.</pre>
|
|
<h3>Default Event</h3>
|
|
<p>"Click" (VB basName: DropDown).</p>
|
|
</div>
|
|
<div class="topic" id="widget.imagebutton">
|
|
<h2>ImageButton</h2>
|
|
<p>A clickable button that displays an image instead of text. Has the same press/release visual feedback and focus handling as a regular Button. Can be created from raw pixel data or loaded from an image file. Typically used to populate a Toolbar.</p>
|
|
<p>Header: widgets/imgBtn.h</p>
|
|
<h3>Creation</h3>
|
|
<pre><code>WidgetT *btn = wgtImageButtonFromFile(parent, "icons/save.bmp");
|
|
btn->onClick = onSaveClicked;</code></pre>
|
|
<h3>API Functions</h3>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
WidgetT *wgtImageButton(parent, data, w, h, pitch) Create from raw pixel data (in display's native format).
|
|
WidgetT *wgtImageButtonFromFile(parent, path) Create by loading an image file.
|
|
void wgtImageButtonSetData(w, data, imgW, imgH, pitch) Replace the image with new raw pixel data.
|
|
void wgtImageButtonLoadFile(w, path) Replace the image by loading a new file.</pre>
|
|
<h3>API Struct (wgtRegisterApi "imagebutton")</h3>
|
|
<pre> Slot Function
|
|
---- --------
|
|
header WGT_API_HEADER (ABI version and size, checked by wgtGetApi)
|
|
create wgtImageButton
|
|
fromFile wgtImageButtonFromFile
|
|
setData wgtImageButtonSetData
|
|
loadFile wgtImageButtonLoadFile</pre>
|
|
<h3>Properties (BASIC Interface)</h3>
|
|
<pre> Property Type Access Description
|
|
-------- ---- ------ -----------
|
|
Picture String Read/Write Setting writes a file path (loads image). Reading returns the path of the loaded file (also set by wgtImageButtonFromFile); wgtImageButtonSetData clears it. Setting "" clears the image and the path; a file that cannot be loaded leaves both unchanged.
|
|
ImageWidth Integer Read-only Width of the currently loaded image in pixels.
|
|
ImageHeight Integer Read-only Height of the currently loaded image in pixels.</pre>
|
|
<h3>Methods</h3>
|
|
<p>No widget-specific BASIC methods. Setting Picture loads a new image.</p>
|
|
<h3>Events</h3>
|
|
<pre> Callback Description
|
|
-------- -----------
|
|
onClick Fires when the image button is clicked (press and release inside).</pre>
|
|
<h3>Default Event</h3>
|
|
<p>"Click" (VB basName: ImageButton).</p>
|
|
</div>
|
|
<div class="topic" id="widget.image">
|
|
<h2>Image</h2>
|
|
<p>Displays a bitmap image. Can be created from raw pixel data or loaded from a file on disk (BMP/PNG/etc. via dvxLoadImage). Supports optional scaling down to fit the widget and transparent color keys for chroma-key style masking (also when scaled). A disabled image is drawn grayscale; its transparent color key still applies.</p>
|
|
<p>Header: widgets/image.h</p>
|
|
<h3>Creation</h3>
|
|
<pre><code>WidgetT *img = wgtImageFromFile(parent, "logo.bmp");
|
|
// or from raw pixels:
|
|
// WidgetT *img = wgtImage(parent, pixelData, w, h, pitch);</code></pre>
|
|
<h3>API Functions</h3>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
WidgetT *wgtImage(parent, data, w, h, pitch) Create from raw pixel data (in display's native format).
|
|
WidgetT *wgtImageFromFile(parent, path) Create by loading an image file.
|
|
void wgtImageSetData(w, data, imgW, imgH, pitch) Replace the displayed image with new raw pixel data.
|
|
void wgtImageLoadFile(w, path) Replace by loading a new file. Static but exposed via the API struct.
|
|
void wgtImageSetTransparent(w, hasTransparency, keyColor) Set chroma-key color. Pixels matching keyColor are not drawn.</pre>
|
|
<h3>API Struct (wgtRegisterApi "image")</h3>
|
|
<pre> Slot Function
|
|
---- --------
|
|
header WGT_API_HEADER (ABI version and size, checked by wgtGetApi)
|
|
create wgtImage
|
|
fromFile wgtImageFromFile
|
|
setData wgtImageSetData
|
|
loadFile wgtImageLoadFile
|
|
setTransparent wgtImageSetTransparent</pre>
|
|
<h3>Properties (BASIC Interface)</h3>
|
|
<pre> Property Type Access Description
|
|
-------- ---- ------ -----------
|
|
Picture String Read/Write Setting writes a file path (loads image). Reading returns the path of the loaded file (also set by wgtImageFromFile); wgtImageSetData clears it. Setting "" clears the image and the path; a file that cannot be loaded leaves both unchanged.
|
|
Stretch Boolean Read/Write When true, an image larger than the widget is scaled down to fit (aspect preserved, nearest neighbour); a smaller image stays at natural size. When false, shown at natural size. Changing it relayouts the widget.
|
|
ImageWidth Integer Read-only Width of the currently loaded image in pixels.
|
|
ImageHeight Integer Read-only Height of the currently loaded image in pixels.</pre>
|
|
<h3>Methods</h3>
|
|
<p>No widget-specific BASIC methods. Setting Picture loads a new image.</p>
|
|
<h3>Events</h3>
|
|
<pre> Callback Description
|
|
-------- -----------
|
|
onClick Fires when the image is clicked.</pre>
|
|
<h3>Default Event</h3>
|
|
<p>"Click" (VB basName: Image).</p>
|
|
</div>
|
|
<div class="topic" id="widget.label">
|
|
<h2>Label</h2>
|
|
<p>A static text label. Does not accept keyboard focus directly, but supports accelerator keys that forward focus to the next focusable sibling (WCLASS_FOCUS_FORWARD). Useful for describing other widgets. Supports left, center, and right text alignment. The label fills its rectangle before drawing: with its own bgColor if set, else the nearest ancestor's bgColor, else the face of a Toolbar it sits in, else the scheme's content background.</p>
|
|
<p>Header: widgets/label.h</p>
|
|
<h3>Creation</h3>
|
|
<pre><code>WidgetT *lbl = wgtLabel(parent, "&Name:");</code></pre>
|
|
<h3>API Functions</h3>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
WidgetT *wgtLabel(parent, text) Create a text label.
|
|
void wgtLabelSetAlign(w, align) Set the text alignment (AlignStartE = left, AlignCenterE = center, AlignEndE = right) and repaint. Out-of-range values are ignored.
|
|
WidgetAlignE wgtLabelGetAlign(w) Get the current text alignment.</pre>
|
|
<h3>API Struct (wgtRegisterApi "label")</h3>
|
|
<pre> Slot Function
|
|
---- --------
|
|
header WGT_API_HEADER (ABI version and size, checked by wgtGetApi)
|
|
create wgtLabel
|
|
setAlign wgtLabelSetAlign</pre>
|
|
<h3>Properties (BASIC Interface)</h3>
|
|
<pre> Property Type Access Description
|
|
-------- ---- ------ -----------
|
|
Alignment Enum (Left, Center, Right) Read/Write Text alignment within the label.</pre>
|
|
<p>Label text is managed via the standard wgtSetText() / wgtGetText() interface. BASIC code uses the generic "Caption" or "Text" property. Place an '&' before a character in the caption to mark an accelerator key.</p>
|
|
<h3>Methods</h3>
|
|
<p>No widget-specific methods.</p>
|
|
<h3>Events</h3>
|
|
<p>Labels are not focusable, so they typically have no event callbacks. The common onClick callback will fire if the label is clicked.</p>
|
|
<h3>Default Event</h3>
|
|
<p>"Click" (VB basName: Label).</p>
|
|
</div>
|
|
<div class="topic" id="widget.listbox">
|
|
<h2>ListBox</h2>
|
|
<p>A scrollable list of text items. Supports single and multi-select modes (Shift-click for range, Ctrl-click to toggle), drag-to-reorder, mouse wheel scroll, and keyboard navigation (Up/Down/PgUp/PgDn/Home/End with type-ahead search on printable keys). Depends on the "listhelp" helper library (declared in listbox.dep).</p>
|
|
<p>Items can be set from a caller-owned array (wgtListBoxSetItems, which keeps a pointer to it) or managed with AddItem/RemoveItem/Clear. AddItem and RemoveItem on a list set from an array first copy that array, so they extend or shorten the caller's list without modifying it; drag-to-reorder also works on the copy.</p>
|
|
<p>Header: widgets/listBox.h</p>
|
|
<h3>Creation</h3>
|
|
<pre><code>WidgetT *lb = wgtListBox(parent);
|
|
wgtListBoxAddItem(lb, "Alpha");
|
|
wgtListBoxAddItem(lb, "Beta");
|
|
wgtListBoxAddItem(lb, "Gamma");</code></pre>
|
|
<h3>API Functions</h3>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
WidgetT *wgtListBox(parent) Create a list box.
|
|
void wgtListBoxSetItems(w, items, count) Set items from a caller-owned const char ** array.
|
|
int32_t wgtListBoxGetSelected(w) Get the primary selected index (-1 if none).
|
|
void wgtListBoxSetSelected(w, idx) Set the selected index. Scrolls the item into view.
|
|
void wgtListBoxSetMultiSelect(w, multi) Enable or disable multi-select mode.
|
|
bool wgtListBoxIsItemSelected(w, idx) Returns true if the item is selected (multi-select mode).
|
|
void wgtListBoxSetItemSelected(w, idx, selected) Select or deselect a specific item (multi-select mode).
|
|
void wgtListBoxSelectAll(w) Select every item (multi-select mode).
|
|
void wgtListBoxClearSelection(w) Deselect all items.
|
|
void wgtListBoxSetReorderable(w, r) Enable drag-to-reorder of items.
|
|
void wgtListBoxAddItem(w, text) Append an owned item (strdup'd).
|
|
void wgtListBoxRemoveItem(w, idx) Remove the item at idx. Items after it keep their selection; removing the current item leaves no current item.
|
|
void wgtListBoxClear(w) Remove all owned items.
|
|
const char *wgtListBoxGetItem(w, idx) Get the text of an item by index.
|
|
int32_t wgtListBoxGetItemCount(w) Get the total number of items.</pre>
|
|
<h3>API Struct (wgtRegisterApi "listbox")</h3>
|
|
<pre> Slot Function
|
|
---- --------
|
|
header WGT_API_HEADER (ABI version and size, checked by wgtGetApi)
|
|
create wgtListBox
|
|
setItems wgtListBoxSetItems
|
|
getSelected wgtListBoxGetSelected
|
|
setSelected wgtListBoxSetSelected
|
|
setMultiSelect wgtListBoxSetMultiSelect
|
|
isItemSelected wgtListBoxIsItemSelected
|
|
setItemSelected wgtListBoxSetItemSelected
|
|
selectAll wgtListBoxSelectAll
|
|
clearSelection wgtListBoxClearSelection
|
|
setReorderable wgtListBoxSetReorderable
|
|
addItem wgtListBoxAddItem
|
|
removeItem wgtListBoxRemoveItem
|
|
clear wgtListBoxClear
|
|
getItem wgtListBoxGetItem
|
|
getItemCount wgtListBoxGetItemCount</pre>
|
|
<h3>Properties (BASIC Interface)</h3>
|
|
<pre> Property Type Access Description
|
|
-------- ---- ------ -----------
|
|
ListIndex Integer Read/Write Index of the currently selected item.</pre>
|
|
<h3>Methods (BASIC Interface)</h3>
|
|
<pre> Method Description
|
|
------ -----------
|
|
AddItem text$ Append an item.
|
|
Clear Remove all items.
|
|
ClearSelection Deselect all items.
|
|
IsItemSelected(index%) Returns True if the item is selected (multi-select mode).
|
|
List(index%) Return the text of the item at the given index.
|
|
ListCount() Return the total number of items.
|
|
RemoveItem index% Remove the item at the given index. Items after it keep their selection; removing the selected item leaves no selection.
|
|
SelectAll Select all items.
|
|
SetItemSelected index%, sel Select or deselect a specific item.
|
|
SetItems spec$ Bulk-load items from a pipe-delimited string (e.g. "Red|Green|Blue"); replaces existing items. Every "|" separates two items ("a|" is "a" and an empty item); "" empties the list.
|
|
SetMultiSelect multi Enable or disable multi-select mode.
|
|
SetReorderable r Enable or disable drag-to-reorder.</pre>
|
|
<h3>Events</h3>
|
|
<pre> Callback Description
|
|
-------- -----------
|
|
onClick Fires when an item is clicked.
|
|
onDblClick Fires when an item is double-clicked.
|
|
onChange Fires when the selection changes.</pre>
|
|
<h3>Default Event</h3>
|
|
<p>"Click" (VB basName: ListBox).</p>
|
|
</div>
|
|
<div class="topic" id="widget.listview">
|
|
<h2>ListView</h2>
|
|
<p>A multi-column list with sortable headers. Supports single and multi-selection, column alignment, header click sorting, column resizing (drag a header border; double-click it to fit the contents), and drag-to-reorder. Data is provided as a flat array of strings (row-major order, one string per cell). wgtListViewSetData keeps a pointer to the caller's array; AddItem, RemoveRow, Clear, SetCell and drag-to-reorder work on a copy the widget makes first, so the caller's array is never modified.</p>
|
|
<p>Sorting and drag-to-reorder are exclusive: enabling reorder clears the sort, and while reorder is enabled wgtListViewSetSort and header clicks do not sort. A column width set by dragging or double-clicking a header border is kept when the data changes, until new columns are set.</p>
|
|
<p>Header: widgets/listView.h</p>
|
|
<h3>Creation</h3>
|
|
<pre><code>WidgetT *lv = wgtListView(parent);
|
|
ListViewColT cols[] = {
|
|
{ "Name", wgtChars(20), ListViewAlignLeftE },
|
|
{ "Size", wgtChars(10), ListViewAlignRightE }
|
|
};
|
|
wgtListViewSetColumns(lv, cols, 2);
|
|
const char *cells[] = { "file.txt", "1234", "readme.md", "5678" };
|
|
wgtListViewSetData(lv, cells, 2);</code></pre>
|
|
<h3>Column Definition</h3>
|
|
<pre><code>typedef struct {
|
|
const char *title;
|
|
int32_t width; // tagged size (wgtPixels/wgtChars/wgtPercent, 0 = auto)
|
|
ListViewAlignE align; // ListViewAlignLeftE, ListViewAlignCenterE, ListViewAlignRightE
|
|
} ListViewColT;</code></pre>
|
|
<h3>Sort Direction</h3>
|
|
<pre><code>typedef enum {
|
|
ListViewSortNoneE,
|
|
ListViewSortAscE,
|
|
ListViewSortDescE
|
|
} ListViewSortE;</code></pre>
|
|
<h3>Macros</h3>
|
|
<pre> Macro Description
|
|
----- -----------
|
|
wgtListView(parent) Create a list view.
|
|
wgtListViewSetColumns(w, cols, count) Define columns from an array of ListViewColT.
|
|
wgtListViewSetData(w, cellData, rowCount) Set row data. cellData is a flat const char ** array of size rowCount * colCount.
|
|
wgtListViewGetSelected(w) Get the index of the selected row (-1 if none).
|
|
wgtListViewSetSelected(w, idx) Set the selected row by index (outside the list = none) and scroll it into view.
|
|
wgtListViewSetSort(w, col, dir) Set the sort column and direction. A column outside the defined columns, or ListViewSortNoneE, clears the sort. Ignored while reorder is enabled.
|
|
wgtListViewSetHeaderClickCallback(w, cb) Set a callback for header clicks. Signature: void (*cb)(WidgetT *w, int32_t col, ListViewSortE dir).
|
|
wgtListViewSetMultiSelect(w, multi) Enable or disable multi-selection.
|
|
wgtListViewIsItemSelected(w, idx) Check if a specific row is selected.
|
|
wgtListViewSetItemSelected(w, idx, selected) Select or deselect a specific row.
|
|
wgtListViewSelectAll(w) Select all rows.
|
|
wgtListViewClearSelection(w) Deselect all rows.
|
|
wgtListViewSetReorderable(w, reorderable) Enable drag-to-reorder of rows.</pre>
|
|
<h3>Events</h3>
|
|
<pre> Callback Description
|
|
-------- -----------
|
|
onClick Fires when a row is clicked.
|
|
onDblClick Fires when a row is double-clicked.
|
|
onChange Fires when the selection changes.</pre>
|
|
<h3>Properties (BASIC Interface)</h3>
|
|
<pre> Property Type Access Description
|
|
-------- ---- ------ -----------
|
|
ListIndex Integer Read/Write Index of the currently selected row.</pre>
|
|
<h3>Methods (BASIC Interface)</h3>
|
|
<pre> Method Description
|
|
------ -----------
|
|
AddItem text$ Add a row (sets first column text).
|
|
Clear Remove all rows.
|
|
ClearSelection Deselect all rows.
|
|
GetCell(row%, col%) Returns the text of a cell.
|
|
IsItemSelected(index%) Check if a specific row is selected.
|
|
RemoveItem index% Remove a row by index. Rows after it keep their selection; removing the selected row leaves no selection.
|
|
RowCount() Returns the number of rows.
|
|
SelectAll Select all rows.
|
|
SetCell row%, col%, text$ Set the text of a cell.
|
|
SetColumns spec$ Define columns from a pipe-delimited spec string ("Name,Width|Name,Width").
|
|
SetItemSelected idx%, sel Select or deselect a specific row by index.
|
|
SetMultiSelect multi Enable or disable multi-selection.
|
|
SetReorderable reorderable Enable or disable drag-to-reorder.
|
|
SetSort col%, dir% Set the sort column and direction (0=none, 1=ascending, 2=descending). An invalid column or direction clears the sort; ignored while reorder is enabled.</pre>
|
|
<h3>Default Event</h3>
|
|
<p>"Click" (VB basName: ListView).</p>
|
|
</div>
|
|
<div class="topic" id="widget.progressbar">
|
|
<h2>ProgressBar</h2>
|
|
<p>A visual indicator of progress, displayed as a filled bar. Supports both horizontal and vertical orientations. Value ranges from 0 to 100.</p>
|
|
<p>Header: widgets/progress.h</p>
|
|
<h3>Creation</h3>
|
|
<pre> Macro Description
|
|
----- -----------
|
|
wgtProgressBar(parent) Create a horizontal progress bar.
|
|
wgtProgressBarV(parent) Create a vertical progress bar.</pre>
|
|
<h3>API Functions</h3>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
WidgetT *wgtProgressBar(parent) Create a horizontal progress bar.
|
|
WidgetT *wgtProgressBarV(parent) Create a vertical progress bar.
|
|
void wgtProgressBarSetValue(w, val) Set the progress value (0-100). Clamped to 0..maxValue.
|
|
int32_t wgtProgressBarGetValue(w) Get the current progress value.</pre>
|
|
<h3>API Struct (wgtRegisterApi "progressbar")</h3>
|
|
<pre> Slot Function
|
|
---- --------
|
|
header WGT_API_HEADER (ABI version and size, checked by wgtGetApi)
|
|
create wgtProgressBar
|
|
createV wgtProgressBarV
|
|
setValue wgtProgressBarSetValue
|
|
getValue wgtProgressBarGetValue</pre>
|
|
<h3>Properties (BASIC Interface)</h3>
|
|
<pre> Property Type Access Description
|
|
-------- ---- ------ -----------
|
|
Value Integer Read/Write Current progress value (0-100).</pre>
|
|
<h3>Methods</h3>
|
|
<p>No widget-specific methods.</p>
|
|
<h3>Events</h3>
|
|
<p>ProgressBar is a display-only widget. Typically no callbacks are set.</p>
|
|
<h3>Default Event</h3>
|
|
<p>None. (VB basName: ProgressBar.)</p>
|
|
</div>
|
|
<div class="topic" id="widget.radio">
|
|
<h2>Radio Button</h2>
|
|
<p>A mutually exclusive selection control. Radio buttons are normally placed inside a radio group container, which holds the selected index. Radio buttons placed directly in any other container (as BASIC forms do) form a group with their radio siblings, each button keeping its own selected state. Only one radio button within a group can be selected at a time. A button's index is its position among the radio buttons of its container, so it stays correct when buttons are added or destroyed.</p>
|
|
<p>Header: widgets/radio.h</p>
|
|
<h3>Creation</h3>
|
|
<pre><code>WidgetT *grp = wgtRadioGroup(parent);
|
|
WidgetT *r1 = wgtRadio(grp, "Option A");
|
|
WidgetT *r2 = wgtRadio(grp, "Option B");</code></pre>
|
|
<h3>API Functions</h3>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
WidgetT *wgtRadioGroup(parent) Create a radio group container.
|
|
WidgetT *wgtRadio(parent, text) Create a radio button inside a group.
|
|
void wgtRadioGroupSetSelected(group, idx) Set the selected radio button by index within the group.
|
|
int32_t wgtRadioGroupGetSelected(group) Index of the selected radio button among group's radio children (group may be a RadioGroup or any container), or -1 when none is selected.
|
|
int32_t wgtRadioGetIndex(w) Get the index of this radio button within its group.
|
|
bool wgtRadioIsSelected(w) True when this radio button is the selected one.
|
|
void wgtRadioSetSelected(w, selected) Select this button (clearing its siblings), or deselect it leaving none selected. Fires no event.</pre>
|
|
<p>wgtRadioGroupSetSelected and wgtRadioSetSelected repaint the affected buttons and do not fire onChange. Arrow-key navigation moves focus through the standard focus path, so the blur/focus callbacks fire.</p>
|
|
<h3>API Struct (wgtRegisterApi "radio")</h3>
|
|
<pre> Slot Function
|
|
---- --------
|
|
header WGT_API_HEADER (ABI version and size, checked by wgtGetApi)
|
|
group wgtRadioGroup
|
|
create wgtRadio
|
|
groupSetSelected wgtRadioGroupSetSelected
|
|
getIndex wgtRadioGetIndex
|
|
isSelected wgtRadioIsSelected
|
|
setSelected wgtRadioSetSelected
|
|
groupGetSelected wgtRadioGroupGetSelected</pre>
|
|
<h3>Properties (BASIC Interface)</h3>
|
|
<pre> Property Type Access Description
|
|
-------- ---- ------ -----------
|
|
Value Boolean Read/Write True when this button is selected. Setting True selects it and clears its siblings; setting False deselects it.
|
|
OptionIndex Integer Read-only Index of this radio button within its group.</pre>
|
|
<p>Caption is managed via the standard wgtSetText() / wgtGetText() interface (WCLASS_HAS_TEXT).</p>
|
|
<h3>Methods (BASIC Interface)</h3>
|
|
<pre> Method Description
|
|
------ -----------
|
|
SetSelected idx% Select the radio button at index idx% within this button's group (called on a group, within that group).</pre>
|
|
<h3>Events</h3>
|
|
<pre> Callback Description
|
|
-------- -----------
|
|
onClick Fires on the radio button when clicked.
|
|
onChange Fires when the selection changes.</pre>
|
|
<h3>Default Event</h3>
|
|
<p>"Click" (VB basName: OptionButton, namePrefix: Option).</p>
|
|
</div>
|
|
<div class="topic" id="widget.scrollpane">
|
|
<h2>ScrollPane</h2>
|
|
<p>A scrollable container that provides vertical and/or horizontal scrollbars when its content exceeds the visible area. Place a single child (typically a VBox or HBox) inside the scroll pane. A click on content that does not handle the mouse itself (a label, an image or empty space) gives the scroll pane the keyboard focus, so the arrow and page keys scroll it.</p>
|
|
<p>Header: widgets/scrlPane.h</p>
|
|
<h3>Creation</h3>
|
|
<pre><code>WidgetT *sp = wgtScrollPane(parent);
|
|
WidgetT *content = wgtVBox(sp);
|
|
// add children to content...</code></pre>
|
|
<h3>API Functions</h3>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
WidgetT *wgtScrollPane(parent) Create a scroll pane container.
|
|
void wgtScrollPaneScrollToChild(sp, child) Scroll so that the given child widget is visible.
|
|
void wgtScrollPaneScrollToTop(w) Scroll back to the top-left of the content.
|
|
void wgtScrollPaneSetNoBorder(w, noBorder) When true, removes the border around the scroll pane.</pre>
|
|
<h3>API Struct (wgtRegisterApi "scrollpane")</h3>
|
|
<pre> Slot Function
|
|
---- --------
|
|
header WGT_API_HEADER (ABI version and size, checked by wgtGetApi)
|
|
create wgtScrollPane
|
|
scrollToChild wgtScrollPaneScrollToChild
|
|
setNoBorder wgtScrollPaneSetNoBorder
|
|
scrollToTop wgtScrollPaneScrollToTop</pre>
|
|
<h3>Properties (BASIC Interface)</h3>
|
|
<pre> Property Type Access Description
|
|
-------- ---- ------ -----------
|
|
NoBorder Boolean Read/Write Whether the border around the scroll pane is hidden.</pre>
|
|
<h3>Methods</h3>
|
|
<p>No widget-specific methods.</p>
|
|
<h3>Events</h3>
|
|
<pre> Callback Description
|
|
-------- -----------
|
|
onScroll Fires when the scroll position changes.</pre>
|
|
<h3>Default Event</h3>
|
|
<p>None. (VB basName: ScrollPane, namePrefix: Scroll.)</p>
|
|
</div>
|
|
<div class="topic" id="widget.separator">
|
|
<h2>Separator</h2>
|
|
<p>A visual dividing line used to separate groups of widgets. Available in horizontal and vertical orientations.</p>
|
|
<p>Header: widgets/separatr.h</p>
|
|
<h3>API Functions</h3>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
WidgetT *wgtSeparator(parent) Create a separator oriented from the parent: vertical in a horizontal container, horizontal otherwise.
|
|
WidgetT *wgtHSeparator(parent) Create a horizontal separator line.
|
|
WidgetT *wgtVSeparator(parent) Create a vertical separator line.</pre>
|
|
<h3>API Struct (wgtRegisterApi "separator")</h3>
|
|
<pre> Slot Function
|
|
---- --------
|
|
header WGT_API_HEADER (ABI version and size, checked by wgtGetApi)
|
|
hSeparator wgtHSeparator
|
|
vSeparator wgtVSeparator
|
|
create wgtSeparator</pre>
|
|
<p>The BASIC Line control is registered as interface "line" with its own one-slot API (create = wgtSeparator).</p>
|
|
<h3>Properties</h3>
|
|
<p>No widget-specific properties.</p>
|
|
<h3>Methods</h3>
|
|
<p>No widget-specific methods.</p>
|
|
<h3>Events</h3>
|
|
<p>Separator is a visual-only widget. No events.</p>
|
|
<h3>Default Event</h3>
|
|
<p>None. (VB basName: Line.)</p>
|
|
</div>
|
|
<div class="topic" id="widget.slider">
|
|
<h2>Slider</h2>
|
|
<p>A horizontal slider (track bar) for selecting an integer value within a range. The user drags the thumb or clicks the track to change the value.</p>
|
|
<p>Header: widgets/slider.h</p>
|
|
<h3>Creation</h3>
|
|
<pre><code>WidgetT *sl = wgtSlider(parent, 0, 100);</code></pre>
|
|
<h3>API Functions</h3>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
WidgetT *wgtSlider(parent, minVal, maxVal) Create a slider with the given integer range.
|
|
void wgtSliderSetValue(w, value) Set the slider value programmatically.
|
|
int32_t wgtSliderGetValue(w) Get the current slider value.</pre>
|
|
<h3>API Struct (wgtRegisterApi "slider")</h3>
|
|
<pre> Slot Function
|
|
---- --------
|
|
header WGT_API_HEADER (ABI version and size, checked by wgtGetApi)
|
|
create wgtSlider
|
|
setValue wgtSliderSetValue
|
|
getValue wgtSliderGetValue</pre>
|
|
<h3>Properties (BASIC Interface)</h3>
|
|
<pre> Property Type Access Description
|
|
-------- ---- ------ -----------
|
|
Value Integer Read/Write Current slider value.
|
|
Min Integer Read/Write Lowest value; raising it above Max raises Max too.
|
|
Max Integer Read/Write Highest value; lowering it below Min lowers Min too.</pre>
|
|
<h3>Methods</h3>
|
|
<p>No widget-specific methods.</p>
|
|
<h3>Events</h3>
|
|
<pre> Callback Description
|
|
-------- -----------
|
|
onChange Fires when the slider value changes (not when a key, click or drag leaves it unchanged).</pre>
|
|
<h3>Default Event</h3>
|
|
<p>"Change" (VB basName: HScrollBar, namePrefix: HScroll).</p>
|
|
</div>
|
|
<div class="topic" id="widget.spacer">
|
|
<h2>Spacer</h2>
|
|
<p>An invisible widget used for layout purposes. By default it has weight=100, so it absorbs available extra space. Useful for pushing other widgets apart or aligning them to edges.</p>
|
|
<p>Header: widgets/spacer.h</p>
|
|
<h3>Creation</h3>
|
|
<pre><code>WidgetT *row = wgtHBox(parent);
|
|
wgtButton(row, "OK");
|
|
wgtSpacer(row); // pushes Cancel to the right
|
|
wgtButton(row, "Cancel");</code></pre>
|
|
<h3>API Functions</h3>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
WidgetT *wgtSpacer(parent) Create an invisible spacer widget.</pre>
|
|
<h3>API Struct (wgtRegisterApi "spacer")</h3>
|
|
<pre> Slot Function
|
|
---- --------
|
|
header WGT_API_HEADER (ABI version and size, checked by wgtGetApi)
|
|
create wgtSpacer</pre>
|
|
<h3>Properties, Methods, Events</h3>
|
|
<p>Spacer is an invisible layout widget. No type-specific properties, methods, or events.</p>
|
|
<h3>Default Event</h3>
|
|
<p>None. (VB basName: Spacer.)</p>
|
|
</div>
|
|
<div class="topic" id="widget.spinner">
|
|
<h2>Spinner</h2>
|
|
<p>A numeric input with up/down buttons for incrementing and decrementing a value within a range. Supports both integer and floating-point (real) modes.</p>
|
|
<p>Typing, Backspace/Delete, paste, cut and undo start an edit of the text; Enter or focus loss commits it and Esc cancels it. Undo (Ctrl+Z) covers edits of the typed text only: every reformat from the value (creation, SetText, commit, step) empties the undo slot. A paste that is not a valid number is rolled back.</p>
|
|
<p>Header: widgets/spinner.h</p>
|
|
<h3>Creation</h3>
|
|
<pre><code>WidgetT *sp = wgtSpinner(parent, 0, 100, 1);</code></pre>
|
|
<h3>API Functions</h3>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
WidgetT *wgtSpinner(parent, minVal, maxVal, step) Create a spinner with the given integer range and step size.
|
|
void wgtSpinnerSetValue(w, value) Set the integer value.
|
|
int32_t wgtSpinnerGetValue(w) Get the current integer value; an edit in progress is parsed but not committed.
|
|
void wgtSpinnerSetRange(w, minVal, maxVal) Set the range for both modes (the real range starts as the creation range).
|
|
void wgtSpinnerSetStep(w, step) Set the integer step size.
|
|
void wgtSpinnerSetRealMode(w, enable) Switch to floating-point mode.
|
|
double wgtSpinnerGetRealValue(w) Get the current floating-point value.
|
|
void wgtSpinnerSetRealValue(w, value) Set the floating-point value.
|
|
void wgtSpinnerSetRealRange(w, minVal, maxVal) Set the floating-point range only (call after wgtSpinnerSetRange).
|
|
void wgtSpinnerSetRealStep(w, step) Set the floating-point step size.
|
|
void wgtSpinnerSetDecimals(w, decimals) Set the number of decimal places displayed in real mode.</pre>
|
|
<h3>API Struct (wgtRegisterApi "spinner")</h3>
|
|
<pre> Slot Function
|
|
---- --------
|
|
header WGT_API_HEADER (ABI version and size, checked by wgtGetApi)
|
|
create wgtSpinner
|
|
setValue wgtSpinnerSetValue
|
|
getValue wgtSpinnerGetValue
|
|
setRange wgtSpinnerSetRange
|
|
setStep wgtSpinnerSetStep
|
|
setRealMode wgtSpinnerSetRealMode
|
|
getRealValue wgtSpinnerGetRealValue
|
|
setRealValue wgtSpinnerSetRealValue
|
|
setRealRange wgtSpinnerSetRealRange
|
|
setRealStep wgtSpinnerSetRealStep
|
|
setDecimals wgtSpinnerSetDecimals</pre>
|
|
<h3>Properties (BASIC Interface)</h3>
|
|
<pre> Property Type Access Description
|
|
-------- ---- ------ -----------
|
|
Value Integer Read/Write Current integer value.
|
|
RealMode Boolean Read/Write Whether floating-point mode is active.
|
|
Decimals Integer Read/Write Number of decimal places displayed in real mode.</pre>
|
|
<h3>Methods (BASIC Interface)</h3>
|
|
<pre> Method Description
|
|
------ -----------
|
|
SetRange min%, max% Set the range (integer and real mode).
|
|
SetStep step% Set the integer step size.</pre>
|
|
<h3>Events</h3>
|
|
<pre> Callback Description
|
|
-------- -----------
|
|
onChange Fires when the value changes and on each typed edit, but not when a step or commit leaves the value unchanged.</pre>
|
|
<h3>Default Event</h3>
|
|
<p>"Change" (VB basName: SpinButton, namePrefix: Spin).</p>
|
|
</div>
|
|
<div class="topic" id="widget.splitter">
|
|
<h2>Splitter</h2>
|
|
<p>A two-pane container with a draggable divider. The user drags the splitter bar to resize the two panes. Can be oriented vertically (left/right panes) or horizontally (top/bottom panes). Add exactly two children.</p>
|
|
<p>Header: widgets/splitter.h</p>
|
|
<h3>Creation</h3>
|
|
<pre><code>WidgetT *sp = wgtSplitter(parent, true); // vertical = left|right
|
|
WidgetT *left = wgtVBox(sp);
|
|
WidgetT *right = wgtVBox(sp);</code></pre>
|
|
<h3>API Functions</h3>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
WidgetT *wgtSplitter(parent, vertical) Create a splitter. vertical=true for left/right panes (vertical divider), false for top/bottom.
|
|
void wgtSplitterSetPos(w, pos) Set the divider position in pixels from the leading edge.
|
|
int32_t wgtSplitterGetPos(w) Get the current divider position in pixels.</pre>
|
|
<h3>API Struct (wgtRegisterApi "splitter")</h3>
|
|
<pre> Slot Function
|
|
---- --------
|
|
header WGT_API_HEADER (ABI version and size, checked by wgtGetApi)
|
|
create wgtSplitter
|
|
setPos wgtSplitterSetPos
|
|
getPos wgtSplitterGetPos</pre>
|
|
<h3>Properties (BASIC Interface)</h3>
|
|
<pre> Property Type Access Description
|
|
-------- ---- ------ -----------
|
|
Position Integer Read/Write Divider position in pixels.</pre>
|
|
<h3>Methods</h3>
|
|
<p>No widget-specific methods.</p>
|
|
<h3>Events</h3>
|
|
<p>Splitter has no widget-specific events. Common events apply.</p>
|
|
<h3>Default Event</h3>
|
|
<p>None. (VB basName: Splitter.)</p>
|
|
</div>
|
|
<div class="topic" id="widget.statusbar">
|
|
<h2>StatusBar</h2>
|
|
<p>A horizontal bar typically placed at the bottom of a window for displaying status text and informational widgets. Children are laid out horizontally, each inside a 1-pixel sunken panel border. Padding defaults to 1 pixel and spacing to 2 pixels; the common padding/spacing fields override them.</p>
|
|
<p>Header: widgets/statBar.h</p>
|
|
<h3>Creation</h3>
|
|
<pre><code>WidgetT *sb = wgtStatusBar(parent);
|
|
wgtLabel(sb, "Ready");</code></pre>
|
|
<h3>API Functions</h3>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
WidgetT *wgtStatusBar(parent) Create a status bar container.</pre>
|
|
<h3>API Struct (wgtRegisterApi "statusbar")</h3>
|
|
<pre> Slot Function
|
|
---- --------
|
|
header WGT_API_HEADER (ABI version and size, checked by wgtGetApi)
|
|
create wgtStatusBar</pre>
|
|
<h3>Properties, Methods, Events</h3>
|
|
<p>StatusBar has no widget-specific properties, methods, or events. Add child widgets (labels, progress bars, etc.) to populate.</p>
|
|
<h3>Default Event</h3>
|
|
<p>None. (VB basName: StatusBar.)</p>
|
|
</div>
|
|
<div class="topic" id="widget.tabcontrol">
|
|
<h2>TabControl</h2>
|
|
<p>A tabbed container that displays one page at a time with clickable tabs along the top. Each tab page is a container that holds its own child widgets.</p>
|
|
<p>Header: widgets/tabCtrl.h</p>
|
|
<h3>Creation</h3>
|
|
<pre><code>WidgetT *tabs = wgtTabControl(parent);
|
|
WidgetT *page1 = wgtTabPage(tabs, "General");
|
|
WidgetT *page2 = wgtTabPage(tabs, "Advanced");
|
|
// add children to page1, page2...</code></pre>
|
|
<h3>API Functions</h3>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
WidgetT *wgtTabControl(parent) Create a tab control.
|
|
WidgetT *wgtTabPage(parent, title) Add a tab page with the given title. Returns the page container widget.
|
|
void wgtTabControlSetActive(w, idx) Set the active tab by index (0-based). Negative selects the first tab; an index past the last page selects the last page (with no pages yet it is kept and clamped at layout, so it may be set before the pages are added). Closes a popup owned by a control on the hidden page and moves focus off that page; does not fire onChange.
|
|
int32_t wgtTabControlGetActive(w) Get the index of the active tab.</pre>
|
|
<h3>API Struct (wgtRegisterApi "tabcontrol")</h3>
|
|
<pre> Slot Function
|
|
---- --------
|
|
header WGT_API_HEADER (ABI version and size, checked by wgtGetApi)
|
|
create wgtTabControl
|
|
page wgtTabPage
|
|
setActive wgtTabControlSetActive
|
|
getActive wgtTabControlGetActive</pre>
|
|
<h3>Properties (BASIC Interface)</h3>
|
|
<pre> Property Type Access Description
|
|
-------- ---- ------ -----------
|
|
TabIndex Integer Read/Write Index of the currently active tab (0-based).</pre>
|
|
<h3>Methods (BASIC Interface)</h3>
|
|
<pre> Method Description
|
|
------ -----------
|
|
AddPage title$ Add a new tab page with the given title.
|
|
GetActive() Returns the index of the active tab.
|
|
SetActive idx% Set the active tab by index (0-based).</pre>
|
|
<h3>Events</h3>
|
|
<pre> Callback Description
|
|
-------- -----------
|
|
onChange Fires when the user changes the active tab (mouse, keyboard or accelerator); not for wgtTabControlSetActive.</pre>
|
|
<h3>Default Event</h3>
|
|
<p>"Click" (VB basName: TabStrip).</p>
|
|
</div>
|
|
<div class="topic" id="widget.textinput">
|
|
<h2>TextInput / TextArea</h2>
|
|
<p>Single-line text input, password input, masked input, and multi-line text area with optional syntax colorization, line numbers, find/replace, and gutter decorators.</p>
|
|
<p>Header: widgets/textInpt.h</p>
|
|
<h3>Creation</h3>
|
|
<pre> Macro Description
|
|
----- -----------
|
|
wgtTextInput(parent, maxLen) Create a single-line text input. maxLen is the maximum number of characters (0 => default 256).
|
|
wgtPasswordInput(parent, maxLen) Create a password input (characters displayed as bullets).
|
|
wgtMaskedInput(parent, mask) Create a masked input field. The mask string defines the input format ('#' digit, 'A' letter, '*' any printable; other characters are literals). The mask is copied. Setting the text keeps the mask layout: text with the mask's shape fills it slot by slot, other text fills the slots in order.
|
|
wgtTextArea(parent, maxLen) Create a multi-line text area.</pre>
|
|
<h3>API Functions (TextArea-specific)</h3>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
void wgtTextAreaAppendText(w, text) Append text to the end of the buffer and invalidate the widget.
|
|
void wgtTextAreaSetColorize(w, fn, ctx) Set a syntax colorization callback. The callback receives each line and fills a color index array.
|
|
void wgtTextAreaGoToLine(w, line) Scroll to the given line number and select the whole line.
|
|
void wgtTextAreaSetAutoIndent(w, enable) Enable or disable automatic indentation on newline.
|
|
void wgtTextAreaSetShowLineNumbers(w, show) Show or hide line numbers in the gutter.
|
|
void wgtTextAreaSetCaptureTabs(w, capture) When true, Tab key inserts a tab/spaces instead of moving focus.
|
|
void wgtTextAreaSetTabWidth(w, width) Set the tab stop width in characters.
|
|
void wgtTextAreaSetUseTabChar(w, useChar) When true, insert literal tab characters; when false, insert spaces.
|
|
bool wgtTextAreaFindNext(w, needle, caseSens, fwd) Search from the cursor and select the match (a forward match at the cursor counts; repeating moves past it). Returns true if found.
|
|
int32_t wgtTextAreaReplaceAll(w, needle, repl, caseSens) Replace all occurrences. Returns the number of replacements made.
|
|
bool wgtTextAreaReplaceSelection(w, needle, repl, caseSens, fwd) If the current selection matches needle, replace it, then find the next occurrence in direction fwd. Returns true if anything was replaced or found.
|
|
void wgtTextAreaSetLineDecorator(w, fn, ctx) Set a gutter line decorator callback. Returns a color and receives the line number.
|
|
int32_t wgtTextAreaGetCursorLine(w) Get the current cursor line number.
|
|
void wgtTextAreaSetGutterClickCallback(w, fn) Set a callback for gutter clicks (e.g. for breakpoint toggling).
|
|
int32_t wgtTextAreaGetWordAtCursor(w, buf, bufSize) Copy the word under the cursor into buf. Returns its length.
|
|
void wgtTextAreaSetSyntaxColors(w, colors, count) Set the color palette used by the colorizer.
|
|
void wgtTextAreaCursorToEnd(w) Move the cursor to the very end of the buffer (past the last character).
|
|
void wgtTextAreaSetCursor(w, line, col) Move the cursor to 1-based line/column without selecting; scrolls it into view. Out-of-range values clamp to the text.</pre>
|
|
<h3>API Struct (wgtRegisterApi "textinput")</h3>
|
|
<p>The combined "textinput" API exposes all four constructors plus the TextArea functions. The designer uses per-type APIs registered under the names "textbox" (single-line) and "textarea" (multi-line), each exposing only the header and a create slot. Every API struct starts with a header (WGT_API_HEADER: ABI version and size, checked by wgtGetApi).</p>
|
|
<h3>Properties (BASIC Interface)</h3>
|
|
<p>TextBox has no widget-specific properties. Text is managed via the generic "Text" property. TextArea exposes:</p>
|
|
<pre> Property Type Access Description
|
|
-------- ---- ------ -----------
|
|
CursorLine Integer Read-only Current cursor line number (1-based).</pre>
|
|
<h3>Methods (BASIC Interface, TextArea)</h3>
|
|
<pre> Method Description
|
|
------ -----------
|
|
AppendText text$ Append text to the end of the buffer.
|
|
FindNext needle$, caseSensitive, forward Search from the cursor (a match at the cursor counts; repeating moves past it). Returns True if found.
|
|
GetWordAtCursor() Returns the word under the cursor.
|
|
GoToLine line% Scroll to a line and select it.
|
|
ReplaceAll needle$, replacement$, caseSensitive Replace all occurrences. Returns the count.
|
|
SetAutoIndent enabled Enable or disable automatic indentation on newline.
|
|
SetCaptureTabs enabled When True, Tab inserts whitespace instead of moving focus.
|
|
SetCursor line%, col% Move the cursor to a 1-based line and column without selecting.
|
|
SetShowLineNumbers show Show or hide line numbers in the gutter.
|
|
SetSyntaxMode mode$ Activate built-in syntax highlighting (e.g. "dhs", "bas"; "" to disable).
|
|
SetTabWidth width% Set the tab stop width in characters.
|
|
SetUseTabChar useChar When True, Tab inserts a literal tab; when False, spaces.</pre>
|
|
<h3>Events</h3>
|
|
<pre> Callback Description
|
|
-------- -----------
|
|
onChange Fires when the text content changes.
|
|
onKeyPress Fires on each key press (ASCII value).
|
|
onValidate Called before committing a change. Return false to cancel.</pre>
|
|
<h3>Default Event</h3>
|
|
<p>"Change" for both TextBox (basName: TextBox) and TextArea (basName: TextArea).</p>
|
|
</div>
|
|
<div class="topic" id="widget.timer">
|
|
<h2>Timer</h2>
|
|
<p>A non-visual widget that fires its onChange callback at a regular interval. Useful for animations, polling, and periodic updates. A new timer is running; call wgtTimerStop to keep it dormant.</p>
|
|
<p>Header: widgets/timer.h</p>
|
|
<h3>Creation</h3>
|
|
<pre><code>WidgetT *tmr = wgtTimer(parent, 1000, true); // 1 second, repeating
|
|
tmr->onChange = onTimerTick;</code></pre>
|
|
<h3>API Functions</h3>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
WidgetT *wgtTimer(parent, intervalMs, repeat) Create a timer. intervalMs is the interval in milliseconds. repeat: true for repeating, false for one-shot.
|
|
void wgtTimerStart(w) Start the timer.
|
|
void wgtTimerStop(w) Stop the timer.
|
|
void wgtTimerSetInterval(w, intervalMs) Change the timer interval and restart the countdown. An interval of 0 or less never fires.
|
|
int32_t wgtTimerGetInterval(w) Returns the current interval in milliseconds.
|
|
bool wgtTimerIsRunning(w) Returns true if the timer is currently running.
|
|
void wgtTimerSetEnabled(w, enabled) Start or stop the timer based on enabled flag.
|
|
void wgtUpdateTimers(void) Global tick function. Called by the event loop to advance all active timers.</pre>
|
|
<h3>API Struct (wgtRegisterApi "timer")</h3>
|
|
<pre> Slot Function
|
|
---- --------
|
|
header WGT_API_HEADER (ABI version and size, checked by wgtGetApi)
|
|
create wgtTimer
|
|
start wgtTimerStart
|
|
stop wgtTimerStop
|
|
setInterval wgtTimerSetInterval
|
|
isRunning wgtTimerIsRunning
|
|
updateTimers wgtUpdateTimers
|
|
testReset wgtTimerTestReset (present only in host test builds, DVX_HOST_TEST)</pre>
|
|
<h3>Events</h3>
|
|
<pre> Callback Description
|
|
-------- -----------
|
|
onChange Fires each time the timer elapses (the BASIC layer surfaces this as the "Timer" event). A one-shot timer is stopped before the callback runs, so the callback may restart it.</pre>
|
|
<h3>Properties (BASIC Interface)</h3>
|
|
<pre> Property Type Access Description
|
|
-------- ---- ------ -----------
|
|
Enabled Boolean Read/Write Whether the timer is running. Reading returns the running state; writing starts or stops it.
|
|
Interval Integer Read/Write Timer interval in milliseconds. Setting it restarts the countdown; 0 or less disables firing.</pre>
|
|
<h3>Methods (BASIC Interface)</h3>
|
|
<pre> Method Description
|
|
------ -----------
|
|
Start Start the timer.
|
|
Stop Stop the timer.</pre>
|
|
<h3>Extra Events (BASIC Interface)</h3>
|
|
<pre> Event Description
|
|
----- -----------
|
|
Timer Fires each time the timer elapses. Default event.</pre>
|
|
<hr>
|
|
</div>
|
|
<div class="topic" id="widget.toolbar">
|
|
<h2>Toolbar</h2>
|
|
<p>A horizontal container for toolbar buttons and controls. Typically placed at the top of a window. Children (usually ImageButtons or Buttons) are laid out horizontally with toolbar-appropriate spacing: 2 pixels of padding and spacing by default, overridable through the common padding/spacing fields.</p>
|
|
<p>Header: widgets/toolbar.h</p>
|
|
<h3>Creation</h3>
|
|
<pre><code>WidgetT *tb = wgtToolbar(parent);
|
|
wgtImageButtonFromFile(tb, "icons/save.bmp");
|
|
wgtImageButtonFromFile(tb, "icons/open.bmp");</code></pre>
|
|
<h3>API Functions</h3>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
WidgetT *wgtToolbar(parent) Create a toolbar container.
|
|
bool wgtToolbarGetFace(w, colors, face) True when w is a toolbar; sets *face to the fill color its children sit on.</pre>
|
|
<h3>API Struct (wgtRegisterApi "toolbar")</h3>
|
|
<pre> Slot Function
|
|
---- --------
|
|
header WGT_API_HEADER (ABI version and size, checked by wgtGetApi)
|
|
create wgtToolbar
|
|
getFace wgtToolbarGetFace</pre>
|
|
<h3>Properties, Methods, Events</h3>
|
|
<p>Toolbar has no widget-specific properties, methods, or events. Add children (buttons, separators, etc.) to populate.</p>
|
|
<h3>Default Event</h3>
|
|
<p>None. (VB basName: Toolbar.)</p>
|
|
</div>
|
|
<div class="topic" id="widget.treeview">
|
|
<h2>TreeView</h2>
|
|
<p>A hierarchical tree control with expandable/collapsible nodes. Supports single and multi-selection and drag-to-reorder. Tree items are added as children of the TreeView or of other tree items to create nested hierarchies.</p>
|
|
<p>Header: widgets/treeView.h</p>
|
|
<h3>Creation</h3>
|
|
<pre><code>WidgetT *tv = wgtTreeView(parent);
|
|
WidgetT *root = wgtTreeItem(tv, "Root");
|
|
WidgetT *child = wgtTreeItem(root, "Child");</code></pre>
|
|
<h3>API Functions</h3>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
WidgetT *wgtTreeView(parent) Create a tree view control.
|
|
WidgetT *wgtTreeItem(parent, text) Add a tree item as a child of the tree view or another tree item.
|
|
WidgetT *wgtTreeViewGetSelected(w) Get the currently selected tree item (NULL if none).
|
|
void wgtTreeViewSetSelected(w, item) Set the selected tree item (expanding its ancestors and scrolling it into view). Items of other trees are ignored.
|
|
void wgtTreeViewSetMultiSelect(w, multi) Enable or disable multi-selection. Turning it off clears the per-item selection.
|
|
void wgtTreeViewSetReorderable(w, reorderable) Enable drag-to-reorder of items.
|
|
void wgtTreeItemSetExpanded(w, expanded) Expand or collapse a tree item. Collapsing over the selected item moves the selection to w.
|
|
bool wgtTreeItemIsExpanded(w) Check if a tree item is expanded.
|
|
bool wgtTreeItemIsSelected(w) Check if a tree item is selected (single-select: it is the selected item).
|
|
void wgtTreeItemSetSelected(w, selected) Select or deselect a tree item. In single-select mode this sets or clears the tree's selected item.</pre>
|
|
<h3>API Struct (wgtRegisterApi "treeview")</h3>
|
|
<pre> Slot Function
|
|
---- --------
|
|
header WGT_API_HEADER (ABI version and size, checked by wgtGetApi)
|
|
create wgtTreeView
|
|
getSelected wgtTreeViewGetSelected
|
|
setSelected wgtTreeViewSetSelected
|
|
setMultiSelect wgtTreeViewSetMultiSelect
|
|
setReorderable wgtTreeViewSetReorderable
|
|
item wgtTreeItem
|
|
itemSetExpanded wgtTreeItemSetExpanded
|
|
itemIsExpanded wgtTreeItemIsExpanded
|
|
itemIsSelected wgtTreeItemIsSelected
|
|
itemSetSelected wgtTreeItemSetSelected</pre>
|
|
<h3>Properties</h3>
|
|
<p>No widget-specific properties.</p>
|
|
<h3>Methods (BASIC Interface)</h3>
|
|
<pre> Method Description
|
|
------ -----------
|
|
AddChildItem parentIdx%, text$ Add a child node under the node at the given index.
|
|
AddItem text$ Add a root-level node.
|
|
Clear Remove all nodes.
|
|
GetItemText(index%) Returns the text of the node at the given depth-first index.
|
|
IsExpanded(index%) Returns True if the node is expanded.
|
|
IsItemSelected(index%) Returns True if the node is selected.
|
|
ItemCount() Returns the total number of nodes.
|
|
SetExpanded index%, expanded Expand or collapse the node (collapsing moves a hidden selection to the node).
|
|
SetItemSelected index%, selected Select or deselect the node (single-select: sets or clears the selection).
|
|
SetMultiSelect multi Enable or disable multi-selection.
|
|
SetReorderable reorderable Enable or disable drag-to-reorder.</pre>
|
|
<h3>Events</h3>
|
|
<pre> Callback Description
|
|
-------- -----------
|
|
onClick Fires when a tree item is clicked.
|
|
onDblClick Fires when a tree item is double-clicked.
|
|
onChange Fires when the selection changes (including Space toggles in multi-select mode) and after a drag-reorder.</pre>
|
|
<h3>Default Event</h3>
|
|
<p>"Click" (VB basName: TreeView).</p>
|
|
</div>
|
|
<div class="topic" id="widget.wrapbox">
|
|
<h2>WrapBox</h2>
|
|
<p>A flow/wrap layout container that arranges children left-to-right, wrapping to the next row when the available width is exceeded. Each row's height is the maximum child height in that row. Supports configurable spacing between items and rows, and per-row alignment for short rows.</p>
|
|
<p>Header: widgets/wrapBox.h</p>
|
|
<h3>Creation</h3>
|
|
<pre><code>WidgetT *wrap = wgtWrapBox(parent);
|
|
wgtButton(wrap, "Tag 1");
|
|
wgtButton(wrap, "Tag 2");
|
|
wgtButton(wrap, "Tag 3");</code></pre>
|
|
<h3>API Functions</h3>
|
|
<pre> Function Description
|
|
-------- -----------
|
|
wgtWrapBox(parent) Create a wrap box container. Macro that calls the registered "wrapbox" create slot.</pre>
|
|
<h3>API Struct (wgtRegisterApi "wrapbox")</h3>
|
|
<pre> Slot Function
|
|
---- --------
|
|
header WGT_API_HEADER (ABI version and size, checked by wgtGetApi)
|
|
create wrapBoxCreate</pre>
|
|
<h3>Properties (Common Container Fields)</h3>
|
|
<p>WrapBox uses the common WidgetT container fields for layout control:</p>
|
|
<pre> Property Description
|
|
-------- -----------
|
|
spacing Gap between children in both the horizontal and vertical directions (tagged size). Default is 4 pixels.
|
|
padding Internal padding around children (tagged size). Default is 2 pixels.</pre>
|
|
<h3>Properties (BASIC Interface)</h3>
|
|
<pre> Property Type Access Description
|
|
-------- ---- ------ -----------
|
|
Alignment Enum (Left, Center, Right) Read/Write Row alignment for rows that do not fill the full width.</pre>
|
|
<h3>Methods</h3>
|
|
<p>No widget-specific methods.</p>
|
|
<h3>Events</h3>
|
|
<p>WrapBox is a container widget. It uses the common events only. No widget-specific events are defined.</p>
|
|
<h3>Default Event</h3>
|
|
<p>"Click" (VB basName: WrapBox, namePrefix: WrapBox).</p>
|
|
</div>
|
|
</main>
|
|
</body>
|
|
</html>
|