#include "scanner_manager.h" #include #include #include #include #include #include #include "hg_scanner.h" #include "user-opt/user.h" #include "user-opt/offline_opt.h" #include #include // kinds of scanners ... #define SCAN_PTR(ptr) ((hg_scanner*)ptr) static struct _scanner_device { int vid; int pid; std::string family; std::string name; } g_supporting_devices[] = { ALL_FAMILIES }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // language option ... class lang_opt : public sane_opt_provider { public: lang_opt() { set_where("language-option"); const char* lang = language_option_descriptor(); if (lang && *lang) { std::string t(lang); set_opt_json_text(&t[0]); } } protected: virtual ~lang_opt() {} public: virtual int set_value(const char* name, void* val) override { if (strcmp(name, "language")) return SCANNER_ERR_NO_DATA; LANATTR** pla = lang_get_supported_languages(); int err = SCANNER_ERR_OK; if (!pla) err = SCANNER_ERR_DEVICE_NOT_SUPPORT; else { std::string n(to_default_language((char*)val, nullptr)), now(""); int id = -1, cur = lang_get_cur_code_page(); for (int i = 0; pla[i]; ++i) { if (pla[i]->cp == cur) now = pla[i]->name; if (n == pla[i]->name) { id = pla[i]->cp; break; } } if (id == -1) { err = SCANNER_ERR_NOT_EXACT; ::strcpy((char*)val, now.c_str()); } else if (cur != id) { err = SCANNER_ERR_CONFIGURATION_CHANGED; lang_set_code_page(id); } } return err; } }; static lang_opt* g_language = nullptr; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // hg_scanner_mgr* hg_scanner_mgr::inst_ = NULL; sane_callback hg_scanner_mgr::event_callback_ = NULL; bool hg_scanner_mgr::async_io_enabled_ = false; int hg_scanner_mgr::ver_major_ = 1; int hg_scanner_mgr::ver_minor_ = 0; int hg_scanner_mgr::ver_build_ = 0; int hg_scanner_mgr::ver_patch_ = 1; std::string hg_scanner_mgr::pe_path_(""); std::string hg_scanner_mgr::pe_name_(""); std::string hg_scanner_mgr::last_open_msg_(""); bool hg_scanner_mgr::read_over_with_eof_ = true; uint32_t hg_scanner_mgr::unique_img_id_ = 0;; std::mutex hg_scanner_mgr::mutex_img_id; hg_scanner_mgr::hg_scanner_mgr() : same_ind_(1), cf_name("") { std::string cfgf(utils::get_local_data_path() + PATH_SEPARATOR + "config" + PATH_SEPARATOR + "debug.cfg"); char buf[260] = {0}; int l = GetPrivateProfileStringA("devs_name", "name", "", buf, _countof(buf) - 1, cfgf.c_str()); init_debug_config(cfgf.c_str()); buf[l] = 0; cf_name = buf; usb_manager::instance()->register_hotplug(&hg_scanner_mgr::usb_event_handle, this); int code_page = lang_get_cur_code_page(); url_en = BRAND_COMPANY_URL; url_link_en = BRAND_URL_COMPANY_URL; if (code_page == 20127) { url_en = BRAND_COMPANY_URL_EN; url_link_en = BRAND_URL_COMPANY_URL_EN; } user_ = new hguser(); offline_ = new offline_opts(user_); g_language = new lang_opt(); } hg_scanner_mgr::~hg_scanner_mgr() { usb_manager::instance()->register_hotplug(nullptr, nullptr); { std::lock_guard lock(mutex_dev_); for (auto& v : online_devices_) { if (v.scanner) { v.scanner->close(); v.scanner->release(); v.scanner = nullptr; } libusb_unref_device(v.dev); } online_devices_.clear(); } usb_manager::clear(); offline_->release(); delete user_; g_language->release(); } int hg_scanner_mgr::ui_default_callback(scanner_handle h, int ev, void* data, unsigned int* len, void* param) { // if (!hg_scanner_mgr::async_io_enabled_ && // ev == SANE_EVENT_IS_MEMORY_ENOUGH) // { // struct sysinfo si; // if(sysinfo(&si) == 0) // return si.freeram * si.mem_unit > *len + 200 * 1024 * 1024 ? SCANNER_ERR_OK : SCANNER_ERR_INSUFFICIENT_MEMORY; // return SCANNER_ERR_OK; // } if (hg_scanner_mgr::event_callback_) return hg_scanner_mgr::event_callback_(h, ev, data, len, param); return SCANNER_ERR_OK; } hg_scanner_mgr* hg_scanner_mgr::instance(sane_callback cb) { if (!hg_scanner_mgr::inst_) { hg_scanner_mgr::event_callback_ = cb; if (cb) hg_scanner_mgr::async_io_enabled_ = cb(NULL, SANE_EVENT_SUPPORT_ASYNC_IO, NULL, NULL, NULL) == 0; hg_scanner_mgr::unique_img_id_ = (uint32_t)INT_MAX + 1; hg_scanner_mgr::inst_ = new hg_scanner_mgr(); utils::to_log(LOG_LEVEL_DEBUG, "async image transferring is %s\n", hg_scanner_mgr::async_io_enabled_ ? "enabled" : "disabled"); } return hg_scanner_mgr::inst_; } void hg_scanner_mgr::clear(void) { if (hg_scanner_mgr::inst_) { { std::lock_guard lock(hg_scanner_mgr::inst_->mutex_dev_); hg_scanner_mgr::event_callback_ = nullptr; } delete hg_scanner_mgr::inst_; hg_scanner_mgr::inst_ = NULL; } } void hg_scanner_mgr::set_version(int hh, int hl, int lh, int ll) { hg_scanner_mgr::ver_major_ = hh; hg_scanner_mgr::ver_minor_ = hl; hg_scanner_mgr::ver_build_ = lh; hg_scanner_mgr::ver_patch_ = ll; } void hg_scanner_mgr::get_version(int* hh, int* hl, int* lh, int* ll) { if(hh) *hh = hg_scanner_mgr::ver_major_; if(hl) *hl = hg_scanner_mgr::ver_minor_; if(lh) *lh = hg_scanner_mgr::ver_build_; if(ll) *ll = hg_scanner_mgr::ver_patch_; } void hg_scanner_mgr::set_exe_name(const char* path, const char* name) { std::string cfgf(utils::get_local_data_path() + PATH_SEPARATOR + "config" + PATH_SEPARATOR + "debug.cfg"); char buf[260] = {0}; int l = 0; hg_scanner_mgr::pe_path_ = path ? path : ""; hg_scanner_mgr::pe_name_ = name ? name : ""; std::string str(utils::get_command_result("scanimage -V")); bool is_scanimg =false; is_scanimg = str >= "1.0.29" ? true : false; utils::to_log(LOG_LEVEL_ALL, "imgascan ver is: %s is_scanimg is:%d\n", str.c_str(),is_scanimg); if (name && *name) { // int ret = system("scanimage -V"); l = GetPrivateProfileStringA("read_eof", name, "", buf, _countof(buf) - 1, cfgf.c_str()); if (l == 0) { if(is_scanimg) hg_scanner_mgr::read_over_with_eof_ = STRICMP(name, "simple-scan") != 0; else hg_scanner_mgr::read_over_with_eof_ = STRICMP(name, "simple-scan") != 0 && (!is_scanimg? (STRICMP(name, "scanimage") != 0) : 1); } else hg_scanner_mgr::read_over_with_eof_ = strcmp(buf, "0") != 0; } else { l = GetPrivateProfileStringA("read_eof", "first", "", buf, _countof(buf) - 1, cfgf.c_str()); if (l == 0) hg_scanner_mgr::read_over_with_eof_ = true; else hg_scanner_mgr::read_over_with_eof_ = strcmp(buf, "0") != 0; } utils::to_log(LOG_LEVEL_ALL, "read image data end with EOF: %s\n", hg_scanner_mgr::read_over_with_eof_ ? "true" : "false"); } std::string hg_scanner_mgr::get_pe_name(std::string* path) { if (path) *path = hg_scanner_mgr::pe_path_; return hg_scanner_mgr::pe_name_; } uint32_t hg_scanner_mgr::unique_id(int type) { if (type == UNIQUE_ID_IMG) { std::lock_guard lock(hg_scanner_mgr::mutex_img_id); return hg_scanner_mgr::unique_img_id_++; } return -1; } void hg_scanner_mgr::usb_event_handle(usb_event ev, libusb_device* device, int vid, int pid, int usb_ver_h, int usb_ver_l, bool* retry, void* user) // usb_ver_h.usb_ver_l { hg_scanner_mgr* obj = (hg_scanner_mgr*)user; obj->on_hgscanner_pnp(ev, device, vid, pid, usb_ver_h, usb_ver_l, retry); } void hg_scanner_mgr::on_hgscanner_pnp(usb_event ev, libusb_device* device, int vid, int pid, int usb_ver_h, int usb_ver_l, bool* retry) { char model[40], vendor[40]; SANE_Device_Ex de; int ev_ui = 0, addr = 0; scanner_handle h = NULL; unsigned int len = sizeof(de); std::string name(""), type(""); std::lock_guard lock(mutex_dev_); sprintf(model, "%x", pid); sprintf(vendor, "%x", vid); de.model = model; de.name = NULL; de.family = NULL; de.vendor = vendor; de.openned = SANE_FALSE; addr = libusb_get_device_address(device); if (ev == USB_EVENT_DEVICE_ARRIVED) { int index = -1; for (int i = 0; i < _countof(g_supporting_devices); ++i) { if (g_supporting_devices[i].vid == vid && g_supporting_devices[i].pid == pid) { index = i; ev_ui = SANE_EVENT_DEVICE_ARRIVED; name = g_supporting_devices[i].name.c_str(); type = g_supporting_devices[i].family.c_str(); if (vid == 0x064B && !cf_name.empty()) { if (cf_name == g_supporting_devices[i].family) { name = g_supporting_devices[i].name.c_str(); type = g_supporting_devices[i].family.c_str(); break; } } else break; } } if (index != -1) { bool add = true; size_t i = 0; for (; i < online_devices_.size(); ++i) { if (online_devices_[i].dev == device) { add = false; break; } } if (add) { i = 0; for (auto& v : online_devices_) { if (v.scanner && !v.scanner->is_online()) { add = false; break; } i++; } } if (add) { ONLNSCANNER ols; ols.vid = vid; ols.pid = pid; ols.addr = addr; ols.dev = device; ols.scanner = nullptr; ols.family = g_supporting_devices[index].family; ols.display_name = g_supporting_devices[index].name; if (std::find(online_devices_.begin(), online_devices_.end(), ols.display_name.c_str()) != online_devices_.end()) { int sn = 0; while (std::find(online_devices_.begin(), online_devices_.end(), (ols.display_name + " - " + std::to_string(++sn)).c_str()) != online_devices_.end()); ols.display_name += " - " + std::to_string(sn); } libusb_ref_device(ols.dev); // ref to the device of queue online_devices_ online_devices_.push_back(ols); name = ols.display_name; utils::to_log(LOG_LEVEL_DEBUG, "%s connected.\n", name.c_str()); type = g_supporting_devices[index].family; } else if (i < online_devices_.size() && online_devices_[i].scanner && !online_devices_[i].scanner->is_online()) { name = online_devices_[i].display_name; type = g_supporting_devices[index].family; if (online_devices_[i].dev) libusb_unref_device(online_devices_[i].dev); online_devices_[i].dev = device; libusb_ref_device(online_devices_[i].dev); h = online_devices_[i].scanner; if (pid == 0x300 || pid == 0x400) std::this_thread::sleep_for(std::chrono::milliseconds(1000)); //len = usb_manager::instance()->open(device, &io); utils::to_log(LOG_LEVEL_WARNING, "[%04x:%04x]%s re-connected.\n", vid, pid, online_devices_[i].display_name.c_str()); len = online_devices_[i].scanner->re_connect(); if (len == SCANNER_ERR_OK) { //online_devices_[i].scanner->reset_io(io); de.openned = SANE_TRUE; } add = false; } } } else if (ev == USB_EVENT_DEVICE_LEFT) { std::vector::iterator it = std::find(online_devices_.begin(), online_devices_.end(), device); if (it != online_devices_.end()) { ev_ui = SANE_EVENT_DEVICE_LEFT; name = it->display_name; type = it->family; h = it->scanner; utils::to_log(LOG_LEVEL_DEBUG, "%s Dis-connected.\n", name.c_str()); if (!it->scanner) { libusb_unref_device(it->dev); // unref the device of queue online_devices_ online_devices_.erase(it); } } } if (ev_ui) { de.name = name.c_str(); de.family = type.c_str(); hg_scanner_mgr::ui_default_callback(h, ev_ui, &de, &len, NULL); } } void hg_scanner_mgr::get_online_devices(std::vector& devs) { std::lock_guard lock(mutex_dev_); devs = online_devices_; } void hg_scanner_mgr::init_debug_config(const char* cfg_file) { // dumping images ... dump_img_path_ = utils::get_local_data_path() + PATH_SEPARATOR + "imgs"; if (GetPrivateProfileIntA("dump", "dumpusb", 0, cfg_file) == 1) { char strbuf[260] = { 0 }; int getl = 0; dump_img_ = true; getl = GetPrivateProfileStringA("dump", "usb_path", "", strbuf, _countof(strbuf) - 1, cfg_file); if (getl) dump_img_path_ = strbuf; } } imgproc_mgr* hg_scanner_mgr::create_image_processor(device_option* devopts) { imgproc_mgr* proc = new imgproc_mgr(devopts, dump_img_, dump_img_path_.c_str()); std::string path(utils::get_module_full_path(MODULE_NAME_SCANNER)); size_t pos = path.rfind(PATH_SEPARATOR[0]); if (pos++ == std::string::npos) pos = 0; path.erase(pos); path += std::string("imgproc") + PATH_SEPARATOR; // load image-processors ... proc->load_processor(path.c_str()); return proc; } scanner_err hg_scanner_mgr::hg_scanner_enum(ScannerInfo* scanner_list, long* count, bool local_only) { std::vector devusbuf; long size = *count; scanner_err ret = SCANNER_ERR_OK; std::string g_vendor(from_default_language(COMPANY_NAME, nullptr)); get_online_devices(devusbuf); *count = devusbuf.size(); if (*count > size) { ret = SCANNER_ERR_INSUFFICIENT_MEMORY; } else { for (size_t i = 0; i < devusbuf.size(); i++) { scanner_list->vid = devusbuf[i].vid; scanner_list->pid = devusbuf[i].pid; strcpy(scanner_list->name, devusbuf[i].display_name.c_str()); strcpy(scanner_list->model, devusbuf[i].family.c_str()); strcpy(scanner_list->vendor, g_vendor.c_str()); scanner_list++; } } return ret; } scanner_err hg_scanner_mgr::hg_scanner_open(scanner_handle* h, const char* name, bool shared, const char* user, const char* pwd, const char* check, char* rsc) { std::vector::iterator it; scanner_err ret = SCANNER_ERR_DEVICE_NOT_FOUND; SIMPLE_LOCK(mutex_dev_); *h = NULL; it = std::find(online_devices_.begin(), online_devices_.end(), name); if (it != online_devices_.end()) { std::vector cnst; cnst.push_back(offline_); cnst.push_back(g_language); it->scanner = new hg_scanner(&(*it), nullptr, user_, &cnst); if (it->scanner->status() == SCANNER_ERR_OK) { *h = it->scanner; } else { ret = (scanner_err)it->scanner->status(); hg_scanner_mgr::last_open_msg_ = it->scanner->status_message(); it->scanner->close(); it->scanner->release(); it->scanner = nullptr; } } return *h ? SCANNER_ERR_OK : ret; } scanner_err hg_scanner_mgr::hg_scanner_close(scanner_handle h, bool force) { { std::lock_guard lock(mutex_dev_); for (auto& v : online_devices_) { if (v.scanner == h) { v.scanner->close(/*force*/); v.scanner->release(); v.scanner = nullptr; break; } } } return SCANNER_ERR_OK; } scanner_err hg_scanner_mgr::hg_scanner_get_parameter(scanner_handle h, const char* name, char* data, long* len, int type) { scanner_err err = SCANNER_ERR_INVALID_PARAMETER; if (len) { device_option* tmp = nullptr; if (!h) { tmp = new device_option(); tmp->add(offline_); tmp->add(g_language); } else { err = SCANNER_ERR_DEVICE_NOT_FOUND; std::lock_guard lock(mutex_dev_); for (auto& v : online_devices_) { if (v.scanner == h) { tmp = v.scanner->get_device_opt(); break; } } } if (tmp) { std::string raw(tmp->get_option_value(name == PARAM_ALL ? nullptr : name, type, nullptr, data)); if (*len < raw.length()) { *len = raw.length(); err = SCANNER_ERR_INSUFFICIENT_MEMORY; } else { memcpy(data, raw.c_str(), raw.length()); if (*len > raw.length()) data[raw.length()] = 0; *len = raw.length(); err = SCANNER_ERR_OK; #if defined(_WIN32) && defined(_DEBUG) if (name == PARAM_ALL) { std::string file(utils::get_local_data_path() + PATH_SEPARATOR + "Log" + PATH_SEPARATOR + "allopt.txt"); FILE* dst = fopen(file.c_str(), "wb"); if (dst) { fwrite(raw.c_str(), 1, raw.length(), dst); fclose(dst); } } #endif } tmp->release(); } } return err; } scanner_err hg_scanner_mgr::hg_scanner_set_parameter(scanner_handle h, const char* name, void* data, bool to_default) { scanner_err err = SCANNER_ERR_OK, se = SCANNER_ERR_OK; std::string init(""); device_option* tmp = nullptr; if (!h) { tmp = new device_option(); tmp->add(offline_); tmp->add(g_language); } else { for (auto& v : online_devices_) { if (v.scanner == h) { tmp = v.scanner->get_device_opt(); break; } } } if (tmp) { // do restore here ? if (name && strcmp(name, SANE_STD_OPT_NAME_RESTORE) == 0) { sane_opt_provider* sop = dynamic_cast(SCAN_PTR(h)); err = (scanner_err)tmp->restore(sop); utils::to_log(LOG_LEVEL_DEBUG, "Restore all options ...\n"); } else { // 1 - paper option removes 'lateral' choices and add new option with name 'lateral' // 2 - page option replaces 'single side' with 'front side' and 'back side' // // here to transfer them ... std::string prev(""); bool lateral = false, restore_data = false; if (to_default) { int size = 0; init = tmp->get_option_value(name, SANE_ACTION_GET_DEFAULT_VALUE, &size); if (size > init.length()) { std::string t(std::move(init)); init.reserve(size); memset(&init[0], 0, size); memcpy(&init[0], &t[0], t.length()); } data = &init[0]; } else { if (strcmp(name, SANE_STD_OPT_NAME_PAPER) == 0) { std::string hx(from_default_language("\346\250\252\345\220\221")); char* lat = strstr((char*)data, hx.c_str()); if (lat) { prev = (char*)data; lateral = true; restore_data = true; *lat = 0; } } else if (strcmp(name, SANE_STD_OPT_NAME_PAGE) == 0) { if (strcmp((char*)data, from_default_language("\345\215\225\351\235\242")) == 0) // single side { prev = (char*)data; restore_data = true; strcpy((char*)data, from_default_language("\346\255\243\351\235\242")); // front side utils::to_log(LOG_LEVEL_DEBUG, "compatible for old page option '%s' to '%s'.\n", prev.c_str(), (char*)data); } } err = tmp->refine_data(name, data) ? SCANNER_ERR_NOT_EXACT : SCANNER_ERR_OK; } se = (scanner_err)tmp->update_data(name, data); if (se != SCANNER_ERR_OK) err = se; else if (restore_data) { // here handle options paper and page are both string ... strcpy((char*)data, prev.c_str()); if (lateral) { utils::to_log(LOG_LEVEL_DEBUG, "compatible for old paper option '%s', set lateral to '%s' additional.\n", prev.c_str(), lateral ? "true" : "false"); tmp->refine_data(SANE_STD_OPT_NAME_LATERAL, &lateral); tmp->update_data(SANE_STD_OPT_NAME_LATERAL, &lateral); } } } tmp->release(); } return err; } scanner_err hg_scanner_mgr::hg_scanner_start(scanner_handle h, void* async_event, int num) { return (scanner_err)SCAN_PTR(h)->start(); } scanner_err hg_scanner_mgr::hg_scanner_stop(scanner_handle h) { scanner_err err = (scanner_err)SCAN_PTR(h)->stop(); // call from APP, block when all working-threads stopped - added on 2023-10-18 when handled double-feeding in SANE //while (SCAN_PTR(h)->is_running() != hg_scanner::THREAD_RUNNING_IDLE) // std::this_thread::sleep_for(std::chrono::milliseconds(3)); return err; } scanner_err hg_scanner_mgr::hg_scanner_get_img_info(scanner_handle h, SANE_Parameters* bmi, long len) { return (scanner_err)SCAN_PTR(h)->get_image_info(bmi); } scanner_err hg_scanner_mgr::hg_scanner_read_img_data(scanner_handle h, unsigned char* data, long* len) { if (!len) return SCANNER_ERR_INVALID_PARAMETER; size_t l = *len, err = SCAN_PTR(h)->read_image_data(data, &l); *len = l; return (scanner_err)err; } scanner_err hg_scanner_mgr::hg_scanner_get_status(scanner_handle h, int setstutas) { return (scanner_err)SCAN_PTR(h)->status(); } scanner_err hg_scanner_mgr::hg_scanner_reset(scanner_handle h) { return SCANNER_ERR_OK; // (scanner_err)SCAN_PTR(h)->reset(); } scanner_err hg_scanner_mgr::hg_scanner_control(scanner_handle h, unsigned long code, void* data, unsigned* len) { return SCANNER_ERR_DEVICE_NOT_SUPPORT; } void hg_scanner_mgr::on_language_changed(void) { std::lock_guard lock(mutex_dev_); for (auto& v: online_devices_) { if (v.scanner) { device_option* opt = v.scanner->get_device_opt(); opt->update_data(nullptr, nullptr, false); opt->release(); } } } const char* hg_scanner_mgr::last_open_message(void) { return hg_scanner_mgr::last_open_msg_.c_str(); }