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