resizing works and whatnot
[mikachu/openbox.git] / src / screen.cc
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2
3 #ifdef HAVE_CONFIG_H
4 # include "../config.h"
5 #endif
6
7 extern "C" {
8 #ifdef    HAVE_STDIO_H
9 #  include <stdio.h>
10 #endif // HAVE_STDIO_H
11
12 #ifdef    HAVE_UNISTD_H
13 #  include <sys/types.h>
14 #  include <unistd.h>
15 #endif // HAVE_UNISTD_H
16
17 #include "gettext.h"
18 #define _(str) gettext(str)
19 }
20
21 #include "screen.hh"
22 #include "client.hh"
23 #include "openbox.hh"
24 #include "frame.hh"
25 #include "otk/display.hh"
26
27 static bool running;
28 static int anotherWMRunning(Display *display, XErrorEvent *) {
29   printf(_("Another window manager already running on display %s.\n"),
30          DisplayString(display));
31   running = true;
32   return -1;
33 }
34
35
36 namespace ob {
37
38
39 OBScreen::OBScreen(int screen, const otk::Configuration &config)
40   : _number(screen),
41     _root(screen)
42 {
43   assert(screen >= 0); assert(screen < ScreenCount(otk::OBDisplay::display));
44   _info = otk::OBDisplay::screenInfo(screen);
45
46   ::running = false;
47   XErrorHandler old = XSetErrorHandler(::anotherWMRunning);
48   XSelectInput(otk::OBDisplay::display, _info->getRootWindow(),
49                OBScreen::event_mask);
50   XSync(otk::OBDisplay::display, false);
51   XSetErrorHandler(old);
52
53   _managed = !::running;
54   if (! _managed) return; // was unable to manage the screen
55
56   printf(_("Managing screen %d: visual 0x%lx, depth %d\n"),
57          _number, XVisualIDFromVisual(_info->getVisual()), _info->getDepth());
58
59   Openbox::instance->property()->set(_info->getRootWindow(),
60                                      otk::OBProperty::openbox_pid,
61                                      otk::OBProperty::Atom_Cardinal,
62                                      (unsigned long) getpid());
63
64   // set the mouse cursor for the root window (the default cursor)
65   XDefineCursor(otk::OBDisplay::display, _info->getRootWindow(),
66                 Openbox::instance->cursors().session);
67
68   // initialize the shit that is used for all drawing on the screen
69   _image_control = new otk::BImageControl(Openbox::instance->timerManager(),
70                                           _info, true);
71   _image_control->installRootColormap();
72   _root_cmap_installed = True;
73
74   // initialize the screen's style
75   _style.setImageControl(_image_control);
76   _style.load(config);
77
78   
79   // Set the netwm atoms for geomtery and viewport
80   unsigned long geometry[] = { _info->getWidth(),
81                                _info->getHeight() };
82   Openbox::instance->property()->set(_info->getRootWindow(),
83                                      otk::OBProperty::net_desktop_geometry,
84                                      otk::OBProperty::Atom_Cardinal,
85                                      geometry, 2);
86   unsigned long viewport[] = { 0, 0 };
87   Openbox::instance->property()->set(_info->getRootWindow(),
88                                      otk::OBProperty::net_desktop_viewport,
89                                      otk::OBProperty::Atom_Cardinal,
90                                      viewport, 2);
91
92   // these may be further updated if any pre-existing windows are found in
93   // the manageExising() function
94   setClientList();     // initialize the client lists, which will be empty
95   calcArea();          // initialize the available working area
96 }
97
98
99 OBScreen::~OBScreen()
100 {
101   if (! _managed) return;
102
103   // unmanage all windows
104   while (!_clients.empty())
105     unmanageWindow(_clients.front());
106
107   delete _image_control;
108 }
109
110
111 void OBScreen::manageExisting()
112 {
113   unsigned int i, j, nchild;
114   Window r, p, *children;
115   XQueryTree(otk::OBDisplay::display, _info->getRootWindow(), &r, &p,
116              &children, &nchild);
117
118   // preen the window list of all icon windows... for better dockapp support
119   for (i = 0; i < nchild; i++) {
120     if (children[i] == None) continue;
121
122     XWMHints *wmhints = XGetWMHints(otk::OBDisplay::display,
123                                     children[i]);
124
125     if (wmhints) {
126       if ((wmhints->flags & IconWindowHint) &&
127           (wmhints->icon_window != children[i])) {
128         for (j = 0; j < nchild; j++) {
129           if (children[j] == wmhints->icon_window) {
130             children[j] = None;
131             break;
132           }
133         }
134       }
135
136       XFree(wmhints);
137     }
138   }
139
140   // manage shown windows
141   for (i = 0; i < nchild; ++i) {
142     if (children[i] == None)
143       continue;
144
145     XWindowAttributes attrib;
146     if (XGetWindowAttributes(otk::OBDisplay::display, children[i], &attrib)) {
147       if (attrib.override_redirect) continue;
148
149       if (attrib.map_state != IsUnmapped) {
150         manageWindow(children[i]);
151       }
152     }
153   }
154
155   XFree(children);
156 }
157
158
159 //! Adds a window's strut to the screen's list of reserved spaces
160 void OBScreen::addStrut(otk::Strut *strut)
161 {
162   _struts.push_back(strut);
163 }
164
165
166 //! Removes a window's strut from the screen's list of reserved spaces
167 void OBScreen::removeStrut(otk::Strut *strut)
168 {
169   _struts.remove(strut);
170 }
171
172
173 void OBScreen::calcArea()
174 {
175   otk::Rect old_area = _area;
176
177 /*
178 #ifdef    XINERAMA
179   // reset to the full areas
180   if (isXineramaActive())
181     xineramaUsableArea = getXineramaAreas();
182 #endif // XINERAMA
183 */
184   
185   /* these values represent offsets from the screen edge
186    * we look for the biggest offset on each edge and then apply them
187    * all at once
188    * do not be confused by the similarity to the names of Rect's members
189    */
190   unsigned int current_left = 0, current_right = 0, current_top = 0,
191     current_bottom = 0;
192
193   StrutList::const_iterator it = _struts.begin(), end = _struts.end();
194
195   for(; it != end; ++it) {
196     otk::Strut *strut = *it;
197     if (strut->left > current_left)
198       current_left = strut->left;
199     if (strut->top > current_top)
200       current_top = strut->top;
201     if (strut->right > current_right)
202       current_right = strut->right;
203     if (strut->bottom > current_bottom)
204       current_bottom = strut->bottom;
205   }
206
207   _area.setRect(current_left, current_top,
208                 _info->getWidth() - (current_left + current_right),
209                 _info->getHeight() - (current_top + current_bottom));
210
211 /*
212 #ifdef    XINERAMA
213   if (isXineramaActive()) {
214     // keep each of the ximerama-defined areas inside the strut
215     RectList::iterator xit, xend = xineramaUsableArea.end();
216     for (xit = xineramaUsableArea.begin(); xit != xend; ++xit) {
217       if (xit->x() < usableArea.x()) {
218         xit->setX(usableArea.x());
219         xit->setWidth(xit->width() - usableArea.x());
220       }
221       if (xit->y() < usableArea.y()) {
222         xit->setY(usableArea.y());
223         xit->setHeight(xit->height() - usableArea.y());
224       }
225       if (xit->x() + xit->width() > usableArea.width())
226         xit->setWidth(usableArea.width() - xit->x());
227       if (xit->y() + xit->height() > usableArea.height())
228         xit->setHeight(usableArea.height() - xit->y());
229     }
230   }
231 #endif // XINERAMA
232 */
233   
234   if (old_area != _area)
235     // XXX: re-maximize windows
236
237   setWorkArea();
238 }
239
240
241 void OBScreen::setClientList()
242 {
243   Window *windows;
244
245   // create an array of the window ids
246   if (_clients.size() > 0) {
247     Window *win_it;
248     
249     windows = new Window[_clients.size()];
250     win_it = windows;
251     ClientList::const_iterator it = _clients.begin();
252     const ClientList::const_iterator end = _clients.end();
253     for (; it != end; ++it, ++win_it)
254       *win_it = (*it)->window();
255   } else
256     windows = (Window*) 0;
257
258   Openbox::instance->property()->set(_info->getRootWindow(),
259                                      otk::OBProperty::net_client_list,
260                                      otk::OBProperty::Atom_Window,
261                                      windows, _clients.size());
262
263   if (_clients.size())
264     delete [] windows;
265
266   setStackingList();
267 }
268
269
270 void OBScreen::setStackingList()
271 {
272   // The below comment is wrong now hopefully :> but ill keep it here for
273   // reference anyways
274   /*
275     Get the stacking order from all of the workspaces.
276     We start with the current workspace so that the sticky windows will be
277     in the right order on the current workspace.
278   */
279   /*
280   Openbox::instance->property()->set(_info->getRootWindow(),
281                                      otk::OBProperty::net_client_list_stacking,
282                                      otk::OBProperty::Atom_Window,
283                                      _stacking, _stacking.size());
284   */
285 }
286
287
288 void OBScreen::setWorkArea() {
289   unsigned long area[] = { _area.x(), _area.y(),
290                            _area.width(), _area.height() };
291   Openbox::instance->property()->set(_info->getRootWindow(),
292                                      otk::OBProperty::net_workarea,
293                                      otk::OBProperty::Atom_Cardinal,
294                                      area, 4);
295   /*
296   if (workspacesList.size() > 0) {
297     unsigned long *dims = new unsigned long[4 * workspacesList.size()];
298     for (unsigned int i = 0, m = workspacesList.size(); i < m; ++i) {
299       // XXX: this could be different for each workspace
300       const otk::Rect &area = availableArea();
301       dims[(i * 4) + 0] = area.x();
302       dims[(i * 4) + 1] = area.y();
303       dims[(i * 4) + 2] = area.width();
304       dims[(i * 4) + 3] = area.height();
305     }
306     xatom->set(getRootWindow(), otk::OBProperty::net_workarea,
307                otk::OBProperty::Atom_Cardinal,
308                dims, 4 * workspacesList.size());
309     delete [] dims;
310   } else
311     xatom->set(getRootWindow(), otk::OBProperty::net_workarea,
312                otk::OBProperty::Atom_Cardinal, 0, 0);
313   */
314 }
315
316
317 void OBScreen::loadStyle(const otk::Configuration &config)
318 {
319   _style.load(config);
320
321   // XXX: make stuff redraw!
322 }
323
324
325 void OBScreen::manageWindow(Window window)
326 {
327   OBClient *client = 0;
328   XWMHints *wmhint;
329   XSetWindowAttributes attrib_set;
330
331   // is the window a docking app
332   if ((wmhint = XGetWMHints(otk::OBDisplay::display, window))) {
333     if ((wmhint->flags & StateHint) &&
334         wmhint->initial_state == WithdrawnState) {
335       //slit->addClient(w); // XXX: make dock apps work!
336       XFree(wmhint);
337       return;
338     }
339     XFree(wmhint);
340   }
341
342   otk::OBDisplay::grab();
343
344   // choose the events we want to receive on the CLIENT window
345   attrib_set.event_mask = OBClient::event_mask;
346   attrib_set.do_not_propagate_mask = OBClient::no_propagate_mask;
347   XChangeWindowAttributes(otk::OBDisplay::display, window,
348                           CWEventMask|CWDontPropagate, &attrib_set);
349
350   // create the OBClient class, which gets all of the hints on the window
351   client = new OBClient(_number, window);
352   // register for events
353   Openbox::instance->registerHandler(window, client);
354   // add to the wm's map
355   Openbox::instance->addClient(window, client);
356
357   // we dont want a border on the client
358   client->toggleClientBorder(false);
359   
360   // specify that if we exit, the window should not be destroyed and should be
361   // reparented back to root automatically
362   XChangeSaveSet(otk::OBDisplay::display, window, SetModeInsert);
363
364   if (!client->positionRequested()) {
365     // XXX: position the window intelligenty
366   }
367
368   // create the decoration frame for the client window
369   client->frame = new OBFrame(client, &_style);
370
371   // add to the wm's map
372   Openbox::instance->addClient(client->frame->getWindow(), client);
373   Openbox::instance->addClient(client->frame->plate(), client);
374   Openbox::instance->addClient(client->frame->titlebar(), client);
375   Openbox::instance->addClient(client->frame->label(), client);
376   Openbox::instance->addClient(client->frame->button_max(), client);
377   Openbox::instance->addClient(client->frame->button_iconify(), client);
378   Openbox::instance->addClient(client->frame->button_stick(), client);
379   Openbox::instance->addClient(client->frame->button_close(), client);
380   Openbox::instance->addClient(client->frame->handle(), client);
381   Openbox::instance->addClient(client->frame->grip_left(), client);
382   Openbox::instance->addClient(client->frame->grip_right(), client);
383
384   // XXX: if on the current desktop..
385   client->frame->show();
386  
387   // XXX: handle any requested states such as shaded/maximized
388
389   otk::OBDisplay::ungrab();
390
391   // add to the screen's list
392   _clients.push_back(client);
393   // update the root properties
394   setClientList();
395 }
396
397
398 void OBScreen::unmanageWindow(OBClient *client)
399 {
400   OBFrame *frame = client->frame;
401
402   // XXX: pass around focus if this window was focused
403
404   // remove from the wm's map
405   Openbox::instance->removeClient(client->window());
406   Openbox::instance->removeClient(frame->getWindow());
407   Openbox::instance->removeClient(frame->plate());
408   Openbox::instance->removeClient(frame->titlebar());
409   Openbox::instance->removeClient(frame->label());
410   Openbox::instance->removeClient(frame->button_max());
411   Openbox::instance->removeClient(frame->button_iconify());
412   Openbox::instance->removeClient(frame->button_stick());
413   Openbox::instance->removeClient(frame->button_close());
414   Openbox::instance->removeClient(frame->handle());
415   Openbox::instance->removeClient(frame->grip_left());
416   Openbox::instance->removeClient(frame->grip_right());
417   // unregister for handling events
418   Openbox::instance->clearHandler(client->window());
419   
420   // remove the window from our save set
421   XChangeSaveSet(otk::OBDisplay::display, client->window(), SetModeDelete);
422
423   // we dont want events no more
424   XSelectInput(otk::OBDisplay::display, client->window(), NoEventMask);
425
426   frame->hide();
427
428   // give the client its border back
429   client->toggleClientBorder(true);
430
431   delete client->frame;
432   client->frame = 0;
433
434   // remove from the screen's list
435   _clients.remove(client);
436   delete client;
437
438   // update the root properties
439   setClientList();
440 }
441
442 }