code_device/twain/ds/scanner.cpp

1524 lines
41 KiB
C++
Raw Normal View History

2023-07-10 07:28:45 +00:00

#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"
2023-07-10 07:28:45 +00:00
#define START_SCAN_IN_THREAD
#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);
2023-07-15 02:24:25 +00:00
else if(code != SANE_EVENT_SCANNER_CLOSED)
2023-07-10 07:28:45 +00:00
{
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);
2023-07-10 07:28:45 +00:00
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);
}
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";
}
2023-07-10 07:28:45 +00:00
// 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(MODULE_NAME_SCANNER));
2023-07-10 07:28:45 +00:00
size_t pos = root.rfind(PATH_SEPARATOR[0]);
2023-07-18 06:13:43 +00:00
if(pos != std::string::npos)
2023-07-10 07:28:45 +00:00
root.erase(pos);
2023-07-18 06:13:43 +00:00
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;
if (hui)
FreeLibrary(hui);
// hui = LoadLibraryExA(root.c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH);
2023-08-22 08:47:28 +00:00
hui = utils::load_dll(root.c_str(), LOAD_WITH_ALTERED_SEARCH_PATH);
2023-07-10 07:28:45 +00:00
if (!hui)
{
std::string info("Load '" + root);
info += "' failed: " + std::string(strerror(GetLastError())) + "\r\n";
2023-07-21 08:56:28 +00:00
utils::log_info(info.c_str(), LOG_LEVEL_DEBUG);
2023-07-10 07:28:45 +00:00
}
else
{
#define GET_API(api) \
proc = (FARPROC*)&api; \
2023-07-11 03:20:11 +00:00
*proc = GetProcAddress(hui, #api); \
if(*proc == nullptr) \
utils::to_log(LOG_LEVEL_FATAL, "TWAINUI - function '%s' not found!\n", #api);
2023-07-10 07:28:45 +00:00
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;
}
}
static SANE_Bool get_invisible_fix_id(int id, void* param)
{
((std::vector<int>*)param)->push_back(id);
return SANE_TRUE;
}
2023-07-10 07:28:45 +00:00
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// class scanner
scanner::scanner(const char* model) : handle_(nullptr), model_(model), ex_id_(EXTENSION_ID_BASE), prev_start_result_(SCANNER_ERR_NOT_START)
, dpi_(200), tmp_path_(""), img_ind_(0), err_(SCANNER_ERR_NOT_OPEN)
2023-07-10 07:28:45 +00:00
, 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;
}
}
scanner::~scanner()
{
close();
if (thread_starting_.get() && thread_starting_->joinable())
thread_starting_->join();
thread_starting_.reset();
}
void scanner::get_scanner_name(const char* model, std::vector<std::string>& names)
2023-07-10 07:28:45 +00:00
{
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]->model, model) == 0)
names.push_back(devs[count]->name);
2023-07-10 07:28:45 +00:00
}
}
}
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;
}
double scanner::to_double(SANE_Fixed v)
2023-07-10 07:28:45 +00:00
{
return (double)SANE_UNFIX(v);
2023-07-10 07:28:45 +00:00
}
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;
}
int scanner::transfer_id(int id)
{
if (id > SANE_OPT_ID_BASE)
{
if (sane_opts_.count((sane_option_id)id) == 0)
2023-07-10 07:28:45 +00:00
id = -1;
else
id = sane_opts_[(sane_option_id)id].opt_sn;
2023-07-10 07:28:45 +00:00
}
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);
}
2023-07-10 07:28:45 +00:00
}
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(model_.c_str(), que);
2023-07-10 07:28:45 +00:00
init_options_id();
2023-07-10 07:28:45 +00:00
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;
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 = 0, fix_id = 0;
int ret = SCANNER_ERR_OK;
2023-07-10 07:28:45 +00:00
const SANE_Option_Descriptor* desc = nullptr;
sane_opts_.clear();
while ((desc = sane_helper_->invoke_sane_get_option_descriptor(handle_, ++op_id)))
{
fix_id = -1;
if (sane_helper_->invoke_sane_control_option(handle_, op_id, (SANE_Action)SANE_ACTION_GET_FIX_ID, &fix_id, nullptr)
== SANE_STATUS_GOOD)
2023-07-10 07:28:45 +00:00
{
SANEOPT opt;
opt.opt_sn = op_id;
if (desc->type == SANE_TYPE_BOOL) // bool to enumeration constraint
opt.limit = VAL_LIMIT_ENUM;
else
opt.limit = scanner::from_sane_constraint(desc->constraint_type);
opt.type = scanner::from_sane_type(desc->type);
opt.size = desc->size;
opt.cap = desc->cap;
sane_opts_[fix_id] = opt;
2023-07-10 07:28:45 +00:00
}
}
// invisible option in SANE
{
std::vector<int> inv;
struct _fix_id_cb fcb;
fcb.cb = callback::get_invisible_fix_id;
fcb.param = &inv;
sane_helper_->invoke_sane_control_option(handle_, SANE_OPT_ID_BASE, (SANE_Action)SANE_ACTION_ENUM_INVISIBLE_FIX_ID, &fcb, nullptr);
for (auto& v : inv)
{
desc = sane_helper_->invoke_sane_get_option_descriptor(handle_, v);
if (desc)
{
SANEOPT opt;
opt.opt_sn = v;
if (desc->type == SANE_TYPE_BOOL) // bool to enumeration constraint
opt.limit = VAL_LIMIT_ENUM;
else
opt.limit = scanner::from_sane_constraint(desc->constraint_type);
opt.type = scanner::from_sane_type(desc->type);
opt.size = desc->size;
opt.cap = desc->cap;
sane_opts_[v] = opt;
}
}
}
2023-07-10 07:28:45 +00:00
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;
}
bool scanner::get_option_value_with_parent(int sn, set_opt_value setv, void* param) // return true if handled
{
bool handled = true;
if (sn == SANE_OPT_ID_SCAN_COUNT)
2023-07-10 07:28:45 +00:00
{
const SANE_Option_Descriptor* parent = sane_helper_->invoke_sane_get_option_descriptor(handle_, SANE_OPT_ID_SCAN_MODE);
char* buf = get_opt_value(SANE_OPT_ID_SCAN_MODE, parent->size, false);
2023-07-10 07:28:45 +00:00
if (buf)
2023-07-10 07:28:45 +00:00
{
handled = strcmp(local_trans::lang_trans_between_hz936(OPTION_VALUE_SMZS_LXSM, true, nullptr), buf) == 0;
if (handled)
{
int count = -1;
value_role role = VAL_ROLE_CURRENT;
sane_helper_->invoke_sane_control_option(handle_, SANE_OPT_ID_SCAN_MODE, (SANE_Action)SANE_ACTION_GET_DEFAULT_VALUE, buf, nullptr);
if (strcmp(local_trans::lang_trans_between_hz936(OPTION_VALUE_SMZS_LXSM, true, nullptr), buf) == 0)
role = value_role(role | VAL_ROLE_DEFAULT);
2023-07-10 07:28:45 +00:00
setv(&count, role, VAL_LIMIT_NONE, param);
}
delete[] buf;
2023-07-10 07:28:45 +00:00
}
}
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 == SANE_OPT_ID_SCAN_COUNT)
2023-07-10 07:28:45 +00:00
{
const SANE_Option_Descriptor* parent = sane_helper_->invoke_sane_get_option_descriptor(handle_, SANE_OPT_ID_SCAN_MODE);
char* val = get_opt_value(SANE_OPT_ID_SCAN_MODE, parent->size, false);
2023-07-10 07:28:45 +00:00
SANE_Int after = 0;
if(val)
{
if (strcmp(local_trans::lang_trans_between_hz936(OPTION_VALUE_SMZS_LXSM, true, nullptr), val) == 0)
2023-07-10 07:28:45 +00:00
{
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_, SANE_OPT_ID_SCAN_MODE, SANE_ACTION_SET_VALUE, val, &after);
2023-07-10 07:28:45 +00:00
}
}
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_, SANE_OPT_ID_SCAN_MODE, SANE_ACTION_SET_VALUE, val, &after);
2023-07-10 07:28:45 +00:00
}
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());
strcpy(buf, (char*)data);
2023-07-10 07:28:45 +00:00
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::thread_start(void)
{
SANE_Int dpi = 0;
int ret = 0;
sane_helper_->invoke_sane_control_option(handle_, SANE_OPT_ID_RESOLUTION, SANE_ACTION_GET_VALUE, &dpi, nullptr);
dpi_ = dpi;
utils::to_log(LOG_LEVEL_DEBUG, "start scanning with DPI %d\r\n", dpi_);
ret = sane_helper_->invoke_sane_start(handle_);
2023-07-10 07:28:45 +00:00
// 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;
}
// IRef
COM_API_IMPLEMENT(scanner, int32_t, add_ref(void))
2023-07-10 07:28:45 +00:00
{
return refer::add_ref();
2023-07-10 07:28:45 +00:00
}
COM_API_IMPLEMENT(scanner, int32_t, release(void))
2023-07-10 07:28:45 +00:00
{
return refer::release();
}
2023-07-10 07:28:45 +00:00
// ISaneInvoker
COM_API_IMPLEMENT(scanner, int, start(void))
{
int ret = SANE_STATUS_GOOD;
2023-07-10 07:28:45 +00:00
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();
2023-07-10 07:28:45 +00:00
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
2023-07-10 07:28:45 +00:00
return ret;
}
COM_API_IMPLEMENT(scanner, int, stop(void))
2023-07-10 07:28:45 +00:00
{
user_cancel_ = true;
2023-07-10 07:28:45 +00:00
sane_helper_->invoke_sane_cancel(handle_);
2023-07-10 07:28:45 +00:00
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);
2023-07-10 07:28:45 +00:00
return count > 0;
2023-07-10 07:28:45 +00:00
}
COM_API_IMPLEMENT(scanner, int, get_scanned_images(DWORD milliseconds))
2023-07-10 07:28:45 +00:00
{
size_t count = images_.count();
DWORD elapse = 2;
2023-07-10 07:28:45 +00:00
is_ui_wait_img_ = true;
while (is_scanning_ && count == 0 && milliseconds)
2023-07-10 07:28:45 +00:00
{
// MSG msg = { 0 };
// if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE))
// {
// TranslateMessage(&msg);
// DispatchMessageW(&msg);
// }
// else
Sleep(elapse);
2023-07-10 07:28:45 +00:00
count = images_.count();
2023-07-10 07:28:45 +00:00
//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)
2023-07-10 07:28:45 +00:00
{
if (milliseconds <= elapse)
break;
2023-07-10 07:28:45 +00:00
milliseconds -= elapse;
2023-07-10 07:28:45 +00:00
}
}
is_ui_wait_img_ = false;
count = images_.count();
2023-07-10 07:28:45 +00:00
{
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;
2023-07-10 07:28:45 +00:00
}
COM_API_IMPLEMENT(scanner, IScanImg*, take_first_image(twain_xfer xfer))
2023-07-10 07:28:45 +00:00
{
scanned_img* img = images_.take(false);
2023-07-10 07:28:45 +00:00
if (img)
2023-07-10 07:28:45 +00:00
{
img->prepare_data_for_transfer(xfer);
img->add_ref();
2023-07-10 07:28:45 +00:00
utils::to_log(LOG_LEVEL_DEBUG, "Begin transferring image %d of %p\r\n", fetch_imgs_ + 1, img);
}
2023-07-10 07:28:45 +00:00
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, discard_first_image(void))
{
scanned_img* img = images_.take();
if (img)
{
img->release();
return true;
}
else
{
return false;
}
}
2023-07-10 07:28:45 +00:00
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);
2023-07-10 07:28:45 +00:00
return 0;
}
COM_API_IMPLEMENT(scanner, bool, get_option_info(int sn, value_type* type, value_limit* limit, int* bytes, bool* readonly))
2023-07-10 07:28:45 +00:00
{
if (sane_opts_.count(sn))
2023-07-10 07:28:45 +00:00
{
if (type)
*type = sane_opts_[sn].type;
2023-07-10 07:28:45 +00:00
if (limit)
*limit = sane_opts_[sn].limit;
2023-07-10 07:28:45 +00:00
if (bytes)
*bytes = sane_opts_[sn].size;
if (readonly)
*readonly = IS_CAP_READONLY(sane_opts_[sn].cap);
2023-07-10 07:28:45 +00:00
return true;
}
else
{
return false;
2023-07-10 07:28:45 +00:00
}
}
COM_API_IMPLEMENT(scanner, bool, get_value(int sn, set_opt_value setval, void* param))
{
const SANE_Option_Descriptor* desc = sane_helper_->invoke_sane_get_option_descriptor(handle_, sn);
if (desc)
utils::to_log(LOG_LEVEL_DEBUG, "get_value(0x%04X - %s) ...\r\n", sn, desc->name);
else
{
utils::to_log(LOG_LEVEL_WARNING, "get_value(0x%04X, ...), but the option is not found !!!\r\n", sn);
return SCANNER_ERR_DEVICE_NOT_SUPPORT;
}
if (get_option_value_with_parent(sn, setval, param))
return true;
2023-07-10 07:28:45 +00:00
char* init = get_opt_value(sn, desc->size, true),
* now = get_opt_value(sn, desc->size, false);
int ret = SANE_STATUS_GOOD;
2023-07-10 07:28:45 +00:00
if (desc->type == SANE_TYPE_BOOL)
{
bool val = false;
int role = VAL_ROLE_NONE;
2023-07-10 07:28:45 +00:00
if (*(SANE_Bool*)init == SANE_FALSE)
role |= VAL_ROLE_DEFAULT;
if (*(SANE_Bool*)now == SANE_FALSE)
role |= VAL_ROLE_CURRENT;
setval(&val, (value_role)role, VAL_LIMIT_ENUM, param);
2023-07-10 07:28:45 +00:00
val = true;
role = VAL_ROLE_NONE;
if (*(SANE_Bool*)init == SANE_TRUE)
role |= VAL_ROLE_DEFAULT;
if (*(SANE_Bool*)now == SANE_TRUE)
role |= VAL_ROLE_CURRENT;
setval(&val, (value_role)role, VAL_LIMIT_ENUM, param);
}
else if (desc->type == SANE_TYPE_INT)
2023-07-10 07:28:45 +00:00
{
do
2023-07-10 07:28:45 +00:00
{
if (desc->constraint_type == SANE_CONSTRAINT_RANGE)
2023-07-10 07:28:45 +00:00
{
set_value_range<SANE_Int, int>(*(SANE_Int*)now, *(SANE_Int*)init, desc->constraint.range->min, desc->constraint.range->max, desc->constraint.range->quant, setval, param, scanner::to_int);
2023-07-10 07:28:45 +00:00
}
else if (desc->constraint_type == SANE_CONSTRAINT_WORD_LIST)
2023-07-10 07:28:45 +00:00
{
const SANE_Word* v = desc->constraint.word_list;
for (int i = 0; i < v[0]; ++i)
2023-07-10 07:28:45 +00:00
{
value_role role = VAL_ROLE_NONE;
if (v[i + 1] == *(SANE_Int*)now)
role = value_role(role | VAL_ROLE_CURRENT);
if (v[i + 1] == *(SANE_Int*)init)
role = value_role(role | VAL_ROLE_DEFAULT);
int val = v[i + 1];
if (!setval(&val, role, VAL_LIMIT_ENUM, param))
break;
2023-07-10 07:28:45 +00:00
}
}
else
set_cur_and_def_value<int>(*(SANE_Int*)now, *(SANE_Int*)init, setval, param);
}while (0);
}
else if (desc->type == SANE_TYPE_FIXED)
{
do
2023-07-10 07:28:45 +00:00
{
if (desc->constraint_type == SANE_CONSTRAINT_RANGE)
2023-07-10 07:28:45 +00:00
{
set_value_range<SANE_Fixed, double>(*(SANE_Fixed*)now, *(SANE_Fixed*)init, desc->constraint.range->min, desc->constraint.range->max, desc->constraint.range->quant, setval, param, scanner::to_double);
}
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)
2023-07-10 07:28:45 +00:00
{
value_role role = VAL_ROLE_NONE;
if (v[i + 1] == *(SANE_Fixed*)now)
role = value_role(role | VAL_ROLE_CURRENT);
if (v[i + 1] == *(SANE_Fixed*)init)
role = value_role(role | VAL_ROLE_DEFAULT);
double val = SANE_UNFIX(v[i + 1]);
if (!setval(&val, role, VAL_LIMIT_ENUM, param))
break;
2023-07-10 07:28:45 +00:00
}
}
else
set_cur_and_def_value<double>(SANE_UNFIX(*(SANE_Int*)now), SANE_UNFIX(*(SANE_Int*)init), setval, param);
} while (0);
}
else if (desc->type == SANE_TYPE_STRING)
{
do
2023-07-10 07:28:45 +00:00
{
if (desc->constraint_type == SANE_CONSTRAINT_STRING_LIST)
2023-07-10 07:28:45 +00:00
{
for (int i = 0; desc->constraint.string_list[i]; ++i)
2023-07-10 07:28:45 +00:00
{
value_role role = VAL_ROLE_NONE;
if (strcmp(desc->constraint.string_list[i], now) == 0)
role = value_role(role | VAL_ROLE_CURRENT);
if (strcmp(desc->constraint.string_list[i], init) == 0)
role = value_role(role | VAL_ROLE_DEFAULT);
std::string val(desc->constraint.string_list[i]);
if (!setval(&val, role, VAL_LIMIT_ENUM, param))
break;
2023-07-10 07:28:45 +00:00
}
}
else
set_cur_and_def_value<std::string>(now, init, setval, param);
} while (0);
2023-07-10 07:28:45 +00:00
}
else
{
ret = SANE_STATUS_INVAL;
2023-07-10 07:28:45 +00:00
}
delete[] init;
delete[] now;
2023-07-10 07:28:45 +00:00
return ret == SANE_STATUS_GOOD;
}
COM_API_IMPLEMENT(scanner, bool, get_value(int sn, void* data, int* len))
{
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))
{
int ret = SANE_STATUS_INVAL;
SANE_Int after = 0;
const SANE_Option_Descriptor* desc = sane_helper_->invoke_sane_get_option_descriptor(handle_, sn);
if (desc)
utils::to_log(LOG_LEVEL_DEBUG, "set_value(0x%04X - %s, '%s') ...\r\n", sn, desc->name, callback::option_value_2_string(desc->type, val).c_str());
else
{
utils::to_log(LOG_LEVEL_WARNING, "set_value(0x%04X, ...), but the option is not found !!!\r\n", sn);
2023-07-10 07:28:45 +00:00
return SCANNER_ERR_DEVICE_NOT_SUPPORT;
}
2023-07-10 07:28:45 +00:00
twain_set_ = true;
if (!set_option_value_with_parent(sn, val, &ret))
ret = set_option_value(sn, desc->type, desc->size, val);
2023-07-10 07:28:45 +00:00
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, int, get_fixed_ids(SANE_Bool(* cb)(int id, void* param), void* param))
2023-07-10 07:28:45 +00:00
{
bool stopped = false;
for (auto& v : sane_opts_)
2023-07-10 07:28:45 +00:00
{
if (!cb(v.first, param))
{
stopped = true;
break;
}
2023-07-10 07:28:45 +00:00
}
return 0;
}
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_setting(HWND parent, bool with_scan, bool indicator))
{
bool ret = false;
utils::to_log(LOG_LEVEL_ALL, "ui_show_setting(%p, '%s', '%s'), api = %p\r\n", parent, with_scan ? "with scan" : "only ui", indicator ? "has indicator" : "no indicator", callback::show_setting_ui);
2023-07-10 07:28:45 +00:00
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;
}
};
ret = callback::show_setting_ui(handle_, parent, &sane_api_, scanner_name_.c_str(), with_scan, ui) == ui_result::UI_RESULT_OK;
2023-07-10 07:28:45 +00:00
}
return ret;
2023-07-10 07:28:45 +00:00
}
COM_API_IMPLEMENT(scanner, bool, ui_show_progress(HWND parent, bool bIndicator))
{
bIndicator = false;
2023-07-10 07:28:45 +00:00
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))
{
2023-07-18 06:13:43 +00:00
return callback::show_setting_ui != nullptr;
2023-07-10 07:28:45 +00:00
}
// 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);
2023-07-10 07:28:45 +00:00
}
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);
2023-07-10 07:28:45 +00:00
}
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);
2023-07-10 07:28:45 +00:00
}
}
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);
2023-07-10 07:28:45 +00:00
}
}
//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(OEM_SHORT_NAME_E))
2023-07-10 07:28:45 +00:00
{
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(MODULE_NAME_SCANNER));
2023-07-10 07:28:45 +00:00
size_t pos = lang.rfind(PATH_SEPARATOR[0]);
if(pos++ == std::string::npos)
pos = 0;
lang.erase(pos);
lang += MODULE_NAME_LANG;
2023-07-10 07:28:45 +00:00
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;
}
bool is_scanner_online(const char* model)
2023-07-10 07:28:45 +00:00
{
std::vector<std::string> que;
2023-07-10 07:28:45 +00:00
scanner::get_scanner_name(model, que);
return !que.empty();
}
ISaneInvoker* open_scanner(const char* model, int* err)
{
if (!sane_helper_)
2023-07-10 07:28:45 +00:00
{
int rv = initialize_sane(nullptr);
if (rv)
2023-07-10 07:28:45 +00:00
{
if (err)
*err = rv;
2023-07-10 07:28:45 +00:00
return nullptr;
2023-07-10 07:28:45 +00:00
}
}
scanner* s = new scanner(model);
int ret = s->open(), cnt = 0;
2023-07-10 07:28:45 +00:00
while (ret)
2023-07-10 07:28:45 +00:00
{
if (ret == SCANNER_ERR_OPENED_BY_OTHER_PROCESS)
break;
std::this_thread::sleep_for(std::chrono::milliseconds(5));
if (cnt++ > 100)
break;
ret = s->open();
}
if (ret && ret != SCANNER_ERR_OPENED_BY_OTHER_PROCESS) // error SCANNER_ERR_OPENED_BY_OTHER_PROCESS has tipped already
{
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);
2023-07-10 07:28:45 +00:00
}
if (err)
*err = ret;
2023-07-10 07:28:45 +00:00
return s ? dynamic_cast<ISaneInvoker*>(s) : nullptr;
2023-07-10 07:28:45 +00:00
}
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;
utils::uninit();
2023-07-10 07:28:45 +00:00
return 0;
}
}