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