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