1 #include <X11/X.h>
2 #include <X11/Xlib.h>
3 #include <getopt.h>
4 #include <locale.h>
5 #include <sys/wait.h>
6 #include <unistd.h>
7 #include <csignal>
8 #include <cstdio>
9 #include <cstdlib>
10 #include <iostream>
11 #include <vector>
12 
13 #include "client.h"
14 #include "clientmanager.h"
15 #include "command.h"
16 #include "commandio.h"
17 #include "ewmh.h"
18 #include "fontdata.h"
19 #include "frametree.h"
20 #include "globals.h"
21 #include "hook.h"
22 #include "ipc-protocol.h"
23 #include "ipc-server.h"
24 #include "keymanager.h"
25 #include "layout.h"
26 #include "metacommands.h"
27 #include "monitordetection.h"
28 #include "monitormanager.h"
29 #include "mousemanager.h"
30 #include "rectangle.h"
31 #include "root.h"
32 #include "rulemanager.h"
33 #include "settings.h"
34 #include "tagmanager.h"
35 #include "tmp.h"
36 #include "utils.h"
37 #include "watchers.h"
38 #include "xconnection.h"
39 #include "xmainloop.h"
40 
41 using std::endl;
42 using std::make_shared;
43 using std::pair;
44 using std::shared_ptr;
45 using std::string;
46 using std::unique_ptr;
47 
48 // globals:
49 int g_verbose = 0;
50 Display*    g_display;
51 int         g_screen;
52 Window      g_root;
53 
54 // module internals:
55 static char*    g_autostart_path = nullptr; // if not set, then find it in $HOME or $XDG_CONFIG_HOME
56 static bool     g_exec_before_quit = false;
57 static char**   g_exec_args = nullptr;
58 static XMainLoop* g_main_loop = nullptr;
59 
60 int quit();
61 int version(Output output);
62 int print_tag_status_command(int argc, char** argv, Output output);
63 void execute_autostart_file();
64 int raise_command(int argc, char** argv, Output output);
65 int spawn(int argc, char** argv);
66 int wmexec(int argc, char** argv);
67 static void remove_zombies(int signal);
68 int custom_hook_emit(Input input);
69 int jumpto_command(int argc, char** argv, Output output);
70 
commands(shared_ptr<Root> root)71 unique_ptr<CommandTable> commands(shared_ptr<Root> root) {
72     MetaCommands* meta_commands = root->meta_commands.get();
73 
74     ClientManager* clients = root->clients();
75     KeyManager *keys = root->keys();
76     MonitorManager* monitors = root->monitors();
77     MouseManager* mouse = root->mouse();
78     RuleManager* rules = root->rules();
79     Settings* settings = root->settings();
80     TagManager* tags = root->tags();
81     Tmp* tmp = root->tmp();
82     Watchers* watchers = root->watchers();
83 
84     std::initializer_list<pair<const string,CommandBinding>> init =
85     {
86         {"quit",           { quit } },
87         {"echo",           {meta_commands, &MetaCommands::echoCommand,
88                                            &MetaCommands::echoCompletion }},
89         {"true",           {[] { return 0; }}},
90         {"false",          {[] { return 1; }}},
91         {"try",            {meta_commands, &MetaCommands::tryCommand,
92                                            &MetaCommands::completeCommandShifted1}},
93         {"silent",         {meta_commands, &MetaCommands::silentCommand,
94                                            &MetaCommands::completeCommandShifted1}},
95         {"reload",         {[] { execute_autostart_file(); return 0; }}},
96         {"version",        { version }},
97         {"list_commands",  { list_commands }},
98         {"list_monitors",  {monitors, &MonitorManager::list_monitors }},
99         {"set_monitors",   {monitors, &MonitorManager::setMonitorsCommand,
100                                       &MonitorManager::setMonitorsCompletion} },
101         {"disjoin_rects",  disjoin_rects_command},
102         {"list_keybinds",  {keys, &KeyManager::listKeybindsCommand }},
103         {"list_padding",   monitors->byFirstArg(&Monitor::list_padding, &Monitor::noComplete) },
104         {"keybind",        {keys, &KeyManager::addKeybindCommand,
105                                   &KeyManager::addKeybindCompletion}},
106         {"keyunbind",      {keys, &KeyManager::removeKeybindCommand,
107                                   &KeyManager::removeKeybindCompletion}},
108         {"mousebind",      {mouse, &MouseManager::addMouseBindCommand,
109                                    &MouseManager::addMouseBindCompletion}},
110         {"mouseunbind",    {mouse, &MouseManager::mouse_unbind_all }},
111         {"drag",           {mouse, &MouseManager::dragCommand,
112                                    &MouseManager::dragCompletion}},
113         {"spawn",          spawn},
114         {"wmexec",         wmexec},
115         {"emit_hook",      { custom_hook_emit }},
116         {"bring",          frame_current_bring},
117         {"focus_nth",      { tags->frameCommand(&FrameTree::focusNthCommand) }},
118         {"cycle",          { tags->frameCommand(&FrameTree::cycleSelectionCommand) }},
119         {"cycle_all",      monitors->tagCommand(&HSTag::cycleAllCommand,
120                                                 &HSTag::cycleAllCompletion)},
121         {"cycle_layout",   tags->frameCommand(&FrameTree::cycleLayoutCommand, &FrameTree::cycleLayoutCompletion) },
122         {"cycle_frame",    { tags->frameCommand(&FrameTree::cycleFrameCommand) }},
123         {"close",          { close_command }},
124         {"close_or_remove",{ monitors->tagCommand(&HSTag::closeOrRemoveCommand) }},
125         {"close_and_remove",{ monitors->tagCommand(&HSTag::closeAndRemoveCommand) }},
126         {"split",          { tags->frameCommand(&FrameTree::splitCommand) }},
127         {"resize",         monitors->tagCommand(&HSTag::resizeCommand,
128                                                 &HSTag::resizeCompletion)},
129         {"focus_edge",     frame_focus_edge},
130         {"focus",          monitors->tagCommand(&HSTag::focusInDirCommand,
131                                                 &HSTag::focusInDirCompletion)},
132         {"shift_edge",     frame_move_window_edge},
133         {"shift",          monitors->tagCommand(&HSTag::shiftInDirCommand,
134                                                 &HSTag::shiftInDirCompletion)},
135         {"shift_to_monitor",shift_to_monitor},
136         {"remove",         { tags->frameCommand(&FrameTree::removeFrameCommand) }},
137         {"set",            { settings, &Settings::set_cmd,
138                                        &Settings::set_complete }},
139         {"get",            { settings, &Settings::get_cmd,
140                                        &Settings::get_complete }},
141         {"toggle",         { settings, &Settings::toggle_cmd,
142                                        &Settings::toggle_complete}},
143         {"cycle_value",    { settings, &Settings::cycle_value_cmd,
144                                        &Settings::cycle_value_complete}},
145         {"cycle_monitor",  monitor_cycle_command},
146         {"focus_monitor",  monitor_focus_command},
147         {"add",            BIND_OBJECT(tags, tag_add_command) },
148         {"use",            monitor_set_tag_command},
149         {"use_index",      monitor_set_tag_by_index_command},
150         {"use_previous",   { monitor_set_previous_tag_command }},
151         {"jumpto",         jumpto_command},
152         {"floating",       { tags, &TagManager::floatingCmd,
153                                    &TagManager::floatingComplete }},
154         {"fullscreen",     {clients, &ClientManager::fullscreen_cmd,
155                                      &ClientManager::fullscreen_complete}},
156         {"pseudotile",     {clients, &ClientManager::pseudotile_cmd,
157                                      &ClientManager::pseudotile_complete}},
158         {"tag_status",     print_tag_status_command},
159         {"merge_tag",      BIND_OBJECT(tags, removeTag)},
160         {"rename",         BIND_OBJECT(tags, tag_rename_command) },
161         {"move",           BIND_OBJECT(tags, tag_move_window_command) },
162         {"rotate",         { tags->frameCommand(&FrameTree::rotateCommand) }},
163         {"mirror",         { tags->frameCommand(&FrameTree::mirrorCommand, &FrameTree::mirrorCompletion) }},
164         {"move_index",     BIND_OBJECT(tags, tag_move_window_by_index_command) },
165         {"add_monitor",    BIND_OBJECT(monitors, addMonitor)},
166         {"raise_monitor",  { monitors, &MonitorManager::raiseMonitorCommand,
167                                        &MonitorManager::raiseMonitorCompletion }},
168         {"remove_monitor", BIND_OBJECT(monitors, removeMonitor)},
169         {"move_monitor",   monitors->byFirstArg(&Monitor::move_cmd, &Monitor::move_complete) } ,
170         {"rename_monitor", monitors->byFirstArg(&Monitor::renameCommand, &Monitor::renameComplete) },
171         {"monitor_rect",   monitor_rect_command},
172         {"pad",            monitor_set_pad_command},
173         {"raise",          raise_command},
174         {"rule",           {rules, &RuleManager::addRuleCommand,
175                                    &RuleManager::addRuleCompletion}},
176         {"unrule",         {rules, &RuleManager::unruleCommand,
177                                    &RuleManager::unruleCompletion}},
178         {"apply_rules",    {clients, &ClientManager::applyRulesCmd,
179                                      &ClientManager::applyRulesCompletion}},
180         {"apply_tmp_rule", {clients, &ClientManager::applyTmpRuleCmd,
181                                      &ClientManager::applyTmpRuleCompletion}},
182         {"list_rules",     {rules, &RuleManager::listRulesCommand }},
183         {"layout",         tags->frameCommand(&FrameTree::dumpLayoutCommand, &FrameTree::dumpLayoutCompletion)},
184         {"stack",          { monitors, &MonitorManager::stackCommand }},
185         {"dump",           tags->frameCommand(&FrameTree::dumpLayoutCommand, &FrameTree::dumpLayoutCompletion)},
186         {"load",           { tags->frameCommand(&FrameTree::loadCommand) }},
187         {"complete",       complete_command},
188         {"complete_shell", complete_command},
189         {"lock",           { [monitors] { monitors->lock(); return 0; } }},
190         {"unlock",         { [monitors] { monitors->unlock(); return 0; } }},
191         {"lock_tag",       monitors->byFirstArg(&Monitor::lock_tag_cmd, &Monitor::noComplete) },
192         {"unlock_tag",     monitors->byFirstArg(&Monitor::unlock_tag_cmd, &Monitor::noComplete) },
193         {"set_layout",     { tags->frameCommand(&FrameTree::setLayoutCommand, &FrameTree::setLayoutCompletion) }},
194         {"detect_monitors",{ monitors, &MonitorManager::detectMonitorsCommand,
195                                        &MonitorManager::detectMonitorsCompletion }},
196         {"!",              { meta_commands, &MetaCommands::negateCommand,
197                                             &MetaCommands::completeCommandShifted1 }},
198         {"chain",          { meta_commands, &MetaCommands::chainCommand,
199                                             &MetaCommands::chainCompletion}},
200         {"and",            { meta_commands, &MetaCommands::chainCommand,
201                                             &MetaCommands::chainCompletion}},
202         {"or",             { meta_commands, &MetaCommands::chainCommand,
203                                             &MetaCommands::chainCompletion}},
204         {"object_tree",    { meta_commands, &MetaCommands::print_object_tree_command,
205                                             &MetaCommands::print_object_tree_complete} },
206         {"substitute",     { meta_commands, &MetaCommands::substitute_cmd,
207                                             &MetaCommands::substitute_complete} },
208         {"foreach",        { meta_commands, &MetaCommands::foreachCmd,
209                                             &MetaCommands::foreachComplete} },
210         {"sprintf",        { meta_commands, &MetaCommands::sprintf_cmd,
211                                             &MetaCommands::sprintf_complete} },
212         {"new_attr",       { meta_commands, &MetaCommands::new_attr_cmd,
213                                             &MetaCommands::new_attr_complete} },
214         {"remove_attr",    { meta_commands, &MetaCommands::remove_attr_cmd,
215                                             &MetaCommands::remove_attr_complete }},
216         {"compare",        { meta_commands, &MetaCommands::compare_cmd,
217                                             &MetaCommands::compare_complete} },
218         {"getenv",         { meta_commands, &MetaCommands::getenvCommand,
219                                             &MetaCommands::getenvUnsetenvCompletion}},
220         {"setenv",         { meta_commands, &MetaCommands::setenvCommand,
221                                             &MetaCommands::setenvCompletion}},
222         {"export",         { meta_commands, &MetaCommands::exportEnvCommand,
223                                             &MetaCommands::exportEnvCompletion}},
224         {"unsetenv",       { meta_commands, &MetaCommands::unsetenvCommand,
225                                             &MetaCommands::getenvUnsetenvCompletion}},
226         {"get_attr",       { meta_commands, &MetaCommands::get_attr_cmd,
227                                             &MetaCommands::get_attr_complete }},
228         {"set_attr",       { meta_commands, &MetaCommands::set_attr_cmd,
229                                             &MetaCommands::set_attr_complete }},
230         {"help",           { meta_commands, &MetaCommands::helpCommand,
231                                             &MetaCommands::helpCompletion }},
232         {"attr",           { meta_commands, &MetaCommands::attr_cmd,
233                                             &MetaCommands::attr_complete }},
234         {"watch",          { watchers, &Watchers::watchCommand,
235                                        &Watchers::watchCompletion }},
236         {"mktemp",         { tmp, &Tmp::mktemp,
237                                   &Tmp::mktempComplete }},
238     };
239     return unique_ptr<CommandTable>(new CommandTable(init));
240 }
241 
242 // core functions
quit()243 int quit() {
244     if (g_main_loop) {
245         g_main_loop->quit();
246     }
247     return 0;
248 }
249 
version(Output output)250 int version(Output output) {
251     output << WINDOW_MANAGER_NAME << " " << HERBSTLUFT_VERSION << endl;
252     output << "Copyright (c) 2011-2021 Thorsten Wißmann" << endl;
253     output << "Released under the Simplified BSD License" << endl;
254     for (const auto& d : MonitorDetection::detectors()) {
255         output << d.name_ << " support: " << (d.detect_ ? "on" : "off") << endl;
256     }
257     return 0;
258 }
259 
print_tag_status_command(int argc,char ** argv,Output output)260 int print_tag_status_command(int argc, char** argv, Output output) {
261     Monitor* monitor;
262     if (argc >= 2) {
263         monitor = string_to_monitor(argv[1]);
264     } else {
265         monitor = get_current_monitor();
266     }
267     if (!monitor) {
268         output << argv[0] << ": Monitor \"" << argv[1] << "\" not found!\n";
269         return HERBST_INVALID_ARGUMENT;
270     }
271     tag_update_flags();
272     output << '\t';
273     for (int i = 0; i < tag_get_count(); i++) {
274         HSTag* tag = get_tag_by_index(i);
275         // print flags
276         char c = '.';
277         if (tag->flags & TAG_FLAG_USED) {
278             c = ':';
279         }
280         Monitor *tag_monitor = find_monitor_with_tag(tag);
281         if (tag_monitor == monitor) {
282             c = '+';
283             if (monitor == get_current_monitor()) {
284                 c = '#';
285             }
286         } else if (tag_monitor) {
287             c = '-';
288             if (get_current_monitor() == tag_monitor) {
289                 c = '%';
290             }
291         }
292         if (tag->flags & TAG_FLAG_URGENT) {
293             c = '!';
294         }
295         output << c;
296         output << *tag->name;
297         output << '\t';
298     }
299     return 0;
300 }
301 
custom_hook_emit(Input input)302 int custom_hook_emit(Input input) {
303     hook_emit(input.toVector());
304     return 0;
305 }
306 
execvp_helper(char * const command[])307 static void execvp_helper(char *const command[]) {
308     execvp(command[0], command);
309     std::cerr << "herbstluftwm: execvp \"" << command << "\"";
310     perror(" failed");
311 }
312 
313 // spawn() heavily inspired by dwm.c
spawn(int argc,char ** argv)314 int spawn(int argc, char** argv) {
315     if (argc < 2) {
316         return HERBST_NEED_MORE_ARGS;
317     }
318     if (fork() == 0) {
319         // only look in child
320         if (g_display) {
321             close(ConnectionNumber(g_display));
322         }
323         // shift all args in argv by 1 to the front
324         // so that we have space for a NULL entry at the end for execvp
325         char** execargs = argv_duplicate(argc, argv);
326         free(execargs[0]);
327         int i;
328         for (i = 0; i < argc-1; i++) {
329             execargs[i] = execargs[i+1];
330         }
331         execargs[i] = nullptr;
332         // do actual exec
333         setsid();
334         execvp_helper(execargs);
335         exit(0);
336     }
337     return 0;
338 }
339 
wmexec(int argc,char ** argv)340 int wmexec(int argc, char** argv) {
341     if (argc >= 2) {
342         // shift all args in argv by 1 to the front
343         // so that we have space for a NULL entry at the end for execvp
344         char** execargs = argv_duplicate(argc, argv);
345         free(execargs[0]);
346         int i;
347         for (i = 0; i < argc-1; i++) {
348             execargs[i] = execargs[i+1];
349         }
350         execargs[i] = nullptr;
351         // quit and exec to new window manger
352         g_exec_args = execargs;
353     } else {
354         // exec into same command
355         g_exec_args = nullptr;
356     }
357     g_exec_before_quit = true;
358     g_main_loop->quit();
359     return EXIT_SUCCESS;
360 }
361 
raise_command(int argc,char ** argv,Output output)362 int raise_command(int argc, char** argv, Output output) {
363     if (argc < 2) {
364         return HERBST_NEED_MORE_ARGS;
365     }
366     auto client = get_client(argv[1]);
367     if (client) {
368         client->raise();
369         client->needsRelayout.emit(client->tag());
370     } else {
371         auto window = get_window(argv[1]);
372         if (window) {
373             XRaiseWindow(g_display, window);
374         } else {
375             output << argv[0] << ": Could not find client \"" << argv[1] << "\".\n";
376             return HERBST_INVALID_ARGUMENT;
377         }
378     }
379     return 0;
380 }
381 
jumpto_command(int argc,char ** argv,Output output)382 int jumpto_command(int argc, char** argv, Output output) {
383     if (argc < 2) {
384         return HERBST_NEED_MORE_ARGS;
385     }
386     auto client = get_client(argv[1]);
387     if (client) {
388         focus_client(client, true, true, true);
389         return 0;
390     } else {
391         output << argv[0] << ": Could not find client \"" << argv[1] << "\".\n";
392         return HERBST_INVALID_ARGUMENT;
393     }
394 }
395 
execute_autostart_file()396 void execute_autostart_file() {
397     string path;
398     if (g_autostart_path) {
399         path = g_autostart_path;
400     } else {
401         // find right directory
402         char* xdg_config_home = getenv("XDG_CONFIG_HOME");
403         if (xdg_config_home) {
404             path = xdg_config_home;
405         } else {
406             char* home = getenv("HOME");
407             if (!home) {
408                 HSWarning("Will not run autostart file. "
409                           "Neither $HOME or $XDG_CONFIG_HOME is set.\n");
410                 return;
411             }
412             path = string(home) + "/.config";
413         }
414         path += "/" HERBSTLUFT_AUTOSTART;
415     }
416     if (0 == fork()) {
417         if (g_display) {
418             close(ConnectionNumber(g_display));
419         }
420         setsid();
421         execl(path.c_str(), path.c_str(), nullptr);
422 
423         const char* global_autostart = HERBSTLUFT_GLOBAL_AUTOSTART;
424         HSDebug("Cannot execute %s, falling back to %s\n", path.c_str(), global_autostart);
425         execl(global_autostart, global_autostart, nullptr);
426 
427         fprintf(stderr, "herbstluftwm: execvp \"%s\"", global_autostart);
428         perror(" failed");
429         exit(EXIT_FAILURE);
430     }
431 }
432 
parse_arguments(int argc,char ** argv,Globals & g)433 static void parse_arguments(int argc, char** argv, Globals& g) {
434     int exit_on_xerror = g.exitOnXlibError;
435     int no_tag_import = 0;
436     struct option long_options[] = {
437         {"version",         0, nullptr, 'v'},
438         {"help",            0, nullptr, 'h'},
439         {"autostart",       1, nullptr, 'c'},
440         {"locked",          0, nullptr, 'l'},
441         {"exit-on-xerror",  0, &exit_on_xerror, 1},
442         {"no-tag-import",   0, &no_tag_import, 1},
443         {"verbose",         0, &g_verbose, 1},
444         {}
445     };
446     // parse options
447     while (true) {
448         int option_index = 0;
449         int c = getopt_long(argc, argv, "+c:vlh", long_options, &option_index);
450         if (c == -1) {
451             break;
452         }
453         switch (c) {
454             case 0:
455                 /* ignore recognized long option */
456                 break;
457             case 'v':
458                 version(std::cout);
459                 exit(0);
460             case 'c':
461                 g_autostart_path = optarg;
462                 break;
463             case 'l':
464                 g.initial_monitors_locked = 1;
465                 break;
466             case 'h':
467                 std::cout << "This starts the herbstluftwm window manager. In order to" << endl;
468                 std::cout << "interact with a running herbstluftwm instance, use the" << endl;
469                 std::cout << "\'herbstclient\' command." << endl;
470                 std::cout << endl;
471                 std::cout << "The herbstluftwm command accepts the following options:" << endl;
472                 std::cout << endl;
473                 for (size_t i = 0; long_options[i].name; i++) {
474                     std::cout << "    ";
475                     if (long_options[i].val != 1) {
476                         std::cout << "-" << static_cast<char>(long_options[i].val);
477                         if (long_options[i].has_arg) {
478                             std::cout << " ARG";
479                         }
480                         std::cout << ", ";
481                     }
482                     std::cout << "--" << long_options[i].name;
483                     if (long_options[i].has_arg) {
484                         std::cout << " ARG";
485                     }
486                     std::cout << endl;
487                 }
488                 std::cout << endl;
489                 std::cout << "See the herbstluftwm(1) man page for their meaning." << endl;
490                 exit(EXIT_SUCCESS);
491             default:
492                 exit(EXIT_FAILURE);
493         }
494     }
495     g.exitOnXlibError = exit_on_xerror != 0;
496     g.importTagsFromEwmh = (no_tag_import == 0);
497 }
498 
remove_zombies(int)499 static void remove_zombies(int) {
500     int bgstatus;
501     while (waitpid(-1, &bgstatus, WNOHANG) > 0) {
502         ;
503     }
504 }
505 
handle_signal(int signal)506 static void handle_signal(int signal) {
507     HSDebug("Interrupted by signal %d\n", signal);
508     if (g_main_loop) {
509         g_main_loop->quit();
510     }
511     return;
512 }
513 
sigaction_signal(int signum,void (* handler)(int))514 static void sigaction_signal(int signum, void (*handler)(int)) {
515     struct sigaction act = {};
516     act.sa_handler = handler;
517     sigemptyset(&act.sa_mask);
518     act.sa_flags = SA_NOCLDSTOP | SA_RESTART;
519     sigaction(signum, &act, nullptr);
520 }
521 /* ---- */
522 /* main */
523 /* ---- */
524 
main(int argc,char * argv[])525 int main(int argc, char* argv[]) {
526     Globals g;
527     parse_arguments(argc, argv, g);
528 
529     if (!setlocale(LC_CTYPE, "") || !XSupportsLocale()) {
530         std::cerr << "warning: no locale support" << endl;
531     }
532     XConnection::setExitOnError(g.exitOnXlibError);
533     XConnection* X = XConnection::connect();
534     g_display = X->display();
535     if (!g_display) {
536         std::cerr << "herbstluftwm: cannot open display" << endl;
537         delete X;
538         exit(EXIT_FAILURE);
539     }
540     if (X->checkotherwm()) {
541         std::cerr << "herbstluftwm: another window manager is already running" << endl;
542         delete X;
543         exit(EXIT_FAILURE);
544     }
545     // remove zombies on SIGCHLD
546     sigaction_signal(SIGCHLD, remove_zombies);
547     sigaction_signal(SIGINT,  handle_signal);
548     sigaction_signal(SIGQUIT, handle_signal);
549     sigaction_signal(SIGTERM, handle_signal);
550     // set some globals
551     g_screen = X->screen();
552     g_root = X->root();
553     XSelectInput(X->display(), X->root(), SubstructureRedirectMask|SubstructureNotifyMask|ButtonPressMask|EnterWindowMask|LeaveWindowMask|StructureNotifyMask);
554 
555     // setup ipc server
556     IpcServer* ipcServer = new IpcServer(*X);
557     FontData::s_xconnection = X;
558     auto root = make_shared<Root>(g, *X, *ipcServer);
559     Root::setRoot(root);
560     //test_object_system();
561 
562     Commands::initialize(commands(root));
563 
564     XMainLoop mainloop(*X, root.get());
565     g_main_loop = &mainloop;
566 
567     // setup
568     if (g.importTagsFromEwmh) {
569         const auto& initialState = root->ewmh->initialState();
570         for (auto n : initialState.desktopNames) {
571             root->tags->add_tag(n.c_str());
572         }
573     }
574     root->monitors()->ensure_monitors_are_available();
575     mainloop.scanExistingClients();
576     tag_force_update_flags();
577     all_monitors_apply_layout();
578     root->ewmh->updateAll();
579     execute_autostart_file();
580 
581     // main loop
582     mainloop.run();
583 
584     // enforce to clear the root
585     root.reset();
586     Root::setRoot(root);
587     // and then close the x connection
588     FontData::s_xconnection = nullptr;
589     delete ipcServer;
590     delete X;
591     // check if we shall restart an other window manager
592     if (g_exec_before_quit) {
593         if (g_exec_args) {
594             // do actual exec
595             HSDebug("==> Doing wmexec to %s\n", g_exec_args[0]);
596             execvp_helper(g_exec_args);
597         }
598         // on failure or if no other wm given, then fall back
599         HSDebug("==> Doing wmexec to %s\n", argv[0]);
600         execvp_helper(argv);
601         return EXIT_FAILURE;
602     }
603     return EXIT_SUCCESS;
604 }
605 
606