From 33b68ec7cb179d739765e8bd750e8b291b286e8a Mon Sep 17 00:00:00 2001
From: patrick96
Date: Tue, 25 Jun 2019 00:23:44 +0200
Subject: [PATCH] fix(randr): Undefined behavior when removing clones
Because of how monitors are removed inside the loop and depending on the
monitor order a cloned monitor may be assigned a width of 0 but is never
actually removed resulting in polybar saying the bar is out of bounds
Fixes #1794
---
include/x11/extensions/randr.hpp | 2 +
src/x11/extensions/randr.cpp | 63 +++++++++++++++++---------------
2 files changed, 36 insertions(+), 29 deletions(-)
diff --git a/include/x11/extensions/randr.hpp b/include/x11/extensions/randr.hpp
index 5a42b703..b53f395e 100644
--- a/include/x11/extensions/randr.hpp
+++ b/include/x11/extensions/randr.hpp
@@ -40,6 +40,8 @@ struct randr_output {
bool match(const string& o, bool exact = true) const;
bool match(const position& p) const;
+
+ bool contains(const randr_output& output) const;
};
using monitor_t = shared_ptr;
diff --git a/src/x11/extensions/randr.cpp b/src/x11/extensions/randr.cpp
index 64bde0b9..9127b4e5 100644
--- a/src/x11/extensions/randr.cpp
+++ b/src/x11/extensions/randr.cpp
@@ -32,6 +32,16 @@ bool randr_output::match(const position& p) const {
return p.x == x && p.y == y;
}
+/**
+ * Checks if inner is contained withing 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
@@ -135,40 +145,35 @@ namespace randr_util {
}
}
- // clang-format off
- const auto remove_monitor = [&](const monitor_t& monitor) {
- monitors.erase(find(monitors.begin(), monitors.end(), monitor));
- };
- // clang-format on
-
- for (auto m = monitors.rbegin(); m != monitors.rend(); m++) {
- if ((*m)->w == 0) {
- remove_monitor(*m);
- continue;
- }
-
- // Test if there are any clones in the set
-
- if (!purge_clones) {
- continue;
- }
-
- for (auto& monitor : monitors) {
- if ((*m) == monitor || monitor->w == 0) {
- continue;
- } else if (check_monitor_support() && (monitor->output == XCB_NONE || (*m)->output == XCB_NONE)) {
+ if(purge_clones) {
+ for (auto& outer : monitors) {
+ if (outer->w == 0) {
continue;
}
- // clang-format off
- if (monitor->x >= (*m)->x && monitor->x + monitor->w <= (*m)->x + (*m)->w &&
- monitor->y >= (*m)->y && monitor->y + monitor->h <= (*m)->y + (*m)->h) {
- // Reset width so that the output gets
- // removed in the base loop
- monitor->w = 0;
+ 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;
+ }
}
- // clang-format on
}
+
+ // 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 {