1 #include "settings.h"
2
3 #include <sstream>
4
5 #include "client.h"
6 #include "completion.h"
7 #include "ewmh.h"
8 #include "framedata.h"
9 #include "ipc-protocol.h"
10 #include "monitormanager.h"
11 #include "root.h"
12 #include "utils.h"
13
14 using std::endl;
15 using std::function;
16 using std::string;
17 using std::to_string;
18
19 Settings* g_settings = nullptr; // the global settings object
20
Settings()21 Settings::Settings()
22 : window_border_width("window_border_width",
23 getIntAttr("theme.tiling.active.border_width"),
24 setIntAttr("theme.border_width"))
25 , window_border_inner_width("window_border_inner_width",
26 getIntAttr("theme.tiling.active.inner_width"),
27 setIntAttr("theme.inner_width"))
28 , window_border_inner_color("window_border_inner_color",
29 getColorAttr("theme.tiling.active.inner_color"),
30 setColorAttr("theme.inner_color"))
31 , window_border_active_color("window_border_active_color",
32 getColorAttr("theme.tiling.active.color"),
33 setColorAttr("theme.active.color"))
34 , window_border_normal_color("window_border_normal_color",
35 getColorAttr("theme.tiling.normal.color"),
36 setColorAttr("theme.normal.color"))
37 , window_border_urgent_color("window_border_urgent_color",
38 getColorAttr("theme.tiling.urgent.color"),
39 setColorAttr("theme.urgent.color"))
40 {
41 verbose = g_verbose > 0;
42 verbose.changed().connect([](bool newVal) { g_verbose = newVal; });
43 wireAttributes({
44 &verbose,
45 &frame_gap,
46 &frame_padding,
47 &window_gap,
48 &snap_distance,
49 &snap_gap,
50 &mouse_recenter_gap,
51 &frame_border_active_color,
52 &frame_border_normal_color,
53 &frame_border_inner_color,
54 &frame_bg_normal_color,
55 &frame_bg_active_color,
56 &frame_bg_transparent,
57 &frame_transparent_width,
58 &frame_border_width,
59 &frame_border_inner_width,
60 &frame_active_opacity,
61 &frame_normal_opacity,
62 &focus_crosses_monitor_boundaries,
63 &always_show_frame,
64 &default_direction_external_only,
65 &default_frame_layout,
66 &focus_follows_mouse,
67 &focus_stealing_prevention,
68 &swap_monitors_to_get_tag,
69 &raise_on_focus,
70 &raise_on_focus_temporarily,
71 &raise_on_click,
72 &gapless_grid,
73 &hide_covered_windows,
74 &smart_frame_surroundings,
75 &smart_window_surroundings,
76 &monitors_locked,
77 &auto_detect_monitors,
78 &auto_detect_panels,
79 &pseudotile_center_threshold,
80 &update_dragged_clients,
81 &tree_style,
82 &wmname,
83
84 &window_border_width,
85 &window_border_inner_width,
86 &window_border_inner_color,
87 &window_border_active_color,
88 &window_border_normal_color,
89 &window_border_urgent_color,
90 });
91 for (auto i : {&frame_gap, &frame_padding, &window_gap}) {
92 i->changed().connect([] { all_monitors_apply_layout(); });
93 }
94 hide_covered_windows.changed().connect([] { all_monitors_apply_layout(); });
95 for (auto i : {
96 &frame_border_active_color,
97 &frame_border_normal_color,
98 &frame_border_inner_color,
99 &frame_bg_normal_color,
100 &frame_bg_active_color}) {
101 i->changed().connect(&reset_client_colors);
102 }
103 frame_bg_transparent.changed().connect(&reset_client_colors);
104 for (auto i : {&frame_transparent_width,
105 &frame_border_width,
106 &frame_border_inner_width,
107 &frame_active_opacity,
108 &frame_normal_opacity}) {
109 i->changed().connect(&reset_client_colors);
110 }
111 frame_bg_transparent.setWritable();
112 for (auto i : {&always_show_frame,
113 &gapless_grid,
114 &smart_frame_surroundings,
115 &smart_window_surroundings,
116 &raise_on_focus_temporarily}) {
117 i->changed().connect(&all_monitors_apply_layout);
118 }
119 wmname.changed().connect([]() { Ewmh::get().updateWmName(); });
120
121 default_frame_layout.setValidator([] (size_t layout) {
122 if (layout >= layoutAlgorithmCount()) {
123 return "layout number must be at most "
124 + to_string(layoutAlgorithmCount() - 1);
125 }
126 return string();
127 });
128 tree_style.setValidator([] (string new_value) {
129 if (utf8_string_length(new_value) < 8) {
130 return string("tree_style needs 8 characters");
131 }
132 return string();
133 });
134 g_settings = this;
135 for (auto i : attributes()) {
136 i.second->setWritable();
137 }
138 setDoc(
139 "This has an attribute for each setting. Many settings are "
140 "wrappers around attributes and only exist for compatibility."
141 );
142 }
143
injectDependencies(Root * root)144 void Settings::injectDependencies(Root* root) {
145 root_ = root;
146 // TODO: the lock level is not a setting! should move somewhere else
147 monitors_locked = root->globals.initial_monitors_locked;
148 monitors_locked.changed().connect([root](bool) {
149 root->monitors()->lock_number_changed();
150 });
151 }
152
getIntAttr(string name)153 function<int()> Settings::getIntAttr(string name) {
154 return [this, name]() {
155 Attribute* a = this->root_->deepAttribute(name);
156 if (a) {
157 try {
158 return std::stoi(a->str());
159 } catch (...) {
160 return 0;
161 }
162 } else {
163 HSDebug("Internal Error: No such attribute %s\n", name.c_str());
164 return 0;
165 }
166 };
167 }
168
getColorAttr(string name)169 function<Color()> Settings::getColorAttr(string name) {
170 return [this, name]() {
171 Attribute* a = this->root_->deepAttribute(name);
172 if (a) {
173 return Color(a->str());
174 } else {
175 HSDebug("Internal Error: No such attribute %s\n", name.c_str());
176 return Color("black");
177 }
178 };
179 }
180
setIntAttr(string name)181 function<string(int)> Settings::setIntAttr(string name) {
182 return [this, name](int val) {
183 Attribute* a = this->root_->deepAttribute(name);
184 if (a) {
185 return a->change(to_string(val));
186 } else {
187 string msg = "Internal Error: No such attribute ";
188 msg += name;
189 msg += "\"";
190 return msg;
191 }
192 };
193 }
setColorAttr(string name)194 function<string(Color)> Settings::setColorAttr(string name) {
195 return [this, name](Color val) {
196 Attribute* a = this->root_->deepAttribute(name);
197 if (a) {
198 return a->change(val.str());
199 } else {
200 string msg = "Internal Error: No such attribute ";
201 msg += name;
202 msg += "\"";
203 return msg;
204 }
205 };
206 }
207
set_cmd(Input input,Output output)208 int Settings::set_cmd(Input input, Output output) {
209 string set_name, value;
210 if (!(input >> set_name >> value)) {
211 return HERBST_NEED_MORE_ARGS;
212 }
213
214 auto attr = attribute(set_name);
215 if (!attr) {
216 output << input.command() <<
217 ": Setting \"" << set_name << "\" not found\n";
218 return HERBST_SETTING_NOT_FOUND;
219 }
220 auto msg = attr->change(value);
221 if (!msg.empty()) {
222 output << input.command()
223 << ": Invalid value \"" << value
224 << "\" for setting \"" << set_name << "\": "
225 << msg << endl;
226 return HERBST_INVALID_ARGUMENT;
227 }
228 return 0;
229 }
230
set_complete(Completion & complete)231 void Settings::set_complete(Completion& complete) {
232 if (complete == 0) {
233 for (auto& a : attributes()) {
234 complete.full(a.first);
235 }
236 } else if (complete == 1) {
237 Attribute* a = attribute(complete[0]);
238 if (a) {
239 a->complete(complete);
240 }
241 } else {
242 complete.none();
243 }
244 }
245
toggle_cmd(Input argv,Output output)246 int Settings::toggle_cmd(Input argv, Output output) {
247 if (argv.empty()) {
248 return HERBST_NEED_MORE_ARGS;
249 }
250 auto set_name = argv.front();
251 auto attr = attribute(set_name);
252 if (!attr) {
253 output << argv.command() <<
254 ": Setting \"" << set_name << "\" not found\n";
255 return HERBST_SETTING_NOT_FOUND;
256 }
257 if (attr->type() == Type::BOOL) {
258 attr->change("toggle");
259 } else {
260 output << argv.command()
261 << ": Setting \"" << set_name
262 << "\" is not of type bool\n";
263 return HERBST_INVALID_ARGUMENT;
264 }
265 return 0;
266 }
267
toggle_complete(Completion & complete)268 void Settings::toggle_complete(Completion& complete) {
269 if (complete == 0) {
270 for (auto a : attributes()) {
271 if (a.second->type() == Type::BOOL) {
272 complete.full(a.first);
273 }
274 }
275 } else {
276 complete.none();
277 }
278 }
279
cycle_value_cmd(Input argv,Output output)280 int Settings::cycle_value_cmd(Input argv, Output output) {
281 if (argv.empty()) {
282 return HERBST_NEED_MORE_ARGS;
283 }
284 auto set_name = argv.front();
285 argv.shift();
286 if (argv.empty()) {
287 return HERBST_NEED_MORE_ARGS;
288 }
289 auto attr = attribute(set_name);
290 if (!attr) {
291 output << argv.command() <<
292 ": Setting \"" << set_name << "\" not found\n";
293 return HERBST_SETTING_NOT_FOUND;
294 }
295 auto msg = attr->cycleValue(argv.begin(), argv.end());
296 if (!msg.empty()) {
297 output << argv.command()
298 << ": Invalid value for setting \""
299 << set_name << "\": "
300 << msg << endl;
301 return HERBST_INVALID_ARGUMENT;
302 }
303 return 0;
304 }
305
cycle_value_complete(Completion & complete)306 void Settings::cycle_value_complete(Completion& complete) {
307 if (complete == 0) {
308 for (auto a : attributes()) {
309 complete.full(a.first);
310 }
311 } else {
312 Attribute* a = attribute(complete[0]);
313 if (a) {
314 a->complete(complete);
315 }
316 }
317 }
318
get_cmd(Input argv,Output output)319 int Settings::get_cmd(Input argv, Output output) {
320 if (argv.empty()) {
321 return HERBST_NEED_MORE_ARGS;
322 }
323 auto attr = attribute(argv.front());
324 if (!attr) {
325 output << argv.command() <<
326 ": Setting \"" << argv.front() << "\" not found\n";
327 return HERBST_SETTING_NOT_FOUND;
328 }
329 output << attr->str();
330 return 0;
331 }
332
get_complete(Completion & complete)333 void Settings::get_complete(Completion& complete) {
334 if (complete == 0) {
335 for (auto& a : attributes()) {
336 complete.full(a.first);
337 }
338 } else if (complete == 1) {
339 Attribute* a = attribute(complete[0]);
340 if (a) {
341 a->complete(complete);
342 }
343 } else {
344 complete.none();
345 }
346 }
347
348
349
350