533 lines
11 KiB
C++
533 lines
11 KiB
C++
#include "sane_opts.h"
|
|
|
|
#include <json/gb_json.h>
|
|
#include <algorithm>
|
|
#include <string.h>
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// sane_opt
|
|
sane_opt::sane_opt() : fix_id_(-1), enabled_(true), visible_(true)
|
|
{
|
|
memset(&opt_desc_, 0, sizeof(opt_desc_));
|
|
}
|
|
sane_opt::~sane_opt()
|
|
{
|
|
clear();
|
|
}
|
|
|
|
std::string sane_opt::readable_text(const char* type, void* value)
|
|
{
|
|
if (strcmp(type, JSON_SANE_TYPE_BOOL) == 0)
|
|
return sane_opt::readable_text(SANE_TYPE_BOOL, value);
|
|
else if (strcmp(type, JSON_SANE_TYPE_INT) == 0)
|
|
return sane_opt::readable_text(SANE_TYPE_INT, value);
|
|
else if (strcmp(type, JSON_SANE_TYPE_FIXED) == 0)
|
|
return sane_opt::readable_text(SANE_TYPE_FIXED, value);
|
|
else if (strcmp(type, JSON_SANE_TYPE_STRING) == 0)
|
|
return sane_opt::readable_text(SANE_TYPE_STRING, value);
|
|
else
|
|
return "";
|
|
}
|
|
std::string sane_opt::readable_text(SANE_Value_Type type, void* value)
|
|
{
|
|
if (type == SANE_TYPE_BOOL)
|
|
return *(bool*)value ? "true" : "false";
|
|
else if (type == SANE_TYPE_INT)
|
|
return std::to_string(*(int*)value);
|
|
else if (type == SANE_TYPE_FIXED)
|
|
return std::to_string(SANE_UNFIX(*(SANE_Fixed*)value));
|
|
else if (type == SANE_TYPE_STRING)
|
|
return (char*)value;
|
|
else
|
|
return "";
|
|
}
|
|
|
|
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;
|
|
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;
|
|
|
|
std::string cat("");
|
|
if (jsn->get_value("cat", cat))
|
|
{
|
|
if (cat == "advanced")
|
|
opt_desc_.cap |= SANE_CAP_ADVANCED;
|
|
}
|
|
|
|
int visb = 0;
|
|
if (jsn->get_value("visible", visb))
|
|
{
|
|
visible_ = visb == OPT_VISIBLE_ALL;
|
|
}
|
|
}
|
|
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<std::string> 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<int> 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<double> 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_;
|
|
}
|
|
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::is_visible(void)
|
|
{
|
|
return visible_;
|
|
}
|
|
|
|
|
|
bool sane_opt::from_json_text(const char* key, const char* json, void(*err_msg)(const char*))
|
|
{
|
|
bool ret = false;
|
|
std::string text(json);
|
|
gb_json *jsn = new gb_json();
|
|
|
|
if (jsn->attach_text(&text[0]))
|
|
{
|
|
jsn->key() = key;
|
|
ret = from_json_object(jsn, err_msg);
|
|
}
|
|
jsn->release();
|
|
|
|
return ret;
|
|
}
|
|
bool sane_opt::from_json_object(gb_json* jsn, void(*err_msg)(const char*))
|
|
{
|
|
bool ret = false;
|
|
std::string text("");
|
|
|
|
clear();
|
|
while (1)
|
|
{
|
|
if (!jsn->get_value("fix-id", fix_id_))
|
|
fix_id_ = -1;
|
|
if (!jsn->get_value("enabled", enabled_))
|
|
enabled_ = true;
|
|
|
|
set_opt_desc_string_value((char**)&opt_desc_.name, jsn->key().c_str());
|
|
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;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
// sane_opt
|
|
sane_options::sane_options() : 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);
|
|
}
|
|
sane_options::~sane_options()
|
|
{
|
|
clear();
|
|
}
|
|
|
|
|
|
void sane_options::clear(void)
|
|
{
|
|
std::vector<sane_opt*> tofree;
|
|
|
|
for (auto& v : opts_)
|
|
{
|
|
if (std::find(tofree.begin(), tofree.end(), v.second) == tofree.end())
|
|
tofree.push_back(v.second);
|
|
}
|
|
for (auto& v : tofree)
|
|
delete v;
|
|
|
|
tofree.clear();
|
|
opts_.clear();
|
|
opt_cnt_ = 0;
|
|
}
|
|
|
|
bool sane_options::init_from(const char* jsn_text/*all options*/, void(*err_msg)(const char*), bool clear_prev)
|
|
{
|
|
bool ret = false;
|
|
gb_json* jsn = new gb_json();
|
|
std::string str(jsn_text);
|
|
int sn = 0;
|
|
|
|
if(clear_prev)
|
|
clear(); // keep the SANE_Option_Descriptor*
|
|
if (jsn->attach_text(&str[0]))
|
|
{
|
|
gb_json* child = jsn->first_child();
|
|
str = "";
|
|
|
|
while (child)
|
|
{
|
|
str = std::move(child->to_string());
|
|
sn++;
|
|
ret = add_or_replace_opt(sn, child, err_msg);
|
|
child->release();
|
|
|
|
if (!ret)
|
|
{
|
|
if (clear_prev)
|
|
clear();
|
|
break;
|
|
}
|
|
child = jsn->next_child();
|
|
}
|
|
}
|
|
jsn->release();
|
|
|
|
return ret;
|
|
}
|
|
bool sane_options::add_or_replace_opt(int& sn, const char* name, const char* jsn_text, void(*err_msg)(const char*))
|
|
{
|
|
bool ret = false;
|
|
std::string text(jsn_text);
|
|
gb_json *jsn = new gb_json();
|
|
|
|
if (jsn->attach_text(&text[0]))
|
|
{
|
|
jsn->key() = name;
|
|
ret = add_or_replace_opt(sn, jsn, err_msg);
|
|
}
|
|
jsn->release();
|
|
|
|
return ret;
|
|
}
|
|
bool sane_options::add_or_replace_opt(int& sn, gb_json* jsn, void(*err_msg)(const char*))
|
|
{
|
|
int id = sn;
|
|
if (!jsn->get_value("fix-id", id))
|
|
id = sn;
|
|
|
|
if (opts_.count(id))
|
|
{
|
|
return opts_[id]->from_json_object(jsn, err_msg);
|
|
}
|
|
|
|
sane_opt* opt = new sane_opt();
|
|
|
|
if (opt->from_json_object(jsn, err_msg))
|
|
{
|
|
if (opt->is_visible())
|
|
{
|
|
opts_[sn] = opt;
|
|
opt_cnt_++;
|
|
}
|
|
else
|
|
sn--;
|
|
if (opt->get_fix_id() > SANE_OPT_ID_BASE)
|
|
{
|
|
opts_[opt->get_fix_id()] = opt;
|
|
}
|
|
else if (!opt->is_visible()) // no fix-id and SANE_Int number will not be existence
|
|
delete opt;
|
|
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
delete opt;
|
|
|
|
return false;
|
|
}
|
|
}
|
|
SANE_Option_Descriptor* sane_options::get_opt_descriptor(const void* opt, int* fix_id, int ind_base)
|
|
{
|
|
if (fix_id)
|
|
*fix_id = -1;
|
|
|
|
if (opt == nullptr)
|
|
{
|
|
return &opt_0_;
|
|
}
|
|
|
|
if (IS_PTR_NUMBER(opt))
|
|
{
|
|
int sn = (int)(long)opt;
|
|
if(sn < SANE_OPT_ID_BASE)
|
|
sn -= ind_base;
|
|
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 sane_options::get_option_count(void)
|
|
{
|
|
return opt_cnt_;
|
|
}
|
|
bool sane_options::enum_invisible_fix_ids(struct _fix_id_cb* fcb) // return whether the callback stopped the enumeration
|
|
{
|
|
bool stopped = false;
|
|
|
|
for (auto& v : opts_)
|
|
{
|
|
if (!v.second->is_visible())
|
|
{
|
|
if (!fcb->cb(v.second->get_fix_id(), fcb->param))
|
|
{
|
|
stopped = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return stopped;
|
|
}
|