From 7fa1e1ac46a3329f8e40cd1bdfd37f75aa4c1197 Mon Sep 17 00:00:00 2001 From: James A <33671033+Jallerton00@users.noreply.github.com> Date: Tue, 22 Sep 2026 13:13:31 +0100 Subject: [PATCH] add --serial and --list-connected to pick one of several printers When more than one printer is connected, ptouch-print used the first one it found. --serial now uses only the printer with that USB serial number, and --list-connected lists the connected printers with their serials. libptouch gets ptouch_open_serial() and ptouch_list_connected(); ptouch_open() keeps its signature and calls ptouch_open_serial() with NULL. The serial is checked before the P-Lite and unsupported-raster checks, so a different printer in the wrong mode no longer ends the search, and the "found on USB bus" line now shows the serial. The early error returns in the open loop now free the device list. --- include/ptouch.h | 2 + ptouch-print.1 | 7 ++++ src/libptouch.c | 97 ++++++++++++++++++++++++++++++++++++++++++---- src/ptouch-print.c | 18 ++++++++- 4 files changed, 115 insertions(+), 9 deletions(-) diff --git a/include/ptouch.h b/include/ptouch.h index 1e5c21b..2b245b5 100644 --- a/include/ptouch.h +++ b/include/ptouch.h @@ -107,6 +107,7 @@ struct _ptouch_dev { typedef struct _ptouch_dev *ptouch_dev; int ptouch_open(ptouch_dev *ptdev); +int ptouch_open_serial(ptouch_dev *ptdev, const char *serial); int ptouch_close(ptouch_dev ptdev); int ptouch_send(ptouch_dev ptdev, uint8_t *data, size_t len); int ptouch_init(ptouch_dev ptdev); @@ -130,6 +131,7 @@ int ptouch_rasterstart(ptouch_dev ptdev); int ptouch_sendraster(ptouch_dev ptdev, uint8_t *data, size_t len); void ptouch_rawstatus(uint8_t raw[32]); void ptouch_list_supported(); +int ptouch_list_connected(); const char* pt_mediatype(unsigned char media_type); const char* pt_tapecolor(unsigned char tape_color); diff --git a/ptouch-print.1 b/ptouch-print.1 index fb55623..0acd39c 100644 --- a/ptouch-print.1 +++ b/ptouch-print.1 @@ -34,6 +34,10 @@ Set the seconds ptouch-print is waiting for finishing a previous ptouch-print co Default is 1 second. 0 (zero) means wait forever. Useful if ptouch-print is used in a script multiple times, and the device is waiting for the user to use the mechanical cutter. +.TP +.BR \-\-serial\ \fI +Use the printer with this USB serial number, when more than one printer is connected. +Use \-\-list\-connected to see the serial numbers. .SS "Font selection options" .TP @@ -90,6 +94,9 @@ Show info about the tape detected (like printing width etc.) and exit. .TP .BR \-\-list-supported List all supported printers +.TP +.BR \-\-list-connected +List all connected printers with their USB serial numbers, for use with \-\-serial .SH DEFAULTS The default font used is 'DejaVuSans'. diff --git a/src/libptouch.c b/src/libptouch.c index c1c61a6..0ce5ee4 100644 --- a/src/libptouch.c +++ b/src/libptouch.c @@ -112,6 +112,13 @@ struct _pt_dev_info ptdevs[] = { }; int ptouch_open(ptouch_dev *ptdev) +{ + return ptouch_open_serial(ptdev, NULL); +} + +/* Like ptouch_open(), but when serial is not NULL only a printer whose + USB serial number matches it is used. */ +int ptouch_open_serial(ptouch_dev *ptdev, const char *serial) { libusb_device **devs; libusb_device *dev; @@ -148,22 +155,43 @@ int ptouch_open(ptouch_dev *ptdev) } for (int k=0; ptdevs[k].vid > 0; ++k) { if ((desc.idVendor == ptdevs[k].vid) && (desc.idProduct == ptdevs[k].pid) && (ptdevs[k].flags >= 0)) { - fprintf(stderr, _("%s found on USB bus %d, device %d\n"), + unsigned char buf[64] = {0}; + if ((r=libusb_open(dev, &handle)) != 0) { + fprintf(stderr, _("libusb_open error :%s\n"), libusb_error_name(r)); + if (serial) { + /* can't read its serial, so it can't be the one we want */ + break; + } + libusb_free_device_list(devs, 1); + return -1; + } + if (desc.iSerialNumber) { + libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, buf, sizeof(buf)); + } + fprintf(stderr, _("%s found on USB bus %d, device %d, serial %s\n"), ptdevs[k].name, libusb_get_bus_number(dev), - libusb_get_device_address(dev)); + libusb_get_device_address(dev), + buf[0] ? (char *)buf : "-"); + /* check the serial before the P-Lite/unsupported checks, so + a different printer in the wrong mode doesn't end the search */ + if (serial && (strcmp((char *)buf, serial) != 0)) { + libusb_close(handle); + handle = NULL; + break; /* next device */ + } if (ptdevs[k].flags & FLAG_PLITE) { printf("Printer is in P-Lite Mode, which is unsupported\n\n"); printf("Turn off P-Lite mode by changing switch from position EL to position E\n"); printf("or by pressing the PLite button for ~ 2 seconds (or consult the manual)\n"); + libusb_close(handle); + libusb_free_device_list(devs, 1); return -1; } if (ptdevs[k].flags & FLAG_UNSUP_RASTER) { printf("Unfortunately, that printer currently is unsupported (it has a different raster data transfer)\n"); - return -1; - } - if ((r=libusb_open(dev, &handle)) != 0) { - fprintf(stderr, _("libusb_open error :%s\n"), libusb_error_name(r)); + libusb_close(handle); + libusb_free_device_list(devs, 1); return -1; } libusb_free_device_list(devs, 1); @@ -186,7 +214,11 @@ int ptouch_open(ptouch_dev *ptdev) } } } - fprintf(stderr, _("No P-Touch printer found on USB (remember to put switch to position E)\n")); + if (serial) { + fprintf(stderr, _("No P-Touch printer with serial %s found\n"), serial); + } else { + fprintf(stderr, _("No P-Touch printer found on USB (remember to put switch to position E)\n")); + } libusb_free_device_list(devs, 1); return -1; } @@ -555,6 +587,57 @@ void ptouch_list_supported() return; } +/* Print every connected P-Touch printer with its USB serial number, which + can then be passed to ptouch_open_serial(). Returns the number of + printers found, or -1 on error. */ +int ptouch_list_connected() +{ + libusb_device **devs; + libusb_device *dev; + libusb_device_handle *handle; + struct libusb_device_descriptor desc; + int r, i=0, found=0; + + if ((libusb_init(NULL)) < 0) { + fprintf(stderr, _("libusb_init() failed\n")); + return -1; + } + if (libusb_get_device_list(NULL, &devs) < 0) { + libusb_exit(NULL); + return -1; + } + while ((dev=devs[i++]) != NULL) { + if (libusb_get_device_descriptor(dev, &desc) < 0) { + continue; + } + for (int k=0; ptdevs[k].vid > 0; ++k) { + if ((desc.idVendor == ptdevs[k].vid) && (desc.idProduct == ptdevs[k].pid) && (ptdevs[k].flags >= 0)) { + unsigned char buf[64] = {0}; + const char *serial = "-"; + if ((r=libusb_open(dev, &handle)) != 0) { + serial = libusb_error_name(r); + } else { + if (desc.iSerialNumber && (libusb_get_string_descriptor_ascii(handle, desc.iSerialNumber, buf, sizeof(buf)) > 0)) { + serial = (char *)buf; + } + libusb_close(handle); + } + printf(_("%s\tserial %s\t(USB bus %d, device %d)%s\n"), + ptdevs[k].name, serial, + libusb_get_bus_number(dev), + libusb_get_device_address(dev), + (ptdevs[k].flags & FLAG_PLITE) ? _(" - in P-Lite mode, unsupported") : + (ptdevs[k].flags & FLAG_UNSUP_RASTER) ? _(" - unsupported") : ""); + ++found; + break; + } + } + } + libusb_free_device_list(devs, 1); + libusb_exit(NULL); + return found; +} + const char* pt_mediatype(const uint8_t media_type) { switch (media_type) { diff --git a/src/ptouch-print.c b/src/ptouch-print.c index c1dff6e..f5f2c65 100644 --- a/src/ptouch-print.c +++ b/src/ptouch-print.c @@ -65,6 +65,7 @@ struct arguments { char *save_png; int verbose; int timeout; + char *serial; }; typedef enum { JOB_CUTMARK, JOB_IMAGE, JOB_PAD, JOB_TEXT, JOB_UNDEFINED } job_type_t; @@ -109,6 +110,7 @@ static struct argp_option options[] = { { "force-tape-width", 6, "", 0, "Set tape width in pixels, use together with --writepng without a printer connected", 1}, { "copies", 7, "", 0, "Sets the number of identical prints", 1}, { "timeout", 8, "", 0, "Set timeout waiting for finishing previous job. Default:1, or a per-printer minimum where one is known. 0 means infinity", 1}, + { "serial", 12, "", 0, "Use the printer with this USB serial number", 1 }, { 0, 0, 0, 0, "print commands:", 2}, { "image", 'i', "", 0, "Print the given image which must be a 2 color (black/white) png", 2}, @@ -123,6 +125,7 @@ static struct argp_option options[] = { { 0, 0, 0, 0, "other commands:", 3}, { "info", 20, 0, 0, "Show info about detected tape", 3}, { "list-supported", 21, 0, 0, "Show printers supported by this version", 3}, + { "list-connected", 22, 0, 0, "Show connected printers and their USB serial numbers (for --serial)", 3}, { 0 } }; @@ -141,7 +144,8 @@ struct arguments arguments = { .forced_tape_width = 0, .save_png = NULL, .verbose = 0, - .timeout = -1 /* -1 = not given; 0 is already "infinity" */ + .timeout = -1, /* -1 = not given; 0 is already "infinity" */ + .serial = NULL }; job_t *jobs = NULL; @@ -688,6 +692,9 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state) case 11: // precut arguments->precut = true; break; + case 12: // serial + arguments->serial = arg; + break; case 'a': // align if ((strcmp(arg, "c") == 0) || (strcmp(arg, "center") == 0)) { arguments->align = ALIGN_CENTER; @@ -709,6 +716,13 @@ static error_t parse_opt(int key, char *arg, struct argp_state *state) case 21: // list-supported ptouch_list_supported(); exit(0); + case 22: { // list-connected + int n = ptouch_list_connected(); + if (n == 0) { + fprintf(stderr, _("No P-Touch printer found on USB (remember to put switch to position E)\n")); + } + exit(n > 0 ? 0 : 5); + } case ARGP_KEY_ARG: argp_failure(state, 1, E2BIG, _("No arguments supported")); break; @@ -745,7 +759,7 @@ int main(int argc, char *argv[]) argp_parse(&argp, argc, argv, 0, 0, &arguments); if (!arguments.forced_tape_width) { - if ((ptouch_open(&ptdev)) < 0) { + if ((ptouch_open_serial(&ptdev, arguments.serial)) < 0) { return 5; } if (ptouch_init(ptdev) != 0) {