From 32f8cd792c3a343fdd97b0abc67e0f90b27420a0 Mon Sep 17 00:00:00 2001 From: Emery Hemingway Date: Thu, 30 May 2024 20:51:11 +0300 Subject: [PATCH] Add esc_printer_actor --- README.md | 33 +++++++++++++++ sbom.json | 6 ++- src/esc_printer_actor.nim | 84 +++++++++++++++++++++++++++++++++++++++ src/private/esc_p.nim | 11 +++++ 4 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 src/esc_printer_actor.nim create mode 100644 src/private/esc_p.nim diff --git a/README.md b/README.md index bca8064..f99e3cc 100644 --- a/README.md +++ b/README.md @@ -241,6 +241,39 @@ Examples: --- +## esc_printer_actor + +A basic [ESC/P](https://en.wikipedia.org/wiki/ESC/P) printer controller. + +Takes a path to a printer device file as a command line argument and publishes a `` to its environment. +The capability in this assertion is an entity that prints the strings it receives as messages. +While the `` or `` is asserted to this entity the printer will go into the corresponding font mode (if the printer supports it). + +Sample Syndicate server script: +``` +> + +? [ + $log ?? [ + $printer ! $text + $printer ! "\r\n" + # Print log messages. + ] +] + + +? ?cap> [ + $cap ? [ + $config + ] +] + + +``` + ## http_client The inverse of `http-driver`. diff --git a/sbom.json b/sbom.json index cb4ad9e..6820070 100644 --- a/sbom.json +++ b/sbom.json @@ -7,7 +7,7 @@ "bom-ref": "pkg:nim/syndicate_utils", "name": "syndicate_utils", "description": "Utilites for Syndicated Actors and Synit", - "version": "20240523", + "version": "20240530", "authors": [ { "name": "Emery Hemingway" @@ -41,6 +41,10 @@ "name": "nim:bin:mintsturdyref", "value": "mintsturdyref" }, + { + "name": "nim:bin:esc-printer-actor", + "value": "esc_printer_actor" + }, { "name": "nim:bin:msg", "value": "msg" diff --git a/src/esc_printer_actor.nim b/src/esc_printer_actor.nim new file mode 100644 index 0000000..5d00623 --- /dev/null +++ b/src/esc_printer_actor.nim @@ -0,0 +1,84 @@ +# SPDX-FileCopyrightText: ☭ Emery Hemingway +# SPDX-License-Identifier: Unlicense + +## ESC/P printer control actor. + +import + std/[cmdline, oserrors, posix, sets], + preserves, preserves/sugar, + syndicate, syndicate/relays, + ./private/esc_p + +proc echo(args: varargs[string, `$`]) {.used.} = + stderr.writeLine(args) + +type + HandleSet = HashSet[Handle] + + Printer = ref object of Entity + device: cint + boldHandles, italicHandles, superscriptHandles, subscriptHandles: HandleSet + +proc write(printer: Printer; s: string) {.inline.} = + if posix.write(printer.device, s[0].addr, s.len) < 0: + osLastError().osErrorMsg().quit() + +proc writeLine(printer: Printer; s: string) {.inline.} = + printer.write(s) + printer.write("\r\n") + +method message(printer: Printer; t: Turn; a: AssertionRef) = + if a.value.isString: + printer.write(a.value.string) + # TODO: unicode? + # TODO: line breaks? + +proc assert(printer: Printer; handles: var HandleSet; ctrl: string; h: Handle) = + if handles.len == 0: printer.write(ctrl) + handles.incl h + +proc retract(printer: Printer; handles: var HandleSet; ctrl: string; h: Handle) = + handles.excl h + if handles.len == 0: printer.write(ctrl) + +method publish(printer: Printer; t: Turn; a: AssertionRef; h: Handle) = + if a.value.isRecord("bold"): + printer.assert(printer.boldHandles, SelectBoldFont, h) + + elif a.value.isRecord("italic"): + printer.assert(printer.italicHandles, SelectItalicFont, h) + + elif a.value.isRecord("superscript"): + printer.assert(printer.superscriptHandles, SelectSuperScript, h) + + elif a.value.isRecord("subscript"): + printer.assert(printer.subscriptHandles, SelectSubScript, h) + +method retract(printer: Printer; t: Turn; h: Handle) = + if printer.boldHandles.contains h: + printer.retract(printer.boldHandles, CancelBoldFont, h) + + elif printer.italicHandles.contains h: + printer.retract(printer.italicHandles, CanceItalicFont, h) + + elif printer.superscriptHandles.contains h: + printer.retract(printer.superscriptHandles, CancelAltScript, h) + + elif printer.subscriptHandles.contains h: + printer.retract(printer.subscriptHandles, CancelAltScript, h) + +proc openPrinter(turn: Turn; devicePath: string): Printer = + new result + result.facet = turn.facet + result.device = posix.open(devicePath, O_WRONLY, 0) + result.write(InitializePrinter) + +proc main = + let devicePath = paramStr(1) + runActor(devicePath) do (turn: Turn): + let printer = turn.newCap openPrinter(turn, devicePath) + resolveEnvironment(turn) do (turn: Turn; ds: Cap): + publish(turn, ds, initRecord( + toSymbol"printer", printer.embed, %devicePath)) + +main() diff --git a/src/private/esc_p.nim b/src/private/esc_p.nim new file mode 100644 index 0000000..b14f5d4 --- /dev/null +++ b/src/private/esc_p.nim @@ -0,0 +1,11 @@ +const + ESC* = "\x1b" + InitializePrinter* = ESC & "@" + CancelLine* = ESC & "\x18" + SelectBoldFont* = ESC & "E" + CancelBoldFont* = ESC & "F" + SelectItalicFont* = ESC & "4" + CanceItalicFont* = ESC & "5" + SelectSuperScript* = ESC & "S0" + SelectSubScript* = ESC & "S1" + CancelAltScript* = ESC & "T"