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