2016-11-24 18:24:47 +00:00
|
|
|
#include <cassert>
|
|
|
|
|
|
|
|
#include "components/logger.hpp"
|
2016-11-02 19:22:45 +00:00
|
|
|
#include "components/parser.hpp"
|
2016-11-21 14:07:00 +00:00
|
|
|
#include "components/signals.hpp"
|
|
|
|
#include "components/types.hpp"
|
2016-11-02 19:22:45 +00:00
|
|
|
#include "utils/math.hpp"
|
|
|
|
#include "utils/string.hpp"
|
|
|
|
|
2016-11-19 05:22:44 +00:00
|
|
|
POLYBAR_NS
|
2016-11-02 19:22:45 +00:00
|
|
|
|
2016-11-24 18:24:47 +00:00
|
|
|
/**
|
|
|
|
* Construct parser instance
|
|
|
|
*/
|
|
|
|
parser::parser(const logger& logger, const bar_settings& bar) : m_log(logger), m_bar(bar) {
|
|
|
|
assert(g_signals::parser::background_change != nullptr);
|
|
|
|
assert(g_signals::parser::foreground_change != nullptr);
|
|
|
|
assert(g_signals::parser::underline_change != nullptr);
|
|
|
|
assert(g_signals::parser::overline_change != nullptr);
|
|
|
|
assert(g_signals::parser::alignment_change != nullptr);
|
|
|
|
assert(g_signals::parser::attribute_set != nullptr);
|
|
|
|
assert(g_signals::parser::attribute_unset != nullptr);
|
|
|
|
assert(g_signals::parser::attribute_toggle != nullptr);
|
|
|
|
assert(g_signals::parser::font_change != nullptr);
|
|
|
|
assert(g_signals::parser::pixel_offset != nullptr);
|
|
|
|
assert(g_signals::parser::action_block_open != nullptr);
|
|
|
|
assert(g_signals::parser::action_block_close != nullptr);
|
|
|
|
assert(g_signals::parser::ascii_text_write != nullptr);
|
|
|
|
assert(g_signals::parser::unicode_text_write != nullptr);
|
|
|
|
assert(g_signals::parser::string_write != nullptr);
|
|
|
|
}
|
2016-11-21 14:07:00 +00:00
|
|
|
|
2016-11-02 19:22:45 +00:00
|
|
|
/**
|
2016-11-24 18:24:47 +00:00
|
|
|
* Process input string
|
2016-11-02 19:22:45 +00:00
|
|
|
*/
|
|
|
|
void parser::operator()(string data) {
|
|
|
|
size_t pos;
|
|
|
|
|
2016-11-24 18:24:47 +00:00
|
|
|
m_log.trace_x("parser: %s", data);
|
|
|
|
|
2016-11-02 19:22:45 +00:00
|
|
|
while (data.length()) {
|
|
|
|
if (data.compare(0, 2, "%{") == 0 && (pos = data.find("}")) != string::npos) {
|
|
|
|
codeblock(data.substr(2, pos - 2));
|
|
|
|
data.erase(0, pos + 1);
|
|
|
|
} else {
|
|
|
|
if ((pos = data.find("%{")) == string::npos)
|
|
|
|
pos = data.length();
|
|
|
|
data.erase(0, text(data.substr(0, pos)));
|
|
|
|
}
|
|
|
|
}
|
2016-11-25 03:10:26 +00:00
|
|
|
|
|
|
|
if (!m_actions.empty()) {
|
|
|
|
throw unclosed_actionblocks(to_string(m_actions.size()) + " unclosed action block(s)");
|
|
|
|
}
|
2016-11-02 19:22:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-11-24 18:24:47 +00:00
|
|
|
* Process contents within tag blocks, i.e: %{...}
|
2016-11-02 19:22:45 +00:00
|
|
|
*/
|
|
|
|
void parser::codeblock(string data) {
|
|
|
|
size_t pos;
|
|
|
|
|
|
|
|
while (data.length()) {
|
|
|
|
data = string_util::ltrim(data, ' ');
|
|
|
|
|
|
|
|
if (data.empty())
|
|
|
|
break;
|
|
|
|
|
2016-11-24 18:24:47 +00:00
|
|
|
char tag{data[0]};
|
2016-11-02 19:22:45 +00:00
|
|
|
string value;
|
|
|
|
|
|
|
|
// Remove the tag
|
|
|
|
data.erase(0, 1);
|
|
|
|
|
|
|
|
if ((pos = data.find_first_of(" }")) != string::npos)
|
|
|
|
value = data.substr(0, pos);
|
|
|
|
else
|
|
|
|
value = data;
|
|
|
|
|
|
|
|
switch (tag) {
|
|
|
|
case 'B':
|
2016-11-24 18:24:47 +00:00
|
|
|
g_signals::parser::background_change(parse_color(value, m_bar.background));
|
2016-11-02 19:22:45 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'F':
|
2016-11-24 18:24:47 +00:00
|
|
|
g_signals::parser::foreground_change(parse_color(value, m_bar.foreground));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'T':
|
|
|
|
g_signals::parser::font_change(parse_fontindex(value));
|
2016-11-02 19:22:45 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'U':
|
2016-11-24 18:24:47 +00:00
|
|
|
g_signals::parser::underline_change(parse_color(value, m_bar.underline.color));
|
|
|
|
g_signals::parser::overline_change(parse_color(value, m_bar.overline.color));
|
2016-11-02 19:22:45 +00:00
|
|
|
break;
|
|
|
|
|
2016-11-24 18:24:47 +00:00
|
|
|
case 'u':
|
|
|
|
g_signals::parser::underline_change(parse_color(value, m_bar.underline.color));
|
2016-11-02 19:22:45 +00:00
|
|
|
break;
|
|
|
|
|
2016-11-24 18:24:47 +00:00
|
|
|
case 'o':
|
|
|
|
g_signals::parser::overline_change(parse_color(value, m_bar.overline.color));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 'R':
|
|
|
|
g_signals::parser::background_change(m_bar.foreground);
|
|
|
|
g_signals::parser::foreground_change(m_bar.background);
|
2016-11-02 19:22:45 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'O':
|
2016-11-24 18:24:47 +00:00
|
|
|
g_signals::parser::pixel_offset(atoi(value.c_str()));
|
2016-11-02 19:22:45 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'l':
|
2016-11-24 18:24:47 +00:00
|
|
|
g_signals::parser::alignment_change(alignment::LEFT);
|
2016-11-02 19:22:45 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'c':
|
2016-11-24 18:24:47 +00:00
|
|
|
g_signals::parser::alignment_change(alignment::CENTER);
|
2016-11-02 19:22:45 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'r':
|
2016-11-24 18:24:47 +00:00
|
|
|
g_signals::parser::alignment_change(alignment::RIGHT);
|
2016-11-02 19:22:45 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case '+':
|
2016-11-24 18:24:47 +00:00
|
|
|
g_signals::parser::attribute_set(parse_attr(value[0]));
|
2016-11-02 19:22:45 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case '-':
|
2016-11-24 18:24:47 +00:00
|
|
|
g_signals::parser::attribute_unset(parse_attr(value[0]));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case '!':
|
|
|
|
g_signals::parser::attribute_toggle(parse_attr(value[0]));
|
2016-11-02 19:22:45 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case 'A':
|
|
|
|
if (isdigit(data[0]) || data[0] == ':') {
|
|
|
|
value = parse_action_cmd(data);
|
|
|
|
mousebtn btn = parse_action_btn(data);
|
|
|
|
m_actions.push_back(static_cast<int>(btn));
|
|
|
|
|
2016-11-24 18:24:47 +00:00
|
|
|
g_signals::parser::action_block_open(btn, value);
|
2016-11-02 19:22:45 +00:00
|
|
|
|
|
|
|
// make sure we strip the correct length (btn+wrapping colons)
|
2016-11-24 18:24:47 +00:00
|
|
|
if (value[0] != ':')
|
2016-11-02 19:22:45 +00:00
|
|
|
value += "0";
|
|
|
|
value += "::";
|
|
|
|
} else if (!m_actions.empty()) {
|
2016-11-24 18:24:47 +00:00
|
|
|
g_signals::parser::action_block_close(parse_action_btn(value));
|
2016-11-02 19:22:45 +00:00
|
|
|
m_actions.pop_back();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
2016-11-25 03:10:26 +00:00
|
|
|
throw unrecognized_token("Unrecognized token '" + string{tag} + "'");
|
2016-11-02 19:22:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!data.empty())
|
|
|
|
data.erase(0, !value.empty() ? value.length() : 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-11-24 18:24:47 +00:00
|
|
|
* Process text contents
|
2016-11-02 19:22:45 +00:00
|
|
|
*/
|
|
|
|
size_t parser::text(string data) {
|
|
|
|
uint8_t* utf = (uint8_t*)data.c_str();
|
|
|
|
|
2016-11-24 18:24:47 +00:00
|
|
|
if (utf[0] < 0x80) {
|
2016-11-02 19:22:45 +00:00
|
|
|
// grab all consecutive ascii chars
|
|
|
|
size_t next_tag = data.find("%{");
|
|
|
|
if (next_tag != string::npos) {
|
|
|
|
data.erase(next_tag);
|
|
|
|
}
|
|
|
|
size_t n = 0;
|
|
|
|
while (utf[n] != '\0' && utf[++n] < 0x80)
|
|
|
|
;
|
|
|
|
g_signals::parser::string_write(data.substr(0, n).c_str(), n);
|
|
|
|
return n;
|
|
|
|
} else if ((utf[0] & 0xe0) == 0xc0) { // 2 byte utf-8 sequence
|
2016-11-24 18:24:47 +00:00
|
|
|
g_signals::parser::unicode_text_write((utf[0] & 0x1f) << 6 | (utf[1] & 0x3f));
|
2016-11-02 19:22:45 +00:00
|
|
|
return 2;
|
|
|
|
} else if ((utf[0] & 0xf0) == 0xe0) { // 3 byte utf-8 sequence
|
2016-11-24 18:24:47 +00:00
|
|
|
g_signals::parser::unicode_text_write((utf[0] & 0xf) << 12 | (utf[1] & 0x3f) << 6 | (utf[2] & 0x3f));
|
2016-11-02 19:22:45 +00:00
|
|
|
return 3;
|
|
|
|
} else if ((utf[0] & 0xf8) == 0xf0) { // 4 byte utf-8 sequence
|
2016-11-24 18:24:47 +00:00
|
|
|
g_signals::parser::unicode_text_write(0xfffd);
|
2016-11-02 19:22:45 +00:00
|
|
|
return 4;
|
|
|
|
} else if ((utf[0] & 0xfc) == 0xf8) { // 5 byte utf-8 sequence
|
2016-11-24 18:24:47 +00:00
|
|
|
g_signals::parser::unicode_text_write(0xfffd);
|
2016-11-02 19:22:45 +00:00
|
|
|
return 5;
|
|
|
|
} else if ((utf[0] & 0xfe) == 0xfc) { // 6 byte utf-8 sequence
|
2016-11-24 18:24:47 +00:00
|
|
|
g_signals::parser::unicode_text_write(0xfffd);
|
2016-11-02 19:22:45 +00:00
|
|
|
return 6;
|
|
|
|
} else { // invalid utf-8 sequence
|
2016-11-24 18:24:47 +00:00
|
|
|
g_signals::parser::ascii_text_write(utf[0]);
|
2016-11-02 19:22:45 +00:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-11-24 18:24:47 +00:00
|
|
|
* Process color hex string and convert it to the correct value
|
2016-11-02 19:22:45 +00:00
|
|
|
*/
|
2016-11-21 14:07:00 +00:00
|
|
|
uint32_t parser::parse_color(string s, uint32_t fallback) {
|
|
|
|
uint32_t color{0};
|
|
|
|
if (s.empty() || s[0] == '-' || (color = color_util::parse(s, fallback)) == fallback)
|
2016-11-02 19:22:45 +00:00
|
|
|
return fallback;
|
2016-11-21 14:07:00 +00:00
|
|
|
return color_util::premultiply_alpha(color);
|
2016-11-02 19:22:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-11-24 18:24:47 +00:00
|
|
|
* Process font index and convert it to the correct value
|
2016-11-02 19:22:45 +00:00
|
|
|
*/
|
2016-11-21 14:07:00 +00:00
|
|
|
int8_t parser::parse_fontindex(string s) {
|
2016-11-24 18:24:47 +00:00
|
|
|
if (s.empty() || s[0] == '-') {
|
2016-11-02 19:22:45 +00:00
|
|
|
return -1;
|
2016-11-21 14:07:00 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
return std::stoul(s.c_str(), nullptr, 10);
|
|
|
|
} catch (const std::invalid_argument& err) {
|
|
|
|
return -1;
|
|
|
|
}
|
2016-11-02 19:22:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-11-24 18:24:47 +00:00
|
|
|
* Process attribute token and convert it to the correct value
|
2016-11-02 19:22:45 +00:00
|
|
|
*/
|
2016-11-24 18:24:47 +00:00
|
|
|
attribute parser::parse_attr(const char attr) {
|
|
|
|
switch (attr) {
|
2016-11-02 19:22:45 +00:00
|
|
|
case 'o':
|
2016-11-24 18:24:47 +00:00
|
|
|
return attribute::OVERLINE;
|
2016-11-02 19:22:45 +00:00
|
|
|
case 'u':
|
2016-11-24 18:24:47 +00:00
|
|
|
return attribute::UNDERLINE;
|
|
|
|
default:
|
2016-11-25 03:10:26 +00:00
|
|
|
throw unrecognized_token("Unrecognized attribute '" + string{attr} + "'");
|
2016-11-02 19:22:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-11-24 18:24:47 +00:00
|
|
|
* Process action button token and convert it to the correct value
|
2016-11-02 19:22:45 +00:00
|
|
|
*/
|
|
|
|
mousebtn parser::parse_action_btn(string data) {
|
|
|
|
if (data[0] == ':')
|
|
|
|
return mousebtn::LEFT;
|
|
|
|
else if (isdigit(data[0]))
|
|
|
|
return static_cast<mousebtn>(data[0] - '0');
|
|
|
|
else if (!m_actions.empty())
|
|
|
|
return static_cast<mousebtn>(m_actions.back());
|
|
|
|
else
|
|
|
|
return mousebtn::NONE;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2016-11-24 18:24:47 +00:00
|
|
|
* Process action command string
|
2016-11-02 19:22:45 +00:00
|
|
|
*/
|
|
|
|
string parser::parse_action_cmd(string data) {
|
2016-11-21 14:07:00 +00:00
|
|
|
size_t start, end;
|
|
|
|
if ((start = data.find(':')) == string::npos)
|
|
|
|
return "";
|
|
|
|
if ((end = data.find(':', start + 1)) == string::npos)
|
|
|
|
return "";
|
2016-11-02 19:22:45 +00:00
|
|
|
return string_util::trim(data.substr(start, end), ':');
|
|
|
|
}
|
|
|
|
|
2016-11-19 05:22:44 +00:00
|
|
|
POLYBAR_NS_END
|