code_device/twain/ds/huagaods.cpp

4024 lines
134 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#ifdef max
#undef max
#endif
#ifdef min
#undef min
#endif
#include <memory>
#include "huagaods.hpp"
//#include "twpp/twglue.hpp"
#include <list>
#include <map>
#if defined(WIN32) || defined(_WIN64) // WIN32
#include <shlobj.h>
#include <Psapi.h>
#include <io.h>
#define mktemp _mktemp
#endif // WIN32
#include <algorithm>
using namespace std;
#define enum2str(R) #R
#pragma warning(disable: 4700)
using namespace Twpp;
using namespace std::placeholders;
extern HMODULE me_;
// WIA COM: IStiUSD & IWiaMiniDrv
//custom define caps enum
enum CapTypeEx : unsigned short {
CAP_TYPE_EX_FILL_BLACK_BKG = 0x8004,
CAP_TYPE_EX_ROTATE_BKG_180 = 0x8005,
CAP_TYPE_EX_SCREW_DETECT = 0x8006,
CAP_TYPE_EX_ENHANCE_COLOR = 0x8007,
CAP_TYPE_EX_DARK_SAMPLE = 0x8016,
CAP_TYPE_EX_FILL_HOLE = 0x8018,
CAP_TYPE_EX_SCREW_DETECT_LEVEL = 0x8021,
CAP_TYPE_EX_SHARPEN = 0x8022,
CAP_TYPE_EX_HARDWARE_VERSION = 0x8025,
CAP_TYPE_EX_RID_RED = 0x8026,
CAP_TYPE_EX_FOLD = 0x8037,
CAP_TYPE_EX_STAPLE_DETECT = 0x8090,
CAP_TYPE_EX_DISCARD_BLANK_RECEIPT = 0x8091,
CAP_TYPE_EX_FILL_HOLE_RATIO = 0x8092,
CAP_TYPE_EX_FLIP = 0x8094, // switch front back
CAP_TYPE_EX_RID_RED_HSV = 0x8095,
CAP_TYPE_EX_DOGEAR_DETECT = 0x8096,
CAP_TYPE_EX_BKG_FILLING_METHOD = 0x8097,
CAP_TYPE_EX_EDGE_IDENT = 0x8098,
CAP_TYPE_EX_ANTI_NOISE = 0x8099,
CAP_TYPE_EX_THRESHOLD = 0x8100,
CAP_TYPE_EX_DETACH_NOISE = 0x8101,
CAP_TYPE_EX_DETACH_NOISE_THRESHOLD = 0x8102,
CAP_TYPE_EX_SIZE_DETECT = 0x8103,
CAP_TYPE_EX_POWER_LEVEL = 0x8104,
CAP_TYPE_EX_ENCODE = 0x8105,
CAP_TYPE_EX_CROP_MODEL = 0x8106,
CAP_TYPE_EX_DOGEAR_DIST = 0x8107,
CAP_TYPE_EX_IMAGE_SPLIT = 0x8108,
CAP_TYPE_EX_FADE_BKG = 0x8109,
CAP_TYPE_EX_FADE_BKG_VALUE = 0x8110,
CAP_TYPE_EX_TO_BE_SCAN = 0x8111,
CAP_TYPE_EX_MULTI_OUT = 0x8112,
CAP_TYPE_EX_MULTI_OUT_TYPE = 0x8113,
CAP_TYPE_EX_SCAN_WITH_HOLE = 0x8114,
CAP_TYPE_EX_IP = 0x8200,
};
enum // .twain/first.cfg: [twain-app] flow=0
{
TWAIN_APP_TRANSFER_NORMAL = 0, // first get head and then bits
TWAIN_APP_TRANSFER_REVERSE, // first get bits and then head
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// entry ...
TWPP_ENTRY(huagao_ds)
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// utilites ...
// some helper functions to handle capability stuff
#define RETURN_ENUM_DESC(en, v, space) \
if(v == space::en) \
return #en;
const char* desc_state(DsState s, char unk[20])
{
RETURN_ENUM_DESC(Closed, s, DsState);
RETURN_ENUM_DESC(Open, s, DsState);
RETURN_ENUM_DESC(Enabled, s, DsState);
RETURN_ENUM_DESC(XferReady, s, DsState);
RETURN_ENUM_DESC(Xferring, s, DsState);
sprintf(unk, "%d", s);
return unk;
}
const char* desc_data_group(DataGroup d, char unk[20])
{
RETURN_ENUM_DESC(Control, d, DataGroup);
RETURN_ENUM_DESC(Image, d, DataGroup);
RETURN_ENUM_DESC(Audio, d, DataGroup);
{
sprintf(unk, "%d", d);
return unk;
}
}
const char* desc_data(Dat d, char unk[20])
{
RETURN_ENUM_DESC(Null, d, Dat);
RETURN_ENUM_DESC(Capability, d, Dat);
RETURN_ENUM_DESC(Event, d, Dat);
RETURN_ENUM_DESC(Identity, d, Dat);
RETURN_ENUM_DESC(Parent, d, Dat);
RETURN_ENUM_DESC(PendingXfers, d, Dat);
RETURN_ENUM_DESC(SetupMemXfer, d, Dat);
RETURN_ENUM_DESC(SetupFileXfer, d, Dat);
RETURN_ENUM_DESC(Status, d, Dat);
RETURN_ENUM_DESC(UserInterface, d, Dat);
RETURN_ENUM_DESC(XferGroup, d, Dat);
RETURN_ENUM_DESC(CustomData, d, Dat);
RETURN_ENUM_DESC(DeviceEvent, d, Dat);
RETURN_ENUM_DESC(FileSystem, d, Dat);
RETURN_ENUM_DESC(PassThrough, d, Dat);
RETURN_ENUM_DESC(Callback, d, Dat);
RETURN_ENUM_DESC(StatusUtf8, d, Dat);
RETURN_ENUM_DESC(Callback2, d, Dat);
RETURN_ENUM_DESC(ImageInfo, d, Dat);
RETURN_ENUM_DESC(ImageLayout, d, Dat);
RETURN_ENUM_DESC(ImageMemXfer, d, Dat);
RETURN_ENUM_DESC(ImageNativeXfer, d, Dat);
RETURN_ENUM_DESC(ImageFileXfer, d, Dat);
RETURN_ENUM_DESC(CieColor, d, Dat);
RETURN_ENUM_DESC(GrayResponse, d, Dat);
RETURN_ENUM_DESC(RgbResponse, d, Dat);
RETURN_ENUM_DESC(JpegCompression, d, Dat);
RETURN_ENUM_DESC(Palette8, d, Dat);
RETURN_ENUM_DESC(ExtImageInfo, d, Dat);
RETURN_ENUM_DESC(Filter, d, Dat);
RETURN_ENUM_DESC(AudioFileXfer, d, Dat);
RETURN_ENUM_DESC(AudioInfo, d, Dat);
RETURN_ENUM_DESC(AudioNativeXfer, d, Dat);
RETURN_ENUM_DESC(IccProfile, d, Dat);
RETURN_ENUM_DESC(ImageMemFileXfer, d, Dat);
RETURN_ENUM_DESC(EntryPoint, d, Dat);
{
sprintf(unk, "%d", d);
return unk;
}
}
const char* desc_msg(Msg m, char unk[20])
{
RETURN_ENUM_DESC(Null, m, Msg);
RETURN_ENUM_DESC(Get, m, Msg);
RETURN_ENUM_DESC(GetCurrent, m, Msg);
RETURN_ENUM_DESC(GetDefault, m, Msg);
RETURN_ENUM_DESC(GetFirst, m, Msg);
RETURN_ENUM_DESC(GetNext, m, Msg);
RETURN_ENUM_DESC(Set, m, Msg);
RETURN_ENUM_DESC(Reset, m, Msg);
RETURN_ENUM_DESC(QuerySupport, m, Msg);
RETURN_ENUM_DESC(GetHelp, m, Msg);
RETURN_ENUM_DESC(GetLabel, m, Msg);
RETURN_ENUM_DESC(GetLabelEnum, m, Msg);
RETURN_ENUM_DESC(SetConstraint, m, Msg);
RETURN_ENUM_DESC(XferReady, m, Msg);
RETURN_ENUM_DESC(CloseDsReq, m, Msg);
RETURN_ENUM_DESC(CloseDsOk, m, Msg);
RETURN_ENUM_DESC(DeviceEvent, m, Msg);
RETURN_ENUM_DESC(OpenDsm, m, Msg);
RETURN_ENUM_DESC(CloseDsm, m, Msg);
RETURN_ENUM_DESC(OpenDs, m, Msg);
RETURN_ENUM_DESC(CloseDs, m, Msg);
RETURN_ENUM_DESC(UserSelect, m, Msg);
RETURN_ENUM_DESC(DisableDs, m, Msg);
RETURN_ENUM_DESC(EnableDs, m, Msg);
RETURN_ENUM_DESC(EnableDsUiOnly, m, Msg);
RETURN_ENUM_DESC(ProcessEvent, m, Msg);
RETURN_ENUM_DESC(EndXfer, m, Msg);
RETURN_ENUM_DESC(StopFeeder, m, Msg);
RETURN_ENUM_DESC(ChangeDir, m, Msg);
RETURN_ENUM_DESC(CreateDir, m, Msg);
RETURN_ENUM_DESC(Delete, m, Msg);
RETURN_ENUM_DESC(FormatMedia, m, Msg);
RETURN_ENUM_DESC(GetClose, m, Msg);
RETURN_ENUM_DESC(GetFirstFile, m, Msg);
RETURN_ENUM_DESC(GetInfo, m, Msg);
RETURN_ENUM_DESC(GetNextFile, m, Msg);
RETURN_ENUM_DESC(Rename, m, Msg);
RETURN_ENUM_DESC(Copy, m, Msg);
RETURN_ENUM_DESC(AutomaticCaptureDir, m, Msg);
RETURN_ENUM_DESC(PassThrough, m, Msg);
RETURN_ENUM_DESC(RegisterCallback, m, Msg);
RETURN_ENUM_DESC(ResetAll, m, Msg);
RETURN_ENUM_DESC(CustomBase, m, Msg);
{
sprintf(unk, "%d", m);
return unk;
}
}
const char* desc_return_code(ReturnCode rc, char unk[20])
{
RETURN_ENUM_DESC(Success, rc, ReturnCode);
RETURN_ENUM_DESC(Failure, rc, ReturnCode);
RETURN_ENUM_DESC(CheckStatus, rc, ReturnCode);
RETURN_ENUM_DESC(Cancel, rc, ReturnCode);
RETURN_ENUM_DESC(DsEvent, rc, ReturnCode);
RETURN_ENUM_DESC(NotDsEvent, rc, ReturnCode);
RETURN_ENUM_DESC(XferDone, rc, ReturnCode);
RETURN_ENUM_DESC(EndOfList, rc, ReturnCode);
RETURN_ENUM_DESC(InfoNotSupported, rc, ReturnCode);
RETURN_ENUM_DESC(DataNotAvailable, rc, ReturnCode);
RETURN_ENUM_DESC(Busy, rc, ReturnCode);
RETURN_ENUM_DESC(ScannerLocked, rc, ReturnCode);
{
sprintf(unk, "%d", rc);
return unk;
}
}
const char* desc_condition_code(ConditionCode c, char unk[20])
{
RETURN_ENUM_DESC(Success, c, ConditionCode);
RETURN_ENUM_DESC(Bummer, c, ConditionCode);
RETURN_ENUM_DESC(LowMemory, c, ConditionCode);
RETURN_ENUM_DESC(NoDs, c, ConditionCode);
RETURN_ENUM_DESC(MaxConnections, c, ConditionCode);
RETURN_ENUM_DESC(OperationError, c, ConditionCode);
RETURN_ENUM_DESC(BadCap, c, ConditionCode);
RETURN_ENUM_DESC(BadProtocol, c, ConditionCode);
RETURN_ENUM_DESC(BadValue, c, ConditionCode);
RETURN_ENUM_DESC(SeqError, c, ConditionCode);
RETURN_ENUM_DESC(BadDest, c, ConditionCode);
RETURN_ENUM_DESC(CapUnsupported, c, ConditionCode);
RETURN_ENUM_DESC(CapBadOperation, c, ConditionCode);
RETURN_ENUM_DESC(CapSeqError, c, ConditionCode);
RETURN_ENUM_DESC(Denied, c, ConditionCode);
RETURN_ENUM_DESC(FileExists, c, ConditionCode);
RETURN_ENUM_DESC(FileNotFound, c, ConditionCode);
RETURN_ENUM_DESC(NotEmpty, c, ConditionCode);
RETURN_ENUM_DESC(PaperJam, c, ConditionCode);
RETURN_ENUM_DESC(PaperDoubleFeed, c, ConditionCode);
RETURN_ENUM_DESC(FileWriteError, c, ConditionCode);
RETURN_ENUM_DESC(CheckDeviceOnline, c, ConditionCode);
RETURN_ENUM_DESC(InterLock, c, ConditionCode);
RETURN_ENUM_DESC(DamagedCorner, c, ConditionCode);
RETURN_ENUM_DESC(FocusError, c, ConditionCode);
RETURN_ENUM_DESC(DocTooLight, c, ConditionCode);
RETURN_ENUM_DESC(DocTooDark, c, ConditionCode);
RETURN_ENUM_DESC(NoMedia, c, ConditionCode);
{
sprintf(unk, "%d", c);
return unk;
}
}
template<typename T>
static Result oneValGet(Msg msg, Capability& data, const T& value) {
switch (msg) {
case Msg::Get:
case Msg::GetCurrent:
case Msg::GetDefault:
data = Capability::createOneValue(data.type(), value);
return {};
default:
return { ReturnCode::Failure, ConditionCode::CapBadOperation };
}
}
static Result oneValGetString(Msg msg, Capability& data, std::string value) {
Str255 str;
str.setData(value.c_str(), value.size());
return oneValGet(msg, data, str);
}
template<typename T>
static Result enmGet(Msg msg, Capability& data, const T& value) {
switch (msg) {
case Msg::Get:
data = Capability::createEnumeration(data.type(), { value });
return {};
case Msg::GetCurrent:
case Msg::GetDefault:
data = Capability::createOneValue(data.type(), value);
return {};
default:
return { ReturnCode::Failure, ConditionCode::CapBadOperation };
}
}
template<typename T>
static Result oneValGetSet(Msg msg, Capability& data, T& value, const T& def) {
switch (msg) {
case Msg::Reset:
value = def;
// fallthrough
case Msg::Get:
case Msg::GetCurrent:
data = Capability::createOneValue(data.type(), value);
return {};
case Msg::GetDefault:
data = Capability::createOneValue(data.type(), def);
return {};
case Msg::Set:
value = data.currentItem<T>();
return {};
default:
return { ReturnCode::Failure, ConditionCode::CapBadOperation };
}
}
template<typename T>
static Result oneValGetSetConst(Msg msg, Capability& data, const T& def) {
switch (msg) {
case Msg::Get:
case Msg::GetCurrent:
case Msg::GetDefault:
case Msg::Reset:
data = Capability::createOneValue(data.type(), def);
return {};
case Msg::Set:
return data.currentItem<T>() == def ?
Result() : Result(ReturnCode::Failure, ConditionCode::BadValue);
default:
return { ReturnCode::Failure, ConditionCode::CapBadOperation };
}
}
template<typename T>
static Result enmGetSetConst(Msg msg, Capability& data, const T& def) {
switch (msg) {
case Msg::Get:
data = Capability::createEnumeration(data.type(), { def });
return {};
case Msg::GetCurrent:
case Msg::GetDefault:
case Msg::Reset:
data = Capability::createOneValue(data.type(), def);
return {};
case Msg::Set:
return data.currentItem<T>() == def ?
Result() : Result(ReturnCode::Failure, ConditionCode::BadValue);
default:
return { ReturnCode::Failure, ConditionCode::CapBadOperation };
}
}
template<typename T1, typename T2, Twpp::CapType cap>
Result CapSupGetAllReset(Msg msg, Capability& data, std::initializer_list<T2> values, T1& currvalue, T2 defaultvalue, UInt32 currindex, UInt32 defaultindex) {
switch (msg) {
case Msg::Get:
data = Capability::createEnumeration<cap>(values, currindex, defaultindex);
return { ReturnCode::Success, ConditionCode::Success };
case Msg::GetCurrent:
data = Capability::createOneValue<cap>((T2)currvalue);
return { ReturnCode::Success, ConditionCode::Success };
case Msg::Reset:
case Msg::GetDefault:
currvalue = (T1)defaultvalue;
data = Capability::createOneValue<cap>(defaultvalue);
return { ReturnCode::Success, ConditionCode::Success };
default:
return { ReturnCode::Failure, ConditionCode::CapBadOperation };
}
}
template<typename T1, typename T2, Twpp::CapType cap>
Result CapSupGetAllReset(Msg msg, Capability& data, std::list<T2> values, T1& currvalue, T2 defaultvalue, UInt32 currindex, UInt32 defaultindex) {
switch (msg) {
case Msg::Get:
data = Capability::createEnumeration<cap>(values, currindex, defaultindex);
return { ReturnCode::Success, ConditionCode::Success };
case Msg::GetCurrent:
data = Capability::createOneValue<cap>((T2)currvalue);
return { ReturnCode::Success, ConditionCode::Success };
case Msg::Reset:
case Msg::GetDefault:
currvalue = (T1)defaultvalue;
data = Capability::createOneValue<cap>(defaultvalue);
return { ReturnCode::Success, ConditionCode::Success };
default:
return { ReturnCode::Failure, ConditionCode::CapBadOperation };
}
}
template<typename T1, typename T2, Twpp::CapType cap>
Result CapSupGetAllResetEx(Msg msg, Capability& data, std::initializer_list<T2> values, T1& currvalue, T2 defaultvalue, UInt32 currindex, UInt32 defaultindex) {
switch (msg) {
case Msg::Get:
data = Capability::createEnumeration<T2>(cap, values, currindex, defaultindex);
return { ReturnCode::Success, ConditionCode::Success };
case Msg::GetCurrent:
data = Capability::createOneValue<T2>(cap, (T2)currvalue);
return { ReturnCode::Success, ConditionCode::Success };
case Msg::Reset:
case Msg::GetDefault:
currvalue = (T1)defaultvalue;
data = Capability::createOneValue<T2>(cap, defaultvalue);
return { ReturnCode::Success, ConditionCode::Success };
default:
return { ReturnCode::Failure, ConditionCode::CapBadOperation };
}
}
template<typename T1, typename T2, Twpp::CapType cap>
Result CapSupGetAllResetEx(Msg msg, Capability& data, std::list<T2> values, T1& currvalue, T2 defaultvalue, UInt32 currindex, UInt32 defaultindex) {
switch (msg) {
case Msg::Get:
data = Capability::createEnumeration<T2>(cap, values, currindex, defaultindex);
return { ReturnCode::Success, ConditionCode::Success };
case Msg::GetCurrent:
data = Capability::createOneValue<T2>(cap, (T2)currvalue);
return { ReturnCode::Success, ConditionCode::Success };
case Msg::Reset:
case Msg::GetDefault:
currvalue = (T1)defaultvalue;
data = Capability::createOneValue<T2>(cap, defaultvalue);
return { ReturnCode::Success, ConditionCode::Success };
default:
return { ReturnCode::Failure, ConditionCode::CapBadOperation };
}
}
template<typename T1, typename T2, Twpp::CapType cap>
Result CapSupGetAllReset(Msg msg, Capability& data, T1& currvalue, T2 defaultvalue) {
switch (msg) {
case Msg::Get:
case Msg::GetCurrent:
data = Capability::createOneValue<cap>((T2)currvalue);
return { ReturnCode::Success, ConditionCode::Success };
case Msg::Reset:
case Msg::GetDefault:
currvalue = (T1)defaultvalue;
data = Capability::createOneValue<cap>(defaultvalue);
return { ReturnCode::Success, ConditionCode::Success };
default:
return { ReturnCode::Failure, ConditionCode::CapBadOperation };
}
}
template<typename T1, typename T2, Twpp::CapType cap>
Result CapSupGetAllResetEx(Msg msg, Capability& data, T1& currvalue, T2 defaultvalue) {
switch (msg) {
case Msg::Get:
case Msg::GetCurrent:
data = Capability::createOneValue<T2>(cap, (T2)currvalue);
return { ReturnCode::Success, ConditionCode::Success };
case Msg::Reset:
case Msg::GetDefault:
currvalue = (T1)defaultvalue;
data = Capability::createOneValue<T2>(cap, defaultvalue);
return { ReturnCode::Success, ConditionCode::Success };
default:
return { ReturnCode::Failure, ConditionCode::CapBadOperation };
}
}
template<typename T, Twpp::CapType cap>
Result cap_get_enum_values(Msg msg, Capability& data, std::list<T> values, T& cur, T& def, UInt32 cur_ind, UInt32 def_ind)
{
switch (msg)
{
case Msg::Get:
data = Capability::createEnumeration<T>(cap, values.size(), cur_ind, def_ind);
//data = Capability::createArray<T>(cap, values.size());
{
auto arr = data.enumeration<T>();
int i = 0;
for (const auto& v : values)
arr[i++] = v;
}
return { ReturnCode::Success, ConditionCode::Success };
case Msg::GetCurrent:
data = Capability::createOneValue<T>(cap, cur);
return { ReturnCode::Success, ConditionCode::Success };
case Msg::Reset:
case Msg::GetDefault:
data = Capability::createOneValue<T>(cap, def);
return { ReturnCode::Success, ConditionCode::Success };
default:
return { ReturnCode::Failure, ConditionCode::CapBadOperation };
}
}
template<typename T, Twpp::CapType cap>
Result cap_get_one_value(Msg msg, Capability& data, T& cur, T& def, T* lower, T* upper, T* step)
{
switch (msg)
{
case Msg::Get:
if (lower && upper && step)
{
data = Capability::createRange<T>(cap, *lower, *upper, *step, cur, def);
return { ReturnCode::Success, ConditionCode::Success };
}
case Msg::GetCurrent:
data = Capability::createOneValue<T>(cap, cur);
return { ReturnCode::Success, ConditionCode::Success };
case Msg::Reset:
case Msg::GetDefault:
data = Capability::createOneValue<T>(cap, def);
return { ReturnCode::Success, ConditionCode::Success };
default:
return { ReturnCode::Failure, ConditionCode::CapBadOperation };
}
}
template<typename T1, typename T2, Twpp::CapType cap>
Result CapSupGetAll(Msg msg, Capability& data, T1& currvalue, T2 defaultvalue) {
switch (msg) {
case Msg::Get:
case Msg::GetCurrent:
case Msg::GetDefault:
data = Capability::createOneValue<cap>((T2)defaultvalue);
return { ReturnCode::Success, ConditionCode::Success };
default:
return { ReturnCode::Failure, ConditionCode::CapBadOperation };
}
}
template<typename T1, typename T2, Twpp::CapType cap>
Result CapSupGetAllEx(Msg msg, Capability& data, T1& currvalue, T2 defaultvalue) {
switch (msg) {
case Msg::Get:
case Msg::GetCurrent:
case Msg::GetDefault:
data = Capability::createOneValue<cap, T2>(defaultvalue);
return { ReturnCode::Success, ConditionCode::Success };
default:
return { ReturnCode::Failure, ConditionCode::CapBadOperation };
}
}
static void copy_type(Bool& to, bool from)
{
to = from;
}
static void copy_type(bool& to, Bool from)
{
to = (bool)from;
}
static void copy_type(BYTE& to, bool from)
{
to = from;
}
static void copy_type(bool& to, BYTE from)
{
to = (bool)from;
}
static void copy_type(UInt32& to, int from)
{
to = from;
}
static void copy_type(int& to, UInt32 from)
{
to = from;
}
static void copy_type(Fix32& to, double from)
{
to = (float)from;
}
static void copy_type(Fix32& to, float from)
{
to = from;
}
static void copy_type(float& to, Fix32 from)
{
to = from.toFloat();
}
static void copy_type(Str255& to, std::string from)
{
to.setData(utils::utf82ansi(from.c_str()).c_str());
}
static void copy_type(std::string& to, Str255 from)
{
to = std::move(utils::ansi2utf8(from.data()));
}
static void copy_type(Str64& to, std::string from)
{
to.setData(utils::utf82ansi(from.c_str()).c_str());
}
static void copy_type(std::string& to, Str64 from)
{
to = std::move(utils::ansi2utf8(from.data()));
}
template<typename T>
bool list_value_at(std::list<T>& lst, int ind, T& t)
{
bool found = false;
if (ind >= 0)
{
typename std::list<T>::iterator it = lst.begin();
for (; it != lst.end(); ++it, --ind)
{
if (ind == 0)
{
t = *it;
found = true;
break;
}
}
}
return found;
}
template<typename T>
int distance(std::vector<T>& vec, const T& v, int offset = sane_opts::RANGE_POS_ENUM_BEGIN)
{
return std::distance(vec.begin() + offset,
std::find(vec.begin() + offset, vec.end(), v));
}
UINT16 bit_depth_from_sane(int sane)
{
if (sane == COLOR_BW)
return 1;
else if (sane == COLOR_GRAY)
return 8;
else if (sane == COLOR_RGB)
return 24;
else
return /*sane*/24;
}
static ImageFileFormat from_sane_image_type(int sane_img_type)
{
ImageFileFormat fmt = ImageFileFormat::Bmp;
switch (sane_img_type)
{
case SANE_IMAGE_TYPE_PNG:
fmt = ImageFileFormat::Png;
break;
case SANE_IMAGE_TYPE_JPG:
fmt = ImageFileFormat::Jpx;
break;
case SANE_IMAGE_TYPE_TIFF:
fmt = ImageFileFormat::Tiff;
break;
case SANE_IMAGE_TYPE_JFIF:
fmt = ImageFileFormat::Jfif;
break;
case SANE_IMAGE_TYPE_WEBP:
break;
case SANE_IMAGE_TYPE_PDF:
fmt = ImageFileFormat::Pdf;
break;
case SANE_IMAGE_TYPE_GIF:
break;
case SANE_IMAGE_TYPE_SVG:
break;
default:
break;
}
return fmt;
}
struct
{
Filter twain;
int sane;
}g_filter[] = { {Filter::Red, FILTER_RED}, {Filter::Green, FILTER_GREEN}, {Filter::Blue, FILTER_BLUE}, {Filter::None, FILTER_NONE} };
Filter from_sane_filter(int sane)
{
for (int i = 0; i < _countof(g_filter); ++i)
{
if (g_filter[i].sane == sane)
return g_filter[i].twain;
}
return Filter::None;
}
int to_sane_filter(Filter twain)
{
for (int i = 0; i < _countof(g_filter); ++i)
{
if (g_filter[i].twain == twain)
return g_filter[i].sane;
}
return FILTER_NONE;
}
struct
{
Filter twain;
int sane;
}g_enhance[] = { {Filter::Red, ENHANCE_RED}, {Filter::Green, ENHANCE_GREEN}, {Filter::Blue, ENHANCE_BLUE}, {Filter::None, ENHANCE_NONE} };
Filter from_sane_enhance(int sane)
{
for (int i = 0; i < _countof(g_filter); ++i)
{
if (g_filter[i].sane == sane)
return g_filter[i].twain;
}
return Filter::None;
}
int to_sane_enhance(Filter twain)
{
for (int i = 0; i < _countof(g_filter); ++i)
{
if (g_filter[i].twain == twain)
return g_filter[i].sane;
}
return FILTER_NONE;
}
float trans_range(float val, float min_from, float max_from, float min_to, float max_to, bool for_step = false)
{
// transfer val in range [min_from, max_from] to value in range [min_to, max_to]
if (for_step)
{
val /= max_from - min_from;
val *= max_to - min_to;
}
else
{
val -= min_from;
val /= max_from - min_from;
val *= max_to - min_to;
val += min_to;
}
return val;
}
static void log_attr_access(int attr, int method)
{
const char* op = "Unknown Oper";
#define METHOD_DESC(oper) \
if (method == (int)Msg::oper) \
op = #oper;
METHOD_DESC(Set)
else METHOD_DESC(Reset)
else METHOD_DESC(Get)
else METHOD_DESC(GetCurrent)
else METHOD_DESC(GetDefault)
utils::to_log(1, "%s 0x%04x\r\n", op, attr);
}
// get fixed-ids from SANE option ...
static bool got_fixed_id(uint32_t id, void* param)
{
((std::vector<uint32_t>*)param)->push_back(id);
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// huagao_ds ...
#include "../../../sdk/include/huagao/brand.h"
static Identity* srcIdent = nullptr;
// static Identity* srcIdent = new Identity(
// Version(VERSION_MAIN, VERSION_SUB, Language::English, Country::China, VERSION_STR(VERSION_MAIN, VERSION_SUB, VERSION_BUILD1, VERSION_PATCH)),
// DataGroup::Image, PRODUCT_VENDOR, PRODUCT_FAMILY, Str32(product_twain_name.c_str()));
static void init_identity(void)
{
if(!srcIdent)
{
Str32 n;
n.setData((std::string(PRODUCT_NAME) + " TWAIN").c_str());
srcIdent = new Identity(Version(VERSION_MAIN, VERSION_SUB, Language::English, Country::China, VERSION_STR(VERSION_MAIN, VERSION_SUB, VERSION_BUILD1, VERSION_PATCH)),
DataGroup::Image, PRODUCT_VENDOR, PRODUCT_FAMILY, n);
}
}
class my_cleanup
{
public:
my_cleanup()
{}
~my_cleanup()
{
if (srcIdent)
delete srcIdent;
srcIdent = nullptr;
}
};
static my_cleanup clear_global_objects_;
static std::once_flag oc;
huagao_ds::huagao_ds() : cur_head_(NULL), dpi_(200), xfer_ready_failed_(false), log_all_triple_(false), scanner_status_(SCANNER_STATUS_NOT_INIT)
,count_(-1), bUiOnly_(false), show_setting_(false)
{
//std::call_once(oc, [&]() { log4cplus::Initializer(); });
}
huagao_ds::~huagao_ds()
{
if (memoryinfo.get()) {
m_memoryfalg = false;
if (memoryinfo->joinable())
memoryinfo->join();
}
if (cur_head_)
delete cur_head_;
uninitialize_sane(nullptr);
}
std::string huagao_ds::get_hidedlg_path(void)
{
char szIniFile[MAX_PATH] = { 0 };
// SHGetSpecialFolderPathA(NULL, szIniFile, CSIDL_WINDOWS, TRUE);
// #ifdef MAKEHUAGAO
// strcat(szIniFile, "\\twain_32\\HuaGoScan\\hidedlg.exe");
// #elif defined AUGE
// strcat(szIniFile, "\\twain_32\\AuGeScan\\hidedlg.exe");
// #elif defined HANVON
// strcat(szIniFile, "\\twain_32\\HanvonScan\\hidedlg.exe");
// #elif defined LANXUM
// strcat(szIniFile, "\\twain_32\\LANXUMSCAN\\hidedlg.exe");
// #else // MAKEHUAGAO
// strcat(szIniFile, "\\twain_32\\ZhibenScan\\hidedlg.exe");
// #endif
return szIniFile;
}
void huagao_ds::showmsg(const char* msg, int err)
{
// ShellExecuteA(NULL, "open", huagao_ds::get_hidedlg_path().c_str(), msg, NULL, SW_HIDE);
}
int huagao_ds::on_scanner_event(int ev, void* param)
{
return ((huagao_ds*)param)->handle_scanner_event(ev, false);
}
const Identity& huagao_ds::defaultIdentity() noexcept {
// remember, we return a reference, therefore the identity must not be placed on the stack of this method
init_identity();
return *srcIdent;
}
Result huagao_ds::selectIdentity(Twpp::Identity& ident) noexcept {
// remember, we return a reference, therefore the identity must not be placed on the stack of this method
init_identity();
ident = *srcIdent;
return success();
return { ReturnCode::Failure, ConditionCode::NoDs };
}
Twpp::ConditionCode huagao_ds::condition_code_from_hg_error(int hgerr)
{
if (hgerr == SCANNER_ERR_OK)
return Twpp::ConditionCode::Success;
if (hgerr == SCANNER_ERR_DEVICE_NOT_FOUND)
return Twpp::ConditionCode::CheckDeviceOnline;
if (hgerr == SCANNER_ERR_IO || hgerr == SANE_STATUS_IO_ERROR)
return Twpp::ConditionCode::OperationError;
if(hgerr == SCANNER_ERR_OUT_OF_RANGE)
return Twpp::ConditionCode::BadCap;
if(hgerr == SCANNER_ERR_DEVICE_NOT_SUPPORT || hgerr == SANE_STATUS_UNSUPPORTED)
return Twpp::ConditionCode::BadProtocol;
if(hgerr == SCANNER_ERR_INVALID_PARAMETER || hgerr == SANE_STATUS_INVAL)
return Twpp::ConditionCode::BadValue;
if(hgerr == SCANNER_ERR_ACCESS_DENIED || hgerr == SANE_STATUS_ACCESS_DENIED)
return Twpp::ConditionCode::Denied;
if(hgerr == SCANNER_ERR_OPEN_FILE_FAILED)
return Twpp::ConditionCode::FileNotFound;
if (hgerr == SCANNER_ERR_DEVICE_PAPER_JAMMED || hgerr == SANE_STATUS_JAMMED)
return Twpp::ConditionCode::PaperJam;
if (hgerr == SCANNER_ERR_DEVICE_DOUBLE_FEEDING)
return Twpp::ConditionCode::PaperDoubleFeed;
if (hgerr == SCANNER_ERR_WRITE_FILE_FAILED)
return Twpp::ConditionCode::FileWriteError;
if (hgerr == SCANNER_ERR_DEVICE_DOGEAR)
return Twpp::ConditionCode::DamagedCorner;
if (hgerr == SCANNER_ERR_DEVICE_NO_PAPER || hgerr == SANE_STATUS_NO_DOCS)
return Twpp::ConditionCode::NoMedia;
if (hgerr == SCANNER_ERR_OPENED_BY_OTHER_PROCESS)
return Twpp::ConditionCode::MaxConnections;
if(hgerr == SCANNER_ERR_INSUFFICIENT_MEMORY || hgerr == SANE_STATUS_NO_MEM)
return Twpp::ConditionCode::LowMemory;
if(hgerr == SCANNER_ERR_DEVICE_NOT_FOUND)
return Twpp::ConditionCode::NoDs;
return ConditionCode::Bummer;
// return (Twpp::ConditionCode)((int)Twpp::ConditionCode::CustomBase + hgerr);
}
Result huagao_ds::capabilityGet(const Identity& origin, Capability& data)
{
return capCommon(origin, Msg::Get, data);
}
Result huagao_ds::capabilityGetCurrent(const Identity& origin, Capability& data)
{
return capCommon(origin, Msg::GetCurrent, data);
}
Result huagao_ds::capabilityGetDefault(const Identity& origin, Capability& data)
{
return capCommon(origin, Msg::GetDefault, data);
}
Result huagao_ds::capabilityQuerySupport(const Identity&, Capability& data)
{
auto it = m_query.find(data.type());
MsgSupport sup = it != m_query.end() ? it->second : msgSupportEmpty;
data = Capability::createOneValue(data.type(), sup);
return success();
}
Result huagao_ds::capabilityReset(const Identity& origin, Capability& data)
{
return capCommon(origin, Msg::Reset, data);
}
Result huagao_ds::capabilityResetAll(const Identity& origin)
{
for (auto& pair : m_query) {
if ((pair.second & MsgSupport::Reset) != msgSupportEmpty) {
Capability dummyCap(pair.first);
capCommon(origin, Msg::Reset, dummyCap);
}
}
return success();
}
Result huagao_ds::capabilitySet(const Identity& origin, Capability& data)
{
return capCommon(origin, Msg::Set, data);
}
Result huagao_ds::eventProcess(const Identity&, Event& event)
{
// const MSG* msg = (const MSG*)event.event();
//if (scanner_.get() && show_setting_)
//{
// int ev = scanner_->get_event();
//
// if(ev)
// handle_scanner_event(ev);
//}
/*if (xfer_ready_)
{
notifyXferReady();
xfer_ready_ = false;
}
else*/ if (take_and_reset_notify_close_flag())
{
notifyCloseCancel();
}
event.setMessage(Msg::Null);
return { ReturnCode::NotDsEvent, ConditionCode::Success };
}
Twpp::Result huagao_ds::deviceEventGet(const Twpp::Identity& origin, Twpp::DeviceEvent& data)
{
// data = DeviceEvent::simple(load_sane_util::take_event(), "HUAGAO");
// return success();
return seqError();
}
Result huagao_ds::identityOpenDs(const Identity& id)
{
Identity target(id);
Result result = huagao_ds::selectIdentity(target);
if (scanner_.get())
scanner_.reset();
if (result.returnCode() != ReturnCode::Success)
{
// load_sane_util::uninitialize();
return result;
}
int err = 0, attempt = 0, max_try = 100;
scanner_.reset(open_scanner(PRODUCT_FAMILY, &err));
if (!scanner_.get())
{
utils::to_log(3, "OpenDS(%s) error: %d\r\n", PRODUCT_FAMILY, err);
return { ReturnCode::Failure, huagao_ds::condition_code_from_hg_error(err) };
}
// ui_.reset(new twain_ui(local_utility::reg_get_app_installing_path().c_str()));
scanner_->set_event_callback(&huagao_ds::on_scanner_event, this);
if (get_config_number("twain-app", "flow") == TWAIN_APP_TRANSFER_REVERSE)
{
cur_head_ = new SANE_Parameters;
memset(cur_head_, 0, sizeof(SANE_Parameters));
}
log_all_triple_ = get_config_number("twain-app", "log-all-triple") == 1;
notify_close_ = get_config_number("twain-app", "notify-close");
double_check_mode_ = get_config_number("twain-app", "double-check");
if (double_check_mode_ == 0)
{
char pe[MAX_PATH] = { 0 }, * name = NULL;
GetModuleFileNameA(NULL, pe, _countof(pe) - 1);
name = strrchr(pe, '\\');
if (name++ == NULL)
name = pe;
if (STRICMP(name, "\u597D\u5206\u6570\u9605\u5377\u626B\u63CF\u7AEF.exe") == 0)
double_check_mode_ = DOUBLE_CHECK_ULTRASONIC;
else
double_check_mode_ = DOUBLE_CHECK_TWAIN;
}
utils::to_log(1, "Double check mode = %d\r\n", double_check_mode_);
int nobd = get_config_number("twain-app", "no-bitdepth", 0, -1);
if (nobd == -1)
{
char pe[MAX_PATH] = { 0 }, * name = NULL;
GetModuleFileNameA(NULL, pe, _countof(pe) - 1);
name = strrchr(pe, '\\');
if (name++ == NULL)
name = pe;
if (STRICMP(name, "\u519B\u961F\u626B\u63CF2.0.exe") == 0) // 军队扫描2.0.exe
nobd = 1;
else
nobd = 0;
}
no_bitdepth_ = nobd == 1;
utils::to_log(LOG_LEVEL_DEBUG, "BitDepth protocol disabled = %d\r\n", nobd);
m_compression = Compression::None;
init_support_caps(err);
m_fileXfer.setFormat(ImageFileFormat::Bmp);
scanner_status_ = SCANNER_STATUS_READY;
utils::log_mem_info("srcIdent:", srcIdent, sizeof(*srcIdent));
err = 0;
return err == 0 ? success() : Result(ReturnCode::Failure, huagao_ds::condition_code_from_hg_error(err));
}
Result huagao_ds::identityCloseDs(const Identity&)
{
if (notify_close_thread_.get() && notify_close_thread_->joinable())
notify_close_thread_->join();
// ui_.reset();
if (scanner_.get())
{
scanner_->set_event_callback();
scanner_->ui_hide();
scanner_.reset();
}
// load_sane_util::uninitialize();
scanner_status_ = SCANNER_STATUS_NOT_INIT;
return success();
}
Result huagao_ds::pendingXfersGet(const Identity&, PendingXfers& data)
{
if (!scanner_.get())
return seqError();
// <20><>ʾ<EFBFBD><CABE><EFBFBD>ý<EFBFBD><C3BD><EFBFBD>ʱ<EFBFBD><CAB1><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>EndXfer<65><72><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><D2BB>ͼƬ<CDBC><C6AC><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ʾ<EFBFBD><CABE><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>ڴ<EFBFBD><DAB4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
int cnt = get_scanned_image_count(-1);
// FIX-2023-05-29: <20>Ƿ񱣳<C7B7><F1B1A3B3><EFBFBD><EFBFBD>ý<EFBFBD><C3BD><EFBFBD><EFBFBD><EFBFBD>ʾ<EFBFBD><CABE><EFBFBD><EFBFBD>APP<50><50><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>˴<EFBFBD><CBB4><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>Ƿ<EFBFBD><C7B7><EFBFBD>ʾUI<55><49><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>һ<EFBFBD><D2BB>ɨ<EFBFBD><C9A8><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>
//int cnt = show_setting_ ? 1 : get_scanned_image_count(-1);
data.setCount(cnt);
return success();
}
Result huagao_ds::pendingXfersEnd(const Identity& id, PendingXfers& data)
{
// complete transferring of current image ...
pending_xfer_.clear();
if (scanner_.get())
{
scanner_->discard_first_image();
}
return pendingXfersGet(id, data);
}
Result huagao_ds::pendingXfersReset(const Identity& id, PendingXfers& data)
{
pending_xfer_.clear();
if (scanner_.get())
{
scanner_->stop();
while (scanner_->discard_first_image());
}
data.setCount(0);
return success();
//return pendingXfersGet(id, data);
}
Result huagao_ds::setupMemXferGet(const Identity& id, SetupMemXfer& data)
{
SANE_Parameters head;
size_t total = 0;
DWORD to = cur_head_ ? -1 : 0;
if (!scanner_.get() || get_scanned_image_count(to) == 0)
{
if (cur_head_)
{
UserInterface ui(FALSE, FALSE, (Twpp::Handle)0);
data.setPreferredSize(1);
call(id, DataGroup::Control, Dat::UserInterface, Msg::DisableDs, &ui);
}
else
{
int def_w = 2000,
def_h = 6000;
data.setMinSize(def_w * 3/* * def_h*/);
data.setPreferredSize(def_w * 3 * def_h);
data.setMaxSize(def_w * 3 * def_h);
}
return success();
}
if (scanner_->get_first_image_header(&head, &total))
{
if (m_compression == Compression::None)
{
int line_bytes = (head.bytes_per_line + 3) / 4 * 4;
data.setMinSize(head.bytes_per_line);
data.setPreferredSize(line_bytes * head.lines);
data.setMaxSize(line_bytes * head.lines);
}
else
{
data.setMinSize(total);
data.setPreferredSize(total);
data.setMaxSize(total);
}
return success();
}
else
return badValue();
}
Result huagao_ds::userInterfaceDisable(const Identity&, UserInterface& ui)
{
if (scanner_.get())
{
scanner_->stop();
scanner_->ui_hide();
}
return success();
}
Result huagao_ds::userInterfaceEnable(const Identity&, UserInterface& ui)
{
notfify_close_ = false;
bUiOnly_ = false;
show_setting_ = false;
scanner_->twain_set_transfer((twain_xfer)m_capXferMech);
if (!ui.showUi() || !scanner_->ui_is_ok())
{
if(scanner_->ui_is_ok())
scanner_->ui_show_progress((HWND)ui.parent().raw(), m_bIndicator);
else
utils::to_log(LOG_LEVEL_WARNING, "APP want to show setting UI, but UI is not in service!\n");
xfer_ready_failed_ = false;
scanner_status_ = SCANNER_STATUS_SCAN_1;
app_trigger_event_ = false;
int err = scanner_->start();
if (err == SCANNER_ERR_OK)
{
return success();
}
else
{
scanner_status_ = SCANNER_STATUS_READY;
// if (err == SCANNER_ERR_DEVICE_NO_PAPER)
return { ReturnCode::Failure, huagao_ds::condition_code_from_hg_error(err) };
return bummer();
}
}
return showTwainUI(ui);
}
Result huagao_ds::userInterfaceEnableUiOnly(const Identity&, UserInterface& ui)
{
// as a minimal source, we do not support GUI that just saves settings
if (!scanner_->ui_is_ok())
return bummer();
return showTwainUI(ui, true);
}
Twpp::Result huagao_ds::extImageInfoGet(const Identity& origin, ExtImageInfo& data)
{
for (int i = 0; i < data.size(); ++i)
{
InfoId id = data.at(i).id();
Twpp::Info& info = data.at(i);
if (id == InfoId::BarCodeCount)
{
info.allocSimple(Type::UInt32);
*info.items<Twpp::InfoId::BarCodeCount>()[0].data() = 0;
}
else if (id == InfoId::BarCodeType)
{
info.allocSimple(Type::UInt32);
*info.items<Twpp::InfoId::BarCodeType>()[0].data() = BarCodeType::ThreeOfNine;
}
else if(id == InfoId::BarCodeTextLength)
{
info.allocSimple(Type::UInt32);
*info.items<Twpp::InfoId::BarCodeTextLength>()[0].data() = 0;
}
else if (id == InfoId::BarCodeText)
{
//info.allocSimple(Type::Str255, 1);
//info.items<Twpp::InfoId::PrinterText>()[0].data()->setData(std::to_string(6922868285266 + i).c_str());
}
else
info.setReturnCode(ReturnCode::InfoNotSupported);
}
return success();
return { ReturnCode::Failure, ConditionCode::NoMedia };
}
Result huagao_ds::imageInfoGet(const Identity&, ImageInfo& data)
{
SANE_Parameters head;
bool ok = false;
int res = 200;
if (!scanner_.get())
return seqError();
if (cur_head_ && cur_head_->lines)
{
memcpy(&head, cur_head_, sizeof(head));
ok = true;
cur_head_->lines = 0;
res = dpi_;
}
else
{
//if (!scanner_->wait_image())
//{
//// notifyCloseOk();
// return success(); // 好分数需要返回成<E59B9E>?
//}
if (get_scanned_image_count(-1) > 0) // 显示设置界面时不能阻塞在EndXfer否则最后一张图片传输后显示不出来故在此做阻塞调用
{
ok = scanner_->get_first_image_header(&head, NULL, &res);
if (ok)
dpi_ = res;
}
}
if (ok)
{
if (head.format == SANE_FRAME_RGB)
{
data.setBitsPerPixel(head.depth * 3);
data.setSamplesPerPixel(3);
data.bitsPerSample()[0] = data.bitsPerSample()[1] = data.bitsPerSample()[2] = head.depth;
}
else
{
data.setSamplesPerPixel(1);
data.setBitsPerPixel(head.depth);
data.bitsPerSample()[0] = head.depth;
}
data.setHeight(head.lines);
if (m_compression == Compression::Group4)
data.setPixelType(PixelType::BlackWhite);
else
{
if (head.format == SANE_FRAME_RGB)
data.setPixelType(PixelType::Rgb);
else if(head.format == SANE_FRAME_GRAY)
data.setPixelType(head.depth == 1 ? PixelType::BlackWhite : PixelType::Gray);
}
data.setPlanar(false);
data.setWidth(head.pixels_per_line);
data.setXResolution((float)res);
data.setYResolution((float)res);
data.compression(m_compression);
}
else
{
utils::log_info(" imageInfoGet = false\r\n", 1);
data.setHeight(0);
return { ReturnCode::XferDone, ConditionCode::Bummer };
}
int h = data.height();
int w = data.width();
int bitsPerPixel = data.bitsPerPixel();
Compression compression = data.compression();
PixelType pixelType = data.pixelType();
bool planar = data.planar();
utils::to_log(LOG_LEVEL_DEBUG, "imageInfoGet = (%d * %d * %d), %dDPI\r\n", w, h, bitsPerPixel, res);
return success();
}
Result huagao_ds::imageLayoutGet(const Identity&, ImageLayout& data)
{
SANE_Parameters head;
memset(&head, 0, sizeof(head));
if (!scanner_.get())
return seqError();
else if(get_scanned_image_count(-1) == 0)
return { ReturnCode::Failure, condition_code_from_hg_error(scanner_->last_error()) };
int res = 200;
scanner_->get_first_image_header(&head, NULL, &res);
data.setDocumentNumber(1);
data.setFrameNumber(1);
data.setPageNumber(1);
data.setFrame(Frame(0, 0, static_cast<float>(head.pixels_per_line) / res, static_cast<float>(head.lines) / res));
return success();
}
Result huagao_ds::imageLayoutGetDefault(const Identity& origin, ImageLayout& data)
{
return imageLayoutGet(origin, data);
}
Result huagao_ds::imageLayoutSet(const Identity& origin, ImageLayout& lay)
{
// we dont support setting image frame
ImageLayout def;
imageLayoutGetDefault(origin, def);
return lay.frame() == def.frame() ? success() : badValue();
}
Result huagao_ds::imageLayoutReset(const Identity& origin, ImageLayout& data)
{
return imageLayoutGet(origin, data);
}
Result huagao_ds::imageMemXferGet(const Identity& origin, ImageMemXfer& data)
{
if (!scanner_.get())
return seqError();
if (get_scanned_image_count(-1) == 0 && !pending_xfer_.img)
{
//if (!cur_head_ || !scanner_->wait_image())
//{
// notifyCloseOk();
// return seqError();
//}
return { ReturnCode::Failure, condition_code_from_hg_error(scanner_->last_error()) };
}
IScanImg* img = pending_xfer_.img ? pending_xfer_.img : scanner_->take_first_image(TWAIN_XFER_Memory);
unsigned long long off = pending_xfer_.img ? pending_xfer_.off : img->get_bits_offset();
unsigned char* dst = (unsigned char*)data.memory().data().data();
UInt32 buf_l = data.memory().size();
unsigned int line_l = img->line_bytes(),
rows = data.memory().size() / line_l;
size_t want_read = rows * line_l;
Result ret = { ReturnCode::XferDone, ConditionCode::Success };
if (rows == 0)
return badValue();
else if (want_read > img->bytes() - off)
{
want_read = img->bytes() - off;
rows = (want_read + line_l - 1) / line_l;
}
if (pending_xfer_.img)
{
img->add_ref();
pending_xfer_.clear();
}
else if (cur_head_)
{
img->copy_header(cur_head_);
}
data.setBytesPerRow(line_l);
data.setColumns(img->width());
data.setRows(rows);
data.setXOffset(0);
data.setYOffset(UInt32((off - img->get_bits_offset()) / line_l));
data.setCompression(m_compression);
if (m_compression != Compression::None)
{
want_read = img->bytes() - off;
if (buf_l < want_read)
{
want_read = buf_l;
}
img->read(dst, &want_read, off);
data.setBytesWritten(want_read);
off += want_read;
if (off < img->bytes())
{
pending_xfer_.img = img;
pending_xfer_.off = (unsigned int)off;
img->add_ref();
ret = success();
}
else
scanner_->image_fetched(img);
}
else if (img->read(dst, &want_read, off) == SCANNER_ERR_OK)
{
want_read /= line_l;
// data.setRows(want_read);
want_read *= line_l;
data.setBytesWritten(want_read);
off += want_read;
if (off < img->bytes())
{
pending_xfer_.img = img;
pending_xfer_.off = (unsigned int)off;
img->add_ref();
ret = success();
}
else
scanner_->image_fetched(img);
}
else
{
ret = { ReturnCode::XferDone, ConditionCode::Bummer };
}
img->release();
return ret;
}
Result huagao_ds::imageNativeXferGet(const Identity& id, ImageNativeXfer& data)
{
if (!scanner_.get())
return seqError();
else if (get_scanned_image_count(-1) == 0)
{
return { ReturnCode::Failure, condition_code_from_hg_error(scanner_->last_error()) };
}
IScanImg* img = scanner_->take_first_image(TWAIN_XFER_Native);
if (!img)
return seqError();
if (data)
data.release();
data = ImageNativeXfer(img->bytes());
unsigned long long off = sizeof(BITMAPFILEHEADER);
unsigned int total = img->bytes() - off;
unsigned char* src = img->data(off, &total),
* dst = data.data<unsigned char>().data();
while (off < img->bytes() && src)
{
std::copy(src, src + total, dst);
dst += total;
off += total;
if (off >= img->bytes())
break;
total = img->bytes() - (unsigned int)off;
src = img->data(off, &total);
}
scanner_->image_fetched(img);
img->release();
return { ReturnCode::XferDone, ConditionCode::Success };
}
Twpp::Result huagao_ds::pendingXfersStopFeeder(const Identity& origin, PendingXfers& data)
{
if (scanner_.get())
scanner_->stop();
return success();
}
Twpp::Result huagao_ds::imageFileXferGet(const Twpp::Identity& origin)
{
// assume that the file format has set before start-scanning, so we write-down the image content to file directly here ...
if (!scanner_.get())
return seqError();
else if(get_scanned_image_count(-1) == 0)
return { ReturnCode::Failure, condition_code_from_hg_error(scanner_->last_error()) };
IScanImg* img = scanner_->take_first_image(TWAIN_XFER_File);
Twpp::Result ret = seqError();
FILE* dst = NULL;
if (img)
{
std::string file(img->file());
int cv_e = 0,
status = img->image_status();
if (file.empty())
{
dst = fopen(m_fileXfer.filePath().string().c_str(), "wb");
ret = { ReturnCode::Failure, ConditionCode::FileWriteError };
if (dst)
{
unsigned long long off = 0;
unsigned int total = img->bytes();
unsigned char* src = img->data(off, &total);
while (src)
{
if (fwrite(src, 1, total, dst) != total)
break;
off += total;
if (off >= img->bytes())
{
ret = Result(ReturnCode::XferDone, ConditionCode::Success);
break;
}
total = img->bytes() - (unsigned int)off;
src = img->data(off, &total);
}
fclose(dst);
scanner_->image_fetched(img);
}
else
{
std::string f("CreateFile(" + m_fileXfer.filePath().string());
char msg[128] = { 0 };
sprintf(msg, ") = %d\r\n", GetLastError());
f += msg;
utils::log_info(f.c_str(), 3);
}
img->release();
}
else
{
utils::log_info(("Map file to " + m_fileXfer.filePath().string() + "\r\n").c_str(), 1);
img->keep_file(true);
scanner_->image_fetched(img);
img->release();
cv_e = utils::move_file(file.c_str(), m_fileXfer.filePath().string().c_str());
if (cv_e == 0)
ret = Result(ReturnCode::XferDone, ConditionCode::Success);
else
remove(file.c_str());
}
if (ret.status() == ConditionCode::Success && m_fileXfer.format() != ImageFileFormat::Bmp)
{
SANE_ImageFormatConvert conv;
std::string target(m_fileXfer.filePath().string().c_str());
memset(&conv, 0, sizeof(conv));
file = target + ".src";
conv.src.fmt.img_format = SANE_IMAGE_TYPE_BMP;
conv.src.is_file = SANE_TRUE;
conv.src.data = file.c_str();
conv.src.data_len = file.length();
conv.dst.fmt.img_format = (SANE_ImageType)m_fileXfer.format();
conv.dst.fmt.compress.compression = (SANE_CompressionType)m_compression;
conv.dst.fmt.compress.detail = (void*)0;
conv.dst.fmt.detail = (void*)m_jpegQuality;
conv.dst.is_file = SANE_TRUE;
conv.dst.data = target.c_str();
conv.dst.data_len = target.length();
while (rename(target.c_str(), file.c_str()))
{
if (cv_e == 0)
{
std::string info("[convert image format] Move '");
char buf[80] = { 0 };
sprintf(buf, "%d\r\n", GetLastError());
info += target + "' to '" + file + "' failed: ";
utils::log_info((info + buf).c_str(), 3);
}
if (++cv_e >= 9)
break;
Sleep(30);
}
if (cv_e < 9)
{
cv_e = scanner_->convert_image(&conv);
if (cv_e != SCANNER_ERR_OK)
ret = { ReturnCode::Failure, ConditionCode::OperationError };
remove(file.c_str());
}
}
//if (ret.status() == ConditionCode::Success)
//{
// switch (status)
// {
// case SANE_Image_Statu_Blank:
// break;
// case SANE_Image_Statu_Double:
// ret.setStatus(ConditionCode::PaperDoubleFeed);
// break;
// case SANE_Image_Statu_Jammed:
// ret.setStatus(ConditionCode::PaperJam);
// break;
// }
//}
{
std::string info(m_fileXfer.filePath().string());
char r[80] = { 0 };
sprintf(r, ": status = %d(Fail: 0x%x)\r\n", (int)ret.status(), cv_e);
utils::log_info((info + r).c_str(), 1);
}
}
return ret;
}
Twpp::Result huagao_ds::setupFileXferGet(const Twpp::Identity& origin, Twpp::SetupFileXfer& data)
{
data.setFilePath(m_fileXfer.filePath());
data.setFormat(m_fileXfer.format());
return success();
}
Twpp::Result huagao_ds::setupFileXferGetDefault(const Twpp::Identity& origin, Twpp::SetupFileXfer& data)
{
Str255 str("HGTwain.bmp");
data.setFilePath(str);
data.setFormat(ImageFileFormat::Bmp);
return success();
}
Twpp::Result huagao_ds::setupFileXferSet(const Twpp::Identity& origin, Twpp::SetupFileXfer& data)
{
m_fileXfer.setFilePath(data.filePath());
m_fileXfer.setFormat(data.format());
return success();
}
Twpp::Result huagao_ds::setupFileXferReset(const Twpp::Identity& origin, Twpp::SetupFileXfer& data)
{
m_fileXfer.setFormat(Twpp::ImageFileFormat::Bmp);
std::string templateName = "HG";
char* tempPath = mktemp((char*)templateName.c_str());
if (tempPath) {
Str255 str;
str.setData(tempPath, strlen(tempPath));
m_fileXfer.setFilePath(str);
return success();
}
return badProtocol();
}
Result huagao_ds::call(const Identity& origin, DataGroup dg, Dat dat, Msg msg, void* data) {
try {
// we can override almost anything from SourceFromThis, even the top-most source instance call
//FileTools::write_log("D:\\1.txt", "call:datagroup-"+to_string((int)dg)+"dat-"+to_string(int(dat))+"msg-"+to_string(int(msg)));
Result rt;
char dgs[20] = { 0 }, dts[20] = { 0 }, ms[20] = { 0 }, ss[20] = { 0 }, rcs[20] = { 0 }, cs[20] = { 0 };
//trigger_ProcessEvent(dg, dat, msg); // some APPs may be not trigger (Control, Event, ProcessEvent), we help them :( ... // 云阅卷扫描端不等状态改变直接取图此处设置一次状<E6ACA1>?2022-11-07
rt = Base::call(origin, dg, dat, msg, data);
if (log_all_triple_ || ((int)rt.returnCode() && rt.returnCode() != ReturnCode::NotDsEvent))
{
if (dat == Dat::Capability)
{
Twpp::Capability& cap = *(Twpp::Capability*)data;
utils::to_log(7, "[%x - %s]DSEntry(%s, %s.%x, %s) = {%s, %s}\r\n", GetCurrentThreadId(), desc_state(state(), ss),
desc_data_group(dg, dgs), desc_data(dat, dts), (int)cap.type(), desc_msg(msg, ms), desc_return_code(rt, rcs), desc_condition_code((ConditionCode)(Status)rt, cs));
}
else
utils::to_log(7, "[%x - %s]DSEntry(%s, %s, %s) = {%s, %s}\r\n", GetCurrentThreadId(), desc_state(state(), ss),
desc_data_group(dg, dgs), desc_data(dat, dts), desc_msg(msg, ms), desc_return_code(rt, rcs), desc_condition_code((ConditionCode)(Status)rt, cs));
if ((int)rt.returnCode() && rt.returnCode() != ReturnCode::NotDsEvent)
{
bool changed = false;
if (dat == Dat::PendingXfers && Msg::Reset == msg && scanner_status_ == SCANNER_STATUS_STOPPED) // PurvarScannerForHomework.exe - move modifying from EndXfer to xfer::Reset
{
changed = true;
rt = success();
}
else if (rt.status() == ConditionCode::SeqError)
{
if (dat == Dat::ImageInfo && Msg::Get == msg && scanner_status_ == SCANNER_STATUS_STOPPED) // for demo.exe
{
changed = true;
rt = { ReturnCode::Success, ConditionCode::Bummer };
}
else if (dat == Dat::PendingXfers && Msg::EndXfer == msg && scanner_status_ == SCANNER_STATUS_STOPPED) // for photoshop.exe
{
// PendingXfers& data
(static_cast<PendingXfers*>(data))->setCount(0);
changed = true;
rt = { ReturnCode::Success, ConditionCode::Success };
}
}
else if (rt.status() == ConditionCode::Bummer && rt.returnCode() == ReturnCode::XferDone && scanner_status_ == SCANNER_STATUS_STOPPED) // for demo.exe
{
changed = true;
rt = { ReturnCode::Success, ConditionCode::Bummer };
}
if(changed)
utils::to_log(7, " modify result to {%s, %s}.\r\n", desc_return_code(rt, rcs), desc_condition_code((ConditionCode)(Status)rt, cs));
}
}
return rt;
}
catch (const CapabilityException& e) {
//FileTools::writelog(log_ERROR, e.what());
//UNREFERENCED_PARAMETER(e);
return badValue();
}
}
Result huagao_ds::customDataGet(const Twpp::Identity& origin, Twpp::CustomData& data)
{
// get user setting from local file ...
if (!scanner_.get())
return seqError();
char * buf = NULL;
size_t len = 0;
if (scanner_->twain_get_config(buf, &len) == SCANNER_ERR_INSUFFICIENT_MEMORY)
{
data = CustomData(len);
buf = data.lock<char>();
scanner_->twain_get_config(buf, &len);
}
return success();
}
Result huagao_ds::customDataSet(const Twpp::Identity& origin, Twpp::CustomData& data)
{
// write user setting to local file ...
if (!scanner_.get())
return seqError();
char* buf = data.lock<char>();
scanner_->twain_set_config(buf, data.size());
return success();
}
void huagao_ds::CapabilityPrintf(Twpp::Msg msg, std::string capability, std::string value)
{
}
Result huagao_ds::capCommon(const Identity&, Msg msg, Capability& data) {
auto it = m_caps.find(data.type());
if (it != m_caps.end()) {
int type = (int)data.type();
while (org_func_.count(type))
type = org_func_[type];
return (it->second)((Twpp::CapType)type, msg, data);
}
return capUnsupported();
}
Twpp::Result huagao_ds::showTwainUI(Twpp::UserInterface& data, bool bUiOnly)
{
if (!scanner_->is_online())
return { ReturnCode::Failure, huagao_ds::condition_code_from_hg_error(SCANNER_ERR_DEVICE_NOT_FOUND) };
show_setting_ = true;
bUiOnly_ = bUiOnly;
// display user UI ... (setting UI, can we show my own main window here ?)
return scanner_->ui_show_setting((HWND)data.parent().raw(), !bUiOnly, m_bIndicator) ? success() : seqError();
}
void huagao_ds::init_support_caps(int err)
{
std::vector<uint32_t> ids;
scanner_->get_fixed_ids(got_fixed_id, &ids);
std::sort(ids.begin(), ids.end());
m_query.clear();
m_caps.clear();
org_func_.clear();
m_query[CapType::SupportedCaps] = msgSupportGetAll;
m_caps[CapType::SupportedCaps] = [this](Twpp::CapType type, Msg msg, Capability& data) {
log_attr_access((int)type, (int)msg);
if ((msg == Msg::Get) || (Msg::GetCurrent == msg) || (Msg::GetDefault == msg)) {
data = Capability::createArray<CapType::SupportedCaps>(m_caps.size());
auto arr = data.array<CapType::SupportedCaps>();
std::vector<unsigned int> all;
for (const auto& v : m_caps)
all.push_back((int)v.first);
std::sort(all.begin(), all.end());
UInt32 i = 0;
for (const auto& kv : all) {
arr[i] = (CapType)kv;
i++;
}
return success();
}
else
return capBadOperation();
};
if(err == 0)
{
m_query[CapType::IExtImageInfo] = msgSupportGetAll;
m_caps[CapType::IExtImageInfo] = std::bind(enmGet<Bool>, _2, _3, Bool(true));
m_query[CapType::ISupportedExtImageInfo] = msgSupportGetAll;
m_caps[CapType::ISupportedExtImageInfo] = [this](Twpp::CapType type, Msg msg, Capability& data) {
data = Capability::createArray< CapType::ISupportedExtImageInfo>({ InfoId::BarCodeCount, InfoId::BarCodeText, InfoId::BarCodeType, InfoId::BarCodeTextLength });
return success();
};
m_query[CapType::XferCount] = msgSupportGetAllSetReset;
m_caps[CapType::XferCount] = [this](Twpp::CapType type, Msg msg, Capability& data) -> Result {
log_attr_access((int)type, (int)msg);
std::vector<std::string> all;
bool dup = true;
GET_SANE_OPT(std::string, scanner_, PAGE, NULL, &all);
dup = s2t::page_is_duplex(all[sane_opts::RANGE_POS_CURRENT].c_str());
if (msg == Msg::Set) {
auto item = data.currentItem<Int16>();
if (item > 65535 || item < -1 || item == 0) {
return badValue();
}
int ret = SCANNER_ERR_OK;
int count = count_ = item;
if (dup && count >= 2)
{
count++;
count /= 2;
}
SET_SANE_OPT(ret, scanner_, SCAN_COUNT, &count);
return ret == SCANNER_ERR_OK ? success() : badValue();
}
std::vector<int> count;
Int16 tmp_count = 0;
GET_SANE_OPT(int, scanner_, SCAN_COUNT, NULL, &count);
tmp_count = count[sane_opts::RANGE_POS_CURRENT];
if (dup && tmp_count > 0 && tmp_count < 65535)
{
tmp_count *= 2;
}
return oneValGetSet<Int16>(msg, data, tmp_count, (Int16)count[sane_opts::RANGE_POS_DEFAULT]);
};
m_bIndicator = scanner_->ui_is_ok();
if (m_bIndicator)
{
m_query[CapType::UiControllable] = msgSupportGetAll;
m_caps[CapType::UiControllable] = std::bind(oneValGet<Bool>, _2, _3, Bool(true));
m_query[CapType::Indicators] = msgSupportGetAllSetReset;
m_caps[CapType::Indicators] = [this](Twpp::CapType type, Msg msg, Capability& data) -> Result {
log_attr_access((int)type, (int)msg);
if (Msg::Set == msg) {
auto show = data.currentItem<CapType::Indicators>();
m_bIndicator = show;
utils::log_info(m_bIndicator ? "Set show indicator: true\r\n" : "Set show indicator: false\r\n", 0);
return success();
}
if (!data.operator bool())
{
data = Capability::createEnumeration<CapType::Indicators>({ FALSE,TRUE }, m_bIndicator ? 1 : 0, 1);
}
else
data = Capability::createOneValue<CapType::Indicators>(m_bIndicator);
return success();
};
}
m_query[CapType::DeviceOnline] = msgSupportGetAll;
m_caps[CapType::DeviceOnline] = [this](Twpp::CapType type, Msg msg, Capability& data) -> Result {
log_attr_access((int)type, (int)msg);
switch (msg) {
case Msg::Get:
case Msg::GetCurrent:
case Msg::GetDefault:
data = Capability::createOneValue<CapType::DeviceOnline>((Twpp::Bool)scanner_->is_online());
return success();
default:
return { ReturnCode::Failure, ConditionCode::CapBadOperation };
}
};
m_query[CapType::ICompression] = msgSupportGetAllSetReset;
m_caps[CapType::ICompression] = [this](Twpp::CapType type, Msg msg, Capability& data)->Result {
log_attr_access((int)type, (int)msg);
if (!scanner_.get())
return seqError();
if (Msg::Set == msg) {
auto mech = data.currentItem<CapType::ICompression>();
if (Compression::None == mech || mech == Compression::Group4) {
m_compression = mech;
scanner_->twain_set_compression((SANE_CompressionType)m_compression);
{
utils::to_log(LOG_LEVEL_ALL, "set CapType::ICompression = %s\r\n", Compression::None == mech ? "None" : "Group4");
}
return success();
}
else
return badValue();
}
return CapSupGetAllReset<Compression, Compression, CapType::ICompression>(msg, data, { Compression::None, Compression::Group4 }, m_compression, Compression::None, m_compression == Compression::None ? 0 : 1, 0);
};
if (scanner_->get_option_info(SANE_OPT_ID_LANGUAGE, NULL, NULL, NULL, NULL))
{
m_query[CapType::Language] = msgSupportGetAllSetReset;
m_caps[CapType::Language] = [this](Twpp::CapType type, Msg msg, Capability& data)->Result {
log_attr_access((int)type, (int)msg);
if (!scanner_.get())
return seqError();
std::vector<int> all;
std::vector<std::string> sall;
GET_SANE_OPT(std::string, scanner_, LANGUAGE, NULL, &sall);
for (auto& v : sall)
all.push_back(s2t::language_to_twain(v.c_str()));
sall.clear();
if (Msg::Set == msg || Msg::Reset == msg) {
int mech = (int)data.currentItem<CapType::Language>();
if (Msg::Reset == msg)
mech = all[sane_opts::RANGE_POS_DEFAULT];
std::string val(s2t::language_from_twain(mech));
int ret = 0;
SET_SANE_OPT(ret, scanner_, LANGUAGE, &val[0]);
if (ret)
return { ReturnCode::Failure, huagao_ds::condition_code_from_hg_error(ret) };
else
return success();
}
UInt16 cur = all[sane_opts::RANGE_POS_CURRENT], init = all[sane_opts::RANGE_POS_DEFAULT];
int ni = 0, ii = 0;
std::list<UInt16> alll;
for (int i = 0; i < all.size() - sane_opts::RANGE_POS_ENUM_BEGIN; ++i)
{
if (all[i + sane_opts::RANGE_POS_ENUM_BEGIN] == cur)
ni = i;
else if (all[i + sane_opts::RANGE_POS_ENUM_BEGIN] == init)
ii = i;
alll.push_back(all[i + sane_opts::RANGE_POS_ENUM_BEGIN]);
}
return cap_get_enum_values<UInt16, CapType::Language>(msg, data, alll, cur, init, ni, ii);
};
}
m_query[CapType::IUnits] = msgSupportGetAllSetReset;
m_caps[CapType::IUnits] = std::bind(enmGetSetConst<Unit>, _2, _3, Unit::Inches);
m_query[CapType::IBitDepth] = msgSupportGetAllSetReset;
m_caps[CapType::IBitDepth] = [this](Twpp::CapType type, Msg msg, Capability& data) -> Result {
log_attr_access((int)type, (int)msg);
std::vector<int> all;
std::vector<std::string> sall;
int val = 0;
GET_SANE_OPT(std::string, scanner_, COLOR_MODE, NULL, &sall);
for (auto& v : sall)
all.push_back(s2t::bits_from_sane(v.c_str()));
sall.clear();
if (Msg::Set == msg || Msg::Reset == msg) {
if (no_bitdepth_)
return success();
int ret = SCANNER_ERR_INVALID_PARAMETER;
val = all[sane_opts::RANGE_POS_DEFAULT];
if (Msg::Set == msg)
{
auto mech = data.currentItem<CapType::IBitDepth>();
if (mech == 1)
val = COLOR_BW;
else if (mech == 8)
val = COLOR_GRAY;
else if (mech == 24)
val = COLOR_RGB;
else
{
val = COLOR_RGB; // mech;
utils::to_log(LOG_LEVEL_WARNING, "Set bit-depth to %d is out of range, we set to RGB\r\n", (int)mech);
}
}
{
char info[128] = { 0 };
sprintf(info, "set CapType::IBitDepth = %d\r\n", val);
utils::log_info(info, 0);
}
std::string sv(s2t::bits_to_sane(val));
SET_SANE_OPT(ret, scanner_, COLOR_MODE, &sv[0]);
if (Msg::Reset == msg)
{
UINT16 v = val;
data = Capability::createOneValue<UINT16>(CapType::IBitDepth, v);
}
return ret == SCANNER_ERR_OK ? success() : badValue();
}
UINT16 Now = bit_depth_from_sane(all[sane_opts::RANGE_POS_CURRENT]), Init = bit_depth_from_sane(all[sane_opts::RANGE_POS_DEFAULT]);
std::list<UINT16> vals;
UInt32 ni = distance<int>(all, all[sane_opts::RANGE_POS_CURRENT]),
ii = distance<int>(all, all[sane_opts::RANGE_POS_DEFAULT]);
for (size_t i = sane_opts::RANGE_POS_ENUM_BEGIN; i < all.size(); ++i)
vals.push_back(bit_depth_from_sane(all[i]));
return cap_get_enum_values<UINT16, CapType::IBitDepth>(msg, data, vals, Now, Init, ni, ii);
};
m_query[CapType::IBitOrder] = msgSupportGetAllSetReset;
m_caps[CapType::IBitOrder] = std::bind(oneValGetSetConst<BitOrder>, _2, _3, BitOrder::MsbFirst);
m_query[CapType::IPlanarChunky] = msgSupportGetAllSetReset;
m_caps[CapType::IPlanarChunky] = std::bind(enmGetSetConst<PlanarChunky>, _2, _3, PlanarChunky::Chunky);
m_query[CapType::IXResolution] = msgSupportGetAllSetReset;
m_caps[CapType::IXResolution] = /*m_caps[CapTypeEx::CAP_EX_SANE_resolution];*/ [this](Twpp::CapType type, Twpp::Msg msg, Capability& data) {
log_attr_access((int)type, (int)msg);
if (!scanner_.get())
return seqError();
std::vector<int> values;
value_limit limit = VAL_LIMIT_RANGE;
int init = 0, now = 0;
GET_SANE_OPT(int, scanner_, RESOLUTION, NULL, &values);
init = values[sane_opts::RANGE_POS_DEFAULT];
now = values[sane_opts::RANGE_POS_CURRENT];
switch (msg) {
case Msg::Get:
if (limit == VAL_LIMIT_RANGE)
{
data = Capability::createRange<CapType::IXResolution>(Fix32((float)values[sane_opts::RANGE_POS_LOWER])
, Fix32((float)values[sane_opts::RANGE_POS_UPPER])
, Fix32((float)values[sane_opts::RANGE_POS_STEP])
, Fix32((float)values[sane_opts::RANGE_POS_CURRENT])
, Fix32((float)values[sane_opts::RANGE_POS_DEFAULT]));
return success();
}
else if (limit == VAL_LIMIT_ENUM)
{
std::list<Fix32> vals;
Fix32 Now, Init;
UInt32 ni, ii;
for (size_t i = sane_opts::RANGE_POS_ENUM_BEGIN; i < values.size(); ++i)
{
Fix32 f;
float vf = (float)values[i];
copy_type(f, vf);
vals.push_back(f);
}
float nowf = (float)now, initf = (float)init;
copy_type(Now, nowf);
copy_type(Init, initf);
ni = std::distance(vals.begin(), std::find(vals.begin(), vals.end(), Now));
ii = std::distance(vals.begin(), std::find(vals.begin(), vals.end(), Init));
return cap_get_enum_values<Fix32, CapType::IXResolution>(msg, data, vals, Now, Init, ni, ii);
}
case Msg::GetCurrent:
data = Capability::createOneValue<CapType::IXResolution>(Fix32((float)now));
return success();
case Msg::GetDefault:
data = Capability::createOneValue<CapType::IXResolution>(Fix32((float)init));
return success();
case Msg::Reset:
data = Capability::createOneValue<CapType::IXResolution>(Fix32((float)init));
case Msg::Set: {
auto mech = data.currentItem<CapType::IXResolution>();
int ret = SCANNER_ERR_OK;
float resl = (float)mech;
int resli = (int)resl;
SET_SANE_OPT(ret, scanner_, RESOLUTION, &resli);
return ret == SCANNER_ERR_OK ? success() : badValue();
}
default:
return capBadOperation();
}
};
m_query[CapType::IYResolution] = msgSupportGetAllSetReset;
m_caps[CapType::IYResolution] = /*m_caps[CapType::IXResolution];*/ [this](Twpp::CapType type, Twpp::Msg msg, Capability& data)
{
Result ret = m_caps[CapType::IXResolution](type, msg, data);
if (data.type() == CapType::IXResolution)
data.dangerous_set_cap(CapType::IYResolution);
return ret;
};
m_query[CapType::IXNativeResolution] = msgSupportGetAll;
m_caps[CapType::IXNativeResolution] = std::bind(enmGet<Fix32>, _2, _3, Fix32(200.0));
m_query[CapType::IYNativeResolution] = msgSupportGetAll;
m_caps[CapType::IYNativeResolution] = m_caps[CapType::IXNativeResolution];
m_query[CapType::ISupportedSizes] = msgSupportGetAllSetReset;
m_caps[CapType::ISupportedSizes] = [this](Twpp::CapType type, Msg msg, Capability& data) {
log_attr_access((int)type, (int)msg);
int now = 0,
init = 0;
std::vector<int> all;
std::vector<std::string> sall;
GET_SANE_OPT(std::string, scanner_, PAPER, NULL, &sall);
for (auto& v : sall)
{
int e = s2t::paper_size_from_sane(v.c_str());
if(all.size() < 2 || std::find(all.begin() + 2, all.end(), e) == all.end())
all.push_back(e);
}
sall.clear();
now = all[sane_opts::RANGE_POS_CURRENT];
init = all[sane_opts::RANGE_POS_DEFAULT];
if (msg == Msg::Set || msg == Msg::Reset)
{
if(msg == Msg::Set)
init = (int)data.currentItem<CapType::ISupportedSizes>();
else
data = Capability::createOneValue<CapType::ISupportedSizes>((Twpp::PaperSize)init);
std::string val(s2t::paper_size_to_sane(init));
SET_SANE_OPT(now, scanner_, PAPER, &val[0]);
return now == SCANNER_ERR_OK ? success() : badValue();
}
std::list<Twpp::PaperSize> vals;
Twpp::PaperSize Now = (PaperSize)now,
Init = (PaperSize)init;
UInt32 ni = distance<int>(all, now),
ii = distance<int>(all, init);
for (size_t i = sane_opts::RANGE_POS_ENUM_BEGIN; i < all.size(); ++i)
vals.push_back((PaperSize)all[i]);
return cap_get_enum_values<Twpp::PaperSize, CapType::ISupportedSizes>(msg, data, vals, Now, Init, ni, ii);
};
m_query[CapType::IPhysicalWidth] = msgSupportGetAll;
m_caps[CapType::IPhysicalWidth] = [this](Twpp::CapType type, Msg msg, Capability& data) -> Result {
log_attr_access((int)type, (int)msg);
switch (msg) {
case Msg::Get:
case Msg::GetCurrent:
case Msg::GetDefault:
{
float init = 10.0f;
int cur = dpi_;
SANE_Parameters param;
memset(&param, 0, sizeof(param));
if (scanner_.get() && scanner_->get_first_image_header(&param, NULL, &cur))
init = param.pixels_per_line * 1.0f / cur;
data = Capability::createOneValue<Fix32>(data.type(), Fix32(init));
return success();
}
default:
return { ReturnCode::Failure, ConditionCode::CapBadOperation };
}
};
m_query[CapType::IPhysicalHeight] = msgSupportGetAll;
m_caps[CapType::IPhysicalHeight] = [this](Twpp::CapType type, Msg msg, Capability& data) -> Result {
log_attr_access((int)CapType::IPhysicalHeight, (int)msg);
switch (msg) {
case Msg::Get:
case Msg::GetCurrent:
case Msg::GetDefault:
{
float init = 10.0f;
int cur = dpi_;
SANE_Parameters param;
memset(&param, 0, sizeof(param));
if (scanner_.get() && scanner_->get_first_image_header(&param, NULL, &cur))
init = param.lines * 1.0f / cur;
data = Capability::createOneValue<Fix32>(data.type(), Fix32(init));
return success();
}
default:
return { ReturnCode::Failure, ConditionCode::CapBadOperation };
}
};
m_query[CapType::IPixelFlavor] = msgSupportGetAllSetReset;
m_caps[CapType::IPixelFlavor] = std::bind(enmGetSetConst<PixelFlavor>, _2, _3, PixelFlavor::Chocolate);
m_query[CapType::IXferMech] = msgSupportGetAllSetReset;
m_caps[CapType::IXferMech] = [this](Twpp::CapType type, Msg msg, Capability& data) -> Result {
log_attr_access((int)type, (int)msg);
if (Msg::Set == msg) {
auto mech = data.currentItem<CapType::IXferMech>();
if (mech == XferMech::Native || mech == XferMech::Memory || mech == XferMech::File) {
m_capXferMech = mech;
scanner_->twain_set_transfer((twain_xfer)m_capXferMech);
{
char * des[] = {"Native", "File", "Memory", "Unknown", "MemFile"};
utils::to_log(LOG_LEVEL_DEBUG, "set CapType::IXferMech = %s\r\n", des[(int)m_capXferMech]);
}
return success();
}
else {
return badValue();
}
}
Result r = CapSupGetAllReset<XferMech, XferMech, CapType::IXferMech>(msg, data, { XferMech::Native, XferMech::File, XferMech::Memory }, m_capXferMech, XferMech::Native, (int)m_capXferMech, 0);
return r;
};
m_query[CapType::IPixelType] = msgSupportGetAllSetReset;
m_caps[CapType::IPixelType] = [this](Twpp::CapType type, Msg msg, Capability& data) -> Result {
log_attr_access((int)type, (int)msg);
if (!scanner_.get())
return seqError();
std::vector<int> values;
std::vector<std::string> svalues;
int init = 0, now = 0;
GET_SANE_OPT(std::string, scanner_, COLOR_MODE, NULL, &svalues);
for (auto& v : svalues)
values.push_back(s2t::pixel_type_from_sane(v.c_str()));
svalues.clear();
now = values[sane_opts::RANGE_POS_CURRENT];
init = values[sane_opts::RANGE_POS_DEFAULT];
if (Msg::Reset == msg || Msg::Set == msg)
{
if(Msg::Set == msg)
init = (int)data.currentItem<CapType::IPixelType>();
{
char * des[] = { "BlackWhite", "Gray", "Rgb" };
if((int)init < _countof(des))
utils::to_log(LOG_LEVEL_DEBUG, "set CapType::IPixelType = %s\r\n", des[(int)init]);
else
utils::to_log(LOG_LEVEL_DEBUG, "set CapType::IPixelType = 0x%x\r\n", (int)init);
}
int ret = SCANNER_ERR_OK;
std::string sv(s2t::pixel_type_to_sane(init));
SET_SANE_OPT(ret, scanner_, COLOR_MODE, &sv[0]);
data = Capability::createOneValue<CapType::IPixelType>((Twpp::PixelType)init);
return ret == SCANNER_ERR_OK ? success() : badValue();
}
switch (msg) {
case Msg::Get:
if (1)
{
std::list<Twpp::PixelType> vals;
UInt32 ni = distance<int>(values, now), ii = distance<int>(values, init);
Twpp::PixelType Init = (Twpp::PixelType)init, Now = (Twpp::PixelType)now;
for (size_t i = sane_opts::RANGE_POS_ENUM_BEGIN; i < values.size(); ++i)
vals.push_back((Twpp::PixelType)values[i]);
return cap_get_enum_values<Twpp::PixelType, CapType::IPixelType>(msg, data, vals, Now, Init, ni, ii);
}
case Msg::GetCurrent:
data = Capability::createOneValue<CapType::IPixelType>((Twpp::PixelType)now);
return success();
case Msg::GetDefault:
data = Capability::createOneValue<CapType::IPixelType>((Twpp::PixelType)init);
return success();
}
return capBadOperation();
};
m_query[CapType::IAutomaticColorEnabled] = msgSupportGetAllSetReset;
m_caps[CapType::IAutomaticColorEnabled] = [this](Twpp::CapType type, Msg msg, Capability& data)->Result {
log_attr_access((int)type, (int)msg);
if (Msg::Set == msg) {
auto mech = data.currentItem<CapType::IAutomaticColorEnabled>();
int ret = SCANNER_ERR_OK;
std::string val(s2t::color_mode_from_auto((bool)mech));
SET_SANE_OPT(ret, scanner_, COLOR_MODE, &val);
return success();
}
std::vector<std::string> all;
GET_SANE_OPT(std::string, scanner_, COLOR_MODE, NULL, &all);
int twpt = s2t::color_mode_is_auto(all[sane_opts::RANGE_POS_CURRENT].c_str()) ? 1 : 0;
return CapSupGetAllReset<int, Bool, CapType::IAutomaticColorEnabled>(msg, data, { FALSE,TRUE }, twpt, false, twpt ? 1 : 0, 0);
};
m_query[CapType::IAutomaticColorNonColorPixelType] = msgSupportGetAllSetReset;
m_caps[CapType::IAutomaticColorNonColorPixelType] = [this](Twpp::CapType type, Msg msg, Capability& data)->Result {
log_attr_access((int)type, (int)msg);
if (msg == Msg::Set) {
int mech = (int)data.currentItem<CapType::IAutomaticColorNonColorPixelType>();
{
if ((UInt16)mech == 0 || (UInt16)mech == 1) {
int ret = SCANNER_ERR_OK;
std::string val(s2t::color_mode_from_auto((bool)mech));
SET_SANE_OPT(ret, scanner_, COLOR_MODE, &val);
if (ret == SCANNER_ERR_OK)
{
automaticcolortype_ = (UInt16)mech;
return success();
}
}
}
return seqError();
}
return CapSupGetAllReset<int, PixelType, CapType::IAutomaticColorNonColorPixelType>(msg, data, { PixelType::BlackWhite, PixelType::Gray }, automaticcolortype_, PixelType::Gray, automaticcolortype_, 1);
};
m_query[CapType::IJpegQuality] = msgSupportGetAllSetReset;
m_caps[CapType::IJpegQuality] = [this](Twpp::CapType type, Msg msg, Capability& data)->Result {
log_attr_access((int)type, (int)msg);
SANE_FinalImgFormat fif;
fif.img_format = SANE_IMAGE_TYPE_JPG;
if (Msg::Set == msg) {
auto mech = data.currentItem<CapType::IJpegQuality>();
if ((int)mech <= 0 || (int)mech > 100)
return badValue();
m_jpegQuality = (int)mech;
return success();
//int ret = SCANNER_ERR_OK;
//fif.detail = (void*)mech;
//SET_SANE_OPT(ret, scanner_, ex_final_format, &fif);
//return ret == SCANNER_ERR_OK ? success() : badValue();
}
//unsigned short q = 80;
//fif.detail = (void*)q;
//GET_SANE_OPT(SANE_FinalImgFormat, scanner_, ex_final_format, &fif, NULL, NULL, NULL);
//if(fif.img_format == SANE_IMAGE_TYPE_JPG)
// q = (unsigned short)fif.detail;
//return CapSupGetAllResetEx<unsigned short, UInt16, CapType::IJpegQuality>(msg, data, q, 80);
unsigned short q = m_jpegQuality;
return CapSupGetAllResetEx<unsigned short, UInt16, CapType::IJpegQuality>(msg, data, q, (UInt16)80);
};
m_query[CapType::IOrientation] = msgSupportGetAllSetReset;
m_caps[CapType::IOrientation] = [this](Twpp::CapType type, Msg msg, Capability& data) -> Result {
log_attr_access((int)type, (int)msg);
int l = 0;
char* buf = nullptr;
std::string cur("");
bool is_land = false, sp_land = false;
scanner_->get_option_info(SANE_OPT_ID_PAPER, nullptr, nullptr, &l, nullptr);
buf = new char[l + 4];
memset(buf, 0, l + 4);
scanner_->get_value(SANE_OPT_ID_PAPER, buf, &l);
cur = buf;
delete[] buf;
is_land = s2t::lateral_from_sane(cur.c_str(), &sp_land);
if (Msg::Set == msg) {
auto mech = data.currentItem<CapType::IOrientation>();
bool lateral = mech == Orientation::Landscape;
int ret = SCANNER_ERR_OK;
const char* lats[] = {"portrait", "lateral"};
if (lateral == is_land)
{
// nothing to do ...
utils::to_log(LOG_LEVEL_DEBUG, "set paper to '%s' while '%s' is '%s', omit this oper.\r\n", lats[lateral], cur.c_str(), lats[lateral]);
return success();
}
if (lateral && !sp_land)
{
utils::to_log(LOG_LEVEL_DEBUG, "set paper to 'lateral' but '%s' is not support 'lateral', omit this oper.\r\n", cur.c_str());
return badValue();
}
cur = s2t::lateral_to_sane(cur.c_str(), lateral);
SET_SANE_OPT(ret, scanner_, PAPER, &cur[0]);
return ret == SCANNER_ERR_OK ? success() : badValue();
}
int lateral = is_land ? (int)Orientation::Landscape : (int)Orientation::Portrait;
return CapSupGetAllReset<int, Orientation, CapType::IOrientation>(msg, data, { Orientation::Portrait, Orientation::Landscape }, lateral, Orientation::Portrait, lateral == 0 ? 0 : 1, 0);
};
m_query[CapType::IRotation] = msgSupportGetAllSetReset;
m_caps[CapType::IRotation] = [this](Twpp::CapType type, Msg msg, Capability& data) -> Result {
log_attr_access((int)type, (int)msg);
if (Msg::Set == msg) {
auto res = data.currentItem<Fix32>();
float angle = res.toFloat();
std::string val(s2t::rotation_to_sane(angle));
int ret = SCANNER_ERR_OK;
SET_SANE_OPT(ret, scanner_, TEXT_DIRECTION, &val[0]);
return ret == SCANNER_ERR_OK ? success() : badValue();
}
std::vector<float> values;
std::vector<std::string> svalues;
GET_SANE_OPT(std::string, scanner_, TEXT_DIRECTION, NULL, &svalues);
for (auto& v : svalues)
values.push_back(s2t::rotation_from_sane(v.c_str()));
svalues.clear();
Fix32 Init = Fix32(values[sane_opts::RANGE_POS_DEFAULT]), Now = Fix32(values[sane_opts::RANGE_POS_CURRENT]);
std::list<Fix32> vls;
UInt32 i = distance<float>(values, values[sane_opts::RANGE_POS_DEFAULT]),
n = distance<float>(values, values[sane_opts::RANGE_POS_CURRENT]);
for (size_t i = sane_opts::RANGE_POS_ENUM_BEGIN; i < values.size(); ++i)
vls.push_back(Fix32(values[i]));
return cap_get_enum_values<Fix32, CapType::IRotation>(msg, data, vls, Now, Init, n, i);
};
m_query[CapType::EnableDsUiOnly] = msgSupportGetAll;
m_caps[CapType::EnableDsUiOnly] = std::bind(enmGet<Bool>, _2, _3, Bool(true));
m_query[CapType::PaperDetectable] = msgSupportGetAll;
m_caps[CapType::PaperDetectable] = std::bind(oneValGet<Bool>, _2, _3, Bool(true));
m_query[CapType::FeederEnabled] = msgSupportGetAllSetReset;
m_caps[CapType::FeederEnabled] = [this](Twpp::CapType type, Msg msg, Capability& data) -> Result {
log_attr_access((int)type, (int)msg);
if (Msg::Set == msg) {
auto mech = data.currentItem<CapType::FeederEnabled>();
m_bFeederEnabled = mech;
return success();
}
return CapSupGetAllReset<bool, Bool, CapType::FeederEnabled>(msg, data, m_bFeederEnabled, Bool(true));
};
m_query[CapType::Duplex] = msgSupportGetAll;
m_caps[CapType::Duplex] = std::bind(oneValGet<Duplex>, _2, _3, Duplex::OnePass);
m_query[CapType::DuplexEnabled] = msgSupportGetAllSetReset;
m_caps[CapType::DuplexEnabled] = [this](Twpp::CapType type, Msg msg, Capability& data) -> Result {
log_attr_access((int)type, (int)msg);
int count = 0, l = sizeof(count);
scanner_->get_value(SANE_OPT_ID_SCAN_COUNT, &count, &l);
if (Msg::Set == msg) {
bool mech = data.currentItem<CapType::DuplexEnabled>();
std::string val(s2t::page_from_duplex(mech));
int ret = SCANNER_ERR_OK;
SET_SANE_OPT(ret, scanner_, PAGE, &val[0]);
utils::log_info(mech ? "Set1 Duplex is: true\r\n" : "Set1 Duplex is: false\r\n", 1);
if (ret == SCANNER_ERR_OK)
{
if (mech && count >= 2)
{
count /= 2;
SET_SANE_OPT(ret, scanner_, SCAN_COUNT, &count);
}
else if (!mech && count_ >= 2)
SET_SANE_OPT(ret, scanner_, SCAN_COUNT, &count);
}
return ret == SCANNER_ERR_OK ? success() : badValue();
}
BYTE dup = 1;
std::vector<std::string> all;
GET_SANE_OPT(std::string, scanner_, PAGE, NULL, &all);
dup = s2t::page_is_duplex(all[sane_opts::RANGE_POS_CURRENT].c_str());
utils::log_info(dup ? "Get Duplex is: true\r\n" : "Get Duplex is: false\r\n", 1);
int ret = SCANNER_ERR_OK;
if (dup && count >= 2)
{
count /= 2;
SET_SANE_OPT(ret, scanner_, SCAN_COUNT, &count);
}
else if (!dup && count >= 2)
SET_SANE_OPT(ret, scanner_, SCAN_COUNT, &count)
return CapSupGetAllReset<BYTE, Bool, CapType::DuplexEnabled>(msg, data, dup, Bool(true));
};
m_query[CapType::AutoFeed] = msgSupportGetAllSetReset;
m_caps[CapType::AutoFeed] = [this](Twpp::CapType type, Msg msg, Capability& data) -> Result {
log_attr_access((int)type, (int)msg);
if (Msg::Set == msg) {
auto mech = data.currentItem<CapType::AutoFeed>();
m_bAutoFeed = mech;
return success();
}
return CapSupGetAllReset<bool, Bool, CapType::AutoFeed>(msg, data, { false,true }, m_bAutoFeed, true, m_bAutoFeed ? 1 : 0, 1);
};
m_query[CapType::IImageFileFormat] = msgSupportGetAllSetReset;
m_caps[CapType::IImageFileFormat] = [this](Twpp::CapType type, Msg msg, Capability& data) -> Result {
log_attr_access((int)type, (int)msg);
SANE_FinalImgFormat now, init;
if (Msg::Set == msg || Msg::Reset == msg) {
if (Msg::Set == msg)
{
auto fmt = data.currentItem<ImageFileFormat>();
if (fmt != Twpp::ImageFileFormat::Bmp &&
fmt != Twpp::ImageFileFormat::Jfif &&
fmt != Twpp::ImageFileFormat::Tiff)
return badValue();
init.img_format = (SANE_ImageType)fmt;
{
if (fmt == Twpp::ImageFileFormat::Bmp)
utils::log_info("set CapType::IImageFileFormat = Bmp\r\n", 1);
else if (fmt == Twpp::ImageFileFormat::Jfif)
utils::log_info("set CapType::IImageFileFormat = Jfif\r\n", 1);
else if (fmt == Twpp::ImageFileFormat::Tiff)
utils::log_info("set CapType::IImageFileFormat = Tiff\r\n", 1);
}
}
else
data = Capability::createOneValue<CapType::IImageFileFormat>((Twpp::ImageFileFormat)init.img_format);
m_fileXfer.setFormat((Twpp::ImageFileFormat)init.img_format);
return success();
}
ImageFileFormat Now = m_fileXfer.format() , Init = Twpp::ImageFileFormat::Bmp;
std::list<ImageFileFormat> vals;
UInt32 i = 0, // distance<SANE_FinalImgFormat>(all, init),
n = 0; // distance<SANE_FinalImgFormat>(all, now);
vals.push_back(Twpp::ImageFileFormat::Bmp);
vals.push_back(Twpp::ImageFileFormat::Jfif);
vals.push_back(Twpp::ImageFileFormat::Tiff);
int ind = 0;
for (auto& v: vals)
{
if (v == Now)
{
n = ind;
break;
}
ind++;
}
return cap_get_enum_values<ImageFileFormat, CapType::IImageFileFormat>(msg, data, vals, Now, Init, n, i);
};
m_query[CapType::IAutomaticDeskew] = msgSupportGetAllSetReset;
m_caps[CapType::IAutomaticDeskew] = [this](Twpp::CapType type, Msg msg, Capability& data)->Result {
log_attr_access((int)type, (int)msg);
if (Msg::Set == msg) {
auto atuodsw = data.currentItem<CapType::IAutomaticDeskew>();
int ret = SCANNER_ERR_OK;
SET_SANE_OPT(ret, scanner_, ANTI_SKEW, (bool*)&atuodsw);
return ret == SCANNER_ERR_OK ? success() : seqError();
}
BYTE ato = 1;
std::vector<bool> all;
GET_SANE_OPT(bool, scanner_, ANTI_SKEW, NULL, &all);
ato = all[sane_opts::RANGE_POS_CURRENT];
return CapSupGetAllReset<BYTE, bool, CapType::IAutomaticDeskew>(msg, data, ato, true);
};
m_query[CapType::IAutomaticRotate] = msgSupportGetAllSetReset;
m_caps[CapType::IAutomaticRotate] = [this](Twpp::CapType type, Msg msg, Capability& data)->Result {
log_attr_access((int)type, (int)msg);
if (Msg::Set == msg) {
auto mech = data.currentItem<CapType::IAutomaticRotate>();
int ret = SCANNER_ERR_OK;
std::string val(s2t::auto_rotation_to_sane((bool)mech));
SET_SANE_OPT(ret, scanner_, TEXT_DIRECTION, &val[0]);
return ret == SCANNER_ERR_OK ? success() : badValue();
}
float direction = .0f;
BYTE am = 0;
std::vector<std::string> all;
GET_SANE_OPT(std::string, scanner_, TEXT_DIRECTION, NULL, &all);
am = s2t::auto_rotation_from_sane(all[sane_opts::RANGE_POS_CURRENT].c_str());
return CapSupGetAllReset<BYTE, bool, CapType::IAutomaticRotate>(msg, data, am, false);
};
if (std::find(ids.begin(), ids.end(), SANE_OPT_ID_DEVICE_SERIAL_NO) != ids.end())
{
m_query[CapType::SerialNumber] = msgSupportGetAll;
m_caps[CapType::SerialNumber] = [this](Twpp::CapType type, Msg msg, Capability& data)->Result {
log_attr_access((int)type, (int)msg);
Str255 str;
int l = 255;
scanner_->get_value(SANE_OPT_ID_DEVICE_SERIAL_NO, str.data(), &l);
return CapSupGetAll<Str255, Str255, CapType::SerialNumber>(msg, data, str, str);
};
}
if (std::find(ids.begin(), ids.end(), SANE_OPT_ID_WAIT_TO_SCAN) != ids.end())
{
m_query[CapType::AutoScan] = msgSupportGetAllSetReset;
m_caps[CapType::AutoScan] = [this](Twpp::CapType type, Msg msg, Capability& data)->Result {
log_attr_access((int)type, (int)msg);
if (Msg::Set == msg || Msg::Reset == msg) {
bool val = false;
if(Msg::Set == msg)
val = (bool)data.currentItem<CapType::AutoScan>();
int ret = SANE_STATUS_GOOD;
m_autoscan = val;
SET_SANE_OPT(ret, scanner_, WAIT_TO_SCAN, &val);
return ret == SANE_STATUS_GOOD ? success() : badValue();
}
std::vector<bool> all;
GET_SANE_OPT(bool, scanner_, WAIT_TO_SCAN, NULL, &all);
m_autoscan = (bool)all[sane_opts::RANGE_POS_CURRENT];
return CapSupGetAllReset<Bool, Bool, CapType::AutoScan>(msg, data, m_autoscan, (bool)all[sane_opts::RANGE_POS_DEFAULT]);
};
}
m_query[CapType::IAutoSize] = msgSupportGetAllSetReset;
m_caps[CapType::IAutoSize] = [this](Twpp::CapType type, Msg msg, Capability& data)->Result {
log_attr_access((int)type, (int)msg);
CapabilityPrintf(msg, enum2str(CapType::IAutoSize), msg == Msg::Set ? to_string((int)data.currentItem<CapType::IAutoSize>()) : "");
if (Msg::Set == msg) {
auto autosize = data.currentItem<CapType::IAutoSize>();
std::string val(s2t::auto_size_to_sane(autosize == AutoSize::Auto));
int ret = SCANNER_ERR_OK;
SET_SANE_OPT(ret, scanner_, PAPER, &val[0]);
return ret == SCANNER_ERR_OK ? success() : badValue();
}
std::vector<std::string> sall;
GET_SANE_OPT(std::string, scanner_, PAPER, NULL, &sall);
UInt16 size = s2t::auto_size_from_sane(sall[sane_opts::RANGE_POS_CURRENT].c_str()) ? (UInt16)AutoSize::Auto : (UInt16)AutoSize::None;
return CapSupGetAllReset<UInt16, AutoSize, CapType::IAutoSize>(msg, data, { AutoSize::None, AutoSize::Auto }, size, AutoSize::None, (size == (UInt16)AutoSize::Auto) ? 1 : 0, 0);
};
m_query[CapType::IAutomaticBorderDetection] = msgSupportGetAllSetReset;
m_caps[CapType::IAutomaticBorderDetection] = [this](Twpp::CapType type, Msg msg, Capability& data)->Result {
log_attr_access((int)type, (int)msg);
if (Msg::Set == msg) {
auto autodetectborder = data.currentItem<CapType::IAutomaticBorderDetection>();
int ret = SCANNER_ERR_OK;
SET_SANE_OPT(ret, scanner_, SIZE_CHECK, (bool*)&autodetectborder);
return ret == SCANNER_ERR_OK ? success() : badValue();
}
Bool init = false,
erase = false;
std::vector<bool> all;
GET_SANE_OPT(bool, scanner_, SIZE_CHECK, NULL, &all);
init = (bool)all[sane_opts::RANGE_POS_DEFAULT];
erase = (bool)all[sane_opts::RANGE_POS_CURRENT];
return CapSupGetAllReset<Bool, Bool, CapType::IAutomaticBorderDetection>(msg, data, { false,true }, erase, init, erase ? 1 : 0, 0);
};
if (std::find(ids.begin(), ids.end(), SANE_OPT_ID_DISCARDBLANK) == ids.end())
{
m_query[CapType::IAutoDiscardBlankPages] = msgSupportGetAllSetReset;
m_caps[CapType::IAutoDiscardBlankPages] = [this](Twpp::CapType type, Msg msg, Capability& data)->Result {
log_attr_access((int)type, (int)msg);
if (Msg::Set == msg) {
auto mech = data.currentItem<CapType::IAutoDiscardBlankPages>();
bool discard = mech == DiscardBlankPages::Auto;
int ret = SCANNER_ERR_OK;
std::string val(s2t::discard_blank_to_sane(discard));
SET_SANE_OPT(ret, scanner_, PAGE, &val[0]);
return ret == SCANNER_ERR_OK ? success() : badValue();
}
std::vector<bool> all;
std::vector<std::string> sall;
GET_SANE_OPT(std::string, scanner_, PAGE, NULL, &sall);
for (auto& v : sall)
all.push_back(s2t::discard_blank_from_sane(v.c_str()));
DiscardBlankPages autodiscradblank = all[sane_opts::RANGE_POS_CURRENT] ? DiscardBlankPages::Auto : DiscardBlankPages::Disabled;
return CapSupGetAllReset<DiscardBlankPages, DiscardBlankPages, CapType::IAutoDiscardBlankPages>(msg, data, autodiscradblank, DiscardBlankPages::Disabled);
};
}
else
{
m_query[CapType::IAutoDiscardBlankPages] = msgSupportGetAllSetReset;
m_caps[CapType::IAutoDiscardBlankPages] = [this](Twpp::CapType type, Msg msg, Capability& data)->Result {
log_attr_access((int)type, (int)msg);
if (Msg::Set == msg) {
auto mech = data.currentItem<CapType::IAutoDiscardBlankPages>();
bool discard = mech == DiscardBlankPages::Auto;
int ret = SCANNER_ERR_OK;
{
SET_SANE_OPT(ret, scanner_, DISCARDBLANK, &discard);
}
return ret == SCANNER_ERR_OK ? success() : badValue();
}
std::vector<bool> all;
{
GET_SANE_OPT(bool, scanner_, DISCARDBLANK, NULL, &all);
}
DiscardBlankPages autodiscradblank = all[sane_opts::RANGE_POS_CURRENT] ? DiscardBlankPages::Auto : DiscardBlankPages::Disabled;
return CapSupGetAllReset<DiscardBlankPages, DiscardBlankPages, CapType::IAutoDiscardBlankPages>(msg, data, autodiscradblank, DiscardBlankPages::Disabled);
};
}
m_query[CapType::IFilter] = msgSupportGetAllSetReset;
m_caps[CapType::IFilter] = [this](Twpp::CapType type, Msg msg, Capability& data)->Result {
log_attr_access((int)type, (int)msg);
if (Msg::Set == msg) {
auto mech = data.currentItem<CapType::IFilter>();
int ret = SCANNER_ERR_OK;
std::string val(s2t::filter_to_sane((int)mech));
SET_SANE_OPT(ret, scanner_, FILTER, &val[0]);
return ret == SCANNER_ERR_OK ? success() : badValue();
}
int cur = FILTER_NONE, def = FILTER_NONE;
std::vector<int> vals;
std::vector<std::string> svals;
GET_SANE_OPT(std::string, scanner_, FILTER, NULL, &svals);
s2t::filter_from_sane(svals, vals, true);
svals.clear();
std::list<Filter> vs;
Filter now = (Filter)(vals[sane_opts::RANGE_POS_CURRENT]),
init = (Filter)(vals[sane_opts::RANGE_POS_DEFAULT]);
UInt32 curInd = distance<int>(vals, vals[sane_opts::RANGE_POS_CURRENT]),
defInd = distance<int>(vals, vals[sane_opts::RANGE_POS_DEFAULT]);;
for (size_t i = sane_opts::RANGE_POS_ENUM_BEGIN; i < vals.size(); ++i)
{
vs.push_back((Filter)vals[i]);
}
BYTE f = (BYTE)now;
return CapSupGetAllReset<BYTE, Filter, CapType::IFilter>(msg, data, vs, f, init, curInd, defInd);
};
m_query[CapType::IBrightness] = msgSupportGetAllSetReset;
m_caps[CapType::IBrightness] = [this](Twpp::CapType type, Msg msg, Capability& data)->Result {
log_attr_access((int)type, (int)msg);
int init = 128, l = 1, u = 255, step = 1, now = 128;
int ret = SCANNER_ERR_OK;
std::vector<int> all;
GET_SANE_OPT(int, scanner_, BRIGHTNESS, NULL, &all);
init = all[sane_opts::RANGE_POS_DEFAULT];
now = all[sane_opts::RANGE_POS_CURRENT];
l = all[sane_opts::RANGE_POS_LOWER];
u = all[sane_opts::RANGE_POS_UPPER];
step = all[sane_opts::RANGE_POS_STEP];
float sf = trans_range((float)step, (float)l, (float)u, -1000.0f, 1000.0f, true),
nf = trans_range((float)now, (float)l, (float)u, -1000.0f, 1000.0f),
initf = trans_range((float)init, (float)l, (float)u, -1000.0f, 1000.0f);
switch (msg) {
case Msg::Get:
sf = 333.333f;
data = Capability::createRange<CapType::IBrightness>(Fix32(-1000.0f), Fix32(1000.0f), Fix32(sf), Fix32(nf), Fix32(initf));
return success();
case Msg::GetCurrent:
data = Capability::createOneValue<CapType::IBrightness>(Fix32(nf));
return success();
case Msg::GetDefault:
data = Capability::createOneValue<CapType::IBrightness>(Fix32(initf));
return success();
case Msg::Set: {
auto mech = data.currentItem<CapType::IBrightness>();
if (mech > 1000.0f || mech < -1000.0f)
return badValue();
initf = mech.toFloat();
}
case Msg::Reset:
now = (int)(trans_range(initf, -1000.0f, 1000.0f, (float)l, (float)u) + .5f);
SET_SANE_OPT(ret, scanner_, BRIGHTNESS, &now);
if (Msg::Reset == msg)
{
initf = trans_range((float)now, (float)l, (float)u, -1000.0f, 1000.0f, true);
data = Capability::createOneValue<CapType::IBrightness>(Fix32(initf));
}
return ret == SCANNER_ERR_OK ? success() : badValue();
default:
return capBadOperation();
}
};
m_query[CapType::IContrast] = msgSupportGetAllSetReset;
m_caps[CapType::IContrast] = [this](Twpp::CapType type, Msg msg, Capability& data)->Result {
log_attr_access((int)type, (int)msg);
int init = 4, l = 1, u = 7, step = 1, now = 4;
int ret = SCANNER_ERR_OK;
std::vector<int> all;
GET_SANE_OPT(int, scanner_, CONTRAST, NULL, &all);
init = all[sane_opts::RANGE_POS_DEFAULT];
now = all[sane_opts::RANGE_POS_CURRENT];
l = all[sane_opts::RANGE_POS_LOWER];
u = all[sane_opts::RANGE_POS_UPPER];
step = all[sane_opts::RANGE_POS_STEP];
float sf = trans_range((float)step, (float)l, (float)u, -1000.0f, 1000.0f, true),
nf = trans_range((float)now, (float)l, (float)u, -1000.0f, 1000.0f),
initf = trans_range((float)init, (float)l, (float)u, -1000.0f, 1000.0f);
switch (msg) {
case Msg::Get:
// sf = 333.333f;
data = Capability::createRange<CapType::IContrast>(Fix32(-1000.0f), Fix32(1000.0f), Fix32(sf), Fix32(nf), Fix32(initf));
return success();
case Msg::GetCurrent:
data = Capability::createOneValue<CapType::IContrast>(Fix32(nf));
return success();
case Msg::GetDefault:
data = Capability::createOneValue<CapType::IContrast>(Fix32(initf));
return success();
case Msg::Set: {
auto mech = data.currentItem<CapType::IContrast>();
if (mech > 1000.0f || mech < -1000.0f)
return badValue();
initf = mech.toFloat();
}
case Msg::Reset:
now = (int)(trans_range(initf, -1000.0f, 1000.0f, (float)l, (float)u) + .5f);
SET_SANE_OPT(ret, scanner_, CONTRAST, &now);
if (Msg::Reset == msg)
{
initf = trans_range((float)now, (float)l, (float)u, -1000.0f, 1000.0f, true);
data = Capability::createOneValue<CapType::IContrast>(Fix32(initf));
}
return ret == SCANNER_ERR_OK ? success() : badValue();
default:
return capBadOperation();
}
};
m_query[CapType::IGamma] = msgSupportGetAllSetReset;
m_caps[CapType::IGamma] = [this](Twpp::CapType type, Msg msg, Capability& data)->Result {
log_attr_access((int)type, (int)msg);
double init = .0f, l = .0f, u = .0f, step = .0f, now = .0f;
std::vector<double> all;
int ret = SCANNER_ERR_OK;
GET_SANE_OPT(double, scanner_, GAMMA, NULL, &all);
init = all[sane_opts::RANGE_POS_DEFAULT];
now = all[sane_opts::RANGE_POS_CURRENT];
l = all[sane_opts::RANGE_POS_LOWER];
u = all[sane_opts::RANGE_POS_UPPER];
step = all[sane_opts::RANGE_POS_STEP];
switch (msg) {
case Msg::Get:
data = Capability::createRange<CapType::IGamma>(Fix32(l), Fix32(u), Fix32(step), Fix32(now), Fix32(init));
return success();
case Msg::GetCurrent:
data = Capability::createOneValue<CapType::IGamma>(Fix32(now));
return success();
case Msg::GetDefault:
data = Capability::createOneValue<CapType::IGamma>(Fix32(init));
return success();
case Msg::Set: {
auto mech = data.currentItem<CapType::IGamma>();
if (mech > u || mech < l)
return badValue();
init = mech.toFloat();
}
case Msg::Reset:
SET_SANE_OPT(ret, scanner_, GAMMA, &init);
return ret == SCANNER_ERR_OK ? success() : badValue();
default:
return capBadOperation();
}
};
m_query[CapType::CustomDsData] = msgSupportGetAll;
m_caps[CapType::CustomDsData] = [this](Twpp::CapType type, Msg msg, Capability& data) -> Result {
log_attr_access((int)type, (int)msg);
Bool init(true);
return CapSupGetAll<Bool, Bool, CapType::CustomDsData>(msg, data, init, init);
};
m_query[CapType::DoubleFeedDetection] = msgSupportGetAllSetReset;
m_caps[CapType::DoubleFeedDetection] = [this](Twpp::CapType type, Msg msg, Capability& data)->Result {
log_attr_access((int)type, (int)msg);
if (Msg::Set == msg) {
auto val = data.currentItem<CapType::DoubleFeedDetection>();
int ret = SCANNER_ERR_OK;
bool enable = val == DoubleFeedDetection::Ultrasonic;
if (double_check_mode_ == DOUBLE_CHECK_ULTRASONIC)
{
enable = (bool)val;
utils::to_log(1, "DoubleFeedDetection parameter is boolean for Ultrasonic. set to %s\r\n", enable ? "TRUE" : "FALSE");
}
SET_SANE_OPT(ret, scanner_, IS_ULTROSONIC_CHECK, &enable);
return ret == SCANNER_ERR_OK ? success() : seqError();
}
DoubleFeedDetection init = DoubleFeedDetection::Ultrasonic;
std::vector<bool> all;
GET_SANE_OPT(bool, scanner_, IS_ULTROSONIC_CHECK, NULL, &all);
if (double_check_mode_ == DOUBLE_CHECK_ULTRASONIC)
{
Bool ni(all[sane_opts::RANGE_POS_CURRENT]),
ii(all[sane_opts::RANGE_POS_DEFAULT]);
data = Capability::createEnumeration<Bool>(CapType::DoubleFeedDetection, { FALSE, TRUE }, ni, ii);
return success();
}
else
{
BYTE ato = !all[sane_opts::RANGE_POS_CURRENT];
init = all[sane_opts::RANGE_POS_DEFAULT] ? DoubleFeedDetection::Ultrasonic : DoubleFeedDetection::ByLength;
return CapSupGetAllReset<BYTE, DoubleFeedDetection, CapType::DoubleFeedDetection>(msg, data, ato, init);
}
};
m_query[CapType::IAutomaticCropUsesFrame] = msgSupportGetAll;
m_caps[CapType::IAutomaticCropUsesFrame] = [this](Twpp::CapType type, Msg msg, Capability& data)->Result {
log_attr_access((int)type, (int)msg);
std::vector<std::string> all;
GET_SANE_OPT(std::string, scanner_, PAPER, NULL, &all);
BYTE crop = s2t::auto_size_from_sane(all[sane_opts::RANGE_POS_CURRENT].c_str()); // FIXED ME !!! whether should be !AutoSize
return CapSupGetAll<BYTE, bool, CapType::IAutomaticCropUsesFrame>(msg, data, crop, false);
};
m_query[CapType::FeederLoaded] = msgSupportGetAll;
m_caps[CapType::FeederLoaded] = [this](Twpp::CapType type, Msg msg, Capability& data) -> Result {
log_attr_access((int)type, (int)msg);
Bool paperon = scanner_->is_paper_on();
return CapSupGetAll<Bool, Bool, CapType::FeederLoaded>(msg, data, paperon, paperon);
};
m_query[(CapType)CAP_TYPE_EX_DISCARD_BLANK_RECEIPT] = msgSupportGetAllSetReset;
m_caps[(CapType)CAP_TYPE_EX_DISCARD_BLANK_RECEIPT] = [this](Twpp::CapType type, Msg msg, Capability& data)->Result {
log_attr_access((int)type, (int)msg);
if (Msg::Set == msg) {
auto mech = data.currentItem<DiscardBlankPages>();
bool discard = mech == DiscardBlankPages::Auto;
std::string val(s2t::discard_blank_receipt_to_sane(discard));
int ret = SCANNER_ERR_OK;
SET_SANE_OPT(ret, scanner_, PAGE, &val[0]);
return ret == SCANNER_ERR_OK ? success() : badValue();
}
std::vector<std::string> all;
GET_SANE_OPT(std::string, scanner_, PAGE, NULL, &all);
DiscardBlankPages autodiscradblank = s2t::discard_blank_receipt_from_sane(all[sane_opts::RANGE_POS_CURRENT].c_str()) ? DiscardBlankPages::Auto : DiscardBlankPages::Disabled;
return CapSupGetAllResetEx<DiscardBlankPages, DiscardBlankPages, (CapType)CapTypeEx::CAP_TYPE_EX_DISCARD_BLANK_RECEIPT>(msg, data, autodiscradblank, DiscardBlankPages::Disabled);
// return success();
};
m_query[(CapType)CapTypeEx::CAP_TYPE_EX_FILL_HOLE_RATIO] = msgSupportGetAllSetReset;
m_caps[(CapType)CapTypeEx::CAP_TYPE_EX_FILL_HOLE_RATIO] = [this](Twpp::CapType type, Msg msg, Capability& data)->Result {
log_attr_access((int)type, (int)msg);
double init = .0f, l = .0f, u = .0f, step = .0f, now = .0f;
std::vector<double> all;
int ret = SCANNER_ERR_OK;
GET_SANE_OPT(double, scanner_, SEARCH_HOLE_RANGE_L, NULL, &all);
init = all[sane_opts::RANGE_POS_DEFAULT] * 100.0f;
now = all[sane_opts::RANGE_POS_CURRENT] * 100.0f;
l = all[sane_opts::RANGE_POS_LOWER] * 100.0f;
u = all[sane_opts::RANGE_POS_UPPER] * 100.0f;
step = all[sane_opts::RANGE_POS_STEP] * 100.0f;
switch (msg) {
case Msg::Get:
data = Capability::createRange<UInt32>((CapType)CapTypeEx::CAP_TYPE_EX_FILL_HOLE_RATIO, UInt32(l), UInt32(u), UInt32(step), UInt32(now), UInt32(init));
return success();
case Msg::GetCurrent:
data = Capability::createOneValue<UInt32>((CapType)CapTypeEx::CAP_TYPE_EX_FILL_HOLE_RATIO, UInt32(now));
return success();
case Msg::GetDefault:
data = Capability::createOneValue<UInt32>((CapType)CapTypeEx::CAP_TYPE_EX_FILL_HOLE_RATIO, UInt32(init));
return success();
case Msg::Set: {
auto mech = data.currentItem<UInt32>();
if (mech > u || mech < l)
return badValue();
init = (float)mech;
}
case Msg::Reset:
init /= 100.0f;
SET_SANE_OPT(ret, scanner_, SEARCH_HOLE_RANGE_L, &init);
return ret == SCANNER_ERR_OK ? success() : badValue();
default:
return capBadOperation();
}
};
m_query[CapType(CapTypeEx::CAP_TYPE_EX_FOLD)] = msgSupportGetAllSetReset;
m_caps[CapType(CapTypeEx::CAP_TYPE_EX_FOLD)] = [this](Twpp::CapType type, Msg msg, Capability& data)->Result {
log_attr_access((int)type, (int)msg);
if (Msg::Set == msg || Msg::Reset == msg) {
bool fold = false;
if (msg == Msg::Set)
fold = (bool)data.currentItem<Int32>();
std::string val(s2t::fold_to_sane(fold));
int ret = SCANNER_ERR_OK;
SET_SANE_OPT(ret, scanner_, PAGE, &val[0]);
return ret == SCANNER_ERR_OK ? success() : badValue();
}
std::vector<std::string> all;
GET_SANE_OPT(std::string, scanner_, PAGE, NULL, &all);
BYTE fold = s2t::fold_from_sane(all[sane_opts::RANGE_POS_CURRENT].c_str());
return CapSupGetAllResetEx<BYTE, Int32, (CapType)CapTypeEx::CAP_TYPE_EX_FOLD>(msg, data, fold, 0);
};
if (std::find(ids.begin(), ids.end(), SANE_OPT_ID_DOG_EAR_SIZE) == ids.end())
{
m_query[(CapType)(CapTypeEx::CAP_TYPE_EX_DOGEAR_DIST)] = msgSupportGetAllSetReset;
m_caps[(CapType)(CapTypeEx::CAP_TYPE_EX_DOGEAR_DIST)] = [this](Twpp::CapType type, Msg msg, Capability& data)->Result {
log_attr_access((int)type, (int)msg);
int now = 10, init = 10, l = 0, u = 100, s = 10;
std::vector<int> all;
GET_SANE_OPT(int, scanner_, DOG_EAR_SIZE, NULL, &all);
init = all[sane_opts::RANGE_POS_DEFAULT];
now = all[sane_opts::RANGE_POS_CURRENT];
l = all[sane_opts::RANGE_POS_LOWER];
u = all[sane_opts::RANGE_POS_UPPER];
s = all[sane_opts::RANGE_POS_STEP];
if (Msg::Set == msg || Msg::Reset == msg) {
if (Msg::Set == msg)
{
auto mech = data.currentItem<UInt32>();
if (mech < 10 || mech > 300)
return badValue();
init = (int)(trans_range((float)mech, 10.0f, 300.0f, (float)l, (float)u) + .5f);
}
int ret = SCANNER_ERR_OK;
SET_SANE_OPT(ret, scanner_, DOG_EAR_SIZE, &init);
return ret == SCANNER_ERR_OK ? success() : badValue();
}
UInt32 Now = UInt32(trans_range((float)now, (float)l, (float)u, 10.0f, 300.0f) + .5f),
Init = UInt32(trans_range((float)init, (float)l, (float)u, 10.0f, 300.0f) + .5f);
return CapSupGetAllResetEx<UInt32, UInt32, (CapType)CapTypeEx::CAP_TYPE_EX_DOGEAR_DIST>(msg, data, Now, Init);
};
}
m_query[(CapType)(CapTypeEx::CAP_TYPE_EX_CROP_MODEL)] = msgSupportGetAllSetReset;
m_caps[(CapType)(CapTypeEx::CAP_TYPE_EX_CROP_MODEL)] = [this](Twpp::CapType type, Msg msg, Capability& data)->Result {
log_attr_access((int)type, (int)msg);
std::vector<std::string> all;
GET_SANE_OPT(std::string, scanner_, PAPER, NULL, &all);
if (Msg::Set == msg || Msg::Reset == msg)
{
bool def = s2t::auto_crop_from_sane(all[sane_opts::RANGE_POS_DEFAULT].c_str());
if (Msg::Set == msg)
def = data.currentItem<BYTE>() == 1;
std::string val(s2t::auto_crop_to_sane(def));
int ret = SCANNER_ERR_OK;
SET_SANE_OPT(ret, scanner_, PAPER, &val[0]);
return ret == SCANNER_ERR_OK ? success() : badValue();
}
BYTE crop = s2t::auto_crop_from_sane(all[sane_opts::RANGE_POS_CURRENT].c_str()),
init = s2t::auto_crop_from_sane(all[sane_opts::RANGE_POS_DEFAULT].c_str());
return CapSupGetAll<BYTE, bool, CapType::IAutomaticCropUsesFrame>(msg, data, crop, init);
};
m_query[(CapType)CapTypeEx::CAP_TYPE_EX_MULTI_OUT_TYPE] = msgSupportGetAllSetReset;
m_caps[(CapType)CapTypeEx::CAP_TYPE_EX_MULTI_OUT_TYPE] = [this](Twpp::CapType type, Msg msg, Capability& data)->Result {
log_attr_access((int)type, (int)msg);
int cur = MULTI_OUT_NONE, def = MULTI_OUT_NONE;
std::vector<int> all;
std::vector<std::string> sall;
GET_SANE_OPT(std::string, scanner_, MULTI_OUT_TYPE, NULL, &sall);
for (auto& v : sall)
all.push_back(s2t::multi_out_from_sane(v.c_str()));
sall.clear();
cur = all[sane_opts::RANGE_POS_CURRENT];
def = all[sane_opts::RANGE_POS_DEFAULT];
if (Msg::Set == msg || Msg::Reset == msg) {
auto mech = def;
if (msg == Msg::Set)
mech = data.currentItem<UInt32>();
int ret = SCANNER_ERR_OK;
std::string val(s2t::multi_out_to_sane((int)mech));
SET_SANE_OPT(ret, scanner_, MULTI_OUT_TYPE, &val[0]);
return ret == SCANNER_ERR_OK ? success() : badValue();
}
Int32 now = cur, init = def, ind = 0,
curInd = distance<int>(all, cur),
defInd = distance<int>(all, def);
std::list<Int32> vals;
for (size_t i = sane_opts::RANGE_POS_ENUM_BEGIN; i < all.size(); ++i)
vals.push_back(all[i]);
return CapSupGetAllResetEx<Int32, Int32, (CapType)CapTypeEx::CAP_TYPE_EX_MULTI_OUT_TYPE>(msg, data, vals, now, init, curInd, defInd);
};
if (std::find(ids.begin(), ids.end(), SANE_OPT_ID_WAIT_TO_SCAN) != ids.end())
{
m_query[CapType(CapTypeEx::CAP_TYPE_EX_TO_BE_SCAN)] = m_query[CapType::AutoScan];
m_caps[CapType(CapTypeEx::CAP_TYPE_EX_TO_BE_SCAN)] = m_caps[CapType::AutoScan];
}
m_query[CapType(CapTypeEx::CAP_TYPE_EX_SCAN_WITH_HOLE)] = msgSupportGetAllSetReset;
m_caps[CapType(CapTypeEx::CAP_TYPE_EX_SCAN_WITH_HOLE)] = [this](Twpp::CapType type, Msg msg, Capability& data)->Result {
log_attr_access((int)type, (int)msg);
if (Msg::Set == msg || Msg::Reset == msg) {
auto tobe = false;
if (msg == Msg::Set)
tobe = data.currentItem<Int32>();
int ret = SCANNER_ERR_OK;
SET_SANE_OPT(ret, scanner_, RID_HOLE_L, &tobe);
return ret == SCANNER_ERR_OK ? success() : seqError();
}
std::vector<bool> all;
GET_SANE_OPT(bool, scanner_, RID_HOLE_L, NULL, &all);
BYTE tobe = all[sane_opts::RANGE_POS_CURRENT];;
return CapSupGetAllResetEx<BYTE, Int32, (CapType)CapTypeEx::CAP_TYPE_EX_SCAN_WITH_HOLE>(msg, data, tobe, 0);
};
if (std::find(ids.begin(), ids.end(), SANE_OPT_ID_DEVICE_SERIAL_NO) != ids.end())
{
m_query[(CapType)(CapTypeEx::CAP_TYPE_EX_ENCODE)] = m_query[CapType::SerialNumber];
m_caps[(CapType)(CapTypeEx::CAP_TYPE_EX_ENCODE)] = m_caps[CapType::SerialNumber];
}
if (std::find(ids.begin(), ids.end(), SANE_OPT_ID_TIME_TO_SLEEP) != ids.end())
{
m_query[(CapType)(CapTypeEx::CAP_TYPE_EX_POWER_LEVEL)] = msgSupportGetAllSetReset;
m_caps[(CapType)(CapTypeEx::CAP_TYPE_EX_POWER_LEVEL)] = [this](Twpp::CapType type, Msg msg, Capability& data)->Result {
log_attr_access((int)type, (int)msg);
int cur = SANE_POWER_MINUTES_30, def = SANE_POWER_MINUTES_30;
std::vector<int> all;
std::vector<std::string> sall;
GET_SANE_OPT(std::string, scanner_, TIME_TO_SLEEP, NULL, &sall);
for (auto& v : sall)
all.push_back(s2t::power_from_sane(v.c_str()));
sall.clear();
cur = all[sane_opts::RANGE_POS_CURRENT];
def = all[sane_opts::RANGE_POS_DEFAULT];
if (Msg::Set == msg || Msg::Reset == msg) {
if (msg == Msg::Set)
def = data.currentItem<UInt32>();
int ret = SCANNER_ERR_OK;
std::string val(s2t::power_to_sane(def));
SET_SANE_OPT(ret, scanner_, TIME_TO_SLEEP, &val[0]);
return ret == SCANNER_ERR_OK ? success() : badValue();
}
UInt32 now = cur, init = def, ind = 0,
curInd = distance<int>(all, cur),
defInd = distance<int>(all, def);
std::list<UInt32> vals;
for (size_t i = sane_opts::RANGE_POS_ENUM_BEGIN; i < all.size(); ++i)
{
vals.push_back(all[i]);
}
return CapSupGetAllResetEx<UInt32, UInt32, (CapType)CapTypeEx::CAP_TYPE_EX_POWER_LEVEL>(msg, data, vals, now, init, curInd, defInd);
};
}
if (std::find(ids.begin(), ids.end(), SANE_OPT_ID_FILL_BKG_MODE) != ids.end())
{
m_query[(CapType)CapTypeEx::CAP_TYPE_EX_BKG_FILLING_METHOD] = msgSupportGetAllSetReset;
m_caps[(CapType)CapTypeEx::CAP_TYPE_EX_BKG_FILLING_METHOD] = [this](Twpp::CapType type, Msg msg, Capability& data)->Result {
log_attr_access((int)type, (int)msg);
if (Msg::Set == msg) {
auto convex = data.currentItem<Bool>();
std::string val(s2t::convex_to_sane((bool)convex));
int ret = SCANNER_ERR_OK;
SET_SANE_OPT(ret, scanner_, FILL_BKG_MODE, &val[0]);
return ret == SCANNER_ERR_OK ? success() : badValue();
}
bool val = false, cur = false;
Bool init = false,
erase = false;
std::vector<std::string> all;
GET_SANE_OPT(std::string, scanner_, FILL_BKG_MODE, NULL, &all);
init = (bool)s2t::convex_from_sane(all[sane_opts::RANGE_POS_DEFAULT].c_str());
erase = (bool)s2t::convex_from_sane(all[sane_opts::RANGE_POS_CURRENT].c_str());
return CapSupGetAllResetEx<Bool, Bool, (CapType)CapTypeEx::CAP_TYPE_EX_BKG_FILLING_METHOD>(msg, data, { false,true }, erase, init, erase ? 1 : 0, 0);
};
}
if (std::find(ids.begin(), ids.end(), SANE_OPT_ID_SHARPEN) != ids.end())
{
m_query[(CapType)CapTypeEx::CAP_TYPE_EX_SHARPEN] = msgSupportGetAllSetReset;
m_caps[(CapType)CapTypeEx::CAP_TYPE_EX_SHARPEN] = [this](Twpp::CapType type, Msg msg, Capability& data)->Result {
log_attr_access((int)type, (int)msg);
int cur = MULTI_OUT_NONE, def = MULTI_OUT_NONE;
std::vector<int> all;
std::vector<std::string> sall;
GET_SANE_OPT(std::string, scanner_, SHARPEN, NULL, &sall);
for (auto& v : sall)
all.push_back(s2t::sharpen_from_sane(v.c_str()));
sall.clear();
cur = all[sane_opts::RANGE_POS_CURRENT];
def = all[sane_opts::RANGE_POS_DEFAULT];
if (Msg::Set == msg || Msg::Reset == msg) {
auto mech = def;
if (msg == Msg::Set)
mech = data.currentItem<UInt32>();
int ret = SCANNER_ERR_OK;
std::string val(s2t::sharpen_to_sane((int)mech));
SET_SANE_OPT(ret, scanner_, SHARPEN, &val[0]);
return ret == SCANNER_ERR_OK ? success() : badValue();
}
Int32 now = cur, init = def,
curInd = distance<int>(all, cur),
defInd = distance<int>(all, def);
std::list<Int32> vals;
for (size_t i = sane_opts::RANGE_POS_ENUM_BEGIN; i < all.size(); ++i)
vals.push_back(all[i]);
return CapSupGetAllResetEx<Int32, Int32, (CapType)CapTypeEx::CAP_TYPE_EX_SHARPEN>(msg, data, vals, now, init, curInd, defInd);
};
}
if (std::find(ids.begin(), ids.end(), SANE_OPT_ID_FILTER) != ids.end())
{
m_query[(CapType)CapTypeEx::CAP_TYPE_EX_ENHANCE_COLOR] = msgSupportGetAllSetReset;
m_caps[(CapType)CapTypeEx::CAP_TYPE_EX_ENHANCE_COLOR] = [this](Twpp::CapType type, Msg msg, Capability& data)->Result {
log_attr_access((int)type, (int)msg);
int cur = FILTER_NONE, def = FILTER_NONE;
std::vector<int> vals;
std::vector<std::string> svals;
GET_SANE_OPT(std::string, scanner_, FILTER, NULL, &svals);
s2t::filter_from_sane(svals, vals, false);
svals.clear();
cur = vals[sane_opts::RANGE_POS_CURRENT];
def = vals[sane_opts::RANGE_POS_DEFAULT];
if (Msg::Set == msg || Msg::Reset == msg) {
if (Msg::Set == msg)
def = data.currentItem<UInt32>();
int ret = SCANNER_ERR_OK;
std::string val(s2t::enhance_to_sane((int)def));
SET_SANE_OPT(ret, scanner_, FILTER, &val[0]);
return ret == SCANNER_ERR_OK ? success() : badValue();
}
std::list<Filter> vs;
Filter now = (Filter)(cur), init = (Filter)(def);
UInt32 curInd = distance<int>(vals, cur), defInd = distance<int>(vals, def);
for (size_t i = sane_opts::RANGE_POS_ENUM_BEGIN; i < vals.size(); ++i)
vs.push_back((Filter)vals[i]);
UInt32 val = (UInt32)now;
return CapSupGetAllResetEx<UInt32, Filter, (CapType)CapTypeEx::CAP_TYPE_EX_ENHANCE_COLOR>(msg, data, vs, val, init, curInd, defInd);
};
}
}
init_caps_from_sane_directly(ids);
if (std::find(ids.begin(), ids.end(), SANE_OPT_ID_FIRMWARE_VERSION) != ids.end())
{
m_query[(CapType)CapTypeEx::CAP_TYPE_EX_HARDWARE_VERSION] = m_query[(CapType)SANE_OPT_ID_FIRMWARE_VERSION];
m_caps[(CapType)CapTypeEx::CAP_TYPE_EX_HARDWARE_VERSION] = [this](Twpp::CapType type, Msg msg, Capability& data) -> Result
{
return m_caps[(CapType)SANE_OPT_ID_FIRMWARE_VERSION]((Twpp::CapType)SANE_OPT_ID_FIRMWARE_VERSION, msg, data);
};
}
if (std::find(ids.begin(), ids.end(), SANE_OPT_ID_DEVICE_IP_ADDR) != ids.end())
{
m_query[(CapType)CapTypeEx::CAP_TYPE_EX_IP] = m_query[(CapType)SANE_OPT_ID_DEVICE_IP_ADDR];
m_caps[(CapType)CapTypeEx::CAP_TYPE_EX_IP] = [this](Twpp::CapType type, Msg msg, Capability& data) -> Result
{
return m_caps[(CapType)SANE_OPT_ID_FIRMWARE_VERSION]((Twpp::CapType)SANE_OPT_ID_DEVICE_IP_ADDR, msg, data);
};
}
#define SET_EXISTING_EXTENSION(name, cap) \
if(m_query.count((CapType)SANE_OPT_ID_##name)) \
{ \
m_query[(CapType)cap] = m_query[(CapType)SANE_OPT_ID_##name]; \
m_caps[(CapType)cap] = [this](Twpp::CapType type, Msg msg, Capability& data) -> Result { \
return m_caps[CapType((int)SANE_OPT_ID_##name)]((Twpp::CapType)SANE_OPT_ID_##name, msg, data); \
}; \
org_func_[(int)cap] = (int)SANE_OPT_ID_##name; \
}
SET_EXISTING_EXTENSION(EXCHANGE, CapTypeEx::CAP_TYPE_EX_FLIP);
SET_EXISTING_EXTENSION(IS_ROTATE_BKG_180, CapTypeEx::CAP_TYPE_EX_ROTATE_BKG_180);
SET_EXISTING_EXTENSION(IS_FILL_COLOR, CapTypeEx::CAP_TYPE_EX_FILL_BLACK_BKG);
SET_EXISTING_EXTENSION(MARGIN, CapTypeEx::CAP_TYPE_EX_EDGE_IDENT);
SET_EXISTING_EXTENSION(ANTI_NOISE_LEVEL, CapTypeEx::CAP_TYPE_EX_ANTI_NOISE);
SET_EXISTING_EXTENSION(THRESHOLD, CapTypeEx::CAP_TYPE_EX_THRESHOLD);
SET_EXISTING_EXTENSION(RID_HOLE, CapTypeEx::CAP_TYPE_EX_FILL_HOLE);
SET_EXISTING_EXTENSION(NOISE_OPTIMIZE, CapTypeEx::CAP_TYPE_EX_DETACH_NOISE);
SET_EXISTING_EXTENSION(NOISE_SIZE, CapTypeEx::CAP_TYPE_EX_DETACH_NOISE_THRESHOLD);
SET_EXISTING_EXTENSION(RID_MULTIOUT_RED, CapTypeEx::CAP_TYPE_EX_RID_RED);
SET_EXISTING_EXTENSION(RID_ANSWER_SHEET_RED, CapTypeEx::CAP_TYPE_EX_RID_RED_HSV);
SET_EXISTING_EXTENSION(IS_CHECK_ASKEW, CapTypeEx::CAP_TYPE_EX_SCREW_DETECT);
SET_EXISTING_EXTENSION(ASKEW_RANGE, CapTypeEx::CAP_TYPE_EX_SCREW_DETECT_LEVEL);
SET_EXISTING_EXTENSION(IS_CHECK_STAPLE, CapTypeEx::CAP_TYPE_EX_STAPLE_DETECT);
SET_EXISTING_EXTENSION(IS_CHECK_DOG_EAR, CapTypeEx::CAP_TYPE_EX_DOGEAR_DETECT);
SET_EXISTING_EXTENSION(DARK_SAMPLE, CapTypeEx::CAP_TYPE_EX_DARK_SAMPLE);
SET_EXISTING_EXTENSION(SPLIT, CapTypeEx::CAP_TYPE_EX_IMAGE_SPLIT);
SET_EXISTING_EXTENSION(ERASE_BACKGROUND, CapTypeEx::CAP_TYPE_EX_FADE_BKG);
SET_EXISTING_EXTENSION(BKG_COLOR_RANGE, CapTypeEx::CAP_TYPE_EX_FADE_BKG_VALUE);
SET_EXISTING_EXTENSION(SIZE_CHECK, CapTypeEx::CAP_TYPE_EX_SIZE_DETECT);
SET_EXISTING_EXTENSION(IS_MULTI_OUT, CapTypeEx::CAP_TYPE_EX_MULTI_OUT);
//m_query[(CapType)SANE_OPT_ID_DEVICE_SERIAL_NO] = m_query[CapType::SerialNumber];
//m_caps[(CapType)SANE_OPT_ID_DEVICE_SERIAL_NO] = m_caps[CapType::SerialNumber];
//org_func_[SANE_OPT_ID_DEVICE_SERIAL_NO] = (int)CapType::SerialNumber;
//m_query[(CapType)SANE_OPT_ID_FIRMWARE_VERSION] = m_query[(CapType)CapTypeEx::CAP_TYPE_EX_HARDWARE_VERSION];
//m_caps[(CapType)SANE_OPT_ID_FIRMWARE_VERSION] = m_caps[(CapType)CapTypeEx::CAP_TYPE_EX_HARDWARE_VERSION];
//org_func_[SANE_OPT_ID_FIRMWARE_VERSION] = (int)CapTypeEx::CAP_TYPE_EX_HARDWARE_VERSION;
//m_query[(CapType)SANE_OPT_ID_DEVICE_IP_ADDR] = m_query[(CapType)CapTypeEx::CAP_TYPE_EX_IP];
//m_caps[(CapType)SANE_OPT_ID_DEVICE_IP_ADDR] = m_caps[(CapType)CapTypeEx::CAP_TYPE_EX_IP];
//org_func_[SANE_OPT_ID_DEVICE_IP_ADDR] = (int)CapTypeEx::CAP_TYPE_EX_IP;
}
void huagao_ds::init_caps_from_sane_directly(const std::vector<uint32_t>& ids)
{
for (auto& v : ids)
{
value_type type = VAL_TYPE_NONE;
value_limit limit = VAL_LIMIT_NONE;
int bytes = 0;
bool rdo = false;
if (!scanner_->get_option_info(v, &type, &limit, &bytes, &rdo))
continue;
//rdo = (limit & VAL_LIMIT_READONLY) == VAL_LIMIT_READONLY;
limit = (value_limit)(limit & VAL_LIMIT_MASK);
if (type == VAL_TYPE_BOOL)
{
if (rdo)
{
m_query[(CapType)v] = msgSupportGetAll;
m_caps[(CapType)v] = [this](Twpp::CapType type, Msg msg, Capability& data) -> Result {
std::vector<bool> all;
sane_opts::get_opts<bool> op(NULL, &all);
scanner_->get_value((int)type, sane_opts::set_opt_value<bool>, &op);
op.re_order();
type = data.type();
data = Capability::createOneValue<Bool>(type, Bool(all[sane_opts::RANGE_POS_CURRENT]));
return success();
};
}
else
{
m_query[(CapType)v] = msgSupportGetAllSetReset;
m_caps[(CapType)v] = [this](Twpp::CapType type, Msg msg, Capability& data) -> Result {
std::vector<bool> all;
sane_opts::get_opts<bool> op(NULL, &all);
scanner_->get_value((int)type, sane_opts::set_opt_value<bool>, &op);
op.re_order();
if (msg == Msg::Set || msg == Msg::Reset)
{
bool init = all[sane_opts::RANGE_POS_DEFAULT];
if (msg == Msg::Set)
init = (bool)data.currentItem<Bool>();
int ret = scanner_->set_value((int)type, &init);
if (ret)
return { ReturnCode::Failure, huagao_ds::condition_code_from_hg_error(ret) };
else
return success();
}
else if(msg == Msg::GetCurrent || msg == Msg::GetDefault)
{
Bool t;
if (msg == Msg::GetCurrent)
t = Bool(all[sane_opts::RANGE_POS_CURRENT]);
else
t = (Bool)all[sane_opts::RANGE_POS_DEFAULT];
type = data.type();
data = Capability::createOneValue<Bool>(type, t);
return success();
}
else
{
UInt32 ni = all[sane_opts::RANGE_POS_CURRENT] ? 1 : 0, ii = all[sane_opts::RANGE_POS_DEFAULT] ? 1 : 0;
type = data.type();
data = Capability::createEnumeration<Bool>(type, { FALSE, TRUE }, ni, ii);
return success();
}
};
}
}
else if (type == VAL_TYPE_INT)
{
// int -> Int32
if (rdo)
{
m_query[(CapType)v] = msgSupportGetAll;
m_caps[(CapType)v] = [this](Twpp::CapType type, Msg msg, Capability& data) -> Result {
std::vector<int> all;
Int32 val;
sane_opts::get_opts<int> op(NULL, &all);
scanner_->get_value((int)type, sane_opts::set_opt_value<int>, &op);
op.re_order();
copy_type(val, all[sane_opts::RANGE_POS_CURRENT]);
type = data.type();
data = Capability::createOneValue<Int32>(type, val);
return success();
};
}
else
{
m_query[(CapType)v] = msgSupportGetAllSetReset;
m_caps[(CapType)v] = [this](Twpp::CapType type, Msg msg, Capability& data) -> Result {
std::vector<int> all;
value_limit lmt;
sane_opts::get_opts<int> op(&lmt, &all);
scanner_->get_value((int)type, sane_opts::set_opt_value<int>, &op);
op.re_order();
if (msg == Msg::Set || msg == Msg::Reset)
{
int val = (int)data.currentItem<Int32>();
if (msg == Msg::Reset)
val = all[sane_opts::RANGE_POS_DEFAULT];
int ret = scanner_->set_value((int)type, &val);
type = data.type();
data = Capability::createOneValue<Int32>(type, (Int32)val);
if (ret)
return { ReturnCode::Failure, huagao_ds::condition_code_from_hg_error(ret) };
else
return success();
}
else if (msg == Msg::GetCurrent || msg == Msg::GetDefault)
{
Int32 val;
if (msg == Msg::GetCurrent)
copy_type(val, all[sane_opts::RANGE_POS_CURRENT]);
else
copy_type(val, all[sane_opts::RANGE_POS_DEFAULT]);
type = data.type();
data = Capability::createOneValue<Int32>(type, val);
return success();
}
else
{
if (lmt == VAL_LIMIT_ENUM)
{
std::list<Int32> vals;
UInt32 ni = distance<int>(all, all[sane_opts::RANGE_POS_CURRENT]),
ii = distance<int>(all, all[sane_opts::RANGE_POS_DEFAULT]);
for (size_t i = sane_opts::RANGE_POS_ENUM_BEGIN; i < all.size(); ++i)
{
Int32 t;
copy_type(t, all[i]);
vals.push_back(t);
}
type = data.type();
data = Capability::createEnumeration<Int32>(type, vals, ni, ii);
}
else if (lmt == VAL_LIMIT_RANGE)
{
Int32 now, init, lower, upper, step;
copy_type(now, all[sane_opts::RANGE_POS_CURRENT]);
copy_type(init, all[sane_opts::RANGE_POS_DEFAULT]);
copy_type(lower, all[sane_opts::RANGE_POS_LOWER]);
copy_type(upper, all[sane_opts::RANGE_POS_UPPER]);
copy_type(step, all[sane_opts::RANGE_POS_STEP]);
type = data.type();
data = Capability::createRange<Int32>(type, lower, upper, step, now, init);
}
else
{
Int32 val;
copy_type(val, all[sane_opts::RANGE_POS_CURRENT]);
type = data.type();
data = Capability::createOneValue<Int32>(type, val);
}
return success();
}
};
}
}
else if (type == VAL_TYPE_FLOAT)
{
// float -> Fix32
if (rdo)
{
m_query[(CapType)v] = msgSupportGetAll;
m_caps[(CapType)v] = [this](Twpp::CapType type, Msg msg, Capability& data) -> Result {
std::vector<float> all;
Fix32 val;
sane_opts::get_opts<float> op(NULL, &all);
scanner_->get_value((int)type, sane_opts::set_opt_value<float>, &op);
op.re_order();
copy_type(val, all[sane_opts::RANGE_POS_CURRENT]);
type = data.type();
data = Capability::createOneValue<Fix32>(type, val);
return success();
};
}
else
{
m_query[(CapType)v] = msgSupportGetAllSetReset;
m_caps[(CapType)v] = [this](Twpp::CapType type, Msg msg, Capability& data) -> Result {
std::vector<float> all;
value_limit lmt;
sane_opts::get_opts<float> op(&lmt, &all);
scanner_->get_value((int)type, sane_opts::set_opt_value<float>, &op);
op.re_order();
if (msg == Msg::Set || msg == Msg::Reset)
{
float val = .0f;
copy_type(val, data.currentItem<Fix32>());
if (msg == Msg::Reset)
val = all[sane_opts::RANGE_POS_DEFAULT];
double dbv = val;
int ret = scanner_->set_value((int)type, &dbv);
Fix32 fv;
val = dbv;
copy_type(fv, val);
type = data.type();
data = Capability::createOneValue<Fix32>(type, fv);
if (ret)
return { ReturnCode::Failure, huagao_ds::condition_code_from_hg_error(ret) };
else
return success();
}
else if (msg == Msg::GetCurrent || msg == Msg::GetDefault)
{
Fix32 val;
if (msg == Msg::GetCurrent)
copy_type(val, all[sane_opts::RANGE_POS_CURRENT]);
else
copy_type(val, all[sane_opts::RANGE_POS_DEFAULT]);
type = data.type();
data = Capability::createOneValue<Fix32>(type, val);
return success();
}
else
{
type = data.type();
if (lmt == VAL_LIMIT_ENUM)
{
std::list<Fix32> vals;
UInt32 ni = distance<float>(all, all[sane_opts::RANGE_POS_CURRENT]),
ii = distance<float>(all, all[sane_opts::RANGE_POS_DEFAULT]);
for (size_t i = sane_opts::RANGE_POS_ENUM_BEGIN; i < all.size(); ++i)
{
Fix32 t;
copy_type(t, all[i]);
vals.push_back(t);
}
data = Capability::createEnumeration<Fix32>(type, vals, ni, ii);
}
else if (lmt == VAL_LIMIT_RANGE)
{
Fix32 now, init, lower, upper, step;
copy_type(now, all[sane_opts::RANGE_POS_CURRENT]);
copy_type(init, all[sane_opts::RANGE_POS_DEFAULT]);
copy_type(lower, all[sane_opts::RANGE_POS_LOWER]);
copy_type(upper, all[sane_opts::RANGE_POS_UPPER]);
copy_type(step, all[sane_opts::RANGE_POS_STEP]);
data = Capability::createRange<Fix32>(type, lower, upper, step, now, init);
}
else
{
Fix32 val;
copy_type(val, all[sane_opts::RANGE_POS_CURRENT]);
data = Capability::createOneValue<Fix32>(type, val);
}
return success();
}
};
}
}
else if (type == VAL_TYPE_STR)
{
// std::string -> Str255
if (v == SANE_OPT_ID_LOGIN || v == SANE_OPT_ID_LOGOUT)
{
m_query[(CapType)v] = MsgSupport::Set;
m_caps[(CapType)v] = [this](Twpp::CapType type, Msg msg, Capability& data) -> Result {
if (msg != Msg::Set)
{
if (msg == Msg::Get)
{
data = Capability::createArray<Str32>(type, 2);
return success();
}
return { ReturnCode::Failure, ConditionCode::CapUnsupported };
}
Str32 n(data.array<Str32>().at(0)),
p(data.array<Str32>().at(1));
char buf[64] = { 0 };
int ret = 0;
strcpy(buf, n.data());
strcpy(buf + 32, p.data());
ret = scanner_->set_value((int)type, buf);
if (ret == SCANNER_ERR_OK)
return success();
return { RC::Failure, huagao_ds::condition_code_from_hg_error(ret) };
};
}
else if (v == SANE_OPT_ID_DRIVER_LOG || v == SANE_OPT_ID_DEVICE_LOG)
{
m_query[(CapType)v] = MsgSupport::GetCurrent | MsgSupport::Reset;
m_caps[(CapType)v] = [this](Twpp::CapType type, Msg msg, Capability& data) -> Result {
if (msg == Msg::GetCurrent || msg == Msg::Get)
{
Str255 str;
if (msg == Msg::Get) { data = Capability::createOneValue<Str255>(type, str); return success(); }
std::string path("");
int len = 0;
copy_type(path, data.currentItem<Str255>());
len = path.length();
if (scanner_->get_value((int)type, &path[0], &len)) return success(); else return { RC::Failure, CC::OperationError };
}
else if (msg == Msg::Reset)
{
int tmp = 0, ret = scanner_->set_value((int)type, &tmp);
if (ret == SCANNER_ERR_OK)
return success();
return { RC::Failure, huagao_ds::condition_code_from_hg_error(ret) };
}
return { ReturnCode::Failure, ConditionCode::CapUnsupported };
};
}
else if (rdo)
{
m_query[(CapType)v] = msgSupportGetAll;
m_caps[(CapType)v] = [this](Twpp::CapType type, Msg msg, Capability& data) -> Result {
std::vector<std::string> all;
Str255 val;
sane_opts::get_opts<std::string> op(NULL, &all);
scanner_->get_value((int)type, sane_opts::set_opt_value<std::string>, &op);
op.re_order();
copy_type(val, all[sane_opts::RANGE_POS_CURRENT]);
type = data.type();
data = Capability::createOneValue<Str255>(type, val);
return success();
};
}
else
{
m_query[(CapType)v] = msgSupportGetAllSetReset;
m_caps[(CapType)v] = [this](Twpp::CapType type, Msg msg, Capability& data) -> Result {
std::vector<std::string> all;
value_limit lmt;
sane_opts::get_opts<std::string> op(&lmt, &all);
scanner_->get_value((int)type, sane_opts::set_opt_value<std::string>, &op);
op.re_order();
if (msg == Msg::Set || msg == Msg::Reset)
{
std::string val("");
if (msg == Msg::Set)
copy_type(val, data.currentItem<Str255>());
else
val = all[sane_opts::RANGE_POS_DEFAULT];
val.resize(255);
int ret = scanner_->set_value((int)type, &val[0]);
Str255 rv;
copy_type(rv, val);
type = data.type();
data = Capability::createOneValue<Str255>(type, rv);
if (ret)
return { ReturnCode::Failure, huagao_ds::condition_code_from_hg_error(ret) };
else
return success();
}
else if (msg == Msg::GetCurrent || msg == Msg::GetDefault)
{
Str255 val;
if (msg == Msg::GetCurrent)
copy_type(val, all[sane_opts::RANGE_POS_CURRENT]);
else
copy_type(val, all[sane_opts::RANGE_POS_DEFAULT]);
type = data.type();
data = Capability::createOneValue<Str255>(type, val);
return success();
}
else
{
type = data.type();
if (lmt == VAL_LIMIT_ENUM)
{
std::list<Str255> vals;
UInt32 ni = distance<std::string>(all, all[sane_opts::RANGE_POS_CURRENT]),
ii = distance<std::string>(all, all[sane_opts::RANGE_POS_DEFAULT]);
for(size_t i = sane_opts::RANGE_POS_ENUM_BEGIN; i < all.size(); ++i)
{
Str255 t;
copy_type(t, all[i]);
vals.push_back(t);
}
data = Capability::createEnumeration<Str255>(type, vals, ni, ii);
}
else
{
Str255 val;
copy_type(val, all[sane_opts::RANGE_POS_CURRENT]);
data = Capability::createOneValue<Str255>(type, val);
}
return success();
}
};
}
}
else if (type == VAL_TYPE_STREAM)
{
if (rdo)
{
m_query[(CapType)v] = msgSupportGetAll;
m_caps[(CapType)v] = [this](Twpp::CapType type, Msg msg, Capability& data) -> Result {
std::vector<std::string> all;
sane_opts::get_opts<std::string> op(NULL, &all);
scanner_->get_value((int)type, sane_opts::set_opt_value<std::string>, &op);
op.re_order();
type = data.type();
if (all[sane_opts::RANGE_POS_CURRENT].length())
{
size_t ind = 0;
data = Capability::createArray<Int8>(type, all[sane_opts::RANGE_POS_CURRENT].length());
for (auto& c : all[sane_opts::RANGE_POS_CURRENT])
data.array<Int8>()[ind++] = (Int8)c;
return success();
}
else
return bummer();
};
}
else
{
m_query[(CapType)v] = msgSupportGetAllSetReset;
m_caps[(CapType)v] = [this](Twpp::CapType type, Msg msg, Capability& data) -> Result {
std::vector<std::string> all;
sane_opts::get_opts<std::string> op(NULL, &all);
scanner_->get_value((int)type, sane_opts::set_opt_value<std::string>, &op);
op.re_order();
if (msg == Msg::Set || msg == Msg::Reset)
{
if (data.array<Int8>().size() < all[sane_opts::RANGE_POS_CURRENT].length())
return badValue();
Int8* ptr = &data.array<Int8>().at(0);
int ret = scanner_->set_value((int)type, ptr);
if (ret)
return { ReturnCode::Failure, huagao_ds::condition_code_from_hg_error(ret) };
else
return success();
}
else
{
if (all[sane_opts::RANGE_POS_CURRENT].length())
{
size_t ind = 0;
type = data.type();
data = Capability::createArray<Int8>(type, all[sane_opts::RANGE_POS_CURRENT].length());
for (auto& c : all[sane_opts::RANGE_POS_CURRENT])
data.array<Int8>()[ind++] = c;
return success();
}
else
return bummer();
}
};
}
}
else if (type == VAL_TYPE_BUTTON)
{
m_query[(CapType)v] = MsgSupport::Set;
m_caps[(CapType)v] = [this](Twpp::CapType type, Msg msg, Capability& data) -> Result {
if (msg == Msg::Set)
{
int val = 0,
ret = scanner_->set_value((int)type, &val);
if (ret)
return { ReturnCode::Failure, huagao_ds::condition_code_from_hg_error(ret) };
else
return success();
}
return { ReturnCode::Failure, ConditionCode::BadProtocol };
};
}
}
}
std::string huagao_ds::get_config_file(void)
{
#if defined(WIN32) || defined(_WIN64)
char* tmp = getenv("LOCALAPPDATA");
if (tmp)
{
std::string str("");
std::string path(tmp);
path += std::string("\\") + PRODUCT_VENDOR + "Scan\\config\\debug.cfg";
return std::move(path);
}
else
{
char path[MAX_PATH] = { 0 }, * name = NULL;
GetModuleFileNameA(me_, path, _countof(path) - 1);
name = strrchr(path, '\\');
if (name++ == NULL)
name = path;
strcpy(name, "debug.cfg");
return path;
}
#else
return std::move(utils::get_local_data_path() + "/config/debug.cfg");
#endif
}
std::string huagao_ds::get_config_value(const char* sec, const char* key)
{
char v[256] = { 0 };
std::string cfg_f(get_config_file());
if(!cfg_f.empty())
GetPrivateProfileStringA(sec, key, "", v, _countof(v) - 1, get_config_file().c_str());
return v;
}
DWORD huagao_ds::get_config_number(const char* sec, const char* key, DWORD def, DWORD empty)
{
std::string cfg_f(get_config_file());
if (cfg_f.empty())
return empty;
else
return GetPrivateProfileIntA(sec, key, def, get_config_file().c_str());
}
int huagao_ds::handle_scanner_event(int ev, bool from_event_proc)
{
static int count_0 = 0;
ReturnCode rc = ReturnCode::Success;
int ret = 0;
if (ev == 0)
count_0++;
else
{
char msg[128] = { 0 };
if (count_0)
sprintf(msg, "[%x]handle_scanner_event(0x0 +%d)\r\nds::eventProcess(0x%x)\r\n", GetCurrentThreadId(), count_0, ev);
else
sprintf(msg, "[%x]handle_scanner_event(0x%x)\r\n", GetCurrentThreadId(), ev);
utils::log_info(msg, 0);
count_0 = 0;
}
switch (ev)
{
case SANE_EVENT_WORKING:
scanner_status_ = SCANNER_STATUS_SCANNING;
rc = notifyXferReady();
//if (!Twpp::success(rc))
//{
// char msg[128] = { 0 };
// sprintf(msg, _countof(msg) - 1, "[%x]Warning: change state to XferReady failed with error %d while in state(%d), STOP scanning ...\r\n", GetCurrentThreadId(), rc, state());
// utils::log_info(msg, 0);
// // we stop scanning here ...
// scanner_->stop();
//}
break;
case SANE_EVENT_UI_CLOSE_SETTING:
scanner_->ui_hide();
if (notify_close_ == NOTIFY_ALWAYS)
{
utils::to_log(1, "close setting ui and notify close immediately.\r\n");
rc = notifyCloseCancel();
}
else if (notify_close_ == NOTIFY_NONE)
{
utils::to_log(1, "close setting ui and notify close passively (wait eventProcess to do).\r\n");
notfify_close_ = true;
}
else // if (notify_close_ == NOTIFY_AUTO)
{
utils::to_log(1, "close setting ui and notify close in auto mode.\r\n");
//if (has_event_called_)
// notfify_close_ = true;
//else
// rc = notifyCloseCancel();
notfify_close_ = true;
if (notify_close_thread_.get() && notify_close_thread_->joinable())
notify_close_thread_->join();
notify_close_thread_.reset(new std::thread(&huagao_ds::notify_close_thread, this));
}
//if(main_thread_id_ == GetCurrentThreadId())
// rc = notifyCloseCancel();
//else
//{
// HANDLE thrd = OpenThread(THREAD_ALL_ACCESS, FALSE, main_thread_id_);
// int err = -1;
// if (thrd)
// {
// if (QueueUserAPC(&huagao_ds::notify_close, thrd, (ULONG_PTR)this))
// err = 0;
// else
// err = GetLastError();
// CloseHandle(thrd);
// }
// utils::to_log(3, "Notify close setting UI in different thread(%x), invoke 'notifyCloseCancel' in enable-thread %x(%x), result %d\r\n", GetCurrentThreadId(), main_thread_id_, thrd, err);
//}
//if (!Twpp::success(rc))
//{
// rc = notifyXferReady(); // 好分数需要再通知 FAINT :( - modified on 2022-10-20
// if (!Twpp::success(rc))
// {
// char msg[128] = { 0 }, unk[20] = { 0 };
// sprintf(msg, _countof(msg) - 1, "[%x]yscan: notifyXferReady failed after setting UI closed with error %d\r\n", GetCurrentThreadId(), rc);
// utils::log_info(msg, 0);
// }
// rc = notifyCloseCancel();
//}
show_setting_ = false;
break;
case SANE_EVENT_UI_CLOSE_CANCEL:
//scanner_->stop();
//notifyCloseCancel(); // 修复点击进度<E8BF9B>?取消"按钮UI不能正常结束的BUG - added on 2023-02-14
//break;
case SANE_EVENT_UI_CLOSE_NORMAL:
case SANE_EVENT_SCAN_FINISHED:
//scanner_->ui_hide();
scanner_status_ = SCANNER_STATUS_STOPPED; // notifyCloseCancel is not need, because it done in EndXfer
//if(show_setting_)
// notifyCloseCancel();
// notifyCloseOk();
break;
case SANE_EVENT_UI_SCAN_COMMAND:
scanner_->ui_show_progress(NULL, m_bIndicator);
scanner_status_ = SCANNER_STATUS_SCAN_1;
app_trigger_event_ = false;
if ((ret = scanner_->start()))
{
char msg[128] = { 0 }, unk[20] = { 0 };
sprintf(msg, "[%x - %s]Fatal: start scanning from setting UI failed with error %d\r\n", GetCurrentThreadId(), desc_state(state(), unk), ret);
utils::log_info(msg, 1);
scanner_status_ = SCANNER_STATUS_STOPPED;
if(bUiOnly_)
rc = notifyCloseCancel();
if (Twpp::success(rc))
{
scanner_status_ = SCANNER_STATUS_READY;
}
else
{
sprintf(msg, "[%x - %s]Warning: notifyCloseCancel failed with error %d after start scanning failed\r\n", GetCurrentThreadId(), desc_state(state(), unk), rc);
utils::log_info(msg, 1);
}
}
else
{
notifyXferReady(); // scan from UI, should notify this state mannually
}
break;
}
return 0;
}
int huagao_ds::get_scanned_image_count(DWORD timeout)
{
int cnt = scanner_->get_scanned_images(timeout);
//if (cnt == -1)
//{
// // This is a special value indicates the scanning is over
// cnt = 0;
// scanner_->ui_hide();
// notifyCloseCancel();
//}
return cnt;
}
void huagao_ds::trigger_ProcessEvent(Twpp::DataGroup dg, Twpp::Dat dat, Twpp::Msg msg)
{
ReturnCode rc;
if (state() == DsState::Enabled && scanner_status_ >= SCANNER_STATUS_SCAN_1) // in scanning events ...
{
// here ensure APP enter into XferImage process ...
if (!app_trigger_event_ && scanner_status_ == SCANNER_STATUS_STOPPED)
{
// scanning stopped, reset APP state to ready ...
scanner_status_ = SCANNER_STATUS_READY;
rc = notifyCloseCancel();
if (!Twpp::success(rc))
{
char info[128] = { 0 }, unk[20] = { 0 };
sprintf(info, "[%x]Warning: notifyCloseCancel failed with error %s!\r\n", GetCurrentThreadId(), desc_return_code(rc, unk));
utils::log_info(info, 1);
}
}
else
{
// start scanning, expect frist TRIPLE is ProcessEvent ...
if (dg == DataGroup::Control && dat == Dat::Event && msg == Msg::ProcessEvent && scanner_status_ == SCANNER_STATUS_SCAN_1)
{
app_trigger_event_ = true; // nothing else to do
utils::log_info("Good! first event is (Control, Event, ProcessEvent) after start scanning ^_^.\r\n", 1);
}
else if(!app_trigger_event_)
{
int ev = 0;
if (scanner_status_ == SCANNER_STATUS_SCAN_1)
utils::log_info("Sorry, first event is not (Control, Event, ProcessEvent) after start scanning, we takeover it!\r\n", 1);
if (scanner_.get())
{
ev = scanner_->get_event();
if (ev)
handle_scanner_event(ev, true);
}
}
}
}
else if (state() >= DsState::XferReady && scanner_status_ == SCANNER_STATUS_STOPPED)
{
// here ensure APP return to ready state ...
if (scanner_.get() && scanner_->get_scanned_images(0) == 0)
{
rc = notifyCloseCancel();
if (!Twpp::success(rc))
{
char info[128] = { 0 }, unk[20] = { 0 };
sprintf(info, "[%x]Warning: notifyCloseCancel failed with error %s when scanner is stopped!\r\n", GetCurrentThreadId(), desc_return_code(rc, unk));
utils::log_info(info, 1);
}
}
}
}
bool huagao_ds::take_and_reset_notify_close_flag(void)
{
std::lock_guard<std::mutex> lock(notify_close_lock_);
bool notify = notfify_close_;
if (notfify_close_)
notfify_close_ = false;
return notify;
}
void huagao_ds::notify_close_thread(void)
{
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
if (take_and_reset_notify_close_flag())
{
notifyCloseCancel();
utils::to_log(1, "Trigger notifyCloseCancel in custom thread!\r\n");
}
else
{
utils::to_log(1, "Good! Trigger notifyCloseCancel in eventProcess ^_^\r\n");
}
}