share code for raising/lowering windows
[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 "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   // unmanage all windows
126   while (!clients.empty())
127     unmanageWindow(clients.front());
128
129   delete _image_control;
130 }
131
132
133 void OBScreen::manageExisting()
134 {
135   unsigned int i, j, nchild;
136   Window r, p, *children;
137   XQueryTree(otk::OBDisplay::display, _info->rootWindow(), &r, &p,
138              &children, &nchild);
139
140   // preen the window list of all icon windows... for better dockapp support
141   for (i = 0; i < nchild; i++) {
142     if (children[i] == None) continue;
143
144     XWMHints *wmhints = XGetWMHints(otk::OBDisplay::display,
145                                     children[i]);
146
147     if (wmhints) {
148       if ((wmhints->flags & IconWindowHint) &&
149           (wmhints->icon_window != children[i])) {
150         for (j = 0; j < nchild; j++) {
151           if (children[j] == wmhints->icon_window) {
152             children[j] = None;
153             break;
154           }
155         }
156       }
157
158       XFree(wmhints);
159     }
160   }
161
162   // manage shown windows
163   for (i = 0; i < nchild; ++i) {
164     if (children[i] == None)
165       continue;
166
167     XWindowAttributes attrib;
168     if (XGetWindowAttributes(otk::OBDisplay::display, children[i], &attrib)) {
169       if (attrib.override_redirect) continue;
170
171       if (attrib.map_state != IsUnmapped) {
172         manageWindow(children[i]);
173       }
174     }
175   }
176
177   XFree(children);
178 }
179
180
181 //! Adds a window's strut to the screen's list of reserved spaces
182 void OBScreen::addStrut(otk::Strut *strut)
183 {
184   _struts.push_back(strut);
185 }
186
187
188 //! Removes a window's strut from the screen's list of reserved spaces
189 void OBScreen::removeStrut(otk::Strut *strut)
190 {
191   _struts.remove(strut);
192 }
193
194
195 void OBScreen::calcArea()
196 {
197   otk::Rect old_area = _area;
198
199 /*
200 #ifdef    XINERAMA
201   // reset to the full areas
202   if (isXineramaActive())
203     xineramaUsableArea = getXineramaAreas();
204 #endif // XINERAMA
205 */
206   
207   /* these values represent offsets from the screen edge
208    * we look for the biggest offset on each edge and then apply them
209    * all at once
210    * do not be confused by the similarity to the names of Rect's members
211    */
212   unsigned int current_left = 0, current_right = 0, current_top = 0,
213     current_bottom = 0;
214
215   StrutList::const_iterator it = _struts.begin(), end = _struts.end();
216
217   for(; it != end; ++it) {
218     otk::Strut *strut = *it;
219     if (strut->left > current_left)
220       current_left = strut->left;
221     if (strut->top > current_top)
222       current_top = strut->top;
223     if (strut->right > current_right)
224       current_right = strut->right;
225     if (strut->bottom > current_bottom)
226       current_bottom = strut->bottom;
227   }
228
229   _area.setRect(current_left, current_top,
230                 _info->width() - (current_left + current_right),
231                 _info->height() - (current_top + current_bottom));
232
233 /*
234 #ifdef    XINERAMA
235   if (isXineramaActive()) {
236     // keep each of the ximerama-defined areas inside the strut
237     RectList::iterator xit, xend = xineramaUsableArea.end();
238     for (xit = xineramaUsableArea.begin(); xit != xend; ++xit) {
239       if (xit->x() < usableArea.x()) {
240         xit->setX(usableArea.x());
241         xit->setWidth(xit->width() - usableArea.x());
242       }
243       if (xit->y() < usableArea.y()) {
244         xit->setY(usableArea.y());
245         xit->setHeight(xit->height() - usableArea.y());
246       }
247       if (xit->x() + xit->width() > usableArea.width())
248         xit->setWidth(usableArea.width() - xit->x());
249       if (xit->y() + xit->height() > usableArea.height())
250         xit->setHeight(usableArea.height() - xit->y());
251     }
252   }
253 #endif // XINERAMA
254 */
255   
256   if (old_area != _area)
257     // XXX: re-maximize windows
258
259   setWorkArea();
260 }
261
262
263 void OBScreen::setClientList()
264 {
265   Window *windows;
266
267   // create an array of the window ids
268   if (clients.size() > 0) {
269     Window *win_it;
270     
271     windows = new Window[clients.size()];
272     win_it = windows;
273     ClientList::const_iterator it = clients.begin();
274     const ClientList::const_iterator end = clients.end();
275     for (; it != end; ++it, ++win_it)
276       *win_it = (*it)->window();
277   } else
278     windows = (Window*) 0;
279
280   Openbox::instance->property()->set(_info->rootWindow(),
281                                      otk::OBProperty::net_client_list,
282                                      otk::OBProperty::Atom_Window,
283                                      windows, clients.size());
284
285   if (clients.size())
286     delete [] windows;
287
288   setStackingList();
289 }
290
291
292 void OBScreen::setStackingList()
293 {
294   // The below comment is wrong now hopefully :> but ill keep it here for
295   // reference anyways
296   /*
297     Get the stacking order from all of the workspaces.
298     We start with the current workspace so that the sticky windows will be
299     in the right order on the current workspace.
300   */
301   /*
302   Openbox::instance->property()->set(_info->getRootWindow(),
303                                      otk::OBProperty::net_client_list_stacking,
304                                      otk::OBProperty::Atom_Window,
305                                      _stacking, _stacking.size());
306   */
307 }
308
309
310 void OBScreen::setWorkArea() {
311   unsigned long area[] = { _area.x(), _area.y(),
312                            _area.width(), _area.height() };
313   Openbox::instance->property()->set(_info->rootWindow(),
314                                      otk::OBProperty::net_workarea,
315                                      otk::OBProperty::Atom_Cardinal,
316                                      area, 4);
317   /*
318   if (workspacesList.size() > 0) {
319     unsigned long *dims = new unsigned long[4 * workspacesList.size()];
320     for (unsigned int i = 0, m = workspacesList.size(); i < m; ++i) {
321       // XXX: this could be different for each workspace
322       const otk::Rect &area = availableArea();
323       dims[(i * 4) + 0] = area.x();
324       dims[(i * 4) + 1] = area.y();
325       dims[(i * 4) + 2] = area.width();
326       dims[(i * 4) + 3] = area.height();
327     }
328     xatom->set(getRootWindow(), otk::OBProperty::net_workarea,
329                otk::OBProperty::Atom_Cardinal,
330                dims, 4 * workspacesList.size());
331     delete [] dims;
332   } else
333     xatom->set(getRootWindow(), otk::OBProperty::net_workarea,
334                otk::OBProperty::Atom_Cardinal, 0, 0);
335   */
336 }
337
338
339 void OBScreen::manageWindow(Window window)
340 {
341   OBClient *client = 0;
342   XWMHints *wmhint;
343   XSetWindowAttributes attrib_set;
344
345   // is the window a docking app
346   if ((wmhint = XGetWMHints(otk::OBDisplay::display, window))) {
347     if ((wmhint->flags & StateHint) &&
348         wmhint->initial_state == WithdrawnState) {
349       //slit->addClient(w); // XXX: make dock apps work!
350       XFree(wmhint);
351       return;
352     }
353     XFree(wmhint);
354   }
355
356   otk::OBDisplay::grab();
357
358   // choose the events we want to receive on the CLIENT window
359   attrib_set.event_mask = OBClient::event_mask;
360   attrib_set.do_not_propagate_mask = OBClient::no_propagate_mask;
361   XChangeWindowAttributes(otk::OBDisplay::display, window,
362                           CWEventMask|CWDontPropagate, &attrib_set);
363
364   // create the OBClient class, which gets all of the hints on the window
365   client = new OBClient(_number, window);
366   // register for events
367   Openbox::instance->registerHandler(window, client);
368   // add to the wm's map
369   Openbox::instance->addClient(window, client);
370
371   // we dont want a border on the client
372   client->toggleClientBorder(false);
373   
374   // specify that if we exit, the window should not be destroyed and should be
375   // reparented back to root automatically
376   XChangeSaveSet(otk::OBDisplay::display, window, SetModeInsert);
377
378   if (!client->positionRequested()) {
379     // XXX: position the window intelligenty
380   }
381
382   // create the decoration frame for the client window
383   client->frame = new OBFrame(client, &_style);
384
385   // add to the wm's map
386   Openbox::instance->addClient(client->frame->window(), client);
387   Openbox::instance->addClient(client->frame->plate(), client);
388   Openbox::instance->addClient(client->frame->titlebar(), client);
389   Openbox::instance->addClient(client->frame->label(), client);
390   Openbox::instance->addClient(client->frame->button_max(), client);
391   Openbox::instance->addClient(client->frame->button_iconify(), client);
392   Openbox::instance->addClient(client->frame->button_stick(), client);
393   Openbox::instance->addClient(client->frame->button_close(), client);
394   Openbox::instance->addClient(client->frame->handle(), client);
395   Openbox::instance->addClient(client->frame->grip_left(), client);
396   Openbox::instance->addClient(client->frame->grip_right(), client);
397
398   // XXX: if on the current desktop..
399   client->frame->show();
400  
401   // XXX: handle any requested states such as shaded/maximized
402
403   otk::OBDisplay::ungrab();
404
405   // add to the screen's list
406   clients.push_back(client);
407   // this puts into the stacking order, then raises it
408   _stacking.push_back(client);
409   restack(true, client);
410   // update the root properties
411   setClientList();
412
413   Openbox::instance->bindings()->grabButtons(true, client);
414 }
415
416
417 void OBScreen::unmanageWindow(OBClient *client)
418 {
419   OBFrame *frame = client->frame;
420
421   Openbox::instance->bindings()->grabButtons(false, client);
422
423   // XXX: pass around focus if this window was focused
424
425   // remove from the wm's map
426   Openbox::instance->removeClient(client->window());
427   Openbox::instance->removeClient(frame->window());
428   Openbox::instance->removeClient(frame->plate());
429   Openbox::instance->removeClient(frame->titlebar());
430   Openbox::instance->removeClient(frame->label());
431   Openbox::instance->removeClient(frame->button_max());
432   Openbox::instance->removeClient(frame->button_iconify());
433   Openbox::instance->removeClient(frame->button_stick());
434   Openbox::instance->removeClient(frame->button_close());
435   Openbox::instance->removeClient(frame->handle());
436   Openbox::instance->removeClient(frame->grip_left());
437   Openbox::instance->removeClient(frame->grip_right());
438   // unregister for handling events
439   Openbox::instance->clearHandler(client->window());
440   
441   // remove the window from our save set
442   XChangeSaveSet(otk::OBDisplay::display, client->window(), SetModeDelete);
443
444   // we dont want events no more
445   XSelectInput(otk::OBDisplay::display, client->window(), NoEventMask);
446
447   frame->hide();
448
449   // give the client its border back
450   client->toggleClientBorder(true);
451
452   delete client->frame;
453   client->frame = 0;
454
455   // remove from the screen's lists
456   _stacking.remove(client);
457   clients.remove(client);
458   delete client;
459
460   // update the root properties
461   setClientList();
462 }
463
464 void OBScreen::restack(bool raise, OBClient *client)
465 {
466   const int layer = client->layer();
467   std::vector<Window> wins;
468
469   _stacking.remove(client);
470
471   // the stacking list is from highest to lowest
472   
473   ClientList::iterator it = _stacking.begin(), end = _stacking.end();
474   // insert the windows above this window
475   for (; it != end; ++it) {
476     if ((*it)->layer() < layer || (raise && (*it)->layer() == layer))
477       break;
478     wins.push_back((*it)->frame->window());
479   }
480   // insert our client
481   wins.push_back(client->frame->window());
482   _stacking.insert(it, client);
483   // insert the remaining below this window
484   for (; it != end; ++it)
485     wins.push_back((*it)->frame->window());
486
487   XRestackWindows(otk::OBDisplay::display, &wins[0], wins.size());
488 }
489
490 }