Skip to content

Console, scripts & GUI

This page shows the three shapes a d/BASIC program can take: a console program that talks to you at a prompt, a script that quietly does a job for another program, and a graphical application that opens a window and stays alive for as long as you need it. You will see a small example of each, learn how one compiled file can be both a console program and a window, and learn which shape to choose for which job.

These are different program shapes, not different versions of the language.

The important split is between the two ways a program can start. d/OS calls these the program's two faces:

  • a console program runs the statements at the top of your file, in order; and
  • a window program defines handlers that the host calls, one short turn at a time, when the window opens, needs drawing, or receives input.

A console tool and a GUI share the same types, procedures, objects, arrays, imports, optimizer, and compiled bytecode. What changes is who leads the conversation: your code, or the host.

Three useful shapes

Shape Who drives? Input Output Lifetime A good fit
interactive console your top-level code prompts, lines, keys PRINT start → converse → finish lessons, small tools, text adventures
console script your top-level code arguments, embedded data, services the host provides stable text and an exit status start → do one job → finish reports, conversions, tests, automation
GUI application the host, calling your handlers events, the same on every host windows, widgets, and intents (named requests such as "fill this rectangle" that the machine carries out however it can) OPEN → event and draw turns → CLOSE editors, dashboards, games, long-lived apps

The console is not just 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 gentle.

The console: a conversation in order

Top-level statements run from top to bottom. INPUT pauses for an answer; PRINT replies; END finishes 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 you can see the flow of control. It is also the right shape whenever a person really does want a prompt.

A script: the same shape without the small talk

A script is a console program written to run with nobody at the keyboard. Skip the prompts, say where the input comes from, keep the output stable, and give the caller an honest result when you finish.

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 the source directly while you are developing, then build it once so every later run is the same:

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 that the program stopped on purpose. There is a third outcome, separate from both: when a capability, a version, or an operation the program needs is not available, the program declines and tells the caller exactly what is missing, instead of disguising the problem as ordinary output.

Command-line arguments and console input come through the host's services, so a desktop shell, the browser, and the d/OS launcher can each supply them in their own honest way. Your script sees plain argument text and ordinary console behaviour, never a borrowed native pointer or a host API object.

Habits that make good scripts

  • do not print a prompt when another program will read the output;
  • keep headings and field names stable;
  • keep calculation and presentation in separate procedures;
  • use embedded DATA for small fixed tables, FILE intents for named files on disk, and DATA intents for database records and their revisions;
  • treat "declined" as a real outcome and handle it; and
  • build a compiled program file, .dbc, when it matters exactly which build runs.

A GUI: state across turns

A graphical application does not sit inside INPUT waiting for the next click. The host calls OPEN, then DRAW, then your input and lifecycle handlers as things happen, and finally CLOSE. Your program's state lives on between those calls.

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 state; the draw handler paints it. Neither one polls a native event queue. That is why the same source can run on a browser page, in a Windows, Linux or macOS window, or on a d/OS screen.

A menu is not a second event system. It is a surface your application owns, and choosing an item produces the same stable command that a toolbar button or a keyboard shortcut would. WorksMenu, from the d/Works widget library, gives you one bounded menu pane; WorksMenuCascade connects a root menu and up to three child panes without allocating a new window for every submenu.

The cascade opens to the right when there is room and to the left when there is not, gives the deepest open pane first claim on the pointer, skips disabled rows and separators when you navigate with the keyboard, and refuses to loop back on itself. A check mark or an icon is presentation; the command string is behaviour.

The lesson here is about structure: menus send commands into the same controller as buttons and shortcuts. Build a complete File → Export → PDF/Text cascade in d/Works Widgets, which teaches construction, painting, focus, pointer routing, dismissal, and font roles together.

One compiled file can be both

Which way a program runs is decided by its structure, not by a flag:

  1. no window handlers means a console program;
  2. window handlers mean a window program;
  3. top-level statements plus window handlers mean both; and
  4. whatever launches the program picks the face. There is no second copy of the source and no target flag.

A program that works both ways is useful when one 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

Launched from a console, the program runs the top-level report. Launched as a window, the host calls the window handlers. Both call the same Greeting$ function in the same compiled file.

What should be shared, and what should not

Keep these independent of how the program is launched:

  • records, classes, validation, calculations, and document state;
  • whole-array operations and other data transformations;
  • imported libraries; and
  • operations whose meaning has nothing to do with how they are shown.

Keep these at the edge:

  • prompts and console formatting;
  • focus, hit testing, window layout, and widget routing;
  • the checks the host makes before a program runs, and how the host presents things; and
  • turning a click or a key into an application command.

That separation pays twice: console tests can exercise the model without opening a window, and the GUI can change without rewriting the model.

Choosing a shape

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, layout on screen, and events are part of the experience. Offer both when people deserve an interface and automation deserves a way in.


Next: Source, Names & Types →