diff --git a/include/components/bar.hpp b/include/components/bar.hpp index 7bca30e8..b9642898 100644 --- a/include/components/bar.hpp +++ b/include/components/bar.hpp @@ -23,7 +23,11 @@ LEMONBUDDY_NS -class bar { +namespace bar_signals { + delegate::Signal1 action_click; +}; + +class bar : public xpp::event::sink { public: /** * Construct bar @@ -53,6 +57,8 @@ class bar { parser_signals::unicode_text_write.disconnect(this, &bar::draw_character); if (m_tray.align != alignment::NONE) tray_signals::report_slotcount.disconnect(this, &bar::on_tray_report); + if (m_sinkattached) + m_connection.detach_sink(this, 1); m_window.destroy(); } @@ -393,6 +399,9 @@ class bar { // }}} + m_connection.attach_sink(this, 1); + m_sinkattached = true; + m_connection.flush(); } //}}} @@ -455,6 +464,19 @@ class bar { m_pixmap, m_window, m_gcontexts.at(gc::BL), 0, 0, 0, 0, m_bar.width, m_bar.height); m_connection.copy_area( m_pixmap, m_window, m_gcontexts.at(gc::BR), 0, 0, 0, 0, m_bar.width, m_bar.height); + + for (auto&& action : m_actions) { + if (action.active) { + m_log.warn("Action block not closed"); + m_log.warn("action.command = %s", action.command); + } else { + m_log.trace("bar: Action details"); + m_log.trace("action.command = %s", action.command); + m_log.trace("action.mousebtn = %i", static_cast(action.mousebtn)); + m_log.trace("action.start_x = %i", action.start_x); + m_log.trace("action.end_x = %i", action.end_x); + } + } } //}}} /** @@ -471,6 +493,51 @@ class bar { return m_tray; } // }}} + /** + * Mouse button event handler + */ + void handle(const evt::button_press& evt) { // {{{ + std::lock_guard lck(m_lock); + { + m_log.trace("bar: Received button press event: %i at pos(%i, %i)", + static_cast(evt->detail), evt->event_x, evt->event_y); + + mousebtn button = static_cast(evt->detail); + + for (auto&& action : m_actions) { + if (action.active) { + m_log.trace("bar: Ignoring action: unclosed)"); + continue; + } else if (action.mousebtn != button) { + m_log.trace("bar: Ignoring action: button mismatch"); + continue; + } else if (action.start_x > evt->event_x) { + m_log.trace( + "bar: Ignoring action: start_x(%i) > event_x(%i)", action.start_x, evt->event_x); + continue; + } else if (action.end_x < evt->event_x) { + m_log.trace("bar: Ignoring action: end_x(%i) < event_x(%i)", action.end_x, evt->event_x); + continue; + } + + m_log.info("Found matching input area"); + m_log.trace("action.command = %s", action.command); + m_log.trace("action.mousebtn = %i", static_cast(action.mousebtn)); + m_log.trace("action.start_x = %i", action.start_x); + m_log.trace("action.end_x = %i", action.end_x); + + if (!bar_signals::action_click.empty()) + bar_signals::action_click.emit(action.command); + else + m_log.warn("No signal handler's connected to 'action_click'"); + + return; + } + + m_log.warn("No matching input area found"); + } + } // }}} + protected: /** * Handle alignment update @@ -480,7 +547,14 @@ class bar { return; m_log.trace("bar: alignment_change(%i)", static_cast(align)); m_bar.align = align; - m_xpos = 0; + + if (align == alignment::LEFT) { + m_xpos = m_borders[border::LEFT].size; + } else if (align == alignment::RIGHT) { + m_xpos = m_borders[border::RIGHT].size; + } else { + m_xpos = 0; + } } //}}} /** @@ -523,6 +597,7 @@ class bar { m_log.trace("bar: action_block_open(%i, %s)", static_cast(btn), cmd); action_block action; action.active = true; + action.align = m_bar.align; action.mousebtn = btn; action.start_x = m_xpos; action.command = cmd; @@ -538,10 +613,26 @@ class bar { auto n_actions = m_actions.size(); while (n_actions--) { action_block& action = m_actions[n_actions]; + if (!action.active || action.mousebtn != btn) continue; + action.end_x = m_xpos; action.active = false; + + if (action.align == alignment::CENTER) { + auto width = action.end_x - action.start_x; + auto center = m_bar.width / 2; + auto x1 = center - width / 2; + auto x2 = center + width / 2; + action.start_x = x1; + action.end_x = x2; + } else if (action.align == alignment::RIGHT) { + auto x1 = m_bar.width - action.end_x; + auto x2 = m_bar.width - action.start_x; + action.start_x = x1; + action.end_x = x2; + } } } //}}} @@ -761,6 +852,8 @@ class bar { map m_gcontexts; vector m_actions; + stateflag m_sinkattached{false}; + string m_prevdata; int m_xpos{0}; int m_attributes{0}; diff --git a/include/components/controller.hpp b/include/components/controller.hpp index cb2f6797..9bbff24a 100644 --- a/include/components/controller.hpp +++ b/include/components/controller.hpp @@ -11,6 +11,7 @@ #include "components/x11/tray.hpp" #include "components/x11/types.hpp" #include "config.hpp" +#include "utils/command.hpp" #include "utils/inotify.hpp" #include "utils/process.hpp" #include "utils/socket.hpp" @@ -66,6 +67,7 @@ class controller { } m_log.trace("controller: Deconstruct bar instance"); + bar_signals::action_click.disconnect(this, &controller::on_module_click); m_bar.reset(); if (m_traymanager) { @@ -143,6 +145,8 @@ class controller { if (dump_wmname) { std::cout << m_bar->settings().wmname << std::endl; return; + } else if (!to_stdout) { + bar_signals::action_click.connect(this, &controller::on_module_click); } } catch (const std::exception& err) { throw application_error("Failed to setup bar renderer: " + string{err.what()}); @@ -452,6 +456,34 @@ class controller { m_bar->parse(contents); } + void on_module_click(string input) { + std::lock_guard lck(m_lock); + + for (auto&& block : m_modules) { + for (auto&& module : block.second) { + if (!module->receive_events()) + continue; + if (module->handle_event(input)) + return; + } + } + + m_log.trace("controller: Unrecognized input '%s'", input); + m_log.trace("controller: Forwarding input to shell"); + + auto command = command_util::make_command("/usr/bin/env\nsh\n-c\n"+ input); + + try { + command->exec(false); + command->tail([this](std::string output){ + m_log.trace("> %s", output); + }); + command->wait(); + } catch (const application_error& err) { + m_log.err(err.what()); + } + } + private: connection& m_connection; registry m_registry{m_connection}; diff --git a/include/components/parser.hpp b/include/components/parser.hpp index 6224bbb4..7acb3444 100644 --- a/include/components/parser.hpp +++ b/include/components/parser.hpp @@ -234,7 +234,7 @@ class parser { } // }}} mousebtn parse_action_btn(string data) { // {{{ - if (isdigit(data[0] - '0')) + if (isdigit(data[0])) return static_cast(data[0] - '0'); else if (!m_actions.empty()) return static_cast(m_actions.back()); diff --git a/include/modules/bspwm.hpp b/include/modules/bspwm.hpp index d3305616..3646df98 100644 --- a/include/modules/bspwm.hpp +++ b/include/modules/bspwm.hpp @@ -305,27 +305,29 @@ namespace modules { return true; } - bool handle_command(string cmd) { - return false; - // if (cmd.find(EVENT_CLICK) == string::npos || cmd.length() <= strlen(EVENT_CLICK)) - // return false; - // - // stringstream payload_s; - // - // payload_s << "desktop -f " << m_monitor << ":^" - // << std::atoi(cmd.substr(strlen(EVENT_CLICK)).c_str()); - // - // int payload_fd; - // string socket_path = get_socket_path(); - // - // if ((payload_fd = io_util::socket::open(socket_path)) == -1) - // m_log.err("%s: Failed to open socket '%s'", name(), socket_path); - // else if (!send_payload(payload_fd, generate_payload(payload_s.str()))) - // m_log.err("%s: Failed to change desktop", name()); - // - // close(payload_fd); - // - // return true; + bool handle_event(string cmd) { + if (cmd.find(EVENT_CLICK) == string::npos || cmd.length() <= strlen(EVENT_CLICK)) + return false; + + try { + auto ipc = bspwm_util::make_subscriber(); + auto command = string_util::from_stream(stringstream() << "desktop -f " << m_monitor << ":^" + << cmd.substr(strlen(EVENT_CLICK))); + auto payload = bspwm_util::make_payload(command); + + m_log.info("%s: Sending command to ipc handler '%s'", name(), command); + + ipc->send(payload->data, payload->len, 0); + ipc->disconnect(); + } catch (const system_error& err) { + m_log.err("%s: %s", name(), err.what()); + } + + return true; + } + + bool receive_events() const { + return true; } private: diff --git a/include/modules/date.hpp b/include/modules/date.hpp index 93234d3b..a79ae7d0 100644 --- a/include/modules/date.hpp +++ b/include/modules/date.hpp @@ -49,6 +49,18 @@ namespace modules { return tag == TAG_DATE; } + bool handle_event(string cmd) { + if (cmd == EVENT_TOGGLE) { + m_detailed = !m_detailed; + wakeup(); + } + return cmd == EVENT_TOGGLE; + } + + bool receive_events() const { + return true; + } + private: static constexpr auto TAG_DATE = ""; diff --git a/include/modules/i3.hpp b/include/modules/i3.hpp index fca097fe..4c943ed0 100644 --- a/include/modules/i3.hpp +++ b/include/modules/i3.hpp @@ -206,14 +206,18 @@ namespace modules { return true; } - // bool handle_command(string cmd) { - // if (cmd.find(EVENT_CLICK) == string::npos || cmd.length() < strlen(EVENT_CLICK)) - // return false; - // - // m_ipc->send_command("workspace number " + cmd.substr(strlen(EVENT_CLICK))); - // - // return true; - // } + bool handle_event(string cmd) { + if (cmd.find(EVENT_CLICK) == string::npos || cmd.length() < strlen(EVENT_CLICK)) + return false; + + m_ipc->send_command("workspace number " + cmd.substr(strlen(EVENT_CLICK))); + + return true; + } + + bool receive_events() const { + return true; + } private: static constexpr auto DEFAULT_WS_ICON = "workspace_icon-default"; diff --git a/include/modules/menu.hpp b/include/modules/menu.hpp index 7c60c284..28e95912 100644 --- a/include/modules/menu.hpp +++ b/include/modules/menu.hpp @@ -97,32 +97,36 @@ namespace modules { return true; } - // bool handle_command(string cmd) { - // if (cmd.compare(0, strlen(EVENT_MENU_OPEN), EVENT_MENU_OPEN) == 0) { - // auto level = cmd.substr(strlen(EVENT_MENU_OPEN)); - // - // if (level.empty()) - // level = "0"; - // - // m_level = std::atoi(level.c_str()); - // - // if (m_level >= (int)m_levels.size()) { - // m_log.err("%s: Cannot open unexisting menu level '%d'", name(), level); - // m_level = -1; - // } - // - // } else if (cmd == EVENT_MENU_CLOSE) { - // m_level = -1; - // } else { - // m_level = -1; - // broadcast(); - // return false; - // } - // - // broadcast(); - // - // return true; - // } + bool handle_event(string cmd) { + if (cmd.compare(0, strlen(EVENT_MENU_OPEN), EVENT_MENU_OPEN) == 0) { + auto level = cmd.substr(strlen(EVENT_MENU_OPEN)); + + if (level.empty()) + level = "0"; + + m_level = std::atoi(level.c_str()); + + if (m_level >= (int)m_levels.size()) { + m_log.err("%s: Cannot open unexisting menu level '%d'", name(), level); + m_level = -1; + } + + } else if (cmd == EVENT_MENU_CLOSE) { + m_level = -1; + } else { + m_level = -1; + broadcast(); + return false; + } + + broadcast(); + + return true; + } + + bool receive_events() const { + return true; + } private: static constexpr auto TAG_LABEL_TOGGLE = ""; diff --git a/include/modules/meta.hpp b/include/modules/meta.hpp index e2fb52ae..7e45265b 100644 --- a/include/modules/meta.hpp +++ b/include/modules/meta.hpp @@ -155,8 +155,8 @@ namespace modules { virtual void refresh() = 0; virtual string contents() = 0; - virtual bool handle_command(string cmd) = 0; - virtual bool register_for_events() const = 0; + virtual bool handle_event(string cmd) = 0; + virtual bool receive_events() const = 0; delegate::Signal1 on_update; }; @@ -216,11 +216,11 @@ namespace modules { return m_cache; } - bool handle_command(string cmd) { - return CAST_MODULE(Impl)->handle_command(cmd); + bool handle_event(string cmd) { + return CAST_MODULE(Impl)->handle_event(cmd); } - bool register_for_events() const { + bool receive_events() const { return false; } diff --git a/include/modules/mpd.hpp b/include/modules/mpd.hpp index e412e4e2..6aab9a72 100644 --- a/include/modules/mpd.hpp +++ b/include/modules/mpd.hpp @@ -267,51 +267,57 @@ namespace modules { return true; } - // bool handle_command(string cmd) { - // if (cmd.compare(0, 3, "mpd") != 0) - // return false; - // - // try { - // auto mpd = make_unique(m_log, m_host, m_port, m_pass); - // auto status = mpd->get_status(); - // - // if (cmd == EVENT_PLAY) - // mpd->play(); - // else if (cmd == EVENT_PAUSE) - // mpd->pause(!(status->match_state(mpdstate::PAUSED))); - // else if (cmd == EVENT_STOP) - // mpd->stop(); - // else if (cmd == EVENT_PREV) - // mpd->prev(); - // else if (cmd == EVENT_NEXT) - // mpd->next(); - // else if (cmd == EVENT_REPEAT_ONE) - // mpd->set_single(!status->single); - // else if (cmd == EVENT_REPEAT) - // mpd->set_repeat(!status->repeat); - // else if (cmd == EVENT_RANDOM) - // mpd->set_random(!status->random); - // else if (cmd.compare(0, strlen(EVENT_SEEK), EVENT_SEEK) == 0) { - // auto s = cmd.substr(strlen(EVENT_SEEK)); - // int percentage = 0; - // if (s.empty()) - // return false; - // if (s[0] == '+') { - // percentage = status->get_elapsed_percentage() + std::atoi(s.substr(1).c_str()); - // } else if (s[0] == '-') { - // percentage = status->get_elapsed_percentage() - std::atoi(s.substr(1).c_str()); - // } else { - // percentage = std::atoi(s.c_str()); - // } - // mpd->seek(status->get_songid(), status->get_seek_position(percentage)); - // } else - // return false; - // } catch (const mpd_exception& e) { - // m_log.err(e.what()); - // } - // - // return true; - // } + bool handle_event(string cmd) { + if (cmd.compare(0, 3, "mpd") != 0) + return false; + + try { + auto mpd = make_unique(m_log, m_host, m_port, m_pass); + mpd->connect(); + + auto status = mpd->get_status(); + + if (cmd == EVENT_PLAY) + mpd->play(); + else if (cmd == EVENT_PAUSE) + mpd->pause(!(status->match_state(mpdstate::PAUSED))); + else if (cmd == EVENT_STOP) + mpd->stop(); + else if (cmd == EVENT_PREV) + mpd->prev(); + else if (cmd == EVENT_NEXT) + mpd->next(); + else if (cmd == EVENT_REPEAT_ONE) + mpd->set_single(!status->single()); + else if (cmd == EVENT_REPEAT) + mpd->set_repeat(!status->repeat()); + else if (cmd == EVENT_RANDOM) + mpd->set_random(!status->random()); + else if (cmd.compare(0, strlen(EVENT_SEEK), EVENT_SEEK) == 0) { + auto s = cmd.substr(strlen(EVENT_SEEK)); + int percentage = 0; + if (s.empty()) + return false; + if (s[0] == '+') { + percentage = status->get_elapsed_percentage() + std::atoi(s.substr(1).c_str()); + } else if (s[0] == '-') { + percentage = status->get_elapsed_percentage() - std::atoi(s.substr(1).c_str()); + } else { + percentage = std::atoi(s.c_str()); + } + mpd->seek(status->get_songid(), status->get_seek_position(percentage)); + } else + return false; + } catch (const mpd_exception& e) { + m_log.err("%s: %s", name(), e.what()); + } + + return true; + } + + bool receive_events() const { + return true; + } private: // static const int PROGRESSBAR_THREAD_SYNC_COUNT = 10; diff --git a/include/modules/volume.hpp b/include/modules/volume.hpp index 4a10307e..a15b9816 100644 --- a/include/modules/volume.hpp +++ b/include/modules/volume.hpp @@ -114,7 +114,7 @@ namespace modules { } bool has_event() { - if (m_has_changed) + if (m_updated) return true; try { @@ -135,7 +135,7 @@ namespace modules { } bool update() { - m_has_changed = false; + m_updated = false; // Consume any other pending events if (m_master_mixer) @@ -187,11 +187,19 @@ namespace modules { string get_output() { m_builder->cmd(mousebtn::LEFT, EVENT_TOGGLE_MUTE); + if (!m_muted && m_volume < 100) m_builder->cmd(mousebtn::SCROLL_UP, EVENT_VOLUME_UP); + else + m_log.trace("%s: Not adding scroll up handler (muted or volume = 100)", name()); + if (!m_muted && m_volume > 0) m_builder->cmd(mousebtn::SCROLL_DOWN, EVENT_VOLUME_DOWN); + else + m_log.trace("%s: Not adding scroll down handler (muted or volume = 0)", name()); + m_builder->node(module::get_output()); + return m_builder->flush(); } @@ -211,51 +219,45 @@ namespace modules { return true; } - // bool handle_command(string cmd) { - // if (cmd.length() < strlen(EVENT_PREFIX)) - // return false; - // if (std::strncmp(cmd.c_str(), EVENT_PREFIX, 3) != 0) - // return false; - // - // // std::lock_guard lck(this->update_lock); - // - // alsa_mixer* master_mixer = nullptr; - // alsa_mixer* other_mixer = nullptr; - // - // if (m_master_mixer) - // master_mixer = m_master_mixer.get(); - // - // if (master_mixer == nullptr) - // return false; - // - // if (m_headphone_mixer && m_headphone_ctrl && m_headphone_ctrl->test_device_plugged()) - // other_mixer = m_headphone_mixer.get(); - // else if (m_speaker_mixer) - // other_mixer = m_speaker_mixer.get(); - // - // if (std::strncmp(cmd.c_str(), EVENT_TOGGLE_MUTE, strlen(EVENT_TOGGLE_MUTE)) == 0) { - // // Toggle mute state - // master_mixer->set_mute(m_muted); - // if (other_mixer != nullptr) - // other_mixer->set_mute(m_muted); - // } else if (std::strncmp(cmd.c_str(), EVENT_VOLUME_UP, strlen(EVENT_VOLUME_UP)) == 0) { - // // Increase volume - // master_mixer->set_volume(math_util::cap(master_mixer->get_volume() + 5, 0, 100)); - // if (other_mixer != nullptr) - // other_mixer->set_volume(math_util::cap(other_mixer->get_volume() + 5, 0, 100)); - // } else if (std::strncmp(cmd.c_str(), EVENT_VOLUME_DOWN, strlen(EVENT_VOLUME_DOWN)) == 0) { - // // Decrease volume - // master_mixer->set_volume(math_util::cap(master_mixer->get_volume() - 5, 0, 100)); - // if (other_mixer != nullptr) - // other_mixer->set_volume(math_util::cap(other_mixer->get_volume() - 5, 0, 100)); - // } else { - // return false; - // } - // - // m_has_changed = true; - // - // return true; - // } + bool handle_event(string cmd) { + if (cmd.compare(0, 3, EVENT_PREFIX) != 0) + return false; + + if (!m_master_mixer) + return false; + + alsa_mixer* master_mixer = m_master_mixer.get(); + alsa_mixer* other_mixer = nullptr; + + if (m_headphone_mixer && m_headphone_ctrl && m_headphone_ctrl->test_device_plugged()) + other_mixer = m_headphone_mixer.get(); + else if (m_speaker_mixer) + other_mixer = m_speaker_mixer.get(); + + if (cmd.compare(0, strlen(EVENT_TOGGLE_MUTE), EVENT_TOGGLE_MUTE) == 0) { + master_mixer->set_mute(m_muted); + if (other_mixer != nullptr) + other_mixer->set_mute(m_muted); + } else if (cmd.compare(0, strlen(EVENT_VOLUME_UP), EVENT_VOLUME_UP) == 0) { + master_mixer->set_volume(math_util::cap(master_mixer->get_volume() + 5, 0, 100)); + if (other_mixer != nullptr) + other_mixer->set_volume(math_util::cap(other_mixer->get_volume() + 5, 0, 100)); + } else if (cmd.compare(0, strlen(EVENT_VOLUME_DOWN), EVENT_VOLUME_DOWN) == 0) { + master_mixer->set_volume(math_util::cap(master_mixer->get_volume() - 5, 0, 100)); + if (other_mixer != nullptr) + other_mixer->set_volume(math_util::cap(other_mixer->get_volume() - 5, 0, 100)); + } else { + return false; + } + + m_updated = true; + + return true; + } + + bool receive_events() const { + return true; + } private: static constexpr auto FORMAT_VOLUME = "format-volume"; @@ -287,7 +289,7 @@ namespace modules { int m_headphone_ctrl_numid = -1; int m_volume = 0; bool m_muted = false; - bool m_has_changed = false; + bool m_updated = false; bool m_headphones = false; }; }