blob: ea77f33ef506dafca2bd6cb54df803187b27ee4b [file] [log] [blame]
// Copyright 2020 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <cstdint>
#include <memory>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
#include <base/optional.h>
#include <base/synchronization/lock.h>
#include <brillo/errors/error.h>
#include <lorgnette/proto_bindings/lorgnette_service.pb.h>
#include <sane/sane.h>
#include "lorgnette/sane_client.h"
namespace lorgnette {
using DeviceSet = std::pair<base::Lock, std::unordered_set<std::string>>;
class SaneClientImpl : public SaneClient {
static std::unique_ptr<SaneClientImpl> Create();
base::Optional<std::vector<ScannerInfo>> ListDevices(
brillo::ErrorPtr* error) override;
static base::Optional<std::vector<ScannerInfo>> DeviceListToScannerInfo(
const SANE_Device** device_list);
std::unique_ptr<SaneDevice> ConnectToDeviceInternal(
brillo::ErrorPtr* error, const std::string& device_name) override;
base::Lock lock_;
std::shared_ptr<DeviceSet> open_devices_;
class SaneOption {
SaneOption(const SANE_Option_Descriptor& opt, int index);
bool Set(double d);
bool Set(int i);
bool Set(const std::string& s);
template <typename T>
base::Optional<T> Get() const = delete;
template <>
base::Optional<int> Get() const;
template <>
base::Optional<std::string> Get() const;
// This returns a pointer to the internal storage. Care must be taken that the
// pointer does not outlive the SaneOption.
void* GetPointer();
int GetIndex() const;
std::string GetName() const;
std::string DisplayValue() const;
std::string name_;
int index_;
SANE_Value_Type type_; // The type that the backend uses for the option.
// The integer data, if this is an int option.
union {
SANE_Int i;
SANE_Fixed f;
} int_data_;
// The buffer containing string data, if this is a string option.
std::vector<char> string_data_;
// Represents the possible values for an option.
struct OptionRange {
double start;
double size;
class SaneDeviceImpl : public SaneDevice {
friend class SaneClientImpl;
base::Optional<ValidOptionValues> GetValidOptionValues(
brillo::ErrorPtr* error) override;
base::Optional<int> GetScanResolution(brillo::ErrorPtr* error) override;
bool SetScanResolution(brillo::ErrorPtr* error, int resolution) override;
base::Optional<std::string> GetDocumentSource(
brillo::ErrorPtr* error) override;
bool SetDocumentSource(brillo::ErrorPtr* error,
const std::string& source_name) override;
bool SetColorMode(brillo::ErrorPtr* error, ColorMode color_mode) override;
bool SetScanRegion(brillo::ErrorPtr* error,
const ScanRegion& region) override;
SANE_Status StartScan(brillo::ErrorPtr* error) override;
base::Optional<ScanParameters> GetScanParameters(
brillo::ErrorPtr* error) override;
SANE_Status ReadScanData(brillo::ErrorPtr* error,
uint8_t* buf,
size_t count,
size_t* read_out) override;
bool CancelScan(brillo::ErrorPtr* error) override;
static base::Optional<std::vector<std::string>> GetValidStringOptionValues(
brillo::ErrorPtr* error, const SANE_Option_Descriptor& opt);
static base::Optional<std::vector<uint32_t>> GetValidIntOptionValues(
brillo::ErrorPtr* error, const SANE_Option_Descriptor& opt);
static base::Optional<OptionRange> GetOptionRange(
brillo::ErrorPtr* error, const SANE_Option_Descriptor& opt);
friend class SaneDeviceImplTest;
enum ScanOption {
SaneDeviceImpl(SANE_Handle handle,
const std::string& name,
std::shared_ptr<DeviceSet> open_devices);
bool LoadOptions(brillo::ErrorPtr* error);
bool UpdateDeviceOption(brillo::ErrorPtr* error, SaneOption* option);
base::Optional<ScannableArea> CalculateScannableArea(brillo::ErrorPtr* error);
base::Optional<double> GetOptionOffset(brillo::ErrorPtr* error,
ScanOption option);
const char* OptionDisplayName(ScanOption option);
template <typename T>
bool SetOption(brillo::ErrorPtr* error, ScanOption option, T value);
template <typename T>
base::Optional<T> GetOption(brillo::ErrorPtr* error, ScanOption option);
SANE_Handle handle_;
std::string name_;
std::shared_ptr<DeviceSet> open_devices_;
std::unordered_map<ScanOption, SaneOption> options_;
// This is true if we are currently acquiring an image frame (i.e. page) from
// SANE. Once we've reached EOF for a frame, this will be false until
// another call is made to StartScan().
bool scan_running_;
} // namespace lorgnette