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 <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.
This commit is contained in:
James A
2026-09-25 09:51:25 +02:00
committed by Dominic Radermacher
parent d05d31bb86
commit 7fa1e1ac46
4 changed files with 115 additions and 9 deletions
+2
View File
@@ -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);
+7
View File
@@ -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<serial>
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'.
+89 -6
View File
@@ -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)
}
}
}
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) {
+16 -2
View File
@@ -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, "<px>", 0, "Set tape width in pixels, use together with --writepng without a printer connected", 1},
{ "copies", 7, "<number>", 0, "Sets the number of identical prints", 1},
{ "timeout", 8, "<seconds>", 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, "<serial>", 0, "Use the printer with this USB serial number", 1 },
{ 0, 0, 0, 0, "print commands:", 2},
{ "image", 'i', "<file>", 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) {