473 lines
16 KiB
C
473 lines
16 KiB
C
// The MIT License (MIT)
|
|
//
|
|
// Copyright (C) 2026 Scott Duensing
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
// of this software and associated documentation files (the "Software"), to
|
|
// deal in the Software without restriction, including without limitation the
|
|
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
// sell copies of the Software, and to permit persons to whom the Software is
|
|
// furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
// IN THE SOFTWARE.
|
|
|
|
// printBas.c -- DVX BASIC entry points of the printing library
|
|
//
|
|
// Called through DECLARE LIBRARY (src/include/basic/print.bas), so the
|
|
// signatures use only BASIC-marshalable types: strings in, LONG out, True
|
|
// (-1) / False (0) for success. The dialogs need the GUI context, which
|
|
// the BASIC runtime hands over with printBasSetContext when it starts a
|
|
// program; without it (a program run without the GUI) nothing prints.
|
|
//
|
|
// LPRINT and OPEN "LPT1:" are handled by the BASIC runtime itself, through
|
|
// printUiOpenPort / printJobWrite / printJobClose.
|
|
//
|
|
// The printer settings functions (PrintModel*, PrintDest*, PrintSetting*,
|
|
// PrintSettingsProblem$, PrintSettingsStore) take a prefs.bas handle, so a
|
|
// program can edit DVX.INI [printer] the way the Control Panel does.
|
|
#include "printInt.h"
|
|
#include "dvxPrefs.h"
|
|
|
|
#include <stddef.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <strings.h>
|
|
|
|
#define BAS_TRUE (-1)
|
|
#define BAS_FALSE 0
|
|
#define BAS_NONE (-1)
|
|
#define BAS_LPT_FIRST 1 // BASIC numbers the printer ports LPT1 = 1
|
|
|
|
// A number field of PrintPageSetupT by name (PrintPageSetting)
|
|
typedef struct {
|
|
const char *name;
|
|
size_t offset;
|
|
} BasPageFieldT;
|
|
|
|
static const BasPageFieldT sPageFields[] = {
|
|
{ "Lines", offsetof(PrintPageSetupT, linesPerPage) },
|
|
{ "Columns", offsetof(PrintPageSetupT, columns) },
|
|
{ "MarginTop", offsetof(PrintPageSetupT, marginTop) },
|
|
{ "MarginBottom", offsetof(PrintPageSetupT, marginBottom) },
|
|
{ "MarginLeft", offsetof(PrintPageSetupT, marginLeft) },
|
|
{ "MarginRight", offsetof(PrintPageSetupT, marginRight) },
|
|
{ "TabWidth", offsetof(PrintPageSetupT, tabWidth) }
|
|
};
|
|
|
|
#define BAS_PAGE_FIELD_COUNT ((int32_t)(sizeof(sPageFields) / sizeof(sPageFields[0])))
|
|
#define BAS_PAGE_WRAP "Wrap"
|
|
#define BAS_PAGE_HEADER "Header"
|
|
#define BAS_PAGE_FOOTER "Footer"
|
|
|
|
static AppContextT *sBasCtx = NULL;
|
|
static PrintChoiceT sChoice; // the last PrintChoose answer
|
|
static char sText[PRINT_MSG_MAX];
|
|
|
|
// ============================================================
|
|
// Prototypes
|
|
// ============================================================
|
|
|
|
static int32_t basBool(bool value);
|
|
static bool destValid(int32_t dest);
|
|
static bool modelValid(int32_t model);
|
|
static void pageSetupFrom(PrintPageSetupT *ps, int32_t lines, int32_t columns, int32_t marginTop, int32_t marginBottom, int32_t marginLeft, int32_t marginRight, int32_t tabWidth, int32_t wrap, const char *header, const char *footer);
|
|
void printBasSetContext(AppContextT *ctx);
|
|
int32_t PrintCanPrintPictures(void);
|
|
int32_t PrintChoiceCopies(void);
|
|
int32_t PrintChoiceDest(void);
|
|
const char *PrintChoiceFile(void);
|
|
int32_t PrintChoiceFirst(void);
|
|
int32_t PrintChoiceLast(void);
|
|
int32_t PrintChoose(const char *title, int32_t pageCount);
|
|
int32_t PrintDestCount(void);
|
|
int32_t PrintDestIsFile(int32_t dest);
|
|
const char *PrintDestLabel(int32_t dest);
|
|
int32_t PrintModelCount(void);
|
|
const char *PrintModelLabel(int32_t model);
|
|
int32_t PrintPageCount(const char *text);
|
|
int32_t PrintPageLines(int32_t prefs, int32_t model);
|
|
int32_t PrintPageSetting(int32_t prefs, const char *field);
|
|
int32_t PrintPageSettingsStore(int32_t prefs, int32_t lines, int32_t columns, int32_t marginTop, int32_t marginBottom, int32_t marginLeft, int32_t marginRight, int32_t tabWidth, int32_t wrap, const char *header, const char *footer);
|
|
int32_t PrintPageSetup(void);
|
|
const char *PrintPageSetupProblem(int32_t lines, int32_t columns, int32_t marginTop, int32_t marginBottom, int32_t marginLeft, int32_t marginRight, int32_t tabWidth, int32_t wrap, const char *header, const char *footer);
|
|
const char *PrintPageText(int32_t prefs, const char *field);
|
|
int32_t PrintPicture(const char *path, const char *title);
|
|
int32_t PrintPictureDialog(const char *path, const char *title);
|
|
int32_t PrintPortPresent(int32_t port);
|
|
int32_t PrintPortStatus(int32_t port);
|
|
int32_t PrintSettingDest(int32_t prefs);
|
|
const char *PrintSettingFile(int32_t prefs);
|
|
int32_t PrintSettingModel(int32_t prefs);
|
|
const char *PrintSettingsProblem(int32_t dest, const char *filePath);
|
|
int32_t PrintSettingsStore(int32_t prefs, int32_t model, int32_t dest, const char *filePath);
|
|
const char *PrintStatusProblem(int32_t status, int32_t afterWrite, const char *where);
|
|
int32_t PrintText(const char *text, const char *title);
|
|
int32_t PrintTextDialog(const char *text, const char *title);
|
|
int32_t PrintTextPages(const char *text, const char *title, int32_t firstPage, int32_t lastPage, int32_t copies);
|
|
|
|
|
|
static int32_t basBool(bool value) {
|
|
return value ? BAS_TRUE : BAS_FALSE;
|
|
}
|
|
|
|
|
|
static bool destValid(int32_t dest) {
|
|
return dest >= 0 && dest < PrintDestCountE;
|
|
}
|
|
|
|
|
|
static bool modelValid(int32_t model) {
|
|
return model >= 0 && model < PrintModelCountE;
|
|
}
|
|
|
|
|
|
// A page setup from PrintPageSettingsStore's arguments (texts too long
|
|
// are cut off).
|
|
static void pageSetupFrom(PrintPageSetupT *ps, int32_t lines, int32_t columns, int32_t marginTop, int32_t marginBottom, int32_t marginLeft, int32_t marginRight, int32_t tabWidth, int32_t wrap, const char *header, const char *footer) {
|
|
memset(ps, 0, sizeof(*ps));
|
|
ps->linesPerPage = lines;
|
|
ps->columns = columns;
|
|
ps->marginTop = marginTop;
|
|
ps->marginBottom = marginBottom;
|
|
ps->marginLeft = marginLeft;
|
|
ps->marginRight = marginRight;
|
|
ps->tabWidth = tabWidth;
|
|
ps->wrap = wrap != 0;
|
|
snprintf(ps->header, sizeof(ps->header), "%s", header ? header : "");
|
|
snprintf(ps->footer, sizeof(ps->footer), "%s", footer ? footer : "");
|
|
}
|
|
|
|
|
|
void printBasSetContext(AppContextT *ctx) {
|
|
sBasCtx = ctx;
|
|
}
|
|
|
|
|
|
// True when the printer model in the settings can print pictures.
|
|
int32_t PrintCanPrintPictures(void) {
|
|
PrintSettingsT s;
|
|
|
|
printSettingsLoad(&s);
|
|
return basBool(printDriver(s.model) != NULL);
|
|
}
|
|
|
|
|
|
// The copies the user chose in the last PrintChoose.
|
|
int32_t PrintChoiceCopies(void) {
|
|
return sChoice.copies;
|
|
}
|
|
|
|
|
|
// Where the user chose to print in the last PrintChoose (a destination
|
|
// number).
|
|
int32_t PrintChoiceDest(void) {
|
|
return sChoice.dest;
|
|
}
|
|
|
|
|
|
// The file name chosen in the last PrintChoose when printing to a file.
|
|
const char *PrintChoiceFile(void) {
|
|
return platformPathNative(sChoice.filePath);
|
|
}
|
|
|
|
|
|
// The page range chosen in the last PrintChoose (0 and 0: every page).
|
|
int32_t PrintChoiceFirst(void) {
|
|
return sChoice.firstPage;
|
|
}
|
|
|
|
|
|
int32_t PrintChoiceLast(void) {
|
|
return sChoice.lastPage;
|
|
}
|
|
|
|
|
|
// The Print dialog alone (printer, copies and, when pageCount is above
|
|
// 0, a page range), for a program that prints its own output; read the
|
|
// answer with the PrintChoice functions. True when the user chose Print.
|
|
int32_t PrintChoose(const char *title, int32_t pageCount) {
|
|
if (!sBasCtx) {
|
|
return BAS_FALSE;
|
|
}
|
|
|
|
return basBool(printDialog(sBasCtx, title, pageCount, &sChoice));
|
|
}
|
|
|
|
|
|
int32_t PrintDestCount(void) {
|
|
return PrintDestCountE;
|
|
}
|
|
|
|
|
|
// True for the destination that prints into a file (it needs a name).
|
|
int32_t PrintDestIsFile(int32_t dest) {
|
|
return basBool(dest == PrintDestFileE);
|
|
}
|
|
|
|
|
|
// "" for a number out of range.
|
|
const char *PrintDestLabel(int32_t dest) {
|
|
return destValid(dest) ? printDestLabel((PrintDestE)dest) : "";
|
|
}
|
|
|
|
|
|
int32_t PrintModelCount(void) {
|
|
return PrintModelCountE;
|
|
}
|
|
|
|
|
|
// "" for a number out of range.
|
|
const char *PrintModelLabel(int32_t model) {
|
|
return modelValid(model) ? printModelLabel((PrintModelE)model) : "";
|
|
}
|
|
|
|
|
|
// Pages text takes with the page setup and printer in the settings.
|
|
int32_t PrintPageCount(const char *text) {
|
|
PrintSettingsT s;
|
|
|
|
printSettingsLoad(&s);
|
|
return printTextPageCount(text ? text : "", text ? (int32_t)strlen(text) : 0, &s.page, s.model);
|
|
}
|
|
|
|
|
|
// Lines per page of the page setup in prefs on printer model (the model's
|
|
// usual page length when the setup says 0); -1 for a model out of range.
|
|
int32_t PrintPageLines(int32_t prefs, int32_t model) {
|
|
PrintSettingsT s;
|
|
|
|
if (!modelValid(model)) {
|
|
return BAS_NONE;
|
|
}
|
|
|
|
printSettingsRead(prefsBasHandle(prefs), &s);
|
|
return printPageLines(&s.page, (PrintModelE)model);
|
|
}
|
|
|
|
|
|
// A number of the page setup in prefs: "Lines" (0 = the printer's usual
|
|
// page length), "Columns", "MarginTop", "MarginBottom", "MarginLeft",
|
|
// "MarginRight", "TabWidth", "Wrap" (True / False); -1 for an unknown
|
|
// field.
|
|
int32_t PrintPageSetting(int32_t prefs, const char *field) {
|
|
PrintSettingsT s;
|
|
|
|
if (!field) {
|
|
return BAS_NONE;
|
|
}
|
|
|
|
printSettingsRead(prefsBasHandle(prefs), &s);
|
|
|
|
if (strcasecmp(field, BAS_PAGE_WRAP) == 0) {
|
|
return basBool(s.page.wrap);
|
|
}
|
|
|
|
for (int32_t i = 0; i < BAS_PAGE_FIELD_COUNT; i++) {
|
|
if (strcasecmp(field, sPageFields[i].name) == 0) {
|
|
return *(const int32_t *)((const char *)&s.page + sPageFields[i].offset);
|
|
}
|
|
}
|
|
|
|
return BAS_NONE;
|
|
}
|
|
|
|
|
|
// Put a page setup into the [printer] settings of prefs (the printer
|
|
// stays as it is there); save prefs afterwards. False for a bad handle
|
|
// or a setup PrintPageSetupProblem$ finds a problem with.
|
|
int32_t PrintPageSettingsStore(int32_t prefs, int32_t lines, int32_t columns, int32_t marginTop, int32_t marginBottom, int32_t marginLeft, int32_t marginRight, int32_t tabWidth, int32_t wrap, const char *header, const char *footer) {
|
|
PrefsHandleT *h = prefsBasHandle(prefs);
|
|
PrintSettingsT s;
|
|
|
|
if (!h) {
|
|
return BAS_FALSE;
|
|
}
|
|
|
|
printSettingsRead(h, &s);
|
|
pageSetupFrom(&s.page, lines, columns, marginTop, marginBottom, marginLeft, marginRight, tabWidth, wrap, header, footer);
|
|
|
|
if (printPageSetupProblem(&s.page)) {
|
|
return BAS_FALSE;
|
|
}
|
|
|
|
printSettingsWrite(h, &s);
|
|
return BAS_TRUE;
|
|
}
|
|
|
|
|
|
int32_t PrintPageSetup(void) {
|
|
return basBool(sBasCtx && printPageSetupDialog(sBasCtx));
|
|
}
|
|
|
|
|
|
// "" when this page setup can be used, else why not.
|
|
const char *PrintPageSetupProblem(int32_t lines, int32_t columns, int32_t marginTop, int32_t marginBottom, int32_t marginLeft, int32_t marginRight, int32_t tabWidth, int32_t wrap, const char *header, const char *footer) {
|
|
PrintPageSetupT ps;
|
|
const char *problem;
|
|
|
|
pageSetupFrom(&ps, lines, columns, marginTop, marginBottom, marginLeft, marginRight, tabWidth, wrap, header, footer);
|
|
problem = printPageSetupProblem(&ps);
|
|
return problem ? problem : "";
|
|
}
|
|
|
|
|
|
// A text of the page setup in prefs: "Header" or "Footer"; "" for an
|
|
// unknown field.
|
|
const char *PrintPageText(int32_t prefs, const char *field) {
|
|
PrintSettingsT s;
|
|
|
|
sText[0] = '\0';
|
|
|
|
if (!field) {
|
|
return sText;
|
|
}
|
|
|
|
printSettingsRead(prefsBasHandle(prefs), &s);
|
|
|
|
if (strcasecmp(field, BAS_PAGE_HEADER) == 0) {
|
|
snprintf(sText, sizeof(sText), "%s", s.page.header);
|
|
} else if (strcasecmp(field, BAS_PAGE_FOOTER) == 0) {
|
|
snprintf(sText, sizeof(sText), "%s", s.page.footer);
|
|
}
|
|
|
|
return sText;
|
|
}
|
|
|
|
|
|
int32_t PrintPicture(const char *path, const char *title) {
|
|
return basBool(sBasCtx && printUiImageFile(sBasCtx, path, title, false));
|
|
}
|
|
|
|
|
|
int32_t PrintPictureDialog(const char *path, const char *title) {
|
|
return basBool(sBasCtx && printUiImageFile(sBasCtx, path, title, true));
|
|
}
|
|
|
|
|
|
// True when printer port LPTn (1-3) exists.
|
|
int32_t PrintPortPresent(int32_t port) {
|
|
return basBool(port >= BAS_LPT_FIRST && port < BAS_LPT_FIRST + PRINT_PORT_COUNT && printPortPresent(port - BAS_LPT_FIRST));
|
|
}
|
|
|
|
|
|
// The PRINT_STATUS_ bits of printer port LPTn (1-3); 0 for a port out of
|
|
// range.
|
|
int32_t PrintPortStatus(int32_t port) {
|
|
if (port < BAS_LPT_FIRST || port >= BAS_LPT_FIRST + PRINT_PORT_COUNT) {
|
|
return 0;
|
|
}
|
|
|
|
return printPortStatus(port - BAS_LPT_FIRST);
|
|
}
|
|
|
|
|
|
// The destination in the [printer] settings of a prefs.bas handle
|
|
// (the defaults for 0).
|
|
int32_t PrintSettingDest(int32_t prefs) {
|
|
PrintSettingsT s;
|
|
|
|
printSettingsRead(prefsBasHandle(prefs), &s);
|
|
return s.dest;
|
|
}
|
|
|
|
|
|
// The file printed into when the destination is a file.
|
|
const char *PrintSettingFile(int32_t prefs) {
|
|
static char path[PRINT_PATH_MAX];
|
|
PrintSettingsT s;
|
|
|
|
printSettingsRead(prefsBasHandle(prefs), &s);
|
|
snprintf(path, sizeof(path), "%s", s.filePath);
|
|
return path;
|
|
}
|
|
|
|
|
|
int32_t PrintSettingModel(int32_t prefs) {
|
|
PrintSettingsT s;
|
|
|
|
printSettingsRead(prefsBasHandle(prefs), &s);
|
|
return s.model;
|
|
}
|
|
|
|
|
|
// "" when the choice can be used, else why not.
|
|
const char *PrintSettingsProblem(int32_t dest, const char *filePath) {
|
|
const char *problem;
|
|
|
|
if (!destValid(dest)) {
|
|
return "Choose where the printer is connected.";
|
|
}
|
|
|
|
problem = printDeviceProblem((PrintDestE)dest, filePath);
|
|
return problem ? problem : "";
|
|
}
|
|
|
|
|
|
// Store model, destination and file into the [printer] settings of a
|
|
// prefs.bas handle (the page setup as it is in DVX.INI now). False for
|
|
// a bad handle or a number out of range.
|
|
int32_t PrintSettingsStore(int32_t prefs, int32_t model, int32_t dest, const char *filePath) {
|
|
PrefsHandleT *h = prefsBasHandle(prefs);
|
|
|
|
if (!h || !modelValid(model) || !destValid(dest)) {
|
|
return BAS_FALSE;
|
|
}
|
|
|
|
printDeviceStore(h, (PrintModelE)model, (PrintDestE)dest, filePath);
|
|
return BAS_TRUE;
|
|
}
|
|
|
|
|
|
// What is wrong with a printer whose status (PrintPortStatus) is status
|
|
// -- after writing a byte when afterWrite is True -- for printer where
|
|
// ("LPT1"); "" when nothing is.
|
|
const char *PrintStatusProblem(int32_t status, int32_t afterWrite, const char *where) {
|
|
sText[0] = '\0';
|
|
|
|
if (!printStatusProblem((uint8_t)status, afterWrite != 0, where ? where : "", sText, sizeof(sText))) {
|
|
sText[0] = '\0';
|
|
}
|
|
|
|
return sText;
|
|
}
|
|
|
|
|
|
int32_t PrintText(const char *text, const char *title) {
|
|
return basBool(sBasCtx && printUiText(sBasCtx, text, title, false));
|
|
}
|
|
|
|
|
|
int32_t PrintTextDialog(const char *text, const char *title) {
|
|
return basBool(sBasCtx && printUiText(sBasCtx, text, title, true));
|
|
}
|
|
|
|
|
|
// Print text without asking first on the printer in the settings: pages
|
|
// firstPage to lastPage (0 and 0: every page), copies times.
|
|
int32_t PrintTextPages(const char *text, const char *title, int32_t firstPage, int32_t lastPage, int32_t copies) {
|
|
PrintSettingsT s;
|
|
PrintChoiceT ch;
|
|
|
|
if (!sBasCtx) {
|
|
return BAS_FALSE;
|
|
}
|
|
|
|
printSettingsLoad(&s);
|
|
memset(&ch, 0, sizeof(ch));
|
|
ch.dest = s.dest;
|
|
ch.firstPage = firstPage;
|
|
ch.lastPage = lastPage;
|
|
ch.copies = copies;
|
|
snprintf(ch.filePath, sizeof(ch.filePath), "%s", s.filePath);
|
|
return basBool(printUiTextChoice(sBasCtx, text, title, &ch));
|
|
}
|