From fdd12a953c409bbadf495fc2ee31d3df1ae80df8 Mon Sep 17 00:00:00 2001 From: gb <741021719@qq.com> Date: Fri, 1 Jul 2022 15:24:58 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=85=8D=E7=BD=AE=E5=AD=98?= =?UTF-8?q?=E5=8F=96=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sane/DlgIndicator.cpp | 14 +- sane/DlgIndicator.h | 1 + sane/DlgPage.cpp | 93 +++- sane/DlgPage.h | 13 +- sane/DlgSetting.cpp | 57 +- sane/DlgSetting.h | 9 +- sane/gb_json.cpp | 1073 +++++++++++++++++++++++++++++++++++++ sane/gb_json.h | 116 ++++ sane/s2t_api.h | 2 + sane/sane.vcxproj | 2 + sane/sane.vcxproj.filters | 6 + sane/scanner.cpp | 127 ++++- sane/scanner.h | 11 + twain/twain/huagaods.cpp | 102 +++- twain/twain/huagaods.hpp | 1 + 15 files changed, 1580 insertions(+), 47 deletions(-) create mode 100644 sane/gb_json.cpp create mode 100644 sane/gb_json.h diff --git a/sane/DlgIndicator.cpp b/sane/DlgIndicator.cpp index 8544599..c03d371 100644 --- a/sane/DlgIndicator.cpp +++ b/sane/DlgIndicator.cpp @@ -6,9 +6,10 @@ #include "scanned_img.h" // for local_trans // CDlgIndicator 对话框 -#define WM_USB_PACKET_RECEIVED WM_USER + 1 -#define WM_IMAGE_RECEIVED WM_USER + 2 -#define WM_SCAN_FINISHED WM_USER + 3 // WPARAM: std::string* msg; LPARAM: boo err +#define WM_SCAN_WORKING WM_USER + 1 // WPARAM: unused; LPARAM: unsed +#define WM_USB_PACKET_RECEIVED WM_USER + 2 // WPARAM: unused; LPARAM: unsed +#define WM_IMAGE_RECEIVED WM_USER + 3 // WPARAM: unused; LPARAM: unsed +#define WM_SCAN_FINISHED WM_USER + 4 // WPARAM: std::string* msg; LPARAM: boo err dlg_indicator::dlg_indicator(HWND parent) : dlg_base(parent, IDD_INDICATOR) @@ -62,6 +63,9 @@ BOOL dlg_indicator::handle_message(UINT msg, WPARAM wp, LPARAM lp) } ret = FALSE; break; + case WM_SCAN_WORKING: + notify_ui_event(UI_EVENT_SCAN_WORKING); + break; case WM_SCAN_FINISHED: if (lp) { @@ -158,4 +162,8 @@ void dlg_indicator::notify_scan_over(const char* msg, bool err) notify_over(false); } } +void dlg_indicator::notify_working(void) +{ + PostMessage(hwnd_, WM_SCAN_WORKING, 0, 0); +} // CDlgIndicator 消息处理程序 diff --git a/sane/DlgIndicator.h b/sane/DlgIndicator.h index d4a1353..789d232 100644 --- a/sane/DlgIndicator.h +++ b/sane/DlgIndicator.h @@ -28,4 +28,5 @@ public: void hide(void); void notify_data_arrived(bool image); void notify_scan_over(const char* msg, bool err); + void notify_working(void); }; diff --git a/sane/DlgPage.cpp b/sane/DlgPage.cpp index 8d2a208..69caa78 100644 --- a/sane/DlgPage.cpp +++ b/sane/DlgPage.cpp @@ -6,6 +6,7 @@ #include "scanned_img.h" // for local_trans #include "DlgArea.h" #include "DlgGamma.h" +#include "gb_json.h" ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // @@ -105,6 +106,14 @@ void dlg_base::notify_ui_event(ui_event ev) if (ui_event_notify_) ui_event_notify_(ev, this, ui_notify_param_); } +gb::sane_config* dlg_base::get_config(void) +{ + gb::sane_config* cfg = NULL; + + SendMessage(parent_, WM_GET_CONFIG_OBJ, 0, (LPARAM)&cfg); + + return cfg; +} void dlg_base::set_ui_event_notify(void(__stdcall* notify)(ui_event, void*, void*), void* param) { @@ -115,6 +124,51 @@ HWND dlg_base::hwnd(void) { return hwnd_; } +void dlg_base::show(bool visible) +{ + UINT cmd = SW_HIDE; + if (visible) + { + DWORD style = GetWindowLong(hwnd_, GWL_STYLE); + + cmd = SW_SHOW; + if (!(style & WS_CHILD)) + { + RECT r0 = { 0 }, rp = { 0 }, rme = { 0 }; + POINT pt = { 0 }; + + if (IsWindow(parent_)) + { + HMONITOR mon = MonitorFromWindow(parent_, MONITOR_DEFAULTTOPRIMARY); + MONITORINFO mi = { 0 }; + + mi.cbSize = sizeof(mi); + GetMonitorInfoW(mon, &mi); + r0 = mi.rcWork; + GetWindowRect(parent_, &rp); + } + else + { + GetWindowRect(GetDesktopWindow(), &r0); + rp = r0; + } + GetWindowRect(hwnd_, &rme); + pt.x = rp.left + (RECT_W(rp) - RECT_W(rme)) / 2; + pt.y = rp.top + (RECT_H(rp) - RECT_H(rme)) / 2; + if (pt.x + RECT_W(rme) > r0.right) + pt.x = r0.right - RECT_W(rme); + if (pt.x < r0.left) + pt.x = r0.left; + if (pt.y + RECT_H(rme) > r0.bottom) + pt.y = r0.bottom - RECT_H(rme); + if (pt.y < r0.top) + pt.y = r0.top; + SetWindowPos(hwnd_, HWND_TOPMOST, pt.x, pt.y, RECT_W(rme), RECT_H(rme), SWP_NOSIZE); + UpdateWindow(hwnd_); + } + } + ShowWindow(hwnd_, cmd); +} void dlg_base::enable(bool enable) { EnableWindow(hwnd_, enable); @@ -536,16 +590,26 @@ void dlg_page::handle_command(WORD code, WORD id, HANDLE ctrl) { SANE_Fixed val = SANE_FIX(dlg.x_in_mm()); SANE_Int after = 0; + gb::sane_config* cfg = get_config(); + sane_.sane_control_option_api(dev_, id_custom_left_, SANE_ACTION_SET_VALUE, &val, &after); + if (cfg) + cfg->config_changed(id_custom_left_, (char*)&val, sizeof(val)); val = SANE_FIX(dlg.y_in_mm()); sane_.sane_control_option_api(dev_, id_custom_top_, SANE_ACTION_SET_VALUE, &val, &after); + if (cfg) + cfg->config_changed(id_custom_top_, (char*)&val, sizeof(val)); val = SANE_FIX(dlg.x_in_mm() + dlg.w_in_mm()); sane_.sane_control_option_api(dev_, id_custom_right_, SANE_ACTION_SET_VALUE, &val, &after); + if (cfg) + cfg->config_changed(id_custom_right_, (char*)&val, sizeof(val)); val = SANE_FIX(dlg.y_in_mm() + dlg.h_in_mm()); sane_.sane_control_option_api(dev_, id_custom_bottom_, SANE_ACTION_SET_VALUE, &val, &after); + if (cfg) + cfg->config_changed(id_custom_bottom_, (char*)&val, sizeof(val)); } return; } @@ -583,9 +647,11 @@ BOOL dlg_page::on_notify(int ctrl_id, LPNMHDR pnmh) else if (IS_UPDOWN_ARROW(cls)) { HWND host = (HWND)GetPropW(pnmh->hwndFrom, dlg_page::property_host.c_str()); - if(IsWindow(host) && (int)GetPropW(host, dlg_page::property_type.c_str()) == SANE_TYPE_INT) + if (IsWindow(host) && (int)GetPropW(host, dlg_page::property_type.c_str()) == SANE_TYPE_INT) return FALSE; } + else if (IS_BUTTON(cls)) + return FALSE; control_action(pnmh->hwndFrom); @@ -798,7 +864,31 @@ void dlg_page::control_action(HWND wnd) { SANE_Int after = 0; SANE_Status statu = sane_.sane_control_option_api(dev_, id - dlg_page::dyn_id_base, SANE_ACTION_SET_VALUE, val, &after); + gb::sane_config* cfg = get_config(); done_ = false; + if (cfg) + { + size_t len = 0; + switch (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->config_changed(id - dlg_page::dyn_id_base, (char*)val, len); + } set_ctrl_value(wnd, type, val, false, !(after || statu)); done_ = true; if (id == dlg_page::dyn_id_base + id_dpi_) @@ -1010,6 +1100,5 @@ const wchar_t* dlg_page::name(void) return name_.c_str(); } - ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // diff --git a/sane/DlgPage.h b/sane/DlgPage.h index 64f1186..cd6c283 100644 --- a/sane/DlgPage.h +++ b/sane/DlgPage.h @@ -8,16 +8,24 @@ #include // CDlgIndicator 对话框 +#define RECT_W(r) (r.right - r.left) +#define RECT_H(r) (r.bottom - r.top) #define WM_REFRESH_OPTION WM_USER + 111 // WPARAM: source option SN, LPARAM: unused now +#define WM_GET_CONFIG_OBJ WM_USER + 112 // WPARAM: not use, LPARAM: to receive the gb::sane_config* object enum ui_event { UI_EVENT_CLOSE_NORMAL = 0, UI_EVENT_CLOSE_CANCEL, - UI_EVENT_BEGIN_SCANNING, + UI_EVENT_BEGIN_SCANNING, // setting ui notify button 'scan' clicked + UI_EVENT_SCAN_WORKING, // scanner thread notify working }; extern HMODULE g_my_inst; +namespace gb +{ + class sane_config; +} class dlg_base { @@ -34,6 +42,7 @@ protected: virtual void on_font_changed(void); void create(void); void notify_ui_event(ui_event ev); + gb::sane_config* get_config(void); public: dlg_base(HWND parent, UINT idd); @@ -45,6 +54,7 @@ public: public: void set_ui_event_notify(void(__stdcall* notify)(ui_event, void*, void*), void* param); HWND hwnd(void); + void show(bool visible); void enable(bool enable); void screen_2_client(LPRECT r); void client_2_screen(LPRECT r); @@ -85,6 +95,7 @@ class dlg_page : public dlg_base static int gap_y; static int spin_w; + BOOL handle_message(UINT msg, WPARAM wp, LPARAM lp) override; void on_font_changed(void) override; diff --git a/sane/DlgSetting.cpp b/sane/DlgSetting.cpp index e2049e2..5dc8e53 100644 --- a/sane/DlgSetting.cpp +++ b/sane/DlgSetting.cpp @@ -5,6 +5,7 @@ #include "resource.h" #include "scanned_img.h" // for local_trans #include "DlgPage.h" +#include "gb_json.h" // CDlgIndicator 对话框 #define WM_USB_PACKET_RECEIVED WM_USER + 1 @@ -17,7 +18,7 @@ static IMPLEMENT_OPTION_STRING_COMPARE(cmp_sane_opt); dlg_setting::dlg_setting(HWND parent, LPSANEAPI api, SANE_Handle dev, bool with_scan, const wchar_t* name) : dlg_base(parent, IDD_SETTING) , sane_api_(*api), sane_dev_(dev), with_scan_(with_scan) - , papers_(0), images_(0), err_(false), tab_(NULL) + , papers_(0), images_(0), err_(false), tab_(NULL), cfg_(NULL), cfg_file_(L"") { create(); SetWindowTextW(hwnd(), (std::wstring(name) + L" \u8bbe\u7f6e").c_str()); @@ -37,6 +38,7 @@ dlg_setting::~dlg_setting() } DestroyWindow(tab_); } + cfg_->end_setting(false); } BOOL dlg_setting::handle_message(UINT msg, WPARAM wp, LPARAM lp) @@ -58,6 +60,9 @@ BOOL dlg_setting::handle_message(UINT msg, WPARAM wp, LPARAM lp) case WM_REFRESH_OPTION: refresh_controls((int)wp); break; + case WM_GET_CONFIG_OBJ: + *((gb::sane_config**)lp) = cfg_; + break; default: ret = FALSE; break; @@ -81,6 +86,12 @@ void dlg_setting::handle_command(WORD code, WORD id, HANDLE ctrl) SANE_Int after = 0; SANE_Status statu = sane_api_.sane_control_option_api(sane_dev_, id_restore_, SANE_ACTION_SET_VALUE, &after, &after); refresh_controls(id_restore_); + if (cfg_) + { + cfg_->begin_setting(true); + cfg_->end_setting(false); + cfg_->begin_setting(); + } } else if (id == IDC_BUTTON_SCAN) { @@ -328,6 +339,11 @@ void dlg_setting::refresh_controls(int src_sn) } } +void dlg_setting::set_config(gb::sane_config* cfg, const wchar_t* file) +{ + cfg_ = cfg; + cfg_file_ = file; +} HWND dlg_setting::window(void) { return hwnd_; @@ -336,21 +352,30 @@ HWND dlg_setting::parent(void) { return parent_; } -void dlg_setting::show(void) -{ - RECT rp, r; - - if (IsWindow(parent_)) - GetWindowRect(parent_, &rp); - else - GetWindowRect(GetDesktopWindow(), &rp); - GetWindowRect(hwnd_, &r); - - rp.left += (rp.right - rp.left - (r.right - r.left)) / 2; - rp.top += (rp.bottom - rp.top - (r.bottom - r.top)) / 2; - SetWindowPos(hwnd_, HWND_TOPMOST, rp.left, rp.top, r.right - r.left, r.bottom - r.top, SWP_NOSIZE | SWP_SHOWWINDOW); - UpdateWindow(hwnd_); -} +//void dlg_setting::show(void) +//{ +// RECT rp, r, desk; +// +// if (IsWindow(parent_)) +// GetWindowRect(parent_, &rp); +// else +// GetWindowRect(GetDesktopWindow(), &rp); +// GetWindowRect(hwnd_, &r); +// GetWindowRect(GetDesktopWindow(), &desk); +// +// rp.left += (rp.right - rp.left - (r.right - r.left)) / 2; +// rp.top += (rp.bottom - rp.top - (r.bottom - r.top)) / 2; +// if (rp.top > desk.bottom) +// rp.top = desk.bottom - (r.bottom - r.top); +// if (rp.top < desk.top) +// rp.top = desk.top; +// if (rp.left > desk.right) +// rp.left = desk.right - (rp.right - rp.left); +// if (rp.left < desk.left) +// rp.left = desk.left; +// SetWindowPos(hwnd_, HWND_TOPMOST, rp.left, rp.top, r.right - r.left, r.bottom - r.top, SWP_NOSIZE | SWP_SHOWWINDOW); +// UpdateWindow(hwnd_); +//} void dlg_setting::hide(void) { ShowWindow(hwnd_, SW_HIDE); diff --git a/sane/DlgSetting.h b/sane/DlgSetting.h index f419e5d..da7221c 100644 --- a/sane/DlgSetting.h +++ b/sane/DlgSetting.h @@ -15,6 +15,10 @@ #include #endif +namespace gb +{ + class sane_config; +} class dlg_setting : public dlg_base { SANEAPI sane_api_; @@ -25,6 +29,8 @@ class dlg_setting : public dlg_base bool err_; int id_help_; int id_restore_; + gb::sane_config* cfg_; + std::wstring cfg_file_; HWND tab_; @@ -49,9 +55,10 @@ public: ~dlg_setting(); public: + void set_config(gb::sane_config* cfg, const wchar_t* file); HWND window(void); HWND parent(void); - void show(void); + //void show(void); void hide(void); void notify_data_arrived(bool image); void notify_scan_over(const char* msg, bool err); diff --git a/sane/gb_json.cpp b/sane/gb_json.cpp new file mode 100644 index 0000000..d22b0a4 --- /dev/null +++ b/sane/gb_json.cpp @@ -0,0 +1,1073 @@ + +#include "gb_json.h" +#include +#include + +#ifdef WIN32 +#define bzero(b, s) memset(b, 0, s) +#endif + +namespace gb +{ + static std::vector split_with(const char* str, const char* splitor = "/") + { + std::vector ret; + std::string src(str); + size_t pos = src.find(splitor); + + while(pos != std::string::npos) + { + if(pos++) + ret.push_back(src.substr(0, pos - 1)); + src.erase(0, pos + strlen(splitor)); + pos = src.find(splitor); + } + if(src.length()) + ret.push_back(src); + + return ret; + } + + json::json(char* json_txt) : obj_(0), cur_child_(0), is_array_(false) + { + attach_text(json_txt); + } + json::~json() + { + clear(); + } + + std::string json::to_string(cJSON* root, bool formatted) + { + char* txt = formatted ? cJSON_Print(root) : cJSON_PrintUnformatted(root); + std::string ret(txt ? txt : ""); + + if (txt) + free(txt); + + return ret; + } + std::string json::get_value_as_string(cJSON* root, bool integer) + { + std::string ret(""); + + switch (root->type) + { + case cJSON_False: + ret = "false"; + break; + case cJSON_True: + ret = "true"; + break; + case cJSON_NULL: + ret = "null"; + break; + case cJSON_Number: + { + char buf[40]; + if (integer) + sprintf(buf, "%d", root->valueint); + else + sprintf(buf, "%f", root->valuedouble); + ret = buf; + } + break; + case cJSON_String: + if (root->valuestring) + ret = root->valuestring; + break; + default: + ret = json::to_string(root, false); + break; + } + + return ret; + } + void json::free_node_data(cJSON* node) + { + if (node->type == cJSON_String && node->valuestring) + free(node->valuestring); + else if((node->type == cJSON_Object || node->type == cJSON_Array) && node->child) + cJSON_Delete(node->child); + + node->type = cJSON_NULL; + node->valuestring = NULL; + node->child = NULL; + } + cJSON* json::create_element_with_name(const char* name) + { + cJSON* obj = cJSON_CreateObject(); + + bzero(obj, sizeof(*obj)); + if(name) + { + obj->string = (char*)malloc(strlen(name) + 4); + bzero(obj->string, strlen(name) + 4); + strcpy(obj->string, name); + } + + return obj; + } + + cJSON* json::find_sibling(cJSON* first, const char* name, cJSON*** prev) + { + cJSON* now = first, **prv = NULL; + while(now) + { + if(now->string && strcmp(now->string, name) == 0) + { + break; + } + prv = &now->next; + now = now->next; + } + if(prev) + *prev = prv; + + return now; + } + cJSON* json::find_child(cJSON *parent, std::vector& path, bool create, cJSON*** addr) + { + if(!parent->child) + { + if(!create) + return NULL; + + parent->child = json::create_element_with_name(path[0].c_str()); + if(path.size() == 1) + { + if(addr) + *addr = &parent->child; + + return parent->child; + } + } + + cJSON** prev = NULL, + *now = find_sibling(parent->child, path[0].c_str(), &prev); + if(!now) + { + if(!create) + return now; + + now = json::create_element_with_name(path[0].c_str()); + if(prev) + *prev = now; + else + { + obj_->child = now; + prev = &obj_->child; + } + } + + path.erase(path.begin()); + if(path.empty()) + { + if(addr) + *addr = prev ? prev : &parent->child; + + return now; + } + + return find_child(now, path, create, addr); + } + cJSON* json::find(const char* path, bool create, cJSON*** addr) + { + std::vector tree(split_with(path)); + + if(tree.empty()) + return NULL; + + if(!obj_) + { + if(!create) + return NULL; + + obj_ = cJSON_CreateObject(); + obj_->child = json::create_element_with_name(tree[0].c_str()); + } + + return find_child(obj_, tree, create, addr); + } + + bool json::attach_text(char* json_txt) + { + clear(); + + obj_ = cJSON_Parse(json_txt); + if(obj_) + is_array_ = obj_->type == cJSON_Array; + + return obj_ != 0; + } + bool json::attach_cjson(cJSON* cjson) + { + clear(); + + if (cjson) + { + std::string txt(json::to_string(cjson, false)); + if (txt.length()) + obj_ = cJSON_Parse(txt.c_str()); + } + if(obj_) + is_array_ = obj_->type == cJSON_Array; + + return obj_ != 0; + } + bool json::create_empty(bool array) + { + clear(); + + obj_ = array ? cJSON_CreateArray() : cJSON_CreateObject(); + is_array_ = array; + + return true; + } + void json::clear(void) + { + if (obj_) + { + cJSON_Delete(obj_); + obj_ = 0; + } + } + std::string json::to_string(bool formatted) + { + if (obj_) + return json::to_string(obj_, formatted); + else + return ""; + } + + bool json::get_value(const char* key, bool& val) + { + cJSON* obj = find(key); + + if (!obj) + return false; + + if (obj->type == cJSON_True) + val = true; + else if (obj->type == cJSON_False) + val = false; + else + return false; + + return true; + } + bool json::get_value(const char* key, int& val) + { + cJSON* obj = find(key); + + if (!obj) + return false; + + if (obj->type != cJSON_Number) + return false; + + val = obj->valueint; + + return true; + } + bool json::get_value(const char* key, double& val) + { + cJSON *obj = find(key); + + if (!obj) + return false; + + if (obj->type != cJSON_Number) + return false; + + val = obj->valuedouble; + + return true; + } + bool json::get_value(const char* key, std::string& val) + { + cJSON *obj = find(key); + + if (!obj) + return false; + + if (obj->type != cJSON_String) + return false; + + val = obj->valuestring ? obj->valuestring : ""; + + return true; + } + bool json::get_value(const char* key, json*& val) + { + cJSON *obj = find(key); + + if (!obj) + return false; + + val = new json(); + if (!val->attach_cjson(obj)) + { + delete val; + + return false; + } + + return true; + } + bool json::get_value_as_string(const char* key, std::string& val, bool integer) + { + cJSON* obj = find(key); + + if (!obj) + return false; + + val = json::get_value_as_string(obj, integer); + + return true; + } + bool json::get_as_array(const char* key, std::vector& val) + { + cJSON *obj = find(key); + + val.clear(); + if (obj && obj->type == cJSON_Array) + { + cJSON *child = obj->child; + while (child) + { + if (child->type == cJSON_Number) + { + char buf[40]; + sprintf(buf, "%d", child->valueint); + val.push_back(buf); + } + else if (child->type == cJSON_String) + val.push_back(child->valuestring ? child->valuestring : ""); + else + { + char *text = cJSON_Print(child); + val.push_back(text); + free(text); + } + + child = child->next; + } + + return true; + } + + return false; + } + + bool json::first_child(std::string& val, std::string* name) + { + cur_child_ = obj_->child; + val = ""; + if (cur_child_) + { + val = json::get_value_as_string(cur_child_); + if (name && cur_child_->string) + *name = cur_child_->string; + + return true; + } + else + { + return false; + } + } + bool json::next_child(std::string& val, std::string* name) + { + if (cur_child_) + cur_child_ = cur_child_->next; + + val = ""; + if (cur_child_) + { + val = json::get_value_as_string(cur_child_); + if (name && cur_child_->string) + *name = cur_child_->string; + + return true; + } + else + { + return false; + } + } + + bool json::set_value(const char* key, bool val) + { + if(!key) + { + if(is_array_) + { + if(!obj_) + obj_ = cJSON_CreateArray(); + cJSON_AddItemToArray(obj_, val ? cJSON_CreateTrue() : cJSON_CreateFalse()); + } + + return is_array_; + } + + cJSON* ele = this->find(key, true); + + if (!ele) + return false; + + json::free_node_data(ele); + + if (val) + ele->type = cJSON_True; + else + ele->type = cJSON_False; + + return true; + } + bool json::set_value(const char* key, int val) + { + if(!key) + { + if(is_array_) + { + if(!obj_) + obj_ = cJSON_CreateArray(); + cJSON_AddItemToArray(obj_, cJSON_CreateNumber(val)); + } + + return is_array_; + } + + cJSON* ele = this->find(key, true); + + if (!ele) + return false; + + json::free_node_data(ele); + + ele->type = cJSON_Number; + ele->valuedouble = ele->valueint = val; + + return true; + } + bool json::set_value(const char* key, double val) + { + if(!key) + { + if(is_array_) + { + if(!obj_) + obj_ = cJSON_CreateArray(); + cJSON_AddItemToArray(obj_, cJSON_CreateNumber(val)); + } + + return is_array_; + } + + cJSON* ele = this->find(key, true); + + if (!ele) + return false; + + json::free_node_data(ele); + + ele->type = cJSON_Number; + ele->valuedouble = val; + + return true; + } + bool json::set_value(const char* key, std::string val) + { + if(!key) + { + if(is_array_) + { + if(!obj_) + obj_ = cJSON_CreateArray(); + cJSON_AddItemToArray(obj_, cJSON_CreateString(val.c_str())); + } + + return is_array_; + } + + cJSON* ele = this->find(key, true); + + if (!ele) + return false; + + json::free_node_data(ele); + + ele->type = cJSON_String; + ele->valuestring = (char*)malloc(val.length() + 4); + bzero(ele->valuestring, val.length() + 4); + strcpy(ele->valuestring, val.c_str()); + + return true; + } + bool json::set_value(const char* key, const char* val) + { + return set_value(key, std::string(val)); + } + bool json::set_value(const char* key, json* obj) + { + if(!key) + { + if(is_array_) + { + if(!obj_) + obj_ = cJSON_CreateArray(); + if(obj && obj->obj_) + { + cJSON_AddItemToArray(obj_, obj->obj_); + obj->obj_ = NULL; + } + } + + return is_array_; + } + + cJSON** addr = NULL; + cJSON* ele = this->find(key, true, &addr); + + if (!ele) + return false; + +// json::free_node_data(ele); + cJSON_Delete(ele); + *addr = obj->obj_; + ele = obj->obj_; + if(ele->string) + free(ele->string); + ele->string = (char*)malloc(strlen(key) + 4); + bzero(ele->string, strlen(key) + 4); + strcpy(ele->string, key); + obj->obj_ = NULL; + + return true; + } + + bool json::remove(const char* key) + { + if(!obj_) + return false; + + cJSON **addr = NULL, + *ele = find(key, false, &addr); + + if(ele) + { + if(addr) + *addr = NULL; + cJSON_Delete(ele); + + return true; + } + else + return false; + } +} + + + +namespace gb +{ + static char base64_default_table[] = { "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" }; + + base64::base64() : padding_char_('=') + { + base64_ind_[0] = base64_char_[0] = 0; + + initialize_base64_table(base64_default_table); + } + base64::~base64() + {} + + bool base64::is_valid_base64_table(const char* table) + { + bool valid = false; + + if (table && strlen(table) >= 64) + { + char repeat[4] = { 0 }; + + valid = true; + for (int i = 0; i < 63; ++i) + { + repeat[0] = table[i]; + if (strstr(table + i + 1, repeat)) + { + valid = false; + break; + } + } + } + + return valid; + } + bool base64::initialize_base64_table(const char* table) + { + if (!table || strlen(table) < 64) + { + if (memcmp(base64_default_table, base64_char_, 64) == 0) + { + return !table; + } + + memcpy(base64_char_, base64_default_table, 64); + } + else + { + if (memcmp(base64_char_, table, 64) == 0) + { + return true; + } + else if (!is_valid_base64_table(table)) + return false; + + memcpy(base64_char_, table, 64); + } + base64_char_[64] = base64_char_[65] = 0; + + // initialize base64_index + memset(base64_ind_, 0, sizeof(base64_ind_)); + for (int i = 0; i < 64; ++i) + { + base64_ind_[base64_char_[i]] = i; + } + + // padding char + padding_char_ = '='; + if (base64_ind_[padding_char_]) + { + for (padding_char_ = 0x21; padding_char_ < 0x7e && base64_ind_[padding_char_] && padding_char_ != base64_char_[0]; ++padding_char_); + } + + return padding_char_ < 0x7e; + } + + bool base64::set_base64_table(const char* table) + { + return initialize_base64_table(table ? table : base64_default_table); + } + std::string base64::encode(const char* data, size_t bytes, unsigned int line_bytes, bool need_padding) + { + char* str = (char*)malloc(bytes * 2 + 3); + unsigned char c1 = 0, c2 = 0, c3 = 0; + unsigned long line_len = 0; + unsigned long words = bytes / 3; + int rest = bytes % 3, + pos = 0; + std::string ret(""); + + for (unsigned long i = 0; i < words; ++i) + { + // fetch 3 letters + c1 = *data++; + c2 = *data++; + c3 = *data++; + + // encoding into 4-bytes + str[pos++] = base64_char_[c1 >> 2]; + str[pos++] = base64_char_[((c1 << 4) | (c2 >> 4)) & 0x3f]; + str[pos++] = base64_char_[((c2 << 2) | (c3 >> 6)) & 0x3f]; + str[pos++] = base64_char_[c3 & 0x3f]; + line_len += 4; + + // new line ... + if ((unsigned int)line_len > line_bytes - 4) + { + str[pos++] = '\r'; + str[pos++] = '\n'; + line_len = 0; + } + } + + // rest ... + if (rest == 1) + { + c1 = *data++; + str[pos++] = base64_char_[(c1 & 0xfc) >> 2]; + str[pos++] = base64_char_[((c1 & 0x03) << 4)]; + if (need_padding) + { + str[pos++] = padding_char_; + str[pos++] = padding_char_; + } + } + else if (rest == 2) + { + c1 = *data++; + c2 = *data++; + str[pos++] = base64_char_[(c1 & 0xfc) >> 2]; + str[pos++] = base64_char_[((c1 & 0x03) << 4) | ((c2 & 0xf0) >> 4)]; + str[pos++] = base64_char_[((c2 & 0x0f) << 2)]; + if (need_padding) + { + str[pos++] = padding_char_; + } + } + if (pos > 0) + { + str[pos++] = 0; + ret = std::string(str, pos - 1); + } + free(str); + + return ret; + } + std::string base64::decode(const char* data, size_t bytes) + { + char* str = (char*)malloc(bytes + 1); + int pos = 0, + shifts = 18, + value = 0; + std::string ret(""); + + for (int i = 0; i < (int)bytes; ++i) + { + if (*data != '\r' && *data != '\n') + { + if (*data == padding_char_) + { + break; + } + + value += base64_ind_[*data] << shifts; + if (shifts == 0) + { + shifts = 18; + str[pos++] = (value >> 16) & 0x0ff; + str[pos++] = (value >> 8) & 0x0ff; + str[pos++] = (value >> 0) & 0x0ff; + value = 0; + } + else + { + shifts -= 6; + } + } + data++; + } + + if (shifts == 12 || shifts == 6) + { + str[pos++] = (value >> 16) & 0x0ff; + } + else if (shifts == 0) + { + str[pos++] = (value >> 16) & 0x0ff; + str[pos++] = (value >> 8) & 0x0ff; + } + + if (pos > 0) + { + str[pos++] = 0; + ret = std::string(str, pos - 1); + } + free(str); + + return ret; + } +} + + +namespace gb +{ + sane_config::sane_config() : jsn_(NULL), bkp_(NULL), in_setting_(false) + { + def_val_ = new gb::json(); + } + sane_config::~sane_config() + { + clear(); + delete def_val_; + } + + bool sane_config::hex(unsigned char ch, unsigned char* val) + { + bool ret = true; + + if (ch >= '0' && ch <= '9') + ch -= '0'; + else if (ch >= 'a' && ch <= 'f') + ch = ch - 'a' + 10; + else if (ch >= 'A' && ch <= 'F') + ch = ch - 'A' + 10; + else + ret = false; + + if (ret && val) + { + *val &= 0x0f0; + *val |= ch; + } + + return ret; + } + bool sane_config::hex_char(const char* data, unsigned char* val) + { + unsigned char v = 0; + bool ret = false; + + if (sane_config::hex(*data++, &v)) + { + v <<= 4; + if (sane_config::hex(*data++, &v)) + { + if (val) + *val = v; + + ret = true; + } + } + + return ret; + } + + void sane_config::clear() + { + if (jsn_) + delete jsn_; + jsn_ = NULL; + if (bkp_) + delete bkp_; + bkp_ = NULL; + } + std::string sane_config::to_hex_letter(const char* data, size_t bytes) + { + std::string hex(""); + const unsigned char* ptr = (const unsigned char*)data; + char buf[8] = { 0 }; + + for (size_t i = 0; i < bytes; ++i) + { + sprintf_s(buf, _countof(buf) - 1, "%02X", ptr[i]); + hex += buf; + } + + return hex; + } + std::string sane_config::from_hex_letter(const char* data, size_t bytes) + { + std::string stream(""); + + bytes /= 2; + for (size_t i = 0; i < bytes; ++i) + { + unsigned char ch = 0; + if (!sane_config::hex_char(data, &ch)) + break; + stream.append(1, ch); + data += 2; + } + + return stream; + } + std::string sane_config::default_value(const char* hex_title) + { + std::string val(""); + + def_val_->get_value(hex_title, val); + + return val; + } + + bool sane_config::load_from_file(const wchar_t* file) + { + clear(); + + bool ret = false; + FILE* src = _wfopen(file, L"rb"); + if (src) + { + size_t size = 0; + char* buf = NULL; + gb::base64 b64; + + fseek(src, 0, SEEK_END); + size = ftell(src); + fseek(src, 0, SEEK_SET); + buf = new char[size + 4]; + memset(buf, 0, size + 4); + fread(buf, 1, size, src); + fclose(src); + + std::string raw(b64.decode(buf, size)); + + delete[] buf; + + if (raw.length()) + { + jsn_ = new gb::json(); + if (jsn_->attach_text(&raw[0])) + ret = true; + else + { + delete jsn_; + jsn_ = NULL; + } + } + } + + return ret; + } + bool sane_config::load_from_mem(const char* mem) + { + gb::base64 b64; + std::string stream(b64.decode(mem, lstrlenA(mem))); + + clear(); + jsn_ = new gb::json(); + if (!jsn_->attach_text(&stream[0])) + { + delete jsn_; + jsn_ = NULL; + + return false; + } + + return true; + } + bool sane_config::save_to(const wchar_t* file) + { + bool ret = false; + std::string encode(to_text_stream()); + + if (encode.length()) + { + FILE* dst = _wfopen(file, L"wb"); + + if (dst) + { + fwrite(encode.c_str(), 1, encode.length(), dst); + fclose(dst); + ret = true; + } + } + + return ret; + } + void sane_config::set_default_value(int sn, const char* title, const char* val, size_t bytes) + { + std::string hex_t(to_hex_letter(title, lstrlenA(title))); + + id_title_[sn] = hex_t; + def_val_->set_value(hex_t.c_str(), to_hex_letter(val, bytes).c_str()); + } + bool sane_config::first_config(std::string& title, std::string& val) + { + bool ret = false; + std::string raw_n(""), raw_v(""); + + if (jsn_ && jsn_->first_child(raw_v, &raw_n)) + { + title = sane_config::from_hex_letter(raw_n.c_str(), raw_n.length()); + val = sane_config::from_hex_letter(raw_v.c_str(), raw_v.length()); + + ret = true; + } + + return ret; + } + bool sane_config::next_config(std::string& title, std::string& val) + { + bool ret = false; + std::string raw_n(""), raw_v(""); + + if (jsn_ && jsn_->next_child(raw_v, &raw_n)) + { + title = sane_config::from_hex_letter(raw_n.c_str(), raw_n.length()); + val = sane_config::from_hex_letter(raw_v.c_str(), raw_v.length()); + + ret = true; + } + + return ret; + } + void sane_config::begin_setting(bool restore) + { + if (bkp_) + delete bkp_; + bkp_ = jsn_; + in_setting_ = true; + jsn_ = new gb::json(); + if (!restore && bkp_) + { + std::string stream(bkp_->to_string(false)); + if(stream.length()) + jsn_->attach_text(&stream[0]); + } + } + void sane_config::config_changed(const char* title, const char* val, size_t bytes) + { + std::string hex_t(to_hex_letter(title, lstrlenA(title))), + hex_v(to_hex_letter(val, bytes)), + def(default_value(hex_t.c_str())); + + if (hex_v == def) + jsn_->remove(hex_t.c_str()); + else + jsn_->set_value(hex_t.c_str(), hex_v.c_str()); + } + void sane_config::config_changed(int sn, const char* val, size_t bytes) + { + std::string hex_t(""), + hex_v(to_hex_letter(val, bytes)), + def(""); + + if (id_title_.count(sn)) + { + hex_t = id_title_[sn]; + def = default_value(hex_t.c_str()); + if (hex_v == def) + jsn_->remove(hex_t.c_str()); + else + jsn_->set_value(hex_t.c_str(), hex_v.c_str()); + } + } + void sane_config::end_setting(bool cancel) + { + if (in_setting_) + { + if (cancel) + { + delete jsn_; + jsn_ = bkp_; + bkp_ = NULL; + } + else if (bkp_) + { + delete bkp_; + bkp_ = NULL; + } + } + in_setting_ = false; + } + int sane_config::id_from_title(const char* title) + { + std::string hex_t(to_hex_letter(title, lstrlenA(title))); + + for (const auto& v : id_title_) + { + if (v.second == hex_t) + return v.first; + } + + return -1; + } + std::string sane_config::to_text_stream(void) + { + if (jsn_) + { + std::string cont(jsn_->to_string(false)), encode(""); + gb::base64 b64; + + encode = b64.encode(cont.c_str(), cont.length()); + + return encode; + } + else + return ""; + } +} diff --git a/sane/gb_json.h b/sane/gb_json.h new file mode 100644 index 0000000..886bd3e --- /dev/null +++ b/sane/gb_json.h @@ -0,0 +1,116 @@ +#pragma once + +#ifdef WIN32 +#include +#endif + +// #include "cJSON.h" +#include "../../code_device/hgsane/cJSON.h" +#include +#include +#include + +namespace gb +{ + class json + { + cJSON *obj_; + cJSON* cur_child_; + bool is_array_; + + cJSON* find_sibling(cJSON* first, const char* name, cJSON*** addr); + cJSON* find_child(cJSON *parent, std::vector& path, bool create, cJSON*** addr = NULL); + cJSON* find(const char* path, bool create = false, cJSON*** addr = NULL); + + public: + json(char* json_txt = 0); + ~json(); + + static std::string to_string(cJSON* root, bool formatted); + static std::string get_value_as_string(cJSON* root, bool integer = false); + static void free_node_data(cJSON* node); + static cJSON* create_element_with_name(const char* name); + + public: + bool attach_text(char* json_txt); + bool attach_cjson(cJSON* cjson); + bool create_empty(bool array = false); + void clear(void); + std::string to_string(bool formatted); + + // can be path: child/value ... + bool get_value(const char* key, bool& val); + bool get_value(const char* key, int& val); + bool get_value(const char* key, double& val); + bool get_value(const char* key, std::string& val); + bool get_value(const char* key, json*& val); // caller shoud call "delete" to free the returned object !!! + bool get_value_as_string(const char* key, std::string& val, bool integer); + bool get_as_array(const char* key, std::vector& val); + + bool first_child(std::string& val, std::string* name = NULL); + bool next_child(std::string& val, std::string* name = NULL); + + bool set_value(const char* key, bool val); + bool set_value(const char* key, int val); + bool set_value(const char* key, double val); + bool set_value(const char* key, std::string val); + bool set_value(const char* key, const char* val); + bool set_value(const char* key, json* obj); + + bool remove(const char* key); + }; + + class base64 + { + char base64_ind_[128]; + char base64_char_[80]; + char padding_char_; + + bool is_valid_base64_table(const char* table); + bool initialize_base64_table(const char* table); + + public: + base64(); + ~base64(); + + public: + bool set_base64_table(const char* table = NULL); + std::string encode(const char* data, size_t bytes, unsigned int line_bytes = -1, bool need_padding = true); + std::string decode(const char* data, size_t bytes); + }; + + class sane_config + { + json* jsn_; + json* bkp_; + json* def_val_; + bool in_setting_; + std::map id_title_; + + void clear(); + std::string to_hex_letter(const char* data, size_t bytes); + std::string from_hex_letter(const char* data, size_t bytes); + std::string default_value(const char* hex_title); + + public: + sane_config(); + ~sane_config(); + + static bool hex(unsigned char ch, unsigned char* val); + static bool hex_char(const char* data, unsigned char* val); + + public: + bool load_from_file(const wchar_t* file); + bool load_from_mem(const char* mem); + bool save_to(const wchar_t* file); + void set_default_value(int sn, const char* title, const char* val, size_t bytes); + bool first_config(std::string& title, std::string& val); + bool next_config(std::string& title, std::string& val); + void begin_setting(bool restore = false); + void config_changed(const char* title, const char* val, size_t bytes); + void config_changed(int sn, const char* val, size_t bytes); + void end_setting(bool cancel); + int id_from_title(const char* title); + std::string to_text_stream(void); + }; +} diff --git a/sane/s2t_api.h b/sane/s2t_api.h index b54a11c..19a2f33 100644 --- a/sane/s2t_api.h +++ b/sane/s2t_api.h @@ -266,6 +266,8 @@ __declspec(novtable) struct ISaneInvoker : public IRef // twain COM_API_DECLARE(void, twain_set_transfer(twain_xfer xfer)); COM_API_DECLARE(void, twain_set_compression(SANE_CompressionType compression, void* detail = NULL)); + COM_API_DECLARE(int, twain_get_config(char* buf, size_t* len)); + COM_API_DECLARE(int, twain_set_config(char* buf, size_t len)); }; struct delete_scanner diff --git a/sane/sane.vcxproj b/sane/sane.vcxproj index 3711a28..1b32e88 100644 --- a/sane/sane.vcxproj +++ b/sane/sane.vcxproj @@ -202,6 +202,7 @@ move /Y "$(OutDirFullPath)$(ProjectName).pdb" "$(SolutionDir)..\..\sdk\lib\win\$ + @@ -225,6 +226,7 @@ move /Y "$(OutDirFullPath)$(ProjectName).pdb" "$(SolutionDir)..\..\sdk\lib\win\$ + diff --git a/sane/sane.vcxproj.filters b/sane/sane.vcxproj.filters index f9d2520..4edc5da 100644 --- a/sane/sane.vcxproj.filters +++ b/sane/sane.vcxproj.filters @@ -63,6 +63,9 @@ sane2twain\UI + + sane2twain\UI + @@ -134,6 +137,9 @@ sane2twain\UI + + sane2twain\UI + diff --git a/sane/scanner.cpp b/sane/scanner.cpp index e6fba7d..5736d09 100644 --- a/sane/scanner.cpp +++ b/sane/scanner.cpp @@ -9,6 +9,7 @@ #include #include "DlgIndicator.h" #include "DlgSetting.h" +#include "gb_json.h" static IMPLEMENT_OPTION_STRING_COMPARE(compare_sane_opt); @@ -109,9 +110,13 @@ namespace callback // 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), cb_invoker_(NULL), cb_param_(NULL), working_(false) - , ui_quit_(true), scanner_name_(L"") + , ui_quit_(true), scanner_name_(L""), cfg_(NULL) { + cfg_ = new gb::sane_config(); tmp_path_ = local_trans::a2u(hg_sane_middleware::sane_path().c_str()); + 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"\\"; @@ -123,6 +128,8 @@ scanner::scanner(SCANNERID id) : handle_(NULL), id_(id), ex_id_(EXTENSION_ID_BAS scanner::~scanner() { callback::unreg_callback(this); + if (cfg_) + delete cfg_; } std::string scanner::get_scanner_name(SCANNERID id) @@ -198,6 +205,45 @@ COM_API_IMPLEMENT(scanner, long, release(void)) return refer::release(); } +void scanner::load_config(const wchar_t* file) +{ + cfg_->load_from_file(file); +} +void scanner::save_config(const wchar_t* file) +{ + cfg_->save_to(file); +} +void scanner::apply_config(void) +{ + std::string t(""), v(""); + if (cfg_->first_config(t, v)) + { + do + { + int id = cfg_->id_from_title(t.c_str()); + if (id != -1) + { + void* data = &v[0]; + const SANE_Option_Descriptor* desc = hg_sane_middleware::instance()->get_option_descriptor(handle_, id); + if (desc) + { + char* buf = NULL; + 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; + } + SANE_Int after = 0; + hg_sane_middleware::instance()->set_option(handle_, id, SANE_ACTION_SET_VALUE, data, &after); + if (buf) + delete[] buf; + } + } + } while (cfg_->next_config(t, v)); + } +} void scanner::on_ui_quit(ui_event uev, void* sender) { switch (uev) @@ -205,12 +251,12 @@ void scanner::on_ui_quit(ui_event uev, void* sender) case UI_EVENT_CLOSE_CANCEL: // post from indicator stop(); case UI_EVENT_CLOSE_NORMAL: // post from indicator & dlg_setting - if (sender == indicator_.get() && setting_.get()) + if (/*sender == indicator_.get() &&*/ setting_.get()) { setting_->hide(); setting_.reset(); } - else if (sender == setting_.get() && indicator_.get()) + /*else*/ if (/*sender == setting_.get() &&*/ indicator_.get()) { indicator_->hide(); indicator_.reset(); @@ -221,10 +267,17 @@ void scanner::on_ui_quit(ui_event uev, void* sender) if (start() == SANE_STATUS_GOOD) { if (indicator_.get()) + { indicator_->show(); + return; + } } else + { setting_->enable(true); + return; + } + case UI_EVENT_SCAN_WORKING: break; default: break; @@ -247,6 +300,8 @@ int scanner::open(void) scanner_name_ = local_trans::a2u(name.c_str(), CP_UTF8); callback::reg_callback(handle_, this); ret = init_options_id(); + load_config((cfg_path_ + scanner_name_).c_str()); + // apply_config(); } return ret; @@ -271,6 +326,32 @@ int scanner::init_options_id(void) while ((desc = hg_sane_middleware::instance()->get_option_descriptor(handle_, op_id))) { + void* val = hg_sane_middleware::instance()->get_def_value(handle_, op_id); + 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_->set_default_value(op_id, desc->title, (char*)val, len); + local_utility::free_memory(val); + } + SET_OPT_ID(is_multiout, DLSC, extension_none) else SET_OPT_ID(multiout_type, DLSCLX, extension_multiout_type) else SET_OPT_ID(color_mode, YSMS, extension_color_mode) @@ -1879,6 +1960,31 @@ COM_API_IMPLEMENT(scanner, void, twain_set_compression(SANE_CompressionType comp img_fmt_.compress.compression = compression; img_fmt_.compress.detail = detail; } +COM_API_IMPLEMENT(scanner, int, twain_get_config(char* buf, size_t* len)) +{ + std::string cont(cfg_->to_text_stream()); + + if (*len < cont.length()) + { + *len = cont.length() + 4; + + return SCANNER_ERR_INSUFFICIENT_MEMORY; + } + strcpy(buf, cont.c_str()); + + return SCANNER_ERR_OK; +} +COM_API_IMPLEMENT(scanner, int, twain_set_config(char* buf, size_t len)) +{ + if(cfg_->load_from_mem(buf)) + { + apply_config(); + + return SCANNER_ERR_OK; + } + + return SCANNER_ERR_DATA_DAMAGED; +} // ui ... @@ -1906,6 +2012,8 @@ COM_API_IMPLEMENT(scanner, bool, ui_show_setting(HWND parent, bool with_scan)) setting_.reset(new dlg_setting(parent, &api, handle_, with_scan, scanner_name_.c_str())); setting_->set_ui_event_notify(&scanner::ui_callback, this); + setting_->set_config(cfg_, (cfg_path_ + scanner_name_).c_str()); + cfg_->begin_setting(); if (with_scan) { indicator_.reset(new dlg_indicator(parent)); @@ -1913,7 +2021,7 @@ COM_API_IMPLEMENT(scanner, bool, ui_show_setting(HWND parent, bool with_scan)) } else indicator_.reset(); - setting_->show(); + setting_->show(true); ui_quit_ = false; return true; @@ -1941,10 +2049,15 @@ COM_API_IMPLEMENT(scanner, bool, ui_is_ok(void)) - +// called from device-layer ... int scanner::handle_event(int ev_code, void* data, unsigned int* len) { - if (ev_code == SANE_EVENT_IMAGE_OK) + if (ev_code == SANE_EVENT_WORKING) + { + if (indicator_.get()) + indicator_->notify_working(); + } + else if (ev_code == SANE_EVENT_IMAGE_OK) { SANE_Image* simg = (SANE_Image*)data; scanned_img* img = NULL; @@ -1955,6 +2068,8 @@ int scanner::handle_event(int ev_code, void* data, unsigned int* len) if (img->bytes() /*>= simg->bytes*/) { images_.save(img); + if (cb_invoker_) // added for notify twain-app XferReady ... + cb_invoker_(ev_code, NULL, NULL, cb_param_); } else { diff --git a/sane/scanner.h b/sane/scanner.h index 051f286..f2490a1 100644 --- a/sane/scanner.h +++ b/sane/scanner.h @@ -24,6 +24,10 @@ #include "DlgPage.h" class dlg_indicator; class dlg_setting; +namespace gb +{ + class sane_config; +} class scanner : public ISaneInvoker, virtual public refer { @@ -38,6 +42,7 @@ class scanner : public ISaneInvoker, virtual public refer unsigned int img_ind_; std::wstring scanner_name_; std::wstring tmp_path_; + std::wstring cfg_path_; std::string scan_msg_; bool scan_err_; twain_xfer xfer_; @@ -47,7 +52,11 @@ class scanner : public ISaneInvoker, virtual public refer SANE_FinalImgFormat img_fmt_; std::unique_ptr indicator_; std::unique_ptr setting_; + gb::sane_config* cfg_; + void load_config(const wchar_t* file); + void save_config(const wchar_t* file); + void apply_config(void); void on_ui_quit(ui_event uev, void* sender); int open(void); int close(void); @@ -293,6 +302,8 @@ public: // twain COM_API_OVERRIDE(void, twain_set_transfer(twain_xfer xfer)); COM_API_OVERRIDE(void, twain_set_compression(SANE_CompressionType compression, void* detail = NULL)); + COM_API_OVERRIDE(int, twain_get_config(char* buf, size_t* len)); + COM_API_OVERRIDE(int, twain_set_config(char* buf, size_t len)); // methods: public: diff --git a/twain/twain/huagaods.cpp b/twain/twain/huagaods.cpp index 3a86616..6b50c48 100644 --- a/twain/twain/huagaods.cpp +++ b/twain/twain/huagaods.cpp @@ -581,7 +581,7 @@ static const SCANNERID scanner_guid = MAKE_SCANNER_ID(PRODUCT_PID, PRODUCT_VID); static std::once_flag oc; -huagao_ds::huagao_ds() +huagao_ds::huagao_ds() : cur_head_(NULL) { //std::call_once(oc, [&]() { log4cplus::Initializer(); }); } @@ -592,6 +592,8 @@ huagao_ds::~huagao_ds() if (memoryinfo->joinable()) memoryinfo->join(); } + if (cur_head_) + delete cur_head_; } std::string huagao_ds::get_hidedlg_path(void) @@ -705,6 +707,9 @@ Result huagao_ds::capabilitySet(const Identity& origin, Capability& data) } Result huagao_ds::eventProcess(const Identity&, Event& event) { + const MSG* msg = (const MSG*)event.event(); +// event.setMessage(Msg::Null); + return { ReturnCode::NotDsEvent, ConditionCode::Success }; } Twpp::Result huagao_ds::deviceEventGet(const Twpp::Identity& origin, Twpp::DeviceEvent& data) @@ -799,13 +804,24 @@ Result huagao_ds::pendingXfersReset(const Identity&, PendingXfers& data) return success(); } -Result huagao_ds::setupMemXferGet(const Identity&, SetupMemXfer& data) +Result huagao_ds::setupMemXferGet(const Identity& id, SetupMemXfer& data) { SANE_Parameters head; size_t total = 0; + DWORD to = cur_head_ ? -1 : 0; - if (!scanner_.get() || scanner_->get_scanned_images() == 0) - return seqError(); + if (!scanner_.get() || scanner_->get_scanned_images(to) == 0) + { + if (cur_head_) + { + UserInterface ui(FALSE, FALSE, (Twpp::Handle)0); + data.setPreferredSize(1); + call(id, DataGroup::Control, Dat::UserInterface, Msg::DisableDs, &ui); + return success(); + } + else + return seqError(); + } if (scanner_->get_first_image_header(&head, &total)) { @@ -838,12 +854,24 @@ Result huagao_ds::userInterfaceEnable(const Identity&, UserInterface& ui) { if (!ui.showUi()) { + bool xfer_ready = !m_bIndicator; + if (m_bIndicator && !scanner_->ui_show_progress((HWND)ui.parent().raw())) return seqError(); scanner_->twain_set_transfer((twain_xfer)m_capXferMech); - return scanner_->start() == SCANNER_ERR_OK ? success() : seqError(); + if (scanner_->start() == SCANNER_ERR_OK) + { + if (xfer_ready) + notifyXferReady(); + + return success(); + } + else + { + return seqError(); + } } return showTwainUI(ui); @@ -856,18 +884,35 @@ Result huagao_ds::userInterfaceEnableUiOnly(const Identity&, UserInterface& ui) Result huagao_ds::imageInfoGet(const Identity&, ImageInfo& data) { SANE_Parameters head; + bool ok = false; if (!scanner_.get()) return seqError(); - if (!scanner_->wait_image()) - return bummer(); - - if (scanner_->get_first_image_header(&head)) + if (cur_head_ && cur_head_->lines) { - if(head.format == SANE_FRAME_RGB) + memcpy(&head, cur_head_, sizeof(head)); + ok = true; + cur_head_->lines = 0; + } + else + { + if (!scanner_->wait_image()) + return bummer(); + ok = scanner_->get_first_image_header(&head); + } + + if (ok) + { + if (head.format == SANE_FRAME_RGB) + { data.setBitsPerPixel(head.depth * 3); + data.bitsPerSample()[0] = data.bitsPerSample()[1] = data.bitsPerSample()[2] = 8; + } else + { data.setBitsPerPixel(head.depth); + data.bitsPerSample()[0] = 8; + } data.setHeight(head.lines); data.setPixelType(head.format == SANE_FRAME_RGB ? PixelType::Rgb : PixelType::Gray); data.setPlanar(false); @@ -938,6 +983,10 @@ Result huagao_ds::imageMemXferGet(const Identity& origin, ImageMemXfer& data) img->add_ref(); pending_xfer_.clear(); } + else if(cur_head_) + { + img->copy_header(cur_head_); + } data.setBytesPerRow(line_l); data.setColumns(img->width()); data.setRows(rows); @@ -949,8 +998,7 @@ Result huagao_ds::imageMemXferGet(const Identity& origin, ImageMemXfer& data) want_read = img->bytes(); off = 0; img->read(dst, &want_read, off); - data.setRows(1); - data.setColumns(want_read); + data.setRows(img->height()); data.setBytesWritten(want_read); } else if (img->read(dst, &want_read, off) == SCANNER_ERR_OK) @@ -1009,10 +1057,8 @@ Result huagao_ds::imageNativeXferGet(const Identity& id, ImageNativeXfer& data) } Twpp::Result huagao_ds::pendingXfersStopFeeder(const Identity& origin, PendingXfers& data) { - if (!scanner_.get()) - return seqError(); - - scanner_->stop(); + if (scanner_.get()) + scanner_->stop(); return success(); } @@ -1085,7 +1131,7 @@ Twpp::Result huagao_ds::imageFileXferGet(const Twpp::Identity& origin) conv.dst.data = target.c_str(); conv.dst.data_len = target.length(); MoveFileA(target.c_str(), file.c_str()); - if(scanner_->convert_image(&conv) != SANE_STATUS_GOOD) + if(scanner_->convert_image(&conv) != SCANNER_ERR_OK) ret = { ReturnCode::Failure, ConditionCode::FileWriteError }; DeleteFileA(file.c_str()); } @@ -1140,12 +1186,28 @@ Result huagao_ds::call(const Identity& origin, DataGroup dg, Dat dat, Msg msg, v Result huagao_ds::customDataGet(const Twpp::Identity& origin, Twpp::CustomData& data) { // get user setting from local file ... + if (!scanner_.get()) + return seqError(); + + char * buf = NULL; + size_t len = 0; + if (scanner_->twain_get_config(buf, &len) == SCANNER_ERR_INSUFFICIENT_MEMORY) + { + data = CustomData(len); + buf = data.lock(); + scanner_->twain_get_config(buf, &len); + } return success(); } Result huagao_ds::customDataSet(const Twpp::Identity& origin, Twpp::CustomData& data) { // write user setting to local file ... + if (!scanner_.get()) + return seqError(); + + char* buf = data.lock(); + scanner_->twain_set_config(buf, data.size()); return success(); } @@ -2529,7 +2591,11 @@ void huagao_ds::on_scan_event(int sane_event, void* data, unsigned int* len) { if (scanner_.get()) { - if (sane_event == SANE_EVENT_SCAN_FINISHED) + /*if (sane_event == SANE_EVENT_IMAGE_OK) + { + notifyDeviceEvent(); + } + else*/ if (sane_event == SANE_EVENT_SCAN_FINISHED) { notifyCloseCancel(); } diff --git a/twain/twain/huagaods.hpp b/twain/twain/huagaods.hpp index 2e4f9d8..eff091a 100644 --- a/twain/twain/huagaods.hpp +++ b/twain/twain/huagaods.hpp @@ -40,6 +40,7 @@ class huagao_ds : public Twpp::SourceFromThis { bool m_bIndicator = true; int m_jpegQuality = 80; Twpp::Compression m_compression = Twpp::Compression::None; + SANE_Parameters* cur_head_; static std::string get_hidedlg_path(void); static void showmsg(const char* msg, int err);