1 #include "client.h"
2 
3 #include <X11/Xlib.h>
4 #include <X11/Xutil.h>
5 #include <cstdlib>
6 #include <cstring>
7 #include <sstream>
8 
9 #include "clientmanager.h"
10 #include "decoration.h"
11 #include "ewmh.h"
12 #include "frametree.h"
13 #include "globals.h"
14 #include "hook.h"
15 #include "ipc-protocol.h"
16 #include "keymanager.h"
17 #include "layout.h"
18 #include "monitor.h"
19 #include "monitormanager.h"
20 #include "mousemanager.h"
21 #include "root.h"
22 #include "stack.h"
23 #include "tag.h"
24 #include "theme.h"
25 #include "utils.h"
26 #include "xconnection.h"
27 
28 using std::string;
29 using std::stringstream;
30 
31 static int g_monitor_float_treshold = 24;
32 
33 static Client* lastfocus = nullptr;
34 
35 
Client(Window window,bool visible_already,ClientManager & cm)36 Client::Client(Window window, bool visible_already, ClientManager& cm)
37     : window_(window)
38     , dec(make_unique<Decoration>(this, *cm.settings))
39     , visible_(this, "visible", visible_already)
40     , urgent_(this, "urgent", false)
41     , floating_(this,  "floating", false)
42     , fullscreen_(this,  "fullscreen", false)
43     , minimized_(this,  "minimized", false)
44     , title_(this,  "title", "")
45     , tag_str_(this,  "tag", &Client::tagName)
46     , parent_frame_(*this,  "parent_frame", &Client::parentFrame)
47     , window_id_str(this,  "winid", "")
48     , keyMask_(this,  "keymask", RegexStr::fromStr(""))
49     , keysInactive_(this,  "keys_inactive", RegexStr::fromStr(""))
50     , pid_(this,  "pid", -1)
51     , pgid_(this, "pgid", -1)
52     , pseudotile_(this,  "pseudotile", false)
53     , ewmhrequests_(this, "ewmhrequests", true)
54     , ewmhnotify_(this, "ewmhnotify", true)
55     , sizehints_floating_(this, "sizehints_floating", true)
56     , sizehints_tiling_(this, "sizehints_tiling", false)
57     , window_class_(this, "class", &Client::getWindowClass)
58     , window_instance_(this, "instance", &Client::getWindowInstance)
59     , manager(cm)
60     , theme(*cm.theme)
61     , settings(*cm.settings)
62     , ewmh(*cm.ewmh)
63     , mostRecentThemeType(Theme::Type::Tiling)
64 {
65     stringstream tmp;
66     window_id_str = WindowID(window).str();
67     floating_.setWritable();
68     keyMask_.setWritable();
69     keysInactive_.setWritable();
70     ewmhnotify_.setWritable();
71     ewmhrequests_.setWritable();
72     fullscreen_.setWritable();
73     pseudotile_.setWritable();
74     sizehints_floating_.setWritable();
75     sizehints_tiling_.setWritable();
76     minimized_.setWritable();
77     for (auto i : {&fullscreen_, &pseudotile_, &sizehints_floating_, &sizehints_tiling_}) {
78         i->changed().connect(this, &Client::requestRedraw);
79     }
80 
81     keyMask_.changed().connect([this] {
82             if (Root::get()->clients()->focus() == this) {
83                 Root::get()->keys()->ensureKeyMask();
84             }
85             });
86     keysInactive_.changed().connect([this] {
87             if (Root::get()->clients()->focus() == this) {
88                 Root::get()->keys()->ensureKeyMask();
89             }
90             });
91     fullscreen_.changed().connect([this] {
92         updateEwmhState();
93         hook_emit({"fullscreen", fullscreen_() ? "on" : "off", WindowID(window_).str()});
94     });
95     minimized_.changed().connect(this, &Client::updateEwmhState);
96 
97     init_from_X();
98     visible_.setDoc("whether this client is rendered currently");
99     parent_frame_.setDoc("the frame contaning this client if the client is tiled");
100     setDoc("a managed window");
101 
102     window_id_str.setDoc("its window id (as a hexadecimal number with 0x prefix)");
103     title_.setDoc("its window title");
104     keyMask_.setDoc(
105         "A regular expression that is matched against the string "
106         "representation of all key bindings (as they are printed "
107         "by list_keybinds). While this client is focused, only "
108         "bindings that match the expression will be active. "
109         "Any other bindings will be disabled. The default keymask "
110         "is an empty string (""), which does not disable any keybinding.");
111     keysInactive_.setDoc(
112         "A regular expression that describes which keybindings are inactive "
113         "while the client is focused. If a key combination is pressed and "
114         "its string representation (as given by list_keybinds) matches the "
115         "regex, then the key press is propagated to the client.");
116     tag_str_.setDoc("the name of the tag it's currently on.");
117     pid_.setDoc("the process id of it (-1 if unset).");
118     window_class_.setDoc("the class of it (second entry in WM_CLASS)");
119     window_instance_.setDoc("the instance of it (first entry in WM_CLASS)");
120     fullscreen_.setDoc(
121                 "whether this client covers all other "
122                 "windows and panels on its monitor.");
123     minimized_.setDoc(
124                 "whether this client is minimized (also called "
125                 "iconified).");
126     floating_.setDoc("whether this client is floated above the tiled clients.");
127     pseudotile_.setDoc(
128                 "if activated, the client always has its floating "
129                 "window size, even if it is in tiling mode.");
130     ewmhrequests_.setDoc("if ewmh requests are permitted for this client");
131     ewmhnotify_.setDoc("if the client is told about its state via ewmh");
132     urgent_.setDoc("the urgency state (also known as: demands attention)");
133     sizehints_tiling_.setDoc("if sizehints for this client "
134                              "should be respected in tiling mode");
135     sizehints_floating_.setDoc("if sizehints for this client should "
136                                "be respected in floating mode");
137 }
138 
init_from_X()139 void Client::init_from_X() {
140     // treat wanted coordinates as floating coords
141     auto root = Root::get();
142     auto globalGeometry = root->X.windowSize(window_);
143     float_size_ = root->monitors->interpretGlobalGeometry(globalGeometry);
144     last_size_ = float_size_;
145 
146     pid_ = Root::get()->X.windowPid(window_);
147     pgid_ = Root::get()->X.windowPgid(window_);
148 
149     update_title();
150     update_wm_hints();
151     updatesizehints();
152 }
153 
make_full_client()154 void Client::make_full_client() {
155     // setup decoration
156     XSetWindowBorderWidth(g_display, window_, 0);
157     // specify that the client window survives if hlwm dies, i.e. it will be
158     // reparented back to root
159     XChangeSaveSet(g_display, window_, SetModeInsert);
160     XReparentWindow(g_display, window_, dec->decorationWindow(), 40, 40);
161     // if this client is visible, then reparenting will make it invisible
162     // and will create a unmap notify event
163     if (visible_()) {
164         ignore_unmaps_++;
165         visible_ = false;
166     }
167     // get events from window
168     XSelectInput(g_display, dec->decorationWindow(), (EnterWindowMask | LeaveWindowMask |
169                             ButtonPressMask | ButtonReleaseMask |
170                             ExposureMask |
171                             SubstructureRedirectMask | FocusChangeMask));
172     XSelectInput(g_display, window_,
173                             StructureNotifyMask|FocusChangeMask
174                             |EnterWindowMask|PropertyChangeMask);
175     // redraw decoration on title change
176     title_.changed().connect(dec.get(), &Decoration::redraw);
177 }
178 
listen_for_events()179 void Client::listen_for_events() {
180     XSelectInput(g_display, window_, PropertyChangeMask);
181 }
182 
setTag(HSTag * tag)183 void Client::setTag(HSTag *tag) {
184     tag_ = tag;
185     ewmh.windowUpdateTag(window_, tag);
186 }
187 
ignore_unmapnotify()188 bool Client::ignore_unmapnotify() {
189     if (ignore_unmaps_ > 0) {
190         ignore_unmaps_--;
191         return true;
192     } else {
193         return false;
194     }
195 }
196 
reset_client_colors()197 void reset_client_colors() {
198     all_monitors_apply_layout();
199 }
200 
get_client_from_window(Window window)201 Client* get_client_from_window(Window window) {
202     return Root::get()->clients()->client(window);
203 }
204 
205 // destroys a special client
~Client()206 Client::~Client() {
207     if (lastfocus == this) {
208         lastfocus = nullptr;
209     }
210     if (slice) {
211         delete slice;
212     }
213 }
214 
window_unfocus()215 void Client::window_unfocus() {
216     Root::get()->mouse->grab_client_buttons(this, false);
217 }
218 
window_unfocus_last()219 void Client::window_unfocus_last() {
220     if (lastfocus) {
221         lastfocus->window_unfocus();
222     }
223     // give focus to root window
224     Ewmh::get().clearInputFocus();
225     if (lastfocus) {
226         /* only emit the hook if the focus *really* changes */
227         hook_emit({"focus_changed", "0x0", ""});
228         Ewmh::get().updateActiveWindow(None);
229 
230         // Enable all keys in the root window
231         Root::get()->keys()->clearActiveKeyMask();
232     }
233     lastfocus = 0;
234 }
235 
window_focus()236 void Client::window_focus() {
237     // set keyboard focus
238     if (!this->neverfocus_) {
239         XSetInputFocus(g_display, this->window_, RevertToPointerRoot, CurrentTime);
240     } else {
241         ewmh.sendEvent(window_, Ewmh::WM::TakeFocus, True);
242     }
243 
244     if (this != lastfocus) {
245         /* FIXME: this is a workaround because window_focus always is called
246          * twice.
247          *
248          * only emit the hook if the focus *really* changes */
249         // unfocus last one
250         if (lastfocus) {
251             lastfocus->window_unfocus();
252         }
253         ewmh.updateActiveWindow(this->window_);
254         hook_emit({"focus_changed", WindowID(window_).str(), title_()});
255     }
256 
257     // change window-colors
258     //HSDebug("window_focus ACTIVE: 0x%lx\n", client->window);
259     //client_setup_border(client, true);
260 
261     lastfocus = this;
262     Root::get()->mouse->grab_client_buttons(this, true);
263 
264     // XXX: At this point, ClientManager does not yet know about the focus
265     // change. So as a workaround, we pass ourselves directly to KeyManager:
266     Root::get()->keys()->ensureKeyMask(this);
267 
268     this->set_urgent(false);
269 }
270 
getDecTriple()271 const DecTriple& Client::getDecTriple() {
272     return theme[mostRecentThemeType];
273 }
274 
setup_border(bool focused)275 void Client::setup_border(bool focused) {
276     dec->change_scheme(getDecTriple()(focused, urgent_()));
277 }
278 
resize_fullscreen(Rectangle monitor_rect,bool isFocused)279 void Client::resize_fullscreen(Rectangle monitor_rect, bool isFocused) {
280     dec->resize_outline(monitor_rect, theme[Theme::Type::Fullscreen](isFocused,urgent_()));
281     mostRecentThemeType = Theme::Type::Fullscreen;
282 }
283 
raise()284 void Client::raise() {
285     this->tag()->stack->raiseSlice(this->slice);
286 }
287 
288 /**
289  * @brief Client::resize_tiling
290  * @param the outer geometry of the client
291  * @param whether this client has the focus
292  * @param whether the client should use the 'minimal decoration' scheme
293  */
resize_tiling(Rectangle rect,bool isFocused,bool minimalDecoration)294 void Client::resize_tiling(Rectangle rect, bool isFocused, bool minimalDecoration) {
295     // only apply minimal decoration if the window is not pseudotiled
296     auto themetype = (minimalDecoration && !pseudotile_())
297             ? Theme::Type::Minimal : Theme::Type::Tiling;
298     mostRecentThemeType = themetype;
299     auto& scheme = theme[themetype](isFocused, urgent_());
300     if (this->pseudotile_) {
301         auto inner = this->float_size_;
302         applysizehints(&inner.width, &inner.height);
303         auto outline = scheme.inner_rect_to_outline(inner);
304         rect.x += std::max(0, (rect.width - outline.width)/2);
305         rect.y += std::max(0, (rect.height - outline.height)/2);
306         rect.width = std::min(outline.width, rect.width);
307         rect.height = std::min(outline.height, rect.height);
308     }
309     dec->resize_outline(rect, scheme);
310 }
311 
312 // from dwm.c
applysizehints(int * w,int * h)313 bool Client::applysizehints(int *w, int *h) {
314     bool baseismin;
315 
316     /* set minimum possible */
317     *w = std::max(1, *w);
318     *h = std::max(1, *h);
319     if (*h < WINDOW_MIN_HEIGHT) {
320         *h = WINDOW_MIN_HEIGHT;
321     }
322     if (*w < WINDOW_MIN_WIDTH) {
323         *w = WINDOW_MIN_WIDTH;
324     }
325     bool sizehints = (this->is_client_floated() || this->pseudotile_)
326                         ? this->sizehints_floating_
327                         : this->sizehints_tiling_;
328     if (sizehints) {
329         /* see last two sentences in ICCCM 4.1.2.3 */
330         baseismin = this->basew_ == this->minw_ && this->baseh_ == this->minh_;
331         if(!baseismin) { /* temporarily remove base dimensions */
332             *w -= this->basew_;
333             *h -= this->baseh_;
334         }
335         /* adjust for aspect limits */
336         if(this->mina_ > 0 && this->maxa_ > 0) {
337             if (this->maxa_ < (float)*w / *h) {
338                 *w = int(*h * this->maxa_ + 0.5f);
339             } else if (this->mina_ < (float)*h / *w) {
340                 *h = int(*w * this->mina_ + 0.5f);
341             }
342         }
343         if(baseismin) { /* increment calculation requires this */
344             *w -= this->basew_;
345             *h -= this->baseh_;
346         }
347         /* adjust for increment value */
348         if (this->incw_) {
349             *w -= *w % this->incw_;
350         }
351         if (this->inch_) {
352             *h -= *h % this->inch_;
353         }
354         /* restore base dimensions */
355         *w += this->basew_;
356         *h += this->baseh_;
357         *w = std::max(*w, this->minw_);
358         *h = std::max(*h, this->minh_);
359         if (this->maxw_) {
360             *w = std::min(*w, this->maxw_);
361         }
362         if (this->maxh_) {
363             *h = std::min(*h, this->maxh_);
364         }
365     }
366     return *w != this->last_size_.width || *h != this->last_size_.height;
367 }
368 
369 // from dwm.c
updatesizehints()370 void Client::updatesizehints() {
371     long msize;
372     XSizeHints size;
373 
374     if (!XGetWMNormalHints(g_display, this->window_, &size, &msize)) {
375         /* size is uninitialized, ensure that size.flags aren't used */
376         size.flags = PSize;
377     }
378     if(size.flags & PBaseSize) {
379         this->basew_ = size.base_width;
380         this->baseh_ = size.base_height;
381     }
382     else if(size.flags & PMinSize) {
383         this->basew_ = size.min_width;
384         this->baseh_ = size.min_height;
385     } else {
386         this->basew_ = this->baseh_ = 0;
387     }
388     if(size.flags & PResizeInc) {
389         this->incw_ = size.width_inc;
390         this->inch_ = size.height_inc;
391     } else {
392         this->incw_ = this->inch_ = 0;
393     }
394     if(size.flags & PMaxSize) {
395         this->maxw_ = size.max_width;
396         this->maxh_ = size.max_height;
397     } else {
398         this->maxw_ = this->maxh_ = 0;
399     }
400     if(size.flags & PMinSize) {
401         this->minw_ = size.min_width;
402         this->minh_ = size.min_height;
403     }
404     else if(size.flags & PBaseSize) {
405         this->minw_ = size.base_width;
406         this->minh_ = size.base_height;
407     } else {
408         this->minw_ = this->minh_ = 0;
409     }
410     if(size.flags & PAspect) {
411         this->mina_ = (float)size.min_aspect.y / size.min_aspect.x;
412         this->maxa_ = (float)size.max_aspect.x / size.max_aspect.y;
413     } else {
414         this->maxa_ = this->mina_ = 0.0;
415     }
416     //this->isfixed = (this->maxw && this->minw && this->maxh && this->minh
417     //             && this->maxw == this->minw && this->maxh == this->minh);
418 }
419 
420 
421 
422 
send_configure()423 void Client::send_configure() {
424     auto last_inner_rect = dec->last_inner();
425     XConfigureEvent ce;
426     ce.type = ConfigureNotify;
427     ce.display = g_display;
428     ce.event = this->window_;
429     ce.window = this->window_;
430     ce.x = last_inner_rect.x;
431     ce.y = last_inner_rect.y;
432     ce.width = std::max(last_inner_rect.width, WINDOW_MIN_WIDTH);
433     ce.height = std::max(last_inner_rect.height, WINDOW_MIN_HEIGHT);
434     ce.border_width = 0;
435     ce.above = None;
436     ce.override_redirect = False;
437     XSendEvent(g_display, this->window_, False, StructureNotifyMask, (XEvent *)&ce);
438 }
439 
resize_floating(Monitor * m,bool isFocused)440 void Client::resize_floating(Monitor* m, bool isFocused) {
441     if (!m) {
442         return;
443     }
444     auto rect = this->float_size_;
445     rect.x += m->rect->x;
446     rect.y += m->rect->y;
447     rect.x += m->pad_left();
448     rect.y += m->pad_up();
449     // ensure position is on monitor
450     int space = g_monitor_float_treshold;
451     rect.x =
452         CLAMP(rect.x,
453               m->rect->x + m->pad_left() - rect.width + space,
454               m->rect->x + m->rect->width - m->pad_left() - m->pad_right() - space);
455     rect.y =
456         CLAMP(rect.y,
457               m->rect->y + m->pad_up() - rect.height + space,
458               m->rect->y + m->rect->height - m->pad_up() - m->pad_down() - space);
459     dec->resize_inner(rect, theme[Theme::Type::Floating](isFocused,urgent_()));
460     mostRecentThemeType = Theme::Type::Floating;
461 }
462 
outer_floating_rect()463 Rectangle Client::outer_floating_rect() {
464     return dec->inner_to_outer(float_size_);
465 }
466 
close_command(Input input,Output)467 int close_command(Input input, Output) {
468     string winid = "";
469     input >> winid; // try to read, use "" otherwise
470     auto window = get_window(winid);
471     if (window != 0) {
472         Ewmh::get().windowClose(window);
473     } else {
474         return HERBST_INVALID_ARGUMENT;
475     }
476     return 0;
477 }
478 
is_client_floated()479 bool Client::is_client_floated() {
480     if (floating_()) {
481         return true;
482     }
483     auto t = tag();
484     if (!t) {
485         return false;
486     } else {
487         return tag()->floating;
488     }
489 }
490 
requestClose()491 void Client::requestClose() { //! ask the client to close
492     ewmh.windowClose(window_);
493 }
494 
set_visible(bool visible)495 void Client::set_visible(bool visible) {
496     if (visible == this->visible_()) {
497         return;
498     }
499     if (visible) {
500         /* Grab the server to make sure that the frame window is mapped before
501            the client gets its MapNotify, i.e. to make sure the client is
502            _visible_ when it gets MapNotify. */
503         XGrabServer(g_display);
504         ewmh.windowUpdateWmState(this->window_, WmState::WSNormalState);
505         XMapWindow(g_display, this->window_);
506         XMapWindow(g_display, this->dec->decorationWindow());
507         XUngrabServer(g_display);
508     } else {
509         /* we unmap the client itself so that we can get MapRequest
510            events, and because the ICCCM tells us to! */
511         XUnmapWindow(g_display, this->dec->decorationWindow());
512         XUnmapWindow(g_display, this->window_);
513         ewmh.windowUpdateWmState(this->window_, WmState::WSIconicState);
514         this->ignore_unmaps_++;
515     }
516     this->visible_ = visible;
517 }
518 
519 // heavily inspired by dwm.c
set_urgent(bool state)520 void Client::set_urgent(bool state) {
521     if (this->urgent_() == state) {
522         // nothing to do
523         return;
524     }
525     if (this == manager.focus() && state == true) {
526         // ignore it if the focused client wants to be urgent
527         // because it will be removed by window_focus() anyway
528         return;
529     }
530     set_urgent_force(state);
531 }
532 
set_urgent_force(bool state)533 void Client::set_urgent_force(bool state) {
534     hook_emit({"urgent", state ? "on" : "off", WindowID(window_).str() });
535 
536     this->urgent_ = state;
537 
538     setup_border(this == manager.focus());
539 
540     XWMHints* wmh;
541     if (!(wmh = XGetWMHints(g_display, this->window_))) {
542         // just allocate new wm hints for the case the window
543         // did not have wm hints set before.
544         // here, we ignore what happens on insufficient memory
545         wmh = XAllocWMHints();
546     }
547     if (state) {
548         wmh->flags |= XUrgencyHint;
549     } else {
550         wmh->flags &= ~XUrgencyHint;
551     }
552 
553     XSetWMHints(g_display, this->window_, wmh);
554     XFree(wmh);
555     ewmh.updateWindowState(this);
556     // report changes to tags
557     tag_set_flags_dirty();
558 }
559 
560 // heavily inspired by dwm.c
update_wm_hints()561 void Client::update_wm_hints() {
562     XWMHints* wmh = XGetWMHints(g_display, this->window_);
563     if (!wmh) {
564         return;
565     }
566 
567     Client* focused_client = manager.focus();
568     if ((focused_client == this)
569         && wmh->flags & XUrgencyHint) {
570         // remove urgency hint if window is focused
571         wmh->flags &= ~XUrgencyHint;
572         XSetWMHints(g_display, this->window_, wmh);
573     } else {
574         bool newval = (wmh->flags & XUrgencyHint) ? true : false;
575         if (newval != this->urgent_()) {
576             this->urgent_ = newval;
577             this->setup_border(focused_client == this);
578             hook_emit({"urgent", urgent_() ? "on":"off", WindowID(window_).str()});
579             tag_set_flags_dirty();
580         }
581     }
582     if (wmh->flags & InputHint) {
583         this->neverfocus_ = !wmh->input;
584     } else {
585         this->neverfocus_ = false;
586     }
587     XFree(wmh);
588 }
589 
update_title()590 void Client::update_title() {
591     string newName = ewmh.getWindowTitle(window_);
592     bool changed = title_() != newName;
593     title_ = newName;
594     if (changed && get_current_client() == this) {
595         hook_emit({"window_title_changed", WindowID(window_).str(), title_()});
596     }
597 }
598 
get_current_client()599 Client* get_current_client() {
600     return Root::get()->monitors->focus()->tag->focusedClient();
601 }
602 
updateEwmhState()603 void Client::updateEwmhState() {
604     if (ewmhnotify_) {
605         ewmhfullscreen_ = fullscreen_();
606     }
607     ewmh.updateWindowState(this);
608 }
609 
getWindowClass()610 string Client::getWindowClass()
611 {
612     return ewmh.X().getClass(window_);
613 }
614 
getWindowInstance()615 string Client::getWindowInstance()
616 {
617     return ewmh.X().getInstance(window_);
618 }
619 
parentFrame()620 FrameLeaf* Client::parentFrame()
621 {
622     if (is_client_floated()) {
623         return nullptr;
624     } else {
625         return tag_->frame->findFrameWithClient(this).get();
626     }
627 }
628 
requestRedraw()629 void Client::requestRedraw()
630 {
631     if (tag_) {
632         needsRelayout.emit(tag_);
633     }
634 }
635 
636 /**
637  * \brief   Resolve a window description to a client
638  *
639  * \param   str     Describes the window: "" means the focused one, "urgent"
640  *                  resolves to a arbitrary urgent window, "0x..." just
641  *                  resolves to the given window given its hexadecimal window id,
642  *                  a decimal number its decimal window id.
643  * \return          Pointer to the resolved client, or null, if client not found
644  */
get_client(const char * str)645 Client* get_client(const char* str) {
646     if (!strcmp(str, "")) {
647         return get_current_client();
648     } else {
649         return Root::get()->clients()->client(str);
650     }
651 }
652 
653 /**
654  * \brief   Resolve a window description to a window
655  *
656  * \param   str     Describes the window: "" means the focused one, "urgent"
657  *                  resolves to a arbitrary urgent window, "0x..." just
658  *                  resolves to the given window given its hexadecimal window id,
659  *                  a decimal number its decimal window id.
660  * \return          Window id, or 0, if unconvertable
661  */
get_window(const string & str)662 Window get_window(const string& str) {
663     // managed window?
664     auto client = get_client(str.c_str());
665     if (client) {
666         return client->window_;
667     }
668 
669     // unmanaged window? try to convert from base 16 or base 10 at the same time
670     try {
671         return Converter<WindowID>::parse(str);
672     } catch (...) {
673         return 0;
674     }
675 }
676 
fuzzy_fix_initial_position()677 void Client::fuzzy_fix_initial_position() {
678     // find out the top-left-most position of the decoration,
679     // considering the current settings of possible floating decorations
680     int extreme_x = float_size_.x;
681     int extreme_y = float_size_.y;
682     const auto& t = theme[Theme::Type::Floating];
683     mostRecentThemeType = Theme::Type::Floating;
684     auto r = t.active.inner_rect_to_outline(float_size_);
685     extreme_x = std::min(extreme_x, r.x);
686     extreme_y = std::min(extreme_y, r.y);
687     r = t.normal.inner_rect_to_outline(float_size_);
688     extreme_x = std::min(extreme_x, r.x);
689     extreme_y = std::min(extreme_y, r.y);
690     r = t.urgent.inner_rect_to_outline(float_size_);
691     extreme_x = std::min(extreme_x, r.x);
692     extreme_y = std::min(extreme_y, r.y);
693     // if top left corner might be outside of the monitor, move it accordingly
694     if (extreme_x < 0) { float_size_.x += abs(extreme_x); }
695     if (extreme_y < 0) { float_size_.y += abs(extreme_y); }
696 }
697 
clear_properties()698 void Client::clear_properties() {
699     ewmh.clearClientProperties(window_);
700 }
701 
702 //! name of the tag on which the client is
tagName()703 string Client::tagName() {
704     // be safe during initialization phase and don't assume
705     // that tag is set.
706     return tag_ ? tag_->name() : "";
707 }
708 
decorationWindow()709 Window Client::decorationWindow() {
710     return dec->decorationWindow();
711 }
712 
713