VESA 1.2 supported!
This commit is contained in:
parent
1c4de91dea
commit
7540e9e34f
273 changed files with 3229 additions and 1291 deletions
2
Makefile
2
Makefile
|
|
@ -287,5 +287,5 @@ clean:
|
|||
$(MAKE) -C src/tools/proxy clean
|
||||
$(MAKE) -C src/installer clean
|
||||
rm -rf obj bin/host bin/test bin/system
|
||||
rm -rf bin/config bin/widgets bin/libs bin/sdk
|
||||
rm -rf bin/shared bin/widgets bin/libs bin/sdk
|
||||
rm -f docs/*.html
|
||||
|
|
|
|||
36
README.md
36
README.md
|
|
@ -5,8 +5,9 @@ It provides a Motif-inspired desktop, a cooperative multitasking kernel,
|
|||
a widget toolkit, a compiled BASIC language with a visual form designer,
|
||||
and a small set of bundled applications.
|
||||
|
||||
The system is a compositor built on VESA VBE 2.0+ linear framebuffer
|
||||
video modes. A dirty-rectangle compositor keeps redraw cost proportional
|
||||
The system is a compositor built on VESA VBE video modes: the linear
|
||||
framebuffer where the card has one, its 64 KB bank window where it does
|
||||
not. A dirty-rectangle compositor keeps redraw cost proportional
|
||||
to what actually changes on screen, so the desktop is responsive even at
|
||||
1024x768 on modest hardware.
|
||||
|
||||
|
|
@ -43,10 +44,12 @@ to what actually changes on screen, so the desktop is responsive even at
|
|||
|
||||
* DOS with a 386-class CPU or newer (486 recommended, Pentium for best
|
||||
performance)
|
||||
* VESA BIOS Extensions 2.0 or newer with linear framebuffer support
|
||||
* A VESA BIOS (VBE 1.2 or newer; a VBE driver such as UniVBE for cards
|
||||
without one in ROM). A linear framebuffer (VBE 2.0) is faster;
|
||||
without one DVX writes through the 64 KB bank window
|
||||
* 4 MB of extended memory minimum; 8 MB or more recommended
|
||||
* Any resolution the video card reports at 8, 15, 16, 24, or 32 bits
|
||||
per pixel. 640x480, 800x600, and 1024x768 are the common cases.
|
||||
* Any resolution the video card reports at 8, 15, 16, or 32 bits per
|
||||
pixel (24 is not supported). 640x480, 800x600, and 1024x768 are the common cases.
|
||||
* Two-button mouse; scroll wheel supported if a compatible driver is
|
||||
loaded
|
||||
|
||||
|
|
@ -108,7 +111,7 @@ it in 640x480 safe video mode.
|
|||
dvxgui/
|
||||
assets/ logo and splash artwork
|
||||
bin/ build output (runtime tree; mirrors what ships)
|
||||
config/ shipped INI, themes, wallpapers
|
||||
config/ shipped DVX.INI, ASSOC.INI, themes, and wallpapers
|
||||
dist/ release media from `make dist` (not in git)
|
||||
docs/ HTML exports of the built-in help content
|
||||
LICENSE.txt MIT license
|
||||
|
|
@ -128,6 +131,27 @@ dvxgui/
|
|||
installToolchain.sh)
|
||||
```
|
||||
|
||||
An installed DVX (and `bin/`) keeps program files and user files apart:
|
||||
|
||||
```
|
||||
DVX.EXE SETUP.EXE SETUP.LST
|
||||
SYSTEM/ tools, SPLASH.RAW, and the shipped DVX.INI and ASSOC.INI
|
||||
LIBS/ WIDGETS/
|
||||
APPS/<vendor>/<app>/ a program, its help, and DATA/ (files it ships)
|
||||
SHARED/ FONTS, SOUNDS, THEMES, WPAPER: shipped for every program
|
||||
USER/ everything written while DVX runs (back up this folder)
|
||||
DVX.INI ASSOC.INI DVX.LOG
|
||||
FONTS/ SOUNDS/ THEMES/ WPAPER/ the user's own (found before SHARED's)
|
||||
APPS/<vendor>/<app>/ each program's settings and saved files
|
||||
DOWNLOAD/ Terminal's downloads
|
||||
```
|
||||
|
||||
`SET DVXUSER=D:\MYDVX` puts the user folder elsewhere (when DVX's disk
|
||||
cannot be written it goes in TEMP). Only DVX's own package fills
|
||||
SHARED; programs keep their own files with them. The package never
|
||||
holds USER, so an upgrade replaces every program file and leaves the
|
||||
user's alone. `claudeDocs/folderLayout.md` has the reasoning.
|
||||
|
||||
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
|
||||
|
|
|
|||
|
|
@ -1,45 +1,38 @@
|
|||
# DVX Configuration Files
|
||||
# DVX Shipped Settings and Shared Files
|
||||
|
||||
Runtime configuration, theme files, wallpaper images, and module
|
||||
dependency files. These are copied into `bin/config/` (INI, themes,
|
||||
wallpapers) or `bin/libs/` and `bin/widgets/` (dep files) during the
|
||||
build. Text files are converted to DOS line endings (CR+LF) via sed.
|
||||
The settings files DVX ships and the files it shares with every program.
|
||||
The build copies them into `bin/` (text files converted to DOS line
|
||||
endings, CR+LF, with sed): the settings into `bin/system/`, the themes and
|
||||
wallpapers into `bin/shared/`. At its first start DVX copies `DVX.INI` to
|
||||
the user folder (`USER`, or the folder `DVXUSER` names), where every
|
||||
change is saved; the shipped copy is never written. File types work the
|
||||
same way, except that the user's `ASSOC.INI` holds only their changes to
|
||||
the shipped list.
|
||||
|
||||
|
||||
## Files
|
||||
|
||||
| Source File | Build Output | Description |
|
||||
|-------------|-------------|-------------|
|
||||
| `dvx.ini` | `bin/config/dvx.ini` | System configuration |
|
||||
| `ASSOC.INI` | `bin/config/ASSOC.INI` | File associations: which program opens each document type (shellAssoc.h; edited on the Control Panel's File Types page) |
|
||||
| `themes/cde.thm` | `bin/config/themes/cde.thm` | CDE color theme |
|
||||
| `themes/geos.thm` | `bin/config/themes/geos.thm` | GEOS Ensemble theme |
|
||||
| `themes/hotdog.thm` | `bin/config/themes/hotdog.thm` | Hot Dog Stand theme |
|
||||
| `themes/win31.thm` | `bin/config/themes/win31.thm` | Windows 3.1 theme |
|
||||
| `wpaper/blueglow.jpg` | `bin/config/wpaper/blueglow.jpg` | Wallpaper image |
|
||||
| `wpaper/swoop.jpg` | `bin/config/wpaper/swoop.jpg` | Wallpaper image |
|
||||
| `wpaper/triangle.jpg` | `bin/config/wpaper/triangle.jpg` | Wallpaper image |
|
||||
| `libdvx.dep` | `bin/libs/libdvx.dep` | libdvx dependency file |
|
||||
| `texthelp.dep` | `bin/libs/texthelp.dep` | texthelp dependency file |
|
||||
| `listhelp.dep` | `bin/libs/listhelp.dep` | listhelp dependency file |
|
||||
| `dvxshell.dep` | `bin/libs/dvxshell.dep` | dvxshell dependency file |
|
||||
| `textinpt.dep` | `bin/widgets/textinpt.dep` | TextInput widget dep file |
|
||||
| `combobox.dep` | `bin/widgets/combobox.dep` | ComboBox widget dep file |
|
||||
| `spinner.dep` | `bin/widgets/spinner.dep` | Spinner widget dep file |
|
||||
| `terminal.dep` | `bin/widgets/terminal.dep` | AnsiTerm widget dep file |
|
||||
| `dropdown.dep` | `bin/widgets/dropdown.dep` | Dropdown widget dep file |
|
||||
| `listbox.dep` | `bin/widgets/listbox.dep` | ListBox widget dep file |
|
||||
| `listview.dep` | `bin/widgets/listview.dep` | ListView widget dep file |
|
||||
| `treeview.dep` | `bin/widgets/treeview.dep` | TreeView widget dep file |
|
||||
| `basrt.dep` | `bin/libs/basrt.dep` | BASIC runtime dep file |
|
||||
| `serial.dep` | `bin/libs/serial.dep` | Serial communications dep file |
|
||||
| `dvx.ini` | `bin/system/DVX.INI` | The settings a new user starts with |
|
||||
| `ASSOC.INI` | `bin/system/ASSOC.INI` | File associations: which program opens each document type (shellAssoc.h; changed on the Control Panel's File Types page, in the user's ASSOC.INI) |
|
||||
| `themes/cde.thm` | `bin/shared/themes/cde.thm` | CDE color theme |
|
||||
| `themes/geos.thm` | `bin/shared/themes/geos.thm` | GEOS Ensemble theme |
|
||||
| `themes/hotdog.thm` | `bin/shared/themes/hotdog.thm` | Hot Dog Stand theme |
|
||||
| `themes/win31.thm` | `bin/shared/themes/win31.thm` | Windows 3.1 theme |
|
||||
| `wpaper/blueglow.jpg` | `bin/shared/wpaper/blueglow.jpg` | Wallpaper image |
|
||||
| `wpaper/swoop.jpg` | `bin/shared/wpaper/swoop.jpg` | Wallpaper image |
|
||||
| `wpaper/triangle.jpg` | `bin/shared/wpaper/triangle.jpg` | Wallpaper image |
|
||||
|
||||
The desktop sounds (`bin/shared/sounds/`) are made by mksounds and the
|
||||
fonts (`bin/shared/fonts/`) by mkfont (src/tools).
|
||||
|
||||
|
||||
## dvx.ini Format
|
||||
|
||||
Standard INI format with `[section]` headers and `key = value` pairs.
|
||||
Comments start with `;`. The shell loads this at startup via
|
||||
`prefsLoad("CONFIG/DVX.INI")`.
|
||||
Comments start with `;`. The shell loads the user's copy at startup via
|
||||
`prefsLoad(platformDvxIniPath())`.
|
||||
|
||||
### [video] Section
|
||||
|
||||
|
|
|
|||
|
|
@ -113,20 +113,22 @@ fmTones = true
|
|||
midi = auto
|
||||
mpuMode = gm
|
||||
|
||||
; Desktop sounds (Control Panel's Sounds page): the sound file (WAV or VOC,
|
||||
; path from the DVX folder) played for each event; empty = no sound.
|
||||
; Desktop sounds (Control Panel's Sounds page): the sound file (WAV or VOC)
|
||||
; played for each event; empty = no sound. A name alone is a file in the
|
||||
; sound folders (your own in USER\SOUNDS first, then DVX's in
|
||||
; SHARED\SOUNDS); a name with a folder in it is that file.
|
||||
; default: the default beep (also message boxes without an icon)
|
||||
; startup, exit: DVX starting and ending
|
||||
; error, warning, question, information: message boxes of that kind
|
||||
; windowOpen, windowClose: a window opening or closing (not dialogs)
|
||||
|
||||
[sounds]
|
||||
default = config/sounds/beep.wav
|
||||
startup = config/sounds/startup.wav
|
||||
exit = config/sounds/exit.wav
|
||||
error = config/sounds/error.wav
|
||||
warning = config/sounds/warning.wav
|
||||
question = config/sounds/question.wav
|
||||
information = config/sounds/info.wav
|
||||
default = beep.wav
|
||||
startup = startup.wav
|
||||
exit = exit.wav
|
||||
error = error.wav
|
||||
warning = warning.wav
|
||||
question = question.wav
|
||||
information = info.wav
|
||||
windowOpen =
|
||||
windowClose =
|
||||
|
|
|
|||
|
|
@ -196,9 +196,9 @@ img { max-width: 100%; }
|
|||
<li><a href="#lang.operators">AND</a></li>
|
||||
<li><a href="#ctrl.terminal">ANSI Terminal</a></li>
|
||||
<li><a href="#lang.app">App</a></li>
|
||||
<li><a href="#lang.app">App.Config</a></li>
|
||||
<li><a href="#lang.app">App.Data</a></li>
|
||||
<li><a href="#lang.app">App.Path</a></li>
|
||||
<li><a href="#lang.app">App.User</a></li>
|
||||
<li><a href="#lang.screenobj">Arrange</a></li>
|
||||
<li><a href="#lang.variants">ARRAY</a></li>
|
||||
<li><a href="#lang.func.string">ASC</a></li>
|
||||
|
|
@ -511,6 +511,11 @@ img { max-width: 100%; }
|
|||
<li><a href="#lang.runtime">FileOpsIsWritable</a></li>
|
||||
<li><a href="#lang.runtime">FileOpsJobBytesDone</a></li>
|
||||
<li><a href="#lang.runtime">FileOpsJobClearAttr</a></li>
|
||||
<li><a href="#lang.runtime">FileOpsKindBrowseDir$</a></li>
|
||||
<li><a href="#lang.runtime">FileOpsKindDir$</a></li>
|
||||
<li><a href="#lang.runtime">FileOpsKindFind$</a></li>
|
||||
<li><a href="#lang.runtime">FileOpsKindList</a></li>
|
||||
<li><a href="#lang.runtime">FileOpsKindName$</a></li>
|
||||
<li><a href="#lang.runtime">FileOpsLongNames</a></li>
|
||||
<li><a href="#lang.runtime">FileOpsMakePath</a></li>
|
||||
<li><a href="#lang.runtime">FileOpsPathIsAbsolute</a></li>
|
||||
|
|
@ -523,6 +528,9 @@ img { max-width: 100%; }
|
|||
<li><a href="#lang.runtime">FileOpsShortPath$</a></li>
|
||||
<li><a href="#lang.runtime">FileOpsTempDir$</a></li>
|
||||
<li><a href="#lang.runtime">FileOpsTotalBytes</a></li>
|
||||
<li><a href="#lang.runtime">FileOpsUserDir$</a></li>
|
||||
<li><a href="#lang.runtime">FileOpsUserInTemp</a></li>
|
||||
<li><a href="#lang.runtime">FileOpsUserPath$</a></li>
|
||||
<li><a href="#lang.runtime">FileOpsValidLongName</a></li>
|
||||
<li><a href="#lang.runtime">FileOpsValidName</a></li>
|
||||
<li><a href="#lang.runtime">FileOpsValidShortName</a></li>
|
||||
|
|
@ -1082,7 +1090,6 @@ img { max-width: 100%; }
|
|||
<li><a href="#lang.runtime">ShellArgQuote$</a></li>
|
||||
<li><a href="#lang.runtime">ShellCommandArgs$</a></li>
|
||||
<li><a href="#lang.runtime">ShellCommandProgram$</a></li>
|
||||
<li><a href="#lang.runtime">ShellConfigFile$</a></li>
|
||||
<li><a href="#lang.runtime">ShellGroupsLoad</a></li>
|
||||
<li><a href="#lang.runtime">ShellItemGet$</a></li>
|
||||
<li><a href="#lang.runtime">ShellItemIcon$</a></li>
|
||||
|
|
@ -1250,6 +1257,7 @@ img { max-width: 100%; }
|
|||
<li><a href="#ctrl.form">Unload</a></li>
|
||||
<li><a href="#lang.loops">UNTIL</a></li>
|
||||
<li><a href="#ctrl.image">UseMaskColor</a></li>
|
||||
<li><a href="#lang.runtime">User Folder</a></li>
|
||||
<li><a href="#lang.usercontrol">UserControl</a></li>
|
||||
<li><a href="#lang.usercontrol">USERCONTROL</a></li>
|
||||
<li><a href="#ide.usercontrols">UserControls in the IDE</a></li>
|
||||
|
|
@ -2076,7 +2084,7 @@ End Sub</code></pre>
|
|||
</div>
|
||||
<div class="topic" id="ide.preferences">
|
||||
<h1>Preferences</h1>
|
||||
<p>Open via Tools > Preferences. Settings are saved to dvxbasic.ini in the app's config directory. The dialog has two tabs.</p>
|
||||
<p>Open via Tools > Preferences. Settings are saved to dvxbasic.ini in the IDE's folder in your user folder (USER\APPS\KPUNCH\DVXBASIC). The dialog has two tabs.</p>
|
||||
<h2>General Tab</h2>
|
||||
<h3>Editor Section</h3>
|
||||
<pre> Setting Description Default
|
||||
|
|
@ -4608,14 +4616,16 @@ SQLClose db</code></pre>
|
|||
</div>
|
||||
<div class="topic" id="lang.app">
|
||||
<h1>App Object</h1>
|
||||
<p>The App object provides read-only properties for the application's directory paths.</p>
|
||||
<p>The App object provides read-only properties for the program's folders.</p>
|
||||
<pre> Property Returns Description
|
||||
-------- ------- -----------
|
||||
App.Path String Directory containing the application's executable
|
||||
App.Config String Directory for application configuration files
|
||||
App.Data String Directory for application data files (databases, etc.)</pre>
|
||||
<pre><code>configFile$ = App.Config & "\settings.ini"
|
||||
dbPath$ = App.Data & "\myapp.db"
|
||||
App.Path String The folder the program is in
|
||||
App.User String The program's own folder in the user folder: its settings and every file it saves (databases, saved games, documents)
|
||||
App.Data String The DATA folder in the program's folder: the files the program comes with (read-only)</pre>
|
||||
<p>App.User is the place to write: it is kept apart from the program, so installing a new version never overwrites what is there, and backing up the user folder keeps it. Every copy of the program gets the same folder. It is made the first time App.User is used. App.Data is for reading only: on a CD-ROM it cannot be written at all. Programs never write in DVX's SHARED folder.</p>
|
||||
<pre><code>settingsFile$ = App.User & "\settings.ini"
|
||||
dbPath$ = App.User & "\myapp.db"
|
||||
levelFile$ = App.Data & "\level1.dat"
|
||||
Print "Running from: " & App.Path</code></pre>
|
||||
</div>
|
||||
<div class="topic" id="lang.clipboard">
|
||||
|
|
@ -4670,13 +4680,13 @@ End Sub</code></pre>
|
|||
<h2>IniRead</h2>
|
||||
<p>Reads a value from an INI file. Returns the default value if the file, section, or key is not found. All four arguments are required. Section and key names match without regard to case, and blanks around them are ignored ([ User ] is section User). If a name appears twice, the first one counts. Keys above the first section header belong to section "" (an empty section name). Lines starting with ; or # are comments.</p>
|
||||
<pre><code>value$ = IniRead(file$, section$, key$, default$)</code></pre>
|
||||
<pre><code>name$ = IniRead(App.Config & "\app.ini", "User", "Name", "Unknown")
|
||||
fontSize = Val(IniRead(App.Config & "\app.ini", "Display", "FontSize", "12"))</code></pre>
|
||||
<pre><code>name$ = IniRead(App.User & "\app.ini", "User", "Name", "Unknown")
|
||||
fontSize = Val(IniRead(App.User & "\app.ini", "Display", "FontSize", "12"))</code></pre>
|
||||
<h2>IniWrite</h2>
|
||||
<p>Writes a value to an INI file. Creates the file, section, or key if they do not exist. A new key goes after the last key of its section and a new section at the end of the file. Every other line -- comments, blank lines, other keys, lines of any length -- is kept exactly as it is. The changed key is written as key = value. Every line of the saved file ends in a carriage return and line feed (CR LF). The file is saved through a temporary file, so a failed write leaves the old file intact. If the file exists but cannot be read, or the new file cannot be written, IniWrite raises error 75 (Path/file access error) and leaves the file alone.</p>
|
||||
<pre><code>INIWRITE file$, section$, key$, value$</code></pre>
|
||||
<pre><code>IniWrite App.Config & "\app.ini", "User", "Name", "Scott"
|
||||
IniWrite App.Config & "\app.ini", "Display", "FontSize", Str$(fontSize)</code></pre>
|
||||
<pre><code>IniWrite App.User & "\app.ini", "User", "Name", "Scott"
|
||||
IniWrite App.User & "\app.ini", "Display", "FontSize", Str$(fontSize)</code></pre>
|
||||
</div>
|
||||
<div class="topic" id="lang.constants">
|
||||
<h1>Predefined Constants</h1>
|
||||
|
|
@ -4917,7 +4927,7 @@ End Sub</code></pre>
|
|||
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$, ...)</pre>
|
||||
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, 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, ShellGroupsLoad, ShellItemGet$, ShellItemIcon$, ...)</pre>
|
||||
<h2>Using an Include Library</h2>
|
||||
<pre><code>' With commdlg.bas added to the project
|
||||
Dim path As String
|
||||
|
|
@ -5093,7 +5103,8 @@ End Sub</code></pre>
|
|||
<p>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.</p>
|
||||
<p>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.</p>
|
||||
<p>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$.</p>
|
||||
<p>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).</p>
|
||||
<p>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 nothing can be saved: DVX runs from such a disk and there is no TEMP folder to keep the user folder in. FileOpsTempDir$() is the folder for scratch files ("" when there is none).</p>
|
||||
<p>DVX's folders: FileOpsUserDir$() is the user folder (USER in the DVX folder, or the folder DVXUSER names), where everything DVX and its programs save goes; FileOpsUserPath$(name) is a file or folder in it, for example FileOpsUserPath$("DOWNLOAD"); FileOpsUserInTemp() is True when DVX runs from a disk it cannot write to and keeps the user folder in the TEMP folder. A program's own folder there is App.User. The fonts, sounds, themes, and desktop pictures come in two folders each, DVX's own in SHARED and the user's in the user folder; the kinds are FILEOPS_KIND_FONTS, FILEOPS_KIND_SOUNDS, FILEOPS_KIND_THEMES, and FILEOPS_KIND_WPAPER. FileOpsKindDir$(kind, user) is DVX's folder of a kind (user False) or the user's (True); FileOpsKindBrowseDir$(kind) is the one a file dialog starts in (the user's when it exists); FileOpsKindFind$(kind, fileName) finds a file by name, the user's first ("" when there is none); FileOpsKindName$(kind, path) is the name alone of a file in one of the two folders, the way settings store it ("" for any other path); FileOpsKindList(kind) lists both folders as one, sorted by name, a user's file hiding DVX's of the same name (a list for FileOpsListCount, FileOpsListName$, and the rest; FileOpsListFree when done). Programs read DVX's SHARED folder but never write in it.</p>
|
||||
<p>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.</p>
|
||||
<pre><code>Dim job As Long
|
||||
|
||||
|
|
@ -5149,15 +5160,15 @@ Sub FillTaskList ()
|
|||
End Sub</code></pre>
|
||||
<p>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.</p>
|
||||
<p>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).</p>
|
||||
<p>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).</p>
|
||||
<p>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. A program keeps its settings in App.User, as DVX's own programs do; 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).</p>
|
||||
<p>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.</p>
|
||||
<pre><code>Dim groups As Long, main As Long, i As Long
|
||||
groups = ShellGroupsLoad(ShellConfigFile$("groups.ini"))
|
||||
groups = ShellGroupsLoad(App.User & "\groups.ini")
|
||||
main = ShellGroupFind(groups, "Main")
|
||||
i = ShellItemAdd(groups, main, "Notepad", "APPS\KPUNCH\NOTEPAD\NOTEPAD.APP")
|
||||
If Not ShellItemSet(groups, main, i, "key", "Ctrl+Alt+N") Then MsgBox "Not a shortcut."
|
||||
picIcon.Picture = LOADPICTURE(ShellItemIcon$(ShellItemGet$(groups, main, i, "icon"), ShellItemGet$(groups, main, i, "program")))
|
||||
If Not ShellGroupsSave(groups, ShellConfigFile$("groups.ini")) Then MsgBox "Not saved."
|
||||
If Not ShellGroupsSave(groups, App.User & "\groups.ini") Then MsgBox "Not saved."
|
||||
ShellGroupsFree groups</code></pre>
|
||||
<pre><code>Sub Form1_Load ()
|
||||
Dim copies As Long
|
||||
|
|
@ -5181,7 +5192,7 @@ End Sub</code></pre>
|
|||
<h2>Changing DVX's Settings</h2>
|
||||
<p>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.</p>
|
||||
<p>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.</p>
|
||||
<p>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.</p>
|
||||
<p>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; DVX's own are in SHARED\THEMES, the user's in the THEMES folder of the user folder, FileOpsKindDir$(FILEOPS_KIND_THEMES, True)); both return True when they worked.</p>
|
||||
<p>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.</p>
|
||||
<p>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.</p>
|
||||
<p>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).</p>
|
||||
|
|
@ -5569,7 +5580,7 @@ SecDhFree dh</code></pre>
|
|||
<h2>Settings Files</h2>
|
||||
<p>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.</p>
|
||||
<pre><code>Dim ini As Long
|
||||
ini = PrefsLoad(ShellConfigFile$("MYAPP.INI"))
|
||||
ini = PrefsLoad(App.User & "\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."
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ img { max-width: 100%; }
|
|||
<li><a href="#arch.dialogs">Modal Dialogs</a></li>
|
||||
<li><a href="#arch.resources">Resource System</a></li>
|
||||
<li><a href="#arch.prefs">Preferences System</a></li>
|
||||
<li><a href="#arch.folders">DVX Folders</a></li>
|
||||
<li><a href="#arch.memtrack">Per-App Memory Tracking</a></li>
|
||||
<li><a href="#arch.dxe">DXE Module System</a></li>
|
||||
<li><a href="#arch.events">Event Model</a></li>
|
||||
|
|
@ -131,7 +132,7 @@ img { max-width: 100%; }
|
|||
<li><a href="#shell.lifecycle">Lifecycle API</a></li>
|
||||
<li><a href="#shell.query">Query API</a></li>
|
||||
<li><a href="#shell.startup">Starting Up</a></li>
|
||||
<li><a href="#shell.config">Configuration API</a></li>
|
||||
<li><a href="#shell.config">Program Folders API</a></li>
|
||||
<li><a href="#shell.desktop">Desktop Callbacks</a></li>
|
||||
<li><a href="#shell.info">System Information</a></li>
|
||||
<li><a href="#shell.programs">Installed Programs</a></li>
|
||||
|
|
@ -358,6 +359,7 @@ img { max-width: 100%; }
|
|||
<li><a href="#lib.audio">audioTone</a></li>
|
||||
<li><a href="#user.help">Back</a></li>
|
||||
<li><a href="#arch.pipeline">Backbuffer</a></li>
|
||||
<li><a href="#user.settings">Backing Up</a></li>
|
||||
<li><a href="#lib.basrt.values">basArrayNew</a></li>
|
||||
<li><a href="#lib.basrt">BasArrayT</a></li>
|
||||
<li><a href="#lib.basrt.values">BasArrayT</a></li>
|
||||
|
|
@ -442,7 +444,6 @@ img { max-width: 100%; }
|
|||
<li><a href="#widget.base">Common Properties</a></li>
|
||||
<li><a href="#lib.basdev">compiler.bas</a></li>
|
||||
<li><a href="#arch.pipeline">Compositing</a></li>
|
||||
<li><a href="#games.files">configDir</a></li>
|
||||
<li><a href="#lib.conn">conn</a></li>
|
||||
<li><a href="#lib.conn.basic">conn.bas</a></li>
|
||||
<li><a href="#lib.conn.basic">ConnAttach</a></li>
|
||||
|
|
@ -777,6 +778,8 @@ img { max-width: 100%; }
|
|||
<li><a href="#api.app">dvxUnregisterAppHotkeys</a></li>
|
||||
<li><a href="#api.app">dvxUnregisterHotkey</a></li>
|
||||
<li><a href="#api.app">dvxUpdate</a></li>
|
||||
<li><a href="#user.settings">DVXUSER</a></li>
|
||||
<li><a href="#arch.folders">DVXUSER</a></li>
|
||||
<li><a href="#arch.layers">dvxVideo</a></li>
|
||||
<li><a href="#api.video">dvxVideo.h</a></li>
|
||||
<li><a href="#api.app">dvxWallpaperFromPrefs</a></li>
|
||||
|
|
@ -823,6 +826,7 @@ img { max-width: 100%; }
|
|||
<li><a href="#api.flow">Flowing text</a></li>
|
||||
<li><a href="#api.comp">flushRect</a></li>
|
||||
<li><a href="#user.filedlg">Folders</a></li>
|
||||
<li><a href="#arch.folders">Folders</a></li>
|
||||
<li><a href="#api.font">Font</a></li>
|
||||
<li><a href="#api.text">Font families</a></li>
|
||||
<li><a href="#arch.fonts">Font files</a></li>
|
||||
|
|
@ -1102,6 +1106,7 @@ img { max-width: 100%; }
|
|||
<li><a href="#api.platform">Platform Layer</a></li>
|
||||
<li><a href="#api.platform">PLATFORM_LONG_NAME_MAX</a></li>
|
||||
<li><a href="#api.platform">platformAppExit</a></li>
|
||||
<li><a href="#api.platform">platformBankChunk</a></li>
|
||||
<li><a href="#api.platform">platformBootDrive</a></li>
|
||||
<li><a href="#api.platform">platformClock</a></li>
|
||||
<li><a href="#api.platform">platformDirIsWritable</a></li>
|
||||
|
|
@ -1160,6 +1165,7 @@ img { max-width: 100%; }
|
|||
<li><a href="#api.platform">platformValidateLongName</a></li>
|
||||
<li><a href="#api.platform">platformValidateShortName</a></li>
|
||||
<li><a href="#api.platform">platformVideoInit</a></li>
|
||||
<li><a href="#api.platform">platformVideoReadBack</a></li>
|
||||
<li><a href="#lib.audio">PlayTone</a></li>
|
||||
<li><a href="#lib.audio">PlayWave</a></li>
|
||||
<li><a href="#shell.groups">pmHotkeyParse</a></li>
|
||||
|
|
@ -1316,6 +1322,7 @@ img { max-width: 100%; }
|
|||
<li><a href="#user.settings">Settings</a></li>
|
||||
<li><a href="#user.trouble">Setup</a></li>
|
||||
<li><a href="#lib.serial.security">SHA-256</a></li>
|
||||
<li><a href="#arch.folders">SHARED</a></li>
|
||||
<li><a href="#lib.texthelp">Shared Library</a></li>
|
||||
<li><a href="#shell.basic">shell.bas</a></li>
|
||||
<li><a href="#shell.types">SHELL_APP_DESCRIPTOR</a></li>
|
||||
|
|
@ -1360,12 +1367,11 @@ img { max-width: 100%; }
|
|||
<li><a href="#shell.lifecycle">shellCloseApp</a></li>
|
||||
<li><a href="#shell.basic">ShellCommandArgs</a></li>
|
||||
<li><a href="#shell.basic">ShellCommandProgram</a></li>
|
||||
<li><a href="#shell.basic">ShellConfigFile</a></li>
|
||||
<li><a href="#shell.config">shellConfigPath</a></li>
|
||||
<li><a href="#shell.desktop">shellCtrlEscFn</a></li>
|
||||
<li><a href="#shell.config">shellDataPath</a></li>
|
||||
<li><a href="#shell.desktop">shellDesktopChangeCount</a></li>
|
||||
<li><a href="#shell.desktop">shellDesktopUpdate</a></li>
|
||||
<li><a href="#shell.config">shellEnsureConfigDir</a></li>
|
||||
<li><a href="#shell.config">shellEnsureUserDir</a></li>
|
||||
<li><a href="#shell.basic">ShellExitDvx</a></li>
|
||||
<li><a href="#shell.lifecycle">shellForceKillApp</a></li>
|
||||
<li><a href="#shell.query">shellGetApp</a></li>
|
||||
|
|
@ -1438,6 +1444,7 @@ img { max-width: 100%; }
|
|||
<li><a href="#shell.desktop">shellUnregisterAppCleanup</a></li>
|
||||
<li><a href="#shell.desktop">shellUnregisterDesktopUpdate</a></li>
|
||||
<li><a href="#shell.desktop">shellUnregisterIdle</a></li>
|
||||
<li><a href="#shell.config">shellUserPath</a></li>
|
||||
<li><a href="#user.trouble">Shift Key at Start</a></li>
|
||||
<li><a href="#user.keys">Shortcut Keys</a></li>
|
||||
<li><a href="#widget.slider">Slider</a></li>
|
||||
|
|
@ -1556,8 +1563,11 @@ img { max-width: 100%; }
|
|||
<li><a href="#lib.texthelp.onkey">Undo</a></li>
|
||||
<li><a href="#user.trouble">Uninstalling DVX</a></li>
|
||||
<li><a href="#user.trouble">Upgrading DVX</a></li>
|
||||
<li><a href="#user.settings">User Folder</a></li>
|
||||
<li><a href="#arch.folders">User folder</a></li>
|
||||
<li><a href="#user.welcome">User Guide</a></li>
|
||||
<li><a href="#sdk.firstcontrol">UserControl (SDK)</a></li>
|
||||
<li><a href="#games.files">userDir</a></li>
|
||||
<li><a href="#sdk.basiccontrols">Using BASIC Controls from C</a></li>
|
||||
<li><a href="#arch.platform">VBE</a></li>
|
||||
<li><a href="#widget.box">VBox</a></li>
|
||||
|
|
@ -1992,7 +2002,7 @@ img { max-width: 100%; }
|
|||
<h2>What You Need</h2>
|
||||
<ul>
|
||||
<li>A PC running DOS with a 386 processor or better. A 486 or Pentium is recommended.</li>
|
||||
<li>A video card that supports VESA 2.0 graphics. Most video cards made since the mid 1990s do.</li>
|
||||
<li>A video card with VESA graphics, which nearly every card from about 1992 on has (some need their VESA driver loaded first; see Troubleshooting).</li>
|
||||
<li>At least 4 MB of memory. 8 MB or more is recommended.</li>
|
||||
<li>A mouse, with its DOS mouse driver loaded before you start DVX. A scroll wheel works if your mouse driver supports it.</li>
|
||||
</ul>
|
||||
|
|
@ -2286,6 +2296,10 @@ img { max-width: 100%; }
|
|||
<p>Settings you keep are saved, and DVX uses them again the next time it starts.</p>
|
||||
<p>DVX plays a short sound when it starts and when it ends, and when a message appears (a different one for errors, warnings, questions, and information). Each sound can be changed or turned off, and the volume set, in the settings program.</p>
|
||||
<blockquote><strong>Tip:</strong> If a new screen resolution leaves the screen unreadable, just wait: DVX goes back to the old one after 15 seconds unless you confirm the change.</blockquote>
|
||||
<h2>Where your settings and files are kept</h2>
|
||||
<p>Everything DVX and its programs save for you goes in one folder, the USER folder inside the DVX folder (for example C:\DVX\USER): your settings, each program's settings and the files it saves (such as saved games), Terminal's downloads, and DVX's log. To back up your work, copy this one folder. Installing a new version of DVX never changes it.</p>
|
||||
<p>The fonts, sounds, themes, and desktop pictures that come with DVX are in the SHARED folder. To add your own, put them in the folder of the same name in your USER folder: FONTS, SOUNDS, THEMES, or WPAPER. The settings program lists yours together with DVX's, and a file of yours with the same name as one of DVX's is used instead of it. Themes you save go there too.</p>
|
||||
<p>To keep the USER folder somewhere else (on another disk, or one for each person who uses the computer), set DVXUSER to that folder before starting DVX, for example SET DVXUSER=D:\MYDVX in AUTOEXEC.BAT.</p>
|
||||
</div>
|
||||
<div class="topic" id="user.taskmgr">
|
||||
<h1>Task Manager</h1>
|
||||
|
|
@ -2359,7 +2373,7 @@ img { max-width: 100%; }
|
|||
<h2>The mouse pointer does not move</h2>
|
||||
<p>DVX uses your DOS mouse driver. Load the mouse driver (usually from AUTOEXEC.BAT, or by typing its name at the DOS prompt) before you start DVX. If the scroll wheel does nothing, your mouse driver may not support wheels; everything else still works. If the pointer is too fast or too slow, change the mouse settings (see Changing Settings).</p>
|
||||
<h2>DVX will not start, or the screen stays blank</h2>
|
||||
<p>DVX needs a video card with VESA 2.0 graphics. Some older cards need a VESA driver program loaded first; check the card's manual.</p>
|
||||
<p>DVX needs a video card with a VESA video BIOS and a mode of at least 640x480 at 256 colors or more, which nearly every card from about 1992 on has. Many older cards need their VESA driver program loaded first (from AUTOEXEC.BAT, or by typing its name at the DOS prompt): UniVBE (Scitech Display Doctor) works with most cards, and some cards came with their own, such as S3VBE20 for S3 cards and M64VBE for ATI Mach64 cards. Older cards work too, but the screen updates more slowly on them. If DVX still will not start, DVX.LOG in the USER folder says why.</p>
|
||||
<p>If the screen goes wrong right after you picked a new screen resolution, wait 15 seconds and DVX puts the old one back.</p>
|
||||
<h2>Safe video mode</h2>
|
||||
<p>If the screen resolution DVX is set to use does not work on your computer (for example after changing video cards), DVX starts in safe video mode instead: 640x480, which every video card can show. A message tells you so and asks whether to use this mode from now on. Answer Yes to keep it, or No to try the old resolution again at the next start.</p>
|
||||
|
|
@ -2372,9 +2386,9 @@ img { max-width: 100%; }
|
|||
<h2>Installing, upgrading, and removing DVX</h2>
|
||||
<p>DVX is installed with SETUP, from the DVX CD or from Disk 1 of the DVX floppy disks: change to that drive and type SETUP. Setup shows the licence, asks for the folder to install DVX in 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.</p>
|
||||
<p>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.</p>
|
||||
<p>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.</p>
|
||||
<p>To upgrade or repair DVX, run Setup again and choose the same folder: answer Yes to keep your settings and saved files (the USER folder), or No to remove them and start over. 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 the USER folder is only removed if you ask.</p>
|
||||
<h2>Running DVX from a CD-ROM or a write-protected disk</h2>
|
||||
<p>DVX also runs straight from a disk it cannot write to. It then tells you once that changed settings last only until you exit DVX, and the help files are used as they are. To keep your settings, install DVX on your hard disk with Setup.</p>
|
||||
<p>DVX also runs straight from a disk it cannot write to. It then keeps your settings and files in a DVXUSER folder in your TEMP folder, and tells you so once. To keep them somewhere of your choosing, set DVXUSER to a folder on your hard disk before starting DVX (see Changing Settings). Without a TEMP folder, DVX tells you that changed settings last only until you exit DVX. Either way the help files are used as they are. To keep everything on your hard disk, install DVX there with Setup.</p>
|
||||
<h2>A program is already running</h2>
|
||||
<p>Most programs can only be open once. Use Alt+Tab or the Task Manager (Ctrl+Esc) to find its window. If you cannot see it, it may be minimized to an icon at the bottom of the screen.</p>
|
||||
<h2>"This program was built for a different version of DVX"</h2>
|
||||
|
|
@ -2388,7 +2402,7 @@ img { max-width: 100%; }
|
|||
<h2>DVX will not exit</h2>
|
||||
<p>When you exit DVX, each program is closed as if you had closed it yourself. If you answer Cancel when a program asks to save its work, DVX stays running. Save or close that program, then choose Exit DVX again.</p>
|
||||
<h2>Reporting a problem</h2>
|
||||
<p>DVX writes a record of what happened to the file DVX.LOG, in the same folder as DVX.EXE. If a program crashes, this file contains details that help the program's author find the problem. The file is started fresh each time DVX starts, so copy it before starting DVX again.</p>
|
||||
<p>DVX writes a record of what happened to the file DVX.LOG, in your USER folder (for example C:\DVX\USER\DVX.LOG). If a program crashes, this file contains details that help the program's author find the problem. The file is started fresh each time DVX starts, so copy it before starting DVX again.</p>
|
||||
<p><a href="#user.settings">Changing Settings</a></p>
|
||||
<p><a href="#user.taskmgr">Task Manager</a></p>
|
||||
</div>
|
||||
|
|
@ -2407,7 +2421,7 @@ img { max-width: 100%; }
|
|||
<h2>What Is libdvx?</h2>
|
||||
<p>libdvx is the heart of DVX -- a DXE3 library that provides:</p>
|
||||
<ul>
|
||||
<li>A VESA VBE 2.0+ video backend (LFB only, no bank switching).</li>
|
||||
<li>A VESA VBE video backend: the linear framebuffer where the card has one, else its 64 KB bank window.</li>
|
||||
<li>Optimised 2D drawing primitives (rectangle fill, bevels, text, bitmap blits, cursors).</li>
|
||||
<li>A dirty-rectangle compositor that minimises LFB traffic.</li>
|
||||
<li>A window manager with Motif-style chrome, drag/resize, menus, and scrollbars.</li>
|
||||
|
|
@ -2459,7 +2473,7 @@ img { max-width: 100%; }
|
|||
<p>DVX (DOS Visual eXecutive) is a complete windowing GUI compositor targeting DJGPP/DPMI on DOS. It provides overlapping windows with Motif-style chrome, a retained-mode widget toolkit, cooperative multitasking of DXE-loaded applications, and a dirty-rectangle compositor optimized for 486/Pentium hardware.</p>
|
||||
<h2>Key Design Constraints</h2>
|
||||
<ul>
|
||||
<li>VESA VBE 2.0+ LFB only -- no bank switching. If the hardware cannot provide a linear framebuffer, initialization fails.</li>
|
||||
<li>VESA VBE video: the linear framebuffer whenever the card offers one; otherwise (VBE 1.x, or a DPMI host that will not map it) the platform flush writes through the 64 KB bank window. Drawing always targets the system RAM backbuffer, so nothing above the flush knows the difference. Initialization fails only without VBE or a usable mode.</li>
|
||||
<li>486 baseline -- all hot paths are written to be fast on a 486, with Pentium-specific paths where the gain is significant.</li>
|
||||
<li>Single-tasking cooperative model -- applications yield the CPU via tsYield(); there is no preemptive scheduler.</li>
|
||||
<li>A single emulator is the trusted reference platform for testing; any bugs observed there are treated as DVX bugs.</li>
|
||||
|
|
@ -2474,6 +2488,7 @@ img { max-width: 100%; }
|
|||
<p><a href="#arch.dialogs">Modal Dialogs</a></p>
|
||||
<p><a href="#arch.resources">Resource System</a></p>
|
||||
<p><a href="#arch.prefs">Preferences System</a></p>
|
||||
<p><a href="#arch.folders">DVX Folders</a></p>
|
||||
<p><a href="#arch.memtrack">Per-App Memory Tracking</a></p>
|
||||
<p><a href="#arch.dxe">DXE Module System</a></p>
|
||||
<p><a href="#arch.events">Event Model</a></p>
|
||||
|
|
@ -2728,9 +2743,9 @@ img { max-width: 100%; }
|
|||
</div>
|
||||
<div class="topic" id="arch.prefs">
|
||||
<h1>Preferences System (dvxPrefs.h)</h1>
|
||||
<p>Handle-based API over classic INI files. Multiple files can be open at once. Each prefsLoad / prefsCreate returns a PrefsHandleT opaque pointer passed to subsequent calls and freed with prefsClose. The system INI lives at DVX_INI_PATH = "CONFIG\\DVX.INI".</p>
|
||||
<p>Handle-based API over classic INI files. Multiple files can be open at once. Each prefsLoad / prefsCreate returns a PrefsHandleT opaque pointer passed to subsequent calls and freed with prefsClose. The system INI is DVX.INI in the user folder: prefsLoad(platformDvxIniPath()).</p>
|
||||
<h2>Usage Pattern</h2>
|
||||
<pre><code>PrefsHandleT *h = prefsLoad(DVX_INI_PATH);
|
||||
<pre><code>PrefsHandleT *h = prefsLoad(platformDvxIniPath());
|
||||
int32_t w = prefsGetInt(h, "video", "width", 640);
|
||||
bool sound = prefsGetBool(h, "audio", "enable", true);
|
||||
const char *bg = prefsGetString(h, "desktop", "wallpaper", "");
|
||||
|
|
@ -2739,6 +2754,32 @@ prefsSetInt(h, "video", "width", 800);
|
|||
prefsSave(h);
|
||||
prefsClose(h);</code></pre>
|
||||
<p>Boolean getters recognise "true"/"yes"/"1" and "false"/"no"/"0" (case-insensitive). If the file does not exist, prefsLoad still returns a valid empty handle with the path captured so prefsSave can write a new file.</p>
|
||||
<p><a href="#arch.folders">DVX Folders</a></p>
|
||||
</div>
|
||||
<div class="topic" id="arch.folders">
|
||||
<h1>DVX Folders (dvxPlat.h)</h1>
|
||||
<p>Files are kept apart by who writes them. The DVX folder holds the program: Setup installs it, an upgrade replaces all of it, and DVX only reads it. Everything written while DVX runs goes in the user folder, the one folder to back up.</p>
|
||||
<pre><code>C:\DVX\
|
||||
DVX.EXE SETUP.EXE SETUP.LST
|
||||
SYSTEM\ tools, and the shipped DVX.INI and ASSOC.INI
|
||||
LIBS\ WIDGETS\
|
||||
APPS\<vendor>\<program>\ a program, its help, and DATA\ (files it ships)
|
||||
SHARED\ shipped for every program
|
||||
FONTS\ SOUNDS\ THEMES\ WPAPER\
|
||||
USER\ the user folder
|
||||
DVX.INI ASSOC.INI DVX.LOG
|
||||
FONTS\ SOUNDS\ THEMES\ WPAPER\ the user's own files
|
||||
APPS\<vendor>\<program>\ each program's settings and saved files
|
||||
DOWNLOAD\ Terminal's downloads</code></pre>
|
||||
<ul>
|
||||
<li>The user folder is USER in the DVX folder unless the DVXUSER environment variable names another (a second disk, a network folder per person, a writable folder when DVX runs from a CD-ROM). When it cannot be written, DVX uses DVXUSER in the TEMP folder and says so; without TEMP, settings are kept until DVX exits.</li>
|
||||
<li>The first start copies SYSTEM\DVX.INI (with its comments and defaults) to the user folder. ASSOC.INI in the user folder holds only the user's changes to SYSTEM\ASSOC.INI (an empty value removes a shipped file type), so an upgrade's new file types appear while the user's choices stay.</li>
|
||||
<li>SHARED belongs to DVX: only DVX's own package fills it. A program reads it but never writes or installs into it; a program's own files live with it (DATA, or resources in its .app), under its vendor and program folder, so two programs' files never collide.</li>
|
||||
<li>SHARED and the user folder have the same four kinds of folder. A lookup looks in the user's first, so a user's file of the same name wins; lists show both; saving always goes to the user's. Settings store a file in one of these folders by its name alone.</li>
|
||||
<li>Each program's writable folder is the user folder's APPS\ plus the program's path (DxeAppContextT.userDir, BASIC App.User), made on first use. Its shipped files are in DATA beside the .app (shellDataPath, BASIC App.Data), read-only.</li>
|
||||
<li>The help files the loader recompiles when libraries or widgets change are written in APPS: DVX updating its own install. The step is skipped when APPS cannot be written.</li>
|
||||
</ul>
|
||||
<p>Every folder name is defined once in dvxPlat.h (DVX_SYSTEM_DIR, DVX_APPS_DIR, DVX_SHARED_DIR, DVX_USER_DIR, DVX_DATA_DIR, and the kinds' names), with the functions that find them (platformUserInit, platformUserPath, platformDvxIniPath, platformAppUserDir, platformKindFind, platformKindReadDir, and the rest; see the API reference).</p>
|
||||
</div>
|
||||
<div class="topic" id="arch.memtrack">
|
||||
<h1>Per-App Memory Tracking (dvxMem.h)</h1>
|
||||
|
|
@ -2953,7 +2994,7 @@ prefsClose(h);</code></pre>
|
|||
<h3>Video</h3>
|
||||
<p>platformVideoInit() -- mode probe and framebuffer setup. platformVideoShutdown() -- restore previous mode. platformVideoEnumModes() -- enumerate available modes.</p>
|
||||
<h3>Framebuffer Flush</h3>
|
||||
<p>platformFlushRect() -- copy dirty rect from backBuf to LFB. On DOS, each scanline uses rep movsd for near-optimal aligned 32-bit writes over the PCI bus.</p>
|
||||
<p>platformFlushRect() -- copy dirty rect from backBuf to video memory. On DOS, each scanline uses rep movsd for near-optimal aligned 32-bit writes over the bus, to the linear framebuffer or, on a card without one, through the 64 KB bank window (platformBankChunk cuts the runs at the window's end).</p>
|
||||
<h3>Optimized Memory Spans</h3>
|
||||
<p>Six functions: platformSpanFill8/16/32() and platformSpanCopy8/16/32(). Called once per scanline of every rectangle fill, blit, and text draw. On DOS these use inline assembly for critical inner loops.</p>
|
||||
<h3>Mouse Input</h3>
|
||||
|
|
@ -3045,7 +3086,7 @@ prefsClose(h);</code></pre>
|
|||
+-- serial/ Combined serial stack DXE
|
||||
+-- proxy/ Linux proxy (emulator <-> secLink <-> telnet)
|
||||
+-- sql/ SQLite integration
|
||||
+-- bin/ Build output (dvx.exe, libs/, widgets/, apps/, config/)
|
||||
+-- bin/ Build output (dvx.exe, system/, libs/, widgets/, apps/, shared/)
|
||||
+-- obj/ Intermediate object files
|
||||
+-- docs/ Documentation</code></pre>
|
||||
</div>
|
||||
|
|
@ -3499,12 +3540,12 @@ DVX_ABI_STAMP(mylib);</code></pre>
|
|||
<p>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.</p>
|
||||
<h2>Fonts a Program Carries</h2>
|
||||
<p>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.</p>
|
||||
<p>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.</p>
|
||||
<p>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 installed fonts. 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.</p>
|
||||
<p>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).</p>
|
||||
<h2>Constants</h2>
|
||||
<pre> Define Value Description
|
||||
------ ----- -----------
|
||||
DVX_FONTS_DIR "FONTS" The folder of .DFN files (beside CONFIG)
|
||||
DVX_FONTS_DIR "FONTS" The fonts folder (dvxPlat.h), in SHARED and in the user folder
|
||||
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</pre>
|
||||
|
|
@ -3659,7 +3700,7 @@ dvxFlowPaint(flow, d, ops, x, y, fg);</code></pre>
|
|||
</div>
|
||||
<div class="topic" id="api.video">
|
||||
<h1>dvxVideo.h -- Layer 1: VESA VBE Video Backend</h1>
|
||||
<p>The lowest layer. Responsible for VESA VBE mode negotiation, LFB mapping via DPMI, system RAM backbuffer allocation, pixel format discovery, and color packing. LFB-only design: bank switching is deliberately unsupported.</p>
|
||||
<p>The lowest layer. Responsible for VESA VBE mode negotiation, LFB mapping via DPMI (or, for a card with no linear framebuffer, its bank window), system RAM backbuffer allocation, pixel format discovery, and color packing.</p>
|
||||
<h2>videoInit</h2>
|
||||
<pre><code>int32_t videoInit(DisplayT *d, int32_t requestedW, int32_t requestedH, int32_t preferredBpp);</code></pre>
|
||||
<p>Probe VBE for a mode matching the requested resolution and depth, enable it, map the LFB into DPMI linear address space, and allocate a system RAM backbuffer. preferredBpp is a hint; the closest available depth is used if an exact match is not found.</p>
|
||||
|
|
@ -4921,7 +4962,7 @@ void dvxSetSoundHook(DvxSoundHookT fn);</code></pre>
|
|||
typedef void (*ExclusiveFnT)(WindowT *win, ExclusiveStateE state, void *userData);</code></pre>
|
||||
<h3>dvxExclusiveBegin</h3>
|
||||
<pre><code>int32_t dvxExclusiveBegin(AppContextT *ctx, WindowT *win, int32_t width, int32_t height, int32_t bpp, ExclusiveFnT onChange, void *userData);</code></pre>
|
||||
<p>Take the whole screen for win (raised and focused): the smallest mode of at least width x height, at bpp when there is one (0: any depth). At 8 bpp (or 0) a request that fits 320x200 gets VGA mode 13h, and one that fits 320x240 VGA Mode X (square pixels, two pages flipped at the retrace), unless the card has a VBE mode no larger. onChange (may be NULL) hears ExclusiveActiveE before this returns. A window that already has full screen gets the new size.</p>
|
||||
<p>Take the whole screen for win (raised and focused): the smallest mode of at least width x height, at bpp when there is one (0: any depth). At 8 bpp (or 0) a request that fits 320x200 gets VGA mode 13h, and one that fits 320x240 VGA Mode X (square pixels, two pages flipped at the retrace), unless the card has a VBE mode no larger with a linear frame buffer. onChange (may be NULL) hears ExclusiveActiveE before this returns. A window that already has full screen gets the new size.</p>
|
||||
<p>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).</p>
|
||||
<h3>dvxExclusiveEnd</h3>
|
||||
<pre><code>void dvxExclusiveEnd(AppContextT *ctx, WindowT *win);</code></pre>
|
||||
|
|
@ -6152,14 +6193,11 @@ if (dvxFontDialog(ctx, NULL, 0, &choice)) {
|
|||
<div class="topic" id="api.prefs">
|
||||
<h1>dvxPrefs.h -- INI Preferences System</h1>
|
||||
<p>Handle-based API over classic INI files. Each prefsLoad/prefsCreate returns a PrefsHandleT that must be passed to all subsequent calls and freed with prefsClose when done. Multiple INI files can be open simultaneously.</p>
|
||||
<p>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).</p>
|
||||
<p>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 platformDvxIniPath() (the user folder's DVX.INI), 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).</p>
|
||||
<pre><code>PrefsHandleT *prefsBasHandle(int32_t handle);</code></pre>
|
||||
<p>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.</p>
|
||||
<h2>Constants</h2>
|
||||
<pre> Define Value Description
|
||||
------ ----- -----------
|
||||
DVX_CONFIG_DIR "CONFIG" Settings folder, relative to the DVX folder
|
||||
DVX_INI_PATH DVX_CONFIG_DIR DVX_PATH_SEP "DVX.INI" Canonical system INI path</pre>
|
||||
<h2>The system INI</h2>
|
||||
<p>DVX's own settings are in DVX.INI in the user folder: prefsLoad(platformDvxIniPath()) (dvxPlat.h, DVX folders). The first start copies the shipped SYSTEM\DVX.INI there, with its comments and defaults; keys it does not have keep the defaults compiled into the code.</p>
|
||||
<h2>Types</h2>
|
||||
<h3>PrefsHandleT</h3>
|
||||
<p>Opaque handle representing an in-memory INI file. Fields are not exposed.</p>
|
||||
|
|
@ -6599,20 +6637,28 @@ void platformShutdownHookRemove(PlatformShutdownFnT fn);</code></pre>
|
|||
<h3>platformVideoInit / platformVideoShutdown</h3>
|
||||
<pre><code>int32_t platformVideoInit(DisplayT *d, int32_t requestedW, int32_t requestedH, int32_t preferredBpp);
|
||||
void platformVideoShutdown(DisplayT *d);</code></pre>
|
||||
<p>Low-level video mode setup and teardown. Called by videoInit / videoShutdown.</p>
|
||||
<p>Low-level video mode setup and teardown. Called by videoInit / videoShutdown. On DOS the mode is used through its linear framebuffer when the card offers one (VBE 2.0+) and the DPMI host maps it; otherwise through its 64 KB bank window (any VBE version), which only platformFlushRect sees. The log says which ("Video: 640x480 at 16 bpp, linear frame buffer" or "..., banked: window A, 64 KB in 64 KB steps"). It fails only when there is no VBE ("VBE: No VESA BIOS extension (VBE) found") or no usable mode; the shell then tells the user to load a VESA driver such as UniVBE.</p>
|
||||
<h3>platformVideoEnumModes</h3>
|
||||
<pre><code>void platformVideoEnumModes(void (*cb)(int32_t w, int32_t h, int32_t bpp, void *userData), void *userData);</code></pre>
|
||||
<p>Enumerate the graphics modes platformVideoInit can actually set (on DOS: VBE modes with a linear framebuffer at 8, 15, 16, or 32 bpp; 24 bpp is not offered). cb is invoked once per distinct width/height/depth. Feeds the video-mode list offered to the user; platformVideoInit selects its own best match independently.</p>
|
||||
<p>Enumerate the graphics modes platformVideoInit can actually set (on DOS: VBE modes at 8, 15, 16, or 32 bpp, linear or banked; 24 bpp is not offered). cb is invoked once per distinct width/height/depth. Feeds the video-mode list offered to the user; platformVideoInit selects its own best match independently.</p>
|
||||
<h3>platformVideoSetPalette</h3>
|
||||
<pre><code>void platformVideoSetPalette(const uint8_t *pal, int32_t firstEntry, int32_t count);</code></pre>
|
||||
<p>Program the VGA/VESA DAC palette registers (8-bit mode only). pal is the full 256-entry RGB table (3 bytes per entry); entries firstEntry..firstEntry+count-1 are written, clamped to the 256-entry table.</p>
|
||||
<h3>platformVideoFreeBuffers</h3>
|
||||
<pre><code>void platformVideoFreeBuffers(DisplayT *d);</code></pre>
|
||||
<p>Free the backbuffer and palette without restoring text mode. Used when switching between graphics modes live.</p>
|
||||
<h3>platformVideoReadBack</h3>
|
||||
<pre><code>bool platformVideoReadBack(const DisplayT *d, uint8_t *out);</code></pre>
|
||||
<p>Copy what video memory holds for d (the display on show: the desktop, or a full-screen program's) into out, pitch * height bytes, from the linear framebuffer or through a banked mode's window that can be read (which may be the other window). False where it cannot: Mode X (platformVideoExclusiveCapture reads that), or a banked mode with no readable window. The DOS test harness's VIDEOCHECK step compares it with the backbuffer, since screenshots are taken from the backbuffer and could not show a flush that wrote to the wrong place.</p>
|
||||
<h2>Framebuffer Flush</h2>
|
||||
<h3>platformFlushRect</h3>
|
||||
<pre><code>void platformFlushRect(const DisplayT *d, const RectT *r);</code></pre>
|
||||
<p>Copy a rectangle from the system RAM backbuffer to the LFB. The single critical path where PCI bus write speed matters. On DOS, each scanline uses rep movsd for aligned 32-bit writes.</p>
|
||||
<p>Copy a rectangle from the system RAM backbuffer to video memory. The single critical path where bus write speed matters. On DOS, each scanline (or the whole rectangle, when it spans whole scanlines) is one rep movsd run of aligned 32-bit writes to the linear framebuffer. A mode with no linear framebuffer (a VBE 1.x card, or a DPMI host that will not map it) is written through its bank window instead: a run is cut where the window ends and the window moved on (VBE function 4F05h) only when the next byte is outside it. System Information says which ("Video memory access").</p>
|
||||
<h3>platformBankChunk</h3>
|
||||
<pre><code>#define PLATFORM_BANK_NONE 0xFFFFFFFFu
|
||||
|
||||
uint32_t platformBankChunk(uint32_t offset, uint32_t length, uint32_t granBytes, uint32_t winBytes, uint32_t *winStart);</code></pre>
|
||||
<p>The window arithmetic of the banked flush, shared so the host tests check it. The window shows winBytes of video memory from *winStart and moves in steps of granBytes (granBytes <= winBytes). For a copy of length bytes to offset, it moves *winStart to the step at or below offset when offset is not in the window (or *winStart is PLATFORM_BANK_NONE, not known yet, as after a mode set), and returns how many bytes fit before the window ends. The caller switches the bank when *winStart changed, copies that many bytes, and goes on with the rest.</p>
|
||||
<h2>Span Operations</h2>
|
||||
<h3>platformSpanFill8/16/32</h3>
|
||||
<pre><code>void platformSpanFill8(uint8_t *dst, uint32_t color, int32_t count);
|
||||
|
|
@ -6735,7 +6781,7 @@ char *platformPathTidy(char *path);</code></pre>
|
|||
#define PLATFORM_SIBLING_EXT_MAX 3
|
||||
|
||||
bool platformPathSibling(char *out, int32_t outSize, const char *path, const char *ext);</code></pre>
|
||||
<p>Write path with its file-name extension replaced by ext into out, followed by as many leading characters of the original extension as fit in PLATFORM_SIBLING_EXT_MAX characters (a full-length ext such as ".BAK" replaces the extension outright; a name with no extension gets ext appended). Replacing rather than appending keeps an 8.3 name valid on DOS without long file names, and keeping part of the original extension gives files that differ only in extension distinct scratch names: CONFIG\DVX.INI becomes CONFIG\DVX.$IN (temp) and CONFIG\DVX.~IN (backup), FOO.APP and FOO.HLP become FOO.$AP and FOO.$HL. Returns false when the result does not fit in outSize or would name path itself (compared case-insensitively).</p>
|
||||
<p>Write path with its file-name extension replaced by ext into out, followed by as many leading characters of the original extension as fit in PLATFORM_SIBLING_EXT_MAX characters (a full-length ext such as ".BAK" replaces the extension outright; a name with no extension gets ext appended). Replacing rather than appending keeps an 8.3 name valid on DOS without long file names, and keeping part of the original extension gives files that differ only in extension distinct scratch names: USER\DVX.INI becomes USER\DVX.$IN (temp) and USER\DVX.~IN (backup), FOO.APP and FOO.HLP become FOO.$AP and FOO.$HL. Returns false when the result does not fit in outSize or would name path itself (compared case-insensitively).</p>
|
||||
<h3>platformReplaceFile</h3>
|
||||
<pre><code>bool platformReplaceFile(const char *tmpPath, const char *path, const char *bakPath);</code></pre>
|
||||
<p>Crash-safe swap of a fully written tmpPath in for path. The original is renamed aside to bakPath first (DOS rename cannot overwrite) and removed only once the replacement is in place; if the swap fails the original is restored and tmpPath removed. A directory at path is never moved aside: the call fails and removes tmpPath. A leftover bakPath is removed only while path exists; with path missing it may be the only copy left by a crash between the two renames, so it is kept (prefsLoad reads it back). The preferences system, the resource writer, and the help compiler all save this way, with the PLATFORM_TMP_EXT and PLATFORM_BAK_EXT siblings from platformPathSibling.</p>
|
||||
|
|
@ -6770,6 +6816,53 @@ char platformBootDrive(void);</code></pre>
|
|||
<p>platformDriveList fills out[] with the drives that exist, in letter order, and returns the count. On DOS it uses IOCTL calls only (INT 21h AX=4409h remote, 440Eh logical drive map, 4408h removable), never the media, so empty floppy and CD-ROM drives are listed at once; the second letter of a shared floppy drive (B: on a one-floppy system) is left out because using it makes DOS ask for a disk swap. The host backend reports one drive, "/". System Information and the file operations library both use it.</p>
|
||||
<p>platformDiskSpace reports the size and free space of the disk holding path (DOS: INT 21h AX=7303h, the FAT32 call that counts past 2 GB, on the root of the path's drive letter or the current drive, falling back to AH=36h, which tops out near 2 GB, where DOS does not have it; host: statvfs of the nearest existing folder of path, so a folder about to be created reports its disk). False when unknown.</p>
|
||||
<p>platformBootDrive returns the letter of the drive DOS started from (INT 21h AX=3305h, DOS 4.0+), whose root holds AUTOEXEC.BAT, or 0 where there is none (older DOS, and every platform but DOS).</p>
|
||||
<h3>DVX folders: platformUserInit / platformUserDir / platformUserPath / platformDvxIniPath / platformAppUserDir / platformKindFind</h3>
|
||||
<pre><code>#define DVX_SYSTEM_DIR "SYSTEM"
|
||||
#define DVX_APPS_DIR "APPS"
|
||||
#define DVX_SHARED_DIR "SHARED"
|
||||
#define DVX_USER_DIR "USER"
|
||||
#define DVX_DATA_DIR "DATA"
|
||||
#define DVX_USER_ENV "DVXUSER"
|
||||
#define DVX_INI_NAME "DVX.INI"
|
||||
#define DVX_ASSOC_NAME "ASSOC.INI"
|
||||
#define DVX_FONTS_DIR "FONTS"
|
||||
#define DVX_SOUNDS_DIR "SOUNDS"
|
||||
#define DVX_THEMES_DIR "THEMES"
|
||||
#define DVX_WPAPER_DIR "WPAPER"
|
||||
|
||||
typedef enum {
|
||||
DvxKindFontsE,
|
||||
DvxKindSoundsE,
|
||||
DvxKindThemesE,
|
||||
DvxKindWpaperE,
|
||||
DvxKindCountE
|
||||
} DvxKindE;
|
||||
|
||||
void platformUserInit(void);
|
||||
const char *platformUserDir(void);
|
||||
bool platformUserDirOf(char *out, int32_t outSize, const char *dvxDir);
|
||||
bool platformUserInTemp(void);
|
||||
bool platformUserPath(char *out, int32_t outSize, const char *rel);
|
||||
const char *platformDvxIniPath(void);
|
||||
bool platformAppUserDir(char *out, int32_t outSize, const char *appPath);
|
||||
const char *platformKindName(DvxKindE kind);
|
||||
bool platformKindDir(char *out, int32_t outSize, DvxKindE kind, bool user);
|
||||
bool platformKindBrowseDir(char *out, int32_t outSize, DvxKindE kind);
|
||||
bool platformKindFind(char *out, int32_t outSize, DvxKindE kind, const char *name);
|
||||
bool platformKindNameOf(DvxKindE kind, const char *path, char *out, int32_t outSize);
|
||||
char **platformKindReadDir(DvxKindE kind);</code></pre>
|
||||
<p>The DVX folder (the working directory) holds the program, which Setup installs, an upgrade replaces, and DVX only reads; everything written while DVX runs goes in the user folder, so it is the one folder to back up.</p>
|
||||
<pre> Folder Holds
|
||||
------ -----
|
||||
SYSTEM DVX's own tools and files, and the shipped DVX.INI and ASSOC.INI
|
||||
LIBS, WIDGETS The libraries and widgets
|
||||
APPS\<vendor>\<program> A program, its help, and DATA (the files it ships; read-only)
|
||||
SHARED FONTS, SOUNDS, THEMES, and WPAPER: what DVX ships for every program
|
||||
USER (the user folder) DVX.INI, ASSOC.INI (the user's changes to SYSTEM's), DVX.LOG, the user's own FONTS, SOUNDS, THEMES, and WPAPER, APPS\<program path> (each program's settings and saved files), and DOWNLOAD</pre>
|
||||
<p>Only DVX's own package puts files in SHARED. A program reads it but never writes or installs into it; a program's own files live with it (its DATA folder, or resources inside the .app), under its own vendor and program folder, so two programs' files never collide.</p>
|
||||
<p>platformUserInit, called once by the loader, decides the user folder: the folder the DVXUSER environment variable names, else USER in the DVX folder, made when missing. When it cannot be written (DVX on a CD-ROM without DVXUSER), DVXUSER in the TEMP folder is used instead and platformUserInTemp is true; without a TEMP folder nothing can be saved and platformIsReadOnly is true. The first start copies SYSTEM\DVX.INI to the user folder. platformUserDir is the folder (DVXUSER or USER before platformUserInit has decided); platformUserPath joins a name to it; platformDvxIniPath is its DVX.INI. platformUserDirOf is the user folder of a DVX installed in another folder (DVXUSER, else that folder's USER), for Setup.</p>
|
||||
<p>platformAppUserDir is a program's folder in the user folder: APPS\KPUNCH\TERMINAL\TERMINAL.APP gives USER\APPS\KPUNCH\TERMINAL; a program outside the DVX folder gives USER\APPS\<its file name without the extension>. It is not made here (the shell's userDir and BASIC's App.User are made on first use).</p>
|
||||
<p>SHARED and the user folder have the same four kinds of folder (DvxKindE). platformKindName is a kind's folder name; platformKindDir is SHARED's (user false) or the user's (user true); platformKindBrowseDir is where a file dialog for the kind starts (the user's when it exists, else SHARED's). platformKindFind finds a file of a kind by name, the user's first, so a user's file of the same name wins; a name with a folder in it is taken as the path it is. platformKindNameOf is the other way: a file directly in one of the kind's folders gives its name alone, which is what settings store (DVX.INI [sounds] and [desktop] wallpaper), so moving the user folder or DVX keeps them working. platformKindReadDir lists both folders: the user's names (sorted, case ignored), then SHARED's not among them; free it with dvxReadDirFree. BASIC: FileOpsUserDir$, FileOpsUserPath$, FileOpsUserInTemp, FileOpsKindDir$, FileOpsKindBrowseDir$, FileOpsKindFind$, FileOpsKindName$, and FileOpsKindList (fileops.bas), and App.User for a program's own folder.</p>
|
||||
<h3>Read-only media: platformSetReadOnly / platformIsReadOnly / platformDirIsWritable / platformTempDir / platformSetLogMemory / platformLogMemory</h3>
|
||||
<pre><code>#define PLATFORM_LOG_MEM_MAX 16384
|
||||
|
||||
|
|
@ -6779,7 +6872,7 @@ bool platformDirIsWritable(const char *dir);
|
|||
const char *platformTempDir(void);
|
||||
void platformSetLogMemory(void);
|
||||
const char *platformLogMemory(void);</code></pre>
|
||||
<p>The loader tests at startup whether the DVX folder can be written to (platformDirIsWritable creates and removes a scratch file in dir) and records the answer with platformSetReadOnly. When DVX runs from a CD-ROM or a write-protected disk (platformIsReadOnly is true): the log goes to DVX.LOG in the TEMP folder (platformTempDir: the TEMP or else TMP variable in native separators, NULL when neither is set) or, without one, to memory; the boot-time help rebuild is skipped (its output and counts could not be saved); prefsSave and prefsSaveAs keep a file they cannot write in memory, and prefsLoad reads it back until DVX exits; the shell says so once at startup; and a program that allows several copies runs only once when there is no TEMP folder (a second copy is a temporary copy of the module). platformSetLogMemory sends dvxLog to a buffer holding the latest PLATFORM_LOG_MEM_MAX bytes (the oldest whole lines are dropped), which platformLogMemory returns; platformSetLogPath goes back to a file. SETUP.EXE logs to memory unless /LOG= is given, so it never writes to its own disk.</p>
|
||||
<p>platformUserInit records with platformSetReadOnly whether anything can be saved: platformIsReadOnly is true only when the user folder cannot be written and there is no TEMP folder to put it in (platformTempDir: the TEMP or else TMP variable in native separators, NULL when neither is set; platformDirIsWritable creates and removes a scratch file in dir). Then: the log is kept in memory; prefsSave and prefsSaveAs keep a file they cannot write in memory, and prefsLoad reads it back until DVX exits; the shell says so once at startup (and, when the user folder went in TEMP, says that instead); and a program that allows several copies runs only once when there is no TEMP folder (a second copy is a temporary copy of the module). The boot-time help rebuild is skipped when nothing can be saved or the APPS folder cannot be written (its output and counts could not be kept). platformSetLogMemory sends dvxLog to a buffer holding the latest PLATFORM_LOG_MEM_MAX bytes (the oldest whole lines are dropped), which platformLogMemory returns; platformSetLogPath goes back to a file. SETUP.EXE logs to memory unless /LOG= is given, so it never writes to its own disk.</p>
|
||||
<h3>platformLineEnding / platformStripLineEndings</h3>
|
||||
<pre><code>const char *platformLineEnding(void);
|
||||
int32_t platformStripLineEndings(char *buf, int32_t len);</code></pre>
|
||||
|
|
@ -6941,7 +7034,7 @@ audioFmFree(audio, v); // key off, back to everyone</co
|
|||
midiBank the OP2 instrument bank for FM MIDI (music.lib), a path from the DVX folder</pre>
|
||||
<p>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.</p>
|
||||
<h2>Desktop Sounds</h2>
|
||||
<p>The library plays the sounds DVX makes for its events (dvxPlaySound in dvxApp.h): when it loads it sets DVX's sound hook to audioDeskPlay(event, wait). The file of each event is read from DVX.INI [sounds] at the first sound: the key is audioDeskKey(event) and the value a WAV or VOC file (a path from the DVX folder), empty or missing for none; audioDeskReload makes the next sound read the section again (the Control Panel calls it after saving). The sounds play on a context of the library's own, so no program's end cuts one short, at full volume under the master volume. When no sound device starts, the sounds stay silent without trying again until the next reload. With wait the call returns once the sound has played (at most 5 seconds); after the exit sound further events are quiet (DVX is ending). audioDeskLabel(event) names an event for people, audioDeskPlayFile(path) plays any file the same way (to try one out). audioDeskFile(prefs, event) reads an event's file from a copy of DVX.INI ("" for none) and audioDeskSetFile(prefs, event, path) sets it; the library's own reading of [sounds] goes through audioDeskFile too. The default set, made by DVX's own sound generator at build time, is in AUDIO_SOUNDS_DIR (config/sounds).</p>
|
||||
<p>The library plays the sounds DVX makes for its events (dvxPlaySound in dvxApp.h): when it loads it sets DVX's sound hook to audioDeskPlay(event, wait). The file of each event is read from DVX.INI [sounds] at the first sound: the key is audioDeskKey(event) and the value a WAV or VOC file, empty or missing for none: a name alone is found in the sound folders (the user folder's SOUNDS first, then SHARED\SOUNDS: platformKindFind), anything else is a path; audioDeskReload makes the next sound read the section again (the Control Panel calls it after saving). The sounds play on a context of the library's own, so no program's end cuts one short, at full volume under the master volume. When no sound device starts, the sounds stay silent without trying again until the next reload. With wait the call returns once the sound has played (at most 5 seconds); after the exit sound further events are quiet (DVX is ending). audioDeskLabel(event) names an event for people, audioDeskPlayFile(path) plays any file the same way (to try one out). audioDeskFile(prefs, event) reads an event's file from a copy of DVX.INI ("" for none) and audioDeskSetFile(prefs, event, path) sets it; the library's own reading of [sounds] goes through audioDeskFile too. The default set, made by DVX's own sound generator at build time, is in SHARED\SOUNDS.</p>
|
||||
<pre> Key Event
|
||||
--- -----
|
||||
default the default beep (dvxBeep; a message box without an icon)
|
||||
|
|
@ -6968,7 +7061,7 @@ audioFmFree(audio, v); // key off, back to everyone</co
|
|||
<p>A synthesizer driver exports an AudioSynthDriverT named audioSynth instead, written with AUDIO_SYNTH_DRIVER(name, priority, .kind = AUDIO_SYNTH_FM or AUDIO_SYNTH_MIDI, .open = ..., .close = ..., .write = ... or .put = ...). open detects the hardware, fills AudioSynthDrvInfoT (description; for FM the voices and whether it is stereo) and leaves it silent; close silences it. write(reg, value) sets an FM register (bank x AUDIO_FM_BANK_REGS + index) and must keep the index/data pair together (audioIrqOff / audioIrqRestore); put(byte) sends a MIDI byte, returning false when the port is not ready. Both run at interrupt time too (timer callbacks), so they follow the rules above and are pinned by the driver.</p>
|
||||
<h2>BASIC</h2>
|
||||
<p>src/include/basic/sound.bas (DECLARE LIBRARY "audio"): PlayWave(fileName), PlayWaveLoop(fileName), PlayTone(frequency, milliseconds, volume), PlayNoise(frequency, milliseconds, volume) return a voice number (0 on failure: SoundError$ says why); StopWave(voice), StopAllSounds, SoundPlaying(voice), SoundVolume(percent), SoundInfo$ (the device, e.g. "Sound Blaster 16 (DSP 4.05) at 220h, 22050 Hz, 16-bit stereo"). Volumes are percent. Each BASIC program plays through its own context, closed when it ends.</p>
|
||||
<p>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.</p>
|
||||
<p>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 name alone found in the sound folders as the events' files are: a voice number, or 0 with SoundError$); SoundFolder$ (SHARED\SOUNDS, the sounds DVX ships; FileOpsKindDir$ and FileOpsKindList give the user's too); 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.</p>
|
||||
<p>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_*).</p>
|
||||
<p>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).</p>
|
||||
</div>
|
||||
|
|
@ -7021,7 +7114,7 @@ audioFmFree(audio, v); // key off, back to everyone</co
|
|||
<p>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.</p>
|
||||
<p>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).</p>
|
||||
<h2>Running a Program (basRunHost.h, in basrt.lib)</h2>
|
||||
<p>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.</p>
|
||||
<p>basRunHostCreate makes the VM and form runtime for a module from a BasRunSpecT: App.Path, App.User (from the program's file: its folder in the user folder), App.Data (the DATA folder beside it), 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.</p>
|
||||
<p>VM accessors a debugger host uses (vm.h): basVmSetAppPaths, basVmSetBreakpointCallback, basVmSetDebugPaused, basVmResume, basVmStop, basVmClearStepState, basVmGetCurrentLine, basVmGetErrorLine.</p>
|
||||
<h2>From BASIC</h2>
|
||||
<p>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.</p>
|
||||
|
|
@ -7373,8 +7466,7 @@ void basVmResume(BasVmT *vm);
|
|||
void basVmStop(BasVmT *vm);
|
||||
int32_t basVmGetCurrentLine(const BasVmT *vm);
|
||||
int32_t basVmGetErrorLine(const BasVmT *vm);
|
||||
void basVmSetAppPaths(BasVmT *vm, const char *path,
|
||||
const char *config, const char *data);</code></pre>
|
||||
void basVmSetAppPaths(BasVmT *vm, const char *path, const char *user, const char *data);</code></pre>
|
||||
<pre> Function Description
|
||||
-------- -----------
|
||||
basVmSetBreakpoints Set the breakpoint list (sorted array of source line numbers, host-owned memory).
|
||||
|
|
@ -7389,7 +7481,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.</pre>
|
||||
basVmSetAppPaths Set App.Path, App.User (the program's user folder, made on first use), and App.Data.</pre>
|
||||
<p>The breakpoint callback notifies the host when a breakpoint fires during nested sub calls:</p>
|
||||
<pre><code>typedef void (*BasBreakpointFnT)(void *ctx, int32_t line);</code></pre>
|
||||
<h2>BasModuleT</h2>
|
||||
|
|
@ -7839,7 +7931,7 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</code></pre>
|
|||
<p><a href="#shell.lifecycle">Lifecycle API</a></p>
|
||||
<p><a href="#shell.query">Query API</a></p>
|
||||
<p><a href="#shell.startup">Starting Up</a></p>
|
||||
<p><a href="#shell.config">Configuration API</a></p>
|
||||
<p><a href="#shell.config">Program Folders API</a></p>
|
||||
<p><a href="#shell.desktop">Desktop Callbacks</a></p>
|
||||
<p><a href="#shell.info">System Information</a></p>
|
||||
<p><a href="#shell.programs">Installed Programs</a></p>
|
||||
|
|
@ -7890,7 +7982,7 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</code></pre>
|
|||
appId int32_t This app's unique ID (slot index, 1-based).
|
||||
appPath[DVX_MAX_PATH] char[] Full path to the .app DXE file.
|
||||
appDir[DVX_MAX_PATH] char[] Directory containing the .app file (for loading resources).
|
||||
configDir[DVX_MAX_PATH] char[] Writable config directory (e.g. CONFIG/APPS/KPUNCH/DVXBASIC/).
|
||||
userDir[DVX_MAX_PATH] char[] The program's folder in the user folder, for its settings and the files it saves (e.g. USER\APPS\KPUNCH\DVXBASIC). Made on first use (shellEnsureUserDir).
|
||||
args[1024] char[] Launch arguments (empty string if none): blank-separated, one with blanks in double quotes. Walk them with dvxNextArg. A document opened through its file type arrives here (see File Associations); BASIC programs read these arguments with COMMAND$.
|
||||
helpFile[DVX_MAX_PATH] char[] Help file path (for F1 context help). Before appMain the shell sets it to <app>.hlp in the .app's directory when that file exists (else empty); appMain may change it.
|
||||
helpTopic[128] char[] Current help topic ID (updated by the app at runtime). Size is SHELL_HELP_TOPIC_MAX.
|
||||
|
|
@ -7898,7 +7990,7 @@ ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.</code></pre>
|
|||
helpQueryCtx void * Opaque context pointer passed to onHelpQuery.
|
||||
onNewArgs void (*)(void *ctx, const char *args) Optional. Set by an app that can take new launch arguments while running. Loading a single-instance app that is already running and sets this hands it the new arguments and raises it (the load returns its app id) instead of saying it is already running; F1 hands its "helpfile [topic]" arguments to a running help viewer that sets this, and raises it, instead of loading another copy.
|
||||
newArgsCtx void * Opaque context pointer passed to onNewArgs.</pre>
|
||||
<p>The appDir field is derived from the .app file path at load time so apps can find their own resources via relative paths. This is necessary because the working directory is shared by all apps in DOS. The config directory is mirrored from the app path: an app at APPS/KPUNCH/DVXBASIC/dvxbasic.app gets CONFIG/APPS/KPUNCH/DVXBASIC/. A full path inside the DVX directory (for example one picked with the Task Manager's Run... dialog) is mirrored the same way. An app outside the DVX directory, or loaded by a bare file name, gets CONFIG/APPS/<file name without extension>/.</p>
|
||||
<p>The appDir field is derived from the .app file path at load time so apps can find their own resources via relative paths. This is necessary because the working directory is shared by all apps in DOS. The user directory is mirrored from the app path in the user folder (platformAppUserDir): an app at APPS\KPUNCH\DVXBASIC\dvxbasic.app gets USER\APPS\KPUNCH\DVXBASIC. A full path inside the DVX directory (for example one picked with the Task Manager's Run... dialog) is mirrored the same way. An app outside the DVX directory, or loaded by a bare file name, gets USER\APPS\<file name without extension>. The user folder is USER in the DVX directory unless the DVXUSER environment variable names another (see DVX Folders in the architecture guide).</p>
|
||||
<h2>AppStateE</h2>
|
||||
<pre> Value Description
|
||||
----- -----------
|
||||
|
|
@ -8009,28 +8101,32 @@ bool shellCloseApp(AppContextT *ctx, int32_t appId);</code></pre>
|
|||
<h2>Start-up Programs</h2>
|
||||
<p>After the desktop app has loaded:</p>
|
||||
<ul>
|
||||
<li>When the DVX folder cannot be written to (platformIsReadOnly: the loader found it read-only, as on a CD-ROM), a message says once that changed settings last until DVX exits and that the help is used as it is.</li>
|
||||
<li>When DVX's disk cannot be written to (as on a CD-ROM), a message says so once: either that the user folder is in the TEMP folder (platformUserInTemp; SHELL_USER_TEMP_MSG), or, without a TEMP folder, that nothing can be saved and changed settings last until DVX exits (platformIsReadOnly; SHELL_READ_ONLY_MSG). Both suggest setting DVXUSER to a folder on a hard disk.</li>
|
||||
<li>The program named by DVX.INI [shell] welcome (SHELL_INI_WELCOME), if the key is there. The shipped DVX.INI names the Welcome window, which removes the key when the user ticks "Don't show this again", so the key itself is the "first start" flag.</li>
|
||||
<li>The program given with /RUN=path on the DVX command line (for example DVX /RUN=APPS\KPUNCH\NOTEPAD\NOTEPAD.APP).</li>
|
||||
</ul>
|
||||
<p>Both are started with shellRunCommand, so a missing program is reported to the user.</p>
|
||||
</div>
|
||||
<div class="topic" id="shell.config">
|
||||
<h1>Configuration API</h1>
|
||||
<p>Each app has a per-app configuration directory derived from its DXE path, mirrored under CONFIG/. For example, an app at APPS/KPUNCH/DVXBASIC/dvxbasic.app gets the config directory CONFIG/APPS/KPUNCH/DVXBASIC/.</p>
|
||||
<h2>shellEnsureConfigDir</h2>
|
||||
<pre><code>int32_t shellEnsureConfigDir(const DxeAppContextT *ctx);</code></pre>
|
||||
<p>Ensure the app's config directory exists, creating all parent directories as needed. Returns 0 on success, -1 on failure.</p>
|
||||
<p>Call this before writing any config files. The directory path comes from ctx->configDir.</p>
|
||||
<h2>shellConfigPath</h2>
|
||||
<pre><code>void shellConfigPath(const DxeAppContextT *ctx, const char *filename, char *outPath, int32_t outSize);</code></pre>
|
||||
<p>Build a full path to a file in the app's config directory by joining ctx->configDir with the given filename.</p>
|
||||
<h1>Program Folders API</h1>
|
||||
<p>Each program has two folders of its own. Its user folder (ctx->userDir) is in DVX's user folder, mirrored from its path: an app at APPS\KPUNCH\DVXBASIC\dvxbasic.app gets USER\APPS\KPUNCH\DVXBASIC. Settings and every file the program saves go there, apart from the program files, so an upgrade or a reinstall never overwrites them and backing up the user folder keeps them. Its DATA folder, beside its .app, holds the files it ships; DVX never writes there, and on a CD-ROM it cannot be written at all.</p>
|
||||
<p>A program never writes or installs into SHARED (DVX's own fonts, sounds, themes, and wallpapers): its own files live in its DATA folder or as resources in its .app.</p>
|
||||
<h2>shellEnsureUserDir</h2>
|
||||
<pre><code>int32_t shellEnsureUserDir(const DxeAppContextT *ctx);</code></pre>
|
||||
<p>Make the program's user folder, with all parent folders as needed. Returns 0 on success, -1 on failure. Call it before writing the first file there.</p>
|
||||
<h2>shellUserPath</h2>
|
||||
<pre><code>void shellUserPath(const DxeAppContextT *ctx, const char *filename, char *outPath, int32_t outSize);</code></pre>
|
||||
<p>Build the full path of a file in the program's user folder by joining ctx->userDir with filename.</p>
|
||||
<pre><code>// Example:
|
||||
char path[DVX_MAX_PATH];
|
||||
shellConfigPath(ctx, "settings.ini", path, sizeof(path));
|
||||
// -> "CONFIG/APPS/KPUNCH/PROGMAN/settings.ini"</code></pre>
|
||||
shellEnsureUserDir(ctx);
|
||||
shellUserPath(ctx, "PROGMAN.INI", path, sizeof(path));
|
||||
// -> "USER\APPS\KPUNCH\PROGMAN\PROGMAN.INI"</code></pre>
|
||||
<h2>shellDataPath</h2>
|
||||
<pre><code>void shellDataPath(const DxeAppContextT *ctx, const char *filename, char *outPath, int32_t outSize);</code></pre>
|
||||
<p>Build the full path of a file the program ships in the DATA folder beside its .app ("" when it does not fit), e.g. "APPS\ACME\MYGAME\DATA\LEVEL1.DAT". Read-only.</p>
|
||||
<h2>Random Seed File</h2>
|
||||
<p>When the serial library is loaded, the shell feeds keyboard and mouse event timings to its random-number generator (dvxSetInputEntropyHook) and carries generator state across runs in CONFIG/SECLINK.RND: the file is read and immediately rewritten at startup, and rewritten again at shutdown. The library is found at run time; without it no seed file is written.</p>
|
||||
<p>When the serial library is loaded, the shell feeds keyboard and mouse event timings to its random-number generator (dvxSetInputEntropyHook) and carries generator state across runs in SECLINK.RND in the user folder: the file is read and immediately rewritten at startup, and rewritten again at shutdown. The library is found at run time; without it no seed file is written.</p>
|
||||
</div>
|
||||
<div class="topic" id="shell.desktop">
|
||||
<h1>Desktop Callbacks</h1>
|
||||
|
|
@ -8145,11 +8241,11 @@ void shellMinimizeIdle(AppContextT *ctx);</code></pre>
|
|||
<div class="topic" id="shell.assoc">
|
||||
<h1>File Associations</h1>
|
||||
<p>Header: dvxshell/shellAssoc.h</p>
|
||||
<p>Which program opens each kind of document. One store serves every program: CONFIG\ASSOC.INI (SHELL_ASSOC_INI_PATH), section [associations]. Each key is an extension without the dot, upper case; each value is a program path relative to the DVX root, optionally followed by ,arguments where %1 stands for the document:</p>
|
||||
<p>Which program opens each kind of document. One store serves every program, in two layers: the shipped list, SYSTEM\ASSOC.INI (SHELL_ASSOC_SHIPPED_PATH), and the user's changes, ASSOC.INI in the user folder, both with the section [associations]. A type in the user's file replaces the shipped one; an empty value there removes a shipped type. Each key is an extension without the dot, upper case; each value is a program path relative to the DVX root, optionally followed by ,arguments where %1 stands for the document:</p>
|
||||
<pre><code>[associations]
|
||||
TXT = apps/kpunch/notepad/notepad.app
|
||||
FRM = apps/kpunch/dvxbasic/dvxbasic.app,%1</code></pre>
|
||||
<p>DVX ships a default ASSOC.INI; users change it on the Control Panel's File Types page, and a setup program registers its own types with shellAssocSet. The store is read once through dvxPrefs and kept; every change is saved at once with the crash-safe rewrite, and a failed save drops the cached copy so the next call rereads the file. Extension arguments may have a leading dot and any case.</p>
|
||||
<p>Users change the types on the Control Panel's File Types page, and a setup program registers its own types with shellAssocSet. Every change goes to the user's file, so an upgrade's new shipped types appear while the user's choices stay. Both files are read once through dvxPrefs and kept; every change is saved at once with the crash-safe rewrite, and a failed save drops the cached copies so the next call rereads the files. Extension arguments may have a leading dot and any case.</p>
|
||||
<h2>Looking Up</h2>
|
||||
<pre><code>bool shellAssocLookup(const char *extension, char *programPath, int32_t programPathSize);
|
||||
int32_t shellAssocCount(void);
|
||||
|
|
@ -8231,18 +8327,17 @@ const char *HotkeyText(const char *keyText);</code></pre>
|
|||
<p>HotkeyAdd parses keyText with dvxAccelParse and registers it with dvxRegisterHotkey for the running program; it returns an id of its own (> 0), or 0 when keyText is not a key or dvxRegisterHotkey refuses it (it needs Ctrl or Alt unless it is a function or navigation key, and a key already registered is refused). A BASIC program cannot be called back, so the hotkey's callback queues the id for its program (up to 16 presses not yet collected); HotkeyNext returns and removes the oldest one, 0 when there is none. HotkeyRemove drops one of the running program's hotkeys (other ids are ignored). HotkeyText is dvxAccelParse followed by dvxAccelFormat ("" when keyText is not a key): the spelling menus show. dvxUnregisterAppHotkeys removes a program's hotkeys when it ends; the app-cleanup hook drops their records and queued presses.</p>
|
||||
<h2>This program and DVX settings</h2>
|
||||
<pre><code>const char *ShellAppPath(void);
|
||||
const char *ShellConfigFile(const char *fileName);
|
||||
int32_t ShellInstanceCount(void);
|
||||
void ShellLog(const char *text);
|
||||
int32_t ShellSetDesktop(const char *program);
|
||||
int32_t ShellSetWelcome(const char *program);</code></pre>
|
||||
<p>ShellAppPath is the running program's .app path as started ("" when unknown). ShellConfigFile is shellConfigPath of fileName in the running program's config folder, which shellEnsureConfigDir makes first ("" when it cannot); every copy of a program gets the same folder, the one a C app's settings use. ShellInstanceCount counts the running apps with the same path, this one included (a second copy of a multiInstance program loads from a temporary copy but keeps the original path). ShellLog writes one line with dvxLog. ShellSetDesktop and ShellSetWelcome set, or with "" remove, DVX.INI [shell] desktop (the desktop app; SHELL_DESKTOP_APP when missing) and [shell] welcome (the program started after the desktop); both share one writer and return False (logged) when DVX.INI cannot be saved.</p>
|
||||
<p>ShellAppPath is the running program's .app path as started ("" when unknown). A BASIC program's user folder is App.User (the folder a C program's userDir names; every copy of a program gets the same one). ShellInstanceCount counts the running apps with the same path, this one included (a second copy of a multiInstance program loads from a temporary copy but keeps the original path). ShellLog writes one line with dvxLog. ShellSetDesktop and ShellSetWelcome set, or with "" remove, DVX.INI [shell] desktop (the desktop app; SHELL_DESKTOP_APP when missing) and [shell] welcome (the program started after the desktop); both share one writer and return False (logged) when DVX.INI cannot be saved.</p>
|
||||
<h2>Programs folder, item icons, program groups</h2>
|
||||
<pre><code>const char *ShellProgramsFolder(void);
|
||||
const char *ShellItemIcon(const char *iconPath, const char *path);</code></pre>
|
||||
<p>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.</p>
|
||||
<h2>A desktop in BASIC</h2>
|
||||
<p>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.</p>
|
||||
<p>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 its App.User 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.</p>
|
||||
</div>
|
||||
<div class="topic" id="shell.groups">
|
||||
<h1>Program Groups</h1>
|
||||
|
|
@ -8651,7 +8746,7 @@ fileOpsJobFree(job);</code></pre>
|
|||
<p>fileOpsJobClearAttr(job, attr), called before the first step, clears those attribute bits on every file a copy or move writes: Setup passes FILEOPS_ATTR_READONLY, because every file on a CD-ROM is read-only and the installed copies must not be.</p>
|
||||
<p>fileOpsJobRun(job, callbacks) runs a job to the end instead: progress(job, ctx) is called after every step (return false to cancel) and ask(job, kind, message, ctx) answers questions. With no callbacks, existing files are kept (No) and failing items are skipped.</p>
|
||||
<h2>BASIC</h2>
|
||||
<p>src/include/basic/fileops.bas declares 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.</p>
|
||||
<p>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)), DVX's folders (FileOpsUserDir$ (platformUserDir), FileOpsUserPath$ (platformUserPath, "" when it does not fit), FileOpsUserInTemp (platformUserInTemp), and for the kinds of DvxKindE (CONSTs FILEOPS_KIND_*) FileOpsKindDir$ (platformKindDir), FileOpsKindBrowseDir$ (platformKindBrowseDir), FileOpsKindFind$ (platformKindFind), FileOpsKindName$ (platformKindNameOf), and FileOpsKindList (platformKindReadDir as a listing, sorted by name; "" or 0 for a kind out of range)), 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.</p>
|
||||
<p>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.</p>
|
||||
</div>
|
||||
<div class="topic" id="lib.game">
|
||||
|
|
@ -8902,7 +8997,7 @@ if (!dvxExclusivePresent8(ctx, fb, 320, 200, 320)) {
|
|||
// not on the whole screen: present into the window as usual
|
||||
}</code></pre>
|
||||
<ul>
|
||||
<li>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.</li>
|
||||
<li>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 with a linear frame buffer. Either way the game draws into an ordinary linear backbuffer. The display may be larger than asked for; dvxExclusiveDisplay says what it is.</li>
|
||||
<li>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.</li>
|
||||
<li>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.</li>
|
||||
<li>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.</li>
|
||||
|
|
@ -8936,14 +9031,14 @@ if (!dvxExclusivePresent8(ctx, fb, 320, 200, 320)) {
|
|||
<pre> Field What it is Use it for
|
||||
----- ---------- ----------
|
||||
appPath the program's .app file dvxResOpen: resources inside the .app
|
||||
appDir the folder the .app is in read-only game data installed beside it
|
||||
configDir the program's own settings folder saved games, high scores, options
|
||||
appDir the folder the .app is in its DATA folder: read-only game data
|
||||
userDir the program's own folder in USER saved games, high scores, options
|
||||
helpFile the program's .hlp file shellOpenHelp (F1)</pre>
|
||||
<p>configDir is CONFIG plus the folder the .app is in: a game in APPS\ACME\MYGAME keeps its files in CONFIG\APPS\ACME\MYGAME.</p>
|
||||
<p>userDir is the user folder plus the folder the .app is in: a game in APPS\ACME\MYGAME keeps its files in USER\APPS\ACME\MYGAME.</p>
|
||||
<ul>
|
||||
<li>Small data (sounds, music, a title picture) can travel inside the .app as resources: list them in NAME.res as binary (or icon, text) and read them with dvxResOpen(ctx->appPath) and dvxResRead. The template carries its sound effect and music this way.</li>
|
||||
<li>Bigger data goes in files beside the .app (the DATA variable in the Makefile packages them there): build paths from appDir, never from the current directory.</li>
|
||||
<li>Saved games go in configDir, which DVX keeps apart from the program files so a reinstall never overwrites them. Create it with shellEnsureConfigDir(ctx) before the first write and build names with shellConfigPath(ctx, "SAVE1.DAT", path, size). For settings and scores, prefsLoad and prefsSaveAs read and write INI files safely (a failed save leaves the old file intact).</li>
|
||||
<li>Bigger data goes in the DATA folder beside the .app (the DATA variable in the Makefile packages it there): build paths with shellDataPath(ctx, "LEVEL1.DAT", path, size), never from the current directory. DVX never writes there.</li>
|
||||
<li>Saved games go in userDir, which DVX keeps apart from the program files so a reinstall never overwrites them and backing up the user folder keeps them. Create it with shellEnsureUserDir(ctx) before the first write and build names with shellUserPath(ctx, "SAVE1.DAT", path, size). For settings and scores, prefsLoad and prefsSaveAs read and write INI files safely (a failed save leaves the old file intact).</li>
|
||||
<li>Use 8.3 file names (8 characters, a dot, 3 characters).</li>
|
||||
<li>platformDiskSpace tells how much room is free before a big save.</li>
|
||||
<li>dvxLog writes a line to DVX.LOG, the place to look when something goes wrong.</li>
|
||||
|
|
@ -9001,7 +9096,7 @@ End Sub</code></pre>
|
|||
Tone and noise voices audioTone, audioVoiceSetFrequency, audioVoiceSetVolume, audioNotesOpen, audioFmAlloc
|
||||
Per-frame audio work audioPump, musicPump
|
||||
Read-only data folder DxeAppContextT.appDir, dvxResOpen, dvxResRead
|
||||
Save folder DxeAppContextT.configDir, shellEnsureConfigDir, shellConfigPath
|
||||
Save folder DxeAppContextT.userDir, shellEnsureUserDir, shellUserPath
|
||||
Disk free platformDiskSpace
|
||||
Memory malloc, free
|
||||
Serial port the serial library (rs232Open)
|
||||
|
|
@ -9018,14 +9113,14 @@ End Sub</code></pre>
|
|||
<li>Joystick: axes are -32768..32767 after calibration and dead zone; divide down to the library's range, or compare with a threshold for a digital stick. Buttons are bits. Never read the game port directly; calibration lives in the Control Panel.</li>
|
||||
<li>Timer callbacks run inside the timer interrupt: no C library, no DVX calls, no floating point, code and data pinned. Never reprogram the PIT or hook the timer interrupt.</li>
|
||||
<li>Sound effects: DVX's 8-bit samples are unsigned; flip the top bit of signed 8-bit data or convert it to 16-bit. Stream fill functions run in program time, not in an interrupt.</li>
|
||||
<li>Files: the library's fixed "DATA" and save folders become run-time paths under appDir and configDir; use remove() to delete a save.</li>
|
||||
<li>Files: the library's fixed "DATA" and save folders become run-time paths (shellDataPath and shellUserPath); use remove() to delete a save.</li>
|
||||
<li>Serial: one owner per COM port, so the port uses DVX's serial library rather than programming the UART.</li>
|
||||
<li>exit() ends only the game, like End Task; atexit handlers do not run.</li>
|
||||
</ul>
|
||||
<h2>Things to Decide</h2>
|
||||
<ul>
|
||||
<li>Shared library or static: built as a DVX library (.lib, loaded when DVX starts) the port is shared by every game, but its global state (the stage, the key table) means one game at a time. Linked into each game's .app (the LIBS variable) every game has its own copy, several can run at once, and nothing is loaded while no game runs.</li>
|
||||
<li>Paths: libraries that open "DATA/..." or write saves to a fixed folder need a run-time root (appDir and configDir) instead of compile-time paths.</li>
|
||||
<li>Paths: libraries that open "DATA/..." or write saves to a fixed folder need a run-time root (the DATA folder and userDir) instead of compile-time paths.</li>
|
||||
<li>Speed: every frame costs a conversion and a composite. Present only what changed; a 1x scale on a 486 and larger scales on faster machines.</li>
|
||||
<li>Tearing: there is no vertical retrace in a window. On the whole screen there is (dvxExclusiveWaitRetrace), but games that change registers mid-frame (raster effects) still need rework.</li>
|
||||
<li>Whole screen: offer it (Alt+Enter is the custom) and handle ExclusiveSuspendedE by drawing in the window again: the player can Alt+Tab to the desktop at any time.</li>
|
||||
|
|
@ -9120,8 +9215,9 @@ End Sub</code></pre>
|
|||
<h2>How the Program Works</h2>
|
||||
<p>SHELL_APP_DESCRIPTOR(...) declares the exported appDescriptor: the program's name, whether it has its own main loop, whether several copies may run, its task stack size, and priority. It also records the binary interface version the program was built for.</p>
|
||||
<p>DVX calls appMain(DxeAppContextT *ctx) once. The template is a callback-only program (hasMainLoop = false): appMain creates a window with a File and Help menu, a label, and a button, and returns 0. From then on DVX calls the window's onClose and onMenu callbacks and the button's onClick. Destroying the program's last window ends it. A program that needs to keep working between events sets hasMainLoop = true, loops in appMain, and calls tsYield() often (see the Clock program's source for an example).</p>
|
||||
<p>ctx gives the program its paths: appPath (the .app file, used to read its own resources with dvxResOpen and dvxResRead), appDir, configDir (a writable directory for its settings), and helpFile (preset to hello.hlp beside hello.app).</p>
|
||||
<p>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.</p>
|
||||
<p>ctx gives the program its paths: appPath (the .app file, used to read its own resources with dvxResOpen and dvxResRead), appDir, userDir (its folder in the user folder, for its settings and the files it saves: shellEnsureUserDir makes it, shellUserPath names a file there), and helpFile (preset to hello.hlp beside hello.app). The files it ships are in the DATA folder beside it (shellDataPath), which DVX never writes.</p>
|
||||
<p>A program writes only in its user folder, and never writes or installs anything into DVX's SHARED folder (DVX's own fonts, sounds, themes, and wallpapers): its own files stay with it, in its DATA folder or as resources in the .app, under its vendor and program folder, so two programs' files never collide.</p>
|
||||
<p>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 SHARED\FONTS folder. A .DFN carries the notice and licence of the font it was made from.</p>
|
||||
<h2>Run</h2>
|
||||
<pre><code> make install DVX_DIR=/path/to/dvx</code></pre>
|
||||
<p>This copies hello.app and hello.hlp to APPS/LOCAL/HELLO/ in the DVX directory. Start DVX and run the program from Program Manager (File > Run), or add an icon for it. After a change, rebuild and install again; close the running copy first.</p>
|
||||
|
|
@ -9281,7 +9377,7 @@ End Sub</code></pre>
|
|||
<div class="topic" id="sdk.debug">
|
||||
<h1>Debugging: the Log and Crash Reports</h1>
|
||||
<h2>The Log</h2>
|
||||
<p>dvxLog(fmt, ...) (dvxPlat.h) appends one printf-style line to DVX.LOG in the DVX directory. The file is opened and closed for every line, so everything written before a crash is kept. DVX logs there too: each module it loads, why a module was refused, each program started (Shell: loaded 'Name' ... entry=0x...), and each help rebuild.</p>
|
||||
<p>dvxLog(fmt, ...) (dvxPlat.h) appends one printf-style line to DVX.LOG in the user folder (USER in the DVX directory, or the folder DVXUSER names). The file is opened and closed for every line, so everything written before a crash is kept. DVX logs there too: each module it loads, why a module was refused, each program started (Shell: loaded 'Name' ... entry=0x...), and each help rebuild.</p>
|
||||
<h2>Crash Reports</h2>
|
||||
<p>When a program faults, DVX ends that program with the message "'Name' has caused a fault and will be terminated." and keeps running. DVX.LOG then holds the report:</p>
|
||||
<pre><code> === CRASH ===
|
||||
|
|
@ -9322,13 +9418,13 @@ End Sub</code></pre>
|
|||
<p>make package lays out pkg/ exactly as the files go into the DVX directory; make install DVX_DIR=... copies it there. VENDOR (default local) names the vendor directory; use a short name of your own (8 characters at most).</p>
|
||||
<pre> Kind Directory Files
|
||||
---- --------- -----
|
||||
Program APPS/<VENDOR>/<NAME>/ NAME.app, NAME.hlp, the DATA files (if any)
|
||||
Program APPS/<VENDOR>/<NAME>/ NAME.app, NAME.hlp, DATA/ with the DATA files (if any)
|
||||
Widget WIDGETS/<VENDOR>/<NAME>/ NAME.wgt, NAME.DHS, NAME.BHS, NAME.dep (if any)
|
||||
Library LIBS/<VENDOR>/<NAME>/ NAME.lib, NAME.DHS, NAME.dep (if any)</pre>
|
||||
<ul>
|
||||
<li>Programs carry their compiled help. Widgets and libraries carry help sources, which DVX compiles into its system help when the set of installed modules changes.</li>
|
||||
<li>.dep files are written with DOS line endings.</li>
|
||||
<li>A program's data files (levels, graphics, sounds it loads at run time) go in the Makefile's DATA variable, for example DATA = levels sounds; the package puts them beside NAME.app, where the program finds them through appDir in its DxeAppContextT. Small ones can instead travel inside the .app as binary resources (see the .res file).</li>
|
||||
<li>A program's data files (levels, graphics, sounds it loads at run time) go in the Makefile's DATA variable, for example DATA = levels sounds; the package puts them in the DATA folder beside NAME.app, where the program finds them through shellDataPath (BASIC: App.Data). Small ones can instead travel inside the .app as binary resources (see the .res file).</li>
|
||||
<li>To ship a module, zip the contents of pkg/; the user unpacks it into the DVX directory. A library's header and .bas file go to the developers who use it, not into the DVX directory.</li>
|
||||
<li>Removing a module is deleting its directory.</li>
|
||||
</ul>
|
||||
|
|
@ -10255,7 +10351,7 @@ void pktSetResetCallback(PktConnT *conn, PktResetCallbackT callback);</code></pr
|
|||
<h2>RNG Functions</h2>
|
||||
<p>A SHA-256 hash DRBG over a 32-byte entropy pool. secRngAddEntropy hashes new bytes into the pool and never discards what is already there, so it is safe to call at any time, before or after the first output. Each output block is a hash of the pool and a counter; after every request the pool is re-hashed so earlier output cannot be recomputed from a later state.</p>
|
||||
<p>The first secRngBytes call gathers hardware entropy automatically. On DOS that is 256 PIT samples, each taken after a busy wait sized by the previous sample, plus the full time stamp counter on CPUs that have one (Pentium and later) and the BIOS tick count. On a CPU without a time stamp counter the samples depend mostly on the PIT phase and time of day, which an observer can narrow down, so applications should feed user-input timings to secRngAddEntropy and keep a seed file: call secRngLoadSeed at startup and secRngSaveSeed at shutdown. Host builds read /dev/urandom.</p>
|
||||
<p>Under the DVX shell this is automatic when the serial library is installed: the shell loads the seed file CONFIG\SECLINK.RND at startup, feeds the timing of every key press and mouse event to secRngAddEntropy, and saves a fresh seed at shutdown. Applications only need their own calls when they run without the shell.</p>
|
||||
<p>Under the DVX shell this is automatic when the serial library is installed: the shell loads the seed file SECLINK.RND in the user folder at startup, feeds the timing of every key press and mouse event to secRngAddEntropy, and saves a fresh seed at shutdown. Applications only need their own calls when they run without the shell.</p>
|
||||
<pre><code>void secRngAddEntropy(const uint8_t *data, int32_t len);
|
||||
void secRngBytes(uint8_t *buf, int32_t len);
|
||||
int32_t secRngGatherEntropy(uint8_t *buf, int32_t len);
|
||||
|
|
|
|||
BIN
screenshots/86box/apps-filemgr.png
(Stored with Git LFS)
BIN
screenshots/86box/apps-filemgr.png
(Stored with Git LFS)
Binary file not shown.
BIN
screenshots/86box/banked-bk-back.png
(Stored with Git LFS)
Normal file
BIN
screenshots/86box/banked-bk-back.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
screenshots/86box/banked-bk-desk.png
(Stored with Git LFS)
Normal file
BIN
screenshots/86box/banked-bk-desk.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
screenshots/86box/banked-bk-drag.png
(Stored with Git LFS)
Normal file
BIN
screenshots/86box/banked-bk-drag.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
screenshots/86box/banked-bk-full.png
(Stored with Git LFS)
Normal file
BIN
screenshots/86box/banked-bk-full.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
screenshots/86box/banked-bk-note.png
(Stored with Git LFS)
Normal file
BIN
screenshots/86box/banked-bk-note.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
screenshots/86box/cpanel-cp-desk.png
(Stored with Git LFS)
BIN
screenshots/86box/cpanel-cp-desk.png
(Stored with Git LFS)
Binary file not shown.
BIN
screenshots/86box/f1-h-filemg.png
(Stored with Git LFS)
BIN
screenshots/86box/f1-h-filemg.png
(Stored with Git LFS)
Binary file not shown.
BIN
screenshots/86box/filedlg-fd-back.png
(Stored with Git LFS)
BIN
screenshots/86box/filedlg-fd-back.png
(Stored with Git LFS)
Binary file not shown.
BIN
screenshots/86box/filedlg-fd-list.png
(Stored with Git LFS)
BIN
screenshots/86box/filedlg-fd-list.png
(Stored with Git LFS)
Binary file not shown.
BIN
screenshots/86box/filedlg-fd-nordy.png
(Stored with Git LFS)
BIN
screenshots/86box/filedlg-fd-nordy.png
(Stored with Git LFS)
Binary file not shown.
BIN
screenshots/86box/filedlg-fd-open.png
(Stored with Git LFS)
BIN
screenshots/86box/filedlg-fd-open.png
(Stored with Git LFS)
Binary file not shown.
BIN
screenshots/86box/install-su-unin.png
(Stored with Git LFS)
BIN
screenshots/86box/install-su-unin.png
(Stored with Git LFS)
Binary file not shown.
BIN
screenshots/86box/instbase-su-unin.png
(Stored with Git LFS)
BIN
screenshots/86box/instbase-su-unin.png
(Stored with Git LFS)
Binary file not shown.
BIN
screenshots/86box/telnet-telnet.png
(Stored with Git LFS)
BIN
screenshots/86box/telnet-telnet.png
(Stored with Git LFS)
Binary file not shown.
BIN
screenshots/86box/upgrade-su-unin.png
(Stored with Git LFS)
BIN
screenshots/86box/upgrade-su-unin.png
(Stored with Git LFS)
Binary file not shown.
BIN
screenshots/86box/upgrade-su-upgr.png
(Stored with Git LFS)
BIN
screenshots/86box/upgrade-su-upgr.png
(Stored with Git LFS)
Binary file not shown.
BIN
screenshots/dosbox/apps-filemgr.png
(Stored with Git LFS)
BIN
screenshots/dosbox/apps-filemgr.png
(Stored with Git LFS)
Binary file not shown.
BIN
screenshots/dosbox/banked-bk-back.png
(Stored with Git LFS)
Normal file
BIN
screenshots/dosbox/banked-bk-back.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
screenshots/dosbox/banked-bk-desk.png
(Stored with Git LFS)
Normal file
BIN
screenshots/dosbox/banked-bk-desk.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
screenshots/dosbox/banked-bk-drag.png
(Stored with Git LFS)
Normal file
BIN
screenshots/dosbox/banked-bk-drag.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
screenshots/dosbox/banked-bk-full.png
(Stored with Git LFS)
Normal file
BIN
screenshots/dosbox/banked-bk-full.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
screenshots/dosbox/banked-bk-note.png
(Stored with Git LFS)
Normal file
BIN
screenshots/dosbox/banked-bk-note.png
(Stored with Git LFS)
Normal file
Binary file not shown.
BIN
screenshots/dosbox/cpanel-cp-video.png
(Stored with Git LFS)
BIN
screenshots/dosbox/cpanel-cp-video.png
(Stored with Git LFS)
Binary file not shown.
BIN
screenshots/dosbox/f1-h-filemg.png
(Stored with Git LFS)
BIN
screenshots/dosbox/f1-h-filemg.png
(Stored with Git LFS)
Binary file not shown.
BIN
screenshots/dosbox/filedlg-fd-back.png
(Stored with Git LFS)
BIN
screenshots/dosbox/filedlg-fd-back.png
(Stored with Git LFS)
Binary file not shown.
BIN
screenshots/dosbox/filedlg-fd-list.png
(Stored with Git LFS)
BIN
screenshots/dosbox/filedlg-fd-list.png
(Stored with Git LFS)
Binary file not shown.
BIN
screenshots/dosbox/filedlg-fd-nordy.png
(Stored with Git LFS)
BIN
screenshots/dosbox/filedlg-fd-nordy.png
(Stored with Git LFS)
Binary file not shown.
BIN
screenshots/dosbox/filedlg-fd-open.png
(Stored with Git LFS)
BIN
screenshots/dosbox/filedlg-fd-open.png
(Stored with Git LFS)
Binary file not shown.
BIN
screenshots/dosbox/install-su-unin.png
(Stored with Git LFS)
BIN
screenshots/dosbox/install-su-unin.png
(Stored with Git LFS)
Binary file not shown.
BIN
screenshots/dosbox/instbase-su-unin.png
(Stored with Git LFS)
BIN
screenshots/dosbox/instbase-su-unin.png
(Stored with Git LFS)
Binary file not shown.
BIN
screenshots/dosbox/upgrade-su-unin.png
(Stored with Git LFS)
BIN
screenshots/dosbox/upgrade-su-unin.png
(Stored with Git LFS)
Binary file not shown.
BIN
screenshots/dosbox/upgrade-su-upgr.png
(Stored with Git LFS)
BIN
screenshots/dosbox/upgrade-su-upgr.png
(Stored with Git LFS)
Binary file not shown.
BIN
screenshots/dosbox/video-v-800.png
(Stored with Git LFS)
BIN
screenshots/dosbox/video-v-800.png
(Stored with Git LFS)
Binary file not shown.
BIN
screenshots/dosbox/video-v-pick.png
(Stored with Git LFS)
BIN
screenshots/dosbox/video-v-pick.png
(Stored with Git LFS)
Binary file not shown.
|
|
@ -1,2 +1,2 @@
|
|||
# Blinking terminal cursor
|
||||
16 184 8 16
|
||||
# Blinking terminal cursor (the row after the closed-connection line)
|
||||
16 200 8 16
|
||||
|
|
|
|||
|
|
@ -69,9 +69,11 @@ SHELL_APP_DESCRIPTOR(
|
|||
### `appMain`
|
||||
|
||||
Entry point. The shell calls it with a `DxeAppContextT` that exposes
|
||||
the shell's GUI context, the app's ID, its install directory, a
|
||||
writable config directory, launch arguments, and a help-topic query
|
||||
callback for F1.
|
||||
the shell's GUI context, the app's ID, its install directory, its
|
||||
writable folder in the user folder (`userDir`: `shellEnsureUserDir`,
|
||||
`shellUserPath`; shipped files are in `DATA/` beside the .app:
|
||||
`shellDataPath`), launch arguments, and a help-topic query callback
|
||||
for F1.
|
||||
|
||||
Returning from `appMain` signals that the app has finished; the
|
||||
shell unloads the DXE, frees per-app memory, and reclaims the app
|
||||
|
|
@ -189,7 +191,7 @@ View > Refresh, and when the window comes to the front with programs
|
|||
added or removed; items in the "Startup" group run once DVX is up.
|
||||
Groups, items (name, program, arguments, working folder, icon, shortcut
|
||||
key, run minimized, grid cell), view, options, and Run history are saved
|
||||
in `CONFIG/APPS/KPUNCH/PROGMAN/PROGMAN.INI` after every change (crash-safe
|
||||
in `USER/APPS/KPUNCH/PROGMAN/PROGMAN.INI` after every change (crash-safe
|
||||
rewrite). Running, the Run dialog, program discovery, and file
|
||||
associations are shell library services (`shellRun.h`, `shellProg.h`,
|
||||
`shellAssoc.h`) shared with other programs. Closing it exits DVX
|
||||
|
|
@ -328,8 +330,10 @@ System configuration with five tabs:
|
|||
* **File Types** -- file associations (`cpTypes.c`): add, change, and
|
||||
remove which program opens each extension
|
||||
|
||||
Changes preview live. OK writes to `CONFIG/DVX.INI` and the file types
|
||||
to `CONFIG/ASSOC.INI` (through `shellAssocSet`/`shellAssocRemove`);
|
||||
Changes preview live. OK writes to `USER/DVX.INI` and the file types
|
||||
to `USER/ASSOC.INI` (the user's changes to `SYSTEM/ASSOC.INI`, through
|
||||
`shellAssocSet`/`shellAssocRemove`); themes it saves go to
|
||||
`USER/THEMES`, and its lists show `SHARED`'s and the user's together;
|
||||
Cancel reverts to the snapshot taken when the panel opened.
|
||||
|
||||
### Image Viewer (imgview, BASIC)
|
||||
|
|
@ -536,7 +540,7 @@ Launch the full system (`bin/dvx.exe`) and the Program Manager picks
|
|||
up the new app automatically on the next boot.
|
||||
|
||||
To iterate quickly: rebuild the single app, restart DVX, and launch
|
||||
it. The log at `bin/DVX.LOG` records load errors, missing
|
||||
it. The log at `bin/USER/DVX.LOG` records load errors, missing
|
||||
dependencies, and per-app crash diagnostics.
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -654,7 +654,7 @@ SUB btnFileIO_Click
|
|||
|
||||
DIM path AS STRING
|
||||
DIM f AS INTEGER
|
||||
path = FileOpsPathJoin$(App.Data, "demo.txt")
|
||||
path = FileOpsPathJoin$(App.User, "demo.txt")
|
||||
Say "Writing: " + path
|
||||
|
||||
f = FREEFILE
|
||||
|
|
@ -688,7 +688,7 @@ SUB btnSystem_Click
|
|||
Header "System / App"
|
||||
|
||||
Say "App.Path = " + App.Path
|
||||
Say "App.Config = " + App.Config
|
||||
Say "App.User = " + App.User
|
||||
Say "App.Data = " + App.Data
|
||||
Say "CurDir = " + CurDir()
|
||||
Say "Date = " + Date$
|
||||
|
|
@ -709,7 +709,7 @@ SUB btnIni_Click
|
|||
Header "INI Read/Write"
|
||||
|
||||
DIM path AS STRING
|
||||
path = FileOpsPathJoin$(App.Data, "demo.ini")
|
||||
path = FileOpsPathJoin$(App.User, "demo.ini")
|
||||
Say "Writing: " + path
|
||||
|
||||
IniWrite path, "General", "UserName", "Scott"
|
||||
|
|
|
|||
|
|
@ -159,15 +159,17 @@ static int32_t addDriver(const char *name, const char *label) {
|
|||
}
|
||||
|
||||
|
||||
// The item of a sound file, added when it is not listed yet ("" is
|
||||
// "(None)"). Files are kept with native separators (DVX.INI may say
|
||||
// "config/sounds/x.wav") and told apart ignoring case: DOS names have
|
||||
// none.
|
||||
// The item of a sound, added when it is not listed yet ("" is "(None)").
|
||||
// A file in the sound folders (the user's or DVX's) is kept by its name
|
||||
// alone, as DVX.INI stores it; any other by its path, in native
|
||||
// separators. Told apart ignoring case: DOS names have none.
|
||||
static int32_t addFile(const char *path) {
|
||||
char native[DVX_MAX_PATH];
|
||||
|
||||
if (!platformKindNameOf(DvxKindSoundsE, path, native, sizeof(native))) {
|
||||
snprintf(native, sizeof(native), "%s", path);
|
||||
platformPathNative(native);
|
||||
}
|
||||
|
||||
for (int32_t i = 0; i < (int32_t)arrlen(sFileValues); i++) {
|
||||
if (strcasecmp(sFileValues[i], native) == 0) {
|
||||
|
|
@ -181,14 +183,7 @@ static int32_t addFile(const char *path) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
size_t dirLen = strlen(AUDIO_SOUNDS_DIR);
|
||||
const char *label = value;
|
||||
|
||||
if (!value[0]) {
|
||||
label = "(None)";
|
||||
} else if (strncasecmp(value, AUDIO_SOUNDS_DIR, dirLen) == 0 && value[dirLen] == DVX_PATH_SEP[0]) {
|
||||
label = value + dirLen + 1;
|
||||
}
|
||||
const char *label = value[0] ? value : "(None)";
|
||||
|
||||
arrput(sFileValues, value);
|
||||
arrput(sFileLabels, label);
|
||||
|
|
@ -207,23 +202,16 @@ static WidgetT *addRow(WidgetT *parent, const char *label) {
|
|||
}
|
||||
|
||||
|
||||
// The sound files of the default set, by name.
|
||||
// The sound files of the sound folders (the user's and DVX's), by name.
|
||||
static void addSoundsDir(void) {
|
||||
char **names = dvxReadDir(AUDIO_SOUNDS_DIR);
|
||||
|
||||
if (!names) {
|
||||
return;
|
||||
}
|
||||
char **names = platformKindReadDir(DvxKindSoundsE);
|
||||
|
||||
qsort(names, arrlen(names), sizeof(names[0]), compareNames);
|
||||
|
||||
for (int32_t i = 0; i < (int32_t)arrlen(names); i++) {
|
||||
for (int32_t e = 0; e < DVX_ARRAY_LEN(sSoundExts); e++) {
|
||||
if (dvxHasExt(names[i], sSoundExts[e])) {
|
||||
char path[DVX_MAX_PATH];
|
||||
|
||||
snprintf(path, sizeof(path), "%s" DVX_PATH_SEP "%s", AUDIO_SOUNDS_DIR, names[i]);
|
||||
addFile(path);
|
||||
addFile(names[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -376,8 +364,11 @@ static void onBrowse(WidgetT *w) {
|
|||
{ "All Files (*.*)" }
|
||||
};
|
||||
char path[DVX_MAX_PATH];
|
||||
char dir[DVX_MAX_PATH];
|
||||
|
||||
if (!dvxFileDialog(sAc, "Choose a Sound", FD_OPEN, AUDIO_SOUNDS_DIR, filters, DVX_ARRAY_LEN(filters), path, sizeof(path))) {
|
||||
platformKindBrowseDir(dir, sizeof(dir), DvxKindSoundsE);
|
||||
|
||||
if (!dvxFileDialog(sAc, "Choose a Sound", FD_OPEN, dir, filters, DVX_ARRAY_LEN(filters), path, sizeof(path))) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -409,17 +400,19 @@ static void onFile(WidgetT *w) {
|
|||
}
|
||||
|
||||
|
||||
// Play the sound chosen in the dropdown.
|
||||
// Play the sound chosen in the dropdown (a name alone is found in the
|
||||
// sound folders).
|
||||
static void onTest(WidgetT *w) {
|
||||
(void)w;
|
||||
|
||||
int32_t item = wgtDropdownGetSelected(sFileDrop);
|
||||
char path[DVX_MAX_PATH];
|
||||
|
||||
if (item <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t rc = sSnd.playFile(sFileValues[item]);
|
||||
int32_t rc = sSnd.playFile(platformKindFind(path, sizeof(path), DvxKindSoundsE, sFileValues[item]) ? path : sFileValues[item]);
|
||||
|
||||
if (rc < 0) {
|
||||
dvxMessageBox(sAc, "Sounds", sSnd.errorText(rc), MB_OK | MB_ICONWARNING);
|
||||
|
|
|
|||
|
|
@ -105,8 +105,6 @@
|
|||
#define CP_SPEED_SLIDER_MIN 2
|
||||
#define CP_SPEED_SLIDER_MAX 32
|
||||
#define CP_SPEED_INVERT (CP_SPEED_SLIDER_MIN + CP_SPEED_SLIDER_MAX)
|
||||
#define WPAPER_DIR "CONFIG" DVX_PATH_SEP "WPAPER"
|
||||
#define THEME_DIR "CONFIG" DVX_PATH_SEP "THEMES"
|
||||
#define THEME_EXT ".THM"
|
||||
|
||||
// Menu command IDs
|
||||
|
|
@ -126,7 +124,7 @@ typedef struct {
|
|||
// Tables
|
||||
// ============================================================
|
||||
|
||||
// Image types listed from WPAPER_DIR and offered by Browse.
|
||||
// Image types listed from the wallpaper folders and offered by Browse.
|
||||
static const char *sWpaperExts[] = { ".bmp", ".jpg", ".png", ".gif" };
|
||||
|
||||
static const char *sThemeExts[] = { THEME_EXT };
|
||||
|
|
@ -198,6 +196,7 @@ static void buildColorsTab(WidgetT *page);
|
|||
static void buildDesktopTab(WidgetT *page);
|
||||
static void buildMouseTab(WidgetT *page);
|
||||
static void buildVideoTab(WidgetT *page);
|
||||
static int entryNameCompare(const void *a, const void *b);
|
||||
static void onAccelChange(WidgetT *w);
|
||||
static void onApplyTheme(WidgetT *w);
|
||||
static void onApplyWallpaper(WidgetT *w);
|
||||
|
|
@ -225,7 +224,7 @@ static void onWheelChange(WidgetT *w);
|
|||
static void onWheelStepSlider(WidgetT *w);
|
||||
static void restoreSnapshot(void);
|
||||
static void saveSnapshot(void);
|
||||
static void scanDirEntries(const char *dir, const char *const *exts, int32_t extCount, bool stripExt, FileEntryT **entries, const char ***labels);
|
||||
static void scanKindEntries(DvxKindE kind, const char *const *exts, int32_t extCount, bool stripExt, FileEntryT **entries, const char ***labels);
|
||||
static void scanThemes(void);
|
||||
static void scanWallpapers(void);
|
||||
static void setIntLabel(WidgetT *lbl, const char *prefix, int32_t val, const char *suffix);
|
||||
|
|
@ -590,6 +589,12 @@ static void buildVideoTab(WidgetT *page) {
|
|||
}
|
||||
|
||||
|
||||
// qsort: entries by display name, case ignored (scanKindEntries)
|
||||
static int entryNameCompare(const void *a, const void *b) {
|
||||
return strcasecmp(((const FileEntryT *)a)->name, ((const FileEntryT *)b)->name);
|
||||
}
|
||||
|
||||
|
||||
static void onAccelChange(WidgetT *w) {
|
||||
(void)w;
|
||||
applyMouseConfig();
|
||||
|
|
@ -627,8 +632,11 @@ static void onBrowseTheme(WidgetT *w) {
|
|||
(void)w;
|
||||
|
||||
char path[DVX_MAX_PATH];
|
||||
char dir[DVX_MAX_PATH];
|
||||
|
||||
if (dvxFileDialog(sAc, "Load Theme", FD_OPEN, THEME_DIR, sThemeFilters, DVX_ARRAY_LEN(sThemeFilters), path, sizeof(path))) {
|
||||
platformKindBrowseDir(dir, sizeof(dir), DvxKindThemesE);
|
||||
|
||||
if (dvxFileDialog(sAc, "Load Theme", FD_OPEN, dir, sThemeFilters, DVX_ARRAY_LEN(sThemeFilters), path, sizeof(path))) {
|
||||
dvxLoadTheme(sAc, path);
|
||||
updateColorSwatch();
|
||||
}
|
||||
|
|
@ -664,8 +672,11 @@ static void onChooseWallpaper(WidgetT *w) {
|
|||
{ "All Files (*.*)" }
|
||||
};
|
||||
char path[DVX_MAX_PATH];
|
||||
char dir[DVX_MAX_PATH];
|
||||
|
||||
if (dvxFileDialog(sAc, "Choose Wallpaper", FD_OPEN, WPAPER_DIR, filters, DVX_ARRAY_LEN(filters), path, sizeof(path))) {
|
||||
platformKindBrowseDir(dir, sizeof(dir), DvxKindWpaperE);
|
||||
|
||||
if (dvxFileDialog(sAc, "Choose Wallpaper", FD_OPEN, dir, filters, DVX_ARRAY_LEN(filters), path, sizeof(path))) {
|
||||
setWallpaperFromPath(path);
|
||||
}
|
||||
}
|
||||
|
|
@ -809,8 +820,13 @@ static void onSaveTheme(WidgetT *w) {
|
|||
(void)w;
|
||||
|
||||
char path[DVX_MAX_PATH];
|
||||
char dir[DVX_MAX_PATH];
|
||||
|
||||
if (!dvxFileDialog(sAc, "Save Theme", FD_SAVE, THEME_DIR, sThemeFilters, DVX_ARRAY_LEN(sThemeFilters), path, sizeof(path))) {
|
||||
// The user's own themes (DVX's shipped ones are never written)
|
||||
platformKindDir(dir, sizeof(dir), DvxKindThemesE, true);
|
||||
platformMkdirRecursive(dir);
|
||||
|
||||
if (!dvxFileDialog(sAc, "Save Theme", FD_SAVE, dir, sThemeFilters, DVX_ARRAY_LEN(sThemeFilters), path, sizeof(path))) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -1032,21 +1048,17 @@ static void saveSnapshot(void) {
|
|||
}
|
||||
|
||||
|
||||
// Lists the files in dir whose extension is one of exts into *entries
|
||||
// (full path plus display name) and *labels (the display names). With
|
||||
// stripExt the display name has the extension removed. Names longer
|
||||
// than the display buffer are shortened for display only; the path always
|
||||
// uses the original filename.
|
||||
static void scanDirEntries(const char *dir, const char *const *exts, int32_t extCount, bool stripExt, FileEntryT **entries, const char ***labels) {
|
||||
// The files of a kind (the user's and DVX's folders, each name once)
|
||||
// whose extension is one of exts, sorted by name: *entries (full path plus
|
||||
// display name) and *labels (the display names). With stripExt the
|
||||
// display name has the extension removed. Names longer than the display
|
||||
// buffer are shortened for display only; the path always uses the
|
||||
// original filename.
|
||||
static void scanKindEntries(DvxKindE kind, const char *const *exts, int32_t extCount, bool stripExt, FileEntryT **entries, const char ***labels) {
|
||||
arrsetlen(*entries, 0);
|
||||
arrsetlen(*labels, 0);
|
||||
|
||||
char **names = dvxReadDir(dir);
|
||||
|
||||
if (!names) {
|
||||
return;
|
||||
}
|
||||
|
||||
char **names = platformKindReadDir(kind);
|
||||
int32_t n = (int32_t)arrlen(names);
|
||||
|
||||
for (int32_t i = 0; i < n; i++) {
|
||||
|
|
@ -1075,12 +1087,20 @@ static void scanDirEntries(const char *dir, const char *const *exts, int32_t ext
|
|||
|
||||
memcpy(entry.name, names[i], nameLen);
|
||||
entry.name[nameLen] = '\0';
|
||||
snprintf(entry.path, sizeof(entry.path), "%s" DVX_PATH_SEP "%s", dir, names[i]);
|
||||
|
||||
if (platformKindFind(entry.path, sizeof(entry.path), kind, names[i])) {
|
||||
arrput(*entries, entry);
|
||||
}
|
||||
}
|
||||
|
||||
dvxReadDirFree(names);
|
||||
|
||||
// Both folders' files in one order (qsort's base must not be NULL,
|
||||
// which an empty list is)
|
||||
if (arrlen(*entries) > 1) {
|
||||
qsort(*entries, (size_t)arrlen(*entries), sizeof((*entries)[0]), entryNameCompare);
|
||||
}
|
||||
|
||||
// Build label array now that the entry array is stable
|
||||
for (int32_t i = 0; i < arrlen(*entries); i++) {
|
||||
arrput(*labels, (*entries)[i].name);
|
||||
|
|
@ -1089,12 +1109,12 @@ static void scanDirEntries(const char *dir, const char *const *exts, int32_t ext
|
|||
|
||||
|
||||
static void scanThemes(void) {
|
||||
scanDirEntries(THEME_DIR, sThemeExts, DVX_ARRAY_LEN(sThemeExts), true, &sThemeEntries, &sThemeLabels);
|
||||
scanKindEntries(DvxKindThemesE, sThemeExts, DVX_ARRAY_LEN(sThemeExts), true, &sThemeEntries, &sThemeLabels);
|
||||
}
|
||||
|
||||
|
||||
static void scanWallpapers(void) {
|
||||
scanDirEntries(WPAPER_DIR, sWpaperExts, DVX_ARRAY_LEN(sWpaperExts), false, &sWpaperEntries, &sWpaperLabels);
|
||||
scanKindEntries(DvxKindWpaperE, sWpaperExts, DVX_ARRAY_LEN(sWpaperExts), false, &sWpaperEntries, &sWpaperLabels);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -1156,7 +1176,7 @@ static void updateWheelStepLabel(void) {
|
|||
int32_t appMain(DxeAppContextT *ctx) {
|
||||
sAc = ctx->shellCtx;
|
||||
sCtx = ctx;
|
||||
sPrefs = prefsLoad(DVX_INI_PATH);
|
||||
sPrefs = prefsLoad(platformDvxIniPath());
|
||||
|
||||
int32_t winX = (sAc->display.width - CP_WIN_W) / 2;
|
||||
int32_t winY = (sAc->display.height - CP_WIN_H) / 2;
|
||||
|
|
|
|||
|
|
@ -1683,9 +1683,14 @@ int32_t RunCreate(int32_t mdl, int32_t src, const char *appDir, const char *args
|
|||
snprintf(helpPath, sizeof(helpPath), "%s" DVX_PATH_SEP "%s", s->projectDir, s->helpFile);
|
||||
}
|
||||
|
||||
// App.Path: the folder given, else the project's, else the caller's
|
||||
// App.Path: the folder given, else the project's, else the caller's;
|
||||
// App.User from the program as if it were a file there
|
||||
char appFile[DVX_MAX_PATH];
|
||||
|
||||
snprintf(appFile, sizeof(appFile), "%s" DVX_PATH_SEP "%s", devAppDir(s, appDir, parent), devTitle(s));
|
||||
memset(&spec, 0, sizeof(spec));
|
||||
spec.appDir = devAppDir(s, appDir, parent);
|
||||
spec.appFile = appFile;
|
||||
spec.args = args;
|
||||
spec.title = run->title;
|
||||
spec.helpFile = helpPath[0] ? helpPath : NULL;
|
||||
|
|
|
|||
|
|
@ -116,7 +116,7 @@ A UserControl's header (the Begin UserControl block of a .ctl) is read and chang
|
|||
|
||||
.h2 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, App.User (from the program's file: its folder in the user folder), App.Data (the DATA folder beside it), 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.
|
||||
|
||||
|
|
|
|||
|
|
@ -579,8 +579,7 @@ void basVmResume(BasVmT *vm);
|
|||
void basVmStop(BasVmT *vm);
|
||||
int32_t basVmGetCurrentLine(const BasVmT *vm);
|
||||
int32_t basVmGetErrorLine(const BasVmT *vm);
|
||||
void basVmSetAppPaths(BasVmT *vm, const char *path,
|
||||
const char *config, const char *data);
|
||||
void basVmSetAppPaths(BasVmT *vm, const char *path, const char *user, const char *data);
|
||||
.endcode
|
||||
|
||||
.table
|
||||
|
|
@ -598,7 +597,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.User (the program's user folder, made on first use), and App.Data.
|
||||
.endtable
|
||||
|
||||
The breakpoint callback notifies the host when a breakpoint fires during nested sub calls:
|
||||
|
|
|
|||
|
|
@ -461,7 +461,7 @@ typedef enum {
|
|||
|
||||
// App object
|
||||
#define OP_APP_PATH 0xDD // push App.Path string
|
||||
#define OP_APP_CONFIG 0xDE // push App.Config string
|
||||
#define OP_APP_USER 0xDE // push App.User string (its folder made on first use)
|
||||
#define OP_APP_DATA 0xDF // push App.Data string
|
||||
|
||||
// INI file operations
|
||||
|
|
@ -1284,7 +1284,7 @@ static inline int32_t basOpcodeOperandSize(uint8_t op) {
|
|||
case OP_STR_MID_ASGN:
|
||||
case OP_PRINT_TAB_N: case OP_PRINT_SPC_N:
|
||||
case OP_FORMAT: case OP_SHELL:
|
||||
case OP_APP_PATH: case OP_APP_CONFIG: case OP_APP_DATA:
|
||||
case OP_APP_PATH: case OP_APP_USER: case OP_APP_DATA:
|
||||
case OP_INI_READ: case OP_INI_WRITE:
|
||||
case OP_FS_KILL: case OP_FS_NAME: case OP_FS_FILECOPY:
|
||||
case OP_FS_MKDIR: case OP_FS_RMDIR: case OP_FS_CHDIR:
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@
|
|||
|
||||
// Names of the App object's members.
|
||||
#define BAS_APP_PATH "Path"
|
||||
#define BAS_APP_CONFIG "Config"
|
||||
#define BAS_APP_USER "User"
|
||||
#define BAS_APP_DATA "Data"
|
||||
|
||||
// INPUT$(n, #channel): its own argument syntax.
|
||||
|
|
@ -505,7 +505,7 @@ static BasNodeT *parseAndExpr(BasCompilerT *c) {
|
|||
}
|
||||
|
||||
|
||||
// App.Path / App.Config / App.Data (the App token is current).
|
||||
// App.Path / App.User / App.Data (the App token is current).
|
||||
static BasNodeT *parseAppMember(BasCompilerT *c) {
|
||||
basParseAdvance(c);
|
||||
basParseExpect(c, TOK_DOT);
|
||||
|
|
@ -514,13 +514,13 @@ static BasNodeT *parseAppMember(BasCompilerT *c) {
|
|||
|
||||
if (basParseCheckWord(c, BAS_APP_PATH)) {
|
||||
opcode = OP_APP_PATH;
|
||||
} else if (basParseCheckWord(c, BAS_APP_CONFIG)) {
|
||||
opcode = OP_APP_CONFIG;
|
||||
} else if (basParseCheckWord(c, BAS_APP_USER)) {
|
||||
opcode = OP_APP_USER;
|
||||
} else if (basParseCheckWord(c, BAS_APP_DATA) || basParseCheck(c, TOK_DATA)) {
|
||||
// "Data" lexes as the DATA keyword
|
||||
opcode = OP_APP_DATA;
|
||||
} else {
|
||||
basParseError(c, "Expected 'Path', 'Config', or 'Data' after 'App.'");
|
||||
basParseError(c, "Expected 'Path', 'User', or 'Data' after 'App.'");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,8 +33,6 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define RUN_HOST_CONFIG_DIR "CONFIG"
|
||||
#define RUN_HOST_DATA_DIR "DATA"
|
||||
#define RUN_HOST_ERR_VM "Out of memory creating the BASIC runtime."
|
||||
#define RUN_HOST_ERR_FORMRT "Out of memory creating the form runtime."
|
||||
#define RUN_HOST_ERR_OTHER "Runtime error"
|
||||
|
|
@ -64,17 +62,19 @@ BasFormRtT *basRunHostCreate(AppContextT *ctx, BasModuleT *module, const BasRunS
|
|||
|
||||
basVmLoadModule(vm, module);
|
||||
|
||||
// App.Path is the program's folder (read-only on a CD); App.Config and
|
||||
// App.Data are writable folders beneath it, made on demand.
|
||||
// App.Path is the program's folder and App.Data the DATA folder of the
|
||||
// files it ships, both only read; App.User is its user folder, made
|
||||
// when the program first asks for it (the VM).
|
||||
const char *dir = spec->appDir ? spec->appDir : "";
|
||||
char config[DVX_MAX_PATH];
|
||||
char user[DVX_MAX_PATH];
|
||||
char data[DVX_MAX_PATH];
|
||||
|
||||
snprintf(config, sizeof(config), "%s" DVX_PATH_SEP RUN_HOST_CONFIG_DIR, dir);
|
||||
snprintf(data, sizeof(data), "%s" DVX_PATH_SEP RUN_HOST_DATA_DIR, dir);
|
||||
basVmSetAppPaths(vm, dir, config, data);
|
||||
platformMkdirRecursive(config);
|
||||
platformMkdirRecursive(data);
|
||||
if (!spec->appFile || !platformAppUserDir(user, sizeof(user), spec->appFile)) {
|
||||
snprintf(user, sizeof(user), "%s", platformUserDir());
|
||||
}
|
||||
|
||||
snprintf(data, sizeof(data), "%s%s" DVX_DATA_DIR, dir, dir[0] ? DVX_PATH_SEP : "");
|
||||
basVmSetAppPaths(vm, dir, user, data);
|
||||
basVmSetCommand(vm, spec->args);
|
||||
|
||||
if (spec->printFn) {
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@
|
|||
// basRunHost.h -- set up and drive one BASIC program (VM + form runtime)
|
||||
//
|
||||
// Every host of a compiled module does the same setup: a VM with the
|
||||
// module, App.Path / App.Config / App.Data, COMMAND$, the I/O and
|
||||
// module, App.Path / App.User / App.Data, COMMAND$, the I/O and
|
||||
// DECLARE LIBRARY callbacks, a form runtime and the program's .frm
|
||||
// texts. The standalone stub (basstub.c), the IDE (ideMain.c) and the
|
||||
// basdev library (a BASIC program running another as a child) all call
|
||||
|
|
@ -41,7 +41,8 @@
|
|||
|
||||
// What a program is run with. Any pointer may be NULL (none / default).
|
||||
typedef struct {
|
||||
const char *appDir; // App.Path; App.Config / App.Data are its CONFIG and DATA folders (made)
|
||||
const char *appDir; // App.Path; App.Data is its DATA folder (the files it ships)
|
||||
const char *appFile; // the program's file (.app, .dbp, .ctl): App.User is its user folder (platformAppUserDir); NULL: the user folder
|
||||
const char *args; // COMMAND$
|
||||
const char *title; // name of the program's screen window
|
||||
const char *helpFile; // full path of the help file F1 opens
|
||||
|
|
|
|||
|
|
@ -1208,6 +1208,7 @@ static bool ucFileReady(UcFileT *file, WidgetT *parent) {
|
|||
|
||||
memset(&spec, 0, sizeof(spec));
|
||||
spec.appDir = dir;
|
||||
spec.appFile = file->path;
|
||||
spec.title = platformPathBaseName(file->path);
|
||||
|
||||
// Making a runtime must not change which program is current
|
||||
|
|
|
|||
|
|
@ -6849,9 +6849,10 @@ static void runModule(BasModuleT *mod) {
|
|||
return;
|
||||
}
|
||||
|
||||
// The program's VM and form runtime. In the IDE, App.Config and
|
||||
// App.Data live under the project directory so everything stays
|
||||
// together during development; the program's screen window carries
|
||||
// The program's VM and form runtime. In the IDE, App.Path is the
|
||||
// project's folder, App.Data its DATA folder (what the program will
|
||||
// ship), and App.User the user folder the project's program gets
|
||||
// once installed beside it; the program's screen window carries
|
||||
// the project's name and F1 opens the project's help file. The IDE
|
||||
// reports runtime errors through the Output pane and jumps the editor
|
||||
// to the faulting line, so the form runtime's modal error box is
|
||||
|
|
@ -6867,6 +6868,7 @@ static void runModule(BasModuleT *mod) {
|
|||
|
||||
memset(&spec, 0, sizeof(spec));
|
||||
spec.appDir = sProject.projectDir;
|
||||
spec.appFile = sProject.projectPath;
|
||||
spec.args = sProject.commandArgs;
|
||||
spec.title = sProject.name;
|
||||
spec.helpFile = helpPath[0] ? helpPath : NULL;
|
||||
|
|
@ -9081,9 +9083,9 @@ int32_t appMain(DxeAppContextT *ctx) {
|
|||
basUcSetPropChangedHook(onUcPropChanged);
|
||||
|
||||
// Load persisted settings
|
||||
shellEnsureConfigDir(sCtx);
|
||||
shellEnsureUserDir(sCtx);
|
||||
char prefsPath[DVX_MAX_PATH];
|
||||
shellConfigPath(sCtx, IDE_PREFS_FILE, prefsPath, sizeof(prefsPath));
|
||||
shellUserPath(sCtx, IDE_PREFS_FILE, prefsPath, sizeof(prefsPath));
|
||||
sPrefs = prefsLoad(prefsPath);
|
||||
|
||||
if (!sPrefs) {
|
||||
|
|
|
|||
|
|
@ -1121,7 +1121,7 @@ F3 repeats the last search (Find Next) without opening the dialog, also after th
|
|||
|
||||
.h1 Preferences
|
||||
|
||||
Open via Tools > Preferences. Settings are saved to dvxbasic.ini in the app's config directory. The dialog has two tabs.
|
||||
Open via Tools > Preferences. Settings are saved to dvxbasic.ini in the IDE's folder in your user folder (USER\APPS\KPUNCH\DVXBASIC). The dialog has two tabs.
|
||||
|
||||
.h2 General Tab
|
||||
|
||||
|
|
|
|||
|
|
@ -4156,24 +4156,27 @@ SQLClose db
|
|||
.toc 1 App Object
|
||||
.index App
|
||||
.index App.Path
|
||||
.index App.Config
|
||||
.index App.User
|
||||
.index App.Data
|
||||
|
||||
.h1 App Object
|
||||
|
||||
The App object provides read-only properties for the application's directory paths.
|
||||
The App object provides read-only properties for the program's folders.
|
||||
|
||||
.table
|
||||
Property Returns Description
|
||||
-------- ------- -----------
|
||||
App.Path String Directory containing the application's executable
|
||||
App.Config String Directory for application configuration files
|
||||
App.Data String Directory for application data files (databases, etc.)
|
||||
App.Path String The folder the program is in
|
||||
App.User String The program's own folder in the user folder: its settings and every file it saves (databases, saved games, documents)
|
||||
App.Data String The DATA folder in the program's folder: the files the program comes with (read-only)
|
||||
.endtable
|
||||
|
||||
App.User is the place to write: it is kept apart from the program, so installing a new version never overwrites what is there, and backing up the user folder keeps it. Every copy of the program gets the same folder. It is made the first time App.User is used. App.Data is for reading only: on a CD-ROM it cannot be written at all. Programs never write in DVX's SHARED folder.
|
||||
|
||||
.code
|
||||
configFile$ = App.Config & "\settings.ini"
|
||||
dbPath$ = App.Data & "\myapp.db"
|
||||
settingsFile$ = App.User & "\settings.ini"
|
||||
dbPath$ = App.User & "\myapp.db"
|
||||
levelFile$ = App.Data & "\level1.dat"
|
||||
Print "Running from: " & App.Path
|
||||
.endcode
|
||||
|
||||
|
|
@ -4304,8 +4307,8 @@ value$ = IniRead(file$, section$, key$, default$)
|
|||
.endcode
|
||||
|
||||
.code
|
||||
name$ = IniRead(App.Config & "\app.ini", "User", "Name", "Unknown")
|
||||
fontSize = Val(IniRead(App.Config & "\app.ini", "Display", "FontSize", "12"))
|
||||
name$ = IniRead(App.User & "\app.ini", "User", "Name", "Unknown")
|
||||
fontSize = Val(IniRead(App.User & "\app.ini", "Display", "FontSize", "12"))
|
||||
.endcode
|
||||
|
||||
.h2 IniWrite
|
||||
|
|
@ -4317,8 +4320,8 @@ INIWRITE file$, section$, key$, value$
|
|||
.endcode
|
||||
|
||||
.code
|
||||
IniWrite App.Config & "\app.ini", "User", "Name", "Scott"
|
||||
IniWrite App.Config & "\app.ini", "Display", "FontSize", Str$(fontSize)
|
||||
IniWrite App.User & "\app.ini", "User", "Name", "Scott"
|
||||
IniWrite App.User & "\app.ini", "Display", "FontSize", Str$(fontSize)
|
||||
.endcode
|
||||
|
||||
.topic lang.constants
|
||||
|
|
@ -4869,7 +4872,6 @@ Colors in the form RGB makes (&HBBGGRR, see Color Functions), as in Visual Basic
|
|||
.index PrefsCreate
|
||||
.index PrefsSaveAs
|
||||
.index Settings Files
|
||||
.index ShellConfigFile$
|
||||
.index DlgFileOpenIn$
|
||||
.index DlgFileSaveIn$
|
||||
.index FileOpsMakePath
|
||||
|
|
@ -5029,6 +5031,15 @@ Colors in the form RGB makes (&HBBGGRR, see Color Functions), as in Visual Basic
|
|||
.index FileOpsIsWritable
|
||||
.index FileOpsReadOnlySystem
|
||||
.index FileOpsTempDir$
|
||||
.index FileOpsUserDir$
|
||||
.index FileOpsUserPath$
|
||||
.index FileOpsUserInTemp
|
||||
.index FileOpsKindDir$
|
||||
.index FileOpsKindBrowseDir$
|
||||
.index FileOpsKindFind$
|
||||
.index FileOpsKindName$
|
||||
.index FileOpsKindList
|
||||
.index User Folder
|
||||
.index FileOpsJobClearAttr
|
||||
.index FileOpsJobBytesDone
|
||||
.index FILEOPS_ATTR_EDITABLE
|
||||
|
|
@ -5077,7 +5088,7 @@ DVX ships a set of BASIC include files (.bas) in the sdk/include/basic directory
|
|||
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$, ...)
|
||||
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, 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, ShellGroupsLoad, ShellItemGet$, ShellItemIcon$, ...)
|
||||
.endtable
|
||||
|
||||
.h2 Using an Include Library
|
||||
|
|
@ -5336,7 +5347,9 @@ Paths: FileOpsPathNormalize$ (the full path of an existing folder, or ""), FileO
|
|||
|
||||
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).
|
||||
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 nothing can be saved: DVX runs from such a disk and there is no TEMP folder to keep the user folder in. FileOpsTempDir$() is the folder for scratch files ("" when there is none).
|
||||
|
||||
DVX's folders: FileOpsUserDir$() is the user folder (USER in the DVX folder, or the folder DVXUSER names), where everything DVX and its programs save goes; FileOpsUserPath$(name) is a file or folder in it, for example FileOpsUserPath$("DOWNLOAD"); FileOpsUserInTemp() is True when DVX runs from a disk it cannot write to and keeps the user folder in the TEMP folder. A program's own folder there is App.User. The fonts, sounds, themes, and desktop pictures come in two folders each, DVX's own in SHARED and the user's in the user folder; the kinds are FILEOPS_KIND_FONTS, FILEOPS_KIND_SOUNDS, FILEOPS_KIND_THEMES, and FILEOPS_KIND_WPAPER. FileOpsKindDir$(kind, user) is DVX's folder of a kind (user False) or the user's (True); FileOpsKindBrowseDir$(kind) is the one a file dialog starts in (the user's when it exists); FileOpsKindFind$(kind, fileName) finds a file by name, the user's first ("" when there is none); FileOpsKindName$(kind, path) is the name alone of a file in one of the two folders, the way settings store it ("" for any other path); FileOpsKindList(kind) lists both folders as one, sorted by name, a user's file hiding DVX's of the same name (a list for FileOpsListCount, FileOpsListName$, and the rest; FileOpsListFree when done). Programs read DVX's SHARED folder but never write in it.
|
||||
|
||||
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.
|
||||
|
||||
|
|
@ -5408,18 +5421,18 @@ Command lines. ShellCommandProgram$(commandLine) and ShellCommandArgs$(commandLi
|
|||
|
||||
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).
|
||||
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. A program keeps its settings in App.User, as DVX's own programs do; 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.
|
||||
|
||||
.code
|
||||
Dim groups As Long, main As Long, i As Long
|
||||
groups = ShellGroupsLoad(ShellConfigFile$("groups.ini"))
|
||||
groups = ShellGroupsLoad(App.User & "\groups.ini")
|
||||
main = ShellGroupFind(groups, "Main")
|
||||
i = ShellItemAdd(groups, main, "Notepad", "APPS\KPUNCH\NOTEPAD\NOTEPAD.APP")
|
||||
If Not ShellItemSet(groups, main, i, "key", "Ctrl+Alt+N") Then MsgBox "Not a shortcut."
|
||||
picIcon.Picture = LOADPICTURE(ShellItemIcon$(ShellItemGet$(groups, main, i, "icon"), ShellItemGet$(groups, main, i, "program")))
|
||||
If Not ShellGroupsSave(groups, ShellConfigFile$("groups.ini")) Then MsgBox "Not saved."
|
||||
If Not ShellGroupsSave(groups, App.User & "\groups.ini") Then MsgBox "Not saved."
|
||||
ShellGroupsFree groups
|
||||
.endcode
|
||||
|
||||
|
|
@ -5455,7 +5468,7 @@ With system.bas in the project, a program changes what Control Panel changes, at
|
|||
|
||||
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.
|
||||
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; DVX's own are in SHARED\THEMES, the user's in the THEMES folder of the user folder, FileOpsKindDir$(FILEOPS_KIND_THEMES, True)); 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.
|
||||
|
||||
|
|
@ -5985,7 +5998,7 @@ With prefs.bas in the project, a program keeps its settings in an INI file the w
|
|||
|
||||
.code
|
||||
Dim ini As Long
|
||||
ini = PrefsLoad(ShellConfigFile$("MYAPP.INI"))
|
||||
ini = PrefsLoad(App.User & "\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."
|
||||
|
|
|
|||
|
|
@ -1346,10 +1346,11 @@ void basVmRunToCursor(BasVmT *vm, int32_t line) {
|
|||
}
|
||||
|
||||
|
||||
void basVmSetAppPaths(BasVmT *vm, const char *path, const char *config, const char *data) {
|
||||
void basVmSetAppPaths(BasVmT *vm, const char *path, const char *user, const char *data) {
|
||||
snprintf(vm->appPath, sizeof(vm->appPath), "%s", path);
|
||||
snprintf(vm->appConfig, sizeof(vm->appConfig), "%s", config);
|
||||
snprintf(vm->appUser, sizeof(vm->appUser), "%s", user);
|
||||
snprintf(vm->appData, sizeof(vm->appData), "%s", data);
|
||||
vm->appUserMade = false;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -4102,9 +4103,14 @@ static BasVmResultE basVmStepExec(BasVmT *vm) {
|
|||
}
|
||||
|
||||
case OP_APP_PATH:
|
||||
case OP_APP_CONFIG:
|
||||
case OP_APP_USER:
|
||||
case OP_APP_DATA: {
|
||||
const char *path = (op == OP_APP_PATH) ? vm->appPath : (op == OP_APP_CONFIG) ? vm->appConfig : vm->appData;
|
||||
const char *path = (op == OP_APP_PATH) ? vm->appPath : (op == OP_APP_USER) ? vm->appUser : vm->appData;
|
||||
|
||||
// The program writes there next: its folder is made now
|
||||
if (op == OP_APP_USER && !vm->appUserMade && vm->appUser[0]) {
|
||||
vm->appUserMade = platformMkdirRecursive(vm->appUser) == 0;
|
||||
}
|
||||
|
||||
if (!push(vm, basValStringFromC(path))) {
|
||||
return BAS_VM_STACK_OVERFLOW;
|
||||
|
|
|
|||
|
|
@ -896,9 +896,10 @@ typedef struct {
|
|||
int32_t currentFormVarCount; // number of form variables
|
||||
|
||||
// App object paths (set by host)
|
||||
char appPath[DVX_MAX_PATH]; // App.Path -- project/app directory
|
||||
char appConfig[DVX_MAX_PATH]; // App.Config -- writable config directory
|
||||
char appData[DVX_MAX_PATH]; // App.Data -- writable data directory
|
||||
char appPath[DVX_MAX_PATH]; // App.Path -- the program's folder
|
||||
char appUser[DVX_MAX_PATH]; // App.User -- its user folder (writable; made on first use)
|
||||
char appData[DVX_MAX_PATH]; // App.Data -- the files it ships (read-only)
|
||||
bool appUserMade; // App.User's folder was made
|
||||
char *commandArgs; // COMMAND$ -- launch arguments (malloc'd copy, NULL = none; see basVmSetCommand)
|
||||
|
||||
// Dir$ iterator state -- per-VM so a second embedded VM (e.g. the IDE
|
||||
|
|
@ -936,8 +937,9 @@ BasVmResultE basVmStep(BasVmT *vm);
|
|||
// False when the copy cannot be allocated (COMMAND$ is then empty).
|
||||
bool basVmSetCommand(BasVmT *vm, const char *args);
|
||||
|
||||
// Set App.Path, App.Config and App.Data (copied, cut to DVX_MAX_PATH).
|
||||
void basVmSetAppPaths(BasVmT *vm, const char *path, const char *config, const char *data);
|
||||
// Set App.Path, App.User, and App.Data (copied, cut to DVX_MAX_PATH).
|
||||
// App.User's folder is made when the program first asks for it.
|
||||
void basVmSetAppPaths(BasVmT *vm, const char *path, const char *user, const char *data);
|
||||
|
||||
// Give the host a turn: show what changed on the program screen, then
|
||||
// the DoEvents callback. False when the program must stop (the host said
|
||||
|
|
|
|||
|
|
@ -39,7 +39,8 @@
|
|||
//
|
||||
// Options:
|
||||
// --steps N abort after N bytecode steps (0 = unlimited)
|
||||
// --data DIR directory for App.Config / App.Data (default: program dir)
|
||||
// --user DIR App.User (default: the program's user folder,
|
||||
// platformAppUserDir); App.Data is the program's DATA folder
|
||||
// --args TEXT launch arguments returned by COMMAND$ (default: none)
|
||||
// --compact .bas/.dbp only: strip + compact the module before running
|
||||
// --quiet do not report runtime errors on stderr
|
||||
|
|
@ -78,7 +79,7 @@ typedef struct {
|
|||
int32_t steps;
|
||||
bool compact;
|
||||
bool quiet;
|
||||
const char *dataDir;
|
||||
const char *userDir;
|
||||
const char *args;
|
||||
const char *program;
|
||||
} OptionsT;
|
||||
|
|
@ -237,8 +238,8 @@ static bool parseArgs(int argc, char **argv, OptionsT *opt) {
|
|||
for (int32_t i = 1; i < argc; i++) {
|
||||
if (strcmp(argv[i], "--steps") == 0 && i + 1 < argc) {
|
||||
opt->steps = (int32_t)strtol(argv[++i], NULL, 10);
|
||||
} else if (strcmp(argv[i], "--data") == 0 && i + 1 < argc) {
|
||||
opt->dataDir = argv[++i];
|
||||
} else if (strcmp(argv[i], "--user") == 0 && i + 1 < argc) {
|
||||
opt->userDir = argv[++i];
|
||||
} else if (strcmp(argv[i], "--args") == 0 && i + 1 < argc) {
|
||||
opt->args = argv[++i];
|
||||
} else if (strcmp(argv[i], "--compact") == 0) {
|
||||
|
|
@ -281,10 +282,19 @@ static int32_t runModule(BasModuleT *mod, const OptionsT *opt) {
|
|||
basVmSetDoEventsCallback(vm, hostDoEvents, NULL);
|
||||
|
||||
char dir[DVX_MAX_PATH];
|
||||
platformPathDirName(opt->program, dir, sizeof(dir));
|
||||
char user[DVX_MAX_PATH];
|
||||
char data[DVX_MAX_PATH];
|
||||
|
||||
const char *dataDir = opt->dataDir ? opt->dataDir : dir;
|
||||
basVmSetAppPaths(vm, dir, dataDir, dataDir);
|
||||
platformPathDirName(opt->program, dir, sizeof(dir));
|
||||
platformPathJoin(data, sizeof(data), dir, DVX_DATA_DIR);
|
||||
|
||||
if (opt->userDir) {
|
||||
snprintf(user, sizeof(user), "%s", opt->userDir);
|
||||
} else if (!platformAppUserDir(user, sizeof(user), opt->program)) {
|
||||
snprintf(user, sizeof(user), "%s", platformUserDir());
|
||||
}
|
||||
|
||||
basVmSetAppPaths(vm, dir, user, data);
|
||||
basVmSetCommand(vm, opt->args);
|
||||
|
||||
// Run in slices so a --steps cap can be enforced across the whole run.
|
||||
|
|
@ -346,7 +356,7 @@ static int32_t runModule(BasModuleT *mod, const OptionsT *opt) {
|
|||
|
||||
static void usage(void) {
|
||||
fprintf(stderr, "DVX BASIC headless runner\n\n");
|
||||
fprintf(stderr, "Usage: basrun [--steps N] [--data DIR] [--args TEXT] [--compact] [--quiet] program.app|program.bas|project.dbp\n");
|
||||
fprintf(stderr, "Usage: basrun [--steps N] [--user DIR] [--args TEXT] [--compact] [--quiet] program.app|program.bas|project.dbp\n");
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -195,9 +195,9 @@ int32_t appMain(DxeAppContextT *ctx) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
// The VM and form runtime. App.Path is the .app's directory
|
||||
// (read-only on CD); App.Config and App.Data are writable
|
||||
// subdirectories made on demand -- the IDE runs projects the same way.
|
||||
// The VM and form runtime. App.Path is the .app's folder, App.Data
|
||||
// the DATA folder of its shipped files, App.User its user folder --
|
||||
// the IDE runs projects the same way.
|
||||
// The program's screen window (console / graphics) carries its name;
|
||||
// COMMAND$ is what it was started with.
|
||||
ShellAppT *self = shellGetApp(ctx->appId);
|
||||
|
|
@ -206,6 +206,7 @@ int32_t appMain(DxeAppContextT *ctx) {
|
|||
|
||||
memset(&spec, 0, sizeof(spec));
|
||||
spec.appDir = ctx->appDir;
|
||||
spec.appFile = ctx->appPath;
|
||||
spec.args = ctx->args;
|
||||
spec.title = self ? self->name : NULL;
|
||||
spec.printFn = stubPrint;
|
||||
|
|
|
|||
|
|
@ -11734,13 +11734,13 @@ int main(void) {
|
|||
|
||||
expectCompileError("Unknown App member rejected",
|
||||
"PRINT App.Foo\n",
|
||||
"Expected 'Path', 'Config', or 'Data' after 'App.'"
|
||||
"Expected 'Path', 'User', or 'Data' after 'App.'"
|
||||
);
|
||||
|
||||
compileOnly("App path members",
|
||||
"DIM p$\n"
|
||||
"p$ = App.Path\n"
|
||||
"p$ = App.Config\n"
|
||||
"p$ = App.User\n"
|
||||
"p$ = App.Data\n"
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -2831,7 +2831,7 @@ static void testOpcodeGuards(void) {
|
|||
{ "math-abs", { OP_MATH_ABS }, 1, false },
|
||||
{ "get-green", { OP_GET_GREEN }, 1, false },
|
||||
{ "get-blue", { OP_GET_BLUE }, 1, false },
|
||||
{ "app-config-full", { OP_APP_CONFIG }, 1, true },
|
||||
{ "app-user-full", { OP_APP_USER }, 1, true },
|
||||
{ "app-data-full", { OP_APP_DATA }, 1, true },
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -564,7 +564,7 @@ static void onPaintText(WindowT *win, RectT *dirtyArea) {
|
|||
"GUI compositor for DOS.",
|
||||
"",
|
||||
"Features:",
|
||||
" - VESA VBE 2.0+ LFB",
|
||||
" - VESA VBE, linear or banked",
|
||||
" - Dirty-rect compositing",
|
||||
" - Beveled Motif-style chrome",
|
||||
" - Draggable/resizable windows",
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ DVX features include:
|
|||
.item Over 25 built-in widget types including buttons, lists, and trees
|
||||
.item Dynamic application loading at runtime
|
||||
.item Theme support with customizable color schemes
|
||||
.item VESA VBE 2.0+ linear framebuffer rendering
|
||||
.item VESA VBE rendering, linear framebuffer or banked
|
||||
.endlist
|
||||
|
||||
.link overview See also: System Overview
|
||||
|
|
|
|||
|
|
@ -517,11 +517,15 @@ END SUB
|
|||
|
||||
|
||||
' The printing library prints picture files, so the icon goes through a
|
||||
' scratch copy in the program's data folder.
|
||||
' scratch copy in the TEMP folder (else the program's user folder).
|
||||
SUB mnuPrint_Click
|
||||
DIM tmp AS STRING
|
||||
DIM title AS STRING
|
||||
tmp = FileOpsPathJoin$(App.Data, "PRINTICO.BMP")
|
||||
tmp = FileOpsTempDir$()
|
||||
IF tmp = "" THEN
|
||||
tmp = App.User
|
||||
END IF
|
||||
tmp = FileOpsPathJoin$(tmp, "PRINTICO.BMP")
|
||||
IF NOT SaveIcon(tmp) THEN
|
||||
MsgBox "Could not prepare the icon for printing."
|
||||
EXIT SUB
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@
|
|||
// and the programs they start.
|
||||
//
|
||||
// The groups and items (pmModel.c) are saved in PROGMAN.INI in Program
|
||||
// Manager's config folder with a crash-safe rewrite after every change.
|
||||
// Manager's user folder with a crash-safe rewrite after every change.
|
||||
// "Installed Programs" is automatic: it lists every program found under
|
||||
// APPS\ (shellProgramScan), is rebuilt at start, on Refresh and whenever
|
||||
// the window comes back to the front with programs added or removed, and
|
||||
|
|
@ -1085,7 +1085,7 @@ static void refreshView(void) {
|
|||
|
||||
// Changes are written at once (crash-safe rewrite through dvxPrefs).
|
||||
static void saveModel(void) {
|
||||
shellEnsureConfigDir(sCtx);
|
||||
shellEnsureUserDir(sCtx);
|
||||
|
||||
if (!pmModelSave(&sModel, sIniPath)) {
|
||||
dvxLog("Progman: cannot save %s", sIniPath);
|
||||
|
|
@ -1265,7 +1265,7 @@ int32_t appMain(DxeAppContextT *ctx) {
|
|||
sCtx = ctx;
|
||||
sAc = ctx->shellCtx;
|
||||
|
||||
shellConfigPath(sCtx, PM_INI_NAME, sIniPath, sizeof(sIniPath));
|
||||
shellUserPath(sCtx, PM_INI_NAME, sIniPath, sizeof(sIniPath));
|
||||
pmModelInit(&sModel);
|
||||
pmModelLoad(&sModel, sIniPath);
|
||||
pmViewRegister();
|
||||
|
|
|
|||
|
|
@ -82,7 +82,7 @@ Press F1 at any time to open help. Use the list on the left side of the help win
|
|||
|
||||
.list
|
||||
.item A PC running DOS with a 386 processor or better. A 486 or Pentium is recommended.
|
||||
.item A video card that supports VESA 2.0 graphics. Most video cards made since the mid 1990s do.
|
||||
.item A video card with VESA graphics, which nearly every card from about 1992 on has (some need their VESA driver loaded first; see Troubleshooting).
|
||||
.item At least 4 MB of memory. 8 MB or more is recommended.
|
||||
.item A mouse, with its DOS mouse driver loaded before you start DVX. A scroll wheel works if your mouse driver supports it.
|
||||
.endlist
|
||||
|
|
@ -646,6 +646,9 @@ Closing the Program Manager window leaves DVX, after asking you first.
|
|||
.index Screen Resolution
|
||||
.index Sounds
|
||||
.index Volume
|
||||
.index User Folder
|
||||
.index Backing Up
|
||||
.index DVXUSER
|
||||
|
||||
.h1 Changing Settings
|
||||
|
||||
|
|
@ -659,6 +662,14 @@ DVX plays a short sound when it starts and when it ends, and when a message appe
|
|||
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.
|
||||
.endnote
|
||||
|
||||
.h2 Where your settings and files are kept
|
||||
|
||||
Everything DVX and its programs save for you goes in one folder, the USER folder inside the DVX folder (for example C:\DVX\USER): your settings, each program's settings and the files it saves (such as saved games), Terminal's downloads, and DVX's log. To back up your work, copy this one folder. Installing a new version of DVX never changes it.
|
||||
|
||||
The fonts, sounds, themes, and desktop pictures that come with DVX are in the SHARED folder. To add your own, put them in the folder of the same name in your USER folder: FONTS, SOUNDS, THEMES, or WPAPER. The settings program lists yours together with DVX's, and a file of yours with the same name as one of DVX's is used instead of it. Themes you save go there too.
|
||||
|
||||
To keep the USER folder somewhere else (on another disk, or one for each person who uses the computer), set DVXUSER to that folder before starting DVX, for example SET DVXUSER=D:\MYDVX in AUTOEXEC.BAT.
|
||||
|
||||
.topic user.taskmgr
|
||||
.title Task Manager
|
||||
.toc 0 Task Manager
|
||||
|
|
@ -811,7 +822,7 @@ DVX uses your DOS mouse driver. Load the mouse driver (usually from AUTOEXEC.BAT
|
|||
|
||||
.h2 DVX will not start, or the screen stays blank
|
||||
|
||||
DVX needs a video card with VESA 2.0 graphics. Some older cards need a VESA driver program loaded first; check the card's manual.
|
||||
DVX needs a video card with a VESA video BIOS and a mode of at least 640x480 at 256 colors or more, which nearly every card from about 1992 on has. Many older cards need their VESA driver program loaded first (from AUTOEXEC.BAT, or by typing its name at the DOS prompt): UniVBE (Scitech Display Doctor) works with most cards, and some cards came with their own, such as S3VBE20 for S3 cards and M64VBE for ATI Mach64 cards. Older cards work too, but the screen updates more slowly on them. If DVX still will not start, DVX.LOG in the USER folder says why.
|
||||
|
||||
If the screen goes wrong right after you picked a new screen resolution, wait 15 seconds and DVX puts the old one back.
|
||||
|
||||
|
|
@ -837,11 +848,11 @@ DVX is installed with SETUP, from the DVX CD or from Disk 1 of the DVX floppy di
|
|||
|
||||
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.
|
||||
To upgrade or repair DVX, run Setup again and choose the same folder: answer Yes to keep your settings and saved files (the USER folder), or No to remove them and start over. 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 the USER folder is only removed if you ask.
|
||||
|
||||
.h2 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.
|
||||
DVX also runs straight from a disk it cannot write to. It then keeps your settings and files in a DVXUSER folder in your TEMP folder, and tells you so once. To keep them somewhere of your choosing, set DVXUSER to a folder on your hard disk before starting DVX (see Changing Settings). Without a TEMP folder, DVX tells you that changed settings last only until you exit DVX. Either way the help files are used as they are. To keep everything on your hard disk, install DVX there with Setup.
|
||||
|
||||
.h2 A program is already running
|
||||
|
||||
|
|
@ -869,7 +880,7 @@ When you exit DVX, each program is closed as if you had closed it yourself. If y
|
|||
|
||||
.h2 Reporting a problem
|
||||
|
||||
DVX writes a record of what happened to the file DVX.LOG, in the same folder as DVX.EXE. If a program crashes, this file contains details that help the program's author find the problem. The file is started fresh each time DVX starts, so copy it before starting DVX again.
|
||||
DVX writes a record of what happened to the file DVX.LOG, in your USER folder (for example C:\DVX\USER\DVX.LOG). If a program crashes, this file contains details that help the program's author find the problem. The file is started fresh each time DVX starts, so copy it before starting DVX again.
|
||||
|
||||
.link user.settings Changing Settings
|
||||
.link user.taskmgr Task Manager
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ SUB cmdXferOk_Click
|
|||
downloadDir = txtDir.Text
|
||||
|
||||
IF downloadDir = "" THEN
|
||||
downloadDir = TERM_DEFAULT_DIR
|
||||
downloadDir = DefaultDownloadDir$()
|
||||
END IF
|
||||
|
||||
xferExists = ChosenIndex(ddExists)
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ CONST TERM_DEFAULT_REDIAL_DELAY = 30
|
|||
CONST TERM_DEFAULT_TCP_PORT = 23
|
||||
CONST TERM_DEFAULT_INIT = "ATZ"
|
||||
CONST TERM_DEFAULT_PREFIX = "ATDT"
|
||||
CONST TERM_DEFAULT_DIR = "DOWNLOAD"
|
||||
CONST TERM_DOWNLOAD_NAME = "DOWNLOAD" ' the default download folder, in the user folder
|
||||
|
||||
' Started with "telnet://host[:port]"
|
||||
CONST TERM_TELNET_SCHEME = "telnet://"
|
||||
|
|
@ -535,6 +535,12 @@ FUNCTION BookAdd(s AS TermSession) AS LONG
|
|||
END FUNCTION
|
||||
|
||||
|
||||
' The default download folder: DOWNLOAD in the user folder.
|
||||
FUNCTION DefaultDownloadDir$()
|
||||
DefaultDownloadDir$ = FileOpsUserPath$(TERM_DOWNLOAD_NAME)
|
||||
END FUNCTION
|
||||
|
||||
|
||||
' One default session and the default transfer settings.
|
||||
SUB BookDefaults
|
||||
SessionDefaults book(0)
|
||||
|
|
@ -545,7 +551,7 @@ SUB BookDefaults
|
|||
autoZmodem = True
|
||||
escapeCtl = False
|
||||
resumeUploads = False
|
||||
downloadDir = TERM_DEFAULT_DIR
|
||||
downloadDir = DefaultDownloadDir$()
|
||||
END SUB
|
||||
|
||||
|
||||
|
|
@ -599,7 +605,7 @@ SUB BookLoad
|
|||
autoZmodem = PrefsGetBool(ini, SEC_TRANSFER, KEY_AUTO_ZMODEM, autoZmodem)
|
||||
escapeCtl = PrefsGetBool(ini, SEC_TRANSFER, KEY_ESCAPE_CTL, escapeCtl)
|
||||
resumeUploads = PrefsGetBool(ini, SEC_TRANSFER, KEY_RESUME, resumeUploads)
|
||||
downloadDir = Left$(PrefsGet$(ini, SEC_TRANSFER, KEY_DIRECTORY, TERM_DEFAULT_DIR), TERM_DIR_MAX)
|
||||
downloadDir = Left$(PrefsGet$(ini, SEC_TRANSFER, KEY_DIRECTORY, DefaultDownloadDir$()), TERM_DIR_MAX)
|
||||
PrefsClose ini
|
||||
END SUB
|
||||
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ Telnet uses these settings, and so does the secure link when "Connect over the n
|
|||
ANSI music Plays the music some bulletin boards send. Also on the Settings > ANSI Music menu.
|
||||
.endtable
|
||||
|
||||
The phone book and all settings are saved in the file TERMINAL.INI in Terminal's folder under CONFIG.
|
||||
The phone book and all settings are saved in the file TERMINAL.INI in Terminal's folder in your user folder (USER\APPS\KPUNCH\TERMINAL).
|
||||
|
||||
.topic terminal.connect
|
||||
.title Connecting and Disconnecting
|
||||
|
|
@ -271,7 +271,7 @@ Start the receive on the other computer first (usually a menu choice on the bull
|
|||
|
||||
.h2 Receiving (Downloading)
|
||||
|
||||
Start the send on the other computer, then choose Transfer > Receive Files.... XMODEM asks for the name to save the file as. YMODEM and ZMODEM save the files under the names the sender gives, in the download folder (the DOWNLOAD folder in your DVX folder unless you choose another in Transfer Options).
|
||||
Start the send on the other computer, then choose Transfer > Receive Files.... XMODEM asks for the name to save the file as. YMODEM and ZMODEM save the files under the names the sender gives, in the download folder (the DOWNLOAD folder in your user folder unless you choose another in Transfer Options).
|
||||
|
||||
ZMODEM downloads usually start by themselves: when the other computer starts a ZMODEM send, Terminal starts receiving without you choosing anything.
|
||||
|
||||
|
|
|
|||
|
|
@ -241,7 +241,7 @@ SUB TermStart
|
|||
END
|
||||
END IF
|
||||
|
||||
iniPath = ShellConfigFile$(TERM_INI_NAME)
|
||||
iniPath = FileOpsPathJoin$(App.User, TERM_INI_NAME)
|
||||
BookLoad
|
||||
telnetUrl = OpenUrlSession(COMMAND$)
|
||||
session = book(bookCurrent)
|
||||
|
|
|
|||
|
|
@ -67,6 +67,12 @@ CONST FILEOPS_ATTR_SYSTEM = 4
|
|||
CONST FILEOPS_ATTR_ARCHIVE = 32
|
||||
CONST FILEOPS_ATTR_EDITABLE = 39 ' all four: the bits FileOpsSetAttr and FileOpsJobClearAttr change
|
||||
|
||||
' FileOpsKindDir$ / FileOpsKindFind$ kinds (DvxKindE)
|
||||
CONST FILEOPS_KIND_FONTS = 0
|
||||
CONST FILEOPS_KIND_SOUNDS = 1
|
||||
CONST FILEOPS_KIND_THEMES = 2
|
||||
CONST FILEOPS_KIND_WPAPER = 3
|
||||
|
||||
' FileOpsDriveType kinds
|
||||
CONST FILEOPS_DRIVE_FIXED = 0 ' a hard disk
|
||||
CONST FILEOPS_DRIVE_REMOVABLE = 1 ' a floppy disk drive
|
||||
|
|
@ -127,14 +133,51 @@ DECLARE LIBRARY "fileops"
|
|||
' path itself; "" when it does not fit.
|
||||
DECLARE FUNCTION FileOpsShortPath$(BYVAL path AS STRING) AS STRING
|
||||
|
||||
' True when DVX itself runs from a disk it cannot write (a CD-ROM):
|
||||
' settings cannot be saved in its folder then.
|
||||
' True when nothing can be saved: the user folder cannot be written
|
||||
' (DVX runs from a CD-ROM) and there is no TEMP folder to use instead.
|
||||
DECLARE FUNCTION FileOpsReadOnlySystem() AS LONG
|
||||
|
||||
' The folder for scratch files: the TEMP (or else TMP) setting; ""
|
||||
' when neither is set.
|
||||
DECLARE FUNCTION FileOpsTempDir$() AS STRING
|
||||
|
||||
' ---- DVX's folders ----
|
||||
|
||||
' The user folder: where everything written while DVX runs goes (the
|
||||
' DVXUSER setting, else USER in the DVX folder; in the TEMP folder
|
||||
' when DVX's disk cannot be written). A program's own settings and
|
||||
' files go in its folder there, App.User.
|
||||
DECLARE FUNCTION FileOpsUserDir$() AS STRING
|
||||
|
||||
' rel in the user folder, e.g. FileOpsUserPath$("DOWNLOAD").
|
||||
DECLARE FUNCTION FileOpsUserPath$(BYVAL rel AS STRING) AS STRING
|
||||
|
||||
' True when the user folder had to go in the TEMP folder.
|
||||
DECLARE FUNCTION FileOpsUserInTemp() AS LONG
|
||||
|
||||
' The folder of a kind of file: the user's own (user True) or the one
|
||||
' DVX ships (SHARED). Programs read SHARED but never put files there.
|
||||
DECLARE FUNCTION FileOpsKindDir$(BYVAL kind AS LONG, BYVAL user AS LONG) AS STRING
|
||||
|
||||
' The folder a file dialog for a kind starts in: the user's when it
|
||||
' exists, else SHARED's.
|
||||
DECLARE FUNCTION FileOpsKindBrowseDir$(BYVAL kind AS LONG) AS STRING
|
||||
|
||||
' A file of a kind by name, the user's first, then SHARED's (a name
|
||||
' with a folder in it is taken as it is); "" when there is none.
|
||||
DECLARE FUNCTION FileOpsKindFind$(BYVAL kind AS LONG, BYVAL fileName AS STRING) AS STRING
|
||||
|
||||
' The name alone of a file directly in one of a kind's folders, the
|
||||
' way settings store it (FileOpsKindFind$ finds it again); "" for any
|
||||
' other path.
|
||||
DECLARE FUNCTION FileOpsKindName$(BYVAL kind AS LONG, BYVAL path AS STRING) AS STRING
|
||||
|
||||
' Both folders of a kind as one list, sorted by name (a user's file
|
||||
' hides DVX's of the same name): a list for FileOpsListCount,
|
||||
' FileOpsListName$, and the rest (FileOpsKindFind$ gives a file's
|
||||
' path); 0 for a kind out of range. FileOpsListFree it when done.
|
||||
DECLARE FUNCTION FileOpsKindList(BYVAL kind AS LONG) AS LONG
|
||||
|
||||
' ---- Folder listings ----
|
||||
|
||||
' Read a folder: its folders, and its files that match filter
|
||||
|
|
|
|||
|
|
@ -236,8 +236,8 @@ DECLARE LIBRARY "dvxshell"
|
|||
|
||||
' ---- Program groups (Program Manager's PROGMAN.INI) ----
|
||||
|
||||
' Open a groups file (Program Manager keeps its own in
|
||||
' ShellConfigFile$("progman.ini") of Program Manager); a file never
|
||||
' Open a groups file (Program Manager keeps its own, PROGMAN.INI, in
|
||||
' its user folder: USER\APPS\KPUNCH\PROGMAN); a file never
|
||||
' saved gives the groups Main and Startup. Returns a handle. Groups
|
||||
' and items are numbered from 0; group 0 is Installed Programs, which
|
||||
' ShellGroupsScan fills and which is never saved or changed by hand.
|
||||
|
|
@ -377,17 +377,11 @@ DECLARE LIBRARY "dvxshell"
|
|||
' ShellRun to start it again).
|
||||
DECLARE FUNCTION ShellAppPath$() AS STRING
|
||||
|
||||
' The full path of fileName in this program's settings folder (in
|
||||
' DVX's CONFIG folder; the same place for every copy of the program,
|
||||
' where DVX's own programs keep their settings). The folder is made
|
||||
' when it does not exist yet; "" when it cannot be made.
|
||||
DECLARE FUNCTION ShellConfigFile$(BYVAL fileName AS STRING) AS STRING
|
||||
|
||||
' How many copies of this program are running, this one included
|
||||
' (see Project Properties: Allow more than one copy to run at once).
|
||||
DECLARE FUNCTION ShellInstanceCount() AS LONG
|
||||
|
||||
' Write one line to the DVX log, DVX.LOG in the DVX folder.
|
||||
' Write one line to the DVX log, DVX.LOG in the user folder.
|
||||
DECLARE SUB ShellLog(BYVAL text AS STRING)
|
||||
|
||||
' The desktop DVX starts the next time it starts (instead of Program
|
||||
|
|
|
|||
|
|
@ -259,11 +259,14 @@ DECLARE LIBRARY "audio"
|
|||
' After DVX.INI was saved: DVX plays the new event sounds from now on.
|
||||
DECLARE SUB SoundEventsReload()
|
||||
|
||||
' Play a file the way DVX plays its event sounds, to try it out.
|
||||
' Returns a voice number, or 0 (SoundError$ says why).
|
||||
' Play a file the way DVX plays its event sounds, to try it out (a
|
||||
' name alone, as the settings keep one, is found in the sound
|
||||
' folders). Returns a voice number, or 0 (SoundError$ says why).
|
||||
DECLARE FUNCTION SoundTestFile(BYVAL fileName AS STRING) AS LONG
|
||||
|
||||
' The folder of DVX's own sound files.
|
||||
' The folder of the sounds DVX ships (SHARED\SOUNDS). A sound named
|
||||
' by its name alone in the settings is looked for in the user's own
|
||||
' sound folder first (FileOpsKindDir$(FILEOPS_KIND_SOUNDS, True)).
|
||||
DECLARE FUNCTION SoundFolder$() AS STRING
|
||||
|
||||
' The volume of all sound from every program (percent); setting it
|
||||
|
|
|
|||
|
|
@ -101,7 +101,8 @@ DECLARE LIBRARY "dvxshell"
|
|||
' Every color back to DVX's own scheme.
|
||||
DECLARE SUB SysResetColors()
|
||||
|
||||
' Use the colors of a theme file (.THM, in CONFIG\THEMES). Returns
|
||||
' Use the colors of a theme file (.THM; DVX's are in SHARED\THEMES,
|
||||
' the user's in FileOpsKindDir$(FILEOPS_KIND_THEMES, True)). Returns
|
||||
' True when the file was read.
|
||||
DECLARE FUNCTION SysLoadTheme(BYVAL path AS STRING) AS LONG
|
||||
|
||||
|
|
|
|||
|
|
@ -9,8 +9,10 @@ What you need
|
|||
-------------
|
||||
|
||||
* DOS on a 386 or newer (486 recommended, Pentium for the best speed)
|
||||
* A video card with VESA BIOS Extensions 2.0 or newer (linear frame
|
||||
buffer); a VESA driver such as UNIVBE works with older cards
|
||||
* A video card with a VESA BIOS and a 640x480 mode at 256 colors or
|
||||
more; many older cards need their VESA driver loaded first, such as
|
||||
UNIVBE (Scitech Display Doctor), S3VBE20 for S3 cards, or M64VBE for
|
||||
ATI Mach64 cards
|
||||
* 4 MB of extended memory (8 MB or more recommended)
|
||||
* A two-button mouse and its DOS driver
|
||||
* About 12 MB of free space on the hard disk (14 MB with the base set
|
||||
|
|
@ -52,8 +54,8 @@ Upgrading or repairing
|
|||
----------------------
|
||||
|
||||
Run Setup again and choose the same folder. Setup asks whether to keep
|
||||
your settings (the CONFIG folder); say Yes to upgrade or repair DVX and
|
||||
keep them. Setup starts with the fonts the folder already has; choose
|
||||
your settings and saved files (the USER folder); say Yes to upgrade or
|
||||
repair DVX and keep them. An upgrade never changes the USER folder. Setup starts with the fonts the folder already has; choose
|
||||
others to add fonts or remove them.
|
||||
|
||||
|
||||
|
|
@ -79,8 +81,8 @@ Unattended install
|
|||
|
||||
SETUP /AUTO=C:\DVX [/FONTS=NONE|BASE|ALL] [/LOG=C:\SETUP.LOG]
|
||||
|
||||
installs without asking (the licence is taken as accepted, settings are
|
||||
kept on an upgrade, and AUTOEXEC.BAT is not changed). /FONTS chooses the
|
||||
installs without asking (the licence is taken as accepted, the USER
|
||||
folder is kept on an upgrade, and AUTOEXEC.BAT is not changed). /FONTS chooses the
|
||||
fonts; without it Setup installs the fonts the folder already has, or
|
||||
all of them in a new folder. The exit code is 0 when DVX was installed.
|
||||
|
||||
|
|
|
|||
|
|
@ -59,16 +59,18 @@ STAGE="${DIST}/stage"
|
|||
TOOL="${BIN}/host/dvxpkg"
|
||||
MKFONT="${BIN}/host/mkfont"
|
||||
FONT_LIST="${ROOT}/src/fonts/fonts.ini"
|
||||
# bin/'s fonts folder (DVX_FONTS_DIR, any case)
|
||||
FONTS_DIR="fonts"
|
||||
# bin/'s fonts folder (DVX_SHARED_DIR/DVX_FONTS_DIR, any case)
|
||||
FONTS_DIR="shared/fonts"
|
||||
SETUP="${BIN}/setup.exe"
|
||||
README="${ROOT}/src/installer/README.TXT"
|
||||
FLOPPY="${1:-1.44M}"
|
||||
|
||||
export MTOOLS_SKIP_CHECK=1
|
||||
|
||||
# What in bin/ is not part of an installed DVX
|
||||
EXCLUDES=(--exclude=./host --exclude=./test --exclude=./sdk --exclude='*.map' --exclude='*.LOG' --exclude='*.log')
|
||||
# What in bin/ is not part of an installed DVX: the build's own folders,
|
||||
# and the user folder a DVX run from bin/ made (the package never holds
|
||||
# one: Setup leaves the user's alone)
|
||||
EXCLUDES=(--exclude=./host --exclude=./test --exclude=./sdk --exclude=./USER --exclude=./user --exclude='*.map' --exclude='*.LOG' --exclude='*.log')
|
||||
|
||||
# Floppy formats: name -> mformat -f size (KB)
|
||||
declare -A FORMATS=([360K]=360 [720K]=720 [1.2M]=1200 [1.44M]=1440 [2.88M]=2880)
|
||||
|
|
|
|||
|
|
@ -455,7 +455,7 @@ static int32_t tierOf(const char *name, const PkgTierRuleT *rules, int32_t ruleC
|
|||
size_t len = strlen(rule);
|
||||
bool wild = len >= wildLen && strcmp(rule + len - wildLen, PKG_TIER_WILD) == 0;
|
||||
|
||||
// A wildcard rule's folder name keeps its '/': "FONTS/*" takes "FONTS/" and "FONTS/A.DFN"
|
||||
// A wildcard rule's folder name keeps its '/': "SHARED/FONTS/*" takes "SHARED/FONTS/" and "SHARED/FONTS/A.DFN"
|
||||
if (wild ? strncasecmp(name, rule, len - wildLen + 1) == 0 : strcasecmp(name, rule) == 0) {
|
||||
return r + 1;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,7 +77,6 @@ struct PkgJobT {
|
|||
PkgTierT want; // the tiers to unpack together: where they end, files, bytes
|
||||
char destDir[DVX_MAX_PATH];
|
||||
PkgAskDiskFnT ask;
|
||||
PkgKeepFnT keep;
|
||||
void *user;
|
||||
FILE *part;
|
||||
int32_t disk;
|
||||
|
|
@ -95,7 +94,6 @@ struct PkgJobT {
|
|||
char shownName[PKG_NAME_MAX]; // name in native separators, for people
|
||||
char outPath[DVX_MAX_PATH];
|
||||
FILE *out;
|
||||
bool skipping; // kept: its data is read, not written
|
||||
uint16_t method;
|
||||
uint16_t dosDate;
|
||||
uint16_t dosTime;
|
||||
|
|
@ -133,7 +131,7 @@ static PkgDiskProblemE otherDisk(const PkgJobT *job, int32_t wanted);
|
|||
static bool partPath(const PkgSourceT *src, int32_t disk, char *out, int32_t outSize);
|
||||
void pkgFind(const char *exePath, PkgSourceT *src);
|
||||
void pkgJobCancel(PkgJobT *job);
|
||||
PkgJobT *pkgJobCreate(const PkgSourceT *src, const char *destDir, int32_t tier, PkgAskDiskFnT ask, PkgKeepFnT keep, void *user);
|
||||
PkgJobT *pkgJobCreate(const PkgSourceT *src, const char *destDir, int32_t tier, PkgAskDiskFnT ask, void *user);
|
||||
char *const *pkgJobCreatedDirs(const PkgJobT *job);
|
||||
char *const *pkgJobFiles(const PkgJobT *job);
|
||||
void pkgJobFree(PkgJobT *job);
|
||||
|
|
@ -232,8 +230,8 @@ static PkgStepE entryData(PkgJobT *job) {
|
|||
|
||||
bool done;
|
||||
|
||||
if (job->skipping || job->method == PKG_ZIP_STORED) {
|
||||
if (!job->skipping && !writeOut(job, job->in + job->inPos, avail)) {
|
||||
if (job->method == PKG_ZIP_STORED) {
|
||||
if (!writeOut(job, job->in + job->inPos, avail)) {
|
||||
return PkgStepErrorE;
|
||||
}
|
||||
|
||||
|
|
@ -277,7 +275,6 @@ static PkgStepE entryData(PkgJobT *job) {
|
|||
|
||||
// The file's data has all been read: check it, close it, date it.
|
||||
static bool entryFinish(PkgJobT *job) {
|
||||
if (!job->skipping) {
|
||||
if (job->packedLeft != 0 || job->sizeDone != job->size || job->crcHave != job->crcWant) {
|
||||
entryAbort(job);
|
||||
fail(job, "The file %s in the DVX package is damaged (disk %d).", job->shownName, (int)job->disk);
|
||||
|
|
@ -295,7 +292,6 @@ static bool entryFinish(PkgJobT *job) {
|
|||
}
|
||||
|
||||
fileOpsSetTime(job->outPath, dosTimeToTime(job->dosDate, job->dosTime));
|
||||
}
|
||||
|
||||
job->bytesDone += job->size;
|
||||
job->filesDone++;
|
||||
|
|
@ -397,10 +393,8 @@ static PkgStepE entryHeader(PkgJobT *job) {
|
|||
}
|
||||
|
||||
|
||||
// Set up the file of the header just read: kept, or opened for writing.
|
||||
// Set up the file of the header just read: opened for writing.
|
||||
static bool entryOpen(PkgJobT *job) {
|
||||
struct stat st;
|
||||
|
||||
if (!ensureDirs(job, job->name, false)) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -411,17 +405,11 @@ static bool entryOpen(PkgJobT *job) {
|
|||
}
|
||||
|
||||
arrput(job->files, strdup(job->name));
|
||||
job->skipping = stat(job->outPath, &st) == 0 && job->keep && job->keep(job->user, job->name);
|
||||
job->sizeDone = 0;
|
||||
job->crcHave = (uint32_t)MZ_CRC32_INIT;
|
||||
job->dictPos = 0;
|
||||
job->state = JobDataE;
|
||||
tinfl_init(&job->inflator);
|
||||
|
||||
if (job->skipping) {
|
||||
return true;
|
||||
}
|
||||
|
||||
job->out = fopen(job->outPath, "wb");
|
||||
|
||||
// A read-only copy (an older install copied from a CD) is replaced.
|
||||
|
|
@ -630,7 +618,7 @@ void pkgJobCancel(PkgJobT *job) {
|
|||
}
|
||||
|
||||
|
||||
PkgJobT *pkgJobCreate(const PkgSourceT *src, const char *destDir, int32_t tier, PkgAskDiskFnT ask, PkgKeepFnT keep, void *user) {
|
||||
PkgJobT *pkgJobCreate(const PkgSourceT *src, const char *destDir, int32_t tier, PkgAskDiskFnT ask, void *user) {
|
||||
PkgJobT *job = (PkgJobT *)calloc(1, sizeof(PkgJobT));
|
||||
|
||||
if (!job) {
|
||||
|
|
@ -640,7 +628,6 @@ PkgJobT *pkgJobCreate(const PkgSourceT *src, const char *destDir, int32_t tier,
|
|||
job->src = *src;
|
||||
job->want = src->info.tiers[DVX_MAX(0, DVX_MIN(tier, PKG_TIERS - 1))];
|
||||
job->ask = ask;
|
||||
job->keep = keep;
|
||||
job->user = user;
|
||||
job->state = JobHeaderE;
|
||||
snprintf(job->destDir, sizeof(job->destDir), "%s", destDir);
|
||||
|
|
|
|||
|
|
@ -89,9 +89,6 @@ typedef struct {
|
|||
// cancel the job.
|
||||
typedef bool (*PkgAskDiskFnT)(void *user, int32_t disk, PkgDiskProblemE problem);
|
||||
|
||||
// A file that already exists at relName: true keeps it (not unpacked).
|
||||
typedef bool (*PkgKeepFnT)(void *user, const char *relName);
|
||||
|
||||
typedef struct PkgJobT PkgJobT;
|
||||
|
||||
// Look for the package of the program at exePath.
|
||||
|
|
@ -103,10 +100,11 @@ void pkgJobCancel(PkgJobT *job);
|
|||
|
||||
// Start unpacking tiers 0 .. tier of src (pkgFormat.h; PKG_TIERS - 1: the
|
||||
// whole package) into destDir (which must exist). The job ends at that
|
||||
// tier's end, so the disks after it are never asked for. keep may be
|
||||
// NULL (replace everything). NULL when out of memory or the package
|
||||
// cannot be opened (see pkgJobMessage of NULL: a fixed message).
|
||||
PkgJobT *pkgJobCreate(const PkgSourceT *src, const char *destDir, int32_t tier, PkgAskDiskFnT ask, PkgKeepFnT keep, void *user);
|
||||
// tier's end, so the disks after it are never asked for. Every file is
|
||||
// written (the package never holds the user's files). NULL when out of
|
||||
// memory or the package cannot be opened (see pkgJobMessage of NULL: a
|
||||
// fixed message).
|
||||
PkgJobT *pkgJobCreate(const PkgSourceT *src, const char *destDir, int32_t tier, PkgAskDiskFnT ask, void *user);
|
||||
|
||||
// Folders the job made (full paths, in the order made; stb_ds array owned
|
||||
// by the job).
|
||||
|
|
|
|||
|
|
@ -339,9 +339,8 @@ static SetupFontsE fontsChosen(const char *destDir);
|
|||
static void fontsLoad(void);
|
||||
static void installAutoexec(const char *destDir);
|
||||
static bool installCheck(const char *destDir, SetupFontsE fonts, bool *upgrade, bool *keepSettings);
|
||||
static PkgStepE installCopy(const char *destDir, SetupFontsE fonts, bool keepSettings, bool newInstall);
|
||||
static PkgStepE installCopy(const char *destDir, SetupFontsE fonts, bool newInstall);
|
||||
static void installRun(void);
|
||||
static bool keepSetting(void *user, const char *relName);
|
||||
static uint64_t megabytes(uint64_t bytes);
|
||||
static void onAccept(WidgetT *w);
|
||||
static void onAskIdle(void *ctx);
|
||||
|
|
@ -635,7 +634,7 @@ static void buildPages(WidgetT *root) {
|
|||
// Uninstall
|
||||
p = sSetup.pages[PageUninstallE];
|
||||
sSetup.unText = wgtLabel(p, "");
|
||||
sSetup.removeSettings = wgtCheckbox(p, "Also remove my &settings (the CONFIG folder)");
|
||||
sSetup.removeSettings = wgtCheckbox(p, "Also remove my &settings and saved files (the USER folder)");
|
||||
wgtLabel(p, "Files you added to the folder yourself are not removed.");
|
||||
row = buttonRow(p);
|
||||
sSetup.unBackBtn = wgtButton(row, "< &Back");
|
||||
|
|
@ -764,7 +763,7 @@ static bool installCheck(const char *destDir, SetupFontsE fonts, bool *upgrade,
|
|||
if (*upgrade && sSetup.asks) {
|
||||
char msg[SETUP_MSG_MAX];
|
||||
|
||||
snprintf(msg, sizeof(msg), "DVX is already installed in %s.\n\nYes: upgrade or repair it and keep your settings.\nNo: replace everything, your settings too.", destDir);
|
||||
snprintf(msg, sizeof(msg), "DVX is already installed in %s.\n\nYes: upgrade or repair it and keep your settings and saved files.\nNo: replace everything, and remove your settings and saved files (the USER folder).", destDir);
|
||||
|
||||
int32_t answer = ask(msg, MB_YESNOCANCEL | MB_ICONQUESTION, SETUP_SHOT_UPGRADE, 0);
|
||||
|
||||
|
|
@ -801,8 +800,8 @@ static bool installCheck(const char *destDir, SetupFontsE fonts, bool *upgrade,
|
|||
|
||||
// Unpack in slices, drawing progress and seeing Cancel between them.
|
||||
// Returns how it ended; a new install that did not finish is removed.
|
||||
static PkgStepE installCopy(const char *destDir, SetupFontsE fonts, bool keepSettings, bool newInstall) {
|
||||
PkgJobT *job = pkgJobCreate(&sSetup.src, destDir, (int32_t)fonts, askDisk, keepSettings ? keepSetting : NULL, NULL);
|
||||
static PkgStepE installCopy(const char *destDir, SetupFontsE fonts, bool newInstall) {
|
||||
PkgJobT *job = pkgJobCreate(&sSetup.src, destDir, (int32_t)fonts, askDisk, NULL);
|
||||
PlatformTicksT slice = PLATFORM_MS_TO_TICKS(SETUP_SLICE_MS);
|
||||
PlatformTicksT sliceStart = platformClock();
|
||||
PlatformTicksT boardTime = PLATFORM_MS_TO_TICKS(SETUP_BOARD_MS);
|
||||
|
|
@ -903,7 +902,7 @@ static void installRun(void) {
|
|||
showPage(PageCopyE);
|
||||
dvxLog("Setup: installing in %s (%s), fonts %s", destDir, upgrade ? (keepSettings ? "upgrade, settings kept" : "settings replaced") : "new", sFontsArgs[fonts]);
|
||||
|
||||
PkgStepE result = installCopy(destDir, fonts, keepSettings, !upgrade);
|
||||
PkgStepE result = installCopy(destDir, fonts, !upgrade);
|
||||
|
||||
if (result != PkgStepDoneE) {
|
||||
if (madeDest) {
|
||||
|
|
@ -922,8 +921,15 @@ static void installRun(void) {
|
|||
return;
|
||||
}
|
||||
|
||||
// The settings are new: start in the mode Setup runs in (it works here).
|
||||
if (!keepSettings && !setupWriteIni(destDir, sSetup.ctx.display.width, sSetup.ctx.display.height, sSetup.ctx.display.format.bitsPerPixel)) {
|
||||
// The settings start over (the user folder removed, now that the new
|
||||
// files are in): start in the mode Setup runs in (it works here).
|
||||
if (upgrade && !keepSettings && !setupRemoveUser(destDir)) {
|
||||
snprintf(msg, sizeof(msg), "Your old settings and saved files in %s could not all be removed.", destDir);
|
||||
report(msg);
|
||||
}
|
||||
|
||||
// (also when kept settings have no DVX.INI yet: an older DVX)
|
||||
if ((!keepSettings || !setupHasSettings(destDir)) && !setupWriteIni(destDir, sSetup.ctx.display.width, sSetup.ctx.display.height, sSetup.ctx.display.format.bitsPerPixel)) {
|
||||
snprintf(msg, sizeof(msg), "DVX was installed, but the settings file in %s could not be written.", destDir);
|
||||
report(msg);
|
||||
}
|
||||
|
|
@ -956,12 +962,6 @@ static void installRun(void) {
|
|||
}
|
||||
|
||||
|
||||
static bool keepSetting(void *user, const char *relName) {
|
||||
(void)user;
|
||||
return setupKeepSetting(relName);
|
||||
}
|
||||
|
||||
|
||||
// bytes in megabytes, rounded up
|
||||
static uint64_t megabytes(uint64_t bytes) {
|
||||
return (bytes + SETUP_BYTES_PER_MB - 1) / SETUP_BYTES_PER_MB;
|
||||
|
|
@ -1323,7 +1323,7 @@ int main(int argc, char *argv[]) {
|
|||
}
|
||||
|
||||
if (dvxInit(&sSetup.ctx, DVX_DEFAULT_VIDEO_W, DVX_DEFAULT_VIDEO_H, DVX_DEFAULT_VIDEO_BPP) != 0) {
|
||||
fprintf(stderr, "DVX Setup needs a VESA 2.0 video card (see README.TXT).\n");
|
||||
fprintf(stderr, "DVX Setup needs a video card with a VESA video BIOS and a 640x480 mode at 256 colors or more; many older cards need their VESA driver loaded first, such as UniVBE (see README.TXT).\n");
|
||||
return SETUP_EXIT_FAILED;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -58,10 +58,11 @@ bool setupAutoexecPath(char bootDrive, char *out, int32_t outSize);
|
|||
bool setupAutoexecRemove(const char *path, const char *line, char *err, int32_t errSize);
|
||||
bool setupCheckDest(const char *srcDir, const char *destDir, char *err, int32_t errSize);
|
||||
SetupFontsE setupFontsInstalled(const char *destDir, const PkgHeaderT *info);
|
||||
bool setupHasSettings(const char *destDir);
|
||||
bool setupIsInstalled(const char *destDir);
|
||||
bool setupKeepSetting(const char *relName);
|
||||
bool setupListWrite(const char *destDir, char *const *files, int32_t count);
|
||||
void setupRemoveDropped(const char *destDir, const char *folder, char *const *files, int32_t count);
|
||||
bool setupRemoveUser(const char *destDir);
|
||||
void setupRemoveWritten(char *const *written, int32_t writtenCount, char *const *dirs, int32_t dirCount);
|
||||
uint64_t setupSpaceNeeded(const PkgHeaderT *info, SetupFontsE tier);
|
||||
bool setupUninstall(const char *destDir, bool removeSettings, char *err, int32_t errSize);
|
||||
|
|
@ -364,18 +365,28 @@ SetupFontsE setupFontsInstalled(const char *destDir, const PkgHeaderT *info) {
|
|||
}
|
||||
|
||||
|
||||
bool setupIsInstalled(const char *destDir) {
|
||||
bool setupHasSettings(const char *destDir) {
|
||||
char dir[DVX_MAX_PATH];
|
||||
char path[DVX_MAX_PATH];
|
||||
struct stat st;
|
||||
|
||||
return fileOpsPathJoin(path, sizeof(path), destDir, DVX_INI_PATH) && stat(path, &st) == 0 && S_ISREG(st.st_mode);
|
||||
return platformUserDirOf(dir, sizeof(dir), destDir) && platformPathJoin(path, sizeof(path), dir, DVX_INI_NAME) && stat(path, &st) == 0 && S_ISREG(st.st_mode);
|
||||
}
|
||||
|
||||
|
||||
bool setupKeepSetting(const char *relName) {
|
||||
size_t len = strlen(DVX_CONFIG_DIR);
|
||||
bool setupIsInstalled(const char *destDir) {
|
||||
static const char *const marks[] = { SETUP_EXE_NAME, SETUP_LIST_NAME };
|
||||
|
||||
return strncasecmp(relName, DVX_CONFIG_DIR, len) == 0 && relName[len] == PKG_NAME_SEP;
|
||||
for (int32_t i = 0; i < DVX_ARRAY_LEN(marks); i++) {
|
||||
char path[DVX_MAX_PATH];
|
||||
struct stat st;
|
||||
|
||||
if (fileOpsPathJoin(path, sizeof(path), destDir, marks[i]) && stat(path, &st) == 0 && S_ISREG(st.st_mode)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -469,6 +480,25 @@ void setupRemoveDropped(const char *destDir, const char *folder, char *const *fi
|
|||
}
|
||||
|
||||
|
||||
bool setupRemoveUser(const char *destDir) {
|
||||
char dir[DVX_MAX_PATH];
|
||||
char ini[DVX_MAX_PATH];
|
||||
struct stat st;
|
||||
|
||||
if (!platformUserDirOf(dir, sizeof(dir), destDir) || stat(dir, &st) != 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Only a folder DVX made (it has DVX.INI), whatever DVXUSER names
|
||||
if (!platformPathJoin(ini, sizeof(ini), dir, DVX_INI_NAME) || stat(ini, &st) != 0) {
|
||||
dvxLog("Setup: %s has no %s; left as it is", dir, DVX_INI_NAME);
|
||||
return true;
|
||||
}
|
||||
|
||||
return FileOpsDeleteTree(dir) != 0;
|
||||
}
|
||||
|
||||
|
||||
void setupRemoveWritten(char *const *written, int32_t writtenCount, char *const *dirs, int32_t dirCount) {
|
||||
for (int32_t i = 0; i < writtenCount; i++) {
|
||||
removeFile(written[i]);
|
||||
|
|
@ -518,7 +548,7 @@ bool setupUninstall(const char *destDir, bool removeSettings, char *err, int32_t
|
|||
arrput(dirs, strdup(path));
|
||||
}
|
||||
|
||||
if ((!removeSettings && setupKeepSetting(names[i])) || !pkgLocalPath(destDir, names[i], path, sizeof(path))) {
|
||||
if (!pkgLocalPath(destDir, names[i], path, sizeof(path))) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -527,12 +557,8 @@ bool setupUninstall(const char *destDir, bool removeSettings, char *err, int32_t
|
|||
}
|
||||
}
|
||||
|
||||
if (removeSettings && fileOpsPathJoin(path, sizeof(path), destDir, DVX_CONFIG_DIR)) {
|
||||
FileOpsDeleteTree(path);
|
||||
}
|
||||
|
||||
if (fileOpsPathJoin(path, sizeof(path), destDir, DVX_LOG_FILE)) {
|
||||
removeFile(path);
|
||||
if (removeSettings && !setupRemoveUser(destDir)) {
|
||||
failed++;
|
||||
}
|
||||
|
||||
if (fileOpsPathJoin(path, sizeof(path), destDir, SETUP_LIST_NAME) && failed == 0) {
|
||||
|
|
@ -563,12 +589,21 @@ bool setupUninstall(const char *destDir, bool removeSettings, char *err, int32_t
|
|||
|
||||
|
||||
bool setupWriteIni(const char *destDir, int32_t width, int32_t height, int32_t bpp) {
|
||||
char dir[DVX_MAX_PATH];
|
||||
char path[DVX_MAX_PATH];
|
||||
char sys[DVX_MAX_PATH];
|
||||
char shipped[DVX_MAX_PATH];
|
||||
struct stat st;
|
||||
|
||||
if (!fileOpsPathJoin(path, sizeof(path), destDir, DVX_INI_PATH)) {
|
||||
if (!platformUserDirOf(dir, sizeof(dir), destDir) || platformMkdirRecursive(dir) != 0 || !platformPathJoin(path, sizeof(path), dir, DVX_INI_NAME)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The first time: the shipped DVX.INI, with its comments and defaults
|
||||
if (stat(path, &st) != 0 && platformPathJoin(sys, sizeof(sys), destDir, DVX_SYSTEM_DIR) && platformPathJoin(shipped, sizeof(shipped), sys, DVX_INI_NAME)) {
|
||||
platformCopyFile(shipped, path);
|
||||
}
|
||||
|
||||
PrefsHandleT *ini = prefsLoad(path);
|
||||
|
||||
if (!ini) {
|
||||
|
|
|
|||
|
|
@ -27,18 +27,23 @@
|
|||
// SETUP.EXE unpacks the DVX package (pkgRead.h) into the target folder:
|
||||
// the program, and of the fonts none, the base set, or all (the package's
|
||||
// tiers, pkgFormat.h).
|
||||
// On a folder that already holds DVX (its CONFIG\DVX.INI exists) an
|
||||
// upgrade keeps the files under CONFIG, so the user's settings, groups
|
||||
// and file types survive. Every file the package put there is listed in
|
||||
// SETUP.LST in the folder; Uninstall removes those files (and CONFIG when
|
||||
// asked), then the folders left empty. A new install gets DVX.INI with
|
||||
// the display mode Setup ran in (known to work on this computer).
|
||||
// The package holds the program only (the DVX folder layout: dvxPlat.h);
|
||||
// everything written while DVX runs is in the user folder (USER, or the
|
||||
// folder DVXUSER names), which the package never holds and an upgrade
|
||||
// never touches, so the user's settings, groups, and files survive. On a
|
||||
// folder that already holds DVX (its SETUP.LST) Setup asks whether to
|
||||
// keep them or start them over (the user folder removed). Every file the
|
||||
// package put there is listed in SETUP.LST in the folder; Uninstall
|
||||
// removes those files (and the user folder when asked), then the folders
|
||||
// left empty. A new install (or one whose settings start over) gets the
|
||||
// user's DVX.INI: the shipped SYSTEM\DVX.INI with the display mode Setup
|
||||
// ran in (known to work on this computer).
|
||||
|
||||
#ifndef SETUP_CORE_H
|
||||
#define SETUP_CORE_H
|
||||
|
||||
#include "pkgFormat.h"
|
||||
#include "dvxText.h"
|
||||
#include "dvxPlat.h"
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
|
@ -68,7 +73,7 @@ _Static_assert(SetupFontsCountE == PKG_TIERS, "a fonts choice per package tier")
|
|||
|
||||
// The fonts folder as SETUP.LST names it (an upgrade with fewer fonts
|
||||
// removes the ones it leaves out: setupRemoveDropped)
|
||||
#define SETUP_FONTS_FOLDER DVX_FONTS_DIR "/"
|
||||
#define SETUP_FONTS_FOLDER DVX_SHARED_DIR "/" DVX_FONTS_DIR "/"
|
||||
|
||||
// Room a file takes beyond its size on the target disk (half a 16 KB
|
||||
// cluster, typical of a FAT16 hard disk), for the free space check
|
||||
|
|
@ -112,11 +117,17 @@ bool setupCheckDest(const char *srcDir, const char *destDir, char *err, int3
|
|||
// without a list (a new install) gets all of them.
|
||||
SetupFontsE setupFontsInstalled(const char *destDir, const PkgHeaderT *info);
|
||||
|
||||
// True when destDir already holds a DVX with settings (CONFIG\DVX.INI).
|
||||
// True when the DVX in destDir has the user's DVX.INI (platformUserDirOf).
|
||||
bool setupHasSettings(const char *destDir);
|
||||
|
||||
// True when destDir already holds a DVX: its DVX.EXE (installed, copied
|
||||
// there, or run from there) or SETUP.LST (an install partly removed).
|
||||
bool setupIsInstalled(const char *destDir);
|
||||
|
||||
// An upgrade keeps an existing file when it is a setting (under CONFIG).
|
||||
bool setupKeepSetting(const char *relName);
|
||||
// Remove the user folder of the DVX in destDir (platformUserDirOf), its
|
||||
// settings and files, when it holds a DVX.INI (a folder that does not is
|
||||
// left alone, whatever DVXUSER names). False only when removing failed.
|
||||
bool setupRemoveUser(const char *destDir);
|
||||
|
||||
// Write destDir's SETUP.LST: the names in files (relative, '/') plus
|
||||
// those of the old list whose files are still there. False when it
|
||||
|
|
@ -136,15 +147,16 @@ void setupRemoveWritten(char *const *written, int32_t writtenCount, char *co
|
|||
// Bytes tiers 0 .. tier of the package need on the target disk.
|
||||
uint64_t setupSpaceNeeded(const PkgHeaderT *info, SetupFontsE tier);
|
||||
|
||||
// Remove DVX from destDir: every file in SETUP.LST (except the settings
|
||||
// under CONFIG unless removeSettings), the log, the list itself, then
|
||||
// every folder left empty, destDir too. Files the user added stay.
|
||||
// Remove DVX from destDir: every file in SETUP.LST, the user folder when
|
||||
// removeSettings (setupRemoveUser), the list itself, then every folder
|
||||
// left empty, destDir too. Files the user added stay.
|
||||
// False with a reason in err when the list is missing or a file could
|
||||
// not be removed (the rest is still removed).
|
||||
bool setupUninstall(const char *destDir, bool removeSettings, char *err, int32_t errSize);
|
||||
|
||||
// Store the display mode in destDir's CONFIG\DVX.INI. False when it
|
||||
// cannot be saved.
|
||||
// Store the display mode in the user's DVX.INI of the DVX in destDir,
|
||||
// made from the shipped SYSTEM\DVX.INI when there is none yet. False
|
||||
// when it cannot be saved.
|
||||
bool setupWriteIni(const char *destDir, int32_t width, int32_t height, int32_t bpp);
|
||||
|
||||
#endif // SETUP_CORE_H
|
||||
|
|
|
|||
|
|
@ -1273,7 +1273,7 @@ static void devIdle(void) {
|
|||
|
||||
|
||||
static bool devOpen(void) {
|
||||
PrefsHandleT *prefs = prefsLoad(DVX_INI_PATH);
|
||||
PrefsHandleT *prefs = prefsLoad(platformDvxIniPath());
|
||||
AudioDrvConfigT cfg;
|
||||
|
||||
devConfig(prefs, &cfg);
|
||||
|
|
@ -1362,7 +1362,7 @@ static void devOpenSynths(DvxDriverModT *mods, const AudioDrvConfigT *cfg) {
|
|||
// a new format (DVX.INI changed meanwhile) only needs the mixer reset.
|
||||
static bool devResume(void) {
|
||||
const AudioDriverT *drv = soundOf(&sDev.mod);
|
||||
PrefsHandleT *prefs = prefsLoad(DVX_INI_PATH);
|
||||
PrefsHandleT *prefs = prefsLoad(platformDvxIniPath());
|
||||
AudioDrvConfigT cfg;
|
||||
AudioDrvInfoT info;
|
||||
|
||||
|
|
|
|||
|
|
@ -194,7 +194,7 @@ A settings screen reads and writes the keys it offers through the library, so th
|
|||
|
||||
.h2 Desktop Sounds
|
||||
|
||||
The library plays the sounds DVX makes for its events (dvxPlaySound in dvxApp.h): when it loads it sets DVX's sound hook to audioDeskPlay(event, wait). The file of each event is read from DVX.INI [sounds] at the first sound: the key is audioDeskKey(event) and the value a WAV or VOC file (a path from the DVX folder), empty or missing for none; audioDeskReload makes the next sound read the section again (the Control Panel calls it after saving). The sounds play on a context of the library's own, so no program's end cuts one short, at full volume under the master volume. When no sound device starts, the sounds stay silent without trying again until the next reload. With wait the call returns once the sound has played (at most 5 seconds); after the exit sound further events are quiet (DVX is ending). audioDeskLabel(event) names an event for people, audioDeskPlayFile(path) plays any file the same way (to try one out). audioDeskFile(prefs, event) reads an event's file from a copy of DVX.INI ("" for none) and audioDeskSetFile(prefs, event, path) sets it; the library's own reading of [sounds] goes through audioDeskFile too. The default set, made by DVX's own sound generator at build time, is in AUDIO_SOUNDS_DIR (config/sounds).
|
||||
The library plays the sounds DVX makes for its events (dvxPlaySound in dvxApp.h): when it loads it sets DVX's sound hook to audioDeskPlay(event, wait). The file of each event is read from DVX.INI [sounds] at the first sound: the key is audioDeskKey(event) and the value a WAV or VOC file, empty or missing for none: a name alone is found in the sound folders (the user folder's SOUNDS first, then SHARED\SOUNDS: platformKindFind), anything else is a path; audioDeskReload makes the next sound read the section again (the Control Panel calls it after saving). The sounds play on a context of the library's own, so no program's end cuts one short, at full volume under the master volume. When no sound device starts, the sounds stay silent without trying again until the next reload. With wait the call returns once the sound has played (at most 5 seconds); after the exit sound further events are quiet (DVX is ending). audioDeskLabel(event) names an event for people, audioDeskPlayFile(path) plays any file the same way (to try one out). audioDeskFile(prefs, event) reads an event's file from a copy of DVX.INI ("" for none) and audioDeskSetFile(prefs, event, path) sets it; the library's own reading of [sounds] goes through audioDeskFile too. The default set, made by DVX's own sound generator at build time, is in SHARED\SOUNDS.
|
||||
|
||||
.table
|
||||
Key Event
|
||||
|
|
@ -236,7 +236,7 @@ A synthesizer driver exports an AudioSynthDriverT named audioSynth instead, writ
|
|||
|
||||
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.
|
||||
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 name alone found in the sound folders as the events' files are: a voice number, or 0 with SoundError$); SoundFolder$ (SHARED\SOUNDS, the sounds DVX ships; FileOpsKindDir$ and FileOpsKindList give the user's too); 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_*).
|
||||
|
||||
|
|
|
|||
|
|
@ -451,14 +451,14 @@ int32_t audioMidiRelease(AudioCtxT *ctx);
|
|||
//
|
||||
// The sounds DVX plays for its events (dvxPlaySound, DvxSoundE in
|
||||
// dvxApp.h). Each event's sound is a WAV or VOC file named in DVX.INI
|
||||
// [sounds] under the event's key (a path from the DVX folder; empty or
|
||||
// missing: none). The library connects itself to DVX when it loads and
|
||||
// plays them on a context of its own, so they belong to no program; with
|
||||
// no sound hardware they are silent. The default set lives in
|
||||
// AUDIO_SOUNDS_DIR.
|
||||
// [sounds] under the event's key: a name alone is a file in the sound
|
||||
// folders (the user's SOUNDS, then SHARED\SOUNDS: platformKindFind), a
|
||||
// name with a folder in it a path (empty or missing: none). The library
|
||||
// connects itself to DVX when it loads and plays them on a context of its
|
||||
// own, so they belong to no program; with no sound hardware they are
|
||||
// silent.
|
||||
|
||||
#define AUDIO_SOUNDS_SECTION "sounds"
|
||||
#define AUDIO_SOUNDS_DIR "config" DVX_PATH_SEP "sounds"
|
||||
|
||||
// The DVX.INI key of an event ("startup", "error", ...), and its name for
|
||||
// people ("Start DVX", "Error", ...); NULL for an event out of range.
|
||||
|
|
|
|||
|
|
@ -613,8 +613,9 @@ int32_t SoundEventCount(void) {
|
|||
}
|
||||
|
||||
|
||||
// The file DVX.INI (a prefs.bas handle) names for the event ("" none), in
|
||||
// native separators (the shipped DVX.INI says "config/sounds/...").
|
||||
// The file DVX.INI (a prefs.bas handle) names for the event ("" none): a
|
||||
// name alone (a file in the sound folders) or a path, in native
|
||||
// separators.
|
||||
const char *SoundEventFile(int32_t prefs, int32_t eventIdx) {
|
||||
static char file[DVX_MAX_PATH];
|
||||
|
||||
|
|
@ -645,9 +646,12 @@ void SoundEventsReload(void) {
|
|||
}
|
||||
|
||||
|
||||
// The folder of DVX's own sound files (from the DVX folder).
|
||||
// The folder of DVX's own sound files (SHARED\SOUNDS; the user's are in
|
||||
// FileOpsKindDir$(FILEOPS_KIND_SOUNDS, True)).
|
||||
const char *SoundFolder(void) {
|
||||
return AUDIO_SOUNDS_DIR;
|
||||
static char dir[DVX_MAX_PATH];
|
||||
|
||||
return platformKindDir(dir, sizeof(dir), DvxKindSoundsE, false) ? dir : "";
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -921,10 +925,12 @@ int32_t SoundStreamWrite(int32_t stream, const uint8_t *pcm, int32_t len) {
|
|||
}
|
||||
|
||||
|
||||
// Play a file the way desktop sounds play: a voice number, or 0 with
|
||||
// SoundError$ saying why.
|
||||
// Play a file the way desktop sounds play (a name alone is found in the
|
||||
// sound folders): a voice number, or 0 with SoundError$ saying why.
|
||||
int32_t SoundTestFile(const char *fileName) {
|
||||
return basResult(audioDeskPlayFile(fileName));
|
||||
char path[DVX_MAX_PATH];
|
||||
|
||||
return basResult(audioDeskPlayFile(platformKindFind(path, sizeof(path), DvxKindSoundsE, fileName) ? path : fileName));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -184,10 +184,16 @@ static void filesLoad(void) {
|
|||
return;
|
||||
}
|
||||
|
||||
PrefsHandleT *prefs = prefsLoad(DVX_INI_PATH);
|
||||
PrefsHandleT *prefs = prefsLoad(platformDvxIniPath());
|
||||
|
||||
// A name is found in the sound folders; one not found is kept as it
|
||||
// is, so playing it logs what is missing
|
||||
for (int32_t i = 0; i < SoundCountE; i++) {
|
||||
snprintf(sFiles[i], sizeof(sFiles[i]), "%s", audioDeskFile(prefs, (DvxSoundE)i));
|
||||
const char *file = audioDeskFile(prefs, (DvxSoundE)i);
|
||||
|
||||
if (!platformKindFind(sFiles[i], sizeof(sFiles[i]), DvxKindSoundsE, file)) {
|
||||
snprintf(sFiles[i], sizeof(sFiles[i]), "%s", file);
|
||||
}
|
||||
}
|
||||
|
||||
prefsClose(prefs);
|
||||
|
|
|
|||
|
|
@ -154,7 +154,7 @@ void netConfigFromPrefs(PrefsHandleT *h, NetConfigT *cfg) {
|
|||
|
||||
|
||||
void netConfigLoad(NetConfigT *cfg) {
|
||||
PrefsHandleT *h = prefsLoad(DVX_INI_PATH);
|
||||
PrefsHandleT *h = prefsLoad(platformDvxIniPath());
|
||||
|
||||
netConfigFromPrefs(h, cfg);
|
||||
prefsClose(h);
|
||||
|
|
|
|||
|
|
@ -30,9 +30,10 @@ CFLAGS = $(DOS_CFLAGS) -I../libdvx -I../libdvx/platform -I../../../widget
|
|||
|
||||
OBJDIR = ../../../../obj/shell
|
||||
LIBSDIR = ../../../../bin/libs
|
||||
CONFIGDIR = ../../../../bin/config
|
||||
THEMEDIR = ../../../../bin/config/themes
|
||||
WPAPERDIR = ../../../../bin/config/wpaper
|
||||
SYSTEMDIR = ../../../../bin/system
|
||||
SHAREDDIR = ../../../../bin/shared
|
||||
THEMEDIR = $(SHAREDDIR)/themes
|
||||
WPAPERDIR = $(SHAREDDIR)/wpaper
|
||||
|
||||
SRCS = shellMain.c shellApp.c shellBas.c shellSysBas.c shellInfo.c shellAssoc.c shellProg.c shellRun.c pmModel.c pmModelBas.c
|
||||
OBJS = $(patsubst %.c,$(OBJDIR)/%.o,$(SRCS))
|
||||
|
|
@ -44,7 +45,7 @@ TARGET = $(TARGETDIR)/dvxshell.lib
|
|||
THEMES = $(THEMEDIR)/geos.thm $(THEMEDIR)/win31.thm $(THEMEDIR)/cde.thm $(THEMEDIR)/hotdog.thm
|
||||
WPAPERS = $(WPAPERDIR)/blueglow.jpg $(WPAPERDIR)/swoop.jpg $(WPAPERDIR)/triangle.jpg
|
||||
|
||||
all: $(TARGET) $(TARGETDIR)/dvxshell.dep $(CONFIGDIR)/DVX.INI $(CONFIGDIR)/ASSOC.INI $(THEMES) $(WPAPERS)
|
||||
all: $(TARGET) $(TARGETDIR)/dvxshell.dep $(SYSTEMDIR)/DVX.INI $(SYSTEMDIR)/ASSOC.INI $(THEMES) $(WPAPERS)
|
||||
|
||||
$(TARGETDIR)/dvxshell.dep: dvxshell.dep | $(TARGETDIR)
|
||||
sed 's/$$/\r/' $< > $@
|
||||
|
|
@ -53,11 +54,12 @@ $(TARGET): $(OBJS) | $(TARGETDIR)
|
|||
$(DXE3GEN) -o $(TARGETDIR)/dvxshell.dxe -U $(OBJS)
|
||||
mv $(TARGETDIR)/dvxshell.dxe $@
|
||||
|
||||
$(CONFIGDIR)/DVX.INI: ../../../../config/dvx.ini | $(CONFIGDIR)
|
||||
# The shipped DVX.INI (the first start copies it to the user folder) and
|
||||
# file types (shellAssoc.c lays the user's changes over them).
|
||||
$(SYSTEMDIR)/DVX.INI: ../../../../config/dvx.ini | $(SYSTEMDIR)
|
||||
sed 's/$$/\r/' $< > $@
|
||||
|
||||
# Shipped file associations (shellAssoc.c); user changes rewrite the copy.
|
||||
$(CONFIGDIR)/ASSOC.INI: ../../../../config/ASSOC.INI | $(CONFIGDIR)
|
||||
$(SYSTEMDIR)/ASSOC.INI: ../../../../config/ASSOC.INI | $(SYSTEMDIR)
|
||||
sed 's/$$/\r/' $< > $@
|
||||
|
||||
$(OBJDIR)/%.o: %.c $(DVX_MAKEFILES) | $(OBJDIR)
|
||||
|
|
@ -69,8 +71,8 @@ $(OBJDIR):
|
|||
$(TARGETDIR):
|
||||
mkdir -p $(TARGETDIR)
|
||||
|
||||
$(CONFIGDIR):
|
||||
mkdir -p $(CONFIGDIR)
|
||||
$(SYSTEMDIR):
|
||||
mkdir -p $(SYSTEMDIR)
|
||||
|
||||
$(THEMEDIR):
|
||||
mkdir -p $(THEMEDIR)
|
||||
|
|
@ -88,4 +90,4 @@ $(WPAPERDIR)/%.jpg: ../../../../config/wpaper/%.jpg | $(WPAPERDIR)
|
|||
|
||||
clean:
|
||||
rm -rf $(OBJS) $(OBJS:.o=.d) $(TARGET) $(TARGETDIR)/dvxshell.dep $(TARGETDIR)
|
||||
rm -rf $(WPAPERDIR) $(THEMEDIR) $(CONFIGDIR)
|
||||
rm -rf $(WPAPERDIR) $(THEMEDIR) $(SYSTEMDIR)/DVX.INI $(SYSTEMDIR)/ASSOC.INI
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@ program discovery, running programs and documents, and file associations.
|
|||
|
||||
- `shellApp.h` -- application contract (AppDescriptorT, DxeAppContextT)
|
||||
- `shellInf.h` -- shell-internal interfaces exposed to apps
|
||||
- `shellAssoc.h` -- file associations (CONFIG/ASSOC.INI)
|
||||
- `shellAssoc.h` -- file associations (SYSTEM/ASSOC.INI, with the
|
||||
user's changes in USER/ASSOC.INI laid over it)
|
||||
- `shellProg.h` -- installed programs: discovery, names, icons
|
||||
- `shellRun.h` -- running programs, documents, and command lines; the Run dialog
|
||||
- `shellApp.c` -- app registry, load, unload, lifecycle
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ LoadedE is transient (only during shellLoadApp before the entry point is called)
|
|||
.link shell.lifecycle Lifecycle API
|
||||
.link shell.query Query API
|
||||
.link shell.startup Starting Up
|
||||
.link shell.config Configuration API
|
||||
.link shell.config Program Folders API
|
||||
.link shell.desktop Desktop Callbacks
|
||||
.link shell.info System Information
|
||||
.link shell.programs Installed Programs
|
||||
|
|
@ -183,7 +183,7 @@ Passed as the sole argument to appMain(). Gives the app access to the shell's GU
|
|||
appId int32_t This app's unique ID (slot index, 1-based).
|
||||
appPath[DVX_MAX_PATH] char[] Full path to the .app DXE file.
|
||||
appDir[DVX_MAX_PATH] char[] Directory containing the .app file (for loading resources).
|
||||
configDir[DVX_MAX_PATH] char[] Writable config directory (e.g. CONFIG/APPS/KPUNCH/DVXBASIC/).
|
||||
userDir[DVX_MAX_PATH] char[] The program's folder in the user folder, for its settings and the files it saves (e.g. USER\APPS\KPUNCH\DVXBASIC). Made on first use (shellEnsureUserDir).
|
||||
args[1024] char[] Launch arguments (empty string if none): blank-separated, one with blanks in double quotes. Walk them with dvxNextArg. A document opened through its file type arrives here (see File Associations); BASIC programs read these arguments with COMMAND$.
|
||||
helpFile[DVX_MAX_PATH] char[] Help file path (for F1 context help). Before appMain the shell sets it to <app>.hlp in the .app's directory when that file exists (else empty); appMain may change it.
|
||||
helpTopic[128] char[] Current help topic ID (updated by the app at runtime). Size is SHELL_HELP_TOPIC_MAX.
|
||||
|
|
@ -193,7 +193,7 @@ Passed as the sole argument to appMain(). Gives the app access to the shell's GU
|
|||
newArgsCtx void * Opaque context pointer passed to onNewArgs.
|
||||
.endtable
|
||||
|
||||
The appDir field is derived from the .app file path at load time so apps can find their own resources via relative paths. This is necessary because the working directory is shared by all apps in DOS. The config directory is mirrored from the app path: an app at APPS/KPUNCH/DVXBASIC/dvxbasic.app gets CONFIG/APPS/KPUNCH/DVXBASIC/. A full path inside the DVX directory (for example one picked with the Task Manager's Run... dialog) is mirrored the same way. An app outside the DVX directory, or loaded by a bare file name, gets CONFIG/APPS/<file name without extension>/.
|
||||
The appDir field is derived from the .app file path at load time so apps can find their own resources via relative paths. This is necessary because the working directory is shared by all apps in DOS. The user directory is mirrored from the app path in the user folder (platformAppUserDir): an app at APPS\KPUNCH\DVXBASIC\dvxbasic.app gets USER\APPS\KPUNCH\DVXBASIC. A full path inside the DVX directory (for example one picked with the Task Manager's Run... dialog) is mirrored the same way. An app outside the DVX directory, or loaded by a bare file name, gets USER\APPS\<file name without extension>. The user folder is USER in the DVX directory unless the DVXUSER environment variable names another (see DVX Folders in the architecture guide).
|
||||
|
||||
.h2 AppStateE
|
||||
|
||||
|
|
@ -453,7 +453,7 @@ The GUI starts in the [video] mode of DVX.INI (prefsGetVideoMode). If that mode
|
|||
After the desktop app has loaded:
|
||||
|
||||
.list
|
||||
.item When the DVX folder cannot be written to (platformIsReadOnly: the loader found it read-only, as on a CD-ROM), a message says once that changed settings last until DVX exits and that the help is used as it is.
|
||||
.item When DVX's disk cannot be written to (as on a CD-ROM), a message says so once: either that the user folder is in the TEMP folder (platformUserInTemp; SHELL_USER_TEMP_MSG), or, without a TEMP folder, that nothing can be saved and changed settings last until DVX exits (platformIsReadOnly; SHELL_READ_ONLY_MSG). Both suggest setting DVXUSER to a folder on a hard disk.
|
||||
.item The program named by DVX.INI [shell] welcome (SHELL_INI_WELCOME), if the key is there. The shipped DVX.INI names the Welcome window, which removes the key when the user ticks "Don't show this again", so the key itself is the "first start" flag.
|
||||
.item The program given with /RUN=path on the DVX command line (for example DVX /RUN=APPS\KPUNCH\NOTEPAD\NOTEPAD.APP).
|
||||
.endlist
|
||||
|
|
@ -461,43 +461,53 @@ After the desktop app has loaded:
|
|||
Both are started with shellRunCommand, so a missing program is reported to the user.
|
||||
|
||||
.topic shell.config
|
||||
.title Configuration API
|
||||
.toc 1 Configuration API
|
||||
.index shellEnsureConfigDir
|
||||
.index shellConfigPath
|
||||
.title Program Folders API
|
||||
.toc 1 Program Folders API
|
||||
.index shellEnsureUserDir
|
||||
.index shellUserPath
|
||||
.index shellDataPath
|
||||
|
||||
.h1 Configuration API
|
||||
.h1 Program Folders API
|
||||
|
||||
Each app has a per-app configuration directory derived from its DXE path, mirrored under CONFIG/. For example, an app at APPS/KPUNCH/DVXBASIC/dvxbasic.app gets the config directory CONFIG/APPS/KPUNCH/DVXBASIC/.
|
||||
Each program has two folders of its own. Its user folder (ctx->userDir) is in DVX's user folder, mirrored from its path: an app at APPS\KPUNCH\DVXBASIC\dvxbasic.app gets USER\APPS\KPUNCH\DVXBASIC. Settings and every file the program saves go there, apart from the program files, so an upgrade or a reinstall never overwrites them and backing up the user folder keeps them. Its DATA folder, beside its .app, holds the files it ships; DVX never writes there, and on a CD-ROM it cannot be written at all.
|
||||
|
||||
.h2 shellEnsureConfigDir
|
||||
A program never writes or installs into SHARED (DVX's own fonts, sounds, themes, and wallpapers): its own files live in its DATA folder or as resources in its .app.
|
||||
|
||||
.h2 shellEnsureUserDir
|
||||
|
||||
.code
|
||||
int32_t shellEnsureConfigDir(const DxeAppContextT *ctx);
|
||||
int32_t shellEnsureUserDir(const DxeAppContextT *ctx);
|
||||
.endcode
|
||||
|
||||
Ensure the app's config directory exists, creating all parent directories as needed. Returns 0 on success, -1 on failure.
|
||||
Make the program's user folder, with all parent folders as needed. Returns 0 on success, -1 on failure. Call it before writing the first file there.
|
||||
|
||||
Call this before writing any config files. The directory path comes from ctx->configDir.
|
||||
|
||||
.h2 shellConfigPath
|
||||
.h2 shellUserPath
|
||||
|
||||
.code
|
||||
void shellConfigPath(const DxeAppContextT *ctx, const char *filename, char *outPath, int32_t outSize);
|
||||
void shellUserPath(const DxeAppContextT *ctx, const char *filename, char *outPath, int32_t outSize);
|
||||
.endcode
|
||||
|
||||
Build a full path to a file in the app's config directory by joining ctx->configDir with the given filename.
|
||||
Build the full path of a file in the program's user folder by joining ctx->userDir with filename.
|
||||
|
||||
.code
|
||||
// Example:
|
||||
char path[DVX_MAX_PATH];
|
||||
shellConfigPath(ctx, "settings.ini", path, sizeof(path));
|
||||
// -> "CONFIG/APPS/KPUNCH/PROGMAN/settings.ini"
|
||||
shellEnsureUserDir(ctx);
|
||||
shellUserPath(ctx, "PROGMAN.INI", path, sizeof(path));
|
||||
// -> "USER\APPS\KPUNCH\PROGMAN\PROGMAN.INI"
|
||||
.endcode
|
||||
|
||||
.h2 shellDataPath
|
||||
|
||||
.code
|
||||
void shellDataPath(const DxeAppContextT *ctx, const char *filename, char *outPath, int32_t outSize);
|
||||
.endcode
|
||||
|
||||
Build the full path of a file the program ships in the DATA folder beside its .app ("" when it does not fit), e.g. "APPS\ACME\MYGAME\DATA\LEVEL1.DAT". Read-only.
|
||||
|
||||
.h2 Random Seed File
|
||||
|
||||
When the serial library is loaded, the shell feeds keyboard and mouse event timings to its random-number generator (dvxSetInputEntropyHook) and carries generator state across runs in CONFIG/SECLINK.RND: the file is read and immediately rewritten at startup, and rewritten again at shutdown. The library is found at run time; without it no seed file is written.
|
||||
When the serial library is loaded, the shell feeds keyboard and mouse event timings to its random-number generator (dvxSetInputEntropyHook) and carries generator state across runs in SECLINK.RND in the user folder: the file is read and immediately rewritten at startup, and rewritten again at shutdown. The library is found at run time; without it no seed file is written.
|
||||
|
||||
.topic shell.desktop
|
||||
.title Desktop Callbacks
|
||||
|
|
@ -795,7 +805,7 @@ Run Minimized. shellMinimizeApp minimizes every visible window of appId (an id f
|
|||
|
||||
Header: dvxshell/shellAssoc.h
|
||||
|
||||
Which program opens each kind of document. One store serves every program: CONFIG\ASSOC.INI (SHELL_ASSOC_INI_PATH), section [associations]. Each key is an extension without the dot, upper case; each value is a program path relative to the DVX root, optionally followed by ,arguments where %1 stands for the document:
|
||||
Which program opens each kind of document. One store serves every program, in two layers: the shipped list, SYSTEM\ASSOC.INI (SHELL_ASSOC_SHIPPED_PATH), and the user's changes, ASSOC.INI in the user folder, both with the section [associations]. A type in the user's file replaces the shipped one; an empty value there removes a shipped type. Each key is an extension without the dot, upper case; each value is a program path relative to the DVX root, optionally followed by ,arguments where %1 stands for the document:
|
||||
|
||||
.code
|
||||
[associations]
|
||||
|
|
@ -803,7 +813,7 @@ TXT = apps/kpunch/notepad/notepad.app
|
|||
FRM = apps/kpunch/dvxbasic/dvxbasic.app,%1
|
||||
.endcode
|
||||
|
||||
DVX ships a default ASSOC.INI; users change it on the Control Panel's File Types page, and a setup program registers its own types with shellAssocSet. The store is read once through dvxPrefs and kept; every change is saved at once with the crash-safe rewrite, and a failed save drops the cached copy so the next call rereads the file. Extension arguments may have a leading dot and any case.
|
||||
Users change the types on the Control Panel's File Types page, and a setup program registers its own types with shellAssocSet. Every change goes to the user's file, so an upgrade's new shipped types appear while the user's choices stay. Both files are read once through dvxPrefs and kept; every change is saved at once with the crash-safe rewrite, and a failed save drops the cached copies so the next call rereads the files. Extension arguments may have a leading dot and any case.
|
||||
|
||||
.h2 Looking Up
|
||||
|
||||
|
|
@ -886,7 +896,6 @@ shellAssocSet adds or replaces an association (programPath may carry ,arguments)
|
|||
.index HotkeyNext
|
||||
.index HotkeyText
|
||||
.index ShellAppPath
|
||||
.index ShellConfigFile
|
||||
.index ShellInstanceCount
|
||||
.index ShellLog
|
||||
.index ShellSetDesktop
|
||||
|
|
@ -997,14 +1006,13 @@ HotkeyAdd parses keyText with dvxAccelParse and registers it with dvxRegisterHot
|
|||
|
||||
.code
|
||||
const char *ShellAppPath(void);
|
||||
const char *ShellConfigFile(const char *fileName);
|
||||
int32_t ShellInstanceCount(void);
|
||||
void ShellLog(const char *text);
|
||||
int32_t ShellSetDesktop(const char *program);
|
||||
int32_t ShellSetWelcome(const char *program);
|
||||
.endcode
|
||||
|
||||
ShellAppPath is the running program's .app path as started ("" when unknown). ShellConfigFile is shellConfigPath of fileName in the running program's config folder, which shellEnsureConfigDir makes first ("" when it cannot); every copy of a program gets the same folder, the one a C app's settings use. ShellInstanceCount counts the running apps with the same path, this one included (a second copy of a multiInstance program loads from a temporary copy but keeps the original path). ShellLog writes one line with dvxLog. ShellSetDesktop and ShellSetWelcome set, or with "" remove, DVX.INI [shell] desktop (the desktop app; SHELL_DESKTOP_APP when missing) and [shell] welcome (the program started after the desktop); both share one writer and return False (logged) when DVX.INI cannot be saved.
|
||||
ShellAppPath is the running program's .app path as started ("" when unknown). A BASIC program's user folder is App.User (the folder a C program's userDir names; every copy of a program gets the same one). ShellInstanceCount counts the running apps with the same path, this one included (a second copy of a multiInstance program loads from a temporary copy but keeps the original path). ShellLog writes one line with dvxLog. ShellSetDesktop and ShellSetWelcome set, or with "" remove, DVX.INI [shell] desktop (the desktop app; SHELL_DESKTOP_APP when missing) and [shell] welcome (the program started after the desktop); both share one writer and return False (logged) when DVX.INI cannot be saved.
|
||||
|
||||
.h2 Programs folder, item icons, program groups
|
||||
|
||||
|
|
@ -1017,7 +1025,7 @@ ShellProgramsFolder is SHELL_PROG_DIR (where ShellProgramScan("") looks and a Br
|
|||
|
||||
.h2 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 its App.User 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.
|
||||
|
||||
.topic shell.groups
|
||||
.title Program Groups
|
||||
|
|
|
|||
|
|
@ -50,9 +50,6 @@
|
|||
// on the stack.
|
||||
#define SHELL_TOP_DISPATCH_DEPTH 1
|
||||
|
||||
// Root of the per-app config tree and the fallback parent directory
|
||||
#define SHELL_CONFIG_ROOT "CONFIG"
|
||||
#define SHELL_CONFIG_APPS "APPS"
|
||||
|
||||
// Extension of an app's default help file (<app>.hlp beside <app>.app)
|
||||
#define SHELL_HELP_EXT ".hlp"
|
||||
|
|
@ -95,7 +92,6 @@ static int32_t copyFile(const char *src, const char *dst);
|
|||
static void defaultHelpFile(DxeAppContextT *dxeCtx);
|
||||
static void deferForceKill(AppContextT *ctx, ShellAppT *app);
|
||||
static void deliverNewArgs(AppContextT *ctx, ShellAppT *app, const char *args);
|
||||
static void deriveConfigDir(DxeAppContextT *dxeCtx, const char *path);
|
||||
static void destroyAppWindows(AppContextT *ctx, int32_t appId);
|
||||
static ShellAppT *findLoadedPath(const char *path);
|
||||
static ShellAppT *findRunningPath(const char *path);
|
||||
|
|
@ -114,8 +110,8 @@ int32_t shellAppSlotCount(void);
|
|||
#ifdef DVX_HOST_TEST
|
||||
void shellAppTestReset(void);
|
||||
#endif
|
||||
void shellConfigPath(const DxeAppContextT *ctx, const char *filename, char *outPath, int32_t outSize);
|
||||
int32_t shellEnsureConfigDir(const DxeAppContextT *ctx);
|
||||
void shellDataPath(const DxeAppContextT *ctx, const char *filename, char *outPath, int32_t outSize);
|
||||
int32_t shellEnsureUserDir(const DxeAppContextT *ctx);
|
||||
void shellForceKillApp(AppContextT *ctx, ShellAppT *app);
|
||||
ShellAppT *shellGetApp(int32_t appId);
|
||||
int32_t shellLoadApp(AppContextT *ctx, const char *path);
|
||||
|
|
@ -131,6 +127,7 @@ int32_t shellRunningAppCount(void);
|
|||
int32_t shellRunningAppId(int32_t index);
|
||||
void shellTerminateAllApps(AppContextT *ctx);
|
||||
void shellUnregisterAppCleanup(ShellAppCleanupFnT fn);
|
||||
void shellUserPath(const DxeAppContextT *ctx, const char *filename, char *outPath, int32_t outSize);
|
||||
|
||||
|
||||
// Find the first free slot, starting at 1 (slot 0 is the shell).
|
||||
|
|
@ -375,35 +372,6 @@ static void deliverNewArgs(AppContextT *ctx, ShellAppT *app, const char *args) {
|
|||
}
|
||||
|
||||
|
||||
// Derive the app's writable config directory by mirroring its directory
|
||||
// under CONFIG/: "APPS/KPUNCH/DVXBASIC" -> "CONFIG/APPS/KPUNCH/DVXBASIC".
|
||||
// An absolute path (e.g. from the Run... file dialog) is first made
|
||||
// relative to the DVX root (the working directory). When that is not
|
||||
// possible -- outside the DVX tree, or a bare file name -- the fallback is
|
||||
// CONFIG/APPS/<app file name without extension>.
|
||||
static void deriveConfigDir(DxeAppContextT *dxeCtx, const char *path) {
|
||||
const char *rel = dxeCtx->appDir;
|
||||
|
||||
if (platformPathIsAbsolute(rel)) {
|
||||
char cwd[DVX_MAX_PATH];
|
||||
|
||||
rel = getcwd(cwd, sizeof(cwd)) ? platformPathIsUnder(rel, cwd) : NULL;
|
||||
}
|
||||
|
||||
if (rel && rel[0] && strcmp(rel, ".") != 0) {
|
||||
snprintf(dxeCtx->configDir, sizeof(dxeCtx->configDir), SHELL_CONFIG_ROOT DVX_PATH_SEP "%s", rel);
|
||||
platformPathNative(dxeCtx->configDir);
|
||||
return;
|
||||
}
|
||||
|
||||
const char *base = platformPathBaseName(path);
|
||||
const char *dot = strrchr(base, '.');
|
||||
int32_t len = dot ? (int32_t)(dot - base) : (int32_t)strlen(base);
|
||||
|
||||
snprintf(dxeCtx->configDir, sizeof(dxeCtx->configDir), SHELL_CONFIG_ROOT DVX_PATH_SEP SHELL_CONFIG_APPS DVX_PATH_SEP "%.*s", (int)len, base);
|
||||
}
|
||||
|
||||
|
||||
// Destroy every window belonging to an app SYNCHRONOUSLY, before its DXE
|
||||
// is unmapped -- a deferred destroy would leave live timer/poll/window
|
||||
// callbacks pointing into unmapped code for the rest of the dispatch pass.
|
||||
|
|
@ -527,10 +495,9 @@ static void killAppTask(ShellAppT *app) {
|
|||
|
||||
|
||||
// Build a temp path for a multi-instance copy. Uses the TEMP or TMP
|
||||
// environment variable if set, otherwise falls back to the current directory
|
||||
// (the DVX folder). The slot ID is embedded in the filename to ensure
|
||||
// uniqueness. False when there is nowhere to put the copy: no TEMP and a
|
||||
// read-only DVX folder.
|
||||
// environment variable if set, otherwise falls back to the user folder.
|
||||
// The slot ID is embedded in the filename to ensure uniqueness. False
|
||||
// when there is nowhere to put the copy: no TEMP and nothing writable.
|
||||
// Example: TEMP=C:\TEMP -> "C:\TEMP\_dvx02.app"
|
||||
static bool makeTempPath(const char *origPath, int32_t id, char *out, int32_t outSize) {
|
||||
// Find extension from original path
|
||||
|
|
@ -542,17 +509,19 @@ static bool makeTempPath(const char *origPath, int32_t id, char *out, int32_t ou
|
|||
dot = origPath + strlen(origPath);
|
||||
}
|
||||
|
||||
// The fallback is anchored to the working directory: a bare file name
|
||||
// would make dlopen search the library path instead of opening it.
|
||||
// The user folder has a folder part, so dlopen opens the file instead
|
||||
// of searching the library path for a bare name.
|
||||
char name[DVX_MAX_PATH];
|
||||
|
||||
snprintf(name, sizeof(name), "_dvx%02ld%s", (long)id, dot);
|
||||
|
||||
if (!tmpDir && platformIsReadOnly()) {
|
||||
// Without TEMP, the user folder (made again if it went missing),
|
||||
// unless nothing can be saved
|
||||
if (!tmpDir && (platformIsReadOnly() || platformMkdirRecursive(platformUserDir()) != 0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return platformPathJoin(out, outSize, tmpDir ? tmpDir : ".", name);
|
||||
return platformPathJoin(out, outSize, tmpDir ? tmpDir : platformUserDir(), name);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -674,13 +643,17 @@ void shellAppTestReset(void) {
|
|||
#endif
|
||||
|
||||
|
||||
void shellConfigPath(const DxeAppContextT *ctx, const char *filename, char *outPath, int32_t outSize) {
|
||||
platformPathJoin(outPath, outSize, ctx->configDir, filename);
|
||||
void shellDataPath(const DxeAppContextT *ctx, const char *filename, char *outPath, int32_t outSize) {
|
||||
char dir[DVX_MAX_PATH];
|
||||
|
||||
if (!platformPathJoin(dir, sizeof(dir), ctx->appDir, DVX_DATA_DIR) || !platformPathJoin(outPath, outSize, dir, filename)) {
|
||||
outPath[0] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int32_t shellEnsureConfigDir(const DxeAppContextT *ctx) {
|
||||
return platformMkdirRecursive(ctx->configDir);
|
||||
int32_t shellEnsureUserDir(const DxeAppContextT *ctx) {
|
||||
return platformMkdirRecursive(ctx->userDir);
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -891,7 +864,12 @@ static int32_t shellLoadAppInternal(AppContextT *ctx, const char *path, const ch
|
|||
// than the shell's working directory.
|
||||
platformPathDirName(app->path, app->dxeCtx->appDir, sizeof(app->dxeCtx->appDir));
|
||||
|
||||
deriveConfigDir(app->dxeCtx, path);
|
||||
// Its user folder (APPS\<its path> in the user folder); the user
|
||||
// folder itself if that path does not fit
|
||||
if (!platformAppUserDir(app->dxeCtx->userDir, sizeof(app->dxeCtx->userDir), path)) {
|
||||
snprintf(app->dxeCtx->userDir, sizeof(app->dxeCtx->userDir), "%s", platformUserDir());
|
||||
}
|
||||
|
||||
defaultHelpFile(app->dxeCtx);
|
||||
|
||||
// The fonts the program carries (font resources), for it alone; gone
|
||||
|
|
@ -1172,3 +1150,8 @@ void shellUnregisterAppCleanup(ShellAppCleanupFnT fn) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void shellUserPath(const DxeAppContextT *ctx, const char *filename, char *outPath, int32_t outSize) {
|
||||
platformPathJoin(outPath, outSize, ctx->userDir, filename);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ typedef struct {
|
|||
int32_t appId; // this app's ID
|
||||
char appPath[DVX_MAX_PATH]; // full path to the .app file
|
||||
char appDir[DVX_MAX_PATH]; // directory containing the .app file
|
||||
char configDir[DVX_MAX_PATH]; // writable config directory (CONFIG/<apppath>/)
|
||||
char userDir[DVX_MAX_PATH]; // writable folder: the user folder's APPS\<program path> (platformAppUserDir)
|
||||
char args[SHELL_ARGS_MAX]; // launch arguments (empty if none)
|
||||
char helpFile[DVX_MAX_PATH]; // help file path (for F1 context help); preset to <app>.hlp beside the .app when it exists
|
||||
char helpTopic[SHELL_HELP_TOPIC_MAX]; // current help topic ID (updated by app)
|
||||
|
|
@ -318,14 +318,22 @@ int32_t shellRunningAppCount(void);
|
|||
// order), 0 out of range.
|
||||
int32_t shellRunningAppId(int32_t index);
|
||||
|
||||
// Ensure an app's config directory exists (creates all parent dirs).
|
||||
// Returns 0 on success, -1 on failure.
|
||||
int32_t shellEnsureConfigDir(const DxeAppContextT *ctx);
|
||||
// A program's folders (dvxPlat.h, DVX folders): its user folder (ctx->
|
||||
// userDir: settings and the files it saves, made by shellEnsureUserDir)
|
||||
// and the DATA folder beside its .app (the files it ships, read-only).
|
||||
|
||||
// Build a full path to a file in the app's config directory.
|
||||
// e.g. shellConfigPath(ctx, "settings.ini", buf, sizeof(buf))
|
||||
// -> "CONFIG/PROGMAN/settings.ini"
|
||||
void shellConfigPath(const DxeAppContextT *ctx, const char *filename, char *outPath, int32_t outSize);
|
||||
// Make the program's user folder (and its parents). 0 on success, -1 on
|
||||
// failure.
|
||||
int32_t shellEnsureUserDir(const DxeAppContextT *ctx);
|
||||
|
||||
// The full path of a file in the program's user folder, e.g.
|
||||
// shellUserPath(ctx, "PROGMAN.INI", buf, sizeof(buf)) ->
|
||||
// "USER\APPS\KPUNCH\PROGMAN\PROGMAN.INI"
|
||||
void shellUserPath(const DxeAppContextT *ctx, const char *filename, char *outPath, int32_t outSize);
|
||||
|
||||
// The full path of a file the program ships in its DATA folder, e.g.
|
||||
// "APPS\KPUNCH\GAME\DATA\LEVEL1.DAT"
|
||||
void shellDataPath(const DxeAppContextT *ctx, const char *filename, char *outPath, int32_t outSize);
|
||||
|
||||
|
||||
// ============================================================
|
||||
|
|
@ -475,10 +483,6 @@ int32_t ShellChangeCount(void);
|
|||
const char *ShellCommandArgs(const char *command);
|
||||
const char *ShellCommandProgram(const char *command);
|
||||
|
||||
// fileName in this program's settings folder (made when missing; "" when
|
||||
// it cannot be).
|
||||
const char *ShellConfigFile(const char *fileName);
|
||||
|
||||
// Exit DVX (every program is asked to close first).
|
||||
void ShellExitDvx(void);
|
||||
|
||||
|
|
|
|||
|
|
@ -22,11 +22,13 @@
|
|||
|
||||
// shellAssoc.c -- File associations (see shellAssoc.h)
|
||||
//
|
||||
// The store is CONFIG/ASSOC.INI read through dvxPrefs, so its parsing,
|
||||
// comment preservation and crash-safe rewrite are the same as DVX.INI's.
|
||||
// It is loaded on first use and kept; every change is saved at once, and a
|
||||
// failed save drops the cached copy so the next call rereads the file (the
|
||||
// file on disk stays the one source of truth).
|
||||
// Both files are read through dvxPrefs, so their parsing, comment
|
||||
// preservation and crash-safe rewrite are the same as DVX.INI's. They are
|
||||
// loaded on first use and kept, with the view readers see (the shipped
|
||||
// list, then the user's types over it); every change goes to the user's
|
||||
// file and is saved at once, and a failed save drops the cached copies so
|
||||
// the next call rereads the files (the files on disk stay the one source
|
||||
// of truth).
|
||||
|
||||
#include "shellAssoc.h"
|
||||
#include "shellApp.h"
|
||||
|
|
@ -40,14 +42,18 @@
|
|||
// Module state
|
||||
// ============================================================
|
||||
|
||||
static PrefsHandleT *sStore = NULL;
|
||||
static const char *sStorePath = SHELL_ASSOC_INI_PATH;
|
||||
static PrefsHandleT *sShipped = NULL; // the list DVX ships
|
||||
static PrefsHandleT *sUser = NULL; // the user's changes to it
|
||||
static PrefsHandleT *sView = NULL; // the two together (assocStore)
|
||||
static const char *sShippedPath = NULL; // NULL: the real files
|
||||
static const char *sUserPath = NULL;
|
||||
static ShellAssocLaunchFnT sLaunch = shellLoadAppWithArgs;
|
||||
|
||||
// ============================================================
|
||||
// Prototypes
|
||||
// ============================================================
|
||||
|
||||
static void assocDrop(void);
|
||||
static void assocProgramNative(char *value);
|
||||
static bool assocSave(void);
|
||||
static PrefsHandleT *assocStore(void);
|
||||
|
|
@ -60,12 +66,21 @@ bool shellAssocOpenDocument(AppContextT *ctx, const char *docume
|
|||
bool shellAssocRemove(const char *extension);
|
||||
bool shellAssocSet(const char *extension, const char *programPath);
|
||||
#ifdef DVX_HOST_TEST
|
||||
void shellAssocTestReset(const char *storePath, ShellAssocLaunchFnT launcher);
|
||||
void shellAssocTestReset(const char *shippedPath, const char *userPath, ShellAssocLaunchFnT launcher);
|
||||
#endif
|
||||
|
||||
|
||||
// Writes the store; on failure the cached copy (which holds the change
|
||||
// that did not reach the disk) is dropped.
|
||||
// Forget the cached files and view (the next call reads them again).
|
||||
static void assocDrop(void) {
|
||||
prefsClose(sShipped);
|
||||
prefsClose(sUser);
|
||||
prefsClose(sView);
|
||||
sShipped = NULL;
|
||||
sUser = NULL;
|
||||
sView = NULL;
|
||||
}
|
||||
|
||||
|
||||
// A stored "program,arguments" value with its program part in native
|
||||
// separators (the shipped ASSOC.INI says "apps/kpunch/..."); the
|
||||
// arguments are left alone ("/doc" is a switch, not a path).
|
||||
|
|
@ -84,24 +99,68 @@ static void assocProgramNative(char *value) {
|
|||
}
|
||||
|
||||
|
||||
// Writes the user's file; the view is made again next time. On failure
|
||||
// the cached copies (which hold the change that did not reach the disk)
|
||||
// are dropped.
|
||||
static bool assocSave(void) {
|
||||
if (prefsSave(sStore)) {
|
||||
return true;
|
||||
}
|
||||
bool ok = prefsSave(sUser);
|
||||
|
||||
dvxLog("Assoc: cannot save %s", sStorePath);
|
||||
prefsClose(sStore);
|
||||
sStore = NULL;
|
||||
if (!ok) {
|
||||
dvxLog("Assoc: cannot save the file types (%s)", sUserPath ? sUserPath : DVX_ASSOC_NAME);
|
||||
assocDrop();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static PrefsHandleT *assocStore(void) {
|
||||
if (!sStore) {
|
||||
sStore = prefsLoad(sStorePath);
|
||||
prefsClose(sView);
|
||||
sView = NULL;
|
||||
return true;
|
||||
}
|
||||
|
||||
return sStore;
|
||||
|
||||
// The view: every shipped type the user did not change, then the user's
|
||||
// own (an empty value removes a type).
|
||||
static PrefsHandleT *assocStore(void) {
|
||||
char userPath[DVX_MAX_PATH];
|
||||
|
||||
if (!sShipped) {
|
||||
sShipped = prefsLoad(sShippedPath ? sShippedPath : SHELL_ASSOC_SHIPPED_PATH);
|
||||
}
|
||||
|
||||
// (a user folder path too long for the name: no view)
|
||||
if (!sUser && sUserPath) {
|
||||
sUser = prefsLoad(sUserPath);
|
||||
} else if (!sUser && platformUserPath(userPath, sizeof(userPath), DVX_ASSOC_NAME)) {
|
||||
sUser = prefsLoad(userPath);
|
||||
}
|
||||
|
||||
// Made already, or a file could not be read
|
||||
if (sView || !sShipped || !sUser) {
|
||||
return sView;
|
||||
}
|
||||
|
||||
sView = prefsCreate();
|
||||
|
||||
if (!sView) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
const char *key;
|
||||
|
||||
for (int32_t i = 0; (key = prefsKeyName(sShipped, SHELL_ASSOC_SECTION, i)) != NULL; i++) {
|
||||
if (!prefsGetString(sUser, SHELL_ASSOC_SECTION, key, NULL)) {
|
||||
prefsSetString(sView, SHELL_ASSOC_SECTION, key, prefsGetString(sShipped, SHELL_ASSOC_SECTION, key, ""));
|
||||
}
|
||||
}
|
||||
|
||||
for (int32_t i = 0; (key = prefsKeyName(sUser, SHELL_ASSOC_SECTION, i)) != NULL; i++) {
|
||||
const char *value = prefsGetString(sUser, SHELL_ASSOC_SECTION, key, "");
|
||||
|
||||
if (value[0]) {
|
||||
prefsSetString(sView, SHELL_ASSOC_SECTION, key, value);
|
||||
}
|
||||
}
|
||||
|
||||
return sView;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -239,37 +298,53 @@ bool shellAssocOpenDocument(AppContextT *ctx, const char *documentPath) {
|
|||
}
|
||||
|
||||
|
||||
// A shipped type is removed with an empty value in the user's file; one
|
||||
// only the user had is simply dropped from it.
|
||||
bool shellAssocRemove(const char *extension) {
|
||||
char ext[SHELL_ASSOC_EXT_MAX];
|
||||
PrefsHandleT *store = assocStore();
|
||||
|
||||
if (!shellAssocNormalize(extension, ext) || !prefsGetString(store, SHELL_ASSOC_SECTION, ext, NULL)) {
|
||||
if (!store || !shellAssocNormalize(extension, ext) || !prefsGetString(store, SHELL_ASSOC_SECTION, ext, NULL)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
prefsRemove(store, SHELL_ASSOC_SECTION, ext);
|
||||
if (prefsGetString(sShipped, SHELL_ASSOC_SECTION, ext, NULL)) {
|
||||
prefsSetString(sUser, SHELL_ASSOC_SECTION, ext, "");
|
||||
} else {
|
||||
prefsRemove(sUser, SHELL_ASSOC_SECTION, ext);
|
||||
}
|
||||
|
||||
return assocSave();
|
||||
}
|
||||
|
||||
|
||||
// The same program as the shipped list's leaves the user's file without
|
||||
// the type (so a later shipped change reaches it).
|
||||
bool shellAssocSet(const char *extension, const char *programPath) {
|
||||
char ext[SHELL_ASSOC_EXT_MAX];
|
||||
const char *value = dvxSkipWs(programPath);
|
||||
|
||||
if (!shellAssocNormalize(extension, ext) || !value || !value[0] || value[0] == SHELL_ASSOC_ARGS_SEP) {
|
||||
if (!assocStore() || !shellAssocNormalize(extension, ext) || !value || !value[0] || value[0] == SHELL_ASSOC_ARGS_SEP) {
|
||||
return false;
|
||||
}
|
||||
|
||||
prefsSetString(assocStore(), SHELL_ASSOC_SECTION, ext, value);
|
||||
const char *shipped = prefsGetString(sShipped, SHELL_ASSOC_SECTION, ext, NULL);
|
||||
|
||||
if (shipped && strcmp(shipped, value) == 0) {
|
||||
prefsRemove(sUser, SHELL_ASSOC_SECTION, ext);
|
||||
} else {
|
||||
prefsSetString(sUser, SHELL_ASSOC_SECTION, ext, value);
|
||||
}
|
||||
|
||||
return assocSave();
|
||||
}
|
||||
|
||||
|
||||
#ifdef DVX_HOST_TEST
|
||||
void shellAssocTestReset(const char *storePath, ShellAssocLaunchFnT launcher) {
|
||||
prefsClose(sStore);
|
||||
sStore = NULL;
|
||||
sStorePath = storePath ? storePath : SHELL_ASSOC_INI_PATH;
|
||||
void shellAssocTestReset(const char *shippedPath, const char *userPath, ShellAssocLaunchFnT launcher) {
|
||||
assocDrop();
|
||||
sShippedPath = shippedPath;
|
||||
sUserPath = userPath;
|
||||
sLaunch = launcher ? launcher : shellLoadAppWithArgs;
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -23,7 +23,10 @@
|
|||
// shellAssoc.h -- File associations (document type -> program)
|
||||
//
|
||||
// One store for every program that opens documents (Program Manager,
|
||||
// File Manager, Control Panel): CONFIG/ASSOC.INI, section [associations].
|
||||
// File Manager, Control Panel), section [associations] of two files: the
|
||||
// list DVX ships (SYSTEM\ASSOC.INI, replaced by upgrades) and the user's
|
||||
// changes to it (ASSOC.INI in the user folder: a type there replaces the
|
||||
// shipped one; an empty value removes it). Readers see the two together.
|
||||
// Each key is a file extension without the dot, in upper case; each value
|
||||
// is a program path relative to the DVX root, optionally followed by
|
||||
// ",args" where %1 stands for the document path:
|
||||
|
|
@ -43,8 +46,9 @@
|
|||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
// The association store, relative to the DVX root.
|
||||
#define SHELL_ASSOC_INI_PATH "CONFIG" DVX_PATH_SEP "ASSOC.INI"
|
||||
// The shipped list, relative to the DVX root (the user's changes are
|
||||
// DVX_ASSOC_NAME in the user folder)
|
||||
#define SHELL_ASSOC_SHIPPED_PATH DVX_SYSTEM_DIR DVX_PATH_SEP DVX_ASSOC_NAME
|
||||
|
||||
// Its one section.
|
||||
#define SHELL_ASSOC_SECTION "associations"
|
||||
|
|
@ -87,19 +91,20 @@ bool shellAssocOpenDocument(AppContextT *ctx, const char *documentPath);
|
|||
// eight characters, or one with anything but letters and digits.
|
||||
bool shellAssocNormalize(const char *extension, char *out);
|
||||
|
||||
// Remove an association and save. False when it did not exist or the
|
||||
// save failed.
|
||||
// Remove an association (in the user's file) and save. False when it did
|
||||
// not exist or the save failed.
|
||||
bool shellAssocRemove(const char *extension);
|
||||
|
||||
// Add or replace an association and save. programPath may carry a ",args"
|
||||
// template. False on a bad extension or a failed save.
|
||||
// Add or replace an association (in the user's file) and save.
|
||||
// programPath may carry a ",args" template. False on a bad extension or a
|
||||
// failed save.
|
||||
bool shellAssocSet(const char *extension, const char *programPath);
|
||||
|
||||
// Test seam (host harness only, DVX_HOST_TEST): use another store file
|
||||
// and launcher (NULL = the real CONFIG/ASSOC.INI and shellLoadAppWithArgs)
|
||||
// and drop the cached copy of the store.
|
||||
// Test seam (host harness only, DVX_HOST_TEST): use other files for the
|
||||
// shipped list and the user's changes, and another launcher (NULL = the
|
||||
// real files and shellLoadAppWithArgs), and drop the cached copies.
|
||||
#ifdef DVX_HOST_TEST
|
||||
void shellAssocTestReset(const char *storePath, ShellAssocLaunchFnT launcher);
|
||||
void shellAssocTestReset(const char *shippedPath, const char *userPath, ShellAssocLaunchFnT launcher);
|
||||
#endif
|
||||
|
||||
#endif // SHELL_ASSOC_H
|
||||
|
|
|
|||
|
|
@ -122,7 +122,6 @@ const char *ShellAssocValue(int32_t index);
|
|||
int32_t ShellChangeCount(void);
|
||||
const char *ShellCommandArgs(const char *command);
|
||||
const char *ShellCommandProgram(const char *command);
|
||||
const char *ShellConfigFile(const char *fileName);
|
||||
void ShellExitDvx(void);
|
||||
int32_t ShellInstanceCount(void);
|
||||
int32_t ShellIsProgram(const char *path);
|
||||
|
|
@ -370,7 +369,7 @@ static ShellAppT *runningApp(int32_t appId) {
|
|||
// DVX.INI [shell] key = program; "" removes the key. True when DVX.INI
|
||||
// was saved (a failure is logged).
|
||||
static int32_t setShellKey(const char *key, const char *program) {
|
||||
PrefsHandleT *ini = prefsLoad(DVX_INI_PATH);
|
||||
PrefsHandleT *ini = prefsLoad(platformDvxIniPath());
|
||||
|
||||
if (!ini) {
|
||||
return BAS_FALSE;
|
||||
|
|
@ -385,7 +384,7 @@ static int32_t setShellKey(const char *key, const char *program) {
|
|||
bool saved = prefsSave(ini);
|
||||
|
||||
if (!saved) {
|
||||
dvxLog("Shell: could not save %s", DVX_INI_PATH);
|
||||
dvxLog("Shell: could not save %s", platformDvxIniPath());
|
||||
}
|
||||
|
||||
prefsClose(ini);
|
||||
|
|
@ -560,22 +559,6 @@ const char *ShellCommandProgram(const char *command) {
|
|||
}
|
||||
|
||||
|
||||
// The full path of fileName in this program's settings folder (DVX's
|
||||
// CONFIG folder, in a folder named after the program's path), which is
|
||||
// made when it does not exist yet; "" when it cannot be made.
|
||||
const char *ShellConfigFile(const char *fileName) {
|
||||
static char path[DVX_MAX_PATH];
|
||||
const ShellAppT *app = currentApp();
|
||||
|
||||
if (!app || !app->dxeCtx || shellEnsureConfigDir(app->dxeCtx) != 0) {
|
||||
return "";
|
||||
}
|
||||
|
||||
shellConfigPath(app->dxeCtx, orEmpty(fileName), path, sizeof(path));
|
||||
return path;
|
||||
}
|
||||
|
||||
|
||||
void ShellExitDvx(void) {
|
||||
shellRequestExit();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@
|
|||
DVX_ABI_STAMP(dvxshell);
|
||||
|
||||
// Random-seed file carried across runs for the serial library's RNG
|
||||
#define SHELL_SEED_PATH "CONFIG" DVX_PATH_SEP "SECLINK.RND"
|
||||
#define SHELL_SEED_NAME "SECLINK.RND" // in the user folder
|
||||
|
||||
// Exit DVX: how long an app may take to close a window it was asked to
|
||||
// close before it is reported as not responding, and the frames a main-loop
|
||||
|
|
@ -107,8 +107,9 @@ DVX_ABI_STAMP(dvxshell);
|
|||
#define SHELL_SWITCH_SAFE "/SAFE"
|
||||
#define SHELL_SWITCH_RUN "/RUN="
|
||||
#define SHELL_SAFE_TITLE "Safe Video Mode"
|
||||
#define SHELL_READ_ONLY_MSG "DVX is running from a disk it cannot write to, such as a CD-ROM. Settings you change are kept until you exit DVX, and the help is used as it is. To keep your settings, install DVX on your hard disk with SETUP."
|
||||
#define SHELL_NO_VIDEO_MSG "DVX could not start: no usable graphics screen mode was found (VESA VBE 2.0 or later with a linear frame buffer is needed). The details are in DVX.LOG."
|
||||
#define SHELL_READ_ONLY_MSG "DVX cannot save anything: it is running from a disk it cannot write to, such as a CD-ROM, and there is no TEMP folder. Settings you change are kept until you exit DVX. To keep them, set DVXUSER to a folder on your hard disk before starting DVX, or install DVX with SETUP."
|
||||
#define SHELL_USER_TEMP_MSG "DVX is running from a disk it cannot write to, such as a CD-ROM, so your settings are kept in the TEMP folder. To keep them somewhere else, set DVXUSER to a folder on your hard disk before starting DVX."
|
||||
#define SHELL_NO_VIDEO_MSG "DVX could not start: the video card offered no usable graphics mode. DVX needs a VESA video BIOS with a mode of at least 640x480 at 256 colors or more. Many older cards need their VESA driver loaded first: UniVBE (Scitech Display Doctor) works with most cards, and some cards came with their own, such as S3VBE20 for S3 cards and M64VBE for ATI Mach64 cards. The details are in DVX.LOG in the USER folder."
|
||||
|
||||
// How the GUI was started (videoStart).
|
||||
typedef enum {
|
||||
|
|
@ -193,6 +194,7 @@ static int32_t sDepthBase = 0; // dispatchDepth when that task was switc
|
|||
typedef int (*RngSeedFileFnT)(const char *path);
|
||||
|
||||
static RngSeedFileFnT sRngSaveSeed = NULL;
|
||||
static char sSeedPath[DVX_MAX_PATH]; // SHELL_SEED_NAME in the user folder
|
||||
|
||||
// Set by shellRequestExit, acted on at the top of the main loop.
|
||||
static bool sExitRequested = false;
|
||||
|
|
@ -350,13 +352,14 @@ static void entropyInit(void) {
|
|||
DvxEntropyHookT addEntropy = (DvxEntropyHookT)dlsym(NULL, DVX_SYM("secRngAddEntropy"));
|
||||
|
||||
sRngSaveSeed = (RngSeedFileFnT)dlsym(NULL, DVX_SYM("secRngSaveSeed"));
|
||||
platformUserPath(sSeedPath, sizeof(sSeedPath), SHELL_SEED_NAME);
|
||||
|
||||
if (addEntropy) {
|
||||
dvxSetInputEntropyHook(addEntropy);
|
||||
}
|
||||
|
||||
if (loadSeed && loadSeed(SHELL_SEED_PATH) != SEC_SUCCESS) {
|
||||
dvxLog("Shell: no usable random seed in %s (a new one is written at shutdown)", SHELL_SEED_PATH);
|
||||
if (loadSeed && loadSeed(sSeedPath) != SEC_SUCCESS) {
|
||||
dvxLog("Shell: no usable random seed in %s (a new one is written at shutdown)", sSeedPath);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -365,8 +368,8 @@ static void entropyInit(void) {
|
|||
static void entropyShutdown(void) {
|
||||
dvxSetInputEntropyHook(NULL);
|
||||
|
||||
if (sRngSaveSeed && sRngSaveSeed(SHELL_SEED_PATH) != SEC_SUCCESS) {
|
||||
dvxLog("Shell: failed to save random seed to %s", SHELL_SEED_PATH);
|
||||
if (sRngSaveSeed && sRngSaveSeed(sSeedPath) != SEC_SUCCESS) {
|
||||
dvxLog("Shell: failed to save random seed to %s", sSeedPath);
|
||||
}
|
||||
|
||||
sRngSaveSeed = NULL;
|
||||
|
|
@ -703,7 +706,7 @@ int shellMain(int argc, char *argv[]) {
|
|||
dvxLog("DVX Shell starting...");
|
||||
|
||||
// Load preferences (missing file or keys silently use defaults)
|
||||
sPrefs = prefsLoad(DVX_INI_PATH);
|
||||
sPrefs = prefsLoad(platformDvxIniPath());
|
||||
|
||||
// Initialize task system (no GUI needed)
|
||||
if (tsInit() != TS_OK) {
|
||||
|
|
@ -821,10 +824,12 @@ int shellMain(int argc, char *argv[]) {
|
|||
|
||||
dvxPlaySound(SoundStartE);
|
||||
|
||||
// Read-only media (the loader found DVX's folder unwritable): said
|
||||
// once, before the other start-up windows.
|
||||
// Nowhere to save, or settings only in TEMP (the loader's
|
||||
// platformUserInit): said once, before the other start-up windows.
|
||||
if (platformIsReadOnly()) {
|
||||
dvxInfoBox(&sCtx, NULL, SHELL_READ_ONLY_MSG);
|
||||
} else if (platformUserInTemp()) {
|
||||
dvxInfoBox(&sCtx, NULL, SHELL_USER_TEMP_MSG);
|
||||
}
|
||||
|
||||
// Then the Welcome window while it is wanted, and a /RUN= program.
|
||||
|
|
@ -1258,7 +1263,7 @@ static void videoOfferKeep(VideoStartE how, int32_t savedW, int32_t savedH, int3
|
|||
|
||||
|
||||
// Start the GUI in the saved mode, or in safe mode (the built-in default
|
||||
// mode, which every VBE 2.0 card offers) when it was asked for or the saved
|
||||
// mode, which every VESA card offers) when it was asked for or the saved
|
||||
// mode cannot be set.
|
||||
static VideoStartE videoStart(bool safeAsked, int32_t savedW, int32_t savedH, int32_t savedBpp) {
|
||||
if (safeAsked) {
|
||||
|
|
|
|||
|
|
@ -342,6 +342,16 @@ const char *FileOpsJobMessage(int32_t job);
|
|||
int32_t FileOpsJobPercent(int32_t job);
|
||||
int32_t FileOpsJobRunFor(int32_t job, int32_t milliseconds);
|
||||
|
||||
// The DVX folders of a kind of file (dvxPlat.h DvxKindE, BASIC
|
||||
// FILEOPS_KIND_*): platformKindBrowseDir, platformKindDir,
|
||||
// platformKindFind, platformKindNameOf, and platformKindReadDir as a
|
||||
// listing handle.
|
||||
const char *FileOpsKindBrowseDir(int32_t kind);
|
||||
const char *FileOpsKindDir(int32_t kind, int32_t user);
|
||||
const char *FileOpsKindFind(int32_t kind, const char *name);
|
||||
int32_t FileOpsKindList(int32_t kind);
|
||||
const char *FileOpsKindName(int32_t kind, const char *path);
|
||||
|
||||
// Listings: read a folder (sorted by name, folders first), sort it, read
|
||||
// its entries, or all of them as ListView SetRows text (name, size, type,
|
||||
// date per row).
|
||||
|
|
@ -415,6 +425,11 @@ int32_t FileOpsTreeFiles(void);
|
|||
int32_t FileOpsTreeFolders(void);
|
||||
int32_t FileOpsTreeSize(const char *path);
|
||||
|
||||
// The user folder: platformUserDir, platformUserInTemp, platformUserPath.
|
||||
const char *FileOpsUserDir(void);
|
||||
int32_t FileOpsUserInTemp(void);
|
||||
const char *FileOpsUserPath(const char *rel);
|
||||
|
||||
// FileOpsValidName: a valid name for a new file or folder in folder
|
||||
// (fileOpsValidateName); ValidLongName / ValidShortName: the long-name or
|
||||
// 8.3 rules alone (platformValidateLongName / ShortName), whatever the
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Reference in a new issue