change how the widgets' _dirty flag works so that all inheritence levels of the widge...
[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[] = { _info->getWidth(),
80                                _info->getHeight() };
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.front());
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   otk::OBDisplay::grab();
342
343   // choose the events we want to receive on the CLIENT window
344   attrib_set.event_mask = OBClient::event_mask;
345   attrib_set.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask |
346                                      ButtonMotionMask;
347   XChangeWindowAttributes(otk::OBDisplay::display, window,
348                           CWEventMask|CWDontPropagate, &attrib_set);
349
350   // create the OBClient class, which gets all of the hints on the window
351   Openbox::instance->addClient(window, client = new OBClient(_number, window));
352
353   // we dont want a border on the client
354   XSetWindowBorderWidth(otk::OBDisplay::display, window, 0);
355   
356   // specify that if we exit, the window should not be destroyed and should be
357   // reparented back to root automatically
358   XChangeSaveSet(otk::OBDisplay::display, window, SetModeInsert);
359
360   if (!client->positionRequested()) {
361     // XXX: position the window intelligenty
362   }
363
364   // create the decoration frame for the client window
365   client->frame = new OBFrame(client, &_style);
366
367   // XXX: if on the current desktop..
368   XMapWindow(otk::OBDisplay::display, client->frame->window());
369  
370   // XXX: handle any requested states such as shaded/maximized
371
372   otk::OBDisplay::ungrab();
373
374   // add all the client's windows as event handlers for the client
375   Openbox::instance->addClient(window, client);
376   Openbox::instance->addClient(client->frame->window(), client);
377   Openbox::instance->addClient(client->frame->titlebar(), client);
378   Openbox::instance->addClient(client->frame->buttonIconify(), client);
379   Openbox::instance->addClient(client->frame->buttonMax(), client);
380   Openbox::instance->addClient(client->frame->buttonStick(), client);
381   Openbox::instance->addClient(client->frame->buttonClose(), client);
382   Openbox::instance->addClient(client->frame->label(), client);
383   Openbox::instance->addClient(client->frame->handle(), client);
384   Openbox::instance->addClient(client->frame->gripLeft(), client);
385   Openbox::instance->addClient(client->frame->gripRight(), client);
386
387   // add to the screen's list
388   _clients.push_back(client);
389   // update the root properties
390   setClientList();
391 }
392
393
394 void OBScreen::unmanageWindow(OBClient *client)
395 {
396   OBFrame *frame = client->frame;
397
398   // XXX: pass around focus if this window was focused
399   
400   // remove the window from our save set
401   XChangeSaveSet(otk::OBDisplay::display, client->window(), SetModeDelete);
402
403   // we dont want events no more
404   XSelectInput(otk::OBDisplay::display, client->window(), NoEventMask);
405
406   XUnmapWindow(otk::OBDisplay::display, frame->window());
407   
408   // we dont want a border on the client
409   XSetWindowBorderWidth(otk::OBDisplay::display, client->window(),
410                         client->borderWidth());
411
412   // remove the client class from the search list
413   Openbox::instance->removeClient(client->window());
414   // remove the frame's decor elements as event handlers for the client
415   Openbox::instance->removeClient(frame->window());
416   Openbox::instance->removeClient(frame->titlebar());
417   Openbox::instance->removeClient(frame->buttonIconify());
418   Openbox::instance->removeClient(frame->buttonMax());
419   Openbox::instance->removeClient(frame->buttonStick());
420   Openbox::instance->removeClient(frame->buttonClose());
421   Openbox::instance->removeClient(frame->label());
422   Openbox::instance->removeClient(frame->handle());
423   Openbox::instance->removeClient(frame->gripLeft());
424   Openbox::instance->removeClient(frame->gripRight());
425
426   delete client->frame;
427   client->frame = 0;
428
429   // remove from the screen's list
430   _clients.remove(client);
431   delete client;
432
433   // update the root properties
434   setClientList();
435 }
436
437 }