code_twain/sane/scanner.cpp

3815 lines
113 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 <Windows.h>
#include <Shlwapi.h> // for PathFileExistsW
#include "../sdk/hginclude/huagaoxxx_warraper_ex.h"
#include <sane/sane_option_definitions.h>
#include "../../code_device/hgsane/sane_hg_mdw.h"
#include "sane_option_trans.h"
#include <chrono>
#include <mutex>
#include "DlgSetting.h"
#include "gb_json.h"
#include "../../sdk/include/lang/app_language.h"
#include <functional>
#include "DlgIndicator.h"
#include <twainui/twainui.h>
#pragma comment(lib, "Shlwapi.lib")
#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); \
}
#ifdef EXPORT_SANE_API
__declspec(dllexport)
#else
__declspec(dllimport)
#endif
void __stdcall log_info(const wchar_t* info, int level);
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// callback
extern "C"
{
extern SANE_Status inner_sane_init(SANE_Int* version_code, SANE_Auth_Callback authorize);
extern void inner_sane_exit(void);
extern SANE_Status inner_sane_get_devices(const SANE_Device*** device_list, SANE_Bool local_only);
extern SANE_Status inner_sane_open(SANE_String_Const devicename, SANE_Handle* handle);
extern void inner_sane_close(SANE_Handle handle);
extern const SANE_Option_Descriptor* inner_sane_get_option_descriptor(SANE_Handle handle, SANE_Int option);
extern SANE_Status inner_sane_control_option(SANE_Handle handle, SANE_Int option, SANE_Action action, void* value, SANE_Int* info);
extern SANE_Status inner_sane_get_parameters(SANE_Handle handle, SANE_Parameters* params);
extern SANE_Status inner_sane_start(SANE_Handle handle);
extern SANE_Status inner_sane_read(SANE_Handle handle, SANE_Byte* data, SANE_Int max_length, SANE_Int* length);
extern void inner_sane_cancel(SANE_Handle handle);
extern SANE_Status inner_sane_set_io_mode(SANE_Handle handle, SANE_Bool non_blocking);
extern SANE_Status inner_sane_get_select_fd(SANE_Handle handle, SANE_Int* fd);
extern SANE_String_Const inner_sane_strstatus(SANE_Status status);
extern SANE_Status inner_sane_init_ex(SANE_Int* version_code, sane_callback cb, void* param);
extern SANE_Status inner_sane_io_control(SANE_Handle h, unsigned long code, void* data, unsigned* len);
extern const char* inner_sane_err_desc(SANE_Status err);
}
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 // 閺佺増宓侀梹鍨閿涘牆鐡ч懞鍌︾礆閿涘本鍨ㄩ懓鍗恦ent_data閻ㄥ嫮绱﹂崘鎻掑隘闂€鍨閿涘矁顕涚紒鍡氼嚞閻娴夋惔鏃傛畱娴滃娆㈡禒锝囩垳
, void* param // 閻劍鍩涢懛顏勭暰娑斿鏆熼幑顕嗙礉娑撳氦鐨熼悽鈺痑ne_init_ex娴肩姴鍙嗛弮鍓佹畱娣囨繃瀵旀稉鈧懛?
) // 鏉╂柨娲栭崐闂寸贩娑撳秴鎮撻惃鍕皑娴犳湹鍞惍浣解偓灞界暰閿涘矂鈧艾鐖舵稉琛♀偓?閳?
{
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
{
wchar_t msg[218] = { 0 };
swprintf_s(msg, _countof(msg) - 1, L"Lost device(0x%08X) when event(%u) occurs!\r\n", hdev, code);
log_info(msg, 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}
, {SANE_STD_OPT_NAME_DISCARDBLANK , OPTION_TITLE_TGKBY}
},
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"} // 姒涙垹娅ч崶鎯у剼閸欏秷澹婃潏鎾冲毉閿涘牊顒滅敮鎼侇杹閼硅弓璐熼敍?-姒涙垼澹婇敍?-閻у€熷閿?
, {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* __stdcall 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 BOOL CALLBACK main_wnd(HWND hwnd, LPARAM param)
{
DWORD pid = 0;
GetWindowThreadProcessId(hwnd, &pid);
if (pid != GetCurrentProcessId())
return TRUE;
if (((void**)param)[1] == NULL)
{
HWND parent = GetParent(hwnd);
while (IsWindow(parent))
{
hwnd = parent;
parent = GetParent(hwnd);
}
parent = hwnd;
if (!IsWindowVisible(parent))
return TRUE;
RECT r = { 0 };
GetWindowRect(parent, &r);
if (RECT_H(r) > 50 && RECT_W(r) > 50)
{
*(HWND*)param = parent;
return FALSE;
}
}
else
{
wchar_t val[128] = { 0 };
GetClassNameW(hwnd, val, _countof(val) - 1);
if (wcscmp(val, L"#32770"))
return TRUE;
GetWindowTextW(hwnd, val, _countof(val) - 1);
if (*((std::wstring**)param)[1] == val)
{
*(HWND*)param = hwnd;
return FALSE;
}
}
return TRUE;
}
static HWND find_main_wnd(void)
{
HWND wnd[2] = { NULL };
EnumWindows(main_wnd, (LPARAM)wnd);
return wnd[0];
}
static DWORD WINAPI btm(LPVOID para)
{
std::wstring* title[2] = { NULL, (std::wstring*)para };
Sleep(100);
EnumWindows(main_wnd, (LPARAM)title);
if (IsWindow((HWND)title[0]))
{
RECT r = { 0 };
GetWindowRect((HWND)title[0], &r);
SetWindowPos((HWND)title[0], HWND_TOPMOST, r.left, r.top, RECT_W(r), RECT_H(r), SWP_SHOWWINDOW | SWP_NOSENDCHANGING);
SetFocus((HWND)title[0]);
}
delete title[1];
return 0;
}
static void bring_message_box_topmost(const wchar_t* title)
{
DWORD id = 0;
std::wstring* t(new std::wstring(title));
HANDLE h = CreateThread(NULL, 0, btm, t, 0, &id);
if (h)
CloseHandle(h);
else
delete t;
}
static const char* __stdcall language_trans(const char* in, bool from_hz, void* param)
{
if (from_hz)
return from_default_language(in, nullptr);
else
return to_default_language(in, nullptr);
}
// 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 NULL, 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 NULL, flag - unused, be 0
// SANE_EVENT_IMAGE_OK - void* unused, be NULL, flag - unused, be 0
static HMODULE hui = NULL;
int (*choose_scanner)(const std::vector<DEVQUEUI>& devs) = NULL; // blocked. return selected DEVQUE::id or -1 if user cancelled
char* (*apply_current_config)(const char* dev_name, SANE_Handle device, LPSANEAPI api) = NULL; // 鎼存梻鏁ょ拋鎯ь槵閻ㄥ嫬缍嬮崜宥夊帳閿?
int (*show_setting_ui)(SANE_Handle device, HWND parent, LPSANEAPI api, const char* devname, bool with_scan, std::function<void(ui_result)> callback) = NULL;
int (*show_progress_ui)(HWND parent, std::function<void(ui_result)> callback, std::function<void(int/*event*/, void*/*msg*/, int/*flag*/)>* notify) = NULL;
int (*show_messagebox_ui)(HWND parent, int event, void* msg, int flag) = NULL;
int (*close_ui)(int) = NULL;
int (*apply_given_config)(const char* content, SANE_Handle device, LPSANEAPI api) = NULL; // 鎼存梻鏁ら幐鍥х暰閻ㄥ嫰鍘ょ純顕嗙礉content娑撴椽鍘ょ純顔芥殶閹诡喗绁?
char* (*get_config_content)(const char* dev_name, const char* name) = NULL;
void (*twain_ui_free)(void* buf) = NULL;
static int load_dll(const char* path_dll, HMODULE* dll)
{
HMODULE h = LoadLibraryA(path_dll);
int ret = GetLastError();
char info[128] = { 0 };
sprintf_s(info, _countof(info) - 1, " = %d\r\n", ret);
OutputDebugStringA(("[TWAIN]Load: " + std::string(path_dll) + info).c_str());
if (!h && (ret == ERROR_MOD_NOT_FOUND || ret == ERROR_BAD_EXE_FORMAT || ret == 0))
{
std::string dir(path_dll);
size_t pos = dir.rfind(L'\\');
wchar_t path[MAX_PATH] = { 0 };
GetDllDirectoryW(_countof(path) - 1, path);
if (pos != std::wstring::npos)
dir.erase(pos);
OutputDebugStringA(("[TWAIN]Load: try change directory to " + dir + "\r\n").c_str());
SetDllDirectoryA(dir.c_str());
h = LoadLibraryA(path_dll);
// h = LoadLibraryExW(path_dll, NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
ret = GetLastError();
sprintf_s(info, _countof(info) - 1, " = %d\r\n", ret);
OutputDebugStringA(("[TWAIN]Load: " + std::string(path_dll) + info).c_str());
OutputDebugStringW((L"[TWAIN]Load: restore directory to " + std::wstring(path) + L"\r\n").c_str());
SetDllDirectoryW(path);
}
if (dll)
*dll = h;
return ret;
}
static void init_ui(void)
{
std::string root(hg_sane_middleware::sane_path());
if (hui)
FreeLibrary(hui);
hui = NULL;
root += OEM_SHORT_NAME_E;
root += "TwainUI.dll";
// hui = LoadLibraryExA(root.c_str(), NULL, LOAD_WITH_ALTERED_SEARCH_PATH);
//hui = LoadLibraryA(root.c_str());
load_dll(root.c_str(), &hui);
if (!hui)
{
std::wstring info(L"Load '" + local_trans::a2u(root.c_str(), CP_UTF8));
info += L"' failed: " + std::to_wstring(GetLastError()) + L"\r\n";
log_info(info.c_str(), 1);
}
else
{
#define GET_API(api) \
proc = (FARPROC*)&api; \
*proc = GetProcAddress(hui, #api);
FARPROC* proc = NULL;
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);
GET_API(apply_given_config);
GET_API(get_config_content);
}
}
static void unint_ui(void)
{
if (close_ui)
close_ui(UI_UNLOAD_MODULE);
choose_scanner = NULL;
apply_current_config = NULL;
show_setting_ui = NULL;
show_progress_ui = NULL;
show_messagebox_ui = NULL;
close_ui = NULL;
twain_ui_free = NULL;
apply_given_config = NULL;
get_config_content = NULL;
if (hui)
{
FreeLibrary(hui);
hui = NULL;
}
}
// check thread whether has window
static BOOL WINAPI check_window(HWND hwnd, LPARAM lp)
{
LPDWORD id = (LPDWORD)lp;
if (GetWindowThreadProcessId(hwnd, NULL) == id[0])
{
id[1] = 1;
return FALSE;
}
return TRUE;
}
static bool is_thread_has_window(DWORD thread_id = -1)
{
if (thread_id == -1)
thread_id = GetCurrentThreadId();
DWORD param[] = { thread_id, 0 };
EnumWindows(check_window, (LPARAM)param);
return param[1];
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// class scanner
scanner::scanner(SCANNERID id) : handle_(NULL), id_(id), ex_id_(EXTENSION_ID_BASE), prev_start_result_(SCANNER_ERR_NOT_START)
, dpi_(200), tmp_path_(L""), img_ind_(0)
, scanner_name_(L""), cfg_(NULL), is_ui_wait_img_(false), is_scanning_(false)
, scanner_ev_handler_(NULL), evh_param_(NULL), app_wnd_(NULL), 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 = inner_sane_cancel;
sane_api_.sane_close_api = inner_sane_close;
sane_api_.sane_control_option_api = inner_sane_control_option;
sane_api_.sane_get_devices_api = inner_sane_get_devices;
sane_api_.sane_get_option_descriptor_api = inner_sane_get_option_descriptor;
sane_api_.sane_get_parameters_api = inner_sane_get_parameters;
sane_api_.sane_get_select_fd_api = inner_sane_get_select_fd;
sane_api_.sane_io_control_api = inner_sane_io_control;
sane_api_.sane_open_api = inner_sane_open;
sane_api_.sane_read_api = inner_sane_read;
sane_api_.sane_set_io_mode_api = inner_sane_set_io_mode;
sane_api_.sane_start_api = inner_sane_start;
sane_api_.sane_strstatus_api = inner_sane_strstatus;
if (!callback::show_setting_ui)
{
cfg_ = new gb::scanner_cfg();
cfg_->set_language_transform(&callback::language_trans, NULL);
}
tmp_path_ = local_trans::a2u(hg_sane_middleware::sane_path().c_str());
{
char* tmp = getenv("LOCALAPPDATA");
if (tmp)
{
tmp_path_ = local_trans::a2u(tmp) + L"\\";
tmp_path_ += local_trans::a2u(PRODUCT_VENDOR) + L"Scan\\";
CreateDirectoryW(tmp_path_.c_str(), NULL);
}
}
cfg_path_ = tmp_path_ + L"config";
CreateDirectoryW(cfg_path_.c_str(), NULL);
cfg_path_ += L"\\";
tmp_path_ += L"imgs";
CreateDirectoryW(tmp_path_.c_str(), NULL);
tmp_path_ += L"\\";
img_fmt_.img_format = SANE_IMAGE_TYPE_BMP;
img_fmt_.compress.compression = SANE_COMPRESSION_NONE;
int mem_limit = GetPrivateProfileIntW(L"mem", L"max_img", 0, (cfg_path_ + L"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();
if (cfg_)
{
cfg_->remove_all_schemes(); // schemes would add_ref on cfg_, so we clear them first. NOTE: do not call save after this !!!
cfg_->release();
cfg_ = NULL;
}
}
bool scanner::is_belong_serial(int vid, int pid, SCANNERID serial)
{
if (vid == PRODUCT_VENDOR_HG)
{
if (GET_SCANNER_VID(serial) == PRODUCT_VENDOR_HG)
{
if (GET_SCANNER_PID(serial) == 0x100)
{
return pid == 0x100 || pid == 0x139;
}
else if (GET_SCANNER_PID(serial) == 0x200)
{
return pid == 0x200 || pid == 0x239;
}
else if (GET_SCANNER_PID(serial) == 0x300)
{
return pid == 0x300 || pid == 0x302 || pid == 0x339;
}
else if (GET_SCANNER_PID(serial) == 0x400)
{
return pid == 0x400 || pid == 0x402 || pid == 0x439;
}
else if (GET_SCANNER_PID(serial) == 0x138 ||
GET_SCANNER_PID(serial) == 0x238 ||
GET_SCANNER_PID(serial) == 0x303 ||
GET_SCANNER_PID(serial) == 0x404) // OEM_CANGTIAN
return true;
}
return false;
}
else if (vid == PRODUCT_VENDOR_HG1)
{
return pid == 0x7823 && GET_SCANNER_VID(serial) == PRODUCT_VENDOR_HG && GET_SCANNER_PID(serial) == 0x200;
}
else if (vid == PRODUCT_VENDOR_HW)
{
return GET_SCANNER_VID(serial) == vid && GET_SCANNER_PID(serial) == pid;
}
else if (vid == PRODUCT_VENDOR_LSC)
{
if (GET_SCANNER_VID(serial) == PRODUCT_VENDOR_LSC)
{
if (GET_SCANNER_PID(serial) == 0x8420)
{
return pid == 0x8200 || pid == 0x8420 || pid == 0x8429;
}
else if (GET_SCANNER_PID(serial) == 0x8520)
{
return pid == 0x8520 || pid == 0x8529;
}
else if (GET_SCANNER_PID(serial) == 0x8620)
{
return pid == 0x8620 || pid == 0x8629;
}
else if (GET_SCANNER_PID(serial) == 0x8730)
{
return pid == 0x8730 || pid == 0x8739;
}
}
return false;
}
else if (vid == PRODUCT_VENDOR_DL)
{
if (GET_SCANNER_PID(serial) == 0x401C)
{
return pid == 0x401C || pid == 0x4020;
}
else
{
return GET_SCANNER_VID(serial) == vid && GET_SCANNER_PID(serial) == pid;
}
}
else if (vid == PRODUCT_VENDOR_ZG)
{
return GET_SCANNER_VID(serial) == vid && GET_SCANNER_PID(serial) == pid;
}
return true;
}
void scanner::get_scanner_name(SCANNERID id, std::vector<std::string>& names)
{
ScannerInfo* devs = NULL;
long count = 0;
names.clear();
if (hg_scanner_enum(devs, &count, true) == SCANNER_ERR_INSUFFICIENT_MEMORY)
{
count++;
devs = new ScannerInfo[count];
memset(devs, 0, count * sizeof(*devs));
if (hg_scanner_enum(devs, &count, true) == SCANNER_ERR_OK)
{
for (int i = 0; i < count; ++i)
{
if (scanner::is_belong_serial(devs[i].vid, devs[i].pid, id))
{
names.push_back(devs[i].name);
}
}
}
delete[] devs;
}
}
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::control_read_string(SANE_Handle hdev, int code, std::string& str)
{
char* buf = NULL;
unsigned len = 0;
int err = hg_sane_middleware::instance()->io_control(hdev, code, buf, &len);
str = "";
if (err == SANE_STATUS_NO_MEM)
{
len += 4;
buf = new char[len];
memset(buf, 0, len);
err = hg_sane_middleware::instance()->io_control(hdev, code, buf, &len);
if (err == SANE_STATUS_GOOD)
str = buf;
delete[] buf;
}
return err;
}
int __stdcall scanner::to_int(SANE_Int v)
{
return v;
}
float __stdcall scanner::to_float(SANE_Fixed v)
{
return (float)SANE_UNFIX(v);
}
void __stdcall 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)
{
SANE_Option_Descriptor* desc = hg_sane_middleware::instance()->get_option_descriptor((SANE_Handle)param, (const void*)sn);
if (desc)
return desc->type == SANE_TYPE_FIXED;
else
return false;
}
void __stdcall scanner::apply_scheme(gb::sane_config_schm* schm, void* param)
{
((scanner*)param)->apply_scheme(schm);
}
// 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::transport_config_file(void)
{
size_t pos = scanner_name_.find(L" - ");
std::wstring pid(L"G"), old(L"");
if (pos == std::wstring::npos)
return;
pid += scanner_name_.substr(pos + 3);
if (scanner_name_.find(L"HUAGOSCAN ") != std::wstring::npos)
{
old = local_trans::lang_trans_between_hz936(L"\u534E\u9AD8\u626B\u63CF\u4EEA\u2014");
}
else if (scanner_name_.find(L"LANXUMSCAN ") != std::wstring::npos)
{
old = local_trans::lang_trans_between_hz936(L"\u7ACB\u601D\u8FB0\u626B\u63CF\u4EEA\u2014");
pid += L"S";
}
old += pid;
if (PathFileExistsW((cfg_path_ + old).c_str()))
{
log_info((L"Rename config file '" + old + L"' to '" + scanner_name_.substr(0, pos) + L"'\r\n").c_str(), 1);
MoveFileW((cfg_path_ + old).c_str(), (cfg_path_ + scanner_name_.substr(0, pos)).c_str());
}
}
void scanner::update_config(void)
{
if (!cfg_)
return;
gb::sane_config_schm* schm = cfg_->get_scheme();
std::string notice("");
if (schm)
{
//schm->update(&scanner::is_option_float, handle_, &callback::option_title_2_name, &notice);
//if (notice.length())
//{
// std::wstring msg(local_trans::lang_trans_between_hz936(L"\u4E0B\u5217\u914D\u7F6E\u6570\u636E\u9519\u8BEF\uFF0C\u5DF2\u7ECF\u6062\u590D\u5230\u9ED8\u8BA4\u503C\u3002\u5982\u679C\u9700\u8981\uFF0C\u8BF7\u91CD\u65B0\u8BBE\u7F6E") + L"\r\n\r\n");
// size_t pos = notice.find("\r\n");
//
// while (pos != std::string::npos)
// {
// msg += local_trans::a2u(callback::option_name_2_title(notice.substr(0, pos).c_str()), CP_UTF8) + L"\r\n";
// notice.erase(0, pos + 2);
// pos = notice.find("\r\n");
// }
// if (!IsWindow(app_wnd_))
// callback::bring_message_box_topmost(local_trans::lang_trans_between_hz936(L"\u52A0\u8F7D\u914D\u7F6E").c_str());
// MessageBoxW(app_wnd_, msg.c_str(), local_trans::lang_trans_between_hz936(L"\u52A0\u8F7D\u914D\u7F6E").c_str(), MB_OK | MB_ICONINFORMATION);
//}
schm->release();
}
}
void scanner::load_config(const wchar_t* file)
{
if (cfg_)
{
cfg_->load_file(local_trans::u2a(file).c_str());
update_config();
}
}
void scanner::save_config(const wchar_t* file)
{
if(cfg_)
cfg_->save(local_trans::u2a(file).c_str());
}
void scanner::apply_config(void)
{
if (callback::apply_current_config)
{
char* cfg = callback::apply_current_config(local_trans::u2a(scanner_name_.c_str(), CP_UTF8).c_str(), handle_, &sane_api_);
if (cfg && callback::twain_ui_free)
callback::twain_ui_free(cfg);
}
else if (cfg_)
{
gb::sane_config_schm* schm = cfg_->get_scheme();
if (!schm)
return;
apply_scheme(schm);
schm->release();
}
}
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;
if (setting_.get())
setting_.reset();
}
else if (uev == SANE_EVENT_UI_CLOSE_NORMAL)
{
// scan complete
if (indicator_.get())
indicator_.reset();
// 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
if (indicator_.get())
indicator_.reset();
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(__stdcall * 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 = NULL;
int ret = hg_sane_middleware::instance()->open_device(scanners[i].c_str(), &h);
if (h)
{
std::string sn("");
scanner::control_read_string(h, IO_CTRL_CODE_GET_SERIAL, sn);
if (sn.length())
{
DEVQUEUI dev;
dev.id = id++;
dev.name = scanners[i];
dev.sn = sn;
devs.push_back(dev);
}
hg_sane_middleware::instance()->close_device(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(NULL, 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_ = L"";
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 = hg_sane_middleware::instance()->open_device(name.c_str(), &handle_);
if (ret == SANE_STATUS_GOOD)
{
size_t pid = -1;
transport_config_file();
callback::reg_callback(handle_, this);
scanner_name_ = local_trans::a2u(name.c_str(), CP_UTF8);
pid = scanner_name_.find(L" - ");
if (pid == -1)
pid = scanner_name_.length();
ret = init_options_id();
load_config((cfg_path_ + scanner_name_.substr(0, pid) + L".cfg").c_str());
apply_config();
// check roller life ...
SANE_Int cnt = 0, life = 0;
if (hg_sane_middleware::instance()->get_cur_value(handle_, (void*)SANE_OPT_ID_ROLLER_COUNT, &cnt) &&
hg_sane_middleware::instance()->set_option(handle_, (void*)SANE_OPT_ID_ROLLER_LIFE, SANE_ACTION_GET_VALUE, &life, NULL) == SANE_STATUS_GOOD)
//hg_sane_middleware::instance()->get_cur_value(handle_, (void*)SANE_OPT_ID_ROLLER_LIFE, &life))
{
if (cnt >= life / 100 * 99) // 99%
{
if (callback::show_messagebox_ui)
{
// 缁炬瓕鐤嗛幖鎾剁剨濞嗏剝鏆熷鑼剁Т鏉╁洩顔曠拋鈥插▏閻劏瀵栭崶杈剧礉閹殿偅寮挎潻鍥┾柤娑擃厽鎮$痪绋裤亼鐠愩儯鈧焦顒i弬婧库偓浣规偂婢舵艾绱剁粵澶婄磽鐢悂顣跺▎鈥冲讲閼虫垝绱伴弰搴㈡▔婢х偛顦块敍宀冾嚞濞夈劍鍓伴崣濠冩濞撳懏纾ラ妴浣歌嫙閼辨梻閮寸拋鎯ь槵娓氭稑绨查崯鍡氬枠娑旂増娴涢幑銏㈢剨鏉烆噯绱?
std::wstring roller_msgw(local_trans::lang_trans_between_hz936(L"\u7EB8\u8F6E\u6413\u7EB8\u6B21\u6570\u5DF2\u8D85\u8FC7\u8BBE\u8BA1\u4F7F\u7528\u8303\u56F4\uFF0C\u626B\u63CF\u8FC7\u7A0B\u4E2D\u6413\u7EB8\u5931\u8D25\u3001\u6B6A\u659C\u3001\u6413\u591A\u5F20\u7B49\u5F02\u5E38\u9891\u6B21\u53EF\u80FD\u4F1A\u660E\u663E\u589E\u591A\uFF0C\u8BF7\u6CE8\u610F\u53CA\u65F6\u6E05\u6D01\u3001\u5E76\u8054\u7CFB\u8BBE\u5907\u4F9B\u5E94\u5546\u8D2D\u4E70\u66FF\u6362\u7EB8\u8F6E\uFF01"));
std::string roller_msg(local_trans::u2a(roller_msgw.c_str(), CP_UTF8));
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*)hg_scanner_err_description(ret), 0);
}
else
{
std::wstring msg(local_trans::a2u(hg_scanner_err_description(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);
}
}
return ret;
}
int scanner::close(void)
{
scanner_ev_handler_ = NULL;
ui_hide();
callback::unreg_callback(this);
if (handle_)
{
hg_sane_middleware::instance()->close_device(handle_);
}
handle_ = NULL;
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 = NULL;
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);
INIT_FIXED_IDS(INITIAL_BOOT_TIME);
while ((desc = hg_sane_middleware::instance()->get_option_descriptor(handle_, (const void*)op_id)))
{
void* val = hg_sane_middleware::instance()->get_def_value(handle_, (void*)op_id, NULL, true);
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;
}
if (len && cfg_)
{
if (desc->type == SANE_TYPE_STRING)
{
std::string deflan(to_default_language((char*)val, nullptr));
cfg_->set_default_value(op_id, desc->name, desc->title, &deflan[0], deflan.length(), desc->type);
}
else
cfg_->set_default_value(op_id, desc->name, desc->title, (char*)val, len, desc->type);
}
local_utility::free_memory(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)
else SET_OPT_ID(discardblank, DISCARDBLANK, 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;
}
int scanner::control_read_string(int code, std::string& ret)
{
return scanner::control_read_string(handle_, code, 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;
wchar_t 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);
{
swprintf_s(msg, _countof(msg) - 1, L"handle_ex_duplex of id: %d\r\n", ea.ind);
log_info(msg, 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);
{
swprintf_s(msg, _countof(msg) - 1, L"handle_ex_discard_blank_page of id: %d\r\n", ea.ind);
log_info(msg, 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);
{
swprintf_s(msg, _countof(msg) - 1, L"handle_ex_discard_blank_receipt of id: %d\r\n", ea.ind);
log_info(msg, 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);
{
swprintf_s(msg, _countof(msg) - 1, L"handle_ex_page_fold of id: %d\r\n", ea.ind);
log_info(msg, 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_)
{
SANE_Option_Descriptor* parent = hg_sane_middleware::instance()->get_option_descriptor(handle_, (const void*)scan_mode_id_);
char* buf = new char[parent->size + 4];
memset(buf, 0, parent->size);
hg_sane_middleware::instance()->get_cur_value(handle_, (void*)scan_mode_id_, buf);
handled = compare_sane_opt(local_trans::lang_trans_between_hz936(OPTION_VALUE_SMZS_LXSM, true, nullptr), buf);
delete[] buf;
if (handled)
{
int count = -1;
value_role role = VAL_ROLE_CURRENT;
buf = (char*)hg_sane_middleware::instance()->get_def_value(handle_, (void*)scan_mode_id_);
if (compare_sane_opt(local_trans::lang_trans_between_hz936(OPTION_VALUE_SMZS_LXSM, true, nullptr), buf))
role = value_role(role | VAL_ROLE_DEFAULT);
local_utility::free_memory(buf);
setv(&count, value_role(VAL_ROLE_CURRENT | VAL_ROLE_DEFAULT), VAL_LIMIT_NONE, param);
}
}
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_)
{
SANE_Option_Descriptor* parent = hg_sane_middleware::instance()->get_option_descriptor(handle_, (void*)scan_mode_id_);
char* val = new char[parent->size + 4];
SANE_Int after = 0;
memset(val, 0, parent->size + 4);
hg_sane_middleware::instance()->get_cur_value(handle_, (void*)scan_mode_id_, 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 = hg_sane_middleware::instance()->set_option(handle_, (void*)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 = hg_sane_middleware::instance()->set_option(handle_, (void*)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 = NULL;
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 = hg_sane_middleware::instance()->set_option(handle_, (void*)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;
hg_sane_middleware::instance()->get_cur_value(handle_, (void*)is_multiout_id_, &multi); // parent item ...
if (enable ^ (multi == SANE_TRUE))
{
multi = enable ? SANE_TRUE : SANE_FALSE;
ret = hg_sane_middleware::instance()->set_option(handle_, (void*)is_multiout_id_, SANE_ACTION_SET_VALUE, &multi, &after);
after = 0;
}
}
return ret;
}
int scanner::thread_start(void)
{
int ret = hg_sane_middleware::instance()->start(handle_, NULL);
// 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 = hg_sane_middleware::instance()->start(handle_, NULL);
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(hg_scanner_err_description(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 *)hg_scanner_err_description(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*)hg_scanner_err_description(ret), 0);
}
else
{
std::wstring msg(local_trans::a2u(hg_scanner_err_description(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);
}
void scanner::apply_scheme(gb::sane_config_schm* schm)
{
// restore ...
hg_sane_middleware::instance()->io_control(handle_, IO_CTRL_CODE_RESTORE_SETTINGS, nullptr, nullptr);
if (!schm)
return;
std::string n(""), v(""), ver(schm->get_version());
if (schm->first_config(n, v))
{
do
{
int id = schm->id_from_name(n.c_str());
if (id == -1)
{
if (gb::sane_config_schm::is_option_data(n))
{
id = schm->id_from_name(n.c_str());
if (id == is_custom_gamma_id_)
{
if (v.length() == sizeof(SANE_Gamma))
{
unsigned int l = v.length();
hg_sane_middleware::instance()->io_control(handle_, IO_CTRL_CODE_SET_CUSTOM_GAMMA, &v[0], &l);
}
else
{
wchar_t info[128] = { 0 };
swprintf_s(info, _countof(info) - 1, L"ERROR: custom gamma data length is %u, but we expect %u.\r\n", v.length(), sizeof(SANE_Gamma));
log_info(info, 1);
}
}
}
}
else
{
v = from_default_language(v.c_str(), nullptr);
void* data = &v[0];
SANE_Fixed fixed = 0;
const SANE_Option_Descriptor* desc = hg_sane_middleware::instance()->get_option_descriptor(handle_, (const void*)id);
if (desc)
{
char* buf = NULL;
SANE_Int after = 0;
if (desc->type == SANE_TYPE_STRING)
{
buf = new char[desc->size + 4];
memset(buf, 0, desc->size + 4);
strcpy(buf, v.c_str());
data = buf;
}
hg_sane_middleware::instance()->set_option(handle_, (const void*)id, SANE_ACTION_SET_VALUE, data, &after);
if (buf)
delete[] buf;
}
}
} while (schm->next_config(n, v));
}
}
EX_OPTION_HANDLER_IMPL(multiout)
{
int ret = SCANNER_ERR_OK;
SANE_Option_Descriptor* desc = hg_sane_middleware::instance()->get_option_descriptor(handle_, (void*)base_id);
SANE_Bool parent = SANE_FALSE;
hg_sane_middleware::instance()->get_cur_value(handle_, (void*)is_multiout_id_, &parent);
if (setv)
{
char* cur = (char*)hg_sane_middleware::instance()->get_cur_value(handle_, (void*)base_id),
* def = (char*)hg_sane_middleware::instance()->get_def_value(handle_, (void*)base_id);
int now = sane_opt_trans::multiout_value_to_twain(cur),
init = sane_opt_trans::multiout_value_to_twain(def),
val = 0;
local_utility::free_memory(def);
local_utility::free_memory(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 = hg_sane_middleware::instance()->set_option(handle_, (void*)base_id, SANE_ACTION_SET_VALUE, val, &after);
// }
//}
//else
//{
// // disable multi-out, let multiout type aside
// ret = set_is_multiout(false);
//}
delete[] val;
ret = local_utility::sane_statu_2_scanner_err(ret);
}
return ret;
}
EX_OPTION_HANDLER_IMPL(auto_color_type)
{
int ret = SCANNER_ERR_OK;
SANE_Option_Descriptor* desc = hg_sane_middleware::instance()->get_option_descriptor(handle_, (void*)base_id);
int len = desc->size + 4;
char* buf = new char[len];
memset(buf, 0, len);
hg_sane_middleware::instance()->get_cur_value(handle_, (void*)base_id, buf);
if (setv)
{
char* def = (char*)hg_sane_middleware::instance()->get_def_value(handle_, (void*)base_id);
int init = sane_opt_trans::auto_color_type_to_twain(def);
local_utility::free_memory(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 = hg_sane_middleware::instance()->set_option(handle_, (void*)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_mode)
{
int ret = SCANNER_ERR_OK;
SANE_Option_Descriptor* desc = hg_sane_middleware::instance()->get_option_descriptor(handle_, (void*)base_id);
if (setv)
{
char* cur = (char*)hg_sane_middleware::instance()->get_cur_value(handle_, (void*)base_id),
* def = (char*)hg_sane_middleware::instance()->get_def_value(handle_, (void*)base_id);
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;
local_utility::free_memory(def);
local_utility::free_memory(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 = hg_sane_middleware::instance()->set_option(handle_, (void*)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(sharpen)
{
int ret = SCANNER_ERR_OK;
SANE_Option_Descriptor* desc = hg_sane_middleware::instance()->get_option_descriptor(handle_, (void*)base_id);
if (setv)
{
char* cur = (char*)hg_sane_middleware::instance()->get_cur_value(handle_, (void*)base_id),
* def = (char*)hg_sane_middleware::instance()->get_def_value(handle_, (void*)base_id);
int now = sane_opt_trans::multiout_value_to_twain(cur),
init = sane_opt_trans::sharpen_to_twain(def),
val = 0;
local_utility::free_memory(def);
local_utility::free_memory(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 = hg_sane_middleware::instance()->set_option(handle_, (void*)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;
SANE_Option_Descriptor* desc = hg_sane_middleware::instance()->get_option_descriptor(handle_, (void*)base_id);
int len = desc->size + 4;
char* buf = new char[len];
memset(buf, 0, len);
if (setv)
{
hg_sane_middleware::instance()->get_cur_value(handle_, (void*)base_id, buf);
char* def = (char*)hg_sane_middleware::instance()->get_def_value(handle_, (void*)base_id);
int now = sane_opt_trans::paper_to_twain(buf),
init = sane_opt_trans::paper_to_twain(def),
val = 0;
local_utility::free_memory(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 = hg_sane_middleware::instance()->set_option(handle_, (void*)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;
SANE_Option_Descriptor* desc = hg_sane_middleware::instance()->get_option_descriptor(handle_, (void*)base_id);
int len = desc->size + 4;
char* buf = new char[len];
const char* lateral_swap = NULL;
bool lateral = false;
memset(buf, 0, len);
hg_sane_middleware::instance()->get_cur_value(handle_, (void*)base_id, buf);
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 = hg_sane_middleware::instance()->set_option(handle_, (void*)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 = hg_sane_middleware::instance()->set_option(handle_, (void*)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;
SANE_Option_Descriptor* desc = hg_sane_middleware::instance()->get_option_descriptor(handle_, (void*)base_id);
int len = desc->size + 4;
char* buf = new char[len];
memset(buf, 0, len);
hg_sane_middleware::instance()->get_cur_value(handle_, (void*)base_id, buf);
if (setv)
{
char* init = (char*)hg_sane_middleware::instance()->get_def_value(handle_, (void*)base_id);
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;
local_utility::free_memory(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 = hg_sane_middleware::instance()->set_option(handle_, (void*)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;
SANE_Option_Descriptor* desc = hg_sane_middleware::instance()->get_option_descriptor(handle_, (void*)base_id);
int len = desc->size + 4;
char* buf = new char[len];
memset(buf, 0, len);
hg_sane_middleware::instance()->get_cur_value(handle_, (void*)base_id, buf);
if (setv)
{
char* init = (char*)hg_sane_middleware::instance()->get_def_value(handle_, (void*)base_id);
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;
local_utility::free_memory(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 = hg_sane_middleware::instance()->set_option(handle_, (void*)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;
SANE_Option_Descriptor* desc = hg_sane_middleware::instance()->get_option_descriptor(handle_, (void*)base_id);
int len = desc->size + 4;
char* buf = new char[len];
memset(buf, 0, len);
if (setv)
{
char* def = (char*)hg_sane_middleware::instance()->get_def_value(handle_, (void*)base_id);
float now = .0f, init = sane_opt_trans::text_direction_to_twain(def), val = .0f;
local_utility::free_memory(def);
hg_sane_middleware::instance()->get_cur_value(handle_, (void*)base_id, buf);
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 = hg_sane_middleware::instance()->set_option(handle_, (void*)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;
SANE_Option_Descriptor* desc = hg_sane_middleware::instance()->get_option_descriptor(handle_, (void*)base_id);
unsigned len = desc->size + 4;
char* buf = new char[len];
memset(buf, 0, len);
if (setv)
{
char* init = (char*)hg_sane_middleware::instance()->get_def_value(handle_, (void*)base_id);
hg_sane_middleware::instance()->get_cur_value(handle_, (void*)base_id, buf);
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);
local_utility::free_memory(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 = hg_sane_middleware::instance()->set_option(handle_, (void*)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;
SANE_Option_Descriptor* desc = hg_sane_middleware::instance()->get_option_descriptor(handle_, (void*)base_id);
unsigned len = desc->size + 4;
char* buf = new char[len];
memset(buf, 0, len);
if (setv)
{
char* init = (char*)hg_sane_middleware::instance()->get_def_value(handle_, (void*)base_id);
hg_sane_middleware::instance()->get_cur_value(handle_, (void*)base_id, buf);
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);
local_utility::free_memory(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 = hg_sane_middleware::instance()->set_option(handle_, (void*)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;
SANE_Option_Descriptor* desc = hg_sane_middleware::instance()->get_option_descriptor(handle_, (void*)base_id);
unsigned len = desc->size + 4;
char* buf = new char[len];
memset(buf, 0, len);
if (setv)
{
char* init = (char*)hg_sane_middleware::instance()->get_def_value(handle_, (void*)base_id);
bool def = strcmp(init, local_trans::lang_trans_between_hz936(OPTION_VALUE_SMYM_TGKBYTY, true, nullptr)) == 0;
local_utility::free_memory(init);
hg_sane_middleware::instance()->get_cur_value(handle_, (void*)base_id, buf);
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 = (char*)hg_sane_middleware::instance()->get_def_value(handle_, (void*)base_id);
strcpy(buf, init);
local_utility::free_memory(init);
}
SANE_Int after = 0;
ret = hg_sane_middleware::instance()->set_option(handle_, (void*)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;
SANE_Option_Descriptor* desc = hg_sane_middleware::instance()->get_option_descriptor(handle_, (void*)base_id);
unsigned len = desc->size + 4;
char* buf = new char[len];
memset(buf, 0, len);
if (setv)
{
char* init = (char*)hg_sane_middleware::instance()->get_def_value(handle_, (void*)base_id);
bool def = strcmp(init, local_trans::lang_trans_between_hz936(OPTION_VALUE_SMYM_TGKBYFPZ, true, nullptr)) == 0;
local_utility::free_memory(init);
hg_sane_middleware::instance()->get_cur_value(handle_, (void*)base_id, buf);
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 = (char*)hg_sane_middleware::instance()->get_def_value(handle_, (void*)base_id);
strcpy(buf, init);
local_utility::free_memory(init);
}
SANE_Int after = 0;
ret = hg_sane_middleware::instance()->set_option(handle_, (void*)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;
SANE_Option_Descriptor* desc = hg_sane_middleware::instance()->get_option_descriptor(handle_, (void*)base_id);
unsigned len = desc->size + 4;
char* buf = new char[len];
memset(buf, 0, len);
if (setv)
{
char* init = (char*)hg_sane_middleware::instance()->get_def_value(handle_, (void*)base_id);
bool def = strcmp(init, local_trans::lang_trans_between_hz936(OPTION_VALUE_SMYM_DZ, true, nullptr)) == 0;
local_utility::free_memory(init);
hg_sane_middleware::instance()->get_cur_value(handle_, (void*)base_id, buf);
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 = (char*)hg_sane_middleware::instance()->get_def_value(handle_, (void*)base_id);
strcpy(buf, init);
local_utility::free_memory(init);
}
SANE_Int after = 0;
ret = hg_sane_middleware::instance()->set_option(handle_, (void*)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;
SANE_Option_Descriptor* desc = hg_sane_middleware::instance()->get_option_descriptor(handle_, (void*)base_id);
char* buf = NULL;
unsigned int len = 0;
if (desc)
{
len = desc->size + 4;
buf = new char[len];
memset(buf, 0, len);
if (setv)
{
bool filter = false;
int val = FILTER_NONE, now = FILTER_NONE;
hg_sane_middleware::instance()->get_cur_value(handle_, (void*)base_id, buf);
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 = hg_sane_middleware::instance()->set_option(handle_, (void*)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;
SANE_Option_Descriptor* desc = hg_sane_middleware::instance()->get_option_descriptor(handle_, (void*)base_id);
char* buf = NULL;
unsigned int len = 0;
if (desc)
{
len = desc->size + 4;
buf = new char[len];
memset(buf, 0, len);
if (setv)
{
bool filter = false;
int val = ENHANCE_NONE, now = ENHANCE_NONE;
hg_sane_middleware::instance()->get_cur_value(handle_, (void*)base_id, buf);
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 = hg_sane_middleware::instance()->set_option(handle_, (void*)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)
{
int val = 0;
ret = hg_sane_middleware::instance()->io_control(handle_, IO_CTRL_CODE_GET_FINAL_COMPRESSION, &val, &len);
if (ret == SANE_STATUS_GOOD)
{
int i = SANE_COMPRESSION_FIRST;
for (; i < SANE_COMPRESSION_LAST; ++i)
{
value_role role = VAL_ROLE_NONE;
if (i == val)
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);
}
return ret;
}
EX_OPTION_HANDLER_IMPL(final_format)
{
int ret = SCANNER_ERR_OK;
SANE_FinalImgFormat ff;
unsigned int len = sizeof(ff);
if (setv)
{
ret = hg_sane_middleware::instance()->io_control(handle_, IO_CTRL_CODE_GET_FINAL_IMAGE_FORMAT, &ff, &len);
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
{
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;
unsigned int len = sizeof(wait_paper);
if (setv)
{
ret = hg_sane_middleware::instance()->io_control(handle_, IO_CTRL_CODE_GET_SCAN_WHEN_PAPER_ON, &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 = hg_sane_middleware::instance()->io_control(handle_, IO_CTRL_CODE_SET_SCAN_WHEN_PAPER_ON, &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;
unsigned int len = sizeof(with_hole);
if (setv)
{
ret = hg_sane_middleware::instance()->io_control(handle_, IO_CTRL_CODE_GET_SCAN_WITH_HOLE, &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 = hg_sane_middleware::instance()->io_control(handle_, IO_CTRL_CODE_SET_SCAN_WITH_HOLE, &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_DEVICE_CODE, 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)
{
SANE_Power now = SANE_POWER_MINUTES_30, init = SANE_POWER_MINUTES_30;
unsigned int len = sizeof(now);
hg_sane_middleware::instance()->io_control(handle_, IO_CTRL_CODE_GET_POWER_LEVEL, &now, &len);
for (int i = SANE_POWER_FIRST; i < SANE_POWER_LAST; ++i)
{
value_role role = VAL_ROLE_NONE;
if (i == now)
role = VAL_ROLE_CURRENT;
if (i == init)
role = value_role(role | VAL_ROLE_DEFAULT);
SANE_Power power = (SANE_Power)i;
if (!setv(&power, role, VAL_LIMIT_ENUM, data))
break;
}
}
else
{
SANE_Power power = *((SANE_Power*)data);
unsigned int len = sizeof(power);
ret = hg_sane_middleware::instance()->io_control(handle_, IO_CTRL_CODE_SET_POWER_LEVEL, &power, &len);
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)
{
hg_sane_middleware::instance()->get_cur_value(handle_, (void*)is_erase_hole_l_id_, &yes);
hg_sane_middleware::instance()->get_def_value(handle_, (void*)is_erase_hole_l_id_, &def);
}
if (!yes && is_erase_hole_r_id_ != -1)
{
hg_sane_middleware::instance()->get_cur_value(handle_, (void*)is_erase_hole_r_id_, &yes);
hg_sane_middleware::instance()->get_def_value(handle_, (void*)is_erase_hole_r_id_, &def);
}
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 = hg_sane_middleware::instance()->set_option(handle_, (void*)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 = hg_sane_middleware::instance()->set_option(handle_, (void*)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 = hg_sane_middleware::instance()->get_option_descriptor(handle_, (void*)base_id);
void* init = hg_sane_middleware::instance()->get_def_value(handle_, (void*)base_id),
* cur = hg_sane_middleware::instance()->get_cur_value(handle_, (void*)base_id);
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);
local_utility::free_memory(init);
local_utility::free_memory(cur);
ret = SCANNER_ERR_OK;
}
else
{
rv = (double)*(float*)data;
val = SANE_FIX(rv);
if (search_hole_range_l_id_ != -1)
ret = hg_sane_middleware::instance()->set_option(handle_, (void*)search_hole_range_l_id_, SANE_ACTION_SET_VALUE, &val, &after);
val = SANE_FIX(rv);
if (search_hole_range_r_id_ != -1)
ret = hg_sane_middleware::instance()->set_option(handle_, (void*)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_ = setting_.get() ? setting_->hwnd() : 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;
return hg_sane_middleware::instance()->stop(handle_);
}
COM_API_IMPLEMENT(scanner, int, get_event(void))
{
return events_.take();
}
COM_API_IMPLEMENT(scanner, void, set_event_callback(int(__stdcall* 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, NULL, 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();
{
wchar_t msg[128] = { 0 };
swprintf_s(msg, _countof(msg) - 1, L"Wait image count = %d\r\n", count);
log_info(msg, 1);
}
return count;
}
COM_API_IMPLEMENT(scanner, IScanImg*, take_first_image(twain_xfer xfer))
{
scanned_img* img = images_.take(false);
if (img)
{
img->prepare_data_for_transfer(xfer);
img->add_ref();
wchar_t msg[128] = { 0 };
swprintf_s(msg, _countof(msg) - 1, L"Begin transferring image %d of %p\r\n", fetch_imgs_ + 1, img);
log_info(msg, 1);
}
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;
}
}
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 (hg_sane_middleware::instance()->io_control(handle_, IO_CTRL_CODE_GET_PAPER_ON, &on, &len) == 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, NULL, fetch_imgs_);
else if (indicator_.get())
indicator_->notify_data_arrived(false);
{
wchar_t msg[128] = { 0 };
swprintf_s(msg, _countof(msg) - 1, L"Transferring image %d of %p finished.\r\n", fetch_imgs_, tx);
log_info(msg, 1);
}
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);
SIMPLE_STR_INFO(SANE_OPT_ID_INITIAL_BOOT_TIME, true);
sn = transfer_id(sn);
if (sn == -1)
return false;
EXAPIPOS ex = find_ex_api(sn);
SANE_Option_Descriptor* desc = hg_sane_middleware::instance()->get_option_descriptor(handle_, ex == ex_opts_.end() ? (void*)sn : (void*)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 = hg_sane_middleware::instance()->set_option(handle_, (void*)sn, SANE_ACTION_GET_VALUE, buf, NULL);
if (ret == SANE_STATUS_GOOD)
{
get_option_info(sn, &type, NULL, NULL);
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;
SANE_Option_Descriptor* desc = hg_sane_middleware::instance()->get_option_descriptor(handle_, (void*)sn);
void* init = hg_sane_middleware::instance()->get_def_value(handle_, (void*)sn);
ret = SANE_STATUS_GOOD;
if (desc->type == SANE_TYPE_BOOL)
{
SANE_Bool v = SANE_FALSE;
bool val = false;
hg_sane_middleware::instance()->get_cur_value(handle_, (void*)sn, &v);
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;
hg_sane_middleware::instance()->get_cur_value(handle_, (void*)sn, &cur);
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;
hg_sane_middleware::instance()->get_cur_value(handle_, (void*)sn, &cur);
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);
hg_sane_middleware::instance()->get_cur_value(handle_, (void*)sn, buf);
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, NULL);
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;
}
local_utility::free_memory(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;
SANE_Status ret = hg_sane_middleware::instance()->set_option(handle_, (const void*)sn, SANE_ACTION_GET_VALUE, data, NULL);
wchar_t msg[128] = { 0 };
swprintf_s(msg, _countof(msg) - 1, L"get log of(%x) = 0x%x\r\n", sn, ret);
log_info(msg, 1);
return ret == 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 = hg_sane_middleware::instance()->set_option(handle_, (void*)sn, SANE_ACTION_SET_VALUE, val, NULL);
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;
SANE_Option_Descriptor* desc = hg_sane_middleware::instance()->get_option_descriptor(handle_, (void*)sn);
{
wchar_t msg[256] = { 0 };
if (ex == ex_opts_.end())
swprintf_s(msg, _countof(msg) - 1, L"set_value of %s(%s) of ID %d\r\n", local_trans::a2u(desc->name, CP_UTF8).c_str(), local_trans::a2u(hg_sane_middleware::option_value_2_string(desc->type, val).c_str(), CP_UTF8).c_str(), sn);
else if(ex->base_ind != -1)
{
desc = hg_sane_middleware::instance()->get_option_descriptor(handle_, (void*)ex->base_ind);
swprintf_s(msg, _countof(msg) - 1, L"set_value of %s(0x%x) of ID %d, base id = %d)\r\n", local_trans::a2u(desc->name, CP_UTF8).c_str(), *(unsigned*)val, sn, ex->base_ind);
}
log_info(msg, 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, NULL);
}
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 hg_sane_middleware::instance()->io_control(handle_, IO_CTRL_CODE_CONVERT_IMAGE_FORMAT, conv, NULL);
}
COM_API_IMPLEMENT(scanner, void, free_buffer(void* buf, int len))
{
hg_sane_middleware::instance()->io_control(handle_, IO_CTRL_CODE_FREE_MEMORY, buf, (unsigned int*)&len);
}
COM_API_IMPLEMENT(scanner, int, get_fixed_ids(bool(__stdcall* 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_OPTION_ID_IMPLEMENT(discardblank)
// 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))
{
int ret = SCANNER_ERR_NO_DATA;
if (callback::get_config_content)
{
char* cont = callback::get_config_content(local_trans::u2a(scanner_name_.c_str(), CP_UTF8).c_str(), NULL);
if (cont)
{
if (*len <= strlen(cont))
{
*len = strlen(cont) + 4;
ret = SCANNER_ERR_INSUFFICIENT_MEMORY;
}
else
{
strcpy(buf, cont);
ret = SCANNER_ERR_OK;
*len = strlen(cont);
}
if (callback::twain_ui_free)
callback::twain_ui_free(cont);
}
}
return ret;
}
COM_API_IMPLEMENT(scanner, int, twain_set_config(char* buf, size_t len))
{
if(callback::apply_given_config)
{
if(callback::apply_given_config(buf, handle_, &sane_api_) == 0)
return SCANNER_ERR_OK;
}
return SCANNER_ERR_DATA_DAMAGED;
}
// 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))
{
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, NULL);
break;
default:
break;
}
};
int res = callback::show_setting_ui(handle_, parent, &sane_api_, local_trans::u2a(scanner_name_.c_str(), CP_UTF8).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, NULL);
//}
//else if (res == UI_RESULT_CLOSE_CANCEL)
//{
// int ev = SANE_EVENT_UI_CLOSE_CANCEL;
// on_ui_event(ev, (void*)ev);
//}
//on_ui_event(ev, NULL);
}
else if (cfg_)
{
if (with_scan)
{
events_.clear();
images_.clear();
scan_msg_ = "OK";
scan_err_ = false;
}
size_t pid = scanner_name_.find(L" - ");
if (pid == -1)
pid = scanner_name_.length();
setting_.reset(new dlg_setting(parent, &sane_api_, handle_, with_scan, scanner_name_.substr(0, pid).c_str()));
setting_->set_ui_event_notify(&scanner::ui_callback, this);
setting_->set_config(cfg_, (cfg_path_ + scanner_name_.substr(0, pid) + L".cfg").c_str(), &scanner::apply_scheme, this, &twain_set_);
indicator_.reset();
if (indicator)
{
indicator_.reset(new dlg_indicator(setting_->hwnd()));
indicator_->set_ui_event_notify(&scanner::ui_callback, this);
}
setting_->show(true);
}
return true;
}
COM_API_IMPLEMENT(scanner, bool, ui_show_progress(HWND parent, bool bIndicator))
{
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);
}
else if(bIndicator)
{
if (setting_.get() && IsWindowVisible(setting_->hwnd()))
parent = setting_->hwnd();
else if (!IsWindow(parent))
parent = callback::find_main_wnd();
indicator_.reset(new dlg_indicator(parent));
indicator_->set_ui_event_notify(&scanner::ui_callback, this);
indicator_->show(true);
}
return true;
}
COM_API_IMPLEMENT(scanner, void, ui_hide(void))
{
if (indicator_.get())
indicator_.reset();
if (setting_.get())
setting_.reset();
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 true;
}
// 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 (indicator_.get())
indicator_->notify_working();
else if (callback::show_progress_ui && is_bIndicator)
ui_notify(ev_code, data, *len);
on_ui_event(ev_code, (void*)ev_code);
log_info(L"Scanning ...\r\n", 1);
}
else if (ev_code == SANE_EVENT_IMAGE_OK)
{
SANE_Image* simg = (SANE_Image*)data;
scanned_img* img = NULL;
wchar_t name[40] = { 0 };
swprintf_s(name, _countof(name) - 1, L"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)
log_info(L"Memory usage upto limit! wait up to 100 ms ...\r\n", 1);
}
images_.save(img, img->bytes());
}
else
{
img->release();
}
if (indicator_.get())
indicator_->notify_data_arrived(true);
else if (ui_notify)
ui_notify(ev_code, data, img_ind_);
{
wchar_t msg[128] = { 0 };
swprintf_s(msg, _countof(msg) - 1, L"New image(%u) received with %u bytes\r\n", img_ind_, simg->bytes);
log_info(msg, 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 (indicator_.get())
indicator_->notify_scan_over((char*)data, *len != SCANNER_ERR_OK);
else 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);
}
else // windows message box ...
{
std::wstring 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);
}
// is_scanning_ = false;
{
wchar_t msg[128] = { 0 };
swprintf_s(msg, _countof(msg) - 1, L"Scan finished with error: %u\r\n", *len);
log_info(msg, 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::wstring 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;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// log ...
#include <direct.h>
std::mutex g_lock_;
std::string g_path_file_("");
FILE* g_file_ = NULL;
static int g_log_level = 1;
static int get_log_level(const char* log_file)
{
std::string cfg(log_file);
size_t pos = cfg.rfind('\\');
cfg.erase(pos);
pos = cfg.rfind('\\');
if (pos != std::string::npos)
{
if (stricmp(cfg.substr(pos).c_str(), "\\log") == 0)
{
cfg.erase(pos);
cfg += "\\config";
}
}
cfg += "\\debug.cfg";
return GetPrivateProfileIntA("log", "twain-level", 1, cfg.c_str());
}
void init_log(void)
{
char* tmp = getenv("LOCALAPPDATA");
if (tmp)
{
std::string path(tmp);
char name[MAX_PATH] = { 0 }, * last = NULL;
path += std::string("\\") + PRODUCT_VENDOR + "Scan\\Log\\";
mkdir(path.c_str());
GetModuleFileNameA(NULL, name, _countof(name) - 1);
last = strrchr(name, '\\');
if (last++ == NULL)
last = name;
path += last;
path += "_twain.log";
g_file_ = fopen(path.c_str(), "a+b");
g_path_file_ = path;
}
else
{
char name[MAX_PATH] = { 0 }, * last = NULL;
std::string path("");
GetModuleFileNameA(NULL, name, _countof(name) - 1);
path = std::string(name) + "_twain.log";
g_file_ = fopen(path.c_str(), "a+b");
g_path_file_ = path;
}
if (g_file_)
{
fseek(g_file_, 0, SEEK_END);
if (ftell(g_file_))
{
std::wstring sep(L"\r\n\r\n\r\n=======================================================\r\n");
fwrite(sep.c_str(), 2, sep.length(), g_file_);
}
else
{
unsigned short bom = 0x0feff;
fwrite(&bom, sizeof(bom), 1, g_file_);
}
wchar_t ts[128] = { 0 }, now[40] = { 0 };
hg_get_current_time_w(now);
g_log_level = get_log_level(g_path_file_.c_str());
swprintf_s(ts, _countof(ts) - 1, L"==================%s - %d, Proc %d==================\r\n", now, g_log_level, GetCurrentProcessId());
fwrite(ts, 2, lstrlenW(ts), g_file_);
}
}
void close_log(void)
{
if (g_file_)
fclose(g_file_);
g_file_ = NULL;
}
void log(const wchar_t* info)
{
if (g_file_)
{
std::lock_guard<std::mutex> lock(g_lock_);
fwrite(info, 2, lstrlenW(info), g_file_);
fflush(g_file_);
if (ftell(g_file_) > 10 * 1024 * 1024)
{
fclose(g_file_);
remove(g_path_file_.c_str());
g_file_ = fopen(g_path_file_.c_str(), "wb");
if (g_file_)
{
unsigned short bom = 0x0feff;
wchar_t ts[128] = { 0 }, now[40] = { 0 };
hg_get_current_time_w(now);
swprintf_s(ts, _countof(ts) - 1, L"==================%s (Truncated when size > 10MB) ==================\r\n", now);
fwrite(&bom, sizeof(bom), 1, g_file_);
fwrite(ts, 2, lstrlenW(ts), g_file_);
}
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// exports
extern "C"
{
#ifdef EXPORT_SANE_API
__declspec(dllexport)
#else
__declspec(dllimport)
#endif
int __stdcall initialize(void* reserve)
{
init_log();
callback::init_ui();
hg_sane_middleware::set_callback(callback::sane_event_callback, NULL);
if (hg_sane_middleware::instance()->is_ready())
return SANE_STATUS_GOOD;
else
return SCANNER_ERR_LANG_PAK_LOST;
}
#ifdef EXPORT_SANE_API
__declspec(dllexport)
#else
__declspec(dllimport)
#endif
int __stdcall 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(callback::language_trans("\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, NULL)),
title(callback::language_trans("\xE9\x94\x99\xE8\xAF\xAF", true, NULL));
if (callback::show_messagebox_ui)
callback::show_messagebox_ui(parent, 0, &msg[0], SCANNER_ERR_DEVICE_NOT_FOUND);
else
MessageBoxW(parent, local_trans::a2u(msg.c_str(), CP_UTF8).c_str(), local_trans::a2u(title.c_str(), CP_UTF8).c_str(), MB_OK);
}
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 = NULL;
if (last_try)
{
HWND parent = callback::find_main_wnd();
std::string msg(callback::language_trans("\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, NULL)),
title(callback::language_trans("\xE9\x94\x99\xE8\xAF\xAF", true, NULL));
if (callback::show_messagebox_ui)
callback::show_messagebox_ui(parent, 0, &msg[0], ret);
else
MessageBoxW(parent, local_trans::a2u(msg.c_str(), CP_UTF8).c_str(), local_trans::a2u(title.c_str(), CP_UTF8).c_str(), MB_OK);
}
return ret;
}
}
#ifdef EXPORT_SANE_API
__declspec(dllexport)
#else
__declspec(dllimport)
#endif
bool __stdcall is_scanner_online(SCANNERID scanner_id)
{
std::vector<std::string> que;
scanner::get_scanner_name(scanner_id, que);
return !que.empty();
}
#ifdef EXPORT_SANE_API
__declspec(dllexport)
#else
__declspec(dllimport)
#endif
int __stdcall uninitialize(void* reserve)
{
hg_sane_middleware::set_callback(NULL, NULL);
hg_sane_middleware::clear();
close_log();
callback::unint_ui();
return 0;
}
#ifdef EXPORT_SANE_API
__declspec(dllexport)
#else
__declspec(dllimport)
#endif
void __stdcall log_info(const wchar_t* info, int level)
{
if(level >= g_log_level)
log(info);
}
}