From b554094c7e7d47415f91f11cec5976b5d05b9ada Mon Sep 17 00:00:00 2001 From: Alexis Date: Tue, 18 Dec 2018 17:30:54 +0100 Subject: [PATCH] feat: args && class id --- include/ALabel.hpp | 20 +- include/client.hpp | 4 +- include/modules/battery.hpp | 2 +- include/modules/clock.hpp | 2 +- include/modules/cpu.hpp | 2 +- include/modules/custom.hpp | 2 +- include/modules/memory.hpp | 2 +- include/modules/network.hpp | 2 +- include/modules/pulseaudio.hpp | 2 +- include/modules/sni/tray.hpp | 2 +- include/modules/sway/mode.hpp | 2 +- include/modules/sway/window.hpp | 2 +- include/modules/sway/workspaces.hpp | 2 +- include/util/chrono.hpp | 6 +- include/util/clara.hpp | 1264 +++++++++++++++++++++++++++ meson.build | 2 +- src/client.cpp | 71 +- src/factory.cpp | 24 +- src/modules/battery.cpp | 8 +- src/modules/clock.cpp | 5 +- src/modules/cpu.cpp | 5 +- src/modules/custom.cpp | 16 +- src/modules/memory.cpp | 12 +- src/modules/network.cpp | 9 +- src/modules/pulseaudio.cpp | 5 +- src/modules/sni/tray.cpp | 2 +- src/modules/sway/mode.cpp | 13 +- src/modules/sway/window.cpp | 5 +- src/modules/sway/workspaces.cpp | 5 +- 29 files changed, 1405 insertions(+), 93 deletions(-) create mode 100644 include/util/clara.hpp diff --git a/include/ALabel.hpp b/include/ALabel.hpp index 6befe03b..139bef58 100644 --- a/include/ALabel.hpp +++ b/include/ALabel.hpp @@ -6,26 +6,26 @@ namespace waybar { class ALabel : public IModule { - public: - ALabel(const Json::Value&, const std::string format, uint16_t interval = 0); +public: + ALabel(const Json::Value &, const std::string format, uint16_t interval = 0); virtual ~ALabel() = default; virtual auto update() -> void; - virtual std::string getIcon(uint16_t, const std::string& alt = ""); - virtual operator Gtk::Widget&(); + virtual std::string getIcon(uint16_t, const std::string &alt = ""); + virtual operator Gtk::Widget &(); - protected: +protected: Gtk::EventBox event_box_; Gtk::Label label_; - const Json::Value& config_; + const Json::Value &config_; std::string format_; std::mutex mutex_; const std::chrono::seconds interval_; - private: - bool handleToggle(GdkEventButton* const& ev); - bool handleScroll(GdkEventScroll*); +private: + bool handleToggle(GdkEventButton *const &ev); + bool handleScroll(GdkEventScroll *); bool alt = false; const std::string default_format_; }; -} // namespace waybar +} // namespace waybar diff --git a/include/client.hpp b/include/client.hpp index f3ef1351..43947aa8 100644 --- a/include/client.hpp +++ b/include/client.hpp @@ -15,7 +15,7 @@ class Client { Client(int argc, char *argv[]); int main(int argc, char *argv[]); - Glib::RefPtr gtk_app; + Gtk::Main gtk_main; std::string css_file; std::string config_file; Glib::RefPtr gdk_display; @@ -27,8 +27,8 @@ class Client { std::vector> bars; private: + void setupConfigs(const std::string& config, const std::string& style); void bindInterfaces(); - auto setupCss(); const std::string getValidPath(std::vector paths); static void handleGlobal(void *data, struct wl_registry *registry, diff --git a/include/modules/battery.hpp b/include/modules/battery.hpp index c3eadba6..9d6e54a4 100644 --- a/include/modules/battery.hpp +++ b/include/modules/battery.hpp @@ -23,7 +23,7 @@ namespace fs = std::filesystem; class Battery : public ALabel { public: - Battery(const Json::Value&); + Battery(const std::string&, const Json::Value&); ~Battery(); auto update() -> void; private: diff --git a/include/modules/clock.hpp b/include/modules/clock.hpp index fba52cd2..687f3feb 100644 --- a/include/modules/clock.hpp +++ b/include/modules/clock.hpp @@ -9,7 +9,7 @@ namespace waybar::modules { class Clock : public ALabel { public: - Clock(const Json::Value&); + Clock(const std::string&, const Json::Value&); auto update() -> void; private: waybar::util::SleeperThread thread_; diff --git a/include/modules/cpu.hpp b/include/modules/cpu.hpp index a371c323..35c00177 100644 --- a/include/modules/cpu.hpp +++ b/include/modules/cpu.hpp @@ -13,7 +13,7 @@ namespace waybar::modules { class Cpu : public ALabel { public: - Cpu(const Json::Value&); + Cpu(const std::string&, const Json::Value&); auto update() -> void; private: static inline const std::string data_dir_ = "/proc/stat"; diff --git a/include/modules/custom.hpp b/include/modules/custom.hpp index 40ff5f29..937ab76f 100644 --- a/include/modules/custom.hpp +++ b/include/modules/custom.hpp @@ -11,7 +11,7 @@ namespace waybar::modules { class Custom : public ALabel { public: - Custom(const std::string, const Json::Value&); + Custom(const std::string&, const Json::Value&); ~Custom(); auto update() -> void; private: diff --git a/include/modules/memory.hpp b/include/modules/memory.hpp index 0172f019..bb138d03 100644 --- a/include/modules/memory.hpp +++ b/include/modules/memory.hpp @@ -9,7 +9,7 @@ namespace waybar::modules { class Memory : public ALabel { public: - Memory(const Json::Value&); + Memory(const std::string&, const Json::Value&); auto update() -> void; private: static inline const std::string data_dir_ = "/proc/meminfo"; diff --git a/include/modules/network.hpp b/include/modules/network.hpp index c120846d..2f20e180 100644 --- a/include/modules/network.hpp +++ b/include/modules/network.hpp @@ -15,7 +15,7 @@ namespace waybar::modules { class Network : public ALabel { public: - Network(const Json::Value&); + Network(const std::string&, const Json::Value&); ~Network(); auto update() -> void; private: diff --git a/include/modules/pulseaudio.hpp b/include/modules/pulseaudio.hpp index c4100fba..4864533d 100644 --- a/include/modules/pulseaudio.hpp +++ b/include/modules/pulseaudio.hpp @@ -10,7 +10,7 @@ namespace waybar::modules { class Pulseaudio : public ALabel { public: - Pulseaudio(const Json::Value&); + Pulseaudio(const std::string&, const Json::Value&); ~Pulseaudio(); auto update() -> void; private: diff --git a/include/modules/sni/tray.hpp b/include/modules/sni/tray.hpp index 2f79f927..9ee0c9e7 100644 --- a/include/modules/sni/tray.hpp +++ b/include/modules/sni/tray.hpp @@ -12,7 +12,7 @@ namespace waybar::modules::SNI { class Tray : public IModule { public: - Tray(const Json::Value&); + Tray(const std::string&, const Json::Value&); auto update() -> void; operator Gtk::Widget &(); private: diff --git a/include/modules/sway/mode.hpp b/include/modules/sway/mode.hpp index b825f018..51bb086f 100644 --- a/include/modules/sway/mode.hpp +++ b/include/modules/sway/mode.hpp @@ -12,7 +12,7 @@ namespace waybar::modules::sway { class Mode : public ALabel { public: - Mode(const waybar::Bar&, const Json::Value&); + Mode(const std::string&, const waybar::Bar&, const Json::Value&); auto update() -> void; private: void worker(); diff --git a/include/modules/sway/window.hpp b/include/modules/sway/window.hpp index 94c568bb..8cf11b3e 100644 --- a/include/modules/sway/window.hpp +++ b/include/modules/sway/window.hpp @@ -13,7 +13,7 @@ namespace waybar::modules::sway { class Window : public ALabel { public: - Window(const waybar::Bar&, const Json::Value&); + Window(const std::string&, const waybar::Bar&, const Json::Value&); auto update() -> void; private: void worker(); diff --git a/include/modules/sway/workspaces.hpp b/include/modules/sway/workspaces.hpp index a8208774..d4819e52 100644 --- a/include/modules/sway/workspaces.hpp +++ b/include/modules/sway/workspaces.hpp @@ -12,7 +12,7 @@ namespace waybar::modules::sway { class Workspaces : public IModule { public: - Workspaces(const waybar::Bar&, const Json::Value&); + Workspaces(const std::string&, const waybar::Bar&, const Json::Value&); auto update() -> void; operator Gtk::Widget &(); private: diff --git a/include/util/chrono.hpp b/include/util/chrono.hpp index fdca5f47..33bc0ef7 100644 --- a/include/util/chrono.hpp +++ b/include/util/chrono.hpp @@ -63,14 +63,14 @@ struct SleeperThread { { do_run_ = false; condvar_.notify_all(); - if (thread_.joinable()) { - thread_.detach(); - } } ~SleeperThread() { stop(); + if (thread_.joinable()) { + thread_.detach(); + } } private: diff --git a/include/util/clara.hpp b/include/util/clara.hpp new file mode 100644 index 00000000..6be5a98b --- /dev/null +++ b/include/util/clara.hpp @@ -0,0 +1,1264 @@ +// Copyright 2017 Two Blue Cubes Ltd. All rights reserved. +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// See https://github.com/philsquared/Clara for more details + +// Clara v1.1.5 + +#ifndef CLARA_HPP_INCLUDED +#define CLARA_HPP_INCLUDED + +#ifndef CLARA_CONFIG_CONSOLE_WIDTH +#define CLARA_CONFIG_CONSOLE_WIDTH 80 +#endif + +#ifndef CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#define CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CLARA_CONFIG_CONSOLE_WIDTH +#endif + +#ifndef CLARA_CONFIG_OPTIONAL_TYPE +#ifdef __has_include +#if __has_include() && __cplusplus >= 201703L +#include +#define CLARA_CONFIG_OPTIONAL_TYPE std::optional +#endif +#endif +#endif + + +// ----------- #included from clara_textflow.hpp ----------- + +// TextFlowCpp +// +// A single-header library for wrapping and laying out basic text, by Phil Nash +// +// Distributed under the Boost Software License, Version 1.0. (See accompanying +// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// This project is hosted at https://github.com/philsquared/textflowcpp + +#ifndef CLARA_TEXTFLOW_HPP_INCLUDED +#define CLARA_TEXTFLOW_HPP_INCLUDED + +#include +#include +#include +#include + +#ifndef CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH +#define CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 80 +#endif + + +namespace clara { namespace TextFlow { + + inline auto isWhitespace( char c ) -> bool { + static std::string chars = " \t\n\r"; + return chars.find( c ) != std::string::npos; + } + inline auto isBreakableBefore( char c ) -> bool { + static std::string chars = "[({<|"; + return chars.find( c ) != std::string::npos; + } + inline auto isBreakableAfter( char c ) -> bool { + static std::string chars = "])}>.,:;*+-=&/\\"; + return chars.find( c ) != std::string::npos; + } + + class Columns; + + class Column { + std::vector m_strings; + size_t m_width = CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH; + size_t m_indent = 0; + size_t m_initialIndent = std::string::npos; + + public: + class iterator { + friend Column; + + Column const& m_column; + size_t m_stringIndex = 0; + size_t m_pos = 0; + + size_t m_len = 0; + size_t m_end = 0; + bool m_suffix = false; + + iterator( Column const& column, size_t stringIndex ) + : m_column( column ), + m_stringIndex( stringIndex ) + {} + + auto line() const -> std::string const& { return m_column.m_strings[m_stringIndex]; } + + auto isBoundary( size_t at ) const -> bool { + assert( at > 0 ); + assert( at <= line().size() ); + + return at == line().size() || + ( isWhitespace( line()[at] ) && !isWhitespace( line()[at-1] ) ) || + isBreakableBefore( line()[at] ) || + isBreakableAfter( line()[at-1] ); + } + + void calcLength() { + assert( m_stringIndex < m_column.m_strings.size() ); + + m_suffix = false; + auto width = m_column.m_width-indent(); + m_end = m_pos; + while( m_end < line().size() && line()[m_end] != '\n' ) + ++m_end; + + if( m_end < m_pos + width ) { + m_len = m_end - m_pos; + } + else { + size_t len = width; + while (len > 0 && !isBoundary(m_pos + len)) + --len; + while (len > 0 && isWhitespace( line()[m_pos + len - 1] )) + --len; + + if (len > 0) { + m_len = len; + } else { + m_suffix = true; + m_len = width - 1; + } + } + } + + auto indent() const -> size_t { + auto initial = m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos; + return initial == std::string::npos ? m_column.m_indent : initial; + } + + auto addIndentAndSuffix(std::string const &plain) const -> std::string { + return std::string( indent(), ' ' ) + (m_suffix ? plain + "-" : plain); + } + + public: + using difference_type = std::ptrdiff_t; + using value_type = std::string; + using pointer = value_type*; + using reference = value_type&; + using iterator_category = std::forward_iterator_tag; + + explicit iterator( Column const& column ) : m_column( column ) { + assert( m_column.m_width > m_column.m_indent ); + assert( m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent ); + calcLength(); + if( m_len == 0 ) + m_stringIndex++; // Empty string + } + + auto operator *() const -> std::string { + assert( m_stringIndex < m_column.m_strings.size() ); + assert( m_pos <= m_end ); + return addIndentAndSuffix(line().substr(m_pos, m_len)); + } + + auto operator ++() -> iterator& { + m_pos += m_len; + if( m_pos < line().size() && line()[m_pos] == '\n' ) + m_pos += 1; + else + while( m_pos < line().size() && isWhitespace( line()[m_pos] ) ) + ++m_pos; + + if( m_pos == line().size() ) { + m_pos = 0; + ++m_stringIndex; + } + if( m_stringIndex < m_column.m_strings.size() ) + calcLength(); + return *this; + } + auto operator ++(int) -> iterator { + iterator prev( *this ); + operator++(); + return prev; + } + + auto operator ==( iterator const& other ) const -> bool { + return + m_pos == other.m_pos && + m_stringIndex == other.m_stringIndex && + &m_column == &other.m_column; + } + auto operator !=( iterator const& other ) const -> bool { + return !operator==( other ); + } + }; + using const_iterator = iterator; + + explicit Column( std::string const& text ) { m_strings.push_back( text ); } + + auto width( size_t newWidth ) -> Column& { + assert( newWidth > 0 ); + m_width = newWidth; + return *this; + } + auto indent( size_t newIndent ) -> Column& { + m_indent = newIndent; + return *this; + } + auto initialIndent( size_t newIndent ) -> Column& { + m_initialIndent = newIndent; + return *this; + } + + auto width() const -> size_t { return m_width; } + auto begin() const -> iterator { return iterator( *this ); } + auto end() const -> iterator { return { *this, m_strings.size() }; } + + inline friend std::ostream& operator << ( std::ostream& os, Column const& col ) { + bool first = true; + for( auto line : col ) { + if( first ) + first = false; + else + os << "\n"; + os << line; + } + return os; + } + + auto operator + ( Column const& other ) -> Columns; + + auto toString() const -> std::string { + std::ostringstream oss; + oss << *this; + return oss.str(); + } + }; + + class Spacer : public Column { + + public: + explicit Spacer( size_t spaceWidth ) : Column( "" ) { + width( spaceWidth ); + } + }; + + class Columns { + std::vector m_columns; + + public: + + class iterator { + friend Columns; + struct EndTag {}; + + std::vector const& m_columns; + std::vector m_iterators; + size_t m_activeIterators; + + iterator( Columns const& columns, EndTag ) + : m_columns( columns.m_columns ), + m_activeIterators( 0 ) + { + m_iterators.reserve( m_columns.size() ); + + for( auto const& col : m_columns ) + m_iterators.push_back( col.end() ); + } + + public: + using difference_type = std::ptrdiff_t; + using value_type = std::string; + using pointer = value_type*; + using reference = value_type&; + using iterator_category = std::forward_iterator_tag; + + explicit iterator( Columns const& columns ) + : m_columns( columns.m_columns ), + m_activeIterators( m_columns.size() ) + { + m_iterators.reserve( m_columns.size() ); + + for( auto const& col : m_columns ) + m_iterators.push_back( col.begin() ); + } + + auto operator ==( iterator const& other ) const -> bool { + return m_iterators == other.m_iterators; + } + auto operator !=( iterator const& other ) const -> bool { + return m_iterators != other.m_iterators; + } + auto operator *() const -> std::string { + std::string row, padding; + + for( size_t i = 0; i < m_columns.size(); ++i ) { + auto width = m_columns[i].width(); + if( m_iterators[i] != m_columns[i].end() ) { + std::string col = *m_iterators[i]; + row += padding + col; + if( col.size() < width ) + padding = std::string( width - col.size(), ' ' ); + else + padding = ""; + } + else { + padding += std::string( width, ' ' ); + } + } + return row; + } + auto operator ++() -> iterator& { + for( size_t i = 0; i < m_columns.size(); ++i ) { + if (m_iterators[i] != m_columns[i].end()) + ++m_iterators[i]; + } + return *this; + } + auto operator ++(int) -> iterator { + iterator prev( *this ); + operator++(); + return prev; + } + }; + using const_iterator = iterator; + + auto begin() const -> iterator { return iterator( *this ); } + auto end() const -> iterator { return { *this, iterator::EndTag() }; } + + auto operator += ( Column const& col ) -> Columns& { + m_columns.push_back( col ); + return *this; + } + auto operator + ( Column const& col ) -> Columns { + Columns combined = *this; + combined += col; + return combined; + } + + inline friend std::ostream& operator << ( std::ostream& os, Columns const& cols ) { + + bool first = true; + for( auto line : cols ) { + if( first ) + first = false; + else + os << "\n"; + os << line; + } + return os; + } + + auto toString() const -> std::string { + std::ostringstream oss; + oss << *this; + return oss.str(); + } + }; + + inline auto Column::operator + ( Column const& other ) -> Columns { + Columns cols; + cols += *this; + cols += other; + return cols; + } +}} + +#endif // CLARA_TEXTFLOW_HPP_INCLUDED + +// ----------- end of #include from clara_textflow.hpp ----------- +// ........... back in clara.hpp + + +#include +#include +#include + +#if !defined(CLARA_PLATFORM_WINDOWS) && ( defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) ) +#define CLARA_PLATFORM_WINDOWS +#endif + +namespace clara { +namespace detail { + + // Traits for extracting arg and return type of lambdas (for single argument lambdas) + template + struct UnaryLambdaTraits : UnaryLambdaTraits {}; + + template + struct UnaryLambdaTraits { + static const bool isValid = false; + }; + + template + struct UnaryLambdaTraits { + static const bool isValid = true; + using ArgType = typename std::remove_const::type>::type; + using ReturnType = ReturnT; + }; + + class TokenStream; + + // Transport for raw args (copied from main args, or supplied via init list for testing) + class Args { + friend TokenStream; + std::string m_exeName; + std::vector m_args; + + public: + Args( int argc, char const* const* argv ) + : m_exeName(argv[0]), + m_args(argv + 1, argv + argc) {} + + Args( std::initializer_list args ) + : m_exeName( *args.begin() ), + m_args( args.begin()+1, args.end() ) + {} + + auto exeName() const -> std::string { + return m_exeName; + } + }; + + // Wraps a token coming from a token stream. These may not directly correspond to strings as a single string + // may encode an option + its argument if the : or = form is used + enum class TokenType { + Option, Argument + }; + struct Token { + TokenType type; + std::string token; + }; + + inline auto isOptPrefix( char c ) -> bool { + return c == '-' +#ifdef CLARA_PLATFORM_WINDOWS + || c == '/' +#endif + ; + } + + // Abstracts iterators into args as a stream of tokens, with option arguments uniformly handled + class TokenStream { + using Iterator = std::vector::const_iterator; + Iterator it; + Iterator itEnd; + std::vector m_tokenBuffer; + + void loadBuffer() { + m_tokenBuffer.resize( 0 ); + + // Skip any empty strings + while( it != itEnd && it->empty() ) + ++it; + + if( it != itEnd ) { + auto const &next = *it; + if( isOptPrefix( next[0] ) ) { + auto delimiterPos = next.find_first_of( " :=" ); + if( delimiterPos != std::string::npos ) { + m_tokenBuffer.push_back( { TokenType::Option, next.substr( 0, delimiterPos ) } ); + m_tokenBuffer.push_back( { TokenType::Argument, next.substr( delimiterPos + 1 ) } ); + } else { + if( next[1] != '-' && next.size() > 2 ) { + std::string opt = "- "; + for( size_t i = 1; i < next.size(); ++i ) { + opt[1] = next[i]; + m_tokenBuffer.push_back( { TokenType::Option, opt } ); + } + } else { + m_tokenBuffer.push_back( { TokenType::Option, next } ); + } + } + } else { + m_tokenBuffer.push_back( { TokenType::Argument, next } ); + } + } + } + + public: + explicit TokenStream( Args const &args ) : TokenStream( args.m_args.begin(), args.m_args.end() ) {} + + TokenStream( Iterator it, Iterator itEnd ) : it( it ), itEnd( itEnd ) { + loadBuffer(); + } + + explicit operator bool() const { + return !m_tokenBuffer.empty() || it != itEnd; + } + + auto count() const -> size_t { return m_tokenBuffer.size() + (itEnd - it); } + + auto operator*() const -> Token { + assert( !m_tokenBuffer.empty() ); + return m_tokenBuffer.front(); + } + + auto operator->() const -> Token const * { + assert( !m_tokenBuffer.empty() ); + return &m_tokenBuffer.front(); + } + + auto operator++() -> TokenStream & { + if( m_tokenBuffer.size() >= 2 ) { + m_tokenBuffer.erase( m_tokenBuffer.begin() ); + } else { + if( it != itEnd ) + ++it; + loadBuffer(); + } + return *this; + } + }; + + + class ResultBase { + public: + enum Type { + Ok, LogicError, RuntimeError + }; + + protected: + ResultBase( Type type ) : m_type( type ) {} + virtual ~ResultBase() = default; + + virtual void enforceOk() const = 0; + + Type m_type; + }; + + template + class ResultValueBase : public ResultBase { + public: + auto value() const -> T const & { + enforceOk(); + return m_value; + } + + protected: + ResultValueBase( Type type ) : ResultBase( type ) {} + + ResultValueBase( ResultValueBase const &other ) : ResultBase( other ) { + if( m_type == ResultBase::Ok ) + new( &m_value ) T( other.m_value ); + } + + ResultValueBase( Type, T const &value ) : ResultBase( Ok ) { + new( &m_value ) T( value ); + } + + auto operator=( ResultValueBase const &other ) -> ResultValueBase & { + if( m_type == ResultBase::Ok ) + m_value.~T(); + ResultBase::operator=(other); + if( m_type == ResultBase::Ok ) + new( &m_value ) T( other.m_value ); + return *this; + } + + ~ResultValueBase() override { + if( m_type == Ok ) + m_value.~T(); + } + + union { + T m_value; + }; + }; + + template<> + class ResultValueBase : public ResultBase { + protected: + using ResultBase::ResultBase; + }; + + template + class BasicResult : public ResultValueBase { + public: + template + explicit BasicResult( BasicResult const &other ) + : ResultValueBase( other.type() ), + m_errorMessage( other.errorMessage() ) + { + assert( type() != ResultBase::Ok ); + } + + template + static auto ok( U const &value ) -> BasicResult { return { ResultBase::Ok, value }; } + static auto ok() -> BasicResult { return { ResultBase::Ok }; } + static auto logicError( std::string const &message ) -> BasicResult { return { ResultBase::LogicError, message }; } + static auto runtimeError( std::string const &message ) -> BasicResult { return { ResultBase::RuntimeError, message }; } + + explicit operator bool() const { return m_type == ResultBase::Ok; } + auto type() const -> ResultBase::Type { return m_type; } + auto errorMessage() const -> std::string { return m_errorMessage; } + + protected: + void enforceOk() const override { + + // Errors shouldn't reach this point, but if they do + // the actual error message will be in m_errorMessage + assert( m_type != ResultBase::LogicError ); + assert( m_type != ResultBase::RuntimeError ); + if( m_type != ResultBase::Ok ) + std::abort(); + } + + std::string m_errorMessage; // Only populated if resultType is an error + + BasicResult( ResultBase::Type type, std::string const &message ) + : ResultValueBase(type), + m_errorMessage(message) + { + assert( m_type != ResultBase::Ok ); + } + + using ResultValueBase::ResultValueBase; + using ResultBase::m_type; + }; + + enum class ParseResultType { + Matched, NoMatch, ShortCircuitAll, ShortCircuitSame + }; + + class ParseState { + public: + + ParseState( ParseResultType type, TokenStream const &remainingTokens ) + : m_type(type), + m_remainingTokens( remainingTokens ) + {} + + auto type() const -> ParseResultType { return m_type; } + auto remainingTokens() const -> TokenStream { return m_remainingTokens; } + + private: + ParseResultType m_type; + TokenStream m_remainingTokens; + }; + + using Result = BasicResult; + using ParserResult = BasicResult; + using InternalParseResult = BasicResult; + + struct HelpColumns { + std::string left; + std::string right; + }; + + template + inline auto convertInto( std::string const &source, T& target ) -> ParserResult { + std::stringstream ss; + ss << source; + ss >> target; + if( ss.fail() ) + return ParserResult::runtimeError( "Unable to convert '" + source + "' to destination type" ); + else + return ParserResult::ok( ParseResultType::Matched ); + } + inline auto convertInto( std::string const &source, std::string& target ) -> ParserResult { + target = source; + return ParserResult::ok( ParseResultType::Matched ); + } + inline auto convertInto( std::string const &source, bool &target ) -> ParserResult { + std::string srcLC = source; + std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( char c ) { return static_cast( ::tolower(c) ); } ); + if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on") + target = true; + else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off") + target = false; + else + return ParserResult::runtimeError( "Expected a boolean value but did not recognise: '" + source + "'" ); + return ParserResult::ok( ParseResultType::Matched ); + } +#ifdef CLARA_CONFIG_OPTIONAL_TYPE + template + inline auto convertInto( std::string const &source, CLARA_CONFIG_OPTIONAL_TYPE& target ) -> ParserResult { + T temp; + auto result = convertInto( source, temp ); + if( result ) + target = std::move(temp); + return result; + } +#endif // CLARA_CONFIG_OPTIONAL_TYPE + + struct NonCopyable { + NonCopyable() = default; + NonCopyable( NonCopyable const & ) = delete; + NonCopyable( NonCopyable && ) = delete; + NonCopyable &operator=( NonCopyable const & ) = delete; + NonCopyable &operator=( NonCopyable && ) = delete; + }; + + struct BoundRef : NonCopyable { + virtual ~BoundRef() = default; + virtual auto isContainer() const -> bool { return false; } + virtual auto isFlag() const -> bool { return false; } + }; + struct BoundValueRefBase : BoundRef { + virtual auto setValue( std::string const &arg ) -> ParserResult = 0; + }; + struct BoundFlagRefBase : BoundRef { + virtual auto setFlag( bool flag ) -> ParserResult = 0; + virtual auto isFlag() const -> bool { return true; } + }; + + template + struct BoundValueRef : BoundValueRefBase { + T &m_ref; + + explicit BoundValueRef( T &ref ) : m_ref( ref ) {} + + auto setValue( std::string const &arg ) -> ParserResult override { + return convertInto( arg, m_ref ); + } + }; + + template + struct BoundValueRef> : BoundValueRefBase { + std::vector &m_ref; + + explicit BoundValueRef( std::vector &ref ) : m_ref( ref ) {} + + auto isContainer() const -> bool override { return true; } + + auto setValue( std::string const &arg ) -> ParserResult override { + T temp; + auto result = convertInto( arg, temp ); + if( result ) + m_ref.push_back( temp ); + return result; + } + }; + + struct BoundFlagRef : BoundFlagRefBase { + bool &m_ref; + + explicit BoundFlagRef( bool &ref ) : m_ref( ref ) {} + + auto setFlag( bool flag ) -> ParserResult override { + m_ref = flag; + return ParserResult::ok( ParseResultType::Matched ); + } + }; + + template + struct LambdaInvoker { + static_assert( std::is_same::value, "Lambda must return void or clara::ParserResult" ); + + template + static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { + return lambda( arg ); + } + }; + + template<> + struct LambdaInvoker { + template + static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult { + lambda( arg ); + return ParserResult::ok( ParseResultType::Matched ); + } + }; + + template + inline auto invokeLambda( L const &lambda, std::string const &arg ) -> ParserResult { + ArgType temp{}; + auto result = convertInto( arg, temp ); + return !result + ? result + : LambdaInvoker::ReturnType>::invoke( lambda, temp ); + } + + + template + struct BoundLambda : BoundValueRefBase { + L m_lambda; + + static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); + explicit BoundLambda( L const &lambda ) : m_lambda( lambda ) {} + + auto setValue( std::string const &arg ) -> ParserResult override { + return invokeLambda::ArgType>( m_lambda, arg ); + } + }; + + template + struct BoundFlagLambda : BoundFlagRefBase { + L m_lambda; + + static_assert( UnaryLambdaTraits::isValid, "Supplied lambda must take exactly one argument" ); + static_assert( std::is_same::ArgType, bool>::value, "flags must be boolean" ); + + explicit BoundFlagLambda( L const &lambda ) : m_lambda( lambda ) {} + + auto setFlag( bool flag ) -> ParserResult override { + return LambdaInvoker::ReturnType>::invoke( m_lambda, flag ); + } + }; + + enum class Optionality { Optional, Required }; + + struct Parser; + + class ParserBase { + public: + virtual ~ParserBase() = default; + virtual auto validate() const -> Result { return Result::ok(); } + virtual auto parse( std::string const& exeName, TokenStream const &tokens) const -> InternalParseResult = 0; + virtual auto cardinality() const -> size_t { return 1; } + + auto parse( Args const &args ) const -> InternalParseResult { + return parse( args.exeName(), TokenStream( args ) ); + } + }; + + template + class ComposableParserImpl : public ParserBase { + public: + template + auto operator|( T const &other ) const -> Parser; + + template + auto operator+( T const &other ) const -> Parser; + }; + + // Common code and state for Args and Opts + template + class ParserRefImpl : public ComposableParserImpl { + protected: + Optionality m_optionality = Optionality::Optional; + std::shared_ptr m_ref; + std::string m_hint; + std::string m_description; + + explicit ParserRefImpl( std::shared_ptr const &ref ) : m_ref( ref ) {} + + public: + template + ParserRefImpl( T &ref, std::string const &hint ) + : m_ref( std::make_shared>( ref ) ), + m_hint( hint ) + {} + + template + ParserRefImpl( LambdaT const &ref, std::string const &hint ) + : m_ref( std::make_shared>( ref ) ), + m_hint(hint) + {} + + auto operator()( std::string const &description ) -> DerivedT & { + m_description = description; + return static_cast( *this ); + } + + auto optional() -> DerivedT & { + m_optionality = Optionality::Optional; + return static_cast( *this ); + }; + + auto required() -> DerivedT & { + m_optionality = Optionality::Required; + return static_cast( *this ); + }; + + auto isOptional() const -> bool { + return m_optionality == Optionality::Optional; + } + + auto cardinality() const -> size_t override { + if( m_ref->isContainer() ) + return 0; + else + return 1; + } + + auto hint() const -> std::string { return m_hint; } + }; + + class ExeName : public ComposableParserImpl { + std::shared_ptr m_name; + std::shared_ptr m_ref; + + template + static auto makeRef(LambdaT const &lambda) -> std::shared_ptr { + return std::make_shared>( lambda) ; + } + + public: + ExeName() : m_name( std::make_shared( "" ) ) {} + + explicit ExeName( std::string &ref ) : ExeName() { + m_ref = std::make_shared>( ref ); + } + + template + explicit ExeName( LambdaT const& lambda ) : ExeName() { + m_ref = std::make_shared>( lambda ); + } + + // The exe name is not parsed out of the normal tokens, but is handled specially + auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { + return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); + } + + auto name() const -> std::string { return *m_name; } + auto set( std::string const& newName ) -> ParserResult { + + auto lastSlash = newName.find_last_of( "\\/" ); + auto filename = ( lastSlash == std::string::npos ) + ? newName + : newName.substr( lastSlash+1 ); + + *m_name = filename; + if( m_ref ) + return m_ref->setValue( filename ); + else + return ParserResult::ok( ParseResultType::Matched ); + } + }; + + class Arg : public ParserRefImpl { + public: + using ParserRefImpl::ParserRefImpl; + + auto parse( std::string const &, TokenStream const &tokens ) const -> InternalParseResult override { + auto validationResult = validate(); + if( !validationResult ) + return InternalParseResult( validationResult ); + + auto remainingTokens = tokens; + auto const &token = *remainingTokens; + if( token.type != TokenType::Argument ) + return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); + + assert( !m_ref->isFlag() ); + auto valueRef = static_cast( m_ref.get() ); + + auto result = valueRef->setValue( remainingTokens->token ); + if( !result ) + return InternalParseResult( result ); + else + return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); + } + }; + + inline auto normaliseOpt( std::string const &optName ) -> std::string { +#ifdef CLARA_PLATFORM_WINDOWS + if( optName[0] == '/' ) + return "-" + optName.substr( 1 ); + else +#endif + return optName; + } + + class Opt : public ParserRefImpl { + protected: + std::vector m_optNames; + + public: + template + explicit Opt( LambdaT const &ref ) : ParserRefImpl( std::make_shared>( ref ) ) {} + + explicit Opt( bool &ref ) : ParserRefImpl( std::make_shared( ref ) ) {} + + template + Opt( LambdaT const &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} + + template + Opt( T &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {} + + auto operator[]( std::string const &optName ) -> Opt & { + m_optNames.push_back( optName ); + return *this; + } + + auto getHelpColumns() const -> std::vector { + std::ostringstream oss; + bool first = true; + for( auto const &opt : m_optNames ) { + if (first) + first = false; + else + oss << ", "; + oss << opt; + } + if( !m_hint.empty() ) + oss << " <" << m_hint << ">"; + return { { oss.str(), m_description } }; + } + + auto isMatch( std::string const &optToken ) const -> bool { + auto normalisedToken = normaliseOpt( optToken ); + for( auto const &name : m_optNames ) { + if( normaliseOpt( name ) == normalisedToken ) + return true; + } + return false; + } + + using ParserBase::parse; + + auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override { + auto validationResult = validate(); + if( !validationResult ) + return InternalParseResult( validationResult ); + + auto remainingTokens = tokens; + if( remainingTokens && remainingTokens->type == TokenType::Option ) { + auto const &token = *remainingTokens; + if( isMatch(token.token ) ) { + if( m_ref->isFlag() ) { + auto flagRef = static_cast( m_ref.get() ); + auto result = flagRef->setFlag( true ); + if( !result ) + return InternalParseResult( result ); + if( result.value() == ParseResultType::ShortCircuitAll ) + return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); + } else { + auto valueRef = static_cast( m_ref.get() ); + ++remainingTokens; + if( !remainingTokens ) + return InternalParseResult::runtimeError( "Expected argument following " + token.token ); + auto const &argToken = *remainingTokens; + if( argToken.type != TokenType::Argument ) + return InternalParseResult::runtimeError( "Expected argument following " + token.token ); + auto result = valueRef->setValue( argToken.token ); + if( !result ) + return InternalParseResult( result ); + if( result.value() == ParseResultType::ShortCircuitAll ) + return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) ); + } + return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) ); + } + } + return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) ); + } + + auto validate() const -> Result override { + if( m_optNames.empty() ) + return Result::logicError( "No options supplied to Opt" ); + for( auto const &name : m_optNames ) { + if( name.empty() ) + return Result::logicError( "Option name cannot be empty" ); +#ifdef CLARA_PLATFORM_WINDOWS + if( name[0] != '-' && name[0] != '/' ) + return Result::logicError( "Option name must begin with '-' or '/'" ); +#else + if( name[0] != '-' ) + return Result::logicError( "Option name must begin with '-'" ); +#endif + } + return ParserRefImpl::validate(); + } + }; + + struct Help : Opt { + Help( bool &showHelpFlag ) + : Opt([&]( bool flag ) { + showHelpFlag = flag; + return ParserResult::ok( ParseResultType::ShortCircuitAll ); + }) + { + static_cast( *this ) + ("display usage information") + ["-?"]["-h"]["--help"] + .optional(); + } + }; + + + struct Parser : ParserBase { + + mutable ExeName m_exeName; + std::vector m_options; + std::vector m_args; + + auto operator|=( ExeName const &exeName ) -> Parser & { + m_exeName = exeName; + return *this; + } + + auto operator|=( Arg const &arg ) -> Parser & { + m_args.push_back(arg); + return *this; + } + + auto operator|=( Opt const &opt ) -> Parser & { + m_options.push_back(opt); + return *this; + } + + auto operator|=( Parser const &other ) -> Parser & { + m_options.insert(m_options.end(), other.m_options.begin(), other.m_options.end()); + m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end()); + return *this; + } + + template + auto operator|( T const &other ) const -> Parser { + return Parser( *this ) |= other; + } + + // Forward deprecated interface with '+' instead of '|' + template + auto operator+=( T const &other ) -> Parser & { return operator|=( other ); } + template + auto operator+( T const &other ) const -> Parser { return operator|( other ); } + + auto getHelpColumns() const -> std::vector { + std::vector cols; + for (auto const &o : m_options) { + auto childCols = o.getHelpColumns(); + cols.insert( cols.end(), childCols.begin(), childCols.end() ); + } + return cols; + } + + void writeToStream( std::ostream &os ) const { + if (!m_exeName.name().empty()) { + os << "usage:\n" << " " << m_exeName.name() << " "; + bool required = true, first = true; + for( auto const &arg : m_args ) { + if (first) + first = false; + else + os << " "; + if( arg.isOptional() && required ) { + os << "["; + required = false; + } + os << "<" << arg.hint() << ">"; + if( arg.cardinality() == 0 ) + os << " ... "; + } + if( !required ) + os << "]"; + if( !m_options.empty() ) + os << " options"; + os << "\n\nwhere options are:" << std::endl; + } + + auto rows = getHelpColumns(); + size_t consoleWidth = CLARA_CONFIG_CONSOLE_WIDTH; + size_t optWidth = 0; + for( auto const &cols : rows ) + optWidth = (std::max)(optWidth, cols.left.size() + 2); + + optWidth = (std::min)(optWidth, consoleWidth/2); + + for( auto const &cols : rows ) { + auto row = + TextFlow::Column( cols.left ).width( optWidth ).indent( 2 ) + + TextFlow::Spacer(4) + + TextFlow::Column( cols.right ).width( consoleWidth - 7 - optWidth ); + os << row << std::endl; + } + } + + friend auto operator<<( std::ostream &os, Parser const &parser ) -> std::ostream& { + parser.writeToStream( os ); + return os; + } + + auto validate() const -> Result override { + for( auto const &opt : m_options ) { + auto result = opt.validate(); + if( !result ) + return result; + } + for( auto const &arg : m_args ) { + auto result = arg.validate(); + if( !result ) + return result; + } + return Result::ok(); + } + + using ParserBase::parse; + + auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override { + + struct ParserInfo { + ParserBase const* parser = nullptr; + size_t count = 0; + }; + const size_t totalParsers = m_options.size() + m_args.size(); + assert( totalParsers < 512 ); + // ParserInfo parseInfos[totalParsers]; // <-- this is what we really want to do + ParserInfo parseInfos[512]; + + { + size_t i = 0; + for (auto const &opt : m_options) parseInfos[i++].parser = &opt; + for (auto const &arg : m_args) parseInfos[i++].parser = &arg; + } + + m_exeName.set( exeName ); + + auto result = InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) ); + while( result.value().remainingTokens() ) { + bool tokenParsed = false; + + for( size_t i = 0; i < totalParsers; ++i ) { + auto& parseInfo = parseInfos[i]; + if( parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality() ) { + result = parseInfo.parser->parse(exeName, result.value().remainingTokens()); + if (!result) + return result; + if (result.value().type() != ParseResultType::NoMatch) { + tokenParsed = true; + ++parseInfo.count; + break; + } + } + } + + if( result.value().type() == ParseResultType::ShortCircuitAll ) + return result; + if( !tokenParsed ) + return InternalParseResult::runtimeError( "Unrecognised token: " + result.value().remainingTokens()->token ); + } + // !TBD Check missing required options + return result; + } + }; + + template + template + auto ComposableParserImpl::operator|( T const &other ) const -> Parser { + return Parser() | static_cast( *this ) | other; + } +} // namespace detail + + +// A Combined parser +using detail::Parser; + +// A parser for options +using detail::Opt; + +// A parser for arguments +using detail::Arg; + +// Wrapper for argc, argv from main() +using detail::Args; + +// Specifies the name of the executable +using detail::ExeName; + +// Convenience wrapper for option parser that specifies the help option +using detail::Help; + +// enum of result types from a parse +using detail::ParseResultType; + +// Result type for parser operation +using detail::ParserResult; + + +} // namespace clara + +#endif // CLARA_HPP_INCLUDED diff --git a/meson.build b/meson.build index 4dc9db6a..ce520079 100644 --- a/meson.build +++ b/meson.build @@ -9,7 +9,7 @@ project( ], ) -cpp_args = [] +cpp_args = ['-DVERSION="@0@"'.format(meson.project_version())] cpp_link_args = [] if false # libc++ diff --git a/src/client.cpp b/src/client.cpp index 0b1e67d0..c3989e6e 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -1,8 +1,9 @@ #include "client.hpp" +#include "util/clara.hpp" #include waybar::Client::Client(int argc, char* argv[]) - : gtk_app(Gtk::Application::create(argc, argv, "fr.arouillard.waybar")), + : gtk_main(argc, argv), gdk_display(Gdk::Display::get_default()) { if (!gdk_display) { @@ -12,25 +13,6 @@ waybar::Client::Client(int argc, char* argv[]) throw std::runtime_error("Bar need to run under Wayland"); } wl_display = gdk_wayland_display_get_wl_display(gdk_display->gobj()); - - config_file = getValidPath({ - "$XDG_CONFIG_HOME/waybar/config", - "$HOME/.config/waybar/config", - "$HOME/waybar/config", - "/etc/xdg/waybar/config", - "./resources/config", - }); - css_file = getValidPath({ - "$XDG_CONFIG_HOME/waybar/style.css", - "$HOME/.config/waybar/style.css", - "$HOME/waybar/style.css", - "/etc/xdg/waybar/style.css", - "./resources/style.css", - }); - if (css_file.empty() || config_file.empty()) { - throw std::runtime_error("Missing required resources files"); - } - std::cout << "Resources files: " + config_file + ", " + css_file << std::endl; } const std::string waybar::Client::getValidPath(std::vector paths) @@ -88,6 +70,28 @@ void waybar::Client::handleGlobalRemove(void* data, } } +void waybar::Client::setupConfigs(const std::string& config, const std::string& style) +{ + config_file = config.empty() ? getValidPath({ + "$XDG_CONFIG_HOME/waybar/config", + "$HOME/.config/waybar/config", + "$HOME/waybar/config", + "/etc/xdg/waybar/config", + "./resources/config", + }) : config; + css_file = style.empty() ? getValidPath({ + "$XDG_CONFIG_HOME/waybar/style.css", + "$HOME/.config/waybar/style.css", + "$HOME/waybar/style.css", + "/etc/xdg/waybar/style.css", + "./resources/style.css", + }) : style; + if (css_file.empty() || config_file.empty()) { + throw std::runtime_error("Missing required resources files"); + } + std::cout << "Resources files: " + config_file + ", " + css_file << std::endl; +} + void waybar::Client::bindInterfaces() { registry = wl_display_get_registry(wl_display); @@ -103,11 +107,32 @@ void waybar::Client::bindInterfaces() wl_display_roundtrip(wl_display); } -int waybar::Client::main(int /*argc*/, char* /*argv*/[]) +int waybar::Client::main(int argc, char* argv[]) { + bool show_help = false; + bool show_version = false; + std::string config; + std::string style; + auto cli = clara::detail::Help(show_help) + | clara::detail::Opt(show_version)["-v"]["--version"]("Show version") + | clara::detail::Opt(config, "config")["-c"]["--config"]("Config path") + | clara::detail::Opt(style, "style")["-s"]["--style"]("Style path"); + auto res = cli.parse(clara::detail::Args(argc, argv)); + if (!res) { + std::cerr << "Error in command line: " << res.errorMessage() << std::endl; + return 1; + } + if (show_help) { + std::cout << cli << std::endl; + return 0; + } + if (show_version) { + std::cout << "Waybar v" << VERSION << std::endl; + return 0; + } + setupConfigs(config, style); bindInterfaces(); - gtk_app->hold(); - gtk_app->run(); + gtk_main.run(); bars.clear(); zxdg_output_manager_v1_destroy(xdg_output_manager); zwlr_layer_shell_v1_destroy(layer_shell); diff --git a/src/factory.cpp b/src/factory.cpp index a72d938c..a9fdf0c5 100644 --- a/src/factory.cpp +++ b/src/factory.cpp @@ -7,43 +7,45 @@ waybar::Factory::Factory(const Bar& bar, const Json::Value& config) waybar::IModule* waybar::Factory::makeModule(const std::string &name) const { try { - auto ref = name.substr(0, name.find("#")); + auto hash_pos = name.find("#"); + auto ref = name.substr(0, hash_pos); + auto id = hash_pos != std::string::npos ? name.substr(hash_pos + 1) : ""; if (ref == "battery") { - return new waybar::modules::Battery(config_[name]); + return new waybar::modules::Battery(id, config_[name]); } #ifdef HAVE_SWAY if (ref == "sway/mode") { - return new waybar::modules::sway::Mode(bar_, config_[name]); + return new waybar::modules::sway::Mode(id, bar_, config_[name]); } if (ref == "sway/workspaces") { - return new waybar::modules::sway::Workspaces(bar_, config_[name]); + return new waybar::modules::sway::Workspaces(id, bar_, config_[name]); } if (ref == "sway/window") { - return new waybar::modules::sway::Window(bar_, config_[name]); + return new waybar::modules::sway::Window(id, bar_, config_[name]); } #endif if (ref == "memory") { - return new waybar::modules::Memory(config_[name]); + return new waybar::modules::Memory(id, config_[name]); } if (ref == "cpu") { - return new waybar::modules::Cpu(config_[name]); + return new waybar::modules::Cpu(id, config_[name]); } if (ref == "clock") { - return new waybar::modules::Clock(config_[name]); + return new waybar::modules::Clock(id, config_[name]); } #ifdef HAVE_DBUSMENU if (ref == "tray") { - return new waybar::modules::SNI::Tray(config_[name]); + return new waybar::modules::SNI::Tray(id, config_[name]); } #endif #ifdef HAVE_LIBNL if (ref == "network") { - return new waybar::modules::Network(config_[name]); + return new waybar::modules::Network(id, config_[name]); } #endif #ifdef HAVE_LIBPULSE if (ref == "pulseaudio") { - return new waybar::modules::Pulseaudio(config_[name]); + return new waybar::modules::Pulseaudio(id, config_[name]); } #endif if (ref.compare(0, 7, "custom/") == 0 && ref.size() > 7) { diff --git a/src/modules/battery.cpp b/src/modules/battery.cpp index 9fae5ed6..d4d131ab 100644 --- a/src/modules/battery.cpp +++ b/src/modules/battery.cpp @@ -1,8 +1,12 @@ #include "modules/battery.hpp" -waybar::modules::Battery::Battery(const Json::Value& config) +waybar::modules::Battery::Battery(const std::string& id, const Json::Value& config) : ALabel(config, "{capacity}%", 60) { + label_.set_name("battery"); + if (!id.empty()) { + label_.get_style_context()->add_class(id); + } try { if (config_["bat"].isString()) { auto dir = data_dir_ / config_["bat"].asString(); @@ -131,10 +135,8 @@ auto waybar::modules::Battery::update() -> void } if (format.empty()) { event_box_.hide(); - label_.set_name(""); } else { event_box_.show(); - label_.set_name("battery"); label_.set_markup(fmt::format(format, fmt::arg("capacity", capacity), fmt::arg("icon", getIcon(capacity)))); } diff --git a/src/modules/clock.cpp b/src/modules/clock.cpp index 958f15fe..2b7b41fb 100644 --- a/src/modules/clock.cpp +++ b/src/modules/clock.cpp @@ -1,9 +1,12 @@ #include "modules/clock.hpp" -waybar::modules::Clock::Clock(const Json::Value& config) +waybar::modules::Clock::Clock(const std::string& id, const Json::Value& config) : ALabel(config, "{:%H:%M}", 60) { label_.set_name("clock"); + if (!id.empty()) { + label_.get_style_context()->add_class(id); + } thread_ = [this] { auto now = waybar::chrono::clock::now(); dp.emit(); diff --git a/src/modules/cpu.cpp b/src/modules/cpu.cpp index 91e177d2..5d9a1c87 100644 --- a/src/modules/cpu.cpp +++ b/src/modules/cpu.cpp @@ -1,9 +1,12 @@ #include "modules/cpu.hpp" -waybar::modules::Cpu::Cpu(const Json::Value& config) +waybar::modules::Cpu::Cpu(const std::string& id, const Json::Value& config) : ALabel(config, "{usage}%", 10) { label_.set_name("cpu"); + if (!id.empty()) { + label_.get_style_context()->add_class(id); + } thread_ = [this] { dp.emit(); thread_.sleep_for(interval_); diff --git a/src/modules/custom.cpp b/src/modules/custom.cpp index fd6f66bf..68d98462 100644 --- a/src/modules/custom.cpp +++ b/src/modules/custom.cpp @@ -1,18 +1,18 @@ #include "modules/custom.hpp" -waybar::modules::Custom::Custom(const std::string name, +waybar::modules::Custom::Custom(const std::string& name, const Json::Value& config) : ALabel(config, "{}"), name_(name), fp_(nullptr) { + label_.set_name("custom-" + name_); if (config_["exec"].isString()) { if (interval_.count() > 0) { delayWorker(); } else { continuousWorker(); } - } else { - update(); } + dp.emit(); } waybar::modules::Custom::~Custom() @@ -31,8 +31,7 @@ void waybar::modules::Custom::delayWorker() auto res = waybar::util::command::exec(config_["exec-if"].asString()); if (res.exit_code != 0) { can_update = false; - label_.hide(); - label_.set_name(""); + event_box_.hide(); } } if (can_update) { @@ -80,11 +79,8 @@ auto waybar::modules::Custom::update() -> void { // Hide label if output is empty if (config_["exec"].isString() && (output_.out.empty() || output_.exit_code != 0)) { - label_.hide(); - label_.set_name(""); + event_box_.hide(); } else { - label_.set_name("custom-" + name_); - if (config_["return-type"].asString() == "json") { parseOutputJson(); } else { @@ -109,7 +105,7 @@ auto waybar::modules::Custom::update() -> void prevclass_ = ""; } - label_.show(); + event_box_.show(); } } diff --git a/src/modules/memory.cpp b/src/modules/memory.cpp index 591c32a4..9ddace76 100644 --- a/src/modules/memory.cpp +++ b/src/modules/memory.cpp @@ -1,8 +1,12 @@ #include "modules/memory.hpp" -waybar::modules::Memory::Memory(const Json::Value& config) +waybar::modules::Memory::Memory(const std::string& id, const Json::Value& config) : ALabel(config, "{}%", 30) { + label_.set_name("memory"); + if (!id.empty()) { + label_.get_style_context()->add_class(id); + } thread_ = [this] { dp.emit(); thread_.sleep_for(interval_); @@ -17,11 +21,9 @@ auto waybar::modules::Memory::update() -> void label_.set_markup(fmt::format(format_, used_ram_percentage)); auto used_ram_gigabytes = (memtotal_ - memfree_) / std::pow(1024, 2); label_.set_tooltip_text(fmt::format("{:.{}f}Gb used", used_ram_gigabytes, 1)); - label_.set_name("memory"); - label_.show(); + event_box_.show(); } else { - label_.set_name(""); - label_.hide(); + event_box_.hide(); } } diff --git a/src/modules/network.cpp b/src/modules/network.cpp index a27fc53e..c2a8175d 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -1,9 +1,13 @@ #include "modules/network.hpp" -waybar::modules::Network::Network(const Json::Value& config) +waybar::modules::Network::Network(const std::string& id, const Json::Value& config) : ALabel(config, "{ifname}", 60), family_(AF_INET), cidr_(-1), signal_strength_dbm_(0), signal_strength_(0) { + label_.set_name("network"); + if (!id.empty()) { + label_.get_style_context()->add_class(id); + } sock_fd_ = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE); if (sock_fd_ < 0) { throw std::runtime_error("Can't open network socket"); @@ -29,10 +33,9 @@ waybar::modules::Network::Network(const Json::Value& config) } } initNL80211(); - label_.set_name("network"); // Trigger first values getInfo(); - update(); + dp.emit(); worker(); } diff --git a/src/modules/pulseaudio.cpp b/src/modules/pulseaudio.cpp index 213be991..d943ce4a 100644 --- a/src/modules/pulseaudio.cpp +++ b/src/modules/pulseaudio.cpp @@ -1,6 +1,6 @@ #include "modules/pulseaudio.hpp" -waybar::modules::Pulseaudio::Pulseaudio(const Json::Value &config) +waybar::modules::Pulseaudio::Pulseaudio(const std::string& id, const Json::Value &config) : ALabel(config, "{volume}%"), mainloop_(nullptr), mainloop_api_(nullptr), @@ -10,6 +10,9 @@ waybar::modules::Pulseaudio::Pulseaudio(const Json::Value &config) muted_(false), scrolling_(false) { label_.set_name("pulseaudio"); + if (!id.empty()) { + label_.get_style_context()->add_class(id); + } mainloop_ = pa_threaded_mainloop_new(); if (mainloop_ == nullptr) { throw std::runtime_error("pa_mainloop_new() failed."); diff --git a/src/modules/sni/tray.cpp b/src/modules/sni/tray.cpp index 6b974648..2f372ba6 100644 --- a/src/modules/sni/tray.cpp +++ b/src/modules/sni/tray.cpp @@ -2,7 +2,7 @@ #include -waybar::modules::SNI::Tray::Tray(const Json::Value &config) +waybar::modules::SNI::Tray::Tray(const std::string& id, const Json::Value &config) : config_(config), watcher_(), host_(nb_hosts_, config, std::bind(&Tray::onAdd, this, std::placeholders::_1), std::bind(&Tray::onRemove, this, std::placeholders::_1)) diff --git a/src/modules/sway/mode.cpp b/src/modules/sway/mode.cpp index e0c73f8a..2b25a925 100644 --- a/src/modules/sway/mode.cpp +++ b/src/modules/sway/mode.cpp @@ -1,12 +1,17 @@ #include "modules/sway/mode.hpp" -waybar::modules::sway::Mode::Mode(const Bar& bar, const Json::Value& config) +waybar::modules::sway::Mode::Mode(const std::string& id, const Bar& bar, const Json::Value& config) : ALabel(config, "{}"), bar_(bar) { + label_.set_name("mode"); + if (!id.empty()) { + label_.get_style_context()->add_class(id); + } ipc_.connect(); ipc_.subscribe("[ \"mode\" ]"); // Launch worker worker(); + dp.emit(); } void waybar::modules::sway::Mode::worker() @@ -32,12 +37,10 @@ void waybar::modules::sway::Mode::worker() auto waybar::modules::sway::Mode::update() -> void { if (mode_.empty()) { - label_.set_name(""); - label_.hide(); + event_box_.hide(); } else { - label_.set_name("mode"); label_.set_markup(fmt::format(format_, mode_)); label_.set_tooltip_text(mode_); - label_.show(); + event_box_.show(); } } \ No newline at end of file diff --git a/src/modules/sway/window.cpp b/src/modules/sway/window.cpp index 29d147a0..6f18f65c 100644 --- a/src/modules/sway/window.cpp +++ b/src/modules/sway/window.cpp @@ -1,9 +1,12 @@ #include "modules/sway/window.hpp" -waybar::modules::sway::Window::Window(const Bar &bar, const Json::Value& config) +waybar::modules::sway::Window::Window(const std::string& id, const Bar &bar, const Json::Value& config) : ALabel(config, "{}"), bar_(bar), windowId_(-1) { label_.set_name("window"); + if (!id.empty()) { + label_.get_style_context()->add_class(id); + } if (label_.get_max_width_chars() == -1) { label_.set_hexpand(true); label_.set_ellipsize(Pango::EllipsizeMode::ELLIPSIZE_END); diff --git a/src/modules/sway/workspaces.cpp b/src/modules/sway/workspaces.cpp index 07ed0f1a..911e3113 100644 --- a/src/modules/sway/workspaces.cpp +++ b/src/modules/sway/workspaces.cpp @@ -1,10 +1,13 @@ #include "modules/sway/workspaces.hpp" -waybar::modules::sway::Workspaces::Workspaces(const Bar& bar, +waybar::modules::sway::Workspaces::Workspaces(const std::string& id, const Bar& bar, const Json::Value& config) : bar_(bar), config_(config), scrolling_(false) { box_.set_name("workspaces"); + if (!id.empty()) { + box_.get_style_context()->add_class(id); + } ipc_.connect(); ipc_.subscribe("[ \"workspace\" ]"); // Launch worker