File indexing completed on 2024-09-15 12:24:28
0001 /* 0002 * SPDX-FileCopyrightText: 2007-2010 Kare Sars <kare dot sars at iki dot fi> 0003 * SPDX-FileCopyrightText: 2009 Matthias Nagl <matthias at nagl dot info> 0004 * SPDX-FileCopyrightText: 2009 Grzegorz Kurtyka <grzegorz dot kurtyka at gmail dot com> 0005 * SPDX-FileCopyrightText: 2007-2008 Gilles Caulier <caulier dot gilles at gmail dot com> 0006 * SPDX-FileCopyrightText: 2014 Gregor Mitsch : port to KDE5 frameworks 0007 * SPDX-FileCopyrightText: 2021 Alexander Stippich <a.stippich@gmx.net> 0008 * 0009 * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0010 */ 0011 0012 // Qt includes 0013 0014 #include <QMutex> 0015 0016 // Sane includes 0017 extern "C" { 0018 #include <sane/sane.h> 0019 #include <sane/saneopts.h> 0020 } 0021 0022 #include "interface.h" 0023 #include "interface_p.h" 0024 0025 #include <ksanecore_debug.h> 0026 0027 namespace KSaneCore 0028 { 0029 static int s_objectCount = 0; 0030 0031 Q_GLOBAL_STATIC(QMutex, s_objectMutex) 0032 0033 Interface::Interface(QObject *parent) 0034 : QObject(parent) 0035 , d(std::make_unique<InterfacePrivate>(this)) 0036 { 0037 SANE_Int version; 0038 SANE_Status status; 0039 0040 s_objectMutex->lock(); 0041 s_objectCount++; 0042 0043 if (s_objectCount == 1) { 0044 // only call sane init for the first instance 0045 status = sane_init(&version, &Authentication::authorization); 0046 if (status != SANE_STATUS_GOOD) { 0047 qCDebug(KSANECORE_LOG) << "libksane: sane_init() failed(" << sane_strstatus(status) << ")"; 0048 } 0049 } 0050 s_objectMutex->unlock(); 0051 0052 d->m_readValuesTimer.setSingleShot(true); 0053 connect(&d->m_readValuesTimer, &QTimer::timeout, d.get(), &InterfacePrivate::reloadValues); 0054 } 0055 0056 Interface::~Interface() 0057 { 0058 closeDevice(); 0059 0060 s_objectMutex->lock(); 0061 s_objectCount--; 0062 if (s_objectCount <= 0) { 0063 // only delete the find-devices and authorization singletons and call sane_exit 0064 // if this is the last instance 0065 delete d->m_findDevThread; 0066 delete d->m_auth; 0067 sane_exit(); 0068 } 0069 s_objectMutex->unlock(); 0070 } 0071 0072 QString Interface::deviceName() const 0073 { 0074 return d->m_devName; 0075 } 0076 0077 QString Interface::deviceVendor() const 0078 { 0079 return d->m_vendor; 0080 } 0081 0082 QString Interface::deviceModel() const 0083 { 0084 return d->m_model; 0085 } 0086 0087 bool Interface::reloadDevicesList(const DeviceType type) 0088 { 0089 /* On some SANE backends, the handle becomes invalid when 0090 * querying for new devices. Hence, this is only allowed when 0091 * no device is currently opened. */ 0092 if (d->m_saneHandle == nullptr) { 0093 d->m_findDevThread->setDeviceType(type); 0094 d->m_findDevThread->start(); 0095 return true; 0096 } 0097 return false; 0098 } 0099 0100 Interface::OpenStatus Interface::openDevice(const QString &deviceName) 0101 { 0102 SANE_Status status; 0103 0104 if (d->m_saneHandle != nullptr) { 0105 // this CoreInterface already has an open device 0106 return OpenStatus::OpeningFailed; 0107 } 0108 0109 // don't bother trying to open if the device string is empty 0110 if (deviceName.isEmpty()) { 0111 return OpenStatus::OpeningFailed; 0112 } 0113 // save the device name 0114 d->m_devName = deviceName; 0115 0116 // Try to open the device 0117 status = sane_open(deviceName.toLatin1().constData(), &d->m_saneHandle); 0118 0119 if (status == SANE_STATUS_ACCESS_DENIED) { 0120 return OpenStatus::OpeningDenied; 0121 } 0122 0123 if (status != SANE_STATUS_GOOD) { 0124 qCDebug(KSANECORE_LOG) << "sane_open(\"" << deviceName << "\", &handle) failed! status = " << sane_strstatus(status); 0125 d->m_devName.clear(); 0126 return OpenStatus::OpeningFailed; 0127 } 0128 0129 return d->loadDeviceOptions(); 0130 } 0131 0132 Interface::OpenStatus Interface::openRestrictedDevice(const QString &deviceName, const QString &userName, const QString &password) 0133 { 0134 SANE_Status status; 0135 0136 if (d->m_saneHandle != nullptr) { 0137 // this CoreInterface already has an open device 0138 return OpenStatus::OpeningFailed; 0139 } 0140 0141 // don't bother trying to open if the device string is empty 0142 if (deviceName.isEmpty()) { 0143 return OpenStatus::OpeningFailed; 0144 } 0145 // save the device name 0146 d->m_devName = deviceName; 0147 0148 // add/update the device user-name and password for authentication 0149 d->m_auth->setDeviceAuth(d->m_devName, userName, password); 0150 0151 // Try to open the device 0152 status = sane_open(deviceName.toLatin1().constData(), &d->m_saneHandle); 0153 0154 if (status == SANE_STATUS_ACCESS_DENIED) { 0155 return OpenStatus::OpeningDenied; 0156 } 0157 0158 if (status != SANE_STATUS_GOOD) { 0159 qCDebug(KSANECORE_LOG) << "sane_open(\"" << deviceName << "\", &handle) failed! status = " << sane_strstatus(status); 0160 d->m_auth->clearDeviceAuth(d->m_devName); 0161 d->m_devName.clear(); 0162 return OpenStatus::OpeningFailed; 0163 } 0164 0165 return d->loadDeviceOptions(); 0166 } 0167 0168 bool Interface::closeDevice() 0169 { 0170 if (!d->m_saneHandle) { 0171 return false; 0172 } 0173 stopScan(); 0174 0175 disconnect(d->m_scanThread); 0176 if (d->m_scanThread->isRunning()) { 0177 connect(d->m_scanThread, &QThread::finished, d->m_scanThread, &QThread::deleteLater); 0178 } 0179 if (d->m_scanThread->isFinished()) { 0180 d->m_scanThread->deleteLater(); 0181 } 0182 d->m_scanThread = nullptr; 0183 0184 d->m_auth->clearDeviceAuth(d->m_devName); 0185 sane_close(d->m_saneHandle); 0186 d->m_saneHandle = nullptr; 0187 d->clearDeviceOptions(); 0188 0189 return true; 0190 } 0191 0192 void Interface::startScan() 0193 { 0194 if (!d->m_saneHandle) { 0195 return; 0196 } 0197 d->m_cancelMultiPageScan = false; 0198 // execute a pending value reload 0199 while (d->m_readValuesTimer.isActive()) { 0200 d->m_readValuesTimer.stop(); 0201 d->reloadValues(); 0202 } 0203 d->m_optionPollTimer.stop(); 0204 Q_EMIT scanProgress(-1); 0205 d->m_scanThread->start(); 0206 } 0207 0208 void Interface::stopScan() 0209 { 0210 if (!d->m_saneHandle) { 0211 return; 0212 } 0213 0214 d->m_cancelMultiPageScan = true; 0215 if (d->m_scanThread->isRunning()) { 0216 d->m_scanThread->cancelScan(); 0217 } 0218 if (d->m_batchModeTimer.isActive()) { 0219 d->m_batchModeTimer.stop(); 0220 Q_EMIT batchModeCountDown(0); 0221 Q_EMIT scanFinished(ScanStatus::NoError, i18n("Scanning stopped by user.")); 0222 } 0223 } 0224 0225 QImage *Interface::scanImage() const 0226 { 0227 if (d->m_saneHandle != nullptr) { 0228 return d->m_scanThread->scanImage(); 0229 } 0230 return nullptr; 0231 } 0232 0233 void Interface::lockScanImage() 0234 { 0235 if (d->m_saneHandle != nullptr) { 0236 d->m_scanThread->lockScanImage(); 0237 } 0238 } 0239 0240 void Interface::unlockScanImage() 0241 { 0242 if (d->m_saneHandle != nullptr) { 0243 d->m_scanThread->unlockScanImage(); 0244 } 0245 } 0246 0247 QList<Option *> Interface::getOptionsList() 0248 { 0249 return d->m_externalOptionsList; 0250 } 0251 0252 Option *Interface::getOption(Interface::OptionName optionEnum) 0253 { 0254 auto it = d->m_optionsLocation.find(optionEnum); 0255 if (it != d->m_optionsLocation.end()) { 0256 return d->m_externalOptionsList.at(it.value()); 0257 } 0258 return nullptr; 0259 } 0260 0261 Option *Interface::getOption(const QString &optionName) 0262 { 0263 for (const auto &option : qAsConst(d->m_externalOptionsList)) { 0264 if (option->name() == optionName) { 0265 return option; 0266 } 0267 } 0268 return nullptr; 0269 } 0270 0271 QMap<QString, QString> Interface::getOptionsMap() 0272 { 0273 QMap<QString, QString> options; 0274 QString tmp; 0275 0276 for (const auto option : qAsConst(d->m_optionsList)) { 0277 tmp = option->valueAsString(); 0278 if (!tmp.isEmpty()) { 0279 options[option->name()] = tmp; 0280 } 0281 } 0282 return options; 0283 } 0284 0285 int Interface::setOptionsMap(const QMap<QString, QString> &options) 0286 { 0287 if (!d->m_saneHandle || d->m_scanThread->isRunning()) { 0288 return -1; 0289 } 0290 0291 QMap<QString, QString> optionMapCopy = options; 0292 0293 int i; 0294 int ret = 0; 0295 0296 Option *sourceOption = getOption(SourceOption); 0297 Option *modeOption = getOption(ScanModeOption); 0298 0299 // Priorize source option 0300 if (sourceOption != nullptr && optionMapCopy.contains(sourceOption->name())) { 0301 if (sourceOption->setValue(optionMapCopy[sourceOption->name()])) { 0302 ret++; 0303 } 0304 optionMapCopy.remove(sourceOption->name()); 0305 } 0306 0307 // Priorize mode option 0308 if (modeOption != nullptr && optionMapCopy.contains(modeOption->name())) { 0309 if (modeOption->setValue(optionMapCopy[modeOption->name()])) { 0310 ret++; 0311 } 0312 optionMapCopy.remove(modeOption->name()); 0313 } 0314 0315 // Update remaining options 0316 for (i = 0; i < d->m_optionsList.size(); i++) { 0317 const auto it = optionMapCopy.find(d->m_optionsList.at(i)->name()); 0318 if (it != optionMapCopy.end() && d->m_optionsList.at(i)->setValue(it.value())) { 0319 ret++; 0320 } 0321 } 0322 return ret; 0323 } 0324 0325 } // NameSpace KSaneCore 0326 0327 #include "moc_interface.cpp"