code_device/twain/ds/scanner.cpp

3204 lines
94 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

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

#include "scanner.h"
#include <sane/sane_option_definitions.h>
#include "sane_option_trans.h"
#include <chrono>
#include <mutex>
#include <string.h>
#include <functional>
#include <twain_user/twainui.h>
#include <huagao/brand.h>
#include "sane_helper.h"
#include "../../sdk/hginclude/utils.h"
#define START_SCAN_IN_THREAD
static IMPLEMENT_OPTION_STRING_COMPARE(compare_sane_opt);
#define SET_SANE_OPT_ID(id, id_name, name, val, extension) \
if(strcmp(SANE_STD_OPT_NAME_##name, val) == 0) \
{ \
id_name##_id_ = id; \
sane_ids_[(sane_option_id)SANE_OPT_ID_##name] = id; \
extension(id); \
}
#if !defined(WIN32) && !defined(_WIN64)
template<typename ... Args>
int sprintf_s(char* buf, size_t len, const char* fmt, Args ... args)
{
return sprintf(buf, fmt, args ...);
}
std::string g_scanner_path = ""; // hg_log::get_module_full_path((std::string(GET_BACKEND_NAME) + ".so").c_str());
#endif
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// callback
sane_helper* sane_helper_ = nullptr;
namespace callback
{
static std::mutex cb_lock_;
typedef struct _scanner_inst
{
SANE_Handle dev;
scanner* invoker;
bool operator==(const SANE_Handle& h)
{
return dev == h;
}
bool operator==(const scanner* obj)
{
return invoker == obj;
}
}SCNINST;
std::vector<SCNINST> g_scanner_instances;
int sane_event_callback( // 注册回调的对象,需要保证该回调是多线程安全的
SANE_Handle hdev // 产生事件的设备句柄
, int code // 回调事件代码
, void* data // 回调事件数据,根据事件代码有所不同,参照具体事件定义
, unsigned int* len // 数据长度字节或者event_data的缓冲区长度详细请看相应的事件代码
, void* param // 用户自定义数据与调用sane_init_ex传入时的保持一致
) // 返回值依不同的事件代码而定通常为“0”
{
std::lock_guard<std::mutex> lock(cb_lock_);
std::vector<SCNINST>::iterator it = std::find(g_scanner_instances.begin(), g_scanner_instances.end(), hdev);
if (it != g_scanner_instances.end())
return it->invoker->handle_device_event(code, data, len);
else if(code != SANE_EVENT_SCANNER_CLOSED)
{
char msg[218] = { 0 };
sprintf_s(msg, _countof(msg) - 1, "Lost device(0x%08X) when event(%u) occurs!\r\n", hdev, code);
utils::log_info(msg, (log_level)1);
return 0;
}
}
void reg_callback(SANE_Handle dev, scanner* invoker)
{
std::lock_guard<std::mutex> lock(cb_lock_);
std::vector<SCNINST>::iterator it = std::find(g_scanner_instances.begin(), g_scanner_instances.end(), dev);
if (it == g_scanner_instances.end())
{
SCNINST inst;
inst.dev = dev;
inst.invoker = invoker;
g_scanner_instances.push_back(inst);
}
else
it->invoker = invoker;
}
void unreg_callback(scanner* invoker)
{
std::lock_guard<std::mutex> lock(cb_lock_);
std::vector<SCNINST>::iterator it = std::find(g_scanner_instances.begin(), g_scanner_instances.end(), invoker);
if (it != g_scanner_instances.end())
g_scanner_instances.erase(it);
}
struct
{
const char* name;
const char* title;
}g_opts[] = { {SANE_STD_OPT_NAME_RESTORE , OPTION_TITLE_HFMRSZ}
, {SANE_STD_OPT_NAME_HELP , OPTION_TITLE_BZ}
, {SANE_STD_OPT_NAME_IS_MULTI_OUT , OPTION_TITLE_DLSC}
, {SANE_STD_OPT_NAME_MULTI_OUT_TYPE , OPTION_TITLE_DLSCLX}
, {SANE_STD_OPT_NAME_COLOR_MODE , OPTION_TITLE_YSMS}
, {SANE_STD_OPT_NAME_BINARY_THRESHOLD , OPTION_TITLE_HBTXYZ}
, {SANE_STD_OPT_NAME_REVERSE_01 , OPTION_TITLE_HBTXFSSC}
, {SANE_STD_OPT_NAME_FILTER , OPTION_TITLE_HDHHBTX_CSYZQ}
, {SANE_STD_OPT_NAME_RID_MULTIOUT_RED , OPTION_TITLE_24WCSTX_DLSCCH}
, {SANE_STD_OPT_NAME_RID_ANSWER_SHEET_RED , OPTION_TITLE_24WCSTX_DTKCH}
, {SANE_STD_OPT_NAME_ERASE_BACKGROUND , OPTION_TITLE_BJYC}
, {SANE_STD_OPT_NAME_BKG_COLOR_RANGE , OPTION_TITLE_BJSCFDFW}
, {SANE_STD_OPT_NAME_SHARPEN , OPTION_TITLE_RHYMH}
, {SANE_STD_OPT_NAME_RID_MORR , OPTION_TITLE_QCMW}
, {SANE_STD_OPT_NAME_RID_GRID , OPTION_TITLE_CWW}
, {SANE_STD_OPT_NAME_ERROR_EXTENSION , OPTION_TITLE_CWKS}
, {SANE_STD_OPT_NAME_NOISE_OPTIMIZE , OPTION_TITLE_HBTXZDYH}
, {SANE_STD_OPT_NAME_NOISE_SIZE , OPTION_TITLE_ZDYHCC}
, {SANE_STD_OPT_NAME_PAPER , OPTION_TITLE_ZZCC}
, {SANE_STD_OPT_NAME_CUSTOM_AREA , OPTION_TITLE_ZDYSMQY}
, {SANE_STD_OPT_NAME_CUSTOM_AREA_LEFT , OPTION_TITLE_SMQYZCmm}
, {SANE_STD_OPT_NAME_CUSTOM_AREA_RIGHT , OPTION_TITLE_SMQYYCmm}
, {SANE_STD_OPT_NAME_CUSTOM_AREA_TOP , OPTION_TITLE_SMQYSCmm}
, {SANE_STD_OPT_NAME_CUSTOM_AREA_BOTTOM , OPTION_TITLE_SMQYXCmm}
, {SANE_STD_OPT_NAME_SIZE_CHECK , OPTION_TITLE_CCJC}
, {SANE_STD_OPT_NAME_PAGE , OPTION_TITLE_SMYM}
, {SANE_STD_OPT_NAME_DISCARD_BLANK_SENS , OPTION_TITLE_TGKBYLMD}
, {SANE_STD_OPT_NAME_RESOLUTION , OPTION_TITLE_FBL}
, {SANE_STD_OPT_NAME_TIME_TO_SLEEP , OPTION_TITLE_XMSJ}
, {SANE_STD_OPT_NAME_IMAGE_QUALITY , OPTION_TITLE_HZ}
, {SANE_STD_OPT_NAME_EXCHANGE ,OPTION_TITLE_JHZFM}
, {SANE_STD_OPT_NAME_SPLIT ,OPTION_TITLE_TXCF }
, {SANE_STD_OPT_NAME_ANTI_SKEW , OPTION_TITLE_ZDJP}
, {SANE_STD_OPT_NAME_IS_CUSTOM_GAMMA , OPTION_TITLE_QYSDQX}
, {SANE_STD_OPT_NAME_GAMMA , OPTION_TITLE_JMZ}
, {SANE_STD_OPT_NAME_BRIGHTNESS , OPTION_TITLE_LDZ}
, {SANE_STD_OPT_NAME_CONTRAST , OPTION_TITLE_DBD}
, {SANE_STD_OPT_NAME_IS_PHOTO_MODE , OPTION_TITLE_ZPMS}
, {SANE_STD_OPT_NAME_ERASE_BLACK_FRAME , OPTION_TITLE_XCHK}
, {SANE_STD_OPT_NAME_DARK_SAMPLE , OPTION_TITLE_SSYZ}
, {SANE_STD_OPT_NAME_THRESHOLD , OPTION_TITLE_YZ}
, {SANE_STD_OPT_NAME_ANTI_NOISE_LEVEL , OPTION_TITLE_BJKZDJ}
, {SANE_STD_OPT_NAME_MARGIN , OPTION_TITLE_BYSJ}
, {SANE_STD_OPT_NAME_FILL_BKG_MODE , OPTION_TITLE_BJTCFS}
, {SANE_STD_OPT_NAME_IS_ANTI_PERMEATE , OPTION_TITLE_FZST}
, {SANE_STD_OPT_NAME_ANTI_PERMEATE_LEVEL , OPTION_TITLE_FZSTDJ}
, {SANE_STD_OPT_NAME_RID_HOLE_L , OPTION_TITLE_CKYCZC}
, {SANE_STD_OPT_NAME_SEARCH_HOLE_RANGE_L , OPTION_TITLE_ZCCKSSFWZFMBL}
, {SANE_STD_OPT_NAME_RID_HOLE_R , OPTION_TITLE_CKYCYC}
, {SANE_STD_OPT_NAME_SEARCH_HOLE_RANGE_R , OPTION_TITLE_YCCKSSFWZFMBL}
, {SANE_STD_OPT_NAME_RID_HOLE_T , OPTION_TITLE_CKYCSC}
, {SANE_STD_OPT_NAME_SEARCH_HOLE_RANGE_T , OPTION_TITLE_SCCKSSFWZFMBL}
, {SANE_STD_OPT_NAME_RID_HOLE_B , OPTION_TITLE_CKYCXC}
, {SANE_STD_OPT_NAME_SEARCH_HOLE_RANGE_B , OPTION_TITLE_XCCKSSFWZFMBL}
, {SANE_STD_OPT_NAME_IS_FILL_COLOR , OPTION_TITLE_SCTC}
, {SANE_STD_OPT_NAME_IS_ULTROSONIC_CHECK , OPTION_TITLE_CSBJC}
, {SANE_STD_OPT_NAME_DOUBLE_FEED_HANDLE , OPTION_TITLE_SZTPCL}
, {SANE_STD_OPT_NAME_IS_CHECK_STAPLE , OPTION_TITLE_ZDJC}
, {SANE_STD_OPT_NAME_SCAN_MODE , OPTION_TITLE_SMZS}
, {SANE_STD_OPT_NAME_SCAN_COUNT , OPTION_TITLE_SMSL}
, {SANE_STD_OPT_NAME_TEXT_DIRECTION , OPTION_TITLE_WGFX}
, {SANE_STD_OPT_NAME_IS_ROTATE_BKG_180 , OPTION_TITLE_BMXZ180}
, {SANE_STD_OPT_NAME_IS_CHECK_DOG_EAR , OPTION_TITLE_ZJJC}
, {SANE_STD_OPT_NAME_DOG_EAR_SIZE , OPTION_TITLE_ZJDX}
, {SANE_STD_OPT_NAME_IS_CHECK_ASKEW , OPTION_TITLE_WXJC}
, {SANE_STD_OPT_NAME_ASKEW_RANGE , OPTION_TITLE_WXRRD}
, {SANE_STD_OPT_NAME_FEED_STRENGTH , OPTION_TITLE_FZQD}
, {SANE_STD_OPT_NAME_IS_AUTO_FEED_STRENGTH , OPTION_TITLE_ZDFZQD}
, {SANE_STD_OPT_NAME_FEED_STRENGTH_VALUE , OPTION_TITLE_JZSBL}
, {SANE_STD_OPT_NAME_WAIT_TO_SCAN , OPTION_TITLE_DZSM}
, {SANE_STD_OPT_NAME_FOLD_TYPE , OPTION_TITLE_DZMS}
, {SANE_STD_OPT_NAME_COLOR_CORRECTION , OPTION_TITLE_SPJZ}
},
g_discard[] = { {SANE_STD_OPT_NAME_REVERSE_01 , "\351\273\221\347\231\275\345\233\276\345\203\217\345\217\215\350\211\262\350\276\223\345\207\272\357\274\210\346\255\243\345\270\270\351\242\234\350\211\262\344\270\272\357\274\2320-\351\273\221\350\211\262\357\274\2331-\347\231\275\350\211\262\357\274\211"} // 黑白图像反色输出正常颜色为0-黑色1-白色)
, {SANE_STD_OPT_NAME_FILTER , "\347\201\260\345\272\246\346\210\226\351\273\221\347\231\275\345\233\276\345\203\217 - \351\231\244\350\211\262"} // 灰度或黑白图像 - 除色
, {SANE_STD_OPT_NAME_IS_AUTO_FEED_STRENGTH , "\350\207\252\345\212\250\346\220\223\347\272\270\345\274\272\345\272\246"} // 自动搓纸强度
, {SANE_STD_OPT_NAME_FEED_STRENGTH_VALUE , "\346\220\223\347\272\270\351\230\210\345\200\274"} // " 搓纸阈值"
};
const char* option_title_2_name(const char* title)
{
while (*title == ' ')
title++;
for (size_t i = 0; i < _countof(g_discard); ++i)
{
if (strcmp(title, g_discard[i].title) == 0)
return g_discard[i].name;
}
for (size_t i = 0; i < _countof(g_opts); ++i)
{
if (strcmp(title, g_opts[i].title) == 0)
return g_opts[i].name;
}
return "";
}
const char* option_name_2_title(const char* name)
{
for (size_t i = 0; i < _countof(g_opts); ++i)
{
if (strcmp(name, g_opts[i].name) == 0)
return g_opts[i].title;
}
return "";
}
static HWND find_main_wnd(void)
{
return nullptr;
}
static std::string option_value_2_string(SANE_Value_Type type, void* value)
{
if(type == SANE_TYPE_BOOL)
return *(SANE_Bool*)value == SANE_TRUE ? "true" : "false";
if(type == SANE_TYPE_INT)
return std::to_string(*(SANE_Int*)value);
if(type == SANE_TYPE_FIXED)
return std::to_string(SANE_UNFIX(*(SANE_Int*)value));
if(type == SANE_TYPE_STRING)
return (char*)value;
return "Unknown";
}
// UI ...
//
// events code, see SANE_Event
//
// callback events: SANE_EVENT_UI_CLOSE_CANCEL/SANE_EVENT_UI_CLOSE_NORMAL/SANE_EVENT_UI_SCAN_COMMAND/SANE_EVENT_UI_CLOSE_SETTING
//
// notify events: SANE_EVENT_WORKING - void*: unused, be nullptr, flag - unused, be 0
// SANE_EVENT_SCAN_FINISHED - void*: (utf8*)message, flag - error code (0 is success)
// SANE_EVENT_USB_DATA_RECEIVED- void* unused, be nullptr, flag - unused, be 0
// SANE_EVENT_IMAGE_OK - void* unused, be nullptr, flag - unused, be 0
static HMODULE hui = nullptr;
int (*choose_scanner)(const std::vector<DEVQUEUI>& devs) = nullptr; // blocked. return selected DEVQUE::id or -1 if user cancelled
char* (*apply_current_config)(const char* dev_name, SANE_Handle device, LPSANEAPI api) = nullptr; // 应用设备的当前配<E5898D>?
int (*show_setting_ui)(SANE_Handle device, HWND parent, LPSANEAPI api, const char* devname, bool with_scan, std::function<void(ui_result)> callback) = nullptr;
int (*show_progress_ui)(HWND parent, std::function<void(ui_result)> callback, std::function<void(int/*event*/, void* /*msg*/, int/*flag*/)>* notify) = nullptr;
int (*show_messagebox_ui)(HWND parent, int event, void* msg, int flag) = nullptr;
int (*close_ui)(int) = nullptr;
void (*twain_ui_free)(void* buf) = nullptr;
static void init_ui(void)
{
std::string root(utils::get_module_full_path(SCANNER_DRIVER_PART_NAME));
size_t pos = root.rfind(PATH_SEPARATOR[0]);
if(pos != std::string::npos)
root.erase(pos);
root = utils::find_file(root.c_str(), "*twainui*", false);
utils::to_log(LOG_LEVEL_DEBUG, "TwainUI component: %s\n", root.c_str());
// #if defined(WIN32) || defined(_WIN64)
// root += OEM_SHORT_NAME_E;
// #else
// std::string vnd(OEM_SHORT_NAME_E);
//
// std::transform(vnd.begin(), vnd.end(), vnd.begin(), toupper);
// root += "lib" + vnd;
// #endif
// root += "TwainUI.";
// root += DLL_EXTESION;
hui = LoadLibraryExA(root.c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH);
if (!hui)
{
std::string info("Load '" + root);
info += "' failed: " + std::string(strerror(GetLastError())) + "\r\n";
utils::log_info(info.c_str(), LOG_LEVEL_DEBUG);
}
else
{
#define GET_API(api) \
proc = (FARPROC*)&api; \
*proc = GetProcAddress(hui, #api); \
if(*proc == nullptr) \
utils::to_log(LOG_LEVEL_FATAL, "TWAINUI - function '%s' not found!\n", #api);
FARPROC* proc = nullptr;
GET_API(choose_scanner);
GET_API(apply_current_config);
GET_API(show_setting_ui);
GET_API(show_progress_ui);
GET_API(show_messagebox_ui);
GET_API(close_ui);
GET_API(twain_ui_free);
}
}
static void unint_ui(void)
{
if (close_ui)
close_ui(UI_UNLOAD_MODULE);
choose_scanner = nullptr;
apply_current_config = nullptr;
show_setting_ui = nullptr;
show_progress_ui = nullptr;
show_messagebox_ui = nullptr;
close_ui = nullptr;
twain_ui_free = nullptr;
if (hui)
{
FreeLibrary(hui);
hui = nullptr;
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// class scanner
scanner::scanner(SCANNERID id) : handle_(nullptr), id_(id), ex_id_(EXTENSION_ID_BASE), prev_start_result_(SCANNER_ERR_NOT_START)
, dpi_(200), tmp_path_(""), img_ind_(0)
, scanner_name_(""), is_ui_wait_img_(false), is_scanning_(false)
, scanner_ev_handler_(nullptr), evh_param_(nullptr), app_wnd_(nullptr), user_cancel_(false)
, max_img_mem_(1 * 1024), twain_set_(false), ev_cnt_(0), is_bIndicator(false), is_show_setting_(false)
{
ui_notify = std::function<void(int, void*, int)>();
sane_api_.sane_cancel_api = sane_helper_->invoke_sane_cancel;
sane_api_.sane_close_api = sane_helper_->invoke_sane_close;
sane_api_.sane_control_option_api = sane_helper_->invoke_sane_control_option;
sane_api_.sane_get_devices_api = sane_helper_->invoke_sane_get_devices;
sane_api_.sane_get_option_descriptor_api = sane_helper_->invoke_sane_get_option_descriptor;
sane_api_.sane_get_parameters_api = sane_helper_->invoke_sane_get_parameters;
sane_api_.sane_get_select_fd_api = sane_helper_->invoke_sane_get_select_fd;
sane_api_.sane_io_control_api = nullptr;
sane_api_.sane_open_api = sane_helper_->invoke_sane_open;
sane_api_.sane_read_api = sane_helper_->invoke_sane_read;
sane_api_.sane_set_io_mode_api = sane_helper_->invoke_sane_set_io_mode;
sane_api_.sane_start_api = sane_helper_->invoke_sane_start;
sane_api_.sane_strstatus_api = sane_helper_->invoke_sane_strstatus;
tmp_path_ = utils::get_local_data_path() + PATH_SEPARATOR;
cfg_path_ = tmp_path_ + "config";
utils::create_folder(cfg_path_.c_str());
cfg_path_ += PATH_SEPARATOR;
tmp_path_ += "imgs";
utils::create_folder(tmp_path_.c_str());
tmp_path_ += PATH_SEPARATOR;
img_fmt_.img_format = SANE_IMAGE_TYPE_BMP;
img_fmt_.compress.compression = SANE_COMPRESSION_NONE;
int mem_limit = GetPrivateProfileIntA("mem", "max_img", 0, (cfg_path_ + "debug.cfg").c_str());
if (mem_limit > 0)
{
// this value is same as driver memory limit ...
max_img_mem_ = mem_limit;
}
err_ = open();
}
scanner::~scanner()
{
close();
if (thread_starting_.get() && thread_starting_->joinable())
thread_starting_->join();
thread_starting_.reset();
}
void scanner::get_scanner_name(SCANNERID id, std::vector<std::string>& names)
{
const SANE_Device **devs = nullptr;
int count = 0;
names.clear();
if (sane_helper_->invoke_sane_get_devices(&devs, SANE_TRUE) == SANE_STATUS_GOOD)
{
for(; devs[count]; ++count)
{
if (strcmp(devs[count]->type, PRODUCT_FAMILY) == 0)
names.push_back(devs[count]->name);
}
}
}
value_type scanner::from_sane_type(SANE_Value_Type type)
{
if (type == SANE_TYPE_BOOL)
return VAL_TYPE_BOOL;
else if (type == SANE_TYPE_INT)
return VAL_TYPE_INT;
else if (type == SANE_TYPE_FIXED)
return VAL_TYPE_FLOAT;
else if (type == SANE_TYPE_STRING)
return VAL_TYPE_STR;
else
return VAL_TYPE_NONE;
}
value_limit scanner::from_sane_constraint(SANE_Constraint_Type type)
{
if (type == SANE_CONSTRAINT_RANGE)
return VAL_LIMIT_RANGE;
else if (type == SANE_CONSTRAINT_STRING_LIST || type == SANE_CONSTRAINT_WORD_LIST)
return VAL_LIMIT_ENUM;
else
return VAL_LIMIT_NONE;
}
int scanner::to_int(SANE_Int v)
{
return v;
}
float scanner::to_float(SANE_Fixed v)
{
return (float)SANE_UNFIX(v);
}
void scanner::ui_callback(int uev, void* sender, void* param)
{
((scanner*)param)->on_ui_event(uev, sender);
}
bool scanner::is_option_float(int sn, void* param)
{
const SANE_Option_Descriptor* desc = sane_helper_->invoke_sane_get_option_descriptor((SANE_Handle)param, sn);
if (desc)
return desc->type == SANE_TYPE_FIXED;
else
return false;
}
// IRef
COM_API_IMPLEMENT(scanner, long, add_ref(void))
{
return refer::add_ref();
}
COM_API_IMPLEMENT(scanner, long, release(void))
{
return refer::release();
}
int scanner::transfer_id(int id)
{
if (id > SANE_OPT_ID_BASE)
{
if (sane_ids_.count((sane_option_id)id) == 0)
id = -1;
else
id = sane_ids_[(sane_option_id)id];
}
return id;
}
void scanner::apply_config(void)
{
if (callback::apply_current_config)
{
char* cfg = callback::apply_current_config(scanner_name_.c_str(), handle_, &sane_api_);
if (cfg && callback::twain_ui_free)
callback::twain_ui_free(cfg);
}
}
void scanner::on_ui_event(int uev, void* sender)
{
if (uev == SANE_EVENT_UI_CLOSE_SETTING)
{
// setting UI closed ...
is_scanning_ = is_show_setting_ = false;
}
else if (uev == SANE_EVENT_UI_CLOSE_NORMAL)
{
// scan complete
// FIX on 2023-05-30: restore scan finished status, whether close UI is up to APP
//is_scanning_ = is_show_setting_;
is_scanning_ = false;
ui_notify = std::function<void(int, void*, int)>();
}
else if (uev == SANE_EVENT_UI_CLOSE_CANCEL)
{
// scan cancelled
stop(); // nothing to do, the finishing work do in SANE_EVENT_SCAN_FINISHED
}
else if (uev == SANE_EVENT_SCAN_FINISHED)
{
// FIX on 2023-05-30: restore scan finished status, whether close UI is up to APP
//is_scanning_ = is_show_setting_;
is_scanning_ = false;
ui_notify = std::function<void(int, void*, int)>();
}
int( * h)(int, void*) = scanner_ev_handler_;
if (h)
{
h(uev, evh_param_);
}
return;
events_.save(uev, sizeof(uev));
}
std::string scanner::choose_scanner(const std::vector<std::string>& scanners)
{
if (scanners.empty())
return "";
std::vector<DEVQUEUI> devs;
std::string sel("");
int id = 1;
for (size_t i = 0; i < scanners.size(); ++i)
{
SANE_Handle h = nullptr;
int ret = sane_helper_->invoke_sane_open(scanners[i].c_str(), &h);
if (h)
{
char sn[256] = {0};
SANE_Int len = sizeof(sn) - 1;
sane_helper_->invoke_sane_control_option(h, SANE_OPT_ID_DEVICE_SERIAL_NO, SANE_ACTION_GET_VALUE, sn, &len);
if (sn[0])
{
DEVQUEUI dev;
dev.id = id++;
dev.name = scanners[i];
dev.sn = sn;
devs.push_back(dev);
}
sane_helper_->invoke_sane_close(h);
}
}
if (devs.size() == 0)
sel = scanners[0];
else if (devs.size() == 1)
sel = devs[0].name;
else if (callback::choose_scanner)
{
id = callback::choose_scanner(devs);
if (id != -1)
{
for (auto& v : devs)
{
if (v.id == id)
{
sel = v.name;
break;
}
}
}
}
else
{
//dlg_choose_dev dlg(nullptr, devs);
//dlg.show(true, true);
//sel = dlg.get_selected_device();
}
return sel;
}
int scanner::open(void)
{
int ret = close();
std::vector<std::string> que;
std::string name("");
scanner::get_scanner_name(id_, que);
scanner_name_ = "";
if (que.empty())
return SCANNER_ERR_DEVICE_NOT_FOUND;
if (que.size() == 1)
name = que[0];
else
{
name = choose_scanner(que);
if (name.empty())
return SCANNER_ERR_USER_CANCELED;
}
ret = sane_helper_->invoke_sane_open(name.c_str(), &handle_);
if (ret == SANE_STATUS_GOOD)
{
size_t pid = -1;
// transport_config_file();
callback::reg_callback(handle_, this);
scanner_name_ = name;
pid = scanner_name_.find(" - ");
if (pid == -1)
pid = scanner_name_.length();
ret = init_options_id();
apply_config();
img_fmt_.compress.compression = SANE_CompressionType::SANE_COMPRESSION_NONE;
img_fmt_.compress.detail = 0;
img_fmt_.img_format = SANE_IMAGE_TYPE_BMP;
img_fmt_.detail = 0;
// check roller life ...
SANE_Int cnt = 0, life = 0, after = 0;
if (sane_helper_->invoke_sane_control_option(handle_, SANE_OPT_ID_ROLLER_COUNT, SANE_ACTION_GET_VALUE, &cnt, &after) == SANE_STATUS_GOOD &&
sane_helper_->invoke_sane_control_option(handle_, SANE_OPT_ID_ROLLER_LIFE, SANE_ACTION_GET_VALUE, &life, &after) == SANE_STATUS_GOOD)
{
if (cnt >= life)
{
if (callback::show_messagebox_ui)
{
// 纸轮搓纸次数已超过设计使用范围,扫描过程中搓纸失败、歪斜、搓多张等异常频次可能会明显增多,请注意及时清洁、并联系设备供应商购买替换纸轮!
std::string roller_msg(local_trans::lang_trans_between_hz936("\347\272\270\350\275\256\346\220\223\347\272\270\346\254\241\346\225\260\345\267\262\350\266\205\350\277\207\350\256\276\350\256\241\344\275\277\347\224\250\350\214\203\345\233\264\357\274\214\346\211\253\346\217\217\350\277\207\347\250\213\344\270\255\346\220\223\347\272\270\345\244\261\350\264\245\343\200\201\346\255\252\346\226\234\343\200\201\346\220\223\345\244\232\345\274\240\347\255\211\345\274\202\345\270\270\351\242\221\346\254\241\345\217\257\350\203\275\344\274\232\346\230\216\346\230\276\345\242\236\345\244\232\357\274\214\350\257\267\346\263\250\346\204\217\345\217\212\346\227\266\346\270\205\346\264\201\343\200\201\345\271\266\350\201\224\347\263\273\350\256\276\345\244\207\344\276\233\345\272\224\345\225\206\350\264\255\344\271\260\346\233\277\346\215\242\347\272\270\350\275\256\357\274\201"));
app_wnd_ = callback::find_main_wnd();
callback::show_messagebox_ui(app_wnd_, ret, (void*)&roller_msg[0], 0);
}
}
}
}
else
{
if (callback::show_messagebox_ui)
{
app_wnd_ = callback::find_main_wnd();
callback::show_messagebox_ui(app_wnd_, ret, (void*)sane_helper_->invoke_sane_strstatus((SANE_Status)ret), 0);
}
}
return ret;
}
int scanner::close(void)
{
scanner_ev_handler_ = nullptr;
ui_hide();
callback::unreg_callback(this);
if (handle_)
{
sane_helper_->invoke_sane_close(handle_);
}
handle_ = nullptr;
ex_id_ = EXTENSION_ID_BASE;
return SCANNER_ERR_OK;
}
int scanner::init_options_id(void)
{
SANE_Int op_id = 1;
const SANE_Option_Descriptor* desc = nullptr;
int ret = SCANNER_ERR_OK;
#define SET_OPT_ID(var, predef, func) \
SET_SANE_OPT_ID(op_id, var, predef, desc->name, func)
#define INIT_FIXED_IDS(id) \
sane_ids_[SANE_OPT_ID_##id] = SANE_OPT_ID_##id;
INIT_FIXED_IDS(HISTORY_COUNT);
INIT_FIXED_IDS(DRIVER_VERSION);
INIT_FIXED_IDS(MANUFACTURER);
INIT_FIXED_IDS(COPYRIGHT);
INIT_FIXED_IDS(CO_URL);
INIT_FIXED_IDS(CO_TEL);
INIT_FIXED_IDS(CO_ADDR);
INIT_FIXED_IDS(CO_GPS);
INIT_FIXED_IDS(HELP);
INIT_FIXED_IDS(VID);
INIT_FIXED_IDS(PID);
INIT_FIXED_IDS(DEV_NAME);
INIT_FIXED_IDS(DEV_FAMILY);
INIT_FIXED_IDS(LOGIN);
INIT_FIXED_IDS(LOGOUT);
INIT_FIXED_IDS(ROLLER_COUNT);
INIT_FIXED_IDS(DRIVER_LOG);
INIT_FIXED_IDS(DEVICE_LOG);
INIT_FIXED_IDS(ROLLER_LIFE);
INIT_FIXED_IDS(DEVICE_MAC_ADDR);
INIT_FIXED_IDS(CUSTOM_GAMMA);
INIT_FIXED_IDS(MOTOR_VER);
while ((desc = sane_helper_->invoke_sane_get_option_descriptor(handle_, op_id)))
{
char* val = new char[desc->size + 4];
sane_helper_->invoke_sane_control_option(handle_, op_id, (SANE_Action)SANE_ACTION_GET_DEFAULT_VALUE, val, nullptr);
if (val)
{
size_t len = 0;
switch (desc->type)
{
case SANE_TYPE_BOOL:
len = sizeof(SANE_Bool);
break;
case SANE_TYPE_INT:
len = sizeof(SANE_Int);
break;
case SANE_TYPE_FIXED:
len = sizeof(SANE_Fixed);
break;
case SANE_TYPE_STRING:
len = lstrlenA((char*)val);
break;
default:
break;
}
delete[] val;
}
SET_OPT_ID(is_multiout, IS_MULTI_OUT, extension_none)
else SET_OPT_ID(multiout_type, MULTI_OUT_TYPE, extension_multiout_type)
else SET_OPT_ID(color_mode, COLOR_MODE, extension_color_mode)
else SET_OPT_ID(erase_color, FILTER, extension_erase_color)
else SET_OPT_ID(erase_multiout_red, RID_MULTIOUT_RED, extension_none)
else SET_OPT_ID(erase_paper_red, RID_ANSWER_SHEET_RED, extension_none)
else SET_OPT_ID(is_erase_background, ERASE_BACKGROUND, extension_none)
else SET_OPT_ID(background_color_range, BKG_COLOR_RANGE, extension_none)
else SET_OPT_ID(sharpen, SHARPEN, extension_sharpen)
else SET_OPT_ID(erase_morr, RID_MORR, extension_none)
else SET_OPT_ID(erase_grids, RID_GRID, extension_none)
else SET_OPT_ID(error_extend, ERROR_EXTENSION, extension_none)
else SET_OPT_ID(is_noise_modify, NOISE_OPTIMIZE, extension_none)
else SET_OPT_ID(noise_threshold, NOISE_SIZE, extension_none)
else SET_OPT_ID(paper, PAPER, extension_paper)
else SET_OPT_ID(is_custom_area, CUSTOM_AREA, extension_none)
else SET_OPT_ID(curstom_area_l, CUSTOM_AREA_LEFT, extension_none)
else SET_OPT_ID(curstom_area_r, CUSTOM_AREA_RIGHT, extension_none)
else SET_OPT_ID(curstom_area_t, CUSTOM_AREA_TOP, extension_none)
else SET_OPT_ID(curstom_area_b, CUSTOM_AREA_BOTTOM, extension_none)
else SET_OPT_ID(is_size_check, SIZE_CHECK, extension_none)
else SET_OPT_ID(page, PAGE, extension_page)
else SET_OPT_ID(blank_page_threshold, DISCARD_BLANK_SENS, extension_none)
else SET_OPT_ID(resolution, RESOLUTION, extension_none)
else SET_OPT_ID(image_quality, IMAGE_QUALITY, extension_none)
else SET_OPT_ID(is_swap, EXCHANGE, extension_none)
else SET_OPT_ID(is_split, SPLIT, extension_none)
else SET_OPT_ID(is_auto_deskew, ANTI_SKEW, extension_none)
else SET_OPT_ID(is_custom_gamma, IS_CUSTOM_GAMMA, extension_none)
else SET_OPT_ID(bright, BRIGHTNESS, extension_none)
else SET_OPT_ID(contrast, CONTRAST, extension_none)
else SET_OPT_ID(gamma, GAMMA, extension_none)
else SET_OPT_ID(is_erase_black_frame, ERASE_BLACK_FRAME, extension_none)
else SET_OPT_ID(deep_sample, DARK_SAMPLE, extension_none)
else SET_OPT_ID(threshold, THRESHOLD, extension_none)
else SET_OPT_ID(anti_noise, ANTI_NOISE_LEVEL, extension_none)
else SET_OPT_ID(margin, MARGIN, extension_none)
else SET_OPT_ID(fill_background, FILL_BKG_MODE, extension_fill_bkg_method)
else SET_OPT_ID(is_anti_permeate, IS_ANTI_PERMEATE, extension_none)
else SET_OPT_ID(anti_permeate_level, ANTI_PERMEATE_LEVEL, extension_none)
else SET_OPT_ID(is_erase_hole, RID_HOLE, extension_none)
else SET_OPT_ID(search_hole_range, SEARCH_HOLE_RANGE, extension_none)
else SET_OPT_ID(is_filling_color, IS_FILL_COLOR, extension_none)
else SET_OPT_ID(is_ultrasonic_check, IS_ULTROSONIC_CHECK, extension_none)
else SET_OPT_ID(is_check_staple, IS_CHECK_STAPLE, extension_none)
else SET_OPT_ID(scan_mode, SCAN_MODE, extension_none)
else SET_OPT_ID(scan_count, SCAN_COUNT, extension_none)
else SET_OPT_ID(text_direction, TEXT_DIRECTION, extension_text_direction)
else SET_OPT_ID(is_rotate_bkg180, IS_ROTATE_BKG_180, extension_none)
else SET_OPT_ID(is_check_dogear, IS_CHECK_DOG_EAR, extension_none)
else SET_OPT_ID(dogear_size, DOG_EAR_SIZE, extension_none)
else SET_OPT_ID(is_check_skew, IS_CHECK_ASKEW, extension_none)
else SET_OPT_ID(skew_range, ASKEW_RANGE, extension_none)
else SET_OPT_ID(black_white_threshold, BINARY_THRESHOLD, extension_none)
else SET_OPT_ID(is_photo_mode, IS_PHOTO_MODE, extension_none)
else SET_OPT_ID(double_feed_handle, DOUBLE_FEED_HANDLE, extension_none)
else SET_OPT_ID(scan_when_paper_on, WAIT_TO_SCAN, extension_none)
else SET_OPT_ID(feed_strength, FEED_STRENGTH, extension_none)
else SET_OPT_ID(power_scheme, TIME_TO_SLEEP, extension_none)
else SET_OPT_ID(is_auto_strength, IS_AUTO_FEED_STRENGTH, extension_none)
else SET_OPT_ID(feed_strength_value , FEED_STRENGTH_VALUE, extension_none)
else SET_OPT_ID(is_reverse_bw, REVERSE_01, extension_none)
else SET_OPT_ID(is_erase_hole_l, RID_HOLE_L, extension_none)
else SET_OPT_ID(search_hole_range_l, SEARCH_HOLE_RANGE_L, extension_none)
else SET_OPT_ID(is_erase_hole_r, RID_HOLE_R, extension_none)
else SET_OPT_ID(search_hole_range_r, SEARCH_HOLE_RANGE_R, extension_none)
else SET_OPT_ID(is_erase_hole_t, RID_HOLE_T, extension_none)
else SET_OPT_ID(search_hole_range_t, SEARCH_HOLE_RANGE_T, extension_none)
else SET_OPT_ID(is_erase_hole_b, RID_HOLE_B, extension_none)
else SET_OPT_ID(search_hole_range_b, SEARCH_HOLE_RANGE_B, extension_none)
else SET_OPT_ID(fold_direction, FOLD_TYPE, extension_none)
else SET_OPT_ID(color_correction, COLOR_CORRECTION, extension_none)
else SET_OPT_ID(language, LANGUAGE, extension_none)
op_id++;
}
#define EX_APPENDIX_API(name) \
{ \
EXAPI ea; \
ea.ind = ex_##name##_id_ = ex_id_++; \
ea.ex_api = &scanner::handle_ex_##name; \
ea.base_ind = -1; \
ex_opts_.push_back(ea); \
}
EX_APPENDIX_API(final_compression);
EX_APPENDIX_API(final_format);
EX_APPENDIX_API(serial);
EX_APPENDIX_API(to_be_scan);
EX_APPENDIX_API(scan_with_hole);
EX_APPENDIX_API(device_code);
EX_APPENDIX_API(power);
EX_APPENDIX_API(hardware_version);
EX_APPENDIX_API(ip);
if (black_white_threshold_id_ == -1)
black_white_threshold_id_ = 0x8836;
if (is_erase_hole_id_ == -1)
{
// 兼容老的除孔算法
EXAPI ea;
ea.ind = is_erase_hole_id_ = ex_id_++;
ea.base_ind = is_erase_hole_l_id_;
ea.ex_api = &scanner::handle_ex_erase_hole;
ex_opts_.push_back(ea);
ea.ind = search_hole_range_id_ = ex_id_++;
ea.base_ind = search_hole_range_l_id_;
ea.ex_api = &scanner::handle_ex_search_hole_range;
ex_opts_.push_back(ea);
}
return ret;
}
char* scanner::get_opt_value(int id, int size, bool def_val)
{
char *buf = new char[size + 4];
SANE_Action act = def_val ? (SANE_Action)SANE_ACTION_GET_DEFAULT_VALUE : SANE_ACTION_GET_VALUE;
memset(buf, 0, size + 4);
if(sane_helper_->invoke_sane_control_option(handle_, id, act, buf, nullptr))
{
delete[] buf;
buf = nullptr;
}
return buf;
}
bool scanner::get_opt_value(int id, void* buf, bool def_val)
{
SANE_Action act = def_val ? (SANE_Action)SANE_ACTION_GET_DEFAULT_VALUE : SANE_ACTION_GET_VALUE;
return sane_helper_->invoke_sane_control_option(handle_, id, act, buf, nullptr) == SANE_STATUS_GOOD;
}
int scanner::control_read_string(int io_code, std::string& val)
{
char buf[256] = {0};
int ret = SCANNER_ERR_OK, len = sizeof(buf) - 1;
if(io_code == IO_CTRL_CODE_GET_SERIAL)
io_code = SANE_OPT_ID_DEVICE_SERIAL_NO;
else
io_code = -1;
if(io_code == -1)
{
ret = SCANNER_ERR_DEVICE_NOT_SUPPORT;
}
else
{
ret = sane_helper_->invoke_sane_control_option(handle_, io_code, SANE_ACTION_GET_VALUE, buf, &len);
val = buf;
}
return ret;
}
void scanner::extension_none(int id)
{
}
void scanner::extension_multiout_type(int id)
{
EXAPI ea;
ex_multiout_type_id_ = ex_id_++;
ea.ind = ex_multiout_type_id_;
ea.base_ind = id;
ea.ex_api = &scanner::handle_ex_multiout;
ex_opts_.push_back(ea);
}
void scanner::extension_color_mode(int id)
{
EXAPI ea;
ea.ind = ex_color_mode_id_ = ex_id_++;
ea.base_ind = id;
ea.ex_api = &scanner::handle_ex_color_mode;
ex_opts_.push_back(ea);
ex_auto_color_type_id_ = ex_id_++;
ea.base_ind = id;
ea.ind = ex_auto_color_type_id_;
ea.ex_api = &scanner::handle_ex_auto_color_type;
ex_opts_.push_back(ea);
}
void scanner::extension_sharpen(int id)
{
EXAPI ea;
ex_sharpen_id_ = ex_id_++;
ea.base_ind = id;
ea.ind = ex_sharpen_id_;
ea.ex_api = &scanner::handle_ex_sharpen;
ex_opts_.push_back(ea);
}
void scanner::extension_paper(int id)
{
EXAPI ea;
ea.ind = ex_paper_id_ = ex_id_++;
ea.base_ind = id;
ea.ex_api = &scanner::handle_ex_paper;
ex_opts_.push_back(ea);
ea.ind = ex_paper_lateral_id_ = ex_id_++;
ea.base_ind = id;
ea.ex_api = &scanner::handle_ex_paper_lateral;
ex_opts_.push_back(ea);
ea.ind = ex_auto_paper_size_id_ = ex_id_++;
ea.base_ind = id;
ea.ex_api = &scanner::handle_ex_auto_paper_size;
ex_opts_.push_back(ea);
ea.ind = ex_is_paper_auto_crop_id_ = ex_id_++;
ea.base_ind = id;
ea.ex_api = &scanner::handle_ex_auto_paper_crop;
ex_opts_.push_back(ea);
}
void scanner::extension_fill_bkg_method(int id)
{
EXAPI ea;
ea.ind = ex_fill_background_id_ = ex_id_++;
ea.base_ind = id;
ea.ex_api = &scanner::handle_ex_fill_background;
ex_opts_.push_back(ea);
}
void scanner::extension_text_direction(int id)
{
EXAPI ea;
ea.ind = ex_text_direction_id_ = ex_id_++;
ea.base_ind = id;
ea.ex_api = &scanner::handle_ex_text_direction;
ex_opts_.push_back(ea);
}
void scanner::extension_page(int id)
{
EXAPI ea;
char msg[128] = { 0 };
ea.ind = ex_duplex_id_ = ex_id_++;
ea.base_ind = id;
ea.ex_api = &scanner::handle_ex_duplex;
ex_opts_.push_back(ea);
{
sprintf_s(msg, _countof(msg) - 1, "handle_ex_duplex of id: %d\r\n", ea.ind);
utils::log_info(msg, (log_level)0);
}
ea.ind = ex_discard_blank_page_id_ = ex_id_++;
ea.base_ind = id;
ea.ex_api = &scanner::handle_ex_discard_blank_page;
ex_opts_.push_back(ea);
{
sprintf_s(msg, _countof(msg) - 1, "handle_ex_discard_blank_page of id: %d\r\n", ea.ind);
utils::log_info(msg, (log_level)0);
}
ea.ind = ex_discard_blank_receipt_id_ = ex_id_++;
ea.base_ind = id;
ea.ex_api = &scanner::handle_ex_discard_blank_receipt;
ex_opts_.push_back(ea);
{
sprintf_s(msg, _countof(msg) - 1, "handle_ex_discard_blank_receipt of id: %d\r\n", ea.ind);
utils::log_info(msg, (log_level)0);
}
ea.ind = ex_is_page_fold_id_ = ex_id_++;
ea.base_ind = id;
ea.ex_api = &scanner::handle_ex_page_fold;
ex_opts_.push_back(ea);
{
sprintf_s(msg, _countof(msg) - 1, "handle_ex_page_fold of id: %d\r\n", ea.ind);
utils::log_info(msg, (log_level)0);
}
}
void scanner::extension_erase_color(int id)
{
EXAPI ea;
ea.ind = ex_color_filter_id_ = ex_id_++;
ea.base_ind = id;
ea.ex_api = &scanner::handle_ex_color_filter;
ex_opts_.push_back(ea);
ea.ind = ex_color_enhance_id_ = ex_id_++;
ea.base_ind = id;
ea.ex_api = &scanner::handle_ex_color_enhance;
ex_opts_.push_back(ea);
}
bool scanner::get_option_value_with_parent(int sn, set_opt_value setv, void* param) // return true if handled
{
bool handled = true;
if (sn == scan_count_id_)
{
const SANE_Option_Descriptor* parent = sane_helper_->invoke_sane_get_option_descriptor(handle_, scan_mode_id_);
char* buf = new char[parent->size + 4];
memset(buf, 0, parent->size);
sane_helper_->invoke_sane_control_option(handle_, scan_mode_id_, SANE_ACTION_GET_VALUE, buf, nullptr);
handled = compare_sane_opt(local_trans::lang_trans_between_hz936(OPTION_VALUE_SMZS_LXSM, true, nullptr), buf);
if (handled)
{
int count = -1;
value_role role = VAL_ROLE_CURRENT;
sane_helper_->invoke_sane_control_option(handle_, scan_mode_id_, (SANE_Action)SANE_ACTION_GET_DEFAULT_VALUE, buf, nullptr);
if (compare_sane_opt(local_trans::lang_trans_between_hz936(OPTION_VALUE_SMZS_LXSM, true, nullptr), buf))
role = value_role(role | VAL_ROLE_DEFAULT);
setv(&count, role, VAL_LIMIT_NONE, param);
}
delete[] buf;
}
else
handled = false;
return handled;
}
bool scanner::set_option_value_with_parent(int sn, void* data, int* err) // return true if handled sn
{
bool handled = false;
if (sn == scan_count_id_)
{
const SANE_Option_Descriptor* parent = sane_helper_->invoke_sane_get_option_descriptor(handle_, scan_mode_id_);
char* val = get_opt_value(scan_mode_id_, parent->size, false);
SANE_Int after = 0;
if(val)
{
if (compare_sane_opt(local_trans::lang_trans_between_hz936(OPTION_VALUE_SMZS_LXSM, true, nullptr), val))
{
if (*(int*)data != -1)
{
strcpy(val, local_trans::lang_trans_between_hz936(OPTION_VALUE_SMZS_SMZDZS, true, nullptr));
*err = sane_helper_->invoke_sane_control_option(handle_, scan_mode_id_, SANE_ACTION_SET_VALUE, val, &after);
}
}
else if (*(int*)data == -1)
{
strcpy(val, local_trans::lang_trans_between_hz936(OPTION_VALUE_SMZS_LXSM, true, nullptr));
*err = sane_helper_->invoke_sane_control_option(handle_, scan_mode_id_, SANE_ACTION_SET_VALUE, val, &after);
}
delete[] val;
}
}
return handled;
}
int scanner::set_option_value(int sn, SANE_Value_Type type, int size, void* data)
{
char* buf = nullptr;
SANE_Bool sb = SANE_FALSE;
SANE_Int si = 0, after = 0;
SANE_Fixed sf = 0;
int ret = SCANNER_ERR_OK;
void* val = data;
if (type == SANE_TYPE_BOOL)
{
sb = *(bool*)data ? SANE_TRUE : SANE_FALSE;
val = &sb;
}
else if (type == SANE_TYPE_INT)
{
si = *(int*)data;
val = &si;
}
else if (type == SANE_TYPE_FIXED)
{
sf = SANE_FIX(*(float*)data);
val = &sf;
}
else
{
buf = new char[size + 4];
memset(buf, 0, size + 4);
strcpy(buf, ((std::string*)data)->c_str());
val = buf;
}
ret = sane_helper_->invoke_sane_control_option(handle_, sn, SANE_ACTION_SET_VALUE, val, &after);
if (type == SANE_TYPE_BOOL)
{
*(bool*)data = sb == SANE_TRUE;
}
else if (type == SANE_TYPE_INT)
{
*(int*)data = si;
}
else if (type == SANE_TYPE_FIXED)
{
*(float*)data = (float)SANE_UNFIX(sf);
}
else if(buf)
{
strcpy((char*)val, buf);
delete[] buf;
}
return ret;
}
int scanner::set_is_multiout(bool enable)
{
int ret = SCANNER_ERR_OK;
if (is_multiout_id_ > 0)
{
SANE_Bool multi = SANE_FALSE;
SANE_Int after = 0;
char *val = get_opt_value(is_multiout_id_, 4, false);
if(val)
{
multi = *(SANE_Bool*)val;
delete[] val;
}
if (enable ^ (multi == SANE_TRUE))
{
multi = enable ? SANE_TRUE : SANE_FALSE;
ret = sane_helper_->invoke_sane_control_option(handle_, is_multiout_id_, SANE_ACTION_SET_VALUE, &multi, &after);
after = 0;
}
}
return ret;
}
int scanner::thread_start(void)
{
int ret = sane_helper_->invoke_sane_start(handle_);
// the third-APPs in linux will call 'stop' after every start, but it not happens in windows-apps, so we handle this as following ...
if (ret == SANE_STATUS_NO_DOCS && prev_start_result_ == SANE_STATUS_GOOD)
ret = sane_helper_->invoke_sane_start(handle_);
if (ret == SANE_STATUS_GOOD)
{
/*if (indicator_.get() && !IsWindowVisible(indicator_->hwnd()))
indicator_->show(true);*/
// unsigned int l = sizeof(img_fmt_);
// SANE_CompressionType cmprsn = img_fmt_.compress.compression;
// if (hg_sane_middleware::instance()->io_control(handle_, IO_CTRL_CODE_GET_FINAL_IMAGE_FORMAT, &img_fmt_, &l))
// img_fmt_.img_format = SANE_IMAGE_TYPE_BMP;
// img_fmt_.compress.compression = cmprsn;
}
//else if (indicator_.get())
//{
// indicator_->notify_scan_over(sane_helper_->invoke_sane_strstatus((SANE_Status)ret), true);
//}
else
{
// display error message on progress UI, may be closed immediately by APP, so we hide progress UI and call message_box ...
//
#ifdef START_SCAN_IN_THREAD
if (callback::show_progress_ui && is_bIndicator && ui_notify)
{
int ev = SANE_EVENT_WORKING;
ui_notify(SANE_EVENT_SCAN_FINISHED, (void *)sane_helper_->invoke_sane_strstatus((SANE_Status)ret), ret);
}
else
#endif
{
if (callback::close_ui)
callback::close_ui(UI_INDICATOR);
if (callback::show_messagebox_ui)
{
callback::show_messagebox_ui(app_wnd_, ret, (void*)sane_helper_->invoke_sane_strstatus((SANE_Status)ret), 0);
}
// else
// {
// std::string msg(local_trans::a2u(sane_helper_->invoke_sane_strstatus((SANE_Status)ret), CP_UTF8));
// //if (indicator_.get())
// // indicator_->show(false);
// if (!IsWindow(app_wnd_))
// callback::bring_message_box_topmost(local_trans::lang_trans_between_hz936(CONST_STRING_START_FAILED).c_str());
// MessageBoxW(app_wnd_, msg.c_str(), local_trans::lang_trans_between_hz936(CONST_STRING_START_FAILED).c_str(), MB_OK | MB_ICONERROR);
// }
is_scanning_ = false;
}
}
prev_start_result_ = ret;
return ret;
}
scanner::EXAPIPOS scanner::find_ex_api(int op_id)
{
return std::find(ex_opts_.begin(), ex_opts_.end(), op_id);
}
EX_OPTION_HANDLER_IMPL(multiout)
{
int ret = SCANNER_ERR_OK;
const SANE_Option_Descriptor* desc = sane_helper_->invoke_sane_get_option_descriptor(handle_, base_id);
SANE_Bool parent = SANE_FALSE;
void *val = get_opt_value(is_multiout_id_, sizeof(SANE_Bool), false);
if(val)
{
parent = *(SANE_Bool*)val;
delete[] val;
}
if (setv)
{
char* cur = get_opt_value(base_id, desc->size, false),
* def = get_opt_value(base_id, desc->size, true);
int now = sane_opt_trans::multiout_value_to_twain(cur),
init = sane_opt_trans::multiout_value_to_twain(def),
val = 0;
delete[] def;
delete[] cur;
{
// parent item ...
if (!parent)
now = MULTI_OUT_NONE;
}
do
{
if (desc->constraint_type == SANE_CONSTRAINT_STRING_LIST)
{
// we have no 'MULTI_OUT_NONE' item in this option, this is used as is_multiout_id_
val = MULTI_OUT_NONE;
value_role role = val == now ? VAL_ROLE_CURRENT : VAL_ROLE_NONE;
if (val == init)
role = (value_role)(role | VAL_ROLE_DEFAULT);
if (!setv(&val, role, VAL_LIMIT_ENUM, data))
break;
for (int i = 0; desc->constraint.string_list[i]; ++i)
{
value_role role = VAL_ROLE_NONE;
val = sane_opt_trans::multiout_value_to_twain(desc->constraint.string_list[i]);
if (val == now)
role = VAL_ROLE_CURRENT;
if (val == init)
role = value_role(role | VAL_ROLE_DEFAULT);
if (!setv(&val, role, VAL_LIMIT_ENUM, data))
break;
}
}
else
set_cur_and_def_value<int>(now, init, setv, data);
} while (0);
}
else
{
char* val = new char[desc->size];
const char* in = sane_opt_trans::multiout_value_from_twain(*(int*)data);
SANE_Int after = 0;
//if (in && strcmp(in, "\346\227\240"))
//{
// ret = set_is_multiout(true);
// if (ret == SANE_STATUS_GOOD)
// {
strcpy(val, in);
ret = sane_helper_->invoke_sane_control_option(handle_, base_id, SANE_ACTION_SET_VALUE, val, &after);
// }
//}
//else
//{
// // disable multi-out, let multiout type aside
// ret = set_is_multiout(false);
//}
delete[] val;
}
return ret;
}
EX_OPTION_HANDLER_IMPL(auto_color_type)
{
int ret = SCANNER_ERR_OK;
const SANE_Option_Descriptor* desc = sane_helper_->invoke_sane_get_option_descriptor(handle_, base_id);
int len = desc->size + 4;
char* buf = get_opt_value(base_id, desc->size, false);
if (setv)
{
char* def = get_opt_value(base_id, desc->size, true);
int init = sane_opt_trans::auto_color_type_to_twain(def);
delete[] def;
len = sane_opt_trans::auto_color_type_to_twain(buf);
set_cur_and_def_value<int>(len, init, setv, data);
}
else
{
SANE_Int after = 0;
// ret = set_is_multiout(false);
if (ret == SCANNER_ERR_OK)
{
strcpy(buf, sane_opt_trans::auto_color_type_from_twain(*(int*)data));
ret = sane_helper_->invoke_sane_control_option(handle_, base_id, SANE_ACTION_SET_VALUE, buf, &after);
}
ret = ret;
}
delete[] buf;
return ret;
}
EX_OPTION_HANDLER_IMPL(color_mode)
{
int ret = SCANNER_ERR_OK;
const SANE_Option_Descriptor* desc = sane_helper_->invoke_sane_get_option_descriptor(handle_, base_id);
if (setv)
{
char* cur = get_opt_value(base_id, desc->size, false),
* def = get_opt_value(base_id, desc->size, true);
int now = sane_opt_trans::color_mode_to_twain(cur), // sane_opt_trans::multiout_value_to_twain(cur)
init = sane_opt_trans::color_mode_to_twain(def),
val = 0;
delete[] def;
delete[] cur;
do
{
if (desc->constraint_type == SANE_CONSTRAINT_STRING_LIST)
{
for (int i = 0; desc->constraint.string_list[i]; ++i)
{
value_role role = VAL_ROLE_NONE;
val = sane_opt_trans::color_mode_to_twain(desc->constraint.string_list[i]);
if (val == now)
role = VAL_ROLE_CURRENT;
if (val == init)
role = value_role(role | VAL_ROLE_DEFAULT);
if (!setv(&val, role, VAL_LIMIT_ENUM, data))
break;
}
}
else
set_cur_and_def_value<int>(now, init, setv, data);
} while (0);
}
else
{
SANE_Int after = 0;
// ret = set_is_multiout(false);
if (ret == SCANNER_ERR_OK)
{
char* val = new char[desc->size];
const char* in = sane_opt_trans::color_mode_from_twain(*(int*)data);
strcpy(val, in);
ret = sane_helper_->invoke_sane_control_option(handle_, base_id, SANE_ACTION_SET_VALUE, val, &after);
delete[] val;
}
}
return ret;
}
EX_OPTION_HANDLER_IMPL(sharpen)
{
int ret = SCANNER_ERR_OK;
const SANE_Option_Descriptor* desc = sane_helper_->invoke_sane_get_option_descriptor(handle_, base_id);
if (setv)
{
char* cur = get_opt_value(base_id, desc->size, false),
* def = get_opt_value(base_id, desc->size, true);
int now = sane_opt_trans::multiout_value_to_twain(cur),
init = sane_opt_trans::sharpen_to_twain(def),
val = 0;
delete[] def;
delete[] cur;
do
{
if (desc->constraint_type == SANE_CONSTRAINT_STRING_LIST)
{
for (int i = 0; desc->constraint.string_list[i]; ++i)
{
value_role role = VAL_ROLE_NONE;
val = sane_opt_trans::sharpen_to_twain(desc->constraint.string_list[i]);
if (val == now)
role = VAL_ROLE_CURRENT;
if (val == init)
role = value_role(role | VAL_ROLE_DEFAULT);
if (!setv(&val, role, VAL_LIMIT_ENUM, data))
break;
}
}
else
set_cur_and_def_value<int>(now, init, setv, data);
} while (0);
}
else
{
char* val = new char[desc->size];
const char* in = sane_opt_trans::sharpen_from_twain(*(int*)data);
SANE_Int after = 0;
strcpy(val, in);
ret = sane_helper_->invoke_sane_control_option(handle_, base_id, SANE_ACTION_SET_VALUE, val, &after);
delete[] val;
// ret = local_utility::sane_statu_2_scanner_err(ret);
}
return ret;
}
EX_OPTION_HANDLER_IMPL(paper)
{
int ret = SCANNER_ERR_OK;
const SANE_Option_Descriptor* desc = sane_helper_->invoke_sane_get_option_descriptor(handle_, base_id);
int len = desc->size + 4;
char* buf = get_opt_value(base_id, desc->size, false);
if (setv)
{
char* def = get_opt_value(base_id, desc->size, true);
int now = sane_opt_trans::paper_to_twain(buf),
init = sane_opt_trans::paper_to_twain(def),
val = 0;
delete[] def;
do
{
if (desc->constraint_type == SANE_CONSTRAINT_STRING_LIST)
{
for (int i = 0; desc->constraint.string_list[i]; ++i)
{
value_role role = VAL_ROLE_NONE;
val = sane_opt_trans::paper_to_twain(desc->constraint.string_list[i]);
if (val == -1)
continue;
if (val == now)
role = VAL_ROLE_CURRENT;
if (val == init)
role = value_role(role | VAL_ROLE_DEFAULT);
if (!setv(&val, role, VAL_LIMIT_ENUM, data))
break;
}
}
else
set_cur_and_def_value<int>(now, init, setv, data);
} while (0);
}
else if (sane_opt_trans::paper_from_twain(*(int*)data))
{
SANE_Int after = 0;
strcpy(buf, sane_opt_trans::paper_from_twain(*(int*)data));
ret = sane_helper_->invoke_sane_control_option(handle_, base_id, SANE_ACTION_SET_VALUE, buf, &after);
}
else
ret = SCANNER_ERR_INVALID_PARAMETER;
delete[] buf;
return ret;
}
EX_OPTION_HANDLER_IMPL(paper_lateral)
{
int ret = SCANNER_ERR_OK;
const SANE_Option_Descriptor* desc = sane_helper_->invoke_sane_get_option_descriptor(handle_, base_id);
int len = desc->size + 4;
char* buf = get_opt_value(base_id, desc->size, false);
const char* lateral_swap = nullptr;
bool lateral = false;
lateral_swap = sane_opt_trans::switch_paper_lateral(buf);
lateral = sane_opt_trans::is_paper_lateral(buf);
if (setv)
{
set_cur_and_def_value<bool>(lateral, false, setv, data);
}
else if (lateral_swap)
{
SANE_Int after = 0;
if (lateral != *(bool*)data)
{
strcpy(buf, lateral_swap);
ret = sane_helper_->invoke_sane_control_option(handle_, base_id, SANE_ACTION_SET_VALUE, buf, &after);
// ret = local_utility::sane_statu_2_scanner_err(ret);
}
}
else
{
SANE_Int after = 0;
if (*(bool*)data)
{
// set to A4Lateral ...
strcpy(buf, local_trans::lang_trans_between_hz936(OPTION_VALUE_ZZCC_A4HX, true, nullptr));
ret = sane_helper_->invoke_sane_control_option(handle_, base_id, SANE_ACTION_SET_VALUE, buf, &after);
// ret = local_utility::sane_statu_2_scanner_err(ret);
}
//ret = SCANNER_ERR_DEVICE_NOT_SUPPORT;
}
delete[] buf;
return ret;
}
EX_OPTION_HANDLER_IMPL(auto_paper_size)
{
int ret = SCANNER_ERR_OK;
const SANE_Option_Descriptor* desc = sane_helper_->invoke_sane_get_option_descriptor(handle_, base_id);
int len = desc->size + 4;
char* buf = get_opt_value(base_id, desc->size, false);
if (setv)
{
char* init = get_opt_value(base_id, desc->size, true);
bool yes = strcmp(buf, local_trans::lang_trans_between_hz936(OPTION_VALUE_ZZCC_PPYSCC, true, nullptr)) == 0,
def = strcmp(init, local_trans::lang_trans_between_hz936(OPTION_VALUE_ZZCC_PPYSCC, true, nullptr)) == 0;
delete[] init;
set_cur_and_def_value<bool>(yes, def, setv, data);
}
else
{
SANE_Int after = 0;
strcpy(buf, *(bool*)data ? local_trans::lang_trans_between_hz936(OPTION_VALUE_ZZCC_PPYSCC, true, nullptr) : local_trans::lang_trans_between_hz936(OPTION_VALUE_ZZCC_A4, true, nullptr));
ret = sane_helper_->invoke_sane_control_option(handle_, base_id, SANE_ACTION_SET_VALUE, buf, &after);
// ret = local_utility::sane_statu_2_scanner_err(ret);
}
delete[] buf;
return ret;
}
EX_OPTION_HANDLER_IMPL(auto_paper_crop)
{
int ret = SCANNER_ERR_OK;
const SANE_Option_Descriptor* desc = sane_helper_->invoke_sane_get_option_descriptor(handle_, base_id);
int len = desc->size + 4;
char* buf = get_opt_value(base_id, desc->size, false);
if (setv)
{
char* init = get_opt_value(base_id, desc->size, true);
bool yes = strcmp(buf, local_trans::lang_trans_between_hz936(OPTION_VALUE_ZZCC_ZDSMCCZDCQ, true, nullptr)) == 0,
def = strcmp(init, OPTION_VALUE_ZZCC_ZDSMCCZDCQ) == 0;
delete[] init;
set_cur_and_def_value<bool>(yes, def, setv, data);
}
else
{
SANE_Int after = 0;
strcpy(buf, *(bool*)data ? local_trans::lang_trans_between_hz936(OPTION_VALUE_ZZCC_ZDSMCCZDCQ, true, nullptr) : local_trans::lang_trans_between_hz936(OPTION_VALUE_ZZCC_ZDSMCC, true, nullptr));
ret = sane_helper_->invoke_sane_control_option(handle_, base_id, SANE_ACTION_SET_VALUE, buf, &after);
// ret = local_utility::sane_statu_2_scanner_err(ret);
}
delete[] buf;
return ret;
}
EX_OPTION_HANDLER_IMPL(text_direction)
{
int ret = SCANNER_ERR_OK;
const SANE_Option_Descriptor* desc = sane_helper_->invoke_sane_get_option_descriptor(handle_, base_id);
int len = desc->size + 4;
char* buf = get_opt_value(base_id, desc->size, false);
if (setv)
{
char* def = get_opt_value(base_id, desc->size, true);
float now = .0f, init = sane_opt_trans::text_direction_to_twain(def), val = .0f;
delete[] def;
now = sane_opt_trans::text_direction_to_twain(buf);
do
{
if (desc->constraint_type == SANE_CONSTRAINT_STRING_LIST)
{
for (int i = 0; desc->constraint.string_list[i]; ++i)
{
value_role role = VAL_ROLE_NONE;
val = sane_opt_trans::text_direction_to_twain(desc->constraint.string_list[i]);
if (IS_DOUBLE_EQUAL(val, now))
role = VAL_ROLE_CURRENT;
if (IS_DOUBLE_EQUAL(val, init))
role = value_role(role | VAL_ROLE_DEFAULT);
if (!setv(&val, role, VAL_LIMIT_ENUM, data))
break;
}
}
else
set_cur_and_def_value<float>(now, init, setv, data);
} while (0);
}
else
{
SANE_Int after = 0;
strcpy(buf, sane_opt_trans::text_direction_from_twain(*(float*)data));
ret = sane_helper_->invoke_sane_control_option(handle_, base_id, SANE_ACTION_SET_VALUE, buf, &after);
// ret = local_utility::sane_statu_2_scanner_err(ret);
}
delete[] buf;
return ret;
}
EX_OPTION_HANDLER_IMPL(duplex)
{
int ret = SCANNER_ERR_OK;
bool val = *(bool*)data;
const SANE_Option_Descriptor* desc = sane_helper_->invoke_sane_get_option_descriptor(handle_, base_id);
unsigned len = desc->size + 4;
char* buf = get_opt_value(base_id, desc->size, false);
if (setv)
{
char* init = get_opt_value(base_id, desc->size, true);
set_cur_and_def_value<bool>(strcmp(buf, local_trans::lang_trans_between_hz936(OPTION_VALUE_SMYM_SM, true, nullptr)) == 0, strcmp(init, local_trans::lang_trans_between_hz936(OPTION_VALUE_SMYM_SM, true, nullptr)) == 0, setv, data);
delete[] init;
}
else
{
strcpy(buf, val ? local_trans::lang_trans_between_hz936(OPTION_VALUE_SMYM_SM, true, nullptr) : local_trans::lang_trans_between_hz936(OPTION_VALUE_SMYM_DM, true, nullptr));
SANE_Int after = 0;
ret = sane_helper_->invoke_sane_control_option(handle_, base_id, SANE_ACTION_SET_VALUE, buf, &after);
// ret = local_utility::sane_statu_2_scanner_err(ret);
}
delete[] buf;
return ret;
}
EX_OPTION_HANDLER_IMPL(fill_background)
{
int ret = SCANNER_ERR_OK;
bool val = *(bool*)data;
const SANE_Option_Descriptor* desc = sane_helper_->invoke_sane_get_option_descriptor(handle_, base_id);
unsigned len = desc->size + 4;
char* buf = get_opt_value(base_id, desc->size, false);
if (setv)
{
char* init = get_opt_value(base_id, desc->size, true);
val = strcmp(buf, local_trans::lang_trans_between_hz936(OPTION_VALUE_SMYM_SM, true, nullptr)) == 0;
set_cur_and_def_value<bool>(strcmp(buf, local_trans::lang_trans_between_hz936(OPTION_VALUE_BJTCFS_TDBX, true, nullptr)) == 0, strcmp(init, local_trans::lang_trans_between_hz936(OPTION_VALUE_BJTCFS_TDBX, true, nullptr)) == 0, setv, data);
delete[] init;
}
else
{
strcpy(buf, val ? local_trans::lang_trans_between_hz936(OPTION_VALUE_BJTCFS_TDBX, true, nullptr) : local_trans::lang_trans_between_hz936(OPTION_VALUE_BJTCFS_ADBX, true, nullptr));
SANE_Int after = 0;
ret = sane_helper_->invoke_sane_control_option(handle_, base_id, SANE_ACTION_SET_VALUE, buf, &after);
// ret = local_utility::sane_statu_2_scanner_err(ret);
}
delete[] buf;
return ret;
}
EX_OPTION_HANDLER_IMPL(discard_blank_page)
{
int ret = SCANNER_ERR_OK;
bool val = *(bool*)data;
const SANE_Option_Descriptor* desc = sane_helper_->invoke_sane_get_option_descriptor(handle_, base_id);
unsigned len = desc->size + 4;
char* buf = get_opt_value(base_id, desc->size, false);
if (setv)
{
char* init = get_opt_value(base_id, desc->size, true);
bool def = strcmp(init, local_trans::lang_trans_between_hz936(OPTION_VALUE_SMYM_TGKBYTY, true, nullptr)) == 0;
delete[] init;
val = strcmp(buf, local_trans::lang_trans_between_hz936(OPTION_VALUE_SMYM_TGKBYTY, true, nullptr)) == 0;
set_cur_and_def_value<bool>(val, def, setv, data);
}
else
{
if (val)
strcpy(buf, local_trans::lang_trans_between_hz936(OPTION_VALUE_SMYM_TGKBYTY, true, nullptr));
else
{
char* init = get_opt_value(base_id, desc->size, true);
strcpy(buf, init);
delete[] init;
}
SANE_Int after = 0;
ret = sane_helper_->invoke_sane_control_option(handle_, base_id, SANE_ACTION_SET_VALUE, buf, &after);
// ret = local_utility::sane_statu_2_scanner_err(ret);
}
delete[] buf;
return ret;
}
EX_OPTION_HANDLER_IMPL(discard_blank_receipt)
{
int ret = SCANNER_ERR_OK;
bool val = *(bool*)data;
const SANE_Option_Descriptor* desc = sane_helper_->invoke_sane_get_option_descriptor(handle_, base_id);
unsigned len = desc->size + 4;
char* buf = get_opt_value(base_id, desc->size, false);
if (setv)
{
char* init = get_opt_value(base_id, desc->size, true);
bool def = strcmp(init, local_trans::lang_trans_between_hz936(OPTION_VALUE_SMYM_TGKBYFPZ, true, nullptr)) == 0;
delete[] init;
val = strcmp(buf, local_trans::lang_trans_between_hz936(OPTION_VALUE_SMYM_TGKBYFPZ, true, nullptr)) == 0;
set_cur_and_def_value<bool>(val, def, setv, data);
}
else
{
if (val)
strcpy(buf, local_trans::lang_trans_between_hz936(OPTION_VALUE_SMYM_TGKBYFPZ, true, nullptr));
else
{
char* init = get_opt_value(base_id, desc->size, true);
strcpy(buf, init);
delete[] init;
}
SANE_Int after = 0;
ret = sane_helper_->invoke_sane_control_option(handle_, base_id, SANE_ACTION_SET_VALUE, buf, &after);
// ret = local_utility::sane_statu_2_scanner_err(ret);
}
delete[] buf;
return ret;
}
EX_OPTION_HANDLER_IMPL(page_fold)
{
int ret = SCANNER_ERR_OK;
bool val = *(bool*)data;
const SANE_Option_Descriptor* desc = sane_helper_->invoke_sane_get_option_descriptor(handle_, base_id);
unsigned len = desc->size + 4;
char* buf = get_opt_value(base_id, desc->size, false);
if (setv)
{
char* init = get_opt_value(base_id, desc->size, true);
bool def = strcmp(init, local_trans::lang_trans_between_hz936(OPTION_VALUE_SMYM_DZ, true, nullptr)) == 0;
delete[] init;
val = strcmp(buf, local_trans::lang_trans_between_hz936(OPTION_VALUE_SMYM_DZ, true, nullptr)) == 0;
set_cur_and_def_value<bool>(val, def, setv, data);
}
else
{
if (val)
strcpy(buf, local_trans::lang_trans_between_hz936(OPTION_VALUE_SMYM_DZ, true, nullptr));
else
{
char* init = get_opt_value(base_id, desc->size, true);
strcpy(buf, init);
delete[] init;
}
SANE_Int after = 0;
ret = sane_helper_->invoke_sane_control_option(handle_, base_id, SANE_ACTION_SET_VALUE, buf, &after);
// ret = local_utility::sane_statu_2_scanner_err(ret);
}
delete[] buf;
return ret;
}
EX_OPTION_HANDLER_IMPL(color_filter) // int (filter_value)
{
int ret = SCANNER_ERR_DEVICE_NOT_SUPPORT;
const SANE_Option_Descriptor* desc = sane_helper_->invoke_sane_get_option_descriptor(handle_, base_id);
char* buf = nullptr;
unsigned int len = 0;
if (desc)
{
len = desc->size + 4;
buf = get_opt_value(base_id, desc->size, false);
if (setv)
{
bool filter = false;
int val = FILTER_NONE, now = FILTER_NONE;
now = sane_opt_trans::filter_enhance_value_to_twain(buf, &filter);
if (!filter)
now = val;
do
{
if (desc->constraint_type == SANE_CONSTRAINT_STRING_LIST)
{
for (int i = 0; desc->constraint.string_list[i]; ++i)
{
value_role role = VAL_ROLE_NONE;
int v = sane_opt_trans::filter_enhance_value_to_twain(desc->constraint.string_list[i], &filter);
if (!filter && v != FILTER_NONE)
continue;
if (v == now)
role = VAL_ROLE_CURRENT;
if (v == val)
role = value_role(role | VAL_ROLE_DEFAULT);
if (!setv(&v, role, VAL_LIMIT_ENUM, data))
break;
}
}
}while (0);
}
else
{
const char* val = sane_opt_trans::filter_enhance_value_from_twain(*((int*)data), true);
SANE_Int after = 0;
strcpy(buf, val);
ret = sane_helper_->invoke_sane_control_option(handle_, base_id, SANE_ACTION_SET_VALUE, buf, &after);
// ret = local_utility::sane_statu_2_scanner_err(ret);
}
delete[] buf;
}
return ret;
}
EX_OPTION_HANDLER_IMPL(color_enhance) // int (enhance_value)
{
int ret = SCANNER_ERR_DEVICE_NOT_SUPPORT;
const SANE_Option_Descriptor* desc = sane_helper_->invoke_sane_get_option_descriptor(handle_, base_id);
char* buf = nullptr;
unsigned int len = 0;
if (desc)
{
len = desc->size + 4;
buf = get_opt_value(base_id, desc->size, false);
if (setv)
{
bool filter = false;
int val = ENHANCE_NONE, now = ENHANCE_NONE;
now = sane_opt_trans::filter_enhance_value_to_twain(buf, &filter);
if (filter)
now = val;
do
{
if (desc->constraint_type == SANE_CONSTRAINT_STRING_LIST)
{
for (int i = 0; desc->constraint.string_list[i]; ++i)
{
value_role role = VAL_ROLE_NONE;
int v = sane_opt_trans::filter_enhance_value_to_twain(desc->constraint.string_list[i], &filter);
if (filter && v != ENHANCE_NONE)
continue;
if (v == now)
role = VAL_ROLE_CURRENT;
if (v == val)
role = value_role(role | VAL_ROLE_DEFAULT);
if (!setv(&v, role, VAL_LIMIT_ENUM, data))
break;
}
}
} while (0);
}
else
{
const char* val = sane_opt_trans::filter_enhance_value_from_twain(*((int*)data), false);
SANE_Int after = 0;
strcpy(buf, val);
ret = sane_helper_->invoke_sane_control_option(handle_, base_id, SANE_ACTION_SET_VALUE, buf, &after);
// ret = local_utility::sane_statu_2_scanner_err(ret);
}
delete[] buf;
}
return ret;
}
EX_OPTION_HANDLER_IMPL(final_compression)
{
int ret = SCANNER_ERR_OK;
int* compression = (int*)data;
unsigned int len = sizeof(*compression);
if (setv)
{
if (ret == SANE_STATUS_GOOD)
{
int i = SANE_COMPRESSION_FIRST;
for (; i < SANE_COMPRESSION_LAST; ++i)
{
value_role role = VAL_ROLE_NONE;
if (i == img_fmt_.img_format)
role = VAL_ROLE_CURRENT;
if (i == SANE_COMPRESSION_NONE)
role = value_role(role | VAL_ROLE_DEFAULT);
int v = sane_opt_trans::compression_to_twain(i);
if (!setv(&v, role, VAL_LIMIT_ENUM, data))
break;
}
}
}
else
{
int val = sane_opt_trans::compression_from_twain(*(int*)data);
len = sizeof(val);
// ret = hg_sane_middleware::instance()->io_control(handle_, IO_CTRL_CODE_SET_FINAL_COMPRESSION, &val, &len);
// ret = local_utility::sane_statu_2_scanner_err(ret);
img_fmt_.compress.compression = (SANE_CompressionType)val;
}
return ret;
}
EX_OPTION_HANDLER_IMPL(final_format)
{
int ret = SCANNER_ERR_OK;
SANE_FinalImgFormat ff = img_fmt_;
unsigned int len = sizeof(ff);
if (setv)
{
if (ret == SANE_STATUS_GOOD)
{
int now = ff.img_format, init = SANE_IMAGE_TYPE_BMP;
std::vector<int> all(sane_opt_trans::support_image_types());
for (int i = 0; i < (int)all.size(); ++i)
{
value_role role = VAL_ROLE_NONE;
ff.img_format = (SANE_ImageType)all[i];
if (ff.img_format == now)
role = VAL_ROLE_CURRENT;
if (ff.img_format == init)
role = value_role(role | VAL_ROLE_DEFAULT);
if (!setv(&ff, role, VAL_LIMIT_ENUM, data))
break;
}
}
}
else
{
img_fmt_ = *(SANE_FinalImgFormat*)data;
// ret = hg_sane_middleware::instance()->io_control(handle_, IO_CTRL_CODE_SET_FINAL_IMAGE_FORMAT, data, &len);
// ret = local_utility::sane_statu_2_scanner_err(ret);
}
return ret;
}
EX_OPTION_HANDLER_IMPL(serial)
{
int ret = SCANNER_ERR_INVALID_PARAMETER;
if (setv)
{
std::string val("");
ret = control_read_string(IO_CTRL_CODE_GET_SERIAL, val);
if (ret == SANE_STATUS_GOOD)
setv(&val, VAL_ROLE_CURRENT, VAL_LIMIT_NONE, data);
}
return ret;
}
EX_OPTION_HANDLER_IMPL(to_be_scan)
{
int ret = SCANNER_ERR_OK;
SANE_Bool wait_paper = SANE_FALSE;
SANE_Int len = sizeof(wait_paper);
if (setv)
{
ret = sane_helper_->invoke_sane_control_option(handle_, SANE_OPT_ID_WAIT_TO_SCAN, SANE_ACTION_GET_VALUE, &wait_paper, &len);
if (ret == SANE_STATUS_GOOD)
{
bool val = wait_paper == SANE_TRUE;
set_cur_and_def_value<bool>(val, false, setv, data);
}
}
else
{
wait_paper = *((bool*)data) ? SANE_TRUE : SANE_FALSE;
ret = sane_helper_->invoke_sane_control_option(handle_, SANE_OPT_ID_WAIT_TO_SCAN, SANE_ACTION_SET_VALUE, &wait_paper, &len);
// ret = local_utility::sane_statu_2_scanner_err(ret);
}
return ret;
}
EX_OPTION_HANDLER_IMPL(scan_with_hole)
{
int ret = SCANNER_ERR_OK;
SANE_Bool with_hole = SANE_FALSE;
SANE_Int len = sizeof(with_hole);
if (setv)
{
ret = sane_helper_->invoke_sane_control_option(handle_, SANE_OPT_ID_RID_HOLE, SANE_ACTION_GET_VALUE, &with_hole, &len);
if (ret == SANE_STATUS_GOOD)
{
bool val = with_hole == SANE_TRUE;
set_cur_and_def_value<bool>(val, false, setv, data);
}
}
else
{
with_hole = *((bool*)data) ? SANE_TRUE : SANE_FALSE;
ret = sane_helper_->invoke_sane_control_option(handle_, SANE_OPT_ID_RID_HOLE, SANE_ACTION_SET_VALUE, &with_hole, &len);
// ret = local_utility::sane_statu_2_scanner_err(ret);
}
return ret;
}
EX_OPTION_HANDLER_IMPL(device_code)
{
int ret = SCANNER_ERR_INVALID_PARAMETER;
if (setv)
{
std::string val("");
ret = control_read_string(IO_CTRL_CODE_GET_SERIAL, val);
if (ret == SANE_STATUS_GOOD)
setv(&val, VAL_ROLE_CURRENT, VAL_LIMIT_NONE, data);
}
return ret;
}
EX_OPTION_HANDLER_IMPL(power)
{
int ret = SCANNER_ERR_OK;
if (setv)
{
const SANE_Option_Descriptor* desc = sane_helper_->invoke_sane_get_option_descriptor(handle_, SANE_OPT_ID_TIME_TO_SLEEP);
if(desc)
{
char init[256] = {0}, now[256] = {0};
int len = sizeof(now) - 1;
value_role role = VAL_ROLE_NONE;
int8_t power = SANE_POWER_NONE, cur = SANE_POWER_NONE, def = SANE_POWER_NONE;
sane_helper_->invoke_sane_control_option(handle_, SANE_OPT_ID_TIME_TO_SLEEP, SANE_ACTION_GET_VALUE, now, &len);
len = sizeof(init) - 1;
sane_helper_->invoke_sane_control_option(handle_, SANE_OPT_ID_TIME_TO_SLEEP, (SANE_Action)SANE_ACTION_GET_DEFAULT_VALUE, init, &len);
cur = sane_opt_trans::sleep_time(now);
def = sane_opt_trans::sleep_time(init);
if(desc->constraint_type == SANE_CONSTRAINT_STRING_LIST && desc->constraint.string_list)
{
for(int i = 0; desc->constraint.string_list[i]; ++i)
{
power = sane_opt_trans::sleep_time(desc->constraint.string_list[i]);
role = VAL_ROLE_NONE;
if(power == cur)
role = VAL_ROLE_CURRENT;
if(power == def)
role = (value_role)(VAL_ROLE_CURRENT | VAL_ROLE_DEFAULT);
if(!setv(&power, role, VAL_LIMIT_NONE, data))
break;
}
}
else
{
role = VAL_ROLE_CURRENT;
if(cur == def)
{
role = (value_role)(VAL_ROLE_CURRENT | VAL_ROLE_DEFAULT);
setv(&cur, role, VAL_LIMIT_NONE, data);
}
else
{
setv(&cur, role, VAL_LIMIT_NONE, data);
setv(&def, VAL_ROLE_DEFAULT, VAL_LIMIT_NONE, data);
}
}
}
}
else
{
SANE_Power power = *((SANE_Power*)data);
char buf[128] = {0};
unsigned int len = sizeof(buf);
strcpy(buf, sane_opt_trans::sleep_time(power));
ret = sane_helper_->invoke_sane_control_option(handle_, SANE_OPT_ID_TIME_TO_SLEEP, SANE_ACTION_SET_VALUE, buf, (SANE_Int*)&len);
*(int*)data = sane_opt_trans::sleep_time(buf);
// ret = local_utility::sane_statu_2_scanner_err(ret);
}
return ret;
}
EX_OPTION_HANDLER_IMPL(hardware_version)
{
int ret = SCANNER_ERR_INVALID_PARAMETER;
if (setv)
{
std::string val("");
ret = control_read_string(IO_CTRL_CODE_GET_HARDWARE_VERSION, val);
if (ret == SANE_STATUS_GOOD)
setv(&val, VAL_ROLE_CURRENT, VAL_LIMIT_NONE, data);
}
return ret;
}
EX_OPTION_HANDLER_IMPL(ip)
{
int ret = SCANNER_ERR_INVALID_PARAMETER;
if (setv)
{
std::string val("");
ret = control_read_string(IO_CTRL_CODE_GET_IP, val);
if (ret == SANE_STATUS_GOOD)
setv(&val, VAL_ROLE_CURRENT, VAL_LIMIT_NONE, data);
}
return ret;
}
EX_OPTION_HANDLER_IMPL(erase_hole)
{
int ret = SCANNER_ERR_DEVICE_NOT_SUPPORT;
if (is_erase_hole_l_id_ != -1 || is_erase_hole_r_id_ != -1)
{
SANE_Bool yes = SANE_FALSE, def = SANE_FALSE;
SANE_Int after = 0;
if (setv)
{
if (is_erase_hole_l_id_ != -1)
{
get_opt_value(is_erase_hole_l_id_, &yes, false);
get_opt_value(is_erase_hole_l_id_, &def, true);
}
if (!yes && is_erase_hole_r_id_ != -1)
{
get_opt_value(is_erase_hole_r_id_, &yes, false);
get_opt_value(is_erase_hole_r_id_, &def, true);
}
set_cur_and_def_value<bool>(yes == SANE_TRUE, def == SANE_TRUE, setv, data);
ret = SCANNER_ERR_OK;
}
else
{
yes = *(bool*)data ? SANE_TRUE : SANE_FALSE;
if (is_erase_hole_l_id_ != -1)
ret = sane_helper_->invoke_sane_control_option(handle_, is_erase_hole_l_id_, SANE_ACTION_SET_VALUE, &yes, &after);
yes = *(bool*)data ? SANE_TRUE : SANE_FALSE;
if (is_erase_hole_r_id_ != -1)
ret = sane_helper_->invoke_sane_control_option(handle_, is_erase_hole_r_id_, SANE_ACTION_SET_VALUE, &yes, &after);
}
}
return ret;
}
EX_OPTION_HANDLER_IMPL(search_hole_range)
{
int ret = SCANNER_ERR_DEVICE_NOT_SUPPORT;
if (search_hole_range_l_id_ != -1 || search_hole_range_r_id_ != -1)
{
SANE_Fixed val = 0;
SANE_Int after = 0;
double rv = .0f, def = .0f;
if (setv)
{
const SANE_Option_Descriptor* desc = sane_helper_->invoke_sane_get_option_descriptor(handle_, base_id);
void* init = get_opt_value(base_id, desc->size, true),
* cur = get_opt_value(base_id, desc->size, false);
float n = SANE_UNFIX(*(SANE_Fixed*)cur),
d = SANE_UNFIX(*(SANE_Fixed*)init);
if (desc->constraint_type == SANE_CONSTRAINT_RANGE)
{
float l = SANE_UNFIX(desc->constraint.range->min),
u = SANE_UNFIX(desc->constraint.range->max),
s = SANE_UNFIX(desc->constraint.range->quant);
set_value_range<SANE_Fixed, float>(*(SANE_Fixed*)cur, *(SANE_Fixed*)init, desc->constraint.range->min, desc->constraint.range->max, desc->constraint.range->quant, setv, data, &scanner::to_float);
}
else
set_cur_and_def_value<float>(n, d, setv, data);
delete[] init;
delete[] cur;
ret = SCANNER_ERR_OK;
}
else
{
rv = (double)*(float*)data;
val = SANE_FIX(rv);
if (search_hole_range_l_id_ != -1)
ret = sane_helper_->invoke_sane_control_option(handle_, search_hole_range_l_id_, SANE_ACTION_SET_VALUE, &val, &after);
val = SANE_FIX(rv);
if (search_hole_range_r_id_ != -1)
ret = sane_helper_->invoke_sane_control_option(handle_, search_hole_range_r_id_, SANE_ACTION_SET_VALUE, &val, &after);
}
}
return ret;
}
// ISaneInvoker
COM_API_IMPLEMENT(scanner, int, start(void))
{
int ret = SANE_STATUS_GOOD;
ev_cnt_ = 0;
events_.clear();
images_.clear();
scan_msg_ = "OK";
scan_err_ = false;
user_cancel_ = false;
fetch_imgs_ = 0;
is_scanning_ = true;
app_wnd_ = callback::find_main_wnd();
if (thread_starting_.get() && thread_starting_->joinable())
thread_starting_->join();
#ifdef START_SCAN_IN_THREAD
thread_starting_.reset(new std::thread(&scanner::thread_start, this));
#else
ret = thread_start();
#endif
return ret;
}
COM_API_IMPLEMENT(scanner, int, stop(void))
{
user_cancel_ = true;
sane_helper_->invoke_sane_cancel(handle_);
return SANE_STATUS_GOOD;
}
COM_API_IMPLEMENT(scanner, int, get_event(void))
{
return events_.take();
}
COM_API_IMPLEMENT(scanner, void, set_event_callback(int(* handle_ev)(int, void*), void* para))
{
scanner_ev_handler_ = handle_ev;
evh_param_ = para;
}
COM_API_IMPLEMENT(scanner, bool, wait_image(DWORD milliseconds))
{
int count = get_scanned_images(milliseconds);
return count > 0;
}
COM_API_IMPLEMENT(scanner, int, get_scanned_images(DWORD milliseconds))
{
size_t count = images_.count();
DWORD elapse = 2;
is_ui_wait_img_ = true;
while (is_scanning_ && count == 0 && milliseconds)
{
// MSG msg = { 0 };
// if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
// {
// TranslateMessage(&msg);
// DispatchMessageW(&msg);
// }
// else
Sleep(elapse);
count = images_.count();
//int ev = get_event();
//
//if (ev == SANE_EVENT_SCAN_FINISHED)
//{
// ui_hide();
// break;
//}
//else if (ev == SANE_EVENT_UI_CLOSE_CANCEL)
//{
// stop();
// ui_hide();
// break;
//}
//else if (ev == SANE_EVENT_UI_CLOSE_NORMAL)
//{
// ui_hide();
// break;
//}
if (milliseconds != -1)
{
if (milliseconds <= elapse)
break;
milliseconds -= elapse;
}
}
is_ui_wait_img_ = false;
count = images_.count();
{
char msg[128] = { 0 };
sprintf_s(msg, _countof(msg) - 1, "Wait image count = %d\r\n", count);
utils::log_info(msg, (log_level)1);
}
return count;
}
COM_API_IMPLEMENT(scanner, IScanImg*, take_first_image(twain_xfer xfer))
{
scanned_img* img = images_.take();
if (img)
{
img->prepare_data_for_transfer(xfer);
utils::to_log(LOG_LEVEL_DEBUG, "Begin transferring image %d of %p\r\n", fetch_imgs_ + 1, img);
}
return dynamic_cast<IScanImg*>(img);
}
COM_API_IMPLEMENT(scanner, bool, get_first_image_header(SANE_Parameters* header, size_t* bytes, int* dpi))
{
return images_.get_header(header, bytes, dpi);
}
COM_API_IMPLEMENT(scanner, bool, is_online(void))
{
std::string sn("");
return handle_ && control_read_string(IO_CTRL_CODE_GET_SERIAL, sn) != SCANNER_ERR_DEVICE_NOT_FOUND;
}
COM_API_IMPLEMENT(scanner, bool, is_paper_on(void))
{
SANE_Bool on = SANE_FALSE;
unsigned int len = sizeof(on);
if (get_opt_value(SANE_OPT_ID_PAPER_ON, &on, false) == SANE_STATUS_GOOD)
return on == SANE_TRUE;
else
return false;
}
COM_API_IMPLEMENT(scanner, int, last_error(void))
{
return err_;
}
COM_API_IMPLEMENT(scanner, int, image_fetched(IScanImg* tx))
{
fetch_imgs_++;
if (ui_notify)
ui_notify(SANE_EVENT_IMG_UPLOADED, nullptr, fetch_imgs_);
utils::to_log(LOG_LEVEL_DEBUG, "Transferring image %d of %p finished.\r\n", fetch_imgs_, tx);
return 0;
}
COM_API_IMPLEMENT(scanner, bool, get_option_info(int sn, value_type* type, value_limit* limit, int* bytes))
{
#define SIMPLE_STR_INFO(id, rdo) \
if (sn == id) \
{ \
if (type) \
*type = VAL_TYPE_STR; \
if (limit) \
*limit = rdo ? VAL_LIMIT_READONLY : VAL_LIMIT_NONE; \
if (bytes) \
*bytes = 255; \
\
return true; \
}
#define SIMPLE_INT_INFO(id, rdo) \
if (sn == id) \
{ \
if (type) \
*type = VAL_TYPE_INT; \
if (limit) \
*limit = rdo ? VAL_LIMIT_READONLY : VAL_LIMIT_NONE; \
if (bytes) \
*bytes = sizeof(int); \
\
return true; \
}
SIMPLE_STR_INFO(SANE_OPT_ID_DRIVER_VERSION, true);
SIMPLE_STR_INFO(SANE_OPT_ID_MANUFACTURER, true);
SIMPLE_STR_INFO(SANE_OPT_ID_COPYRIGHT, true);
SIMPLE_STR_INFO(SANE_OPT_ID_CO_URL, true);
SIMPLE_STR_INFO(SANE_OPT_ID_CO_TEL, true);
SIMPLE_STR_INFO(SANE_OPT_ID_CO_ADDR, true);
SIMPLE_STR_INFO(SANE_OPT_ID_CO_GPS, true);
SIMPLE_STR_INFO(SANE_OPT_ID_DEV_NAME, true);
SIMPLE_STR_INFO(SANE_OPT_ID_DEV_FAMILY, true);
SIMPLE_STR_INFO(SANE_OPT_ID_LOGIN, false);
SIMPLE_STR_INFO(SANE_OPT_ID_LOGOUT, false);
SIMPLE_STR_INFO(SANE_OPT_ID_DRIVER_LOG, false);
SIMPLE_STR_INFO(SANE_OPT_ID_DEVICE_LOG, false);
SIMPLE_STR_INFO(SANE_OPT_ID_DEVICE_MAC_ADDR, true);
SIMPLE_STR_INFO(SANE_OPT_ID_MOTOR_VER, true);
SIMPLE_INT_INFO(SANE_OPT_ID_HELP, false);
SIMPLE_INT_INFO(SANE_OPT_ID_HISTORY_COUNT, true);
SIMPLE_INT_INFO(SANE_OPT_ID_ROLLER_COUNT, false);
SIMPLE_INT_INFO(SANE_OPT_ID_VID, true);
SIMPLE_INT_INFO(SANE_OPT_ID_PID, true);
SIMPLE_INT_INFO(SANE_OPT_ID_ROLLER_LIFE, true);
sn = transfer_id(sn);
if (sn == -1)
return false;
EXAPIPOS ex = find_ex_api(sn);
const SANE_Option_Descriptor* desc = sane_helper_->invoke_sane_get_option_descriptor(handle_, ex == ex_opts_.end() ? sn : ex->base_ind);
bool ret = false;
if (desc)
{
if (type)
*type = scanner::from_sane_type(desc->type);
if (limit)
{
int lmt = scanner::from_sane_constraint(desc->constraint_type);
if (IS_CAP_READONLY(desc->cap))
lmt |= VAL_LIMIT_READONLY;
*limit = (value_limit)lmt;
}
if (bytes)
*bytes = desc->size;
ret = true;
}
return ret;
}
COM_API_IMPLEMENT(scanner, bool, get_value(int sn, set_opt_value setval, void* param))
{
if (sane_ids_.count((sane_option_id)sn) &&
sane_ids_[(sane_option_id)sn] == sn)
{
char buf[256] = { 0 };
value_type type = VAL_TYPE_NONE;
int ret = sane_helper_->invoke_sane_control_option(handle_, sn, SANE_ACTION_GET_VALUE, buf, nullptr);
if (ret == SANE_STATUS_GOOD)
{
get_option_info(sn, &type, nullptr, nullptr);
if (type == VAL_TYPE_STR)
{
std::string str(buf);
setval(&str, VAL_ROLE_CURRENT, VAL_LIMIT_NONE, param);
}
else
setval(buf, VAL_ROLE_CURRENT, VAL_LIMIT_NONE, param);
return true;
}
return false;
}
int org_id = sn;
if(sn == 0x102c) // CapType::Language
sn = transfer_id(SANE_OPT_ID_LANGUAGE);
else
sn = transfer_id(sn);
if (sn == -1)
return false;
EXAPIPOS ex = find_ex_api(sn);
int ret = SANE_STATUS_INVAL;
SANE_Int after = 0;
if (ex == ex_opts_.end())
{
if (get_option_value_with_parent(sn, setval, param))
return true;
const SANE_Option_Descriptor* desc = sane_helper_->invoke_sane_get_option_descriptor(handle_, sn);
void* init = get_opt_value(sn, desc->size, true);
ret = SANE_STATUS_GOOD;
if (desc->type == SANE_TYPE_BOOL)
{
SANE_Bool v = SANE_FALSE;
bool val = false;
get_opt_value(sn, &v, false);
val = v == SANE_TRUE;
//set_cur_and_def_value<bool>(val, *(SANE_Bool*)init == SANE_TRUE, setval, param);
{
int role = VAL_ROLE_NONE;
val = false;
if (*(SANE_Bool*)init == SANE_FALSE)
role |= VAL_ROLE_DEFAULT;
if (v == SANE_FALSE)
role |= VAL_ROLE_CURRENT;
setval(&val, (value_role)role, VAL_LIMIT_ENUM, param);
val = true;
role = VAL_ROLE_NONE;
if (*(SANE_Bool*)init == SANE_TRUE)
role |= VAL_ROLE_DEFAULT;
if (v == SANE_TRUE)
role |= VAL_ROLE_CURRENT;
setval(&val, (value_role)role, VAL_LIMIT_ENUM, param);
}
}
else if (desc->type == SANE_TYPE_INT)
{
SANE_Int cur = 0, def = *(SANE_Int*)init;
int val = 0;
get_opt_value(sn, &cur, false);
val = cur;
if (sn == resolution_id_)
dpi_ = cur;
do
{
if (desc->constraint_type == SANE_CONSTRAINT_RANGE)
{
set_value_range<SANE_Int, int>(cur, def, desc->constraint.range->min, desc->constraint.range->max, desc->constraint.range->quant, setval, param, scanner::to_int);
}
else if (desc->constraint_type == SANE_CONSTRAINT_WORD_LIST)
{
const SANE_Word* v = desc->constraint.word_list;
for (int i = 0; i < v[0]; ++i)
{
value_role role = VAL_ROLE_NONE;
if (v[i + 1] == cur)
role = value_role(role | VAL_ROLE_CURRENT);
if (v[i + 1] == *(SANE_Int*)init)
role = value_role(role | VAL_ROLE_DEFAULT);
val = v[i + 1];
if (!setval(&val, role, VAL_LIMIT_ENUM, param))
break;
}
}
else
set_cur_and_def_value<int>(val, *(SANE_Int*)init, setval, param);
}while (0);
}
else if (desc->type == SANE_TYPE_FIXED)
{
SANE_Fixed cur = 0, def = *(SANE_Fixed*)init;
float val = .0f;
get_opt_value(sn, &cur, false);
if (sn == resolution_id_)
dpi_ = (int)(SANE_UNFIX(cur) + .5f);
do
{
if (desc->constraint_type == SANE_CONSTRAINT_RANGE)
{
set_value_range<SANE_Fixed, float>(cur, def, desc->constraint.range->min, desc->constraint.range->max, desc->constraint.range->quant, setval, param, scanner::to_float);
}
else if (desc->constraint_type == SANE_CONSTRAINT_WORD_LIST)
{
const SANE_Word* v = desc->constraint.word_list;
for (int i = 0; i < v[0]; ++i)
{
value_role role = VAL_ROLE_NONE;
if (v[i + 1] == cur)
role = value_role(role | VAL_ROLE_CURRENT);
if (v[i + 1] == def)
role = value_role(role | VAL_ROLE_DEFAULT);
val = (float)SANE_UNFIX(v[i + 1]);
if (!setval(&val, role, VAL_LIMIT_ENUM, param))
break;
}
}
else
set_cur_and_def_value<float>(val, (float)SANE_UNFIX(*(SANE_Fixed*)init), setval, param);
} while (0);
}
else if (desc->type == SANE_TYPE_STRING)
{
char* buf = new char[desc->size + 4];
std::string val(""), def((char*)init);
memset(buf, 0, desc->size + 4);
get_opt_value(sn, buf, false);
val = buf;
do
{
if (desc->constraint_type == SANE_CONSTRAINT_STRING_LIST)
{
for (int i = 0; desc->constraint.string_list[i]; ++i)
{
value_role role = VAL_ROLE_NONE;
if (strcmp(desc->constraint.string_list[i], buf) == 0)
role = value_role(role | VAL_ROLE_CURRENT);
if (strcmp(desc->constraint.string_list[i], (char*)init) == 0)
role = value_role(role | VAL_ROLE_DEFAULT);
val = desc->constraint.string_list[i];
if (org_id == 0x102c)
{
// const char* hz = local_trans::lang_trans_between_hz936(val.c_str(), false, nullptr);
int lid = sane_opt_trans::language_to_twain(val.c_str());
if (!setval(&lid, role, VAL_LIMIT_ENUM, param))
break;
}
else if (!setval(&val, role, VAL_LIMIT_ENUM, param))
break;
}
}
else
set_cur_and_def_value<std::string>(val, def, setval, param);
} while (0);
delete[] buf;
}
else
{
ret = SANE_STATUS_INVAL;
}
delete[] init;
}
else
{
ret = (this->*ex->ex_api)(ex->base_ind, param, setval);
}
return ret == SANE_STATUS_GOOD;
}
COM_API_IMPLEMENT(scanner, bool, get_value(int sn, void* data, int* len))
{
if (sn != SANE_OPT_ID_DRIVER_LOG && sn != SANE_OPT_ID_DEVICE_LOG)
return false;
return sane_helper_->invoke_sane_control_option(handle_, sn, SANE_ACTION_GET_VALUE, data, nullptr) == SANE_STATUS_GOOD;
}
COM_API_IMPLEMENT(scanner, int, set_value(int sn, void* val))
{
if (sane_ids_.count((sane_option_id)sn) &&
sane_ids_[(sane_option_id)sn] == sn)
{
int ret = sane_helper_->invoke_sane_control_option(handle_, sn, SANE_ACTION_SET_VALUE, val, nullptr);
return ret;
}
int org_id = sn;
if (sn == 0x102c) // CapType::Language
sn = transfer_id(SANE_OPT_ID_LANGUAGE);
else
sn = transfer_id(sn);
if (sn == -1)
return SANE_STATUS_UNSUPPORTED;
EXAPIPOS ex = find_ex_api(sn);
int ret = SANE_STATUS_INVAL;
SANE_Int after = 0;
const SANE_Option_Descriptor* desc = sane_helper_->invoke_sane_get_option_descriptor(handle_, sn);
{
char msg[256] = { 0 };
if (ex == ex_opts_.end())
sprintf_s(msg, _countof(msg) - 1, "set_value of %s(%s) of ID %d\r\n", desc->name, callback::option_value_2_string(desc->type, val).c_str(), sn);
else if(ex->base_ind != -1)
{
desc = sane_helper_->invoke_sane_get_option_descriptor(handle_, ex->base_ind);
sprintf_s(msg, _countof(msg) - 1, "set_value of %s(0x%x) of ID %d, base id = %d)\r\n", desc->name, *(unsigned*)val, sn, ex->base_ind);
}
utils::log_info(msg, (log_level)0);
}
twain_set_ = true;
if (ex == ex_opts_.end())
{
std::string strv("");
void* w = val;
if (org_id == 0x102c)
{
// const char* str = sane_opt_trans::language_from_twain(*(int*)val);
strv = sane_opt_trans::language_from_twain(*(int*)val);
w = &strv;
}
if (!set_option_value_with_parent(sn, w, &ret))
ret = set_option_value(sn, desc->type, desc->size, w);
// ret = local_utility::sane_statu_2_scanner_err(ret);
}
else
{
ret = (this->*ex->ex_api)(ex->base_ind, val, nullptr);
}
if (sn == resolution_id_)
{
if (desc->type == SANE_TYPE_FIXED)
dpi_ = (int)(*(float*)val + .5f);
else
dpi_ = *(int*)val;
}
return ret;
}
COM_API_IMPLEMENT(scanner, int, convert_image(SANE_ImageFormatConvert* conv))
{
return sane_helper_->invoke_sane_control_option(handle_, SANE_OPT_ID_TRANSFORM_IMAGE_FORMAT, SANE_ACTION_SET_VALUE, conv, nullptr);
}
COM_API_IMPLEMENT(scanner, void, free_buffer(void* buf, int len))
{
sane_helper_->invoke_sane_control_option(handle_, SANE_OPT_ID_FREE_BUFFER, SANE_ACTION_SET_VALUE, &buf, nullptr);
}
COM_API_IMPLEMENT(scanner, int, get_fixed_ids(bool(* cb)(uint32_t id, void* param), void* param))
{
for (auto& v : sane_ids_)
{
if (!cb(v.first, param))
break;
}
return 0;
}
// SANE options ID ...
SANE_OPTION_ID_IMPLEMENT(color_correction)
SANE_OPTION_ID_IMPLEMENT(fold_type)
SANE_OPTION_ID_IMPLEMENT(is_multiout)
SANE_OPTION_ID_IMPLEMENT(multiout_type)
SANE_OPTION_ID_IMPLEMENT(color_mode)
SANE_OPTION_ID_IMPLEMENT(erase_color)
SANE_OPTION_ID_IMPLEMENT(erase_multiout_red)
SANE_OPTION_ID_IMPLEMENT(erase_paper_red)
SANE_OPTION_ID_IMPLEMENT(is_erase_background)
SANE_OPTION_ID_IMPLEMENT(background_color_range)
SANE_OPTION_ID_IMPLEMENT(sharpen)
SANE_OPTION_ID_IMPLEMENT(erase_morr)
SANE_OPTION_ID_IMPLEMENT(erase_grids)
SANE_OPTION_ID_IMPLEMENT(error_extend)
SANE_OPTION_ID_IMPLEMENT(is_noise_modify)
SANE_OPTION_ID_IMPLEMENT(noise_threshold)
SANE_OPTION_ID_IMPLEMENT(paper)
SANE_OPTION_ID_IMPLEMENT(is_custom_area)
SANE_OPTION_ID_IMPLEMENT(curstom_area_l)
SANE_OPTION_ID_IMPLEMENT(curstom_area_r)
SANE_OPTION_ID_IMPLEMENT(curstom_area_t)
SANE_OPTION_ID_IMPLEMENT(curstom_area_b)
SANE_OPTION_ID_IMPLEMENT(is_size_check)
SANE_OPTION_ID_IMPLEMENT(page)
SANE_OPTION_ID_IMPLEMENT(blank_page_threshold)
SANE_OPTION_ID_IMPLEMENT(resolution)
SANE_OPTION_ID_IMPLEMENT(image_quality)
SANE_OPTION_ID_IMPLEMENT(is_swap)
SANE_OPTION_ID_IMPLEMENT(is_split)
SANE_OPTION_ID_IMPLEMENT(is_auto_deskew)
SANE_OPTION_ID_IMPLEMENT(is_custom_gamma)
SANE_OPTION_ID_IMPLEMENT(bright)
SANE_OPTION_ID_IMPLEMENT(contrast)
SANE_OPTION_ID_IMPLEMENT(gamma)
SANE_OPTION_ID_IMPLEMENT(is_erase_black_frame)
SANE_OPTION_ID_IMPLEMENT(deep_sample)
SANE_OPTION_ID_IMPLEMENT(threshold)
SANE_OPTION_ID_IMPLEMENT(anti_noise)
SANE_OPTION_ID_IMPLEMENT(margin)
SANE_OPTION_ID_IMPLEMENT(fill_background)
SANE_OPTION_ID_IMPLEMENT(is_anti_permeate)
SANE_OPTION_ID_IMPLEMENT(anti_permeate_level)
SANE_OPTION_ID_IMPLEMENT(is_erase_hole)
SANE_OPTION_ID_IMPLEMENT(search_hole_range)
SANE_OPTION_ID_IMPLEMENT(is_filling_color)
SANE_OPTION_ID_IMPLEMENT(is_ultrasonic_check)
SANE_OPTION_ID_IMPLEMENT(is_check_staple)
SANE_OPTION_ID_IMPLEMENT(scan_mode)
SANE_OPTION_ID_IMPLEMENT(scan_count)
SANE_OPTION_ID_IMPLEMENT(text_direction)
SANE_OPTION_ID_IMPLEMENT(is_rotate_bkg180)
SANE_OPTION_ID_IMPLEMENT(is_check_dogear)
SANE_OPTION_ID_IMPLEMENT(dogear_size)
SANE_OPTION_ID_IMPLEMENT(is_check_skew)
SANE_OPTION_ID_IMPLEMENT(skew_range)
SANE_OPTION_ID_IMPLEMENT(black_white_threshold)
SANE_OPTION_ID_IMPLEMENT(is_photo_mode)
SANE_OPTION_ID_IMPLEMENT(double_feed_handle)
SANE_OPTION_ID_IMPLEMENT(scan_when_paper_on)
SANE_OPTION_ID_IMPLEMENT(feed_strength)
SANE_OPTION_ID_IMPLEMENT(power_scheme)
SANE_OPTION_ID_IMPLEMENT(is_auto_strength)
SANE_OPTION_ID_IMPLEMENT(feed_strength_value)
SANE_OPTION_ID_IMPLEMENT(is_reverse_bw)
SANE_OPTION_ID_IMPLEMENT(is_erase_hole_l)
SANE_OPTION_ID_IMPLEMENT(search_hole_range_l)
SANE_OPTION_ID_IMPLEMENT(is_erase_hole_r)
SANE_OPTION_ID_IMPLEMENT(search_hole_range_r)
SANE_OPTION_ID_IMPLEMENT(is_erase_hole_t)
SANE_OPTION_ID_IMPLEMENT(search_hole_range_t)
SANE_OPTION_ID_IMPLEMENT(is_erase_hole_b)
SANE_OPTION_ID_IMPLEMENT(search_hole_range_b)
SANE_OPTION_ID_IMPLEMENT(fold_direction)
// SANE-ex option ID:
SANE_OPTION_ID_IMPLEMENT(ex_multiout_type)
SANE_OPTION_ID_IMPLEMENT(ex_auto_color_type)
SANE_OPTION_ID_IMPLEMENT(ex_color_mode)
SANE_OPTION_ID_IMPLEMENT(ex_sharpen)
SANE_OPTION_ID_IMPLEMENT(ex_paper)
SANE_OPTION_ID_IMPLEMENT(ex_paper_lateral)
SANE_OPTION_ID_IMPLEMENT(ex_auto_paper_size)
SANE_OPTION_ID_IMPLEMENT(ex_is_paper_auto_crop)
SANE_OPTION_ID_IMPLEMENT(ex_text_direction)
SANE_OPTION_ID_IMPLEMENT(ex_duplex)
SANE_OPTION_ID_IMPLEMENT(ex_fill_background)
SANE_OPTION_ID_IMPLEMENT(ex_discard_blank_page)
SANE_OPTION_ID_IMPLEMENT(ex_discard_blank_receipt)
SANE_OPTION_ID_IMPLEMENT(ex_is_page_fold)
SANE_OPTION_ID_IMPLEMENT(ex_color_filter)
SANE_OPTION_ID_IMPLEMENT(ex_color_enhance)
SANE_OPTION_ID_IMPLEMENT(ex_final_compression)
SANE_OPTION_ID_IMPLEMENT(ex_final_format)
SANE_OPTION_ID_IMPLEMENT(ex_serial)
SANE_OPTION_ID_IMPLEMENT(ex_to_be_scan)
SANE_OPTION_ID_IMPLEMENT(ex_scan_with_hole)
SANE_OPTION_ID_IMPLEMENT(ex_device_code)
SANE_OPTION_ID_IMPLEMENT(ex_power)
SANE_OPTION_ID_IMPLEMENT(ex_hardware_version)
SANE_OPTION_ID_IMPLEMENT(ex_ip)
COM_API_IMPLEMENT(scanner, void, twain_set_transfer(twain_xfer xfer))
{
xfer_ = xfer;
}
COM_API_IMPLEMENT(scanner, void, twain_set_compression(SANE_CompressionType compression, void* detail))
{
img_fmt_.compress.compression = compression;
img_fmt_.compress.detail = detail;
}
COM_API_IMPLEMENT(scanner, int, twain_get_config(char* buf, size_t* len))
{
return SCANNER_ERR_DEVICE_NOT_SUPPORT;
}
COM_API_IMPLEMENT(scanner, int, twain_set_config(char* buf, size_t len))
{
return SCANNER_ERR_DEVICE_NOT_SUPPORT;
}
// ui ...
COM_API_IMPLEMENT(scanner, bool, ui_show_main(HWND parent))
{
return false;
}
COM_API_IMPLEMENT(scanner, bool, ui_show_setting(HWND parent, bool with_scan, bool indicator))
{
printf("ui_show_setting(%p, %s, %s), api = %p\n", parent, with_scan ? "with scan" : "only ui", indicator ? "has indicator" : "no indicator", callback::show_setting_ui);
is_show_ui_ = with_scan;
is_show_setting_ = true;
events_.clear();
if (callback::show_setting_ui)
{
if (with_scan)
{
images_.clear();
scan_msg_ = "OK";
scan_err_ = false;
}
auto ui = [this](ui_result res)
{
int uev = SANE_EVENT_SCAN_FINISHED;
switch (res)
{
case UI_RESULT_FAILED:
break;
case UI_RESULT_OK:
break;
case UI_RESULT_CLOSE_NORMAL:
is_show_ui_ = false;
uev = SANE_EVENT_UI_CLOSE_NORMAL;
on_ui_event(uev, (void*)uev);
break;
case UI_RESULT_CLOSE_CANCEL:
is_show_ui_ = false;
uev = SANE_EVENT_UI_CLOSE_CANCEL;
on_ui_event(uev, (void*)uev);
break;
case UI_RESULT_CLOSE_SETTING:
is_show_setting_ = false;
uev = SANE_EVENT_UI_CLOSE_SETTING;
on_ui_event(uev, (void*)uev);
break;
case UI_RESULT_START_SCAN:
on_ui_event(SANE_EVENT_UI_SCAN_COMMAND, nullptr);
break;
default:
break;
}
};
int res = callback::show_setting_ui(handle_, parent, &sane_api_, scanner_name_.c_str(), with_scan, ui);
//if (res == ui_result::UI_RESULT_CLOSE_NORMAL)
//{
// int ev = SANE_EVENT_UI_CLOSE_NORMAL;
// on_ui_event(ev, (void*)ev);
//}
//else if (res == ui_result::UI_RESULT_START_SCAN)
//{
// //callback::show_progress_ui(parent, ui_process, &ui_notify);
// //start();
// on_ui_event(SANE_EVENT_UI_SCAN_COMMAND, nullptr);
//}
//else if (res == UI_RESULT_CLOSE_CANCEL)
//{
// int ev = SANE_EVENT_UI_CLOSE_CANCEL;
// on_ui_event(ev, (void*)ev);
//}
//on_ui_event(ev, nullptr);
}
return true;
}
COM_API_IMPLEMENT(scanner, bool, ui_show_progress(HWND parent, bool bIndicator))
{
bIndicator = false;
is_bIndicator = bIndicator;
ui_notify = std::function<void(int, void*, int)>();
auto ui_process = [this](ui_result res)
{
int uev = SANE_EVENT_SCAN_FINISHED;
switch (res)
{
case UI_RESULT_CLOSE_NORMAL:
uev = SANE_EVENT_UI_CLOSE_NORMAL;
//if (!is_show_ui_)
{
on_ui_event(uev, (void*)uev);
}
break;
case UI_RESULT_CLOSE_CANCEL:
uev = SANE_EVENT_UI_CLOSE_CANCEL;
//if (!is_show_ui_)
{
on_ui_event(uev, (void*)uev);
}
//else
// stop();
break;
default:
break;
}
};
if (callback::show_progress_ui && bIndicator)
{
callback::show_progress_ui(parent, ui_process,&ui_notify);
}
return true;
}
COM_API_IMPLEMENT(scanner, void, ui_hide(void))
{
if (callback::close_ui)
callback::close_ui(UI_INDICATOR | UI_SETTING | UI_MSG_BOX);
is_show_setting_ = false;
ui_notify = std::function<void(int, void*, int)>();
}
COM_API_IMPLEMENT(scanner, bool, ui_is_ok(void))
{
return callback::show_setting_ui != nullptr;
}
// called from device-layer ...
int scanner::handle_device_event(int ev_code, void* data, unsigned int* len)
{
if (ev_code == SANE_EVENT_WORKING)
{
img_ind_ = 0;
if (callback::show_progress_ui && is_bIndicator)
ui_notify(ev_code, data, *len);
on_ui_event(ev_code, (void*)ev_code);
utils::log_info("Scanning ...\r\n", LOG_LEVEL_DEBUG);
}
else if (ev_code == SANE_EVENT_IMAGE_OK)
{
SANE_Image* simg = (SANE_Image*)data;
scanned_img* img = nullptr;
char name[40] = { 0 };
sprintf_s(name, _countof(name) - 1, "img_%05u.bmp", ++img_ind_);
img = new scanned_img(handle_, simg->header, simg->data, simg->bytes, simg->flag.dpi, (tmp_path_ + name).c_str(), xfer_, &img_fmt_);
if (img->bytes() /*>= simg->bytes*/)
{
size_t bytes = 0;
int times = 0;
images_.count(&bytes);
img->set_image_status((SANE_Image_Statu)simg->flag.statu);
bytes /= 1024 * 1024;
while (bytes > max_img_mem_ && !user_cancel_ && times++ < 20) // memory control
{
std::this_thread::sleep_for(std::chrono::milliseconds(5));
images_.count(&bytes);
bytes /= 1024 * 1024;
if (times == 1)
utils::log_info("Memory usage upto limit! wait up to 100 ms ...\r\n", LOG_LEVEL_DEBUG);
}
images_.save(img, img->bytes());
}
else
{
img->release();
}
if (ui_notify)
ui_notify(ev_code, data, img_ind_);
{
char msg[128] = { 0 };
sprintf_s(msg, _countof(msg) - 1, "New image(%u) received with %u bytes\r\n", img_ind_, simg->bytes);
utils::log_info(msg, (log_level)1);
}
}
else if (ev_code == SANE_EVENT_USB_DATA_RECEIVED)
{
//if (indicator_.get())
// indicator_->notify_data_arrived(false);
//else if (ui_notify)
// ui_notify(ev_code, data, 0);
}
else if (ev_code == SANE_EVENT_SCAN_FINISHED)
{
err_ = *len;
if (ui_notify)
ui_notify(ev_code, data, *len);
else
{
if (*len)
{
if (callback::show_messagebox_ui)
{
callback::show_messagebox_ui(app_wnd_, ev_code, (void*)data, 0);
}
}
on_ui_event(ev_code, (void*)ev_code);
}
// is_scanning_ = false;
{
char msg[128] = { 0 };
sprintf_s(msg, _countof(msg) - 1, "Scan finished with error: %u\r\n", *len);
utils::log_info(msg, (log_level)1);
}
}
//else if (ev_code == SANE_EVENT_ERROR) // 屏蔽,在停止扫描时展示信息 - 2023-05-30
//{
// if (callback::show_messagebox_ui && *len)
// {
// callback::show_messagebox_ui(app_wnd_, ev_code, (void*)data, 0);
// }
// else if (*len) //错误弹出
// {
// std::string msg(local_trans::a2u((char*)data, CP_UTF8));
// if (!IsWindow(app_wnd_))
// callback::bring_message_box_topmost(local_trans::lang_trans_between_hz936(CONST_STRING_ERROR).c_str());
// MessageBoxW(app_wnd_, msg.c_str(), local_trans::lang_trans_between_hz936(CONST_STRING_ERROR).c_str(), MB_OK);
// }
//// on_ui_event(ev_code, (void*)ev_code);
//}
return 0;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// exports
extern "C"
{
int initialize_sane(void* reserve)
{
SANE_Int ver = 0;
utils::init_log(LOG_TYPE_FILE, LOG_LEVEL_ALL, "_twain");
sane_helper_ = new sane_helper();
if(!sane_helper_->load_sane((std::string(OEM_SHORT_NAME_E) + "sane").c_str()))
{
printf("Load sane dll failed.\n");
delete sane_helper_;
sane_helper_ = nullptr;
return SCANNER_ERR_NOT_OPEN;
}
callback::init_ui();
if (sane_helper_->invoke_sane_init_ex(&ver, callback::sane_event_callback, nullptr) == SANE_STATUS_GOOD)
{
std::string lang(utils::get_module_full_path(SCANNER_DRIVER_PART_NAME));
size_t pos = lang.rfind(PATH_SEPARATOR[0]);
if(pos++ == std::string::npos)
pos = 0;
lang.erase(pos);
lang += LANG_DLL_NAME;
pos = local_trans::load_lang_pak(lang.c_str());
utils::to_log(LOG_LEVEL_WARNING, "load language component (%s) = %s\n", lang.c_str(), strerror(pos));
return SANE_STATUS_GOOD;
}
else
return SCANNER_ERR_LANG_PAK_LOST;
}
int open_scanner(SCANNERID scanner_id, ISaneInvoker** invoker, bool last_try)
{
if (!invoker)
return SCANNER_ERR_INVALID_PARAMETER;
if (!is_scanner_online(scanner_id))
{
if (last_try)
{
HWND parent = callback::find_main_wnd();
std::string msg(local_trans::lang_trans_between_hz936("\xE6\xB2\xA1\xE6\x9C\x89\xE6\x89\xBE\xE5\x88\xB0\xE6\x89\xAB\xE6\x8F\x8F\xE4\xBB\xAA\xEF\xBC\x81", true, nullptr)),
title(local_trans::lang_trans_between_hz936("\xE9\x94\x99\xE8\xAF\xAF", true, nullptr));
if (callback::show_messagebox_ui)
callback::show_messagebox_ui(parent, 0, &msg[0], SCANNER_ERR_DEVICE_NOT_FOUND);
}
return SCANNER_ERR_DEVICE_NOT_FOUND;
}
scanner* scn = new scanner(scanner_id);
if (scn->last_error() == SCANNER_ERR_OK)
{
*invoker = dynamic_cast<ISaneInvoker*>(scn);
return 0;
}
else
{
int ret = scn->last_error();
scn->release();
*invoker = nullptr;
if (last_try)
{
HWND parent = callback::find_main_wnd();
std::string msg(local_trans::lang_trans_between_hz936("\xE6\x89\x93\xE5\xBC\x80\xE6\x89\xAB\xE6\x8F\x8F\xE4\xBB\xAA\xE5\xA4\xB1\xE8\xB4\xA5\xE3\x80\x82", true, nullptr)),
title(local_trans::lang_trans_between_hz936("\xE9\x94\x99\xE8\xAF\xAF", true, nullptr));
if (callback::show_messagebox_ui)
callback::show_messagebox_ui(parent, 0, &msg[0], ret);
}
return ret;
}
}
bool is_scanner_online(SCANNERID scanner_id)
{
std::vector<std::string> que;
scanner::get_scanner_name(scanner_id, que);
return !que.empty();
}
int uninitialize_sane(void* reserve)
{
if(sane_helper_ && sane_helper_->invoke_sane_exit)
sane_helper_->invoke_sane_exit();
callback::unint_ui();
if(sane_helper_)
delete sane_helper_;
sane_helper_ = nullptr;
return 0;
}
ISaneInvoker* sane_wapper_open_scanner(SCANNERID scanner_id, int* err, bool last_try)
{
if (!sane_helper_)
{
int rv = initialize_sane(nullptr);
if (rv)
{
if (err)
*err = rv;
return nullptr;
}
}
ISaneInvoker* sane = nullptr;
int ret = open_scanner(scanner_id, &sane, last_try);
if (err)
*err = ret;
return sane;
}
}