/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 2; -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef nsPrinterCUPS_h___ #define nsPrinterCUPS_h___ #include "nsPrinterBase.h" #include "nsPrintSettingsImpl.h" #include "nsCUPSShim.h" #include "nsString.h" #include "mozilla/DataMutex.h" #include "mozilla/FunctionRef.h" #include "mozilla/RecursiveMutex.h" /** * @brief Interface to help implementing nsIPrinter using a CUPS printer. */ class nsPrinterCUPS final : public nsPrinterBase { public: NS_IMETHOD GetName(nsAString& aName) override; NS_IMETHOD GetSystemName(nsAString& aName) override; bool SupportsDuplex() const final; bool SupportsColor() const final; bool SupportsMonochrome() const final; bool SupportsCollation() const final; PrinterInfo CreatePrinterInfo() const final; MarginDouble GetMarginsForPaper(nsString aPaperId) const final { MOZ_ASSERT_UNREACHABLE( "The CUPS API requires us to always get the margin when fetching the " "paper list so there should be no need to query it separately"); return {}; } nsPrinterCUPS() = delete; nsPrinterCUPS(const mozilla::CommonPaperInfoArray* aArray, const nsCUPSShim& aShim, nsString aDisplayName, cups_dest_t* aPrinter) : nsPrinterBase(aArray), mShim(aShim), mDisplayName(std::move(aDisplayName)), mPrinterInfoMutex(CUPSPrinterInfo{aPrinter}, "nsPrinterCUPS::mPrinterInfoMutex") {} static void ForEachExtraMonochromeSetting( mozilla::FunctionRef); private: struct CUPSPrinterInfo { explicit constexpr CUPSPrinterInfo(cups_dest_t* aPrinter) : mPrinter(aPrinter) {} cups_dest_t* mPrinter; cups_dinfo_t* mPrinterInfo = nullptr; uint64_t mCUPSMajor = 0; uint64_t mCUPSMinor = 0; uint64_t mCUPSPatch = 0; // Whether we have attempted to fetch mPrinterInfo with CUPS_HTTP_DEFAULT. bool mTriedInitWithDefault = false; // Whether we have attempted to fetch mPrinterInfo with a connection. bool mTriedInitWithConnection = false; CUPSPrinterInfo() = delete; CUPSPrinterInfo(const CUPSPrinterInfo&) = delete; CUPSPrinterInfo(CUPSPrinterInfo&& aOther) : mPrinter(aOther.mPrinter), mPrinterInfo(aOther.mPrinterInfo), mCUPSMajor(aOther.mCUPSMajor), mCUPSMinor(aOther.mCUPSMinor), mCUPSPatch(aOther.mCUPSPatch) { aOther.mPrinter = nullptr; aOther.mPrinterInfo = nullptr; } }; using PrinterInfoMutex = mozilla::DataMutexBase; using PrinterInfoLock = PrinterInfoMutex::AutoLock; ~nsPrinterCUPS(); /** * Retrieves the localized name for a given media (paper). * Returns nullptr if the name cannot be localized. */ const char* LocalizeMediaName(http_t& aConnection, cups_size_t& aMedia) const; void GetPrinterName(nsAString& aName) const; // Little util for getting support flags using the direct CUPS names. bool Supports(const char* aOption, const char* aValue) const; // Returns support value if CUPS meets the minimum version, otherwise returns // |aDefault| bool IsCUPSVersionAtLeast(uint64_t aCUPSMajor, uint64_t aCUPSMinor, uint64_t aCUPSPatch) const; const char* FindCUPSOption(PrinterInfoLock& aLock, const char* name) const { const cups_dest_t* const printer = aLock->mPrinter; return mShim.cupsGetOption(name, printer->num_options, printer->options); } class Connection { public: http_t* GetConnection(cups_dest_t* aDest); inline explicit Connection(const nsCUPSShim& aShim) : mShim(aShim) {} Connection() = delete; ~Connection(); protected: const nsCUPSShim& mShim; http_t* mConnection = CUPS_HTTP_DEFAULT; bool mWasInited = false; }; PrintSettingsInitializer DefaultSettings(Connection& aConnection) const; nsTArray PaperList(Connection& aConnection) const; /** * Attempts to populate the CUPSPrinterInfo object. * This usually works with the CUPS default connection, * but has been known to require an established connection * on older versions of Ubuntu (18 and below). */ PrinterInfoLock TryEnsurePrinterInfo( http_t* const aConnection = CUPS_HTTP_DEFAULT) const { PrinterInfoLock lock = mPrinterInfoMutex.Lock(); TryEnsurePrinterInfo(lock, aConnection); return lock; } /** * TryEnsurePrinterInfo that uses a caller-provided PrinterInfoLock. * * This can be used to avoid unnecessarily redundant locking of * mPrinterInfoLock when getting a connection through * Connection::GetConnection and then passing that into TryEnsurePrinterInfo. */ void TryEnsurePrinterInfo(PrinterInfoLock& aLock, http_t* const aConnection) const; const nsCUPSShim& mShim; nsString mDisplayName; mutable PrinterInfoMutex mPrinterInfoMutex; }; // There's no standard setting in Core Printing for monochrome. Or rather, // there is (PMSetColorMode) but it does nothing. Similarly, the relevant gtk // setting only works on Windows, yay. // // So on CUPS the right setting to use depends on the print driver. So we set / // look for a variety of driver-specific keys that are known to work across // printers. // // We set all the known settings, because the alternative to that is parsing ppd // files from the printer and find the relevant known choices that can apply, // and that is a lot more complex, similarly sketchy (requires the same amount // of driver-specific knowledge), and requires using deprecated CUPS APIs. #define CUPS_EACH_MONOCHROME_PRINTER_SETTING(macro_) \ macro_("ColorModel", "Gray") /* Generic */ \ macro_("BRMonoColor", "Mono") /* Brother */ \ macro_("BRPrintQuality", "Black") /* Brother */ \ macro_("CNIJGrayScale", "1") /* Canon */ \ macro_("CNGrayscale", "True") /* Canon */ \ macro_("INK", "MONO") /* Epson */ \ macro_("HPColorMode", "GrayscalePrint") /* HP */ \ macro_("ColorMode", "Mono") /* Samsung */ \ macro_("PrintoutMode", "Normal.Gray") /* Foomatic */ \ macro_("ProcessColorModel", "Mono") /* Samsung */ \ macro_("ARCMode", "CMBW") /* Sharp */ \ macro_("XRXColor", "BW") /* Xerox */ \ macro_("XROutputColor", "PrintAsGrayscale") /* Xerox, bug 1676191#c32 */ \ macro_("SelectColor", "Grayscale") /* Konica Minolta */ \ macro_("OKControl", "Gray") /* Oki */ \ macro_("BLW", "TrueM") /* Lexmark */ \ macro_("EPRendering", "None") /* Epson */ #endif /* nsPrinterCUPS_h___ */