1
// WinClient.cc for Fluxbox - an X11 Window manager
2
// Copyright (c) 2003 - 2005 Henrik Kinnunen (fluxgen at fluxbox dot org)
4
// Permission is hereby granted, free of charge, to any person obtaining a
5
// copy of this software and associated documentation files (the "Software"),
6
// to deal in the Software without restriction, including without limitation
7
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
8
// and/or sell copies of the Software, and to permit persons to whom the
9
// Software is furnished to do so, subject to the following conditions:
11
// The above copyright notice and this permission notice shall be included in
12
// all copies or substantial portions of the Software.
14
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20
// DEALINGS IN THE SOFTWARE.
22
// $Id: WinClient.cc 3967 2005-04-29 02:52:36Z fluxgen $
24
#include "WinClient.hh"
33
#include "EventManager.hh"
34
#include "FbTk/I18n.hh"
35
#include "FbTk/MultLayers.hh"
51
WinClient::TransientWaitMap WinClient::s_transient_wait;
53
WinClient::WinClient(Window win, BScreen &screen, FluxboxWindow *fbwin):FbTk::FbWindow(win),
56
x(0), y(0), old_bw(0),
57
min_width(1), min_height(1),
58
max_width(0), max_height(0),
59
width_inc(1), height_inc(1),
60
min_aspect_x(0), min_aspect_y(0),
61
max_aspect_x(0), max_aspect_y(0),
62
base_width(1), base_height(1),
68
send_focus_message(false),
69
send_close_message(false),
71
m_title(""), m_icon_title(""),
72
m_class_name(""), m_instance_name(""),
75
m_focus_mode(F_PASSIVE),
76
m_diesig(*this), m_screen(screen),
79
updateBlackboxHints();
82
updateWMNormalHints();
86
Fluxbox::instance()->saveWindowSearch(win, this);
87
if (window_group != None)
88
Fluxbox::instance()->saveGroupSearch(window_group, this);
90
// search for this in transient waiting list
91
if (s_transient_wait.find(win) != s_transient_wait.end()) {
92
// Found transients that are waiting for this.
93
// For each transient that waits call updateTransientInfo
94
for_each(s_transient_wait[win].begin(),
95
s_transient_wait[win].end(),
96
mem_fun(&WinClient::updateTransientInfo));
97
// clear transient waiting list for this window
98
s_transient_wait.erase(win);
102
WinClient::~WinClient() {
104
cerr<<__FILE__<<"(~"<<__FUNCTION__<<")[this="<<this<<"]"<<endl;
107
FbTk::EventManager::instance()->remove(window());
112
m_win->removeClient(*this);
114
// this takes care of any focus issues
117
Fluxbox *fluxbox = Fluxbox::instance();
121
// clear transients and transient_for
123
if (transient_for != 0) {
124
assert(transient_for != this);
125
transient_for->transientList().remove(this);
129
while (!transients.empty()) {
130
transients.back()->transient_for = 0;
131
transients.pop_back();
133
// This fixes issue 1 (see WinClient.hh):
134
// If transients die before the transient_for is created
135
removeTransientFromWaitingList();
136
s_transient_wait.erase(window());
139
screen().removeNetizen(window());
141
if (window_group != 0) {
142
fluxbox->removeGroupSearch(window_group);
149
if (m_blackbox_hint != 0)
150
XFree(m_blackbox_hint);
153
fluxbox->removeWindowSearch(window());
158
bool WinClient::acceptsFocus() const {
159
return (m_focus_mode == F_LOCALLYACTIVE ||
160
m_focus_mode == F_PASSIVE ||
161
m_focus_mode == F_GLOBALLYACTIVE && send_focus_message);
164
bool WinClient::sendFocus() {
165
if (!send_focus_message)
168
cerr<<"WinClient::"<<__FUNCTION__<<": this = "<<this<<
169
" window = 0x"<<hex<<window()<<dec<<endl;
174
ce.xclient.type = ClientMessage;
175
ce.xclient.message_type = FbAtoms::instance()->getWMProtocolsAtom();
176
ce.xclient.display = display();
177
ce.xclient.window = window();
178
ce.xclient.format = 32;
179
ce.xclient.data.l[0] = FbAtoms::instance()->getWMTakeFocusAtom();
180
ce.xclient.data.l[1] = Fluxbox::instance()->getLastTime();
181
ce.xclient.data.l[2] = 0l;
182
ce.xclient.data.l[3] = 0l;
183
ce.xclient.data.l[4] = 0l;
185
XSendEvent(display(), window(), false, NoEventMask, &ce);
189
void WinClient::sendClose(bool forceful) {
190
if (forceful || !send_close_message)
191
XKillClient(display(), window());
193
// send WM_DELETE message
194
// fill in XClientMessage structure for delete message
196
ce.xclient.type = ClientMessage;
197
ce.xclient.message_type = FbAtoms::instance()->getWMProtocolsAtom();
198
ce.xclient.display = display();
199
ce.xclient.window = window();
200
ce.xclient.format = 32;
201
ce.xclient.data.l[0] = FbAtoms::instance()->getWMDeleteAtom();
202
ce.xclient.data.l[1] = CurrentTime;
203
ce.xclient.data.l[2] = 0l;
204
ce.xclient.data.l[3] = 0l;
205
ce.xclient.data.l[4] = 0l;
206
// send event delete message to client window
207
XSendEvent(display(), window(), false, NoEventMask, &ce);
211
bool WinClient::getAttrib(XWindowAttributes &attr) const {
212
return XGetWindowAttributes(display(), window(), &attr);
215
bool WinClient::getWMName(XTextProperty &textprop) const {
216
return XGetWMName(display(), window(), &textprop);
219
bool WinClient::getWMIconName(XTextProperty &textprop) const {
220
return XGetWMName(display(), window(), &textprop);
223
const std::string &WinClient::getWMClassName() const {
224
return m_instance_name;
227
const std::string &WinClient::getWMClassClass() const {
231
void WinClient::updateWMClassHint() {
233
if (XGetClassHint(display(), window(), &ch) == 0) {
235
cerr<<"WinClient: Failed to read class hint!"<<endl;
239
if (ch.res_name != 0) {
240
m_instance_name = const_cast<char *>(ch.res_name);
244
m_instance_name = "";
246
if (ch.res_class != 0) {
247
m_class_name = const_cast<char *>(ch.res_class);
255
void WinClient::updateTransientInfo() {
257
cerr<<__FUNCTION__<<": m_win = "<<m_win<<endl;
263
// remove this from parent
264
if (transientFor() != 0) {
265
transientFor()->transientList().remove(this);
269
// determine if this is a transient window
271
if (!XGetTransientForHint(display(), window(), &win)) {
273
cerr<<__FUNCTION__<<": window() = 0x"<<hex<<window()<<dec<<"Failed to read transient for hint."<<endl;
278
// we can't be transient to ourself
279
if (win == window()) {
281
cerr<<__FUNCTION__<<": transient to ourself"<<endl;
286
if (win != None && m_win->screen().rootWindow() == win) {
287
// transient for root window... = transient for group
288
// I don't think we are group-aware yet
293
transient_for = Fluxbox::instance()->searchWindow(win);
294
// if we did not find a transient WinClient but still
295
// have a transient X window, then we have to put the
296
// X transient_for window in a waiting list and update this clients transient
297
// list later when the transient_for has a Winclient
298
if (!transient_for) {
299
// We might also already waiting for an old transient_for;
301
// this call fixes issue 2:
302
// If transients changes to new transient_for before the old transient_for is created.
303
// (see comment in WinClient.hh)
305
removeTransientFromWaitingList();
307
s_transient_wait[win].push_back(this);
312
cerr<<__FUNCTION__<<": transient_for window = 0x"<<hex<<win<<dec<<endl;
313
cerr<<__FUNCTION__<<": transient_for = "<<transient_for<<endl;
315
// make sure we don't have deadlock loop in transient chain
316
for (WinClient *w = this; w != 0; w = w->transient_for) {
317
if (w == w->transient_for) {
318
w->transient_for = 0;
323
if (transientFor() != 0) {
324
// we need to add ourself to the right client in
325
// the transientFor() window so we search client
326
transient_for->transientList().push_back(this);
328
if (transientFor()->fbwindow() && transientFor()->fbwindow()->isStuck())
335
void WinClient::updateTitle() {
336
// why 512? very very long wmnames seem to either
337
// crash fluxbox or to make it have high cpuload
339
// http://www.securityfocus.com/archive/1/382398/2004-11-24/2004-11-30/2
341
// TODO: - find out why this mostly happens when using xft-fonts
342
// - why other windowmanagers (pekwm/pwm3/openbox etc) are
345
// the limitation to 512 chars only avoids running in that trap
346
m_title = string(Xutil::getWMName(window()), 0, 512);
349
void WinClient::updateIconTitle() {
350
XTextProperty text_prop;
354
if (getWMIconName(text_prop)) {
355
if (text_prop.value && text_prop.nitems > 0) {
356
if (text_prop.encoding != XA_STRING) {
357
text_prop.nitems = strlen((char *) text_prop.value);
359
if (XmbTextPropertyToTextList(display(), &text_prop,
360
&list, &num) == Success &&
362
m_icon_title = (char *)*list;
363
XFreeStringList(list);
365
m_icon_title = text_prop.value ? (char *)text_prop.value : "";
367
m_icon_title = text_prop.value ? (char *)text_prop.value : "";
370
XFree((char *) text_prop.value);
372
m_icon_title = title();
374
m_icon_title = title();
378
void WinClient::saveBlackboxAttribs(FluxboxWindow::BlackboxAttributes &blackbox_attribs) {
379
changeProperty(FbAtoms::instance()->getFluxboxAttributesAtom(),
380
XA_CARDINAL, 32, PropModeReplace,
381
(unsigned char *)&blackbox_attribs,
382
FluxboxWindow::PropBlackboxAttributesElements
386
void WinClient::setFluxboxWindow(FluxboxWindow *win) {
390
void WinClient::updateBlackboxHints() {
393
unsigned long num, len;
394
FbAtoms *atoms = FbAtoms::instance();
396
if (m_blackbox_hint) {
397
XFree(m_blackbox_hint);
401
if (property(atoms->getFluxboxHintsAtom(), 0,
402
PropBlackboxHintsElements, False,
403
atoms->getFluxboxHintsAtom(), &atom_return,
405
(unsigned char **) &m_blackbox_hint) &&
408
if (num != (unsigned)PropBlackboxHintsElements) {
409
XFree(m_blackbox_hint);
415
void WinClient::updateMWMHints() {
418
unsigned long num = 0, len = 0;
424
Atom motif_wm_hints = FbAtoms::instance()->getMWMHintsAtom();
426
if (!(property(motif_wm_hints, 0,
427
PropMwmHintsElements, false,
428
motif_wm_hints, &atom_return,
430
(unsigned char **) &m_mwm_hint) &&
432
if (num != static_cast<unsigned int>(PropMwmHintsElements)) {
440
void WinClient::updateWMHints() {
441
XWMHints *wmhint = XGetWMHints(display(), window());
443
m_focus_mode = F_PASSIVE;
445
initial_state = NormalState;
447
wm_hint_flags = wmhint->flags;
450
*---------------------------------------------
451
* Input Model Input Field WM_TAKE_FOCUS
452
*---------------------------------------------
453
* No Input False Absent
454
* Passive True Absent
455
* Locally Active True Present
456
* Globally Active False Present
457
*---------------------------------------------
458
* Here: WM_TAKE_FOCUS = send_focus_message
459
* Input Field = wmhint->input
460
* Input Model = m_focus_mode
462
if (wmhint->flags & InputHint) {
464
if (send_focus_message)
465
m_focus_mode = F_LOCALLYACTIVE;
467
m_focus_mode = F_PASSIVE;
469
if (send_focus_message)
470
m_focus_mode = F_GLOBALLYACTIVE;
472
m_focus_mode = F_NOINPUT;
474
} else // InputHint not present: ignoring send_focus_message and assuming F_PASSIVE
475
m_focus_mode = F_PASSIVE;
477
if (wmhint->flags & StateHint)
478
initial_state = wmhint->initial_state;
480
initial_state = NormalState;
482
if (wmhint->flags & WindowGroupHint) {
484
window_group = wmhint->window_group;
493
void WinClient::updateWMNormalHints() {
496
if (! XGetWMNormalHints(display(), window(), &sizehint, &icccm_mask)) {
497
min_width = min_height =
498
base_width = base_height =
499
width_inc = height_inc = 1;
500
max_width = 0; // unbounded
502
min_aspect_x = min_aspect_y =
503
max_aspect_x = max_aspect_y = 0;
504
m_win_gravity = NorthWestGravity;
506
normal_hint_flags = sizehint.flags;
508
if (sizehint.flags & PMinSize) {
509
min_width = sizehint.min_width;
510
min_height = sizehint.min_height;
511
if (!(sizehint.flags & PBaseSize)) {
512
base_width = min_width;
513
base_height = min_height;
516
min_width = min_height = 1;
517
base_width = base_height = 0;
520
if (sizehint.flags & PBaseSize) {
521
base_width = sizehint.base_width;
522
base_height = sizehint.base_height;
523
if (!(sizehint.flags & PMinSize)) {
524
min_width = base_width;
525
min_height = base_height;
527
} // default set in PMinSize
529
if (sizehint.flags & PMaxSize) {
530
max_width = sizehint.max_width;
531
max_height = sizehint.max_height;
533
max_width = 0; // unbounded
537
if (sizehint.flags & PResizeInc) {
538
width_inc = sizehint.width_inc;
539
height_inc = sizehint.height_inc;
541
width_inc = height_inc = 1;
543
if (sizehint.flags & PAspect) {
544
min_aspect_x = sizehint.min_aspect.x;
545
min_aspect_y = sizehint.min_aspect.y;
546
max_aspect_x = sizehint.max_aspect.x;
547
max_aspect_y = sizehint.max_aspect.y;
549
min_aspect_x = min_aspect_y =
550
max_aspect_x = max_aspect_y = 0;
552
if (sizehint.flags & PWinGravity)
553
m_win_gravity = sizehint.win_gravity;
555
m_win_gravity = NorthWestGravity;
560
Window WinClient::getGroupLeftWindow() const {
563
unsigned long num = 0, len = 0;
564
Atom group_left_hint = XInternAtom(display(), "_FLUXBOX_GROUP_LEFT", False);
567
if (property(group_left_hint, 0,
569
XA_WINDOW, &atom_return,
571
(unsigned char **) &data) &&
586
void WinClient::setGroupLeftWindow(Window win) {
587
Atom group_left_hint = XInternAtom(display(), "_FLUXBOX_GROUP_LEFT", False);
588
changeProperty(group_left_hint, XA_WINDOW, 32,
589
PropModeReplace, (unsigned char *) &win, 1);
592
bool WinClient::hasGroupLeftWindow() const {
593
// try to find _FLUXBOX_GROUP_LEFT atom in window
594
// if we have one then we have a group left window
597
unsigned long num = 0, len = 0;
598
Atom group_left_hint = XInternAtom(display(), "_FLUXBOX_GROUP_LEFT", False);
601
if (property(group_left_hint, 0,
603
XA_WINDOW, &atom_return,
605
(unsigned char **) &data) &&
617
void WinClient::addModal() {
620
transient_for->addModal();
623
void WinClient::removeModal() {
626
transient_for->removeModal();
629
bool WinClient::validateClient() const {
630
FbTk::App::instance()->sync(false);
633
if (( XCheckTypedWindowEvent(display(), window(), DestroyNotify, &e) ||
634
XCheckTypedWindowEvent(display(), window(), UnmapNotify, &e))
635
&& XPutBackEvent(display(), &e)) {
636
Fluxbox::instance()->ungrab();
643
void WinClient::setStrut(Strut *strut) {
648
void WinClient::clearStrut() {
650
screen().clearStrut(m_strut);
651
screen().updateAvailableWorkspaceArea();
656
bool WinClient::focus() {
660
return m_win->setCurrentClient(*this, true);
663
void WinClient::updateWMProtocols() {
666
FbAtoms *fbatoms = FbAtoms::instance();
668
if (XGetWMProtocols(display(), window(), &proto, &num_return)) {
671
send_focus_message = false;
672
send_close_message = false;
673
// could be added to netizens twice...
674
for (int i = 0; i < num_return; ++i) {
675
if (proto[i] == fbatoms->getWMDeleteAtom())
676
send_close_message = true;
677
else if (proto[i] == fbatoms->getWMTakeFocusAtom())
678
send_focus_message = true;
679
else if (proto[i] == fbatoms->getFluxboxStructureMessagesAtom())
680
screen().addNetizen(window());
685
m_win->updateFunctions();
688
cerr<<"Warning: Failed to read WM Protocols. "<<endl;
695
Note that its slightly simplified in that only the
696
line gradient is given - this is because for aspect
697
ratios, we always have the line going through the origin
699
* Based on this formula:
700
http://astronomy.swin.edu.au/~pbourke/geometry/pointline/
702
Note that a gradient from origin goes through ( grad , 1 )
705
void closestPointToLine(double &ret_x, double &ret_y,
706
double point_x, double point_y,
708
double u = (point_x * gradient + point_y) /
709
(gradient*gradient + 1);
716
* Changes width and height to the nearest (lower) value
717
* that conforms to it's size hints.
719
* display_* give the values that would be displayed
720
* to the user when resizing.
721
* We use pointers for display_* since they are optional.
723
* See ICCCM section 4.1.2.3
725
void WinClient::applySizeHints(int &width, int &height,
726
int *display_width, int *display_height) {
728
int i = width, j = height;
730
// Check minimum size
731
if (width < 0 || width < static_cast<signed>(min_width))
734
if (height < 0 || height < static_cast<signed>(min_height))
737
// Check maximum size
738
if (max_width > 0 && width > static_cast<signed>(max_width))
741
if (max_height > 0 && height > static_cast<signed>(max_height))
744
// we apply aspect ratios before incrementals
745
// Too difficult to exactly satisfy both incremental+aspect
746
// in most situations
747
// (they really shouldn't happen at the same time anyway).
749
/* aspect ratios are applied exclusive to the base_width
751
* min_aspect_x width max_aspect_x
752
* ------------ < ------- < ------------
753
* min_aspect_y height max_aspect_y
755
* beware of integer maximum (so I'll use doubles instead and divide)
757
* The trick is how to get back to the aspect ratio with minimal
758
* change - do we modify x, y or both?
759
* A: we minimise the distance between the current point, and
760
* the target aspect ratio (consider them as x,y coordinates)
761
* Consider that the aspect ratio is a line, and the current
762
* w/h is a point, so we're just using the formula for
763
* shortest distance from a point to a line!
766
if (min_aspect_y > 0 && max_aspect_y > 0 &&
767
(height - base_height) > 0) {
768
double widthd = static_cast<double>(width - base_width);
769
double heightd = static_cast<double>(height - base_height);
771
double min = static_cast<double>(min_aspect_x) /
772
static_cast<double>(min_aspect_y);
774
double max = static_cast<double>(max_aspect_x) /
775
static_cast<double>(max_aspect_y);
777
double actual = widthd / heightd;
779
if (max > 0 && min > 0 && actual > 0) { // don't even try otherwise
780
bool changed = false;
783
closestPointToLine(widthd, heightd, widthd, heightd, min);
784
} else if (actual > max) {
786
closestPointToLine(widthd, heightd, widthd, heightd, max);
790
width = static_cast<int>(widthd) + base_width;
791
height = static_cast<int>(heightd) + base_height;
796
// enforce incremental size limits, wrt base size
797
// only calculate this if we really need to
798
i = (width - base_width) / width_inc;
799
width = i*width_inc + base_width;
801
j = (height - base_height) / height_inc;
802
height = j*height_inc + base_height;
811
void WinClient::removeTransientFromWaitingList() {
813
// holds the windows that dont have empty
814
// transient waiting list
815
std::list<Window> remove_list;
817
// The worst case complexity is huge, but since we usually do not (virtualy never)
818
// have a large transient waiting list the time spent here is neglectable
819
TransientWaitMap::iterator t_it = s_transient_wait.begin();
820
TransientWaitMap::iterator t_it_end = s_transient_wait.end();
821
for (; t_it != t_it_end; ++t_it) {
822
(*t_it).second.remove(this);
823
// if the list is empty, add it to remove list
824
// so we can erase it later
825
if ((*t_it).second.empty())
826
remove_list.push_back((*t_it).first);
829
// erase empty waiting lists
830
std::list<Window>::iterator it = remove_list.begin();
831
std::list<Window>::iterator it_end = remove_list.end();
832
for (; it != it_end; ++it)
833
s_transient_wait.erase(*it);