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