1
0
Fork 0
mirror of https://github.com/polybar/polybar.git synced 2024-11-11 13:50:56 -05:00
polybar/src/components/parser.cpp

276 lines
6.9 KiB
C++
Raw Normal View History

#include <cassert>
#include "components/logger.hpp"
2016-11-02 15:22:45 -04:00
#include "components/parser.hpp"
2016-11-21 09:07:00 -05:00
#include "components/types.hpp"
#include "events/signal.hpp"
#include "events/signal_emitter.hpp"
2016-11-02 15:22:45 -04:00
#include "utils/math.hpp"
#include "utils/string.hpp"
2016-11-19 00:22:44 -05:00
POLYBAR_NS
2016-11-02 15:22:45 -04:00
using namespace signals::parser;
/**
* Construct parser instance
*/
parser::parser(signal_emitter& emitter, const logger& logger, const bar_settings& bar)
: m_sig(emitter), m_log(logger), m_bar(bar) {}
2016-11-21 09:07:00 -05:00
2016-11-02 15:22:45 -04:00
/**
* Process input string
2016-11-02 15:22:45 -04:00
*/
void parser::operator()(string data) {
size_t pos;
m_log.trace_x("parser: %s", data);
2016-11-02 15:22:45 -04:00
while (data.length()) {
2016-11-25 07:55:15 -05:00
if (data.compare(0, 2, "%{") == 0 && (pos = data.find('}')) != string::npos) {
2016-11-02 15:22:45 -04:00
codeblock(data.substr(2, pos - 2));
data.erase(0, pos + 1);
} else {
2016-11-25 07:55:15 -05:00
if ((pos = data.find("%{")) == string::npos) {
2016-11-02 15:22:45 -04:00
pos = data.length();
2016-11-25 07:55:15 -05:00
}
2016-11-02 15:22:45 -04:00
data.erase(0, text(data.substr(0, pos)));
}
}
2016-11-24 22:10:26 -05:00
if (!m_actions.empty()) {
throw unclosed_actionblocks(to_string(m_actions.size()) + " unclosed action block(s)");
}
2016-11-02 15:22:45 -04:00
}
/**
* Process contents within tag blocks, i.e: %{...}
2016-11-02 15:22:45 -04:00
*/
void parser::codeblock(string data) {
size_t pos;
while (data.length()) {
data = string_util::ltrim(data, ' ');
2016-11-25 07:55:15 -05:00
if (data.empty()) {
2016-11-02 15:22:45 -04:00
break;
2016-11-25 07:55:15 -05:00
}
2016-11-02 15:22:45 -04:00
char tag{data[0]};
2016-11-02 15:22:45 -04:00
string value;
// Remove the tag
data.erase(0, 1);
2016-11-25 07:55:15 -05:00
if ((pos = data.find_first_of(" }")) != string::npos) {
2016-11-02 15:22:45 -04:00
value = data.substr(0, pos);
2016-11-25 07:55:15 -05:00
} else {
2016-11-02 15:22:45 -04:00
value = data;
2016-11-25 07:55:15 -05:00
}
2016-11-02 15:22:45 -04:00
switch (tag) {
case 'B':
m_sig.emit(change_background{parse_color(value, m_bar.background)});
2016-11-02 15:22:45 -04:00
break;
case 'F':
m_sig.emit(change_foreground{parse_color(value, m_bar.foreground)});
break;
case 'T':
m_sig.emit(change_font{parse_fontindex(value)});
2016-11-02 15:22:45 -04:00
break;
case 'U':
m_sig.emit(change_underline{parse_color(value, m_bar.underline.color)});
m_sig.emit(change_overline{parse_color(value, m_bar.overline.color)});
2016-11-02 15:22:45 -04:00
break;
case 'u':
m_sig.emit(change_underline{parse_color(value, m_bar.underline.color)});
2016-11-02 15:22:45 -04:00
break;
case 'o':
m_sig.emit(change_overline{parse_color(value, m_bar.overline.color)});
break;
case 'R':
m_sig.emit(change_background{parse_color(value, m_bar.foreground)});
m_sig.emit(change_foreground{parse_color(value, m_bar.background)});
2016-11-02 15:22:45 -04:00
break;
case 'O':
m_sig.emit(offset_pixel{static_cast<int16_t>(std::atoi(value.c_str()))});
2016-11-02 15:22:45 -04:00
break;
case 'l':
m_sig.emit(change_alignment{alignment::LEFT});
2016-11-02 15:22:45 -04:00
break;
case 'c':
m_sig.emit(change_alignment{alignment::CENTER});
2016-11-02 15:22:45 -04:00
break;
case 'r':
m_sig.emit(change_alignment{alignment::RIGHT});
2016-11-02 15:22:45 -04:00
break;
case '+':
m_sig.emit(attribute_set{parse_attr(value[0])});
2016-11-02 15:22:45 -04:00
break;
case '-':
m_sig.emit(attribute_unset{parse_attr(value[0])});
break;
case '!':
m_sig.emit(attribute_toggle{parse_attr(value[0])});
2016-11-02 15:22:45 -04: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));
m_sig.emit(action_begin{action{btn, value}});
2016-11-02 15:22:45 -04:00
// make sure we strip the correct length (btn+wrapping colons)
2016-11-25 07:55:15 -05:00
if (value[0] != ':') {
2016-11-02 15:22:45 -04:00
value += "0";
2016-11-25 07:55:15 -05:00
}
2016-11-02 15:22:45 -04:00
value += "::";
} else if (!m_actions.empty()) {
m_sig.emit(action_end{parse_action_btn(value)});
2016-11-02 15:22:45 -04:00
m_actions.pop_back();
}
break;
default:
2016-11-24 22:10:26 -05:00
throw unrecognized_token("Unrecognized token '" + string{tag} + "'");
2016-11-02 15:22:45 -04:00
}
2016-11-25 07:55:15 -05:00
if (!data.empty()) {
2016-11-02 15:22:45 -04:00
data.erase(0, !value.empty() ? value.length() : 1);
2016-11-25 07:55:15 -05:00
}
2016-11-02 15:22:45 -04:00
}
}
/**
* Process text contents
2016-11-02 15:22:45 -04:00
*/
size_t parser::text(string data) {
uint8_t* utf = reinterpret_cast<uint8_t*>(const_cast<char*>(data.c_str()));
2016-11-02 15:22:45 -04:00
if (utf[0] < 0x80) {
2016-11-02 15:22:45 -04: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;
2016-11-25 07:55:15 -05:00
while (utf[n] != '\0' && utf[++n] < 0x80) {
2016-11-02 15:22:45 -04:00
;
2016-11-25 07:55:15 -05:00
}
m_sig.emit(write_text_string{data.substr(0, n)});
2016-11-02 15:22:45 -04:00
return n;
} else if ((utf[0] & 0xe0) == 0xc0) { // 2 byte utf-8 sequence
m_sig.emit(write_text_unicode{static_cast<uint16_t>((utf[0] & 0x1f) << 6 | (utf[1] & 0x3f))});
2016-11-02 15:22:45 -04:00
return 2;
} else if ((utf[0] & 0xf0) == 0xe0) { // 3 byte utf-8 sequence
m_sig.emit(
write_text_unicode{static_cast<uint16_t>((utf[0] & 0xf) << 12 | (utf[1] & 0x3f) << 6 | (utf[2] & 0x3f))});
2016-11-02 15:22:45 -04:00
return 3;
} else if ((utf[0] & 0xf8) == 0xf0) { // 4 byte utf-8 sequence
m_sig.emit(write_text_unicode{static_cast<uint16_t>(0xfffd)});
2016-11-02 15:22:45 -04:00
return 4;
} else if ((utf[0] & 0xfc) == 0xf8) { // 5 byte utf-8 sequence
m_sig.emit(write_text_unicode{static_cast<uint16_t>(0xfffd)});
2016-11-02 15:22:45 -04:00
return 5;
} else if ((utf[0] & 0xfe) == 0xfc) { // 6 byte utf-8 sequence
m_sig.emit(write_text_unicode{static_cast<uint16_t>(0xfffd)});
2016-11-02 15:22:45 -04:00
return 6;
} else { // invalid utf-8 sequence
m_sig.emit(write_text_ascii{utf[0]});
2016-11-02 15:22:45 -04:00
return 1;
}
}
/**
* Process color hex string and convert it to the correct value
2016-11-02 15:22:45 -04:00
*/
2016-11-21 09:07:00 -05:00
uint32_t parser::parse_color(string s, uint32_t fallback) {
uint32_t color{0};
2016-11-25 07:55:15 -05:00
if (s.empty() || s[0] == '-' || (color = color_util::parse(s, fallback)) == fallback) {
2016-11-02 15:22:45 -04:00
return fallback;
2016-11-25 07:55:15 -05:00
}
2016-11-21 09:07:00 -05:00
return color_util::premultiply_alpha(color);
2016-11-02 15:22:45 -04:00
}
/**
* Process font index and convert it to the correct value
2016-11-02 15:22:45 -04:00
*/
2016-11-21 09:07:00 -05:00
int8_t parser::parse_fontindex(string s) {
if (s.empty() || s[0] == '-') {
2016-11-02 15:22:45 -04:00
return -1;
2016-11-21 09:07:00 -05:00
}
try {
2016-11-25 07:55:15 -05:00
return std::stoul(s, nullptr, 10);
2016-11-21 09:07:00 -05:00
} catch (const std::invalid_argument& err) {
return -1;
}
2016-11-02 15:22:45 -04:00
}
/**
* Process attribute token and convert it to the correct value
2016-11-02 15:22:45 -04:00
*/
attribute parser::parse_attr(const char attr) {
switch (attr) {
2016-11-02 15:22:45 -04:00
case 'o':
return attribute::OVERLINE;
2016-11-02 15:22:45 -04:00
case 'u':
return attribute::UNDERLINE;
default:
2016-11-24 22:10:26 -05:00
throw unrecognized_token("Unrecognized attribute '" + string{attr} + "'");
2016-11-02 15:22:45 -04:00
}
}
/**
* Process action button token and convert it to the correct value
2016-11-02 15:22:45 -04:00
*/
mousebtn parser::parse_action_btn(string data) {
2016-11-25 07:55:15 -05:00
if (data[0] == ':') {
2016-11-02 15:22:45 -04:00
return mousebtn::LEFT;
2016-11-25 07:55:15 -05:00
} else if (isdigit(data[0])) {
2016-11-02 15:22:45 -04:00
return static_cast<mousebtn>(data[0] - '0');
2016-11-25 07:55:15 -05:00
} else if (!m_actions.empty()) {
2016-11-02 15:22:45 -04:00
return static_cast<mousebtn>(m_actions.back());
2016-11-25 07:55:15 -05:00
} else {
2016-11-02 15:22:45 -04:00
return mousebtn::NONE;
2016-11-25 07:55:15 -05:00
}
2016-11-02 15:22:45 -04:00
}
/**
* Process action command string
2016-11-02 15:22:45 -04:00
*/
2016-11-25 07:55:15 -05:00
string parser::parse_action_cmd(const string& data) {
size_t start{0};
while ((start = data.find(':', start)) != string::npos && data[start - 1] == '\\') {
start++;
}
if (start == string::npos) {
2016-11-21 09:07:00 -05:00
return "";
2016-11-25 07:55:15 -05:00
}
size_t end{start + 1};
while ((end = data.find(':', end)) != string::npos && data[end - 1] == '\\') {
end++;
}
if (end == string::npos) {
2016-11-21 09:07:00 -05:00
return "";
2016-11-25 07:55:15 -05:00
}
2016-11-02 15:22:45 -04:00
return string_util::trim(data.substr(start, end), ':');
}
2016-11-19 00:22:44 -05:00
POLYBAR_NS_END