Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
Graphics
libksane
Commits
c2928858
Commit
c2928858
authored
May 03, 2021
by
Alexander Stippich
Browse files
add new KSaneCore component
parent
9a3ab2a3
Changes
12
Expand all
Hide whitespace changes
Inline
Side-by-side
src/CMakeLists.txt
View file @
c2928858
...
...
@@ -28,6 +28,8 @@ target_sources(KF5Sane PRIVATE
ksaneimagebuilder.cpp
ksanewidget_p.cpp
splittercollapser.cpp
ksanecore.cpp
ksanecore_p.cpp
ksaneauth.cpp
ksaneoption.cpp
ksaneinternaloption.cpp
...
...
@@ -80,6 +82,7 @@ set_target_properties(KF5Sane
ecm_generate_headers
(
KSane_HEADERS
HEADER_NAMES
KSaneWidget
KSaneCore
KSaneOption
REQUIRED_HEADERS KSane_HEADERS
RELATIVE
"../src/"
...
...
src/ksanecore.cpp
0 → 100644
View file @
c2928858
/* ============================================================
*
* SPDX-FileCopyrightText: 2007-2010 Kare Sars <kare dot sars at iki dot fi>
* SPDX-FileCopyrightText: 2009 Matthias Nagl <matthias at nagl dot info>
* SPDX-FileCopyrightText: 2009 Grzegorz Kurtyka <grzegorz dot kurtyka at gmail dot com>
* SPDX-FileCopyrightText: 2007-2008 Gilles Caulier <caulier dot gilles at gmail dot com>
* SPDX-FileCopyrightText: 2014 Gregor Mitsch : port to KDE5 frameworks
* SPDX-FileCopyrightText: 2021 Alexander Stippich <a.stippich@gmx.net>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*
* ============================================================ */
//Qt includes
#include <QMutex>
// Sane includes
extern
"C"
{
#include <sane/saneopts.h>
#include <sane/sane.h>
}
#include "ksanecore.h"
#include "ksanecore_p.h"
#include <ksane_debug.h>
namespace
KSaneIface
{
static
int
s_objectCount
=
0
;
Q_GLOBAL_STATIC
(
QMutex
,
s_objectMutex
)
KSaneCore
::
KSaneCore
(
QObject
*
parent
)
:
QObject
(
parent
),
d
(
std
::
unique_ptr
<
KSaneCorePrivate
>
(
new
KSaneCorePrivate
(
this
)))
{
SANE_Int
version
;
SANE_Status
status
;
s_objectMutex
->
lock
();
s_objectCount
++
;
if
(
s_objectCount
==
1
)
{
// only call sane init for the first instance
status
=
sane_init
(
&
version
,
&
KSaneAuth
::
authorization
);
if
(
status
!=
SANE_STATUS_GOOD
)
{
qCDebug
(
KSANE_LOG
)
<<
"libksane: sane_init() failed("
<<
sane_strstatus
(
status
)
<<
")"
;
}
}
s_objectMutex
->
unlock
();
// read the device list to get a list of vendor and model info
d
->
m_findDevThread
->
start
();
d
->
m_readValuesTimer
.
setSingleShot
(
true
);
connect
(
&
d
->
m_readValuesTimer
,
&
QTimer
::
timeout
,
d
.
get
(),
&
KSaneCorePrivate
::
reloadValues
);
}
KSaneCore
::~
KSaneCore
()
{
closeDevice
();
s_objectMutex
->
lock
();
s_objectCount
--
;
if
(
s_objectCount
<=
0
)
{
// only delete the find-devices and authorization singletons and call sane_exit
// if this is the last instance
delete
d
->
m_findDevThread
;
delete
d
->
m_auth
;
sane_exit
();
}
s_objectMutex
->
unlock
();
}
QString
KSaneCore
::
deviceName
()
const
{
return
d
->
m_devName
;
}
QString
KSaneCore
::
deviceVendor
()
const
{
return
d
->
m_vendor
;
}
QString
KSaneCore
::
deviceModel
()
const
{
return
d
->
m_model
;
}
bool
KSaneCore
::
reloadDevicesList
()
{
/* On some SANE backends, the handle becomes invalid when
* querying for new devices. Hence, this is only allowed when
* no device is currently opened. */
if
(
d
->
m_saneHandle
==
nullptr
)
{
d
->
m_findDevThread
->
start
();
return
true
;
}
return
false
;
}
KSaneCore
::
KSaneOpenStatus
KSaneCore
::
openDevice
(
const
QString
&
deviceName
)
{
SANE_Status
status
;
if
(
d
->
m_saneHandle
!=
nullptr
)
{
// this KSaneCore already has an open device
return
KSaneOpenStatus
::
OpeningFailed
;
}
// don't bother trying to open if the device string is empty
if
(
deviceName
.
isEmpty
())
{
return
KSaneOpenStatus
::
OpeningFailed
;
}
// save the device name
d
->
m_devName
=
deviceName
;
// Try to open the device
status
=
sane_open
(
deviceName
.
toLatin1
().
constData
(),
&
d
->
m_saneHandle
);
if
(
status
==
SANE_STATUS_ACCESS_DENIED
)
{
return
KSaneOpenStatus
::
OpeningDenied
;
}
if
(
status
!=
SANE_STATUS_GOOD
)
{
qCDebug
(
KSANE_LOG
)
<<
"sane_open(
\"
"
<<
deviceName
<<
"
\"
, &handle) failed! status = "
<<
sane_strstatus
(
status
);
d
->
m_devName
.
clear
();
return
KSaneOpenStatus
::
OpeningFailed
;
}
return
d
->
loadDeviceOptions
();
}
KSaneCore
::
KSaneOpenStatus
KSaneCore
::
openRestrictedDevice
(
const
QString
&
deviceName
,
QString
userName
,
QString
password
)
{
SANE_Status
status
;
if
(
d
->
m_saneHandle
!=
nullptr
)
{
// this KSaneCore already has an open device
return
KSaneOpenStatus
::
OpeningFailed
;
}
// don't bother trying to open if the device string is empty
if
(
deviceName
.
isEmpty
())
{
return
KSaneOpenStatus
::
OpeningFailed
;
}
// save the device name
d
->
m_devName
=
deviceName
;
// add/update the device user-name and password for authentication
d
->
m_auth
->
setDeviceAuth
(
d
->
m_devName
,
userName
,
password
);
// Try to open the device
status
=
sane_open
(
deviceName
.
toLatin1
().
constData
(),
&
d
->
m_saneHandle
);
if
(
status
==
SANE_STATUS_ACCESS_DENIED
)
{
return
KSaneOpenStatus
::
OpeningDenied
;
}
if
(
status
!=
SANE_STATUS_GOOD
)
{
qCDebug
(
KSANE_LOG
)
<<
"sane_open(
\"
"
<<
deviceName
<<
"
\"
, &handle) failed! status = "
<<
sane_strstatus
(
status
);
d
->
m_auth
->
clearDeviceAuth
(
d
->
m_devName
);
d
->
m_devName
.
clear
();
return
KSaneOpenStatus
::
OpeningFailed
;
}
return
d
->
loadDeviceOptions
();
}
bool
KSaneCore
::
closeDevice
()
{
if
(
!
d
->
m_saneHandle
)
{
return
false
;
}
stopScan
();
disconnect
(
d
->
m_scanThread
);
if
(
d
->
m_scanThread
->
isRunning
())
{
connect
(
d
->
m_scanThread
,
&
QThread
::
finished
,
d
->
m_scanThread
,
&
QThread
::
deleteLater
);
}
if
(
d
->
m_scanThread
->
isFinished
())
{
d
->
m_scanThread
->
deleteLater
();
}
d
->
m_scanThread
=
nullptr
;
d
->
m_auth
->
clearDeviceAuth
(
d
->
m_devName
);
sane_close
(
d
->
m_saneHandle
);
d
->
m_saneHandle
=
nullptr
;
d
->
clearDeviceOptions
();
return
true
;
}
void
KSaneCore
::
startScan
()
{
d
->
m_cancelMultiPageScan
=
false
;
// execute a pending value reload
while
(
d
->
m_readValuesTimer
.
isActive
())
{
d
->
m_readValuesTimer
.
stop
();
d
->
reloadValues
();
}
d
->
m_optionPollTimer
.
stop
();
d
->
m_scanThread
->
start
();
}
void
KSaneCore
::
stopScan
()
{
d
->
m_cancelMultiPageScan
=
true
;
if
(
d
->
m_scanThread
->
isRunning
())
{
d
->
m_scanThread
->
cancelScan
();
}
}
QImage
*
KSaneCore
::
scanImage
()
const
{
return
d
->
m_scanThread
->
scanImage
();
}
void
KSaneCore
::
lockScanImage
()
{
d
->
m_scanThread
->
lockScanImage
();
}
void
KSaneCore
::
unlockScanImage
()
{
d
->
m_scanThread
->
unlockScanImage
();
}
QList
<
KSaneOption
*>
KSaneCore
::
getOptionsList
()
{
return
d
->
m_externalOptionsList
;
}
KSaneOption
*
KSaneCore
::
getOption
(
KSaneCore
::
KSaneOptionName
optionEnum
)
{
auto
it
=
d
->
m_optionsLocation
.
find
(
optionEnum
);
if
(
it
!=
d
->
m_optionsLocation
.
end
())
{
return
d
->
m_externalOptionsList
.
at
(
it
.
value
());
}
return
nullptr
;
}
KSaneOption
*
KSaneCore
::
getOption
(
QString
optionName
)
{
for
(
const
auto
&
option
:
qAsConst
(
d
->
m_externalOptionsList
))
{
if
(
option
->
name
()
==
optionName
)
{
return
option
;
}
}
return
nullptr
;
}
QMap
<
QString
,
QString
>
KSaneCore
::
getOptionsMap
()
{
KSaneBaseOption
*
option
;
QMap
<
QString
,
QString
>
options
;
QString
tmp
;
for
(
int
i
=
0
;
i
<
d
->
m_optionsList
.
size
();
i
++
)
{
option
=
d
->
m_optionsList
.
at
(
i
);
tmp
=
option
->
valueAsString
();
if
(
!
tmp
.
isEmpty
())
{
options
[
option
->
name
()]
=
tmp
;
}
}
return
options
;
}
int
KSaneCore
::
setOptionsMap
(
const
QMap
<
QString
,
QString
>
&
opts
)
{
if
(
d
->
m_scanThread
->
isRunning
())
{
return
-
1
;
}
QMap
<
QString
,
QString
>
optionMapCopy
=
opts
;
QString
tmp
;
int
i
;
int
ret
=
0
;
KSaneOption
*
sourceOption
=
getOption
(
SourceOption
);
KSaneOption
*
modeOption
=
getOption
(
ScanModeOption
);
// Priorize source option
if
(
sourceOption
!=
nullptr
&&
optionMapCopy
.
contains
(
sourceOption
->
name
()))
{
if
(
sourceOption
->
setValue
(
optionMapCopy
[
sourceOption
->
name
()])
)
{
ret
++
;
}
optionMapCopy
.
remove
(
sourceOption
->
name
());
}
// Priorize mode option
if
(
modeOption
!=
nullptr
&&
optionMapCopy
.
contains
(
modeOption
->
name
()))
{
if
(
modeOption
->
setValue
(
optionMapCopy
[
modeOption
->
name
()]))
{
ret
++
;
}
optionMapCopy
.
remove
(
modeOption
->
name
());
}
// Update remaining options
for
(
i
=
0
;
i
<
d
->
m_optionsList
.
size
();
i
++
)
{
const
auto
it
=
optionMapCopy
.
find
(
d
->
m_optionsList
.
at
(
i
)
->
name
());
if
(
it
!=
optionMapCopy
.
end
()
&&
d
->
m_optionsList
.
at
(
i
)
->
setValue
(
it
.
value
()))
{
ret
++
;
}
}
return
ret
;
}
}
// NameSpace KSaneIface
src/ksanecore.h
0 → 100644
View file @
c2928858
/* ============================================================
*
* SPDX-FileCopyrightText: 2007-2010 Kare Sars <kare dot sars at iki dot fi>
* SPDX-FileCopyrightText: 2007 Gilles Caulier <caulier dot gilles at gmail dot com>
* SPDX-FileCopyrightText: 2014 Gregor Mitsch : port to KDE5 frameworks
* SPDX-FileCopyrightText: 2021 Alexander Stippich <a.stippich@gmx.net>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*
* ============================================================ */
#ifndef KSANE_CORE_H
#define KSANE_CORE_H
#include "ksane_export.h"
#include <memory>
#include <QObject>
#include <QList>
#include <QImage>
/** This namespace collects all methods and classes in LibKSane. */
namespace
KSaneIface
{
class
KSaneCorePrivate
;
class
KSaneOption
;
/**
* This class provides the core interface for accessing the scan controls and options.
*/
class
KSANE_EXPORT
KSaneCore
:
public
QObject
{
Q_OBJECT
friend
class
KSaneCorePrivate
;
public:
/** @note There might come more enumerations in the future. */
enum
KSaneScanStatus
{
NoError
,
// The scanning was finished successfully
ErrorGeneral
,
// The error string should contain an error message.
Information
// There is some information to the user.
};
/** Enum determining whether the scanner opened correctly. */
enum
KSaneOpenStatus
{
OpeningSucceeded
,
// scanner opened successfully
OpeningDenied
,
// access was denied,
OpeningFailed
,
// opening the scanner failed for unknown reasons
};
/**
* This enumeration is used to obtain a specific option with getOption(KSaneOptionName).
* Depending on the backend, not all options are available, nor this list is complete.
* For the remaining options, getOptionsList() must be used.
*/
enum
KSaneOptionName
{
SourceOption
,
ScanModeOption
,
BitDepthOption
,
ResolutionOption
,
TopLeftXOption
,
TopLeftYOption
,
BottomRightXOption
,
BottomRightYOption
,
FilmTypeOption
,
NegativeOption
,
InvertColorOption
,
PageSizeOption
,
ThresholdOption
,
XResolutionOption
,
YResolutionOption
,
PreviewOption
,
WaitForButtonOption
,
BrightnessOption
,
ContrastOption
,
GammaOption
,
GammaRedOption
,
GammaGreenOption
,
GammaBlueOption
,
BlackLevelOption
,
WhiteLevelOption
,
};
struct
DeviceInfo
{
QString
name
;
/* unique device name */
QString
vendor
;
/* device vendor string */
QString
model
;
/* device model name */
QString
type
;
/* device type (e.g., "flatbed scanner") */
};
/**
* This constructor initializes the private class variables, but the widget is left empty.
* The options and the preview are added with the call to openDevice().
*/
KSaneCore
(
QObject
*
parent
=
nullptr
);
/** Standard destructor */
~
KSaneCore
();
/**
* Get the list of available scanning devices. Connect to availableDevices()
* which is fired once these devices are known.
* @return whether the devices list are reloaded or not.
*/
bool
reloadDevicesList
();
/**
* This method opens the specified scanner device and adds the scan options to the
* options list.
* @param deviceName is the libsane device name for the scanner to open.
* @return the status of the opening action.
*/
KSaneOpenStatus
openDevice
(
const
QString
&
deviceName
);
/**
* This method opens the specified scanner device with a specified username and password.
* Adds the scan options to the options list.
* @param deviceName is the libsane device name for the scanner to open.
* @param userName the username required to open for the scanner.
* @param password the password required to open for the scanner.
* @return the status of the opening action.
*/
KSaneOpenStatus
openRestrictedDevice
(
const
QString
&
deviceName
,
QString
userName
,
QString
password
);
/**
* This method closes the currently open scanner device.
* @return 'true' if all goes well and 'false' if no device is open.
*/
bool
closeDevice
();
/**
* This method returns the internal device name of the currently opened scanner.
*/
QString
deviceName
()
const
;
/**
* This method returns the vendor name of the currently opened scanner.
*/
QString
deviceVendor
()
const
;
/**
* This method returns the model of the currently opened scanner.
*/
QString
deviceModel
()
const
;
/**
* This function returns all available options when a device is opened.
* @return list containing pointers to all KSaneOptions provided by the backend.
* Becomes invalid when closing a device.
* The pointers must not be deleted by the client.
*/
QList
<
KSaneOption
*>
getOptionsList
();
/**
* This function returns a specific option.
* @param optionEnum the enum specifying the option.
* @return pointer to the KSaneOption. Returns a nullptr in case the options
* is not available for the currently opened device.
*/
KSaneOption
*
getOption
(
KSaneOptionName
optionEnum
);
/**
* This function returns a specific option.
* @param optionName the internal name of the option defined by SANE.
* @return pointer to the KSaneOption. Returns a nullptr in case the options
* is not available for the currently opened device.
*/
KSaneOption
*
getOption
(
QString
optionName
);
/**
* This method reads the available parameters and their values and
* returns them in a QMap (Name, value)
* @param opts is a QMap with the parameter names and values.
*/
QMap
<
QString
,
QString
>
getOptionsMap
();
/**
* This method can be used to write many parameter values at once.
* @param opts is a QMap with the parameter names and values.
* @return This function returns the number of successful writes
* or -1 if scanning is in progress.
*/
int
setOptionsMap
(
const
QMap
<
QString
,
QString
>
&
opts
);
/**
* Gives direct access to the QImage that is used to store the image
* data retrieved from the scanner.
* Useful to display the an in-progress image while scanning.
* When accessing the direct image pointer during a scan, the image
* must be locked before accessing the image and unlocked afterwards
* using the lockScanImage() and unlockScanImage() functions.
* @return pointer for direct access of the QImage data.
*/
QImage
*
scanImage
()
const
;
/**
* Locks the mutex protecting the QImage pointer of scanImage() from
* concurrent access during scanning.
*/
void
lockScanImage
();
/**
* Unlocks the mutex protecting the QImage pointer of scanImage() from
* concurrent access during scanning. The scanning progress will blocked
* when lockScanImage() is called until unlockScanImage() is called.
*/
void
unlockScanImage
();
public
Q_SLOTS
:
/**
* This method can be used to cancel a scan or prevent an automatic new scan.
*/
void
stopScan
();
/**
* This method can be used to start a scan (if no GUI is needed).
* @note libksane may return one or more images as a result of one invocation of this slot.
* If no more images are wanted scanCancel should be called in the slot handling the
* imageReady signal.
*/
void
startScan
();
Q_SIGNALS:
/**
* This signal is emitted when a final scan is ready.
* @param scannedImage is the QImage containing the scanned image data.
*/
void
scannedImageReady
(
const
QImage
&
scannedImage
);
/**
* This signal is emitted when the scanning has ended.
* @param status contains a ScanStatus status code.
* @param strStatus If an error has occurred this string will contain an error message.
* otherwise the string is empty.
*/
void
scanFinished
(
KSaneScanStatus
status
,
const
QString
&
strStatus
);
/**
* This signal is emitted when the user is to be notified about something.
* @note If no slot is connected to this signal the message will be displayed in a KMessageBox.
* @param type contains a ScanStatus code to identify the type of message (error/info/...).
* @param strStatus If an error has occurred this string will contain an error message.
* otherwise the string is empty.
*/
void
userMessage
(
KSaneScanStatus
status
,
const
QString
&
strStatus
);
/**
* This signal is emitted for progress information during a scan.
* The GUI already has a progress bar, but if the GUI is hidden,
* this can be used to display a progress bar.
* @param percent is the percentage of the scan progress (0-100).
*/
void
scanProgress
(
int
percent
);
/**
* This signal is emitted every time the device list is updated or
* after initGetDeviceList() is called.
* @param deviceList is a QList of KSaneCore::DeviceInfo that contain the
* device name, model, vendor and type of the attached scanners.
* @note The list is only a snapshot of the current available devices. Devices
* might be added or removed/opened after the signal is emitted.
*/
void
availableDevices
(
const
QList
<
KSaneCore
::
DeviceInfo
>
&
deviceList
);
/**
* This signal is emitted when a hardware button is pressed.
* @param optionName is the untranslated technical name of the sane-option.
* @param optionLabel is the translated user visible label of the sane-option.
* @param pressed indicates if the value is true or false.
* @note The SANE standard does not specify hardware buttons and their behaviors,
* so this signal is emitted for sane-options that behave like hardware buttons.
* That is the sane-options are read-only and type boolean. The naming of hardware
* buttons also differ from backend to backend.
*/
void
buttonPressed
(
const
QString
&
optionName
,
const
QString
&
optionLabel
,
bool
pressed
);
/**
* This signal is emitted when the device info of the already opened scanner device
* is updated and vendor() and model() return the corresponding names.
* @param deviceName is the technical device name of the currently opened scanner.
* @param deivceVendor is the vendor of the currently opened scanner.
* @param deviceModel is the model name of the currently opened scanner.
*/
void
openedDeviceInfoUpdated
(
const
QString
&
deviceName
,
const
QString
&
deivceVendor
,
const
QString
&
deviceModel
);
private:
std
::
unique_ptr
<
KSaneCorePrivate
>
d
;
};
}
// NameSpace KSaneIface
#endif // KSANE_CORE_H
src/ksanecore_p.cpp
0 → 100644
View file @
c2928858
/* ============================================================
*
* SPDX-FileCopyrightText: 2007-2008 Kare Sars <kare dot sars at iki dot fi>
* SPDX-FileCopyrightText: 2007-2008 Gilles Caulier <caulier dot gilles at gmail dot com>
* SPDX-FileCopyrightText: 2014 Gregor Mitsch : port to KDE5 frameworks
* SPDX-FileCopyrightText: 2021 Alexander Stippich <a.stippich@gmx.net>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*