// 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 #include #include #include #include #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)); }