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:
Michael Carlberg 2016-11-12 09:40:14 +01:00
parent 2740e69a38
commit f2bbd301f2
8 changed files with 184 additions and 70 deletions

View File

@ -68,6 +68,7 @@ using std::to_string;
using std::strerror;
using std::getenv;
using std::thread;
using std::exception;
using boost::optional;

View File

@ -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;
};
}

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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,11 +66,21 @@ 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)
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();
}
@ -76,10 +88,6 @@ namespace modules {
* 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

View File

@ -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, &params, graphics_exposures, false);
xutils::pack_values(mask, &params, 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, &params, graphics_exposures, false);
xutils::pack_values(mask, &params, 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;
}
}

View File

@ -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;
}
}