#include "sane_opts.h" #include #include ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // sane_opt sane_opt::sane_opt() : fix_id_(-1), sn_(0), enabled_(true) { memset(&opt_desc_, 0, sizeof(opt_desc_)); } sane_opt::~sane_opt() { clear(); } void sane_opt::clear() { if (opt_desc_.desc) delete[] opt_desc_.desc; if (opt_desc_.name) delete[] opt_desc_.name; if (opt_desc_.title) delete[] opt_desc_.title; if (opt_desc_.type == SANE_TYPE_STRING && opt_desc_.constraint_type == SANE_CONSTRAINT_STRING_LIST && opt_desc_.constraint.string_list) delete[] opt_desc_.constraint.string_list; else if (opt_desc_.type == SANE_TYPE_INT || opt_desc_.type == SANE_TYPE_FIXED) { if (opt_desc_.constraint_type == SANE_CONSTRAINT_RANGE && opt_desc_.constraint.range) delete[] opt_desc_.constraint.range; else if (opt_desc_.constraint_type == SANE_CONSTRAINT_WORD_LIST && opt_desc_.constraint.word_list) delete[] opt_desc_.constraint.word_list; } memset(&opt_desc_, 0, sizeof(opt_desc_)); fix_id_ = -1; sn_ = 0; enabled_ = true; } void sane_opt::set_opt_desc_string_value(char** buf, const char* val) { int l = strlen(val) + 4; l += 4; l /= 4; l *= 4; *buf = new char[l]; if (*buf) { memset(*buf, 0, l); strcpy(*buf, val); } } void sane_opt::init_cap(gb_json* jsn) { bool bv = false; opt_desc_.cap = SANE_CAP_SOFT_SELECT | SANE_CAP_SOFT_DETECT | SANE_CAP_AUTOMATIC; if (jsn->get_value("readonly", bv) && bv) SET_CAP_READONLY(opt_desc_.cap); if (jsn->get_value("enabled", bv) && !bv) SET_CAP_ACTIVE(opt_desc_.cap, bv); if (jsn->get_value("auto", bv) && !bv) opt_desc_.cap &= ~SANE_CAP_AUTOMATIC; } void sane_opt::init_range(gb_json* jsn) { gb_json* child = nullptr; if (jsn->get_value("range", child) && child) { if (opt_desc_.type == SANE_TYPE_STRING) { gb_json* val = child->first_child(); std::vector vals; int len = 0; char *buf = nullptr, *oper = nullptr; while (val) { std::string v(""); if (val->value(v)) { vals.push_back(v); len += ALIGN_TO(v.length() + 1, 4); } val->release(); val = child->next_child(); } len += (vals.size() + 1) * sizeof(char*); opt_desc_.constraint_type = SANE_CONSTRAINT_STRING_LIST; buf = new char[len]; opt_desc_.constraint.string_list = (SANE_String_Const*)buf; memset(buf, 0, len); oper = buf + (vals.size() + 1) * sizeof(char*); len = 0; for (auto& v : vals) { ((char**)buf)[len++] = oper; strcpy(oper, v.c_str()); oper += ALIGN_TO(v.length() + 1, 4); } } else if (opt_desc_.type == SANE_TYPE_INT) { int v = 0; if (child->get_value("min", v)) { SANE_Range* range = new SANE_Range; opt_desc_.constraint_type = SANE_CONSTRAINT_RANGE; opt_desc_.constraint.range = range; range->min = v; child->get_value("max", range->max); child->get_value("step", range->quant); } else { std::vector vals; gb_json* val = child->first_child(); while (val) { if (val->value(v)) vals.push_back(v); val->release(); val = child->next_child(); } SANE_Word* lst = new SANE_Word[vals.size() + 1]; int ind = 0; lst[ind++] = vals.size(); for (auto& e : vals) lst[ind++] = e; opt_desc_.constraint_type = SANE_CONSTRAINT_WORD_LIST; opt_desc_.constraint.word_list = lst; } } else if (opt_desc_.type == SANE_TYPE_FIXED) { double v = 0; if (child->get_value("min", v)) { SANE_Range* range = new SANE_Range; opt_desc_.constraint_type = SANE_CONSTRAINT_RANGE; opt_desc_.constraint.range = range; range->min = SANE_FIX(v); if(child->get_value("max", v)) range->max = SANE_FIX(v); if(child->get_value("step", v)) range->quant = SANE_FIX(v); } else { std::vector vals; gb_json* val = child->first_child(); while (val) { if (val->value(v)) vals.push_back(v); val->release(); val = child->next_child(); } SANE_Word* lst = new SANE_Word[vals.size() + 1]; int ind = 0; lst[ind++] = vals.size(); for (auto& e : vals) lst[ind++] = SANE_FIX(e); opt_desc_.constraint_type = SANE_CONSTRAINT_WORD_LIST; opt_desc_.constraint.word_list = lst; } } child->release(); } } int sane_opt::get_fix_id(void) { return fix_id_; } int sane_opt::get_sn(void) { return sn_; } SANE_Option_Descriptor* sane_opt::get_descriptor(void) { return &opt_desc_; } const char* sane_opt::name(void) { return opt_desc_.name; } bool sane_opt::is_enabled(void) { return enabled_; } bool sane_opt::from_json_text(int sn, const char* key, const char* json, void(*err_msg)(const char*)) { bool ret = false; std::string text(json); gb_json *jsn = new gb_json(); char *buf = nullptr; clear(); sn_ = sn; while (jsn->attach_text(&text[0])) { jsn->get_value("fix-id", fix_id_); if(!jsn->get_value("enabled", enabled_)) enabled_ = true; set_opt_desc_string_value((char**)&opt_desc_.name, key); if (jsn->get_value("title", text)) { set_opt_desc_string_value((char**)&opt_desc_.title, text.c_str()); } if (jsn->get_value("desc", text)) { set_opt_desc_string_value((char**)&opt_desc_.desc, text.c_str()); } if (!jsn->get_value("type", text)) break; if (text == "bool") { opt_desc_.type = SANE_TYPE_BOOL; } else if (text == "int") { opt_desc_.type = SANE_TYPE_INT; } else if (text == "float") { opt_desc_.type = SANE_TYPE_FIXED; } else if (text == "string") { opt_desc_.type = SANE_TYPE_STRING; } else if (text == "group") opt_desc_.type = SANE_TYPE_GROUP; else if (text == "button") opt_desc_.type = SANE_TYPE_BUTTON; else break; opt_desc_.unit = SANE_UNIT_NONE; if (jsn->get_value("unit", text)) { if (text == "pixel") opt_desc_.unit = SANE_UNIT_PIXEL; else if (text == "bit") opt_desc_.unit = SANE_UNIT_BIT; else if (text == "mm") opt_desc_.unit = SANE_UNIT_MM; else if (text == "DPI") opt_desc_.unit = SANE_UNIT_DPI; else if (text == "%") opt_desc_.unit = SANE_UNIT_PERCENT; else if (text == "microsec") opt_desc_.unit = SANE_UNIT_MICROSECOND; else opt_desc_.unit = SANE_UNIT_NONE; } jsn->get_value("size", opt_desc_.size); // cap ... init_cap(jsn); // range ... init_range(jsn); ret = true; break; } jsn->release(); return ret; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // sane_opt device_opts::device_opts() : opt_cnt_(0) { memset(&opt_0_, 0, sizeof(opt_0_)); opt_0_.cap = SANE_CAP_SOFT_DETECT; opt_0_.name = "option-count"; opt_0_.desc = "option count of this device"; opt_0_.title = "option count"; opt_0_.type = SANE_TYPE_INT; opt_0_.size = sizeof(SANE_Int); } device_opts::~device_opts() { clear(); } void device_opts::clear(void) { for (auto& v : opts_) { if(v.first == v.second->get_sn()) // fix-id has the same object, we delete object at it's option number delete v.second; } opts_.clear(); opt_cnt_ = 0; } bool device_opts::init_from(const char* jsn_text/*all options*/, void(*err_msg)(const char*)) { bool ret = false; gb_json* jsn = new gb_json(); std::string str(jsn_text); int sn = 1; clear(); if (jsn->attach_text(&str[0])) { gb_json* child = jsn->first_child(); str = ""; while (child) { str = std::move(child->to_string()); ret = add_or_replace_opt(sn++, child->key().c_str(), str.c_str(), err_msg); if (!ret) { child->release(); clear(); break; } child->release(); child = jsn->next_child(); } } jsn->release(); return ret; } bool device_opts::add_or_replace_opt(int sn, const char* name, const char* jsn_text, void(*err_msg)(const char*)) { if (opts_.count(sn)) { bool en = opts_[sn]->is_enabled(), ret = opts_[sn]->from_json_text(sn, name, jsn_text, err_msg); return ret; } sane_opt* opt = new sane_opt(); if (opt->from_json_text(sn, name, jsn_text, err_msg)) { opts_[sn] = opt; if (opt->get_fix_id() != -1) { opts_[opt->get_fix_id()] = opt; } opt_cnt_++; return true; } else { delete opt; return false; } } SANE_Option_Descriptor* device_opts::get_opt_descriptor(const void* opt, int* fix_id) { if (opt == nullptr) { if (fix_id) *fix_id = -1; return &opt_0_; } if (IS_PTR_NUMBER(opt)) { int sn = (int)opt; if (opts_.count(sn)) { if (fix_id) *fix_id = opts_[sn]->get_fix_id(); return opts_[sn]->get_descriptor(); } } else { const char* name = (const char*)opt; for (auto& v : opts_) { if (v.second->name() == name) { if (fix_id) *fix_id = v.second->get_fix_id(); return v.second->get_descriptor(); } } } return nullptr; } int device_opts::get_option_count(void) { return opt_cnt_; }