mirror of
https://github.com/polybar/polybar.git
synced 2024-11-11 13:50:56 -05:00
feat(xbacklight): Change value on scroll
- Add action handlers for scroll up/down (conf: enable-sroll = true) - Use xcb_timestamps instead of the throttle util
This commit is contained in:
parent
2740e69a38
commit
f2bbd301f2
8 changed files with 184 additions and 70 deletions
|
@ -68,6 +68,7 @@ using std::to_string;
|
|||
using std::strerror;
|
||||
using std::getenv;
|
||||
using std::thread;
|
||||
using std::exception;
|
||||
|
||||
using boost::optional;
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
#include "drawtypes/progressbar.hpp"
|
||||
#include "drawtypes/ramp.hpp"
|
||||
#include "modules/meta.hpp"
|
||||
#include "utils/throttle.hpp"
|
||||
|
||||
LEMONBUDDY_NS
|
||||
|
||||
|
@ -35,21 +34,31 @@ namespace modules {
|
|||
void setup();
|
||||
void handle(const evt::randr_notify& evt);
|
||||
void update();
|
||||
string get_output();
|
||||
bool build(builder* builder, string tag) const;
|
||||
bool handle_event(string cmd);
|
||||
bool receive_events() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
static constexpr auto TAG_LABEL = "<label>";
|
||||
static constexpr auto TAG_BAR = "<bar>";
|
||||
static constexpr auto TAG_RAMP = "<ramp>";
|
||||
|
||||
throttle_util::throttle_t m_throttler;
|
||||
static constexpr auto EVENT_SCROLLUP = "xbacklight+";
|
||||
static constexpr auto EVENT_SCROLLDOWN = "xbacklight-";
|
||||
|
||||
connection& m_connection{configure_connection().create<connection&>()};
|
||||
monitor_t m_output;
|
||||
xcb_window_t m_proxy;
|
||||
xcb_timestamp_t m_timestamp;
|
||||
|
||||
ramp_t m_ramp;
|
||||
label_t m_label;
|
||||
progressbar_t m_progressbar;
|
||||
|
||||
bool m_scroll = true;
|
||||
int m_percentage = 0;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -18,10 +18,12 @@ namespace graphics_util {
|
|||
xcb_pixmap_t pixmap{0};
|
||||
};
|
||||
|
||||
void get_root_pixmap(connection& conn, root_pixmap* rpix);
|
||||
bool create_window(connection& conn, xcb_window_t* win, int16_t x = 0, int16_t y = 0, uint16_t w = 1, uint16_t h = 1);
|
||||
bool create_pixmap(connection& conn, xcb_drawable_t dst, uint16_t w, uint16_t h, xcb_pixmap_t* pixmap);
|
||||
bool create_pixmap(connection& conn, xcb_drawable_t dst, uint16_t w, uint16_t h, uint8_t d, xcb_pixmap_t* pixmap);
|
||||
bool create_gc(connection& conn, xcb_drawable_t drawable, xcb_gcontext_t* gc);
|
||||
|
||||
void simple_gc(connection& conn, xcb_drawable_t drawable, xcb_gcontext_t* gc);
|
||||
void simple_pixmap(connection& conn, xcb_window_t dst, int w, int h, xcb_pixmap_t* pixmap);
|
||||
bool get_root_pixmap(connection& conn, root_pixmap* rpix);
|
||||
}
|
||||
|
||||
LEMONBUDDY_NS_END
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
LEMONBUDDY_NS
|
||||
|
||||
struct backlight_values {
|
||||
uint32_t atom = 0;
|
||||
uint32_t min = 0;
|
||||
uint32_t max = 0;
|
||||
uint32_t val = 0;
|
||||
|
|
|
@ -61,7 +61,7 @@ void eventloop::run(chrono::duration<double, std::milli> timeframe, int limit) {
|
|||
evt = next;
|
||||
break;
|
||||
} else if (compare_events(evt, next)) {
|
||||
m_log.trace("eventloop: Swallowing event within timeframe");
|
||||
m_log.trace_x("eventloop: Swallowing event within timeframe");
|
||||
evt = next;
|
||||
} else {
|
||||
break;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include "modules/xbacklight.hpp"
|
||||
#include "utils/math.hpp"
|
||||
#include "x11/graphics.hpp"
|
||||
|
||||
LEMONBUDDY_NS
|
||||
|
||||
|
@ -23,26 +24,27 @@ namespace modules {
|
|||
throw module_error("No matching output found for \"" + output + "\", stopping module...");
|
||||
}
|
||||
|
||||
// Get flag to check if we should add scroll handlers for changing value
|
||||
GET_CONFIG_VALUE(name(), m_scroll, "enable-scroll");
|
||||
|
||||
// Query randr for the backlight max and min value
|
||||
try {
|
||||
auto& backlight = m_output->backlight;
|
||||
randr_util::get_backlight_range(m_connection, m_output, backlight);
|
||||
randr_util::get_backlight_value(m_connection, m_output, backlight);
|
||||
} catch (const std::exception& err) {
|
||||
} catch (const exception& err) {
|
||||
throw module_error("No backlight data found for \"" + output + "\", stopping module...");
|
||||
}
|
||||
|
||||
// Connect with the event registry and tell randr that we
|
||||
// want to get notified when an output property gets modified
|
||||
m_connection.attach_sink(this, 1);
|
||||
m_connection.select_input_checked(
|
||||
m_connection.screen()->root, XCB_RANDR_NOTIFY_MASK_OUTPUT_PROPERTY);
|
||||
// Create window that will proxy all RandR notify events
|
||||
if (!graphics_util::create_window(m_connection, &m_proxy, -1, -1, 1, 1)) {
|
||||
throw module_error("Failed to create event proxy");
|
||||
}
|
||||
|
||||
// Create a throttle so that we limit the amount of events
|
||||
// to handle since randr can burst out quite a few
|
||||
// We will allow 1 event per 60 ms. The updates still look smooth
|
||||
// using this setting which is important.
|
||||
m_throttler = throttle_util::make_throttler(1, 60ms);
|
||||
// Connect with the event registry and make sure we get
|
||||
// notified when a RandR output property gets modified
|
||||
m_connection.attach_sink(this, 1);
|
||||
m_connection.select_input_checked(m_proxy, XCB_RANDR_NOTIFY_MASK_OUTPUT_PROPERTY);
|
||||
|
||||
// Add formats and elements
|
||||
m_formatter->add(DEFAULT_FORMAT, TAG_LABEL, {TAG_LABEL, TAG_BAR, TAG_RAMP});
|
||||
|
@ -64,22 +66,28 @@ namespace modules {
|
|||
void xbacklight_module::handle(const evt::randr_notify& evt) {
|
||||
if (evt->subCode != XCB_RANDR_NOTIFY_OUTPUT_PROPERTY)
|
||||
return;
|
||||
else if (evt->u.op.status != XCB_PROPERTY_NEW_VALUE)
|
||||
return;
|
||||
else if (evt->u.op.window != m_proxy)
|
||||
return;
|
||||
else if (evt->u.op.output != m_output->randr_output)
|
||||
return;
|
||||
else if (evt->u.op.atom == Backlight)
|
||||
update();
|
||||
else if (evt->u.op.atom == BACKLIGHT)
|
||||
update();
|
||||
else if (evt->u.op.atom != m_output->backlight.atom)
|
||||
return;
|
||||
else if (evt->u.op.timestamp <= m_timestamp)
|
||||
return;
|
||||
|
||||
// Store the timestamp with a throttle offset (ms)
|
||||
m_timestamp = evt->u.op.timestamp + 50;
|
||||
|
||||
// Fetch the new values
|
||||
update();
|
||||
}
|
||||
|
||||
/**
|
||||
* Query the RandR extension for the new values
|
||||
*/
|
||||
void xbacklight_module::update() {
|
||||
// Test if we are allowed to handle the event
|
||||
if (!m_throttler->passthrough(throttle_util::strategy::try_once_or_leave_yolo{}))
|
||||
return;
|
||||
|
||||
// Query for the new backlight value
|
||||
auto& bl = m_output->backlight;
|
||||
randr_util::get_backlight_value(m_connection, m_output, bl);
|
||||
|
@ -96,6 +104,29 @@ namespace modules {
|
|||
broadcast();
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the module output
|
||||
*/
|
||||
string xbacklight_module::get_output() {
|
||||
if (m_scroll) {
|
||||
if (m_percentage < 100)
|
||||
m_builder->cmd(mousebtn::SCROLL_UP, EVENT_SCROLLUP);
|
||||
if (m_percentage > 0)
|
||||
m_builder->cmd(mousebtn::SCROLL_DOWN, EVENT_SCROLLDOWN);
|
||||
|
||||
m_builder->node(static_module::get_output());
|
||||
|
||||
if (m_percentage < 100)
|
||||
m_builder->cmd_close(true);
|
||||
if (m_percentage > 0)
|
||||
m_builder->cmd_close(true);
|
||||
} else {
|
||||
m_builder->node(static_module::get_output());
|
||||
}
|
||||
|
||||
return m_builder->flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* Output content as defined in the config
|
||||
*/
|
||||
|
@ -110,6 +141,36 @@ namespace modules {
|
|||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process scroll events by changing backlight value
|
||||
*/
|
||||
bool xbacklight_module::handle_event(string cmd) {
|
||||
int value_mod = 0;
|
||||
|
||||
if (cmd == EVENT_SCROLLUP) {
|
||||
value_mod = 10;
|
||||
m_log.info("%s: Increasing value by %i%", name(), value_mod);
|
||||
} else if (cmd == EVENT_SCROLLDOWN) {
|
||||
value_mod = -10;
|
||||
m_log.info("%s: Decreasing value by %i%", name(), -value_mod);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
const int new_perc = math_util::cap(m_percentage + value_mod, 0, 100);
|
||||
const int new_value = math_util::percentage_to_value<int>(new_perc, m_output->backlight.max);
|
||||
const int values[1]{new_value};
|
||||
|
||||
m_connection.change_output_property_checked(
|
||||
m_output->randr_output, m_output->backlight.atom, XCB_ATOM_INTEGER, 32, XCB_PROP_MODE_REPLACE, 1, values);
|
||||
} catch (const exception& err) {
|
||||
m_log.err("%s: %s", name(), err.what());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
LEMONBUDDY_NS_END
|
||||
|
|
|
@ -9,10 +9,73 @@
|
|||
LEMONBUDDY_NS
|
||||
|
||||
namespace graphics_util {
|
||||
/**
|
||||
* Create a basic window
|
||||
*/
|
||||
bool create_window(connection& conn, xcb_window_t* win, int16_t x, int16_t y, uint16_t w, uint16_t h) {
|
||||
try {
|
||||
auto root = conn.screen()->root;
|
||||
auto copy = XCB_COPY_FROM_PARENT;
|
||||
*win = conn.generate_id();
|
||||
conn.create_window_checked(copy, *win, root, x, y, w, h, 0, copy, copy, 0, nullptr);
|
||||
return true;
|
||||
} catch (const exception& err) {
|
||||
*win = XCB_NONE;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a basic pixmap with the same depth as the
|
||||
* root depth of the default screen
|
||||
*/
|
||||
bool create_pixmap(connection& conn, xcb_drawable_t dst, uint16_t w, uint16_t h, xcb_pixmap_t* pixmap) {
|
||||
return graphics_util::create_pixmap(conn, dst, w, h, conn.screen()->root_depth, pixmap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a basic pixmap with specific depth
|
||||
*/
|
||||
bool create_pixmap(connection& conn, xcb_drawable_t dst, uint16_t w, uint16_t h, uint8_t d, xcb_pixmap_t* pixmap) {
|
||||
try {
|
||||
*pixmap = conn.generate_id();
|
||||
conn.create_pixmap_checked(d, *pixmap, dst, w, h);
|
||||
return true;
|
||||
} catch (const exception& err) {
|
||||
*pixmap = XCB_NONE;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a basic gc
|
||||
*/
|
||||
bool create_gc(connection& conn, xcb_drawable_t drawable, xcb_gcontext_t* gc) {
|
||||
try {
|
||||
xcb_params_gc_t params;
|
||||
|
||||
uint32_t mask = 0;
|
||||
uint32_t values[32];
|
||||
|
||||
XCB_AUX_ADD_PARAM(&mask, ¶ms, graphics_exposures, false);
|
||||
xutils::pack_values(mask, ¶ms, values);
|
||||
|
||||
*gc = conn.generate_id();
|
||||
conn.create_gc_checked(*gc, drawable, mask, values);
|
||||
return true;
|
||||
} catch (const exception& err) {
|
||||
*gc = XCB_NONE;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Query for the root window pixmap
|
||||
*/
|
||||
void get_root_pixmap(connection& conn, root_pixmap* rpix) {
|
||||
bool get_root_pixmap(connection& conn, root_pixmap* rpix) {
|
||||
auto screen = conn.screen();
|
||||
const xcb_atom_t pixmap_properties[3]{ESETROOT_PMAP_ID, _XROOTMAP_ID, _XSETROOT_ID};
|
||||
for (auto&& property : pixmap_properties) {
|
||||
|
@ -25,15 +88,15 @@ namespace graphics_util {
|
|||
}
|
||||
|
||||
if (!rpix->pixmap) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto cookie = xcb_get_geometry(conn, rpix->pixmap);
|
||||
auto reply = xcb_get_geometry_reply(conn, cookie, nullptr);
|
||||
|
||||
if (!reply) {
|
||||
rpix->pixmap = 0;
|
||||
return;
|
||||
rpix->pixmap = XCB_NONE;
|
||||
return false;
|
||||
}
|
||||
|
||||
rpix->depth = reply->depth;
|
||||
|
@ -41,38 +104,8 @@ namespace graphics_util {
|
|||
rpix->height = reply->height;
|
||||
rpix->x = reply->x;
|
||||
rpix->y = reply->y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a basic gc
|
||||
*/
|
||||
void simple_gc(connection& conn, xcb_drawable_t drawable, xcb_gcontext_t* gc) {
|
||||
xcb_params_gc_t params;
|
||||
|
||||
uint32_t mask = 0;
|
||||
uint32_t values[32];
|
||||
|
||||
XCB_AUX_ADD_PARAM(&mask, ¶ms, graphics_exposures, false);
|
||||
xutils::pack_values(mask, ¶ms, values);
|
||||
|
||||
try {
|
||||
*gc = conn.generate_id();
|
||||
conn.create_gc_checked(*gc, drawable, mask, values);
|
||||
} catch (const std::exception& err) {
|
||||
// no-op
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a basic pixmap
|
||||
*/
|
||||
void simple_pixmap(connection& conn, xcb_window_t dst, int w, int h, xcb_pixmap_t* pixmap) {
|
||||
try {
|
||||
*pixmap = conn.generate_id();
|
||||
conn.create_pixmap_checked(conn.screen()->root_depth, *pixmap, dst, w, h);
|
||||
} catch (const std::exception& err) {
|
||||
// no-op
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -53,34 +53,41 @@ namespace randr_util {
|
|||
* Get backlight value range for given output
|
||||
*/
|
||||
void get_backlight_range(connection& conn, const monitor_t& mon, backlight_values& dst) {
|
||||
auto reply = conn.query_output_property(mon->randr_output, Backlight);
|
||||
auto atom = Backlight;
|
||||
auto reply = conn.query_output_property(mon->randr_output, atom);
|
||||
|
||||
dst.min = 0;
|
||||
dst.max = 0;
|
||||
|
||||
if (!reply->range || reply->length != 2)
|
||||
reply = conn.query_output_property(mon->randr_output, BACKLIGHT);
|
||||
if (!reply->range || reply->length != 2)
|
||||
if (!reply->range || reply->length != 2) {
|
||||
atom = BACKLIGHT;
|
||||
reply = conn.query_output_property(mon->randr_output, atom);
|
||||
}
|
||||
|
||||
if (!reply->range || reply->length != 2) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto range = reply.valid_values().begin();
|
||||
|
||||
dst.min = static_cast<uint32_t>(*range++);
|
||||
dst.max = static_cast<uint32_t>(*range);
|
||||
dst.atom = atom;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get backlight value for given output
|
||||
*/
|
||||
void get_backlight_value(connection& conn, const monitor_t& mon, backlight_values& dst) {
|
||||
auto reply = conn.get_output_property(mon->randr_output, Backlight, XCB_ATOM_NONE, 0, 4, 0, 0);
|
||||
dst.val = 0;
|
||||
|
||||
if (!dst.atom) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto reply = conn.get_output_property(mon->randr_output, dst.atom, XCB_ATOM_NONE, 0, 4, 0, 0);
|
||||
|
||||
if (reply->num_items != 1 || reply->format != 32 || reply->type != XCB_ATOM_INTEGER)
|
||||
reply = conn.get_output_property(mon->randr_output, BACKLIGHT, XCB_ATOM_NONE, 0, 4, 0, 0);
|
||||
if (reply->num_items == 1 && reply->format == 32 && reply->type == XCB_ATOM_INTEGER)
|
||||
dst.val = *reinterpret_cast<uint32_t*>(xcb_randr_get_output_property_data(reply.get().get()));
|
||||
else
|
||||
dst.val = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue