diff --git a/.vscode/launch.json b/.vscode/launch.json index 33a2921..5ccd544 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,7 @@ "type": "cppdbg", "request": "launch", "program": "${workspaceFolder}/builddir/ptprnt", - "args": [], + "args": ["-t Hello"], "stopAtEntry": false, "cwd": "${fileDirname}", "environment": [], diff --git a/meson.build b/meson.build index 2323591..888c1e7 100644 --- a/meson.build +++ b/meson.build @@ -8,6 +8,13 @@ usb_dep = dependency('libusb-1.0') log_dep = dependency('spdlog') pangocairo_dep = dependency('pangocairo') +# CLI11 +cli11_proj = subproject('cli11') +cli11_dep = cli11_proj.get_variable('CLI11_dep') +if not cli11_dep.found() + error('CLI11 not found, can not proceed') +endif + incdir = include_directories('src') ptprnt_hpps = [ @@ -38,7 +45,7 @@ ptprnt_srcs = [ ptprnt_lib = library('ptprnt', include_directories: incdir, install: true, - dependencies: [usb_dep, log_dep, pangocairo_dep], + dependencies: [usb_dep, log_dep, pangocairo_dep, cli11_dep], sources: [ptprnt_hpps, ptprnt_srcs]) ptprnt_dep = declare_dependency(include_directories: incdir, @@ -48,13 +55,14 @@ ptprnt_exe = executable( 'ptprnt', 'src/main.cpp', install: true, - dependencies : [usb_dep, log_dep, ptprnt_dep], + dependencies : [usb_dep, log_dep, cli11_dep, ptprnt_dep], cpp_args : ['-DPROJ_VERSION="'+meson.project_version()+'"'], ) +### Unit tests -#cmake = import('cmake') +# GTest gtest_proj = subproject('gtest') gtest_dep = gtest_proj.get_variable('gtest_main_dep') if not gtest_dep.found() diff --git a/src/P700Printer.cpp b/src/P700Printer.cpp index 7944375..98ed2ed 100644 --- a/src/P700Printer.cpp +++ b/src/P700Printer.cpp @@ -72,13 +72,10 @@ const PrinterStatus P700Printer::getPrinterStatus() { int tx = 0; int tries = 0; std::vector recvBuf(32); - do { + while (tries++ < MAX_TRIES_GET_STATUS) { std::this_thread::sleep_for(100ms); - mUsbHndl->bulkTransfer(0x81, recvBuf, &tx, 0); - if (tries++ > 10) { - break; - } - } while (tx == 0); + mUsbHndl->bulkTransfer(commands["printerinfo"][0], recvBuf, &tx, 0); + } return PrinterStatus{.tapeWidthMm = recvBuf[10]}; } diff --git a/src/P700Printer.hpp b/src/P700Printer.hpp index b7d74d2..a21fc55 100644 --- a/src/P700Printer.hpp +++ b/src/P700Printer.hpp @@ -31,6 +31,8 @@ namespace ptprnt::printer { +constexpr uint8_t MAX_TRIES_GET_STATUS = 10; + class P700Printer : public ::ptprnt::IPrinterDriver { public: P700Printer() = default; @@ -61,11 +63,11 @@ class P700Printer : public ::ptprnt::IPrinterDriver { std::shared_ptr mUsbHndl{nullptr}; - PrinterInfo mInfo{.driverName = "P700", - .name = "Brother P-touch P700", - .version = "v1.0", - .vid = 0x04f9, - .pid = 0x2061}; + static constexpr PrinterInfo mInfo{.driverName = "P700", + .name = "Brother P-touch P700", + .version = "v1.0", + .vid = 0x04f9, + .pid = 0x2061}; std::map> commands{ {"rasterstart", {0x1b, 0x69, 0x61, @@ -75,7 +77,7 @@ class P700Printer : public ::ptprnt::IPrinterDriver { {"lf", {0x5a}}, {"ff", {0x0c}}, {"eject", {0x1a}}, - }; + {"printerinfo", {0x81}}}; }; } // namespace ptprnt::printer \ No newline at end of file diff --git a/src/PtouchPrint.cpp b/src/PtouchPrint.cpp index 0fd7162..f3528b4 100644 --- a/src/PtouchPrint.cpp +++ b/src/PtouchPrint.cpp @@ -19,30 +19,54 @@ #include "PtouchPrint.hpp" +#include +#include #include +#include + +#include "CLI/Option.hpp" #include "P700Printer.hpp" #include "graphics/Bitmap.hpp" #include "libusbwrap/UsbDeviceFactory.hpp" -PtouchPrint::PtouchPrint() {} +namespace ptprnt { -PtouchPrint::~PtouchPrint() {} +PtouchPrint::PtouchPrint(const char* versionString) : mVersionString{versionString} {} -void PtouchPrint::init() { - mUsbDeviceFactory.init(); +int PtouchPrint::init(int argc, char** argv) { + setupCliParser(); + + try { + mApp.parse(argc, argv); + } catch (const CLI::ParseError& e) { + mApp.exit(e); + return -1; + } + + if (mVerboseFlag) { + setupLogger(spdlog::level::trace); + } else { + setupLogger(spdlog::level::critical); + } + + if (!mUsbDeviceFactory.init()) { + spdlog::error("Could not initialize libusb"); + return -1; + } mCompatiblePrinters = {std::make_shared()}; + return 0; } -void PtouchPrint::run() { +int PtouchPrint::run() { auto numFoundPrinters = getCompatiblePrinters(); if (numFoundPrinters == 0) { spdlog::error( "No compatible printers found, please make sure if they are turned on and connected"); - return; + return -1; } else if (numFoundPrinters > 1) { spdlog::warn("Found more than one compatible printer. Currently not supported."); - return; + return -1; } auto printer = mCompatiblePrinters[0]; @@ -50,14 +74,20 @@ void PtouchPrint::run() { if (devices.size() != 1) { spdlog::warn( "Found more than one device of the same printer on bus. Currently not supported"); - return; + return -1; } printer->attachUsbDevice(devices[0]); auto status = printer->getPrinterStatus(); spdlog::info("Detected tape width is {}mm", status.tapeWidthMm); auto bm = ptprnt::graphics::Bitmap(512, 128); - printer->printBitmap(bm); + //printer->printBitmap(bm); //printer->printText("wurst", 1); + + for (auto& cmd : mCommands) { + std::cout << cmd.second << std::endl; + } + + return 0; } unsigned int PtouchPrint::getCompatiblePrinters() { @@ -81,3 +111,52 @@ unsigned int PtouchPrint::getCompatiblePrinters() { mCompatiblePrinters.clear(); return mDetectedPrinters.size(); } + +void PtouchPrint::setupLogger(spdlog::level::level_enum lvl) { + spdlog::set_level(lvl); + spdlog::debug("Starting ptprnt {}", mVersionString); +} + +void PtouchPrint::setupCliParser() { + auto printVersion = [this](std::size_t) { + std::cout << "ptprnt version: " << mVersionString << std::endl; + }; + + // General options + mApp.add_flag("-v,--verbose", mVerboseFlag, "Enable verbose output"); + mApp.add_flag("-V,--version", printVersion, "Prints the ptprnt's version"); + + // Text printing options + mApp.add_option("-t,--text", + "Text to print (can be used multple times, use formatting options before to " + "influence text layout)") + ->group("Printing") + ->multi_option_policy(CLI::MultiOptionPolicy::TakeAll) + ->trigger_on_parse() + ->each([this](std::string text) { mCommands.emplace_back(CliCmdType::Text, text); }); + mApp.add_option("-f,--font", "Font used for the following text occurences") + ->group("Text printing ") + ->multi_option_policy(CLI::MultiOptionPolicy::TakeAll) + ->trigger_on_parse() + ->each([this](std::string font) { mCommands.emplace_back(CliCmdType::Font, font); }); + mApp.add_option("-s,--fontsize", "Font size of the following text occurences") + ->group("Text printing ") + ->multi_option_policy(CLI::MultiOptionPolicy::TakeAll) + ->trigger_on_parse() + ->each([this](std::string size) { mCommands.emplace_back(CliCmdType::FontSize, size); }); + mApp.add_option("--valign", "Vertical alignment of the following text occurences") + ->group("Text printing ") + ->multi_option_policy(CLI::MultiOptionPolicy::TakeAll) + ->trigger_on_parse() + ->each([this](std::string valign) { mCommands.emplace_back(CliCmdType::VAlign, valign); }); + mApp.add_option("--halign", "Vertical alignment of the following text occurences") + ->group("Text printing ") + ->multi_option_policy(CLI::MultiOptionPolicy::TakeAll) + ->trigger_on_parse() + ->each([this](std::string halign) { mCommands.emplace_back(CliCmdType::HAlign, halign); }); + + // Image options + mApp.add_option("-i,--image", "Image to print. Excludes all text printing ") + ->group("Image printing"); +} +} // namespace ptprnt \ No newline at end of file diff --git a/src/PtouchPrint.hpp b/src/PtouchPrint.hpp index f8b5533..303d78c 100644 --- a/src/PtouchPrint.hpp +++ b/src/PtouchPrint.hpp @@ -19,23 +19,47 @@ #pragma once +#include +#include +#include + +#include "constants.hpp" #include "interface/IPrinterDriver.hpp" #include "libusbwrap/UsbDeviceFactory.hpp" +namespace ptprnt { +enum class CliCmdType { None = 0, Text = 1, FontSize = 2, Font = 3, VAlign = 4, HAlign = 5 }; +using CliCmd = std::pair; + class PtouchPrint { public: - PtouchPrint(); - ~PtouchPrint(); + PtouchPrint(const char* versionString); + ~PtouchPrint() = default; - void init(); - void run(); + // This is basically a singelton application class, no need to copy or move + PtouchPrint(const PtouchPrint&) = delete; + PtouchPrint& operator=(const PtouchPrint&) = delete; + PtouchPrint(PtouchPrint&&) = delete; + PtouchPrint& operator=(PtouchPrint&&) = delete; + + int init(int argc, char** argv); + int run(); private: // methods + void setupLogger(spdlog::level::level_enum lvl); + void setupCliParser(); unsigned int getCompatiblePrinters(); // member variables - libusbwrap::UsbDeviceFactory mUsbDeviceFactory; - std::vector> mCompatiblePrinters; - std::vector> mDetectedPrinters; -}; \ No newline at end of file + CLI::App mApp{ptprnt::APP_DESC}; + libusbwrap::UsbDeviceFactory mUsbDeviceFactory{}; + std::vector> mCompatiblePrinters{}; + std::vector> mDetectedPrinters{}; + std::vector mCommands{}; + std::string mVersionString = ""; + + // CLI flags + bool mVerboseFlag = false; +}; +} // namespace ptprnt \ No newline at end of file diff --git a/src/constants.hpp b/src/constants.hpp new file mode 100644 index 0000000..027b488 --- /dev/null +++ b/src/constants.hpp @@ -0,0 +1,26 @@ +/* + ptrnt - print labels on linux + Copyright (C) 2023 Moritz Martinius + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + */ + +#pragma once + +namespace ptprnt { +constexpr const char* const APP_NAME = "ptprnt"; +constexpr const char* const APP_DESC = + "ptprnt - a userspace driver for Brother P-touch label printer"; +} // namespace ptprnt \ No newline at end of file diff --git a/src/libusbwrap/UsbDeviceFactory.cpp b/src/libusbwrap/UsbDeviceFactory.cpp index 53a8695..7ad09e9 100644 --- a/src/libusbwrap/UsbDeviceFactory.cpp +++ b/src/libusbwrap/UsbDeviceFactory.cpp @@ -29,10 +29,11 @@ #include "libusbwrap/interface/IUsbDevice.hpp" namespace libusbwrap { -UsbDeviceFactory::UsbDeviceFactory() {} UsbDeviceFactory::~UsbDeviceFactory() { - libusb_free_device_list(mLibusbDeviceList, 1); + if (mDeviceListInitialized) { + libusb_free_device_list(mLibusbDeviceList, 1); + } } std::vector> UsbDeviceFactory::findAllDevices() { @@ -52,7 +53,7 @@ int UsbDeviceFactory::refreshDeviceList() { } else if (ret == 0) { spdlog::warn("No USB devices found"); } - + mDeviceListInitialized = true; return ret; } diff --git a/src/libusbwrap/UsbDeviceFactory.hpp b/src/libusbwrap/UsbDeviceFactory.hpp index db4c3fa..e2f4d70 100644 --- a/src/libusbwrap/UsbDeviceFactory.hpp +++ b/src/libusbwrap/UsbDeviceFactory.hpp @@ -24,12 +24,14 @@ namespace libusbwrap { class UsbDeviceFactory : public IUsbDeviceFactory { public: - UsbDeviceFactory(); - ~UsbDeviceFactory(); + UsbDeviceFactory() = default; + virtual ~UsbDeviceFactory(); // delete copy ctor and assignment - UsbDeviceFactory(const UsbDeviceFactory&) = delete; - UsbDeviceFactory& operator=(UsbDeviceFactory&) = delete; + UsbDeviceFactory(const UsbDeviceFactory&) = delete; + UsbDeviceFactory& operator=(UsbDeviceFactory&) = delete; + UsbDeviceFactory(UsbDeviceFactory&&) = delete; + UsbDeviceFactory& operator=(UsbDeviceFactory&&) = delete; bool init(); /** @@ -56,6 +58,7 @@ class UsbDeviceFactory : public IUsbDeviceFactory { uint16_t pid); // members libusb_context* mLibusbCtx{nullptr}; - libusb_device** mLibusbDeviceList; + libusb_device** mLibusbDeviceList{}; + bool mDeviceListInitialized = false; }; } // namespace libusbwrap \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 525fe8a..ddc6b74 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,21 +17,16 @@ */ +#include #include #include "PtouchPrint.hpp" -void setupLogger() { - spdlog::set_level(spdlog::level::debug); - spdlog::info("Starting ptprnt {}", PROJ_VERSION); -} - int main(int argc, char** argv) { - setupLogger(); - - PtouchPrint ptouchprnt; - ptouchprnt.init(); - ptouchprnt.run(); - - return 0; + ptprnt::PtouchPrint ptouchprnt(PROJ_VERSION); + int ret = ptouchprnt.init(argc, argv); + if (ret != 0) { + return ret; + } + return ptouchprnt.run(); } diff --git a/subprojects/cli11.wrap b/subprojects/cli11.wrap new file mode 100644 index 0000000..c2f92b2 --- /dev/null +++ b/subprojects/cli11.wrap @@ -0,0 +1,9 @@ +[wrap-file] +directory = CLI11-2.3.2 +source_url = https://github.com/CLIUtils/CLI11/archive/refs/tags/v2.3.2.tar.gz +source_filename = CLI11-2.3.2.tar.gz +source_hash = aac0ab42108131ac5d3344a9db0fdf25c4db652296641955720a4fbe52334e22 +wrapdb_version = 2.3.2-1 + +[provide] +cli11 = CLI11_dep \ No newline at end of file