]> icculus.org git repositories - mikachu/openbox.git/blob - util/epist/window.cc
better window focusing
[mikachu/openbox.git] / util / epist / window.cc
1 // -*- mode: C++; indent-tabs-mode: nil; -*-
2 // window.cc for Epistophy - a key handler for NETWM/EWMH window managers.
3 // Copyright (c) 2002 - 2002 Ben Jansens <ben at orodu.net>
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a
6 // copy of this software and associated documentation files (the "Software"),
7 // to deal in the Software without restriction, including without limitation
8 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 // and/or sell copies of the Software, and to permit persons to whom the
10 // Software is furnished to do so, subject to the following conditions:
11 //
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 // DEALINGS IN THE SOFTWARE.
22
23 #ifdef    HAVE_CONFIG_H
24 #  include "../../config.h"
25 #endif // HAVE_CONFIG_H
26
27 #include <iostream>
28
29 using std::cout;
30 using std::endl;
31 using std::hex;
32 using std::dec;
33
34 #include "epist.hh"
35 #include "screen.hh"
36 #include "window.hh"
37 #include "../../src/XAtom.hh"
38
39 XWindow::XWindow(epist *epist, screen *screen, Window window)
40   : _epist(epist), _screen(screen), _xatom(epist->xatom()), _window(window) {
41
42   _unmapped = false;
43
44   XSelectInput(_epist->getXDisplay(), _window,
45                PropertyChangeMask | StructureNotifyMask);
46
47   updateHints();
48   updateDimentions();
49   updateState();
50   updateDesktop();
51   updateTitle();
52   updateClass();
53
54   _epist->addWindow(this);
55 }
56
57
58 XWindow::~XWindow() {
59   if (! _unmapped)
60     XSelectInput(_epist->getXDisplay(), _window, None);
61   _epist->removeWindow(this);
62 }
63
64
65 void XWindow::updateDimentions() {
66   Window root, child;
67   int x, y;
68   unsigned int w, h, b, d;
69
70   if (XGetGeometry(_epist->getXDisplay(), _window, &root, &x, &y, &w, &h,
71                      &b, &d) &&
72       XTranslateCoordinates(_epist->getXDisplay(), _window, root, x, y,
73                             &x, &y, &child))
74     _rect.setRect(x, y, w, h);
75   else
76     _rect.setRect(0, 0, 1, 1);
77 }
78
79
80 void XWindow::updateHints() {
81   XSizeHints size;
82   long ret;
83
84   // defaults
85   _gravity = NorthWestGravity;
86   _inc_x = _inc_y = 1;
87   _base_x = _base_y = 0;
88   
89   if (XGetWMNormalHints(_epist->getXDisplay(), _window, &size, &ret)) {
90     if (size.flags & PWinGravity)
91       _gravity = size.win_gravity;
92     if (size.flags & PBaseSize) {
93       _base_x = size.base_width;
94       _base_y = size.base_height;
95     }
96     if (size.flags & PResizeInc) {
97       _inc_x = size.width_inc;
98       _inc_y = size.height_inc;
99     }
100   }
101 }
102
103
104 void XWindow::updateState() {
105   // set the defaults
106   _shaded = _iconic = _max_vert = _max_horz = false;
107   
108   unsigned long num = (unsigned) -1;
109   Atom *state;
110   if (! _xatom->getValue(_window, XAtom::net_wm_state, XAtom::atom,
111                          num, &state))
112     return;
113   for (unsigned long i = 0; i < num; ++i) {
114     if (state[i] == _xatom->getAtom(XAtom::net_wm_state_maximized_vert))
115       _max_vert = true;
116     if (state[i] == _xatom->getAtom(XAtom::net_wm_state_maximized_horz))
117       _max_horz = true;
118     if (state[i] == _xatom->getAtom(XAtom::net_wm_state_shaded))
119       _shaded = true;
120     if (state[i] == _xatom->getAtom(XAtom::net_wm_state_hidden))
121       _iconic = true;
122   }
123
124   delete [] state;
125 }
126
127
128 void XWindow::updateDesktop() {
129   if (! _xatom->getValue(_window, XAtom::net_wm_desktop, XAtom::cardinal,
130                          static_cast<unsigned long>(_desktop)))
131     _desktop = 0;
132 }
133
134
135 void XWindow::updateTitle() {
136   _title = "";
137   
138   // try netwm
139   if (! _xatom->getValue(_window, XAtom::net_wm_name, XAtom::utf8, _title)) {
140     // try old x stuff
141     _xatom->getValue(_window, XAtom::wm_name, XAtom::ansi, _title);
142   }
143
144   if (_title.empty())
145     _title = "Unnamed";
146 }
147
148
149 void XWindow::updateClass() {
150   // set the defaults
151   _app_name = _app_class = "";
152
153   XAtom::StringVect v;
154   unsigned long num = 2;
155
156   if (! _xatom->getValue(_window, XAtom::wm_class, XAtom::ansi, num, v))
157     return;
158
159   if (num > 0) _app_name = v[0];
160   if (num > 1) _app_class = v[1];
161 }
162
163
164 void XWindow::processEvent(const XEvent &e) {
165   assert(e.xany.window == _window);
166
167   switch (e.type) {
168   case ConfigureNotify:
169     updateDimentions();
170     break;
171   case PropertyNotify:
172     if (e.xproperty.atom == XA_WM_NORMAL_HINTS)
173       updateHints();
174     else if (e.xproperty.atom == _xatom->getAtom(XAtom::net_wm_state))
175       updateState();
176     else if (e.xproperty.atom == _xatom->getAtom(XAtom::net_wm_desktop))
177       updateDesktop();
178     else if (e.xproperty.atom == _xatom->getAtom(XAtom::net_wm_name) ||
179              e.xproperty.atom == _xatom->getAtom(XAtom::wm_name))
180       updateTitle();
181     else if (e.xproperty.atom == _xatom->getAtom(XAtom::wm_class))
182       updateClass();
183     break;
184   case DestroyNotify:
185   case UnmapNotify:
186     _unmapped = true;
187     break;
188   }
189 }
190
191
192 void XWindow::shade(const bool sh) const {
193   _xatom->sendClientMessage(_screen->rootWindow(), XAtom::net_wm_state,
194                             _window, (sh ? 1 : 0),
195                             _xatom->getAtom(XAtom::net_wm_state_shaded));
196 }
197
198
199 void XWindow::close() const {
200   _xatom->sendClientMessage(_screen->rootWindow(), XAtom::net_close_window,
201                             _window);
202 }
203
204
205 void XWindow::raise() const {
206   XRaiseWindow(_epist->getXDisplay(), _window);
207 }
208
209
210 void XWindow::lower() const {
211   XLowerWindow(_epist->getXDisplay(), _window);
212 }
213
214
215 void XWindow::iconify() const {
216   _xatom->sendClientMessage(_screen->rootWindow(), XAtom::wm_change_state,
217                             _window, IconicState);
218 }
219
220
221 void XWindow::focus() const {
222   // this will cause the window to be uniconified also
223   _xatom->sendClientMessage(_screen->rootWindow(), XAtom::net_active_window,
224                             _window);
225  
226   //XSetInputFocus(_epist->getXDisplay(), _window, None, CurrentTime);
227 }
228
229
230 void XWindow::sendTo(unsigned int dest) const {
231   _xatom->sendClientMessage(_screen->rootWindow(), XAtom::net_wm_desktop,
232                             _window, dest);
233 }
234
235
236 void XWindow::move(int x, int y) const {
237   // get the window's decoration sizes (margins)
238   Strut margins;
239   Window win = _window, parent, root, last = None;
240   Window *children = 0;
241   unsigned int nchildren;
242   XWindowAttributes wattr;
243   
244   while (XQueryTree(_epist->getXDisplay(), win, &root, &parent, &children,
245                     &nchildren)) {
246     if (children && nchildren > 0)
247       XFree(children); // don't care about the children
248
249     if (! parent) // no parent!?
250       return;
251
252     // if the parent window is the root window, stop here
253     if (parent == root)
254       break;
255
256     last = win;
257     win = parent;
258   }
259
260   if (! (XTranslateCoordinates(_epist->getXDisplay(), last, win, 0, 0,
261                                (int *) &margins.left,
262                                (int *) &margins.top,
263                                &parent) &&
264          XGetWindowAttributes(_epist->getXDisplay(), win, &wattr)))
265     return;
266
267   margins.right = wattr.width - _rect.width() - margins.left;
268   margins.bottom = wattr.height - _rect.height() - margins.top;
269
270   margins.left += wattr.border_width;
271   margins.right += wattr.border_width;
272   margins.top += wattr.border_width;
273   margins.bottom += wattr.border_width;
274
275   // this makes things work. why? i don't know. but you need them.
276   margins.right -= 2;
277   margins.bottom -= 2;
278   
279   // find the frame's reference position based on the window's gravity
280   switch (_gravity) {
281   case NorthWestGravity:
282     x -= margins.left;
283     y -= margins.top;
284     break;
285   case NorthGravity:
286     x += (margins.left + margins.right) / 2;
287     y -= margins.top;
288     break;
289   case NorthEastGravity:
290     x += margins.right;
291     y -= margins.top;
292   case WestGravity:
293     x -= margins.left;
294     y += (margins.top + margins.bottom) / 2;
295     break;
296   case CenterGravity:
297     x += (margins.left + margins.right) / 2;
298     y += (margins.top + margins.bottom) / 2;
299     break;
300   case EastGravity:
301     x += margins.right;
302     y += (margins.top + margins.bottom) / 2;
303   case SouthWestGravity:
304     x -= margins.left;
305     y += margins.bottom;
306     break;
307   case SouthGravity:
308     x += (margins.left + margins.right) / 2;
309     y += margins.bottom;
310     break;
311   case SouthEastGravity:
312     x += margins.right;
313     y += margins.bottom;
314     break;
315   default:
316     break;
317   }
318   
319   XMoveWindow(_epist->getXDisplay(), _window, x, y);
320 }
321
322
323 void XWindow::resize(unsigned int width, unsigned int height) const {
324   // resize in increments if requested by the window
325
326   unsigned int wdest = width / _inc_x * _inc_x + _base_x;
327   unsigned int hdest = height / _inc_y * _inc_y + _base_y;
328
329   if (width > wdest) {
330     while (width > wdest)
331       wdest += _inc_x;
332   } else {
333     while (width < wdest)
334       wdest -= _inc_x;
335   }
336   if (height > hdest) {
337     while (height > hdest)
338       hdest += _inc_y;
339   } else {
340     while (height < hdest)
341       hdest -= _inc_y;
342   }
343   
344   XResizeWindow(_epist->getXDisplay(), _window, wdest, hdest);
345 }
346
347
348 void XWindow::toggleMaximize(Max max) const {
349   switch (max) {
350   case Max_Full:
351     _xatom->
352       sendClientMessage(_screen->rootWindow(), XAtom::net_wm_state,
353                         _window, (_max_vert == _max_horz ? 2 : 1),
354                         _xatom->getAtom(XAtom::net_wm_state_maximized_horz),
355                         _xatom->getAtom(XAtom::net_wm_state_maximized_vert));
356     break;
357
358   case Max_Horz:
359     _xatom->
360       sendClientMessage(_screen->rootWindow(), XAtom::net_wm_state,
361                         _window, 2,
362                         _xatom->getAtom(XAtom::net_wm_state_maximized_horz));
363     break;
364
365   case Max_Vert:
366     _xatom->
367       sendClientMessage(_screen->rootWindow(), XAtom::net_wm_state,
368                         _window, 2,
369                         _xatom->getAtom(XAtom::net_wm_state_maximized_vert));
370     break;
371     
372   case Max_None:
373     assert(false);  // you should not do this. it is pointless and probly a bug
374     break;
375   }
376 }
377
378
379 void XWindow::maximize(Max max) const {
380   switch (max) {
381   case Max_None:
382     _xatom->
383       sendClientMessage(_screen->rootWindow(), XAtom::net_wm_state,
384                         _window, 0,
385                         _xatom->getAtom(XAtom::net_wm_state_maximized_horz),
386                         _xatom->getAtom(XAtom::net_wm_state_maximized_vert));
387     break;
388
389   case Max_Full:
390     _xatom->
391       sendClientMessage(_screen->rootWindow(), XAtom::net_wm_state,
392                         _window, 1,
393                         _xatom->getAtom(XAtom::net_wm_state_maximized_horz),
394                         _xatom->getAtom(XAtom::net_wm_state_maximized_vert));
395     break;
396
397   case Max_Horz:
398     _xatom->
399       sendClientMessage(_screen->rootWindow(), XAtom::net_wm_state,
400                         _window, 1,
401                         _xatom->getAtom(XAtom::net_wm_state_maximized_horz));
402     break;
403
404   case Max_Vert:
405     _xatom->
406       sendClientMessage(_screen->rootWindow(), XAtom::net_wm_state,
407                         _window, 1,
408                         _xatom->getAtom(XAtom::net_wm_state_maximized_vert));
409     break;
410   }
411 }