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