From 17ba917129400f9917ecb39e08824f73d42ec390 Mon Sep 17 00:00:00 2001 From: JJoeDev Date: Sun, 11 Aug 2024 01:42:35 +0200 Subject: [PATCH] README, Custom file parser, Launch args, Examples, Workflow --- .github/workflows/build-and-release.yml | 26 +++++++ README.md | 95 ++++++++++++++++++++++--- examples/config.bar | 42 +++++++++++ examples/time.lua | 4 ++ src/bar.cpp | 32 ++++++--- src/bar.h | 14 +++- src/configParser.cpp | 46 ++++++++++++ src/configParser.h | 61 ++++++++++++++++ src/general.config.h | 42 ----------- src/main.cpp | 30 +++++++- src/renderer.cpp | 28 ++++++-- src/renderer.h | 9 ++- 12 files changed, 351 insertions(+), 78 deletions(-) create mode 100644 .github/workflows/build-and-release.yml create mode 100644 examples/config.bar create mode 100644 examples/time.lua create mode 100644 src/configParser.cpp create mode 100644 src/configParser.h delete mode 100644 src/general.config.h diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml new file mode 100644 index 0000000..4d4bacd --- /dev/null +++ b/.github/workflows/build-and-release.yml @@ -0,0 +1,26 @@ +name: C/C++ CI + +on: + push: + branches: [main] + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Install dependencies + run: | + sudo apt update + sudo apt install -y cmake build-essential libxcb-dev libsol2-dev + + - name: Run cmake + run: | + mkdir build + cd build + cmake -DCMAKE_BUILD_TYPE=Release .. + + - name: Build + run: make -j \ No newline at end of file diff --git a/README.md b/README.md index 2bee677..daff4da 100644 --- a/README.md +++ b/README.md @@ -15,9 +15,27 @@ MiBar is a simple Linux X11 status bar built with the C++ programming language. MiBar is supposed to be customizable and easy to configure. This is the reason for using the Lua language for plugins as it makes it easy to develop for MiBar. +## Contents + +* [Getting Started](#getting-started) + +* * [Dependencies](#dependencies) + +* * [Installing](#installing) + +* * [Running MiBar](#running-mibar) + +* [Configuration](#configuring) + +* [Images](#images) + +* [Plugin Development](#plugin-development) + +* [License](#license) + ## Getting Started -Currently the only way to use MiBar is by building from source. +MiBar has migrated from a C header file for configuration, to a .bar configuration file. This means you can now build MiBar once and change settings without having to build again. ### Dependencies @@ -29,9 +47,7 @@ Currently the only way to use MiBar is by building from source. ### Installing -MiBar can only be run by building from source until a future release. - -But until then this is how you can use MiBar +Building from source is as quick as running the following commands once. ``` $ git clone https://github.com/JJoeDev/MiBar.git @@ -40,18 +56,79 @@ $ cmake -DCMAKE_BUILD_TYPE=Release . $ make ``` -### Executing program +### Running MiBar -Once MiBar has bin built from source the executable can be found in the bin directory +MiBar can be run without any arguments, this will make it look for its own configuration file `config.bar` in `~/.config/MiBar` if this file does not exist the app cannot launch. There is an example file in the example repo ``` $ ./bin/MiBar ``` -## Configuring +MiBar can also be run with some arguments + +```bash +./bin/MiBar -h +./bin/MiBar --help +``` + +using the -h or --help flag MiBar will just display a very short help list + +```bash +./bin/MiBar -c filename +./bin/MiBar --config filename +``` -Currently the bar uses a C header file for its configuration, when changing this file you will need to recompile the application again. +using the -c or --config flag you can specify a custom configuration file name. MiBar will still look in `~/.config/MiBar` for this file. -The configuration file can be found in [src/general.config.h](./src/general.config.h) +Note that adding a file extention with this flag does not help. If the .txt extension is added MiBar will look for `filename.txt.bar` + +## Configuration + +MiBar uses its own configuration format that is designed to be really simple to use. For a demonstration, here is the example config file included in this repo + +```bar +* Comment. Comments can only be placed before the ':' as to not interfer with some settings + +* Colors. Colors use the hexadecimal format and are prefixed with 0x +* Hexadecimal values go from 0 to F (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F) +* The colors are formatted as 0xRR GG BB where RR, GG, and BB reprecent red green and blue +* MiBar has support for 5 colors, all assigned a value as below +* Currently not all values are in use, but they will be in the future + +Background: 0x111111 +Foreground: 0x999999 +Color1: 0x777777 +Color2: 0x555555 +Color3: 0x333333 + +* TargetMonitor is the monitor MiBar should locate and move to + +TargetMonitor: HDMI-0 + +* Currently MiBar only supports fronts built in to X. +* These fonts can be listed with a utility like 'xlsfonts' in a terminal + +Font: lucidasans-10 +FontFallback: fixed + +* The transform values are additive to the information recieved from TargetMonitor +* This means if the monitor is 1920 wide and BarWidth is 0 then the bar is also 1920 wide +* To make the bar slimmer BarWidth needs to be a negative number +* BarHeight is not aditive and is 0 high by default +* BarX and BarY is the monitor coordinates for where to draw the bar at + +BarWidth: 0 +BarHeight: 32 +BarX: 0 +BarY: 0 + +* The following settings are to render a small line under every rendered component on the bar +* UseUnderline will be false unless its value is directly "true" + +UseUnderlines: true +UnderlineHeight: 3 +UnderlineOffsetX: 0 +UnderlineOffsetY: 0 +``` ## Images diff --git a/examples/config.bar b/examples/config.bar new file mode 100644 index 0000000..fd8495b --- /dev/null +++ b/examples/config.bar @@ -0,0 +1,42 @@ +* Comment. Comments can only be placed before the ':' as to not interfer with some settings + +* Colors. Colors use the hexadecimal format and are prefixed with 0x +* Hexadecimal values go from 0 to F (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F) +* The colors are formatted as 0xRR GG BB where RR, GG, and BB reprecent red green and blue +* MiBar has support for 5 colors, all assigned a value as below +* Currently not all values are in use, but they will be in the future + +Background: 0x111111 +Foreground: 0x999999 +Color1: 0x777777 +Color2: 0x555555 +Color3: 0x333333 + +* TargetMonitor is the monitor MiBar should locate and move to + +TargetMonitor: HDMI-0 + +* Currently MiBar only supports fronts built in to X. +* These fonts can be listed with a utility like 'xlsfonts' in a terminal + +Font: lucidasans-10 +FontFallback: fixed + +* The transform values are additive to the information recieved from TargetMonitor +* This means if the monitor is 1920 wide and BarWidth is 0 then the bar is also 1920 wide +* To make the bar slimmer BarWidth needs to be a negative number +* BarHeight is not aditive and is 0 high by default +* BarX and BarY is the monitor coordinates for where to draw the bar at + +BarWidth: 0 +BarHeight: 32 +BarX: 0 +BarY: 0 + +* The following settings are to render a small line under every rendered component on the bar +* UseUnderline will be false unless its value is directly "true" + +UseUnderlines: true +UnderlineHeight: 3 +UnderlineOffsetX: 0 +UnderlineOffsetY: 0 \ No newline at end of file diff --git a/examples/time.lua b/examples/time.lua new file mode 100644 index 0000000..d16a56b --- /dev/null +++ b/examples/time.lua @@ -0,0 +1,4 @@ +local time = os.date("%a %d // %H:%M") + +-- DrawString takes a string, alignment option, and additive x axis value +DrawString(time, Alignment.CENTER, 0) diff --git a/src/bar.cpp b/src/bar.cpp index ac94362..ae4ad03 100644 --- a/src/bar.cpp +++ b/src/bar.cpp @@ -8,7 +8,15 @@ #include #endif -mibar::mibar(){ +MiBar::MiBar(const std::string& file){ + //ConfigParser cfg(file); + m_cfg.Parse(file); + + m_configX = std::stoi(m_cfg.GetConfig(BAR_X)); + m_configY = std::stoi(m_cfg.GetConfig(BAR_Y)); + m_configW = std::stoi(m_cfg.GetConfig(BAR_W)); + m_configH = std::stoi(m_cfg.GetConfig(BAR_H)); + m_conn = xcb_connect(nullptr, nullptr); if(xcb_connection_has_error(m_conn)){ m_logger.Log(__FILE_NAME__, __LINE__, "Could not connect to X server!", LogLvl::ERROR); @@ -22,12 +30,12 @@ mibar::mibar(){ m_winValues[1] = XCB_EVENT_MASK_EXPOSURE; Randr r(m_conn, m_screen); - const auto mon = r.GetDisplayInfo(TARGET_MONITOR); + const auto mon = r.GetDisplayInfo(m_cfg.GetConfig(TARGET_MON)); - m_x = mon->x + BAR_X; - m_y = mon->y + BAR_Y; - m_w = mon->width + BAR_WIDTH; - m_h = BAR_HEIGHT; + m_x = mon->x + m_configX; + m_y = mon->y + m_configY; + m_w = mon->width + m_configW; + m_h = m_configH; m_window = xcb_generate_id(m_conn); xcb_create_window(m_conn, @@ -48,23 +56,25 @@ mibar::mibar(){ // EWMH (Extended Window Manager Hint) Reserve space for bar xcb_atom_t wmStrutPartialAtom = GetAtom(m_conn, "_NET_WM_STRUT_PARTIAL"); - uint32_t strut[12] = {0}; // https://specifications.freedesktop.org/wm-spec/1.3/ar01s05.html + uint32_t strut[12]{}; // https://specifications.freedesktop.org/wm-spec/1.3/ar01s05.html section 5.10 + strut[2] = m_h + m_configY; xcb_change_property(m_conn, XCB_PROP_MODE_REPLACE, m_window, wmStrutPartialAtom, XCB_ATOM_CARDINAL, 32, 12, strut); // Set window title - xcb_change_property(m_conn, XCB_PROP_MODE_REPLACE, m_window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, sizeof(char*), 5, "MiBar"); // 5 = 5 chars + std::string windowTitle = "MiBar_" + m_cfg.GetConfig(TARGET_MON); + xcb_change_property(m_conn, XCB_PROP_MODE_REPLACE, m_window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, sizeof(char*), windowTitle.size(), windowTitle.c_str()); xcb_map_window(m_conn, m_window); xcb_flush(m_conn); } -mibar::~mibar(){ +MiBar::~MiBar(){ } -void mibar::EventLoop() { - Renderer r(m_screen, m_conn, m_window); +void MiBar::EventLoop() { + Renderer r(m_screen, m_conn, m_window, m_cfg); PluginManager pmgr; pmgr.ExposeFuncToLua("DrawString", [&r](const std::string& str, ALIGNMENT align, const int x){r.DrawStr(str, align, x);}); diff --git a/src/bar.h b/src/bar.h index 62ff459..1af9554 100644 --- a/src/bar.h +++ b/src/bar.h @@ -4,11 +4,12 @@ #include #include "logger.h" +#include "configParser.h" -class mibar{ +class MiBar{ public: - mibar(); - ~mibar(); + MiBar(const std::string& file); + ~MiBar(); /** Run the XCB event loop. This loop waits for exposure events to know when to re-render the bar @@ -16,11 +17,18 @@ class mibar{ void EventLoop(); private: + ConfigParser m_cfg; + // Basic connections xcb_connection_t* m_conn = nullptr; xcb_screen_t* m_screen = nullptr; xcb_window_t m_window = 0; + int m_configX = 0; + int m_configY = 0; + int m_configW = 0; + int m_configH = 0; + int m_x, m_y, m_w, m_h; uint32_t m_winMask = 0; diff --git a/src/configParser.cpp b/src/configParser.cpp new file mode 100644 index 0000000..78c514b --- /dev/null +++ b/src/configParser.cpp @@ -0,0 +1,46 @@ +#include +#include +#include + +#include "configParser.h" + +ConfigParser::ConfigParser(){ + +} + +void ConfigParser::Parse(const std::string& file){ + auto configDir = std::filesystem::path(getenv("HOME")) / ".config/MiBar"; + + if(file.empty()){ + configDir /= "config"; + } + else{ + configDir /= file; + } + + configDir += ".bar"; + + // Parsing config file + std::fstream infile(configDir); + + std::string line; + while(std::getline(infile, line)){ + std::istringstream isLine(line); + std::string key; + + if(std::getline(isLine, key, ':')){ + if(key.find('*') != std::string::npos) continue; + + std::string value; + if(std::getline(isLine, value)){ + value.erase(std::remove_if(value.begin(), value.end(), ::isspace), value.end()); + m_configs[key] = value; + } + } + } +} + +const std::string ConfigParser::GetConfig(const int key) const { + auto it = m_configs.find(m_configTable[key]); + return it->second; +} \ No newline at end of file diff --git a/src/configParser.h b/src/configParser.h new file mode 100644 index 0000000..911a623 --- /dev/null +++ b/src/configParser.h @@ -0,0 +1,61 @@ +#ifndef MIBAR_CONFIG_PARSER_H +#define MIBAR_CONFIG_PARSER_H + +#include + +#include "logger.h" + +enum cfgVars { + BG_COL = 0, + FG_COL, + COL1, + COL2, + COL3, + TARGET_MON, + FONT, + FB_FONT, + BAR_W, + BAR_H, + BAR_X, + BAR_Y, + USE_UNDERL, + UNDERL_H, + UNDERL_OFF_X, + UNDERL_OFF_Y, + CFG_COUNT, +}; + +class ConfigParser{ +public: + ConfigParser(); + ~ConfigParser() = default; + + void Parse(const std::string& file = "config"); + + //const std::string GetConfig(const std::string& key) const; + const std::string GetConfig(const int key) const; + +private: + std::unordered_map m_configs; + + const char* m_configTable[CFG_COUNT]{ + "Background", + "Foreground", + "Color1", + "Color2", + "Color3", + "TargetMonitor", + "Font", + "FontFallback", + "BarWidth", + "BarHeight", + "BarX", + "BarY", + "UseUnderlines", + "UnderlineHeight", + "UnderlineOffsetX", + "UnderlineOffsetY", + }; +}; + +#endif diff --git a/src/general.config.h b/src/general.config.h deleted file mode 100644 index 17ceacd..0000000 --- a/src/general.config.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef MIBAR_GENERAL_CONFIG_H -#define MIBAR_GENERAL_CONFIG_H - -// Colors to use -#define BACKGROUND 0x111111 -#define FOREGROUND 0x999999 -#define COLOR1 0x777777 -#define COLOR2 0x555555 -#define COLOR3 0x333333 - -// Monitor for MiBar to find -#define TARGET_MONITOR "HDMI-0" - -// Target font to use (To find available fonts use the xlsfonts command in a terminal) -#define FONT "-bitstream-charter-medium-i-normal--0-0-100-100-p-0-iso8859-1" -//#define FONT "lucidasans-10" -#define FONT_FALLBACK "fixed" - -// Bar configuration -#define BAR_WIDTH -100 -#define BAR_HEIGHT 32 -#define BAR_X 50 -#define BAR_Y 3 - -// If Automatic Updates is enabled the bar will call all lua plugins every set amount of time + when an event is recieved -#define AUTOMATIC_UPDATES_ENABLE 0 -// The amount of time between updates in seconds -#define UPDATE_TIME 30 - -// Underline configuration -#define ENABLE_UNDERLINE 1 -#define UNDERLINE_HEIGHT 3 -#define UNDERLINE_X_OFFSET 0 -#define UNDERLINE_Y_OFFSET 0 - -// Currently padding has not been implemented to the bar as plugins define their own positions -//#define PADDING_TOP 10 -//#define PADDING_RIGHT 0 -//#define PADDING_BOTTOM 0 -//#define PADDING_LEFT 5 - -#endif diff --git a/src/main.cpp b/src/main.cpp index 8a0c0b9..3287b2a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,9 +1,33 @@ +#include + #include "bar.h" -int main(){ - mibar bar; +const char* helpList = "MiBar Help List\n\n-h or --help to show this message again\n-c or --config to change the default config file to a user-defined config file [ -c Monitor2 ]\n"; + +int main(int argc, char** argv){ + std::string userCfgFile = ""; + int option; + + while((option = getopt(argc, argv, ":hc:")) != -1){ + switch(option){ + case 'h': + std::cout << helpList; + return 0; + case 'c': + userCfgFile = optarg; + break; + case ':': + std::cout << "Option needs a value\n"; + break; + case '?': + std::cout << "Unknown option: " << std::string(optarg) << '\n'; + default: + return 0; + } + } + MiBar bar(userCfgFile); bar.EventLoop(); return 0; -} \ No newline at end of file +} diff --git a/src/renderer.cpp b/src/renderer.cpp index 91bd6a3..7c36b16 100644 --- a/src/renderer.cpp +++ b/src/renderer.cpp @@ -6,14 +6,22 @@ #include "renderer.h" -Renderer::Renderer(const xcb_screen_t* s, xcb_connection_t* c, xcb_window_t& w) : m_conn(c), m_window(w){ +Renderer::Renderer(const xcb_screen_t* s, xcb_connection_t* c, xcb_window_t& w, const ConfigParser& cfg) : m_conn(c), m_window(w){ + // Load Background, Foreground, Color1, Color2, Color3 to m_palette + for(int i = 0; i < 5; ++i){ // 5 colors available + m_palette[i] = std::stoul(cfg.GetConfig(i), nullptr, 16); + } + // Font loading m_font = xcb_generate_id(c); - xcb_void_cookie_t fontCookie = xcb_open_font_checked(c, m_font, strlen(FONT), FONT); + const std::string font = cfg.GetConfig(FONT); + const std::string fallbackFont = cfg.GetConfig(FB_FONT); + + xcb_void_cookie_t fontCookie = xcb_open_font_checked(c, m_font, font.size(), font.c_str()); if(!TestCookie(fontCookie, c)){ m_logger.Log(__FILE_NAME__, __LINE__, "Could not open user-defined font. Trying Fallback font", LogLvl::WARNING); - xcb_open_font_checked(c, m_font, strlen(FONT_FALLBACK), FONT_FALLBACK); + xcb_open_font_checked(c, m_font, fallbackFont.size(), fallbackFont.c_str()); } // Initialize graphic contexts @@ -25,8 +33,12 @@ Renderer::Renderer(const xcb_screen_t* s, xcb_connection_t* c, xcb_window_t& w) xcb_create_gc(c, m_clearGC, w, XCB_GC_FOREGROUND, (const uint32_t[]){m_palette[0]}); xcb_create_gc(c, m_underlineGC, w, XCB_GC_FOREGROUND, (const uint32_t[]){m_palette[4]}); - // Get Window Geometry + // Get Geometry m_geometry = xcb_get_geometry_reply(c, xcb_get_geometry(c, w), nullptr); + + // Underline + m_underlineEnabled = (cfg.GetConfig(USE_UNDERL) == "true") ? true : false; + m_underlineHeight = std::stoi(cfg.GetConfig(UNDERL_H)); } Renderer::~Renderer(){ @@ -73,8 +85,8 @@ void Renderer::DrawStr(const std::string& str, ALIGNMENT align, int add_x){ } int x; - int txt_y = BAR_HEIGHT / 2 + text_height / 2; - int under_y = BAR_HEIGHT - UNDERLINE_HEIGHT; + const int txt_y = m_geometry->height / 2 + text_height / 2; + const int under_y = m_geometry->height - m_underlineHeight; switch(align){ case ALIGNMENT::LEFT: @@ -89,7 +101,9 @@ void Renderer::DrawStr(const std::string& str, ALIGNMENT align, int add_x){ } xcb_image_text_8(m_conn, str.length(), m_window, m_drawGC, x, txt_y, str.c_str()); - DrawUnderline((const xcb_rectangle_t){static_cast(x), static_cast(under_y), static_cast(text_width), BAR_HEIGHT}); + + if(!m_underlineEnabled) return; + DrawUnderline((const xcb_rectangle_t){static_cast(x), static_cast(under_y), static_cast(text_width), m_geometry->height}); } // PRIVATE diff --git a/src/renderer.h b/src/renderer.h index 883ec2f..4a4edb3 100644 --- a/src/renderer.h +++ b/src/renderer.h @@ -8,9 +8,9 @@ #include +#include "configParser.h" #include "utils.h" #include "logger.h" -#include "general.config.h" enum class ALIGNMENT{ LEFT = 0, @@ -20,7 +20,7 @@ enum class ALIGNMENT{ class Renderer{ public: - Renderer(const xcb_screen_t* s, xcb_connection_t* c, xcb_window_t& w); + Renderer(const xcb_screen_t* s, xcb_connection_t* c, xcb_window_t& w, const ConfigParser& cfg); ~Renderer(); /** @@ -92,12 +92,15 @@ class Renderer{ xcb_get_geometry_reply_t* m_geometry = nullptr; + bool m_underlineEnabled = true; + int m_underlineHeight = 0; + xcb_char2b_t* BuildChar2b_t(const char* str, size_t length); std::optional> GetStringSize(const std::string& str); void DrawUnderline(const xcb_rectangle_t& rect); - std::array m_palette = {BACKGROUND, FOREGROUND, COLOR1, COLOR2, COLOR3}; + std::array m_palette = {}; xcb_font_t m_font = 0;