1 #include "panelmanager.h"
2
3 #include "settings.h"
4 #include "x11-types.h"
5 #include "xconnection.h"
6
7 using std::make_pair;
8 using std::string;
9 using std::vector;
10
11 class Panel {
12 public:
Panel(Window winid,PanelManager & pm)13 Panel(Window winid, PanelManager& pm) : winid_(winid), pm_(pm) {}
14 Window winid_;
15 PanelManager& pm_;
16 Rectangle size_;
17 vector<long> wmStrut_ = {};
18 using WmStrut = PanelManager::WmStrut;
19
wmStrut(WmStrut idx) const20 int wmStrut(WmStrut idx) const {
21 size_t i = static_cast<size_t>(idx);
22 if (i < wmStrut_.size()) {
23 return static_cast<int>(wmStrut_[i]);
24 } else if (idx == WmStrut::left_end_y
25 || idx == WmStrut::right_end_y)
26 {
27 return pm_.rootWindowGeometry_.height;
28 } else if (idx == WmStrut::top_end_x
29 || idx == WmStrut::bottom_end_x)
30 {
31 return pm_.rootWindowGeometry_.width;
32 } else {
33 return 0;
34 }
35 }
36
37 /** report the panels geometry based on WM_STRUT
38 * The returned rectangle is guaranteed to touch
39 * an edge of the root window rectangle.
40 */
wmStrutGeometry() const41 Rectangle wmStrutGeometry() const {
42 if (wmStrut(WmStrut::top) > 0) {
43 // align with top edge
44 return Rectangle::fromCorners(
45 wmStrut(WmStrut::top_start_x),
46 0,
47 wmStrut(WmStrut::top_end_x),
48 wmStrut(WmStrut::top));
49 }
50 if (wmStrut(WmStrut::bottom) > 0) {
51 // align with bottom edge
52 return Rectangle::fromCorners(
53 wmStrut(WmStrut::bottom_start_x),
54 pm_.rootWindowGeometry_.height - wmStrut(WmStrut::bottom),
55 wmStrut(WmStrut::bottom_end_x),
56 pm_.rootWindowGeometry_.height);
57 }
58 if (wmStrut(WmStrut::left) > 0) {
59 // align with left edge
60 return Rectangle::fromCorners(
61 0,
62 wmStrut(WmStrut::left_start_y),
63 wmStrut(WmStrut::left),
64 wmStrut(WmStrut::left_end_y));
65 }
66 if (wmStrut(WmStrut::right) > 0) {
67 // align with right edge
68 return Rectangle::fromCorners(
69 pm_.rootWindowGeometry_.width - wmStrut(WmStrut::right),
70 wmStrut(WmStrut::right_start_y),
71 pm_.rootWindowGeometry_.width,
72 wmStrut(WmStrut::right_end_y));
73 }
74 return {0, 0, 0, 0};
75 };
76 };
77
PanelManager(XConnection & xcon)78 PanelManager::PanelManager(XConnection& xcon)
79 : xcon_(xcon)
80 {
81 atomWmStrut_ = xcon_.atom("_NET_WM_STRUT");
82 atomWmStrutPartial_ = xcon_.atom("_NET_WM_STRUT_PARTIAL");
83 rootWindowGeometry_ = xcon_.windowSize(xcon_.root());
84 }
85
~PanelManager()86 PanelManager::~PanelManager()
87 {
88 for (auto it : panels_) {
89 delete it.second;
90 }
91 }
92
registerPanel(Window win)93 void PanelManager::registerPanel(Window win)
94 {
95 Panel* p = new Panel(win, *this);
96 panels_.insert(make_pair(win, p));
97 updateReservedSpace(p, xcon_.windowSize(win));
98 panels_changed_.emit();
99 }
100
unregisterPanel(Window win)101 void PanelManager::unregisterPanel(Window win)
102 {
103 auto it = panels_.find(win);
104 if (it == panels_.end()) {
105 return;
106 }
107 Panel* p = it->second;
108 panels_.erase(win);
109 delete p;
110 panels_changed_.emit();
111 }
112
propertyChanged(Window win,Atom property)113 void PanelManager::propertyChanged(Window win, Atom property)
114 {
115 if (property != atomWmStrut_ && property != atomWmStrutPartial_) {
116 return;
117 }
118 auto it = panels_.find(win);
119 if (it != panels_.end()) {
120 Panel* p = it->second;
121 if (updateReservedSpace(p, xcon_.windowSize(win))) {
122 panels_changed_.emit();
123 }
124 }
125 }
126
127 /**
128 * @brief the geometry of a window was changed, where window
129 * is possibly a panel window
130 * @param the window
131 * @param its new geometry
132 */
geometryChanged(Window win,Rectangle geometry)133 void PanelManager::geometryChanged(Window win, Rectangle geometry)
134 {
135 auto it = panels_.find(win);
136 if (it != panels_.end()) {
137 Panel* p = it->second;
138 if (updateReservedSpace(p, geometry)) {
139 panels_changed_.emit();
140 }
141 }
142 }
143
injectDependencies(Settings * settings)144 void PanelManager::injectDependencies(Settings* settings)
145 {
146 settings_ = settings;
147 settings_->auto_detect_panels.changed().connect([this]() {
148 panels_changed_.emit();
149 });
150 }
151
152 /**
153 * read the reserved space from the panel window and return if there are changes
154 * - size is the geometry of the panel
155 */
updateReservedSpace(Panel * p,Rectangle size)156 bool PanelManager::updateReservedSpace(Panel* p, Rectangle size)
157 {
158 auto optionalWmStrut = xcon_.getWindowPropertyCardinal(p->winid_, atomWmStrutPartial_);
159 if (!optionalWmStrut) {
160 optionalWmStrut= xcon_.getWindowPropertyCardinal(p->winid_, atomWmStrut_);
161 }
162 vector<long> wmStrut = optionalWmStrut.value_or(vector<long>());
163 if (p->wmStrut_ != wmStrut || p->size_ != size) {
164 p->wmStrut_ = wmStrut;
165 p->size_ = size;
166 return true;
167 }
168 return false;
169 }
170
171
172 //! given the dimension of a monitor, return the space reserved for panels
computeReservedSpace(Rectangle mon)173 PanelManager::ReservedSpace PanelManager::computeReservedSpace(Rectangle mon)
174 {
175 ReservedSpace rsTotal;
176 if (!settings_->auto_detect_panels()) {
177 return rsTotal;
178 }
179 for (auto it : panels_) {
180 Panel& p = *(it.second);
181 ReservedSpace rs;
182 Rectangle panelArea = p.wmStrutGeometry();
183 if (!panelArea) {
184 // if the panel does not define WmStrut,
185 // then take it's window geometry
186 panelArea = p.size_;
187 }
188 Rectangle intersection = mon.intersectionWith(panelArea);
189 if (!intersection) {
190 // monitor does not intersect with panel at all
191 continue;
192 }
193 // we only reserve space for the panel if the panel defines
194 // wmStrut_ or if the aspect ratio clearly indicates whether the
195 // panel is horizontal or vertical
196 bool verticalPanel = p.wmStrut(WmStrut::left) > 0 || p.wmStrut(WmStrut::right) > 0;
197 bool horizontalPanel = p.wmStrut(WmStrut::top) > 0 || p.wmStrut(WmStrut::bottom) > 0;
198 if (p.wmStrut_.empty()) {
199 // only fall back to aspect ratio if wmStrut is undefined
200 verticalPanel = intersection.height > intersection.width;
201 horizontalPanel = intersection.height < intersection.width;
202 }
203 if (verticalPanel) {
204 // don't affect the monitor, if the intersection spans
205 // the entire monitor width.
206 if (intersection.x == mon.x && intersection.width < mon.width) {
207 rs.left_ = intersection.width;
208 }
209 if (intersection.br().x == mon.br().x && intersection.width < mon.width) {
210 rs.right_ = intersection.width;
211 }
212 }
213 if (horizontalPanel) {
214 // don't affect the monitor, if the intersection spans
215 // the entire monitor height.
216 if (intersection.y == mon.y && intersection.height < mon.height) {
217 rs.top_ = intersection.height;
218 }
219 if (intersection.br().y == mon.br().y && intersection.height < mon.height) {
220 rs.bottom_ = intersection.height;
221 }
222 }
223 for (size_t i = 0; i < 4; i++) {
224 rsTotal[i] = std::max(rsTotal[i], rs[i]);
225 }
226 }
227 return rsTotal;
228 }
229
rootWindowChanged(int width,int height)230 void PanelManager::rootWindowChanged(int width, int height)
231 {
232 rootWindowGeometry_.width = width;
233 rootWindowGeometry_.height = height;
234 }
235
operator [](size_t idx)236 int& PanelManager::ReservedSpace::operator[](size_t idx)
237 {
238 vector<int*> v = { &left_, &right_, &top_, &bottom_ };
239 return *(v[idx]);
240 }
241