manage and unmanage windows in OBScreen
[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[] = { _size.x(),
80                                _size.y() };
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   manageExisting();
97
98   // XXX: "change to" the first workspace to initialize stuff
99 }
100
101
102 OBScreen::~OBScreen()
103 {
104   if (! _managed) return;
105
106   // unmanage all windows
107   while (!_clients.empty())
108     unmanageWindow(_clients[0]);
109
110   delete _image_control;
111 }
112
113
114 void OBScreen::manageExisting()
115 {
116   unsigned int i, j, nchild;
117   Window r, p, *children;
118   XQueryTree(otk::OBDisplay::display, _info->getRootWindow(), &r, &p,
119              &children, &nchild);
120
121   // preen the window list of all icon windows... for better dockapp support
122   for (i = 0; i < nchild; i++) {
123     if (children[i] == None) continue;
124
125     XWMHints *wmhints = XGetWMHints(otk::OBDisplay::display,
126                                     children[i]);
127
128     if (wmhints) {
129       if ((wmhints->flags & IconWindowHint) &&
130           (wmhints->icon_window != children[i])) {
131         for (j = 0; j < nchild; j++) {
132           if (children[j] == wmhints->icon_window) {
133             children[j] = None;
134             break;
135           }
136         }
137       }
138
139       XFree(wmhints);
140     }
141   }
142
143   // manage shown windows
144   for (i = 0; i < nchild; ++i) {
145     if (children[i] == None)
146       continue;
147
148     XWindowAttributes attrib;
149     if (XGetWindowAttributes(otk::OBDisplay::display, children[i], &attrib)) {
150       if (attrib.override_redirect) continue;
151
152       if (attrib.map_state != IsUnmapped) {
153         manageWindow(children[i]);
154       }
155     }
156   }
157
158   XFree(children);
159 }
160
161
162 //! Adds a window's strut to the screen's list of reserved spaces
163 void OBScreen::addStrut(otk::Strut *strut)
164 {
165   _struts.push_back(strut);
166 }
167
168
169 //! Removes a window's strut from the screen's list of reserved spaces
170 void OBScreen::removeStrut(otk::Strut *strut)
171 {
172   _struts.remove(strut);
173 }
174
175
176 void OBScreen::calcArea()
177 {
178   otk::Rect old_area = _area;
179
180 /*
181 #ifdef    XINERAMA
182   // reset to the full areas
183   if (isXineramaActive())
184     xineramaUsableArea = getXineramaAreas();
185 #endif // XINERAMA
186 */
187   
188   /* these values represent offsets from the screen edge
189    * we look for the biggest offset on each edge and then apply them
190    * all at once
191    * do not be confused by the similarity to the names of Rect's members
192    */
193   unsigned int current_left = 0, current_right = 0, current_top = 0,
194     current_bottom = 0;
195
196   StrutList::const_iterator it = _struts.begin(), end = _struts.end();
197
198   for(; it != end; ++it) {
199     otk::Strut *strut = *it;
200     if (strut->left > current_left)
201       current_left = strut->left;
202     if (strut->top > current_top)
203       current_top = strut->top;
204     if (strut->right > current_right)
205       current_right = strut->right;
206     if (strut->bottom > current_bottom)
207       current_bottom = strut->bottom;
208   }
209
210   _area.setRect(current_left, current_top,
211                 _info->getWidth() - (current_left + current_right),
212                 _info->getHeight() - (current_top + current_bottom));
213
214 /*
215 #ifdef    XINERAMA
216   if (isXineramaActive()) {
217     // keep each of the ximerama-defined areas inside the strut
218     RectList::iterator xit, xend = xineramaUsableArea.end();
219     for (xit = xineramaUsableArea.begin(); xit != xend; ++xit) {
220       if (xit->x() < usableArea.x()) {
221         xit->setX(usableArea.x());
222         xit->setWidth(xit->width() - usableArea.x());
223       }
224       if (xit->y() < usableArea.y()) {
225         xit->setY(usableArea.y());
226         xit->setHeight(xit->height() - usableArea.y());
227       }
228       if (xit->x() + xit->width() > usableArea.width())
229         xit->setWidth(usableArea.width() - xit->x());
230       if (xit->y() + xit->height() > usableArea.height())
231         xit->setHeight(usableArea.height() - xit->y());
232     }
233   }
234 #endif // XINERAMA
235 */
236   
237   if (old_area != _area)
238     // XXX: re-maximize windows
239
240   setWorkArea();
241 }
242
243
244 void OBScreen::setClientList()
245 {
246   Window *windows;
247
248   // create an array of the window ids
249   if (_clients.size() > 0) {
250     Window *win_it;
251     
252     windows = new Window[_clients.size()];
253     win_it = windows;
254     ClientList::const_iterator it = _clients.begin();
255     const ClientList::const_iterator end = _clients.end();
256     for (; it != end; ++it, ++win_it)
257       *win_it = (*it)->window();
258   } else
259     windows = (Window*) 0;
260
261   Openbox::instance->property()->set(_info->getRootWindow(),
262                                      otk::OBProperty::net_client_list,
263                                      otk::OBProperty::Atom_Window,
264                                      windows, _clients.size());
265
266   if (_clients.size())
267     delete [] windows;
268
269   setStackingList();
270 }
271
272
273 void OBScreen::setStackingList()
274 {
275   // The below comment is wrong now hopefully :> but ill keep it here for
276   // reference anyways
277   /*
278     Get the stacking order from all of the workspaces.
279     We start with the current workspace so that the sticky windows will be
280     in the right order on the current workspace.
281   */
282   /*
283   Openbox::instance->property()->set(_info->getRootWindow(),
284                                      otk::OBProperty::net_client_list_stacking,
285                                      otk::OBProperty::Atom_Window,
286                                      _stacking, _stacking.size());
287   */
288 }
289
290
291 void OBScreen::setWorkArea() {
292   unsigned long area[] = { _area.x(), _area.y(),
293                            _area.width(), _area.height() };
294   Openbox::instance->property()->set(_info->getRootWindow(),
295                                      otk::OBProperty::net_workarea,
296                                      otk::OBProperty::Atom_Cardinal,
297                                      area, 4);
298   /*
299   if (workspacesList.size() > 0) {
300     unsigned long *dims = new unsigned long[4 * workspacesList.size()];
301     for (unsigned int i = 0, m = workspacesList.size(); i < m; ++i) {
302       // XXX: this could be different for each workspace
303       const otk::Rect &area = availableArea();
304       dims[(i * 4) + 0] = area.x();
305       dims[(i * 4) + 1] = area.y();
306       dims[(i * 4) + 2] = area.width();
307       dims[(i * 4) + 3] = area.height();
308     }
309     xatom->set(getRootWindow(), otk::OBProperty::net_workarea,
310                otk::OBProperty::Atom_Cardinal,
311                dims, 4 * workspacesList.size());
312     delete [] dims;
313   } else
314     xatom->set(getRootWindow(), otk::OBProperty::net_workarea,
315                otk::OBProperty::Atom_Cardinal, 0, 0);
316   */
317 }
318
319
320 void OBScreen::loadStyle(const otk::Configuration &config)
321 {
322   _style.load(config);
323
324   // XXX: make stuff redraw!
325 }
326
327
328 void OBScreen::manageWindow(Window window)
329 {
330   OBClient *client = 0;
331   XWMHints *wmhint;
332   XSetWindowAttributes attrib_set;
333
334   // XXX: manage the window, i.e. grab events n shit
335
336   // is the window a docking app
337   if ((wmhint = XGetWMHints(otk::OBDisplay::display, window))) {
338     if ((wmhint->flags & StateHint) &&
339         wmhint->initial_state == WithdrawnState) {
340       //slit->addClient(w); // XXX: make dock apps work!
341       XFree(wmhint);
342       return;
343     }
344     XFree(wmhint);
345   }
346
347   // choose the events we want to receive on the CLIENT window
348   attrib_set.event_mask = OBClient::event_mask;
349   attrib_set.do_not_propagate_mask = ButtonPressMask | ButtonReleaseMask |
350                                      ButtonMotionMask;
351   XChangeWindowAttributes(otk::OBDisplay::display, window,
352                           CWEventMask|CWDontPropagate, &attrib_set);
353
354   // create the OBClient class, which gets all of the hints on the window
355   Openbox::instance->addClient(window, client = new OBClient(_number, window));
356
357   // we dont want a border on the client
358   XSetWindowBorderWidth(otk::OBDisplay::display, window, 0);
359   
360   // specify that if we exit, the window should not be destroyed and should be
361   // reparented back to root automatically
362   XChangeSaveSet(otk::OBDisplay::display, window, SetModeInsert);
363
364   if (!client->positionRequested()) {
365     // XXX: position the window intelligenty
366   }
367
368   // create the decoration frame for the client window
369   client->frame = new OBFrame(client, &_style);
370
371   // add all the client's decoration windows as event handlers for the client
372   Openbox::instance->addClient(client->frame->window(), client);
373   Openbox::instance->addClient(client->frame->titlebar(), client);
374   Openbox::instance->addClient(client->frame->buttonIconify(), client);
375   Openbox::instance->addClient(client->frame->buttonMax(), client);
376   Openbox::instance->addClient(client->frame->buttonStick(), client);
377   Openbox::instance->addClient(client->frame->buttonClose(), client);
378   Openbox::instance->addClient(client->frame->label(), client);
379   Openbox::instance->addClient(client->frame->handle(), client);
380   Openbox::instance->addClient(client->frame->gripLeft(), client);
381   Openbox::instance->addClient(client->frame->gripRight(), client);
382   
383   // XXX: if on the current desktop..
384   XMapWindow(otk::OBDisplay::display, client->frame->window());
385  
386   // XXX: handle any requested states such as shaded/maximized
387
388
389   _clients.push_back(client);
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   ClientList::iterator it = _clients.begin(), end = _clients.end();
430   for (; it != end; ++it)
431     if (*it == client) {
432       _clients.erase(it);
433       break;
434     }
435   delete client;
436
437   setClientList();
438 }
439
440 }