Console, scripts & GUI¶
D/BASIC can meet a person at a prompt, quietly do a job for another program, or keep a graphical application alive for hours. Those are different program shapes, not different versions of the language.
The important split is between two launch faces:
- a console face runs the module's top-level statements in sequence; and
- a window face lets the host call lifecycle and input handlers in bounded turns.
A console tool and a GUI can use the same types, procedures, objects, arrays, imports, optimizer, and DBC bytecode. What changes is who owns the conversation.
Three useful shapes¶
| Shape | Who drives? | Input | Output | Lifetime | A good fit |
|---|---|---|---|---|---|
| interactive console | top-level D/BASIC code | prompts, lines, keys | PRINT |
start → converse → finish | lessons, small tools, text adventures |
| console script | top-level D/BASIC code | arguments, embedded data, mounted services | stable text and exit status | start → do one job → finish | reports, conversions, tests, automation |
| GUI application | the host's face dispatcher | normalized events | windows, widgets, semantic Intents | OPEN → event/draw turns → CLOSE |
editors, dashboards, games, long-lived apps |
The console is not merely the beginner mode, and the GUI is not a second dialect. A fifty-line report generator is a real program. So is a two-window document tool. D/BASIC keeps the climb between them gradual.
The console: a conversation in order¶
Top-level statements execute from top to bottom. INPUT can pause for an answer; PRINT replies; END completes successfully.
DEFINT A-Z
PRINT "HOW MANY STARS";
INPUT Count
IF Count < 0 THEN Count = 0
IF Count > 40 THEN Count = 40
PRINT STRING$(Count, "*")
END
This shape is wonderful for learning because control flow is visible. It is also useful whenever a human really does want a prompt.
A script: the same face without the small talk¶
A script is a console program designed to be run non-interactively. Avoid prompts, make input sources explicit, keep output stable, and give the caller an honest completion result.
DEFINT A-Z
DIM Scores(1 TO 5)
DATA 17, 23, 19, 31, 26
Total = 0
FOR I = LBOUND(Scores) TO UBOUND(Scores)
READ Scores(I)
Total = Total + Scores(I)
NEXT I
PRINT "COUNT="; UBOUND(Scores) - LBOUND(Scores) + 1
PRINT "TOTAL="; Total
PRINT "MEAN="; Total / 5
END
Run source directly while developing, then build it once for repeatable delivery:
dbasic run score-report.bas
dbasic build score-report.bas -o score-report.dbc
dbasic exec score-report.dbc
END reports normal completion. STOP reports an intentional stopped status. A governed refusal is separate from both: it tells the caller that a required capability, version, or operation was unavailable rather than disguising that condition as ordinary output.
Process arguments and console reads are Service-backed, so a desktop shell, browser workbench, and D/OS launcher can supply their own honest routes. The script sees canonical argument text and console semantics, never a borrowed native pointer or host API object.
Habits that make good scripts¶
- do not print a prompt when another program will consume the output;
- keep headings and field names stable;
- separate calculation from presentation in procedures;
- use embedded
DATAfor small fixed tables, FILE Intents for named filesystem bytes, and DATA Intents for database identities and revisions; - treat refusal as a real outcome; and
- build a
.dbcwhen the exact compiled artifact matters.
A GUI: state across turns¶
A graphical application does not sit inside INPUT waiting for the next gesture. The host calls OPEN, DRAW, normalized input handlers, lifecycle handlers, and finally CLOSE. Your model survives between those turns.
DIM SHARED Message AS STRING
DIM SHARED Width AS INTEGER
DIM SHARED Height AS INTEGER
WINDOW SUB OPEN()
Message = "PRESS ENTER"
Width = 320
Height = 192
SERVICE SET_TITLE("A tiny GUI")
SERVICE INVALIDATE()
END SUB
ON KEY SUB (K AS INTEGER)
IF K = 13 THEN Message = "HELLO FROM THE EVENT LOOP"
SERVICE INVALIDATE()
END SUB
WINDOW SUB DRAW()
INTENT GFX.BEGIN_FRAME()
INTENT GFX.FILL_RECT(0, 0, Width, Height, 2081)
INTENT GFX.DRAW_TEXT(12, 14, Message, 3, -1, 2081)
INTENT GFX.PRESENT()
END SUB
WINDOW SUB RESIZE(W AS INTEGER, H AS INTEGER)
Width = W
Height = H
SERVICE INVALIDATE()
END SUB
WINDOW SUB CLOSE()
END SUB
The key handler changes the model; the draw handler projects that model. Neither polls a native event queue. That is why the same source can inhabit a browser surface, a Windows/Linux/macOS window, or a D/OS composed head.
Menus and cascades: one command model¶
A menu is not a second event system. It is an application-owned surface that turns a choice into the same stable command your toolbar button or keyboard shortcut would publish. WorksMenu provides one bounded pane; WorksMenuCascade connects a root and up to three child panes without allocating a new window for every submenu.
The cascade chooses right-opening or left-opening placement from available space, gives the deepest pane pointer precedence, skips disabled and separator rows during keyboard navigation, and rejects cycles. A checked mark or glyph is presentation; the stable command string is behavior.
This chapter's lesson is architectural: menus publish commands into the same controller as buttons and shortcuts. Build a complete File → Export → PDF/Text cascade in D/Works Widgets, where construction, painting, focus, pointer routing, dismissal, and semantic font roles are taught together.
One image can offer both faces¶
Face selection is structural:
- no window lifecycle or input handlers means console;
- window handlers mean windowed;
- top-level executable statements plus window handlers mean both; and
- the launch surface selects a face—there is no source fork or target flag.
A dual-face program is useful when the same engine deserves both a scriptable report and an interactive view:
DECLARE FUNCTION Greeting$ (Name AS STRING)
' Console face
PRINT Greeting$("CONSOLE")
END
FUNCTION Greeting$ (Name AS STRING)
Greeting$ = "HELLO, " + Name
END FUNCTION
' Window face
WINDOW SUB OPEN()
SERVICE SET_TITLE("Dual-face greeting")
SERVICE INVALIDATE()
END SUB
WINDOW SUB DRAW()
INTENT GFX.BEGIN_FRAME()
INTENT GFX.FILL_RECT(0, 0, 320, 192, 2081)
INTENT GFX.DRAW_TEXT(12, 14, Greeting$("WINDOW"), 3, -1, 2081)
INTENT GFX.PRESENT()
END SUB
WINDOW SUB CLOSE()
END SUB
The console launch executes the top-level report. The window launch dispatches the window faces. Both call the same Greeting$ function from the same bytecode image.
What should be shared—and what should not¶
Keep these independent of the face:
- records, classes, validation, calculations, and document state;
- whole-array operations and other data transformations;
- imported libraries; and
- semantic operations whose meaning is not presentation-specific.
Keep these at the edge:
- prompts and console formatting;
- focus, hit testing, window layout, and widget routing;
- host admission and presentation; and
- conversion from a user gesture into an application command.
That separation pays twice: console tests can exercise the model without a window, and the GUI can evolve without rewriting the model.
Choosing a face¶
Choose a console program when a line of output is the product. Choose a script when another program or a repeatable workflow is the audience. Choose a GUI when state, spatial layout, and events are part of the experience. Offer both when people deserve an interface and automation deserves an entry point.
Next: Source, Names & Types →