1 #include "monitormanager.h"
2 
3 #include <X11/Xlib.h>
4 #include <cassert>
5 #include <memory>
6 
7 #include "command.h"
8 #include "completion.h"
9 #include "ewmh.h"
10 #include "floating.h"
11 #include "frametree.h"
12 #include "globals.h"
13 #include "ipc-protocol.h"
14 #include "monitor.h"
15 #include "monitordetection.h"
16 #include "panelmanager.h"
17 #include "rectangle.h"
18 #include "root.h"
19 #include "settings.h"
20 #include "stack.h"
21 #include "tag.h"
22 #include "tagmanager.h"
23 #include "utils.h"
24 #include "xconnection.h"
25 
26 using std::endl;
27 using std::function;
28 using std::make_pair;
29 using std::make_shared;
30 using std::pair;
31 using std::shared_ptr;
32 using std::string;
33 using std::to_string;
34 using std::vector;
35 
36 MonitorManager* g_monitors;
37 
MonitorManager()38 MonitorManager::MonitorManager()
39     : IndexingObject<Monitor>()
40     , focus(*this, "focus")
41     , by_name_(*this)
42     , panels_(nullptr)
43     , tags_(nullptr)
44     , settings_(nullptr)
45 {
46     cur_monitor = 0;
47     setDoc("Every monitor is a rectangular part of the screen "
48            "on which a tag is shown. These monitors may or may"
49            "not match the actual outputs.\n"
50            "This has an entry \'INDEX\' for each monitor with"
51            "index \'INDEX\'.");
52     focus.setDoc("the focused monitor.");
53     // TODO: add this as soon as by_name_ is of type Child_<ByName>
54     // by_name_.setDoc("contains an entry for each monitor with "
55     //                 "a name.");
56 }
57 
~MonitorManager()58 MonitorManager::~MonitorManager() {
59     clearChildren();
60 }
61 
injectDependencies(Settings * s,TagManager * t,PanelManager * p)62 void MonitorManager::injectDependencies(Settings* s, TagManager* t, PanelManager* p) {
63     settings_ = s;
64     tags_ = t;
65     panels_ = p;
66 }
67 
clearChildren()68 void MonitorManager::clearChildren() {
69     IndexingObject<Monitor>::clearChildren();
70     focus = {};
71     tags_ = {};
72 }
73 
ensure_monitors_are_available()74 void MonitorManager::ensure_monitors_are_available() {
75     if (size() > 0) {
76         // nothing to do
77         return;
78     }
79     // add monitor if necessary
80     Rectangle rect = { 0, 0,
81             DisplayWidth(g_display, DefaultScreen(g_display)),
82             DisplayHeight(g_display, DefaultScreen(g_display))};
83     HSTag* tag = tags_->ensure_tags_are_available();
84     // add monitor with first tag
85     Monitor* m = addMonitor(rect, tag);
86     m->tag->setVisible(true);
87     cur_monitor = 0;
88 
89     autoUpdatePads();
90     monitor_update_focus_objects();
91 }
92 
indexInDirection(Monitor * relativeTo,Direction dir)93 int MonitorManager::indexInDirection(Monitor* relativeTo, Direction dir) {
94     RectangleIdxVec rects;
95     if (!relativeTo) {
96         return -1;
97     }
98     for (Monitor* mon : *this) {
99         rects.push_back(make_pair(mon->index(), mon->rect));
100     }
101     int result = Floating::find_rectangle_in_direction(rects, int(relativeTo->index), dir);
102     return result;
103 }
104 
string_to_monitor_index(string str)105 int MonitorManager::string_to_monitor_index(string str) {
106     if (str[0] == '\0') {
107         return cur_monitor;
108     } else if (str[0] == '-' || str[0] == '+') {
109         if (isdigit(str[1])) {
110             // relative monitor index
111             int idx = cur_monitor + atoi(str.c_str());
112             return MOD(idx, size());
113         } else if (str[0] == '-') {
114             try {
115                 auto dir = Converter<Direction>::parse(str.substr(1));
116                 return indexInDirection(focus(), dir);
117             } catch (...) {
118                 return -1;
119             }
120         } else {
121             return -1;
122         }
123     } else if (isdigit(str[0])) {
124         // absolute monitor index
125         int idx = atoi(str.c_str());
126         if (idx < 0 || idx >= (int)size()) {
127             return -1;
128         }
129         return idx;
130     } else {
131         // monitor string
132         for (unsigned i = 0; i < size(); i++) {
133           if (byIdx(i)->name == str) {
134             return (int)i;
135           }
136         }
137         return -1;
138     }
139 }
140 
completeMonitorName(Completion & complete)141 void MonitorManager::completeMonitorName(Completion& complete) {
142     complete.full(""); // the focused monitor
143     // complete against relative indices
144     complete.full("-1");
145     complete.full("+0");
146     complete.full("+1");
147     for (auto m : *this) {
148         // complete against the absolute index
149         complete.full(to_string(m->index()));
150         // complete against the name
151         if (m->name != "") {
152             complete.full(m->name);
153         }
154     }
155 }
156 
157 
list_monitors(Output output)158 int MonitorManager::list_monitors(Output output) {
159     string monitor_name = "";
160     int i = 0;
161     for (auto monitor : *this) {
162         if (monitor->name != "" ) {
163             monitor_name = ", named \"" + monitor->name() + "\"";
164         } else {
165             monitor_name = "";
166         }
167         output << i << ": " << monitor->rect
168                << " with tag \""
169                << (monitor->tag ? monitor->tag->name->c_str() : "???")
170                << "\""
171                << monitor_name
172                << ((cur_monitor == i) ? " [FOCUS]" : "")
173                << (monitor->lock_tag ? " [LOCKED]" : "")
174                << "\n";
175         i++;
176     }
177     return 0;
178 }
179 
byString(string str)180 Monitor* MonitorManager::byString(string str) {
181     int idx = string_to_monitor_index(str);
182     return ((idx >= 0) && idx < static_cast<int>(size())) ? byIdx(idx) : nullptr;
183 }
184 
byFirstArg(MonitorCommand cmd)185 function<int(Input, Output)> MonitorManager::byFirstArg(MonitorCommand cmd)
186 {
187     return [this,cmd](Input input, Output output) -> int {
188         Monitor *monitor;
189         string monitor_name;
190         if (!(input >> monitor_name)) {
191             monitor = get_current_monitor();
192         } else {
193             monitor = byString(monitor_name);
194             if (!monitor) {
195                 output << input.command() <<
196                     ": Monitor \"" << monitor_name << "\" not found!\n";
197                 return HERBST_INVALID_ARGUMENT;
198             }
199         }
200         return cmd(*monitor, Input(input.command(), input.toVector()), output);
201     };
202 }
203 
byFirstArg(MonitorCommand moncmd,MonitorCompletion moncomplete)204 CommandBinding MonitorManager::byFirstArg(MonitorCommand moncmd, MonitorCompletion moncomplete)
205 {
206     auto cmdBound = byFirstArg(moncmd);
207     auto completeBound = [this,moncomplete](Completion& complete) {
208         if (complete == 0) {
209             this->completeMonitorName(complete);
210         } else {
211             Monitor* monitor = byString(complete[0]);
212             if (!monitor) {
213                 monitor = focus();
214             }
215             moncomplete(*monitor, complete);
216         }
217     };
218     return {cmdBound, completeBound};
219 }
220 
tagCommand(TagCommand cmd,TagCompletion completer)221 CommandBinding MonitorManager::tagCommand(TagCommand cmd, TagCompletion completer)
222 {
223     auto cmdBound = [this,cmd](Input input, Output output) {
224         return cmd(*(this->focus()->tag), input, output);
225     };
226     auto completeBound =  [this,completer](Completion& complete) {
227         completer(*(this->focus()->tag), complete);
228     };
229     return {cmdBound, completeBound};
230 }
231 
tagCommand(function<int (HSTag &)> cmd)232 CommandBinding MonitorManager::tagCommand(function<int (HSTag&)> cmd)
233 {
234     return CommandBinding([this,cmd]() {
235         return cmd(*(this->focus()->tag));
236     });
237 }
238 
239 
byTag(HSTag * tag)240 Monitor* MonitorManager::byTag(HSTag* tag) {
241     for (Monitor* m : *this) {
242         if (m->tag == tag) {
243             return m;
244         }
245     }
246     return nullptr;
247 }
248 
249 /**
250  * @brief Find the monitor having the given coordinate
251  * @param coordinate on a monitor
252  * @return The monitor with the coordinate or nullptr if the coordinate is outside of any monitor
253  */
byCoordinate(Point2D p)254 Monitor* MonitorManager::byCoordinate(Point2D p)
255 {
256     for (Monitor* m : *this) {
257         if (m->rect->x + m->pad_left <= p.x
258             && m->rect->x + m->rect->width - m->pad_right > p.x
259             && m->rect->y + m->pad_up <= p.y
260             && m->rect->y + m->rect->height - m->pad_down > p.y) {
261             return &* m;
262         }
263     }
264     return nullptr;
265 }
266 
byFrame(shared_ptr<Frame> frame)267 Monitor* MonitorManager::byFrame(shared_ptr<Frame> frame)
268 {
269     for (Monitor* m : *this) {
270         if (m->tag->frame->contains(frame)) {
271             return m;
272         }
273     }
274     return nullptr;
275 }
276 
relayoutTag(HSTag * tag)277 void MonitorManager::relayoutTag(HSTag* tag)
278 {
279     Monitor* m = byTag(tag);
280     if (m) {
281         m->applyLayout();
282     }
283 }
284 
relayoutAll()285 void MonitorManager::relayoutAll()
286 {
287     for (Monitor* m : *this) {
288         m->applyLayout();
289     }
290 }
291 
removeMonitor(Input input,Output output)292 int MonitorManager::removeMonitor(Input input, Output output)
293 {
294     string monitorIdxString;
295     if (!(input >> monitorIdxString)) {
296         return HERBST_NEED_MORE_ARGS;
297     }
298     auto monitor = byString(monitorIdxString);
299 
300     if (monitor == nullptr) {
301         output << input.command() << ": Monitor \"" << monitorIdxString << "\" not found!\n";
302         return HERBST_INVALID_ARGUMENT;
303     }
304 
305     if (size() <= 1) {
306         output << input.command() << ": Can't remove the last monitor\n";
307         return HERBST_FORBIDDEN;
308     }
309 
310     removeMonitor(monitor);
311 
312     return HERBST_EXIT_SUCCESS;
313 }
314 
removeMonitor(Monitor * monitor)315 void MonitorManager::removeMonitor(Monitor* monitor)
316 {
317     auto monitorIdx = index_of(monitor);
318 
319     if (cur_monitor > index_of(monitor)) {
320         // Take into account that the current monitor will have a new
321         // index after removal:
322         cur_monitor--;
323     }
324 
325     // Hide all clients visible in monitor
326     assert(monitor->tag != nullptr);
327     assert(monitor->tag->frame->root_ != nullptr);
328     monitor->tag->setVisible(false);
329 
330     monitorStack_.remove(monitor);
331     g_monitors->removeIndexed(monitorIdx);
332 
333     if (cur_monitor >= static_cast<int>(g_monitors->size())) {
334         cur_monitor--;
335         // if selection has changed, then relayout focused monitor
336         get_current_monitor()->applyLayout();
337         monitor_update_focus_objects();
338         // also announce the new selection
339         Ewmh::get().updateCurrentDesktop();
340         emit_tag_changed(get_current_monitor()->tag, cur_monitor);
341     }
342     monitor_update_focus_objects();
343 }
344 
addMonitor(Input input,Output output)345 int MonitorManager::addMonitor(Input input, Output output)
346 {
347     // usage: add_monitor RECTANGLE [TAG [NAME]]
348     string rectString, tagName, monitorName;
349     input >> rectString;
350     if (!input) {
351         return HERBST_NEED_MORE_ARGS;
352     }
353     HSTag* tag = nullptr;
354     if (input >> tagName) {
355         tag = find_tag(tagName.c_str());
356         if (!tag) {
357             output << input.command() << ": Tag \"" << tagName << "\" does not exist\n";
358             return HERBST_INVALID_ARGUMENT;
359         }
360         if (find_monitor_with_tag(tag)) {
361             output << input.command() <<
362                 ": Tag \"" << tagName << "\" is already being viewed on a monitor\n";
363             return HERBST_TAG_IN_USE;
364         }
365     } else { // if no tag is supplied
366         tag = tags_->unusedTag();
367         if (!tag) {
368             output << input.command() << ": There are not enough free tags\n";
369             return HERBST_TAG_IN_USE;
370         }
371     }
372     // TODO: error message on invalid rectString
373     auto rect = Rectangle::fromStr(rectString);
374     if (input >> monitorName) {
375         string error;
376         if (monitorName.empty()) {
377             error = "An empty monitor name is not permitted";
378         } else {
379             error = isValidMonitorName(monitorName);
380         }
381         if (!error.empty()) {
382             output << input.command() << ": " << error << "\n";
383             return HERBST_INVALID_ARGUMENT;
384         }
385     }
386     auto monitor = addMonitor(rect, tag);
387     if (!monitorName.empty()) {
388         monitor->name = monitorName;
389     }
390 
391     autoUpdatePads();
392     monitor->applyLayout();
393     tag->setVisible(true);
394     emit_tag_changed(tag, g_monitors->size() - 1);
395     dropEnterNotifyEvents.emit();
396 
397     return HERBST_EXIT_SUCCESS;
398 }
399 
isValidMonitorName(string name)400 string MonitorManager::isValidMonitorName(string name) {
401     if (isdigit(name[0])) {
402         return "Invalid name \"" + name + "\": The monitor name may not start with a number";
403     }
404     if (name.empty()) {
405         // clearing a name is always OK.
406         return "";
407     }
408     if (find_monitor_by_name(name.c_str())) {
409         return "A monitor with the name \"" + name + "\" already exists";
410     }
411     return "";
412 }
413 
414 //! automatically update the pad settings for all monitors
autoUpdatePads()415 void MonitorManager::autoUpdatePads()
416 {
417     for (Monitor* m : *this) {
418         PanelManager::ReservedSpace rs = panels_->computeReservedSpace(m->rect);
419         // all the sides in the order as it matters for pad_automatically_set
420         vector<pair<Attribute_<int>&, int>> sides = {
421             { m->pad_up,    rs.top_     },
422             { m->pad_right, rs.right_   },
423             { m->pad_down,  rs.bottom_  },
424             { m->pad_left,  rs.left_    },
425         };
426         size_t idx = 0;
427         for (auto& it : sides ) {
428             if (it.first() != it.second) {
429                 if (it.second != 0) {
430                     // if some panel was added or resized
431                     it.first.operator=(it.second);
432                     m->pad_automatically_set[idx] = true;
433                 } else {
434                     // if there is no panel, then only clear the pad
435                     // if the pad was added by us before
436                     if (m->pad_automatically_set[idx]) {
437                         it.first.operator=(0);
438                         m->pad_automatically_set[idx] = false;
439                     }
440                 }
441             }
442             idx++;
443         }
444     }
445 }
446 
addMonitor(Rectangle rect,HSTag * tag)447 Monitor* MonitorManager::addMonitor(Rectangle rect, HSTag* tag) {
448     Monitor* m = new Monitor(settings_, this, rect, tag);
449     addIndexed(m);
450     monitorStack_.insert(m);
451     m->monitorMoved.connect([this]() {
452         this->autoUpdatePads();
453     });
454     return m;
455 }
456 
457 
lock()458 void MonitorManager::lock() {
459     settings_->monitors_locked = settings_->monitors_locked() + 1;
460     lock_number_changed();
461 }
462 
unlock()463 void MonitorManager::unlock() {
464     settings_->monitors_locked = std::max(0, settings_->monitors_locked() - 1);
465     lock_number_changed();
466 }
467 
lock_number_changed()468 string MonitorManager::lock_number_changed() {
469     if (settings_->monitors_locked() < 0) {
470         return "must be non-negative";
471     }
472     if (!settings_->monitors_locked()) {
473         // if not locked anymore, then repaint all the dirty monitors
474         for (auto m : *this) {
475             if (m->dirty) {
476                 m->applyLayout();
477             }
478         }
479     }
480     return {};
481 }
482 
483 //! return the stack of windows by successive calls to the given yield
484 //function. The stack is returned from top to bottom, i.e. the topmost element
485 //is the first element yielded
extractWindowStack(bool real_clients,function<void (Window)> yield)486 void MonitorManager::extractWindowStack(bool real_clients, function<void(Window)> yield)
487 {
488     for (Monitor* monitor : monitorStack_) {
489         if (!real_clients) {
490             yield(monitor->stacking_window);
491         }
492         monitor->tag->stack->extractWindows(real_clients, yield);
493     }
494 }
495 
496 //! restack the entire stack including all monitors
restack()497 void MonitorManager::restack() {
498     vector<Window> buf;
499     extractWindowStack(false, [&buf](Window w) { buf.push_back(w); });
500     XRestackWindows(g_display, buf.data(), buf.size());
501     Ewmh::get().updateClientListStacking();
502 }
503 
504 class StringTree : public TreeInterface {
505 public:
StringTree(string label,vector<shared_ptr<StringTree>> children={})506     StringTree(string label, vector<shared_ptr<StringTree>> children = {})
507         : children_(children)
508         , label_(label)
509     {};
510 
childCount()511     size_t childCount() override {
512         return children_.size();
513     };
514 
nthChild(size_t idx)515     shared_ptr<TreeInterface> nthChild(size_t idx) override {
516         return children_.at(idx);
517     };
518 
appendCaption(Output output)519     void appendCaption(Output output) override {
520         if (!label_.empty()) {
521             output << " " << label_;
522         }
523     };
524 
525 private:
526     vector<shared_ptr<StringTree>> children_;
527     string label_;
528 };
529 
stackCommand(Output output)530 int MonitorManager::stackCommand(Output output) {
531     vector<shared_ptr<StringTree>> monitors;
532     for (Monitor* monitor : monitorStack_) {
533         vector<shared_ptr<StringTree>> layers;
534         for (size_t layerIdx = 0; layerIdx < LAYER_COUNT; layerIdx++) {
535             auto layer = monitor->tag->stack->layers_[layerIdx];
536 
537             vector<shared_ptr<StringTree>> slices;
538             for (auto& slice : layer) {
539                 slices.push_back(make_shared<StringTree>(slice->getLabel()));
540             }
541 
542             auto layerLabel = g_layer_names[layerIdx];
543             layers.push_back(make_shared<StringTree>(layerLabel, slices));
544         }
545 
546         monitors.push_back(make_shared<StringTree>(monitor->getDescription(), layers));
547     }
548 
549     auto stackRoot = make_shared<StringTree>("", monitors);
550     tree_print_to(stackRoot, output);
551     return 0;
552 }
553 
554 /** Add, Move, Remove monitors such that the monitor list matches the given
555  * vector of Rectangles
556  */
setMonitors(const RectangleVec & templates)557 int MonitorManager::setMonitors(const RectangleVec& templates) {
558     if (templates.empty()) {
559         return HERBST_INVALID_ARGUMENT;
560     }
561     HSTag* tag = nullptr;
562     unsigned i;
563     for (i = 0; i < std::min(templates.size(), size()); i++) {
564         auto m = byIdx(i);
565         if (!m) {
566             continue;
567         }
568         m->rect = templates[i];
569     }
570     // add additional monitors
571     for (; i < templates.size(); i++) {
572         tag = tags_->unusedTag();
573         if (!tag) {
574             return HERBST_TAG_IN_USE;
575         }
576         addMonitor(templates[i], tag);
577         tag->setVisible(true);
578     }
579     // remove monitors if there are too much
580     while (i < size()) {
581         removeMonitor(byIdx(i));
582     }
583     monitor_update_focus_objects();
584     autoUpdatePads();
585     all_monitors_apply_layout();
586     return 0;
587 }
588 
setMonitorsCommand(Input input,Output output)589 int MonitorManager::setMonitorsCommand(Input input, Output output) {
590     RectangleVec templates;
591     string rectangleString;
592     while (input >> rectangleString) {
593         Rectangle rect = Rectangle::fromStr(rectangleString);
594         if (rect.width == 0 || rect.height == 0)
595         {
596             output << input.command()
597                    << ": Rectangle invalid or too small: "
598                    << rectangleString << endl;
599             return HERBST_INVALID_ARGUMENT;
600         }
601         templates.push_back(rect);
602     }
603     if (templates.empty()) {
604         return HERBST_NEED_MORE_ARGS;
605     }
606     int status = setMonitors(templates);
607 
608     if (status == HERBST_TAG_IN_USE) {
609         output << input.command() << ": There are not enough free tags\n";
610     } else if (status == HERBST_INVALID_ARGUMENT) {
611         return HERBST_NEED_MORE_ARGS;
612     }
613     return status;
614 }
615 
setMonitorsCompletion(Completion &)616 void MonitorManager::setMonitorsCompletion(Completion&) {
617     // every parameter can be a rectangle specification.
618     // we don't have completion for rectangles
619 }
620 
detectMonitorsCompletion(Completion & complete)621 void MonitorManager::detectMonitorsCompletion(Completion& complete)
622 {
623     complete.full({"-l", "--list", "--list-all", "--no-disjoin"});
624 }
625 
detectMonitorsCommand(Input input,Output output)626 int MonitorManager::detectMonitorsCommand(Input input, Output output)
627 {
628     bool list_all = false;
629     bool list_only = false;
630     bool disjoin = true;
631     string arg;
632     while (input >> arg) {
633         if (arg == "-l" || arg == "--list") {
634             list_only = true;
635         } else if (arg == "--list-all") {
636             list_all = true;
637         } else if (arg == "--no-disjoin") {
638             disjoin = false;
639         } else {
640             output << input.command() << ": unknown flag \"" << arg << "\"\n";
641             return HERBST_INVALID_ARGUMENT;
642         }
643     }
644 
645     auto root = Root::get();
646     if (list_all) {
647         for (const auto& detector : MonitorDetection::detectors()) {
648             output << detector.name_ << ":";
649             if (detector.detect_) {
650                 for (auto m : detector.detect_(root->X)) {
651                     output << " " << m;
652                 }
653             } else {
654                 output << " disabled";
655             }
656             output << endl;
657         }
658         return 0;
659     }
660 
661     RectangleVec monitor_rects = {};
662     for (const auto& detector : MonitorDetection::detectors()) {
663         if (detector.detect_) {
664             auto rects = detector.detect_(root->X);
665             // remove duplicates
666             std::sort(rects.begin(), rects.end());
667             rects.erase(std::unique(rects.begin(), rects.end()), rects.end());
668             // check if this has more outputs than we know already
669             if (rects.size() > monitor_rects.size()) {
670                 monitor_rects = rects;
671             }
672         }
673     }
674     if (monitor_rects.empty()) {
675         monitor_rects = { root->X.windowSize(root->X.root()) };
676     }
677     if (list_only) {
678         for (auto m : monitor_rects) {
679             output << m << "\n";
680         }
681     } else {
682         // possibly disjoin them
683         if (disjoin) {
684             monitor_rects = disjoin_rects(monitor_rects);
685         }
686         // apply it
687         int ret = g_monitors->setMonitors(monitor_rects);
688         if (ret == HERBST_TAG_IN_USE) {
689             output << input.command() << ": There are not enough free tags\n";
690         }
691         return ret;
692     }
693     return 0;
694 }
695 
696 /**
697  * @brief Transform a rectangle on the screen into a rectangle relative to one of the monitor.
698  * Currently, we pick the monitor that has the biggest intersection with the given rectangle.
699  *
700  * @param globalGeometry A rectangle whose coordinates are interpreted relative to (0,0) on the screen
701  * @return The given rectangle with coordinates relative to a monitor
702  */
interpretGlobalGeometry(Rectangle globalGeometry)703 Rectangle MonitorManager::interpretGlobalGeometry(Rectangle globalGeometry)
704 {
705     int bestArea = 0;
706     Monitor* best = nullptr;
707     for (Monitor* m : *this) {
708         auto intersection = m->rect->intersectionWith(globalGeometry);
709         if (!intersection) {
710             continue;
711         }
712         auto area = intersection.width * intersection.height;
713         if (area > bestArea) {
714             bestArea = area;
715             best = m;
716         }
717     }
718     if (best) {
719         globalGeometry.x -= best->rect->x + *best->pad_left;
720         globalGeometry.y -= best->rect->y + *best->pad_up;
721     }
722     return globalGeometry;
723 }
724 
raiseMonitorCommand(Input input,Output output)725 int MonitorManager::raiseMonitorCommand(Input input, Output output) {
726     string monitorName = "";
727     input >> monitorName;
728     Monitor* monitor = string_to_monitor(monitorName.c_str());
729     if (!monitor) {
730         output << input.command() << ": Monitor \"" << monitorName << "\" not found!\n";
731         return HERBST_INVALID_ARGUMENT;
732     }
733     monitorStack_.raise(monitor);
734     restack();
735     return 0;
736 }
737 
raiseMonitorCompletion(Completion & complete)738 void MonitorManager::raiseMonitorCompletion(Completion& complete) {
739     if (complete == 0) {
740         completeMonitorName(complete);
741     } else {
742         complete.none();
743     }
744 }
745 
746