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