#ifdef max #undef max #endif #ifdef min #undef min #endif #include #include "huagaods.hpp" //#include "twpp/twglue.hpp" #include #include #if defined(WIN32) || defined(_WIN64) // WIN32 #include #include #include #define mktemp _mktemp #endif // WIN32 #include 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 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 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 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(); return {}; default: return { ReturnCode::Failure, ConditionCode::CapBadOperation }; } } template 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() == def ? Result() : Result(ReturnCode::Failure, ConditionCode::BadValue); default: return { ReturnCode::Failure, ConditionCode::CapBadOperation }; } } template 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() == def ? Result() : Result(ReturnCode::Failure, ConditionCode::BadValue); default: return { ReturnCode::Failure, ConditionCode::CapBadOperation }; } } template Result CapSupGetAllReset(Msg msg, Capability& data, std::initializer_list values, T1& currvalue, T2 defaultvalue, UInt32 currindex, UInt32 defaultindex) { switch (msg) { case Msg::Get: data = Capability::createEnumeration(values, currindex, defaultindex); return { ReturnCode::Success, ConditionCode::Success }; case Msg::GetCurrent: data = Capability::createOneValue((T2)currvalue); return { ReturnCode::Success, ConditionCode::Success }; case Msg::Reset: case Msg::GetDefault: currvalue = (T1)defaultvalue; data = Capability::createOneValue(defaultvalue); return { ReturnCode::Success, ConditionCode::Success }; default: return { ReturnCode::Failure, ConditionCode::CapBadOperation }; } } template Result CapSupGetAllReset(Msg msg, Capability& data, std::list values, T1& currvalue, T2 defaultvalue, UInt32 currindex, UInt32 defaultindex) { switch (msg) { case Msg::Get: data = Capability::createEnumeration(values, currindex, defaultindex); return { ReturnCode::Success, ConditionCode::Success }; case Msg::GetCurrent: data = Capability::createOneValue((T2)currvalue); return { ReturnCode::Success, ConditionCode::Success }; case Msg::Reset: case Msg::GetDefault: currvalue = (T1)defaultvalue; data = Capability::createOneValue(defaultvalue); return { ReturnCode::Success, ConditionCode::Success }; default: return { ReturnCode::Failure, ConditionCode::CapBadOperation }; } } template Result CapSupGetAllResetEx(Msg msg, Capability& data, std::initializer_list 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 Result CapSupGetAllResetEx(Msg msg, Capability& data, std::list 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 Result CapSupGetAllReset(Msg msg, Capability& data, T1& currvalue, T2 defaultvalue) { switch (msg) { case Msg::Get: case Msg::GetCurrent: data = Capability::createOneValue((T2)currvalue); return { ReturnCode::Success, ConditionCode::Success }; case Msg::Reset: case Msg::GetDefault: currvalue = (T1)defaultvalue; data = Capability::createOneValue(defaultvalue); return { ReturnCode::Success, ConditionCode::Success }; default: return { ReturnCode::Failure, ConditionCode::CapBadOperation }; } } template Result CapSupGetAllResetEx(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 Result cap_get_enum_values(Msg msg, Capability& data, std::list values, T& cur, T& def, UInt32 cur_ind, UInt32 def_ind) { switch (msg) { case Msg::Get: data = Capability::createEnumeration(cap, values.size(), cur_ind, def_ind); //data = Capability::createArray(cap, values.size()); { auto arr = data.enumeration(); int i = 0; for (const auto& v : values) arr[i++] = v; } return { ReturnCode::Success, ConditionCode::Success }; case Msg::GetCurrent: data = Capability::createOneValue(cap, cur); return { ReturnCode::Success, ConditionCode::Success }; case Msg::Reset: case Msg::GetDefault: data = Capability::createOneValue(cap, def); return { ReturnCode::Success, ConditionCode::Success }; default: return { ReturnCode::Failure, ConditionCode::CapBadOperation }; } } template 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(cap, *lower, *upper, *step, cur, def); return { ReturnCode::Success, ConditionCode::Success }; } case Msg::GetCurrent: data = Capability::createOneValue(cap, cur); return { ReturnCode::Success, ConditionCode::Success }; case Msg::Reset: case Msg::GetDefault: data = Capability::createOneValue(cap, def); return { ReturnCode::Success, ConditionCode::Success }; default: return { ReturnCode::Failure, ConditionCode::CapBadOperation }; } } template Result CapSupGetAll(Msg msg, Capability& data, T1& currvalue, T2 defaultvalue) { switch (msg) { case Msg::Get: case Msg::GetCurrent: case Msg::GetDefault: data = Capability::createOneValue((T2)defaultvalue); return { ReturnCode::Success, ConditionCode::Success }; default: return { ReturnCode::Failure, ConditionCode::CapBadOperation }; } } template Result CapSupGetAllEx(Msg msg, Capability& data, T1& currvalue, T2 defaultvalue) { switch (msg) { case Msg::Get: case Msg::GetCurrent: case Msg::GetDefault: data = Capability::createOneValue(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 bool list_value_at(std::list& lst, int ind, T& t) { bool found = false; if (ind >= 0) { typename std::list::iterator it = lst.begin(); for (; it != lst.end(); ++it, --ind) { if (ind == 0) { t = *it; found = true; break; } } } return found; } template int distance(std::vector& 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 SANE_Bool got_fixed_id(int id, void* param) { ((std::vector*)param)->push_back(id); return SANE_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())); class my_cleanup { public: my_cleanup() { 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); } ~my_cleanup() { 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 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 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; std::string utf8(""); GetModuleFileNameA(NULL, pe, _countof(pe) - 1); name = strrchr(pe, '\\'); if (name++ == NULL) name = pe; #ifdef WIN32 utf8 = utils::ansi2utf8(name); #else utf8 = name; #endif if (STRICMP(utf8.c_str(), "\345\245\275\345\210\206\346\225\260\351\230\205\345\215\267\346\211\253\346\217\217\347\253\257.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; std::string utf8(""); GetModuleFileNameA(NULL, pe, _countof(pe) - 1); name = strrchr(pe, '\\'); if (name++ == NULL) name = pe; #ifdef WIN32 utf8 = utils::ansi2utf8(name); #else utf8 = name; #endif if (STRICMP(name, "\345\206\233\351\230\237\346\211\253\346\217\2172.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(); // ��ʾ���ý���ʱ������������EndXfer���������һ��ͼƬ�������ʾ�����������ڴ����������� int cnt = get_scanned_image_count(-1); // FIX-2023-05-29: �Ƿ񱣳����ý�����ʾ����APP�������˴��������Ƿ���ʾUI������������һ��ɨ�������������� //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()[0].data() = 0; } else if (id == InfoId::BarCodeType) { info.allocSimple(Type::UInt32); *info.items()[0].data() = BarCodeType::ThreeOfNine; } else if(id == InfoId::BarCodeTextLength) { info.allocSimple(Type::UInt32); *info.items()[0].data() = 0; } else if (id == InfoId::BarCodeText) { //info.allocSimple(Type::Str255, 1); //info.items()[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(); // 好分数需要返回成�? //} 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::to_log(LOG_LEVEL_DEBUG, " imageInfoGet = false, error = 0x%x\r\n", scanner_->last_error()); data.setHeight(0); //return { ReturnCode::XferDone, ConditionCode::Bummer }; return { ReturnCode::Failure, condition_code_from_hg_error(scanner_->last_error()) }; } 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(head.pixels_per_line) / res, static_cast(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().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; // } //} { utils::to_log(LOG_LEVEL_DEBUG, "FileTransfer '%s' = %d(%d)\r\n", m_fileXfer.filePath().string().c_str(), (CC)ret.status(), cv_e); } } 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 :( ... // 云阅卷扫描端不等状态改变,直接取图,此处设置一次状�?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(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(); 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(); 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 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(m_caps.size()); auto arr = data.array(); std::vector 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, _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 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(); 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 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(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, _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(); 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({ FALSE,TRUE }, m_bIndicator ? 1 : 0, 1); } else data = Capability::createOneValue(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((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(); 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(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 all; std::vector 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(); 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 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(msg, data, alll, cur, init, ni, ii); }; } m_query[CapType::IUnits] = msgSupportGetAllSetReset; m_caps[CapType::IUnits] = std::bind(enmGetSetConst, _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 all; std::vector 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(); 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(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 vals; UInt32 ni = distance(all, all[sane_opts::RANGE_POS_CURRENT]), ii = distance(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(msg, data, vals, Now, Init, ni, ii); }; m_query[CapType::IBitOrder] = msgSupportGetAllSetReset; m_caps[CapType::IBitOrder] = std::bind(oneValGetSetConst, _2, _3, BitOrder::MsbFirst); m_query[CapType::IPlanarChunky] = msgSupportGetAllSetReset; m_caps[CapType::IPlanarChunky] = std::bind(enmGetSetConst, _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 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(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 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(msg, data, vals, Now, Init, ni, ii); } case Msg::GetCurrent: data = Capability::createOneValue(Fix32((float)now)); return success(); case Msg::GetDefault: data = Capability::createOneValue(Fix32((float)init)); return success(); case Msg::Reset: data = Capability::createOneValue(Fix32((float)init)); case Msg::Set: { auto mech = data.currentItem(); 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, _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 all; std::vector 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(); else data = Capability::createOneValue((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 vals; Twpp::PaperSize Now = (PaperSize)now, Init = (PaperSize)init; UInt32 ni = distance(all, now), ii = distance(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(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(¶m, 0, sizeof(param)); if (scanner_.get() && scanner_->get_first_image_header(¶m, NULL, &cur)) init = param.pixels_per_line * 1.0f / cur; data = Capability::createOneValue(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(¶m, 0, sizeof(param)); if (scanner_.get() && scanner_->get_first_image_header(¶m, NULL, &cur)) init = param.lines * 1.0f / cur; data = Capability::createOneValue(data.type(), Fix32(init)); return success(); } default: return { ReturnCode::Failure, ConditionCode::CapBadOperation }; } }; m_query[CapType::IPixelFlavor] = msgSupportGetAllSetReset; m_caps[CapType::IPixelFlavor] = std::bind(enmGetSetConst, _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(); 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(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 values; std::vector 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(); { 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((Twpp::PixelType)init); return ret == SCANNER_ERR_OK ? success() : badValue(); } switch (msg) { case Msg::Get: if (1) { std::list vals; UInt32 ni = distance(values, now), ii = distance(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(msg, data, vals, Now, Init, ni, ii); } case Msg::GetCurrent: data = Capability::createOneValue((Twpp::PixelType)now); return success(); case Msg::GetDefault: data = Capability::createOneValue((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(); 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 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(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(); { 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(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(); 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(msg, data, q, 80); unsigned short q = m_jpegQuality; return CapSupGetAllResetEx(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(); 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(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(); double 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 values; std::vector 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 vls; UInt32 i = distance(values, values[sane_opts::RANGE_POS_DEFAULT]), n = distance(values, values[sane_opts::RANGE_POS_CURRENT]); for (size_t ind = sane_opts::RANGE_POS_ENUM_BEGIN; ind < values.size(); ++ind) vls.push_back(Fix32(values[ind])); return cap_get_enum_values(msg, data, vls, Now, Init, n, i); }; m_query[CapType::EnableDsUiOnly] = msgSupportGetAll; m_caps[CapType::EnableDsUiOnly] = std::bind(enmGet, _2, _3, Bool(true)); m_query[CapType::PaperDetectable] = msgSupportGetAll; m_caps[CapType::PaperDetectable] = std::bind(oneValGet, _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(); m_bFeederEnabled = mech; return success(); } return CapSupGetAllReset(msg, data, m_bFeederEnabled, Bool(true)); }; m_query[CapType::Duplex] = msgSupportGetAll; m_caps[CapType::Duplex] = std::bind(oneValGet, _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(); 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 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(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(); m_bAutoFeed = mech; return success(); } return CapSupGetAllReset(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(); 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((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 vals; UInt32 i = 0, // distance(all, init), n = 0; // distance(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(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(); 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 all; GET_SANE_OPT(bool, scanner_, ANTI_SKEW, NULL, &all); ato = all[sane_opts::RANGE_POS_CURRENT]; return CapSupGetAllReset(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(); 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 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(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(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(); 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 all; GET_SANE_OPT(bool, scanner_, WAIT_TO_SCAN, NULL, &all); m_autoscan = (bool)all[sane_opts::RANGE_POS_CURRENT]; return CapSupGetAllReset(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()) : ""); if (Msg::Set == msg) { auto autosize = data.currentItem(); 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 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(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(); std::string val(s2t::auto_size_to_sane(autodetectborder)); int ret = SCANNER_ERR_OK; SET_SANE_OPT(ret, scanner_, PAPER, (bool*)&autodetectborder); return ret == SCANNER_ERR_OK ? success() : badValue(); } Bool init = false, erase = false; std::vector sall; GET_SANE_OPT(std::string, scanner_, PAPER, NULL, &sall); init = s2t::auto_size_from_sane(sall[sane_opts::RANGE_POS_DEFAULT].c_str()); erase = s2t::auto_size_from_sane(sall[sane_opts::RANGE_POS_CURRENT].c_str()); return CapSupGetAllReset(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(); 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 all; std::vector 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(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(); 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 all; { GET_SANE_OPT(bool, scanner_, DISCARDBLANK, NULL, &all); } DiscardBlankPages autodiscradblank = all[sane_opts::RANGE_POS_CURRENT] ? DiscardBlankPages::Auto : DiscardBlankPages::Disabled; return CapSupGetAllReset(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(); 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 vals; std::vector svals; GET_SANE_OPT(std::string, scanner_, FILTER, NULL, &svals); s2t::filter_from_sane(svals, vals, true); svals.clear(); std::list vs; Filter now = (Filter)(vals[sane_opts::RANGE_POS_CURRENT]), init = (Filter)(vals[sane_opts::RANGE_POS_DEFAULT]); UInt32 curInd = distance(vals, vals[sane_opts::RANGE_POS_CURRENT]), defInd = distance(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(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 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(Fix32(-1000.0f), Fix32(1000.0f), Fix32(sf), Fix32(nf), Fix32(initf)); return success(); case Msg::GetCurrent: data = Capability::createOneValue(Fix32(nf)); return success(); case Msg::GetDefault: data = Capability::createOneValue(Fix32(initf)); return success(); case Msg::Set: { auto mech = data.currentItem(); 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(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 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(Fix32(-1000.0f), Fix32(1000.0f), Fix32(sf), Fix32(nf), Fix32(initf)); return success(); case Msg::GetCurrent: data = Capability::createOneValue(Fix32(nf)); return success(); case Msg::GetDefault: data = Capability::createOneValue(Fix32(initf)); return success(); case Msg::Set: { auto mech = data.currentItem(); 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(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 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(Fix32(l), Fix32(u), Fix32(step), Fix32(now), Fix32(init)); return success(); case Msg::GetCurrent: data = Capability::createOneValue(Fix32(now)); return success(); case Msg::GetDefault: data = Capability::createOneValue(Fix32(init)); return success(); case Msg::Set: { auto mech = data.currentItem(); 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(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(); 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 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(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(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 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(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(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(); 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 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(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 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((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((CapType)CapTypeEx::CAP_TYPE_EX_FILL_HOLE_RATIO, UInt32(now)); return success(); case Msg::GetDefault: data = Capability::createOneValue((CapType)CapTypeEx::CAP_TYPE_EX_FILL_HOLE_RATIO, UInt32(init)); return success(); case Msg::Set: { auto mech = data.currentItem(); 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(); 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 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(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 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(); 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(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 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() == 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(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 all; std::vector 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(); 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(all, cur), defInd = distance(all, def); std::list vals; for (size_t i = sane_opts::RANGE_POS_ENUM_BEGIN; i < all.size(); ++i) vals.push_back(all[i]); return CapSupGetAllResetEx(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(); int ret = SCANNER_ERR_OK; SET_SANE_OPT(ret, scanner_, RID_HOLE_L, &tobe); return ret == SCANNER_ERR_OK ? success() : seqError(); } std::vector all; GET_SANE_OPT(bool, scanner_, RID_HOLE_L, NULL, &all); BYTE tobe = all[sane_opts::RANGE_POS_CURRENT];; return CapSupGetAllResetEx(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 all; std::vector 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(); 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(all, cur), defInd = distance(all, def); std::list vals; for (size_t i = sane_opts::RANGE_POS_ENUM_BEGIN; i < all.size(); ++i) { vals.push_back(all[i]); } return CapSupGetAllResetEx(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(); 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 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(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 all; std::vector 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(); 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(all, cur), defInd = distance(all, def); std::list vals; for (size_t i = sane_opts::RANGE_POS_ENUM_BEGIN; i < all.size(); ++i) vals.push_back(all[i]); return CapSupGetAllResetEx(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 vals; std::vector 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(); 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 vs; Filter now = (Filter)(cur), init = (Filter)(def); UInt32 curInd = distance(vals, cur), defInd = distance(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(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& 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 all; sane_opts::get_opts op(NULL, &all); scanner_->get_value((int)type, sane_opts::set_opt_value, &op); op.re_order(); type = data.type(); data = Capability::createOneValue(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 all; sane_opts::get_opts op(NULL, &all); scanner_->get_value((int)type, sane_opts::set_opt_value, &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(); 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(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(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 all; Int32 val; sane_opts::get_opts op(NULL, &all); scanner_->get_value((int)type, sane_opts::set_opt_value, &op); op.re_order(); copy_type(val, all[sane_opts::RANGE_POS_CURRENT]); type = data.type(); data = Capability::createOneValue(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 all; value_limit lmt; sane_opts::get_opts op(&lmt, &all); scanner_->get_value((int)type, sane_opts::set_opt_value, &op); op.re_order(); if (msg == Msg::Set || msg == Msg::Reset) { int val = (int)data.currentItem(); 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(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(type, val); return success(); } else { if (lmt == VAL_LIMIT_ENUM) { std::list vals; UInt32 ni = distance(all, all[sane_opts::RANGE_POS_CURRENT]), ii = distance(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(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(type, lower, upper, step, now, init); } else { Int32 val; copy_type(val, all[sane_opts::RANGE_POS_CURRENT]); type = data.type(); data = Capability::createOneValue(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 all; Fix32 val; sane_opts::get_opts op(NULL, &all); scanner_->get_value((int)type, sane_opts::set_opt_value, &op); op.re_order(); copy_type(val, all[sane_opts::RANGE_POS_CURRENT]); type = data.type(); data = Capability::createOneValue(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 all; value_limit lmt; sane_opts::get_opts op(&lmt, &all); scanner_->get_value((int)type, sane_opts::set_opt_value, &op); op.re_order(); if (msg == Msg::Set || msg == Msg::Reset) { float val = .0f; copy_type(val, data.currentItem()); 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(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(type, val); return success(); } else { type = data.type(); if (lmt == VAL_LIMIT_ENUM) { std::list vals; UInt32 ni = distance(all, all[sane_opts::RANGE_POS_CURRENT]), ii = distance(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(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(type, lower, upper, step, now, init); } else { Fix32 val; copy_type(val, all[sane_opts::RANGE_POS_CURRENT]); data = Capability::createOneValue(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(type, 2); return success(); } return { ReturnCode::Failure, ConditionCode::CapUnsupported }; } Str32 n(data.array().at(0)), p(data.array().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(type, str); return success(); } std::string path(""); int len = 0; copy_type(path, data.currentItem()); 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 all; Str255 val; sane_opts::get_opts op(NULL, &all); scanner_->get_value((int)type, sane_opts::set_opt_value, &op); op.re_order(); copy_type(val, all[sane_opts::RANGE_POS_CURRENT]); type = data.type(); data = Capability::createOneValue(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 all; value_limit lmt; sane_opts::get_opts op(&lmt, &all); scanner_->get_value((int)type, sane_opts::set_opt_value, &op); op.re_order(); if (msg == Msg::Set || msg == Msg::Reset) { std::string val(""); if (msg == Msg::Set) copy_type(val, data.currentItem()); 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(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(type, val); return success(); } else { type = data.type(); if (lmt == VAL_LIMIT_ENUM) { std::list vals; UInt32 ni = distance(all, all[sane_opts::RANGE_POS_CURRENT]), ii = distance(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(type, vals, ni, ii); } else { Str255 val; copy_type(val, all[sane_opts::RANGE_POS_CURRENT]); data = Capability::createOneValue(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 all; sane_opts::get_opts op(NULL, &all); scanner_->get_value((int)type, sane_opts::set_opt_value, &op); op.re_order(); type = data.type(); if (all[sane_opts::RANGE_POS_CURRENT].length()) { size_t ind = 0; data = Capability::createArray(type, all[sane_opts::RANGE_POS_CURRENT].length()); for (auto& c : all[sane_opts::RANGE_POS_CURRENT]) data.array()[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 all; sane_opts::get_opts op(NULL, &all); scanner_->get_value((int)type, sane_opts::set_opt_value, &op); op.re_order(); if (msg == Msg::Set || msg == Msg::Reset) { if (data.array().size() < all[sane_opts::RANGE_POS_CURRENT].length()) return badValue(); Int8* ptr = &data.array().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(type, all[sane_opts::RANGE_POS_CURRENT].length()); for (auto& c : all[sane_opts::RANGE_POS_CURRENT]) data.array()[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(); // 修复点击进度�?取消"按钮,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; case SANE_EVENT_TWAIN_XFER_READY: notifyXferReady(); // notify ready when the first image arrived 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 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"); } }