diff --git a/Makefile b/Makefile
index 64d7acc0..b09afc37 100644
--- a/Makefile
+++ b/Makefile
@@ -57,15 +57,15 @@ loader: libdvx libtasks
$(MAKE) -C src/loader
# Every .wgt recipe runs bin/host/dvxres, so widgets need the tools.
-widgets: libdvx libtasks texthelp listhelp tools
+widgets: libdvx libtasks texthelp listhelp richtext tools
$(MAKE) -C src/widgets/kpunch
tools:
$(MAKE) -C src/tools
-# SETUP.EXE (bin/setup.exe, a static build of the core it needs) and the
-# package tool bin/host/dvxpkg.
-installer:
+# SETUP.EXE (bin/setup.exe, a static build of the core it needs, with
+# fonts mkfont makes) and the package tool bin/host/dvxpkg.
+installer: tools
$(MAKE) -C src/installer
# Release media in dist/: the CD image and floppy images (FLOPPY=360K,
@@ -218,20 +218,21 @@ include src/sdkHeaders.mk
# The SDK's own files (README, sdk.mk, newProject.sh, project templates)
# live in src/sdk; its bin/ holds the host tools a project build runs.
SDK_SRC = src/sdk
-SDK_TOOLS = bin/host/dvxres bin/host/dvxhlpc bin/host/bascomp src/tools/dxeGen.sh
+SDK_TOOLS = bin/host/dvxres bin/host/dvxhlpc bin/host/bascomp bin/host/mkfont src/tools/dxeGen.sh
-# The host tools come from tools (dvxres, dvxhlpc) and apps (bascomp).
+# The host tools come from tools (dvxres, dvxhlpc, mkfont) and apps (bascomp).
deploy-sdk: tools apps
@echo "Building SDK..."
@# Start from an empty tree so a header dropped from the lists goes away.
rm -rf $(SDKDIR)
- @mkdir -p $(SDKDIR)/bin $(SDKDIR)/include/core $(SDKDIR)/include/shell $(SDKDIR)/include/tasks $(SDKDIR)/include/sql $(SDKDIR)/include/texthelp $(SDKDIR)/include/listhelp $(SDKDIR)/include/fileops $(SDKDIR)/include/hlpfile $(SDKDIR)/include/xfer $(SDKDIR)/include/conn $(SDKDIR)/include/print $(SDKDIR)/include/game $(SDKDIR)/include/joystick $(SDKDIR)/include/audio $(SDKDIR)/include/music $(SDKDIR)/include/net $(SDKDIR)/include/basic $(SDKDIR)/samples/basic/basdemo $(SDKDIR)/samples/basic/widshow
+ @mkdir -p $(SDKDIR)/bin $(SDKDIR)/include/core $(SDKDIR)/include/shell $(SDKDIR)/include/tasks $(SDKDIR)/include/sql $(SDKDIR)/include/texthelp $(SDKDIR)/include/listhelp $(SDKDIR)/include/richtext $(SDKDIR)/include/fileops $(SDKDIR)/include/hlpfile $(SDKDIR)/include/xfer $(SDKDIR)/include/conn $(SDKDIR)/include/print $(SDKDIR)/include/game $(SDKDIR)/include/joystick $(SDKDIR)/include/audio $(SDKDIR)/include/music $(SDKDIR)/include/net $(SDKDIR)/include/basic $(SDKDIR)/samples/basic/basdemo $(SDKDIR)/samples/basic/widshow
cp $(SDK_CORE_H) $(SDKDIR)/include/core/
cp $(SDK_SHELL_H) $(SDKDIR)/include/shell/
cp $(SDK_TASKS_H) $(SDKDIR)/include/tasks/
cp $(SDK_SQL_H) $(SDKDIR)/include/sql/
cp $(SDK_TEXTHELP_H) $(SDKDIR)/include/texthelp/
cp $(SDK_LISTHELP_H) $(SDKDIR)/include/listhelp/
+ cp $(SDK_RICHTEXT_H) $(SDKDIR)/include/richtext/
cp $(SDK_FILEOPS_H) $(SDKDIR)/include/fileops/
cp $(SDK_HLPFILE_H) $(SDKDIR)/include/hlpfile/
cp $(SDK_XFER_H) $(SDKDIR)/include/xfer/
diff --git a/README.md b/README.md
index 936e2a20..4ceb00ba 100644
--- a/README.md
+++ b/README.md
@@ -69,14 +69,15 @@ pinned SHA-256, and unpacks it there (no sudo, nothing outside the
project); run it again with `--force` to reinstall:
* DJGPP (GCC 12.2.0) and the CWSDPMI stub -- the DOS cross compiler
+* UPX 4.2.4 -- packs `SETUP.EXE` to about half its size
* 86Box 6.0 and its ROM set, FreeDOS 1.3 boot files and CuteMouse,
and DOSBox Staging 0.83.0 -- the emulator test harness
* cppcheck 2.21, built from source -- used by the static gate
* lrzsz 0.12.20 -- the Terminal tests' ZMODEM peer
-It needs curl, tar, bzip2, xz, unzip, make, g++, mtools and xvfb-run.
-To build with a different DOS toolchain, pass `DJGPP_PREFIX=...` and
-`CWSDSTUB=...` to `make`.
+It needs curl, tar, bzip2, xz, unzip, make, g++, mtools, and xvfb-run.
+To build with a different DOS toolchain, pass `DJGPP_PREFIX=...`,
+`CWSDSTUB=...`, and `UPX=...` to `make`.
`make` builds in dependency order: core, tasks, loader, helpers,
host tools, widgets, shell, task manager, apps. Build output goes to
@@ -92,7 +93,7 @@ subset. The emulators never touch your home directory. See
`make dist` builds the release media in `dist/`: a CD image
(`DVX.ISO`: `SETUP.EXE` with the DVX package appended, and
`README.TXT`) and a set of floppy images (`make dist FLOPPY=720K`;
-360K, 720K, 1.2M, 1.44M (default), 2.88M or a number of bytes per
+360K, 720K, 1.2M, 1.44M (default), 2.88M, or a number of bytes per
disk). On the target machine, type `SETUP` on the CD or Disk 1: Setup
installs DVX in a hard-disk folder (and can add it to AUTOEXEC.BAT);
the `SETUP.EXE` it leaves in that folder uninstalls. `./mkcd.sh` wraps
@@ -130,7 +131,7 @@ dvxgui/
See `src/tools/README.md` for the host-side development tools
(resource tool, icon generators, help compiler, proxy), and
`src/apps/kpunch/README.md` for the SDK contract applications must
-follow. `make` also writes the SDK to `bin/sdk`: headers, host tools
+follow. `make` also writes the SDK to `bin/sdk`: headers, host tools,
and a template for each kind of module (`bin/sdk/newProject.sh app
NAME`); the SDK Quick Start in the system help walks through it.
diff --git a/cppcheck.sh b/cppcheck.sh
index 8d83e3c7..b46d613f 100755
--- a/cppcheck.sh
+++ b/cppcheck.sh
@@ -24,6 +24,7 @@ INCLUDES=(
-Isrc/libs/kpunch/libtasks
-Isrc/libs/kpunch/texthelp
-Isrc/libs/kpunch/listhelp
+ -Isrc/libs/kpunch/richtext
-Isrc/libs/kpunch/dvxshell
-Isrc/libs/kpunch/taskmgr
-Isrc/libs/kpunch/serial
diff --git a/docs/dvx_basic_reference.html b/docs/dvx_basic_reference.html
index 13736413..49a3d62c 100644
--- a/docs/dvx_basic_reference.html
+++ b/docs/dvx_basic_reference.html
@@ -79,7 +79,7 @@ img { max-width: 100%; }
@@ -1434,13 +1486,13 @@ img { max-width: 100%; }
---
Project Properties... Edit project metadata (name, author, version, startup form, icon, etc.) and the command line arguments used when the program is run from the IDE.
---
- Add File... Ctrl+O Add a .bas, .frm, .cls or .ctl file to the project. If no project is open, creates an implicit project from the file. A file that cannot be read, or is 64 KB or larger, is refused with a message.
+ Add File... Ctrl+O Add a .bas, .frm, .cls, or .ctl file to the project. If no project is open, creates an implicit project from the file. A file that cannot be read, or is 64 KB or larger, is refused with a message.
New Class Module Add a new, empty class module (Class1.cls, Class2.cls, ...) to the project and open it in the Code Editor. See Class Modules under Project System.
New UserControl Add a new UserControl, a control of your own (UserControl1.ctl, UserControl2.ctl, ...), to the project and open it in the Code Editor. See UserControls in the IDE.
Save File Ctrl+S Save the active file to disk.
Save All Save all modified files in the project.
---
- Make Executable... Compile the project and save it as a standalone .app file. Prompts for a debug or release build. A release build leaves out debugging information and gives forms and controls short internal names, except in a program that uses names as text (the Name property, Controls, DesignSelection or a DesignMouse event procedure), which keeps them.
+ Make Executable... Compile the project and save it as a standalone .app file. Prompts for a debug or release build. A release build leaves out debugging information and gives forms and controls short internal names, except in a program that uses names as text (the Name property, Controls, DesignSelection, or a DesignMouse event procedure), which keeps them.
Make Control... In a control project (Project Properties, Type Control) this item replaces Make Executable: it compiles the project and saves its controls as a control file (.wgt, named after the project). See UserControls in the IDE.
---
Rename File... Give the file selected in the Project Explorer a new name, on disk and in the project. See Renaming a File under Project System.
@@ -1467,7 +1519,7 @@ img { max-width: 100%; }
Find... Ctrl+F Open the Find/Replace dialog.
Find Next F3 Find the next occurrence of the search text (also after the dialog is closed).
Replace... Ctrl+H Open the Find/Replace dialog with replace enabled.
-
Cut, Copy, Paste, Select All and Delete act on the window you last worked in: the form designer's selected control, or the text of the code, output or immediate window.
+
Cut, Copy, Paste, Select All, and Delete act on the window you last worked in: the form designer's selected control, or the text of the code, output, or immediate window.
@@ -1478,7 +1530,7 @@ img { max-width: 100%; }
Run F5 Compile and run the program. If paused at a breakpoint, resumes execution with debugging disabled (runs free).
Debug Shift+F5 Compile and run with the debugger active. Breakpoints are active but execution does not pause at the first statement. If paused, resumes to the next breakpoint.
Run Without Recompile Ctrl+F5 Re-run the last compiled module without recompiling.
- Stop Esc Stop the running program. Run, Debug and the project commands stay disabled until the program has finished shutting down.
+ Stop Esc Stop the running program. Run, Debug, and the project commands stay disabled until the program has finished shutting down.
---
Step Into F8 Execute one statement, stepping into SUB/FUNCTION calls. If idle, starts a debug session and breaks at the first statement.
Step Over Shift+F8 Execute one statement, stepping over SUB/FUNCTION calls.
@@ -1584,7 +1636,7 @@ img { max-width: 100%; }
Object -- lists (General) plus all objects (form name, control names, menu item names, and the WithEvents variables the file declares). Selecting an object filters the Function dropdown.
Function -- lists all event handlers (procedures) for the selected object. Implemented handlers are listed first (plain text); unimplemented handlers follow in brackets (e.g., [Click]). Selecting an unimplemented event creates a new event handler stub. Only the events the object can get are listed: Paint, for example, appears for the form and for PictureBoxes, the objects a program draws on.
-
A WithEvents variable (Dim WithEvents acct As Account, also Private or Public, in a module, a form or a class module) is listed as an object; its events are the ones its class module declares with Event. Picking one of them creates the handler acct_EventName with the event's own parameters, so it matches what the class raises. Selecting the variable when it has no handler yet creates the handler for the class's first event, as a control's default event is created.
+
A WithEvents variable (Dim WithEvents acct As Account, also Private or Public, in a module, a form, or a class module) is listed as an object; its events are the ones its class module declares with Event. Picking one of them creates the handler acct_EventName with the event's own parameters, so it matches what the class raises. Selecting the variable when it has no handler yet creates the handler for the class's first event, as a control's default event is created.
The editor shows one procedure at a time. Each procedure has its own buffer, and switching between them is instantaneous. The (General) section contains module-level declarations and code.
Text outside the procedure being shown is never lost: lines typed above or below the SUB move to (General), a second SUB typed into the buffer becomes a procedure of its own, and a procedure whose SUB line was deleted becomes (General) text. When you leave an event handler that was generated and never edited it is removed; empty SUBs and FUNCTIONs you wrote yourself are kept. After a save the editor shows exactly what was stored (blank lines around a procedure are normalized). A section too large for the editor opens read-only, with a message in the status bar.
Syntax Highlighting
@@ -1598,7 +1650,7 @@ img { max-width: 100%; }
Comments ' This is a comment, REM This is a comment
Numbers 42, 3.14, &HFF, date literals such as #2026-09-30# and #1/15/2026 10:30 AM#
Operators =, <, >, +, -, *, /, \, &
-
Date literals are colored as numbers only when they are real dates, as the compiler reads them; the # of a file number (CLOSE #1) is not. Some words are colored like keywords without being reserved -- built-in type names, Empty, Null, EACH, EVENT, WITHEVENTS, and the console and graphics statements such as CLS, LOCATE, COLOR, SCREEN, PSET, CIRCLE, PAINT, PCOPY and PMAP. A program may use them as its own names, and after a dot (Text1.Width, Picture1.Point) they are shown as ordinary names.
+
Date literals are colored as numbers only when they are real dates, as the compiler reads them; the # of a file number (CLOSE #1) is not. Some words are colored like keywords without being reserved -- built-in type names, Empty, Null, EACH, EVENT, WITHEVENTS, and the console and graphics statements such as CLS, LOCATE, COLOR, SCREEN, PSET, CIRCLE, PAINT, PCOPY, and PMAP. A program may use them as its own names, and after a dot (Text1.Width, Picture1.Point) they are shown as ordinary names.
Editor Features
Line numbers -- displayed in the gutter on the left side.
@@ -1618,15 +1670,15 @@ img { max-width: 100%; }
Selection -- click a control to select it. The selected control is highlighted with grab handles.
Grab handles -- 6x6 pixel handles appear on the right edge (E), bottom edge (S), and bottom-right corner (SE) of the selected control. Drag a handle to resize the control.
Reordering -- drag a control to reorder it among its siblings in the layout (VBox/HBox). A container moves together with its contents.
-
Placing controls -- select a control type in the Toolbox, then click on the form to place a new instance. The control is auto-named (e.g., Command1, Command2), skipping names already used by a control, a menu item or the form. Clicking the same tool again deselects it (toggles back to pointer mode). Controls nest at most 15 levels deep; a drop into a container that is already that deep goes into its parent container.
+
Placing controls -- select a control type in the Toolbox, then click on the form to place a new instance. The control is auto-named (e.g., Command1, Command2), skipping names already used by a control, a menu item, or the form. Clicking the same tool again deselects it (toggles back to pointer mode). Controls nest at most 15 levels deep; a drop into a container that is already that deep goes into its parent container.
Menu bar preview -- if the form has menu items (defined via the Menu Editor), a preview menu bar is rendered on the design window.
Delete key -- removes the selected control and everything inside it.
-
Opening a form in the designer does not mark it modified. A control whose Visible property is False is still shown on the design surface (the setting applies when the program runs). Disabling a container greys the controls inside it; a control's own Enabled = False is kept when the container is enabled again. A .frm file without an AutoSize line opens without AutoSize; AutoSize = True wins over Width and Height lines. Text values (Caption, Text and so on) are limited to 379 characters.
+
Opening a form in the designer does not mark it modified. A control whose Visible property is False is still shown on the design surface (the setting applies when the program runs). Disabling a container greys the controls inside it; a control's own Enabled = False is kept when the container is enabled again. A .frm file without an AutoSize line opens without AutoSize; AutoSize = True wins over Width and Height lines. Text values (Caption, Text, and so on) are limited to 379 characters.
Menu Editor
-
The Menu Editor (View > Menu Editor, Ctrl+E) edits the active form's menu bar and popup menus. Menus nest at most 15 levels (0-14); indenting refuses when any item below would go deeper. OK rejects an item nested more than one level below the item above it, and a blank item that has sub-items (it must be named); blank items without sub-items are dropped. The window's close box cancels. Shortcut is a key that runs the item (F2, Ctrl+X, Alt+Enter ...), shown at its right; Text is key text shown without making a shortcut (see Menu Item Properties); OK rejects a Shortcut it does not know. Renaming a menu item renames its event handlers in the code and updates the ContextMenu setting of the form and its controls; inserting, deleting and moving items does not rename anything.
+
The Menu Editor (View > Menu Editor, Ctrl+E) edits the active form's menu bar and popup menus. Menus nest at most 15 levels (0-14); indenting refuses when any item below would go deeper. OK rejects an item nested more than one level below the item above it, and a blank item that has sub-items (it must be named); blank items without sub-items are dropped. The window's close box cancels. Shortcut is a key that runs the item (F2, Ctrl+X, Alt+Enter ...), shown at its right; Text is key text shown without making a shortcut (see Menu Item Properties); OK rejects a Shortcut it does not know. Renaming a menu item renames its event handlers in the code and updates the ContextMenu setting of the form and its controls; inserting, deleting, and moving items does not rename anything.
Form Properties
-
Forms have the following design-time properties: Name, Caption, Width, Height, Left, Top, Layout (VBox or HBox), Centered, AutoSize, Resizable, HelpTopic and ContextMenu.
+
Forms have the following design-time properties: Name, Caption, Width, Height, Left, Top, Layout (VBox or HBox), Centered, AutoSize, Resizable, HelpTopic, ContextMenu, and the font (the Font row; see Properties Panel).
More Than One Copy -- "Allow more than one copy to run at once" ([Settings] MultiInstance = True): the compiled program can be started again while it is running, and each copy runs on its own (like the Clock and the File Manager). Without it, starting the program again only says that it is already running.
Help File -- a compiled help file (.hlp) for the program. It is copied next to the compiled program, and F1 in the running program opens it, at the HelpTopic of the focused control or, failing that, of the form. Add help.bas to the project and call HelpView to open it from a Help menu.
Type -- what the project makes: a program (an .app, the default) or, with Type = Control, a control file (a .wgt holding the project's UserControls for every program). See UserControls in the IDE.
-
File list -- relative paths of all the project's files: modules (.bas) in the [Modules] section, class modules (.cls) in the [Classes] section, UserControls (.ctl) in the [Controls] section and forms (.frm) in the [Forms] section.
+
File list -- relative paths of all the project's files: modules (.bas) in the [Modules] section, class modules (.cls) in the [Classes] section, UserControls (.ctl) in the [Controls] section, and forms (.frm) in the [Forms] section.
+
Fonts -- the .DFN fonts the program carries, in the [Fonts] section (see Fonts a Program Carries).
The file sections all have the same shape: one FileN line per file, numbered from 0, and a Count line:
A program can carry fonts of its own, so its text looks the same whatever fonts were installed. In Project > Properties, Add... in the Fonts row picks a .DFN font (one outside the project's directory can be copied into it); Remove takes the chosen one off the list (the file stays). The fonts are listed in the [Fonts] section of the project file. Make builds them into the program as font resources, and the program has them whenever it runs; running it from the IDE gives it the same fonts until it stops. A program carries at most 64 fonts.
+
A program's fonts are its own: other programs never see them. Where a font it carries is of a family that is installed too, its sizes and styles come first and the installed family fills in the rest. Fonts are made with the SDK's mkfont from TrueType, Type 1, and bitmap fonts -- one size and only the characters the program uses keeps a font small -- or copied from DVX's FONTS folder. Each .DFN carries the notice and licence of the font it was made from.
How Projects are Compiled
-
When the project is compiled, all source files are concatenated into a single source stream: the modules first, then the class modules, then the UserControls, then the forms. The IDE tracks which lines belong to which file so error messages and debugger locations point to the correct .bas, .cls or .frm file. The code section of each .frm file is preceded by a hidden BEGINFORM marker that ties its code to its form, and the code of each class module and UserControl is likewise marked with its class name; the header lines of a class module or UserControl are not compiled.
+
When the project is compiled, all source files are concatenated into a single source stream: the modules first, then the class modules, then the UserControls, then the forms. The IDE tracks which lines belong to which file so error messages and debugger locations point to the correct .bas, .cls, or .frm file. The code section of each .frm file is preceded by a hidden BEGINFORM marker that ties its code to its form, and the code of each class module and UserControl is likewise marked with its class name; the header lines of a class module or UserControl are not compiled.
Compile-Time Validation
Before the source is handed to the compiler, the IDE scans every .frm file and cross-checks the forms against the widget registry and the statements that reference them. Three classes of error are caught and reported without producing a binary:
Unknown widget types -- any Begin TypeName ... End block whose TypeName does not match a registered widget is flagged. The error message names the offending form, control, and type, and hints that DVX uses VB6-style names (SpinButton, not Spinner).
Unknown properties -- statements of the form CtrlName.Property = value, or reads of CtrlName.Property, are checked against the widget's declared property list. A typo such as btn.Captoin triggers a compile error with the form, control, and property name. A control name that is used with different types on different forms is not checked.
Unknown methods -- method calls CtrlName.Method ... are checked the same way against the widget's method list, catching mistakes like list.AddTiem before the program ever runs.
-
Menu items -- only Name (read), Checked and Enabled can be used on a menu item in code; Caption, Visible, RadioCheck, Shortcut and ShortcutText are design-time settings.
+
Menu items -- only Name (read), Checked, and Enabled can be used on a menu item in code; Caption, Visible, RadioCheck, Shortcut, and ShortcutText are design-time settings.
These checks use the same metadata the Properties panel and Toolbox read from each installed control, so property and method names always match the current widget set without any separate list to maintain.
Project Operations
@@ -1676,19 +1732,19 @@ Count = 1
Open Project Opens a .dbp file and loads all referenced files into memory. A project file that is 64 KB or larger, or cannot be read, is not loaded; opening it later reports "Cannot load <file>: unreadable or larger than 65535 bytes." Carriage returns are removed when files are loaded.
Save Project Writes the .dbp file to disk.
Close Project Closes the project, prompting to save unsaved changes.
- Add File Adds a .bas, .frm, .cls or .ctl file to the project. Opening a file without a project auto-creates an implicit project.
+ Add File Adds a .bas, .frm, .cls, or .ctl file to the project. Opening a file without a project auto-creates an implicit project.
New Class Module Adds a new class module to the project and opens it (see Class Modules below).
New UserControl Adds a new UserControl to the project and opens it (see UserControls in the IDE).
Rename File Gives a file a new name on disk and in the project file (see Renaming a File below).
Remove File Removes a file from the project (prompts to save if modified). Removing the startup form clears the Startup Form setting.
- Project Properties Opens a dialog for editing project metadata (name, type, author, company, version, copyright, description, icon, help file, startup form), whether more than one copy may run at once, and the Arguments for runs from the IDE.
+ Project Properties Opens a dialog for editing project metadata (name, type, author, company, version, copyright, description, icon, help file, startup form), the fonts the program carries, whether more than one copy may run at once, and the Arguments for runs from the IDE.
Renaming a File
-
Select a file in the Project Explorer and choose File > Rename File. Type the new file name; the extension may be left out (a module stays .bas, a class module .cls, a UserControl .ctl and a form .frm, and cannot be given another kind's extension). The file is renamed in the project's directory and the project file is saved at once with the new name, so the two always agree. Nothing changes, and a message says why, when the name is not a valid file name, when another project file or any file in the directory already has it, or when the file or the project file cannot be written -- an existing file is never overwritten. A file that has never been saved is simply given the new name, and is written under it when you save.
-
A class module named after its file (it has no Attribute VB_Name line, or one that matches the file name, as File > New Class Module writes it) takes the new name as its class name: its Attribute VB_Name line is changed to match (an edit that is saved with the file, like any other). The new name must then be a valid name that no other form, class module or UserControl uses. Code that uses the old class name is not changed. A UserControl named after its file (as File > New UserControl makes it) takes the new name too, on its Begin UserControl line; forms that use the old name are not changed. A form keeps its Name property; rename the form itself in the Properties window.
+
Select a file in the Project Explorer and choose File > Rename File. Type the new file name; the extension may be left out (a module stays .bas, a class module .cls, a UserControl .ctl, and a form .frm, and cannot be given another kind's extension). The file is renamed in the project's directory and the project file is saved at once with the new name, so the two always agree. Nothing changes, and a message says why, when the name is not a valid file name, when another project file or any file in the directory already has it, or when the file or the project file cannot be written -- an existing file is never overwritten. A file that has never been saved is simply given the new name, and is written under it when you save.
+
A class module named after its file (it has no Attribute VB_Name line, or one that matches the file name, as File > New Class Module writes it) takes the new name as its class name: its Attribute VB_Name line is changed to match (an edit that is saved with the file, like any other). The new name must then be a valid name that no other form, class module, or UserControl uses. Code that uses the old class name is not changed. A UserControl named after its file (as File > New UserControl makes it) takes the new name too, on its Begin UserControl line; forms that use the old name are not changed. A form keeps its Name property; rename the form itself in the Properties window.
Project Explorer
-
The Project Explorer is a tree view window listing all project files, in four groups: Forms, Modules, Classes and Controls (UserControls). Double-click a file to open it: .bas, .cls and .ctl files open in Code view, .frm files open in Design view. The selected file in the Project Explorer determines the target for View > Code and View > Designer commands.
+
The Project Explorer is a tree view window listing all project files, in four groups: Forms, Modules, Classes, and Controls (UserControls). Double-click a file to open it: .bas, .cls, and .ctl files open in Code view, .frm files open in Design view. The selected file in the Project Explorer determines the target for View > Code and View > Designer commands.
Class Modules
-
A class module (.cls file) defines a kind of object for your program: its member variables, properties and methods. The BASIC Language Reference describes how to write one (see Objects and Class Modules); this section covers working with them in the IDE.
+
A class module (.cls file) defines a kind of object for your program: its member variables, properties, and methods. The BASIC Language Reference describes how to write one (see Objects and Class Modules); this section covers working with them in the IDE.
Create -- choose File > New Class Module. The IDE adds a new class module named Class1 (Class2, and so on, when that name is taken) to the Classes group, saved in the project's directory as Class1.cls, and opens it in the Code Editor. The new file holds only its header and Option Explicit; it is written to disk when you save it.
Add an existing one -- choose File > Add File and pick a .cls file (the Class Modules file type). Class files from Visual Basic can be added as they are.
Leave the VERSION and Attribute lines at the top of the file; they are not compiled as code. Each class name must be different from the other class names, the UserControl names and the form names in the project.
+
Leave the VERSION and Attribute lines at the top of the file; they are not compiled as code. Each class name must be different from the other class names, the UserControl names, and the form names in the project.
@@ -1722,18 +1778,18 @@ Option Explicit
Private Sub UserControl_Paint ()
End Sub
-
The lines up to End are the control's header; the code follows. To use a .ctl file you already have, choose File > Add File and pick it (the UserControls file type). Open, save, rename and remove .ctl files as you do class modules: a UserControl named after its file takes the new name when the file is renamed.
+
The lines up to End are the control's header; the code follows. To use a .ctl file you already have, choose File > Add File and pick it (the UserControls file type). Open, save, rename, and remove .ctl files as you do class modules: a UserControl named after its file takes the new name when the file is renamed.
Writing the Code
-
In the Code Editor, the Object list of a UserControl holds UserControl (the control itself) beside (General). Its Function list has the events the control gets: Initialize, Terminate, Resize, Paint, Click, DblClick, MouseDown, MouseMove, MouseUp, MouseWheel, KeyDown, KeyPress, KeyUp, GotFocus and LostFocus. Picking one that has no code yet writes its procedure with the right parameters, for example:
+
In the Code Editor, the Object list of a UserControl holds UserControl (the control itself) beside (General). Its Function list has the events the control gets: Initialize, Terminate, Resize, Paint, Click, DblClick, MouseDown, MouseMove, MouseUp, MouseWheel, KeyDown, KeyPress, KeyUp, GotFocus, and LostFocus. Picking one that has no code yet writes its procedure with the right parameters, for example:
Private Sub UserControl_MouseDown (Button As Integer, Shift As Integer, X As Single, Y As Single)
End Sub
These procedures are Private: a Public one would be a method of the control.
The Header in the Properties Window
-
While a UserControl is the active file, the Properties window shows its header instead of a form's controls: Name, Width, Height, CanGetFocus, BackColor, ToolboxIcon, Description and DefaultEvent, each with the value the control takes (its default when the header leaves it out). Double-click a row to change it:
+
While a UserControl is the active file, the Properties window shows its header instead of a form's controls: Name, Width, Height, CanGetFocus, BackColor, ToolboxIcon, Description, and DefaultEvent, each with the value the control takes (its default when the header leaves it out). Double-click a row to change it:
Row How it is changed
--- -----------------
- Name Type a new name. It must be a valid name that no form, class module or other UserControl of the project uses. Forms that use the old name are not changed.
+ Name Type a new name. It must be a valid name that no form, class module, or other UserControl of the project uses. Forms that use the old name are not changed.
Width, Height Enter a whole number of pixels (at least 1): the control's natural size.
CanGetFocus Each double-click switches between True and False.
BackColor Pick a color in the color dialog.
@@ -1742,13 +1798,13 @@ End Sub
DefaultEvent Pick one of the control's Events (or none): the event the designer opens when a placed control is double-clicked.
The change is written into the header lines of the file (the rest of the file stays as it is) and saved with the file. The Code Editor shows the new header; breakpoints in the code move with the lines below it. You can also edit the header lines in the Code Editor by hand.
Trying It on a Form
-
While the project is open, its UserControls are in the Toolbox like the controls DVX comes with: with the picture of their ToolboxIcon (or their name on the button), and their Description as the tooltip. Place one on a form as any other control. The control on the design surface is a real one: its code runs, with UserControl.UserMode False, so Initialize, Resize and Paint draw it as it will look, while your clicks and keys go to the designer.
+
While the project is open, its UserControls are in the Toolbox like the controls DVX comes with: with the picture of their ToolboxIcon (or their name on the button), and their Description as the tooltip. Place one on a form as any other control. The control on the design surface is a real one: its code runs, with UserControl.UserMode False, so Initialize, Resize, and Paint draw it as it will look, while your clicks and keys go to the designer.
The Properties window lists the control's properties that can be set (its Public variables and Property Let procedures), with the values its code gives them. A value you change there is passed to its Property Let and saved in the .frm; when the control's code calls UserControl.PropertyChanged, the window shows the new values.
-
The Code Editor's Function list for the placed control has the events the control raises, then the events a form gets for any of its controls (GotFocus, LostFocus, Validate, DragDrop and DragOver); picking one writes its handler with the event's parameters (Sub Gauge1_Changed (ByVal Level As Integer)).
+
The Code Editor's Function list for the placed control has the events the control raises, then the events a form gets for any of its controls (GotFocus, LostFocus, Validate, DragDrop, and DragOver); picking one writes its handler with the event's parameters (Sub Gauge1_Changed (ByVal Level As Integer)).
Double-clicking the control on the design surface opens its DefaultEvent's handler.
-
The designer compiles the project's UserControls (with its modules and class modules, but not its forms) when the project opens and again when a .ctl file is saved, added, removed or renamed, or its header is changed in the Properties window; the controls on the design surface are made again then. While the controls have a compile error the designer cannot show them (the status line says so); compile with F5 to see the error. The code of the project's modules (outside their procedures) does not run in the designer, so module variables start empty there. A UserControl whose name is the name of a control installed for every program cannot be shown in the designer, and the program stops when it is run ("Control cannot be registered"): give it another name.
+
The designer compiles the project's UserControls (with its modules and class modules, but not its forms) when the project opens and again when a .ctl file is saved, added, removed, or renamed, or its header is changed in the Properties window; the controls on the design surface are made again then. While the controls have a compile error the designer cannot show them (the status line says so); compile with F5 to see the error. The code of the project's modules (outside their procedures) does not run in the designer, so module variables start empty there. A UserControl whose name is the name of a control installed for every program cannot be shown in the designer, and the program stops when it is run ("Control cannot be registered"): give it another name.
When the program runs, its UserControls are its own: they are registered again for the program, separately from the copies in the designer.
Control Projects and Make Control
A project whose controls every program should be able to use is a control project: in File > Project Properties set Type to Control. A control project needs no forms. File > Make Executable becomes File > Make Control: it compiles the project, asks where to save the control file (the project's name with .wgt) and for a debug or release build, and writes the file -- the same file the BASIC compiler makes from the project. Run (F5) only compiles a control project: it is not a program.
@@ -1766,7 +1822,8 @@ End Sub
Property list -- a two-column ListView below the tree showing property names and values for the selected control. Double-click a property value to edit it via an InputBox dialog. Changes take effect immediately in the designer preview.
Each control type exposes different properties (e.g., Caption, Text, Width, Height, MaxWidth, MaxHeight, Weight, Alignment, Enabled, Visible, and type-specific properties like DataSource and DataField for data-bound controls). A UserControl placed on a form lists the properties its code defines.
-
While a UserControl (.ctl) is the active file, the panel shows the control's header instead: Name, Width, Height, CanGetFocus, BackColor, ToolboxIcon, Description and DefaultEvent (see UserControls in the IDE).
+
The Font row shows the font the selected control, or the form, draws with: its family, size in pixels, and styles, such as "DejaVu Sans, 14, Bold". Double-click it to choose a font in the Font dialog; the design surface shows it at once. The choice is kept as the six Font properties (FontName, FontSize, FontBold, FontItalic, FontUnderline, and FontStrikethru), which then appear in the list and can be changed one at a time too. Choosing the font the control would have anyway (its container's or the form's) removes them, so the control follows the form's font again. A font chosen for the form is used by every control that has none of its own. For a TextBox, TextArea, ComboBox, SpinButton, or Terminal the dialog lists only fixed-pitch families.
+
While a UserControl (.ctl) is the active file, the panel shows the control's header instead: Name, Width, Height, CanGetFocus, BackColor, ToolboxIcon, Description, and DefaultEvent (see UserControls in the IDE).
The Toolbox (Window > Toolbox) is a floating palette of buttons, one for each available control type. Every widget registered with DVX that has a BASIC name appears in the toolbox: the controls DVX comes with, the controls written in BASIC that are installed for every program (control files in the WIDGETS folder), and the open project's own UserControls (see UserControls in the IDE). The buttons are in alphabetical order of the control's name. A button shows the control's picture, or its name when it has none; its tooltip is the control's description.
+
The Toolbox opens four buttons across and as tall as its buttons need, down to the bottom of the screen. Resize it like any window: the buttons rearrange to fill its width, and when they do not all fit, a scroll bar reaches the rest.
Click a tool to select it (the active tool name is stored in the designer state), then click on the form to place a new instance. Click the same tool again to deselect it and return to pointer mode.
Available Controls
VB Name Description
@@ -1795,6 +1853,8 @@ End Sub
OptionButton Radio button (mutually exclusive within a group).
PictureBox Canvas for drawing and images.
ProgressBar Progress indicator bar.
+ RichEdit Text box for text in many fonts, styles, and colors.
+ RichLabel Text in many fonts, styles, and colors, in aligned paragraphs.
ScrollPane Scrollable container for large content.
Spacer Invisible spacing element for layout.
SpinButton Numeric up/down spinner.
@@ -1851,7 +1911,7 @@ End Sub
The current debug line (when paused) has a yellow background.
Breakpoint Adjustment on Edit
-
Breakpoints follow their statements when lines are added, removed, joined, split or pasted. A breakpoint whose line is deleted is removed.
+
Breakpoints follow their statements when lines are added, removed, joined, split, or pasted. A breakpoint whose line is deleted is removed.
Stepping
Action Shortcut Behavior
------ -------- --------
@@ -1982,7 +2042,7 @@ End Sub
Output Window
The Output window is a read-only TextArea at the bottom-left of the screen. It displays:
-
PRINT output -- PRINT statement output from the running program is appended here, until the program uses a console or graphics statement (CLS, LOCATE, COLOR, SCREEN, LINE, INKEY$ and the others): from then on PRINT and INPUT use the program's own screen window instead (see Console and Graphics in the language reference).
+
PRINT output -- PRINT statement output from the running program is appended here, until the program uses a console or graphics statement (CLS, LOCATE, COLOR, SCREEN, LINE, INKEY$, and the others): from then on PRINT and INPUT use the program's own screen window instead (see Console and Graphics in the language reference).
Runtime errors -- if the VM encounters a runtime error (division by zero, out-of-bounds, etc.), the error message and line number are displayed in the output with an Error on line N: prefix.
Compile errors -- if compilation fails, the error message and location are shown.
@@ -2006,7 +2066,7 @@ End Sub
Buttons
Button Action
------ ------
- Find Next Find the next occurrence. Function scope searches the current procedure, Object scope the selected object's procedures, File scope the current file and Project scope every file. At the end of the current procedure the search wraps around to its start.
+ Find Next Find the next occurrence. Function scope searches the current procedure, Object scope the selected object's procedures, File scope the current file, and Project scope every file. At the end of the current procedure the search wraps around to its start.
Replace Replace the current match and find the next one.
Replace All Replace all occurrences within the selected scope.
Close Close the dialog.
@@ -2035,7 +2095,7 @@ End Sub
Copyright Default copyright notice. (empty)
Description Default project description (one line). (empty)
Colors Tab
-
Customizes the syntax-highlighting colors used in the code editor. The left side is a list of seven color entries (Default Text, Keywords, Strings, Comments, Numbers, Operators, Types). Select an entry to see its color, and its red, green and blue amounts, on the right. Click Change... (or double-click the entry) to pick a new color in the color box: click a basic color or mix one with the Red, Green and Blue sliders or number boxes, then click OK. Changes are applied to the open editor when you click OK in the Preferences dialog.
+
Customizes the syntax-highlighting colors used in the code editor. The left side is a list of seven color entries (Default Text, Keywords, Strings, Comments, Numbers, Operators, Types). Select an entry to see its color, and its red, green, and blue amounts, on the right. Click Change... (or double-click the entry) to pick a new color in the color box: click a basic color or mix one with the Red, Green, and Blue sliders or number boxes, then click OK. Changes are applied to the open editor when you click OK in the Preferences dialog.
This release fixes a large number of problems in the language, the runtime, the controls and the IDE. Most programs keep working unchanged. Three changes need attention in existing programs: variable scope (see Module Variables Need SHARED), the order of the bytes in a color, and text stored in numbers (see Colors and Numbers Follow Visual Basic).
+
This release fixes a large number of problems in the language, the runtime, the controls, and the IDE. Most programs keep working unchanged. Three changes need attention in existing programs: variable scope (see Module Variables Need SHARED), the order of the bytes in a color, and text stored in numbers (see Colors and Numbers Follow Visual Basic).
Module Variables Need SHARED
DVX BASIC now uses the QBasic scope rules. A SUB or FUNCTION sees a variable declared at module level (including the code section of a form) only when it is declared with DIM SHARED, or named in a SHARED statement inside the procedure. Before, every module variable was visible everywhere.
-
In an existing program, change DIM to DIM SHARED for every module-level variable that a SUB, FUNCTION or event handler uses.
+
In an existing program, change DIM to DIM SHARED for every module-level variable that a SUB, FUNCTION, or event handler uses.
Without that change such a name silently becomes a local variable of the procedure (or a compile error with OPTION EXPLICIT, or when the procedure indexes or dots it).
New: the SHARED statement inside a procedure, and REDIM SHARED at module level. DIM SHARED inside a procedure is a compile error.
-
CONSTs, TYPEs, procedures, forms and controls are visible everywhere as before; a DEF FN still sees every module variable.
+
CONSTs, TYPEs, procedures, forms, and controls are visible everywhere as before; a DEF FN still sees every module variable.
A color is &HBBGGRR, as in Visual Basic: RGB(255, 0, 0) is &HFF (red) and &HFF0000 is blue. RGB, GETRED / GETGREEN / GETBLUE, ForeColor, BackColor, FillColor, the drawing methods, POINT, the Canvas control and DlgColor all use this form. A program that wrote colors as &HRRGGBB numbers must swap the red and blue bytes, or build its colors with RGB. New: QBCOLOR(n) and the constants vbBlack, vbRed, vbGreen, vbYellow, vbBlue, vbMagenta, vbCyan and vbWhite.
-
Text used as a number must be a number, as in Visual Basic: n = "12" stores 12, but n = "abc", "abc" + 2 or a READ of DATA abc into a number raise error 13 (Type mismatch) and leave the variable as it was. VAL still reads the number at the start of any text, and INPUT reads numbers the way VAL does.
+
A color is &HBBGGRR, as in Visual Basic: RGB(255, 0, 0) is &HFF (red) and &HFF0000 is blue. RGB, GETRED / GETGREEN / GETBLUE, ForeColor, BackColor, FillColor, the drawing methods, POINT, the Canvas control, and DlgColor all use this form. A program that wrote colors as &HRRGGBB numbers must swap the red and blue bytes, or build its colors with RGB. New: QBCOLOR(n) and the constants vbBlack, vbRed, vbGreen, vbYellow, vbBlue, vbMagenta, vbCyan, and vbWhite.
+
Text used as a number must be a number, as in Visual Basic: n = "12" stores 12, but n = "abc", "abc" + 2, or a READ of DATA abc into a number raise error 13 (Type mismatch) and leave the variable as it was. VAL still reads the number at the start of any text, and INPUT reads numbers the way VAL does.
A Long result too large for a Long raises error 6 (Overflow), as in Visual Basic, instead of becoming a Double (an Integer result still becomes a Long). Use CDBL on an operand when a larger result is expected.
GOSUB and RETURN work inside SUB and FUNCTION: each procedure call has its own GOSUB stack. RETURN with no pending GOSUB in the procedure raises error 3 (RETURN without GOSUB); before, RETURN inside a procedure left the procedure (use EXIT SUB / EXIT FUNCTION for that).
-
Labels belong to their SUB or FUNCTION (or to the module level). GOTO, GOSUB, ON GOTO/GOSUB and ON ERROR GOTO reach only labels of the same procedure; the same label name can be used in several procedures, and a label may share a name with a variable. An undefined label, or a jump to a SUB or variable name, is a compile error. "SubName:" at the start of a line is an error instead of a silent label.
+
Labels belong to their SUB or FUNCTION (or to the module level). GOTO, GOSUB, ON GOTO/GOSUB, and ON ERROR GOTO reach only labels of the same procedure; the same label name can be used in several procedures, and a label may share a name with a variable. An undefined label, or a jump to a SUB or variable name, is a compile error. "SubName:" at the start of a line is an error instead of a silent label.
A CONST inside a SUB or FUNCTION is local and no longer overrides a module constant. CONST name AS type converts the literal (Integer and Long round and range-check); a string/number mismatch or an overflow is a compile error.
DEF FN must be defined before it is used; its free names are module variables.
-
DEFINT, DEFLNG and the other DEFtype statements also apply to parameters, STATIC variables and FUNCTION results. A typed FUNCTION converts the value assigned to it (FUNCTION F AS INTEGER with F = 2.5 returns 2). STATIC variables convert on store and their names no longer leak out of the procedure.
+
DEFINT, DEFLNG, and the other DEFtype statements also apply to parameters, STATIC variables, and FUNCTION results. A typed FUNCTION converts the value assigned to it (FUNCTION F AS INTEGER with F = 2.5 returns 2). STATIC variables convert on store and their names no longer leak out of the procedure.
DIM a, b AS STRING, c(3) declares several variables in one statement.
name(...) in an expression only calls FUNCTIONs; a SUB there, or indexing a scalar or CONST, is a compile error.
A FUNCTION can be called as a statement (F 3), discarding its result.
DECLARE must match the definition's BYVAL and OPTIONAL flags too.
SWAP and GET #n, , arr(i) evaluate each subscript exactly once. A BYREF argument written as an expression such as arr(i) + 1 is passed by value.
Numeric literals: an Integer/Long suffix on a literal with a fraction or exponent rounds (1E3% is 1000); -32768% and -2147483648& are accepted; 1. is a valid literal; an over-long literal is an error.
-
DATA: unquoted items are raw text up to the next comma, colon, comment or end of line.
-
READ, INPUT, INPUT # and LINE INPUT # accept array elements and TYPE fields; PRINT #n, alone writes an empty line.
+
DATA: unquoted items are raw text up to the next comma, colon, comment, or end of line.
+
READ, INPUT, INPUT #, and LINE INPUT # accept array elements and TYPE fields; PRINT #n, alone writes an empty line.
PRINT USING: ; and , both separate values, and a trailing ; or , suppresses the newline.
More than 8 array dimensions, or an LBOUND/UBOUND dimension outside 1..8, is a compile error.
-
A missing END SUB, END FUNCTION, END TYPE or END DECLARE is a compile error.
+
A missing END SUB, END FUNCTION, END TYPE, or END DECLARE is a compile error.
FOR: a constant fractional STEP with an Integer or Long counter is a compile error, and a computed STEP is rounded to the counter's type. A fractional STEP no longer turns a Single counter into a Double.
DECLARE LIBRARY arguments are always passed by value, converted to the declared parameter type (a Single parameter receives a Single).
-
Name(idx).Member works with any member name (including keywords such as Name and Text), with method calls and in chains; Me.Method (x) is a method call.
+
Name(idx).Member works with any member name (including keywords such as Name and Text), with method calls, and in chains; Me.Method (x) is a method call.
Compile errors read "Line N, Col C: message" and point at what is wrong (the column is left out only at the end of the source). A SUB called but never defined is reported where it is called.
-
Built-in function names are not reserved: a variable, constant, SUB or FUNCTION the program declares with the same name is used instead (see How Names Are Found in the language reference).
-
Assigning a whole TYPE value to a TYPE variable, array element or field copies it into the record that is there, as in Visual Basic, so a SUB that was passed one of its fields by reference sees the new value.
+
Built-in function names are not reserved: a variable, constant, SUB, or FUNCTION the program declares with the same name is used instead (see How Names Are Found in the language reference).
+
Assigning a whole TYPE value to a TYPE variable, array element, or field copies it into the record that is there, as in Visual Basic, so a SUB that was passed one of its fields by reference sees the new value.
A program's compiled code is no longer limited to 64 KB, and programs compile faster with less memory (a program with many procedures twice as fast).
New: COMMAND$ returns the arguments the program was started with (for example the document it was opened with), and COMMAND$(n) returns the nth argument without its quotes. The DVX programs that open documents (Notepad, Image Viewer, Icon Editor, Resource Editor, Help Editor) open the file named there when they start. In the IDE, set the arguments under Project Properties. A program that uses COMMAND$ does not run on an older DVX.
-
A SUB or FUNCTION may use at most 64 local slots (parameters, local variables and hidden temporaries). More is a compile error "Too many local variables in X (n, limit is 64)" instead of a runtime error 9; READ and INPUT # into many array elements no longer use a slot per target.
+
A SUB or FUNCTION may use at most 64 local slots (parameters, local variables, and hidden temporaries). More is a compile error "Too many local variables in X (n, limit is 64)" instead of a runtime error 9; READ and INPUT # into many array elements no longer use a slot per target.
SHARED naming a variable that does not exist at module level creates it there; in a form's code it becomes a variable of that form, not a program-wide one.
-
New types Byte (0 to 255) and Currency (an exact amount with four decimal places, suffix @), with CBYTE, CCUR, DEFBYTE and DEFCUR. A variable declared with a type has that type before anything is stored in it (VARTYPE of an unassigned Long is vbLong).
-
Named arguments, as in Visual Basic: Report title:="Sales", width:=60 for SUBs, FUNCTIONs, class methods and the built-in functions that have argument names (MsgBox, InputBox, Mid, InStr, Format, DateAdd and others).
-
QBasic line numbers: a line may start with a number from 0 to 65529, which GOTO, GOSUB, ON GOTO, IF ... THEN 100, RESTORE and RESUME can name; ERL gives the last line number before the error. RESTORE label and RESUME label are new too.
+
New types Byte (0 to 255) and Currency (an exact amount with four decimal places, suffix @), with CBYTE, CCUR, DEFBYTE, and DEFCUR. A variable declared with a type has that type before anything is stored in it (VARTYPE of an unassigned Long is vbLong).
+
Named arguments, as in Visual Basic: Report title:="Sales", width:=60 for SUBs, FUNCTIONs, class methods, and the built-in functions that have argument names (MsgBox, InputBox, Mid, InStr, Format, DateAdd, and others).
+
QBasic line numbers: a line may start with a number from 0 to 65529, which GOTO, GOSUB, ON GOTO, IF ... THEN 100, RESTORE, and RESUME can name; ERL gives the last line number before the error. RESTORE label and RESUME label are new too.
STATIC arrays and STATIC TYPE variables keep their contents between calls.
-
Members of an object whose class the compiler knows (declared As the class or As Collection, New, Me in a class) are checked when the program is compiled: a misspelled or Private member, the wrong number of arguments, a Sub used as a value, a property used as a statement or assigned without a Property Let are compile errors that point at the member, and the calls run faster. Through As Object and As Variant members are still found when the program runs. A class's own member variables can be used anywhere in the class, and Me reaches its Private members. Passing a number, text or an object of another class to an object parameter of a known class is a compile error.
+
Members of an object whose class the compiler knows (declared As the class or As Collection, New, Me in a class) are checked when the program is compiled: a misspelled or Private member, the wrong number of arguments, a Sub used as a value, a property used as a statement or assigned without a Property Let are compile errors that point at the member, and the calls run faster. Through As Object and As Variant members are still found when the program runs. A class's own member variables can be used anywhere in the class, and Me reaches its Private members. Passing a number, text, or an object of another class to an object parameter of a known class is a compile error.
-
Objects, Classes and Variants
+
Objects, Classes, and Variants
-
New: class modules (.cls files) define your own objects, with Public and Private member variables, Subs and Functions, Property Get / Let / Set, Class_Initialize and Class_Terminate, Me and a default member. Objects are made with NEW (DIM x AS NEW Account, SET x = NEW Account), compared with Is and tested with TypeOf ... Is. An object is freed, and its Class_Terminate run, as soon as nothing refers to it any more.
+
New: class modules (.cls files) define your own objects, with Public and Private member variables, Subs and Functions, Property Get / Let / Set, Class_Initialize and Class_Terminate, Me, and a default member. Objects are made with NEW (DIM x AS NEW Account, SET x = NEW Account), compared with Is and tested with TypeOf ... Is. An object is freed, and its Class_Terminate run, as soon as nothing refers to it any more.
New: the Collection object (Add, Remove, Item, Count, with optional text keys) and FOR EACH ... NEXT over Collections and arrays.
-
New: the Variant type (DIM v AS VARIANT) with the special values Empty and Null, and the functions VARTYPE, TYPENAME, ISEMPTY, ISNULL, ISNUMERIC, ISOBJECT, ISARRAY and CVAR, with the vb... VarType constants and vbObjectError.
+
New: the Variant type (DIM v AS VARIANT) with the special values Empty and Null, and the functions VARTYPE, TYPENAME, ISEMPTY, ISNULL, ISNUMERIC, ISOBJECT, ISARRAY, and CVAR, with the vb... VarType constants and vbObjectError.
New: PUBLIC and PRIVATE at module level (PUBLIC x is DIM SHARED x, PRIVATE x is DIM x).
New error numbers: 94 Invalid use of Null, 424 Object required, 438 Object doesn't support this property or method, 450 Wrong number of arguments or invalid property assignment, 451 Object not a collection, 457 duplicate Collection key.
Changed: CHOOSE with an index outside its list returns Null instead of raising error 5.
Changed: a PARAMARRAY without an AS clause is an array of Variants (TYPENAME "Variant()").
A form or control name can be used directly wherever an object is expected: SET c = Text1, either side of Is, TypeOf, and an argument for an object parameter.
-
NEW, PROPERTY, PUBLIC, PRIVATE and TYPEOF are now reserved words: a variable, SUB or FUNCTION with one of these names must be renamed. VARIANT, OBJECT, COLLECTION, EMPTY, NULL and EACH are colored like keywords but are not reserved.
-
IDE: File > New Class Module, .cls files in Add File, a Classes group in the Project Explorer and a [Classes] section in the project file. The debugger shows Variants, objects and the member variables of the object whose code is running.
+
NEW, PROPERTY, PUBLIC, PRIVATE, and TYPEOF are now reserved words: a variable, SUB, or FUNCTION with one of these names must be renamed. VARIANT, OBJECT, COLLECTION, EMPTY, NULL, and EACH are colored like keywords but are not reserved.
+
IDE: File > New Class Module, .cls files in Add File, a Classes group in the Project Explorer, and a [Classes] section in the project file. The debugger shows Variants, objects, and the member variables of the object whose code is running.
New: events -- EVENT and RAISEEVENT in class modules, DIM WithEvents variables in modules and forms with handlers named variable_EventName.
New: IMPLEMENTS -- a class can provide the members of another class (the interface) and its objects can be used through variables of the interface type.
New: ISMISSING for OPTIONAL Variant parameters; arguments can be left out in the middle of a call (Report a, , c); FORMAT$ without a format string; LSET of whole TYPE records.
Changed: objects that only refer to each other in a loop are found and freed while the program runs (their Class_Terminate runs first) instead of staying until the program ends.
Changed: an array stored in a Variant is a copy, not shared; ParamArray arguments that are variables are passed by reference; a left-out Variant argument of a method is Missing instead of Empty.
New error numbers: 448 Named argument not found (a Missing value used), 449 Argument not optional.
-
EVENT, RAISEEVENT, WITHEVENTS and IMPLEMENTS are colored like keywords but are not reserved.
-
New: default values for OPTIONAL parameters (Optional count As Integer = 10), in procedures, class methods and Property procedures.
+
EVENT, RAISEEVENT, WITHEVENTS, and IMPLEMENTS are colored like keywords but are not reserved.
+
New: default values for OPTIONAL parameters (Optional count As Integer = 10), in procedures, class methods, and Property procedures.
New: a class module can declare WithEvents member variables and handle the events of the objects it holds; listening never keeps an object alive.
-
Changed: ParamArray arguments that are array elements, TYPE fields or a class's member variables are passed by reference too, and so are variables and array elements given to a class's methods through As Object or As Variant variables (for parameters that are not BYVAL).
+
Changed: ParamArray arguments that are array elements, TYPE fields, or a class's member variables are passed by reference too, and so are variables and array elements given to a class's methods through As Object or As Variant variables (for parameters that are not BYVAL).
New: PLAY's X command and "=" numbers with VARPTR$, as in QBasic; each screen page keeps its own text cursor; a PictureBox fires Paint after its size changes.
New: OPTIONAL object parameters can have the default Nothing (Optional target As Object = Nothing).
New: TYPE fields given to a class's methods (obj.Move rec.x) are passed by reference; values a method stores in a variable passed by reference, or copies back from its ParamArray, are converted to the variable's type. The methods of forms and controls take their arguments by value.
@@ -2151,22 +2211,22 @@ End Sub
New: LOADPICTURE returns a Picture object for DragIcon and the Picture of Image and ImageButton (file names still work); new error 481 Invalid picture.
New: while a control is dragged, the pointer shows the "no drop" sign over objects that have no DragDrop event procedure and outside the program's forms.
IDE: File > Rename File; WithEvents variables and their class's events in the code window's dropdowns; date literals and the console and graphics statement names are colored.
-
Changed: a parameter passed by reference is the caller's variable itself, as in Visual Basic. A TYPE field given to a SUB or FUNCTION (Bump rec.count, Bump recs(2).count) is now passed by reference (it was passed by value); a write reaches the variable at once instead of when the call returns, also through a ParamArray, a class's method and RaiseEvent, so it stays when the procedure later stops with an error and every handler of an event writes to the same variable; a STRING * n variable or field keeps its length at every write. The debugger shows (and changes) the caller's variable for such a parameter.
+
Changed: a parameter passed by reference is the caller's variable itself, as in Visual Basic. A TYPE field given to a SUB or FUNCTION (Bump rec.count, Bump recs(2).count) is now passed by reference (it was passed by value); a write reaches the variable at once instead of when the call returns, also through a ParamArray, a class's method, and RaiseEvent, so it stays when the procedure later stops with an error and every handler of an event writes to the same variable; a STRING * n variable or field keeps its length at every write. The debugger shows (and changes) the caller's variable for such a parameter.
New statements from QBasic: BEEP, SOUND frequency, duration (in clock ticks, 18.2 a second) and PLAY with the whole music macro language (notes, octaves, lengths, dots, pauses, tempo, MN/ML/MS, and MF/MB for foreground or background music), plus the PLAY(n) function (notes still waiting in the background). They play on whatever sound hardware the computer has, or silently when there is none.
+
New statements from QBasic: BEEP, SOUND frequency, duration (in clock ticks, 18.2 a second), and PLAY with the whole music macro language (notes, octaves, lengths, dots, pauses, tempo, MN/ML/MS, and MF/MB for foreground or background music), plus the PLAY(n) function (notes still waiting in the background). They play on whatever sound hardware the computer has, or silently when there is none.
PLAY's new I command plays the notes that follow on a General MIDI instrument (I1 to I128) through the music synthesizer; I0 goes back to tones.
New functions from QBasic: STICK(n) (joystick position, 1 to 200) and STRIG(n) (joystick buttons), on any joystick DVX knows, or the keyboard when there is none.
New statement FRAMEWAIT rate: waits for the next frame of a steady clock, keeping the desktop running, so a game loop runs at the same speed on every computer.
-
BEEP, SOUND, PLAY and FRAMEWAIT are now keywords, and STICK and STRIG built-in functions: a variable, SUB or FUNCTION with one of these names must be renamed.
+
BEEP, SOUND, PLAY, and FRAMEWAIT are now keywords, and STICK and STRIG built-in functions: a variable, SUB, or FUNCTION with one of these names must be renamed.
Message boxes (MsgBox) play the desktop sound chosen for their kind on the Control Panel's Sounds tab.
Nested BYREF parameters reach the original variable or array element.
@@ -2174,8 +2234,8 @@ End Sub
RESUME NEXT after an error in an IF/ELSEIF/WHILE/DO/LOOP condition skips the block or leaves the loop; after an error in a FOR statement the loop is skipped; after an error in the SELECT CASE expression the whole SELECT CASE block is skipped; after an error in a CASE test that test counts as false and the next value or CASE is tried; after an error in an ON selector no branch is taken.
New error numbers: 3 RETURN without GOSUB, 14 Out of string space, 28 Out of stack space (was reported as 7), 63 Bad record number, 91 Object variable not set. ERR is a Long, and ERROR n reports the standard message for known numbers.
Using a property or method (or Show, Hide, Unload) on a value that is not a form or control raises error 91 instead of doing nothing.
-
EXP, ^ and other math that leaves the numeric range raise error 6 (Overflow); an undefined result raises error 5.
-
INT, FIX and SGN return an Integer (Long or Double when needed); RND returns a Double and follows QBasic (RND(0) repeats the last number, a negative argument reseeds). RANDOMIZE with a negative or very large seed, and RND with a very large negative argument, seed predictably (the seed is taken modulo 2^32), so the sequence for a negative RANDOMIZE seed may differ from before. -x of the most negative Integer or Long widens like 0 - x.
+
EXP, ^, and other math that leaves the numeric range raise error 6 (Overflow); an undefined result raises error 5.
+
INT, FIX, and SGN return an Integer (Long or Double when needed); RND returns a Double and follows QBasic (RND(0) repeats the last number, a negative argument reseeds). RANDOMIZE with a negative or very large seed, and RND with a very large negative argument, seed predictably (the seed is taken modulo 2^32), so the sequence for a negative RANDOMIZE seed may differ from before. -x of the most negative Integer or Long widens like 0 - x.
INSTR follows OPTION COMPARE TEXT. INSTR and the MID$ statement reject a start below 1 (error 5); MID$ with a length of 0 replaces nothing.
PRINT # writes a space after numbers, as PRINT does. PRINT USING and FORMAT$ show a value too wide for the field in scientific notation.
SEEK on a RANDOM file works in records (statement and function); a position below 1 is error 63. PUT of a value longer than the record length raises error 59 before writing anything.
@@ -2184,13 +2244,13 @@ End Sub
IniRead and IniWrite use the same INI reader as the DVX system settings. IniWrite keeps the text of every other line (of any length) unchanged, ends every line of the saved file with CR LF, and raises error 75 when it cannot read the existing file or write the new one (a write failure used to be ignored); a failed write leaves the old file intact. IniRead no longer finds keys in the tail of a long line, and keys above the first section header are read with section "". Section names are matched with surrounding blanks ignored.
VAL keeps the magnitude of very long digit strings. Running out of memory while building a string raises error 14.
REDIM PRESERVE of a TYPE array keeps the existing elements.
-
Appending to a String variable with s = s + x or s = s & x no longer copies the whole string each time, so building long strings in a loop is much faster. When x is InputBox$, MsgBox, CreateForm, CreateControl or Shell, s is still read before x runs (left to right).
-
And, Or, Xor, Eqv and Imp of two Booleans give a Boolean, and Not of a Boolean gives a Boolean, as in Visual Basic 4 and later: Print (a > 1) Or (b > 1) prints True instead of -1, and TypeName gives "Boolean". With a number on either side they still work on the bits and give a number.
-
Big disks and long file names: where the disk takes long file names (DVX under Windows, or a long file name driver loaded before DVX), OPEN, KILL, NAME, MKDIR, DIR$ and the other file statements use them, and the File Manager, the file dialogs and Terminal downloads keep them. A file can be bigger than 2 GB on a FAT32 disk; LOF, FILELEN, LOC and SEEK raise error 6 (Overflow) for a size or position past 2 GB rather than give a wrong number, and EOF stays right below it. FileOpsFreeBytes and FileOpsTotalBytes are right for disks past 2 GB.
-
Paths may be written with / or \ wherever a path is taken (OPEN, KILL, NAME, MKDIR, CHDIR, DIR$, FILELEN and the file and folder functions of the libraries), mixed too; paths DVX gives back (CURDIR$, App.Path, App.Data, the file dialogs, the FileOps results) use \, as DOS shows them. New: FileOpsPathNative$ gives a path of your own that form. DIR$("C:\*.*") lists the root of C: (before, it listed C:'s current folder). SHELL commands still go to DOS as written, where / starts a switch.
+
Appending to a String variable with s = s + x or s = s & x no longer copies the whole string each time, so building long strings in a loop is much faster. When x is InputBox$, MsgBox, CreateForm, CreateControl, or Shell, s is still read before x runs (left to right).
+
And, Or, Xor, Eqv, and Imp of two Booleans give a Boolean, and Not of a Boolean gives a Boolean, as in Visual Basic 4 and later: Print (a > 1) Or (b > 1) prints True instead of -1, and TypeName gives "Boolean". With a number on either side they still work on the bits and give a number.
+
Big disks and long file names: where the disk takes long file names (DVX under Windows, or a long file name driver loaded before DVX), OPEN, KILL, NAME, MKDIR, DIR$, and the other file statements use them, and the File Manager, the file dialogs, and Terminal downloads keep them. A file can be bigger than 2 GB on a FAT32 disk; LOF, FILELEN, LOC, and SEEK raise error 6 (Overflow) for a size or position past 2 GB rather than give a wrong number, and EOF stays right below it. FileOpsFreeBytes and FileOpsTotalBytes are right for disks past 2 GB.
+
Paths may be written with / or \ wherever a path is taken (OPEN, KILL, NAME, MKDIR, CHDIR, DIR$, FILELEN, and the file and folder functions of the libraries), mixed too; paths DVX gives back (CURDIR$, App.Path, App.Data, the file dialogs, the FileOps results) use \, as DOS shows them. New: FileOpsPathNative$ gives a path of your own that form. DIR$("C:\*.*") lists the root of C: (before, it listed C:'s current folder). SHELL commands still go to DOS as written, where / starts a switch.
The IDE shows a project's file paths with \ and keeps writing them with / in the .dbp file, so a project reads the same everywhere.
-
The Open and Save As dialogs (DlgFileOpen$, DlgFileSave$ and the IDE's own) have a Drive list beside the Path box (Alt+D); a drive with no disk in it says it is not ready.
-
Changed: FileOpsValidName takes the folder the name is for, FileOpsValidName(folder, itemName), since the rules depend on its disk; XferSafeName$ takes the folder a file is received into, XferSafeName$(name, folder). New: FileOpsLongNames, FileOpsShortPath$, FileOpsValidLongName and FileOpsValidShortName (fileops.bas), and ShellArgQuote$ (shell.bas), which puts a path with spaces in quotes for a command line.
+
The Open and Save As dialogs (DlgFileOpen$, DlgFileSave$, and the IDE's own) have a Drive list beside the Path box (Alt+D); a drive with no disk in it says it is not ready.
+
Changed: FileOpsValidName takes the folder the name is for, FileOpsValidName(folder, itemName), since the rules depend on its disk; XferSafeName$ takes the folder a file is received into, XferSafeName$(name, folder). New: FileOpsLongNames, FileOpsShortPath$, FileOpsValidLongName, and FileOpsValidShortName (fileops.bas), and ShellArgQuote$ (shell.bas), which puts a path with spaces in quotes for a command line.
Programs compiled with an older release must be rebuilt. A program whose procedure table records more than 64 locals for a procedure is refused when it is loaded.
A class may have at most 65536 member variables and a form at most 65536 form-level variables (as many as the compiled program can number); more is a compile error ("Too many member variables in X", "Too many form-level variables"), and a program file that records more is refused when it is loaded.
@@ -2199,11 +2259,12 @@ End Sub
A reference to a removed control or to a control of an unloaded form raises an "unknown control" runtime error. CreateControl with a parent that is not a container on the same form raises "CreateControl: invalid parent".
SetEvent works on forms (Me) and menu items; a handler without parameters is called without the event's arguments.
Frame: the Caption from the .frm file or set by code now shows on the frame's border, and reading Caption gives it back (it was ignored and read back "").
-
A control's Index below 0, in a .frm file or typed in the designer, means the control is not in a control array, in the designer, the form loader and the saved file alike (a negative Index was kept, saved without an Index line and read back as a different control).
+
A control's Index below 0, in a .frm file or typed in the designer, means the control is not in a control array, in the designer, the form loader, and the saved file alike (a negative Index was kept, saved without an Index line, and read back as a different control).
ListBox: a horizontal scroll bar appears when the box is narrower than its longest item (its MaxWidth, or a narrow form); Left and Right scroll it a character at a time.
-
Padding = 0 and Spacing = 0 now mean no padding and no space (before, 0 meant the default). A form, box or ScrollPane that sets none keeps its own (4 pixels for forms, VBox and HBox; a Frame's Spacing is 0, a ScrollPane's both 0), and reading Padding or Spacing gives the pixels in use. In the designer, an empty Padding or Spacing on the form means the layout's own.
+
Padding = 0 and Spacing = 0 now mean no padding and no space (before, 0 meant the default). A form, box, or ScrollPane that sets none keeps its own (4 pixels for forms, VBox, and HBox; a Frame's Spacing is 0, a ScrollPane's both 0), and reading Padding or Spacing gives the pixels in use. In the designer, an empty Padding or Spacing on the form means the layout's own.
MaxWidth and MaxHeight win over MinWidth / MinHeight and over the size a control's contents ask for, and the space above the cap goes to the other controls instead of staying empty.
-
New drawing members on forms and PictureBoxes: FontScale (Print, TextWidth and TextHeight up to 8 times as large, for titles) and ScrollBarsNeeded (which scroll bars content of a size needs in an area, as the controls decide it; vbSBNone, vbHorizontal, vbVertical, vbBoth).
+
New drawing members on forms and PictureBoxes: FontScale (Print, TextWidth, and TextHeight up to 8 times as large, for titles) and ScrollBarsNeeded (which scroll bars content of a size needs in an area, as the controls decide it; vbSBNone, vbHorizontal, vbVertical, vbBoth).
+
Fonts: every control and form has FontName, FontSize, FontBold, FontItalic, FontUnderline, and FontStrikethru, as in Visual Basic. A control without a font of its own uses its container's or its form's. Print, TextWidth, TextHeight, PrintAccel, and TextWidthAccel on forms and PictureBoxes use the object's font. The new DlgFont in commdlg.bas shows the font dialog.
Form.Visible = True/False behaves exactly like Show/Hide, but only on a real change: Visible = True on a form that is already shown does nothing (a modal form stays modal), and on a form that has no window it is ignored. MinWidth and MinHeight read back the value that was set. Size properties are limited to 0..32767.
Disabling a container (for example Frame.Enabled = False) blocks mouse and keyboard input to every control inside it (also inside a disabled ScrollPane or Splitter), and SetFocus on such a control does nothing.
OptionButton.Value is a read/write Boolean (True = selected); the button's index is the new read-only OptionIndex. Setting Value raises no Click.
@@ -2211,14 +2272,14 @@ End Sub
ListBox SetItems "" empties the list, and every | separates items. ListView SetColumns has no length limit. TreeView Clear resets scrolling. TextArea FindNext finds a match that starts at the cursor, and the new SetCursor method moves the caret without selecting.
TabStrip/TabControl: a negative tab index selects the first tab and an index past the end the last one.
The mouse wheel over a DBGrid scrolls the rows without moving the current record, and over a Terminal scrolls its history locally.
-
.frm files: End followed by blanks or a comment (also End'comment) closes its block while End followed by a word does not, in the compiler, the build and the release renamer exactly as in the loader, Begin followed by a tab is accepted, Enabled = False on popup-menu items is applied, properties after a nested child's End apply to the parent, and Layout on a control that is not a container is reported.
-
Include libraries: sql.bas, comm.bas and commdlg.bas take and return Long (SQLFieldInt and SQLAffected no longer wrap above 32767); resource.bas string functions are declared AS STRING. CommHandshake takes an optional passphrase, and the new CommSetPlain accepts unencrypted data on a channel.
-
commdlg.bas functions are renamed to match the other include libraries: basFileOpen is now DlgFileOpen$, basFileSave is DlgFileSave$, basInputBox2 is DlgInputBox$, basInputCancelled is DlgInputCancelled, basChoiceDialog is DlgChoice, basIntInput is DlgIntInput and basPromptSave is DlgPromptSave. The old names are gone; programs that use them must be updated and rebuilt.
+
.frm files: End followed by blanks or a comment (also End'comment) closes its block while End followed by a word does not, in the compiler, the build, and the release renamer exactly as in the loader, Begin followed by a tab is accepted, Enabled = False on popup-menu items is applied, properties after a nested child's End apply to the parent, and Layout on a control that is not a container is reported.
+
Include libraries: sql.bas, comm.bas, and commdlg.bas take and return Long (SQLFieldInt and SQLAffected no longer wrap above 32767); resource.bas string functions are declared AS STRING. CommHandshake takes an optional passphrase, and the new CommSetPlain accepts unencrypted data on a channel.
+
commdlg.bas functions are renamed to match the other include libraries: basFileOpen is now DlgFileOpen$, basFileSave is DlgFileSave$, basInputBox2 is DlgInputBox$, basInputCancelled is DlgInputCancelled, basChoiceDialog is DlgChoice, basIntInput is DlgIntInput, and basPromptSave is DlgPromptSave. The old names are gone; programs that use them must be updated and rebuilt.
New in comm.bas: CommRecvEncrypted(handle, channel) returns True when every byte the last CommRecv$ on that channel returned arrived encrypted and authenticated (also when it returned nothing), and False when any arrived unencrypted (before CommHandshake or on a CommSetPlain channel).
-
KeyDown: Shift, Ctrl or Alt with F1-F12, and Alt with Home, End, PgUp, PgDn, Ins, Del and the arrow keys, report the plain key's KeyCode with the modifier in Shift, even when the modifier is released before the program sees the key.
-
Data control: while its Validate handler runs, MoveFirst, MoveLast, MoveNext, MovePrevious, AddNew, Refresh and Update on the same Data control are ignored (they used to validate the same record again without end); Delete is allowed. Only unloading the Data control itself from Validate or Reposition stops the operation that fired it; removing other controls does not.
+
KeyDown: Shift, Ctrl, or Alt with F1-F12, and Alt with Home, End, PgUp, PgDn, Ins, Del, and the arrow keys, report the plain key's KeyCode with the modifier in Shift, even when the modifier is released before the program sees the key.
+
Data control: while its Validate handler runs, MoveFirst, MoveLast, MoveNext, MovePrevious, AddNew, Refresh, and Update on the same Data control are ignored (they used to validate the same record again without end); Delete is allowed. Only unloading the Data control itself from Validate or Reposition stops the operation that fired it; removing other controls does not.
Data control: writing a field back with the value it already has is not an edit, so leaving a bound control no longer fires Validate. With a SELECT query as RecordSource, edits are dropped when the current record changes. Update and Delete still find a record whose key is a number stored without a declared column type, or is NULL, when the key names exactly one record; Delete keeps the record shown when the database row could not be deleted.
-
DBGrid re-sorts and re-measures its columns when the Data control's records change (Refresh, AddNew, Delete or an edit), keeping the chosen sort column, and not on a plain record move.
+
DBGrid re-sorts and re-measures its columns when the Data control's records change (Refresh, AddNew, Delete, or an edit), keeping the chosen sort column, and not on a plain record move.
Image and ImageButton: Picture = "" clears the picture (Picture then reads back ""); a file that cannot be loaded leaves the picture and Picture unchanged. A disabled Image keeps its transparent color transparent.
Timer: a Timer whose handler shows a modal dialog no longer fires a second time from inside that dialog.
TextArea: the horizontal scroll bar appears as soon as the longest line fills the visible width, and the caret stays visible while typing at the right edge.
@@ -2227,7 +2288,7 @@ End Sub
TabControl: switching tabs closes an open drop-down list only when it belongs to a control on the page being hidden. A Label on a Toolbar is drawn on the toolbar's color.
Compile errors from the command-line compiler name the file and line. A .frm without a Begin Form line is an error. A release build that runs out of memory while renaming names fails instead of producing a broken program.
A form shown modal has no minimize gadget.
-
Menu items accept only Name (read-only), Checked and Enabled in code; Caption, Visible, RadioCheck, Shortcut and ShortcutText are design-time settings, and using them in code is a compile error in the IDE.
+
Menu items accept only Name (read-only), Checked, and Enabled in code; Caption, Visible, RadioCheck, Shortcut, and ShortcutText are design-time settings, and using them in code is a compile error in the IDE.
IDE
@@ -2251,6 +2312,8 @@ End Sub
The title bar's * also shows for a form changed in the designer but not yet saved.
Form designer: a .frm whose container has properties after the controls inside it (Index, Caption ...; DVX writes them before) opens as it runs: the properties are kept and the controls stay in that container. Before, they were dropped, and such a file with another control of the container's name opened as a blank form. Menu items' properties after their submenus are kept too. FrmOpen reads .frm files the same way.
The Toolbox lists the controls in alphabetical order, as CtlTypeName$ does.
+
Properties window: the Font row sets a control's or the form's font in the Font dialog, and the design surface shows the fonts.
+
Toolbox: it can be resized, its buttons rearrange to fit, and a scroll bar appears when they do not all fit (with many controls installed, the bottom rows used to be off the screen).
UserControls: File > New UserControl, a Controls group in the Project Explorer, the UserControl object and its events in the Code Editor, the header in the Properties window, the project's UserControls in the Toolbox and on the design surface (their code runs, with UserMode False), and control projects with File > Make Control. A program built with Make Executable carries its UserControls. See UserControls in the IDE.
Form designer: a property value may be as long as a full .frm line (511 characters); a value too long for the line it is saved on (deeply nested controls) stops the save with "A property value is too long to save at its position in the form". Making a container part of a control array keeps its contents. Only one OptionButton per container keeps Value = True: setting it on one (in the property grid or by pasting a selected one) clears the others.
@@ -2280,7 +2343,7 @@ End Sub
----------- -----
Control A control on a form (any type of control)
Form A form
- Object Any object: a form, a control, an instance of a class module or a Collection
+ Object Any object: a form, a control, an instance of a class module, or a Collection
ClassName An instance of that class module (see Objects and Class Modules)
Collection A Collection object (see Collection Object)
Dim c As Control
@@ -2289,7 +2352,7 @@ c.Text = "Hello"
Dim people As New Collection
people.Add "Ann"
-
An object variable that has not been Set holds Nothing. Control, Form and Object variables accept each other's values; a variable declared As a class name or As Collection accepts only an object of that kind, or Nothing: an object of another kind raises error 13 (Type mismatch), and a value that is not an object at all raises error 424 (Object required). The Source parameter of the DragDrop and DragOver events is a Control.
+
An object variable that has not been Set holds Nothing. Control, Form, and Object variables accept each other's values; a variable declared As a class name or As Collection accepts only an object of that kind, or Nothing: an object of another kind raises error 13 (Type mismatch), and a value that is not an object at all raises error 424 (Object required). The Source parameter of the DragDrop and DragOver events is a Control.
These types are not directly declarable but are used internally by the runtime.
@@ -2297,8 +2360,8 @@ people.Add "Ann"
------------- -----------
Array Reference-counted multi-dimensional array (up to 8 dimensions)
UDT User-defined type instance (created with TYPE...END TYPE)
- Object Reference to a form, a control, a class instance or a Collection; declarable as Control, Form, Object, a class name or Collection
- Ref Reference to a variable, array element, TYPE field or class member variable (what a ByRef parameter holds)
+ Object Reference to a form, a control, a class instance, or a Collection; declarable as Control, Form, Object, a class name, or Collection
+ Ref Reference to a variable, array element, TYPE field, or class member variable (what a ByRef parameter holds)
Type Suffixes
Type suffixes can be appended to variable names to declare their type implicitly:
Note: Both E and D can introduce an exponent (e.g. 1.5E10 and 2.5D3 are equivalent forms of a scientific-notation double).
A literal with an Integer or Long suffix and a fraction or exponent is rounded (1E3% is 1000, 2.5% is 2). The most negative values -32768% and -2147483648& are valid. A trailing decimal point is allowed (1. is the Double 1). A numeric literal too long to read is a compile error.
Type Promotion
-
When mixing types in expressions, values are automatically promoted to a common type: Byte -> Integer -> Long -> Single -> Double. A Currency with a Byte, Integer, Long or Currency stays Currency (see Byte and Currency below). Whole-number arithmetic keeps the type of its operands: an Integer result too large for an Integer becomes a Long (32767 + 1 is the Long 32768) and a Byte result outside 0 to 255 becomes an Integer, but a Long result too large for a Long raises error 6 (Overflow), as in Visual Basic. Convert an operand when a larger result is expected: CDBL(2147483647) + 1 is 2147483648.
+
When mixing types in expressions, values are automatically promoted to a common type: Byte -> Integer -> Long -> Single -> Double. A Currency with a Byte, Integer, Long, or Currency stays Currency (see Byte and Currency below). Whole-number arithmetic keeps the type of its operands: an Integer result too large for an Integer becomes a Long (32767 + 1 is the Long 32768) and a Byte result outside 0 to 255 becomes an Integer, but a Long result too large for a Long raises error 6 (Overflow), as in Visual Basic. Convert an operand when a larger result is expected: CDBL(2147483647) + 1 is 2147483648.
+ joins two strings. When a string meets a number in an arithmetic operator (including +) or a comparison, the string must hold a number, as in Visual Basic: "1" + 2 is 3 and " 2.5 " * 2 is 5, but text that is not a number ("abc" + 2, "12abc" + 2) raises error 13 (Type mismatch). VAL reads the number at the start of any text instead (VAL("12abc") is 12). Use & to join text and numbers, and VAL / STR$ to convert explicitly.
Assignment Conversion
-
Storing a value into a variable declared as Byte, Integer, Long, Single, Double, Currency, String, Boolean or Date (by suffix, by AS clause, by DEFtype, or as a typed parameter, STATIC variable, FUNCTION result, TYPE field or array element) converts the value to that type: a number stored in a String variable becomes its text as STR$ gives it without the leading space ("5"). Text stored in a numeric variable must be a number, as in Visual Basic: "12" and " 3.5 " convert, while text that is not a number ("abc", "12abc") raises error 13 (Type mismatch) and the variable keeps its value. DEFtype statements also give parameters, STATIC variables and FUNCTION results their default type. Byte, Integer and Long round to the nearest whole number, with halves rounding to the nearest even number (2.5 becomes 2, 3.5 becomes 4), and raise error 6 (Overflow) when the result does not fit; Currency rounds to four decimal places the same way. Single keeps about 7 significant digits, so PRINT shows at most 7 digits of it. Boolean stores True for any non-zero number or non-empty string and False otherwise. Date takes a number as a date's number of days and reads a string as a date (error 13 when the text is not a date, error 6 outside 1/1/100 to 12/31/9999); INPUT into a Date variable reads the date the same way. A variable declared with DIM but without a type is Single (or the DEFtype for its first letter). A variable that is never declared keeps whatever type the assigned value has. A variable declared with a type holds that type from the start, before anything is stored in it: VARTYPE of a Long that was never assigned is 3 (vbLong) and of a String 8 (vbString).
+
Storing a value into a variable declared as Byte, Integer, Long, Single, Double, Currency, String, Boolean, or Date (by suffix, by AS clause, by DEFtype, or as a typed parameter, STATIC variable, FUNCTION result, TYPE field, or array element) converts the value to that type: a number stored in a String variable becomes its text as STR$ gives it without the leading space ("5"). Text stored in a numeric variable must be a number, as in Visual Basic: "12" and " 3.5 " convert, while text that is not a number ("abc", "12abc") raises error 13 (Type mismatch) and the variable keeps its value. DEFtype statements also give parameters, STATIC variables, and FUNCTION results their default type. Byte, Integer, and Long round to the nearest whole number, with halves rounding to the nearest even number (2.5 becomes 2, 3.5 becomes 4), and raise error 6 (Overflow) when the result does not fit; Currency rounds to four decimal places the same way. Single keeps about 7 significant digits, so PRINT shows at most 7 digits of it. Boolean stores True for any non-zero number or non-empty string and False otherwise. Date takes a number as a date's number of days and reads a string as a date (error 13 when the text is not a date, error 6 outside 1/1/100 to 12/31/9999); INPUT into a Date variable reads the date the same way. A variable declared with DIM but without a type is Single (or the DEFtype for its first letter). A variable that is never declared keeps whatever type the assigned value has. A variable declared with a type holds that type from the start, before anything is stored in it: VARTYPE of a Long that was never assigned is 3 (vbLong) and of a String 8 (vbString).
Dim n As Integer
n = 3.7 ' n is 4
n = 40000 ' Error 6: Overflow
@@ -2341,7 +2404,7 @@ x = 1 / 3 ' x was never declared and keeps the Double: prints 0.3333333
Boolean values use -1 for True and 0 for False. Any non-zero numeric value is treated as True in a conditional context. The keywords True and False are reserved and may be used anywhere a Boolean value is expected.
Byte and Currency
A Byte holds a whole number from 0 to 255 in one byte. Storing a value outside that range raises error 6 (Overflow). Arithmetic on two Bytes gives a Byte when the result is 0 to 255 and an Integer otherwise; negating a Byte gives an Integer. CBYTE converts a value to a Byte.
-
A Currency holds an amount of money exactly: it counts ten-thousandths, so it has four decimal places and adding, subtracting or multiplying amounts never drifts (adding 0.1@ ten times gives exactly 1). A value with more decimal places is rounded to four, halves to the nearest even digit (CCUR(2.00005) is 2, CCUR(2.00015) is 2.0002), and a result outside the Currency range raises error 6 (Overflow). +, - and * on a Currency and a Byte, Integer, Long or Currency give a Currency; with a Single or Double they give a Double, and / always gives a Double. Comparisons of Currency values are exact. PRINT and STR$ show a Currency with only the decimals it needs (12.5); FORMAT$ lays it out like any other number. A FOR loop with a Currency counter steps exactly. CCUR converts a value to a Currency.
+
A Currency holds an amount of money exactly: it counts ten-thousandths, so it has four decimal places and adding, subtracting, or multiplying amounts never drifts (adding 0.1@ ten times gives exactly 1). A value with more decimal places is rounded to four, halves to the nearest even digit (CCUR(2.00005) is 2, CCUR(2.00015) is 2.0002), and a result outside the Currency range raises error 6 (Overflow). +, -, and * on a Currency and a Byte, Integer, Long, or Currency give a Currency; with a Single or Double they give a Double, and / always gives a Double. Comparisons of Currency values are exact. PRINT and STR$ show a Currency with only the decimals it needs (12.5); FORMAT$ lays it out like any other number. A FOR loop with a Currency counter steps exactly. CCUR converts a value to a Currency.
Dim total As Currency
Dim i As Integer
For i = 1 To 3
@@ -2359,7 +2422,7 @@ d = #2000-01-15 15:30# ' year first, 24-hour time
d = #Jan 15, 2000 3:30:00 PM#
d = #3:30 PM# ' a time alone is on 12/30/1899
Two-digit years 00 to 29 are 2000 to 2029 and 30 to 99 are 1930 to 1999. Date is not a reserved word, so a program may still use Date (or any of the date function names) for its own variables and procedures; the program's own name wins.
-
PRINT, STR$, CSTR and the & operator show a date as m/d/yyyy h:mm:ss AM (1/15/2000 3:30:00 PM). A date at midnight shows only the date and a time on 12/30/1899 only the time; a Date variable that was never assigned is 12:00:00 AM. Use FORMAT$ for any other layout.
+
PRINT, STR$, CSTR, and the & operator show a date as m/d/yyyy h:mm:ss AM (1/15/2000 3:30:00 PM). A date at midnight shows only the date and a time on 12/30/1899 only the time; a Date variable that was never assigned is 12:00:00 AM. Use FORMAT$ for any other layout.
Adding a number of days to a date, or subtracting one, gives a date (fractions are parts of a day, so d + 0.25 is six hours later); subtracting one date from another gives the number of days between them as a Double. Comparisons put dates in time order, and a string compared with a date is read as a date:
Dim due As Date
due = Date + 30 ' thirty days from today
@@ -2372,7 +2435,7 @@ If due = "1/15/2026" Then Print "Match"
Variants
-
A Variant is a variable that can hold any kind of value: a number, text, a date, True or False, an object or a whole array. It takes on the type of whatever is stored in it, and can change type each time something new is stored. A Variant can also hold two special values that no other type has: Empty (nothing has been stored yet) and Null (the value is unknown or missing).
+
A Variant is a variable that can hold any kind of value: a number, text, a date, True or False, an object, or a whole array. It takes on the type of whatever is stored in it, and can change type each time something new is stored. A Variant can also hold two special values that no other type has: Empty (nothing has been stored yet) and Null (the value is unknown or missing).
Dim v As Variant
Print TypeName(v) ' Empty
v = 42
@@ -2384,7 +2447,7 @@ Print TypeName(v) ' Date
v = Null
Print IsNull(v) ' True
Declare a Variant with AS VARIANT. Arrays of Variants (DIM list(10) AS VARIANT), Variant parameters, and FUNCTIONs and properties AS VARIANT work the same way; each element of a Variant array starts Empty. A variable declared with DIM but no AS clause, or never declared at all, is not a Variant: it keeps the older rules described under Assignment Conversion.
-
Storing a Variant in a variable of another type converts the value to that type, as for any other assignment. A Variant that is given an array gets its own copy of the array, as in Visual Basic: after v = list, changing list(1) does not change v(1), and w = v copies again. The same holds when a Variant holding an array is passed BYVAL, stored in an object's member or added to a Collection. Index it like the array itself, v(1).
+
Storing a Variant in a variable of another type converts the value to that type, as for any other assignment. A Variant that is given an array gets its own copy of the array, as in Visual Basic: after v = list, changing list(1) does not change v(1), and w = v copies again. The same holds when a Variant holding an array is passed BYVAL, stored in an object's member, or added to a Collection. Index it like the array itself, v(1).
Dim list(2) As Integer, v As Variant
list(0) = 1
v = list ' v holds a copy
@@ -2397,20 +2460,20 @@ total = total + 5 ' Empty counts as 0: total is 5
If IsEmpty(v) Then Print "Nothing stored yet"
Null
Null means "no valid value" -- for example a field that was never filled in. Null spreads through most expressions: anything calculated from a Null is Null too, so a missing value is not mistaken for a real one.
-
Expression Result
- ---------- ------
- Null + 1, Null * x, -Null, Not Null Null
- Null = 1, Null < x, Null = Null Null (a comparison with Null is never True)
- Abs(Null), Int(Null), Fix(Null), Sgn(Null) Null
- Left(Null, 1), UCase(Null), Len(Null), ... Null (the Variant string functions; see below)
- "Name: " & Null "Name: " (& treats Null as "")
- Null & Null Null
- False And Null False
- True Or Null True
- False Imp Null, Null Imp True True
- Any other And, Or, Xor, Eqv or Imp with Null Null
-
An IF, ELSEIF, WHILE, DO or LOOP condition that is Null counts as False. Any other use that needs a real number -- storing Null in a numeric, Boolean or Date variable, CINT(Null) and the other conversion functions, a string function ending in $ such as UCASE$ or LEFT$, or an array subscript -- raises error 94 (Invalid use of Null).
-
As in Visual Basic, the string functions without $ are Variant functions that pass Null through: LEFT, RIGHT, MID, UCASE, LCASE, TRIM, LTRIM, RTRIM, CHR, HEX, OCT, STR, SPACE and STRING give Null when their text (or number) argument is Null, and so do LEN, INSTR, INSTRREV, STRCOMP, LIKE and the date parts YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, WEEKDAY and DATEPART. Their $ forms (LEFT$, UCASE$ ...) always give text and raise error 94 for Null.
+
Expression Result
+ ---------- ------
+ Null + 1, Null * x, -Null, Not Null Null
+ Null = 1, Null < x, Null = Null Null (a comparison with Null is never True)
+ Abs(Null), Int(Null), Fix(Null), Sgn(Null) Null
+ Left(Null, 1), UCase(Null), Len(Null), ... Null (the Variant string functions; see below)
+ "Name: " & Null "Name: " (& treats Null as "")
+ Null & Null Null
+ False And Null False
+ True Or Null True
+ False Imp Null, Null Imp True True
+ Any other And, Or, Xor, Eqv, or Imp with Null Null
+
An IF, ELSEIF, WHILE, DO, or LOOP condition that is Null counts as False. Any other use that needs a real number -- storing Null in a numeric, Boolean, or Date variable, CINT(Null) and the other conversion functions, a string function ending in $ such as UCASE$ or LEFT$, or an array subscript -- raises error 94 (Invalid use of Null).
+
As in Visual Basic, the string functions without $ are Variant functions that pass Null through: LEFT, RIGHT, MID, UCASE, LCASE, TRIM, LTRIM, RTRIM, CHR, HEX, OCT, STR, SPACE, and STRING give Null when their text (or number) argument is Null, and so do LEN, INSTR, INSTRREV, STRCOMP, LIKE, and the date parts YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, WEEKDAY, and DATEPART. Their $ forms (LEFT$, UCASE$ ...) always give text and raise error 94 for Null.
Because Null = Null is itself Null, test for Null with ISNULL, never with =:
If IsNull(price) Then
Print "No price yet"
@@ -2421,11 +2484,11 @@ End If
Variant Functions
Function Returns Description
-------- ------- -----------
- VARTYPE(v) Integer A number for the kind of value v holds (see VarType Constants): vbEmpty (0), vbNull (1), vbInteger (2), vbString (8), vbObject (9) and so on. For an array it is vbArray (8192) plus the element type, so an Integer array is 8194 and a Variant array 8204.
+ VARTYPE(v) Integer A number for the kind of value v holds (see VarType Constants): vbEmpty (0), vbNull (1), vbInteger (2), vbString (8), vbObject (9), and so on. For an array it is vbArray (8192) plus the element type, so an Integer array is 8194 and a Variant array 8204.
TYPENAME(v) String The name of the kind of value: "Byte", "Integer", "Long", "Single", "Double", "Currency", "String", "Boolean", "Date", "Empty", "Null", "Error" (a left-out OPTIONAL argument), the name of a TYPE, "Integer()" for an array, and for objects the class name, "Collection", "Nothing", or the type of a form or control.
ISEMPTY(v) Boolean True when v is Empty.
ISNULL(v) Boolean True when v is Null.
- ISNUMERIC(v) Boolean True when v is a number, True or False, Empty, or text that reads as a number ("12", "1.5"). False for Null, dates and other text.
+ ISNUMERIC(v) Boolean True when v is a number, True or False, Empty, or text that reads as a number ("12", "1.5"). False for Null, dates, and other text.
ISOBJECT(v) Boolean True when v is an object reference, including Nothing.
ISARRAY(v) Boolean True when v is an array.
ISMISSING(v) Boolean True when v is an OPTIONAL Variant parameter the caller left out (see Procedures).
@@ -2496,7 +2559,7 @@ End Sub
Like The text on the left matches the pattern on the right
String comparisons are controlled by OPTION COMPARE. With OPTION COMPARE BINARY (the default) strings are compared by byte value. With OPTION COMPARE TEXT comparisons are case-insensitive. A comparison with Null gives Null (see Variants).
Is compares object references, not values: a Is b is True when both refer to the very same object, and x Is Nothing tests for an object variable that holds no object. Either side may be a form or control name. Using Is on something that is not an object raises error 424 (Object required).
-
text Like pattern is True when the whole of text matches the pattern. In the pattern, ? stands for any one character, * for any run of characters (none too), # for one digit 0 to 9, [list] for one character in the list and [!list] for one character not in it; anything else stands for itself. A list holds characters and ranges such as a-z ([A-Za-z0-9_]); to match a literal ?, *, # or [, put it in brackets ([*]). OPTION COMPARE TEXT makes the match ignore case. Like with Null gives Null, and a pattern with a [ that has no ] raises error 93.
+
text Like pattern is True when the whole of text matches the pattern. In the pattern, ? stands for any one character, * for any run of characters (none too), # for one digit 0 to 9, [list] for one character in the list, and [!list] for one character not in it; anything else stands for itself. A list holds characters and ranges such as a-z ([A-Za-z0-9_]); to match a literal ?, *, #, or [, put it in brackets ([*]). OPTION COMPARE TEXT makes the match ignore case. Like with Null gives Null, and a pattern with a [ that has no ] raises error 93.
If name$ Like "A*" Then Print "Starts with A"
If code$ Like "[A-Z]##" Then Print "A letter and two digits"
If file$ Like "*.[!b]*" Then Print "Not a .b... extension"
@@ -2570,7 +2633,7 @@ Dim matrix(1 To 10, 1 To 10) As Single
Dim Shared globalFlag As Boolean
Dim record As PersonType
Dim fixedStr As String * 20
-
Note: A module-level variable is visible inside a SUB or FUNCTION only when it is declared with DIM SHARED (or named in a SHARED statement in that procedure). See Variable Scope below. Inside a SUB or FUNCTION, DIM declares a local variable that is recreated on each call (use STATIC to retain its value between calls). A SUB or FUNCTION holds at most 64 parameters and local variables together, including a few hidden ones that statements such as ON ... GOTO, SWAP and READ into array elements use while they run; a procedure that needs more fails to compile with "Too many local variables". A program holds at most 512 module-level variables, a form at most 65536 form-level variables and a class at most 65536 member variables; more fails to compile.
+
Note: A module-level variable is visible inside a SUB or FUNCTION only when it is declared with DIM SHARED (or named in a SHARED statement in that procedure). See Variable Scope below. Inside a SUB or FUNCTION, DIM declares a local variable that is recreated on each call (use STATIC to retain its value between calls). A SUB or FUNCTION holds at most 64 parameters and local variables together, including a few hidden ones that statements such as ON ... GOTO, SWAP, and READ into array elements use while they run; a procedure that needs more fails to compile with "Too many local variables". A program holds at most 512 module-level variables, a form at most 65536 form-level variables, and a class at most 65536 member variables; more fails to compile.
Fixed-length strings (STRING * n) are padded with spaces and truncated when assigned so their length is always exactly n.
DIM variable AS NEW ClassName (or AS NEW Collection) creates a new object at the DIM and stores it in the variable; without NEW an object variable starts as Nothing. See Objects and Class Modules. DIM variable AS NEW FormName makes a new copy of a form of the project (see Several Copies of a Form in the Form topic); DIM variable AS FormName declares a variable for that form or a copy of it.
Variable Scope and SHARED
@@ -2580,7 +2643,7 @@ Dim fixedStr As String * 20
A SUB or FUNCTION sees a module variable only when it was declared with DIM SHARED at module level, or when the procedure names it in a SHARED statement. This applies to arrays as well.
Any other name used inside a SUB or FUNCTION is a local variable of that procedure, even when a module variable of the same name exists. With OPTION EXPLICIT, using such a name without a DIM is a compile error that says the module variable is not SHARED.
Indexing (name(...)) or dotting (name.member) a module variable the procedure cannot see is a compile error.
-
CONSTs declared at module level, TYPEs, SUBs, FUNCTIONs, class modules, forms and controls are visible everywhere; no SHARED is needed for them. PUBLIC at module level is the same as DIM SHARED (see PUBLIC / PRIVATE).
+
CONSTs declared at module level, TYPEs, SUBs, FUNCTIONs, class modules, forms, and controls are visible everywhere; no SHARED is needed for them. PUBLIC at module level is the same as DIM SHARED (see PUBLIC / PRIVATE).
A DEF FN is module-level code: its expression sees every module variable.
DIM SHARED total AS LONG ' visible in every SUB and FUNCTION
@@ -2601,12 +2664,12 @@ END SUB
How Names Are Found
Every name in a program is looked up the same way, in this order:
-
A name the program declares is that declaration: first the procedure's own parameters, local variables, STATIC and SHARED names; then, in a class module, the class's own members; then the variables of the form whose code it is; then the module's variables, SUBs, FUNCTIONs, constants, TYPEs, ENUMs and classes, and the predefined vb constants. Inside a procedure a module variable counts only when it is shared (see above); a class's members always do.
-
Otherwise a built-in function of that name is used. Built-in names are not reserved: a variable, constant, SUB or FUNCTION the program declares with the same name wins (a FUNCTION named StrReverse replaces the built-in one).
+
A name the program declares is that declaration: first the procedure's own parameters, local variables, and STATIC and SHARED names; then, in a class module, the class's own members; then the variables of the form whose code it is; then the module's variables, SUBs, FUNCTIONs, constants, TYPEs, ENUMs, and classes, and the predefined vb constants. Inside a procedure a module variable counts only when it is shared (see above); a class's members always do.
+
Otherwise a built-in function of that name is used. Built-in names are not reserved: a variable, constant, SUB, or FUNCTION the program declares with the same name wins (a FUNCTION named StrReverse replaces the built-in one).
Otherwise, where an object is expected -- before a dot (name.Caption), after SET, around Is or TypeOf, or as an argument for an Object or class parameter -- the name is a control of the current form, or a form of that name. Form1.Text1.Text names the control Text1 on the form Form1.
Otherwise the name is a new variable: local inside a procedure, a module variable elsewhere (a DEF FN expression's names are module variables). With OPTION EXPLICIT this is a compile error.
-
A SUB or FUNCTION can be called before its definition, and a TYPE, ENUM or class -- with its member variables and procedures -- can be used before it is declared: the compiler reads every declaration first. A name after a dot is a member of the object before it: when that object's class is known (see Objects and Class Modules) the member is looked up in the class when the program is compiled, else when it runs.
+
A SUB or FUNCTION can be called before its definition, and a TYPE, ENUM, or class -- with its member variables and procedures -- can be used before it is declared: the compiler reads every declaration first. A name after a dot is a member of the object before it: when that object's class is known (see Objects and Class Modules) the member is looked up in the class when the program is compiled, else when it runs.
PUBLIC / PRIVATE
At module level, PUBLIC and PRIVATE can be used in place of DIM, as in Visual Basic. PUBLIC declares a variable that every SUB and FUNCTION can use (the same as DIM SHARED); PRIVATE declares one that only module-level code sees (the same as DIM).
Public highScore As Long ' same as Dim Shared highScore As Long
@@ -2641,12 +2704,12 @@ Const BIG = MAX_SIZE > 50
literals (numbers, strings, True and False, #date# literals);
constants declared earlier, including ENUM members (Member or EnumName.Member);
the arithmetic operators + - * / \ MOD ^, the comparison operators, AND OR NOT XOR EQV IMP, and string joining with & or +;
Anything else -- a variable, a FUNCTION of your own, or a built-in whose answer can change from run to run such as TIMER, NOW, RND or DIR$ -- is a compile error ("Constant expression required"). An expression that fails, such as a division by zero or a result too big for its type, is also reported when the program is compiled. Text comparisons in a constant follow the OPTION COMPARE setting in effect where the constant is declared. A string constant can be at most 255 characters long.
-
Without an AS clause the constant takes the type of its value: a whole number makes a Long constant, a number with a fraction a Double constant, and strings, Booleans and dates keep their type. With an AS clause the value is converted to that type: Integer and Long values are rounded and must fit the type, and a string for a numeric type (or a number for String) is a compile error.
+
Anything else -- a variable, a FUNCTION of your own, or a built-in whose answer can change from run to run such as TIMER, NOW, RND, or DIR$ -- is a compile error ("Constant expression required"). An expression that fails, such as a division by zero or a result too big for its type, is also reported when the program is compiled. Text comparisons in a constant follow the OPTION COMPARE setting in effect where the constant is declared. A string constant can be at most 255 characters long.
+
Without an AS clause the constant takes the type of its value: a whole number makes a Long constant, a number with a fraction a Double constant, and strings, Booleans, and dates keep their type. With an AS clause the value is converted to that type: Integer and Long values are rounded and must fit the type, and a string for a numeric type (or a number for String) is a compile error.
Constants declared at module level are visible to all procedures. A CONST declared inside a SUB or FUNCTION is local to that procedure; it may reuse the name of a module constant without changing it.
-
PUBLIC CONST and PRIVATE CONST work as in Visual Basic. A Private constant, and any constant of a form or class module, belongs to the file it is declared in: another module, form or class of the project can declare its own constant of the same name, and does not see this one. A Public constant, or a plain CONST in a module, is seen by the whole program. A class module or form cannot have a Public constant ("Constants are not allowed as Public members of a class module or form").
+
PUBLIC CONST and PRIVATE CONST work as in Visual Basic. A Private constant, and any constant of a form or class module, belongs to the file it is declared in: another module, form, or class of the project can declare its own constant of the same name, and does not see this one. A Public constant, or a plain CONST in a module, is seen by the whole program. A class module or form cannot have a Public constant ("Constants are not allowed as Public members of a class module or form").
Public Const APP_VERSION = "2.1" ' every file of the project
Private Const BUFFER_SIZE = 512 ' this file only
TYPE...END TYPE
@@ -2665,7 +2728,7 @@ End Type
Dim p As PersonType
p.firstName = "Scott"
p.age = 30
-
UDT fields can themselves be UDTs (nested types), and field access chains through array elements too: shapes(i).origin.x = 5. A new instance starts with numeric fields at 0, Boolean fields False, variable strings empty and STRING * n fields padded to n spaces. TYPE variables can be written to and read from RANDOM and BINARY files with PUT and GET (see GET / PUT).
+
UDT fields can themselves be UDTs (nested types), and field access chains through array elements too: shapes(i).origin.x = 5. A new instance starts with numeric fields at 0, Boolean fields False, variable strings empty, and STRING * n fields padded to n spaces. TYPE variables can be written to and read from RANDOM and BINARY files with PUT and GET (see GET / PUT).
ENUM...END ENUM
Defines a set of named whole-number constants, and a type name for variables that hold one of them.
ENUM EnumName
@@ -2691,7 +2754,7 @@ Dim heading As Direction
heading = West
If heading = Direction.West Then Print "Going west"
The first member is 0 and each member without a value is one more than the member before it. A value may be any constant expression (see CONST), including members declared earlier in the same ENUM or in another one. Members are Long constants and can be used on their own (West) or with the ENUM name in front (Direction.West). If two ENUMs have a member with the same name, that name must be written with its ENUM name.
-
The ENUM name can be used as a type wherever a type is allowed: DIM, parameters, FUNCTION results and TYPE fields. Such a variable is a Long, so it can hold any whole number, not only the members. ENUMs are declared at module level (not inside a SUB or FUNCTION) and are visible everywhere in the program; declare an ENUM before the code that uses its members. PRIVATE ENUM keeps the ENUM and its members to the file it is declared in, like PRIVATE CONST; PUBLIC ENUM (the same as a plain ENUM) is seen everywhere. Members are constants: Direction.West = 3 is a compile error ("Cannot assign to a constant").
+
The ENUM name can be used as a type wherever a type is allowed: DIM, parameters, FUNCTION results, and TYPE fields. Such a variable is a Long, so it can hold any whole number, not only the members. ENUMs are declared at module level (not inside a SUB or FUNCTION) and are visible everywhere in the program; declare an ENUM before the code that uses its members. PRIVATE ENUM keeps the ENUM and its members to the file it is declared in, like PRIVATE CONST; PUBLIC ENUM (the same as a plain ENUM) is seen everywhere. Members are constants: Direction.West = 3 is a compile error ("Cannot assign to a constant").
DECLARE
Forward-declares a SUB or FUNCTION. This is rarely required because the compiler supports forward references within a module, but it is still accepted for compatibility:
DECLARE SUB name ([BYVAL] [OPTIONAL] param AS type, ...)
@@ -2708,7 +2771,7 @@ END DECLARE
Declare Sub SQLClose(ByVal db As Long)
End Declare
Arguments are always passed by value. Each argument is converted to the type its parameter is declared with before the call, so a Double passed to a Long parameter arrives as a whole number and a Single parameter receives a Single. Declare Integer only for native values that fit in 16 bits; use Long for 32-bit values, or large results are cut down to 16 bits. A Byte parameter or result is 8 bits. A Currency parameter receives the amount as a 64-bit whole number of ten-thousandths; a DECLARE FUNCTION cannot return a Currency.
-
A String parameter or result declared AS STRING is text: it ends at the first CHR$(0). To pass or receive any bytes, CHR$(0) included (network data, sound samples, encrypted data, checksums of binary data), the declaration says BINARY STRING instead. To the program it is an ordinary String; LEN, MID$, ASC and the other string functions see every byte. BINARY STRING is allowed only in a DECLARE LIBRARY block, and the shipped include files already use it where a function works with bytes.
+
A String parameter or result declared AS STRING is text: it ends at the first CHR$(0). To pass or receive any bytes, CHR$(0) included (network data, sound samples, encrypted data, checksums of binary data), the declaration says BINARY STRING instead. To the program it is an ordinary String; LEN, MID$, ASC, and the other string functions see every byte. BINARY STRING is allowed only in a DECLARE LIBRARY block, and the shipped include files already use it where a function works with bytes.
Declare Library "mylib"
Declare Function MyChecksum(ByVal bytes As Binary String) As Long
Declare Function MyRead$(ByVal handle As Long, ByVal maxLen As Long) As Binary String
@@ -2725,7 +2788,7 @@ Print MyChecksum(packet$)
count = count + 1
Print count
End Sub
-
Several variables can be declared in one STATIC statement, separated by commas, each with its own AS clause; STRING * n works as in DIM. Arrays and TYPE variables can be STATIC too and keep their contents between calls: a fixed-size array (Static hits(1 To 10) As Long) or a dynamic one sized later with REDIM (Static names() As String). In a class module each object has its own copy of a procedure's STATIC variables, arrays and TYPE variables, as in Visual Basic: two objects counting calls count separately. A STATIC AS NEW object or WithEvents variable is not supported (a compile error); use a module-level or member variable for those.
+
Several variables can be declared in one STATIC statement, separated by commas, each with its own AS clause; STRING * n works as in DIM. Arrays and TYPE variables can be STATIC too and keep their contents between calls: a fixed-size array (Static hits(1 To 10) As Long) or a dynamic one sized later with REDIM (Static names() As String). In a class module each object has its own copy of a procedure's STATIC variables, arrays, and TYPE variables, as in Visual Basic: two objects counting calls count separately. A STATIC AS NEW object or WithEvents variable is not supported (a compile error); use a module-level or member variable for those.
Sub Remember(ByVal word As String)
Static seen(1 To 3) As String
Static count As Integer
@@ -2765,7 +2828,7 @@ array(index) = expression
udt.field = expression
LET variable = expression
The LET keyword is optional and supported for compatibility.
-
Assigning a whole TYPE value to a TYPE variable, array element or field copies the value's fields into the record that is already there, as in Visual Basic: the two stay separate records (changing one later does not change the other), and a SUB that was passed one of the target's fields by reference sees the new value.
+
Assigning a whole TYPE value to a TYPE variable, array element, or field copies the value's fields into the record that is already there, as in Visual Basic: the two stay separate records (changing one later does not change the other), and a SUB that was passed one of the target's fields by reference sees the new value.
SWAP
Exchanges the values of two variables or array elements. The two operands should be the same type.
SWAP variable1, variable2
@@ -2793,7 +2856,7 @@ Dim h As Header, r As Raw
r.bytes = "AB" + Chr$(10) + Chr$(0)
LSet h = r ' h.kind = "AB", h.size = 10
SET
-
Stores an object reference -- a form, a control, an instance of a class module, a Collection, or Nothing -- in a variable, array element, TYPE field or property. After SET both names refer to the same object; nothing is copied.
+
Stores an object reference -- a form, a control, an instance of a class module, a Collection, or Nothing -- in a variable, array element, TYPE field, or property. After SET both names refer to the same object; nothing is copied.
SET variable = objectExpression
SET variable = NEW ClassName
SET variable = NEW FormName
@@ -2815,7 +2878,7 @@ Set w = New Form1 ' a second Form1 window
w.Show
The value must be an object or Nothing; anything else raises error 424 (Object required). SET is not used for ordinary numeric or string assignment.
ERASE
-
Frees the memory of a dynamic array (declared with DIM name(), created by REDIM, or an array parameter) and resets it to the undimensioned state; using it again before a REDIM raises error 13. A fixed array (declared with bounds) keeps its bounds and has every element reset to zero, an empty string, False or a fresh TYPE instance.
+
Frees the memory of a dynamic array (declared with DIM name(), created by REDIM, or an array parameter) and resets it to the undimensioned state; using it again before a REDIM raises error 13. A fixed array (declared with bounds) keeps its bounds and has every element reset to zero, an empty string, False, or a fresh TYPE instance.
ERASE arrayName
ERASE array1, array2, ...
One ERASE can name several arrays, separated by commas.
@@ -2950,13 +3013,13 @@ END SUB
Sub Greet(ByVal name As String)
Print "Hello, " & name
End Sub
-
A SUB, FUNCTION or Property procedure stands on its own in the program: defining one inside another procedure, or inside an IF, FOR, DO, WHILE, SELECT CASE or WITH block, is a compile error.
-
Parameters are passed by reference by default (nested calls pass the same variable on, so a SUB called from a SUB can still change the original); BYREF may be written out to say so, and BYVAL selects value semantics. A by-reference parameter is the caller's variable itself, as in Visual Basic: a write to it changes the variable at once (the caller's code, or another parameter naming the same variable, sees the new value straight away, even if the procedure later stops with an error), and what is stored is converted to the variable's own type (a STRING * n keeps its length). This holds for a plain variable, an array element (`bump a(3)` changes `a(3)` in place), a TYPE field (`bump rec.count`, `bump recs(2).count`, `bump rec.inner.count`) and, inside a class, one of the class's own member variables. While an array element (or a field of one) is passed by reference, its array cannot be given a new size or erased: REDIM of that array, or ERASE of it when it is a dynamic array, raises error 10 (This array is fixed or temporarily locked), as in Visual Basic (ERASE of a fixed-size array only clears its elements and is allowed). The array is free again as soon as the call returns. Assigning a whole TYPE value to the variable (or element) whose field is being passed copies the value into the same record, so the parameter sees the new value (as in Visual Basic). A parameter written as name() receives a whole array by reference; the caller passes `arr()` (or just `arr`), and the SUB may index, REDIM or ERASE it. Use EXIT SUB to return early. A SUB is called either with or without parentheses; when used as a statement, parentheses are optional:
+
A SUB, FUNCTION, or Property procedure stands on its own in the program: defining one inside another procedure, or inside an IF, FOR, DO, WHILE, SELECT CASE, or WITH block, is a compile error.
+
Parameters are passed by reference by default (nested calls pass the same variable on, so a SUB called from a SUB can still change the original); BYREF may be written out to say so, and BYVAL selects value semantics. A by-reference parameter is the caller's variable itself, as in Visual Basic: a write to it changes the variable at once (the caller's code, or another parameter naming the same variable, sees the new value straight away, even if the procedure later stops with an error), and what is stored is converted to the variable's own type (a STRING * n keeps its length). This holds for a plain variable, an array element (`bump a(3)` changes `a(3)` in place), a TYPE field (`bump rec.count`, `bump recs(2).count`, `bump rec.inner.count`) and, inside a class, one of the class's own member variables. While an array element (or a field of one) is passed by reference, its array cannot be given a new size or erased: REDIM of that array, or ERASE of it when it is a dynamic array, raises error 10 (This array is fixed or temporarily locked), as in Visual Basic (ERASE of a fixed-size array only clears its elements and is allowed). The array is free again as soon as the call returns. Assigning a whole TYPE value to the variable (or element) whose field is being passed copies the value into the same record, so the parameter sees the new value (as in Visual Basic). A parameter written as name() receives a whole array by reference; the caller passes `arr()` (or just `arr`), and the SUB may index, REDIM, or ERASE it. Use EXIT SUB to return early. A SUB is called either with or without parentheses; when used as a statement, parentheses are optional:
Greet "World"
Greet("World")
Call Greet("World")
As in Visual Basic and QBasic, a single argument in parentheses after the name of a SUB called without CALL (Bump (n), or Bump(n)) is an expression in parentheses, so it is passed by value: the SUB gets a copy and n is not changed. Write Bump n, or Call Bump(n), to pass n by reference.
-
An argument is converted to the type of a typed parameter (one declared with AS, a type suffix or a DEFtype rule). A value -- a literal, an expression, a variable in its own parentheses, or anything given to a BYVAL parameter -- is converted the way an assignment would convert it (Report 3.7 gives an Integer parameter 4). A variable, array element or TYPE field given to a by-reference parameter must have the parameter's own type, as in Visual Basic: an Integer parameter cannot be the caller's Double variable ("ByRef argument type mismatch" when the program is compiled); pass CINT(d), or (d) in its own parentheses, to pass a converted copy. A variable declared without a type (a Variant) holds any value and is passed as it is. Through an object variable declared As Object the procedure is only found when the program runs, so a variable of another type is passed as a converted copy instead.
+
An argument is converted to the type of a typed parameter (one declared with AS, a type suffix, or a DEFtype rule). A value -- a literal, an expression, a variable in its own parentheses, or anything given to a BYVAL parameter -- is converted the way an assignment would convert it (Report 3.7 gives an Integer parameter 4). A variable, array element, or TYPE field given to a by-reference parameter must have the parameter's own type, as in Visual Basic: an Integer parameter cannot be the caller's Double variable ("ByRef argument type mismatch" when the program is compiled); pass CINT(d), or (d) in its own parentheses, to pass a converted copy. A variable declared without a type (a Variant) holds any value and is passed as it is. Through an object variable declared As Object the procedure is only found when the program runs, so a variable of another type is passed as a converted copy instead.
Sub Fill(v() As Integer, ByRef count As Integer)
ReDim v(9) As Integer
v(9) = 7
@@ -2978,7 +3041,7 @@ End Sub
Announce "hello" ' loud defaults to 0
Announce "hello", True ' loud is -1
-
An OPTIONAL parameter can also name the value it takes when it is left out, after an equals sign: Optional count As Integer = 10. The default is a constant expression, like the value of a CONST: numbers, strings, dates, other constants and the built-in functions CONST allows, converted to the parameter's type (a value that does not fit is a compile error). A parameter with a default is never Missing -- ISMISSING is False for it -- whether it is left out at the end, skipped between commas, or the procedure is reached through an object. Defaults work for SUB and FUNCTION procedures and for the procedures and Property procedures of class modules, including calls written before the procedure itself. As in Visual Basic the default is the one the declaration being called gives: a call through an interface (a variable declared As the interface class) passes the interface's default, even when the implementing class declares another. An object parameter (As Object, As Control, As Form or As a class) can only have the default Nothing, as in Visual Basic: Optional target As Object = Nothing; a Variant parameter may have Nothing too. A default cannot be given to a required parameter, an array or a TYPE, or in DEF FN and DECLARE LIBRARY. A DECLARE SUB / FUNCTION line must say whether each parameter has a default, as its definition does.
+
An OPTIONAL parameter can also name the value it takes when it is left out, after an equals sign: Optional count As Integer = 10. The default is a constant expression, like the value of a CONST: numbers, strings, dates, other constants, and the built-in functions CONST allows, converted to the parameter's type (a value that does not fit is a compile error). A parameter with a default is never Missing -- ISMISSING is False for it -- whether it is left out at the end, skipped between commas, or the procedure is reached through an object. Defaults work for SUB and FUNCTION procedures and for the procedures and Property procedures of class modules, including calls written before the procedure itself. As in Visual Basic the default is the one the declaration being called gives: a call through an interface (a variable declared As the interface class) passes the interface's default, even when the implementing class declares another. An object parameter (As Object, As Control, As Form, or As a class) can only have the default Nothing, as in Visual Basic: Optional target As Object = Nothing; a Variant parameter may have Nothing too. A default cannot be given to a required parameter, an array, or a TYPE, or in DEF FN and DECLARE LIBRARY. A DECLARE SUB / FUNCTION line must say whether each parameter has a default, as its definition does.
Const SIDE = 10
Function Area(Optional w As Integer = SIDE, Optional h As Integer = SIDE * 2) As Long
@@ -3009,7 +3072,7 @@ Report "Sales", footer:="(end)" ' width is 40
Report width:=60, title:="Stock"
Print Mid$(String:="hello", Start:=2) ' ello
MsgBox Prompt:="Saved", Title:="Notes"
-
Named arguments work for SUB and FUNCTION procedures, DEF FN functions, DECLARE LIBRARY procedures and the procedures of class modules (through a variable of the class, or through an Object or Variant variable), and for the built-in functions that Visual Basic gives argument names to: MsgBox (Prompt, Buttons, Title), InputBox (Prompt, Title, Default), Mid (String, Start, Length), Left and Right (String, Length), InStr (Start, String1, String2), InStrRev (StringCheck, StringMatch, Start, Compare), Replace (Expression, Find, Replace, Start, Count, Compare), Split (Expression, Delimiter, Limit, Compare), Join (SourceArray, Delimiter), Filter (SourceArray, Match, Include, Compare), StrComp (String1, String2, Compare), String (Number, Character), Format (Expression, Format), IIf (Expression, TruePart, FalsePart), DateAdd (Interval, Number, Date), DateDiff and DatePart (Interval, Date1 and Date2 or Date, FirstDayOfWeek, FirstWeekOfYear), DateSerial (Year, Month, Day), TimeSerial (Hour, Minute, Second), Weekday (Date, FirstDayOfWeek), WeekdayName (Weekday, Abbreviate, FirstDayOfWeek), MonthName (Month, Abbreviate), and a Collection's Add (Item, Key, Before, After), Item and Remove (Index).
+
Named arguments work for SUB and FUNCTION procedures, DEF FN functions, DECLARE LIBRARY procedures, and the procedures of class modules (through a variable of the class, or through an Object or Variant variable), and for the built-in functions that Visual Basic gives argument names to: MsgBox (Prompt, Buttons, Title), InputBox (Prompt, Title, Default), Mid (String, Start, Length), Left and Right (String, Length), InStr (Start, String1, String2), InStrRev (StringCheck, StringMatch, Start, Compare), Replace (Expression, Find, Replace, Start, Count, Compare), Split (Expression, Delimiter, Limit, Compare), Join (SourceArray, Delimiter), Filter (SourceArray, Match, Include, Compare), StrComp (String1, String2, Compare), String (Number, Character), Format (Expression, Format), IIf (Expression, TruePart, FalsePart), DateAdd (Interval, Number, Date), DateDiff and DatePart (Interval, Date1 and Date2 or Date, FirstDayOfWeek, FirstWeekOfYear), DateSerial (Year, Month, Day), TimeSerial (Hour, Minute, Second), Weekday (Date, FirstDayOfWeek), WeekdayName (Weekday, Abbreviate, FirstDayOfWeek), MonthName (Month, Abbreviate), and a Collection's Add (Item, Key, Before, After), Item, and Remove (Index).
A name the procedure does not have is a compile error (Named argument not found), and so is a parameter given twice. When the procedure is only found as the program runs (a method called through an Object or Variant variable) the same mistakes raise error 448 (Named argument not found) and error 450 (Wrong number of arguments). The methods of forms and controls do not take named arguments (error 446, Object doesn't support named arguments), and neither does a ParamArray parameter.
ParamArray
The last parameter of a SUB or FUNCTION can be marked PARAMARRAY to accept any number of extra arguments. It is written as an array, name(), and receives every remaining argument as an array numbered from 0. With no extra arguments the array is empty: UBOUND returns -1.
@@ -3033,7 +3096,7 @@ Function Sum(ParamArray n() As Double) As Double
Next
Sum = total
End Function
-
Without an AS clause (and no type suffix or DEFtype rule for the name) the ParamArray is an array of Variants (TYPENAME gives "Variant()"): each element holds the argument that was passed with its own type, so numbers, strings and objects can be mixed, and FOR EACH can walk it. With an AS clause every argument is converted to that type. An argument that is a variable, a whole array, an array element (a(i), m(i, j)), a TYPE field (rec.f, arr(i).f, rec.inner.f) or, inside a class, one of the class's own member variables is passed by reference, as in Visual Basic: the element of the ParamArray is that variable, so a write to it changes the variable at once, converted to the variable's type (a value that cannot be converted raises its error at the assignment), and passing the element on (Bump v(0)) passes the variable. With an AS clause an argument is passed by reference only when its own type is that type; others are converted and passed by value. The same holds for a class's methods, whether the object variable is declared as the class or As Object / As Variant. While the call runs, the arrays whose elements were given are locked: REDIM of one of them (or ERASE of a dynamic one) raises error 10, as for any array element passed by reference. Copying the ParamArray (keep = v) copies the values, not the variables. Expressions and object properties are passed by value. Leaving an argument out (Sum 1, , 3) gives a Missing element. A PARAMARRAY cannot be combined with OPTIONAL parameters, cannot be BYVAL or BYREF, cannot hold a TYPE, and takes at most 31 arguments. It is not available in DEF FN or DECLARE LIBRARY.
+
Without an AS clause (and no type suffix or DEFtype rule for the name) the ParamArray is an array of Variants (TYPENAME gives "Variant()"): each element holds the argument that was passed with its own type, so numbers, strings, and objects can be mixed, and FOR EACH can walk it. With an AS clause every argument is converted to that type. An argument that is a variable, a whole array, an array element (a(i), m(i, j)), a TYPE field (rec.f, arr(i).f, rec.inner.f) or, inside a class, one of the class's own member variables is passed by reference, as in Visual Basic: the element of the ParamArray is that variable, so a write to it changes the variable at once, converted to the variable's type (a value that cannot be converted raises its error at the assignment), and passing the element on (Bump v(0)) passes the variable. With an AS clause an argument is passed by reference only when its own type is that type; others are converted and passed by value. The same holds for a class's methods, whether the object variable is declared as the class or As Object / As Variant. While the call runs, the arrays whose elements were given are locked: REDIM of one of them (or ERASE of a dynamic one) raises error 10, as for any array element passed by reference. Copying the ParamArray (keep = v) copies the values, not the variables. Expressions and object properties are passed by value. Leaving an argument out (Sum 1, , 3) gives a Missing element. A PARAMARRAY cannot be combined with OPTIONAL parameters, cannot be BYVAL or BYREF, cannot hold a TYPE, and takes at most 31 arguments. It is not available in DEF FN or DECLARE LIBRARY.
FUNCTION...END FUNCTION
Defines a function with a return value.
FUNCTION name ([BYVAL] [OPTIONAL] param AS type, ...) AS returnType [STATIC]
@@ -3044,7 +3107,7 @@ END FUNCTION
Function Square(ByVal n As Double) As Double
Square = n * n
End Function
-
Assign to the function name to set the return value. The value is converted to the function's type (AS clause, type suffix or DEFtype), so FUNCTION F AS INTEGER with F = 2.5 returns 2. Inside the function its name on its own reads the value set so far (Total = Total + n), as in Visual Basic; with an argument list -- F() or F(n - 1) -- it calls the function again. Use EXIT FUNCTION to return early.
+
Assign to the function name to set the return value. The value is converted to the function's type (AS clause, type suffix, or DEFtype), so FUNCTION F AS INTEGER with F = 2.5 returns 2. Inside the function its name on its own reads the value set so far (Total = Total + n), as in Visual Basic; with an argument list -- F() or F(n - 1) -- it calls the function again. Use EXIT FUNCTION to return early.
A FUNCTION can also be called as a statement, with or without parentheses; its result is discarded:
Function Beep2(ByVal n As Integer) As Integer
Beep2 = n * 2
@@ -3052,7 +3115,7 @@ End Function
Beep2 3 ' result discarded
name(...) in an expression calls a FUNCTION; using a SUB there is a compile error.
-
Labels, GOSUB and Variables in Procedures
+
Labels, GOSUB, and Variables in Procedures
Labels belong to the SUB or FUNCTION they are written in (or to the module level). GOTO, GOSUB, ON ... GOTO/GOSUB and ON ERROR GOTO can only reach a label of the same procedure, and different procedures can reuse the same label name. GOSUB and RETURN work inside a procedure; see GOTO / GOSUB / RETURN. A procedure sees module variables only when they are SHARED; see Variable Scope and SHARED.
DEF FN
Defines a single-expression function. The function name begins with FN and is invoked just like a normal function. DEF FN functions cannot span multiple statements. A DEF FN must be defined before it is used, and only at module level (not inside a SUB or FUNCTION). Names in the expression other than the parameters are module variables, SHARED or not; the parameters are always passed by value.
@@ -3072,7 +3135,7 @@ EXIT DO
EXIT SUB
EXIT FUNCTION
EXIT PROPERTY
-
EXIT PROPERTY leaves a Property Get, Let or Set procedure of a class module (see Objects and Class Modules).
+
EXIT PROPERTY leaves a Property Get, Let, or Set procedure of a class module (see Objects and Class Modules).
CALL
Explicitly calls a subroutine or function. The return value (if any) is discarded.
CALL name
@@ -3083,7 +3146,7 @@ CALL name(args)
GOSUB label
RETURN
A label is a name followed by a colon at the start of a line, or a line number at the start of a line (see Line Numbers below). Labels belong to the SUB or FUNCTION they are in (module-level labels to the module level): a jump can only reach a label of the same procedure, the same label name can be used in several procedures, and a label may have the same name as a variable. A jump to a label that is not defined in the procedure, or to a SUB or variable name, is a compile error.
-
GOSUB records where it was called from, runs the code at the label, and RETURN goes back to the statement after the GOSUB. This works at module level and inside a SUB or FUNCTION alike. Each procedure call has its own set of pending GOSUBs: RETURN only returns from a GOSUB made in the same procedure call, and leaving the procedure (END SUB, EXIT SUB, EXIT FUNCTION or an error handled in a caller) discards the ones still pending. RETURN with no pending GOSUB raises error 3 (RETURN without GOSUB). To leave a SUB or FUNCTION, use EXIT SUB / EXIT FUNCTION.
+
GOSUB records where it was called from, runs the code at the label, and RETURN goes back to the statement after the GOSUB. This works at module level and inside a SUB or FUNCTION alike. Each procedure call has its own set of pending GOSUBs: RETURN only returns from a GOSUB made in the same procedure call, and leaving the procedure (END SUB, EXIT SUB, EXIT FUNCTION, or an error handled in a caller) discards the ones still pending. RETURN with no pending GOSUB raises error 3 (RETURN without GOSUB). To leave a SUB or FUNCTION, use EXIT SUB / EXIT FUNCTION.
GoSub Initialize
Print "Done"
End
@@ -3106,7 +3169,7 @@ End Sub
ON expression GOSUB label1, label2, ...
If the expression evaluates to 1, control goes to the first label; 2, the second; and so on. If out of range, execution falls through. The labels follow the same rules as GOTO and GOSUB (same procedure only).
Line Numbers
-
As in QBasic, a line can start with a line number, a whole number from 0 to 65529. A line number is a label: GOTO, GOSUB, ON ... GOTO / GOSUB, ON ERROR GOTO, RESUME and RESTORE can name it, it belongs to its procedure like any other label, and the same number cannot start two lines of a procedure. Numbers need not be in order, lines without a number are fine, and the number is read as a value, so 010 and 10 name the same line. IF can branch to a line number directly: IF x THEN 100 ELSE 200 is IF x THEN GOTO 100 ELSE GOTO 200, and IF x GOTO 100 works too.
+
As in QBasic, a line can start with a line number, a whole number from 0 to 65529. A line number is a label: GOTO, GOSUB, ON ... GOTO / GOSUB, ON ERROR GOTO, RESUME, and RESTORE can name it, it belongs to its procedure like any other label, and the same number cannot start two lines of a procedure. Numbers need not be in order, lines without a number are fine, and the number is read as a value, so 010 and 10 name the same line. IF can branch to a line number directly: IF x THEN 100 ELSE 200 is IF x THEN GOTO 100 ELSE GOTO 200, and IF x GOTO 100 works too.
10 INPUT "Guess"; g
20 IF g = 7 THEN 50
30 PRINT "No"
@@ -3137,14 +3200,14 @@ PRINT USING format$; expression [; expression] ...
Print "X="; x, "Y="; y ' comma = next 14-column zone
Print #1, "Written to file"
LPRINT
-
Prints on the printer instead of the screen. Everything PRINT accepts works the same way: ; and , between items, SPC, TAB and LPRINT USING. The printer keeps its own column for TAB and the 14-column zones.
+
Prints on the printer instead of the screen. Everything PRINT accepts works the same way: ; and , between items, SPC, TAB, and LPRINT USING. The printer keeps its own column for TAB and the 14-column zones.
The output goes to the printer chosen in the Control Panel (a printer port, or a file). Each LPRINT is sent at once, and lines end the way printers expect. Nothing starts a new page by itself: LPRINT CHR$(12) ejects the page. If the printer has a problem (out of paper, off line), the user is asked to fix it and choose Retry, or Cancel; after Cancel the LPRINT raises error 57. With no printer to print on, LPRINT raises error 68.
LPrint "Invoice"; Tab(40); Date$
LPrint Using "Total: $$####.##"; total
LPrint Chr$(12) ' eject the page
-
For a whole document with margins, a header, a footer and page numbers, and for the Print dialog, use the Printing Library (print.bas, see Include Libraries).
+
For a whole document with margins, a header, a footer, and page numbers, and for the Print dialog, use the Printing Library (print.bas, see Include Libraries).
PRINT USING
PRINT USING formats each following expression according to a format string and prints the result. The expressions after the format are separated by ; or , (both work the same here). The format string is a mix of fields and literal text: each expression consumes the next field, the literal text before it is printed as it is, and the literal text after the last field is printed at the end. If more expressions are supplied than there are fields, the format string is reused from the start.
Format character Meaning
@@ -3180,13 +3243,13 @@ INPUT "prompt"; variable
INPUT #channel, variable
Input "Enter your name: "; name$
Input #1, line$
-
The target may be a variable, an array element or a TYPE field (INPUT, INPUT #, LINE INPUT # and READ alike). INPUT and INPUT # read a number into a numeric target the way VAL does (12abc gives 12).
+
The target may be a variable, an array element, or a TYPE field (INPUT, INPUT #, LINE INPUT #, and READ alike). INPUT and INPUT # read a number into a numeric target the way VAL does (12abc gives 12).
DATA statements define a pool of values. READ reads the next value from the pool into a variable, array element or TYPE field. RESTORE resets the read pointer to the beginning; RESTORE label (or a line number) makes the next READ take the first DATA item at or after that label. An item in quotes is a string; an unquoted item is taken as raw text up to the next comma, colon, comment or end of line (so keywords, several words and text such as 12abc are read as they are written) and converted when READ into a numeric variable, which raises error 13 (Type mismatch) when the item is not a number. The most negative Integer and Long values (-32768, -2147483648) are accepted.
+
DATA statements define a pool of values. READ reads the next value from the pool into a variable, array element, or TYPE field. RESTORE resets the read pointer to the beginning; RESTORE label (or a line number) makes the next READ take the first DATA item at or after that label. An item in quotes is a string; an unquoted item is taken as raw text up to the next comma, colon, comment, or end of line (so keywords, several words, and text such as 12abc are read as they are written) and converted when READ into a numeric variable, which raises error 13 (Type mismatch) when the item is not a number. The most negative Integer and Long values (-32768, -2147483648) are accepted.
Data 10, 20, 30, "Hello"
Read a, b, c, msg$
Print a; b; c; msg$
@@ -3211,7 +3274,7 @@ ERROR n ' Raise a runtime error with error number n
An error raised inside a running handler passes to the calling procedure's handler (untrapped if there is none).
The label named by ON ERROR GOTO must be in the same procedure.
-
An error that no handler traps ends the program, as in Visual Basic, wherever it happens: in module-level code or in an event handler (a Click, a Timer, a form's Load ...). DVX shows it in a BASIC Runtime Error box -- the error number, the line and the message -- and writes it to the DVX log; the program's windows close once the box is dismissed. In the IDE the Output window shows the error and the editor goes to its line.
+
An error that no handler traps ends the program, as in Visual Basic, wherever it happens: in module-level code or in an event handler (a Click, a Timer, a form's Load ...). DVX shows it in a BASIC Runtime Error box -- the error number, the line, and the message -- and writes it to the DVX log; the program's windows close once the box is dismissed. In the IDE the Output window shows the error and the editor goes to its line.
ON ERROR RESUME NEXT, as in Visual Basic, needs no handler: a statement that fails is abandoned and the program goes on at the next one, with ERR (Err.Number) and Err.Description telling what went wrong. An error in a procedure that has no handler of its own leaves that procedure, and the program goes on after the statement that called it. The statement clears ERR, and so does the end of the procedure that used it; check ERR right after the statements that may fail:
On Error Resume Next
Kill "old.tmp"
@@ -3227,14 +3290,14 @@ On Error GoTo 0
Err.Source The source text the last Err.Raise gave ("" otherwise)
Err.Raise number [, source [, desc]] Raises error number (1 to 65535) with that source and description; without a description the number's standard message
Err.Clear Clears the error: ERR becomes 0, the description and source ""
-
ERROR$(n) (or ERROR(n)) gives the standard message of error number n, "User-defined error" for a number without one and "" for 0; without an argument, the current error's message.
+
ERROR$(n) (or ERROR(n)) gives the standard message of error number n, "User-defined error" for a number without one, and "" for 0; without an argument, the current error's message.
On Error GoTo Failed
Err.Raise 1001, "Loader", "The file has no header"
Exit Sub
Failed:
Print Err.Number; Err.Description; " ("; Err.Source; ")"
Resume Next
-
RESUME NEXT continues after the failing statement. When the error happened in the condition of an IF, ELSEIF, WHILE, DO or LOOP, the condition counts as false (the block is skipped, the loop is left); after an error in a FOR statement the loop is skipped; after an error in the SELECT CASE expression the whole SELECT CASE block is skipped; after an error in a CASE test that test counts as false (the next value or CASE is tried); after an error in an ON ... GOTO/GOSUB selector no branch is taken and execution continues with the next statement.
+
RESUME NEXT continues after the failing statement. When the error happened in the condition of an IF, ELSEIF, WHILE, DO, or LOOP, the condition counts as false (the block is skipped, the loop is left); after an error in a FOR statement the loop is skipped; after an error in the SELECT CASE expression the whole SELECT CASE block is skipped; after an error in a CASE test that test counts as false (the next value or CASE is tried); after an error in an ON ... GOTO/GOSUB selector no branch is taken and execution continues with the next statement.
On Error GoTo ErrorHandler
Open "missing.txt" For Input As #1
Exit Sub
@@ -3249,7 +3312,7 @@ ErrorHandler:
3 RETURN without GOSUB
4 Out of DATA
5 Illegal function call (bad argument to a built-in function; an undefined math result such as a negative number to a fractional power)
- 6 Overflow (value does not fit the target type; EXP, ^ or other math leaving the numeric range; a file size or position past 2 GB in LOF, FILELEN, LOC, SEEK, GET or PUT)
+ 6 Overflow (value does not fit the target type; EXP, ^, or other math leaving the numeric range; a file size or position past 2 GB in LOF, FILELEN, LOC, SEEK, GET, or PUT)
7 Out of memory
9 Subscript out of range / invalid variable or field index
10 This array is fixed or temporarily locked (REDIM, or ERASE of a dynamic array, while one of its elements, or a field of one, is passed by reference to a procedure that is still running)
@@ -3266,7 +3329,7 @@ ErrorHandler:
57 Device I/O error (printing failed or was cancelled)
58 File already exists or rename failed
59 Bad record length (OPEN ... LEN must be 1 to 32767; GET/PUT data longer than LEN)
- 63 Bad record number (SEEK, GET or PUT position below 1)
+ 63 Bad record number (SEEK, GET, or PUT position below 1)
64 Bad file name (WIDTH of a device DVX does not have)
67 Too many files open
68 Device unavailable (no printer to print on)
@@ -3275,7 +3338,7 @@ ErrorHandler:
91 Object variable not set (property, method, Show/Hide/Unload on a value that is not a form or control, or on Nothing)
93 Invalid pattern string (a LIKE pattern with a [ that has no ])
94 Invalid use of Null (Null where a real number or text is needed; see Variants)
- 424 Object required (SET, Is or TypeOf with a value that is not an object)
+ 424 Object required (SET, Is, or TypeOf with a value that is not an object)
438 Object doesn't support this property or method (the object has no member of that name, or it is Private)
446 Object doesn't support named arguments (a form or control method called with Name:=value)
448 Named argument not found (a Name:= the method does not have; a left-out OPTIONAL Variant argument, Missing, used as a value)
@@ -3305,8 +3368,8 @@ RANDOMIZE TIMER ' Seed from system clock
END
-
Sound, Joysticks and Frames
-
These statements work as they do in QBasic, on whatever sound hardware the computer has: a sound card, the PC speaker, or none at all, in which case the program runs the same but silently. Several programs can make sound at once. For sound files, tones of any shape and music files, see the sound.bas and music.bas include libraries; for more joystick detail, joystick.bas.
+
Sound, Joysticks, and Frames
+
These statements work as they do in QBasic, on whatever sound hardware the computer has: a sound card, the PC speaker, or none at all, in which case the program runs the same but silently. Several programs can make sound at once. For sound files, tones of any shape, and music files, see the sound.bas and music.bas include libraries; for more joystick detail, joystick.bas.
BEEP
Sounds a short beep (800 hertz for a quarter of a second).
BEEP
@@ -3332,7 +3395,7 @@ PLAY "T160 O3 L8 E D C D E E E4"
MN Music normal: each note sounds for 7/8 of its length (the default).
ML Music legato: notes sound for their whole length.
MS Music staccato: notes sound for 3/4 of their length.
- MF Music foreground: PLAY, SOUND and BEEP wait until the sound has finished (the default).
+ MF Music foreground: PLAY, SOUND, and BEEP wait until the sound has finished (the default).
MB Music background: the program goes on while the music plays; it only waits when more than 32 notes are waiting.
I n DVX's own: play the notes that follow on General MIDI instrument n (1 to 128; 1 is the piano, 41 the violin, 74 the flute) through the music synthesizer. I0 goes back to plain tones (the default).
X "X" + VARPTR$(s$) plays the commands in s$ at that point (s$ may use X itself, up to 16 levels deep).
@@ -3348,7 +3411,7 @@ PLAY "I74 O4 L8 GAB>C" ' on the flute
PLAY(n) returns how many notes are still waiting to be played in the background (n is ignored). A game can use it to keep its music going:
If PLAY(0) < 4 Then PLAY "MB " + tune$
STICK and STRIG
-
STICK(n) returns where a joystick points, from 1 (all the way left or up) to 200 (all the way right or down), about 100 in the middle, or 0 when there is no such joystick. STICK(0) reads joystick A's X and remembers A's Y and joystick B's X and Y, which STICK(1), STICK(2) and STICK(3) then return; so call STICK(0) first.
+
STICK(n) returns where a joystick points, from 1 (all the way left or up) to 200 (all the way right or down), about 100 in the middle, or 0 when there is no such joystick. STICK(0) reads joystick A's X and remembers A's Y and joystick B's X and Y, which STICK(1), STICK(2), and STICK(3) then return; so call STICK(0) first.
STRIG(n) returns True (-1) or False (0) for the buttons:
n Returns True when
- -----------------
@@ -3360,7 +3423,7 @@ PLAY "I74 O4 L8 GAB>C" ' on the flute
5 joystick A's second button is down
6 joystick B's second button was pressed since the last STRIG(6)
7 joystick B's second button is down
-
Joystick A is the first joystick; joystick B the second one, or the second stick of a joystick with four axes. When no joystick is connected the keyboard stands in for joystick A (see the Control Panel's Game Controllers tab). STRIG ON, STRIG OFF and ON STRIG are not supported; test STRIG in a Timer or a loop instead.
+
Joystick A is the first joystick; joystick B the second one, or the second stick of a joystick with four axes. When no joystick is connected the keyboard stands in for joystick A (see the Control Panel's Game Controllers tab). STRIG ON, STRIG OFF, and ON STRIG are not supported; test STRIG in a Timer or a loop instead.
x = STICK(0)
y = STICK(1)
If STRIG(0) Then FireShot
@@ -3377,7 +3440,7 @@ Loop Until quitting
Console and Graphics
These statements work as they do in QBasic. The first one a program uses gives it a screen of its own: a window, titled with the program's name, the size of the screen mode. From then on PRINT and INPUT use that screen too (before it, PRINT writes to the output window and INPUT asks in a dialog box, as always). Keys typed in the window go to INKEY$ and INPUT. The window stays open when the program's code has finished, so its output can be read; closing it ends the program.
-
The names are not reserved: a program that has its own variable, SUB or constant called Color, Cls, Draw and so on keeps using it.
+
The names are not reserved: a program that has its own variable, SUB, or constant called Color, Cls, Draw, and so on keeps using it.
Screen Modes
Mode Size and colours Text (WIDTH choices) Pages
---- ---------------- -------------------- -----
@@ -3392,7 +3455,7 @@ Loop Until quitting
SCREEN 12 640 x 480, 16 colours 80 x 30 or 80 x 60 1
SCREEN 13 320 x 200, 256 colours 40 x 25 1
A program starts in SCREEN 0. SCREEN with a new mode (and WIDTH) clears the screen and restores the mode's colours: the 16 standard colours, and in SCREEN 13 the standard 256-colour palette. SCREEN with the mode the program is already in only changes the pages, so a program can flip pages in a loop. Other mode numbers, and graphics statements in SCREEN 0, are error 5 (Illegal function call). The colour switch argument is accepted and ignored.
-
Text uses 16-pixel-high characters in SCREEN 0 (25 rows), 11 and 12 (30 rows), 14-pixel ones in SCREEN 9 and 10 (25 rows) and 8-pixel ones everywhere else. The window shows each mode in the shape it has on a monitor: the 640 x 200 modes with every line doubled, 40-column text with every column doubled; SCREEN 9 and 10 show at 640 x 350 as they are. On the whole screen the 320 x 200 modes use the computer's 320 x 200 mode and the others the nearest mode, centred.
+
Text uses 16-pixel-high characters in SCREEN 0 (25 rows), 11 and 12 (30 rows), 14-pixel ones in SCREEN 9 and 10 (25 rows), and 8-pixel ones everywhere else. The window shows each mode in the shape it has on a monitor: the 640 x 200 modes with every line doubled, 40-column text with every column doubled; SCREEN 9 and 10 show at 640 x 350 as they are. On the whole screen the 320 x 200 modes use the computer's 320 x 200 mode and the others the nearest mode, centred.
CLS clears the screen and puts the cursor in the top-left corner (the top of the text viewport). In a graphics mode with a VIEW set, CLS (or CLS 1) clears only the view; with a VIEW PRINT set, CLS (or CLS 2) clears only the text viewport; CLS 0 clears everything.
LOCATE moves the cursor (rows and columns count from 1). cursor 1 shows the text cursor in SCREEN 0, 0 hides it (the default). QBasic's cursor size arguments are accepted and ignored.
COLOR in SCREEN 0 takes a foreground 0 to 31 and a background 0 to 15. Foregrounds 16 to 31 blink (colour minus 16); as on a VGA, backgrounds 8 to 15 show as 0 to 7 and make the text blink too.
-
COLOR in SCREEN 1 takes a background colour (0 to 15) and a palette: an even number gives green, red and brown for colours 1 to 3, an odd one (the default) cyan, magenta and white. SCREEN 2 has no COLOR statement. In SCREEN 7 to 10 COLOR takes a foreground and a background colour (the colour of attribute 0: 0 to 15 in SCREEN 7 and 8, an EGA colour 0 to 63 in SCREEN 9, a shade 0 to 8 in SCREEN 10). In SCREEN 11 to 13 it takes only a foreground. The foreground is also the default colour of the drawing statements.
+
COLOR in SCREEN 1 takes a background colour (0 to 15) and a palette: an even number gives green, red, and brown for colours 1 to 3, an odd one (the default) cyan, magenta, and white. SCREEN 2 has no COLOR statement. In SCREEN 7 to 10 COLOR takes a foreground and a background colour (the colour of attribute 0: 0 to 15 in SCREEN 7 and 8, an EGA colour 0 to 63 in SCREEN 9, a shade 0 to 8 in SCREEN 10). In SCREEN 11 to 13 it takes only a foreground. The foreground is also the default colour of the drawing statements.
CSRLIN is the cursor's row and POS(0) its column. Before the program has a screen, POS(0) is the output window's column.
SCREEN(row, column) is the character code shown there; with a colorflag of 1, its colour (in SCREEN 0, foreground + 16 * background).
PRINT scrolls the screen (or the text viewport) up at the bottom. CHR$(13) and CHR$(10) start a new line and CHR$(9) moves to the next multiple of 8 columns.
Makes rows top to bottom the text viewport: PRINT scrolls only those rows, so the rows around them stay as they are (a title, a status line). The cursor goes to the viewport's first row. VIEW PRINT alone makes the whole screen the viewport again. LOCATE can still reach every row.
VIEW PRINT [top TO bottom]
INKEY$
-
Returns the next key typed in the program's window without waiting: "" when there is none, one character for a character key, and CHR$(0) + CHR$(scan code) for other keys (arrows, function keys). Keys held with Alt, and function keys held with Shift, Ctrl or Alt, give the codes QBasic gives: Alt+A is CHR$(0) + CHR$(30), Alt+1 CHR$(0) + CHR$(120), Shift+F1 CHR$(0) + CHR$(84), Ctrl+F1 CHR$(0) + CHR$(94) and Alt+F1 CHR$(0) + CHR$(104). Keys wait for INKEY$ in a queue of 64.
+
Returns the next key typed in the program's window without waiting: "" when there is none, one character for a character key, and CHR$(0) + CHR$(scan code) for other keys (arrows, function keys). Keys held with Alt, and function keys held with Shift, Ctrl, or Alt, give the codes QBasic gives: Alt+A is CHR$(0) + CHR$(30), Alt+1 CHR$(0) + CHR$(120), Shift+F1 CHR$(0) + CHR$(84), Ctrl+F1 CHR$(0) + CHR$(94), and Alt+F1 CHR$(0) + CHR$(104). Keys wait for INKEY$ in a queue of 64.
Do
k$ = INKEY$
Loop While k$ = ""
@@ -3452,17 +3515,17 @@ Do
If Pen(0) Then PSet (Pen(1), Pen(2)), 14
Loop While InKey$ = ""
INPUT and LINE INPUT on the Screen
-
On the program's screen INPUT shows its prompt at the cursor and reads what is typed there: Backspace takes back a character, Esc clears the line and Enter ends it. LINE INPUT reads a whole line, commas included, without adding "? " to the prompt. Without the program's screen LINE INPUT asks in a dialog box, like INPUT.
+
On the program's screen INPUT shows its prompt at the cursor and reads what is typed there: Backspace takes back a character, Esc clears the line, and Enter ends it. LINE INPUT reads a whole line, commas included, without adding "? " to the prompt. Without the program's screen LINE INPUT asks in a dialog box, like INPUT.
LINE INPUT ["prompt";] variable$
Graphics Coordinates
-
Points are (x, y) in pixels from the top-left corner. STEP(x, y) is relative to the last point drawn (the centre of the screen at first). VIEW makes a smaller part of the screen the drawing area (coordinates start at its corner, and drawing stays inside it); WINDOW gives it coordinates of your own. Colour numbers are attributes: 0 to 3 in SCREEN 1 and 10, 0 and 1 in SCREEN 2 and 11, 0 to 15 in SCREEN 7, 8, 9 and 12, 0 to 255 in SCREEN 13; anything else is error 5.
+
Points are (x, y) in pixels from the top-left corner. STEP(x, y) is relative to the last point drawn (the centre of the screen at first). VIEW makes a smaller part of the screen the drawing area (coordinates start at its corner, and drawing stays inside it); WINDOW gives it coordinates of your own. Colour numbers are attributes: 0 to 3 in SCREEN 1 and 10, 0 and 1 in SCREEN 2 and 11, 0 to 15 in SCREEN 7, 8, 9, and 12, 0 to 255 in SCREEN 13; anything else is error 5.
VIEW with coordinates of its own sets the view; fill fills it and border draws a frame around it. VIEW SCREEN keeps coordinates counting from the screen's corner. VIEW alone is the whole screen again.
WINDOW (x1, y1)-(x2, y2) maps the view to those coordinates with y going up; WINDOW SCREEN keeps y going down. WINDOW alone goes back to pixels.
-
PMAP converts between WINDOW coordinates and pixels of the view: n = 0 gives the pixel column of an x, 1 the pixel row of a y, 2 the x of a pixel column and 3 the y of a pixel row. Without a WINDOW both are pixels.
+
PMAP converts between WINDOW coordinates and pixels of the view: n = 0 gives the pixel column of an x, 1 the pixel row of a y, 2 the x of a pixel column, and 3 the y of a pixel row. Without a WINDOW both are pixels.
PSET draws a point in the given colour or the COLOR foreground, PRESET in the given colour or colour 0. POINT(x, y) is the colour at a point, or -1 outside the view. POINT(0) and POINT(1) are the last point in pixels, POINT(2) and POINT(3) in WINDOW coordinates.
Draws a line, a box (B) or a filled box (BF). Without a start point the line starts at the last point. style is a 16-bit pattern: each set bit (from the top) draws a pixel, each clear one skips it.
+
Draws a line, a box (B), or a filled box (BF). Without a start point the line starts at the last point. style is a 16-bit pattern: each set bit (from the top) draws a pixel, each clear one skips it.
LINE (10, 10)-(100, 50), 4, BF
LINE -STEP(20, 0), 15
LINE (0, 100)-(319, 100), 7, , &HF0F0
Draws a circle, an ellipse or an arc. start and end are angles in radians (0 to 2 * pi, counterclockwise from the right); a negative angle also draws the radius to that end, so CIRCLE can draw pie slices. aspect is the height divided by the width; the default makes circles round on a 4:3 monitor (0.83 in SCREEN 13, 1 in SCREEN 12), so in SCREEN 13 a circle in the window looks slightly flat and a round one needs aspect 1.
+
Draws a circle, an ellipse, or an arc. start and end are angles in radians (0 to 2 * pi, counterclockwise from the right); a negative angle also draws the radius to that end, so CIRCLE can draw pie slices. aspect is the height divided by the width; the default makes circles round on a 4:3 monitor (0.83 in SCREEN 13, 1 in SCREEN 12), so in SCREEN 13 a circle in the window looks slightly flat and a round one needs aspect 1.
CIRCLE (160, 100), 50, 14
CIRCLE (160, 100), 50, 4, -0.0001, -1.5708 ' a quarter pie slice
CIRCLE (160, 100), 80, 2, , , 0.5 ' an ellipse
@@ -3486,7 +3549,7 @@ CIRCLE (160, 100), 80, 2, , , 0.5 ' an ellipse
Fills the area around the point with color, up to pixels of the border colour (by default the fill colour itself).
-
With a string instead of a colour, PAINT fills the area with a pattern, as in QBasic. Each row of the pattern is one byte for each colour plane of the mode, and each bit is a pixel (the first bit is the left pixel): in SCREEN 2 and 11 one byte is eight pixels, colour 0 or 1; in SCREEN 7, 8, 9 and 12 four bytes (the blue, green, red and bright planes) make eight pixels of 16 colours; in SCREEN 10 two bytes. In SCREEN 1 each byte is four pixels of two bits, and in SCREEN 13 each byte is one pixel. A pattern has up to 64 rows and repeats over the screen. The fill stops at the border colour (by default the COLOR foreground) and covers the whole area once, even where it already has the pattern's colours. background$ is accepted as in QBasic: a pattern with more than two rows in a row equal to it is error 5.
+
With a string instead of a colour, PAINT fills the area with a pattern, as in QBasic. Each row of the pattern is one byte for each colour plane of the mode, and each bit is a pixel (the first bit is the left pixel): in SCREEN 2 and 11 one byte is eight pixels, colour 0 or 1; in SCREEN 7, 8, 9, and 12 four bytes (the blue, green, red, and bright planes) make eight pixels of 16 colours; in SCREEN 10 two bytes. In SCREEN 1 each byte is four pixels of two bits, and in SCREEN 13 each byte is one pixel. A pattern has up to 64 rows and repeats over the screen. The fill stops at the border colour (by default the COLOR foreground) and covers the whole area once, even where it already has the pattern's colours. background$ is accepted as in QBasic: a pattern with more than two rows in a row equal to it is error 5.
SCREEN 12
LINE (0, 0)-(100, 100), 15, B
PAINT (50, 50), CHR$(&HAA) + CHR$(&H55) + CHR$(0) + CHR$(0), 15 ' blue and green stripes
@@ -3510,7 +3573,7 @@ PAINT (50, 50), CHR$(&HAA) + CHR$(&H55) + CHR$(0) + CHR$(0), 15 ' b
A n Turn the directions by n * 90 degrees (0 to 3).
TA n Turn the directions by n degrees (-360 to 360).
P c,b PAINT at the current point with colour c up to border colour b.
-
The pen starts at the last point and stays where it ends. The colour, scale and turn stay from one DRAW to the next. A mistake in the string is error 5.
+
The pen starts at the last point and stays where it ends. The colour, scale, and turn stay from one DRAW to the next. A mistake in the string is error 5.
As in QBasic, X runs another DRAW string and "=" takes a number, both given with VARPTR$: DRAW "X" + VARPTR$(shape$) and DRAW "C=" + VARPTR$(c) + "R=" + VARPTR$(n). DVX BASIC has no memory addresses, so VARPTR$ packs the value itself into its result (taken when VARPTR$ runs), and it takes any expression. Building the string with + and STR$ works too, such as DRAW "R" + STR$(n).
DRAW "BM100,100 C14 R50 D50 L50 U50" ' a square
DRAW "BM160,100 TA45 C2 R40" ' a line at 45 degrees
@@ -3521,7 +3584,7 @@ NEXT i
PALETTE
PALETTE [attribute, color]
PALETTE USING array[(first)]
-
Changes the colour an attribute shows. In SCREEN 11, 12 and 13, color is blue * 65536 + green * 256 + red with each part 0 to 63; in SCREEN 0 and 9 it is an EGA colour number 0 to 63; in SCREEN 1, 2, 7 and 8 one of the 16 standard colours; in SCREEN 10 a shade 0 to 8 (0 off, 4 grey, 8 bright, and 1 to 3, 5 to 7 blinking between two of them). PALETTE alone restores the mode's colours. PALETTE USING sets every attribute of the mode (256 in SCREEN 13) from a one-dimensional LONG array starting at element first (or its first element); -1 leaves that attribute as it is. On the whole screen a palette change shows at once, like on a VGA card; in the window, whatever is already drawn in that attribute changes colour too.
+
Changes the colour an attribute shows. In SCREEN 11, 12, and 13, color is blue * 65536 + green * 256 + red with each part 0 to 63; in SCREEN 0 and 9 it is an EGA colour number 0 to 63; in SCREEN 1, 2, 7, and 8 one of the 16 standard colours; in SCREEN 10 a shade 0 to 8 (0 off, 4 grey, 8 bright, and 1 to 3, 5 to 7 blinking between two of them). PALETTE alone restores the mode's colours. PALETTE USING sets every attribute of the mode (256 in SCREEN 13) from a one-dimensional LONG array starting at element first (or its first element); -1 leaves that attribute as it is. On the whole screen a palette change shows at once, like on a VGA card; in the window, whatever is already drawn in that attribute changes colour too.
SCREEN 13
PALETTE 1, 63 * 65536 ' attribute 1 becomes bright blue
FOR i = 0 TO 63
@@ -3573,11 +3636,11 @@ CLOSE
RESET
CLOSE with a list closes each channel in it (the # is optional); CLOSE alone, and RESET, close every open file. Closing a channel that is not open does nothing; a number outside the channels DVX has raises error 52.
PRINT #
-
Writes text to a file, formatted exactly as PRINT shows it: numbers are followed by a space, a comma moves to the next 14-column zone, a semicolon keeps the line going, and TAB(n), SPC(n) and PRINT #channel, USING work as they do for PRINT. See PRINT.
+
Writes text to a file, formatted exactly as PRINT shows it: numbers are followed by a space, a comma moves to the next 14-column zone, a semicolon keeps the line going, and TAB(n), SPC(n), and PRINT #channel, USING work as they do for PRINT. See PRINT.
Sets the line width of an open file, the screen or the printer: output longer than the width goes on in a new line, as in QBasic. 255 means no limit (the default).
+
Sets the line width of an open file, the screen, or the printer: output longer than the width goes on in a new line, as in QBasic. 255 means no limit (the default).
Read and write records in RANDOM or BINARY mode files. When recordNum is omitted the transfer starts at the current file position.
GET #channel, [recordNum], variable
PUT #channel, [recordNum], variable
-
In RANDOM mode recordNum is a 1-based record number and each record is LEN bytes (see OPEN). Numbers are stored in their binary form (Byte 1 byte, Integer and Boolean 2, Long 4, Single 4, Double, Currency and Date 8); a String is stored as a 2-byte length followed by its characters. PUT always writes a whole record, padding the unused part with zero bytes, and GET always advances to the next record; data longer than LEN raises error 59 before anything is written.
+
In RANDOM mode recordNum is a 1-based record number and each record is LEN bytes (see OPEN). Numbers are stored in their binary form (Byte 1 byte, Integer and Boolean 2, Long 4, Single 4, Double, Currency, and Date 8); a String is stored as a 2-byte length followed by its characters. PUT always writes a whole record, padding the unused part with zero bytes, and GET always advances to the next record; data longer than LEN raises error 59 before anything is written.
In BINARY mode recordNum is a 1-based byte position. Numbers are stored as in RANDOM mode. PUT writes the characters of a String with no length prefix, and GET reads as many bytes as the String variable currently holds, so set its length first (for example with SPACE$ or by declaring it as STRING * n).
The variable may be a TYPE variable. Its fields are packed in declaration order with the sizes above; a STRING * n field takes exactly n bytes (padded with spaces), a variable-length String field is stored with its 2-byte length prefix in both modes, and nested TYPE fields are packed inline. GET fills the fields of the existing variable in place.
The variable may also be an array element, arr(i), of any type including a TYPE: PUT writes that element and GET reads into it. The index expression is evaluated once.
@@ -3630,7 +3693,7 @@ pos = SEEK(channel) ' Function: get current position
File System Statements
DVX BASIC provides statements for working with the file system -- creating, deleting, renaming, and copying files and directories, and reading or setting file attributes.
-
A path may be written with / or \ between its parts, mixed too: OPEN "DATA/SCORES.TXT" and OPEN "DATA\SCORES.TXT" open the same file, and so do the FileOps, Shell and other library functions that take a path (SHELL commands are the exception: see SHELL). Paths DVX gives back -- CURDIR$, App.Path, App.Data, the file dialogs, FileOpsPathJoin$ and the other FileOps results -- use \, the separator DOS shows; FileOpsPathNative$ gives a path of your own that form before you show it.
+
A path may be written with / or \ between its parts, mixed too: OPEN "DATA/SCORES.TXT" and OPEN "DATA\SCORES.TXT" open the same file, and so do the FileOps, Shell, and other library functions that take a path (SHELL commands are the exception: see SHELL). Paths DVX gives back -- CURDIR$, App.Path, App.Data, the file dialogs, FileOpsPathJoin$, and the other FileOps results -- use \, the separator DOS shows; FileOpsPathNative$ gives a path of your own that form before you show it.
KILL
Deletes a file.
KILL filename$
@@ -3673,7 +3736,7 @@ End If
SetAttr "secret.dat", vbHidden + vbReadOnly
CURDIR$ / DIR$
-
CURDIR$ returns the current directory. DIR$ returns the first file matching a pattern; subsequent calls with no arguments return additional matches (or "" when done). DIR$ returns only files: never ".", ".." or subdirectories. DIR$("C:\*.*") lists the root of C: (DIR$("C:*.*") the drive's current folder).
+
CURDIR$ returns the current directory. DIR$ returns the first file matching a pattern; subsequent calls with no arguments return additional matches (or "" when done). DIR$ returns only files: never ".", "..", or subdirectories. DIR$("C:\*.*") lists the root of C: (DIR$("C:*.*") the drive's current folder).
TRIM$(s$) String Removes leading and trailing spaces from s$
UCASE$(s$) String Converts s$ to uppercase
VAL(s$) Double Converts string s$ to a numeric value; stops at first non-numeric character
-
A negative count or position passed to LEFT$, RIGHT$, MID$, SPACE$ or STRING$, or a count above 65535 passed to SPACE$ or STRING$, raises error 5 (Illegal function call). INSTR follows OPTION COMPARE TEXT (a case-insensitive search), and an INSTR start position below 1 raises error 5.
-
REPLACE$, JOIN$ and STRREVERSE$ may also be written without the $. Items in square brackets may be left out.
+
A negative count or position passed to LEFT$, RIGHT$, MID$, SPACE$, or STRING$, or a count above 65535 passed to SPACE$ or STRING$, raises error 5 (Illegal function call). INSTR follows OPTION COMPARE TEXT (a case-insensitive search), and an INSTR start position below 1 raises error 5.
+
REPLACE$, JOIN$, and STRREVERSE$ may also be written without the $. Items in square brackets may be left out.
Comparing Text
-
The compare argument of FILTER, INSTRREV, REPLACE$, SPLIT and STRCOMP chooses how text is matched: vbBinaryCompare (0) matches exactly, vbTextCompare (1) ignores upper and lower case, and vbUseCompareOption (-1, the default) follows OPTION COMPARE. Any other value raises error 5.
+
The compare argument of FILTER, INSTRREV, REPLACE$, SPLIT, and STRCOMP chooses how text is matched: vbBinaryCompare (0) matches exactly, vbTextCompare (1) ignores upper and lower case, and vbUseCompareOption (-1, the default) follows OPTION COMPARE. Any other value raises error 5.
Print StrComp("apple", "Apple") ' 1 ("a" sorts after "A")
Print StrComp("apple", "Apple", vbTextCompare) ' 0
Print Replace$("Red red RED", "red", "blue", 1, -1, vbTextCompare) ' blue blue blue
Print InStrRev("C:\DVX\APPS\EDIT.APP", "\") ' 12
REPLACE$ Details
REPLACE$ returns the text from position start on, so REPLACE$("abcabc", "b", "x", 3) is "caxc" -- the characters before start are left out, as in Visual Basic. A count of -1 replaces every occurrence. An empty find$ returns the text unchanged. A start below 1 or a count below -1 raises error 5.
-
SPLIT, JOIN$ and FILTER
+
SPLIT, JOIN$, and FILTER
SPLIT returns a string array numbered from 0. Assign it to a dynamic array (one declared with empty parentheses) and use UBOUND to find the last piece. The last piece keeps the rest of the text when limit stops the split. Splitting an empty string, or a limit of 0, gives an array with no elements: its UBOUND is -1. An empty delim$ gives the whole string as the only piece.
Dim parts() As String
Dim i As Integer
@@ -3748,11 +3811,11 @@ Print Format$(42, "00000") ' "00042"
Print Format$(1234.5, "#,##0.00") ' "1,234.50"
Print Format$(5, "#,##0.00") ' " 5.00"
Print Format$(0.5, "PERCENT") ' "50%"
-
The whole format string is one numeric field (for a number). The accepted format characters are # (digit or pad space), 0 (digit that is always printed; zero pads up to the first 0), . (decimal point), , (thousands separator), + and - (sign placement), $$ (floating dollar sign), ** (asterisk fill), ^^^^ (scientific notation) and the literal word PERCENT (multiplies by 100 and appends %). Unlike PRINT USING, a value wider than the field is printed in full without a % marker. See PRINT USING for details on each character.
+
The whole format string is one numeric field (for a number). The accepted format characters are # (digit or pad space), 0 (digit that is always printed; zero pads up to the first 0), . (decimal point), , (thousands separator), + and - (sign placement), $$ (floating dollar sign), ** (asterisk fill), ^^^^ (scientific notation), and the literal word PERCENT (multiplies by 100 and appends %). Unlike PRINT USING, a value wider than the field is printed in full without a % marker. See PRINT USING for details on each character.
With no format string (or an empty one), FORMAT$ gives the value's plain text, as CSTR does: a number without the leading space STR$ puts before it (Format$(42) is "42"), a date in the usual m/d/yyyy h:mm:ss AM form, text as it is, True / False, and "" for Empty.
FORMAT (without the $) is the same function.
FORMAT$ with Dates
-
When the format string holds a date or time code, or is one of the named date formats, FORMAT$ formats its value as a date. The value may be a Date, a number (taken as a date's number of days) or a string that holds a date (anything else raises error 13). A Date with an empty format string gives the usual m/d/yyyy h:mm:ss AM form. The codes may be written in upper or lower case.
+
When the format string holds a date or time code, or is one of the named date formats, FORMAT$ formats its value as a date. The value may be a Date, a number (taken as a date's number of days), or a string that holds a date (anything else raises error 13). A Date with an empty format string gives the usual m/d/yyyy h:mm:ss AM form. The codes may be written in upper or lower case.
Code Result
---- ------
d / dd Day of the month (dd: two digits)
@@ -3818,10 +3881,10 @@ Print s$ ' Hello, DVX--!
TAN(n) Double Tangent of n (n in radians)
TIMER Double Seconds since midnight
RND follows QBasic: RND or RND(n) with n > 0 returns the next number in the sequence, RND(0) repeats the last number, and RND(n) with n < 0 reseeds the generator from n (the same n always gives the same number). The result is a Double, 0 <= RND < 1.
-
INT, FIX and SGN return an Integer, widening to Long or Double when the value does not fit. EXP, ^ and the other functions raise error 6 (Overflow) when the result leaves the numeric range, and error 5 when it is undefined (for example a negative number to a fractional power).
+
INT, FIX, and SGN return an Integer, widening to Long or Double when the value does not fit. EXP, ^, and the other functions raise error 6 (Overflow) when the result leaves the numeric range, and error 5 when it is undefined (for example a negative number to a fractional power).
Note: Use RANDOMIZE or RANDOMIZE TIMER to seed the generator so you don't get the same sequence each run.
Color Functions
-
The color functions work with 24-bit colors packed into a Long the way Visual Basic packs them, &HBBGGRR: red is the low byte, green the middle byte and blue the high byte. RGB(255, 0, 0) and &HFF are red, RGB(0, 0, 255) and &HFF0000 are blue. ForeColor, BackColor, FillColor, the drawing methods of forms and picture boxes (PSet, Line, Circle, Point), the Canvas control and the color dialog all use this form (the console graphics statements of SCREEN modes use QBasic color numbers instead), as do the color constants vbBlack, vbRed, vbGreen, vbYellow, vbBlue, vbMagenta, vbCyan and vbWhite (see Predefined Constants).
+
The color functions work with 24-bit colors packed into a Long the way Visual Basic packs them, &HBBGGRR: red is the low byte, green the middle byte, and blue the high byte. RGB(255, 0, 0) and &HFF are red, RGB(0, 0, 255) and &HFF0000 are blue. ForeColor, BackColor, FillColor, the drawing methods of forms and picture boxes (PSet, Line, Circle, Point), the Canvas control, and the color dialog all use this form (the console graphics statements of SCREEN modes use QBasic color numbers instead), as do the color constants vbBlack, vbRed, vbGreen, vbYellow, vbBlue, vbMagenta, vbCyan, and vbWhite (see Predefined Constants).
Function Returns Description
-------- ------- -----------
RGB(r, g, b) Long Combine red, green, and blue components (0-255) into a color: r + g * 256 + b * 65536
@@ -3849,12 +3912,12 @@ Me.ForeColor = QBColor(14) ' yellow
CSNG(n) Single Converts n to Single
CSTR(n) String Converts n to its String representation (a Date gives m/d/yyyy h:mm:ss AM)
CVAR(v) Variant Returns v unchanged as a Variant
-
Passing Null to any of these except CVAR raises error 94 (Invalid use of Null). Text passed to one that gives a number must hold a number (CINT("12") is 12); other text raises error 13 (Type mismatch). VARTYPE, TYPENAME and the IS functions (ISNULL, ISEMPTY, ISNUMERIC, ...) are described under Variants.
+
Passing Null to any of these except CVAR raises error 94 (Invalid use of Null). Text passed to one that gives a number must hold a number (CINT("12") is 12); other text raises error 13 (Type mismatch). VARTYPE, TYPENAME, and the IS functions (ISNULL, ISEMPTY, ISNUMERIC, ...) are described under Variants.
These functions work with Date values (see Data Types). Wherever a date is expected, a Date, a number of days since 12/30/1899 or a string holding a date may be given; text that is not a date raises error 13 (Type mismatch).
+
These functions work with Date values (see Data Types). Wherever a date is expected, a Date, a number of days since 12/30/1899, or a string holding a date may be given; text that is not a date raises error 13 (Type mismatch).
Function Returns Description
-------- ------- -----------
NOW Date The current date and time
@@ -3877,9 +3940,9 @@ Me.ForeColor = QBColor(14) ' yellow
ISDATE(v) Boolean True when v is a Date or a string holding a valid date
MONTHNAME(month [, abbreviate]) String January ... December (Jan ... Dec when abbreviate is True)
WEEKDAYNAME(n [, abbreviate [, firstDay]]) String Name of day n of the week counted from firstDay (default vbSunday)
-
NOW, DATE and TIME need no parentheses. DATE$ and TIME$ still return the date and time as text.
+
NOW, DATE, and TIME need no parentheses. DATE$ and TIME$ still return the date and time as text.
Intervals
-
DATEADD, DATEDIFF and DATEPART take the interval as a string, in upper or lower case:
+
DATEADD, DATEDIFF, and DATEPART take the interval as a string, in upper or lower case:
Interval Meaning
-------- -------
"yyyy" Year
@@ -3894,12 +3957,12 @@ Me.ForeColor = QBColor(14) ' yellow
"s" Second
Any other interval raises error 5 (Illegal function call).
DATESERIAL and TIMESERIAL
-
A year from 0 to 29 means 2000 to 2029 and one from 30 to 99 means 1930 to 1999. Months, days, hours, minutes and seconds outside their usual range carry into the next or previous unit, so DATESERIAL(2000, 13, 1) is 1/1/2001, DATESERIAL(2000, 3, 0) is the last day of February 2000 and TIMESERIAL(0, 90, 0) is 1:30 AM. Fractional arguments are rounded. A result before 1/1/100 or after 12/31/9999 raises error 5.
+
A year from 0 to 29 means 2000 to 2029 and one from 30 to 99 means 1930 to 1999. Months, days, hours, minutes, and seconds outside their usual range carry into the next or previous unit, so DATESERIAL(2000, 13, 1) is 1/1/2001, DATESERIAL(2000, 3, 0) is the last day of February 2000, and TIMESERIAL(0, 90, 0) is 1:30 AM. Fractional arguments are rounded. A result before 1/1/100 or after 12/31/9999 raises error 5.
Dim lastDay As Date
lastDay = DateSerial(Year(Date), Month(Date) + 1, 0) ' last day of this month
Print Day(lastDay)
DATEADD
-
Adding months, quarters or years keeps the day of the month where it can; when the new month is shorter the day becomes its last day, so DATEADD("m", 1, #1/31/2000#) is 2/29/2000. The time of day is kept. "y", "d" and "w" all add days and "ww" adds weeks. A fractional n drops its fraction. A result outside the Date range raises error 5.
+
Adding months, quarters, or years keeps the day of the month where it can; when the new month is shorter the day becomes its last day, so DATEADD("m", 1, #1/31/2000#) is 2/29/2000. The time of day is kept. "y", "d", and "w" all add days and "ww" adds weeks. A fractional n drops its fraction. A result outside the Date range raises error 5.
ISDATE is True for a Date and for a string that holds a valid date or time; numbers are not dates to ISDATE. MONTHNAME with a month outside 1 to 12, and WEEKDAYNAME with n outside 1 to 7, raise error 5.
If IsDate(Text1.Text) Then due = CDate(Text1.Text)
Print MonthName(Month(Date)); " "; WeekdayName(Weekday(Date), True)
The dim argument of LBOUND and UBOUND must be a constant from 1 to 8.
-
File sizes and positions are Longs, so they stop at 2 GB (2,147,483,647 bytes). A disk can hold bigger files (up to 4 GB on FAT32); LOF, FILELEN, LOC and SEEK raise error 6 (Overflow) for a size or position past 2 GB rather than give a wrong number, and GET and PUT do the same for a record that would end past it. EOF stays right while the position is below 2 GB. FileOpsListSize and FileOpsTreeBytes (fileops.bas) give any file's size as a Double.
+
File sizes and positions are Longs, so they stop at 2 GB (2,147,483,647 bytes). A disk can hold bigger files (up to 4 GB on FAT32); LOF, FILELEN, LOC, and SEEK raise error 6 (Overflow) for a size or position past 2 GB rather than give a wrong number, and GET and PUT do the same for a record that would end past it. EOF stays right while the position is below 2 GB. FileOpsListSize and FileOpsTreeBytes (fileops.bas) give any file's size as a Double.
Miscellaneous Functions
@@ -3959,15 +4022,15 @@ Print MonthName(Month(Date)); " "; WeekdayName(Weekday(Date), True)
+ LOADPICTURE([file$ [, size [, colordepth [, x, y]]]]) Object A Picture object for a picture property (DragIcon, Picture), with Width, Height, and Type; no file (or "") gives an empty picture
DATE$ and TIME$ return text; the Date and Time Functions return Date values.
IIF and CHOOSE
IIF and CHOOSE evaluate every argument before picking one, as in Visual Basic, so a function called in an argument runs even when its value is not chosen. A fractional n in CHOOSE drops its fraction (2.9 picks the second value). An n below 1 or past the end of the list gives Null, as in Visual Basic (test it with ISNULL; see Variants).
LOADPICTURE(file$) reads a picture file (BMP, PNG, JPEG or GIF) -- or the icon of a DVX program, widget or library file (.app, .wgt, .lib) -- and returns a Picture object (TypeName "Picture") that can be given to a picture property: a control's DragIcon, the Picture of an Image or ImageButton. Use SET, or a plain assignment, as with a file name; file names still work too. LOADPICTURE() or LOADPICTURE("") gives an empty picture, which clears the property. A missing file raises error 53 (File not found), a file that is not a picture error 481 (Invalid picture), so the check happens where the picture is loaded. Loading the same file again gives the same Picture object. Reading a picture property back gives a Picture object too (TypeName "Picture"), as in Visual Basic; used as a value (printed, joined to text, compared) it stands for its file name, so code that treats the property as a file name keeps working. The name of a program, widget or library file works wherever a picture file's name does (a Picture property, DragIcon, PaintPicture, a Toolbar's AddButton) and shows its icon; such a file without an icon is not a picture (error 481).
-
The optional size and colordepth arguments are Visual Basic's: size is vbLPSmall (0), vbLPLarge (1), vbLPSmallShell (2), vbLPLargeShell (3) or vbLPCustom (4, with x and y giving the size), colordepth vbLPDefault (0), vbLPMonochrome (1), vbLPVGAColor (2) or vbLPColor (3). They choose among the images of an icon file; every DVX picture is a single bitmap, so, as for a bitmap in Visual Basic, they are checked and otherwise make no difference. A value outside these, or a negative x or y, raises error 5.
+
LOADPICTURE(file$) reads a picture file (BMP, PNG, JPEG, or GIF) -- or the icon of a DVX program, widget, or library file (.app, .wgt, .lib) -- and returns a Picture object (TypeName "Picture") that can be given to a picture property: a control's DragIcon, the Picture of an Image or ImageButton. Use SET, or a plain assignment, as with a file name; file names still work too. LOADPICTURE() or LOADPICTURE("") gives an empty picture, which clears the property. A missing file raises error 53 (File not found), a file that is not a picture error 481 (Invalid picture), so the check happens where the picture is loaded. Loading the same file again gives the same Picture object. Reading a picture property back gives a Picture object too (TypeName "Picture"), as in Visual Basic; used as a value (printed, joined to text, compared) it stands for its file name, so code that treats the property as a file name keeps working. The name of a program, widget, or library file works wherever a picture file's name does (a Picture property, DragIcon, PaintPicture, a Toolbar's AddButton) and shows its icon; such a file without an icon is not a picture (error 481).
+
The optional size and colordepth arguments are Visual Basic's: size is vbLPSmall (0), vbLPLarge (1), vbLPSmallShell (2), vbLPLargeShell (3), or vbLPCustom (4, with x and y giving the size), colordepth vbLPDefault (0), vbLPMonochrome (1), vbLPVGAColor (2), or vbLPColor (3). They choose among the images of an icon file; every DVX picture is a single bitmap, so, as for a bitmap in Visual Basic, they are checked and otherwise make no difference. A value outside these, or a negative x or y, raises error 5.
A Picture object has three properties, which can only be read:
Property Type Description
-------- ---- -----------
@@ -4059,13 +4122,13 @@ End With
Creates a control on the given form. typeName$ is the control type (e.g. "CommandButton", "Label", "TextBox"). ctrlName$ is the unique name for the new control. If a parentCtrlRef is supplied, the new control is nested inside it (useful for placing controls inside containers like HBox or Frame); the parent must be a container on the same form, otherwise a runtime error (CreateControl: invalid parent) is raised.
SetEvent
SETEVENT ctrlRef, eventName$, handlerName$
-
Wires an arbitrary SUB as the handler for an event of a control, a form (Me or a form reference) or a menu item. The default event-handler convention (ControlName_EventName) is used automatically for controls defined in a .frm file; SetEvent is for controls you create dynamically or for reusing a single handler across multiple controls. A handler declared without parameters is called without the event's arguments.
+
Wires an arbitrary SUB as the handler for an event of a control, a form (Me or a form reference), or a menu item. The default event-handler convention (ControlName_EventName) is used automatically for controls defined in a .frm file; SetEvent is for controls you create dynamically or for reusing a single handler across multiple controls. A handler declared without parameters is called without the event's arguments.
RemoveControl
REMOVECONTROL formRef, ctrlName$
Removes a control from a form. A later reference to the removed control raises a runtime error (unknown control), as does a reference to a control of a form that has been unloaded.
LoadFormText
SET formVar = LOADFORMTEXT(frmText$)
-
Loads a form from the text of a .frm file -- one a program made, read from a file or got from somewhere else -- and returns it, loaded but hidden like Load (its Load event runs; call Show). A form whose name is already loaded becomes another copy of its own, as with New. The form's controls fire events to procedures named after them in the running program (Gen1_Load, Go_Click), as for any form; a code section after the form's End is not compiled. Text that is not a form raises error 5 (Illegal function call). Form designers use it to show a .frm file being edited; Controls, CallByName and the designer.bas functions then read and change it.
+
Loads a form from the text of a .frm file -- one a program made, read from a file, or got from somewhere else -- and returns it, loaded but hidden like Load (its Load event runs; call Show). A form whose name is already loaded becomes another copy of its own, as with New. The form's controls fire events to procedures named after them in the running program (Gen1_Load, Go_Click), as for any form; a code section after the form's End is not compiled. Text that is not a form raises error 5 (Illegal function call). Form designers use it to show a .frm file being edited; Controls, CallByName, and the designer.bas functions then read and change it.
Dim f As Object
Dim n As Integer
Dim t As String
@@ -4201,7 +4264,7 @@ Set acct = Nothing ' prints "Account for Scott closed"
-
The header is everything before the first line of code: blank lines, a VERSION line, a BEGIN ... END block and Attribute VB_... lines, in that order. All of it is optional, and a Visual Basic .cls file can be added as it is. The class's name comes from the Attribute VB_Name line; without one, the name is the file's name without .cls (ACCOUNT.CLS makes a class named ACCOUNT). The class name is the type name used in DIM ... AS, NEW, TypeOf and TYPENAME.
+
The header is everything before the first line of code: blank lines, a VERSION line, a BEGIN ... END block and Attribute VB_... lines, in that order. All of it is optional, and a Visual Basic .cls file can be added as it is. The class's name comes from the Attribute VB_Name line; without one, the name is the file's name without .cls (ACCOUNT.CLS makes a class named ACCOUNT). The class name is the type name used in DIM ... AS, NEW, TypeOf, and TYPENAME.
What a Class Contains
Outside its procedures, a class module may hold only declarations: member variables, CONSTs, OPTION and DEFtype statements, and Attribute lines. Statements that do something belong in a procedure, usually Class_Initialize. The members are:
Member Written as Used from outside as
@@ -4213,7 +4276,7 @@ Option Explicit
Write property Property Let name(..., value) obj.name = x
Object property Property Set name(..., value) Set obj.name = someObject
Friend procedure Friend Sub / Function / Property ... obj.name, by the program's own code
-
A Sub, Function or Property is Public unless it is marked Private; Private procedures can be called only by the class's own code. A Friend one, as in Visual Basic, can be called by the rest of the program through a variable declared as the class, but it is not part of the object's late-bound interface: through As Object or As Variant it raises error 438, and an Implements interface does not include it. Inside the class's procedures the member variables are used by name, with no SHARED needed, and each call works with the variables of the object it was called for. The class's code can also use the program's DIM SHARED (or PUBLIC) module variables, procedures and constants.
+
A Sub, Function, or Property is Public unless it is marked Private; Private procedures can be called only by the class's own code. A Friend one, as in Visual Basic, can be called by the rest of the program through a variable declared as the class, but it is not part of the object's late-bound interface: through As Object or As Variant it raises error 438, and an Implements interface does not include it. Inside the class's procedures the member variables are used by name, with no SHARED needed, and each call works with the variables of the object it was called for. The class's code can also use the program's DIM SHARED (or PUBLIC) module variables, procedures, and constants.
Properties
A property looks like a variable from outside, but reading or assigning it runs a procedure, so the class can check or calculate the value:
Private mC As Double
@@ -4259,16 +4322,16 @@ Set b = Nothing ' last reference gone: Class_Terminate runs now<
Me.X = Me.X + dx ' uses Property Get X and Property Let X
End Sub
Using Objects
-
When the compiler knows an object's class -- a variable, array, parameter, member variable or FUNCTION declared As the class name or As Collection, New ClassName, or Me in a class module -- its members are found when the program is compiled (early binding), as in Visual Basic. A misspelled member, a Private one, a Sub used where a value is needed, a property used as a statement, assigning a property that has no Property Let or Set, reading one that has no Property Get, and the wrong number of arguments are then compile errors that point at the member. Calls made this way are also faster. A class's own members can be used anywhere in the class, also above the line that declares them, and through Me a class reaches its Private members as well (Me.mTotal).
+
When the compiler knows an object's class -- a variable, array, parameter, member variable, or FUNCTION declared As the class name or As Collection, New ClassName, or Me in a class module -- its members are found when the program is compiled (early binding), as in Visual Basic. A misspelled member, a Private one, a Sub used where a value is needed, a property used as a statement, assigning a property that has no Property Let or Set, reading one that has no Property Get, and the wrong number of arguments are then compile errors that point at the member. Calls made this way are also faster. A class's own members can be used anywhere in the class, also above the line that declares them, and through Me a class reaches its Private members as well (Me.mTotal).
Through a variable declared As Object or As Variant the member is found by name when the program runs (late binding): obj.Deposit 5 looks for a Public member named Deposit in whatever object obj holds at that moment. A misspelled or Private member then raises error 438 when the line runs, and the wrong number of arguments raises error 450. Using a member of Nothing raises error 91 either way. An object of another class that reaches a variable of a class anyway (for example through a ByRef As Object argument) has its members found by name, as late binding would.
-
An argument that can never fit an object parameter of a SUB, FUNCTION or method is a compile error too: a number or text, or an object of another class than the parameter's (unless that class Implements it).
+
An argument that can never fit an object parameter of a SUB, FUNCTION, or method is a compile error too: a number or text, or an object of another class than the parameter's (unless that class Implements it).
An object can be kept in a variable declared as its class name, As Object, or As Variant, in an array, in a TYPE field, in a Collection, or in another object's member variable. It can be passed to a SUB or FUNCTION and returned from a FUNCTION (assign the result with SET inside the function, Set MakeAccount = acct). TYPENAME gives an object's class name, and TypeOf obj Is ClassName tests it; Is tells whether two variables refer to the same object.
-
Arguments of a class's procedures follow the rules of SUB and FUNCTION calls when the object variable is declared as the class name: a parameter without BYVAL gets the caller's variable, so the procedure can change it, an omitted OPTIONAL argument gets its default, and a ParamArray collects the rest. When the object is reached through a variable declared As Object or As Variant, a variable or array element given as an argument is passed by reference too if the procedure's parameter is not BYVAL (Visual Basic's rule; other expressions go by value). What the procedure stores in such a parameter is converted to the type of the caller's variable (an Integer stays an Integer, as in Visual Basic). A TYPE field given as an argument (obj.Move rec.x, obj.Move pts(1).x) is passed by reference as well (a BYVAL parameter gets its value). The methods of forms and controls always take their arguments by value. An argument left out in the middle of a call (obj.Report a, , c) or at the end is an OPTIONAL parameter's default value, else its empty value: 0, "" and so on for a typed parameter, Missing for a Variant (ISMISSING is True). Leaving out a parameter that is not OPTIONAL raises error 449 (Argument not optional).
+
Arguments of a class's procedures follow the rules of SUB and FUNCTION calls when the object variable is declared as the class name: a parameter without BYVAL gets the caller's variable, so the procedure can change it, an omitted OPTIONAL argument gets its default, and a ParamArray collects the rest. When the object is reached through a variable declared As Object or As Variant, a variable or array element given as an argument is passed by reference too if the procedure's parameter is not BYVAL (Visual Basic's rule; other expressions go by value). What the procedure stores in such a parameter is converted to the type of the caller's variable (an Integer stays an Integer, as in Visual Basic). A TYPE field given as an argument (obj.Move rec.x, obj.Move pts(1).x) is passed by reference as well (a BYVAL parameter gets its value). The methods of forms and controls always take their arguments by value. An argument left out in the middle of a call (obj.Report a, , c) or at the end is an OPTIONAL parameter's default value, else its empty value: 0, "", and so on for a typed parameter, Missing for a Variant (ISMISSING is True). Leaving out a parameter that is not OPTIONAL raises error 449 (Argument not optional).
An object parameter is itself passed like any other parameter: without BYVAL, SET on the parameter changes the caller's variable (a SUB can make a new object for the caller); with BYVAL, the procedure gets its own reference to the same object, and SET on it does not affect the caller.
Uses the member of an object whose name the program has in a string, as if it had been written after a dot: a form, a control, a class instance or a Collection. callType says what to do: vbGet reads a property (with arguments, a property that takes them), vbLet assigns it the last argument, vbSet assigns it an object (the last argument), vbMethod calls a method or function with the arguments. The function gives the property's value or the method's result (Empty for vbLet and vbSet); as a statement the result is dropped. Another callType raises error 5 (Illegal function call), as does vbLet or vbSet without a value; a member the object does not have raises the error the same member written after a dot would. A form designer's property window uses it to read and set the properties designer.bas lists for a control type.
+
Uses the member of an object whose name the program has in a string, as if it had been written after a dot: a form, a control, a class instance, or a Collection. callType says what to do: vbGet reads a property (with arguments, a property that takes them), vbLet assigns it the last argument, vbSet assigns it an object (the last argument), vbMethod calls a method or function with the arguments. The function gives the property's value or the method's result (Empty for vbLet and vbSet); as a statement the result is dropped. Another callType raises error 5 (Illegal function call), as does vbLet or vbSet without a value; a member the object does not have raises the error the same member written after a dot would. A form designer's property window uses it to read and set the properties designer.bas lists for a control type.
Dim prop As String
prop = "Caption"
Print CallByName(Command1, prop, vbGet) ' Command1.Caption
@@ -4279,7 +4342,7 @@ Print CallByName(acct, "Balance", vbGet) ' a class instance's pro
A class can name one property or method as its default member, used when the object is written with parentheses directly after it. Put this line in the class, inside the procedure (as Visual Basic writes it) or on its own at the top of the class:
Attribute Item.VB_UserMemId = 0
With Item as the default member, obj(3) means obj.Item(3), and obj(3) = x calls Property Let Item. Collection's default member is Item, which is why c(1) and c("key") work. A call's result can be indexed directly, too: c(1)(0) is element 0 of the array item 1 holds, and Split(s, ",")(2) the third part.
-
Where a value is needed -- in arithmetic, a comparison, text, PRINT, a typed variable or a typed parameter -- an object stands for its default member's value, as in Visual Basic: with Value as the default member, total = obj + 1 reads obj.Value, and obj = 9 (without SET) stores 9 through it. A member variable can be the default member too (Attribute Label.VB_UserMemId = 0). An object without a default member used as a value raises error 13. The object name on its own still means the object itself wherever an object is expected: SET, Is, passing it to an object parameter, adding it to a Collection. Controls have default properties as well (Text1 means Text1.Text); see the control overview.
+
Where a value is needed -- in arithmetic, a comparison, text, PRINT, a typed variable, or a typed parameter -- an object stands for its default member's value, as in Visual Basic: with Value as the default member, total = obj + 1 reads obj.Value, and obj = 9 (without SET) stores 9 through it. A member variable can be the default member too (Attribute Label.VB_UserMemId = 0). An object without a default member used as a value raises error 13. The object name on its own still means the object itself wherever an object is expected: SET, Is, passing it to an object parameter, adding it to a Collection. Controls have default properties as well (Text1 means Text1.Text); see the control overview.
WITH...END WITH
WITH names an object or a TYPE record once for a block of statements; inside it, a name starting with a dot (.Value, .Add 3) is that object's member or that record's field:
With acct
@@ -4310,13 +4373,13 @@ NoMoney:
Resume Next
Rules and Limits
-
A member's name cannot be a reserved word. Names such as Name, Show, Hide, Load, Print, Open, Close, Line, Input, Get, Put, Type, Error and Timer are keywords in DVX BASIC and cannot be used; Text, Caption, Value, Count, Item and Add are fine.
+
A member's name cannot be a reserved word. Names such as Name, Show, Hide, Load, Print, Open, Close, Line, Input, Get, Put, Type, Error, and Timer are keywords in DVX BASIC and cannot be used; Text, Caption, Value, Count, Item, and Add are fine.
SHARED and DIM SHARED cannot be used in a class module; a class's code reaches the program's module variables that are declared DIM SHARED or PUBLIC.
A STATIC variable in a class procedure is kept per object, as in Visual Basic: each object has its own copy.
A class has at most 65536 member variables, its procedures' STATIC variables included; more fails to compile with "Too many member variables in ClassName".
Class_Initialize and Class_Terminate take no parameters.
A class module cannot hold statements outside its procedures; the compiler reports "Only declarations are allowed outside procedures in a class module".
-
NEW, PROPERTY, PUBLIC, PRIVATE and TYPEOF are reserved words. VARIANT, OBJECT, COLLECTION, EMPTY, NULL, EACH and IN are not.
+
NEW, PROPERTY, PUBLIC, PRIVATE, and TYPEOF are reserved words. VARIANT, OBJECT, COLLECTION, EMPTY, NULL, EACH, and IN are not.
@@ -4343,9 +4406,9 @@ Public Sub Start()
Next
RaiseEvent Done
End Sub
-
An event may have parameters but no return value. Its parameters cannot be OPTIONAL or a PARAMARRAY, and an event is always public (Public Event and Event mean the same; Private Event is an error). RAISEEVENT can only be used inside the class's own procedures, with exactly the arguments the event declares. A parameter without BYVAL is passed by reference, so a handler can change the variable given for it -- the cancel flag above is how a handler asks the class to stop. This holds for a variable, an array element (a(i)) and a TYPE field (rec.f, arr(i).f), as in Visual Basic. When several handlers get the event they all get the same variable, so each sees what the one before it stored; every store is converted to the variable's or field's type at once, as an assignment would be. While the handlers run, the arrays whose elements (or elements' fields) were given are locked: REDIM of one of them (or ERASE of a dynamic one) raises error 10. An expression, or a variable in its own parentheses, given for a by-reference parameter is passed as one temporary value that every handler shares, as in Visual Basic: the variable itself does not change, but a handler sees what the one before it stored. An argument is converted to the type of a typed event parameter, with the rules of SUB calls (a variable of another type given by reference is a compile error).
+
An event may have parameters but no return value. Its parameters cannot be OPTIONAL or a PARAMARRAY, and an event is always public (Public Event and Event mean the same; Private Event is an error). RAISEEVENT can only be used inside the class's own procedures, with exactly the arguments the event declares. A parameter without BYVAL is passed by reference, so a handler can change the variable given for it -- the cancel flag above is how a handler asks the class to stop. This holds for a variable, an array element (a(i)), and a TYPE field (rec.f, arr(i).f), as in Visual Basic. When several handlers get the event they all get the same variable, so each sees what the one before it stored; every store is converted to the variable's or field's type at once, as an assignment would be. While the handlers run, the arrays whose elements (or elements' fields) were given are locked: REDIM of one of them (or ERASE of a dynamic one) raises error 10. An expression, or a variable in its own parentheses, given for a by-reference parameter is passed as one temporary value that every handler shares, as in Visual Basic: the variable itself does not change, but a handler sees what the one before it stored. An argument is converted to the type of a typed event parameter, with the rules of SUB calls (a variable of another type given by reference is a compile error).
Handling Events
-
In a module, a form or another class module, declare a variable of the class WithEvents, outside any procedure, and write a SUB named variable_EventName with the same parameters as the event:
+
In a module, a form, or another class module, declare a variable of the class WithEvents, outside any procedure, and write a SUB named variable_EventName with the same parameters as the event:
Dim WithEvents dl As Downloader
Sub StartDownload()
@@ -4386,10 +4449,10 @@ End Sub
Rules
-
WITHEVENTS goes with DIM, PUBLIC or PRIVATE at the top level of a module, form or class module (not inside a procedure), for a single variable declared as a class module's name. It cannot be an array and cannot use New: Set it to an object in code.
+
WITHEVENTS goes with DIM, PUBLIC, or PRIVATE at the top level of a module, form, or class module (not inside a procedure), for a single variable declared as a class module's name. It cannot be an array and cannot use New: Set it to an object in code.
A WithEvents variable is visible to the procedures of its module or form without SHARED, as the handlers and the code that sets it need; in a class module it is a member variable of each object.
-
Handlers are SUBs named variable_EventName in the same module, form or class module as the variable.
-
EVENT, RAISEEVENT, WITHEVENTS and IMPLEMENTS are not reserved words: they only have their meaning where a name follows them in those places, so a program may still use them as its own names.
+
Handlers are SUBs named variable_EventName in the same module, form, or class module as the variable.
+
EVENT, RAISEEVENT, WITHEVENTS, and IMPLEMENTS are not reserved words: they only have their meaning where a name follows them in those places, so a program may still use them as its own names.
@@ -4422,13 +4485,13 @@ s.Scale 2
Print s.Area ' runs Circle's IShape_Area
Print TypeOf s Is IShape ' True
Print TypeName(s) ' Circle
-
Through a variable declared as the interface, a member always means the interface's member, even when the class has a public member of the same name of its own. Through the class's own type, As Object or As Variant, the class's own public members are used. SET into a variable of the interface type accepts an object of any class that Implements it (and of the interface class itself); TypeOf obj Is Interface is True for them. A class can implement several interfaces with one IMPLEMENTS line for each. An argument left out of a call through the interface takes the default the interface's procedure declares, as in Visual Basic, even if the implementing procedure declares another. Friend procedures are not part of an interface.
+
Through a variable declared as the interface, a member always means the interface's member, even when the class has a public member of the same name of its own. Through the class's own type, As Object, or As Variant, the class's own public members are used. SET into a variable of the interface type accepts an object of any class that Implements it (and of the interface class itself); TypeOf obj Is Interface is True for them. A class can implement several interfaces with one IMPLEMENTS line for each. An argument left out of a call through the interface takes the default the interface's procedure declares, as in Visual Basic, even if the implementing procedure declares another. Friend procedures are not part of an interface.
A Collection is a ready-made object that holds a list of items of any kind -- numbers, text, objects -- in order. Items can be looked up by position or by a text key, added anywhere in the list and removed, and FOR EACH walks through them. The list grows as needed; there is no size to declare.
+
A Collection is a ready-made object that holds a list of items of any kind -- numbers, text, objects -- in order. Items can be looked up by position or by a text key, added anywhere in the list, and removed, and FOR EACH walks through them. The list grows as needed; there is no size to declare.
Dim fruit As New Collection
fruit.Add "Apple", "a"
fruit.Add "Cherry", "c"
@@ -4458,10 +4521,10 @@ fruit.Remove 1 ' removes Banana, now the first item
Errors
Error When
----- ----
- 5 A key that is not in the Collection (Item, Remove, before or after), or Add given both before and after
+ 5 A key that is not in the Collection (Item, Remove, before, or after), or Add given both before and after
9 A position that is not in the Collection
13 A key that is not text
- 438 A member other than Add, Remove, Item and Count, or assigning one, through As Object or As Variant
+ 438 A member other than Add, Remove, Item, and Count, or assigning one, through As Object or As Variant
450 The wrong number of arguments, through As Object or As Variant
457 Add with a key that is already in use
When a database file could not be opened, read or written, the message ends with the reason in parentheses, for example "unable to open database file (No such file or directory)". If another program, or another handle in your own program, is changing the same database at that moment, the message is "database is locked": try again once the other change is finished, and free result sets with SQLFreeResult as soon as you are done with them, because an open result set stops other handles from saving changes.
+
When a database file could not be opened, read, or written, the message ends with the reason in parentheses, for example "unable to open database file (No such file or directory)". If another program, or another handle in your own program, is changing the same database at that moment, the message is "database is locked": try again once the other change is finished, and free result sets with SQLFreeResult as soon as you are done with them, because an open result set stops other handles from saving changes.
Complete SQL Example
Dim db As Long
Dim rs As Long
@@ -4575,11 +4638,11 @@ If Clipboard.GetFormat(vbCFText) Then Text2.Text = Clipboard.GetText()
-
The sizes are in pixels, the unit of a form's Left, Top, Width and Height in DVX (Visual Basic uses twips there).
+
The sizes are in pixels, the unit of a form's Left, Top, Width, and Height in DVX (Visual Basic uses twips there).
' Put the form at the right edge, 40 pixels from the top
Me.Left = Screen.Width - Me.Width - 40
Me.Top = 40
Sub mnuCascade_Click ()
Screen.Arrange vbCascade
End Sub
-
Any other property, method or value stops the program with an error. The QBasic statement SCREEN and function SCREEN(row, column) are unaffected: Screen followed by a dot is the object. A control or form of your own named Screen takes the name's place.
+
Any other property, method, or value stops the program with an error. The QBasic statement SCREEN and function SCREEN(row, column) are unaffected: Screen followed by a dot is the object. A control or form of your own named Screen takes the name's place.
Constant Value Description
-------- ----- -----------
vbModal 1 Show form as modal dialog
-
Window State, Arrangement and Mouse Pointer
+
Window State, Arrangement, and Mouse Pointer
A form's WindowState and Screen.Arrange (see Form and Screen Object); the MousePointer values are under Mouse Constants.
Constant Value Description
-------- ----- -----------
@@ -4668,7 +4731,7 @@ IniWrite App.Config & "\app.ini", "Display", "FontS
vbDirectory 16 Entry is a directory
vbArchive 32 File has been modified since last backup
Compare Constants
-
The compare argument of FILTER, INSTRREV, REPLACE$, SPLIT and STRCOMP.
+
The compare argument of FILTER, INSTRREV, REPLACE$, SPLIT, and STRCOMP.
Constant Value Description
-------- ----- -----------
vbUseCompareOption -1 Follow OPTION COMPARE (the default)
@@ -4691,7 +4754,7 @@ IniWrite App.Config & "\app.ini", "Display", "FontS
vbFirstFourDays 2 Week 1 is the first with four days in the new year
vbFirstFullWeek 3 Week 1 is the first full week
Drag and Drop Constants
-
The DragMode property, the Drag method and the State argument of DragOver.
+
The DragMode property, the Drag method, and the State argument of DragOver.
Constant Value Description
-------- ----- -----------
vbManual 0 DragMode: the program starts drags with Drag
@@ -4706,7 +4769,7 @@ IniWrite App.Config & "\app.ini", "Display", "FontS
-------- ----- -----------
vbCFText 1 Text, the format the DVX clipboard holds
Mouse Constants
-
The Button argument of the MouseDown, MouseUp and MouseMove events (several bits when several buttons are down), and the values of MousePointer (controls, forms and Screen).
+
The Button argument of the MouseDown, MouseUp, and MouseMove events (several bits when several buttons are down), and the values of MousePointer (controls, forms, and Screen).
Constant Value Description
-------- ----- -----------
vbLeftButton 1 The left button
@@ -4829,30 +4892,32 @@ End Sub
DVX ships a set of BASIC include files (.bas) in the sdk/include/basic directory. Each file is a small library of DECLARE LIBRARY wrappers around native DVX runtime functions. Add an include file to your project (the IDE's Add File command, or editing the .dbp with another tool) and the declared functions become available to your code.
File Provides
---- --------
- commdlg.bas File Open/Save dialogs (DlgFileOpenIn$ and DlgFileSaveIn$ start in a folder you name), input dialogs, choice dialogs, save-prompt dialogs, the color dialog, message boxes with your own buttons (DlgButtons)
+ commdlg.bas File Open/Save dialogs (DlgFileOpenIn$ and DlgFileSaveIn$ start in a folder you name), input dialogs, choice dialogs, save-prompt dialogs, the color dialog, the font dialog (DlgFont), message boxes with your own buttons (DlgButtons)
sql.bas SQLite database access (SQLOpen, SQLQuery, SQLNext, SQLField$, etc.)
- comm.bas Serial-port I/O (raw and packet) and optional encrypted links; port settings read back and changed, why a call failed, breaks, ring and line errors (SerOpen, SerSet, SerLastError, CommOpen, CommHandshakeStep, ...)
- conn.bas Connections the way Terminal makes them: a serial port, a modem that dials, the secure link to the DVX host proxy or Telnet, shown on a Terminal control, with capture, answerback and file transfers (ConnCreate, ConnOpen, ConnPoll, ConnAttach, ConnSendFiles, ...)
- xfer.bas File transfers with XMODEM, XMODEM-1K, YMODEM and ZMODEM, on a serial port or a connection, with their progress and questions (XferSendFile, XferStatus, XferBytes, XferAnswer, ...)
+ comm.bas Serial-port I/O (raw and packet) and optional encrypted links; port settings read back and changed, why a call failed, breaks, ring, and line errors (SerOpen, SerSet, SerLastError, CommOpen, CommHandshakeStep, ...)
+ conn.bas Connections the way Terminal makes them: a serial port, a modem that dials, the secure link to the DVX host proxy, or Telnet, shown on a Terminal control, with capture, answerback, and file transfers (ConnCreate, ConnOpen, ConnPoll, ConnAttach, ConnSendFiles, ...)
+ xfer.bas File transfers with XMODEM, XMODEM-1K, YMODEM, and ZMODEM, on a serial port or a connection, with their progress and questions (XferSendFile, XferStatus, XferBytes, XferAnswer, ...)
prefs.bas Settings files (INI): load one, read and change its settings, list the keys of a section, save it; DVX's own settings file, the screen mode it starts in, the names its choices are stored as and its default choices, and choices stored by names of your own (PrefsLoad, PrefsGet$, PrefsSet, PrefsKeyName$, PrefsSaveAs, PrefsDvxIni$, PrefsVideoWidth, PrefsChoiceLabel$, PrefsChoiceDefault, PrefsGetChoiceFrom, ...)
resource.bas Read and write DVX resource blocks (icons, text, binary data) in bundled files
help.bas Compile help source files and see the compiler's messages, and open the DVX help viewer, at a topic if you like (HelpCompile, HelpCompileMessages$, HelpView, HelpViewTopic; HelpView "" opens the DVX user guide)
- hlpfile.bas Read compiled help files yourself, for a help viewer of your own: topics, table of contents, keyword index, full-text search, and each topic's headings, text, links and pictures; show a topic in a HelpPane control (HlpOpen, HlpTocTitle$, HlpIndexKeyword$, HlpSearch, HlpLoadTopic, ...)
+ hlpfile.bas Read compiled help files yourself, for a help viewer of your own: topics, table of contents, keyword index, full-text search, and each topic's headings, text, links, and pictures; show a topic in a HelpPane control (HlpOpen, HlpTocTitle$, HlpIndexKeyword$, HlpSearch, HlpLoadTopic, ...)
net.bas Network connections: TCP (TcpConnect, TcpListen, TcpSend, TcpRecv$, TcpPeer$, ...), UDP messages and name lookups, any bytes; the network settings and status (NetConfigLoad, NetConfigSet, NetConfigStore, NetRestart, NetStatus$, ...)
print.bas Printing: text with the page setup and pictures, with or without the Print dialog; a form; the Page Setup dialog; the printer and page settings; the printer ports (PrintModelLabel$, PrintSettingsStore, PrintPageSetting, PrintChoose, PrintForm, PrintPortStatus, ...)
listhelp.bas Lists and icon views you draw yourself (a UserControl, a PictureBox) that behave like ListBox and Program Manager: selection with Shift and Ctrl, the arrow keys in a list or a grid, Ctrl+A, Space, type-ahead, where a dragged item lands, and Program Manager's icon view rules -- its cells, the grid with a scroll bar, names on two lines, dragging and dropping (ListSelCreate, ListSelClick, ListSelNavigate, ListTypeAhead, ListIconMetric, ListIconLabel$, ...)
- security.bas Security: random bytes for keys, a key exchange, a cipher, SHA-256 hashes, HMAC signatures, key making and a safe compare (SecRandom$, SecDhCreate, SecCipherCrypt$, SecSha256$, SecHmac$, SecHkdf$, ...)
+ security.bas Security: random bytes for keys, a key exchange, a cipher, SHA-256 hashes, HMAC signatures, key making, and a safe compare (SecRandom$, SecDhCreate, SecCipherCrypt$, SecSha256$, SecHmac$, SecHkdf$, ...)
game.bas Games: whether a key is being held down right now, forgetting held keys, frames FRAMEWAIT skipped, waiting for the screen's retrace (GameKeyDown, GameKeyClear, FrameMissed, GameWaitRetrace)
- joystick.bas Joysticks: how many there are, their names, where each stick points and which buttons are pressed (JoyCount, JoyX, JoyY, JoyButton, ...); setting them up: calibration, dead zone, game port mode, keyboard joystick keys (JoySetCalibration, JoyCalibration, JoyRescan, JoyKeyIndex, JoySettingsStore, ...)
- sound.bas Sound: play WAV and VOC files, tones and noise, set the volume (PlayWave, PlayTone, StopWave, SoundInfo$, ...); sounds loaded once and played with volume, pan and pitch, sound made by the program as it goes, the FM synthesizer chip (SoundLoad, SoundPlay, SoundSetPitch, SoundCreate, SoundStreamOpen, SoundStreamWrite, FmAlloc, FmWrite, ...); DVX's own event sounds (SoundPlayEvent), master volume, sound driver and MIDI settings (SoundEventFile$, SoundSetMasterVolume, SoundSettingsStore, ...)
- music.bas Music: play MOD, S3M, XM, IT and MIDI files, and play notes yourself (MusicLoad, MusicPlay, MusicStop, MusicNoteOn, MusicInfo$, ...); raw bytes to the MIDI port (MidiAcquire, MidiPut, MidiRelease, MidiPortInfo$)
- fileops.bas Files and folders: folder listings for a file list, paths and names, the file dialogs' filter rule, attributes and the time a file was changed, sizes, free space and disk sizes, drive kinds, whether a folder can be written, the folder for scratch files, making a folder path, and copying, moving and deleting whole folders at once or as a job with progress, Cancel and questions (FileOpsList, FileOpsListRows$, FileOpsFilterMatch, FileOpsSetModified, FileOpsDriveType, FileOpsIsWritable, FileOpsMakePath, FileOpsJobCreate, FileOpsJobRunFor, ...)
- system.bas What Control Panel changes, at once for the whole desktop: the screen mode, the system colors and themes, the wallpaper and the mouse (with DVX's limits and defaults, MOUSE_* and SYS_VIDEO_DEFAULT_*), and storing them in DVX's settings; the system font's character pictures; the mouse pointer and the Shift, Ctrl and Alt keys anywhere on the screen, moving the pointer, typing keys into the window in front, a picture of the whole screen, the memory (SysSetVideoMode, SysSetColor, SysLoadTheme, SysSetWallpaper, SysSetMouse, SysColorsStore, SysFontGlyphRow, SysMouseX, SysSendKey, SysScreenshot, SysMemoryFreeKb, ...)
+ joystick.bas Joysticks: how many there are, their names, where each stick points, and which buttons are pressed (JoyCount, JoyX, JoyY, JoyButton, ...); setting them up: calibration, dead zone, game port mode, keyboard joystick keys (JoySetCalibration, JoyCalibration, JoyRescan, JoyKeyIndex, JoySettingsStore, ...)
+ sound.bas Sound: play WAV and VOC files, tones and noise, set the volume (PlayWave, PlayTone, StopWave, SoundInfo$, ...); sounds loaded once and played with volume, pan, and pitch, sound made by the program as it goes, the FM synthesizer chip (SoundLoad, SoundPlay, SoundSetPitch, SoundCreate, SoundStreamOpen, SoundStreamWrite, FmAlloc, FmWrite, ...); DVX's own event sounds (SoundPlayEvent), master volume, sound driver, and MIDI settings (SoundEventFile$, SoundSetMasterVolume, SoundSettingsStore, ...)
+ music.bas Music: play MOD, S3M, XM, IT, and MIDI files, and play notes yourself (MusicLoad, MusicPlay, MusicStop, MusicNoteOn, MusicInfo$, ...); raw bytes to the MIDI port (MidiAcquire, MidiPut, MidiRelease, MidiPortInfo$)
+ fileops.bas Files and folders: folder listings for a file list, paths and names, the file dialogs' filter rule, attributes and the time a file was changed, sizes, free space and disk sizes, drive kinds, whether a folder can be written, the folder for scratch files, making a folder path, and copying, moving, and deleting whole folders at once or as a job with progress, Cancel, and questions (FileOpsList, FileOpsListRows$, FileOpsFilterMatch, FileOpsSetModified, FileOpsDriveType, FileOpsIsWritable, FileOpsMakePath, FileOpsJobCreate, FileOpsJobRunFor, ...)
+ system.bas What Control Panel changes, at once for the whole desktop: the screen mode, the system colors and themes, the wallpaper, text smoothing, and the mouse (with DVX's limits and defaults, MOUSE_* and SYS_VIDEO_DEFAULT_*), and storing them in DVX's settings; the system font's character pictures; the mouse pointer and the Shift, Ctrl, and Alt keys anywhere on the screen, moving the pointer, typing keys into the window in front, a picture of the whole screen, and the memory (SysSetVideoMode, SysSetColor, SysLoadTheme, SysSetWallpaper, SysSetTextSmoothing, SysSetMouse, SysColorsStore, SysFontGlyphRow, SysMouseX, SysSendKey, SysScreenshot, SysMemoryFreeKb, ...)
+ font.bas Fonts: the font families DVX has, the sizes each comes in, whether one is fixed-pitch, and how wide text and how tall a line is in a font (FontFamilyCount, FontFamilyName$, FontFamilyFixed, FontSizeCount, FontSizeAt, FontNearestSize, FontTextWidth, FontLineHeight, FontAscent, FontRescan)
+ richtext.bas RTF without a control: an RTF document as plain text and plain text as RTF (RtfToText$, TextToRtf$); a RichEdit control reads and writes RTF itself (TextRTF, SelRTF, LoadFile, SaveFile)
layout.bas Control sizes in characters or in percent of the container instead of pixels (SizeChars, SizePercent)
- compiler.bas The BASIC compiler: compile a project or files you give it, find compile errors in their files, list a program's procedures and the language's keywords, build an .app or a control file; put a project's UserControls on a form designer's toolbox; read source text as the compiler does (tokens, code or string or comment, renaming a control in code, WithEvents variables, class Events), F1 help topics; open, change and save project files and UserControl headers (CompSourceFromProject, CompSourceAdd, CompCompile, CompError$, CompMapFile$, CompProcList$, CompBuildApp, CompDesignControls, CompTokens$, CompRenameInCode$, CompHelpTopic$, ProjOpen, ProjSave, ProjCtlSetHeader$, ...)
+ compiler.bas The BASIC compiler: compile a project or files you give it, find compile errors in their files, list a program's procedures and the language's keywords, build an .app or a control file; put a project's UserControls on a form designer's toolbox; read source text as the compiler does (tokens, code or string or comment, renaming a control in code, WithEvents variables, class Events), F1 help topics; open, change, and save project files and UserControl headers (CompSourceFromProject, CompSourceAdd, CompCompile, CompError$, CompMapFile$, CompProcList$, CompBuildApp, CompDesignControls, CompTokens$, CompRenameInCode$, CompHelpTopic$, ProjOpen, ProjSave, ProjCtlSetHeader$, ...)
debug.bas Run a compiled program inside yours and debug it: breakpoints, stepping, its variables, expressions, Immediate-window lines, its PRINT output (RunCreate, RunStart, RunPoll, DbgSetBreakpoints, DbgStepOver, DbgLocals$, DbgEval$, DbgExec$, ...)
- designer.bas Form designers: the control types, whether each holds other controls and can be placed, the names of new controls, default events, toolbox tooltips and icons, and each type's properties (with their types and value names), methods and events (with each event's parameters); the names in .frm and class module text; .frm files as data to read, edit and write, controls copied and pasted as text, and a loaded form written back as .frm text (CtlTypeCount, CtlTypeName$, CtlTypeFlags, CtlPropCount, CtlPropName$, CtlPropType, CtlEventList$, CtlEventParams$, CtlTypeEventParams$, FrmFormName$, FrmControlNames$, ClsClassName$, FrmOpen, FrmText$, FrmCtlAdd, FrmCtlText$, FrmCtlPaste, FrmMenuAdd, FormText$, ...)
- shell.bas DVX itself, everything Program Manager and Task Manager do: start programs and open documents, command lines (split into their parts) and the Run dialog, the installed programs and their names, file types, the running programs (find one, bring it to the front, ask it to close or end it), this program's priority, keys that work in every program, Task Manager, system information, Exit DVX, the desktop and the program started after it, this program's settings folder, the DVX log; Program Manager's program groups with their items, shortcut keys and icons (ShellRun, ShellRunProgram, ShellProgramScan, ShellAssocCount, ShellRunningId, ShellRunningActivate, ShellRunningClose, ShellArg$, ShellSetPriority, HotkeyAdd, ShellExitDvx, ShellConfigFile$, ShellGroupsLoad, ShellItemGet$, ShellItemIcon$, ...)
+ designer.bas Form designers: the control types, whether each holds other controls and can be placed, the names of new controls, default events, toolbox tooltips and icons, and each type's properties (with their types and value names), methods, and events (with each event's parameters); the names in .frm and class module text; .frm files as data to read, edit, and write, controls copied and pasted as text, and a loaded form written back as .frm text (CtlTypeCount, CtlTypeName$, CtlTypeFlags, CtlPropCount, CtlPropName$, CtlPropType, CtlEventList$, CtlEventParams$, CtlTypeEventParams$, FrmFormName$, FrmControlNames$, ClsClassName$, FrmOpen, FrmText$, FrmCtlAdd, FrmCtlText$, FrmCtlPaste, FrmMenuAdd, FormText$, ...)
+ shell.bas DVX itself, everything Program Manager and Task Manager do: start programs and open documents, command lines (split into their parts) and the Run dialog, the installed programs and their names, file types, the running programs (find one, bring it to the front, ask it to close, or end it), this program's priority, keys that work in every program, Task Manager, system information, Exit DVX, the desktop and the program started after it, this program's settings folder, the DVX log; Program Manager's program groups with their items, shortcut keys, and icons (ShellRun, ShellRunProgram, ShellProgramScan, ShellAssocCount, ShellRunningId, ShellRunningActivate, ShellRunningClose, ShellArg$, ShellSetPriority, HotkeyAdd, ShellExitDvx, ShellConfigFile$, ShellGroupsLoad, ShellItemGet$, ShellItemIcon$, ...)
Using an Include Library
' With commdlg.bas added to the project
Dim path As String
@@ -4873,7 +4938,7 @@ Else
End If
On Error GoTo 0
Message Boxes with Your Own Buttons
-
MsgBox offers fixed sets of buttons (OK, Yes and No, ...). With commdlg.bas in the project, DlgButtons(title, message, buttons, icon) shows a message box with the buttons you list, separated by |, with & before each button's access key. It returns the index of the button chosen: 0 for the first, which Enter presses. Esc and closing the box choose the last button, so make the last one the choice that changes nothing. icon is vbQuestion, vbInformation, vbExclamation, vbCritical or 0. Up to six buttons; -1 is returned when the list is empty or longer.
+
MsgBox offers fixed sets of buttons (OK, Yes and No, ...). With commdlg.bas in the project, DlgButtons(title, message, buttons, icon) shows a message box with the buttons you list, separated by |, with & before each button's access key. It returns the index of the button chosen: 0 for the first, which Enter presses. Esc and closing the box choose the last button, so make the last one the choice that changes nothing. icon is vbQuestion, vbInformation, vbExclamation, vbCritical, or 0. Up to six buttons; -1 is returned when the list is empty or longer.
Select Case DlgButtons("Copy", "notes.txt already exists. Replace it?", "&Yes|&No|Yes to &All|&Cancel", vbQuestion)
Case 0
' replace this one
@@ -4883,13 +4948,29 @@ On Error GoTo 0
' keep it (No), or stop (Cancel)
End Select
Color Dialog
-
With commdlg.bas in the project, DlgColor(title, defaultColor) shows the color dialog: a palette of basic colors, Red, Green and Blue sliders with number boxes, and a preview. It starts at defaultColor and returns the color chosen, in the form RGB makes and ForeColor and BackColor use, or -1 when the user cancels.
+
With commdlg.bas in the project, DlgColor(title, defaultColor) shows the color dialog: a palette of basic colors, Red, Green, and Blue sliders with number boxes, and a preview. It starts at defaultColor and returns the color chosen, in the form RGB makes and ForeColor and BackColor use, or -1 when the user cancels.
Dim c As Long
c = DlgColor("Text Color", Label1.ForeColor)
If c <> -1 Then
Label1.ForeColor = c
End If
+
Font Dialog
+
With commdlg.bas in the project, DlgFont(title, family, size, style, flags) shows the font dialog: the font families, the styles and sizes each comes in, Underline and Strikeout boxes, and a sample. It starts at the font named by family, size (in pixels), and style, and returns True when the user chooses OK. DlgFontName$(), DlgFontSize(), and DlgFontStyle() then give the font chosen; after Cancel they give the font the dialog started at. style adds up FONT_BOLD, FONT_ITALIC, FONT_UNDERLINE, and FONT_STRIKE, the constants of font.bas (add it to the project too, or use 1, 2, 4, and 8). With flags DLG_FONT_FIXED the dialog lists only the fixed-pitch families, for text that must line up in columns; with 0 it lists them all. The dialog's Licence button shows the chosen font's notice and licence. The answers fit a control's or a form's Font properties (see Fonts).
+
' With commdlg.bas and font.bas added to the project
+Dim style As Long
+
+If Label1.FontBold Then style = style + FONT_BOLD
+If Label1.FontItalic Then style = style + FONT_ITALIC
+
+If DlgFont("Label Font", Label1.FontName, Label1.FontSize, style, 0) Then
+ Label1.FontName = DlgFontName$()
+ Label1.FontSize = DlgFontSize()
+ Label1.FontBold = (DlgFontStyle() And FONT_BOLD) <> 0
+ Label1.FontItalic = (DlgFontStyle() And FONT_ITALIC) <> 0
+ Label1.FontUnderline = (DlgFontStyle() And FONT_UNDERLINE) <> 0
+ Label1.FontStrikethru = (DlgFontStyle() And FONT_STRIKE) <> 0
+End If
Number Input Dialog
With commdlg.bas in the project, DlgIntInput(title, prompt, default, min, max) asks for a whole number with a spin box, and DlgIntInputStep(title, prompt, default, min, max, step) does the same with the arrows moving by step. Both return the number entered, or default when the user cancels; DlgInputCancelled() (True after a Cancel, False after OK) tells the two apart, as it does for DlgInputBox$.
n = DlgIntInputStep("Zoom", "Zoom in percent:", 100, 25, 400, 25)
@@ -4898,7 +4979,7 @@ If Not DlgInputCancelled() Then
SetZoom n
End If
Resources in Files
-
With resource.bas in the project, a program reads and writes the named resources DVX keeps at the end of program, widget and library files: icons, text and any bytes. ResOpen, ResCount, ResName$, ResType and ResSize list them; ResGetText$ and ResAddText read and write text; ResAddFile and ResExtract copy a resource from or to a file; ResRemove takes one out. ResGetData$(file, name) gives every byte of a resource as a String (CHR$(0) included), and ResAddData file, name, type, bytes adds or replaces a resource of any bytes (type RES_TYPE_BINARY, RES_TYPE_TEXT or RES_TYPE_ICON). RES_NAME_ICON ("icon32") and RES_NAME_WIDGET_ICON ("icon24") are the names DVX gives a program's and a widget's icon; a name is at most RES_NAME_MAX - 1 characters.
+
With resource.bas in the project, a program reads and writes the named resources DVX keeps at the end of program, widget, and library files: icons, text, and any bytes. ResOpen, ResCount, ResName$, ResType, and ResSize list them; ResGetText$ and ResAddText read and write text; ResAddFile and ResExtract copy a resource from or to a file; ResRemove takes one out. ResGetData$(file, name) gives every byte of a resource as a String (CHR$(0) included), and ResAddData file, name, type, bytes adds or replaces a resource of any bytes (type RES_TYPE_BINARY, RES_TYPE_TEXT, RES_TYPE_ICON, or RES_TYPE_FONT, a .DFN font the program carries; see Fonts). RES_NAME_ICON ("icon32") and RES_NAME_WIDGET_ICON ("icon24") are the names DVX gives a program's and a widget's icon; a name is at most RES_NAME_MAX - 1 characters.
Dim settings As String
settings = Chr$(1) + Chr$(0) + Chr$(255)
@@ -4917,7 +4998,7 @@ Else
Print "Handshake failed"
End If
After the handshake, data that arrives unencrypted is ignored unless that channel is opened for it with CommSetPlain link, channel, True.
-
CommHandshake waits until the key exchange is done. CommHandshakeStep(link, passphrase) does one step of it and returns at once, so a program can keep working: call it from a Timer until it returns something other than COMM_PENDING -- COMM_OK when the link is ready, else a COMM_ERR_ code. CommLastError(link) gives the COMM_ code of the last CommHandshake, CommHandshakeStep, CommSend, CommSetPlain or CommPoll, which tells a wrong passphrase (COMM_ERR_AUTH) from a line that does not answer (COMM_ERR_TIMEOUT). CommDropped(link) counts arriving packets thrown away because they were tampered with or came unencrypted where that is refused. Channels are numbered 0 to COMM_MAX_CHANNEL.
+
CommHandshake waits until the key exchange is done. CommHandshakeStep(link, passphrase) does one step of it and returns at once, so a program can keep working: call it from a Timer until it returns something other than COMM_PENDING -- COMM_OK when the link is ready, else a COMM_ERR_ code. CommLastError(link) gives the COMM_ code of the last CommHandshake, CommHandshakeStep, CommSend, CommSetPlain, or CommPoll, which tells a wrong passphrase (COMM_ERR_AUTH) from a line that does not answer (COMM_ERR_TIMEOUT). CommDropped(link) counts arriving packets thrown away because they were tampered with or came unencrypted where that is refused. Channels are numbered 0 to COMM_MAX_CHANNEL.
Sub Timer1_Timer ()
Select Case CommHandshakeStep(link, txtPass.Text)
Case COMM_PENDING
@@ -4938,13 +5019,13 @@ End Sub
Function Description
-------- -----------
SerSet(com, baud, dataBits, parity, stopBits, handshake) Change the settings of an open port (SerOpen's arguments). True when done.
- SerGetBaud(com), SerGetDataBits(com), SerGetParity$(com), SerGetStopBits(com), SerGetHandshake(com) The settings of an open port; parity is "N", "E", "O", "M" or "S". When the port is not open the numbers are an SER_ERR_ code and the parity "".
+ SerGetBaud(com), SerGetDataBits(com), SerGetParity$(com), SerGetStopBits(com), SerGetHandshake(com) The settings of an open port; parity is "N", "E", "O", "M", or "S". When the port is not open the numbers are an SER_ERR_ code and the parity "".
SerTxPending(com), SerFlushTx com Bytes written but not sent yet; throw them away.
SerSetBreak com, state Send a break: True holds the line in the break state, False releases it (hold it a quarter to half a second).
SerGetRi(com) True while the other end rings.
SerLineErrors(com) Errors seen on received data, added up: SER_LINE_OVERRUN (data came faster than it was read), SER_LINE_PARITY, SER_LINE_FRAMING, SER_LINE_BREAK; 0 for none.
Printing
-
With print.bas in the project, a program prints like the programs that come with DVX. PrintTextDialog shows the Print dialog (printer, copies, pages) and prints the text on pages laid out by the page setup: margins, a header and a footer with the document name, date and page number. PrintPageSetup shows the Page Setup dialog, which the user's choices are saved from for every program. PrintPictureDialog prints a picture file scaled to the page, on printer models that print pictures (PrintCanPrintPictures says whether the chosen one does). PrintText and PrintPicture print without asking first. Each returns True when it printed.
+
With print.bas in the project, a program prints like the programs that come with DVX. PrintTextDialog shows the Print dialog (printer, copies, pages) and prints the text on pages laid out by the page setup: margins, a header, and a footer with the document name, date, and page number. PrintPageSetup shows the Page Setup dialog, which the user's choices are saved from for every program. PrintPictureDialog prints a picture file scaled to the page, on printer models that print pictures (PrintCanPrintPictures says whether the chosen one does). PrintText and PrintPicture print without asking first. Each returns True when it printed.
Sub mnuPrint_Click ()
PrintTextDialog txtNotes.Text, "Notes"
End Sub
@@ -4958,7 +5039,7 @@ End Sub
-------- -----------
PrintPageCount(text) How many pages text takes with the saved page setup and printer.
PrintTextPages(text, title, firstPage, lastPage, copies) Print text without asking: those pages (0 and 0 for all), copies times.
- PrintChoose(title, pageCount) Show the Print dialog only (the page range when pageCount is above 0). True when the user chose Print; then PrintChoiceDest, PrintChoiceFile$, PrintChoiceCopies, PrintChoiceFirst and PrintChoiceLast say what was chosen (pages 0 and 0: all).
+ PrintChoose(title, pageCount) Show the Print dialog only (the page range when pageCount is above 0). True when the user chose Print; then PrintChoiceDest, PrintChoiceFile$, PrintChoiceCopies, PrintChoiceFirst, and PrintChoiceLast say what was chosen (pages 0 and 0: all).
PrintForm(formName) / PrintFormDialog(formName) Print what a loaded form shows, scaled to the page (a printer model that prints pictures); the second shows the Print dialog first.
PrintPortPresent(port), PrintPortStatus(port) Whether printer port LPTn (1 to 3) exists, and its state: PRINT_STATUS_ values added up (PRINT_STATUS_PAPER_OUT, PRINT_STATUS_SELECTED, PRINT_STATUS_NOT_BUSY, ...).
PrintStatusProblem$(status, afterWrite, where) What is wrong with a printer in that state, in words ("" when nothing is), for printer where such as "LPT1".
@@ -4975,11 +5056,11 @@ End If
Function Description
-------- -----------
PrintPageSetting(prefs, field) A number of the page setup: "Lines" (lines per page, 0 = the printer's usual length), "Columns", "MarginTop", "MarginBottom", "MarginLeft", "MarginRight", "TabWidth", or "Wrap" (True: long lines wrap at a space); -1 for an unknown field.
- PrintPageText$(prefs, field) "Header" or "Footer". In them &f is the document name, &p the page, &n the number of pages, &d the date, &t the time, && an ampersand; &l, &c and &r put what follows left, centred or right.
+ PrintPageText$(prefs, field) "Header" or "Footer". In them &f is the document name, &p the page, &n the number of pages, &d the date, &t the time, && an ampersand; &l, &c, and &r put what follows left, centred, or right.
PrintPageSetupProblem$(lines, columns, marginTop, marginBottom, marginLeft, marginRight, tabWidth, wrap, header, footer) "" when this page setup can be used, else why not.
PrintPageSettingsStore(prefs, lines, columns, marginTop, marginBottom, marginLeft, marginRight, tabWidth, wrap, header, footer) Put a page setup into the settings (then PrefsSave). False for one PrintPageSetupProblem$ objects to.
PrintPageLines(prefs, model) Lines per page the setup gives on printer model.
-
The limits are CONSTs: PRINT_LINES_MIN to PRINT_LINES_MAX, PRINT_COLUMNS_MIN to PRINT_COLUMNS_MAX, PRINT_TAB_MAX, PRINT_COPIES_MAX, with the defaults PRINT_COLUMNS_DEFAULT, PRINT_MARGIN_TB_DEFAULT, PRINT_MARGIN_LR_DEFAULT and PRINT_TAB_DEFAULT.
+
The limits are CONSTs: PRINT_LINES_MIN to PRINT_LINES_MAX, PRINT_COLUMNS_MIN to PRINT_COLUMNS_MAX, PRINT_TAB_MAX, PRINT_COPIES_MAX, with the defaults PRINT_COLUMNS_DEFAULT, PRINT_MARGIN_TB_DEFAULT, PRINT_MARGIN_LR_DEFAULT, and PRINT_TAB_DEFAULT.
A program can also change the printer settings the way the Control Panel's Printer page does. They are kept in DVX's settings file, so these calls take a handle from prefs.bas (add prefs.bas to the project too). Printer models and destinations are numbered from 0.
Function Description
-------- -----------
@@ -5009,11 +5090,11 @@ Sub cmdOK_Click ()
PrefsClose prefs
End Sub
Files and Folders
-
With fileops.bas in the project, a program can show and change what is on the disks. FileOpsList(folder, filter) reads a folder once -- its folders and the files matching filter -- and returns a listing (a number; FileOpsListFree frees it). FileOpsListRows$ gives the whole listing as name, size, type and date columns, ready for a ListView's SetRows, so even a folder of hundreds of files fills the list at once; FileOpsListSort re-sorts it by name, size, type or date with folders first. FileOpsListName$, FileOpsListIsFolder and FileOpsListSize read single entries, and FileOpsListFind finds one by name.
-
Paths: FileOpsPathNormalize$ (the full path of an existing folder, or ""), FileOpsPathParent$, FileOpsPathJoin$, FileOpsPathSame, FileOpsPathIsRoot, FileOpsPathIsAbsolute (True for a path that is complete by itself, such as "C:\DOCS", False for one that counts from the current folder), FileOpsPathIsInside(path, folder) (True when path is folder itself or anywhere inside it: check it before copying or moving a folder, so it is never copied into itself), FileOpsPathSep$ (the character between folder names, "\" on DOS), FileOpsPathNative$ (a path written with that character, for showing it: "C:/DOCS/A.TXT" becomes "C:\DOCS\A.TXT"), FileOpsBaseName$ and FileOpsExtension$. Every one of them takes / and \ alike and gives back \. FileOpsFilterMatch(itemName, filter) tells whether a name matches a filter the way the file dialogs match it: wildcards * and ?, several patterns separated by ; such as "*.bmp;*.png", case not mattering.
-
Files: FileOpsMakeDir and FileOpsRename (FileOpsError$ says why one failed), FileOpsTempPath$ and FileOpsReplaceFile (save safely: write the new file to FileOpsTempPath$(path$), then FileOpsReplaceFile swaps it in; the old file comes back if that fails), FileOpsValidName(folder, itemName) (whether a name can be used for a new file or folder in folder, by the rules of its disk; FileOpsError$ says why not), FileOpsValidLongName and FileOpsValidShortName (the long-name or the 8.3 rules by themselves, whatever the disk takes), FileOpsShortPath$ (the short 8.3 form of an existing path, for an old program that cannot take long names or blanks), FileOpsMakePath (a folder and every missing folder above it, which MKDIR does not make), FileOpsIsFolder, FileOpsGetAttr and FileOpsSetAttr (the FILEOPS_ATTR_ bits), FileOpsModified$ and FileOpsSetModified (when a file was last changed, read and set in the form "YYYY-MM-DD HH:MM"; FileOpsSetModified returns False for a date that does not exist), FileOpsTreeSize with FileOpsTreeBytes, FileOpsTreeFiles and FileOpsTreeFolders (the size of a whole folder) and FileOpsFormatSize$.
-
Drives: FileOpsDriveList$ (the drive letters), FileOpsDriveType (FILEOPS_DRIVE_FIXED for a hard disk, FILEOPS_DRIVE_REMOVABLE for a floppy disk drive, FILEOPS_DRIVE_REMOTE for a network drive or a CD-ROM; asking never makes a drive spin up), FileOpsBootDrive$ (the drive the computer started from), FileOpsFreeBytes, FileOpsFreeKb and FileOpsTotalBytes (the size of the disk; right for disks past 2 GB too), FileOpsLongNames(path) (True when the disk holding path takes long file names: up to 255 characters, with spaces and several dots; otherwise names are 8.3). FileOpsIsWritable(folder) is False where files cannot be made (a CD-ROM, a write-protected floppy), and FileOpsReadOnlySystem() is True when DVX itself runs from such a disk, so settings cannot be saved in its folder. FileOpsTempDir$() is the folder for scratch files ("" when there is none).
-
FileOpsCopyTree, FileOpsMoveTree and FileOpsDeleteTree do a whole folder at once. For a long copy, move or delete the program stays usable when it runs the work as a job, a little at a time from a Timer: FileOpsJobCreate, then FileOpsJobAddSource for each file or folder, then FileOpsJobRunFor from the timer until it returns FILEOPS_STEP_DONE or FILEOPS_STEP_CANCELLED. When it returns FILEOPS_STEP_ASK (a file already exists, a file is read-only) or FILEOPS_STEP_ERROR, show FileOpsJobMessage$ and pass the user's choice to FileOpsJobAnswer. FileOpsJobCurrent$, FileOpsJobFilesDone, FileOpsJobFilesTotal, FileOpsJobPercent, FileOpsJobBytesDone and FileOpsJobBytesTotal show the progress, and FileOpsJobCancel stops the job. Files copied from a CD-ROM are read-only; FileOpsJobClearAttr job, FILEOPS_ATTR_READONLY before the job runs makes every copy changeable (FILEOPS_ATTR_EDITABLE clears all four attribute bits). Whatever a program leaves open is freed when it ends.
+
With fileops.bas in the project, a program can show and change what is on the disks. FileOpsList(folder, filter) reads a folder once -- its folders and the files matching filter -- and returns a listing (a number; FileOpsListFree frees it). FileOpsListRows$ gives the whole listing as name, size, type, and date columns, ready for a ListView's SetRows, so even a folder of hundreds of files fills the list at once; FileOpsListSort re-sorts it by name, size, type, or date with folders first. FileOpsListName$, FileOpsListIsFolder, and FileOpsListSize read single entries, and FileOpsListFind finds one by name.
+
Paths: FileOpsPathNormalize$ (the full path of an existing folder, or ""), FileOpsPathParent$, FileOpsPathJoin$, FileOpsPathSame, FileOpsPathIsRoot, FileOpsPathIsAbsolute (True for a path that is complete by itself, such as "C:\DOCS", False for one that counts from the current folder), FileOpsPathIsInside(path, folder) (True when path is folder itself or anywhere inside it: check it before copying or moving a folder, so it is never copied into itself), FileOpsPathSep$ (the character between folder names, "\" on DOS), FileOpsPathNative$ (a path written with that character, for showing it: "C:/DOCS/A.TXT" becomes "C:\DOCS\A.TXT"), FileOpsBaseName$, and FileOpsExtension$. Every one of them takes / and \ alike and gives back \. FileOpsFilterMatch(itemName, filter) tells whether a name matches a filter the way the file dialogs match it: wildcards * and ?, several patterns separated by ; such as "*.bmp;*.png", case not mattering.
+
Files: FileOpsMakeDir and FileOpsRename (FileOpsError$ says why one failed), FileOpsTempPath$ and FileOpsReplaceFile (save safely: write the new file to FileOpsTempPath$(path$), then FileOpsReplaceFile swaps it in; the old file comes back if that fails), FileOpsValidName(folder, itemName) (whether a name can be used for a new file or folder in folder, by the rules of its disk; FileOpsError$ says why not), FileOpsValidLongName and FileOpsValidShortName (the long-name or the 8.3 rules by themselves, whatever the disk takes), FileOpsShortPath$ (the short 8.3 form of an existing path, for an old program that cannot take long names or blanks), FileOpsMakePath (a folder and every missing folder above it, which MKDIR does not make), FileOpsIsFolder, FileOpsGetAttr, and FileOpsSetAttr (the FILEOPS_ATTR_ bits), FileOpsModified$ and FileOpsSetModified (when a file was last changed, read and set in the form "YYYY-MM-DD HH:MM"; FileOpsSetModified returns False for a date that does not exist), FileOpsTreeSize with FileOpsTreeBytes, FileOpsTreeFiles, and FileOpsTreeFolders (the size of a whole folder), and FileOpsFormatSize$.
+
Drives: FileOpsDriveList$ (the drive letters), FileOpsDriveType (FILEOPS_DRIVE_FIXED for a hard disk, FILEOPS_DRIVE_REMOVABLE for a floppy disk drive, FILEOPS_DRIVE_REMOTE for a network drive or a CD-ROM; asking never makes a drive spin up), FileOpsBootDrive$ (the drive the computer started from), FileOpsFreeBytes, FileOpsFreeKb, and FileOpsTotalBytes (the size of the disk; right for disks past 2 GB too), FileOpsLongNames(path) (True when the disk holding path takes long file names: up to 255 characters, with spaces and several dots; otherwise names are 8.3). FileOpsIsWritable(folder) is False where files cannot be made (a CD-ROM, a write-protected floppy), and FileOpsReadOnlySystem() is True when DVX itself runs from such a disk, so settings cannot be saved in its folder. FileOpsTempDir$() is the folder for scratch files ("" when there is none).
+
FileOpsCopyTree, FileOpsMoveTree, and FileOpsDeleteTree do a whole folder at once. For a long copy, move, or delete the program stays usable when it runs the work as a job, a little at a time from a Timer: FileOpsJobCreate, then FileOpsJobAddSource for each file or folder, then FileOpsJobRunFor from the timer until it returns FILEOPS_STEP_DONE or FILEOPS_STEP_CANCELLED. When it returns FILEOPS_STEP_ASK (a file already exists, a file is read-only) or FILEOPS_STEP_ERROR, show FileOpsJobMessage$ and pass the user's choice to FileOpsJobAnswer. FileOpsJobCurrent$, FileOpsJobFilesDone, FileOpsJobFilesTotal, FileOpsJobPercent, FileOpsJobBytesDone, and FileOpsJobBytesTotal show the progress, and FileOpsJobCancel stops the job. Files copied from a CD-ROM are read-only; FileOpsJobClearAttr job, FILEOPS_ATTR_READONLY before the job runs makes every copy changeable (FILEOPS_ATTR_EDITABLE clears all four attribute bits). Whatever a program leaves open is freed when it ends.
Dim job As Long
Sub cmdCopy_Click ()
@@ -5068,8 +5149,8 @@ Sub FillTaskList ()
End Sub
Command lines. ShellCommandProgram$(commandLine) and ShellCommandArgs$(commandLine) split a command line the way ShellRunCommand does: the program (the first word, or the text in double quotes when it starts with one) and the arguments after it. ShellArgCount(commandLine) and ShellArg$(commandLine, index) split any command line into its words (index from 0) by DVX's rule: words are separated by blanks, and a word with blanks in it is written in double quotes, which ShellArg$ leaves off ("" past the last word). ShellArgQuote$(text) writes one word that way: text in double quotes when it holds blanks, as it is otherwise, so a folder or file name with spaces in it reaches the program it is passed to whole (ShellRun "notepad.app", ShellArgQuote$(path$)). COMMAND$(n) gives this program's own arguments the same way.
Keys for every program. HotkeyAdd(keyText) makes a key such as "Ctrl+Alt+E" work whichever program is in front and returns its id (0 when it is not a key, needs Ctrl or Alt, or another program uses it). The program asks for the keys pressed from a Timer: HotkeyNext() returns the id of the oldest one not asked for yet, 0 when there is none. HotkeyRemove id stops one; the rest stop when the program ends. HotkeyText$(keyText) writes a key the way DVX does ("ctrl+alt+e" gives "Ctrl+Alt+E"; "" when it is not a key), handy for checking what the user typed. ShellItemKeyValid(keyText) is Program Manager's rule for an item's shortcut: Ctrl together with Alt or Shift, so it never takes a key a program uses itself ("" for no shortcut is valid too).
-
This program. ShellAppPath$ is this program's own path (ShellRun ShellAppPath$(), "" starts another copy), and ShellInstanceCount says how many copies are running, for example to place each new window a little lower and to the right. ShellSetPriority(priority) gives this program more or less of the computer's time while other programs run too: SHELL_PRIORITY_LOW, SHELL_PRIORITY_NORMAL (what every program starts with), SHELL_PRIORITY_HIGH or any number between; ShellPriority() reads it. A program busy with long work leaves the others room at low priority. A program allows more than one copy when Project Properties has "Allow more than one copy to run at once" ticked. ShellConfigFile$(fileName) is the full path of fileName in this program's settings folder -- where DVX's own programs keep their settings, the same for every copy of the program -- and makes the folder when it is missing; keep a desktop's groups there with prefs.bas. ShellLog text writes a line to the DVX log. ShellSetDesktop program makes a program the desktop DVX starts with the next time ("" for Program Manager again); when the desktop ends, DVX ends. ShellSetWelcome program sets the program DVX starts after the desktop the next time it starts ("" for none).
-
Program groups. Program Manager keeps its groups in PROGMAN.INI; a desktop written in BASIC uses the same model and file format. ShellGroupsLoad(path) opens a groups file (a file not saved yet has the groups Main and Startup) and returns a handle, ShellGroupsSave(groups, path) writes it and ShellGroupsFree frees it. Groups and items are numbered from 0; group 0 is Installed Programs, which ShellGroupsScan(groups, skipPath) fills with every installed program and which is never saved. ShellGroupsChanged tells cheaply whether a program was installed or removed since. ShellGroupCount, ShellGroupName$, ShellGroupFind, ShellGroupAdd, ShellGroupRename, ShellGroupDelete and ShellGroupNameValid manage the groups. ShellItemCount and ShellItemAdd(groups, group, name, program) list and add items; ShellItemGet$ and ShellItemSet read and change an item's fields by name ("name", "program", "args", "folder", "icon", "key", "minimized", "col", "row", "description"); ShellItemDelete, ShellItemCopy, ShellItemMove, ShellItemReorder and ShellItemPlace rearrange them, ShellGroupArrange and ShellGroupPlaceUnplaced lay out an icon grid, and ShellItemMatches is Program Manager's search. ShellGroupsGet and ShellGroupsSet read and set the options ("view": SHELL_VIEW_LARGE, SHELL_VIEW_SMALL or SHELL_VIEW_LIST; "group", the group shown; "autoArrange", "minimizeOnRun", "restoreAlone"), and ShellGroupsHistory$ / ShellGroupsHistoryAdd keep the Run history (what ShellRunDialog$ takes). ShellItemIcon$(iconFile, program) is the file an item's icon comes from -- its icon file, else its program or picture, else the program that opens the document -- ready for LOADPICTURE.
+
This program. ShellAppPath$ is this program's own path (ShellRun ShellAppPath$(), "" starts another copy), and ShellInstanceCount says how many copies are running, for example to place each new window a little lower and to the right. ShellSetPriority(priority) gives this program more or less of the computer's time while other programs run too: SHELL_PRIORITY_LOW, SHELL_PRIORITY_NORMAL (what every program starts with), SHELL_PRIORITY_HIGH, or any number between; ShellPriority() reads it. A program busy with long work leaves the others room at low priority. A program allows more than one copy when Project Properties has "Allow more than one copy to run at once" ticked. ShellConfigFile$(fileName) is the full path of fileName in this program's settings folder -- where DVX's own programs keep their settings, the same for every copy of the program -- and makes the folder when it is missing; keep a desktop's groups there with prefs.bas. ShellLog text writes a line to the DVX log. ShellSetDesktop program makes a program the desktop DVX starts with the next time ("" for Program Manager again); when the desktop ends, DVX ends. ShellSetWelcome program sets the program DVX starts after the desktop the next time it starts ("" for none).
+
Program groups. Program Manager keeps its groups in PROGMAN.INI; a desktop written in BASIC uses the same model and file format. ShellGroupsLoad(path) opens a groups file (a file not saved yet has the groups Main and Startup) and returns a handle, ShellGroupsSave(groups, path) writes it, and ShellGroupsFree frees it. Groups and items are numbered from 0; group 0 is Installed Programs, which ShellGroupsScan(groups, skipPath) fills with every installed program and which is never saved. ShellGroupsChanged tells cheaply whether a program was installed or removed since. ShellGroupCount, ShellGroupName$, ShellGroupFind, ShellGroupAdd, ShellGroupRename, ShellGroupDelete, and ShellGroupNameValid manage the groups. ShellItemCount and ShellItemAdd(groups, group, name, program) list and add items; ShellItemGet$ and ShellItemSet read and change an item's fields by name ("name", "program", "args", "folder", "icon", "key", "minimized", "col", "row", "description"); ShellItemDelete, ShellItemCopy, ShellItemMove, ShellItemReorder, and ShellItemPlace rearrange them, ShellGroupArrange and ShellGroupPlaceUnplaced lay out an icon grid, and ShellItemMatches is Program Manager's search. ShellGroupsGet and ShellGroupsSet read and set the options ("view": SHELL_VIEW_LARGE, SHELL_VIEW_SMALL, or SHELL_VIEW_LIST; "group", the group shown; "autoArrange", "minimizeOnRun", "restoreAlone"), and ShellGroupsHistory$ / ShellGroupsHistoryAdd keep the Run history (what ShellRunDialog$ takes). ShellItemIcon$(iconFile, program) is the file an item's icon comes from -- its icon file, else its program or picture, else the program that opens the document -- ready for LOADPICTURE.
Dim groups As Long, main As Long, i As Long
groups = ShellGroupsLoad(ShellConfigFile$("groups.ini"))
main = ShellGroupFind(groups, "Main")
@@ -5099,14 +5180,15 @@ End Sub
End Sub
Changing DVX's Settings
With system.bas in the project, a program changes what Control Panel changes, at once for the whole desktop. Add prefs.bas too: the settings are saved through a prefs.bas handle.
-
Screen mode: SysVideoModeCount() says how many modes the display offers (numbered from 0), and SysVideoModeWidth, SysVideoModeHeight and SysVideoModeBpp (bits per pixel) describe each. SysSetVideoMode(wide, high, bpp) changes the mode and keeps every window; it returns False when the mode could not be set (the old one stays), also while a program has the whole screen to itself. A mode the monitor cannot show leaves the user unable to answer, so do as Control Panel does: ask "Keep this mode?" and go back to the old mode by itself after some seconds. Screen.Width, Screen.Height and Screen.BitsPerPixel tell the mode in use, and SYS_VIDEO_DEFAULT_WIDTH, SYS_VIDEO_DEFAULT_HEIGHT and SYS_VIDEO_DEFAULT_BPP the mode DVX starts in when its settings name none. SysScreenshot(path) saves the whole screen as a picture file (BMP when path ends in .bmp, PNG otherwise) and returns True when it was written.
+
Screen mode: SysVideoModeCount() says how many modes the display offers (numbered from 0), and SysVideoModeWidth, SysVideoModeHeight, and SysVideoModeBpp (bits per pixel) describe each. SysSetVideoMode(wide, high, bpp) changes the mode and keeps every window; it returns False when the mode could not be set (the old one stays), also while a program has the whole screen to itself. A mode the monitor cannot show leaves the user unable to answer, so do as Control Panel does: ask "Keep this mode?" and go back to the old mode by itself after some seconds. Screen.Width, Screen.Height, and Screen.BitsPerPixel tell the mode in use, and SYS_VIDEO_DEFAULT_WIDTH, SYS_VIDEO_DEFAULT_HEIGHT, and SYS_VIDEO_DEFAULT_BPP the mode DVX starts in when its settings name none. SysScreenshot(path) saves the whole screen as a picture file (BMP when path ends in .bmp, PNG otherwise) and returns True when it was written.
Colors: the system colors are numbered from 0 to SysColorCount() - 1. SysColorLabel$(id) is the text to show for one ("Desktop", "Window Face" ...), SysColorName$(id) the name it is stored as, and SysColorId(colorName) the id of a name (-1 for none) -- to draw in the user's colors, for example SysGetColor(SysColorId("desktop")). SysGetColor and SysSetColor read and set a color as an RGB() value; each SysSetColor redraws the whole screen. SysResetColors goes back to DVX's own colors. SysLoadTheme(path) and SysSaveTheme(path) read and write a theme file (.THM, kept in CONFIG\THEMES); both return True when they worked.
-
Wallpaper: SysSetWallpaper(path) shows a BMP, JPG, PNG or GIF image behind the desktop ("" for none) and returns False when the image could not be loaded (the old one stays). SysSetWallpaperMode mode says how it fills the screen, a choice of PREFS_CHOICE_WALLPAPER (Stretch, Tile or Center; see Settings Files). SysWallpaper$() and SysWallpaperMode() tell what is in use.
-
Mouse: SysSetMouse wheelDir, dblClickMs, accel, mickeys, wheelStep applies a whole mouse setup at once: the scroll wheel's direction (1 or -1, the values of PREFS_CHOICE_WHEEL), the double-click time in milliseconds, the acceleration (a value of PREFS_CHOICE_ACCEL), how far the mouse moves for 8 pixels of pointer movement (8 is normal, higher is slower) and the lines scrolled per wheel notch (1 to 10). An accel or mickeys of 0 leaves that one as it is. SysMouseWheelDir, SysMouseDoubleClick, SysMouseAccel, SysMouseMickeys and SysMouseWheelStep read the setup in effect. MOUSE_WHEEL_STEP_MIN and MOUSE_WHEEL_STEP_MAX are the limits of the wheel step, and MOUSE_WHEEL_STEP_DEFAULT, MOUSE_DBLCLICK_DEFAULT_MS and MOUSE_SPEED_DEFAULT are DVX's defaults (what a Default button puts back).
-
The pointer and the keyboard: SysMouseX() and SysMouseY() are where the mouse pointer is on the screen, and SysMouseButtons() the buttons held (vbLeftButton, vbRightButton and vbMiddleButton bits) -- also while the pointer is over another program's window, where your MouseMove events do not see it. SysSetMousePos x, y moves the pointer (it is kept on the screen). SysShiftState() is the Shift, Ctrl and Alt keys held now (vbShiftMask, vbCtrlMask and vbAltMask bits). SysSendKey(keyCode, shift) types a key into the window that has the focus as if the user pressed it, like SendKeys in other BASICs: keyCode as a KeyDown event gets it -- a character code such as ASC("a") or 13 for Enter, or a vbKey constant such as vbKeyF1 -- and shift the vbShiftMask, vbCtrlMask and vbAltMask bits held with it (SysSendKey ASC("s"), vbCtrlMask is Ctrl+S). The keys arrive in order, one at a time, once your code has returned to DVX; up to 64 can wait, and SysSendKey returns False when that many are waiting. A program cannot press the mouse buttons: to "click" a control, call its event procedure or set its value.
+
Wallpaper: SysSetWallpaper(path) shows a BMP, JPG, PNG, or GIF image behind the desktop ("" for none) and returns False when the image could not be loaded (the old one stays). SysSetWallpaperMode mode says how it fills the screen, a choice of PREFS_CHOICE_WALLPAPER (Stretch, Tile, or Center; see Settings Files). SysWallpaper$() and SysWallpaperMode() tell what is in use.
+
Text: SysSetTextSmoothing True smooths the edges of text and SysSetTextSmoothing False stops it; SysTextSmoothing() tells which the user chose. Text is smoothed only when the screen shows more than 256 colors, and only in the fonts and sizes that come smoothed. Every window is drawn again when it changes.
+
Mouse: SysSetMouse wheelDir, dblClickMs, accel, mickeys, wheelStep applies a whole mouse setup at once: the scroll wheel's direction (1 or -1, the values of PREFS_CHOICE_WHEEL), the double-click time in milliseconds, the acceleration (a value of PREFS_CHOICE_ACCEL), how far the mouse moves for 8 pixels of pointer movement (8 is normal, higher is slower), and the lines scrolled per wheel notch (1 to 10). An accel or mickeys of 0 leaves that one as it is. SysMouseWheelDir, SysMouseDoubleClick, SysMouseAccel, SysMouseMickeys, and SysMouseWheelStep read the setup in effect. MOUSE_WHEEL_STEP_MIN and MOUSE_WHEEL_STEP_MAX are the limits of the wheel step, and MOUSE_WHEEL_STEP_DEFAULT, MOUSE_DBLCLICK_DEFAULT_MS, and MOUSE_SPEED_DEFAULT are DVX's defaults (what a Default button puts back).
+
The pointer and the keyboard: SysMouseX() and SysMouseY() are where the mouse pointer is on the screen, and SysMouseButtons() the buttons held (vbLeftButton, vbRightButton, and vbMiddleButton bits) -- also while the pointer is over another program's window, where your MouseMove events do not see it. SysSetMousePos x, y moves the pointer (it is kept on the screen). SysShiftState() is the Shift, Ctrl, and Alt keys held now (vbShiftMask, vbCtrlMask, and vbAltMask bits). SysSendKey(keyCode, shift) types a key into the window that has the focus as if the user pressed it, like SendKeys in other BASICs: keyCode as a KeyDown event gets it -- a character code such as ASC("a") or 13 for Enter, or a vbKey constant such as vbKeyF1 -- and shift the vbShiftMask, vbCtrlMask, and vbAltMask bits held with it (SysSendKey ASC("s"), vbCtrlMask is Ctrl+S). The keys arrive in order, one at a time, once your code has returned to DVX; up to 64 can wait, and SysSendKey returns False when that many are waiting. A program cannot press the mouse buttons: to "click" a control, call its event procedure or set its value.
Memory: SysMemoryTotalKb() is the computer's memory in KB and SysMemoryFreeKb() how much of it is free (-1 when it cannot be told).
System font: SysFontWidth() and SysFontHeight() are the size of every character of the system font in pixels, and SysFontFirstChar() to SysFontFirstChar() + SysFontCharCount() - 1 the character codes it has. SysFontGlyphRow(code, row) is one row of a character's picture: a bit per pixel, &H80 the leftmost -- for drawing text dot by dot, enlarged or in a font editor of your own.
-
Saving: SysColorsStore, SysWallpaperStore and SysMouseStore write the colors, the wallpaper and the mouse setup into a prefs.bas handle, in the sections DVX reads when it starts; PrefsSetVideoMode does the same for a screen mode. Store them in PrefsLoad(PrefsDvxIni$()) and save it, and DVX starts with them next time. SysColorsLoad, SysWallpaperLoad and SysMouseLoad apply what a handle holds. A PrefsCreate handle filled with the Store calls is a snapshot in memory: load it again to undo every change, the way Cancel works in Control Panel.
+
Saving: SysColorsStore, SysWallpaperStore, SysFontsStore, and SysMouseStore write the colors, the wallpaper, text smoothing, and the mouse setup into a prefs.bas handle, in the sections DVX reads when it starts; PrefsSetVideoMode does the same for a screen mode. Store them in PrefsLoad(PrefsDvxIni$()) and save it, and DVX starts with them next time. SysColorsLoad, SysWallpaperLoad, SysFontsLoad, and SysMouseLoad apply what a handle holds. A PrefsCreate handle filled with the Store calls is a snapshot in memory: load it again to undo every change, the way Cancel works in Control Panel.
Dim snap As Long
Dim oldW As Long, oldH As Long, oldBpp As Long
@@ -5114,6 +5196,7 @@ Sub Form1_Load ()
snap = PrefsCreate()
SysColorsStore snap
SysWallpaperStore snap
+ SysFontsStore snap
SysMouseStore snap
oldW = Screen.Width
oldH = Screen.Height
@@ -5125,6 +5208,7 @@ Sub cmdOK_Click ()
ini = PrefsLoad(PrefsDvxIni$())
SysColorsStore ini
SysWallpaperStore ini
+ SysFontsStore ini
SysMouseStore ini
PrefsSetVideoMode ini, Screen.Width, Screen.Height, Screen.BitsPerPixel
If Not PrefsSave(ini) Then MsgBox "The settings could not be saved."
@@ -5135,14 +5219,53 @@ End Sub
Sub cmdCancel_Click ()
SysColorsLoad snap
SysWallpaperLoad snap
+ SysFontsLoad snap
SysMouseLoad snap
If Screen.Width <> oldW Or Screen.Height <> oldH Or Screen.BitsPerPixel <> oldBpp Then
SysSetVideoMode oldW, oldH, oldBpp
End If
Unload Me
End Sub
+
Fonts
+
Besides the System font (the 8x16 font DVX draws everything with unless told otherwise), DVX comes with font families in its FONTS folder: families for text (sans-serif, serif, and fixed-pitch), display and handwriting families for headings, and pixel fonts. Which of them a computer has depends on how DVX was installed: Setup puts in none, the base set (DejaVu Sans, Sans Mono, and Serif), or all of them. Text in a family that is not installed is drawn in the System font, so a program that cares can look for the family first (FontFamilyCount and FontFamilyName$). With font.bas in the project a program can list them and measure text in them.
+
A family comes in up to four styles -- regular, bold, italic, and bold italic -- and in the pixel sizes it was made at. A font is always drawn at one of those sizes, never scaled: asking for another size gives the nearest one (FontNearestSize says which), and asking for a style a family has no face for makes it from the nearest face, bold by thickening and italic by slanting. Name a font by its family, size, and style on every call; a family name DVX does not have means the System font. Style is 0 for regular or FONT_BOLD + FONT_ITALIC; FONT_UNDERLINE and FONT_STRIKE are lines drawn with the text.
+
' With font.bas added to the project: every family and its sizes
+Dim i As Long
+Dim j As Long
+Dim sizes As String
+
+For i = 0 To FontFamilyCount() - 1
+ sizes = ""
+ For j = 0 To FontSizeCount(FontFamilyName$(i), 0) - 1
+ sizes = sizes & " " & Str$(FontSizeAt(FontFamilyName$(i), 0, j))
+ Next j
+ Print FontFamilyName$(i); ":"; sizes
+Next i
+
+' How wide a heading is in 24-pixel bold DejaVu Sans
+Print FontTextWidth("DejaVu Sans", 24, FONT_BOLD, "Chapter One")
+
FontFamilyFixed tells a fixed-pitch family (every character as wide as every other, for columns and code) from a proportional one. FontLineHeight is the height of one line of text and FontAscent the distance from the top of a line to the baseline the letters sit on. FontRescan reads the FONTS folder again after fonts were added or removed. FontLicence$(family, size, style) gives the notice and licence that go with a font: where it came from, its copyright notices, and its licence text (empty for the System font).
+
A program can carry fonts of its own, so its text looks the same whatever fonts were installed. List the .DFN files in the project's Fonts (Project > Properties in the IDE): Make builds them into the program, and they are its fonts whenever it runs, from the IDE too. FontAddFile(path), FontAddData(label, bytes), and FontAddResources(file) add more while it runs. A program's fonts are its own -- other programs never see them -- and they go when it ends. Where a font it carries is of a family that is installed too, its sizes and styles come first and the installed family fills in the rest: a program that carries DejaVu Sans Bold at 36 pixels draws that size from its own font and every other size and style of DejaVu Sans from the FONTS folder. Fonts are made from TrueType, Type 1, and bitmap fonts with the SDK's mkfont, or copied from the FONTS folder; each .DFN carries its own licence.
+
' With font.bas added to the project: a title in a font the program carries
+If FontAddFile(App.Path + "\TITLE.DFN") Then
+ lblTitle.FontName = "DejaVu Sans"
+ lblTitle.FontSize = 36
+ lblTitle.FontBold = True
+End If
+
Every control and every form can have a font of its own, set with the properties FontName, FontSize (in pixels), FontBold, FontItalic, FontUnderline, and FontStrikethru, as in Visual Basic; they need no include file. A control that is given no font uses the font of the control it is in (a Frame, for example), else its form's, so setting a form's font changes every control on it that has none. Reading one of the properties gives the font the object draws with: a FontSize the family does not come in reads back as the nearest size it has, and a FontName DVX does not have reads back as "System". Setting one changes only that part of the font. Controls that edit text in columns (TextBox, TextArea, ComboBox, SpinButton, and Terminal) take only fixed-pitch families and show the System font in place of a proportional one. Print, TextWidth, TextHeight, PrintAccel, and TextWidthAccel on forms and picture boxes use the object's font. In the form designer, the Font row of the Properties window sets the six properties at once, and they are saved in the .frm file; DlgFont (see Font Dialog) lets the user choose a font while the program runs.
+
Sub Form1_Load ()
+ Me.FontName = "DejaVu Sans" ' every control without a font of its own
+ Me.FontSize = 14
+ lblTitle.FontSize = 24 ' the title larger, in DejaVu Sans too
+ lblTitle.FontBold = True
+ txtNotes.FontName = "DejaVu Sans Mono"
+End Sub
+
Two controls mix fonts in one control. A RichLabel shows text built by calls -- SetFont, SetStyle, and SetColor choose how the text added next looks, AddText adds it -- in paragraphs aligned left, centered, or right. A RichEdit is a text box the user writes such text in, like a small word processor: the Sel properties (SelBold, SelFontName, SelColor, SelAlignment, ...) read and change the selection's look, TextRTF holds the whole text with its looks as RTF, and LoadFile and SaveFile read and write files. richtext.bas turns RTF into plain text and back without a control. See the controls' own pages for their properties.
+
Sub cmdBold_Click ()
+ RichEdit1.SelBold = Not RichEdit1.SelBold
+End Sub
Held Keys for Games
-
With game.bas in the project, GameKeyDown(keyCode) tells whether a key is being held down right now, so a game can move something for as long as a key is held instead of waiting for key presses. It returns True only while one of the program's own windows is active. Name the key with one of the GAMEKEY_ constants (the arrow keys, Shift, Ctrl, Alt, Space, Enter, Esc and a few letters) or with its PC keyboard scan code (add &H80 for the grey keys between the typing keys and the number pad). A Timer polls it:
+
With game.bas in the project, GameKeyDown(keyCode) tells whether a key is being held down right now, so a game can move something for as long as a key is held instead of waiting for key presses. It returns True only while one of the program's own windows is active. Name the key with one of the GAMEKEY_ constants (the arrow keys, Shift, Ctrl, Alt, Space, Enter, Esc, and a few letters) or with its PC keyboard scan code (add &H80 for the grey keys between the typing keys and the number pad). A Timer polls it:
Sub Timer1_Timer ()
If GameKeyDown(GAMEKEY_LEFT) Then shipX = shipX - 2
If GameKeyDown(GAMEKEY_RIGHT) Then shipX = shipX + 2
@@ -5158,15 +5281,15 @@ End Sub
FRAMEWAIT 60
Loop Until quitting
Joysticks
-
With joystick.bas in the project, a program reads joysticks and game pads. JoyCount() says how many there are; they are numbered from 1, and so are their axes, buttons and hats. JoyX(joy) and JoyY(joy) say where the stick points, from -32768 (all the way left or up) to 32767 (all the way right or down), 0 in the middle; JoyAxis(joy, axis) reads any axis (3 and 4 are a throttle or rudder). JoyButton(joy, button) is True while the button is held down, and also once after a quick press that happened since the program last asked about that button, so a Timer never misses a shot. JoyHat(joy, hat) gives the direction a hat switch is pushed in degrees (0 is up, 90 right) or JOY_HAT_CENTERED. JoyName$(joy), JoyAxes(joy) and JoyButtons(joy) describe the joystick.
+
With joystick.bas in the project, a program reads joysticks and game pads. JoyCount() says how many there are; they are numbered from 1, and so are their axes, buttons, and hats. JoyX(joy) and JoyY(joy) say where the stick points, from -32768 (all the way left or up) to 32767 (all the way right or down), 0 in the middle; JoyAxis(joy, axis) reads any axis (3 and 4 are a throttle or rudder). JoyButton(joy, button) is True while the button is held down, and also once after a quick press that happened since the program last asked about that button, so a Timer never misses a shot. JoyHat(joy, hat) gives the direction a hat switch is pushed in degrees (0 is up, 90 right) or JOY_HAT_CENTERED. JoyName$(joy), JoyAxes(joy), and JoyButtons(joy) describe the joystick.
Sub Timer1_Timer ()
If JoyCount() = 0 Then Exit Sub
shipX = shipX + JoyX(1) \ 8192
If JoyButton(1, 1) Then FireShot
End Sub
-
When no joystick is connected the keyboard stands in for one (the arrow keys, with Left Ctrl, Space, Left Shift and Z as buttons), and it only answers while one of the program's windows is active. Joysticks are calibrated, and the keyboard joystick's keys chosen, on the Control Panel's Game Controllers tab. QBasic's STICK and STRIG work too, without joystick.bas.
-
joystick.bas can also set joysticks up, as the Control Panel does. JoyHats(joy) counts the hats, JoyAxisPresent(joy, axis) is True when an axis answers, JoyConnected(joy) is False once a joystick stops answering, and JoyCanCalibrate(joy) is True for one with analog axes. For calibrating, JoyRawAxis(joy, axis) gives an axis's raw reading and JoyRawButtons(joy) the buttons held (1 for button 1, 2 for button 2, 4 for button 3, ... added up); JoySetCalibration(joy, axis, minV, centerV, maxV) takes the readings at one end, the middle and the other end, and JoyClearCalibration(joy, axis) lets the axis find its own range again. JoyDeadZone(joy) and JoySetDeadZone(joy, percent) are the dead zone round the middle (0 to 50 percent). JoyChoiceCount, JoyChoiceLabel$, JoyChoice and JoySetChoice list and change the game port's mode (JOY_CHOICE_GAMEPORT) and when the keyboard joystick stands in (JOY_CHOICE_FALLBACK); JoyKeyCount and JoyKeyName$(index) list the keys the keyboard joystick can use, and JoyKbdSlot(slot) and JoySetKbdSlot(slot, keyIndex) its keys for up, down, left, right and buttons 1 to 4 (slots 0 to 7). These lists are numbered from 0, so they fill a DropDown in order. JoyKeyIndex(keyName) gives a key's number by its name (-1 when unknown), JoyKeyCode(index) its raw key code, which GameKeyDown in game.bas takes, and JoyKeyNameOf$(rawCode) the name of a raw key code. JoyIsKeyboard(joy) is True for the keyboard standing in for a joystick, and JoyCalibration(joy, axis, part) reads back an axis's calibration: part JOY_CAL_MIN, JOY_CAL_CENTER or JOY_CAL_MAX of the readings set with JoySetCalibration, or of the range the axis found by itself (0 for a joystick that cannot be calibrated). JoySetting$(key, default) and JoySetSetting key, value reach any other joystick setting; JOY_KEY_POLL_HZ is the key of how often joysticks are read, JOY_POLL_HZ_MIN to JOY_POLL_HZ_MAX times a second (JOY_POLL_HZ_DEFAULT normally), and dead zones go up to JOY_DEADZONE_MAX percent (JOY_DEADZONE_DEFAULT normally). After a new game port mode, JoyRescan() looks for joysticks again; it returns the new count, or -1 while another program is using a joystick. Every change applies at once; JoySettingsStore handle puts them all into a settings handle from prefs.bas for the program to save, and JoySettingsLoad handle takes them back from one, to undo.
+
When no joystick is connected the keyboard stands in for one (the arrow keys, with Left Ctrl, Space, Left Shift, and Z as buttons), and it only answers while one of the program's windows is active. Joysticks are calibrated, and the keyboard joystick's keys chosen, on the Control Panel's Game Controllers tab. QBasic's STICK and STRIG work too, without joystick.bas.
+
joystick.bas can also set joysticks up, as the Control Panel does. JoyHats(joy) counts the hats, JoyAxisPresent(joy, axis) is True when an axis answers, JoyConnected(joy) is False once a joystick stops answering, and JoyCanCalibrate(joy) is True for one with analog axes. For calibrating, JoyRawAxis(joy, axis) gives an axis's raw reading and JoyRawButtons(joy) the buttons held (1 for button 1, 2 for button 2, 4 for button 3, ... added up); JoySetCalibration(joy, axis, minV, centerV, maxV) takes the readings at one end, the middle, and the other end, and JoyClearCalibration(joy, axis) lets the axis find its own range again. JoyDeadZone(joy) and JoySetDeadZone(joy, percent) are the dead zone round the middle (0 to 50 percent). JoyChoiceCount, JoyChoiceLabel$, JoyChoice, and JoySetChoice list and change the game port's mode (JOY_CHOICE_GAMEPORT) and when the keyboard joystick stands in (JOY_CHOICE_FALLBACK); JoyKeyCount and JoyKeyName$(index) list the keys the keyboard joystick can use, and JoyKbdSlot(slot) and JoySetKbdSlot(slot, keyIndex) its keys for up, down, left, right, and buttons 1 to 4 (slots 0 to 7). These lists are numbered from 0, so they fill a DropDown in order. JoyKeyIndex(keyName) gives a key's number by its name (-1 when unknown), JoyKeyCode(index) its raw key code, which GameKeyDown in game.bas takes, and JoyKeyNameOf$(rawCode) the name of a raw key code. JoyIsKeyboard(joy) is True for the keyboard standing in for a joystick, and JoyCalibration(joy, axis, part) reads back an axis's calibration: part JOY_CAL_MIN, JOY_CAL_CENTER, or JOY_CAL_MAX of the readings set with JoySetCalibration, or of the range the axis found by itself (0 for a joystick that cannot be calibrated). JoySetting$(key, default) and JoySetSetting key, value reach any other joystick setting; JOY_KEY_POLL_HZ is the key of how often joysticks are read, JOY_POLL_HZ_MIN to JOY_POLL_HZ_MAX times a second (JOY_POLL_HZ_DEFAULT normally), and dead zones go up to JOY_DEADZONE_MAX percent (JOY_DEADZONE_DEFAULT normally). After a new game port mode, JoyRescan() looks for joysticks again; it returns the new count, or -1 while another program is using a joystick. Every change applies at once; JoySettingsStore handle puts them all into a settings handle from prefs.bas for the program to save, and JoySettingsLoad handle takes them back from one, to undo.
Sub cmdCenter_Click ()
' The stick rests in the middle: remember the readings
centerX = JoyRawAxis(1, 1)
@@ -5179,7 +5302,7 @@ Sub cmdFinish_Click ()
End Sub
With sound.bas in the project, a program plays sound files and tones through whatever sound hardware the computer has: a Sound Blaster, the PC speaker (tones only, unless sampled sound on the speaker is switched on) or nothing at all, in which case the program works the same but is silent. Several programs can play at once, and everything a program plays stops when it ends.
+
With sound.bas in the project, a program plays sound files and tones through whatever sound hardware the computer has: a Sound Blaster, the PC speaker (tones only, unless sampled sound on the speaker is switched on), or nothing at all, in which case the program works the same but is silent. Several programs can play at once, and everything a program plays stops when it ends.
PlayWave(fileName) plays a WAV or VOC file once and PlayWaveLoop(fileName) over and over; PlayTone(frequency, milliseconds, volume) plays a tone of frequency hertz (20 to 20000) and PlayNoise(frequency, milliseconds, volume) noise, which sounds brighter the higher the frequency; a length of 0 plays until stopped. Each returns a voice number, or SOUND_FAILED (0) when the sound could not be played, and then SoundError$() says why. StopWave(voice) stops one sound, StopAllSounds() all of the program's sounds, and SoundPlaying(voice) is True while a sound is still playing. SoundVolume(volume) sets the volume of everything the program plays. Volumes are percent (0 to 100). SoundInfo$() describes the sound device, like "Sound Blaster 16 (DSP 4.05) at 220h, 22050 Hz, 16-bit stereo".
Dim Shared boom As Long
@@ -5190,9 +5313,9 @@ Sub cmdFire_Click ()
lblStatus.Caption = SoundError$()
End If
End Sub
-
WAV files must be uncompressed (8 or 16 bits, mono or stereo); VOC files may be 8- or 16-bit. QBasic's BEEP, SOUND and PLAY work too, without sound.bas.
-
PlayTriangle(frequency, milliseconds, volume) plays a triangle tone, softer than PlayTone's square tone. SoundGetVolume() gives the volume of everything the program plays. SoundCaps(field) tells what the sound device can do, by name in any case: "dac" (it plays recorded sound), "synth" (it has a music synthesizer), "tone" (it plays tones on a tone generator) and "running" are True or False; "voices" (how many sounds play at once), "rate" (hertz), "bits" and "channels" are numbers. SoundPlayEvent eventIdx, wait plays one of DVX's own event sounds (SOUND_EVENT_DEFAULT, SOUND_EVENT_ERROR, SOUND_EVENT_WARNING, ...), the one the settings choose; with wait True it returns once the sound has played.
-
A sound played often is best loaded once. SoundLoad(fileName) loads a WAV or VOC file and returns a sound number, or 0 (SoundError$() says why); SoundCreate(data, rate, bits, channels) makes a sound from raw sound data in a string: 8-bit samples (0 to 255, 128 is silence) or 16-bit ones (two bytes each, the low byte first), mono or stereo (left, right, left, ...), rate times a second (SOUND_RATE_MIN to SOUND_RATE_MAX). SoundPlay(sound, volume, pan, loopIt) plays it on a new voice and returns the voice number; pan goes from SOUND_PAN_LEFT (-100) through SOUND_PAN_CENTER (0) to SOUND_PAN_RIGHT (100). SoundLength(sound) is its length in milliseconds and SoundFree sound stops its voices and forgets it. While a voice plays, SoundSetVolume voice, volume, SoundSetPan voice, pan and SoundSetPitch voice, percent change it (pitch is a percent of the recorded speed: SOUND_PITCH_NORMAL is 100, 200 is an octave higher, from SOUND_PITCH_MIN 25 to SOUND_PITCH_MAX 400), and SoundSetFrequency voice, hertz changes a tone's frequency. A program's sounds are freed when it ends.
+
WAV files must be uncompressed (8 or 16 bits, mono or stereo); VOC files may be 8- or 16-bit. QBasic's BEEP, SOUND, and PLAY work too, without sound.bas.
+
PlayTriangle(frequency, milliseconds, volume) plays a triangle tone, softer than PlayTone's square tone. SoundGetVolume() gives the volume of everything the program plays. SoundCaps(field) tells what the sound device can do, by name in any case: "dac" (it plays recorded sound), "synth" (it has a music synthesizer), "tone" (it plays tones on a tone generator), and "running" are True or False; "voices" (how many sounds play at once), "rate" (hertz), "bits", and "channels" are numbers. SoundPlayEvent eventIdx, wait plays one of DVX's own event sounds (SOUND_EVENT_DEFAULT, SOUND_EVENT_ERROR, SOUND_EVENT_WARNING, ...), the one the settings choose; with wait True it returns once the sound has played.
+
A sound played often is best loaded once. SoundLoad(fileName) loads a WAV or VOC file and returns a sound number, or 0 (SoundError$() says why); SoundCreate(data, rate, bits, channels) makes a sound from raw sound data in a string: 8-bit samples (0 to 255, 128 is silence) or 16-bit ones (two bytes each, the low byte first), mono or stereo (left, right, left, ...), rate times a second (SOUND_RATE_MIN to SOUND_RATE_MAX). SoundPlay(sound, volume, pan, loopIt) plays it on a new voice and returns the voice number; pan goes from SOUND_PAN_LEFT (-100) through SOUND_PAN_CENTER (0) to SOUND_PAN_RIGHT (100). SoundLength(sound) is its length in milliseconds and SoundFree sound stops its voices and forgets it. While a voice plays, SoundSetVolume voice, volume, SoundSetPan voice, pan, and SoundSetPitch voice, percent change it (pitch is a percent of the recorded speed: SOUND_PITCH_NORMAL is 100, 200 is an octave higher, from SOUND_PITCH_MIN 25 to SOUND_PITCH_MAX 400), and SoundSetFrequency voice, hertz changes a tone's frequency. A program's sounds are freed when it ends.
Dim Shared laser As Long
Sub Form1_Load ()
@@ -5204,15 +5327,15 @@ Sub Fire (x As Integer)
v = SoundPlay(laser, 80, (x - 160) * 100 \ 160, False)
SoundSetPitch v, 90 + Int(Rnd * 20)
End Sub
-
A program can also make its sound as it goes, for speech or music of its own. SoundStreamOpen(rate, bits, channels, bufferMs) opens a stream that starts playing at once (silence until data arrives) and holds bufferMs milliseconds of sound (0 for the usual SOUND_STREAM_MS_DEFAULT; SOUND_STREAM_MS_MIN to SOUND_STREAM_MS_MAX). SoundStreamWrite(stream, data) adds raw sound data in the stream's format and returns how many bytes it took; keep the rest and write it again later. SoundStreamSpace(stream) is the room left and SoundStreamQueued(stream) what is waiting to play, both in samples (a stereo sample counts once). SoundStreamVoice(stream) is the stream's voice, for SoundSetVolume, SoundSetPan and SoundSetPitch, and SoundStreamClose stream stops it. A Timer keeps a stream fed:
+
A program can also make its sound as it goes, for speech or music of its own. SoundStreamOpen(rate, bits, channels, bufferMs) opens a stream that starts playing at once (silence until data arrives) and holds bufferMs milliseconds of sound (0 for the usual SOUND_STREAM_MS_DEFAULT; SOUND_STREAM_MS_MIN to SOUND_STREAM_MS_MAX). SoundStreamWrite(stream, data) adds raw sound data in the stream's format and returns how many bytes it took; keep the rest and write it again later. SoundStreamSpace(stream) is the room left and SoundStreamQueued(stream) what is waiting to play, both in samples (a stereo sample counts once). SoundStreamVoice(stream) is the stream's voice, for SoundSetVolume, SoundSetPan, and SoundSetPitch, and SoundStreamClose stream stops it. A Timer keeps a stream fed:
Sub Timer1_Timer ()
Dim taken As Long
taken = SoundStreamWrite(stream, pending)
pending = Mid$(pending, taken + 1)
If Len(pending) = 0 Then pending = MakeMoreSound()
End Sub
-
On a computer with an FM synthesizer chip (AdLib and most Sound Blasters), a program can play instruments of its own. FmInfo$(field) describes the chip: "description" (like "OPL3 FM synthesizer at 388h", "" when there is none), "voices", "free" (voices no program has), "stereo" and "owned" (one program has the whole chip). FmAlloc() takes one of its two-operator voices (0 when none is free), FmWrite(voice, register, value) sets one of the voice's registers (the FM_ constants: FM_MOD_CHAR, FM_CAR_LEVEL, FM_FNUM_LOW, FM_KEY_BLOCK with FM_KEY_ON, ...; True when it worked), and FmFree voice gives it back. A player of chip music files can take the whole chip with FmRawAcquire() (False while another program has it), set any register with FmRawWrite(register, value) (bank * FM_BANK_REGS + register; the second bank only on an OPL3) and give it back with FmRawRelease. A frequency number for FM_FNUM_LOW and FM_KEY_BLOCK is hertz * 2 ^ 20 / (FM_CHIP_HZ * 2 ^ block).
-
sound.bas also changes DVX's own sound settings, as the Control Panel's Sounds page does. DVX plays a sound for each of SoundEventCount() events, numbered from 0: SoundEventLabel$(eventIdx) names one ("Start DVX", "Error", ...) and SoundEventKey$(eventIdx) gives its key in DVX.INI. With a settings handle from prefs.bas (PrefsLoad(PrefsDvxIni$())), SoundEventFile$(handle, eventIdx) is the event's sound file ("" for none) and SoundSetEventFile handle, eventIdx, fileName changes it; after the program saves the file, SoundEventsReload makes DVX play the new sounds. SoundTestFile(fileName) plays a file the way DVX plays its event sounds (0 when it cannot, and SoundError$() says why), and SoundFolder$() is the folder of DVX's own sounds. SoundMasterVolume() and SoundSetMasterVolume percent are the volume of all sound from every program. SoundDriverCount() reads the list of sound drivers and SoundDriverName$(index) names each; SoundDriverAuto$() is the setting that means "find the best one". SoundMidiCount() and SoundMidiLabel$(index) list where MIDI music can play. SoundSettingDriver$(handle), SoundSettingMidi(handle) and SoundSettingVolume(handle) read the sound driver, MIDI choice and master volume from the settings, and SoundSettingsStore handle, driver, midi, volume stores them (a new driver or MIDI choice takes effect when DVX next starts).
+
On a computer with an FM synthesizer chip (AdLib and most Sound Blasters), a program can play instruments of its own. FmInfo$(field) describes the chip: "description" (like "OPL3 FM synthesizer at 388h", "" when there is none), "voices", "free" (voices no program has), "stereo", and "owned" (one program has the whole chip). FmAlloc() takes one of its two-operator voices (0 when none is free), FmWrite(voice, register, value) sets one of the voice's registers (the FM_ constants: FM_MOD_CHAR, FM_CAR_LEVEL, FM_FNUM_LOW, FM_KEY_BLOCK with FM_KEY_ON, ...; True when it worked), and FmFree voice gives it back. A player of chip music files can take the whole chip with FmRawAcquire() (False while another program has it), set any register with FmRawWrite(register, value) (bank * FM_BANK_REGS + register; the second bank only on an OPL3) and give it back with FmRawRelease. A frequency number for FM_FNUM_LOW and FM_KEY_BLOCK is hertz * 2 ^ 20 / (FM_CHIP_HZ * 2 ^ block).
+
sound.bas also changes DVX's own sound settings, as the Control Panel's Sounds page does. DVX plays a sound for each of SoundEventCount() events, numbered from 0: SoundEventLabel$(eventIdx) names one ("Start DVX", "Error", ...) and SoundEventKey$(eventIdx) gives its key in DVX.INI. With a settings handle from prefs.bas (PrefsLoad(PrefsDvxIni$())), SoundEventFile$(handle, eventIdx) is the event's sound file ("" for none) and SoundSetEventFile handle, eventIdx, fileName changes it; after the program saves the file, SoundEventsReload makes DVX play the new sounds. SoundTestFile(fileName) plays a file the way DVX plays its event sounds (0 when it cannot, and SoundError$() says why), and SoundFolder$() is the folder of DVX's own sounds. SoundMasterVolume() and SoundSetMasterVolume percent are the volume of all sound from every program. SoundDriverCount() reads the list of sound drivers and SoundDriverName$(index) names each; SoundDriverAuto$() is the setting that means "find the best one". SoundMidiCount() and SoundMidiLabel$(index) list where MIDI music can play. SoundSettingDriver$(handle), SoundSettingMidi(handle), and SoundSettingVolume(handle) read the sound driver, MIDI choice, and master volume from the settings, and SoundSettingsStore handle, driver, midi, volume stores them (a new driver or MIDI choice takes effect when DVX next starts).
Dim ini As Long
Dim i As Long
@@ -5221,9 +5344,9 @@ ini = PrefsLoad(PrefsDvxIni$())
For i = 0 To SoundEventCount() - 1
lstEvents.AddItem SoundEventLabel$(i) & ": " & SoundEventFile$(ini, i)
Next i
With music.bas in the project, a program plays music files: tracker modules (MOD, S3M, XM and IT files, made of recorded instrument sounds) wherever the computer can play sampled sound, and MIDI files (.MID, types 0 and 1) on the computer's music synthesizer, or on a built-in synthesizer that sounds much the same when there is none. Several programs can play music at once, and everything a program plays stops when it ends.
+
With music.bas in the project, a program plays music files: tracker modules (MOD, S3M, XM, and IT files, made of recorded instrument sounds) wherever the computer can play sampled sound, and MIDI files (.MID, types 0 and 1) on the computer's music synthesizer, or on a built-in synthesizer that sounds much the same when there is none. Several programs can play music at once, and everything a program plays stops when it ends.
MusicLoad(fileName) loads a music file and returns its music number, or MUSIC_FAILED (0) when it could not be loaded; MusicError$() then says why. MusicPlay(music, loopIt) plays it from where it is (the start after loading or stopping); with loopIt True it starts over at the end until it is stopped. MusicStop(music) stops and goes back to the start, MusicPause(music) and MusicResume(music) pause and go on, and MusicPlaying(music) is True while it plays. MusicVolume(music, volume) sets its volume in percent (0 to 100). MusicPosition(music) and MusicLength(music) give the place and the length in milliseconds, and MusicSeek(music, milliseconds) goes to a place (a module goes to the start of the part that holds it). MusicFree(music) stops a music and forgets it.
Dim Shared tune As Long
@@ -5236,7 +5359,7 @@ Sub Form1_Load ()
MusicPlay tune, True
End If
End Sub
-
A program can also play notes itself, like a keyboard. MusicMidiOpen() returns a live music that is already playing. MusicNoteOn(music, channel, note, velocity) starts a note and MusicNoteOff(music, channel, note) ends it; MusicProgram(music, channel, program) chooses a channel's instrument, MusicControl(music, channel, controller, value) sets a controller (7 volume, 10 pan, 11 expression, 64 the sustain pedal, 123 all notes off) and MusicPitchBend(music, channel, bend) bends the channel's notes (-8192 to 8191, two semitones either way at the ends). Channels are 1 to 16, and channel 10 (MUSIC_DRUMS) plays drums, one per note. Notes, velocities, instruments and controller values are 0 to 127; middle C is note 60, and instruments are numbered as in General MIDI (0 piano, 24 guitar, 40 violin, 56 trumpet, 73 flute). The same calls work on a MIDI file that is playing.
+
A program can also play notes itself, like a keyboard. MusicMidiOpen() returns a live music that is already playing. MusicNoteOn(music, channel, note, velocity) starts a note and MusicNoteOff(music, channel, note) ends it; MusicProgram(music, channel, program) chooses a channel's instrument, MusicControl(music, channel, controller, value) sets a controller (7 volume, 10 pan, 11 expression, 64 the sustain pedal, 123 all notes off), and MusicPitchBend(music, channel, bend) bends the channel's notes (-8192 to 8191, two semitones either way at the ends). Channels are 1 to 16, and channel 10 (MUSIC_DRUMS) plays drums, one per note. Notes, velocities, instruments, and controller values are 0 to 127; middle C is note 60, and instruments are numbered as in General MIDI (0 piano, 24 guitar, 40 violin, 56 trumpet, 73 flute). The same calls work on a MIDI file that is playing.
Dim Shared keys As Long
Sub Form1_Load ()
@@ -5253,8 +5376,8 @@ Sub tmrNoteOff_Timer ()
MusicNoteOff keys, 1, 60
tmrNoteOff.Enabled = False
End Sub
-
MusicGetVolume(music) gives a music's volume in percent. MusicInfo$(music, field) tells about it, by name in any case: "title" (a module's own title), "output" (where it plays, for people), "kind" ("module", "midi" or "live"), "where" ("stream", "mpu401", "fm" or "software"; "" before it first plays), and "paused" and "looping" (True or False as text; VAL gives the value).
-
A program that sends MIDI itself (system exclusive messages, a sequencer of its own) can have the MIDI port, one program at a time, when the settings send MIDI music to the MIDI port. MidiPortInfo$(field) describes it: "description" ("" when there is none), "present" and "owned" (a program has it). MidiAcquire() takes the port (False when another program has it or there is none), MidiPut(byteValue) sends one byte (False when the port could not take it yet: send it again) and MidiRelease gives the port back.
+
MusicGetVolume(music) gives a music's volume in percent. MusicInfo$(music, field) tells about it, by name in any case: "title" (a module's own title), "output" (where it plays, for people), "kind" ("module", "midi", or "live"), "where" ("stream", "mpu401", "fm", or "software"; "" before it first plays), and "paused" and "looping" (True or False as text; VAL gives the value).
+
A program that sends MIDI itself (system exclusive messages, a sequencer of its own) can have the MIDI port, one program at a time, when the settings send MIDI music to the MIDI port. MidiPortInfo$(field) describes it: "description" ("" when there is none), "present", and "owned" (a program has it). MidiAcquire() takes the port (False when another program has it or there is none), MidiPut(byteValue) sends one byte (False when the port could not take it yet: send it again), and MidiRelease gives the port back.
Network Connections
With net.bas in the project, a program talks to other computers over the network set up in the Control Panel. TcpConnect returns a handle (0 when it failed; NetError$ says why), TcpSend sends text, and TcpRecv$ returns whatever has arrived without waiting, so a Timer keeps the program responsive:
Dim conn As Long
@@ -5344,7 +5467,7 @@ If msg <> "" And Not CommRecvEncrypted(link, 1) Then
Print "Unencrypted: " & msg
End If
Connections and Terminals
-
With conn.bas in the project, a program talks to another computer the way the Terminal program does, whatever is in between: a serial port, a modem (it sends its setup command, dials, dials again when the line is busy and notices when the carrier is lost), the secure link to the DVX host proxy (encrypted, and authenticated with a passphrase), or Telnet over the network. ConnCreate makes a connection (a number); ConnSetSerial, ConnSetModem, ConnSetHost and ConnSetPassphrase set it up, and ConnOpen(conn, type) starts it. Opening only starts: dialing and the key exchange go on while the program runs, so call ConnPoll often from a Timer. ConnState says where it is (CONN_CLOSED, CONN_OPENING, CONN_OPEN) and ConnStatus$ what is happening or why it ended; ConnStatusSerial changes whenever ConnStatus$ does.
+
With conn.bas in the project, a program talks to another computer the way the Terminal program does, whatever is in between: a serial port, a modem (it sends its setup command, dials, dials again when the line is busy, and notices when the carrier is lost), the secure link to the DVX host proxy (encrypted, and authenticated with a passphrase), or Telnet over the network. ConnCreate makes a connection (a number); ConnSetSerial, ConnSetModem, ConnSetHost, and ConnSetPassphrase set it up, and ConnOpen(conn, type) starts it. Opening only starts: dialing and the key exchange go on while the program runs, so call ConnPoll often from a Timer. ConnState says where it is (CONN_CLOSED, CONN_OPENING, CONN_OPEN) and ConnStatus$ what is happening or why it ended; ConnStatusSerial changes whenever ConnStatus$ does.
ConnAttach conn, "Term1" puts what arrives on the Terminal control Term1 (ConnPoll moves it there) and sends what is typed in it. ConnPoll then also appends what arrives to a capture file (ConnCapture), answers the other side's ENQ with an answerback (ConnSetAnswerback), and notices a ZMODEM sender starting a download: ConnZmodemRequest is True once, and the program answers with ConnReceiveFiles in the same Timer event. ConnSetEcho shows what is typed on the Terminal too. Without a Terminal, read with ConnRead$ and send with ConnWrite (any bytes, CHR$(0) too). ConnOpen returns CONN_OK or a CONN_ERR_ code; ConnErrorText$ puts the code in words. ConnSetXferWindow conn, bytes limits how much a ZMODEM sender may send before it waits for an answer (0 = no limit, up to XFER_WINDOW_MAX; -1, the default, picks by the line). ConnSendBreak holds a serial line in the break state for a moment; ConnClose hangs up and ConnFree forgets the connection.
Dim Shared conn As Long
Dim Shared download As Long
@@ -5361,7 +5484,7 @@ Sub Timer1_Timer ()
If ConnZmodemRequest(conn) Then download = ConnReceiveFiles(conn, XFER_ZMODEM, "DOWNLOAD", XFER_EXIST_RENAME, False)
End Sub
File Transfers
-
With xfer.bas in the project, a program sends and receives files with XMODEM, XMODEM-1K, YMODEM or ZMODEM. A transfer is a number: XferSendFile and XferReceive start one on a serial port opened with SerOpen (comm.bas), and ConnSendFiles and ConnReceiveFiles start one on a connection (conn.bas), with the options Terminal has: escaping every control character, asking the receiver to continue interrupted files, and what to do with a file that is already there. On a serial port XferPoll moves the transfer along; on a connection ConnPoll does. XferStatus tells where it is (XFER_RUNNING until it ends with XFER_DONE, XFER_FAILED or XFER_CANCELLED), and XferBytes, XferSize, XferFile$, XferFileIndex, XferFileCount, XferBatchBytes, XferBatchSize, XferResumedAt and XferErrors show its progress; XferMessage$ says how it ended. A receive with XFER_EXIST_ASK stops in XFER_ASKING when a file already exists: XferAskSize, XferAskRemoteSize (the size the sender offers, -1 when it did not say) and XferCanResume describe it, and XferAnswer continues with an XFER_EXIST_ choice. XferCancel stops a transfer and XferClose frees it.
+
With xfer.bas in the project, a program sends and receives files with XMODEM, XMODEM-1K, YMODEM, or ZMODEM. A transfer is a number: XferSendFile and XferReceive start one on a serial port opened with SerOpen (comm.bas), and ConnSendFiles and ConnReceiveFiles start one on a connection (conn.bas), with the options Terminal has: escaping every control character, asking the receiver to continue interrupted files, and what to do with a file that is already there. On a serial port XferPoll moves the transfer along; on a connection ConnPoll does. XferStatus tells where it is (XFER_RUNNING until it ends with XFER_DONE, XFER_FAILED, or XFER_CANCELLED), and XferBytes, XferSize, XferFile$, XferFileIndex, XferFileCount, XferBatchBytes, XferBatchSize, XferResumedAt, and XferErrors show its progress; XferMessage$ says how it ended. A receive with XFER_EXIST_ASK stops in XFER_ASKING when a file already exists: XferAskSize, XferAskRemoteSize (the size the sender offers, -1 when it did not say), and XferCanResume describe it, and XferAnswer continues with an XFER_EXIST_ choice. XferCancel stops a transfer and XferClose frees it.
XferCrc16(bytes) and XferCrc32(bytes) give the check values the protocols use (CRC-16 as XMODEM computes it, 0 to 65535; the CRC-32 of ZMODEM and ZIP files as a Long, negative when its top bit is set) for any bytes. XferSafeName$(name, folder) gives the name a receive into folder stores a sender's file name as: no folder or device name; a long name, its case kept, where folder's disk takes long names (a character a name cannot hold becomes _), else an 8.3 name of characters every disk accepts.
Lists You Draw Yourself
With listhelp.bas in the project, a list or icon view a program draws itself -- in a UserControl or a PictureBox -- follows the same rules as ListBox and ListView. ListSelCreate(multi, count) makes a selection for count items (several at a time when multi is True) and returns a handle; it remembers which items are selected and which one is current (the one the keyboard moves). Items are numbered from 0. Give it the clicks and keys, and ask it what to draw:
@@ -5369,7 +5492,7 @@ End Sub
-------- -----------
ListSelClick sel, index, shift A click on an item, with the MouseDown event's Shift: alone it selects only that item, with Ctrl it turns the item on or off, with Shift it selects the items from the last click up to it.
ListSelKey(sel, keyCode, shift) Ctrl+A selects every item (returns LISTSEL_KEY_ALL), Space turns the current item on or off (LISTSEL_KEY_TOGGLE); LISTSEL_KEY_NONE for other keys.
- ListSelNavigate(sel, keyCode, shift, columns, pageRows) The arrow keys, Home, End, PgUp and PgDn, in a list (columns 1) or a grid with columns items per row, pageRows rows a page; with Shift they select from the last click. The new current item, or -1 when nothing moved.
+ ListSelNavigate(sel, keyCode, shift, columns, pageRows) The arrow keys, Home, End, PgUp, and PgDn, in a list (columns 1) or a grid with columns items per row, pageRows rows a page; with Shift they select from the last click. The new current item, or -1 when nothing moved.
ListSelMove sel, index, shift Make an item current as the keyboard does (after type-ahead).
ListSelCursor(sel), ListSelSetCursor sel, index The current item (-1: none); make one item current and the only one selected.
ListSelIsSelected(sel, index), ListSelSelectedCount(sel) Whether an item is selected; how many are.
@@ -5395,13 +5518,13 @@ Sub UserControl_KeyPress (KeyAscii As Integer)
End If
End Sub
Selections a program leaves open are freed when it ends.
-
An icon view drawn with listhelp.bas looks and behaves like Program Manager's: the same cells for large icons, small icons and the list, the same frame and scroll bar, a large icon's name on two lines, and the same rules for the keys, dragging and dropping. The grid functions take the view mode (LISTICON_VIEW_LARGE, LISTICON_VIEW_SMALL or LISTICON_VIEW_LIST), the size of a character (TextWidth("W") and TextHeight("W")), the size of the whole view, how many items it shows, and rowsTaken: LISTICON_BY_ORDER when the items fill the grid in order, else how many rows items placed in cells of their own take. Draw the view's frame with DrawBevel (LISTICON_FRAME wide; with the face -1 after the items, so a row cut off at the bottom stops at the frame) and its scroll bar with DrawScrollBar as a control's bar (its control argument True); the grid leaves room for it.
+
An icon view drawn with listhelp.bas looks and behaves like Program Manager's: the same cells for large icons, small icons, and the list, the same frame and scroll bar, a large icon's name on two lines, and the same rules for the keys, dragging and dropping. The grid functions take the view mode (LISTICON_VIEW_LARGE, LISTICON_VIEW_SMALL, or LISTICON_VIEW_LIST), the size of a character (TextWidth("W") and TextHeight("W")), the size of the whole view, how many items it shows, and rowsTaken: LISTICON_BY_ORDER when the items fill the grid in order, else how many rows items placed in cells of their own take. Draw the view's frame with DrawBevel (LISTICON_FRAME wide; with the face -1 after the items, so a row cut off at the bottom stops at the frame) and its scroll bar with DrawScrollBar as a control's bar (its control argument True); the grid leaves room for it.
Function Description
-------- -----------
- ListIconMetric(mode, metric, cw, ch, w, h, count, rowsTaken) One value of the grid: a cell's size (LISTICON_CELL_W, LISTICON_CELL_H), where its picture goes and how big it is (LISTICON_PICTURE_X, LISTICON_PICTURE_Y, LISTICON_PICTURE_SIZE), where its name goes and how many characters it shows (LISTICON_TEXT_X, LISTICON_TEXT_Y, LISTICON_NAME_CHARS; a large icon's two lines are each centred in the cell), the list's detail column (LISTICON_DETAIL_X, 0 in the other views); the inside of the frame less the scroll bar (LISTICON_INNER_X, LISTICON_INNER_Y, LISTICON_INNER_W, LISTICON_INNER_H), the columns, the rows shown whole and in all (LISTICON_COLUMNS, LISTICON_ROWS_SHOWN, LISTICON_ROWS), the largest top row (LISTICON_TOP_ROW_MAX), True when the scroll bar is needed (LISTICON_SCROLLBAR), where the text of an empty view goes (LISTICON_EMPTY_X, LISTICON_EMPTY_Y), the frame's width (LISTICON_FRAME) and the rows a wheel notch scrolls (LISTICON_WHEEL_ROWS).
+ ListIconMetric(mode, metric, cw, ch, w, h, count, rowsTaken) One value of the grid: a cell's size (LISTICON_CELL_W, LISTICON_CELL_H), where its picture goes and how big it is (LISTICON_PICTURE_X, LISTICON_PICTURE_Y, LISTICON_PICTURE_SIZE), where its name goes and how many characters it shows (LISTICON_TEXT_X, LISTICON_TEXT_Y, LISTICON_NAME_CHARS; a large icon's two lines are each centred in the cell), the list's detail column (LISTICON_DETAIL_X, 0 in the other views); the inside of the frame less the scroll bar (LISTICON_INNER_X, LISTICON_INNER_Y, LISTICON_INNER_W, LISTICON_INNER_H), the columns, the rows shown whole and in all (LISTICON_COLUMNS, LISTICON_ROWS_SHOWN, LISTICON_ROWS), the largest top row (LISTICON_TOP_ROW_MAX), True when the scroll bar is needed (LISTICON_SCROLLBAR), where the text of an empty view goes (LISTICON_EMPTY_X, LISTICON_EMPTY_Y), the frame's width (LISTICON_FRAME), and the rows a wheel notch scrolls (LISTICON_WHEEL_ROWS).
ListIconCellAt(mode, cw, ch, w, h, count, rowsTaken, topRow, x, y) The cell under x, y with the view scrolled to topRow, numbered row * columns + column; -1 outside the grid.
ListIconLabel$(name, maxChars, lineNo) Line 1 or 2 of a large icon's name, maxChars characters a line (LISTICON_NAME_CHARS): the first line ends at the last blank that fits, else it is cut; the second is the rest, cut. "" for a line the name does not need.
- ListIconNavigate(keyCode, current, cells, columns, pageRows) The item the arrow keys, Home, End, PgUp or PgDn move to when the items stand in cells of their own, gaps allowed: cells holds each item's column and row, all separated by commas ("0,0,2,0,1,1"). -1 for another key or when no item is that way.
+ ListIconNavigate(keyCode, current, cells, columns, pageRows) The item the arrow keys, Home, End, PgUp, or PgDn move to when the items stand in cells of their own, gaps allowed: cells holds each item's column and row, all separated by commas ("0,0,2,0,1,1"). -1 for another key or when no item is that way.
ListIconScrollPress(mode, cw, ch, w, h, count, rowsTaken, topRow, x, y) A left press on the scroll bar: the top row after it (an arrow moves a row, the trough a page, the thumb nothing); -1 when x, y is not on the scroll bar.
ListIconScrollGrab(mode, cw, ch, w, h, count, rowsTaken, topRow, x, y) How far below the top of the thumb a press is; -1 when it is not on the thumb.
ListIconScrollDrag(mode, cw, ch, w, h, count, rowsTaken, grab, y) The top row with the thumb, held grab pixels below its top, dragged to y. While the button is down the control gets the mouse even outside it.
@@ -5429,7 +5552,7 @@ End Sub
SecCipherCreate(key) A cipher with a SEC_XTEA_KEY_SIZE-byte key; 0 for a key of another length.
SecCipherCrypt$(cipher, bytes) Encrypt -- or decrypt, the same call -- bytes. SecCipherSetCounter cipher, n sets the position in the key stream; the same key and position must never encrypt two different messages. SecCipherFree cipher frees it.
SecSha256$(bytes) The SHA-256 hash (SEC_SHA256_SIZE bytes). For data in pieces: SecSha256Open, SecSha256Add h, piece, then SecSha256End$(h), which also frees the handle.
- SecHmac$(key, bytes) The HMAC-SHA256 signature of bytes with key; SecHmacOpen(key), SecHmacAdd and SecHmacEnd$ for pieces.
+ SecHmac$(key, bytes) The HMAC-SHA256 signature of bytes with key; SecHmacOpen(key), SecHmacAdd, and SecHmacEnd$ for pieces.
SecHkdf$(salt, secret, info, length) length bytes (up to SEC_HKDF_MAX_OUT) of keys made from secret; a different info gives different keys.
SecEqual(a, b) True when a and b hold the same bytes, compared in the same time wherever they differ (for signature checks).
' Both sides: make keys, swap public keys (here over a TCP connection), agree a cipher key
@@ -5444,23 +5567,23 @@ End If
SecDhFree dh
Handles a program leaves open are freed, and their keys wiped, when it ends.
Settings Files
-
With prefs.bas in the project, a program keeps its settings in an INI file the way DVX's own programs do. PrefsLoad(path) reads the file once (a file that is not there reads as empty); PrefsGet$, PrefsGetInt and PrefsGetBool read a key of a section (each with the value to use when it is missing), and PrefsSet, PrefsSetInt, PrefsSetBool and PrefsRemove change it in memory. PrefsSave writes it back, keeping comments and every line it did not change. To write a whole new file -- dropping sections that are no longer wanted -- fill a PrefsCreate handle and save it with PrefsSaveAs: the old file is replaced only once the new one is completely written. PrefsClose frees a handle. IniRead and IniWrite (built in) do one value at a time and read or write the whole file each time; prefs.bas is the faster choice for many settings.
+
With prefs.bas in the project, a program keeps its settings in an INI file the way DVX's own programs do. PrefsLoad(path) reads the file once (a file that is not there reads as empty); PrefsGet$, PrefsGetInt, and PrefsGetBool read a key of a section (each with the value to use when it is missing), and PrefsSet, PrefsSetInt, PrefsSetBool, and PrefsRemove change it in memory. PrefsSave writes it back, keeping comments and every line it did not change. To write a whole new file -- dropping sections that are no longer wanted -- fill a PrefsCreate handle and save it with PrefsSaveAs: the old file is replaced only once the new one is completely written. PrefsClose frees a handle. IniRead and IniWrite (built in) do one value at a time and read or write the whole file each time; prefs.bas is the faster choice for many settings.
Dim ini As Long
ini = PrefsLoad(ShellConfigFile$("MYAPP.INI"))
userName = PrefsGet$(ini, "User", "Name", "Unknown")
PrefsSetInt ini, "Window", "Left", Me.Left
If Not PrefsSave(ini) Then MsgBox "The settings could not be saved."
PrefsClose ini
-
PrefsDvxIni$() is the path of DVX's own settings file, DVX.INI, which Control Panel changes; other libraries (system.bas, joystick.bas, net.bas, print.bas and sound.bas) read and store their part of it through a prefs.bas handle. PrefsSetVideoMode handle, wide, high, bpp stores the screen mode DVX starts in, and PrefsVideoWidth(handle), PrefsVideoHeight(handle) and PrefsVideoBpp(handle) read it (DVX's own start-up mode, SYS_VIDEO_DEFAULT_* in system.bas, for a value that is not there). PrefsKeyName$(handle, section, index) is the name of key number index of a section (from 0, in the order of the file; "" past the last key), to go through every key of a section.
-
Some settings are stored by name, from a fixed list of choices: the mouse acceleration (PREFS_CHOICE_ACCEL), the wallpaper mode (PREFS_CHOICE_WALLPAPER) and the scroll wheel direction (PREFS_CHOICE_WHEEL). For each list, PrefsChoiceCount(table) says how many choices it has (numbered from 0), PrefsChoiceLabel$(table, i) is the text to show, PrefsChoiceName$(table, i) the name stored in the file and PrefsChoiceValue(table, i) the value it stands for (what SysSetMouse takes). PrefsChoiceIndex(table, choiceName) and PrefsChoiceIndexOfValue(table, value) find a choice (-1 when there is none), and PrefsGetChoice(handle, section, key, table) reads one from a file, giving DVX's default choice when the key is missing or unknown. PrefsChoiceDefault(table) is that default choice, what a Defaults button selects. For a list of your own, PrefsGetChoiceFrom(handle, section, key, choiceNames, defaultName) does the same with the names separated by "|" ("large|small|list" stores choice 1 as "small"), giving defaultName's choice for a missing or unknown key.
+
PrefsDvxIni$() is the path of DVX's own settings file, DVX.INI, which Control Panel changes; other libraries (system.bas, joystick.bas, net.bas, print.bas, and sound.bas) read and store their part of it through a prefs.bas handle. PrefsSetVideoMode handle, wide, high, bpp stores the screen mode DVX starts in, and PrefsVideoWidth(handle), PrefsVideoHeight(handle), and PrefsVideoBpp(handle) read it (DVX's own start-up mode, SYS_VIDEO_DEFAULT_* in system.bas, for a value that is not there). PrefsKeyName$(handle, section, index) is the name of key number index of a section (from 0, in the order of the file; "" past the last key), to go through every key of a section.
+
Some settings are stored by name, from a fixed list of choices: the mouse acceleration (PREFS_CHOICE_ACCEL), the wallpaper mode (PREFS_CHOICE_WALLPAPER), and the scroll wheel direction (PREFS_CHOICE_WHEEL). For each list, PrefsChoiceCount(table) says how many choices it has (numbered from 0), PrefsChoiceLabel$(table, i) is the text to show, PrefsChoiceName$(table, i) the name stored in the file, and PrefsChoiceValue(table, i) the value it stands for (what SysSetMouse takes). PrefsChoiceIndex(table, choiceName) and PrefsChoiceIndexOfValue(table, value) find a choice (-1 when there is none), and PrefsGetChoice(handle, section, key, table) reads one from a file, giving DVX's default choice when the key is missing or unknown. PrefsChoiceDefault(table) is that default choice, what a Defaults button selects. For a list of your own, PrefsGetChoiceFrom(handle, section, key, choiceNames, defaultName) does the same with the names separated by "|" ("large|small|list" stores choice 1 as "small"), giving defaultName's choice for a missing or unknown key.
For i = 0 To PrefsChoiceCount(PREFS_CHOICE_ACCEL) - 1
cboAccel.AddItem PrefsChoiceLabel$(PREFS_CHOICE_ACCEL, i)
Next i
cboAccel.ListIndex = PrefsChoiceIndexOfValue(PREFS_CHOICE_ACCEL, SysMouseAccel())
Help Files
With help.bas in the project, HelpCompile(source, output) compiles a help source file (.dhs) into a help file (.hlp), True when it worked; HelpCompileMessages$() gives the compiler's errors and warnings, one per line, each with the file and line ("error: NOTES.DHS:1: directive .h1 outside of a topic"). HelpView file opens a help file in the DVX Help Viewer (a file named without a folder is looked for beside the program; "" opens the DVX user guide). HelpViewTopic file, topicId opens it at a topic, the way F1 does. A Help Viewer that is already open shows the file in its window instead of a second copy starting.
-
With hlpfile.bas, a program reads help files itself -- everything a help viewer needs. HlpOpen(path) opens a help file and returns a handle (0 when it is not one); HlpClose closes it (a program's open files are closed when it ends). Topics are numbered from 0: HlpTopicCount, HlpTopicTitle$ and HlpTopicId$ (the name links and HelpTopic use), HlpFindTopic(handle, topicId) (-1 when there is none) and HlpStartTopic(handle, topicId), the topic to show first (topicId when the file has it, else the file's own start page). HlpTocCount, HlpTocTitle$, HlpTocDepth, HlpTocTopic and HlpTocExpanded give the table of contents in order -- a depth of 0 is the top level, a topic of -1 a heading that only groups others -- ready to fill a TreeView. HlpIndexCount, HlpIndexKeyword$ and HlpIndexTopic give the keyword index (sorted), for a list or DlgChoice. HlpSearch(handle, words) searches the titles, text and keywords as the Help Viewer does and returns how many topics have every word; HlpSearchTopic(handle, n) gives them.
-
To show a topic, put a HelpPane control on the form and call HelpPane1.ShowTopic handle, topic: headings, paragraphs, lists, notes, code, tables, pictures and links look as they do in the Help Viewer, and the text wraps to the control's width. A click on a link, or Enter on one reached with Tab, fires the HelpPane's LinkClick event with the ID of the topic the link names; the program decides what to do, usually show that topic and remember it for a Back button. A program that draws topics itself reads them with HlpLoadTopic(handle, topic) and HlpRecordType, HlpRecordFlags, HlpRecordText$ and HlpRecordLink$ (the HLP_REC_ constants), saves a picture for LOADPICTURE with HlpRecordImageSave, and wraps text with HlpWrap$.
+
With hlpfile.bas, a program reads help files itself -- everything a help viewer needs. HlpOpen(path) opens a help file and returns a handle (0 when it is not one); HlpClose closes it (a program's open files are closed when it ends). Topics are numbered from 0: HlpTopicCount, HlpTopicTitle$ and HlpTopicId$ (the name links and HelpTopic use), HlpFindTopic(handle, topicId) (-1 when there is none), and HlpStartTopic(handle, topicId), the topic to show first (topicId when the file has it, else the file's own start page). HlpTocCount, HlpTocTitle$, HlpTocDepth, HlpTocTopic, and HlpTocExpanded give the table of contents in order -- a depth of 0 is the top level, a topic of -1 a heading that only groups others -- ready to fill a TreeView. HlpIndexCount, HlpIndexKeyword$, and HlpIndexTopic give the keyword index (sorted), for a list or DlgChoice. HlpSearch(handle, words) searches the titles, text, and keywords as the Help Viewer does and returns how many topics have every word; HlpSearchTopic(handle, n) gives them.
+
To show a topic, put a HelpPane control on the form and call HelpPane1.ShowTopic handle, topic: headings, paragraphs, lists, notes, code, tables, pictures, and links look as they do in the Help Viewer, and the text wraps to the control's width. A click on a link, or Enter on one reached with Tab, fires the HelpPane's LinkClick event with the ID of the topic the link names; the program decides what to do, usually show that topic and remember it for a Back button. A program that draws topics itself reads them with HlpLoadTopic(handle, topic) and HlpRecordType, HlpRecordFlags, HlpRecordText$, and HlpRecordLink$ (the HLP_REC_ constants), saves a picture for LOADPICTURE with HlpRecordImageSave, and wraps text with HlpWrap$.
Dim hlp As Long
Dim history(63) As Long, histPos As Long
@@ -5499,11 +5622,11 @@ End Sub
lstFiles.MaxHeight = SizePercent(50)
Compiling and Debugging Programs
With compiler.bas in the project, a program compiles BASIC programs -- what a development tool written in BASIC needs. A source set holds the files of one program: CompSourceFromProject(src, dbpPath) reads a whole project (.dbp) from disk, or CompSourceAdd(src, path, text, kind) adds files one at a time, in the order a project compiles: modules, then class modules, then UserControls, then forms (COMP_KIND_AUTO tells them apart by the file name; a form's whole .frm text is added, and the code after its layout is compiled; a UserControl's whole .ctl text, its header and its code). CompCompile(src, optionExplicit, validate) gives a compiled module, or 0 with CompError$() saying what is wrong and CompErrorFile$() / CompErrorFileLine() where. With validate True, Label1.Captoin and other misspelled control members, and controls of an unknown type, are found while compiling instead of when the line runs (the set's own UserControls are known types).
-
Line numbers of compiled programs (errors, breakpoints, the line a program is paused on) count through all the files of the source set. CompMapFile$(src, line) and CompMapLine(src, line) give the file and the line in it; CompConcatLine(src, path, fileLine) goes the other way. CompSourceFiles$ lists the files in compile order. CompBuildApp(module, src, outPath, release) builds an .app the way the IDE's Make Executable does, with the project's name, icon and help file, its forms and its UserControls. For a control project (its Type setting is "Control") it builds a control file instead (outPath NAME.wgt), the way the IDE's Make Control does: the project's UserControls with their toolbox pictures and names, there for every program once the file is copied into DVX's WIDGETS folder (DVX loads it when it starts). CompProcList$(text) lists the SUB and FUNCTION blocks of source text with their first and last lines (also for code that does not compile yet), CompIsIdent checks a name and CompKeyword$ lists the language's keywords, CompKeywordClass the kind of each (COMP_KW_OTHER, COMP_KW_TYPE or COMP_KW_LITERAL) and CompKeywordReserved whether a program may also use it as a name of its own.
-
Project files: ProjOpen(dbpPath) opens a project file (.dbp) and ProjNew(name, folder) starts a new one; ProjSave writes it and ProjClose frees it. ProjFileCount, ProjFile$ (the name as the project lists it), ProjFilePath$ (the full path), ProjFileKind (COMP_KIND_MODULE, COMP_KIND_FORM, COMP_KIND_CLASS or COMP_KIND_CONTROL) and ProjFileFind list its files; ProjAddFile, ProjRemoveFile and ProjRenameFile (on disk and in the project together, saved at once) change the list. ProjGet$ and ProjSet read and set its settings by their names in the file: Name, Type ("Control" for a control project, empty for a program), Author, Publisher, Version, Copyright, Description, Icon, HelpFile, CommandArgs, StartupForm, OptionExplicit and MultiInstance (the last two "true" or "false"). This is the project model the DVX BASIC IDE uses.
-
A UserControl's header (the Begin UserControl block at the top of its .ctl file) is read and changed the way the IDE's Properties window does it, the rest of the text left as it is: ProjCtlKeys$() lists the keys in the window's order with the kind of value each takes (Name, Width, Height, CanGetFocus, BackColor, ToolboxIcon, Description, DefaultEvent; the kinds Name, Integer, Boolean, Color and Text), ProjCtlHeader$(text, key) reads one (its default when the header leaves it out), ProjCtlSetHeader$(text, key, value) gives the text with one set (an empty value removes it; Name renames the control) and ProjCtlNew$(name) the text of a new UserControl, as File > New UserControl makes it.
-
With debug.bas too, a program runs a compiled module as a child program inside itself: RunCreate(module, src, appDir, args) prepares it, RunStart(run, startupForm, breakOnStart) loads its forms, and RunPoll(run, ms), called from a Timer, runs it for at most ms milliseconds at a time and returns what it is doing: RUN_RUNNING, RUN_EVENTS (waiting for events on its forms), RUN_PAUSED, RUN_ENDED or RUN_ERROR (RunError$ and RunErrorLine tell why and where). RunOutput$ collects what it PRINTs; INPUT asks with an input box. RunStop ends it and RunFree forgets it.
-
DbgSetBreakpoints run, "12, 40" sets breakpoints (line numbers of the source set). While the program is paused, DbgLine and DbgProc$ say where, DbgLocals$ and DbgCallStack$ give the variables and the calls as rows for a ListView's SetRows, DbgEval$(run, expr) shows a variable or expression as the IDE's Watch window does, DbgExec$(run, line) runs an Immediate-window line (x = 5 changes a variable, anything else is printed) and DbgSetVar changes a variable; DbgStepInto, DbgStepOver, DbgStepOut, DbgRunToLine and DbgContinue go on, DbgPause stops it at the next line. The program's form captions show [break] while it is paused.
+
Line numbers of compiled programs (errors, breakpoints, the line a program is paused on) count through all the files of the source set. CompMapFile$(src, line) and CompMapLine(src, line) give the file and the line in it; CompConcatLine(src, path, fileLine) goes the other way. CompSourceFiles$ lists the files in compile order. CompBuildApp(module, src, outPath, release) builds an .app the way the IDE's Make Executable does, with the project's name, icon, and help file, its forms, and its UserControls. For a control project (its Type setting is "Control") it builds a control file instead (outPath NAME.wgt), the way the IDE's Make Control does: the project's UserControls with their toolbox pictures and names, there for every program once the file is copied into DVX's WIDGETS folder (DVX loads it when it starts). CompProcList$(text) lists the SUB and FUNCTION blocks of source text with their first and last lines (also for code that does not compile yet), CompIsIdent checks a name and CompKeyword$ lists the language's keywords, CompKeywordClass the kind of each (COMP_KW_OTHER, COMP_KW_TYPE, or COMP_KW_LITERAL), and CompKeywordReserved whether a program may also use it as a name of its own.
+
Project files: ProjOpen(dbpPath) opens a project file (.dbp) and ProjNew(name, folder) starts a new one; ProjSave writes it and ProjClose frees it. ProjFileCount, ProjFile$ (the name as the project lists it), ProjFilePath$ (the full path), ProjFileKind (COMP_KIND_MODULE, COMP_KIND_FORM, COMP_KIND_CLASS, or COMP_KIND_CONTROL), and ProjFileFind list its files; ProjAddFile, ProjRemoveFile, and ProjRenameFile (on disk and in the project together, saved at once) change the list. ProjGet$ and ProjSet read and set its settings by their names in the file: Name, Type ("Control" for a control project, empty for a program), Author, Publisher, Version, Copyright, Description, Icon, HelpFile, CommandArgs, StartupForm, OptionExplicit, and MultiInstance (the last two "true" or "false"). This is the project model the DVX BASIC IDE uses.
+
A UserControl's header (the Begin UserControl block at the top of its .ctl file) is read and changed the way the IDE's Properties window does it, the rest of the text left as it is: ProjCtlKeys$() lists the keys in the window's order with the kind of value each takes (Name, Width, Height, CanGetFocus, BackColor, ToolboxIcon, Description, DefaultEvent; the kinds Name, Integer, Boolean, Color, and Text), ProjCtlHeader$(text, key) reads one (its default when the header leaves it out), ProjCtlSetHeader$(text, key, value) gives the text with one set (an empty value removes it; Name renames the control), and ProjCtlNew$(name) the text of a new UserControl, as File > New UserControl makes it.
+
With debug.bas too, a program runs a compiled module as a child program inside itself: RunCreate(module, src, appDir, args) prepares it, RunStart(run, startupForm, breakOnStart) loads its forms, and RunPoll(run, ms), called from a Timer, runs it for at most ms milliseconds at a time and returns what it is doing: RUN_RUNNING, RUN_EVENTS (waiting for events on its forms), RUN_PAUSED, RUN_ENDED, or RUN_ERROR (RunError$ and RunErrorLine tell why and where). RunOutput$ collects what it PRINTs; INPUT asks with an input box. RunStop ends it and RunFree forgets it.
+
DbgSetBreakpoints run, "12, 40" sets breakpoints (line numbers of the source set). While the program is paused, DbgLine and DbgProc$ say where, DbgLocals$ and DbgCallStack$ give the variables and the calls as rows for a ListView's SetRows, DbgEval$(run, expr) shows a variable or expression as the IDE's Watch window does, DbgExec$(run, line) runs an Immediate-window line (x = 5 changes a variable, anything else is printed), and DbgSetVar changes a variable; DbgStepInto, DbgStepOver, DbgStepOut, DbgRunToLine, and DbgContinue go on, DbgPause stops it at the next line. The program's form captions show [break] while it is paused.
A program paused in its module-level code waits until you step or continue it. One paused inside one of its event handlers -- or in its forms' Load code, which runs inside RunStart -- waits right there while your Timer and menus keep working; RunPoll called meanwhile returns at once, and RunStart returns only after such a pause.
' compiler.bas and debug.bas in the project; tmrRun is a Timer
Dim Shared src As Long, mdl As Long, kid As Long
@@ -5529,9 +5652,9 @@ Sub tmrRun_Timer
End Select
End Sub
Form Designers and Code Editors
-
A form designer or a code editor written in BASIC uses the same pieces as the DVX BASIC IDE. designer.bas describes the control types, which CtlTypeName$(0 to CtlTypeCount - 1) gives in alphabetical order: the toolbox lists the types CtlTypeFlags marks CTL_FLAG_DESIGNABLE (with CtlTypeTooltip$ and the icon CtlTypeIconFile writes), new controls are named CtlTypeNamePrefix$ plus a number, and the properties window lists CtlPropName$ for 0 to CtlPropCount - 1 with CtlPropType (CTL_PROP_STRING ... CTL_PROP_ENUM, plus CTL_PROP_READONLY) and CtlPropEnum$ for an enumerated property's choices. "Form" and "Menu" stand for the form itself and a menu item. The event list is CtlEventList$, and CtlEventParams$ writes an event procedure's parameter list; CtlTypeEventParams$(typeName, eventName) does it for one type, whose events may have parameters of their own (a UserControl's events), and "UserControl" stands for the object in a UserControl's own code (UserControl_MouseDown has Button, Shift, X and Y). For a project's files, FrmFormName$ and FrmControlNames$ read a .frm file's form and control names (the Object list), FrmCode$ its code, and ClsClassName$ a class module's name.
+
A form designer or a code editor written in BASIC uses the same pieces as the DVX BASIC IDE. designer.bas describes the control types, which CtlTypeName$(0 to CtlTypeCount - 1) gives in alphabetical order: the toolbox lists the types CtlTypeFlags marks CTL_FLAG_DESIGNABLE (with CtlTypeTooltip$ and the icon CtlTypeIconFile writes), new controls are named CtlTypeNamePrefix$ plus a number, and the properties window lists CtlPropName$ for 0 to CtlPropCount - 1 with CtlPropType (CTL_PROP_STRING ... CTL_PROP_ENUM, plus CTL_PROP_READONLY) and CtlPropEnum$ for an enumerated property's choices. "Form" and "Menu" stand for the form itself and a menu item. The event list is CtlEventList$, and CtlEventParams$ writes an event procedure's parameter list; CtlTypeEventParams$(typeName, eventName) does it for one type, whose events may have parameters of their own (a UserControl's events), and "UserControl" stands for the object in a UserControl's own code (UserControl_MouseDown has Button, Shift, X, and Y). For a project's files, FrmFormName$ and FrmControlNames$ read a .frm file's form and control names (the Object list), FrmCode$ its code, and ClsClassName$ a class module's name.
The form being designed is a real form: CreateControl and RemoveControl add and remove controls, CallByName reads and sets a property by its name, Me.Controls lists the controls, Container (Set ctl.Container = Frame1) and MoveBefore move them, and LoadFormText shows a .frm file. Its DesignMode (see the Form control) stops the controls from working and reports the mouse and keys in DesignMouse and DesignKey, with the selection handles drawn for DesignSelection.
-
A project's own UserControls go on the toolbox too, as in the IDE. Compile the project -- its forms may be left out -- and give the module and the source set to CompDesignControls(module, src, appDir) from compiler.bas: the UserControls are then control types of this program alone, listed by CtlTypeName$ with their Description as the tooltip and their ToolboxIcon as the icon, with their properties, methods and events, and known to CreateControl and LoadFormText. On the forms they run their code -- not the project's module-level code -- with UserControl.UserMode False on a form in DesignMode; App.Path is appDir, or the project's folder for "". When a control's code calls UserControl.PropertyChanged, the form raises DesignPropertyChanged (CtrlName, PropertyName), so the properties window can read them again. Call CompDesignControls again after a UserControl changed (the ones before are let go first) and CompDesignRelease when the project closes; the end of the program lets them go too. The module stays in use while they are there, even after CompModuleFree, and must be one this program compiled.
+
A project's own UserControls go on the toolbox too, as in the IDE. Compile the project -- its forms may be left out -- and give the module and the source set to CompDesignControls(module, src, appDir) from compiler.bas: the UserControls are then control types of this program alone, listed by CtlTypeName$ with their Description as the tooltip and their ToolboxIcon as the icon, with their properties, methods, and events, and known to CreateControl and LoadFormText. On the forms they run their code -- not the project's module-level code -- with UserControl.UserMode False on a form in DesignMode; App.Path is appDir, or the project's folder for "". When a control's code calls UserControl.PropertyChanged, the form raises DesignPropertyChanged (CtrlName, PropertyName), so the properties window can read them again. Call CompDesignControls again after a UserControl changed (the ones before are let go first) and CompDesignRelease when the project closes; the end of the program lets them go too. The module stays in use while they are there, even after CompModuleFree, and must be one this program compiled.
' compiler.bas and designer.bas in the project; Canvas is the form
' being designed
Sub LoadProjectControls(dbpPath As String)
@@ -5553,10 +5676,10 @@ End Sub
Sub Canvas_DesignPropertyChanged(CtrlName As String, PropertyName As String)
If CtrlName = Canvas.DesignSelection Then RefreshPropertyList
End Sub
-
A .frm file can also be worked on as data, the way the DVX BASIC IDE does it, without showing it. FrmOpen(text$), FrmOpenFile(path$) or FrmNew(name$) gives a handle to a form model (0 when the text is not a form); FrmText$ writes it back as .frm text in the form the IDE saves and FrmSave writes it to a file; FrmFree frees it (a program's open handles are freed when it ends). FrmGet$ and FrmSet read and change the form's properties (Caption, Width, AutoSize, Layout, Icon, Tag ...; Name renames the form), FrmGetCode$ and FrmSetCode its code. The controls are numbered from 0 in file order, each one followed by the controls inside it: FrmCtlCount, FrmCtlFind, FrmCtlName$, FrmCtlType$, FrmCtlIndex (its control array index, -1 when it is not in an array) and FrmCtlParent (the container, -1 for the form) describe them; FrmCtlGet$ and FrmCtlSet read and set any property as text (Name renames the control and its whole control array, the controls inside it follow; Index makes it an array member; an OptionButton set to True clears the others in its container), FrmCtlRemoveProp takes a set one out, and FrmCtlPropCount / FrmCtlPropName$ list the ones that were set. FrmCtlAdd adds a control (its name "" gives the next free one, Command1, Label2 ...) as the last control of a container (-1: the form), FrmCtlRemove removes one with what is inside it, FrmCtlMove puts one before another of the same container and FrmCtlReparent into another container. FrmCtlText$ gives a control with what is inside it as .frm text, what a designer puts on the Clipboard to copy it, and FrmCtlPaste puts such text into a container: a name that is already taken is replaced by a free one. The menu items are numbered from 0 in menu order with a Level (0 for a menu on the menu bar or a popup menu, 1 for its items, 2 and more for submenus): FrmMenuCount, FrmMenuGet$ / FrmMenuSet (Name, Caption, Shortcut, ShortcutText, Checked, RadioCheck, Enabled, Visible, Level), FrmMenuAdd, FrmMenuRemove (with the items under it), FrmMenuMove (past the previous or next item of its level) and FrmMenuIndent (one level in or out, with the items under it). Only what was set is written: a property a control has by default is left out of the file.
+
A .frm file can also be worked on as data, the way the DVX BASIC IDE does it, without showing it. FrmOpen(text$), FrmOpenFile(path$), or FrmNew(name$) gives a handle to a form model (0 when the text is not a form); FrmText$ writes it back as .frm text in the form the IDE saves and FrmSave writes it to a file; FrmFree frees it (a program's open handles are freed when it ends). FrmGet$ and FrmSet read and change the form's properties (Caption, Width, AutoSize, Layout, Icon, Tag ...; Name renames the form), FrmGetCode$ and FrmSetCode its code. The controls are numbered from 0 in file order, each one followed by the controls inside it: FrmCtlCount, FrmCtlFind, FrmCtlName$, FrmCtlType$, FrmCtlIndex (its control array index, -1 when it is not in an array), and FrmCtlParent (the container, -1 for the form) describe them; FrmCtlGet$ and FrmCtlSet read and set any property as text (Name renames the control and its whole control array, the controls inside it follow; Index makes it an array member; an OptionButton set to True clears the others in its container), FrmCtlRemoveProp takes a set one out, and FrmCtlPropCount / FrmCtlPropName$ list the ones that were set. FrmCtlAdd adds a control (its name "" gives the next free one, Command1, Label2 ...) as the last control of a container (-1: the form), FrmCtlRemove removes one with what is inside it, FrmCtlMove puts one before another of the same container, and FrmCtlReparent into another container. FrmCtlText$ gives a control with what is inside it as .frm text, what a designer puts on the Clipboard to copy it, and FrmCtlPaste puts such text into a container: a name that is already taken is replaced by a free one. The menu items are numbered from 0 in menu order with a Level (0 for a menu on the menu bar or a popup menu, 1 for its items, 2 and more for submenus): FrmMenuCount, FrmMenuGet$ / FrmMenuSet (Name, Caption, Shortcut, ShortcutText, Checked, RadioCheck, Enabled, Visible, Level), FrmMenuAdd, FrmMenuRemove (with the items under it), FrmMenuMove (past the previous or next item of its level), and FrmMenuIndent (one level in or out, with the items under it). Only what was set is written: a property a control has by default is left out of the file.
FormText$(formName$) writes a loaded form as it is now as .frm text -- one shown with LoadFormText, made with CreateForm and CreateControl, or one of the program's own: its properties, its controls in their containers with each property that differs from a new control of that type, and its menus (a form loaded this way has no code). A designer that keeps its form live saves it with FormText$, and FrmOpen(FormText$(...)) reads it as a model.
-
compiler.bas reads source text as the compiler does, for a code editor: CompTokens$(source) lists its tokens (line, column, length, kind and text, the kind one of Keyword, Identifier, String, Number, Date, Symbol, Comment and Error); CompInCode(source, position) tells whether a character is code rather than inside a string or a comment; CompRenameInCode$(source, oldName, newName, codeOnly) renames a form, control or menu item where code refers to it (Command1.Caption, Command1_Click), leaving strings and comments alone with codeOnly; CompWithEvents$ lists the WithEvents variables a file declares and CompClassEvents$ the Events of a class module, for the Object and Event lists; CompHelpTopic$(word) and CompHelpCtrlTopic$(typeName) are the help topics F1 opens (HelpViewTopic on dvxbasic.hlp beside the IDE).
-
A code editor is a TextArea: SetSyntaxMode "bas" colours BASIC as the IDE does (SetSyntaxColor changes the colours), SetLineMarker marks breakpoints and the current line, GutterClick reports clicks on the line numbers, SelStart / SelLength / SelText and Cut, Copy, Paste, SelectAll and Undo give the Edit menu, FindNext / ReplaceSelection / ReplaceAll find and replace, and LineCount / GetLine / CursorLine / CursorCol read the text (see TextArea).
+
compiler.bas reads source text as the compiler does, for a code editor: CompTokens$(source) lists its tokens (line, column, length, kind, and text, the kind one of Keyword, Identifier, String, Number, Date, Symbol, Comment, and Error); CompInCode(source, position) tells whether a character is code rather than inside a string or a comment; CompRenameInCode$(source, oldName, newName, codeOnly) renames a form, control, or menu item where code refers to it (Command1.Caption, Command1_Click), leaving strings and comments alone with codeOnly; CompWithEvents$ lists the WithEvents variables a file declares and CompClassEvents$ the Events of a class module, for the Object and Event lists; CompHelpTopic$(word) and CompHelpCtrlTopic$(typeName) are the help topics F1 opens (HelpViewTopic on dvxbasic.hlp beside the IDE).
+
A code editor is a TextArea: SetSyntaxMode "bas" colours BASIC as the IDE does (SetSyntaxColor changes the colours), SetLineMarker marks breakpoints and the current line, GutterClick reports clicks on the line numbers, SelStart / SelLength / SelText and Cut, Copy, Paste, SelectAll, and Undo give the Edit menu, FindNext / ReplaceSelection / ReplaceAll find and replace, and LineCount / GetLine / CursorLine / CursorCol read the text (see TextArea).
' designer.bas in the project: put a copy of the first control of a
' .frm file into a Frame of the same form and save it
Sub CopyIntoFrame(path As String)
@@ -5604,16 +5727,24 @@ End Sub
Enabled Boolean R/W Whether the control accepts user input. Disabling a container (Frame, VBox, HBox...) also blocks mouse and keyboard input to every control inside it.
BackColor Long R/W Background color as a 24-bit RGB value packed in a Long (use the RGB function to construct).
ForeColor Long R/W Foreground (text) color as a 24-bit RGB value packed in a Long (use the RGB function to construct).
+ FontName String R/W The family of the font the control draws its text in, such as "DejaVu Sans" ("System" is DVX's own 8 x 16 font). A family DVX does not have gives the System font. See Fonts below.
+ FontSize Integer R/W The size of the font in pixels. A size the family does not come in becomes the nearest one it has.
+ FontBold Boolean R/W True for the bold style of the font.
+ FontItalic Boolean R/W True for the italic style of the font.
+ FontUnderline Boolean R/W True to underline the text.
+ FontStrikethru Boolean R/W True to draw a line through the text.
TabIndex Integer R Accepted for VB compatibility but ignored. DVX has no tab order.
ToolTipText String R/W Tooltip text shown after the mouse hovers over the control. Empty string or unset = no tooltip.
HelpTopic String R/W Help topic opened by F1 while the control has the focus. Empty = the form's HelpTopic. F1 uses the project's help file.
DragMode Integer R/W vbManual (0, the default): the program starts a drag with the Drag method. vbAutomatic (1): pressing the left mouse button on the control starts a drag. See Drag and Drop below.
- DragIcon Picture R/W Picture shown under the mouse while the control is dragged: a picture file name (BMP, PNG, JPEG or GIF) or a Picture object from LOADPICTURE (Set works too). Reading it gives a Picture object, which stands for the file name where a value is needed. Empty = a dotted outline the size of the control.
+ DragIcon Picture R/W Picture shown under the mouse while the control is dragged: a picture file name (BMP, PNG, JPEG, or GIF) or a Picture object from LOADPICTURE (Set works too). Reading it gives a Picture object, which stands for the file name where a value is needed. Empty = a dotted outline the size of the control.
Parent Form R The form the control is on (Text1.Parent.Caption).
Tag String R/W Any text the program wants to keep with the control (DVX does nothing with it), as in Visual Basic. Also in the .frm file.
- MousePointer Integer R/W The mouse pointer shown over the control (and the controls inside it that have none of their own): vbDefault (0, the control's own pointer), vbArrow (1), vbCrosshair (2), vbSizeNESW (6), vbSizeNS (7), vbSizeNWSE (8), vbSizeWE (9), vbHourglass (11) or vbNoDrop (12). A form's MousePointer is used where no control has one, and Screen.MousePointer over every form of the program. Change it in MouseMove to show different pointers over parts of a control. Also in the .frm file; another value is an error.
+ MousePointer Integer R/W The mouse pointer shown over the control (and the controls inside it that have none of their own): vbDefault (0, the control's own pointer), vbArrow (1), vbCrosshair (2), vbSizeNESW (6), vbSizeNS (7), vbSizeNWSE (8), vbSizeWE (9), vbHourglass (11), or vbNoDrop (12). A form's MousePointer is used where no control has one, and Screen.MousePointer over every form of the program. Change it in MouseMove to show different pointers over parts of a control. Also in the .frm file; another value is an error.
Container Object R/W The container control the control is in (a Frame, VBox, TabPage ...), or its form. Set Text1.Container = Frame1 moves the control into Frame1, after the controls there; Set Text1.Container = Me moves it onto the form itself. The new container must be a container control on the same form, not the control itself or one inside it.
-
Size properties (Width, Height and the Min/Max sizes) are limited to 0..32767.
+
Size properties (Width, Height, and the Min/Max sizes) are limited to 0..32767.
+
Fonts
+
The six Font properties are one font. A control that has not been given one draws in the font of the control it is in (a Frame, for example), or else in its form's; the properties read the font the control draws with. Setting one of them gives the control a font of its own with that one part changed, keeping the others: Label1.FontBold = True makes the label's text bold in whatever family and size it had. Controls that edit text in columns (TextBox, TextArea, ComboBox, SpinButton, and Terminal) take only fixed-pitch families, such as "DejaVu Sans Mono", and show the System font in place of any other. The fonts DVX has, and the sizes each comes in, are listed by font.bas; DlgFont in commdlg.bas lets the user choose one. In the form designer the Font row of the Properties window sets all six, and they are saved in the .frm file.
Default Properties
As in Visual Basic, a control's name used where a value is needed means its default property: Text1 = "hi" sets Text1.Text, Print Label1 prints Label1.Caption, and If Check1 Then reads Check1.Value. Where an object is expected -- SET, Is, an object parameter, Collection.Add, TypeOf -- the name still means the control itself.
Control Default property
@@ -5624,7 +5755,7 @@ End Sub
Other controls, forms and menu items have no default property: using one as a value raises error 13.
+
Other controls, forms, and menu items have no default property: using one as a value raises error 13.
Common Events
These events are wired on every control, whether it is loaded from a .frm file or created at runtime with CreateControl (whose handlers SetEvent names).
Event Parameters Description
@@ -5635,7 +5766,7 @@ End Sub
GotFocus (none) Fires when the control receives keyboard focus.
LostFocus (none) Fires when the control loses keyboard focus.
KeyPress KeyAscii As Integer Fires when a printable key is pressed. KeyAscii is the ASCII code.
- KeyDown KeyCode As Integer, Shift As Integer Fires when any key is pressed down, after the control has handled it. KeyCode is the key (vbKeyDelete, vbKeyReturn, ... or a character's code; Ctrl+A to Ctrl+Z are 1 to 26); Shift holds vbShiftMask, vbCtrlMask and vbAltMask bits (see Key Code Constants in the language reference).
+ KeyDown KeyCode As Integer, Shift As Integer Fires when any key is pressed down, after the control has handled it. KeyCode is the key (vbKeyDelete, vbKeyReturn, ... or a character's code; Ctrl+A to Ctrl+Z are 1 to 26); Shift holds vbShiftMask, vbCtrlMask, and vbAltMask bits (see Key Code Constants in the language reference).
KeyUp KeyCode As Integer, Shift As Integer Fires when a key is released.
MouseDown Button As Integer, X As Integer, Y As Integer Fires when a mouse button is pressed over the control.
MouseUp Button As Integer, X As Integer, Y As Integer Fires when a mouse button is released over the control.
@@ -5654,7 +5785,7 @@ End Sub
Drag and Drop
Any control can be dragged onto another control or onto a form, as in Visual Basic. While a control is dragged, a dotted outline of it (or its DragIcon picture) follows the mouse, and the controls and forms it passes over get DragOver events. Releasing the mouse button drops it: the control or form under the mouse gets a DragDrop event, with the dragged control as Source. Pressing Esc cancels the drag.
The mouse pointer shows where a drop can land: over a control or form that has a DragDrop event procedure it is the usual arrow; over one without, and outside the program's forms, it is the "no drop" sign (a circle with a line through it). The outline or DragIcon picture still follows the mouse either way.
-
With DragMode set to vbAutomatic, pressing the left mouse button on the control starts the drag, and the control gets no Click, MouseDown or MouseUp from the left button. With DragMode vbManual, start the drag yourself, usually in MouseDown. A drag started while the mouse button is up ends at the next click. During a drag no mouse events other than DragOver and DragDrop are raised.
+
With DragMode set to vbAutomatic, pressing the left mouse button on the control starts the drag, and the control gets no Click, MouseDown, or MouseUp from the left button. With DragMode vbManual, start the drag yourself, usually in MouseDown. A drag started while the mouse button is up ends at the next click. During a drag no mouse events other than DragOver and DragDrop are raised.
Drop targets only react: nothing is moved or copied for you. Use Source in DragDrop to decide what to do, for example read its Name or Caption, or check what kind of control it is with TypeOf Source Is TextBox (see Operators).
Note that vbCancel is the MsgBox Cancel value (2), which as a Drag action means vbEndDrag; cancel a drag with Drag 0.
DragIcon takes a file name or a picture loaded with LOADPICTURE: Set Label1.DragIcon = LoadPicture("drag.bmp"). A missing file or one that is not a picture raises its error at LOADPICTURE; a file name that cannot be read when the drag starts shows the outline instead.
@@ -5678,36 +5809,36 @@ End Sub
Drawing on Forms and PictureBoxes
Forms and PictureBoxes have Visual Basic's drawing methods. They draw with the same code as the QBasic graphics statements (see Console and Graphics in the language reference), in pixels unless ScaleMode or Scale says otherwise, with colours as Visual Basic RGB values, &HBBGGRR (RGB(255, 0, 0) or &HFF is red). A PictureBox draws on its picture; a form gets a drawing area the size of its window the first time it draws, placed with its controls like a PictureBox (so draw on a form that has no controls, or use a PictureBox beside them).
-
Drawings stay until they are drawn over or cleared. In a form's own code, write Me.Line, Me.Circle and so on: Line, Circle and PSet without an object are the QBasic graphics statements (they draw on the program's screen), and Print without an object is the PRINT statement.
+
Drawings stay until they are drawn over or cleared. In a form's own code, write Me.Line, Me.Circle, and so on: Line, Circle, and PSet without an object are the QBasic graphics statements (they draw on the program's screen), and Print without an object is the PRINT statement.
Drawing Methods
Method Description
------ -----------
Cls Clears the drawing to BackColor; CurrentX and CurrentY go to 0.
PSet [Step](x, y) [, color] Draws a point (DrawWidth wide).
- Line [[Step](x1, y1)]-[Step](x2, y2) [, color] [, B | BF] Draws a line, a box (B) or a filled box (BF). A box without F is filled with FillColor when FillStyle is 0.
- Circle [Step](x, y), radius [, color [, start [, end [, aspect]]]] Draws a circle, ellipse or arc, filled with FillColor when FillStyle is 0 (full circles).
+ Line [[Step](x1, y1)]-[Step](x2, y2) [, color] [, B | BF] Draws a line, a box (B), or a filled box (BF). A box without F is filled with FillColor when FillStyle is 0.
+ Circle [Step](x, y), radius [, color [, start [, end [, aspect]]]] Draws a circle, ellipse, or arc, filled with FillColor when FillStyle is 0 (full circles).
DrawFocusRect x, y, width, height [, color] Draws the dotted rectangle DVX shows around the control that has the keyboard focus (every other pixel of its edge), for things a program draws itself, such as the focused link of a help topic. A negative width or height counts from the other edge.
- Point(x, y) The colour of a pixel, -1 outside the picture. With fewer than 16 million colours on the screen it is the colour the screen shows: black, white and full colours such as RGB(255, 0, 0) read back exactly, others may differ slightly.
- Print items Writes text at CurrentX, CurrentY in ForeColor, FontScale times as large, with PRINT's ; , SPC and TAB; ends by moving to the next line unless the last item is followed by ; or ,.
- PaintPicture picture, x, y [, width, height [, srcX, srcY, srcWidth, srcHeight [, opcode]]] Copies a picture, stretched to width x height: an image file (BMP, PNG, JPEG or GIF), a picture from LOADPICTURE, or a PictureBox given by its name as a string or by a Control variable. opcode combines each pixel with the one already there (see Raster Operations).
+ Point(x, y) The colour of a pixel, -1 outside the picture. With fewer than 16 million colours on the screen it is the colour the screen shows: black, white, and full colours such as RGB(255, 0, 0) read back exactly, others may differ slightly.
+ Print items Writes text at CurrentX, CurrentY in ForeColor, in the object's font FontScale times as large, with PRINT's separators (; and ,), SPC, and TAB (counted in the font's character width); ends by moving to the next line unless the last item is followed by ; or ,.
+ PaintPicture picture, x, y [, width, height [, srcX, srcY, srcWidth, srcHeight [, opcode]]] Copies a picture, stretched to width x height: an image file (BMP, PNG, JPEG, or GIF), a picture from LOADPICTURE, or a PictureBox given by its name as a string or by a Control variable. opcode combines each pixel with the one already there (see Raster Operations).
Scale [(x1, y1)-(x2, y2)] Gives the drawing coordinates of its own: (x1, y1) is the top-left corner and (x2, y2) the bottom-right one (y2 below y1 makes y go up). Scale alone goes back to pixels.
ScaleX(width [, fromScale [, toScale]]) A width in fromScale converted to toScale (see Scales).
ScaleY(height [, fromScale [, toScale]]) A height in fromScale converted to toScale.
- TextWidth(text) Width of the text in the scale's units, at the FontScale size.
- TextHeight(text) Height of a line of text in the scale's units, at the FontScale size.
+ TextWidth(text) Width of the text in the object's font, in the scale's units, at the FontScale size.
+ TextHeight(text) Height of a line of text in the object's font, in the scale's units, at the FontScale size.
Drawing the DVX Look
These methods draw the parts DVX's own controls are made of, in the colours of the current theme, for controls a program draws itself (a PictureBox that acts as a list or a toolbar). Coordinates and lengths are in the scale's units; colours are RGB values.
Method Description
------ -----------
DrawBevel x, y, width, height [, sunken [, face [, border]]] A raised box like a window or button edge, or with sunken True a sunken one like a text box's. face is the fill colour (the theme's face colour when left out; -1 leaves the inside as it is, to frame what is already drawn); border is the edge width in pixels (2).
DrawButtonFace x, y, width, height [, pressed [, face]] A button face, up or with pressed True pushed down.
- DrawArrow x, y, size, direction [, color] A solid triangle centred on x, y, size pixel rows tall, pointing dvxArrowUp (0), dvxArrowDown (1), dvxArrowLeft (2) or dvxArrowRight (3), in ForeColor unless a colour is given.
+ DrawArrow x, y, size, direction [, color] A solid triangle centred on x, y, size pixel rows tall, pointing dvxArrowUp (0), dvxArrowDown (1), dvxArrowLeft (2), or dvxArrowRight (3), in ForeColor unless a colour is given.
DrawScrollBar x, y, length, position, visible, total [, horizontal [, control]] A scroll bar with its arrows and its thumb, for a list of total rows of which visible are shown from row position, with its top-left corner at x, y. It stands upright (16 pixels wide, length tall, arrows at the top and bottom) unless horizontal is True: then it lies across (length wide, 16 pixels tall, arrows at the left and right ends). With control True it is the narrower bar of a control such as ListBox (14 pixels).
- DrawScrollArrow x, y, size, direction One arrow button of a scroll bar, as at the ends of DrawScrollBar's bars: a square button size wide and tall (size in the scale's horizontal units) with its top-left corner at x, y and an arrow pointing dvxArrowUp, dvxArrowDown, dvxArrowLeft or dvxArrowRight, in the theme's scroll bar colours.
- ScrollBarClick(length, offset, position, visible, total [, horizontal [, control]]) The new position after a click on a scroll bar DrawScrollBar drew with the same length, rows, direction and kind: offset is how far the click is from the top of the bar, or from its left end when horizontal is True. The arrows move one row, the trough a page, the thumb leaves it as it is. Needs no drawing.
- ScrollBarsNeeded(contentWidth, contentHeight, areaWidth, areaHeight) Which scroll bars content that big needs in an area that big, as DVX's controls decide it: vbSBNone (0), vbHorizontal (1), vbVertical (2) or vbBoth (3). A vertical bar narrows the area and may make a horizontal one needed, and the other way round. The sizes are in the object's scale. Needs no drawing.
- PrintAccel text [, enabled] Prints a caption at CurrentX, CurrentY with the letter after & underlined (&& prints one &); enabled False gives the greyed, embossed look of a disabled control. CurrentX moves past the text.
- TextWidthAccel(text) Width of a caption without its & signs, in the scale's units.
+ DrawScrollArrow x, y, size, direction One arrow button of a scroll bar, as at the ends of DrawScrollBar's bars: a square button size wide and tall (size in the scale's horizontal units) with its top-left corner at x, y and an arrow pointing dvxArrowUp, dvxArrowDown, dvxArrowLeft, or dvxArrowRight, in the theme's scroll bar colours.
+ ScrollBarClick(length, offset, position, visible, total [, horizontal [, control]]) The new position after a click on a scroll bar DrawScrollBar drew with the same length, rows, direction, and kind: offset is how far the click is from the top of the bar, or from its left end when horizontal is True. The arrows move one row, the trough a page, the thumb leaves it as it is. Needs no drawing.
+ ScrollBarsNeeded(contentWidth, contentHeight, areaWidth, areaHeight) Which scroll bars content that big needs in an area that big, as DVX's controls decide it: vbSBNone (0), vbHorizontal (1), vbVertical (2), or vbBoth (3). A vertical bar narrows the area and may make a horizontal one needed, and the other way round. The sizes are in the object's scale. Needs no drawing.
+ PrintAccel text [, enabled] Prints a caption in the object's font at CurrentX, CurrentY with the letter after & underlined (&& prints one &); enabled False gives the greyed, embossed look of a disabled control. CurrentX moves past the text.
+ TextWidthAccel(text) Width of a caption without its & signs in the object's font, in the scale's units.
AccelKey(text) The access key of a caption in lower case ("f" for "&File"), "" when it has none.
Sub Picture1_Paint()
Picture1.DrawBevel 0, 0, Picture1.ScaleWidth, Picture1.ScaleHeight, True, vbWhite
@@ -5725,7 +5856,7 @@ Sub Picture1_MouseDown(Button As Integer, X As Integer, Y As Integer)
Picture1.Refresh
End If
End Sub
-
Without a colour, drawing uses ForeColor. Step makes a point relative to CurrentX, CurrentY, which every drawing method leaves at its last point. Coordinates, radii, CurrentX and CurrentY and PaintPicture's rectangles are in the scale's units (a radius in its horizontal units); DrawWidth is always in pixels. A colour or angle out of range stops the program with an error.
+
Without a colour, drawing uses ForeColor. Step makes a point relative to CurrentX, CurrentY, which every drawing method leaves at its last point. Coordinates, radii, CurrentX and CurrentY, and PaintPicture's rectangles are in the scale's units (a radius in its horizontal units); DrawWidth is always in pixels. A colour or angle out of range stops the program with an error.
Raster Operations
PaintPicture's opcode says how each copied pixel is combined with the pixel already there (the destination) and with the FillColor (the pattern), as in Visual Basic. Leaving it out copies the picture (vbSrcCopy).
Constant Value Result
@@ -5750,8 +5881,8 @@ End Sub
Picture1.PaintPicture Picture2, 0, 0, 32, 32, 0, 0, 32, 32, vbSrcInvert ' XOR the picture in
Picture1.PaintPicture Picture2, 0, 0, 32, 32, 0, 0, 32, 32, vbSrcInvert ' and out again
Scales
-
ScaleMode picks the units: 0 user (set by Scale or the Scale properties), 1 twips (1440 an inch, 15 a pixel), 2 points (72 an inch), 3 pixels, 4 characters (120 twips across, 240 down), 5 inches, 6 millimetres, 7 centimetres. Unlike Visual Basic, whose default is twips, DVX starts every form and PictureBox in pixels (ScaleMode 3), and Scale without arguments goes back to pixels. Setting ScaleLeft, ScaleTop, ScaleWidth or ScaleHeight makes a user scale from the current one; ScaleWidth and ScaleHeight may not be 0. With a user scale the units stay fixed to the picture's edges when it is resized; other scale modes keep their unit size.
-
The constants vbUser (0), vbTwips (1), vbPoints (2), vbPixels (3), vbCharacters (4), vbInches (5), vbMillimeters (6) and vbCentimeters (7) name the scale modes. The units assume a standard display of 96 pixels an inch (15 twips a pixel), whatever the screen really is, so an inch is always 96 pixels.
+
ScaleMode picks the units: 0 user (set by Scale or the Scale properties), 1 twips (1440 an inch, 15 a pixel), 2 points (72 an inch), 3 pixels, 4 characters (120 twips across, 240 down), 5 inches, 6 millimetres, 7 centimetres. Unlike Visual Basic, whose default is twips, DVX starts every form and PictureBox in pixels (ScaleMode 3), and Scale without arguments goes back to pixels. Setting ScaleLeft, ScaleTop, ScaleWidth, or ScaleHeight makes a user scale from the current one; ScaleWidth and ScaleHeight may not be 0. With a user scale the units stay fixed to the picture's edges when it is resized; other scale modes keep their unit size.
+
The constants vbUser (0), vbTwips (1), vbPoints (2), vbPixels (3), vbCharacters (4), vbInches (5), vbMillimeters (6), and vbCentimeters (7) name the scale modes. The units assume a standard display of 96 pixels an inch (15 twips a pixel), whatever the screen really is, so an inch is always 96 pixels.
ScaleX and ScaleY convert a width or a height from one scale to another, as in Visual Basic: fromScale is the scale the value is in, vbHimetric (8, hundredths of a millimetre, the unit of a Picture's Width and Height) when it is left out; toScale the scale wanted, the object's ScaleMode when left out. vbUser means the object's current scale. Visual Basic's vbContainerPosition and vbContainerSize are not available; any other scale number stops the program with an error.
Picture1.Scale (0, 100)-(100, 0) ' 0..100 across, y going up
Picture1.Line (0, 0)-(100, 100), RGB(255, 0, 0)
@@ -5776,7 +5907,7 @@ End Sub
DrawWidth Integer 1 Pen width in pixels.
FillStyle Integer 1 How boxes (B) and circles are filled with FillColor: 0 solid, 1 (the default) not at all, 2 horizontal lines, 3 vertical lines, 4 upward diagonals, 5 downward diagonals, 6 a cross hatch, 7 a diagonal cross hatch (the lines 8 pixels apart, the gaps left as they are). Other values are an error. BF fills solid in the line's colour.
FillColor Long &H000000 Colour boxes and circles are filled with.
- FontScale Integer 1 How many times as large Print writes its text (1 to 8): each dot of DVX's 8 x 16 font becomes a FontScale x FontScale block, for titles and banners. TextWidth and TextHeight follow it; PrintAccel does not.
+ FontScale Integer 1 How many times as large Print writes its text (1 to 8): each dot of the font becomes a FontScale x FontScale block, for titles and banners. TextWidth and TextHeight follow it; PrintAccel does not.
AutoRedraw Boolean False True: no Paint events (see above).
ScaleMode Integer 3 Units of the coordinates (see Scales).
ScaleLeft Single 0 Coordinate of the left edge.
@@ -5903,11 +6034,11 @@ End
Caption String The text displayed. Use & for accelerator key. Set to "-" for a separator.
Checked Boolean Whether the menu item shows a checkmark. An item with a Checked line (True or False) is a check item: choosing it turns its checkmark on or off before its Click event runs, so the handler reads the new state.
RadioCheck Boolean Show the check as a radio dot instead of a checkmark. Radio items next to each other form a group: choosing one checks it and clears the others.
- Shortcut String A key that runs the item from anywhere in the form, shown at the right of the item: "F2", "Ctrl+X", "Alt+Enter", "Shift+Del" (Ctrl, Alt and Shift, then a letter, digit, F1 to F12, or Backspace, Del, Down, End, Enter, Esc, Home, Ins, Left, PgDn, PgUp, Right, Space, Tab, Up).
+ Shortcut String A key that runs the item from anywhere in the form, shown at the right of the item: "F2", "Ctrl+X", "Alt+Enter", "Shift+Del" (Ctrl, Alt, and Shift, then a letter, digit, F1 to F12, or Backspace, Del, Down, End, Enter, Esc, Home, Ins, Left, PgDn, PgUp, Right, Space, Tab, Up).
ShortcutText String Key text shown at the right of the item without making a shortcut, for keys a control handles itself (for example "Del" on a Delete item whose list deletes on Del in its KeyDown handler, so a text box keeps its own Del). Shown instead of the Shortcut's text when both are set.
Enabled Boolean Whether the menu item is enabled (default True).
Visible Boolean On a top-level menu, False makes it a popup menu (for ContextMenu) instead of a menu bar entry. On an item or separator inside a menu, False hides it until code sets Visible = True.
-
A Shortcut key works while the form's window has the focus, before the focused control sees the key, so give one only to keys no control there needs (F-keys, Ctrl or Alt combinations). F1 always opens the help and never reaches a shortcut. An unknown Shortcut stops the program with an error when the form loads.
+
A Shortcut key works while the form's window has the focus, before the focused control sees the key, so give one only to keys no control there needs (F-keys, Ctrl, or Alt combinations). F1 always opens the help and never reaches a shortcut. An unknown Shortcut stops the program with an error when the form loads.
Begin Menu mnuRename
Caption = "&Rename..."
Shortcut = "F2"
@@ -5923,7 +6054,7 @@ End
Caption String R/W The item's text, with & before its access key. Setting it keeps the item's key text (Shortcut or ShortcutText), and reading it gives the text without it.
Checked Boolean R/W Whether the menu item shows a checkmark. Setting it on an item that has neither Checked nor RadioCheck in the .frm makes it a check item (as in VB, any item can be checked).
Enabled Boolean R/W Whether the menu item is enabled.
- Visible Boolean R/W Whether the item (or separator) is in its menu. A hidden item keeps its caption, checkmark and place: shown again, it returns where it was. A submenu cannot be hidden (hide or remove its items, or set Enabled = False).
+ Visible Boolean R/W Whether the item (or separator) is in its menu. A hidden item keeps its caption, checkmark, and place: shown again, it returns where it was. A submenu cannot be hidden (hide or remove its items, or set Enabled = False).
Shortcut String R/W The key that runs the item ("Ctrl+K", written as in the .frm file, "" for none). Setting it changes the key and the key text shown in the item; "" removes both. An unknown key is an error.
RadioCheck and ShortcutText are design-time only: using them on a menu item in code is a compile error in the IDE and a runtime error (Unknown menu property) otherwise. The top-level menus of the menu bar (File, Edit) have no run-time properties.
Menus at Run Time
@@ -5934,7 +6065,7 @@ End
CreateMenu menu$ Makes an empty popup menu of that name (nothing happens if one exists).
AddMenu menu$, caption$ Adds a menu at the end of the form's menu bar, which appears with the first one (so a form made with CreateForm can have a menu bar). Add items to it by its name. The window keeps its size; the controls get the height the bar takes.
RemoveMenu menu$ Removes a menu of the menu bar with all its items; with the last one the bar goes too. Not for popup menus (DestroyMenu) or items (RemoveMenuItem).
- AddMenuItem menu$, item$, caption$ [, shortcut$] Adds an item at the end of the menu. Choosing it runs the Sub item_Click, and item.Caption, .Checked, .Enabled, .Visible and .Shortcut work as for .frm items. shortcut$ gives it a key, as Shortcut in the .frm file.
+ AddMenuItem menu$, item$, caption$ [, shortcut$] Adds an item at the end of the menu. Choosing it runs the Sub item_Click, and item.Caption, .Checked, .Enabled, .Visible, and .Shortcut work as for .frm items. shortcut$ gives it a key, as Shortcut in the .frm file.
AddMenuRadioItem menu$, item$, caption$ [, checked] Adds a radio item: radio items next to each other form a group, and choosing one checks it and clears the others.
AddMenuSeparator menu$ [, separator$] Adds a separator line at the end of the menu. With a name it can be hidden and removed like an item.
AddSubMenu menu$, submenu$, caption$ Adds a submenu at the end of the menu; add items to it by its name.
@@ -5965,7 +6096,7 @@ End Sub
A level-0 menu that contains children becomes a top-level menu header. A non-level-0 menu that contains children becomes a submenu.
Event Dispatch
-
Choosing a menu item (not a header, separator or submenu) fires MenuName_Click, whether it is on the menu bar or in a popup menu.
+
Choosing a menu item (not a header, separator, or submenu) fires MenuName_Click, whether it is on the menu bar or in a popup menu.
Sub mnuOpen_Click ()
MsgBox "Open was clicked"
End Sub
@@ -6107,12 +6238,13 @@ End Sub
HelpTopic String "" Help topic opened by F1 while the form is active and the focused control has no HelpTopic of its own. Empty opens the project's help file at its default page.
Padding Integer 4 Space in pixels between the window's edges and the controls (the content box's Padding); 0 is none.
Spacing Integer 4 Space in pixels between one control and the next (the content box's Spacing); 0 is none.
- WindowState Integer 0 vbNormal (0), vbMinimized (1, shown only as an icon at the bottom of the screen) or vbMaximized (2, filling the screen). Set on a hidden form (or in the .frm file), it takes effect when the form is shown; reading it then gives the state the form will have.
+ WindowState Integer 0 vbNormal (0), vbMinimized (1, shown only as an icon at the bottom of the screen), or vbMaximized (2, filling the screen). Set on a hidden form (or in the .frm file), it takes effect when the form is shown; reading it then gives the state the form will have.
Controls Collection (none) The form's controls (read-only), in the order they were made: Me.Controls.Count, Me.Controls(1) (the first), Me.Controls("Text1"), For Each c In Me.Controls. Elements of a control array are in it too but have no name key. Each use makes a new Collection; changing it does not change the form.
DesignMode Boolean False True puts the form in design mode, for a form designer: see Designing Forms below. Not in .frm files.
- Icon Picture (DVX's) The picture shown for the window while it is minimized: a picture file name (BMP, PNG, JPEG or GIF) or a picture from LOADPICTURE (Set works too); "" puts DVX's back. Reading it gives a Picture object. Also in the .frm file. A file that is not a picture is an error.
+ Icon Picture (DVX's) The picture shown for the window while it is minimized: a picture file name (BMP, PNG, JPEG, or GIF) or a picture from LOADPICTURE (Set works too); "" puts DVX's back. Reading it gives a Picture object. Also in the .frm file. A file that is not a picture is an error.
Tag String "" Any text the program wants to keep with the form, as for controls.
MousePointer Integer 0 The mouse pointer over the form where its controls have none (see MousePointer under Common Properties).
+ FontName String "System" The form's font: the font of every control on it that has none of its own, and of the form's Print and TextWidth. FontSize, FontBold, FontItalic, FontUnderline, and FontStrikethru go with it, as for controls (see Fonts under Common Properties). Also in the .frm file.
ActiveControl Control (none) The control of the form that has the keyboard focus (read-only), Nothing when none of them has it: Me.ActiveControl.SelText for an Edit menu's Copy. Screen.ActiveControl is the focused control of the whole program. Not in .frm files.
DesignSelection String "" The name of the control (Text1, or Text1(2) in a control array) whose selection handles are drawn while the form is in design mode; "" for none. Not in .frm files.
Maximizing a window, or restoring a maximized one, changes its size, so Resize fires. Minimizing does not change the size and fires no Resize. A minimized window keeps its maximized state: set WindowState = vbMaximized to bring it back filling the screen, vbNormal to bring it back at its normal size.
@@ -6124,20 +6256,20 @@ frmTerminal.WindowState = vbMinimized ' starts as an icon
Load (none) Fires after the form and all controls are created. This is the default event.
Unload (none) Fires when the form is being closed or unloaded.
QueryUnload Cancel As Integer Fires before Unload. Set Cancel = 1 to abort the close.
- Resize (none) Fires when the window changes size: resized by the user, maximized or restored (by the user or WindowState).
+ Resize (none) Fires when the window changes size: resized by the user, maximized, or restored (by the user or WindowState).
Activate (none) Fires when the window gains focus.
Deactivate (none) Fires when the window loses focus.
Paint (none) Fires when the form is shown or resized and on Refresh, unless AutoRedraw is True (see Drawing on Forms and PictureBoxes).
DragDrop Source As Control, X As Integer, Y As Integer Fires when a dragged control is dropped on an empty part of the form. X and Y are where, in the form.
- DragOver Source As Control, X As Integer, Y As Integer, State As Integer Fires while a dragged control moves over an empty part of the form. State is vbEnter, vbLeave or vbOver.
+ DragOver Source As Control, X As Integer, Y As Integer, State As Integer Fires while a dragged control moves over an empty part of the form. State is vbEnter, vbLeave, or vbOver.
NewArgs (none) Fires when the running program is handed new arguments: COMMAND$ has changed (see New Arguments below).
- DesignMouse Action As Integer, Button As Integer, X As Integer, Y As Integer, CtrlName As String, Handle As Integer In design mode: the mouse was pressed, moved, released or double-clicked in the form. See Designing Forms below.
+ DesignMouse Action As Integer, Button As Integer, X As Integer, Y As Integer, CtrlName As String, Handle As Integer In design mode: the mouse was pressed, moved, released, or double-clicked in the form. See Designing Forms below.
DesignKey KeyCode As Integer, Shift As Integer In design mode: a key was pressed in the form (KeyCode and Shift as in KeyDown).
DesignPropertyChanged CtrlName As String, PropertyName As String In design mode: the code of a UserControl on the form called PropertyChanged. See Designing Forms below.
See Drag and Drop under Common Properties, Events, and Methods. While a control is dragged over a form with no DragDrop event procedure, the mouse pointer shows the "no drop" sign.
New Arguments
A program with a NewArgs event procedure on any of its forms can be handed new arguments while it runs, instead of DVX starting another copy of it. COMMAND$ (and COMMAND$(1), COMMAND$(2), ...) then give the new arguments, and NewArgs fires on every loaded form that has the procedure. When the arguments arrive while one of the program's event procedures is still running -- for example while it shows a message box or a dialog, or a form with vbModal -- they are held, and NewArgs fires once that procedure has finished; if several arrive meanwhile, only the last is given.
-
DVX hands new arguments to the help viewer: F1 and the Help menus of programs (and HelpView in BASIC) send "helpfile topic" to the program installed as APPS\KPUNCH\DVXHELP\DVXHELP.APP when it is already running. A help viewer written in BASIC and installed there gets them in NewArgs. Starting a program that is already running -- from Program Manager, the Run dialog, a document opened in it or ShellRun -- gives it the new arguments when it allows only one copy and has a NewArgs procedure: its window comes to the front and NewArgs fires. Without a NewArgs procedure DVX says it is already running; a program that allows more than one copy (Project Properties) starts another copy instead.
+
DVX hands new arguments to the help viewer: F1 and the Help menus of programs (and HelpView in BASIC) send "helpfile topic" to the program installed as APPS\KPUNCH\DVXHELP\DVXHELP.APP when it is already running. A help viewer written in BASIC and installed there gets them in NewArgs. Starting a program that is already running -- from Program Manager, the Run dialog, a document opened in it, or ShellRun -- gives it the new arguments when it allows only one copy and has a NewArgs procedure: its window comes to the front and NewArgs fires. Without a NewArgs procedure DVX says it is already running; a program that allows more than one copy (Project Properties) starts another copy instead.
Sub Form1_NewArgs ()
' F1 in another program while this help viewer is open
If COMMAND$(1) <> openFile Then OpenHelpFile COMMAND$(1)
@@ -6155,7 +6287,7 @@ End Sub
FormName.FitHeight Make the window just tall enough for its controls, keeping its width.
FormName.SaveScreenshot file$ Save a picture of what the window shows inside its frame to a file: BMP when the name ends in .bmp, PNG otherwise. An error when the form has no window or the file cannot be written.
Several Copies of a Form
-
A form can be open more than once, the way a program opens a second document window. New FormName makes another copy of the form: its own window, controls, menus and form-level variables, built from the same .frm. Its Load event runs, and it starts hidden like any loaded form, so call Show. Keep the copy in a variable declared As the form's name (or As Form, As Object):
+
A form can be open more than once, the way a program opens a second document window. New FormName makes another copy of the form: its own window, controls, menus, and form-level variables, built from the same .frm. Its Load event runs, and it starts hidden like any loaded form, so call Show. Keep the copy in a variable declared As the form's name (or As Form, As Object):
Dim f As Form1 ' a Form1 copy, or the form itself
Set f = New Form1 ' a new copy
f.Caption = "Second window"
@@ -6163,16 +6295,16 @@ f.Text1.Text = "Hello" ' this copy's Text1
f.Show
Dim g As New Form1 ' declares and makes a copy at once
-
The code of the form serves every copy. In it, Me, the controls named on their own (Text1) and the form-level variables are those of the copy whose event is running, so each window keeps its own state. Unload f closes that copy only.
+
The code of the form serves every copy. In it, Me, the controls named on their own (Text1), and the form-level variables are those of the copy whose event is running, so each window keeps its own state. Unload f closes that copy only.
The form's name (Form1.Caption, Form1.Show, Load Form1) always means the first copy loaded -- the one the program starts with -- as in Visual Basic. If that copy was unloaded, the name means the next one still open.
Designing Forms
A form designer written in BASIC shows the form being designed as a real form and turns on its design mode. While DesignMode is True, the controls on the form take no mouse clicks or keys and fire no events (Timers too are quiet); the form instead tells the designer what the user does:
-
DesignMouse (Action, Button, X, Y, CtrlName, Handle): Action is DSGN_MOUSE_DOWN (1, a button was pressed), DSGN_MOUSE_MOVE (2), DSGN_MOUSE_UP (3) or DSGN_MOUSE_DBLCLICK (4, a double click: open the code, say). Button is the button pressed or released (1 left, 2 right, 4 middle; for a move, the buttons held). X and Y are where, in the form. CtrlName is the control there -- the innermost one when containers hold others -- or "" for the form itself. Handle is the selection handle of the selected control under the mouse: DSGN_HANDLE_E (3, the right edge: drag to change the width), DSGN_HANDLE_SE (4, the corner) or DSGN_HANDLE_S (5, the bottom edge), or DSGN_HANDLE_NONE (-1).
-
DesignKey (KeyCode, Shift): a key, for Delete, the arrow keys and so on.
+
DesignMouse (Action, Button, X, Y, CtrlName, Handle): Action is DSGN_MOUSE_DOWN (1, a button was pressed), DSGN_MOUSE_MOVE (2), DSGN_MOUSE_UP (3), or DSGN_MOUSE_DBLCLICK (4, a double click: open the code, say). Button is the button pressed or released (1 left, 2 right, 4 middle; for a move, the buttons held). X and Y are where, in the form. CtrlName is the control there -- the innermost one when containers hold others -- or "" for the form itself. Handle is the selection handle of the selected control under the mouse: DSGN_HANDLE_E (3, the right edge: drag to change the width), DSGN_HANDLE_SE (4, the corner), or DSGN_HANDLE_S (5, the bottom edge), or DSGN_HANDLE_NONE (-1).
+
DesignKey (KeyCode, Shift): a key, for Delete, the arrow keys, and so on.
DesignPropertyChanged (CtrlName, PropertyName): the code of a UserControl on the form -- one of the project's own, put on the designer's toolbox with CompDesignControls (compiler.bas) -- called UserControl.PropertyChanged PropertyName: read its properties again. Such controls run their code on the form with UserControl.UserMode False.
-
DesignSelection names the selected control; the form draws its eight selection handles over the controls (the three that resize in black) and shows the resize mouse pointers over them. The constants are in designer.bas, which also lists the control types, their properties and events (see Include Libraries in the language reference). Together with CreateControl, RemoveControl, CallByName, Controls, Container, MoveBefore, LoadFormText and the .frm model of designer.bas (FrmOpen, FrmText$, FrmCtlText$ / FrmCtlPaste to copy and paste controls, FormText$ to write the live form as .frm text), a designer has what the DVX BASIC IDE's own designer uses.
+
DesignSelection names the selected control; the form draws its eight selection handles over the controls (the three that resize in black) and shows the resize mouse pointers over them. The constants are in designer.bas, which also lists the control types, their properties, and events (see Include Libraries in the language reference). Together with CreateControl, RemoveControl, CallByName, Controls, Container, MoveBefore, LoadFormText, and the .frm model of designer.bas (FrmOpen, FrmText$, FrmCtlText$ / FrmCtlPaste to copy and paste controls, FormText$ to write the live form as .frm text), a designer has what the DVX BASIC IDE's own designer uses.
Sub Canvas_DesignMouse(Action As Integer, Button As Integer, X As Integer, Y As Integer, CtrlName As String, Handle As Integer)
If Action = DSGN_MOUSE_DOWN Then
If Handle <> DSGN_HANDLE_NONE Then
@@ -6186,7 +6318,7 @@ Dim g As New Form1 ' declares and makes a copy at once
End If
End Sub
Drawing
-
Forms have Visual Basic's drawing methods and properties (Cls, PSet, Line, Circle, Point, Print, PaintPicture, DrawFocusRect, DrawBevel, DrawButtonFace, DrawArrow, DrawScrollBar, DrawScrollArrow, ScrollBarClick, ScrollBarsNeeded, PrintAccel, Scale, CurrentX, CurrentY, ForeColor, BackColor, DrawWidth, FillColor, FillStyle, FontScale, AutoRedraw, ScaleMode, ScaleLeft, ScaleTop, ScaleWidth, ScaleHeight): see Drawing on Forms and PictureBoxes. In the form's own code use Me.Line, Me.Circle and so on.
+
Forms have Visual Basic's drawing methods and properties (Cls, PSet, Line, Circle, Point, Print, PaintPicture, DrawFocusRect, DrawBevel, DrawButtonFace, DrawArrow, DrawScrollBar, DrawScrollArrow, ScrollBarClick, ScrollBarsNeeded, PrintAccel, Scale, CurrentX, CurrentY, ForeColor, BackColor, DrawWidth, FillColor, FillStyle, FontScale, AutoRedraw, ScaleMode, ScaleLeft, ScaleTop, ScaleWidth, ScaleHeight): see Drawing on Forms and PictureBoxes. In the form's own code use Me.Line, Me.Circle, and so on.
Example
Sub Form1_Load ()
Form1.Caption = "Hello World"
@@ -6208,7 +6340,7 @@ End Sub
UserControls: Your Own Controls
-
A UserControl is a control you write in DVX BASIC. Once written, it is used exactly like the controls DVX comes with: you put it on a form in the designer (or in a .frm file with Begin), set its properties, call its methods and write handlers for its events. Programs written in C can create and use it too.
+
A UserControl is a control you write in DVX BASIC. Once written, it is used exactly like the controls DVX comes with: you put it on a form in the designer (or in a .frm file with Begin), set its properties, call its methods, and write handlers for its events. Programs written in C can create and use it too.
A UserControl lives in a .ctl file. In the IDE, File > New UserControl makes one (see UserControls in the IDE). The file starts with a header that describes the control, then holds the control's code. The code is a class: every control on a form is one object of the class, with its own copies of the class's variables.
VERSION DVX 1.00
Begin UserControl Gauge
@@ -6260,13 +6392,13 @@ End Sub
ToolboxIcon A 24 x 24 picture file in the project folder, shown in the designer's toolbox.
Description The toolbox tooltip (its name when not given).
DefaultEvent The event the designer opens when the control is double-clicked.
-
Properties, Methods and Events
+
Properties, Methods, and Events
The control's public members are what programs use:
-
Public Property Get / Let (or Set) pairs and Public variables are its properties. A property with only Property Get is read-only, with only Property Let write-only. A property whose type is Integer, Long or Byte is a whole number, Boolean is True / False, Single and Double (and Currency) are numbers, String, Date and Variant are text; a property of an object type, an array or a TYPE cannot be a property of a control.
-
A property declared As an ENUM is a choice of its members, as a built-in control's Alignment is: the designer's Properties window shows the member's name and a double-click steps to the next member, the form file stores the name (Look = Circle), and code sets it with a member, its number or its name as text (Shape1.Look = Circle, Shape1.Look = 1, Shape1.Look = "Circle"). This needs the members numbered 0, 1, 2 and so on, each number once, as an ENUM numbers them when no values are given; an ENUM with other values (Small = 10) makes a whole-number property.
+
Public Property Get / Let (or Set) pairs and Public variables are its properties. A property with only Property Get is read-only, with only Property Let write-only. A property whose type is Integer, Long, or Byte is a whole number, Boolean is True / False, Single and Double (and Currency) are numbers, String, Date, and Variant are text; a property of an object type, an array, or a TYPE cannot be a property of a control.
+
A property declared As an ENUM is a choice of its members, as a built-in control's Alignment is: the designer's Properties window shows the member's name and a double-click steps to the next member, the form file stores the name (Look = Circle), and code sets it with a member, its number, or its name as text (Shape1.Look = Circle, Shape1.Look = 1, Shape1.Look = "Circle"). This needs the members numbered 0, 1, 2, and so on, each number once, as an ENUM numbers them when no values are given; an ENUM with other values (Small = 10) makes a whole-number property.
Public Subs and Functions are its methods. They take any arguments; a Function's value is the method's result.
-
Public Events are its events. RaiseEvent sends one to the program that has the control: its handler CtrlName_EventName runs with the arguments, and a ByRef parameter the handler changes (a number, a Boolean or a String) comes back changed.
+
Public Events are its events. RaiseEvent sends one to the program that has the control: its handler CtrlName_EventName runs with the arguments, and a ByRef parameter the handler changes (a number, a Boolean, or a String) comes back changed.
Names that every control already has (Name, Left, Top, Width, Height, MinWidth, MinHeight, MaxWidth, MaxHeight, Weight, Visible, Enabled, ReadOnly, BackColor, ForeColor, Caption, Text, ToolTipText, ContextMenu, HelpTopic, DataSource, DataField, Tag, MousePointer ...) belong to the form: a control's own property of such a name is not used. Give your properties other names (Title instead of Caption, Value or Level instead of Text).
A property's default is the value it has after UserControl_Initialize ran. The form's .frm sets only the properties the designer user changed, through your Property Let, after Initialize.
@@ -6289,7 +6421,7 @@ End Sub
DrawBevel, DrawButtonFace, DrawArrow, DrawScrollBar, DrawScrollArrow, ScrollBarClick, ScrollBarsNeeded, PrintAccel, TextWidthAccel, AccelKey Drawing in DVX's own look (bevels, button faces, arrows, upright and horizontal scroll bars and their arrow buttons, captions with an underlined access key), as on a PictureBox.
CurrentX, CurrentY, ForeColor, BackColor, FillColor, FillStyle, FontScale, DrawWidth, AutoRedraw, ScaleMode, ScaleLeft, ScaleTop, ScaleWidth, ScaleHeight Drawing properties, as on a PictureBox.
The drawing members work exactly as on a PictureBox (see Drawing on Forms and PictureBoxes). Write them with UserControl in front -- UserControl.Line, UserControl.Print -- as a form's code writes Me.Line: a drawing statement on its own (Line, Cls, Print) draws on the program's screen, not on the control.
-
The drawing stays until you draw again. UserControl_Paint runs when the control first appears, after its size changed, when you call Refresh and after you call Invalidate; draw everything there and call UserControl.Invalidate (or UserControl.Refresh, to see the result at once) whenever what it shows changes.
+
The drawing stays until you draw again. UserControl_Paint runs when the control first appears, after its size changed, when you call Refresh, and after you call Invalidate; draw everything there and call UserControl.Invalidate (or UserControl.Refresh, to see the result at once) whenever what it shows changes.
The Control's Events
The program runs these procedures of the control's code when they exist. Each is a Private Sub with exactly these parameters:
Procedure When
@@ -6309,8 +6441,8 @@ End Sub
UserControl_KeyUp (KeyCode As Integer, Shift As Integer) A key went up.
UserControl_GotFocus () It got the keyboard focus.
UserControl_LostFocus () It lost the keyboard focus.
-
Button is vbLeftButton, vbRightButton or vbMiddleButton (in MouseMove, the buttons held). Shift holds vbShiftMask, vbCtrlMask and vbAltMask. X and Y are pixels from the control's top-left corner. The compiler reports a handler with other parameters.
-
These events are the control's own: the program does not see them. The program sees only the events the control raises. To let a program react to clicks, raise your own Click (Public Event Click, then RaiseEvent Click in UserControl_Click); an event named Click, DblClick, Change, KeyPress, KeyDown, KeyUp, MouseDown, MouseUp, MouseMove or Scroll reaches the program's CtrlName_Click (and so on) handler as for any control. Give those events Visual Basic's parameters: Button, Shift, X, Y for MouseDown, MouseUp and MouseMove; KeyCode, Shift for KeyDown and KeyUp; KeyAscii for KeyPress; Delta for Scroll. GotFocus and LostFocus reach the program by themselves.
+
Button is vbLeftButton, vbRightButton, or vbMiddleButton (in MouseMove, the buttons held). Shift holds vbShiftMask, vbCtrlMask, and vbAltMask. X and Y are pixels from the control's top-left corner. The compiler reports a handler with other parameters.
+
These events are the control's own: the program does not see them. The program sees only the events the control raises. To let a program react to clicks, raise your own Click (Public Event Click, then RaiseEvent Click in UserControl_Click); an event named Click, DblClick, Change, KeyPress, KeyDown, KeyUp, MouseDown, MouseUp, MouseMove, or Scroll reaches the program's CtrlName_Click (and so on) handler as for any control. Give those events Visual Basic's parameters: Button, Shift, X, Y for MouseDown, MouseUp, and MouseMove; KeyCode, Shift for KeyDown and KeyUp; KeyAscii for KeyPress; Delta for Scroll. GotFocus and LostFocus reach the program by themselves.
Using Controls in a Program
In the IDE, File > New UserControl (or File > Add File with a .ctl file) adds a control to the open project; while the project is open the control is in the Toolbox, ready to be placed on the project's forms. In the project file, the .ctl file is listed in the [Controls] section of the project's .dbp:
[Controls]
@@ -6336,7 +6468,7 @@ Count = 1
Limits
A control cannot hold other controls; it draws what it shows on its UserControl surface.
-
Properties are numbers, Booleans, text and ENUM choices; a property of an ENUM whose members are not numbered 0, 1, 2 ... is a whole number in the Properties window.
+
Properties are numbers, Booleans, text, and ENUM choices; a property of an ENUM whose members are not numbered 0, 1, 2 ... is a whole number in the Properties window.
CanGetFocus is fixed by the header (read-only in code).
UserControl_Terminate cannot draw: the surface is already gone.
A program cannot have a control of the same name as one installed in WIDGETS (the program stops with "Control cannot be registered").
@@ -6350,6 +6482,7 @@ Count = 1
Terminal
DVX Extension -- DVX Widget: ansiterm
A VT100/ANSI terminal emulator with a text-mode cell buffer, 16-color CGA palette, and scrollback history. Processes standard ANSI escape sequences (cursor control, erase, scroll, SGR colors, DEC private modes) typically used by DOS BBS software. Default size is 80 columns by 25 rows. Supports text selection with the mouse, Ctrl+C to copy, and Ctrl+V to send clipboard text as input. Shift+PgUp and Shift+PgDn page through the scrollback history and the mouse wheel scrolls it; any other key is sent to the host and returns the view to the live screen.
+
Its font must be fixed-pitch, every character the same width; given a proportional font it draws in the system font instead. A fixed-pitch font of any size changes the size of every cell.
Write text$ Write a string to the terminal with full ANSI escape processing.
No default event. Uses common events only.
Serial Communication
-
The conn.bas library connects a Terminal the way the Terminal program does -- a serial port, a modem, the secure link or Telnet -- with capture, answerback and file transfers (ConnAttach; see Include Libraries). For a packet link of your own, use the comm.bas library:
+
The conn.bas library connects a Terminal the way the Terminal program does -- a serial port, a modem, the secure link, or Telnet -- with capture, answerback, and file transfers (ConnAttach; see Include Libraries). For a packet link of your own, use the comm.bas library:
Dim link As Long
link = CommOpen(1, 115200, 8, "N", 1, SER_HANDSHAKE_NONE)
If link <> 0 Then
@@ -6400,7 +6533,7 @@ End Sub
Caption String The title displayed in the frame border.
Property Type Description
-------- ------- -------------------------------------------
- Alignment Enum Where the children sit along the frame from top to bottom when they do not fill it: Left (the top, the default), Center or Right (the bottom).
+ Alignment Enum Where the children sit along the frame from top to bottom when they do not fill it: Left (the top, the default), Center, or Right (the bottom).
Padding Integer Space in pixels between the border and the children: 4 unless set; 0 is none.
Spacing Integer Space in pixels between one child and the next: none unless set.
No type-specific methods or events.
@@ -6426,7 +6559,7 @@ End
Type-Specific Properties
Property Type Description
-------- ------- -------------------------------------------
- Alignment Enum Where the children sit along the row when they do not fill it: Left (the left, the default), Center or Right (the right).
+ Alignment Enum Where the children sit along the row when they do not fill it: Left (the left, the default), Center, or Right (the right).
Padding Integer Space in pixels between the edge of the box and the children: 4 unless set; 0 is none.
Spacing Integer Space in pixels between one child and the next: 4 unless set; 0 is none.
Alignment = Right puts a row of buttons at the right, as dialog boxes have them.
@@ -6453,7 +6586,7 @@ End
Type-Specific Properties
Property Type Description
-------- ------- -------------------------------------------
- Alignment Enum Where the children sit along the column when they do not fill it: Left (the top, the default), Center or Right (the bottom).
+ Alignment Enum Where the children sit along the column when they do not fill it: Left (the top, the default), Center, or Right (the bottom).
Padding Integer Space in pixels between the edge of the box and the children: 4 unless set; 0 is none.
Spacing Integer Space in pixels between one child and the next: 4 unless set; 0 is none.
A drawing surface (canvas). Supports drawing lines, rectangles, circles, text, and individual pixels. Can load BMP, PNG, JPEG and GIF images and save BMP or PNG images. The default canvas size is 64x64 pixels.
+
A drawing surface (canvas). Supports drawing lines, rectangles, circles, text, and individual pixels. Can load BMP, PNG, JPEG, and GIF images and save BMP or PNG images. The default canvas size is 64x64 pixels.
Colors are Visual Basic RGB values, &HBBGGRR, as RGB() makes them (RGB(255, 0, 0) or &HFF is red).
Type-Specific Properties
Property Type Description
@@ -6518,8 +6651,8 @@ End Sub
DrawText x%, y%, text$ Draw text at the given position.
FillCircle cx%, cy%, radius%, color% Draw a filled circle.
FillRect x%, y%, w%, h%, color% Draw a filled rectangle.
- GetPixel(x%, y%) Returns the color at a pixel. With fewer than 16 million colors on the screen it is the color the screen shows: black, white and full colors such as RGB(255, 0, 0) read back exactly, others may differ slightly.
- Load(path$) Load a BMP, PNG, JPEG or GIF image; the canvas takes the image's size. Returns 0 on success, -1 on failure.
+ GetPixel(x%, y%) Returns the color at a pixel. With fewer than 16 million colors on the screen it is the color the screen shows: black, white, and full colors such as RGB(255, 0, 0) read back exactly, others may differ slightly.
+ Load(path$) Load a BMP, PNG, JPEG, or GIF image; the canvas takes the image's size. Returns 0 on success, -1 on failure.
Resize w%, h% Resize the canvas to new dimensions. 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 failure.
SetPenColor color% Set the drawing color for subsequent operations.
@@ -6569,6 +6702,7 @@ End Sub
ComboBox
VB Equivalent: ComboBox -- DVX Widget: combobox (editable text field + drop-down list, default maxLen 256)
A combination of a single-line text input and a drop-down list. The user can either type a value or pick one from the list. Selecting a list item copies its text into the edit field. Supports the same AddItem / RemoveItem / Clear / List methods as ListBox.
+
Its font must be fixed-pitch, every character the same width; the designer's Font dialog offers only those families for it. Given a proportional font it draws in the system font instead.
Type-Specific Properties
Property Type Description
--------- ------- -------------------------------------------
@@ -6603,7 +6737,7 @@ End Sub
Data
VB Equivalent: Data -- DVX Widget: data | Name Prefix: Data
-
A data access control that connects to a SQLite database and provides record navigation. Other controls bind to a Data control via their DataSource and DataField properties. The Data control cache is populated by calling Refresh. Move operations and Refresh auto-save the current row if dirty; a move is refused while the save fails, and Refresh discards an edit that cannot be saved. Updates find the record by the key value it was loaded with, so the key column itself can be edited; a key stored as a number in a column without a declared type, or a missing (NULL) key, is still found as long as it identifies exactly one record. Writing a field back with the value it already has is not an edit and does not fire Validate. While a Validate handler runs, MoveFirst, MoveLast, MoveNext, MovePrevious, AddNew, Refresh and Update on the same Data control are ignored (they would save, and validate, the same record again); Delete is allowed. A RecordSource that is a SELECT query is read-only, and edits made to its records are dropped when the current record changes.
+
A data access control that connects to a SQLite database and provides record navigation. Other controls bind to a Data control via their DataSource and DataField properties. The Data control cache is populated by calling Refresh. Move operations and Refresh auto-save the current row if dirty; a move is refused while the save fails, and Refresh discards an edit that cannot be saved. Updates find the record by the key value it was loaded with, so the key column itself can be edited; a key stored as a number in a column without a declared type, or a missing (NULL) key, is still found as long as it identifies exactly one record. Writing a field back with the value it already has is not an edit and does not fire Validate. While a Validate handler runs, MoveFirst, MoveLast, MoveNext, MovePrevious, AddNew, Refresh, and Update on the same Data control are ignored (they would save, and validate, the same record again); Delete is allowed. A RecordSource that is a SELECT query is read-only, and edits made to its records are dropped when the current record changes.
A read-only drop-down list. Unlike ComboBox, the user cannot type free text; they can only select from the provided items. Supports AddItem / RemoveItem / Clear / List. Use the Up and Down keys to change the choice. F4, Alt+Down, Space or Enter opens the list; in the open list use the arrow keys to move and Enter (or F4, Alt+Down) to pick. Esc closes the list without changing the choice.
+
A read-only drop-down list. Unlike ComboBox, the user cannot type free text; they can only select from the provided items. Supports AddItem / RemoveItem / Clear / List. Use the Up and Down keys to change the choice. F4, Alt+Down, Space, or Enter opens the list; in the open list use the arrow keys to move and Enter (or F4, Alt+Down) to pick. Esc closes the list without changing the choice.
Type-Specific Properties
Property Type Description
--------- ------- -------------------------------------------
@@ -6742,7 +6876,7 @@ End Sub
HelpPane
VB Equivalent: (none) -- DVX Widget: helpview
-
Shows one topic of a help file the way the DVX Help Viewer does: headings, paragraphs and lists wrapped to the control's width, notes, code, tables, pictures, rules and links, with a scroll bar when the topic is longer than the control. Open the help file with hlpfile.bas (HlpOpen) and give its handle to ShowTopic. With a TreeView for the table of contents and a few buttons, a HelpPane makes a complete help viewer (see Help Files under Include Libraries).
+
Shows one topic of a help file the way the DVX Help Viewer does: headings, paragraphs, and lists wrapped to the control's width, notes, code, tables, pictures, rules, and links, with a scroll bar when the topic is longer than the control. Open the help file with hlpfile.bas (HlpOpen) and give its handle to ShowTopic. With a TreeView for the table of contents and a few buttons, a HelpPane makes a complete help viewer (see Help Files under Include Libraries).
A link is followed with a click, or with Enter or Space after Tab has moved to it: the HelpPane fires LinkClick with the ID of the topic the link names and remembers it in LinkTopic. It does not change topic by itself -- the LinkClick event procedure usually shows the topic and adds it to its Back list. Everything a topic shows is copied when it is shown, so closing the help file afterwards does not change what the HelpPane shows.
Type-Specific Properties
Property Type R/W Description
@@ -6785,7 +6919,7 @@ End Sub
Type-Specific Properties
Property Type Description
----------- ------- -------------------------------------------
- Picture Picture Path to an image file to load (or a Picture object from LOADPICTURE); a program, widget or library file gives its icon. Reads back as a Picture object for the file the image came from, which stands for the file name where a value is needed. Setting it to "" clears the image. A file that cannot be loaded leaves the image and Picture unchanged.
+ Picture Picture Path to an image file to load (or a Picture object from LOADPICTURE); a program, widget, or library file gives its icon. Reads back as a Picture object for the file the image came from, which stands for the file name where a value is needed. Setting it to "" clears the image. A file that cannot be loaded leaves the image and Picture unchanged.
ImageWidth Integer Width of the loaded image in pixels (read-only).
ImageHeight Integer Height of the loaded image in pixels (read-only).
No type-specific methods or events.
@@ -6812,7 +6946,7 @@ End Sub
Type-Specific Properties
Property Type Description
----------- ------- -------------------------------------------
- Picture Picture Path to an image file to load (or a Picture object from LOADPICTURE); a program, widget or library file gives its icon. Reads back as a Picture object for the file the image came from, which stands for the file name where a value is needed. Setting it to "" clears the image. A file that cannot be loaded leaves the image and Picture unchanged. A disabled Image is drawn grayed (its transparent color stays transparent).
+ Picture Picture Path to an image file to load (or a Picture object from LOADPICTURE); a program, widget, or library file gives its icon. Reads back as a Picture object for the file the image came from, which stands for the file name where a value is needed. Setting it to "" clears the image. A file that cannot be loaded leaves the image and Picture unchanged. A disabled Image is drawn grayed (its transparent color stays transparent).
Stretch Boolean When True, an image larger than the control is scaled down to fit, keeping its proportions; a smaller image stays at natural size. When False, the image is shown at natural size.
ImageWidth Integer Width of the loaded image in pixels (read-only).
ImageHeight Integer Height of the loaded image in pixels (read-only).
@@ -6913,7 +7047,7 @@ End Sub
SelectAll Select all rows.
SelectedRows$() Returns the numbers of the selected rows (as IsItemSelected numbers them) separated by spaces, "" when none: one call instead of IsItemSelected for every row of a long list.
SetCell row%, col%, text$ Set the text of a cell.
- SetColumns spec$ Define columns. Pipe-delimited "Name,Width,Align|Name,Width": the width in characters (missing or 0 = as wide as the contents), then Left (the default), Center or Right for the column's text.
+ SetColumns spec$ Define columns. Pipe-delimited "Name,Width,Align|Name,Width": the width in characters (missing or 0 = as wide as the contents), then Left (the default), Center, or Right for the column's text.
SetItemSelected index%, selected Select or deselect a row.
SetMultiSelect multi Enable or disable multi-select.
SetReorderable reorderable Enable or disable row reordering.
@@ -6965,10 +7099,118 @@ ListView2.SetRows "a.txt" & Chr$(9) & "120" & Ch
A text box for text in many fonts, sizes, and colors, bold, italic, underlined, or struck through, in paragraphs aligned left, centered, or right -- a small word processor's page. The user types, selects, and edits as in any text box; a program reads and changes the selection's look with the Sel properties, and the whole text, with its looks, as RTF (Rich Text Format, the format word processors save), so it can be saved to a file and loaded again.
+
Text starts in the control's own font (FontName, FontSize, and the other Font properties, or the form's) and in its ForeColor; text left in them follows the control when they change. The Sel properties read the first character of the selection; setting one changes the selection, or with nothing selected, the text typed next. SelStart counts characters from 0, and the break between two paragraphs is one character.
+
Type-Specific Properties
+
Property Type Description
+ -------- ---- -----------
+ TextRTF String The whole text with its fonts, styles, colors, and alignment, as RTF. Setting it replaces the text (plain text is taken as it is).
+ SelRTF String The selection as RTF. Setting it replaces the selection.
+ SelStart Integer Where the selection starts (the caret when nothing is selected).
+ SelLength Integer How many characters are selected.
+ SelText String The selection's text. Setting it replaces the selection with plain text.
+ SelBold Boolean Bold.
+ SelItalic Boolean Italic.
+ SelUnderline Boolean Underlined.
+ SelStrikethru Boolean Struck through.
+ SelColor Long The color, as RGB() makes it; -1 for the control's ForeColor.
+ SelFontName String The font family ("" for the control's own).
+ SelFontSize Integer The size in pixels (the nearest one the family comes in).
+ SelAlignment Integer The alignment of the selection's paragraphs: 0 left, 1 centered, 2 right.
+ CanUndo Boolean An edit can be undone (read-only).
+ CanRedo Boolean An undone edit can be done again (read-only).
+
Text (a common property) is the plain text, a line per paragraph; setting it replaces everything with plain text. ReadOnly lets the user select and copy but not change the text.
+
Type-Specific Methods
+
Method Description
+ ------ -----------
+ Undo Undo the last edit (typing a run of characters is one edit). Up to 100 edits are kept.
+ Redo Do an undone edit again.
+ Cut, Copy, Paste The clipboard. Text copied from a RichEdit pastes back into a RichEdit with its looks; text from elsewhere takes the look of the text typed next.
+ SelectAll Select all the text.
+ Find(text) Select the next place text is (any case), from the selection on and around past the end; returns where it starts, or -1.
+ LoadFile(path) Load a file: RTF when it is RTF, else plain text. Returns 0, or -1 when it cannot be read.
+ SaveFile(path) Save the text: as RTF when path ends in .rtf, else as plain text. Returns 0, or -1 when it cannot be written.
+
Type-Specific Events
+
Event Parameters Description
+ ----- ---------- -----------
+ SelChange (none) The selection or the caret moved.
+
Change fires after every edit. Default Event: Change
+
Keys
+
Key Action
+ --- ------
+ Ctrl+B, Ctrl+I, Ctrl+U Bold, italic, or underline on or off.
+ Ctrl+L, Ctrl+E, Ctrl+R Align the paragraph left, centered, or right.
+ Ctrl+Z, Ctrl+Y Undo, Redo.
+ Ctrl+X, Ctrl+C, Ctrl+V Cut, Copy, Paste.
+ Ctrl+A Select all.
+ Ctrl+Left, Ctrl+Right A word back or on.
+ Ctrl+Home, Ctrl+End The start or the end of the text.
+ Shift with a moving key Extend the selection.
+
A double click selects a word, a triple click the paragraph.
+
Example
+
Sub Form_Load ()
+ RichEdit1.Text = "Dear friend,"
+ RichEdit1.SelStart = 0
+ RichEdit1.SelLength = 4
+ RichEdit1.SelBold = True
+ RichEdit1.SelColor = RGB(0, 0, 160)
+End Sub
+
+Sub cmdItalic_Click ()
+ RichEdit1.SelItalic = Not RichEdit1.SelItalic
+End Sub
+
+Sub cmdSave_Click ()
+ If RichEdit1.SaveFile("LETTER.RTF") <> 0 Then MsgBox "The letter could not be saved."
+End Sub
Text that mixes fonts, sizes, bold and italic, underlining, and colors, in paragraphs that are each aligned left, centered, or right. Build the text with calls: SetFont, SetStyle, and SetColor choose how the text you add next looks; AddText adds it; AddLineBreak starts a new line; and AddParagraph starts a new paragraph. Clear empties the control so you can build it again.
+
Text starts in the control's own font (FontName, FontSize, and the other Font properties, or the form's). Set Width to have the text wrap at that width; otherwise each paragraph stays on one line and the control is as wide as its longest line.
+
Methods
+
Method Description
+ ------ -----------
+ Clear Remove all the text. The font, style, and color go back to the control's own.
+ SetFont family, size The font family (for example "DejaVu Serif") and size in pixels of the text added next. "" and 0 go back to the control's own font and size.
+ SetStyle style FONT_BOLD, FONT_ITALIC, FONT_UNDERLINE, and FONT_STRIKE added together (from font.bas; 0 for plain) for the text added next.
+ SetColor color The color of the text added next, as RGB() makes it; -1 for the control's ForeColor.
+ AddText text Add text in the font, style, and color chosen.
+ AddLineBreak Start a new line in the same paragraph.
+ AddParagraph alignment Start a new paragraph: 0 left, 1 centered, 2 right (as a Label's Alignment).
+
Changing the font, style, or color affects only the text added afterwards. A line is as tall as the largest font on it.
DVX Extension -- DVX Widget: scrollpane | Name Prefix: Scroll
-
A scrollable container. Place child controls inside and the ScrollPane automatically provides scrollbars when the content exceeds the visible area. Clicking on content that does not respond to the mouse (a Label, an Image or empty space) gives the ScrollPane the focus, so the arrow and page keys scroll it.
+
A scrollable container. Place child controls inside and the ScrollPane automatically provides scrollbars when the content exceeds the visible area. Clicking on content that does not respond to the mouse (a Label, an Image, or empty space) gives the ScrollPane the focus, so the arrow and page keys scroll it.
Event Description
----- -----------
- Change The user scrolled the content (the scrollbars, dragging a scroll box, the arrow, page, Home and End keys); read ScrollTop and ScrollLeft for the new position. Setting ScrollTop or ScrollLeft, or ScrollToTop, does not fire it.
+ Change The user scrolled the content (the scrollbars, dragging a scroll box, the arrow, page, Home, and End keys); read ScrollTop and ScrollLeft for the new position. Setting ScrollTop or ScrollLeft, or ScrollToTop, does not fire it.
Scrolling a Picture Bigger Than the Window
A ScrollPane with NoBorder = True filling a form, holding a PictureBox larger than the window, gives the form scrollbars of its own: the user scrolls the picture, Change says where it is, and ScrollTop / ScrollLeft move it from code. To draw only the part in view (for a picture too large to keep whole), keep the PictureBox the size of the view, and draw from Change with ScrollTop and ScrollLeft as the offset.
Sub Form1_Load ()
@@ -7039,6 +7281,7 @@ End Sub
SpinButton
DVX Extension -- DVX Widget: spinner | Name Prefix: Spin
A numeric input with up/down buttons. Supports integer mode (default) and real-number mode with configurable decimal places. Up/Down arrows step by the step size, Page Up/Page Down by ten steps. Typed or pasted text is committed on Enter or when the control loses focus; Esc cancels the edit. Ctrl+Z undoes the last edit of the typed text; once the text is committed or reformatted there is nothing to undo. Reading Value while the user is typing returns the typed number without interrupting the edit. Change also fires as each character is typed; stepping at a limit, or committing text that parses to the unchanged value, does not fire it.
+
Its font must be fixed-pitch, every character the same width; the designer's Font dialog offers only those families for it. Given a proportional font it draws in the system font instead.
Type-Specific Properties
Property Type Description
-------- ------- -------------------------------------------
@@ -7110,6 +7353,7 @@ End
A single-line text input field. Supports data binding via DataSource and DataField properties.
+
Its font must be fixed-pitch, every character the same width; the designer's Font dialog offers only those families for it. Given a proportional font it draws in the system font instead.
Type-Specific Properties
Property Type Description
------------ ------ -------------------------------------------
@@ -7121,7 +7365,7 @@ End
DataField String Column name for data binding.
SelStart Integer Where the selection starts, or the cursor when nothing is selected (0 = before the first character). Setting it moves the cursor there and selects nothing.
SelLength Integer The number of characters selected (0 = none). Setting it selects that many characters from SelStart.
- SelText String The selected text. Setting it replaces the selection (or inserts at the cursor), leaves the cursor after the new text and fires Change; in a masked box it fills the places from the selection's start, as pasting does.
+ SelText String The selected text. Setting it replaces the selection (or inserts at the cursor), leaves the cursor after the new text, and fires Change; in a masked box it fills the places from the selection's start, as pasting does.
Type-Specific Methods
Method Description
------ -----------
@@ -7130,7 +7374,7 @@ End
Paste Put the clipboard's text in place of the selection (Ctrl+V; line breaks are left out); fires Change.
SelectAll Select the whole text (Ctrl+A).
Undo Take back the last change (Ctrl+Z); a second Undo puts it back.
-
The methods do what the keys do, so Cut, Paste and Undo do nothing in a read-only box. An Edit menu works on whichever box has the focus with Me.ActiveControl (see Form).
+
The methods do what the keys do, so Cut, Paste, and Undo do nothing in a read-only box. An Edit menu works on whichever box has the focus with Me.ActiveControl (see Form).
Default Event: Change
Example
Begin TextBox Text1
@@ -7147,6 +7391,7 @@ End Sub
TextArea
VB Equivalent: TextArea (DVX extension) -- DVX Widget: textarea (multi-line text input, default max 65536 chars)
A multi-line text editing area. This is a DVX extension with no direct VB3 equivalent (VB uses a TextBox with MultiLine=True). Supports line numbers, auto-indent, find/replace, and tab configuration.
+
Its font must be fixed-pitch, every character the same width; the designer's Font dialog offers only those families for it. Given a proportional font it draws in the system font instead.
Type-Specific Properties
Property Type R/W Description
---------- ------- --- -------------------------------------------
@@ -7156,7 +7401,7 @@ End Sub
LineCount Integer R The number of lines (an empty text has one).
SelStart Integer R/W Where the selection starts, or the cursor when nothing is selected (0 = before the first character). Setting it moves the cursor there and selects nothing.
SelLength Integer R/W The number of characters selected (0 = none). Setting it selects that many characters from SelStart.
- SelText String R/W The selected text. Setting it replaces the selection (or inserts at the cursor when nothing is selected), leaves the cursor after the new text and fires Change; Undo takes it back.
+ SelText String R/W The selected text. Setting it replaces the selection (or inserts at the cursor when nothing is selected), leaves the cursor after the new text, and fires Change; Undo takes it back.
MaxLength Integer R/W The most characters the area holds (0 = the default, 65536; at most 262144). A smaller value cuts the text.
Type-Specific Methods
Method Description
@@ -7185,7 +7430,7 @@ End Sub
SetTabWidth width% Set the tab stop width in characters.
SetUseTabChar useChar When True, Tab inserts a tab character; when False, inserts spaces.
Undo Take back the last change (Ctrl+Z); a second Undo puts it back.
-
Cut, Paste and Undo do nothing in a read-only area.
+
Cut, Paste, and Undo do nothing in a read-only area.
DVX Help shows help pages. The list on the left is the table of contents, the page you are reading is on the right, and underlined words are links to other pages. You can also look things up in the Index or with Search.
-
Tip: New to DVX? Click the Program Manager window and press F1, or choose DVX Help from the Program Manager's Help menu, to open the DVX user guide. It explains how to start programs, work with windows, menus and dialog boxes, and everything else that works the same in every program. Each program's own commands are explained in that program's help: press F1 while you work in it.
+
Tip: New to DVX? Click the Program Manager window and press F1, or choose DVX Help from the Program Manager's Help menu, to open the DVX user guide. It explains how to start programs, work with windows, menus, and dialog boxes, and everything else that works the same in every program. Each program's own commands are explained in that program's help: press F1 while you work in it.
Opening Help
Press F1 from any DVX application to open context-sensitive help. Applications can register their own help file and topic so F1 opens the relevant page.
If the help viewer is already running, F1 reuses its window: the requested page is shown there (loading the other application's help file if needed, which starts a fresh Back/Forward history) and the window is brought to the front. The Help menu of the Program Manager does the same. F1 pressed in the help viewer itself shows this help about the viewer. While the viewer's Index or Search dialog is open, the page asked for with F1 is shown once the dialog closes.
@@ -97,7 +97,7 @@ img { max-width: 100%; }
.link <id> <text> Hyperlink to another topic in the same help
file (see Links below)
.image <file> [left|center|right]
- Inline image (BMP, PNG, JPEG or GIF), path
+ Inline image (BMP, PNG, JPEG, or GIF), path
relative to the -i directory
Block Directives
.list Start a bulleted list
@@ -169,11 +169,11 @@ This is a warning message.
skipped)
Multiple input files are merged into a single help file. This allows per-widget or per-feature documentation fragments to be combined automatically.
Input Files
-
Each input file starts fresh: no topic is current and no .list, .note, .code or .table block is left open from the previous file. Text before a file's first .topic is ignored, and directives there are errors. A .code or .table block still open at the end of a file is closed with a warning. Files named with .include are different: they are read in place, as if their lines were part of the including file.
+
Each input file starts fresh: no topic is current and no .list, .note, .code, or .table block is left open from the previous file. Text before a file's first .topic is ignored, and directives there are errors. A .code or .table block still open at the end of a file is closed with a warning. Files named with .include are different: they are read in place, as if their lines were part of the including file.
Source lines longer than 4094 characters, and lines that contain a NUL byte, are errors. The .image directive may name a path relative to the -i directory, including a subdirectory (for example sub/pic.png).
If any error is reported the compile stops and the previous output file is kept.
Links
-
Every .link must name a topic that is compiled into the same help file: a topic in any of the input files or the files they include, before or after the link. A link to any other ID is an error that names the file, the line and the missing ID. The one exception is the help DVX rebuilds by itself when it starts after programs, widgets or libraries were added or removed: there a link to a topic that is no longer present shows as plain text instead, so removing a piece of DVX never breaks the rest of the help. Links cannot reach into another help file; name the other file's page in the text instead. A .link line inside a .code block is example text and is never checked.
+
Every .link must name a topic that is compiled into the same help file: a topic in any of the input files or the files they include, before or after the link. A link to any other ID is an error that names the file, the line, and the missing ID. The one exception is the help DVX rebuilds by itself when it starts after programs, widgets, or libraries were added or removed: there a link to a topic that is no longer present shows as plain text instead, so removing a piece of DVX never breaks the rest of the help. Links cannot reach into another help file; name the other file's page in the text instead. A .link line inside a .code block is example text and is never checked.
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.
+
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.
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.
About This Guide
-
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.
+
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.
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.
Where to Go Next
If you are new to DVX, read these pages in order:
@@ -1906,7 +1997,7 @@ img { max-width: 100%; }
A mouse, with its DOS mouse driver loaded before you start DVX. A scroll wheel works if your mouse driver supports it.
Starting DVX
-
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.
+
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.
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.
When loading is done, the screen changes to the DVX desktop and the Program Manager window opens.
The Desktop and the Program Manager
@@ -1937,19 +2028,19 @@ img { max-width: 100%; }
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.
If you hold the pointer still over some buttons and icons, a small tip appears that tells you what they do.
-
The speed of the pointer, the double-click speed and the scroll wheel can be changed. See Changing Settings.
+
The speed of the pointer, the double-click speed, and the scroll wheel can be changed. See Changing Settings.
The Scroll Wheel
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.
The Keyboard
Most things you can do with the mouse you can also do with the keyboard:
-
Tab moves to the next button, box or list in a window. Shift+Tab moves back.
+
Tab moves to the next button, box, or list in a window. Shift+Tab moves back.
Enter or the Space bar presses the button that is highlighted.
F10 opens the menu bar of the active window. Alt plus an underlined letter opens that menu directly.
Alt+Tab switches to the next window.
F1 opens help.
-
Letters that are underlined in menus, buttons and labels are shortcuts. Hold Alt and press the letter to choose that item.
+
Letters that are underlined in menus, buttons, and labels are shortcuts. Hold Alt and press the letter to choose that item.
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.
+
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.
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.
With the keyboard, press F10 to open the first menu, or hold Alt and press the underlined letter of a menu name. Then:
@@ -2034,13 +2125,13 @@ img { max-width: 100%; }
Many programs use the same menu names for the same kinds of commands:
Menu Usual commands
---- --------------
- File New, Open..., Save, Save As..., Page Setup..., Print... and Exit.
- Edit Cut, Copy, Paste and Select All.
+ 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.
Dialog Boxes
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.
-
Enter presses the main button, usually OK, Open, Save or Yes.
+
Enter presses the main button, usually OK, Open, Save, or Yes.
Esc presses Cancel (or No), and closes the box without making changes.
Closing a dialog box with its Close box counts as Cancel.
Tab moves between the parts of the dialog box.
@@ -2053,11 +2144,11 @@ img { max-width: 100%; }
No throws the changes away.
Cancel goes back to your work without doing anything.
Windows and dialog boxes are built from a small set of controls. They all work the same way in every program.
Buttons
Click a button to use it. When a button is highlighted, Enter or the Space bar presses it too.
@@ -2066,11 +2157,11 @@ img { max-width: 100%; }
Option Buttons
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.
Lists
-
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.
+
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.
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.
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.
Drop-down Lists
-
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.
+
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.
Some drop-down lists also let you type your own entry in the box.
Text Boxes
Click in a text box and type. These keys help you edit:
@@ -2084,7 +2175,7 @@ img { max-width: 100%; }
Ctrl+A Select everything.
Ctrl+Z Undo the last change. Press it again to redo.
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.
-
Cut, Copy and Paste work in every text box; see Copying and Pasting.
+
Cut, Copy, and Paste work in every text box; see Copying and Pasting.
A number box has small up and down arrows. Click the arrows, press Up or Down, or type a number.
@@ -2111,7 +2202,7 @@ img { max-width: 100%; }
Opening a File
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.
Saving a File
-
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.
+
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.
Tip: Alt+D opens the Drive list from the keyboard. You can also change drives by typing the drive letter and a colon, such as A:, in the Path box and pressing Enter.
@@ -2136,10 +2227,10 @@ img { max-width: 100%; }
Printing
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.
The Print Dialog
-
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.
+
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.
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.
When the Printer Has a Problem
-
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.
+
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.
Page Setup
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.
Pictures
@@ -2160,7 +2251,7 @@ img { max-width: 100%; }
Click anywhere in the program's window, or
Press Alt+Tab until its window comes to the front, or
-
Press Ctrl+Esc to open the Task Manager, pick the program and click Switch To.
+
Press Ctrl+Esc to open the Task Manager, pick the program, and click Switch To.
Programs That Use the Whole Screen
Some programs, games especially, can fill the whole screen instead of a window (many use Alt+Enter to switch). Your other programs keep running while they do. Alt+Tab and Ctrl+Esc still work and bring the desktop back; when you return to the program, it fills the screen again.
@@ -2186,14 +2277,14 @@ img { max-width: 100%; }
The Search box finds programs in every group.
The status bar describes the selected program and says how many programs are running.
-
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.
+
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.
Warning: Closing the Program Manager window leaves DVX, after asking you first.
Changing Settings
-
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.
+
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.
Settings you keep are saved, and DVX uses them again the next time it starts.
-
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.
+
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.
Tip: 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.
@@ -2275,12 +2366,13 @@ img { max-width: 100%; }
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.
You can pick another resolution at any time with the settings program (see Changing Settings).
There is no sound
-
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.
+
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.
An empty floppy or CD-ROM drive
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.
-
Installing, upgrading and removing DVX
-
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.
-
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.
+
Installing, upgrading, and removing DVX
+
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 and which fonts to install, checks that there is room, and unpacks DVX there, asking for each floppy disk it needs 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.
+
The fonts are up to you. None keeps DVX to its own font; the base set adds DejaVu Sans, Sans Mono, and Serif in all their styles (about 2 MB); and All adds every font that comes with DVX (about 8 MB). A program that asks for a font you did not install shows its text in DVX's own font instead, unless it carries that font itself (some programs do, such as Welcome and the DVX Demo). Every font file holds its own licence: the Licence button of the Font dialog shows it. With None or the base set, Setup does not ask for the last floppy disks, which hold only fonts.
+
To upgrade or repair DVX, run Setup again and choose the same folder: answer Yes to keep your settings. Setup starts with the fonts that folder already has; choose others to add fonts or remove them. 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.
Running DVX from a CD-ROM or a write-protected disk
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.
A program is already running
@@ -2302,7 +2394,7 @@ img { max-width: 100%; }
For Developers
-
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.
+
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.
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.
@@ -2347,7 +2439,7 @@ img { max-width: 100%; }
Every header uses stdint.h types (int32_t, uint8_t) and stdbool.h types (bool).
Getting Started
-
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.
+
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.
To understand how DVX works, read these topics in order:
@@ -2597,7 +2689,7 @@ img { max-width: 100%; }
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, a drive dropdown and a filter dropdown
+ dvxFileDialog File open/save dialog with directory navigation, a drive dropdown, and a 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
@@ -2621,7 +2713,8 @@ img { max-width: 100%; }
------- ----- -------
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)
+ DVX_RES_BINARY 3 Arbitrary data (app-specific)
+ DVX_RES_FONT 4 A .DFN font the program carries (the shell adds it for the program when it loads it)
Runtime API
Function Description
-------- -----------
@@ -2710,7 +2803,7 @@ prefsClose(h);
Poll mouse -- platformMousePoll() returns position and button bitmask. Compare with previous frame for press/release edge detection.
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.
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.
-
Tooltips, widget polling (WCLASS_NEEDS_POLL), cursor blink and widget timers.
+
Tooltips, widget polling (WCLASS_NEEDS_POLL), cursor blink, and widget timers.
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.
Deferred destroy flush -- windows destroyed from inside callbacks are torn down once the dispatch depth is back to zero.
Compositor pass -- merge dirty rects, composite, flush to LFB.
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.
+
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.
Accelerator Tables
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.
Mouse Wheel
@@ -2748,28 +2841,70 @@ prefsClose(h);
Font System
-
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).
+
Every text call takes a BitmapFontT, and there are two kinds of them. The system font is the 8x16 VGA ROM font (code page 437, 256 glyphs) built into libdvx: 8-pixel cells, one byte per glyph row, so character positions are a multiplication, glyph lookup an index, and every scanline a single byte -- the hot paths of drawChar, drawTextN, and drawTermRow depend on it. A loaded font (dvxText.h) is one size of a font family from the FONTS folder (Setup installs none, the base set of DejaVu Sans, Sans Mono, and Serif, or all of them; a family that is not there opens as the system font): per-glyph advances and bitmap boxes, any width, proportional or fixed-pitch, and in up to four styles. The same calls draw and measure both; a loaded font's glyphs field is what sends them to the general paths.
BitmapFontT
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
+ int32_t charWidth; // cell width (fixed pitch) or the digit 0's advance
+ int32_t charHeight; // line height
+ int32_t firstChar; // 0
+ int32_t numChars; // 256
+ const uint8_t *glyphData; // system font: charHeight bytes per glyph; loaded: bitmaps
+ const FontGlyphT *glyphs; // loaded font: per-glyph metrics; NULL: the system font
+ const uint32_t *glyphAt; // loaded font: each glyph's bitmap offset
+ int32_t ascent; // top of a line to the baseline
+ int32_t pixelSize;
+ uint32_t style; // FONT_STYLE_BOLD / ITALIC
+ uint32_t flags; // FONT_FLAG_PROPORTIONAL, _SYNTH_BOLD, _SYNTH_ITALIC, _GRAY
+ int32_t underlineY; // underline and accelerator row
+ int32_t strikeY; // strike-through row
+ int32_t lineThick;
+ void *priv; // the font cache's entry
+ uint32_t serial; // new whenever the glyphs are built (again)
} BitmapFontT;
-
Design rationale:
-
-
Character positions are pure multiplication (x = col * 8).
-
Glyph lookup is a single array index.
-
Each scanline of a glyph is exactly one byte (1bpp at 8 pixels wide).
-
No glyph-width tables, kerning, or per-character positioning needed.
-
8-pixel width aligns with byte boundaries -- no bit shifting in per-scanline rendering.
-
+
A FontGlyphT is six bytes: advance, bitmap width and height, the bitmap's offset from the pen (left, and top up from the baseline), and flags. A glyph's bitmap is height rows of (width + 7) / 8 bytes, the leftmost pixel in the top bit.
Text Rendering Functions
drawChar() -- Renders a single character. Supports opaque (background fill) and transparent modes.
-
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.
+
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. In a loaded font each glyph is drawn at the pen; every bitmap byte whose 8 pixels lie inside the clip rectangle goes through the same unrolled row writers as the system font, so only the bytes on a clip edge are drawn pixel by pixel.
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.
-
drawTextAccel() -- Renders text with & accelerator markers. The character after & is underlined to indicate the keyboard shortcut.
+
drawTextAccel() -- Renders text with & accelerator markers. The character after & is marked with the font's underline across its advance to indicate the keyboard shortcut.
+
drawTextStyled() -- Text with an underline and / or strike-through as wide as it, at the font's rows.
+
textWidth(), textWidthN(), textWidthAccel() -- Widths: for the system font a multiplication, for a loaded font the sum of the advances.
+
Loading Fonts
+
dvxFontOpen(family, size, style) reads the family list from FONTS (once, at the first call: each .DFN file's header and size table), picks the face for the style -- the exact one, else one with fewer style bits whose missing bits are made -- and that face's size nearest the one asked for. It never scales. The font is built once into a glyph table and bitmaps of its own, so drawing never decides anything per glyph:
+
+
A character the face has is copied; when a style is made, bold thickens each stroke by a column (two from 24 pixels) and italic moves each row one column right per four rows above the baseline.
+
Box drawing, block, and shade characters (CP437 B0-DF) the face lacks -- or all of them, when its file asks, or when a made bold widens a fixed pitch -- are drawn to fill the character cell from a table of each character's arms (none, single, or double; up, down, left, and right), so lines join across characters. They are never thickened or slanted.
+
Any other character the face lacks comes from DejaVu Sans (DejaVu Sans Mono for a fixed-pitch face, centered in its pitch) at the nearest size in the same style, or from the system font when those are not installed.
+
What nothing has is a blank as wide as a space.
+
+
Fonts are cached and counted: the same family, size, and style opened again is the same font. Up to eight fonts no one holds stay loaded, the least recently used unloaded first. Everything the font system allocates is charged to the system, not to the program that happened to open a font.
+
Fonts a Program Carries
+
A family is installed (the FONTS folder, read by its full path so a program that changes the current folder still finds them) or added for an owner: a program's app id, or 0 for every program. The shell calls dvxFontAddResources for each program it loads (its DVX_RES_FONT resources, read in place: a font source is a file and the font's offset and length in it) and dvxFontRemoveOwner when its resources are released; a program can add .DFN files or fonts in memory itself. Every lookup is for the running program (*dvxMemAppIdPtr, the shell's currentAppId; window callbacks run as the window's program).
+
Opening a font picks its face per style from the program's own family and the installed one together: the style's faces in sFaceOrder's order, the first style either has; then the nearest size of either face's sizes; then the program's face when it has that size, else the installed one. So a program's faces and sizes win, and the installed family fills in the rest. A program with no families of its own shares the cache with every other; one with its own gets fonts built for it alone (their fallback glyphs may come from its families too). A program's fonts that change or go are detached from the cache: the ones no one holds unloaded, held ones kept as they are.
+
Each face holds its source counted, and each font built from it holds it too, so a font's licence (dvxFontLicence) is always the one of the face it was built from, its program ended or not.
+
The building works on one level per pixel (0 to FONT_GRAY_FULL) whatever the source, and packs the finished glyph at the font's own depth, so made styles, box characters, and fallback glyphs are the same code for both kinds of font.
+
Smoothed Text
+
A .DFN size can come twice: 1-bit, and smoothed -- FONT_GRAY_BPP (2) bits per pixel, four levels from none to full, rendered by FreeType with light hinting. mkfont smooths the sizes fonts.ini lists under smooth. A 1-bit size is FreeType's 1-bit hinted rendering, except at the sizes listed under graycut, where it is the light-hinted rendering with every pixel at least half covered set: some faces' 1-bit hinting draws them too light (DejaVu Serif Bold at its smoothed sizes, which comes out near regular weight, and DejaVu Sans Bold at 8 pixels). When smoothing is on (dvxFontSetSmooth), such a size opens from its smoothed strike and the font has FONT_FLAG_GRAY; every other size, and every face without one, stays 1-bit.
+
Drawing a full-level pixel is a store of the text color, as for a 1-bit font. A partial one is the text color blended into the pixel under it, channel by channel through the display's PixelFormatT masks and shifts; text mostly stands on one color, so each glyph keeps its last blend per level and reuses it while the color under it stays the same. Only partial pixels cost more than 1-bit text. At 8 bits per pixel there is nothing to blend with: the pixels of FONT_GRAY_HALF and more are drawn and the rest dropped. The scaled text path (drawTextScaled) does the same.
+
The application decides (dvxSetFontSmoothing): smoothing is on when the user wants it (DVX.INI [fonts] smoothing, on by default; Control Panel's Video page) and the screen has more than 8 bits per pixel, and a video mode change applies it again. Turning it on or off builds every open font again in place: widgets keep their font pointers, the glyphs change under them, and the font's serial changes so caches of measurements (list column widths and the like) know to measure again. Every window is then laid out and painted again.
+
Widget Fonts
+
Any widget can have a font of its own (wgtSetFont; BASIC's FontName, FontSize, FontBold ... properties), and the widgets inside a container draw with the container's font unless they have their own, so a form's font is the default for everything on it. wgtFontFor(w, inherited) walks up the parent chain to the nearest font set, else takes the one passed down (the application's). The class dispatch helpers hand every paint, measure, and layout method that font, so widget code just uses its font parameter -- the same code serves the system font, a proportional font, and a fixed-pitch one of any size. Event handlers, which are given no font, call wgtFont(w). Widgets measure text with textWidth / textWidthN, never with character counts. A class that edits text by columns (TextBox, Terminal, ComboBox, Spinner: WCLASS_FIXED_FONT) given a proportional font draws with the system font instead; a fixed-pitch font of any size works there, its column math using that font's charWidth and charHeight.
+
Underline and strike-through are part of a font, as in Visual Basic: a font opened with them shares the glyphs of the same font without them, and every text call draws its lines. A widget holds its font open while it is set and closes it when destroyed (widgetFree, the one place a widget is freed).
+
Rich Text
+
Text in several fonts in one control is built on the text flow (dvxFlow.h): runs of text, each in a font and a color, laid out into wrapped, aligned lines as tall as the tallest font on them, with offsets for carets, hit tests, and selections. The RichLabel widget is one flow built by calls. The RichEdit widget keeps its document in the richtext library (styled text: runs of interned styles over code page 437 text, an alignment per paragraph, copy / replace / restyle, and RTF in and out) and lays each paragraph out as a flow of its own, so an edit lays out only the paragraphs it touched and repaints only the rows they hold (its quick repaint); a change of height repaints the widget. Its undo steps are pieces of styled text swapped back in, and Copy keeps a styled copy for a paste of the same text.
+
Font Files (.DFN)
+
One file holds one family in one style at every size it comes in, made on the build host by mkfont (src/tools/mkfont.c) with FreeType from TrueType, OpenType, Type 1, BDF, and PCF fonts, as listed in src/fonts/fonts.ini. A .DFN is a pure format conversion: the converter never adds or changes a glyph, it only marks the characters a face lacks. Little-endian, every record read whole:
+
Part Size Holds
+ ---- ---- -----
+ DfnHeaderT 60 bytes "DVXF", format version (2), style, face flags (fixed pitch; box characters drawn by DVX), strike count (1 to 32), family name (31 characters), where the notice and licence texts are and their lengths
+ DfnStrikeT x count 24 bytes each per size, smallest first: where its glyph table and bitmaps are, the bitmaps' size, the pixel size, ascent, descent, line height, widest advance
+ FontGlyphT x 256 6 bytes each per size, at its glyph table offset
+ glyph bitmaps as listed per size, back to back in character order
+ notice text as listed the family and style, the source fonts it was converted from, and their copyright notices
+ licence text as listed the licence of the font it came from
+
Each glyph's bitmap starts where the one before ends, so the reader (dvxFontFmt.c) works out the offsets and checks that they fill the strike's bitmaps exactly. Every count, offset, and size is checked against the font's length before it is used: a damaged file is refused, never read past.
+
Each file carries what its licence asks to go with every copy of the font: the notice and licence texts, transliterated to ASCII with CR LF line ends. They are read only when asked for (dvxFontLicence, the Font dialog's Licence button), never when text is drawn, so they cost nothing at run time. A font goes nowhere without its licence: copied, installed, or carried inside a program as a resource.
+
The reader takes a DfnSourceT: a file of its own, a font inside another file at an offset (a program's font resource), or a font in memory. dfnOpen reads the header and strike table, dfnLoadStrike one size, and dfnLoadText a text.
Performance Optimization
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.
@@ -2822,7 +2957,7 @@ prefsClose(h);
Optimized Memory Spans
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.
Mouse Input
-
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.
+
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.
Keyboard Input
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.
Timing
@@ -2923,6 +3058,8 @@ prefsClose(h);
dvxTypes.h -- Shared type definitions and constants
dvxCur.h -- Embedded mouse cursor bitmaps
dvxFont.h -- Embedded 8x16 CP437 bitmap font
+
dvxText.h -- Font families and loaded fonts
+
dvxFlow.h -- Flowing text: runs in fonts and colors, wrapped and aligned
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.
Binary Interface Version
-
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:
+
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:
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).
@@ -2972,7 +3111,7 @@ prefsClose(h);
#include "dvxTypes.h"
DVX_ABI_STAMP(mylib);
-
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.
+
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.
Core Structures
PixelFormatT
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.
@@ -3017,14 +3156,47 @@ DVX_ABI_STAMP(mylib);
uint32_t face Interior fill color (0 = no fill)
int32_t width Border thickness in pixels (typically 2)
BitmapFontT
-
Fixed-width 8-pixel-wide bitmap font descriptor. One size provided: 8x16 (standard VGA ROM font, CP437 encoding).
+
The one font type every text call takes. Two kinds share it: the built-in system font (dvxFont8x16: 8-pixel cells of the VGA 8x16 font, one byte per glyph row, glyphs NULL; text takes the byte-aligned fast paths) and a loaded font (dvxFontOpen, dvxText.h: a size of a .DFN font file, or the system font made bold or italic), whose glyphs give each character's advance and bitmap box. Every character of code page 437 draws in either kind.
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
+ int32_t charWidth Cell width of a fixed-pitch font; the digit 0's width in a proportional one (for "so many characters wide")
+ int32_t charHeight Line height
+ int32_t firstChar Code of the first glyph (0)
+ int32_t numChars Number of glyphs (256)
+ const uint8_t *glyphData System font: charHeight bytes per glyph; loaded font: the glyph bitmaps
+ const FontGlyphT *glyphs Loaded font: each character's glyph; NULL for the system font
+ const uint32_t *glyphAt Loaded font: where each glyph's bitmap starts in glyphData
+ int32_t ascent Rows from the top of a line to the baseline
+ int32_t pixelSize The size the font was made at (16 for the system font)
+ uint32_t style FONT_STYLE_BOLD / FONT_STYLE_ITALIC the font draws
+ uint32_t flags FONT_FLAG_PROPORTIONAL, FONT_FLAG_SYNTH_BOLD, FONT_FLAG_SYNTH_ITALIC, FONT_FLAG_GRAY
+ int32_t underlineY Row of an underline and of accelerator marks, from the top of a line
+ int32_t strikeY Row of a strike-through line
+ int32_t lineThick Rows in those lines
+ void *priv The font cache's (dvxFontClose); NULL for fonts not from dvxFontOpen
+ uint32_t serial A loaded font's own number, never reused; new when its glyphs are built again (dvxFontSetSmooth). 0 for the system font as compiled in
+
Measure text with textWidth / textWidthN, never strlen * charWidth: a loaded font may be proportional.
+
FontGlyphT
+
One character of a loaded font, the same 6-byte record a .DFN file stores. The glyph's bitmap is height rows of FONT_ROW_BYTES(width) bytes, the leftmost pixel in the top bit, at glyphData + glyphAt[character]. In a smoothed font (FONT_FLAG_GRAY) each pixel is FONT_GRAY_BPP bits instead: rows of FONT_ROW_BYTES_BPP(width, FONT_GRAY_BPP) bytes, a pixel's level 0 (none) to FONT_GRAY_FULL read with FONT_GRAY_LEVEL(row, x); FONT_BPP(font) is a font's bits per pixel.
+
Field Description
+ ----- -----------
+ uint8_t advance Pen movement after the glyph
+ uint8_t width, height Bitmap size
+ int8_t left Pen position to the bitmap's left column (negative: the glyph reaches back)
+ int8_t top Baseline to the bitmap's top row (up is positive)
+ uint8_t flags FONT_GLYPH_MISSING in a file (a loaded font has every glyph)
+
Font styles and flags
+
Define Value Description
+ ------ ----- -----------
+ FONT_STYLE_BOLD 0x01 Bold face (dvxFontOpen)
+ FONT_STYLE_ITALIC 0x02 Italic face (dvxFontOpen)
+ FONT_STYLE_UNDERLINE 0x04 Underline drawn with the text (drawTextStyled)
+ FONT_STYLE_STRIKE 0x08 Strike-through drawn with the text (drawTextStyled)
+ FONT_STYLE_FACE 0x03 The face bits (bold and italic)
+ FONT_FLAG_PROPORTIONAL 0x01 Characters differ in width
+ FONT_FLAG_SYNTH_BOLD 0x02 Bold made from the regular face (thickened)
+ FONT_FLAG_SYNTH_ITALIC 0x04 Italic made by slanting the upright face
+ FONT_FLAG_GRAY 0x08 Smoothed: glyph pixels of FONT_GRAY_BPP bits (dvxFontSetSmooth)
ColorSchemeT
All UI colors pre-packed into display pixel format at init time. Theme support is achieved by swapping this struct.
Field Description
@@ -3091,7 +3263,7 @@ DVX_ABI_STAMP(mylib);
onFocus(WindowT *win) Window gained focus
onBlur(WindowT *win) Window lost focus
onRawKey(WindowT *win, const RawKeyEventT *evt) Key pressed or released (gameInput windows)
-
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.
+
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.
WindowStackT
Z-ordered window stack (front-to-back: index count-1 is topmost). Owns system-wide drag/resize/scroll interaction state.
Field Description
@@ -3225,7 +3397,7 @@ DVX_ABI_STAMP(mylib);
Raw Scancodes
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.
Raw Key Codes
-
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.
+
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.
Define Value Key
------ ----- ---
KEY_RAW_LSHIFT, KEY_RAW_RSHIFT 0x2A, 0x36 Left / right Shift
@@ -3313,7 +3485,123 @@ DVX_ABI_STAMP(mylib);
------ ----- -----------
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)
+ FONT_CHAR_HEIGHT 16 Glyph height in pixels (defined in dvxTypes.h)
+ FONT_SYS_ASCENT 12 Rows above the baseline (letters sit on row 11)
+ FONT_SYS_UNDERLINE_Y 15 Underline and accelerator mark row (the cell's last)
+ FONT_SYS_STRIKE_Y 8 Strike-through row (the middle of the lower-case letters)
+ FONT_SYS_LINE_THICK 1 Rows in those lines
+
+
+
dvxText.h -- Fonts
+
The font families DVX draws with: "System", the built-in 8x16 font, and the families in the FONTS folder. Which families that folder holds depends on the install -- Setup puts in none, the base set (DejaVu Sans, Sans Mono, and Serif), or all of them -- and dvxFontOpen of a family that is not there gives the system font. A family has up to four faces (regular, bold, italic, bold italic), each a .DFN file holding the face at the pixel sizes it was made at; the files are made on the build host from TrueType, Type 1, and bitmap fonts (src/tools/mkfont.c), so DVX only copies bitmaps.
+
dvxFontOpen gives a BitmapFontT every text call draws and measures with. It never scales: it picks the face's size nearest the one asked for. A style the family has no face for is made from the nearest face -- bold by thickening each stroke a column (two from 24 pixels), italic by slanting a column per four rows -- and the font's flags say so. Every character of code page 437 draws: what a face lacks is filled in when the font loads. Box drawing, block, and shade characters are drawn to fill the character cell (the pitch, or the digit 0's width) so lines join, also in place of a face's own when its file asks; other characters come from FONT_FALLBACK_FAMILY (FONT_FALLBACK_FIXED for a fixed-pitch face, centered in its pitch) at the nearest size and the same style, or from the system font when that family is not installed.
+
A face can also hold a size smoothed (anti-aliased): each pixel one of four levels, from none to full, drawn as that blend of the text color and what is under it. When smoothing is on (dvxFontSetSmooth) such a size opens smoothed, with FONT_FLAG_GRAY set and FONT_GRAY_BPP bits per pixel; other sizes and faces stay 1-bit. At 8 bits per pixel there are no colors to blend: the pixels of FONT_GRAY_HALF and more are drawn, the rest not.
+
Fonts are shared and counted: the same family, size, and style opened again is the same font, and dvxFontClose gives one up. A few fonts no one holds stay loaded so reopening them is quick. A font's memory belongs to DVX, not to the program that opened it. The family list is read from DVX_FONTS_DIR the first time it is needed; a file that cannot be read is logged and skipped.
+
Fonts a Program Carries
+
A program can carry fonts of its own: font resources in its file (DVX_RES_FONT, dvxRes.h; a .res line "name font FILE.DFN", or a BASIC project's Fonts), .DFN files it adds with dvxFontAddFile, or fonts in memory (dvxFontAddMemory). The shell adds a program's font resources when it loads the program and forgets them when the program ends; whatever a program adds itself goes then too. Its fonts are its own: every call here works for the running program (the app id the shell sets while a program's code runs, window callbacks included), and other programs never see them.
+
The program sees the installed families and its own. Where it has a family that is installed too, its faces and sizes come first, and the installed family fills in the styles and sizes it lacks: a program that carries DejaVu Sans Bold at 36 pixels gets that size from its own file and every other size and style of DejaVu Sans from the FONTS folder. The nearest size is chosen from both together. Fonts built for a program that carries fonts are cached for it alone; programs that carry none share every font.
+
A .DFN carries its notice (where the font came from and its copyright notices) and its licence, so they go wherever the font goes; dvxFontLicence reads them, and the Font dialog shows them (its Licence button).
+
Constants
+
Define Value Description
+ ------ ----- -----------
+ DVX_FONTS_DIR "FONTS" The folder of .DFN files (beside CONFIG)
+ FONT_SYSTEM_FAMILY "System" The built-in 8x16 font's family name
+ FONT_FALLBACK_FAMILY "DejaVu Sans" Fills what a proportional face lacks
+ FONT_FALLBACK_FIXED "DejaVu Sans Mono" Fills what a fixed-pitch face lacks
The font of family at the size nearest pixelSize in style. NULL, "", and a family there is none of give the system font (16 pixels; plain, it is dvxFont8x16 itself, drawn by the fast fixed paths).
+
Parameter Description
+ --------- -----------
+ family Family name (any case)
+ pixelSize Size wanted, in pixels
+ style FONT_STYLE_BOLD and / or FONT_STYLE_ITALIC (the line styles are ignored)
+
Returns: The font, or NULL when out of memory. Close it with dvxFontClose.
+
dvxFontClose
+
void dvxFontClose(BitmapFontT *font);
+
Give up a font dvxFontOpen gave. NULL and fonts not from dvxFontOpen (dvxFont8x16, the application context's font) are ignored.
The number of families (the system font's included) and family index: "System" first, then the others in alphabetical order. dvxFontFamilyName returns NULL for an index out of range.
The pixel sizes family has in style -- those of the face the style is drawn from -- smallest first, into sizes (at most max).
+
Returns: How many sizes there are (which may be more than max); 0 for a family there is none of.
+
dvxFontRetain
+
BitmapFontT *dvxFontRetain(BitmapFontT *font);
+
Hold font once more, for keeping a font someone else opened (each hold is given up with dvxFontClose). Returns font; fonts not from dvxFontOpen are returned as they are.
+
dvxFontRescan
+
void dvxFontRescan(void);
+
Read the FONTS folder again on the next call, after fonts were added or removed. The fonts no one holds are unloaded; open fonts stay valid.
Smooth fonts where their face has the size smoothed, or stop; dvxFontSmooth says which it is (off at first). The fonts open are built again in place: the same BitmapFontT pointers with new glyphs and a new serial, so code that keeps measurements of a font compares its serial too, and text is laid out again. The fonts no one holds are unloaded. Programs call dvxSetFontSmoothing (dvxApp.h) instead, which follows the user's choice and the screen's depth.
Add the .DFN at path, or held in data (length bytes, copied at once; name names it in the log), as a face of its family for program owner (an app id; 0 for every program, as SETUP.EXE adds the fonts it carries). Families added for owner 0 are seen by every program and kept by dvxFontRescan.
+
Returns: True when added; false, logged, when the font cannot be read, calls its family "System", or owner has a face of that family and style already.
Add every font resource (DVX_RES_FONT) of the file at path -- a program, a library, or a resource file -- for owner. The shell calls it for each program it loads.
+
Returns: How many fonts were added.
+
dvxFontRemoveOwner
+
void dvxFontRemoveOwner(int32_t owner);
+
Forget every face added for owner (the shell does when a program ends; for 0, the faces added for every program, the installed ones staying). Its fonts no one holds are unloaded; a held one stays as it is until it is closed.
+
dvxFontLicence
+
char *dvxFontLicence(const BitmapFontT *font);
+
The texts that go with the face font was built from: its notice -- the family and style, the fonts it was converted from, and their copyright notices -- then a blank line and its licence. ASCII with CR LF line ends; malloc'd (the caller frees it).
+
Returns: The texts; NULL for the system font, and when the font's file cannot be read.
+
+
+
dvxFlow.h -- Flowing Text
+
A flow is a document built in order: runs of text, each in a font and a color, line breaks, and paragraph starts, each with an alignment. dvxFlowLayout wraps it to a width into lines; dvxFlowPaint draws them. The RichLabel widget is a flow with calls around it; the RichEdit widget lays out each of its paragraphs as a flow and edits through the offset calls below.
+
Layout is greedy: a word and the spaces after it go on the line when the word fits, else the line ends and the word starts the next one, without its leading spaces. A word wider than a whole line is broken where the line is full. A line is as tall as the tallest font on it -- the largest ascent above one baseline and the deepest descent below it -- and its trailing spaces neither count toward its width (alignment) nor draw (an underline would show them). A word may go on across runs (a style changed inside it, or punctuation in a run of its own); it wraps as one word, and only a word wider than a whole line breaks, where the line is full. Underline and strike-through come with a run's font (dvxText.h).
+
Offsets number the text added from 0, each run's characters after the one before (breaks and paragraphs take none); an offset is the caret before that character. Each line owns the offsets from its start to the next line's start -- the spaces it wrapped at included -- and the last line those to the text's end. Text that is all empty lays out as one empty line in its first run's font, so an empty paragraph keeps a height and a caret place.
+
Constants
+
Define Value Description
+ ------ ----- -----------
+ FLOW_ALIGN_LEFT 0 Paragraph alignment
+ FLOW_ALIGN_CENTER 1
+ FLOW_ALIGN_RIGHT 2
+ FLOW_COLOR_DEFAULT 0xFFFFFFFF A run in the color dvxFlowPaint is given
+ FLOW_WIDTH_NATURAL INT32_MAX dvxFlowLayout's width for the natural size: lines break only at breaks and paragraphs
+
Functions
+
Function Description
+ -------- -----------
+ TextFlowT *dvxFlowCreate(void) An empty flow (one left-aligned paragraph); NULL out of memory.
+ void dvxFlowDestroy(flow) Free it and give up the fonts it holds. NULL is ignored.
+ void dvxFlowClear(flow) Empty it again.
+ bool dvxFlowAddText(flow, text, len, font, color) Add len bytes of text (len below 0: up to the NUL) in font and color (0x00RRGGBB or FLOW_COLOR_DEFAULT). The flow holds the font (dvxFontRetain) and copies the text.
+ bool dvxFlowAddBreak(flow) Start a new line in the same paragraph (two in a row make an empty line, as tall as the font before it).
+ bool dvxFlowAddParagraph(flow, align) Start a new paragraph aligned FLOW_ALIGN_*; it ends the line before it when that has text, and on an empty flow it sets the first paragraph's alignment.
+ int32_t dvxFlowLayout(flow, width) Lay it out width pixels wide; returns its height. The same width again costs nothing.
+ int32_t dvxFlowHeight(flow) / dvxFlowWidth(flow) The last layout's height and widest line.
+ void dvxFlowPaint(flow, d, ops, x, y, defaultFg) Draw the lines that meet the clip rectangle, top-left at x, y, aligned within the width laid out at (the widest line's for FLOW_WIDTH_NATURAL); FLOW_COLOR_DEFAULT runs in defaultFg (a packed pixel). Transparent.
+ void dvxFlowPaintSel(flow, d, ops, x, y, defaultFg, selStart, selEnd, selFg, selBg) dvxFlowPaint with offsets selStart to selEnd selected: their text in selFg on selBg (packed pixels), a line's undrawn trailing spaces too.
+ int32_t dvxFlowTextLength(flow) The characters added (the last offset).
+ int32_t dvxFlowLineCount(flow) The last layout's lines.
+ int32_t dvxFlowLineOf(flow, offset) The line the caret before offset is on (a line's end is the next line's start); -1 with no lines.
+ int32_t dvxFlowLineStart(flow, line) The offset a line starts at; past the last line, the text's length.
+ void dvxFlowLineBox(flow, line, &y, &h) A line's top and height.
+ void dvxFlowCaret(flow, offset, &x, &y, &h) The caret before offset: its x, and its line's top and height (alignment included).
+ int32_t dvxFlowHitTest(flow, x, y) The offset nearest a point: on the line at y (the first or last beyond them), the character boundary nearest x. A line that wrapped at a space ends before it.
+
TextFlowT *flow = dvxFlowCreate();
+BitmapFontT *bold = dvxFontOpen("DejaVu Sans", 14, FONT_STYLE_BOLD);
+
+dvxFlowAddText(flow, "Warning: ", -1, bold, DVX_RGB(160, 0, 0));
+dvxFlowAddText(flow, "the disk is almost full.", -1, &ctx->font, FLOW_COLOR_DEFAULT);
+dvxFontClose(bold);
+dvxFlowLayout(flow, 200);
+dvxFlowPaint(flow, d, ops, x, y, fg);
dvxPal.h -- 256-Color Palette for 8-bit Mode
@@ -3422,7 +3710,7 @@ DVX_ABI_STAMP(mylib);
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.
drawInit
void drawInit(BlitOpsT *ops, const DisplayT *d);
-
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.
+
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.
Draw text (drawText, drawTextN) with the lines style asks for: FONT_STYLE_UNDERLINE at the font's underlineY and FONT_STYLE_STRIKE at its strikeY, lineThick rows, as wide as the text, in fg. Bold and italic are the font's own (dvxFontOpen); those bits of style are ignored here.
+
Parameter Description
+ --------- -----------
+ d Display context
+ ops Blit operations vtable
+ font Bitmap font
+ x, y Top-left of the line
+ text Text (drawTextStyledN: count characters of it)
+ style FONT_STYLE_UNDERLINE and / or FONT_STYLE_STRIKE
+ fg, bg Foreground and background packed colors
+ opaque true = fill background, false = transparent
accelParse
char accelParse(const char *text);
Scan text for an & prefix and return the following character as a lowercase accelerator key. "&File" returns 'f', "E&xit" returns 'x'.
@@ -3547,7 +3858,7 @@ DVX_ABI_STAMP(mylib);
Returns: Lowercase accelerator character, or 0 if none.
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.
+
Draw text with & accelerator markers. The character after & is marked with the font's underline (underlineY; the system font's last row) across its advance to indicate the keyboard shortcut. && produces a literal &. Used for menu items and button labels.
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.
+
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.
Paint a complete scrollbar from a ready-made thumb geometry: the sunken trough, an arrow button (drawScrollbarArrowButton) at each end, and a raised thumb thumbPos pixels past the first arrow button. The window scrollbars, the widget scrollbars and wmDrawScrollbarAt (which computes the thumb from a scroll state) all paint through it.
+
Paint a complete scrollbar from a ready-made thumb geometry: the sunken trough, an arrow button (drawScrollbarArrowButton) at each end, and a raised thumb thumbPos pixels past the first arrow button. The window scrollbars, the widget scrollbars, and wmDrawScrollbarAt (which computes the thumb from a scroll state) all paint through it.
Parameter Description
--------- -----------
d Display context
@@ -3895,7 +4206,7 @@ void drawFocusRectDots(int32_t x, int32_t y, int32_t w, int32_t h, DrawDotFnT fn
enabled true = enabled, false = grayed out
Give an item a new label, written as wmAddMenuItem takes it: & before the accelerator letter, and the key text after a tab ("&Open\tCtrl+O"). The accelerator follows the new label; the item's ID, type and state stay. An open popup shows the new label the next time it is drawn. A NULL item or label does nothing.
+
Give an item a new label, written as wmAddMenuItem takes it: & before the accelerator letter, and the key text after a tab ("&Open\tCtrl+O"). The accelerator follows the new label; the item's ID, type, and state stay. An open popup shows the new label the next time it is drawn. A NULL item or label does nothing.
Create a cascading submenu attached to a parent menu. The child MenuT is heap-allocated and freed when the parent window is destroyed.
@@ -4201,11 +4512,12 @@ void drawFocusRectDots(int32_t x, int32_t y, int32_t w, int32_t h, DrawDotFnT fn
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
+ bool fontSmoothing Text smoothing wanted (dvxSetFontSmoothing; on at start)
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
+ WallpaperModeE wallpaperMode Stretch, tile, or center
dvxSetMouseConfig programs the mouse speed and acceleration into the platform layer, which re-applies them after every mouse driver reset; mouseAccel and mouseMickeys only record the last values applied (for dvxMouseToPrefs and settings pages).
Initialization and Shutdown
dvxInit
@@ -4291,7 +4603,7 @@ void drawFocusRectDots(int32_t x, int32_t y, int32_t w, int32_t h, DrawDotFnT fn
win Window to raise
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.
+
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.
Load a theme file (INI format with [colors] section) and apply it through dvxColorsFromPrefs. 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. A missing file returns false.
+
Load a theme file (INI format with [colors] section) and apply it through dvxColorsFromPrefs. 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. A missing file returns false.
The [colors] section of an INI handle, the one place its keys are read and written: dvxLoadTheme / dvxSaveTheme, the shell at start (DVX.INI) and Control Panel (OK, and its Cancel snapshot in a prefsCreate handle) all use these. FromPrefs reads every "name = r,g,b" key (malformed ones ignored, channels clamped to 0..255), repaints only when a color changed and returns true when any color key was read. ToPrefs writes every color.
+
The [colors] section of an INI handle, the one place its keys are read and written: dvxLoadTheme / dvxSaveTheme, the shell at start (DVX.INI), and Control Panel (OK, and its Cancel snapshot in a prefsCreate handle) all use these. FromPrefs reads every "name = r,g,b" key (malformed ones ignored, channels clamped to 0..255), repaints only when a color changed and returns true when any color key was read. ToPrefs writes every color.
dvxColorName
const char *dvxColorName(ColorIdE id);
Return the INI key name for a color ID (e.g. "desktop", "windowFace").
The [desktop] wallpaper and mode keys of an INI handle (mode through prefsWallpaperChoices). FromPrefs applies them: a missing mode is WALLPAPER_MODE_DEFAULT and a missing wallpaper means none; the image is built again only when the path or mode differs from the current one. It returns false when the named image could not be loaded. ToPrefs writes the path (removes the key when there is none) and the mode.
Smooth text, or stop (ctx->fontSmoothing; on after dvxInit). Text is smoothed when on and the screen has more than 8 bits per pixel (dvxFontSetSmooth, dvxText.h), in the faces and sizes that have a smoothed strike; dvxChangeVideoMode applies it again at the new depth. When what is drawn changes, every window is laid out and painted again.
The [fonts] keys of an INI handle: smoothing (true or false; true when missing). FromPrefs applies it with dvxSetFontSmoothing; ToPrefs writes the setting. The shell uses FromPrefs at start and Control Panel both for OK and for its Cancel snapshot.
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. The values are kept in AppContextT (wheelDirection, dblClickMs, mouseAccel, mouseMickeys, wheelStep).
+
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. The values are kept in AppContextT (wheelDirection, dblClickMs, mouseAccel, mouseMickeys, wheelStep).
The [mouse] keys of an INI handle: wheel and acceleration (by name, through prefsWheelDirChoices and prefsAccelChoices), doubleclick (milliseconds), speed (mickeys per 8 pixels) and wheelspeed. FromPrefs applies them with dvxSetMouseConfig, defaults for missing keys (MOUSE_*_DEFAULT); ToPrefs writes the configuration in effect (an acceleration no choice has leaves that key alone). The shell uses FromPrefs at start and Control Panel both for OK and for its Cancel snapshot.
+
The [mouse] keys of an INI handle: wheel and acceleration (by name, through prefsWheelDirChoices and prefsAccelChoices), doubleclick (milliseconds), speed (mickeys per 8 pixels), and wheelspeed. FromPrefs applies them with dvxSetMouseConfig, defaults for missing keys (MOUSE_*_DEFAULT); ToPrefs writes the configuration in effect (an acceleration no choice has leaves that key alone). The shell uses FromPrefs at start and Control Panel both for OK and for its Cancel snapshot.
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.
+
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.
Parameter Description
--------- -----------
fn Entropy sink, or NULL to disable
The function that plays the sounds, set by the sound library when it loads (NULL: silent). Programs do not call it.
Full-Screen Exclusive Mode
-
A window's program takes the whole screen in a video mode of its own (a game at 320x200, say) while every other program keeps running: windows keep painting into their own buffers, and the desktop is drawn again when the program lets go. The owner's window must have the focus: when it loses it (Alt+Tab, Ctrl+Esc, a dialog, another window coming to the front) the desktop comes back and onChange hears ExclusiveSuspendedE; when the window has the focus again the mode is set again and onChange hears ExclusiveActiveE (draw the whole frame: the screen and the backbuffer start black). While active every key but Alt+Tab, Ctrl+Esc and Alt+F4 goes to the window as in game input mode, the mouse cursor is not drawn, onMouse gets full-screen coordinates, dvxScreenshot saves the full screen, and dvxChangeVideoMode fails. Closing the window, the program ending or being ended, and dvxExclusiveEnd give the screen back. See the Writing Games guide.
+
A window's program takes the whole screen in a video mode of its own (a game at 320x200, say) while every other program keeps running: windows keep painting into their own buffers, and the desktop is drawn again when the program lets go. The owner's window must have the focus: when it loses it (Alt+Tab, Ctrl+Esc, a dialog, another window coming to the front) the desktop comes back and onChange hears ExclusiveSuspendedE; when the window has the focus again the mode is set again and onChange hears ExclusiveActiveE (draw the whole frame: the screen and the backbuffer start black). While active every key but Alt+Tab, Ctrl+Esc, and Alt+F4 goes to the window as in game input mode, the mouse cursor is not drawn, onMouse gets full-screen coordinates, dvxScreenshot saves the full screen, and dvxChangeVideoMode fails. Closing the window, the program ending or being ended, and dvxExclusiveEnd give the screen back. See the Writing Games guide.
typedef enum {
ExclusiveActiveE, // the screen is the program's: draw the whole frame
ExclusiveSuspendedE, // its window lost the focus: the desktop is back until it has the focus again
@@ -4605,7 +4925,7 @@ typedef void (*ExclusiveFnT)(WindowT *win, ExclusiveStateE state, void *userData
Returns: DVX_EXCL_OK; DVX_EXCL_ERR_ARGS (no such window, or a bad size); DVX_EXCL_ERR_BUSY (another window has full screen); DVX_EXCL_ERR_MODE (no mode could be set; the desktop is back).
Write a key and its modifiers (given as for dvxAddAccel) as text, the way menus show it after a tab: the modifiers Ctrl, Alt and Shift in that order, each followed by +, then the key -- a letter or digit, F1 to F12, or one of the names Backspace, Del, Down, End, Enter, Esc, Home, Ins, Left, PgDn, PgUp, Right, Space, Tab, Up. Key 0 gives "".
+
Write a key and its modifiers (given as for dvxAddAccel) as text, the way menus show it after a tab: the modifiers Ctrl, Alt, and Shift in that order, each followed by +, then the key -- a letter or digit, F1 to F12, or one of the names Backspace, Del, Down, End, Enter, Esc, Home, Ins, Left, PgDn, PgUp, Right, Space, Tab, Up. Key 0 gives "".
Read key text as dvxAccelFormat writes it ("F2", "Ctrl+X", "Alt+Enter", "Shift+Del") into a key and ACCEL_* modifiers for dvxAddAccel or dvxRegisterHotkey. Case does not matter, blanks around the parts are ignored, and Delete and Insert are accepted for Del and Ins. Returns false, with *outKey 0, for empty text, an unknown modifier or an unknown key.
+
Read key text as dvxAccelFormat writes it ("F2", "Ctrl+X", "Alt+Enter", "Shift+Del") into a key and ACCEL_* modifiers for dvxAddAccel or dvxRegisterHotkey. Case does not matter, blanks around the parts are ignored, and Delete and Insert are accepted for Del and Ins. Returns false, with *outKey 0, for empty text, an unknown modifier, or an unknown key.
System-Wide Hotkeys
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.
-
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.
+
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.
Load an image file (BMP, PNG, JPEG, GIF) and convert to the display's native pixel format. A file that is no image but a DVX program, widget or library with an icon resource gives that icon (dvxResLoadIconData), so every picture that takes a file name can show a program's icon. Caller must free with dvxFreeImage().
+
Load an image file (BMP, PNG, JPEG, GIF) and convert to the display's native pixel format. A file that is no image but a DVX program, widget, or library with an icon resource gives that icon (dvxResLoadIconData), so every picture that takes a file name can show a program's icon. Caller must free with dvxFreeImage().
The icon of a DVX program, widget or library file as the bytes of its image file: its DVX_RES_NAME_ICON resource ("icon32", a program's icon), else DVX_RES_NAME_WIDGET_ICON ("icon24", a widget's toolbox icon). Decode it with dvxLoadImageFromMemory or dvxLoadImageAlpha. Caller must free() the buffer.
+
The icon of a DVX program, widget, or library file as the bytes of its image file: its DVX_RES_NAME_ICON resource ("icon32", a program's icon), else DVX_RES_NAME_WIDGET_ICON ("icon24", a widget's toolbox icon). Decode it with dvxLoadImageFromMemory or dvxLoadImageAlpha. Caller must free() the buffer.
Parameter Description
--------- -----------
- dxePath Path to the program, widget or library file
+ dxePath Path to the program, widget, or library file
outSize Output: size of the data in bytes
Returns: The data, or NULL when the file has neither resource (or is no DVX file).
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.
+
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.
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).
Types
Type Members
@@ -4892,7 +5212,7 @@ bool dvxAppIsHung(const AppContextT *ctx, int32_t appId);
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.
+
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.
Unloads one module (nothing when it is not loaded) and clears its handle and descriptor.
Writing a Device Class
-
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.
+
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.
dvxWgt.h -- Widget System
@@ -4924,6 +5244,7 @@ bool dvxAppIsHung(const AppContextT *ctx, int32_t appId);
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)
+ BitmapFontT *font The widget's own font (wgtSetFont); NULL = its container's (wgtFontFor)
bool visible, enabled, readOnly State flags
bool swallowTab Tab key goes to widget, not focus navigation
bool paintDirty Needs repaint (set by wgtInvalidatePaint)
@@ -4953,7 +5274,7 @@ bool dvxAppIsHung(const AppContextT *ctx, int32_t appId);
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
-
The universal callbacks belong to the widget's host (the application or the form runtime). A widget class sees the same input earlier through its own class methods: WGT_METHOD_ON_MOUSE_EVENT, WGT_METHOD_ON_KEY, WGT_METHOD_ON_KEY_UP, WGT_METHOD_ON_FOCUS, WGT_METHOD_ON_BLUR and WGT_METHOD_ON_WHEEL run before the matching callback (see Method IDs).
+
The universal callbacks belong to the widget's host (the application or the form runtime). A widget class sees the same input earlier through its own class methods: WGT_METHOD_ON_MOUSE_EVENT, WGT_METHOD_ON_KEY, WGT_METHOD_ON_KEY_UP, WGT_METHOD_ON_FOCUS, WGT_METHOD_ON_BLUR, and WGT_METHOD_ON_WHEEL run before the matching callback (see Method IDs).
WCLASS_PRESS_RELEASE Click = press + release (Button, ImageButton)
WCLASS_ACCEL_WHEN_HIDDEN Accelerator matching works even when invisible
WCLASS_HAS_TEXT The first field of w->data is the widget's own text (strdup / free)
- WCLASS_RAISES_EVENTS The class fires the universal mouse, click, key and scroll callbacks itself (wgtFireEvent); the dispatcher does not call them for its widgets. Its class methods still get every event. A BASIC UserControl has it: its host sees only what the control's code raises. GotFocus / LostFocus still reach onFocus / onBlur.
+ WCLASS_RAISES_EVENTS The class fires the universal mouse, click, key, and scroll callbacks itself (wgtFireEvent); the dispatcher does not call them for its widgets. Its class methods still get every event. A BASIC UserControl has it: its host sees only what the control's code raises. GotFocus / LostFocus still reach onFocus / onBlur.
+ WCLASS_FIXED_FONT The class edits text by character columns (TextBox, TextArea, Terminal, ComboBox, Spinner): given a proportional font it draws with the system font instead (wgtFontFor); a fixed-pitch font of any size is used.
wgtSetFont gives w the font of family at the size nearest size in style (dvxFontOpen, dvxText.h: FONT_STYLE_BOLD, ITALIC, UNDERLINE, STRIKE); the widgets inside w draw with it too unless they have their own -- a form's font is its root widget's. A NULL family gives the font up, so w uses its container's again. The window is laid out again. Returns false when out of memory (w keeps the font it had). The font is closed when w is destroyed.
+
wgtFontFor is the font w draws with: its own, else the nearest container's up the parent chain, else inherited (the font passed down: the application's, or NULL for a metrics query that needs none). A class with WCLASS_FIXED_FONT given a proportional font gets the system font. The class dispatch helpers (wclsPaint, wclsPaintOverlay, wclsCalcMinSize, wclsLayout, wclsGetLayoutMetrics, wclsGetPopupRect) hand every method this font, so a widget's paint, measure, and layout methods simply use their font parameter. Code that is given no font -- mouse and key handlers, helpers -- calls wgtFont(w), the same with the application's font. Size hints in characters (wgtChars) count the widget's own font.
+
Parameter Description
+ --------- -----------
+ w Widget
+ family Family name (NULL: inherit again)
+ size Pixel size wanted
+ style FONT_STYLE_* bits
+ inherited The font the caller would otherwise use
wgtSetEnabled
void wgtSetEnabled(WidgetT *w, bool enabled);
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.
A container's padding (space inside its edge) and spacing (gap between children) in pixels. The getters return what the layout uses: the container's default while the field is WGT_GAP_DEFAULT (every widget starts so), and 0 for a size given in another unit. The setters clamp to 0..WGT_GAP_PIXELS_MAX (0 is no gap) and re-lay out the window. The BASIC Padding and Spacing properties of VBox, HBox, Frame and ScrollPane use these.
+
A container's padding (space inside its edge) and spacing (gap between children) in pixels. The getters return what the layout uses: the container's default while the field is WGT_GAP_DEFAULT (every widget starts so), and 0 for a size given in another unit. The setters clamp to 0..WGT_GAP_PIXELS_MAX (0 is no gap) and re-lay out the window. The BASIC Padding and Spacing properties of VBox, HBox, Frame, and ScrollPane use these.
Execute the full two-pass layout algorithm on a tree at an explicit size. Pass 1 (bottom-up): compute minimum sizes; a cap (maxW/maxH) wins over a larger minimum, so a widget whose content asks for more than its cap is given its cap and the rest of the space goes to its siblings. 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.
Remove the interface registered under name (wgtRegisterIface). A widget class made at run time -- a BASIC program's own UserControl -- leaves the registry this way when its owner is done with it, so the name can be registered again. Nothing happens when no interface has the name.
wgtUnregisterAppClasses
void wgtUnregisterAppClasses(int32_t appId);
-
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.
+
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.
Parameter Description
--------- -----------
appId App whose classes are released
@@ -5277,7 +5612,7 @@ static const ButtonApiT sApi = {
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
- wclsOnMouseEvent(w, action, button, x, y, shift) WGT_METHOD_ON_MOUSE_EVENT Every press, release and move (hover too)
+ wclsOnMouseEvent(w, action, button, x, y, shift) WGT_METHOD_ON_MOUSE_EVENT Every press, release, and move (hover too)
wclsOnKeyUp(w, keyCode, shift) WGT_METHOD_ON_KEY_UP Key released on the focused widget
wclsOnFocus(w) WGT_METHOD_ON_FOCUS Widget gained keyboard focus
The central dispatcher calls WGT_METHOD_ON_MOUSE_EVENT for every widget whose class has it, at exactly the points where it calls the universal mouse callbacks and on the same widget (the one under the cursor; inside a non-focusable WCLASS_NO_HIT_RECURSE container, a child with the method gets them itself, and a container with the method keeps them), and always before the callback. action is a WgtMouseActionE: WgtMouseDownE and WgtMouseUpE carry the 1-based button ordinal (1 left, 2 right, 3 middle), WgtMouseMoveE the MOUSE_* mask of the held buttons (0 is a hover move). x and y are content-relative (contentOffX / contentOffY subtracted), shift is the KEY_MOD_* Shift / Ctrl / Alt state. A class that captures the mouse on a press (sets sDragWidget in its WGT_METHOD_ON_MOUSE handler) also gets the moves while the button is held and the left release, wherever the pointer is. WGT_METHOD_ON_KEY_UP runs before onKeyUp with the same arguments, WGT_METHOD_ON_FOCUS before onFocus (the counterpart of ON_BLUR). If a class method destroys widgets, the callback after it is skipped. Classes without these methods see no change.
@@ -5346,7 +5681,7 @@ static const ButtonApiT sApi = {
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
-
A property is readable when getFn is not NULL and writable when setFn is not NULL -- the one rule for every interface (the form runtime, the designer and the property sheet all apply it). A dynamic interface (WgtIfaceT.dyn) puts WGT_DYN_ACCESS in getFn / setFn for "present"; they are never called. A FLOAT getter returns float and its setter takes float.
+
A property is readable when getFn is not NULL and writable when setFn is not NULL -- the one rule for every interface (the form runtime, the designer, and the property sheet all apply it). A dynamic interface (WgtIfaceT.dyn) puts WGT_DYN_ACCESS in getFn / setFn for "present"; they are never called. A FLOAT getter returns float and its setter takes float.
WgtMethodDescT
Describes an invokable method on a widget type.
Field Description
@@ -5384,7 +5719,7 @@ static const ButtonApiT sApi = {
WGT_SIG_STR_STR_BOOL_BOOL 24 bool fn(WidgetT *, const char *, const char *, bool, bool)
WGT_SIG_RET_INT_INT 25 int32_t fn(const WidgetT *, int32_t)
WGT_SIG_DYNAMIC 26 bool fn(WidgetT *, const WgtValueT *args, int32_t argc, WgtValueT *ret) (WgtDynMethodFnT): any arguments, as they come
-
wgtSigInfo describes each signature (WgtSigInfoT: argc, retType and argTypes[], WGT_SIG_MAX_ARGS = 5; NULL for WGT_SIG_DYNAMIC). wgtIfaceCallMethod is the one place that calls through these signatures.
+
wgtSigInfo describes each signature (WgtSigInfoT: argc, retType, and argTypes[], WGT_SIG_MAX_ARGS = 5; NULL for WGT_SIG_DYNAMIC). wgtIfaceCallMethod is the one place that calls through these signatures.
WgtEventDescT
Describes an event beyond the standard common set (Click, DblClick, Change, GotFocus, LostFocus, the key and mouse events ...). The widget fires it with wgtFireEvent.
One property value, method argument or result, or event argument. WGT_IFACE_NONE (5) is "no value" (a void method's result). A STRING value never owns its text: an argument's text belongs to the caller for the duration of the call; a returned one (a property read, a method result) belongs to the widget and stays valid until the next call on that widget or until the widget changes or is destroyed -- copy it to keep it. wgtValueInt, wgtValueBool, wgtValueFloat, wgtValueDouble and wgtValueStr (NULL becomes "") build values. FLOAT is BASIC's Single, DOUBLE its Double: a DOUBLE property or argument keeps every digit (a value through FLOAT is rounded to a float by a static widget's getter and setter, and the form runtime shows it as a Single); as text a DOUBLE has 15 significant digits.
+
One property value, method argument or result, or event argument. WGT_IFACE_NONE (5) is "no value" (a void method's result). A STRING value never owns its text: an argument's text belongs to the caller for the duration of the call; a returned one (a property read, a method result) belongs to the widget and stays valid until the next call on that widget or until the widget changes or is destroyed -- copy it to keep it. wgtValueInt, wgtValueBool, wgtValueFloat, wgtValueDouble, and wgtValueStr (NULL becomes "") build values. FLOAT is BASIC's Single, DOUBLE its Double: a DOUBLE property or argument keeps every digit (a value through FLOAT is rounded to a float by a static widget's getter and setter, and the form runtime shows it as a Single); as text a DOUBLE has 15 significant digits.
The .frm and property-sheet boolean rule: "True" or "-1" is true, "False" or "0" false (case-insensitive), anything else (and NULL) defaultValue.
Generic Widget Access
-
One implementation of "create, read, write and call by name" for every widget, static or dynamic. The form runtime, the IDE designer and C programs all use it; nothing else switches on WGT_SIG_* or WgtCreateSigE.
+
One implementation of "create, read, write, and call by name" for every widget, static or dynamic. The form runtime, the IDE designer, and C programs all use it; nothing else switches on WGT_SIG_* or WgtCreateSigE.
Fire event name on w. The universal events go to their WidgetT callback with their arguments taken from args (a missing one is 0): Click -> onClick, DblClick -> onDblClick, Change -> onChange, GotFocus -> onFocus, LostFocus -> onBlur, KeyPress (KeyAscii) -> onKeyPress, KeyDown / KeyUp (KeyCode, Shift) -> onKeyDown / onKeyUp, MouseDown / MouseUp / MouseMove (Button, X, Y, or the VB form Button, Shift, X, Y) -> onMouseDown / onMouseUp / onMouseMove (which take no Shift), Scroll (Delta) -> onScroll (names case-insensitive). Every other name goes to w->onEvent; the universal names never do, so a host that sets both sees each event once. Validate is not mapped (onValidate answers a question; fire it directly). onEvent may write back into args (ByRef parameters; the form runtime writes every ByRef parameter back, a String as text that stays valid until the next event). Returns false when a handler destroyed widgets -- do not touch w then.
-
ListView (ColumnClick), TextArea (GutterClick) and HelpView (LinkClick) fire their own events this way after their C callbacks (wgtListViewSetHeaderClickCallback, wgtTextAreaSetGutterClick, wgtHelpViewSetLinkCallback), which stay for C programs; the event is skipped when the C callback destroyed widgets. A TextArea's gutter takes clicks when either is set.
+
ListView (ColumnClick), TextArea (GutterClick), and HelpView (LinkClick) fire their own events this way after their C callbacks (wgtListViewSetHeaderClickCallback, wgtTextAreaSetGutterClick, wgtHelpViewSetLinkCallback), which stay for C programs; the event is skipped when the C callback destroyed widgets. A TextArea's gutter takes clicks when either is set.
Property Data Type Constants
Define Value Description
------ ----- -----------
@@ -5647,14 +5982,14 @@ int32_t pick = dvxButtonBox(ctx, "Copy", "The disk is full."
Display a modal color picker: a palette of 48 basic colors, Red, Green and Blue sliders each paired with a number box (0-255), and a preview swatch. Clicking a basic color sets all three channels; the basic color matching the current color is framed. From the keyboard, Tab moves between the sliders and number boxes (the Red slider has the focus first), the arrow keys, Page Up/Down, Home and End move a slider, and Enter chooses OK. Works at every color depth; on 8-bit desktops the swatches show the nearest palette colors, but the chosen value is exact.
+
Display a modal color picker: a palette of 48 basic colors, Red, Green, and Blue sliders each paired with a number box (0-255), and a preview swatch. Clicking a basic color sets all three channels; the basic color matching the current color is framed. From the keyboard, Tab moves between the sliders and number boxes (the Red slider has the focus first), the arrow keys, Page Up/Down, Home, and End move a slider, and Enter chooses OK. Works at every color depth; on 8-bit desktops the swatches show the nearest palette colors, but the chosen value is exact.
Parameter Description
--------- -----------
ctx Application context
title Window title (NULL: "Color")
rgbInOut In: the starting color; out: the chosen color. Both 0x00RRGGBB (DVX BASIC's colours are &HBBGGRR; wgtBasicColor converts between the two)
Returns: true if OK was chosen, false if cancelled or closed (then *rgbInOut is unchanged).
-
Macros for the 0x00RRGGBB layout: DVX_RGB(r, g, b) builds a color from three 0-255 channels, and DVX_RGB_RED(rgb), DVX_RGB_GREEN(rgb) and DVX_RGB_BLUE(rgb) take one apart.
+
Macros for the 0x00RRGGBB layout: DVX_RGB(r, g, b) builds a color from three 0-255 channels, and DVX_RGB_RED(rgb), DVX_RGB_GREEN(rgb), and DVX_RGB_BLUE(rgb) take one apart.
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.
+
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.
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.
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.
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.
@@ -5691,6 +6026,22 @@ if (dvxDialogRun(ctx, dlg)) {
DVX_SAVE_YES 1 Save, then proceed
DVX_SAVE_NO 2 Discard, then proceed
DVX_SAVE_CANCEL 3 Abort the operation
Display a modal font picker: lists of the font families (dvxText.h), the four styles (Regular, Bold, Italic, and Bold Italic), and the sizes the chosen family comes in for the chosen style; Underline and Strikeout boxes; and a sample drawn in the chosen font as it changes. Choosing another family or style keeps the size nearest the one chosen. The labels' underlined letters move to the lists from the keyboard; Enter chooses OK.
+
Parameter Description
+ --------- -----------
+ ctx Application context
+ title Window title (NULL: "Font")
+ flags DVX_FONT_DLG_FIXED: offer only fixed-pitch families (for a text editor or a terminal)
+ choiceInOut In: the starting font (a family there is none of starts on "System"); out: the chosen one
+
DvxFontChoiceT holds family (FONT_FAMILY_MAX characters with the NUL), size (pixels), and style (FONT_STYLE_* bits, the lines included) -- what wgtSetFont and dvxFontOpen take.
+
Returns: true if OK was chosen, false if cancelled or closed (then *choiceInOut is unchanged).
Display a canonical "Save changes?" dialog with Yes/No/Cancel buttons and an appropriate question icon.
@@ -5715,6 +6066,7 @@ if (dvxDialogRun(ctx, dlg)) {
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_FONT 4 A .DFN font the program carries (added for it when it loads; dvxText.h)
DVX_RES_MAGIC 0x52585644 Footer signature ("DVXR" little-endian)
DVX_RES_NAME_MAX 32 Max resource name length including null
DVX_RES_ONLY_MAGIC "DVXRESONLY" First bytes (NUL included, DVX_RES_ONLY_MAGIC_LEN) of a file that holds only resources
@@ -5800,7 +6152,7 @@ if (dvxDialogRun(ctx, dlg)) {
dvxPrefs.h -- INI Preferences System
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.
-
BASIC programs reach the same functions through prefs.bas (dvxPrefsBas.c in libdvx): PrefsLoad, PrefsCreate, PrefsGet$, PrefsGetInt, PrefsGetBool, PrefsSet, PrefsSetInt, PrefsSetBool, PrefsRemove, PrefsKeyName$ (prefsKeyName, "" past the last key), PrefsSave, PrefsSaveAs and PrefsClose, on handles (1, 2, ...; 0 = none) that belong to the program that made them and are closed, unsaved, when it ends. True is -1, False 0. PrefsDvxIni$ is DVX_INI_PATH, PrefsSetVideoMode is prefsSetVideoMode, and PrefsVideoWidth / PrefsVideoHeight / PrefsVideoBpp are the three values of prefsGetVideoMode (DVX_DEFAULT_VIDEO_* for a missing key or a bad handle). The choice tables below are PREFS_CHOICE_ACCEL (0), PREFS_CHOICE_WALLPAPER (1) and PREFS_CHOICE_WHEEL (2) there: PrefsChoiceCount, PrefsChoiceLabel$, PrefsChoiceName$ (iniName), PrefsChoiceValue, PrefsChoiceIndex, PrefsChoiceIndexOfValue, PrefsChoiceDefault (the index of the choice MOUSE_ACCEL_DEFAULT / WALLPAPER_MODE_DEFAULT / MOUSE_WHEEL_DIR_DEFAULT names; -1 for an unknown table), and PrefsGetChoice(handle, section, key, table) with the table's default. PrefsGetChoiceFrom(handle, section, key, choiceNames, defaultName) is prefsGetChoice on a table the program gives as "|"-separated names (each choice's iniName and label is its name, its value its index).
+
BASIC programs reach the same functions through prefs.bas (dvxPrefsBas.c in libdvx): PrefsLoad, PrefsCreate, PrefsGet$, PrefsGetInt, PrefsGetBool, PrefsSet, PrefsSetInt, PrefsSetBool, PrefsRemove, PrefsKeyName$ (prefsKeyName, "" past the last key), PrefsSave, PrefsSaveAs, and PrefsClose, on handles (1, 2, ...; 0 = none) that belong to the program that made them and are closed, unsaved, when it ends. True is -1, False 0. PrefsDvxIni$ is DVX_INI_PATH, PrefsSetVideoMode is prefsSetVideoMode, and PrefsVideoWidth / PrefsVideoHeight / PrefsVideoBpp are the three values of prefsGetVideoMode (DVX_DEFAULT_VIDEO_* for a missing key or a bad handle). The choice tables below are PREFS_CHOICE_ACCEL (0), PREFS_CHOICE_WALLPAPER (1), and PREFS_CHOICE_WHEEL (2) there: PrefsChoiceCount, PrefsChoiceLabel$, PrefsChoiceName$ (iniName), PrefsChoiceValue, PrefsChoiceIndex, PrefsChoiceIndexOfValue, PrefsChoiceDefault (the index of the choice MOUSE_ACCEL_DEFAULT / WALLPAPER_MODE_DEFAULT / MOUSE_WHEEL_DIR_DEFAULT names; -1 for an unknown table), and PrefsGetChoice(handle, section, key, table) with the table's default. PrefsGetChoiceFrom(handle, section, key, choiceNames, defaultName) is prefsGetChoice on a table the program gives as "|"-separated names (each choice's iniName and label is its name, its value its index).
PrefsHandleT *prefsBasHandle(int32_t handle);
The settings behind a prefs.bas handle, NULL for 0 or a handle that is not open. Other libraries' BASIC entry points that take a prefs.bas handle (system.bas, joystick.bas, net.bas, print.bas, sound.bas) resolve it with this; libdvx is loaded first, so the symbol is always there.
Constants
@@ -5824,8 +6176,8 @@ if (dvxDialogRun(ctx, dlg)) {
Lines of any length are read without truncation; LF and CR LF endings are accepted.
"[name]" starts a section; "key = value" is a key (blanks around key and value are trimmed). Keys before the first section belong to section "".
Section and key names match case-insensitively. With duplicates, the first one wins.
-
Blank lines, ';' and '#' comments and lines that are neither a section nor a key are kept.
-
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.
+
Blank lines, ';' and '#' comments, and lines that are neither a section nor a key are kept.
+
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.
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.
+
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.
Parameter Description
--------- -----------
h Preferences handle
@@ -5886,7 +6238,7 @@ if (dvxDialogRun(ctx, dlg)) {
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.
+
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.
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.
+
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.
typedef struct {
const char *label; // user-facing text (dropdown item)
const char *iniName; // spelling stored in the INI
@@ -5922,8 +6274,8 @@ extern const PrefsChoiceT prefsWheelDirChoices[PREFS_WHEEL_DIR_CHOICE_COUNT];
-
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.
-
Labels are the capitalized names ("Off", "Stretch", "Reversed", ...). The accel table is in dropdown order. dvxMouseFromPrefs and dvxWallpaperFromPrefs (dvxApp.h) read [mouse] wheel, [mouse] acceleration and [desktop] mode through prefsGetChoice, and the Control Panel builds its dropdowns from the same tables.
+
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.
+
Labels are the capitalized names ("Off", "Stretch", "Reversed", ...). The accel table is in dropdown order. dvxMouseFromPrefs and dvxWallpaperFromPrefs (dvxApp.h) read [mouse] wheel, [mouse] acceleration, and [desktop] mode through prefsGetChoice, and the Control Panel builds its dropdowns from the same tables.
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.
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.
+
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.
widgetClosePopup
void widgetClosePopup(void);
-
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.
+
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.
widgetIsEnabledChain
bool widgetIsEnabledChain(const WidgetT *w);
True when w and every ancestor are enabled. Disabling a container disables input to its whole subtree: mouse and keyboard dispatch use this test.
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.
Which scrollbars content totalW x totalH pixels needs in a viewport of *innerW x *innerH (the area inside the widget's border). A vertical bar narrows the viewport, which may make the content too wide; a horizontal bar shortens it, which may make the content too tall; both cases are followed through. *innerW and *innerH come back less the bars (WGT_SB_W each). For rows of a fixed height pass rows * height: the content is too tall exactly when there are more rows than whole rows fit. ListBox, ListView, TreeView, ScrollPane and DBGrid decide their bars with it; BASIC's ScrollBarsNeeded is the same rule.
+
Which scrollbars content totalW x totalH pixels needs in a viewport of *innerW x *innerH (the area inside the widget's border). A vertical bar narrows the viewport, which may make the content too wide; a horizontal bar shortens it, which may make the content too tall; both cases are followed through. *innerW and *innerH come back less the bars (WGT_SB_W each). For rows of a fixed height pass rows * height: the content is too tall exactly when there are more rows than whole rows fit. ListBox, ListView, TreeView, ScrollPane, and DBGrid decide their bars with it; BASIC's ScrollBarsNeeded is the same rule.
A display onto w's window content buffer, clipped to w and every ancestor, for a widget's quick repaint (WGT_METHOD_QUICK_REPAINT) outside the paint walk: the widget draws the rows that changed and reports them, and only those are flushed. Returns false when w cannot be painted that way now -- hidden, clipped away entirely, or inside a scroll container, whose viewport is not its rect (w is then invalidated for the normal paint) -- and the caller drops what it meant to draw. The Terminal and RichEdit widgets use it.
Pressable Button Helpers
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.
Append a formatted line to dvx.log. Implemented in dvx.exe and exported to every DXE via the loader.
platformInit
void platformInit(void);
-
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).
+
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).
platformYield
void platformYield(void);
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.
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.
+
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.
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.
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.
-
Warning: 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.
-
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).
+
Warning: 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.
+
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).
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.
+
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.
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.
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.
+
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.
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.
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).
+
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).
platformAltScanToChar
char platformAltScanToChar(int32_t scancode);
Translate an Alt+key scancode to its corresponding ASCII character. Returns 0 for non-printable scancodes.
Validate a name for a new file or folder in folder dir (NULL or "" for the current one) against the rules of its disk. On DOS: platformValidateLongName where platformLongNames(dir), else platformValidateShortName, and dir with the name must stay under DVX_MAX_PATH. The host backend refuses an empty name, . and .., separators and control characters, and ignores dir. Returns NULL if valid, or a human-readable error string. The file dialog, the File Manager (fileOpsValidateName) and the IDE's project rename all use it.
+
Validate a name for a new file or folder in folder dir (NULL or "" for the current one) against the rules of its disk. On DOS: platformValidateLongName where platformLongNames(dir), else platformValidateShortName, and dir with the name must stay under DVX_MAX_PATH. The host backend refuses an empty name, . and .., separators, and control characters, and ignores dir. Returns NULL if valid, or a human-readable error string. The file dialog, the File Manager (fileOpsValidateName), and the IDE's project rename all use it.
The DOS C library uses the long-name calls (INT 21h 71xxh) by itself whenever a driver provides them (Windows, DOSLFN), so open, fopen, rename, mkdir, stat and the directory readers all take and return long names; these functions cover what the C library does not.
+
The DOS C library uses the long-name calls (INT 21h 71xxh) by itself whenever a driver provides them (Windows, DOSLFN), so open, fopen, rename, mkdir, stat, and the directory readers all take and return long names; these functions cover what the C library does not.
platformLongNames is true when the drive holding path (NULL or "": the current drive) takes long file names. On DOS that is a loaded long-name driver covering that drive; elsewhere always.
-
platformValidateLongName and platformValidateShortName are the two DOS name rules by themselves, whatever the disk takes; each returns NULL or why the name is not valid. A long name has 1 to PLATFORM_LONG_NAME_MAX characters and may hold spaces, several dots and + , ; = [ ]; it may not hold a control character or \ / : * ? " < > |, and may not end with a dot or a space (DOS drops them). An 8.3 name has 1 to 8 printable ASCII characters, then at most one dot and up to 3 more, none of them a space or " * + , / : ; < = > ? [ \ ] |. Neither may be . or .., or a device name (CON, PRN, AUX, NUL, CLOCK$, COM1 to COM9, LPT1 to LPT9) before its first dot. They are shared code, so the host tests check the DOS rules too.
+
platformValidateLongName and platformValidateShortName are the two DOS name rules by themselves, whatever the disk takes; each returns NULL or why the name is not valid. A long name has 1 to PLATFORM_LONG_NAME_MAX characters and may hold spaces, several dots, and + , ; = [ ]; it may not hold a control character or \ / : * ? " < > |, and may not end with a dot or a space (DOS drops them). An 8.3 name has 1 to 8 printable ASCII characters, then at most one dot and up to 3 more, none of them a space or " * + , / : ; < = > ? [ \ ] |. Neither may be . or .., or a device name (CON, PRN, AUX, NUL, CLOCK$, COM1 to COM9, LPT1 to LPT9) before its first dot. They are shared code, so the host tests check the DOS rules too.
platformLongNameChar is true when a long name may hold character c; a file transfer uses it to turn the characters a sender's name cannot keep into _.
platformShortPath writes the short (8.3) form of an existing path into out, for what reads names before a long-name driver loads (AUTOEXEC.BAT) or cannot take blanks (an old program's command line). On a drive with long names DOS gives every part's short name (INT 21h AX=7160h CL=1); elsewhere, and when DOS cannot, path itself is copied (platformPathTidy: "C:/DVX" gives "C:\\DVX"). False when out is too small.
DVX paths follow DOS rules on every platform: '/' and '\\' are the same separator, so a path a program writes with '/' works on DOS exactly as with '\\', and names compare without case (DOS and the host build also share INI files, projects and program paths). Every path DVX shows uses the native separator, DVX_PATH_SEP ('\\' on DOS). DVX's own path code goes through these functions; a program that splits, compares or joins paths should too, never testing for one separator by hand.
+
DVX paths follow DOS rules on every platform: '/' and '\\' are the same separator, so a path a program writes with '/' works on DOS exactly as with '\\', and names compare without case (DOS and the host build also share INI files, projects, and program paths). Every path DVX shows uses the native separator, DVX_PATH_SEP ('\\' on DOS). DVX's own path code goes through these functions; a program that splits, compares, or joins paths should too, never testing for one separator by hand.
platformPathIsSep: true for '/' and '\\'.
platformPathNative: rewrites path's separators as DVX_PATH_SEP, in place, before it is shown (the DOS C library's getcwd and realpath give "c:/dvx"). Returns path.
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).
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.
+
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.
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.
+
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.
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:
+
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:
Log platform-specific crash diagnostics. Called internally from the crash handler and exposed for manual invocation.
platformOutOfMemory
void platformOutOfMemory(size_t bytes);
-
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.
+
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.
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.
+
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.
Loaded as: bin/libs/kpunch/audio/audio.lib, drivers in bin/libs/kpunch/audio/drivers/*.drv
Contexts and the Device
@@ -6523,19 +6878,19 @@ if (voice < 0) {
}
...
audioClose(audio); // or let the program end
-
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.
+
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.
None of the functions may be called from an interrupt (a timer-service callback).
Samples and Voices
-
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.
+
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.
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
-
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.
-
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.
+
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.
+
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.
Tones
-
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.
+
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.
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.
-
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.
-
audioFmAlloc, audioFmFree, audioFmWrite and audioFmRawWrite may also be called from a timer callback (platformTimerAdd); music.lib plays MIDI that way.
+
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.
+
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.
+
audioFmAlloc, audioFmFree, audioFmWrite, and audioFmRawWrite may also be called from a timer callback (platformTimerAdd); music.lib plays MIDI that way.
MIDI Port
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.
Volume and the Voice Limit
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.
Files
-
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.
+
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.
The Mixer
-
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).
+
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).
Settings
DVX.INI section [audio]:
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
+ 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
@@ -6582,7 +6937,7 @@ audioFmFree(audio, v); // key off, back to everyone
A settings screen reads and writes the keys it offers through the library, so the key names live in one place (audio.h, Settings; prefs is a copy of DVX.INI the caller loads and saves): audioSettingDriver(prefs) (AUDIO_DRIVER_AUTO when missing), audioSettingMidi(prefs) (an index in audioMidiChoices, auto when missing or unknown), audioSettingVolume(prefs) (percent, clamped to 0..100, 100 when missing), and audioSettingsStore(prefs, driver, midi, volume) writes all three (an empty driver stores auto, a midi index out of range stores auto). audioMidiChoices[AUDIO_MIDI_CHOICE_COUNT] holds the midi choices in list order, each with its label for people and the name stored (a PrefsChoiceT, dvxPrefs.h). audioPercentToVolume(percent) turns a percent into 0..AUDIO_VOLUME_MAX for audioSetMasterVolume. The device reads the driver and volume through the same functions when it starts. The Control Panel's Sounds page uses all of these.
Desktop Sounds
@@ -6601,7 +6956,7 @@ audioFmFree(audio, v); // key off, back to everyoneDrivers
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.
+ 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.
@@ -6609,44 +6964,44 @@ audioFmFree(audio, v); // key off, back to everyoneaudioDriverNames(names, max) lists the short names of the sound drivers installed, the one tried first first (for a settings screen; AUDIO_DRIVERS_MAX is the most DVX's own callers ask for).
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.
Writing a Driver
-
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.
+
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.
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.
BASIC
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.
-
For a settings program (the Control Panel's Sounds page): SoundEventCount, SoundEventLabel$(eventIdx), SoundEventKey$(eventIdx) (audioDeskLabel / audioDeskKey; events numbered from 0 in DvxSoundE order); SoundEventFile$(prefs, eventIdx) and SoundSetEventFile(prefs, eventIdx, fileName) (audioDeskFile / audioDeskSetFile); SoundEventsReload (audioDeskReload); SoundTestFile(fileName) (audioDeskPlayFile: a voice number, or 0 with SoundError$); SoundFolder$ (AUDIO_SOUNDS_DIR); SoundMasterVolume and SoundSetMasterVolume(percent) (audioGetMasterVolume / audioSetMasterVolume in percent); SoundDriverCount (reads audioDriverNames, at most AUDIO_DRIVERS_MAX) and SoundDriverName$(index); SoundDriverAuto$ (AUDIO_DRIVER_AUTO); SoundMidiCount and SoundMidiLabel$(index) (audioMidiChoices); SoundSettingDriver$(prefs), SoundSettingMidi(prefs), SoundSettingVolume(prefs) and SoundSettingsStore(prefs, driver, midi, volume) (the audioSetting functions). prefs is a prefs.bas handle (prefsBasHandle, dvxPrefs.h); a handle that is not open reads the defaults and stores nothing.
-
The rest of audio.h, in percent: PlayTriangle (audioTone with AUDIO_WAVE_TRIANGLE); SoundGetVolume (audioGetVolume); SoundCaps(field) (audioGetInfo: "dac", "synth", "tone", "running" True / False; "voices" = voiceLimit, "rate", "bits", "channels"; any case, 0 for another field); SoundPlayEvent(eventIdx, wait) (dvxPlaySound / dvxPlaySoundWait, so the hook audioDeskPlay plays it; CONSTs SOUND_EVENT_* in DvxSoundE order). Samples: SoundLoad(fileName) (audioSampleLoad) and SoundCreate(pcm AS BINARY STRING, rate, bits, channels) (audioSampleCreate; frames = bytes / frame size, a trailing partial frame ignored) return a sound number; SoundFree (audioSampleFree), SoundLength (audioSampleInfo, in milliseconds), SoundPlay(sound, volume, pan, loopIt) (audioPlay; pan -100..100 scaled to AUDIO_PAN_LEFT..AUDIO_PAN_RIGHT). Voices: SoundSetVolume, SoundSetPan, SoundSetPitch(voice, percent) (25..400 percent of AUDIO_PITCH_NORMAL, clamped) and SoundSetFrequency(voice, hz) (the audioVoiceSet functions; the result is kept for SoundError$). Streams (push only): SoundStreamOpen(rate, bits, channels, bufferMs) (audioStreamOpen without a fill function), SoundStreamWrite(stream, pcm AS BINARY STRING) (audioStreamWrite of the whole frames; returns the bytes taken), SoundStreamSpace / SoundStreamQueued (frames), SoundStreamVoice, SoundStreamClose. FM: FmInfo$(field) (audioFmGetInfo: "description", "voices", "free", "stereo", "owned" as text), FmAlloc, FmWrite(voice, reg, value) (True / False), FmFree, FmRawAcquire, FmRawWrite(reg, value), FmRawRelease, with CONSTs FM_* for the AUDIO_FM_* values and FM_BANK_REGS (AUDIO_FM_BANK_REGS). The MIDI port, declared in music.bas: MidiPortInfo$(field) (audioMidiGetInfo: "description", "present", "owned"), MidiAcquire, MidiPut(byteValue) (False on AUDIO_ERR_BUSY: send it again), MidiRelease. CONSTs SOUND_PAN_*, SOUND_PITCH_* (percent), SOUND_RATE_* (AUDIO_RATE_*) and SOUND_STREAM_MS_* (AUDIO_STREAM_MS_*).
+
For a settings program (the Control Panel's Sounds page): SoundEventCount, SoundEventLabel$(eventIdx), SoundEventKey$(eventIdx) (audioDeskLabel / audioDeskKey; events numbered from 0 in DvxSoundE order); SoundEventFile$(prefs, eventIdx) and SoundSetEventFile(prefs, eventIdx, fileName) (audioDeskFile / audioDeskSetFile); SoundEventsReload (audioDeskReload); SoundTestFile(fileName) (audioDeskPlayFile: a voice number, or 0 with SoundError$); SoundFolder$ (AUDIO_SOUNDS_DIR); SoundMasterVolume and SoundSetMasterVolume(percent) (audioGetMasterVolume / audioSetMasterVolume in percent); SoundDriverCount (reads audioDriverNames, at most AUDIO_DRIVERS_MAX) and SoundDriverName$(index); SoundDriverAuto$ (AUDIO_DRIVER_AUTO); SoundMidiCount and SoundMidiLabel$(index) (audioMidiChoices); SoundSettingDriver$(prefs), SoundSettingMidi(prefs), SoundSettingVolume(prefs), and SoundSettingsStore(prefs, driver, midi, volume) (the audioSetting functions). prefs is a prefs.bas handle (prefsBasHandle, dvxPrefs.h); a handle that is not open reads the defaults and stores nothing.
+
The rest of audio.h, in percent: PlayTriangle (audioTone with AUDIO_WAVE_TRIANGLE); SoundGetVolume (audioGetVolume); SoundCaps(field) (audioGetInfo: "dac", "synth", "tone", "running" True / False; "voices" = voiceLimit, "rate", "bits", "channels"; any case, 0 for another field); SoundPlayEvent(eventIdx, wait) (dvxPlaySound / dvxPlaySoundWait, so the hook audioDeskPlay plays it; CONSTs SOUND_EVENT_* in DvxSoundE order). Samples: SoundLoad(fileName) (audioSampleLoad) and SoundCreate(pcm AS BINARY STRING, rate, bits, channels) (audioSampleCreate; frames = bytes / frame size, a trailing partial frame ignored) return a sound number; SoundFree (audioSampleFree), SoundLength (audioSampleInfo, in milliseconds), SoundPlay(sound, volume, pan, loopIt) (audioPlay; pan -100..100 scaled to AUDIO_PAN_LEFT..AUDIO_PAN_RIGHT). Voices: SoundSetVolume, SoundSetPan, SoundSetPitch(voice, percent) (25..400 percent of AUDIO_PITCH_NORMAL, clamped), and SoundSetFrequency(voice, hz) (the audioVoiceSet functions; the result is kept for SoundError$). Streams (push only): SoundStreamOpen(rate, bits, channels, bufferMs) (audioStreamOpen without a fill function), SoundStreamWrite(stream, pcm AS BINARY STRING) (audioStreamWrite of the whole frames; returns the bytes taken), SoundStreamSpace / SoundStreamQueued (frames), SoundStreamVoice, SoundStreamClose. FM: FmInfo$(field) (audioFmGetInfo: "description", "voices", "free", "stereo", "owned" as text), FmAlloc, FmWrite(voice, reg, value) (True / False), FmFree, FmRawAcquire, FmRawWrite(reg, value), FmRawRelease, with CONSTs FM_* for the AUDIO_FM_* values and FM_BANK_REGS (AUDIO_FM_BANK_REGS). The MIDI port, declared in music.bas: MidiPortInfo$(field) (audioMidiGetInfo: "description", "present", "owned"), MidiAcquire, MidiPut(byteValue) (False on AUDIO_ERR_BUSY: send it again), MidiRelease. CONSTs SOUND_PAN_*, SOUND_PITCH_* (percent), SOUND_RATE_* (AUDIO_RATE_*), and SOUND_STREAM_MS_* (AUDIO_STREAM_MS_*).
Sound and stream numbers are 1..64 per kind, valid only for the program that got them; audioClose of a BASIC context forgets its numbers (audioBasForget) as it frees the samples and streams. BINARY STRING parameters arrive as a pointer and a byte count (two C parameters; see the DECLARE LIBRARY rules in the BASIC reference).
BASIC Development Library
-
basdev.lib is the DVX BASIC compiler and what a development tool builds on it: the build pipeline (basBuild.h), the compile-time Control.Member validator (basValidate.h), the debugger's view of a paused program (basDebug.h) and the SUB / FUNCTION scanner (basProcs.h). The DVX BASIC IDE uses it, and BASIC programs reach it through compiler.bas and debug.bas (basDevBas.c holds their entry points). It also carries the STUB and noicon resources every build inside DVX uses (BAS_BUILD_STUB_CARRIER). It needs basrt.lib, which holds the run host (basRunHost.h) the IDE, the standalone stub and debug.bas share.
+
basdev.lib is the DVX BASIC compiler and what a development tool builds on it: the build pipeline (basBuild.h), the compile-time Control.Member validator (basValidate.h), the debugger's view of a paused program (basDebug.h), and the SUB / FUNCTION scanner (basProcs.h). The DVX BASIC IDE uses it, and BASIC programs reach it through compiler.bas and debug.bas (basDevBas.c holds their entry points). It also carries the STUB and noicon resources every build inside DVX uses (BAS_BUILD_STUB_CARRIER). It needs basrt.lib, which holds the run host (basRunHost.h) the IDE, the standalone stub, and debug.bas share.
Function Description
-------- -----------
basBuildAddSource Append one file to a BasProjectSourceT the way a project is built: a module in BEGINMODULE, a class module's code in BEGINCLASS, a UserControl's code in BEGINCLASS ... USERCONTROL (its header is kept in ctlTexts), a form's code section in BEGINFORM with its controls (the .frm text is kept in frmTexts), a lone program unwrapped. Records the file in the source map.
basBuildConcatProject Read every file an open .dbp names (modules, class modules, UserControls, forms) into a BasProjectSourceT through basBuildAddSource.
- basBuildCompile Compile source into a BasModuleT with OPTION EXPLICIT and an optional BasCtrlValidatorT. A BasCompileErrorT says why it failed: BAS_COMPILE_ERROR (line, column, message), BAS_COMPILE_NO_MEMORY or BAS_COMPILE_NO_MODULE.
+ basBuildCompile Compile source into a BasModuleT with OPTION EXPLICIT and an optional BasCtrlValidatorT. A BasCompileErrorT says why it failed: BAS_COMPILE_ERROR (line, column, message), BAS_COMPILE_NO_MEMORY, or BAS_COMPILE_NO_MODULE.
basBuildErrorText A compiler message without its "Line N, Col M: " prefix.
basBuildMapLine Line of the concatenated source to file and file line.
basBuildMapFileLine File and file line to the line of the concatenated source (names compared as DOS does: any case, / and \ alike).
basBuildSpecFromProject Fill a BasBuildSpecT's project metadata from an open .dbp.
- basBuildApp Build an .app from a module, the .frm texts and the .ctl headers (ctlSources). A NULL selfPath takes the stub from BAS_BUILD_STUB_CARRIER (this library).
- basBuildControl Build a control file (.wgt, resources only) from a control project's module and .ctl headers: the module, each header as CTLn with its toolbox icon24 and name resources, the project's metadata and help file. The IDE's Make Control and CompBuildApp of a control project.
+ basBuildApp Build an .app from a module, the .frm texts, and the .ctl headers (ctlSources). A NULL selfPath takes the stub from BAS_BUILD_STUB_CARRIER (this library).
+ basBuildControl Build a control file (.wgt, resources only) from a control project's module and .ctl headers: the module, each header as CTLn with its toolbox icon24 and name resources, the project's metadata, and help file. The IDE's Make Control and CompBuildApp of a control project.
basBuildIsControlProject True when an open .dbp is a control project ([Project] Type = Control).
basBuildWrapForPath The kind of a project file by its extension.
Validating Control Members (basValidate.h)
-
A BasValidatorT (zeroed) collects the forms, controls and menu items of the .frm texts basValidateScanFrm parses; its onCode hook also receives each form's code section, so one parse serves both. basValidateCallbacks fills the BasCtrlValidatorT the compiler takes; pass &validator.compiler as basBuildCompile's liveCompiler so a control name resolves on the form whose code is compiled. Member names are checked against the loaded widget interfaces (WgtIfaceT) and the form runtime's own property tables. badTypes lists controls whose type no loaded widget library registers (BAS_VALIDATE_BAD_TYPE_FMT formats one). The command-line compiler does not validate: no widget library is loaded there.
+
A BasValidatorT (zeroed) collects the forms, controls, and menu items of the .frm texts basValidateScanFrm parses; its onCode hook also receives each form's code section, so one parse serves both. basValidateCallbacks fills the BasCtrlValidatorT the compiler takes; pass &validator.compiler as basBuildCompile's liveCompiler so a control name resolves on the form whose code is compiled. Member names are checked against the loaded widget interfaces (WgtIfaceT) and the form runtime's own property tables. badTypes lists controls whose type no loaded widget library registers (BAS_VALIDATE_BAD_TYPE_FMT formats one). The command-line compiler does not validate: no widget library is loaded there.
Debugging (basDebug.h)
Every function takes the paused program's VM (or NULL when there is none).
Function Description
-------- -----------
basDbgCurProcIdx The compiled procedure the paused VM is in (-1: module level).
basDbgProcNameForPc Procedure name holding a code address (BAS_DBG_MODULE_FRAME before every procedure).
- basDbgVarVisible Whether a debug variable is in scope where the VM is paused (QBasic rules: SHARED, STATIC, form and member variables).
+ basDbgVarVisible Whether a debug variable is in scope where the VM is paused (QBasic rules: SHARED, STATIC, form, and member variables).
basDbgFindVar The visible debug variable of a name.
basDbgVarSlot The live value slot of a debug variable.
- basDbgResolvePath The live slot of var, var(i, j), var.field or var(i).field (a TYPE field or a class member).
+ basDbgResolvePath The live slot of var, var(i, j), var.field, or var(i).field (a TYPE field or a class member).
basDbgFormatValue Display text of a value (strings quoted, #dates#, array bounds, class names, Collection item counts).
basDbgLocals The visible variables as rows (name, type, value) through a BasDbgRowFnT.
basDbgCallStack The call stack as rows (procedure, line).
@@ -6657,20 +7012,20 @@ audioFmFree(audio, v); // key off, back to everyone
Procedures (basProcs.h)
-
basProcDeclAt, basProcSkipToEnd and basProcNameFromDecl find the SUB / FUNCTION blocks of source text without compiling it (BAS_PROC_KW_END_SUB / BAS_PROC_KW_END_FUNCTION end them). The IDE's one-procedure-at-a-time editor and CompProcList$ use them.
+
basProcDeclAt, basProcSkipToEnd, and basProcNameFromDecl find the SUB / FUNCTION blocks of source text without compiling it (BAS_PROC_KW_END_SUB / BAS_PROC_KW_END_FUNCTION end them). The IDE's one-procedure-at-a-time editor and CompProcList$ use them.
Source Text (basSource.h)
-
Source text read with the compiler's lexer, line by line, on text that need not compile: basSourceTokens reports every token with its kind (BasSrcKindE: keyword, identifier, string, number, date, symbol, comment, error), line, column and source span; basSourceInCode tells whether an offset is code (not in a string literal or a comment); basSourceRename renames a form or control (oldName followed by '.' or '_' as a whole word), optionally only in code; basSourceWithEvents and basSourceClassEvents report the WithEvents variables and the Events of a class module. The IDE's rename (ideRenameInCode, also after the Menu Editor renames items) and its Object / Event lists use them; compiler.bas has CompTokens$, CompInCode, CompRenameInCode$, CompWithEvents$ and CompClassEvents$.
+
Source text read with the compiler's lexer, line by line, on text that need not compile: basSourceTokens reports every token with its kind (BasSrcKindE: keyword, identifier, string, number, date, symbol, comment, error), line, column, and source span; basSourceInCode tells whether an offset is code (not in a string literal or a comment); basSourceRename renames a form or control (oldName followed by '.' or '_' as a whole word), optionally only in code; basSourceWithEvents and basSourceClassEvents report the WithEvents variables and the Events of a class module. The IDE's rename (ideRenameInCode, also after the Menu Editor renames items) and its Object / Event lists use them; compiler.bas has CompTokens$, CompInCode, CompRenameInCode$, CompWithEvents$, and CompClassEvents$.
Help Topics (basHelpMap.h)
basHelpKeywordTopic gives the DVX BASIC help topic of a keyword or built-in function (NULL for none) and basHelpCtrlTopic the topic of a control type (BAS_HELP_CTRL_PREFIX and the type name in lower case): what F1 opens in the IDE, and CompHelpTopic$ / CompHelpCtrlTopic$.
Projects (basProject.h)
-
The project (.dbp) model: PrjStateT with its file list (PrjFileT: path relative to the project's folder, kind -- PrjFileKindE: module, class module, UserControl ([Controls], .ctl) or form, in compile order -- edit buffer) and settings, prjLoad / prjSave (one table of [Project] / [Settings] keys drives load, save, prjFieldGet and prjFieldSet; [Project] Type = Control makes a control project, prjIsControlProject), prjNew, prjAddFile, prjRemoveFile, prjRenameFile (disk and project together), prjFileIndexByPath, prjKindForPath (the build's rule, basBuildWrapForPath), prjKindWrap, prjClassName (a class module's or UserControl's class name), prjReadFileText and the source map of the last compile (prjMapLine, prjMapFileLine). The IDE keeps only its Project window and Properties dialog (ideProject.c); compiler.bas has ProjOpen, ProjNew, ProjSave, ProjClose, ProjPath$, ProjFileCount, ProjFile$, ProjFilePath$, ProjFileKind, ProjFileFind, ProjAddFile, ProjRemoveFile, ProjRenameFile, ProjGet$ and ProjSet; CompKeywordClass and CompKeywordReserved expose the keyword table's flags.
-
A UserControl's header (the Begin UserControl block of a .ctl) is read and changed key by key, every other line of the text kept: prjCtlKeyAt / prjCtlKeyFind give the keys (PrjCtlKeyT: name and PrjCtlValueE kind -- Name, the control's name on the Begin line, then Width, Height, CanGetFocus, BackColor, ToolboxIcon, Description, DefaultEvent), prjCtlHeaderGet the value the control takes (its default when the header leaves the key out), prjCtlHeaderSet a malloc'd copy of the text with one key changed, added before End or removed (an empty value), written as its kind is (a text in quotes, a color &HBBGGRR&, True or False), and prjCtlNew the text of a new UserControl (its header with Width and Height, Option Explicit, an empty UserControl_Paint written with PRJ_HANDLER_FMT and PRJ_PRIVATE, the form the IDE writes event handlers in). The IDE's File > New UserControl and Properties window use them; compiler.bas has ProjCtlNew$, ProjCtlHeader$, ProjCtlSetHeader$ and ProjCtlKeys$ (the keys and their kinds, prjCtlKeyAt order).
+
The project (.dbp) model: PrjStateT with its file list (PrjFileT: path relative to the project's folder, kind -- PrjFileKindE: module, class module, UserControl ([Controls], .ctl), or form, in compile order -- edit buffer) and settings, prjLoad / prjSave (one table of [Project] / [Settings] keys drives load, save, prjFieldGet, and prjFieldSet; [Project] Type = Control makes a control project, prjIsControlProject), prjNew, prjAddFile, prjRemoveFile, prjRenameFile (disk and project together), prjFileIndexByPath, prjKindForPath (the build's rule, basBuildWrapForPath), prjKindWrap, prjClassName (a class module's or UserControl's class name), prjReadFileText, the fonts the program carries ([Fonts]: prjAddFont, prjRemoveFont, prjFontCount, prjFontPath; the build makes them FONTn font resources, basBuildSpecT fonts), and the source map of the last compile (prjMapLine, prjMapFileLine). The IDE keeps only its Project window and Properties dialog (ideProject.c); compiler.bas has ProjOpen, ProjNew, ProjSave, ProjClose, ProjPath$, ProjFileCount, ProjFile$, ProjFilePath$, ProjFileKind, ProjFileFind, ProjAddFile, ProjRemoveFile, ProjRenameFile, ProjFontCount, ProjFont$, ProjAddFont, ProjRemoveFont, ProjGet$, and ProjSet; CompKeywordClass and CompKeywordReserved expose the keyword table's flags.
+
A UserControl's header (the Begin UserControl block of a .ctl) is read and changed key by key, every other line of the text kept: prjCtlKeyAt / prjCtlKeyFind give the keys (PrjCtlKeyT: name and PrjCtlValueE kind -- Name, the control's name on the Begin line, then Width, Height, CanGetFocus, BackColor, ToolboxIcon, Description, DefaultEvent), prjCtlHeaderGet the value the control takes (its default when the header leaves the key out), prjCtlHeaderSet a malloc'd copy of the text with one key changed, added before End, or removed (an empty value), written as its kind is (a text in quotes, a color &HBBGGRR&, True or False), and prjCtlNew the text of a new UserControl (its header with Width and Height, Option Explicit, an empty UserControl_Paint written with PRJ_HANDLER_FMT and PRJ_PRIVATE, the form the IDE writes event handlers in). The IDE's File > New UserControl and Properties window use them; compiler.bas has ProjCtlNew$, ProjCtlHeader$, ProjCtlSetHeader$, and ProjCtlKeys$ (the keys and their kinds, prjCtlKeyAt order).
Running a Program (basRunHost.h, in basrt.lib)
-
basRunHostCreate makes the VM and form runtime for a module from a BasRunSpecT: App.Path and its CONFIG / DATA folders, COMMAND$, the print, input and DoEvents callbacks, the screen window's name, the help file and whether runtime errors show their own box. basRunHostRegisterFrm registers a .frm text, basRunHostLoadForms loads the forms and shows the startup form, basRunHostSlice runs module code once under the step limit (an error's message and line are always set afterwards), basRunHostAlive says whether the event-loop phase goes on, basRunHostTagBreak puts BAS_RUN_BREAK_TAG on the captions while paused and basRunHostDestroy ends it. The runtime made inside another program's code does not become the current runtime (basFormRtCurrent / basFormRtSetCurrent); each of these calls makes it current while its code runs.
+
basRunHostCreate makes the VM and form runtime for a module from a BasRunSpecT: App.Path and its CONFIG / DATA folders, COMMAND$, the print, input, and DoEvents callbacks, the screen window's name, the help file, and whether runtime errors show their own box. basRunHostRegisterFrm registers a .frm text, basRunHostLoadForms loads the forms and shows the startup form, basRunHostSlice runs module code once under the step limit (an error's message and line are always set afterwards), basRunHostAlive says whether the event-loop phase goes on, basRunHostTagBreak puts BAS_RUN_BREAK_TAG on the captions while paused, and basRunHostDestroy ends it. The runtime made inside another program's code does not become the current runtime (basFormRtCurrent / basFormRtSetCurrent); each of these calls makes it current while its code runs.
VM accessors a debugger host uses (vm.h): basVmSetAppPaths, basVmSetBreakpointCallback, basVmSetDebugPaused, basVmResume, basVmStop, basVmClearStepState, basVmGetCurrentLine, basVmGetErrorLine.
From BASIC
-
compiler.bas and debug.bas declare the entry points (see Include Libraries in the BASIC language reference). Source sets, modules and runs are handles freed with their program. A run is polled: RunPoll runs the child for a bounded time and never waits. A pause in the child's module code returns RUN_PAUSED; a pause inside one of its event handlers (or its forms' Load code, inside RunStart) waits in place while the child pumps DVX events, so the caller's Timer and menus can step, continue or stop it; RunPoll called from there returns at once. RunFree while the child's code is on the stack stops it at once and releases it at the next library call after that code has unwound.
-
CompDesignControls registers a source set's UserControls for the calling program's design forms (basCtlLoadDesign with its runtime, the current one: a child run's own when the child calls it) and holds the module -- one of the caller's own handles; another program's is refused -- until CompDesignRelease, the next CompDesignControls or the end of that runtime (appEnded for a program, runDestroyHost for a child run), CompModuleFree meanwhile only marking it.
+
compiler.bas and debug.bas declare the entry points (see Include Libraries in the BASIC language reference). Source sets, modules, and runs are handles freed with their program. A run is polled: RunPoll runs the child for a bounded time and never waits. A pause in the child's module code returns RUN_PAUSED; a pause inside one of its event handlers (or its forms' Load code, inside RunStart) waits in place while the child pumps DVX events, so the caller's Timer and menus can step, continue, or stop it; RunPoll called from there returns at once. RunFree while the child's code is on the stack stops it at once and releases it at the next library call after that code has unwound.
+
CompDesignControls registers a source set's UserControls for the calling program's design forms (basCtlLoadDesign with its runtime, the current one: a child run's own when the child calls it) and holds the module -- one of the caller's own handles; another program's is refused -- until CompDesignRelease, the next CompDesignControls, or the end of that runtime (appEnded for a program, runDestroyHost for a child run), CompModuleFree meanwhile only marking it.
BASIC Runtime Library
@@ -6696,8 +7051,8 @@ audioFmFree(audio, v); // key off, back to everyone
-
A reference only ever lives in a call's parameters (a ByRef parameter, a ParamArray element). Read through it with basValDeref, which returns the value a reference names (or the value itself). refKind says what ref.owner is: BAS_REF_SLOT (none: a global, local or form variable), BAS_REF_ARRAY (the array, counted and locked so REDIM / ERASE raise error 10), BAS_REF_UDT (the TYPE instance holding the field), BAS_REF_OBJECT (the class instance holding the member variable) or BAS_REF_CELL (a BasRefCellT holding both the locked array and the TYPE instance, for a field of an array element). refType is the target's declared type (stores convert to it) and refLen the length of a STRING * n target. basValCopy and basValRelease count the owner.
+ BAS_TYPE_REF 9 ref Reference (ByRef argument): ref.target is the variable, element, or field it names; ref.owner keeps that storage alive.
+
A reference only ever lives in a call's parameters (a ByRef parameter, a ParamArray element). Read through it with basValDeref, which returns the value a reference names (or the value itself). refKind says what ref.owner is: BAS_REF_SLOT (none: a global, local, or form variable), BAS_REF_ARRAY (the array, counted and locked so REDIM / ERASE raise error 10), BAS_REF_UDT (the TYPE instance holding the field), BAS_REF_OBJECT (the class instance holding the member variable), or BAS_REF_CELL (a BasRefCellT holding both the locked array and the TYPE instance, for a field of an array element). refType is the target's declared type (stores convert to it) and refLen the length of a STRING * n target. basValCopy and basValRelease count the owner.
Function Description
-------- -----------
basCtlIface(typeName) The widget interface of a BASIC type name, or NULL.
- basCtlPropAt(typeName, idx, out) / basCtlPropFind(...) A type's properties as BasCtlPropT (name, type, readable, writable, enum names): the runtime table's ("Form", "Menu" or the common one), the widget's own winning on a name, then the widget's others. The IDE's properties window uses basCtlPropFind.
+ basCtlPropAt(typeName, idx, out) / basCtlPropFind(...) A type's properties as BasCtlPropT (name, type, readable, writable, enum names): the runtime table's ("Form", "Menu", or the common one), the widget's own winning on a name, then the widget's others. The IDE's properties window uses basCtlPropFind.
basCtlMethodAt / basCtlEventAt(typeName, idx) A type's methods (the widget's, then the common ones) and events (the master BAS_EVENT_LIST's for that kind of object, then the widget's); the IDE's event list uses basCtlEventAt.
- basCtlEventParams(typeName, eventName) The parameter list of a handler of a type's event: the widget's own (WgtEventDescT params), else the master list's; typeName NULL searches every widget after the master list, BAS_UC_OBJECT ("UserControl", the object in a UserControl's own code) has BAS_UC_EVENT_LIST's alone. The IDE's handler skeletons, CtlEventParams and CtlTypeEventParams.
+ basCtlEventParams(typeName, eventName) The parameter list of a handler of a type's event: the widget's own (WgtEventDescT params), else the master list's; typeName NULL searches every widget after the master list, BAS_UC_OBJECT ("UserControl", the object in a UserControl's own code) has BAS_UC_EVENT_LIST's alone. The IDE's handler skeletons, CtlEventParams, and CtlTypeEventParams.
basCtlDefaultEvent, basCtlNamePrefix, basCtlIsContainer As the toolbox and designer need them.
basCtlResPath(typeName, resBase, resName, size) The widget file and the (suffixed) resource name of its toolbox icon (BAS_CTL_RES_ICON) or tooltip (BAS_CTL_RES_NAME).
basCtlTypeAt(rt, idx) The idx-th control type rt sees (basUcIfaceVisible; rt NULL: a C host) in alphabetical order of BASIC name, NULL past the end. The IDE's toolbox and CtlTypeName list the types in this order; the registry's own (wgtIfaceAt) is the order the widget files loaded in.
@@ -6966,9 +7321,9 @@ int32_t basVmCollectionAdd(BasObjectT *coll, BasValueT item, const char *key
basDsgnPaintHandles(ctx, win, w) Paints a widget's eight selection handles over a window's painted content.
basDsgnPropertyChanged(w, propName) The code of UserControl widget w called PropertyChanged: a form in DesignMode holding it raises DesignPropertyChanged (CtrlName, PropertyName). formCtl.c calls it beside the C hook (basUcSetPropChangedHook).
basDsgnWidgetHit(w, x, y) True when a point is on a visible widget.
-
Form.DesignMode replaces the form window's mouse, key, paint and cursor handlers while it is True (the controls get no input, and fireCtrlEvent drops their events), raises DesignMouse, DesignKey and DesignPropertyChanged, and paints DesignSelection's handles after the widgets. Form.Controls, Control.Parent / Container and MoveBefore (basFormRtMoveControl) are dispatched there too.
+
Form.DesignMode replaces the form window's mouse, key, paint, and cursor handlers while it is True (the controls get no input, and fireCtrlEvent drops their events), raises DesignMouse, DesignKey, and DesignPropertyChanged, and paints DesignSelection's handles after the widgets. Form.Controls, Control.Parent / Container, and MoveBefore (basFormRtMoveControl) are dispatched there too.
UserControls for a Host
-
formrt/formCtl.c registers UserControls (widget classes written in BASIC) with the widget system. A program's own controls (basFormRtRegisterCtl, the stub and the IDE's runs) and a designer's (basCtlLoadDesign) get private registry keys, the class name plus a serial, so the same project running and being designed, or two programs, never share one; a control file's (basCtlLoadFile) keeps its name. Resolve names through these instead of wgtGetIface:
+
formrt/formCtl.c registers UserControls (widget classes written in BASIC) with the widget system. A program's own controls (basFormRtRegisterCtl: the stub's and the IDE's runs) and a designer's (basCtlLoadDesign) get private registry keys, the class name plus a serial, so the same project running and being designed, or two programs, never share one; a control file's (basCtlLoadFile) keeps its name. Resolve names through these instead of wgtGetIface:
Function Description
-------- -----------
basCtlLoadDesign(rt, module, headers, count, path) Designer rt's controls (rt: a BASIC program designing forms, compiler.bas CompDesignControls; NULL: a C host, the IDE): the .ctl headers' classes of module (the caller keeps it until basCtlUnloadDesign or rt's end), run without the module-level code, App.Path the folder of path. Returns how many were registered (a name a native or installed control has, or rt's own control has, is not). The set rt registered before is let go first.
@@ -7034,7 +7389,7 @@ void basVmSetAppPaths(BasVmT *vm, const char *path,
basVmStop Stop a debugged program: step state cleared, breakpoints detached, break mode ended, the running code unwinds.
basVmGetCurrentLine Get the current source line number (from the last OP_LINE instruction).
basVmGetErrorLine The source line where the current runtime error was raised (0: none).
- basVmSetAppPaths Set App.Path, App.Config and App.Data.
+ basVmSetAppPaths Set App.Path, App.Config, and App.Data.
The breakpoint callback notifies the host when a breakpoint fires during nested sub calls:
A form file as data, with no widgets and no windows: the form's properties, its controls (type, name, control array index, parent, the built-in size and state fields and every other "Key = Value" pair as text), its menu items and its code section. frmModelLoad reads .frm text (through frmParse) and frmModelWrite writes the canonical text the DVX BASIC IDE saves. The IDE's designer, properties window, menu editor, copy and paste all work on this model and keep only their live widgets on the side (each control's userData); designer.bas binds it for BASIC programs (the Frm* functions, formrt/frmModelBas.c).
+
A form file as data, with no widgets and no windows: the form's properties, its controls (type, name, control array index, parent, the built-in size and state fields, and every other "Key = Value" pair as text), its menu items, and its code section. frmModelLoad reads .frm text (through frmParse) and frmModelWrite writes the canonical text the DVX BASIC IDE saves. The IDE's designer, properties window, menu editor, and copy and paste all work on this model and keep only their live widgets on the side (each control's userData); designer.bas binds it for BASIC programs (the Frm* functions, formrt/frmModelBas.c).
Header: apps/dvxbasic/formrt/frmModel.h (part of basrt.lib)
Types
Type Description
---- -----------
- FrmModelT A form: name, caption, width / height / left / top, padding, spacing, windowState, layout, helpTopic, contextMenu, centered / autoSize / resizable, controls (stb_ds array of FrmCtrlT pointers in tree order: each control directly followed by its children), menuItems (stb_ds array), props (other runtime form properties that were set: Icon, Tag, MousePointer ...), code (malloc'd, NULL = none), dirty (changed by an edit since it was made or loaded).
- FrmCtrlT A control: name, typeName, parentName + parentIndex (the parent's control array index, so a child of one member of a container array belongs to that member), index (FRM_NO_INDEX: not in an array), left, top, width (MinWidth), height (MinHeight), maxWidth, maxHeight, weight, dragMode, helpTopic, visible, enabled, props (stb_ds array of FrmPropT: every other key, unquoted), userData (the caller's; the model never reads it and loads, copies and pastes start it at NULL).
+ FrmModelT A form: name, caption, width / height / left / top, padding, spacing, windowState, layout, helpTopic, contextMenu, centered / autoSize / resizable, controls (stb_ds array of FrmCtrlT pointers in tree order: each control directly followed by its children), menuItems (stb_ds array), props (other runtime form properties that were set: Icon, Tag, MousePointer, the Font properties ...), code (malloc'd, NULL = none), dirty (changed by an edit since it was made or loaded).
+ FrmCtrlT A control: name, typeName, parentName + parentIndex (the parent's control array index, so a child of one member of a container array belongs to that member), index (FRM_NO_INDEX: not in an array), left, top, width (MinWidth), height (MinHeight), maxWidth, maxHeight, weight, dragMode, helpTopic, visible, enabled, props (stb_ds array of FrmPropT: every other key, unquoted), userData (the caller's; the model never reads it and loads, copies, and pastes start it at NULL).
FrmMenuItemT A menu item: caption ("-" for a separator), name, shortcut, shortcutText, level (0: a menu-bar menu or a popup; 1: its items; 2+: submenus), id (an editor identity, see frmModelMenuReplace), checked, checkable (Checked was written), radioCheck, enabled, visible (False on a level-0 item: a popup menu).
FrmPropT One stored "Key = Value": name, value.
FrmIntPropT One built-in integer property (Left, Top, MinWidth alias Width, MinHeight alias Height, MaxWidth, MaxHeight, Weight, DragMode): name, alias, offset into FrmCtrlT, saveRule (FRM_SAVE_ALWAYS / IF_NONZERO / IF_POSITIVE).
-
Only values somebody set are stored and written: a property a control has by default (a PictureBox's BorderStyle, a TextBox's MaxLength) is not in the model and not in the file. Values are written in the canonical form for their widget interface type (STRING quoted, BOOL True / False, INT a number, FLOAT and ENUM unquoted); keys the widget does not know are written as quoted strings. The limits come from the parser's: FRM_MAX_NEST_DEPTH control levels, FRM_MAX_MENU_LEVEL menu levels, and no property line longer than FRM_MAX_LINE_LEN - 1 characters (the writer fails with FRM_WRITE_LINE_TOO_LONG rather than write one the parser would cut).
+
Only values somebody set are stored and written: a property a control has by default (a PictureBox's BorderStyle, a TextBox's MaxLength) is not in the model and not in the file. Values are written in the canonical form for their widget interface type, or for a key the widget does not know, for their type in the runtime's common property table (STRING quoted, BOOL True / False, INT a number, FLOAT and ENUM unquoted); other keys are written as quoted strings. The limits come from the parser's: FRM_MAX_NEST_DEPTH control levels, FRM_MAX_MENU_LEVEL menu levels, and no property line longer than FRM_MAX_LINE_LEN - 1 characters (the writer fails with FRM_WRITE_LINE_TOO_LONG rather than write one the parser would cut).
Loading and Writing
Function Description
-------- -----------
frmModelNew(name) A blank form with the runtime form's defaults.
frmModelLoad(text, len) The model of .frm text; NULL when it is not a form (no Begin Form, frmParse refuses it, two controls share a name and index) or out of memory. A Key = Value line is the innermost open block's, also after the blocks inside it, as the runtime reads it; a child names its container by the container's final Index. Of several OptionButtons of one container saved with Value True, the last keeps it.
- frmModelFree(m) Frees the model, its controls and menus.
+ frmModelFree(m) Frees the model, its controls, and menus.
frmModelWrite(m, buf, bufSize) The .frm text into buf: its length, or FRM_WRITE_TOO_LARGE / FRM_WRITE_LINE_TOO_LONG (never a partial text).
frmModelWriteSize(m) A buffer size that always holds the text.
frmModelText(m, &len) The text in a new malloc'd string; NULL with len set to the FRM_WRITE_* code.
frmModelSetCode(m, code) Replaces the code section.
frmCodeSection(trailing, len, &outLen) The code section of the text after a form's End (blank lines skipped, the first code line keeps its indent).
- frmModelFromForm(rt, form) A model of a loaded form as it is now: its properties, its controls in their containers with each property that differs from a new control of the type (compared with one made in a hidden window), its menus; no code. What FormText$ writes.
+ frmModelFromForm(rt, form) A model of a loaded form as it is now: its properties, its controls in their containers with each property that differs from a new control of the type (compared with one made in a hidden window; the six Font properties of the form and of each control that has a font of its own), its menus; no code. What FormText$ writes.
Properties
Function Description
-------- -----------
- frmModelFormProp(m, name, out, size) A form property as text (an unset design property gives "", "0" or "False"); false for run-time-only ones (Visible, DesignMode, DesignSelection, object properties).
+ frmModelFormProp(m, name, out, size) A form property as text (an unset design property gives "", "0", or "False"); false for run-time-only ones (Visible, DesignMode, DesignSelection, object properties).
frmModelSetFormProp(m, name, value) Sets one from text; Name renames the form; an empty value takes an optional one out.
frmCtrlPropText(ctrl, name, out, size) A control property as the control holds it (Name, Index, the built-in integers, HelpTopic, Visible / Enabled unless the widget defines them, the stored ones).
frmCtrlSetPropText(ctrl, key, value) Applies one the way the loader does (a VB TabIndex is dropped unless the widget defines it).
@@ -7136,6 +7491,15 @@ void basVmSetAppPaths(BasVmT *vm, const char *path,
frmCtrlGetProp / frmCtrlSetProp / frmCtrlRemoveProp The stored properties.
frmIntPropAt / frmIntPropFind / frmIntPropGet / Set The built-in integer table.
frmIfaceProp(typeName, propName) The widget interface property of a type, NULL when none.
+
Font Properties
+
FontName, FontSize, FontBold, FontItalic, FontUnderline, and FontStrikethru (BAS_PROP_FONT*) are in both the form table and the common control table. They are one font, a DvxFontChoiceT (dvxDlg.h: family, size in pixels, FONT_STYLE_* flags), and the widget's font (wgtFont, wgtSetFont) is the only place they live: a read gives the font the object draws with (a form's is its root widget's), a write gives it that font with the one part changed. The .frm loader collects a block's Font properties and opens one font when the block's properties are read, so their order in the file does not matter. Helpers in formrt.h, for loaders and designers that hold them as .frm text:
+
Function Description
+ -------- -----------
+ basFontPropAt(idx) The Font property names in .frm order (FontName, FontSize, then the flags), NULL past the end.
+ basFontPropIs(name) True for a Font property name (any case).
+ basFontChoiceOf(font, out) A BitmapFontT as the properties name it (dvxFontFamilyOf, pixelSize, style).
+ basFontPropText(choice, name, out, size) One property of a choice as .frm text (the family, the size, True / False).
+ basFontPropSetText(choice, name, text) Sets one property of a choice from .frm text.
Editing Controls
Function Description
-------- -----------
@@ -7144,7 +7508,7 @@ void basVmSetAppPaths(BasVmT *vm, const char *path,
frmModelRenameCtrl(m, oldName, newName) Renames a whole control array; children and DataSource / MasterSource of a renamed Data control follow. Identifier rules are the caller's.
frmModelMakeArrayMember(m, ctrl, index) Gives a control array index; its children follow.
frmModelMoveCtrl(m, ctrl, before) Moves ctrl with its subtree before a sibling (NULL: after the last).
- frmModelReparent(m, ctrl, parent) Makes ctrl the last child of another container (refuses cycles, non-containers and too deep a nesting).
+ frmModelReparent(m, ctrl, parent) Makes ctrl the last child of another container (refuses cycles, non-containers, and too deep a nesting).
frmModelSibling(m, idx, dir) The sibling next to a control (+1 after its subtree, -1 before).
frmModelFindCtrl / CtrlPos / FindParent / CtrlDepth Lookups.
frmModelIsDescendant / SubtreeEnd / NormalizeOrder The tree order.
@@ -7187,14 +7551,14 @@ if (m) {
Connection Library
-
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.
-
Loaded as: bin/libs/kpunch/conn/conn.lib (needs the serial, networking and transfer libraries; the transfer library for its BASIC functions)
+
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.
+
Loaded as: bin/libs/kpunch/conn/conn.lib (needs the serial, networking, and transfer libraries; the transfer library for its BASIC functions)
Header: conn/conn.h
Connection Types
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.
+ 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.
The Telnet protocol itself is a separate filter (connTelnet.c) that only moves bytes, so it is tested byte for byte without a network.
@@ -7203,7 +7567,7 @@ if (m) {
----- -------
type ConnTypeE.
comPort RS232_COM1 .. RS232_COM4.
- bps, dataBits, parity Line settings (parity 'N', 'E', 'O', 'M' or 'S').
+ 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).
@@ -7216,14 +7580,14 @@ if (m) {
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.
+ 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.
+ 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).
@@ -7242,8 +7606,8 @@ connOpen(c, &cfg, nowMs());
// every frame:
connPoll(c, nowMs());
n = connRead(c, buf, sizeof(buf));
-
A Telnet session is the same with cfg.type = ConnTelnetE, cfg.host = "bbs.example.org" and cfg.tcpPort = 23.
-
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).
+
A Telnet session is the same with cfg.type = ConnTelnetE, cfg.host = "bbs.example.org", and cfg.tcpPort = 23.
+
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).
Connections from BASIC
@@ -7270,16 +7634,16 @@ n = connRead(c, buf, sizeof(buf));
ConnZmodemRequest(h) True once after a ZMODEM sender started (xferDetectFeed).
ConnSendFiles(h, proto, files$, escapeCtl, resume) Send files (one path per line). An xfer.bas handle, with XFER_FAILED and the reason when it could not start; 0 when the line is not open or busy.
ConnReceiveFiles(h, proto, target$, exists, escapeCtl) Receive (exists: XFER_EXIST_).
-
ConnPoll with a Terminal attached does what the C Terminal's heartbeat did: it reads up to 4 KB, appends them to the capture file, answers each ENQ with the answerback (ENQ itself is not shown), stops at a ZMODEM start frame (the rest of what was read is dropped, and ConnZmodemRequest becomes True), and writes the rest to the Terminal. While a transfer started on the connection runs (or waits for XferAnswer), ConnPoll moves it along instead, nothing reaches the Terminal and typing is dropped; a transfer on a line that is no longer open is cancelled. The receive window of a ZMODEM download on a serial or modem line without flow control is 2 KB, so a slow frame never loses data, unless the program set one with ConnSetXferWindow.
-
The Terminal binding goes through the form runtime (basFormRtTermBind, basFormRtTermUnbind and basFormRtTermForget in basrt, looked up when ConnAttach first needs them): the runtime unbinds a Terminal whose form unloads, and tells the library, so a connection never writes to a freed control. Every handle belongs to the program that made it; whatever it leaves open is closed (a modem hangs up, a capture file is closed, a transfer cancelled) when it ends or is ended.
+
ConnPoll with a Terminal attached does what the C Terminal's heartbeat did: it reads up to 4 KB, appends them to the capture file, answers each ENQ with the answerback (ENQ itself is not shown), stops at a ZMODEM start frame (the rest of what was read is dropped, and ConnZmodemRequest becomes True), and writes the rest to the Terminal. While a transfer started on the connection runs (or waits for XferAnswer), ConnPoll moves it along instead, nothing reaches the Terminal, and typing is dropped; a transfer on a line that is no longer open is cancelled. The receive window of a ZMODEM download on a serial or modem line without flow control is 2 KB, so a slow frame never loses data, unless the program set one with ConnSetXferWindow.
+
The Terminal binding goes through the form runtime (basFormRtTermBind, basFormRtTermUnbind, and basFormRtTermForget in basrt, looked up when ConnAttach first needs them): the runtime unbinds a Terminal whose form unloads, and tells the library, so a connection never writes to a freed control. Every handle belongs to the program that made it; whatever it leaves open is closed (a modem hangs up, a capture file is closed, a transfer cancelled) when it ends or is ended.
Networking Library
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.
-
Loaded as: bin/libs/kpunch/dvxnet/dvxnet.lib (needs the shell, the task switcher and the core library)
-
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).
+
Loaded as: bin/libs/kpunch/dvxnet/dvxnet.lib (needs the shell, the task switcher, and the core library)
+
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).
@@ -7338,7 +7702,7 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The Socket API
-
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().
+
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().
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).
@@ -7355,7 +7719,7 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
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).
+ 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.
Error Codes
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.
@@ -7363,19 +7727,19 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Sockets are real descriptors below FD_SETSIZE (256), at most 32 at a time. dup() and dup2() refuse socket descriptors (EOPNOTSUPP), lseek gives ESPIPE.
-
Waiting, Polling and Cooperative Multitasking
-
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.
+
Waiting, Polling, and Cooperative Multitasking
+
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.
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.
-
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.
+
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.
Sockets Belong to Programs
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.
Settings and Status (dvxNet.h)
-
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.
+
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.
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.
+ 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.
@@ -7404,11 +7768,11 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Settings dvxNet.c DVX.INI, status, errors in plain words.
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.
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.
-
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.
+
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.
Networking from BASIC
-
net.bas declares simple functions over the socket API (netBas.c): HostLookup$, HostName$, NetAddress$, NetError$, TcpConnect, TcpListen, TcpAccept, TcpSend, TcpPending, TcpRecv$, TcpConnected, TcpShutdown, TcpPeer$, TcpLocalPort, TcpSetNoDelay, TcpSetKeepAlive, 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. TcpSend, TcpRecv$, UdpSend and UdpRecv$ pass their data as BINARY STRING (pointer and length), so every byte value goes through. See the BASIC language reference, Include Libraries.
+
net.bas declares simple functions over the socket API (netBas.c): HostLookup$, HostName$, NetAddress$, NetError$, TcpConnect, TcpListen, TcpAccept, TcpSend, TcpPending, TcpRecv$, TcpConnected, TcpShutdown, TcpPeer$, TcpLocalPort, TcpSetNoDelay, TcpSetKeepAlive, 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. TcpSend, TcpRecv$, UdpSend, and UdpRecv$ pass their data as BINARY STRING (pointer and length), so every byte value goes through. See the BASIC language reference, Include Libraries.
BASIC C it calls
----- ----------
HostName$(address) getnameinfo: the address itself, as this stack does not look names of addresses up; "" when it is not an address.
@@ -7695,7 +8059,7 @@ shellConfigPath(ctx, "settings.ini", path, sizeof(path));
void shellRegisterAppCleanup(ShellAppCleanupFnT fn);
void shellUnregisterAppCleanup(ShellAppCleanupFnT fn);
-
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.
+
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.
shellDesktopUpdate
void shellDesktopUpdate(void);
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. Each call also adds one to the change counter (shellDesktopChangeCount).
shellCtrlEscFn is set by the taskmgr DXE's constructor; NULL if the task manager is not loaded. shellOpenTaskManager calls it when it is set and does nothing otherwise; Ctrl+Esc, Program Manager's Help > Task Manager and BASIC's ShellTaskManager all open Task Manager through it.
+
shellCtrlEscFn is set by the taskmgr DXE's constructor; NULL if the task manager is not loaded. shellOpenTaskManager calls it when it is set and does nothing otherwise; Ctrl+Esc, Program Manager's Help > Task Manager, and BASIC's ShellTaskManager all open Task Manager through it.
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.
+
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.
Constant Value Meaning
-------- ----- -------
SHELL_PROG_DIR "apps" Folder, relative to the DVX root, holding every program.
@@ -7739,7 +8103,7 @@ void shellOpenTaskManager(AppContextT *ctx);
Every program under dir (NULL for SHELL_PROG_DIR) and its subfolders, except skipPath (compared with platformPathSame; 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.
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.
+
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.
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.
ShellAssocProgram is shellAssocLookup: the stored value, the program followed by ",args" when there is a template. ShellAssocCommand is the program part of shellAssocCommand for a document. ShellAssocCount, ShellAssocExt and ShellAssocValue enumerate shellAssocGet (0-based; "" past the end). ShellAssocSet, ShellAssocRemove and ShellAssocNormalize are shellAssocSet, shellAssocRemove and shellAssocNormalize ("" when the extension cannot be one). Every change is saved at once.
+
ShellAssocProgram is shellAssocLookup: the stored value, the program followed by ",args" when there is a template. ShellAssocCommand is the program part of shellAssocCommand for a document. ShellAssocCount, ShellAssocExt, and ShellAssocValue enumerate shellAssocGet (0-based; "" past the end). ShellAssocSet, ShellAssocRemove, and ShellAssocNormalize are shellAssocSet, shellAssocRemove, and shellAssocNormalize ("" when the extension cannot be one). Every change is saved at once.
ShellProgramsFolder is SHELL_PROG_DIR (where ShellProgramScan("") looks and a Browse for a program starts). ShellItemIcon is shellItemIconFile ("" for none). The program-group bindings (ShellGroups*, ShellGroup*, ShellItem*) are in pmModelBas.c; see Program Groups.
A desktop in BASIC
-
A BASIC program can be the desktop ([shell] desktop, ShellSetDesktop): the shell exits DVX when it ends, so it needs no code for that; its Exit command confirms and calls ShellExitDvx. Its groups use the shell's program-group model (Program Groups: the same PROGMAN.INI format, rules and limits as Program Manager) through shell.bas, in ShellConfigFile$'s folder. An icon grid is drawn on a PictureBox with PaintPicture and pictures from LOADPICTURE of ShellItemIcon$ (a .app gives its icon), with MouseDown/MouseUp for selecting and dragging and KeyDown for the keyboard. The test fixture src/test/dos/fixtures/parpm does all of this.
+
A BASIC program can be the desktop ([shell] desktop, ShellSetDesktop): the shell exits DVX when it ends, so it needs no code for that; its Exit command confirms and calls ShellExitDvx. Its groups use the shell's program-group model (Program Groups: the same PROGMAN.INI format, rules, and limits as Program Manager) through shell.bas, in ShellConfigFile$'s folder. An icon grid is drawn on a PictureBox with PaintPicture and pictures from LOADPICTURE of ShellItemIcon$ (a .app gives its icon), with MouseDown/MouseUp for selecting and dragging and KeyDown for the keyboard. The test fixture src/test/dos/fixtures/parpm does all of this.
Program Manager's groups, items and PROGMAN.INI, as plain data with no windows, part of the shell so the C Program Manager and a desktop written in BASIC share one model (the pm prefix names it). groups[PM_INSTALLED_IDX] is the automatic Installed Programs group (pmInstalledScan: every program under SHELL_PROG_DIR, sorted by name, never saved; pmInstalledChanged tells cheaply whether a program was added or removed); the others are the user's, saved by pmModelSave with a crash-safe rewrite in this layout:
+
Program Manager's groups, items, and PROGMAN.INI, as plain data with no windows, part of the shell so the C Program Manager and a desktop written in BASIC share one model (the pm prefix names it). groups[PM_INSTALLED_IDX] is the automatic Installed Programs group (pmInstalledScan: every program under SHELL_PROG_DIR, sorted by name, never saved; pmInstalledChanged tells cheaply whether a program was added or removed); the others are the user's, saved by pmModelSave with a crash-safe rewrite in this layout:
What Control Panel changes, for DVX BASIC programs: the video mode, the system colors and themes, the wallpaper and the mouse, each applied at once to shellContext(). Only BASIC-marshalable types; True (-1) / False (0); colors are BASIC RGB() values (&HBBGGRR, converted with wgtBasicColor); lists are 0-based. Arguments named prefs are prefs.bas handles, resolved with prefsBasHandle (dvxPrefs.h); 0 or a closed handle makes a Store or Load do nothing (a Load returns False).
+
What Control Panel changes, for DVX BASIC programs: the video mode, the system colors and themes, the wallpaper, text smoothing, and the mouse, each applied at once to shellContext(). Only BASIC-marshalable types; True (-1) / False (0); colors are BASIC RGB() values (&HBBGGRR, converted with wgtBasicColor); lists are 0-based. Arguments named prefs are prefs.bas handles, resolved with prefsBasHandle (dvxPrefs.h); 0 or a closed handle makes a Store or Load do nothing (a Load returns False).
SysSetWallpaperMode dvxSetWallpaperMode with the value of prefsWallpaperChoices[mode]; other modes are ignored
SysWallpaper / Mode ctx->wallpaperPath; the prefsWallpaperChoices index of ctx->wallpaperMode
SysWallpaperStore / Load dvxWallpaperToPrefs / dvxWallpaperFromPrefs (Load: False when the image could not be loaded)
+ SysSetTextSmoothing dvxSetFontSmoothing (nonzero is on)
+ SysTextSmoothing ctx->fontSmoothing (the setting; text is smoothed only above 8 bits per pixel)
+ SysFontsStore / Load dvxFontsToPrefs / dvxFontsFromPrefs
SysSetMouse dvxSetMouseConfig
SysMouse* ctx->wheelDirection, dblClickMs, mouseAccel, mouseMickeys, wheelStep
SysMouseStore / Load dvxMouseToPrefs / dvxMouseFromPrefs
@@ -7977,7 +8348,7 @@ int32_t SysMemoryFreeKb(void);
SysScreenshot dvxScreenshot (BMP for .bmp, else PNG)
SysMemoryTotalKb / FreeKb platformGetMemoryInfo; -1 when unknown
There is no binding for platformInjectMouse: on DOS it replaces the real mouse until the next call and has no way back to it, so a synthesized click would leave the pointer stuck. A program moves the pointer with SysSetMousePos and "clicks" a control by calling its event procedure or setting its value.
-
system.bas also holds MOUSE_WHEEL_STEP_MIN / MAX / DEFAULT, MOUSE_DBLCLICK_DEFAULT_MS and MOUSE_SPEED_DEFAULT, and SYS_VIDEO_DEFAULT_WIDTH / HEIGHT / BPP (DVX_DEFAULT_VIDEO_W / H / BPP, the mode prefsGetVideoMode and safe mode fall back to), as CONSTs; the basdev test suite (caseIncludeConsts) checks each against dvxTypes.h.
+
system.bas also holds MOUSE_WHEEL_STEP_MIN / MAX / DEFAULT, MOUSE_DBLCLICK_DEFAULT_MS, and MOUSE_SPEED_DEFAULT, and SYS_VIDEO_DEFAULT_WIDTH / HEIGHT / BPP (DVX_DEFAULT_VIDEO_W / H / BPP, the mode prefsGetVideoMode and safe mode fall back to), as CONSTs; the basdev test suite (caseIncludeConsts) checks each against dvxTypes.h.
The Store / Load routines are the ones the shell uses at start (dvxMouseFromPrefs, dvxColorsFromPrefs, dvxWallpaperFromPrefs on DVX.INI) and Control Panel uses for OK and for its Cancel snapshot (an in-memory prefsCreate handle), so the INI keys are written in one place (dvxApp.c).
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.
Header: sql/dvxSql.h
SQLite Wrapping
-
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:
+
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:
Database table: each slot holds a sqlite3 * plus a per-database error string and affected-row count.
Cursor table: each slot holds a sqlite3_stmt *, the owning database handle, and EOF tracking.
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:
Double-quoted text is always an identifier (SQLITE_DQS=0): write string literals in single quotes. SELECT "abc" fails with "no such column: abc".
-
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.
-
Temporary tables, sorts and statement journals are kept in memory (PRAGMA temp_store = FILE switches a connection to temporary files).
+
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.
+
Temporary tables, sorts, and statement journals are kept in memory (PRAGMA temp_store = FILE switches a connection to temporary files).
New databases use 1 KB pages; each connection caches up to 512 KB. Existing databases keep their own page size.
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.
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.
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.
-
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.
+
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.
Randomness comes from the serial library's generator (secRngBytes) when that library is loaded, else from platformEntropyStamp mixed with the time of day.
-
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.
+
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.
Handle Model
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.
Parameter Description
--------- -----------
db Database handle.
-
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.
+
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.
dvxSqlAffectedRows
int32_t dvxSqlAffectedRows(int32_t db);
Return the number of rows inserted, updated, or deleted by the last dvxSqlExec call on this handle.
@@ -8202,23 +8573,23 @@ dvxSqlExec(db, sql);
File Operations Library
-
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.
+
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.
Header: fileops/fileOps.h
Loaded as: bin/libs/kpunch/fileops/fileops.lib
DOS Disk Errors
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.
Big Disks and Long File Names
-
Disks past 2 GB report their real size and free space: platformDiskSpace asks DOS with the FAT32 call first and falls back to the older one, which stops near 2 GB. File sizes are 64-bit (FileOpsEntryT.size, fileOpsTreeSize, the job byte counts); a FAT32 file reaches 4 GB - 1 byte, and platformStatSize reads it correctly where stat's st_size would be negative. Where a long-name driver is running (Windows, DOSLFN) and covers the drive (platformLongNames), listings show long names, jobs copy and move them, attributes and renames work on them, fileOpsValidateName accepts them and fileOpsPathNormalize keeps their case; elsewhere the 8.3 rules apply.
+
Disks past 2 GB report their real size and free space: platformDiskSpace asks DOS with the FAT32 call first and falls back to the older one, which stops near 2 GB. File sizes are 64-bit (FileOpsEntryT.size, fileOpsTreeSize, the job byte counts); a FAT32 file reaches 4 GB - 1 byte, and platformStatSize reads it correctly where stat's st_size would be negative. Where a long-name driver is running (Windows, DOSLFN) and covers the drive (platformLongNames), listings show long names, jobs copy and move them, attributes and renames work on them, fileOpsValidateName accepts them, and fileOpsPathNormalize keeps their case; elsewhere the 8.3 rules apply.
Attributes and Dates
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.
-
Listing, Names and Paths
+
Listing, Names, and Paths
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.
+ 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".
@@ -8231,12 +8602,12 @@ dvxSqlExec(db, sql);
fileOpsValidateName(dir, name, err, errSize) Check a new name for folder dir by the rules of its disk (platformValidateFilename): on DOS long names where the drive takes them (platformLongNames), else 8.3; never a device name.
fileOpsPathJoin(out, size, dir, name) dir + separator + name in native separators (platformPathJoin: no doubled separator; an absolute name stands alone); false when it does not fit.
fileOpsPathParent(path, out, size) Parent folder, in native separators; false at a root.
- fileOpsPathIsRoot(path) True for "C:\", "C:" or "/".
+ fileOpsPathIsRoot(path) True for "C:\", "C:", or "/".
fileOpsPathIsInside(path, dir) True when path is dir or below it (platformPathIsUnder).
fileOpsPathSame(a, b) Same place (platformPathSame: separators equal, case ignored, a trailing separator ignored).
fileOpsPathNormalize(path, out, size) Canonical absolute form of an existing folder (DOS: upper case, except names on a disk with long names, which keep their case).
Jobs
-
A job copies, moves or deletes any number of files and folders (recursively):
+
A job copies, moves, or deletes any number of files and folders (recursively):
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.
+ 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.
Until it is answered, the job keeps returning the same question. What the job does:
@@ -8280,12 +8651,12 @@ fileOpsJobFree(job);
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.
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.
BASIC
-
src/include/basic/fileops.bas declares the BASIC functions (fileOpsBas.c): whole trees at once (FileOpsCopyTree, FileOpsMoveTree, FileOpsDeleteTree), drives and disks (FileOpsDriveList$, FileOpsDriveType (the PlatformDriveTypeE of platformDriveList, CONSTs FILEOPS_DRIVE_*; -1 for no such drive), FileOpsBootDrive$ (platformBootDrive, "" when none), FileOpsFreeKb, FileOpsFreeBytes and FileOpsTotalBytes (platformDiskSpace), FileOpsIsWritable (platformDirIsWritable), FileOpsReadOnlySystem (platformIsReadOnly), FileOpsTempDir$ (platformTempDir, "" when none)), folder listings (FileOpsList, FileOpsListCount, FileOpsListSort, FileOpsListFind, FileOpsListName$, FileOpsListIsFolder, FileOpsListSize, FileOpsListRows$, FileOpsListFree), paths and names (FileOpsPathNormalize$, FileOpsPathParent$, FileOpsPathJoin$, FileOpsPathSame, FileOpsPathIsRoot, FileOpsPathIsAbsolute (platformPathIsAbsolute), FileOpsPathIsInside, FileOpsPathSep$ (DVX_PATH_SEP), FileOpsPathNative$ (platformPathNative), FileOpsFilterMatch (platformFilterMatch), FileOpsBaseName$, FileOpsExtension$), files and folders (FileOpsMakeDir, FileOpsMakePath (platformMkdirRecursive), FileOpsRename, FileOpsError$, FileOpsTempPath$ and FileOpsReplaceFile (an atomic save: platformPathSibling + platformReplaceFile), FileOpsValidName (platformValidateFilename for a name in a folder), FileOpsValidLongName and FileOpsValidShortName (platformValidateLongName / platformValidateShortName, the rules alone), FileOpsLongNames (platformLongNames), FileOpsShortPath$ (platformShortPath, "" when it does not fit), FileOpsIsFolder, FileOpsGetAttr, FileOpsSetAttr, FileOpsModified$, FileOpsSetModified (fileOpsParseDate + fileOpsSetTime), FileOpsTreeSize with FileOpsTreeBytes / FileOpsTreeFiles / FileOpsTreeFolders, FileOpsFormatSize$) and jobs (FileOpsJobCreate, FileOpsJobAddSource, FileOpsJobClearAttr (masked to FILEOPS_ATTR_EDITABLE), FileOpsJobRunFor, FileOpsJobMessage$, FileOpsJobAnswer, FileOpsJobCancel, FileOpsJobCurrent$, FileOpsJobFilesDone, FileOpsJobFilesTotal, FileOpsJobPercent, FileOpsJobBytesDone, FileOpsJobBytesTotal (FileOpsProgressT; byte counts are DOUBLE), FileOpsJobFree). The fileops.bas CONSTs mirror fileOps.h and dvxPlat.h; the basdev test suite (caseIncludeConsts) checks each against its C value.
-
Listings and jobs are handles (small numbers, 0 = none) into a table owned by the library; each records the app that made it, and the shell's app-cleanup hook frees what an app leaves open when it ends. FileOpsJobRunFor(job, ms) steps the job until the time is used or it needs an answer, so a BASIC Timer can run it without blocking the program. A returned string stays valid until the next call returning one (the BASIC runtime copies it at once). Single files are handled by the built-in FILECOPY, KILL, NAME, MKDIR, RMDIR, GETATTR and SETATTR statements.
+
src/include/basic/fileops.bas declares the BASIC functions (fileOpsBas.c): whole trees at once (FileOpsCopyTree, FileOpsMoveTree, FileOpsDeleteTree), drives and disks (FileOpsDriveList$, FileOpsDriveType (the PlatformDriveTypeE of platformDriveList, CONSTs FILEOPS_DRIVE_*; -1 for no such drive), FileOpsBootDrive$ (platformBootDrive, "" when none), FileOpsFreeKb, FileOpsFreeBytes, and FileOpsTotalBytes (platformDiskSpace), FileOpsIsWritable (platformDirIsWritable), FileOpsReadOnlySystem (platformIsReadOnly), FileOpsTempDir$ (platformTempDir, "" when none)), folder listings (FileOpsList, FileOpsListCount, FileOpsListSort, FileOpsListFind, FileOpsListName$, FileOpsListIsFolder, FileOpsListSize, FileOpsListRows$, FileOpsListFree), paths and names (FileOpsPathNormalize$, FileOpsPathParent$, FileOpsPathJoin$, FileOpsPathSame, FileOpsPathIsRoot, FileOpsPathIsAbsolute (platformPathIsAbsolute), FileOpsPathIsInside, FileOpsPathSep$ (DVX_PATH_SEP), FileOpsPathNative$ (platformPathNative), FileOpsFilterMatch (platformFilterMatch), FileOpsBaseName$, FileOpsExtension$), files and folders (FileOpsMakeDir, FileOpsMakePath (platformMkdirRecursive), FileOpsRename, FileOpsError$, FileOpsTempPath$ and FileOpsReplaceFile (an atomic save: platformPathSibling + platformReplaceFile), FileOpsValidName (platformValidateFilename for a name in a folder), FileOpsValidLongName and FileOpsValidShortName (platformValidateLongName / platformValidateShortName, the rules alone), FileOpsLongNames (platformLongNames), FileOpsShortPath$ (platformShortPath, "" when it does not fit), FileOpsIsFolder, FileOpsGetAttr, FileOpsSetAttr, FileOpsModified$, FileOpsSetModified (fileOpsParseDate + fileOpsSetTime), FileOpsTreeSize with FileOpsTreeBytes / FileOpsTreeFiles / FileOpsTreeFolders, FileOpsFormatSize$), and jobs (FileOpsJobCreate, FileOpsJobAddSource, FileOpsJobClearAttr (masked to FILEOPS_ATTR_EDITABLE), FileOpsJobRunFor, FileOpsJobMessage$, FileOpsJobAnswer, FileOpsJobCancel, FileOpsJobCurrent$, FileOpsJobFilesDone, FileOpsJobFilesTotal, FileOpsJobPercent, FileOpsJobBytesDone, FileOpsJobBytesTotal (FileOpsProgressT; byte counts are DOUBLE), FileOpsJobFree). The fileops.bas CONSTs mirror fileOps.h and dvxPlat.h; the basdev test suite (caseIncludeConsts) checks each against its C value.
+
Listings and jobs are handles (small numbers, 0 = none) into a table owned by the library; each records the app that made it, and the shell's app-cleanup hook frees what an app leaves open when it ends. FileOpsJobRunFor(job, ms) steps the job until the time is used or it needs an answer, so a BASIC Timer can run it without blocking the program. A returned string stays valid until the next call returning one (the BASIC runtime copies it at once). Single files are handled by the built-in FILECOPY, KILL, NAME, MKDIR, RMDIR, GETATTR, and SETATTR statements.
Game Helpers Library
-
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).
+
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).
Header: game/game.h
Loaded as: bin/libs/kpunch/game/game.lib
Frame Pacing
@@ -8318,14 +8689,14 @@ while (running) {
Help File Library
-
Reads compiled DVX help files (.hlp, written by the help compiler dvxhlpc or HelpCompile): topics, the table of contents, the keyword index, each topic's content records in order, pictures and full-text search. The Help Viewer, the HelpView widget (BASIC HelpPane) and BASIC's hlpfile.bas all read help files through it, so they find, search and show topics the same way.
+
Reads compiled DVX help files (.hlp, written by the help compiler dvxhlpc or HelpCompile): topics, the table of contents, the keyword index, each topic's content records in order, pictures, and full-text search. The Help Viewer, the HelpView widget (BASIC HelpPane), and BASIC's hlpfile.bas all read help files through it, so they find, search, and show topics the same way.
Header: hlpfile/hlpFile.h (the file format: hlpfile/hlpFormat.h, shared with the help compiler)
Loaded as: bin/libs/kpunch/hlpfile/hlpfile.lib
Opening and Topics
A file is an opaque HlpFileT. Strings returned point into the file's string table and stay valid until hlpFileClose.
Function Description
-------- -----------
- hlpFileOpen(path) Open a help file; NULL when it cannot be read or is not a help file. The header, topic directory, strings, table of contents, index and search index are read at once; topic content and pictures are read when asked for.
+ hlpFileOpen(path) Open a help file; NULL when it cannot be read or is not a help file. The header, topic directory, strings, table of contents, index, and search index are read at once; topic content and pictures are read when asked for.
hlpFileClose(f) Close it (NULL is ignored).
hlpFileTopicCount(f) Number of topics.
hlpFileTopicId(f, topic) / Title(f, topic) A topic's ID (what links and HelpTopic name) and title; "" out of range.
@@ -8335,7 +8706,7 @@ while (running) {
hlpFileTocCount(f) / hlpFileToc(f, i) Table of contents entries (HlpTocEntryT: title string, topic or HLP_NO_TOPIC for a section heading, depth, HLP_TOC_EXPANDED flag).
hlpFileIndexCount(f) / hlpFileIndex(f, i) Keyword index entries (HlpIndexEntryT: keyword string, topic), sorted.
Records and Pictures
-
hlpFileForEachRecord(f, topic, fn, userData) calls fn(hdr, payload, userData) for each record of a topic in order (HLP_REC_TEXT, HEADING1 .. 3, LIST_ITEM, NOTE with its HLP_NOTE_ kind in hdr->flags, CODE, TABLE, IMAGE with its HLP_IMG_ alignment, LINK, HRULE); the payload is NUL-terminated. fn returns false to stop. hlpRecordLink gives a link record's target topic ID and display text (the target when the text is empty); hlpRecordImage its picture's place in the file, and hlpFileReadImage the picture itself: the image file as the compiler took it in (BMP, PNG, JPEG or GIF), malloc'd, for dvxLoadImageAlpha. Reading a picture inside the record walk is safe.
+
hlpFileForEachRecord(f, topic, fn, userData) calls fn(hdr, payload, userData) for each record of a topic in order (HLP_REC_TEXT, HEADING1 .. 3, LIST_ITEM, NOTE with its HLP_NOTE_ kind in hdr->flags, CODE, TABLE, IMAGE with its HLP_IMG_ alignment, LINK, HRULE); the payload is NUL-terminated. fn returns false to stop. hlpRecordLink gives a link record's target topic ID and display text (the target when the text is empty); hlpRecordImage its picture's place in the file, and hlpFileReadImage the picture itself: the image file as the compiler took it in (BMP, PNG, JPEG, or GIF), malloc'd, for dvxLoadImageAlpha. Reading a picture inside the record walk is safe.
hlpWrapText(text, cols) wraps flowing text at cols columns (at least HLP_MIN_WRAP_COLS) as the viewer does, a malloc'd copy with '\n' at the wrap points.
Search
hlpFileSearch(f, query, matches) finds the topics whose title or text contains every word of query (any case, separated by blanks), plus those with an index keyword containing the whole query, setting matches[topic] (hlpFileTopicCount bytes) and returning the count (-1 on a failure). The query is not changed. The compiler's trigram index narrows the candidates first; the trigram characters (hlpTrigramSymbol) and the record types searched (hlpRecordSearchable) are defined once in hlpFormat.h for both. Words with no three letters or digits in a row cannot use the index, so then only titles and keywords are searched.
@@ -8352,7 +8723,7 @@ free(matches);
BASIC
-
src/include/basic/hlpfile.bas declares the BASIC functions (hlpFileBas.c): HlpOpen, HlpClose, HlpTopicCount, HlpTopicId$, HlpTopicTitle$, HlpFindTopic, HlpStartTopic, HlpTocCount, HlpTocTitle$, HlpTocTopic, HlpTocDepth, HlpTocExpanded, HlpIndexCount, HlpIndexKeyword$, HlpIndexTopic, HlpSearch, HlpSearchTopic, HlpLoadTopic, HlpRecordType, HlpRecordFlags, HlpRecordText$, HlpRecordLink$, HlpRecordImageSave and HlpWrap$, with the HLP_REC_, HLP_NOTE_ and HLP_IMG_ constants of hlpFormat.h.
+
src/include/basic/hlpfile.bas declares the BASIC functions (hlpFileBas.c): HlpOpen, HlpClose, HlpTopicCount, HlpTopicId$, HlpTopicTitle$, HlpFindTopic, HlpStartTopic, HlpTocCount, HlpTocTitle$, HlpTocTopic, HlpTocDepth, HlpTocExpanded, HlpIndexCount, HlpIndexKeyword$, HlpIndexTopic, HlpSearch, HlpSearchTopic, HlpLoadTopic, HlpRecordType, HlpRecordFlags, HlpRecordText$, HlpRecordLink$, HlpRecordImageSave, and HlpWrap$, with the HLP_REC_, HLP_NOTE_, and HLP_IMG_ constants of hlpFormat.h.
A handle (a small number, 0 = none) is an open file plus what the program last asked of it -- HlpSearch keeps the topics found for HlpSearchTopic, HlpLoadTopic copies a topic's records for the HlpRecord functions -- since BASIC cannot take a callback or a byte array. Each handle records the program that opened it; the shell's app-cleanup hook closes what a program leaves open when it ends. hlpFileBasHandle(handle) gives the HlpFileT behind a handle to C code (the HelpPane control's ShowTopic uses it). A returned string stays valid until the next call returning one (the BASIC runtime copies it at once).
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).
+
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).
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.
-
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).
+
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).
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.
Events
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.
@@ -8393,12 +8764,12 @@ free(matches);
Two settings are offered as lists: joyChoices(which, &count) returns the choices of JoyChoiceGamePortE (gameport.mode: "Two joysticks", "One joystick with 4 axes") or JoyChoiceFallbackE (fallback: "When no joystick is found", "Always", "Never") in list order, each a PrefsChoiceT (dvxPrefs.h) with its label for people and the name stored; joyChoiceGet(which) is the index of the current one (the default for a missing or unknown value) and joyChoiceSet(which, index) stores one. The keyboard joystick's keys go by slot (0 to JOY_KBD_SLOT_COUNT - 1 in JOY_KBD_SLOT_NAMES order: up, down, left, right, button1 to button4): joyKbdSlotGet(slot) is the joyKeyName index of the slot's key (its default key when the setting names no known key), joyKbdSlotSet(slot, keyIndex) stores one; the keyboard driver reads its keys through joyKbdSlotGet. joyKeyIndex(name) finds a key name's index (joyKeyFind its raw code). The Control Panel's Game Controllers page uses these, so the stored names and the defaults live only in the library.
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. joyRescanApp(appId) closes every handle of that program first (joyAppCleanup) and then rescans, for a program that holds handles itself (the BASIC JoyRescan; the Control Panel closes its one handle and calls joyRescan).
The Game Port Driver
-
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.
+
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.
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.
The Keyboard Joystick
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.
Writing a Driver
-
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.
+
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.
Member Meaning
------ -------
hdr.name Lower case, no blanks ("gameport"); the prefix of the device ids and of the driver's own [joystick] keys.
@@ -8406,22 +8777,22 @@ free(matches);
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.
+ 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.
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).
BASIC
-
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.
-
For a settings program (the Control Panel's Game Controllers page) it also declares: JoyHats, JoyAxisPresent(joy, axis), JoyCanCalibrate(joy) (JoyInfoT hatCount, axisPresent, JOY_CAP_CALIBRATE); JoyConnected(joy) (joyRead's connected); JoyRescan (joyRescanApp for the calling program: its own handles and STICK readings go first; -1 while another program has a joystick open); JoyRawAxis(joy, axis) and JoyRawButtons(joy) (joyGetRaw); JoySetCalibration(joy, axis, minV, centerV, maxV) and JoyClearCalibration(joy, axis) (joySetCalibration); JoyDeadZone / JoySetDeadZone (joyGetDeadZone / joySetDeadZone); JoyChoiceCount(which), JoyChoiceLabel$(which, index), JoyChoice(which), JoySetChoice(which, index) with JOY_CHOICE_GAMEPORT = 0 and JOY_CHOICE_FALLBACK = 1 (JoyChoiceE; joyChoices / joyChoiceGet / joyChoiceSet); JoyKeyCount and JoyKeyName$(index); JoyKbdSlotCount, JoyKbdSlot(slot), JoySetKbdSlot(slot, keyIndex) (joyKbdSlotGet / Set); JoySetting$(key, defaultValue) and JoySetSetting(key, value) ("" removes; joySettingGet / Set); JoySettingsLoad(prefs) and JoySettingsStore(prefs) with a prefs.bas handle (prefsBasHandle); JoyCalibration(joy, axis, part) (joyGetCalibration; part JOY_CAL_MIN / JOY_CAL_CENTER / JOY_CAL_MAX, joystick.h; 0 when joyGetCalibration fails); JoyIsKeyboard(joy) (JOY_CAP_KEYBOARD); JoyKeyIndex(keyName), JoyKeyCode(index), JoyKeyNameOf$(rawCode) (joyKeyIndex / joyKeyCode / joyKeyNameOf). CONSTs JOY_DEADZONE_DEFAULT / _MAX, JOY_POLL_HZ_DEFAULT / _MIN / _MAX and JOY_KEY_POLL_HZ mirror joystick.h. Lists (choices, keys, slots) are numbered from 0; True is -1.
+
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.
+
For a settings program (the Control Panel's Game Controllers page) it also declares: JoyHats, JoyAxisPresent(joy, axis), JoyCanCalibrate(joy) (JoyInfoT hatCount, axisPresent, JOY_CAP_CALIBRATE); JoyConnected(joy) (joyRead's connected); JoyRescan (joyRescanApp for the calling program: its own handles and STICK readings go first; -1 while another program has a joystick open); JoyRawAxis(joy, axis) and JoyRawButtons(joy) (joyGetRaw); JoySetCalibration(joy, axis, minV, centerV, maxV) and JoyClearCalibration(joy, axis) (joySetCalibration); JoyDeadZone / JoySetDeadZone (joyGetDeadZone / joySetDeadZone); JoyChoiceCount(which), JoyChoiceLabel$(which, index), JoyChoice(which), JoySetChoice(which, index) with JOY_CHOICE_GAMEPORT = 0 and JOY_CHOICE_FALLBACK = 1 (JoyChoiceE; joyChoices / joyChoiceGet / joyChoiceSet); JoyKeyCount and JoyKeyName$(index); JoyKbdSlotCount, JoyKbdSlot(slot), JoySetKbdSlot(slot, keyIndex) (joyKbdSlotGet / Set); JoySetting$(key, defaultValue) and JoySetSetting(key, value) ("" removes; joySettingGet / Set); JoySettingsLoad(prefs) and JoySettingsStore(prefs) with a prefs.bas handle (prefsBasHandle); JoyCalibration(joy, axis, part) (joyGetCalibration; part JOY_CAL_MIN / JOY_CAL_CENTER / JOY_CAL_MAX, joystick.h; 0 when joyGetCalibration fails); JoyIsKeyboard(joy) (JOY_CAP_KEYBOARD); JoyKeyIndex(keyName), JoyKeyCode(index), JoyKeyNameOf$(rawCode) (joyKeyIndex / joyKeyCode / joyKeyNameOf). CONSTs JOY_DEADZONE_DEFAULT / _MAX, JOY_POLL_HZ_DEFAULT / _MIN / _MAX, and JOY_KEY_POLL_HZ mirror joystick.h. Lists (choices, keys, slots) are numbered from 0; True is -1.
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.
Writing Games for DVX
-
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.
+
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.
Starting From a Template
bin/sdk/newProject.sh game myGame a C game
bin/sdk/newProject.sh basicgame myGame a DVX BASIC game
-
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.
+
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.
Set gameInput on the game's window and give it an onRawKey callback if it wants every key going down and up:
win->gameInput = true;
win->onKey = gameKey; // typed keys: every key but three
win->onRawKey = gameRawKey; // each key going down and up
-
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:
+
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:
Key Always does
--- -----------
Alt+Tab Switch to the next window
Ctrl+Esc Open the Task Manager
Alt+F4 Close the window (its onClose)
-
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.
-
The key that completes one of the three (Esc with Ctrl held, Tab or F4 with Alt held) never reaches the game: not in onRawKey, not in the held-key table, not its repeats and not its release. The modifier itself does, because when Ctrl or Alt goes down nobody can tell yet what will follow. A game that treats any key as "go on" therefore still sees the Ctrl or Alt of Ctrl+Esc or Alt+Tab.
+
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.
+
The key that completes one of the three (Esc with Ctrl held, Tab or F4 with Alt held) never reaches the game: not in onRawKey, not in the held-key table, not its repeats, and not its release. The modifier itself does, because when Ctrl or Alt goes down nobody can tell yet what will follow. A game that treats any key as "go on" therefore still sees the Ctrl or Alt of Ctrl+Esc or Alt+Tab.
Held Keys
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.
Typed Characters and the Mouse
@@ -8486,7 +8857,7 @@ win->onRawKey = gameRawKey; // each key going down and up
The Screen: Framebuffers and Palettes
-
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.
+
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.
The Colour Lookup Table
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):
const DisplayT *d = dvxGetDisplay(ctx);
@@ -8503,7 +8874,7 @@ for (int32_t i = 0; i < 256; i++) {
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.
-
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.
+
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.
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.
dvxExclusiveBegin sets the smallest mode of at least the size asked for, at the depth asked for when there is one (0: any). At 8 bits per pixel a request that fits 320x200 gets VGA mode 13h, and one that fits 320x240 gets VGA Mode X (square pixels, and two pages: a whole-screen flush is shown from the next retrace, so frames never tear), unless the card has a VBE mode no larger. Either way the game draws into an ordinary linear backbuffer. The display may be larger than asked for; dvxExclusiveDisplay says what it is.
The game's window must have the focus. When it loses it (Alt+Tab, Ctrl+Esc, a dialog, another program's window coming to the front) the desktop comes back and the callback hears ExclusiveSuspendedE; when the window has the focus again the mode is set again and it hears ExclusiveActiveE. Draw the whole frame then: the screen starts black.
-
While the game has the screen, every key but Alt+Tab, Ctrl+Esc and Alt+F4 goes to its window, as in a game input window; the mouse cursor is not drawn; onMouse gets whole-screen coordinates.
+
While the game has the screen, every key but Alt+Tab, Ctrl+Esc, and Alt+F4 goes to its window, as in a game input window; the mouse cursor is not drawn; onMouse gets whole-screen coordinates.
dvxExclusivePresent8 shows a picture of palette numbers on the whole screen, scaled by the largest whole factor that fits and centred on black, in any pixel format. At 8 bits per pixel the palette is the screen's own: dvxExclusiveSetPalette changes colours at once, so palette animation costs nothing, and a hardware model of up to 16 palettes of 16 colours fits in the 256 entries.
To draw yourself, draw into dvxExclusiveDisplay(ctx)->backBuf (DVX's draw functions work on it with dvxExclusiveBlitOps) and call dvxExclusiveFlush. dvxExclusiveWaitRetrace waits for the vertical retrace before a flush, so the frame is not torn.
-
dvxExclusiveEnd gives the screen back. Closing the window, the program ending and End Task do too.
+
dvxExclusiveEnd gives the screen back. Closing the window, the program ending, and End Task do too.
-
Sound, Music and MIDI
+
Sound, Music, and MIDI
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.
Sound Effects and Tones (audio.h)
audioOpen() gives the program an audio context (the first one opened starts the sound device).
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).
-
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.
-
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).
+
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.
+
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).
audioStreamOpen takes the game's own sound at any rate: written with audioStreamWrite, or asked for by a fill function.
audioSetVolume sets the program's volume; the master volume belongs to the user (Control Panel).
Music (music.h)
-
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.
+
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.
The FM Chip and the MIDI Port
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.
Keeping Sound Fed
-
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.
-
See the Sound Library, the Music Library and the Joystick Library for every function.
+
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.
+
See the Sound Library, the Music Library, and the Joystick Library for every function.
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).
+
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).
Ending
-
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).
+
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).
Games in DVX BASIC
@@ -8596,13 +8967,13 @@ if (!dvxExclusivePresent8(ctx, fb, 320, 200, 320)) {
FRAMEWAIT 30
Loop Until quitting ' set in the form's Unload event
End Sub
-
GameKeyDown (game.bas) tells whether a key is held, GameKeyClear forgets the held keys, FrameMissed() counts the frames FRAMEWAIT skipped and GameWaitRetrace waits for the screen's retrace; 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, sounds loaded once and played with volume, pan and pitch, sound the game makes as it goes (streams) and FM instruments; 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.
+
GameKeyDown (game.bas) tells whether a key is held, GameKeyClear forgets the held keys, FrameMissed() counts the frames FRAMEWAIT skipped, and GameWaitRetrace waits for the screen's retrace; 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, sounds loaded once and played with volume, pan, and pitch, sound the game makes as it goes (streams), and FM instruments; 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.
Porting a Game Library to DVX
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.
The Model: a Window, a Task, a Virtual Frame Rate
-
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. In a window 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. On the whole screen (see The Whole Screen) the game has its own mode, its own palette and the vertical retrace, while the other programs run on.
+
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. In a window 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. On the whole screen (see The Whole Screen) the game has its own mode, its own palette, and the vertical retrace, while the other programs run on.
Mapping a Hardware Layer to DVX
Hardware layer DVX
-------------- ---
@@ -8661,7 +9032,7 @@ End Sub
The 256-colour desktop: nearest colours only; say so in the game's help if it needs 15-bit colour or better.
Checking a Port
-
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.
+
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.
SDK Quick Start
@@ -8670,13 +9041,13 @@ End Sub
---- --------
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)
+ bin/ dvxres (resources), dvxhlpc (help compiler), bascomp (BASIC compiler), mkfont (fonts a program carries), 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
Requirements
-
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).
+
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).
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.
Creating a Project
@@ -8688,13 +9059,13 @@ End Sub
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
+ basicgame A DVX BASIC game (NAME.dbp): a FRAMEWAIT loop, GameKeyDown, STICK, STRIG, and PLAY
control A control written in DVX BASIC (NAME.dbp with NAME.ctl, built to NAME.wgt) usable from C programs and DVX BASIC
The two game templates are explained in Writing Games for DVX.
-
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.
-
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.
+
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.
+
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.
Building
-
Every project Makefile only names the SDK, the kind and the module; the rules come from sdk.mk:
+
Every project Makefile only names the SDK, the kind, and the module; the rules come from sdk.mk:
Command Does
------- ----
make Builds the module with its resources, and NAME.hlp from the help source
@@ -8703,7 +9074,7 @@ End Sub
make clean Removes everything the build made
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.
Programs That Use the Network
-
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:
+
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:
NET = 1
sdk.mk then puts include/net ahead of the C library's headers, and the program uses the ordinary names:
#include <sys/socket.h>
@@ -8742,14 +9113,15 @@ End Sub
File Purpose
---- -------
hello.c The program
- hello.res Resources appended to hello.app: its icon, name, author, description and a greeting text
+ 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
+ Makefile Names the SDK, KIND = app, and NAME = hello
How the Program Works
-
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.
-
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).
-
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).
+
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.
+
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).
+
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).
+
A program can carry fonts of its own as font resources, so its text looks the same whatever fonts were installed: add a line such as "title font TITLE.DFN" to hello.res (dvxres checks that the file is a DVX font). DVX adds the program's fonts when it starts and forgets them when it ends; other programs never see them, and where a family is installed too, the program's sizes and styles come first (dvxText.h, Fonts a Program Carries). Make a .DFN with bin/mkfont from a TrueType, Type 1, or bitmap font -- its manifest names the sizes, whether they are smoothed too, and the characters to keep (mkfont.c's header documents every key; one size and only the characters the program shows keep the file small) -- or copy one from DVX's FONTS folder. A .DFN carries the notice and licence of the font it was made from.
Run
make install DVX_DIR=/path/to/dvx
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.
@@ -8770,7 +9142,7 @@ End Sub
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.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
@@ -8781,14 +9153,14 @@ End Sub
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).
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)).
-
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. wgtIfaceSetType binds the interface to the class, so wgtGetIfaceOf and the generic calls (wgtGetProp, wgtSetProp, wgtCallMethod) find it from any badge widget.
+
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. wgtIfaceSetType binds the interface to the class, so wgtGetIfaceOf and the generic calls (wgtGetProp, wgtSetProp, wgtCallMethod) find it from any badge widget.
DVX_ABI_STAMP(badge) at file scope records the binary interface version. Its argument must be the module's file name in lower case.
Run
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:
In the DVX BASIC toolbox: draw a Badge on a form, set its Style in the property panel.
-
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.
+
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.
Warning: 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.
Help
@@ -8817,13 +9189,13 @@ End Sub
mathx.dhs Developer help, added to the system help
Makefile KIND = library
How the Library Works
-
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.
+
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.
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 *.
-
A string declared AS STRING is text and stops at the first zero byte. For bytes (network data, samples, encrypted data) declare BINARY STRING: each BINARY STRING parameter becomes two C parameters at its place, the bytes (const uint8_t *, a private copy valid during the call, followed by a zero byte) and their count (int32_t); a BINARY STRING result adds one last C parameter, int32_t *outLen, where the function stores the length of the bytes it returns (the runtime copies them right after the call, so a static buffer is fine; NULL, a negative length or a length never set give an empty string). The template's tplNameXor shows both:
+
A string declared AS STRING is text and stops at the first zero byte. For bytes (network data, samples, encrypted data) declare BINARY STRING: each BINARY STRING parameter becomes two C parameters at its place, the bytes (const uint8_t *, a private copy valid during the call, followed by a zero byte) and their count (int32_t); a BINARY STRING result adds one last C parameter, int32_t *outLen, where the function stores the length of the bytes it returns (the runtime copies them right after the call, so a static buffer is fine; NULL, a negative length, or a length never set give an empty string). The template's tplNameXor shows both:
// tplNameXor$(BYVAL bytes AS BINARY STRING, BYVAL key AS LONG) AS BINARY STRING
const uint8_t *tplNameXor(const uint8_t *data, int32_t len, int32_t key, int32_t *outLen);
Run
-
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.
+
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.
Help
mathx.dhs goes into the system help, under Technical Reference > Libraries, when DVX next starts with the library installed.
Next
@@ -8845,9 +9217,9 @@ End Sub
icon32.bmp The program's icon (the project's Icon entry)
greeter.dhs The program's help, compiled to greeter.hlp
Makefile KIND = basic
-
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.
+
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; fonts the program carries go in its [Fonts] section (Project > Properties in DVX BASIC), and the build makes them font resources.
Run and Debug
-
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.
+
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.
@@ -8865,7 +9237,7 @@ End Sub
icon24.bmp The toolbox picture (the header's ToolboxIcon)
dial.bhs The control's BASIC help, installed beside it and added to the DVX BASIC help
Makefile KIND = control
-
The control's Public properties, methods and events are what programs use; UserControl is its drawing surface. The DVX BASIC help (UserControls: Your Own Controls) describes the language side.
+
The control's Public properties, methods, and events are what programs use; UserControl is its drawing surface. The DVX BASIC help (UserControls: Your Own Controls) describes the language side.
Install
make install puts dial.wgt and DIAL.BHS in WIDGETS/LOCAL/DIAL/. When DVX starts it reads the control file through the BASIC run-time library and registers its controls; BASIC programs put a Dial on a form (Begin Dial Dial1 in a .frm, or from the designer's toolbox) and C programs create one by name (see Using BASIC Controls from C).
Next
@@ -8874,7 +9246,7 @@ End Sub
Using BASIC Controls from C
-
A control written in DVX BASIC is an ordinary widget type once it is registered: it has a widget class, an interface (WgtIfaceT) with its properties, methods and events, and a type name equal to its BASIC name. A C program uses it through the generic widget access, without a header of its own:
+
A control written in DVX BASIC is an ordinary widget type once it is registered: it has a widget class, an interface (WgtIfaceT) with its properties, methods, and events, and a type name equal to its BASIC name. A C program uses it through the generic widget access, without a header of its own:
WidgetT *dial = wgtCreateByName("Dial", parent);
WgtValueT v = wgtValueInt(40);
WgtValueT ret;
@@ -8889,7 +9261,7 @@ End Sub
... // args[0]: the event's first argument
}
}
-
Property values follow the BASIC types: Integer, Long and Byte are WGT_IFACE_INT, Boolean WGT_IFACE_BOOL, Single WGT_IFACE_FLOAT, Double and Currency WGT_IFACE_DOUBLE, String, Date and Variant WGT_IFACE_STRING. A property declared As an Enum whose members are numbered 0, 1, 2 ... (each number once) is WGT_IFACE_ENUM with the member names in value order (WgtPropDescT.enumNames; wgtSetProp takes the number or a member name, wgtIfaceGetPropText gives the name); an Enum with other values is WGT_IFACE_INT. A method takes its arguments as they come (WGT_SIG_DYNAMIC) and returns its Function's value, or no value (WGT_IFACE_NONE) for a Sub. An event's numeric and Boolean arguments that onEvent changes go back to the control (ByRef parameters). Events the control names Click, DblClick, Change, KeyPress, KeyDown, KeyUp, MouseDown, MouseUp, MouseMove or Scroll go to the matching universal callback (onClick ...) instead; the dispatcher calls those for a BASIC control only when its code raises them (WCLASS_RAISES_EVENTS). The interface's events list each event with its parameters as BASIC writes them (WgtEventDescT.params).
+
Property values follow the BASIC types: Integer, Long, and Byte are WGT_IFACE_INT, Boolean WGT_IFACE_BOOL, Single WGT_IFACE_FLOAT, Double and Currency WGT_IFACE_DOUBLE, String, Date, and Variant WGT_IFACE_STRING. A property declared As an Enum whose members are numbered 0, 1, 2 ... (each number once) is WGT_IFACE_ENUM with the member names in value order (WgtPropDescT.enumNames; wgtSetProp takes the number or a member name, wgtIfaceGetPropText gives the name); an Enum with other values is WGT_IFACE_INT. A method takes its arguments as they come (WGT_SIG_DYNAMIC) and returns its Function's value, or no value (WGT_IFACE_NONE) for a Sub. An event's numeric and Boolean arguments that onEvent changes go back to the control (ByRef parameters). Events the control names Click, DblClick, Change, KeyPress, KeyDown, KeyUp, MouseDown, MouseUp, MouseMove, or Scroll go to the matching universal callback (onClick ...) instead; the dispatcher calls those for a BASIC control only when its code raises them (WCLASS_RAISES_EVENTS). The interface's events list each event with its parameters as BASIC writes them (WgtEventDescT.params).
wgtCreateByName goes through the interface's dynamic create hook (WgtIfaceT.dyn); the class has no C API structure (wgtGetApi finds none), because one create function cannot tell which BASIC class to make.
The Control File
A control project builds NAME.wgt, a file that holds only resources:
@@ -8902,7 +9274,7 @@ End Sub
author, version ... The project's metadata; helpfile its help file's name
The loader tells such a file from a module by its first bytes: instead of loading it, it hands it, once every library and native widget is loaded, to the BASIC run-time library's basCtlLoadFile (found with dlsym; without the library the file is skipped and logged). basCtlLoadFile registers each control in the file for every program and the loader records the file as theirs, so the toolbox finds their icon24 and name.
The Bridge
-
Each control is one object of its BASIC class plus a border-less canvas, the UserControl surface its code draws on (the same drawing code as a BASIC PictureBox). The widget class forwards to the class's code: layout sizes the canvas, and between frames a new size runs UserControl_Resize and UserControl_Paint; the mouse, key, wheel and focus class methods run UserControl_MouseDown and the other handlers. A control file's code runs in one VM shared by every program that uses its controls; it never yields, and a call from another task while it runs code is refused (the paint keeps the old picture, the event is dropped). A BASIC program's own controls ([Controls] in its project) are registered by the program when it starts, owned by it, and run in its own VM.
+
Each control is one object of its BASIC class plus a border-less canvas, the UserControl surface its code draws on (the same drawing code as a BASIC PictureBox). The widget class forwards to the class's code: layout sizes the canvas, and between frames a new size runs UserControl_Resize and UserControl_Paint; the mouse, key, wheel, and focus class methods run UserControl_MouseDown and the other handlers. A control file's code runs in one VM shared by every program that uses its controls; it never yields, and a call from another task while it runs code is refused (the paint keeps the old picture, the event is dropped). A BASIC program's own controls ([Controls] in its project) are registered by the program when it starts, owned by it, and run in its own VM.
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, popup scrollbar hit testing (with the same thumb geometry the window manager paints), and the icon view rules of Program Manager's view, which BASIC icon views share through listhelp.bas.
+
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, text measurement in any font (fixed-pitch or proportional), keyboard navigation, type-ahead search, popup scrollbar hit testing (with the same thumb geometry the window manager paints), and the icon view rules of Program Manager's view, which BASIC icon views share through listhelp.bas.
Header: listhelp/listHelp.h
Loaded as: bin/libs/listhelp.lib
Constants
@@ -9110,23 +9482,25 @@ End Sub
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.
ListItemsT (owned-string item list)
-
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.
+
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). maxW caches the widest item's width in pixels of widthFont (NULL until it is first measured); listItemsMaxWidth returns it, measuring every item again when asked for another font, and the mutators keep it current incrementally. NULL entries in a caller's array read as empty strings.
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.
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, and never reads the caller's previous array (it may be freed already). The mutators measure in font, the widget's own (wgtFont), and return true when the widest item's width 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.
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.
ListSelT (selection model)
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.
@@ -9143,7 +9517,7 @@ bool listItemsSetExternal(ListItemsT *li, const char **items, int32_t cou
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).
+ 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.
@@ -9160,7 +9534,7 @@ void listReorderTrack(int32_t relY, int32_t rowH, int32_t visibleRows, int32_
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);
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.
ListPopupT (dropdown popup)
-
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.
+
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.
typedef struct {
ListItemsT items;
int32_t selectedIdx;
@@ -9173,15 +9547,15 @@ void listReorderPaint(DisplayT *d, const BlitOpsT *ops, int32_t x, int32_t to
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).
+ 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).
+ 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.
+ listPopupCalcMinSize(w, font, p) Minimum size from the widest item in font.
listPopupEnsureVisible(p) Scroll the hover item into view.
listPopupVisibleCount(itemCount) Rows the popup shows (1..DROPDOWN_MAX_VISIBLE).
widgetDrawDropdownArrow
@@ -9194,14 +9568,18 @@ void listReorderPaint(DisplayT *d, const BlitOpsT *ops, int32_t x, int32_t to
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.
-
widgetMaxItemLen
-
Scan an array of strings and return the length (in characters) of the longest item.
Parameter Description
--------- -----------
+ font Font the items are drawn in (fixed-pitch or proportional).
items Array of null-terminated string pointers (NULL entries count as empty).
count Number of items in the array.
-
Returns the character length of the longest item, or 0 if count is zero.
+
Returns the width of the widest item, or 0 if count is zero.
+
listTextFit
+
How many leading characters of text fit whole in maxW pixels of font: the part of an item a list draws when the item is cut to its column. Stops at the end of the text; 0 when maxW is too narrow for the first character.
@@ -9213,7 +9591,7 @@ void listReorderPaint(DisplayT *d, const BlitOpsT *ops, int32_t x, int32_t to
pageSize Number of visible items (used for PgUp/PgDn step size).
Returns the new index, clamped to [0, count-1]. Returns -1 for unrecognized keys so callers can detect whether the key was consumed.
widgetGridNavigateIndex
-
The same keys in a grid of count items laid out row by row, columns items per row (icon views): Left and Right step through the items in order, Up, Down, Page Up and Page Down move by rows (pageRows rows a page) the way widgetNavigateIndex moves through a list -- it is called on the row numbers -- keeping the column, or landing on the last item when the row below is shorter; Home and End go to the first and last item. With nothing selected (current below 0) every one of these keys goes to the first item. With one column it gives what widgetNavigateIndex gives.
+
The same keys in a grid of count items laid out row by row, columns items per row (icon views): Left and Right step through the items in order, Up, Down, Page Up, and Page Down move by rows (pageRows rows a page) the way widgetNavigateIndex moves through a list -- it is called on the row numbers -- keeping the column, or landing on the last item when the row below is shorter; Home and End go to the first and last item. With nothing selected (current below 0) every one of these keys goes to the first item. With one column it gives what widgetNavigateIndex gives.
@@ -9276,7 +9654,7 @@ void listReorderPaint(DisplayT *d, const BlitOpsT *ops, int32_t x, int32_t to
scrollPos [in/out] Current scroll offset; updated in place when the click hits the scrollbar.
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).
Icon Views
-
The rules of an icon view with large icons, small icons or a list: Program Manager's view (progman/pmView.c) is built on them, and so are icon views written in BASIC (listhelp.bas ListIcon...), so both look and behave alike. They cover each view's cell and where an item's picture and name go in it, the grid inside a sunken frame with a scroll bar only when the rows do not fit (which narrows the grid), the two label lines under a large icon, the arrow keys among items in order or in cells of their own (gaps allowed), the scroll bar (a control's, as ListBox's: arrows, trough, thumb drag), when a press becomes a drag, the scrolling and the target cell while dragging, and where a dropped item lands. Positions are pixels from the view's top left (ListIconGridT) or the cell's (ListIconCellT).
+
The rules of an icon view with large icons, small icons, or a list: Program Manager's view (progman/pmView.c) is built on them, and so are icon views written in BASIC (listhelp.bas ListIcon...), so both look and behave alike. They cover each view's cell and where an item's picture and name go in it, the grid inside a sunken frame with a scroll bar only when the rows do not fit (which narrows the grid), the two label lines under a large icon, the arrow keys among items in order or in cells of their own (gaps allowed), the scroll bar (a control's, as ListBox's: arrows, trough, thumb drag), when a press becomes a drag, the scrolling and the target cell while dragging, and where a dropped item lands. Positions are pixels from the view's top left (ListIconGridT) or the cell's (ListIconCellT).
Constant Value Description
-------- ----- -----------
LIST_ICON_FRAME 2 Sunken frame around the view.
@@ -9345,13 +9723,14 @@ int32_t listIconScrollDrag(const ListIconGridT *g, int32_t y, int32_t grab);
+
charW and charH are the caller's character metrics (a font's charWidth and charHeight, or numbers from BASIC, which has no font handle here): rows are a line tall, and a name field or column N characters wide is N * charW. A large icon's label is cut by characters (nameChars, listIconLabelSplit): exact for a fixed-pitch font, an estimate for a proportional one.
Program Manager's pmView.c draws the scroll bar with widgetDrawScrollbarV; a BASIC icon view draws the same bar with DrawScrollBar's control argument True.
An icon view's grid is figured from the view's size on each call (no handle). The LISTSEL_KEY_ and LISTICON_ CONSTs of listhelp.bas are compared with ListSelKeyE, ListIconViewE, LIST_ICON_BY_ORDER and ListIconMetricE by a formrt test.
+
An icon view's grid is figured from the view's size on each call (no handle). The LISTSEL_KEY_ and LISTICON_ CONSTs of listhelp.bas are compared with ListSelKeyE, ListIconViewE, LIST_ICON_BY_ORDER, and ListIconMetricE by a formrt test.
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.
+
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.
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.
Where Music Plays
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.
@@ -9414,10 +9793,10 @@ if (rc != MUSIC_OK) {
software the software synthesizer
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.
General MIDI on FM
-
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.
-
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.
+
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.
+
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.
The Software Synthesizer
-
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.
+
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.
The MPU-401 Port
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:
mpuMode Treatment
@@ -9425,7 +9804,7 @@ if (rc != MUSIC_OK) {
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).
-
Stopping, pausing or freeing a music sends sustain off and all notes off on every channel the synthesizer hears.
+
Stopping, pausing, or freeing a music sends sustain off and all notes off on every channel the synthesizer hears.
Live MIDI
MusicT *keys = NULL;
@@ -9433,21 +9812,21 @@ musicMidiOpen(&keys); // plays at once
musicMidiProgram(keys, 0, 73); // flute on channel 0
musicMidiNoteOn(keys, 0, 60, 100); // middle C
musicMidiNoteOff(keys, 0, 60);
-
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.
+
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.
Modules
-
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.
+
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.
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.
-
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.
+
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.
Several Programs at Once
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.
BASIC
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. MusicGetVolume(music) (musicGetVolume in percent) and MusicInfo$(music, field) (musicGetInfo: "title", "output", "kind" = "module" / "midi" / "live" for MusicKindE, "where" = "" / "stream" / "mpu401" / "fm" / "software" for MusicOutputE, "paused" and "looping" as "-1" / "0"; any case, "" for another field). music.bas also declares the raw MIDI port calls of audio.lib (MidiPortInfo$, MidiAcquire, MidiPut, MidiRelease; see the sound library).
-
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. QBasic's X command and "=" numbers (given with VARPTR$) are filled in by the BASIC runtime before musicBasPlay is called, so the macro language itself has neither. SOUND's duration is in clock ticks (18.2 a second) and 0 stops the queue; BEEP is 800 Hz for a quarter second.
+
The BASIC runtime itself calls musicBasBeep, musicBasSound(hz, ticks), musicBasPlay(macro), musicBasPlayCount, and musicBasBusy for the statements BEEP, SOUND, and PLAY and the PLAY(n) function; each BASIC program has its own macro queue. QBasic's X command and "=" numbers (given with VARPTR$) are filled in by the BASIC runtime before musicBasPlay is called, so the macro language itself has neither. SOUND's duration is in clock ticks (18.2 a second) and 0 stops the queue; BEEP is 800 Hz for a quarter second.
Licences
libxmp-lite (Extended Module Player Lite), (C) 1996-2026 Claudio Matsuoka and Hipolito Carraro Jr, MIT licence:
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.
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.
+
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.
Header: print/print.h
Loaded as: bin/libs/kpunch/print/print.lib
Settings
-
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).
+
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).
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).
@@ -9472,9 +9851,9 @@ musicMmlPlay(tune, "MB T140 O3 L8 CDEC CDEC EFG4 EFG4");
printDestChoices[] / printModelChoices[] The labels and INI spellings (PrefsChoiceT), indexed by PrintDestE / PrintModelE.
printDestLabel(d) / printModelLabel(m) The label of one choice (for programs that look the library up by name).
printDeviceProblem(dest, file) NULL when the destination can be used, else why not (printing to a file needs its name).
- printDeviceStore(h, model, dest, file) Put model, destination and file into the [printer] section of h with the page setup as it is in DVX.INI now (the caller saves h). The Control Panel's OK and print.bas use it.
+ printDeviceStore(h, model, dest, file) Put model, destination, and file into the [printer] section of h with the page setup as it is in DVX.INI now (the caller saves h). The Control Panel's OK and print.bas use it.
Jobs and Printer Problems
-
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.
+
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.
PrintHooksT hooks = { myAsk, myIdle, NULL, myState };
char err[PRINT_MSG_MAX];
PrintJobT *job = printJobOpen(PrintDestLpt1E, NULL, &hooks, err, sizeof(err));
@@ -9490,10 +9869,10 @@ if (!job) {
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.
-
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.
+
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.
Text
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).
-
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.
+
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.
PrintTextInfoT also chooses a page range (firstPage, lastPage; 0 = all) and the number of copies. printTextPageCount returns the number of pages text takes.
Pictures and Printer Drivers
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).
@@ -9509,13 +9888,13 @@ if (!job) {
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).
- printUiTextChoice(ctx, text, title, &choice) Print text on the destination, copies and page range of a PrintChoiceT (a Print dialog answer, or one the program filled) without asking; printUiText ends here.
+ printUiTextChoice(ctx, text, title, &choice) Print text on the destination, copies, and page range of a PrintChoiceT (a Print dialog answer, or one the program filled) without asking; printUiText ends here.
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.
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.
BASIC
-
src/include/basic/print.bas declares PrintTextDialog, PrintText, PrintPictureDialog, PrintPicture, PrintPageSetup and PrintCanPrintPictures. For the printer settings it also declares PrintModelCount / PrintModelLabel$, PrintDestCount / PrintDestLabel$ / PrintDestIsFile, PrintSettingModel / PrintSettingDest / PrintSettingFile$ (printSettingsRead on a prefs.bas handle, through prefsBasHandle), PrintSettingsProblem$ (printDeviceProblem) and PrintSettingsStore (printDeviceStore).
+
src/include/basic/print.bas declares PrintTextDialog, PrintText, PrintPictureDialog, PrintPicture, PrintPageSetup, and PrintCanPrintPictures. For the printer settings it also declares PrintModelCount / PrintModelLabel$, PrintDestCount / PrintDestLabel$ / PrintDestIsFile, PrintSettingModel / PrintSettingDest / PrintSettingFile$ (printSettingsRead on a prefs.bas handle, through prefsBasHandle), PrintSettingsProblem$ (printDeviceProblem), and PrintSettingsStore (printDeviceStore).
BASIC C it calls
----- ----------
PrintPageSetting(prefs, field), PrintPageText$(prefs, field) printSettingsRead on a prefs.bas handle: one PrintPageSetupT field by name (Lines, Columns, MarginTop, MarginBottom, MarginLeft, MarginRight, TabWidth, Wrap; Header, Footer).
@@ -9528,7 +9907,74 @@ if (!job) {
PrintForm(form), PrintFormDialog(form) printUiWindow on a loaded form's window (these two live in the BASIC runtime, which knows the forms).
PrintPortPresent(port), PrintPortStatus(port) printPortPresent / printPortStatus; port 1-3 is LPT1-LPT3.
PrintStatusProblem$(status, afterWrite, where) printStatusProblem ("" when the status shows none).
-
print.bas also has CONSTs for the PRINT_STATUS_ bits and the page setup limits and defaults (PRINT_LINES_STANDARD ... PRINT_COPIES_MAX); a formrt test compares them with print.h. 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).
+
print.bas also has CONSTs for the PRINT_STATUS_ bits and the page setup limits and defaults (PRINT_LINES_STANDARD ... PRINT_COPIES_MAX); a formrt test compares them with print.h. 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).
+
+
+
Styled Text Library
+
Styled text and RTF in and out: the document of the RichEdit widget, and of its undo steps and clipboard. A RichTextT is text in code page 437 with '\n' between paragraphs, covered by runs -- each from an offset to the next run's, in a style (font family, pixel size, bold, italic, underline, strike-through, and color) -- and one alignment per paragraph. It needs no widget: a program can build, edit, and convert styled text with it alone.
+
Header: richtext/richText.h
+
Loaded as: bin/libs/richtext.lib
+
Types
+
Type Fields
+ ---- ------
+ RichStyleT family (FONT_FAMILY_MAX; "" = the default family), size (pixels; 0 = the default size), style (FONT_STYLE_*), color (0x00RRGGBB or RICH_COLOR_DEFAULT)
+ RichRunT start (the first character's offset), style (an index in RichTextT.styles)
+ RichTextT text, runs, styles, aligns: stb_ds arrays; the text has no NUL, the first run starts at 0, styles[0] is the default style, one alignment (FLOW_ALIGN_*) per paragraph
+ RichChangeT set (RICH_SET_FAMILY, RICH_SET_SIZE, RICH_SET_COLOR), styleMask (the FONT_STYLE_* bits to set to to.style's), to (the new values)
+
The default family, size, and color are a RichEdit control's own font and ForeColor, so text in them follows the control. Styles are interned: one look is one index, so neighboring runs never share a style and no run covers nothing (but the one of an empty text). Running out of memory ends the program, as stb_ds does everywhere in DVX.
+
Constants
+
Constant Value Description
+ -------- ----- -----------
+ RICH_COLOR_DEFAULT FLOW_COLOR_DEFAULT A run in the default color
+ RICH_TAB_SPACES 4 The spaces a tab in plain text or RTF becomes (DVX text has no tabs)
+ RICH_SET_FAMILY 0x0001 RichChangeT.set: change the family
+ RICH_SET_SIZE 0x0002 RichChangeT.set: change the size
+ RICH_SET_COLOR 0x0004 RichChangeT.set: change the color
Function Description
+ -------- -----------
+ richTextInit / Clear An empty text: one empty, left-aligned paragraph in the default style. Free releases the arrays.
+ richTextIntern The index of a style, added when the text has none like it.
+ richTextRunAt The run the character at an offset is in (at the end, the last run); richTextStyleAt its style.
+ richTextPara* Paragraph count; the paragraph an offset is in (its '\n' belongs to it); where one starts and ends (its '\n' or the text's end); its alignment.
+ richTextCopy A range as a text of its own: its characters, runs, the styles they use, and the alignments of the paragraphs it touches.
+ richTextReplace Replace a range with a text (NULL deletes). Joined paragraphs keep the first one's alignment; each paragraph the new text starts takes its alignment from it. Returns the length inserted.
+ richTextInsertPlain Insert plain text in a style: '\r' dropped, a tab as RICH_TAB_SPACES spaces, '\n' starting a paragraph aligned as the one it is in.
+ richTextFromPlain The whole text as plain text in one style, made the default style.
+ richTextRestyle Apply a RichChangeT to a range; richStyleChanged applies one to a single style.
+
Undo works by these: before a change, richTextCopy saves the range it replaces; undoing replaces what the change put there with the saved copy. A copy taken, deleted, and put back gives the text it came from exactly -- characters, runs, styles, and alignments.
richRtfWrite returns a NUL-terminated RTF document (malloc'd; NULL when out of memory): a font table whose font 0 is defaultFamily (the default family is written as font 0 with no size, and read back as the default), a color table whose entry 0 is the default color, then each paragraph as \pard and its alignment, ended by \par, each run as \plain and its whole style. ASCII characters are themselves; the rest are \uN with a '?' for readers without Unicode. richRtfRead replaces rt with what an RTF document holds and returns false, leaving rt alone, when the text is not RTF.
+
The subset read: \fonttbl and \f, \fs (half points; a pixel is taken as 3/4 point, so \fs24 is 16 pixels), \b, \i, \ul, \strike, and their ends, \ulnone, \plain, \colortbl and \cf, \par, \pard, \ql, \qc, \qr (\qj as left; \line as a new paragraph), \tab, \'hh (Windows-1252), \uN with \ucN, the escapes \\ \{ \} \~ \_, and quotes and dashes as their ASCII forms. Characters are stored as code page 437, '?' for those it lacks. Destinations such as \info, \stylesheet, \pict, headers, footers, and every \* group are skipped.
RtfToText$ is the text of an RTF document, a line per paragraph (text that is not RTF comes back as it is); TextToRtf$ is plain text as RTF in the system font, a paragraph per line. A result stays valid until the next call. The RichEdit control's TextRTF, SelRTF, LoadFile, and SaveFile cover RTF in a control.
Serial Stack
@@ -9670,7 +10116,7 @@ if (!job) {
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 as last read; RS232_LSR_OVERRUN, RS232_LSR_PARITY_ERR, RS232_LSR_FRAME_ERR and RS232_LSR_BREAK are its error bits.
+ rs232GetLsr(com) Line status register value as last read; RS232_LSR_OVERRUN, RS232_LSR_PARITY_ERR, RS232_LSR_FRAME_ERR, and RS232_LSR_BREAK are its error bits.
rs232GetMcr(com) Modem control register value.
rs232GetMsr(com) Modem status register value; RS232_MSR_RI is the ring indicator.
Configuration Setters
@@ -9687,7 +10133,7 @@ if (!job) {
rs232SetDtr(com, dtr) Set DTR line state. The state is kept when the port is closed (dropping DTR is how a modem is hung up).
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. The state is kept when the port is closed.
- 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.
+ 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. rs232Open sets 8 on a 16550A (the 8 free bytes allow 0.7 ms of interrupt latency at 115200 bps; 14 allows only 0.17 ms) and 0 on other UARTs.
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.
+
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.
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.
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.
+
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.
Diffie-Hellman Key Exchange
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.
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).
+ 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.
@@ -9867,7 +10313,7 @@ void secCipherDestroy(SecCipherT *c);
secCipherDestroy Securely zero the cipher context (key and counter state) and free its memory.
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.
BASIC
-
src/include/basic/security.bas (DECLARE LIBRARY "serial") declares the BASIC functions (security/secBas.c). Keys, data and results are BINARY STRING (pointer and length in, a result buffer and length out), so every byte value passes; the result buffer is wiped before it is reused. A handle (1, 2, ...; 0 = none) is a key exchange, a cipher or an incremental SHA-256 / HMAC; each records the program that made it and the shell's app-cleanup hook frees (and wipes) what a program leaves open.
+
src/include/basic/security.bas (DECLARE LIBRARY "serial") declares the BASIC functions (security/secBas.c). Keys, data, and results are BINARY STRING (pointer and length in, a result buffer and length out), so every byte value passes; the result buffer is wiped before it is reused. A handle (1, 2, ...; 0 = none) is a key exchange, a cipher, or an incremental SHA-256 / HMAC; each records the program that made it and the shell's app-cleanup hook frees (and wipes) what a program leaves open.
BASIC C it calls
----- ----------
SecRandom$(count), SecAddEntropy bytes secRngBytes; secRngAddEntropy.
@@ -9887,7 +10333,7 @@ void secCipherDestroy(SecCipherT *c);
Secure Link
-
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.
+
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.
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.
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.
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.
-
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.
+
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.
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.
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.
Constants
@@ -9937,14 +10383,14 @@ void secCipherDestroy(SecCipherT *c);
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).
+ 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.
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.
secLinkOpenStream
-
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.
+
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.
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.
Non-blocking handshake for callers that run their own event loop.
int secLinkHandshakeStep(SecLinkT *link);
-
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.
+
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.
secLinkSend
Send data on a channel, optionally encrypted and MACed.
int secLinkSend(SecLinkT *link, const uint8_t *data, int len, uint8_t channel, bool encrypt, bool block);
int secLinkSetChannelPlain(SecLinkT *link, uint8_t channel, bool plain);
Returns SECLINK_SUCCESS or SECLINK_ERR_PARAM for a bad link or channel.
secLinkPoll
-
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.
+
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.
int secLinkPoll(SecLinkT *link);
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.
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).
Check whether the handshake is complete and the link is ready for encrypted traffic. Cleartext sends do not require the link to be ready.
bool secLinkIsReady(SecLinkT *link);
secLinkGetDropped
-
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.
+
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.
int32_t secLinkGetDropped(SecLinkT *link);
Returns the count or SECLINK_ERR_PARAM when link is NULL.
secLinkGetPending
@@ -10122,9 +10568,9 @@ int main(void) {
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.
Selection clearing. When a widget gains focus it calls clearOtherSelections(self) so only one widget ever has an active text selection.
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.
-
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).
+
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).
-
The multi-line engine (TextArea) works the same way with a TextEditMultiT: widgetTextEditMultiOnKey(w, key, mod, te, visRows, visCols, opts) runs a key on it. Its clipboard and undo commands are functions of their own, which the keys and a TextArea's Copy / Cut / Paste / SelectAll / Undo / SelText run alike: textEditMultiCopy (false when nothing is selected), textEditMultiCut, textEditMultiPaste, textEditMultiReplaceSel(te, text, textLen) (replaces the selection or inserts at the cursor, as much as fits, undo saved first), textEditMultiSelectAll and textEditMultiUndo (a second one is redo). The editing ones return true when the text changed; the caller then fires Change, scrolls to the cursor and repaints.
+
The multi-line engine (TextArea) works the same way with a TextEditMultiT: widgetTextEditMultiOnKey(w, key, mod, te, visRows, visCols, opts) runs a key on it. Its clipboard and undo commands are functions of their own, which the keys and a TextArea's Copy / Cut / Paste / SelectAll / Undo / SelText run alike: textEditMultiCopy (false when nothing is selected), textEditMultiCut, textEditMultiPaste, textEditMultiReplaceSel(te, text, textLen) (replaces the selection or inserts at the cursor, as much as fits, undo saved first), textEditMultiSelectAll, and textEditMultiUndo (a second one is redo). The editing ones return true when the text changed; the caller then fires Change, scrolls to the cursor, and repaints.
Constants
Constant Value Description
-------- ----- -----------
@@ -10257,7 +10703,7 @@ int main(void) {
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.
-
The key handler, the drag update and the widgets' paint code all use textEditVisibleChars, so they agree on the visible width.
+
The key handler, the drag update, and the widgets' paint code all use textEditVisibleChars, so they agree on the visible width.
Parameter Type Description
--------- ---- -----------
- te TextEditSingleT * [in/out] Editor state: cursorPos, scrollOff and selEnd are updated.
+ 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.
@@ -10283,7 +10729,7 @@ int main(void) {
Parameter Type Description
--------- ---- -----------
w WidgetT * The widget receiving the click.
- te TextEditSingleT * [in/out] Editor state: cursorPos, selStart and selEnd are set.
+ 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.
@@ -10293,12 +10739,12 @@ int main(void) {
widgetTextEditOnKey
-
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.
+
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.
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).
+ 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.
@@ -10357,7 +10803,7 @@ int main(void) {
File Transfer Library
-
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.
+
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.
Loaded as: bin/libs/kpunch/xfer/xfer.lib (needs the serial library only for the BASIC functions)
@@ -10388,7 +10834,7 @@ if (xferPoll(x, nowMs()) != XferRunningE) {
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.
+ 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).
Enumerations
Enumeration Values
@@ -10406,10 +10852,10 @@ if (xferPoll(x, nowMs()) != XferRunningE) {
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.
+ 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, longNames, out) Reduce a sender's file name to a safe one: no path, no drive, no DOS device name (CON, PRN, COM1 and so on get a leading _). With longNames (the receiving disk takes long names, platformLongNames) a long name keeping its case, blanks and dots, where a character a long name cannot hold becomes _ and leading blanks and trailing dots and blanks go (RECEIVED when nothing is left); otherwise an 8.3 name of portable characters in upper case. out holds XFER_NAME_MAX bytes (PLATFORM_LONG_NAME_MAX + 1).
+ void xferSafeName(name, longNames, out) Reduce a sender's file name to a safe one: no path, no drive, no DOS device name (CON, PRN, COM1, and so on get a leading _). With longNames (the receiving disk takes long names, platformLongNames) a long name keeping its case, blanks, and dots, where a character a long name cannot hold becomes _ and leading blanks and trailing dots and blanks go (RECEIVED when nothing is left); otherwise an 8.3 name of portable characters in upper case. out holds XFER_NAME_MAX bytes (PLATFORM_LONG_NAME_MAX + 1).
uint16_t xferCrc16(data, len) CRC-16/XMODEM (polynomial 0x1021).
uint32_t xferCrc32(data, len) CRC-32 (IEEE 802.3), as ZMODEM uses it.
int32_t xferBasNew(&x) For the BASIC functions of other libraries: a new xfer.bas handle owning a new idle transfer (in x) that belongs to the running program; 0 when no handle is free. The library starts the transfer over its own transport; the program follows it with the Xfer functions (the connection library's ConnSendFiles and ConnReceiveFiles work this way).
@@ -10424,10 +10870,10 @@ if (xferPoll(x, nowMs()) != XferRunningE) {
YMODEM
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 (the long name when it fits in the 128-byte block beside the other fields, else the 8.3 name), 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.
ZMODEM
-
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.
+
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.
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 (long names as they are) 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.
Files are at most 2 GB (sizes and positions are 32-bit): a send refuses a larger file, a size offered past 2 GB reads as unknown, and a receive that passes 2 GB stops with "A file larger than 2 GB cannot be transferred". A file already on the disk is never mistaken for a missing one, whatever its size.
-
Interoperability: the engines are tested against lrz and lsz (lrzsz 0.12.20) in both directions for every protocol, including escaping, resume and long file names.
+
Interoperability: the engines are tested against lrz and lsz (lrzsz 0.12.20) in both directions for every protocol, including escaping, resume, and long file names.
File Transfers from BASIC
@@ -10437,7 +10883,7 @@ if (xferPoll(x, nowMs()) != XferRunningE) {
-------- -----------
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_ASKING, XFER_DONE, XFER_FAILED or XFER_CANCELLED.
+ XferPoll(handle) Move the transfer along. Returns XFER_RUNNING, XFER_ASKING, XFER_DONE, XFER_FAILED, or XFER_CANCELLED.
XferStatus(handle) The same, without moving it along (XFER_IDLE for a bad handle).
XferSending(handle), XferProtocol(handle) True for a send; the protocol.
XferBytes(handle), XferSize(handle) Bytes of the current file done, and its size (-1 when unknown).
@@ -10447,7 +10893,7 @@ if (xferPoll(x, nowMs()) != XferRunningE) {
XferResumedAt(handle), XferErrors(handle) Where the current file continued from; retries so far.
XferAskSize(handle), XferCanResume(handle) XFER_ASKING: the size of the file already there; True when XFER_EXIST_RESUME can continue it.
XferAskRemoteSize(handle) XFER_ASKING: the size the sender offers (XferProgressT.askRemoteSize; -1 when unknown).
- XferAnswer handle, action Answer XFER_ASKING with XFER_EXIST_RENAME, _RESUME, _OVERWRITE or _SKIP.
+ XferAnswer handle, action Answer XFER_ASKING with XFER_EXIST_RENAME, _RESUME, _OVERWRITE, or _SKIP.
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.
@@ -10524,7 +10970,7 @@ End Sub
onEvent void (*)(WidgetT *w, const char *name, WgtValueT *args, int32_t argc) A named event the widget fires that has no callback above (ListView ColumnClick, TextArea GutterClick, HelpView LinkClick, an event a BASIC control raises). args may be written back.
wgtFireEvent(w, name, args, argc) fires an event by name: the names above (Click, DblClick, Change, GotFocus, LostFocus, KeyPress, KeyDown, KeyUp, MouseDown, MouseUp, MouseMove, Scroll) go to their callback, every other name to onEvent (see dvxWgt.h, Custom Events).
Generic Access
-
Every widget type can be created, read, written and called by name through its interface descriptor, with values as WgtValueT -- the way the BASIC form runtime and the designer drive widgets, and the way a C program uses a widget it has no header for (a BASIC control, for one):
+
Every widget type can be created, read, written, and called by name through its interface descriptor, with values as WgtValueT -- the way the BASIC form runtime and the designer drive widgets, and the way a C program uses a widget it has no header for (a BASIC control, for one):
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.
+
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, ANSI music, 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.
+
WCLASS_FIXED_FONT: it edits by columns, so its font must be fixed-pitch. Any size works (the column math uses the font's charWidth and charHeight); a proportional font set with wgtSetFont, or inherited, is replaced by the system font when it draws.
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.
Header: widgets/ansiTerm.h
Creation
@@ -10586,11 +11033,13 @@ wgtCallMethod(list, "ListCount", NULL, 0, &count); // count.i ==
---- -------- -----------
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.
+ 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.
+ setMusic void wgtAnsiTermSetMusic(w, on) Recognize ANSI music (on at first). Off, ESC[M is always Delete Line and ESC[N and ESC[| do nothing.
Properties (BASIC Interface)
Property Type Access Description
-------- ---- ------ -----------
- Cols Integer Read/Write Number of columns. Setting it (or Rows) resizes the terminal (1-512): screen and scrollback start empty and the modes the host set (wrap, origin, cursor visible, scroll region) are reset; palette, scrollback length and comm callbacks stay; the window is laid out again.
+ Cols Integer Read/Write Number of columns. Setting it (or Rows) resizes the terminal (1-512): screen and scrollback start empty and the modes the host set (wrap, origin, cursor visible, scroll region) are reset; palette, scrollback length, and comm callbacks stay; the window is laid out again.
+ Music Boolean Read/Write Recognize ANSI music (setMusic; True at first).
Rows Integer Read/Write Number of rows.
Scrollback Integer Read/Write Maximum number of scrollback lines. Setting it discards the existing history.
Methods (BASIC Interface)
@@ -10604,7 +11053,12 @@ wgtCallMethod(list, "ListCount", NULL, 0, &count); // count.i ==
SetColor index, color: one attribute color (0-15) from a BASIC color (&HBBGGRR, converted with wgtBasicColor).
Write Write a string into the terminal (with ANSI escape processing).
Events
-
AnsiTerm uses the common events only. No widget-specific events are defined.
+
Besides the common events, two of its own, fired through wgtFireEvent (a C host gets them in onEvent with the names ANSITERM_EVT_MUSIC and ANSITERM_EVT_BELL):
+
Event Parameters Description
+ ----- ---------- -----------
+ Music Macro As String An ANSI music string arrived: the BASIC PLAY text between ESC[| or ESC[N, or ESC[MF (MB, MN, ML, MS), and Ctrl+N (0x0E). For the ESC[M form the macro begins with that M command (e.g. "MFT120O4L8CDE"). Line ends are left out; a string longer than 4096 characters, an empty one, or one cut short by ESC fires nothing.
+ Bell (none) A BEL character (Ctrl+G) arrived.
+
The widget plays nothing itself: a handler plays the macro (BASIC: PLAY Macro; C: the music library's musicMmlPlay). While a Music or Bell handler runs, the terminal takes no input: Poll leaves the bytes in the connection, and Write keeps them, in order, until the handler returns. A tune played in the foreground (MF) therefore holds back the text after it, as on a DOS terminal, while the window stays responsive. Bare ESC[M (no parameter) followed by anything but F, B, N, L, or S is still Delete Line. A handler may close the terminal's window; the input after the event is then dropped.
Keyboard Shortcuts
Within the terminal widget:
Key Action
@@ -10616,7 +11070,7 @@ wgtCallMethod(list, "ListCount", NULL, 0, &count); // count.i ==
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.
-
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.
+
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.
Box (VBox / HBox / Frame)
@@ -10713,7 +11167,7 @@ btn->onClick = onOkClicked;
void wgtCanvasResize(w, newW, newH) Resize the canvas buffer and relayout. The old content is discarded; the new buffer is white.
void wgtCanvasSetBorderStyle(w, style) CANVAS_BORDER_NONE (0) drops the sunken frame, any other value (CANVAS_BORDER_FIXED, 1, the default) shows it. The buffer keeps its size; the widget relays out.
int32_t wgtCanvasGetBorderStyle(w) CANVAS_BORDER_FIXED or CANVAS_BORDER_NONE.
- bool wgtCanvasGetDisplay(w, out) Fill a DisplayT aimed at the canvas pixels (the screen's pixel format, clipped to the canvas), so any libdvx drawing call -- drawText, drawBevel, drawArrowGlyph, wmDrawScrollbarAt, rectCopyTransparent -- draws on the canvas with the context's blit ops; call wgtInvalidatePaint afterwards. False (out untouched) for a widget that is not a canvas. The BASIC DrawBevel, DrawButtonFace, DrawArrow, DrawScrollBar, DrawScrollArrow and PrintAccel methods use it.
+ bool wgtCanvasGetDisplay(w, out) Fill a DisplayT aimed at the canvas pixels (the screen's pixel format, clipped to the canvas), so any libdvx drawing call -- drawText, drawBevel, drawArrowGlyph, wmDrawScrollbarAt, rectCopyTransparent -- draws on the canvas with the context's blit ops; call wgtInvalidatePaint afterwards. False (out untouched) for a widget that is not a canvas. The BASIC DrawBevel, DrawButtonFace, DrawArrow, DrawScrollBar, DrawScrollArrow, and PrintAccel methods use it.
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 color at a pixel (&HBBGGRR). With fewer than 16 million colors on the screen it is the color the screen shows: black, white and full colors such as RGB(255, 0, 0) read back exactly, others may differ slightly.
- 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).
+ GetPixel(x%, y%) Returns the color at a pixel (&HBBGGRR). With fewer than 16 million colors on the screen it is the color the screen shows: black, white, and full colors such as RGB(255, 0, 0) read back exactly, others may differ slightly.
+ 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 (&HBBGGRR).
@@ -10805,9 +11259,10 @@ btn->onClick = onOkClicked;
ComboBox
-
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).
-
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.
+
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).
+
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.
Depends on "texthelp" and "listhelp" helper libraries (declared in combobox.dep).
+
WCLASS_FIXED_FONT: it edits by columns, so its font must be fixed-pitch. Any size works (the column math uses the font's charWidth and charHeight); a proportional font set with wgtSetFont, or inherited, is replaced by the system font when it draws.
----- -----------
Reposition Fires when the current row changes. Default event.
Validate Fires before writing. Return false to cancel the write.
-
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.
+
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.
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.
+
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.
Depends on the "listhelp" helper library (declared in dropdown.dep).
One topic of a compiled help file, shown the way the DVX Help Viewer shows it -- the Help Viewer itself uses this widget. Headings (three levels), word-wrapped paragraphs and list items, notes (info, tip and warning), code blocks and tables (preformatted), pictures (left, centered or right), horizontal rules and links, in a scrolling pane, in the theme's colours. Text re-wraps when the widget changes width.
-
Links take the keyboard focus (Tab) and are followed with a click, Enter or Space: the widget keeps the link's target topic ID (wgtHelpViewGetLinkTarget) and calls the link callback with it. The widget does not change topic by itself; the callback usually finds the topic (hlpFileFindTopic) and shows it, which may rebuild the pane from inside the link's own handler. Everything a topic shows is copied when it is shown, so the help file may be closed afterwards.
+
One topic of a compiled help file, shown the way the DVX Help Viewer shows it -- the Help Viewer itself uses this widget. Headings (three levels), word-wrapped paragraphs and list items, notes (info, tip, and warning), code blocks and tables (preformatted), pictures (left, centered, or right), horizontal rules and links, in a scrolling pane, in the theme's colours. Text re-wraps when the widget changes width.
+
Links take the keyboard focus (Tab) and are followed with a click, Enter, or Space: the widget keeps the link's target topic ID (wgtHelpViewGetLinkTarget) and calls the link callback with it. The widget does not change topic by itself; the callback usually finds the topic (hlpFileFindTopic) and shows it, which may rebuild the pane from inside the link's own handler. Everything a topic shows is copied when it is shown, so the help file may be closed afterwards.
The help file is read with the help file library (hlpfile/hlpFile.h).
Header: widgets/helpView.h
BASIC name: HelpPane (see the HelpPane control in the BASIC reference)
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.
+
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.
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.
A text box for styled text: runs in fonts, sizes, styles, and colors, in paragraphs aligned left, centered, or right, edited with the keyboard and the mouse, with undo and redo, a clipboard that keeps styles between RichEdit controls, find, and RTF in and out. The document is a RichTextT of the richtext library (richtext/richText.h); a style's family "" and size 0 are the widget's own font (wgtSetFont, or its container's) and RICHEDIT_COLOR_DEFAULT its fgColor, so such text follows the widget.
+
Each paragraph is laid out by a text flow of its own (dvxFlow.h): an edit lays out only the paragraphs it touched, and the paragraphs' tops are kept for binary searches from an offset or a row. The widget polls (WCLASS_NEEDS_POLL) and has a quick repaint (WGT_METHOD_QUICK_REPAINT): an edit that left every paragraph's height alone, a caret or selection move, and the caret's blink repaint only the rows they touched, painted straight into the window's content buffer (widgetQuickPaintBegin); a change of height, a scroll, or a new width repaints the widget. Undo keeps for each edit what it replaced and the length it put in its place (alignment changes keep the old alignments); typed characters join one step; 100 steps are kept.
+
Depends on the "texthelp" (word boundaries, the caret blink time, scroll bar drags) and "richtext" libraries (declared in richedit.dep).
Function Description
+ -------- -----------
+ WidgetT *wgtRichEdit(parent) Create an empty rich edit box.
+ const char *wgtRichEditGetRtf(w) The whole text as RTF (valid until the next call that returns RTF).
+ void wgtRichEditSetRtf(w, rtf) Replace the whole text from RTF (plain text when it is not RTF); undo is emptied.
+ const char *wgtRichEditGetSelRtf(w) The selection as RTF.
+ void wgtRichEditSetSelRtf(w, rtf) Replace the selection from RTF.
+ int32_t wgtRichEditGetSelStart(w) / SetSelStart Where the selection starts; setting it places the caret.
+ int32_t wgtRichEditGetSelLength(w) / SetSelLength The selection's length; setting it selects from SelStart.
+ const char *wgtRichEditGetSelText(w) / SetSelText The selection's text; setting it replaces the selection with plain text.
+ const char *wgtRichEditGetSelFontName(w) The selection's family (the widget's own when it uses that).
+ int32_t wgtRichEditGetSelFontSize(w) The selection's pixel size (as opened: the nearest the family has).
+ void wgtRichEditSetSelFont(w, family, size) The family (NULL: unchanged; "": the widget's) and size (0: unchanged).
+ uint32_t wgtRichEditGetSelStyle(w) The selection's FONT_STYLE_* bits.
+ void wgtRichEditSetSelStyle(w, style, mask) Make the bits in mask style's.
+ uint32_t wgtRichEditGetSelColor(w) / SetSelColor 0x00RRGGBB or RICHEDIT_COLOR_DEFAULT.
+ int32_t wgtRichEditGetSelAlign(w) / SetSelAlign FLOW_ALIGN_* of the selection's paragraphs.
+ bool wgtRichEditCanUndo(w) / CanRedo Whether there is a step to undo or redo.
+ void wgtRichEditUndo(w) / Redo Undo or redo a step.
+ void wgtRichEditCut(w) / Copy / Paste The clipboard (plain text there; the styled copy kept for a paste of the same text).
+ void wgtRichEditSelectAll(w) Select everything.
+ int32_t wgtRichEditFind(w, text, matchCase) Select the next match from the selection's end, around past the end; its offset or -1.
+ int32_t wgtRichEditLoadFile(w, path) Load RTF or plain text; 0 or -1.
+ int32_t wgtRichEditSaveFile(w, path) Save RTF (path ending in .rtf) or plain text; 0 or -1.
+
The style and alignment setters change the selection, or with none, the style typing uses next (alignment: the caret's paragraph); each change is an undo step and fires Change. wgtGetText / wgtSetText are the plain text, a line per paragraph. RICHEDIT_EVT_SELCHANGE ("SelChange") is fired with wgtFireEvent when the selection or the caret moves.
+
API Struct (wgtRegisterApi "richedit")
+
The slots are the functions above in order: create, getRtf, setRtf, getSelRtf, setSelRtf, getSelStart, setSelStart, getSelLength, setSelLength, getSelText, setSelText, getSelFontName, getSelFontSize, setSelFont, getSelStyle, setSelStyle, getSelColor, setSelColor, getSelAlign, setSelAlign, canUndo, canRedo, undo, redo, cut, copy, paste, selectAll, find, loadFile, and saveFile.
+
BASIC Interface
+
Properties: TextRTF and SelRTF (wgtRichEditGetRtf / SetRtf, GetSelRtf / SetSelRtf), SelStart, SelLength, SelText, SelBold, SelItalic, SelUnderline, SelStrikethru (one FONT_STYLE_* bit of SetSelStyle each), SelColor (a BASIC color; -1 for ForeColor), SelFontName and SelFontSize (wgtRichEditSetSelFont with the other part left alone), SelAlignment, and the read-only CanUndo and CanRedo. Methods: Undo, Redo, Cut, Copy, Paste, SelectAll, Find (any case), LoadFile, and SaveFile. Event: SelChange; default event Change.
+
Methods Used
+
PAINT, CALC_MIN_SIZE (20 by 4 characters of its font, with border and scroll bar), ON_MOUSE, ON_KEY, ON_DRAG_UPDATE, ON_DRAG_END, ON_WHEEL, ON_FOCUS, ON_BLUR, POLL, QUICK_REPAINT, GET_TEXT, SET_TEXT, CLEAR_SELECTION, DESTROY. Flags: WCLASS_FOCUSABLE, WCLASS_NEEDS_POLL.
+
+
+
RichLabel
+
Text in many fonts, styles, and colors, wrapped and aligned paragraph by paragraph. The text is built by calls: wgtRichLabelSetFont, wgtRichLabelSetStyle, and wgtRichLabelSetColor choose what the following wgtRichLabelAddText uses; wgtRichLabelAddLineBreak starts a new line and wgtRichLabelAddParagraph a new paragraph with its own alignment. Text starts in the widget's own font (wgtSetFont, or its container's). The widget is laid out at its width hint (prefW, else minW, which BASIC's Width sets), wrapping at spaces (a word too wide for a line breaks inside it); without one each paragraph is one line. Every line is as tall as the tallest font on it, its text standing on one baseline. Not focusable; drawn the same enabled or not. It fills its rectangle as a Label does: with the nearest explicit bgColor, else the content background. Built on the text flow in libdvx (dvxFlow.h).
Function Description
+ -------- -----------
+ WidgetT *wgtRichLabel(parent) Create an empty rich label.
+ void wgtRichLabelClear(w) Remove all text; the font, style, and color chosen go back to the widget's.
+ void wgtRichLabelSetFont(w, family, size) The font family and pixel size of the text added next ("" and 0: the widget's own).
+ void wgtRichLabelSetStyle(w, style) FONT_STYLE_BOLD, ITALIC, UNDERLINE, and STRIKE bits for the text added next.
+ void wgtRichLabelSetColor(w, rgb) 0x00RRGGBB color of the text added next; RICHLABEL_COLOR_DEFAULT: the widget's fgColor (else the scheme's).
+ void wgtRichLabelAddText(w, text) Add text in the font, style, and color chosen.
+ void wgtRichLabelAddLineBreak(w) Start a new line in the same paragraph.
+ void wgtRichLabelAddParagraph(w, align) Start a new paragraph aligned FLOW_ALIGN_LEFT (0), FLOW_ALIGN_CENTER (1), or FLOW_ALIGN_RIGHT (2), as a Label's alignment; others are ignored.
+
Setting the font, style, or color changes only the text added afterwards.
+
API Struct (wgtRegisterApi "richlabel")
+
Slot Function
+ ---- --------
+ header WGT_API_HEADER (ABI version and size, checked by wgtGetApi)
+ create wgtRichLabel
+ clear wgtRichLabelClear
+ setFont wgtRichLabelSetFont
+ setStyle wgtRichLabelSetStyle
+ setColor wgtRichLabelSetColor
+ addText wgtRichLabelAddText
+ addLineBreak wgtRichLabelAddLineBreak
+ addParagraph wgtRichLabelAddParagraph
+
Methods (BASIC Interface)
+
Method Arguments Description
+ ------ --------- -----------
+ Clear (none) wgtRichLabelClear
+ SetFont family, size wgtRichLabelSetFont
+ SetStyle style wgtRichLabelSetStyle
+ SetColor color A BASIC color (RGB()); -1 for ForeColor
+ AddText text wgtRichLabelAddText
+ AddLineBreak (none) wgtRichLabelAddLineBreak
+ AddParagraph alignment wgtRichLabelAddParagraph
+
Default Event
+
"Click" (VB basName: RichLabel).
+
ScrollPane
-
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.
+
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.
Header: widgets/scrlPane.h
Creation
WidgetT *sp = wgtScrollPane(parent);
@@ -11446,7 +12003,7 @@ WidgetT *content = wgtVBox(sp);
int32_t wgtScrollPaneGetScrollLeft(w) Horizontal scroll position in pixels.
void wgtScrollPaneSetScrollLeft(w, pos) Set the horizontal scroll position; the layout clamps it to the content.
Padding and spacing are the common WidgetT padding / spacing fields (WGT_GAP_DEFAULT, the default, is none); wgtSetPaddingPx / wgtSetSpacingPx set them in pixels.
-
When the user moves the scroll position (scrollbar arrows and trough, dragging a thumb, the arrow, page, Home and End keys) the scroll pane fires onChange (widgetFireChange) after the position is clamped; the setters, wgtScrollPaneScrollToTop and scrolling a focused child into view do not. BASIC sees it as the ScrollPane's Change event.
+
When the user moves the scroll position (scrollbar arrows and trough, dragging a thumb, the arrow, page, Home, and End keys) the scroll pane fires onChange (widgetFireChange) after the position is clamped; the setters, wgtScrollPaneScrollToTop, and scrolling a focused child into view do not. BASIC sees it as the ScrollPane's Change event.
A numeric input with up/down buttons for incrementing and decrementing a value within a range. Supports both integer and floating-point (real) modes.
-
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.
+
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.
+
WCLASS_FIXED_FONT: it edits by columns, so its font must be fixed-pitch. Any size works (the column math uses the font's charWidth and charHeight); a proportional font set with wgtSetFont, or inherited, is replaced by the system font when it draws.
Callback Description
-------- -----------
- onChange Fires when the user changes the active tab (mouse, keyboard or accelerator); not for wgtTabControlSetActive.
+ onChange Fires when the user changes the active tab (mouse, keyboard, or accelerator); not for wgtTabControlSetActive.
Default Event
"Click" (VB basName: TabStrip).
TextInput / TextArea
-
Single-line text input, password input, masked input, and multi-line text area with optional syntax colorization (built-in BASIC and help-source colorizers, the BASIC one shared with the DVX BASIC IDE), line numbers, find/replace, line markers and gutter clicks.
+
Single-line text input, password input, masked input, and multi-line text area with optional syntax colorization (built-in BASIC and help-source colorizers, the BASIC one shared with the DVX BASIC IDE), line numbers, find/replace, line markers, and gutter clicks.
+
WCLASS_FIXED_FONT: it edits by columns, so its font must be fixed-pitch. Any size works (the column math uses the font's charWidth and charHeight); a proportional font set with wgtSetFont, or inherited, is replaced by the system font when it draws.
Header: widgets/textInpt.h
Creation
Macro Description
@@ -11742,7 +12301,7 @@ WidgetT *page2 = wgtTabPage(tabs, "Advanced");
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 wgtTextAreaSetLineMarker(w, line, gutterRgb, backRgb) Mark 1-based line: a dot of gutterRgb (0xRRGGBB) in the line-number gutter and / or the line's background in backRgb; TEXTAREA_MARKER_NONE leaves a part out, both NONE removes the marker. Markers keep their line numbers when the text changes (the owner moves them). The DVX BASIC IDE marks breakpoints and the paused line this way.
void wgtTextAreaClearLineMarkers(w) Remove every line marker.
- void wgtTextAreaSetSyntaxMode(w, mode) Use a built-in colorizer: "bas" / "basic" (BASIC: the compiler's keyword list, date literals by the lexer's shape rule, &H / &O and exponent numbers, REM and ' comments; the IDE's code editor uses it), "dhs" / "help" (help source) or "custom" (each painted line goes to the "Colorize" event through wgtFireEvent: args LineText and Colors, a string of one digit per character, "0" = TEXT_SYNTAX_DEFAULT; the host writes the TEXT_SYNTAX_* digits back into Colors; the BASIC Colorize event); NULL or "" turns colouring off.
+ void wgtTextAreaSetSyntaxMode(w, mode) Use a built-in colorizer: "bas" / "basic" (BASIC: the compiler's keyword list, date literals by the lexer's shape rule, &H / &O and exponent numbers, REM and ' comments; the IDE's code editor uses it), "dhs" / "help" (help source), or "custom" (each painted line goes to the "Colorize" event through wgtFireEvent: args LineText and Colors, a string of one digit per character, "0" = TEXT_SYNTAX_DEFAULT; the host writes the TEXT_SYNTAX_* digits back into Colors; the BASIC Colorize event); NULL or "" turns colouring off.
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.
@@ -11755,7 +12314,7 @@ WidgetT *page2 = wgtTabPage(tabs, "Advanced");
void wgtTextInputSetSelStart(w, start) Cursor to offset start, nothing selected.
void wgtTextInputSetSelLength(w, length) Select length characters from SelStart (clamped to the text).
void wgtTextInputSetSelText(w, text) Replace the selection (or insert at the cursor) with text (texthelp textEditSingleReplaceSel; a masked box fills its slots); fires onChange.
- void wgtTextInputCut(w), wgtTextInputCopy(w), wgtTextInputPaste(w), wgtTextInputSelectAll(w), wgtTextInputUndo(w) The box's Ctrl+X / C / V / A / Z keys, run through its own key handler (password, mask and read-only rules included).
+ void wgtTextInputCut(w), wgtTextInputCopy(w), wgtTextInputPaste(w), wgtTextInputSelectAll(w), wgtTextInputUndo(w) The box's Ctrl+X / C / V / A / Z keys, run through its own key handler (password, mask, and read-only rules included).
API Struct (wgtRegisterApi "textinput")
The combined "textinput" API exposes all four constructors plus the TextArea functions and, appended at its end (inputGetSelStart ... inputUndo), the TextBox selection and clipboard 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).