Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
9d6b9c5
Cross-platform port to Avalonia 12 + .NET 10 (barcodes/QR, serial, ex…
danielmeza Jun 5, 2026
97ada87
Fix macOS "damaged" error: ad-hoc code-sign the .app bundle (#2)
danielmeza Jun 5, 2026
bc6d3da
Fix Test print/Hex Dump in packaged app: resolve test_receipt.txt via…
danielmeza Jun 5, 2026
b988820
Give buzzer / cash-drawer real cross-platform feedback (sound + on-sc…
danielmeza Jun 5, 2026
8ffbcad
Add printer state model, bidirectional transport, status commands + s…
danielmeza Jun 6, 2026
d917bae
Add manufacturer buzzer, config no-ops, and PDF417/DataMatrix/Aztec 2…
danielmeza Jun 6, 2026
9b75cca
Add bit-image commands: ESC * inline raster, GS * define + GS / print
danielmeza Jun 6, 2026
3abe2cc
Add page mode (ESC L/S, FF, CAN) and positioning command parsing
danielmeza Jun 6, 2026
efe492a
Add code-page tables (ESC t) and user-defined characters (ESC & / % / ?)
danielmeza Jun 6, 2026
b6d09d9
Update README for status/state panel, page mode, 2D, bit-image, code …
danielmeza Jun 6, 2026
77557ed
Comprehensive test print: all text styles, barcodes, 2D symbols, bit …
danielmeza Jun 6, 2026
e812b13
Add Monitor window: ESC-POS-.NET client to exercise the emulator
danielmeza Jun 6, 2026
b4f0727
Printer reacts to ready-state; richer monitor status indicators
danielmeza Jun 6, 2026
bb1900a
Gate line feed when not ready so blocked prints leave no blank receipts
danielmeza Jun 6, 2026
65d73dd
Re-arm print-blocked notification per job so every blocked print noti…
danielmeza Jun 6, 2026
8d7e110
Treat warnings (incl. NuGet audit) as errors; fix all warnings; de-ma…
danielmeza Jun 6, 2026
c1ddc88
Toast stays longer (re-armable timer); move test responsibility to th…
danielmeza Jun 6, 2026
084f15f
Showcase the Monitor window in the README with screenshots
danielmeza Jun 6, 2026
bb68523
README: list ESC-POS-.NET (Monitor test client) in Built with
danielmeza Jun 6, 2026
ca4d767
Monitor can connect over serial as well as TCP
danielmeza Jun 6, 2026
765767a
Monitor: add direct USB printing (libusb) as a third transport
danielmeza Jun 6, 2026
fe8e6ae
Fix 'Unable to load libusb-1.0' by resolving the native lib at runtime
danielmeza Jun 6, 2026
6b72bbb
Monitor: clear libusb-not-found log with install steps; copyable acti…
danielmeza Jun 6, 2026
e8a9055
Fix libusb discovery: augment NATIVE_DLL_SEARCH_DIRECTORIES instead o…
danielmeza Jun 6, 2026
8206738
Monitor USB: bidirectional transport with status (UsbPrinter : BasePr…
danielmeza Jun 6, 2026
9fb355b
Comprehensive test print: all text styles, barcodes, 2D symbols, bit …
danielmeza Jun 6, 2026
a2d7484
Add Monitor window: ESC-POS-.NET client to exercise the emulator
danielmeza Jun 6, 2026
4723f18
Printer reacts to ready-state; richer monitor status indicators
danielmeza Jun 6, 2026
64cf58c
Gate line feed when not ready so blocked prints leave no blank receipts
danielmeza Jun 6, 2026
3a81f2a
Re-arm print-blocked notification per job so every blocked print noti…
danielmeza Jun 6, 2026
d69caf4
Treat warnings (incl. NuGet audit) as errors; fix all warnings; de-ma…
danielmeza Jun 6, 2026
77e3a4e
Toast stays longer (re-armable timer); move test responsibility to th…
danielmeza Jun 6, 2026
84d23d2
Showcase the Monitor window in the README with screenshots
danielmeza Jun 6, 2026
328590e
README: list ESC-POS-.NET (Monitor test client) in Built with
danielmeza Jun 6, 2026
a141911
Monitor can connect over serial as well as TCP
danielmeza Jun 6, 2026
7d8aefb
Monitor: add direct USB printing (libusb) as a third transport
danielmeza Jun 6, 2026
f527f5f
Fix 'Unable to load libusb-1.0' by resolving the native lib at runtime
danielmeza Jun 6, 2026
97e0594
Monitor: clear libusb-not-found log with install steps; copyable acti…
danielmeza Jun 6, 2026
a26eaa9
Fix libusb discovery: augment NATIVE_DLL_SEARCH_DIRECTORIES instead o…
danielmeza Jun 6, 2026
97d30ba
Monitor USB: bidirectional transport with status (UsbPrinter : BasePr…
danielmeza Jun 6, 2026
dc582ec
Fix app crash on USB disconnect; log unhandled exceptions; lock trans…
danielmeza Jun 6, 2026
1dbcdd5
Merge branch 'main' into fix/usb-disconnect-crash
danielmeza Jun 6, 2026
009f118
Merge pull request #7 from danielmeza/fix/usb-disconnect-crash
danielmeza Jun 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: CI

on:
push:
branches: [main, migrate-to-avalonia]
pull_request:
branches: [main]

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-dotnet@v4
with:
global-json-file: global.json
- name: Restore
run: dotnet restore
- name: Build
run: dotnet build -c Release --no-restore
93 changes: 93 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
name: Release

# Builds self-contained apps for Windows, Linux and macOS and publishes them to a GitHub Release.
# Trigger by pushing a tag like `v1.2.3`, or run manually (workflow_dispatch) to just build artifacts.

on:
push:
tags: ['v*']
workflow_dispatch:

permissions:
contents: write

jobs:
build:
name: Build ${{ matrix.rid }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
include:
- { rid: win-x64, os: ubuntu-latest, kind: zip }
- { rid: linux-x64, os: ubuntu-latest, kind: targz }
- { rid: osx-x64, os: macos-latest, kind: app }
- { rid: osx-arm64, os: macos-latest, kind: app }
steps:
- uses: actions/checkout@v4

- uses: actions/setup-dotnet@v4
with:
global-json-file: global.json

- name: Determine version
id: ver
shell: bash
run: |
VER=1.0.0
if [ "$GITHUB_REF_TYPE" = "tag" ]; then VER="${GITHUB_REF_NAME#v}"; fi
echo "version=$VER" >> "$GITHUB_OUTPUT"

- name: Publish (self-contained)
run: >
dotnet publish ReceiptPrinterEmulator.csproj
-c Release
-r ${{ matrix.rid }}
--self-contained true
-p:PublishSingleFile=false
-p:Version=${{ steps.ver.outputs.version }}
-o publish/${{ matrix.rid }}

- name: Package — zip (Windows)
if: matrix.kind == 'zip'
run: |
cd publish/${{ matrix.rid }}
zip -r ../../ReceiptPrinterEmulator-${{ matrix.rid }}.zip .

- name: Package — tar.gz (Linux)
if: matrix.kind == 'targz'
run: tar -czf ReceiptPrinterEmulator-${{ matrix.rid }}.tar.gz -C publish/${{ matrix.rid }} .

- name: Package — .app bundle (macOS)
if: matrix.kind == 'app'
run: |
chmod +x scripts/make-macos-app.sh
scripts/make-macos-app.sh publish/${{ matrix.rid }} dist "${{ steps.ver.outputs.version }}"
ditto -c -k --keepParent dist/ReceiptPrinterEmulator.app ReceiptPrinterEmulator-${{ matrix.rid }}.zip

- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.rid }}
path: |
ReceiptPrinterEmulator-${{ matrix.rid }}.zip
ReceiptPrinterEmulator-${{ matrix.rid }}.tar.gz
if-no-files-found: ignore

release:
name: Create GitHub Release
needs: build
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/')
steps:
- uses: actions/download-artifact@v4
with:
path: artifacts

- name: List artifacts
run: find artifacts -type f

- uses: softprops/action-gh-release@v2
with:
files: artifacts/**/*
generate_release_notes: true
fail_on_unmatched_files: false
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,8 @@ riderModule.iml
/_ReSharper.Caches/
/.vs
/.idea/

# Runtime debug dumps written by ReceiptPrinter.FeedEscPos
last_escpos_receive.txt
last_ticket.bin
last_*.txt
10 changes: 10 additions & 0 deletions App.axaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="ReceiptPrinterEmulator.App"
RequestedThemeVariant="Dark">

<Application.Styles>
<FluentTheme />
</Application.Styles>

</Application>
61 changes: 61 additions & 0 deletions App.axaml.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using System;
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Markup.Xaml;
using Avalonia.Threading;
using ReceiptPrinterEmulator.Emulator;
using ReceiptPrinterEmulator.Services;
using ReceiptPrinterEmulator.ViewModels;
using ReceiptPrinterEmulator.Views;

namespace ReceiptPrinterEmulator;

public partial class App : Application
{
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}

public override void OnFrameworkInitializationCompleted()
{
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
var printer = new ReceiptPrinter(PaperConfiguration.Default);
// Marshal off-thread state mutations (e.g. a drawer kick over TCP) to the UI thread.
printer.UiDispatch = a => Dispatcher.UIThread.Post(a);
var notifications = new NotificationService();
var dialogs = new FileDialogService();

// Initial transport settings come from environment variables; the UI can change them at
// runtime. ESCPOS_TCP_PORT ("off"/"0" disables auto-start), ESCPOS_SERIAL_PORT / _BAUD.
var (tcpEnabled, tcpPort) = ReadTcpSettings();
var serialPort = Environment.GetEnvironmentVariable("ESCPOS_SERIAL_PORT");
int serialBaud = int.TryParse(Environment.GetEnvironmentVariable("ESCPOS_SERIAL_BAUD"), out var b) ? b : 9600;
var listenAddress = Environment.GetEnvironmentVariable("ESCPOS_LISTEN_ADDRESS") ?? "0.0.0.0";

var viewModel = new MainWindowViewModel(printer, notifications, dialogs,
listenAddress, tcpPort, tcpEnabled,
string.IsNullOrWhiteSpace(serialPort) ? null : serialPort, serialBaud);

var window = new MainWindow { DataContext = viewModel };
notifications.AttachWindow(window);
dialogs.AttachTopLevel(window); // Window is a TopLevel
desktop.MainWindow = window;

desktop.ShutdownRequested += (_, _) => viewModel.Shutdown();
}

base.OnFrameworkInitializationCompleted();
}

private static (bool enabled, int port) ReadTcpSettings()
{
var setting = Environment.GetEnvironmentVariable("ESCPOS_TCP_PORT");
if (setting is "off" or "none" or "disabled" or "0")
return (false, 9100);

int port = int.TryParse(setting, out var p) && p > 0 ? p : 9100;
return (true, port);
}
}
10 changes: 0 additions & 10 deletions App.xaml

This file was deleted.

25 changes: 0 additions & 25 deletions App.xaml.cs

This file was deleted.

10 changes: 0 additions & 10 deletions AssemblyInfo.cs

This file was deleted.

93 changes: 93 additions & 0 deletions Assets/Fonts/OFL.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono)

This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
https://openfontlicense.org


-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------

PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.

The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.

DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.

"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).

"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).

"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.

"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.

PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:

1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.

2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.

3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.

4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.

5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.

TERMINATION
This license becomes null and void if any of the above conditions are
not met.

DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.
Binary file added Assets/Fonts/receipt-mono-bold.ttf
Binary file not shown.
Binary file added Assets/Fonts/receipt-mono-bolditalic.ttf
Binary file not shown.
Binary file added Assets/Fonts/receipt-mono-italic.ttf
Binary file not shown.
Binary file added Assets/Fonts/receipt-mono.ttf
Binary file not shown.
Binary file added Assets/Icon/icon-256.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Assets/Icon/icon.ico
Binary file not shown.
Binary file added Assets/Icon/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
11 changes: 11 additions & 0 deletions Emulator/Abstraction/IPrinterResponder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace ReceiptPrinterEmulator.Emulator.Abstraction;

/// <summary>
/// A channel the printer can write bytes back to the host over (TCP client or serial port).
/// Implemented by the transports so status / transmit-back commands can reply to the host that
/// sent the request.
/// </summary>
public interface IPrinterResponder
{
void Send(byte[] data);
}
10 changes: 7 additions & 3 deletions Emulator/Abstraction/IReceiptPrintable.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
using System.Drawing;
using SkiaSharp;

namespace ReceiptPrinterEmulator.Emulator.Abstraction;

public interface IReceiptPrintable
{
public void Render(Bitmap bitmap, Graphics g, int offsetX, int offsetY);
/// <summary>
/// Draws this line onto the receipt canvas at the given top-left offset.
/// </summary>
public void Render(SKCanvas canvas, int offsetX, int offsetY);

public int GetPrintHeight();
}
}
12 changes: 12 additions & 0 deletions Emulator/Enums/HriPosition.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace ReceiptPrinterEmulator.Emulator.Enums;

/// <summary>
/// Human Readable Interpretation (HRI) text position for 1D barcodes (ESC/POS GS H).
/// </summary>
public enum HriPosition
{
None = 0,
Above = 1,
Below = 2,
Both = 3
}
Loading