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