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
14#include <QMutex>
15
16// Sane includes
17extern "C" {
18#include <sane/sane.h>
19#include <sane/saneopts.h>
20}
21
22#include "interface.h"
23#include "interface_p.h"
24
25#include <ksanecore_debug.h>
26
27namespace KSaneCore
28{
29static int s_objectCount = 0;
30
31Q_GLOBAL_STATIC(QMutex, s_objectMutex)
32
34 : QObject(parent)
35 , d(std::make_unique<InterfacePrivate>(this))
36{
37 SANE_Int version;
38 SANE_Status status;
39
40 s_objectMutex->lock();
41 s_objectCount++;
42
43 if (s_objectCount == 1) {
44 // only call sane init for the first instance
45 status = sane_init(&version, &Authentication::authorization);
46 if (status != SANE_STATUS_GOOD) {
47 qCDebug(KSANECORE_LOG) << "libksane: sane_init() failed(" << sane_strstatus(status) << ")";
48 }
49 }
50 s_objectMutex->unlock();
51
52 d->m_readValuesTimer.setSingleShot(true);
53 connect(&d->m_readValuesTimer, &QTimer::timeout, d.get(), &InterfacePrivate::reloadValues);
54}
55
57{
59
60 s_objectMutex->lock();
61 s_objectCount--;
62 if (s_objectCount <= 0) {
63 // only delete the find-devices and authorization singletons and call sane_exit
64 // if this is the last instance
65 delete d->m_findDevThread;
66 delete d->m_auth;
67 sane_exit();
68 }
69 s_objectMutex->unlock();
70}
71
73{
74 return d->m_devName;
75}
76
78{
79 return d->m_vendor;
80}
81
83{
84 return d->m_model;
85}
86
88{
89 /* On some SANE backends, the handle becomes invalid when
90 * querying for new devices. Hence, this is only allowed when
91 * no device is currently opened. */
92 if (d->m_saneHandle == nullptr) {
93 d->m_findDevThread->setDeviceType(type);
94 d->m_findDevThread->start();
95 return true;
96 }
97 return false;
98}
99
101{
102 SANE_Status status;
103
104 if (d->m_saneHandle != nullptr) {
105 // this CoreInterface already has an open device
106 return OpenStatus::OpeningFailed;
107 }
108
109 // don't bother trying to open if the device string is empty
110 if (deviceName.isEmpty()) {
111 return OpenStatus::OpeningFailed;
112 }
113 // save the device name
114 d->m_devName = deviceName;
115
116 // Try to open the device
117 status = sane_open(deviceName.toLatin1().constData(), &d->m_saneHandle);
118
119 if (status == SANE_STATUS_ACCESS_DENIED) {
120 return OpenStatus::OpeningDenied;
121 }
122
123 if (status != SANE_STATUS_GOOD) {
124 qCDebug(KSANECORE_LOG) << "sane_open(\"" << deviceName << "\", &handle) failed! status = " << sane_strstatus(status);
125 d->m_devName.clear();
126 return OpenStatus::OpeningFailed;
127 }
128
129 return d->loadDeviceOptions();
130}
131
132Interface::OpenStatus Interface::openRestrictedDevice(const QString &deviceName, const QString &userName, const QString &password)
133{
134 SANE_Status status;
135
136 if (d->m_saneHandle != nullptr) {
137 // this CoreInterface already has an open device
138 return OpenStatus::OpeningFailed;
139 }
140
141 // don't bother trying to open if the device string is empty
142 if (deviceName.isEmpty()) {
143 return OpenStatus::OpeningFailed;
144 }
145 // save the device name
146 d->m_devName = deviceName;
147
148 // add/update the device user-name and password for authentication
149 d->m_auth->setDeviceAuth(d->m_devName, userName, password);
150
151 // Try to open the device
152 status = sane_open(deviceName.toLatin1().constData(), &d->m_saneHandle);
153
154 if (status == SANE_STATUS_ACCESS_DENIED) {
155 return OpenStatus::OpeningDenied;
156 }
157
158 if (status != SANE_STATUS_GOOD) {
159 qCDebug(KSANECORE_LOG) << "sane_open(\"" << deviceName << "\", &handle) failed! status = " << sane_strstatus(status);
160 d->m_auth->clearDeviceAuth(d->m_devName);
161 d->m_devName.clear();
162 return OpenStatus::OpeningFailed;
163 }
164
165 return d->loadDeviceOptions();
166}
167
169{
170 if (!d->m_saneHandle) {
171 return false;
172 }
173 stopScan();
174
175 disconnect(d->m_scanThread);
176 if (d->m_scanThread->isRunning()) {
177 connect(d->m_scanThread, &QThread::finished, d->m_scanThread, &QThread::deleteLater);
178 }
179 if (d->m_scanThread->isFinished()) {
180 d->m_scanThread->deleteLater();
181 }
182 d->m_scanThread = nullptr;
183
184 d->m_auth->clearDeviceAuth(d->m_devName);
185 sane_close(d->m_saneHandle);
186 d->m_saneHandle = nullptr;
187 d->clearDeviceOptions();
188
189 return true;
190}
191
193{
194 if (!d->m_saneHandle) {
195 return;
196 }
197 d->m_cancelMultiPageScan = false;
198 // execute a pending value reload
199 while (d->m_readValuesTimer.isActive()) {
200 d->m_readValuesTimer.stop();
201 d->reloadValues();
202 }
203 d->m_optionPollTimer.stop();
205 d->m_scanThread->start();
206}
207
209{
210 if (!d->m_saneHandle) {
211 return;
212 }
213
214 d->m_cancelMultiPageScan = true;
215 if (d->m_scanThread->isRunning()) {
216 d->m_scanThread->cancelScan();
217 }
218 if (d->m_batchModeTimer.isActive()) {
219 d->m_batchModeTimer.stop();
221 Q_EMIT scanFinished(ScanStatus::NoError, i18n("Scanning stopped by user."));
222 }
223}
224
226{
227 if (d->m_saneHandle != nullptr) {
228 return d->m_scanThread->scanImage();
229 }
230 return nullptr;
231}
232
234{
235 if (d->m_saneHandle != nullptr) {
236 d->m_scanThread->lockScanImage();
237 }
238}
239
241{
242 if (d->m_saneHandle != nullptr) {
243 d->m_scanThread->unlockScanImage();
244 }
245}
246
248{
249 return d->m_externalOptionsList;
250}
251
253{
254 auto it = d->m_optionsLocation.find(optionEnum);
255 if (it != d->m_optionsLocation.end()) {
256 return d->m_externalOptionsList.at(it.value());
257 }
258 return nullptr;
259}
260
262{
263 for (const auto &option : qAsConst(d->m_externalOptionsList)) {
264 if (option->name() == optionName) {
265 return option;
266 }
267 }
268 return nullptr;
269}
270
272{
274 QString tmp;
275
276 for (const auto option : qAsConst(d->m_optionsList)) {
277 tmp = option->valueAsString();
278 if (!tmp.isEmpty()) {
279 options[option->name()] = tmp;
280 }
281 }
282 return options;
283}
284
286{
287 if (!d->m_saneHandle || d->m_scanThread->isRunning()) {
288 return -1;
289 }
290
291 QMap<QString, QString> optionMapCopy = options;
292
293 int i;
294 int ret = 0;
295
296 Option *sourceOption = getOption(SourceOption);
297 Option *modeOption = getOption(ScanModeOption);
298
299 // Priorize source option
300 if (sourceOption != nullptr && optionMapCopy.contains(sourceOption->name())) {
301 if (sourceOption->setValue(optionMapCopy[sourceOption->name()])) {
302 ret++;
303 }
304 optionMapCopy.remove(sourceOption->name());
305 }
306
307 // Priorize mode option
308 if (modeOption != nullptr && optionMapCopy.contains(modeOption->name())) {
309 if (modeOption->setValue(optionMapCopy[modeOption->name()])) {
310 ret++;
311 }
312 optionMapCopy.remove(modeOption->name());
313 }
314
315 // Update remaining options
316 for (i = 0; i < d->m_optionsList.size(); i++) {
317 const auto it = optionMapCopy.find(d->m_optionsList.at(i)->name());
318 if (it != optionMapCopy.end() && d->m_optionsList.at(i)->setValue(it.value())) {
319 ret++;
320 }
321 }
322 return ret;
323}
324
325} // NameSpace KSaneCore
326
327#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:33
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:62
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:87
QString deviceModel() const
This method returns the model of the currently opened scanner.
Definition interface.cpp:82
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:77
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:56
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,...
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:51
void batchModeCountDown(int remainingSeconds)
This signal is emitted for the count down when in batch mode.
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:97
QString deviceName() const
This method returns the internal device name of the currently opened scanner.
Definition interface.cpp:72
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
bool contains(const Key &key) const const
iterator end()
iterator find(const Key &key)
size_type remove(const Key &key)
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 Tue Mar 26 2024 11:19:34 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.