mirror of
https://github.com/polybar/polybar.git
synced 2026-02-11 01:55:36 +00:00
264 lines
7.4 KiB
C++
264 lines
7.4 KiB
C++
#include <algorithm>
|
|
#include <utility>
|
|
|
|
#include "components/types.hpp"
|
|
#include "errors.hpp"
|
|
#include "settings.hpp"
|
|
#include "utils/string.hpp"
|
|
#include "x11/atoms.hpp"
|
|
#include "x11/connection.hpp"
|
|
#include "x11/extensions/randr.hpp"
|
|
|
|
POLYBAR_NS
|
|
|
|
/**
|
|
* Workaround for the inconsistent naming of outputs between intel and nvidia
|
|
* drivers (xf86-video-intel drops the dash)
|
|
*
|
|
* If exact == false, dashes will be ignored while matching
|
|
*/
|
|
bool randr_output::match(const string& o, bool exact) const {
|
|
if (exact) {
|
|
return name == o;
|
|
} else {
|
|
return string_util::replace(name, "-", "") == string_util::replace(o, "-", "");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Match position
|
|
*/
|
|
bool randr_output::match(const position& p) const {
|
|
return p.x == x && p.y == y;
|
|
}
|
|
|
|
/**
|
|
* Checks if inner is contained within `this`
|
|
*
|
|
* Also returns true for outputs that occupy the exact same space
|
|
*/
|
|
bool randr_output::contains(const randr_output& inner) const {
|
|
return inner.x >= x && inner.x + inner.w <= x + w
|
|
&& inner.y >= y && inner.y + inner.h <= y + h;
|
|
}
|
|
|
|
namespace randr_util {
|
|
/**
|
|
* XRandR version
|
|
*/
|
|
static unsigned int g_major_version = 0;
|
|
static unsigned int g_minor_version = 0;
|
|
|
|
/**
|
|
* Query for the XRandR extension
|
|
*/
|
|
void query_extension(connection& conn) {
|
|
auto ext = conn.randr().query_version(XCB_RANDR_MAJOR_VERSION, XCB_RANDR_MINOR_VERSION);
|
|
|
|
g_major_version = ext->major_version;
|
|
g_minor_version = ext->minor_version;
|
|
|
|
if (!conn.extension<xpp::randr::extension>()->present) {
|
|
throw application_error("Missing X extension: Randr");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Check for XRandR monitor support
|
|
*/
|
|
bool check_monitor_support() {
|
|
return WITH_XRANDR_MONITORS && g_major_version >= 1 && g_minor_version >= 5;
|
|
}
|
|
|
|
/**
|
|
* Define monitor
|
|
*/
|
|
monitor_t make_monitor(
|
|
xcb_randr_output_t randr, string name, unsigned short int w, unsigned short int h, short int x, short int y,
|
|
bool primary) {
|
|
monitor_t mon{new monitor_t::element_type{}};
|
|
mon->output = randr;
|
|
mon->name = move(name);
|
|
mon->x = x;
|
|
mon->y = y;
|
|
mon->h = h;
|
|
mon->w = w;
|
|
mon->primary = primary;
|
|
return mon;
|
|
}
|
|
|
|
/**
|
|
* Create a list of all available randr outputs
|
|
*/
|
|
vector<monitor_t> get_monitors(connection& conn, xcb_window_t root, bool connected_only, bool purge_clones) {
|
|
vector<monitor_t> monitors;
|
|
|
|
#if WITH_XRANDR_MONITORS
|
|
if (check_monitor_support()) {
|
|
for (auto&& mon : conn.get_monitors(root, true).monitors()) {
|
|
try {
|
|
auto name = conn.get_atom_name(mon.name).name();
|
|
monitors.emplace_back(make_monitor(XCB_NONE, move(name), mon.width, mon.height, mon.x, mon.y, mon.primary));
|
|
} catch (const exception&) {
|
|
// silently ignore output
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
auto primary_output = conn.get_output_primary(root).output();
|
|
string primary_name{};
|
|
|
|
if (primary_output != XCB_NONE) {
|
|
auto primary_info = conn.get_output_info(primary_output);
|
|
auto name_iter = primary_info.name();
|
|
primary_name = {name_iter.begin(), name_iter.end()};
|
|
}
|
|
|
|
for (auto&& output : conn.get_screen_resources(root).outputs()) {
|
|
try {
|
|
auto info = conn.get_output_info(output);
|
|
if (info->crtc == XCB_NONE) {
|
|
continue;
|
|
} else if (connected_only && info->connection != XCB_RANDR_CONNECTION_CONNECTED) {
|
|
continue;
|
|
}
|
|
|
|
auto name_iter = info.name();
|
|
string name{name_iter.begin(), name_iter.end()};
|
|
|
|
#if WITH_XRANDR_MONITORS
|
|
if (check_monitor_support()) {
|
|
auto mon = std::find_if(
|
|
monitors.begin(), monitors.end(), [&name](const monitor_t& mon) { return mon->name == name; });
|
|
if (mon != monitors.end()) {
|
|
(*mon)->output = output;
|
|
continue;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
auto crtc = conn.get_crtc_info(info->crtc);
|
|
auto primary = (primary_name == name);
|
|
monitors.emplace_back(make_monitor(output, move(name), crtc->width, crtc->height, crtc->x, crtc->y, primary));
|
|
} catch (const exception&) {
|
|
// silently ignore output
|
|
}
|
|
}
|
|
|
|
if(purge_clones) {
|
|
for (auto& outer : monitors) {
|
|
if (outer->w == 0) {
|
|
continue;
|
|
}
|
|
|
|
for (auto& inner : monitors) {
|
|
if (outer == inner || inner->w == 0) {
|
|
continue;
|
|
} else if (check_monitor_support() && (inner->output == XCB_NONE || outer->output == XCB_NONE)) {
|
|
continue;
|
|
}
|
|
|
|
// If inner is contained in outer, inner is removed
|
|
// If both happen to be the same size and have the same coordinates,
|
|
// inner is still removed but it doesn't matter since both occupy the
|
|
// exact same space
|
|
if (outer->contains(*inner)) {
|
|
// Reset width so that the output gets removed afterwards
|
|
inner->w = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Remove all cloned monitors (monitors with 0 width)
|
|
monitors.erase(
|
|
std::remove_if(monitors.begin(), monitors.end(),
|
|
[](const auto & monitor) { return monitor->w == 0; }),
|
|
monitors.end());
|
|
}
|
|
|
|
sort(monitors.begin(), monitors.end(), [](monitor_t& m1, monitor_t& m2) -> bool {
|
|
if (m1->x < m2->x || m1->y + m1->h <= m2->y) {
|
|
return true;
|
|
}
|
|
if (m1->x > m2->x || m1->y + m1->h > m2->y) {
|
|
return -1;
|
|
}
|
|
return false;
|
|
});
|
|
|
|
return monitors;
|
|
}
|
|
|
|
/**
|
|
* Searches for a monitor with name in monitors
|
|
*
|
|
* Does best-fit matching (if exact_match == true, this is also first-fit)
|
|
*/
|
|
monitor_t match_monitor(vector<monitor_t> monitors, const string& name, bool exact_match) {
|
|
monitor_t result{};
|
|
for(auto&& monitor : monitors) {
|
|
// If we can do an exact match, we have found our result
|
|
if(monitor->match(name, true)) {
|
|
result = move(monitor);
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Non-exact matches are moved into the result but we continue searching
|
|
* through the list, maybe we can find an exact match
|
|
* Note: If exact_match == true, we don't need to run this because it
|
|
* would be the exact same check as above
|
|
*/
|
|
if(!exact_match && monitor->match(name, false)) {
|
|
result = move(monitor);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* Get backlight value range for given output
|
|
*/
|
|
void get_backlight_range(connection& conn, const monitor_t& mon, backlight_values& dst) {
|
|
auto atom = Backlight;
|
|
auto reply = conn.query_output_property(mon->output, atom);
|
|
|
|
dst.min = 0;
|
|
dst.max = 0;
|
|
|
|
if (!reply->range || reply->length != 2) {
|
|
atom = BACKLIGHT;
|
|
reply = conn.query_output_property(mon->output, atom);
|
|
}
|
|
|
|
if (!reply->range || reply->length != 2) {
|
|
return;
|
|
}
|
|
|
|
auto range = reply.valid_values().begin();
|
|
dst.min = static_cast<double>(*range++);
|
|
dst.max = static_cast<double>(*range);
|
|
dst.atom = atom;
|
|
}
|
|
|
|
/**
|
|
* Get backlight value for given output
|
|
*/
|
|
void get_backlight_value(connection& conn, const monitor_t& mon, backlight_values& dst) {
|
|
dst.val = 0.0;
|
|
|
|
if (!dst.atom) {
|
|
return;
|
|
}
|
|
|
|
auto reply = conn.get_output_property(mon->output, dst.atom, XCB_ATOM_NONE, 0, 4, 0, 0);
|
|
if (reply->num_items == 1 && reply->format == 32 && reply->type == XCB_ATOM_INTEGER) {
|
|
int value = *reinterpret_cast<int*>(xcb_randr_get_output_property_data(reply.get().get()));
|
|
dst.val = static_cast<double>(value);
|
|
}
|
|
}
|
|
}
|
|
|
|
POLYBAR_NS_END
|