]> icculus.org git repositories - mikachu/openbox.git/blob - util/epist/window.cc
implement window resizing. using resizing incrememnts if the window has requested...
[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 also unshade the window..
223   _xatom->sendClientMessage(_screen->rootWindow(), XAtom::net_active_window,
224                             _window);
225 }
226
227
228 void XWindow::sendTo(unsigned int dest) const {
229   _xatom->sendClientMessage(_screen->rootWindow(), XAtom::net_wm_desktop,
230                             _window, dest);
231 }
232
233
234 void XWindow::move(int x, int y) const {
235   // get the window's decoration sizes (margins)
236   Strut margins;
237   Window win = _window, parent, root, last = None;
238   Window *children = 0;
239   unsigned int nchildren;
240   XWindowAttributes wattr;
241   
242   while (XQueryTree(_epist->getXDisplay(), win, &root, &parent, &children,
243                     &nchildren)) {
244     if (children && nchildren > 0)
245       XFree(children); // don't care about the children
246
247     if (! parent) // no parent!?
248       return;
249
250     // if the parent window is the root window, stop here
251     if (parent == root)
252       break;
253
254     last = win;
255     win = parent;
256   }
257
258   if (! (XTranslateCoordinates(_epist->getXDisplay(), last, win, 0, 0,
259                                (int *) &margins.left,
260                                (int *) &margins.top,
261                                &parent) &&
262          XGetWindowAttributes(_epist->getXDisplay(), win, &wattr)))
263     return;
264
265   margins.right = wattr.width - _rect.width() - margins.left;
266   margins.bottom = wattr.height - _rect.height() - margins.top;
267
268   margins.left += wattr.border_width;
269   margins.right += wattr.border_width;
270   margins.top += wattr.border_width;
271   margins.bottom += wattr.border_width;
272
273   // this makes things work. why? i don't know. but you need them.
274   margins.right -= 2;
275   margins.bottom -= 2;
276   
277   // find the frame's reference position based on the window's gravity
278   switch (_gravity) {
279   case NorthWestGravity:
280     x -= margins.left;
281     y -= margins.top;
282     break;
283   case NorthGravity:
284     x += (margins.left + margins.right) / 2;
285     y -= margins.top;
286     break;
287   case NorthEastGravity:
288     x += margins.right;
289     y -= margins.top;
290   case WestGravity:
291     x -= margins.left;
292     y += (margins.top + margins.bottom) / 2;
293     break;
294   case CenterGravity:
295     x += (margins.left + margins.right) / 2;
296     y += (margins.top + margins.bottom) / 2;
297     break;
298   case EastGravity:
299     x += margins.right;
300     y += (margins.top + margins.bottom) / 2;
301   case SouthWestGravity:
302     x -= margins.left;
303     y += margins.bottom;
304     break;
305   case SouthGravity:
306     x += (margins.left + margins.right) / 2;
307     y += margins.bottom;
308     break;
309   case SouthEastGravity:
310     x += margins.right;
311     y += margins.bottom;
312     break;
313   default:
314     break;
315   }
316   
317   XMoveWindow(_epist->getXDisplay(), _window, x, y);
318 }
319
320
321 void XWindow::resize(unsigned int width, unsigned int height) const {
322   // resize in increments if requested by the window
323
324   unsigned int wdest = width / _inc_x * _inc_x + _base_x;
325   unsigned int hdest = height / _inc_y * _inc_y + _base_y;
326
327   if (width > wdest) {
328     while (width > wdest)
329       wdest += _inc_x;
330   } else {
331     while (width < wdest)
332       wdest -= _inc_x;
333   }
334   if (height > hdest) {
335     while (height > hdest)
336       hdest += _inc_y;
337   } else {
338     while (height < hdest)
339       hdest -= _inc_y;
340   }
341   
342   XResizeWindow(_epist->getXDisplay(), _window, wdest, hdest);
343 }
344
345
346 void XWindow::toggleMaximize(Max max) const {
347   switch (max) {
348   case Max_Full:
349     _xatom->
350       sendClientMessage(_screen->rootWindow(), XAtom::net_wm_state,
351                         _window, (_max_vert == _max_horz ? 2 : 1),
352                         _xatom->getAtom(XAtom::net_wm_state_maximized_horz),
353                         _xatom->getAtom(XAtom::net_wm_state_maximized_vert));
354     break;
355
356   case Max_Horz:
357     _xatom->
358       sendClientMessage(_screen->rootWindow(), XAtom::net_wm_state,
359                         _window, 2,
360                         _xatom->getAtom(XAtom::net_wm_state_maximized_horz));
361     break;
362
363   case Max_Vert:
364     _xatom->
365       sendClientMessage(_screen->rootWindow(), XAtom::net_wm_state,
366                         _window, 2,
367                         _xatom->getAtom(XAtom::net_wm_state_maximized_vert));
368     break;
369     
370   case Max_None:
371     assert(false);  // you should not do this. it is pointless and probly a bug
372     break;
373   }
374 }
375
376
377 void XWindow::maximize(Max max) const {
378   switch (max) {
379   case Max_None:
380     _xatom->
381       sendClientMessage(_screen->rootWindow(), XAtom::net_wm_state,
382                         _window, 0,
383                         _xatom->getAtom(XAtom::net_wm_state_maximized_horz),
384                         _xatom->getAtom(XAtom::net_wm_state_maximized_vert));
385     break;
386
387   case Max_Full:
388     _xatom->
389       sendClientMessage(_screen->rootWindow(), XAtom::net_wm_state,
390                         _window, 1,
391                         _xatom->getAtom(XAtom::net_wm_state_maximized_horz),
392                         _xatom->getAtom(XAtom::net_wm_state_maximized_vert));
393     break;
394
395   case Max_Horz:
396     _xatom->
397       sendClientMessage(_screen->rootWindow(), XAtom::net_wm_state,
398                         _window, 1,
399                         _xatom->getAtom(XAtom::net_wm_state_maximized_horz));
400     break;
401
402   case Max_Vert:
403     _xatom->
404       sendClientMessage(_screen->rootWindow(), XAtom::net_wm_state,
405                         _window, 1,
406                         _xatom->getAtom(XAtom::net_wm_state_maximized_vert));
407     break;
408   }
409 }