KSaneCore

interface.cpp
1/*
2 * SPDX-FileCopyrightText: 2007-2010 Kare Sars <kare dot sars at iki dot fi>
3 * SPDX-FileCopyrightText: 2009 Matthias Nagl <matthias at nagl dot info>
4 * SPDX-FileCopyrightText: 2009 Grzegorz Kurtyka <grzegorz dot kurtyka at gmail dot com>
5 * SPDX-FileCopyrightText: 2007-2008 Gilles Caulier <caulier dot gilles at gmail dot com>
6 * SPDX-FileCopyrightText: 2014 Gregor Mitsch : port to KDE5 frameworks
7 * SPDX-FileCopyrightText: 2021 Alexander Stippich <a.stippich@gmx.net>
8 *
9 * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
10 */
11
12// Qt includes
13#include <QJsonArray>
14#include <QJsonObject>
15#include <QJsonValue>
16#include <QMetaEnum>
17#include <QMutex>
18#include <QUrl>
19
20// Sane includes
21extern "C" {
22#include <sane/sane.h>
23#include <sane/saneopts.h>
24}
25
26#include "interface.h"
27#include "interface_p.h"
28
29#include <ksanecore_debug.h>
30
31namespace KSaneCore
32{
33static int s_objectCount = 0;
34
35Q_GLOBAL_STATIC(QMutex, s_objectMutex)
36
38 : QObject(parent)
39 , d(std::make_unique<InterfacePrivate>(this))
40{
41 SANE_Int version;
42 SANE_Status status;
43
44 s_objectMutex->lock();
45 s_objectCount++;
46
47 if (s_objectCount == 1) {
48 // only call sane init for the first instance
49 status = sane_init(&version, &Authentication::authorization);
50 if (status != SANE_STATUS_GOOD) {
51 qCDebug(KSANECORE_LOG) << "libksane: sane_init() failed(" << sane_strstatus(status) << ")";
52 }
53 }
54 s_objectMutex->unlock();
55
56 d->m_readValuesTimer.setSingleShot(true);
57 connect(&d->m_readValuesTimer, &QTimer::timeout, d.get(), &InterfacePrivate::reloadValues);
58}
59
61{
63
64 s_objectMutex->lock();
65 s_objectCount--;
66 if (s_objectCount <= 0) {
67 // only delete the find-devices and authorization singletons and call sane_exit
68 // if this is the last instance
69 delete d->m_findDevThread;
70 delete d->m_auth;
71 sane_exit();
72 }
73 s_objectMutex->unlock();
74}
75
77{
78 return d->m_devName;
79}
80
82{
83 return d->m_vendor;
84}
85
87{
88 return d->m_model;
89}
90
92{
93 /* On some SANE backends, the handle becomes invalid when
94 * querying for new devices. Hence, this is only allowed when
95 * no device is currently opened. */
96 if (d->m_saneHandle == nullptr) {
97 d->m_findDevThread->setDeviceType(type);
98 d->m_findDevThread->start();
99 return true;
100 }
101 return false;
102}
103
105{
106 SANE_Status status;
107
108 if (d->m_saneHandle != nullptr) {
109 // this CoreInterface already has an open device
110 return OpenStatus::OpeningFailed;
111 }
112
113 // don't bother trying to open if the device string is empty
114 if (deviceName.isEmpty()) {
115 return OpenStatus::OpeningFailed;
116 }
117 // save the device name
118 d->m_devName = deviceName;
119
120 // Try to open the device
121 status = sane_open(deviceName.toLatin1().constData(), &d->m_saneHandle);
122
123 if (status == SANE_STATUS_ACCESS_DENIED) {
124 return OpenStatus::OpeningDenied;
125 }
126
127 if (status != SANE_STATUS_GOOD) {
128 qCDebug(KSANECORE_LOG) << "sane_open(\"" << deviceName << "\", &handle) failed! status = " << sane_strstatus(status);
129 d->m_devName.clear();
130 return OpenStatus::OpeningFailed;
131 }
132
133 return d->loadDeviceOptions();
134}
135
136Interface::OpenStatus Interface::openRestrictedDevice(const QString &deviceName, const QString &userName, const QString &password)
137{
138 SANE_Status status;
139
140 if (d->m_saneHandle != nullptr) {
141 // this CoreInterface already has an open device
142 return OpenStatus::OpeningFailed;
143 }
144
145 // don't bother trying to open if the device string is empty
146 if (deviceName.isEmpty()) {
147 return OpenStatus::OpeningFailed;
148 }
149 // save the device name
150 d->m_devName = deviceName;
151
152 // add/update the device user-name and password for authentication
153 d->m_auth->setDeviceAuth(d->m_devName, userName, password);
154
155 // Try to open the device
156 status = sane_open(deviceName.toLatin1().constData(), &d->m_saneHandle);
157
158 if (status == SANE_STATUS_ACCESS_DENIED) {
159 return OpenStatus::OpeningDenied;
160 }
161
162 if (status != SANE_STATUS_GOOD) {
163 qCDebug(KSANECORE_LOG) << "sane_open(\"" << deviceName << "\", &handle) failed! status = " << sane_strstatus(status);
164 d->m_auth->clearDeviceAuth(d->m_devName);
165 d->m_devName.clear();
166 return OpenStatus::OpeningFailed;
167 }
168
169 return d->loadDeviceOptions();
170}
171
173{
174 if (!d->m_saneHandle) {
175 return false;
176 }
177 stopScan();
178
179 disconnect(d->m_scanThread);
180 if (d->m_scanThread->isRunning()) {
181 connect(d->m_scanThread, &QThread::finished, d->m_scanThread, &QThread::deleteLater);
182 }
183 if (d->m_scanThread->isFinished()) {
184 d->m_scanThread->deleteLater();
185 }
186 d->m_scanThread = nullptr;
187
188 d->m_auth->clearDeviceAuth(d->m_devName);
189 sane_close(d->m_saneHandle);
190 d->m_saneHandle = nullptr;
191 d->clearDeviceOptions();
192
193 return true;
194}
195
197{
198 if (!d->m_saneHandle) {
199 return;
200 }
201 d->m_cancelMultiPageScan = false;
202 // execute a pending value reload
203 while (d->m_readValuesTimer.isActive()) {
204 d->m_readValuesTimer.stop();
205 d->reloadValues();
206 }
207 d->m_optionPollTimer.stop();
209 d->m_scanThread->start();
210}
211
213{
214 if (!d->m_saneHandle) {
215 return;
216 }
217
218 d->m_cancelMultiPageScan = true;
219 if (d->m_scanThread->isRunning()) {
220 d->m_scanThread->cancelScan();
221 }
222 if (d->m_batchModeTimer.isActive()) {
223 d->m_batchModeTimer.stop();
225 Q_EMIT scanFinished(ScanStatus::NoError, i18n("Scanning stopped by user."));
226 }
227}
228
230{
231 if (d->m_saneHandle != nullptr) {
232 return d->m_scanThread->scanImage();
233 }
234 return nullptr;
235}
236
238{
239 if (d->m_saneHandle != nullptr) {
240 d->m_scanThread->lockScanImage();
241 }
242}
243
245{
246 if (d->m_saneHandle != nullptr) {
247 d->m_scanThread->unlockScanImage();
248 }
249}
250
252{
253 if (d->m_saneHandle == nullptr) {
254 return QJsonObject();
255 }
256 QJsonObject scannerData;
257 scannerData[QLatin1String("deviceName")] = d->m_devName;
258 scannerData[QLatin1String("deviceModel")] = d->m_model;
259 scannerData[QLatin1String("deviceVendor")] = d->m_vendor;
260
261 return scannerData;
262}
263
265{
266 if (d->m_saneHandle == nullptr) {
267 return QJsonObject();
268 }
269
270 QJsonObject optionData;
271 for (const auto &option : std::as_const(d->m_optionsList)) {
272 QJsonObject JsonOption;
273 JsonOption[QLatin1String("Title")] = option->title();
274 JsonOption[QLatin1String("Description")] = option->description();
275 JsonOption[QLatin1String("Type")] = QLatin1String(QMetaEnum::fromType<KSaneCore::Option::OptionType>().valueToKey(option->type()));
276 JsonOption[QLatin1String("State")] = QLatin1String(QMetaEnum::fromType<KSaneCore::Option::OptionState>().valueToKey(option->state()));
277 JsonOption[QLatin1String("Unit")] = QLatin1String(QMetaEnum::fromType<KSaneCore::Option::OptionUnit>().valueToKey(option->valueUnit()));
278 JsonOption[QLatin1String("Value size")] = option->valueSize();
279 JsonOption[QLatin1String("Step value")] = option->stepValue().toString();
280 JsonOption[QLatin1String("Current value")] = option->value().toString();
281 JsonOption[QLatin1String("Max value")] = option->maximumValue().toString();
282 JsonOption[QLatin1String("Min value")] = option->minimumValue().toString();
283 JsonOption[QLatin1String("Value list")] = QJsonArray::fromVariantList(option->valueList());
284 JsonOption[QLatin1String("Internal value list")] = QJsonArray::fromVariantList(option->internalValueList());
285 optionData[option->name()] = JsonOption;
286 }
287 return optionData;
288}
289
291{
292 return d->m_externalOptionsList;
293}
294
296{
297 auto it = d->m_optionsLocation.find(optionEnum);
298 if (it != d->m_optionsLocation.end()) {
299 return d->m_externalOptionsList.at(it.value());
300 }
301 return nullptr;
302}
303
305{
306 for (const auto &option : std::as_const(d->m_externalOptionsList)) {
307 if (option->name() == optionName) {
308 return option;
309 }
310 }
311 return nullptr;
312}
313
315{
317 QString tmp;
318
319 for (const auto option : std::as_const(d->m_optionsList)) {
320 tmp = option->valueAsString();
321 if (!tmp.isEmpty()) {
322 options[option->name()] = tmp;
323 }
324 }
325 return options;
326}
327
329{
330 if (!d->m_saneHandle || d->m_scanThread->isRunning()) {
331 return -1;
332 }
333
334 QMap<QString, QString> optionMapCopy = options;
335
336 int i;
337 int ret = 0;
338
339 Option *sourceOption = getOption(SourceOption);
340 Option *modeOption = getOption(ScanModeOption);
341
342 // Priorize source option
343 if (sourceOption != nullptr && optionMapCopy.contains(sourceOption->name())) {
344 if (sourceOption->setValue(optionMapCopy[sourceOption->name()])) {
345 ret++;
346 }
347 optionMapCopy.remove(sourceOption->name());
348 }
349
350 // Priorize mode option
351 if (modeOption != nullptr && optionMapCopy.contains(modeOption->name())) {
352 if (modeOption->setValue(optionMapCopy[modeOption->name()])) {
353 ret++;
354 }
355 optionMapCopy.remove(modeOption->name());
356 }
357
358 // Update remaining options
359 for (i = 0; i < d->m_optionsList.size(); i++) {
360 const auto it = optionMapCopy.find(d->m_optionsList.at(i)->name());
361 if (it != optionMapCopy.end() && d->m_optionsList.at(i)->setValue(it.value())) {
362 ret++;
363 }
364 }
365 return ret;
366}
367
368} // NameSpace KSaneCore
369
370#include "moc_interface.cpp"
static void authorization(SANE_String_Const resource, SANE_Char *username, SANE_Char *password)
static function called by sane_open to get authorization from user
This class provides the core interface for accessing the scan controls and options.
Definition interface.h:34
QList< Option * > getOptionsList()
This function returns all available options when a device is opened.
int setOptionsMap(const QMap< QString, QString > &options)
This method can be used to write many parameter values at once.
void stopScan()
This method is used to cancel a scan or prevent an automatic new scan.
OptionName
This enumeration is used to obtain a specific option with getOption(KSaneOptionName).
Definition interface.h:63
OpenStatus openRestrictedDevice(const QString &deviceName, const QString &userName, const QString &password)
This method opens the specified scanner device with a specified username and password.
bool reloadDevicesList(DeviceType type=AllDevices)
Get the list of available scanning devices.
Definition interface.cpp:91
QString deviceModel() const
This method returns the model of the currently opened scanner.
Definition interface.cpp:86
Option * getOption(OptionName optionEnum)
This function returns a specific option.
QString deviceVendor() const
This method returns the vendor name of the currently opened scanner.
Definition interface.cpp:81
OpenStatus openDevice(const QString &deviceName)
This method opens the specified scanner device and adds the scan options to the options list.
~Interface() override
Standard destructor.
Definition interface.cpp:60
void lockScanImage()
Locks the mutex protecting the QImage pointer of scanImage() from concurrent access during scanning.
bool closeDevice()
This method closes the currently open scanner device.
void unlockScanImage()
Unlocks the mutex protecting the QImage pointer of scanImage() from concurrent access during scanning...
QMap< QString, QString > getOptionsMap()
This method reads the available parameters and their values and returns them in a QMap (Name,...
QJsonObject scannerDeviceToJson()
Returns a JSON object containing the device name, model and vendor.
void scanFinished(KSaneCore::Interface::ScanStatus status, const QString &strStatus)
This signal is emitted when the scanning has ended.
OpenStatus
Enum determining whether the scanner opened correctly.
Definition interface.h:52
void batchModeCountDown(int remainingSeconds)
This signal is emitted for the count down when in batch mode.
QJsonObject scannerOptionsToJson()
Returns a JSON Object with all available data for all scanner options.
QImage * scanImage() const
Gives direct access to the QImage that is used to store the image data retrieved from the scanner.
DeviceType
This enumeration is used to filter the devices found by SANE.
Definition interface.h:98
QString deviceName() const
This method returns the internal device name of the currently opened scanner.
Definition interface.cpp:76
void scanProgress(int percent)
This signal is emitted for progress information during a scan.
void startScan()
This method is used to start a scan.
A wrapper class providing access to the internal KSaneBaseOption to access all options provided by KS...
Definition option.h:29
bool setValue(const QVariant &value)
This slot allows to change the current value of the option.
Definition option.cpp:141
QString name() const
This function returns the internal name of the option.
Definition option.cpp:33
Q_SCRIPTABLE CaptureState status()
QString i18n(const char *text, const TYPE &arg...)
const char * constData() const const
QJsonArray fromVariantList(const QVariantList &list)
QJsonValue value(QLatin1StringView key) const const
QString toString() const const
bool contains(const Key &key) const const
iterator end()
iterator find(const Key &key)
size_type remove(const Key &key)
QMetaEnum fromType()
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
bool disconnect(const QMetaObject::Connection &connection)
bool isEmpty() const const
QByteArray toLatin1() const const
void finished()
void timeout()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jul 26 2024 11:57:17 by doxygen 1.11.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.