add a skeletal OBActions class for user actions
[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   Openbox::instance->addClient(window, client = new OBClient(_number, window));
353   // register for events
354   Openbox::instance->registerHandler(window, client);
355
356   // we dont want a border on the client
357   XSetWindowBorderWidth(otk::OBDisplay::display, window, 0);
358   
359   // specify that if we exit, the window should not be destroyed and should be
360   // reparented back to root automatically
361   XChangeSaveSet(otk::OBDisplay::display, window, SetModeInsert);
362
363   if (!client->positionRequested()) {
364     // XXX: position the window intelligenty
365   }
366
367   // create the decoration frame for the client window
368   client->frame = new OBFrame(client, &_style);
369
370   // XXX: if on the current desktop..
371   client->frame->show();
372  
373   // XXX: handle any requested states such as shaded/maximized
374
375   otk::OBDisplay::ungrab();
376
377   // add to the screen's list
378   _clients.push_back(client);
379   // update the root properties
380   setClientList();
381 }
382
383
384 void OBScreen::unmanageWindow(OBClient *client)
385 {
386   OBFrame *frame = client->frame;
387
388   // XXX: pass around focus if this window was focused
389
390   // remove from the wm's map
391   Openbox::instance->removeClient(client->window());
392   // unregister for handling events
393   Openbox::instance->clearHandler(client->window());
394   
395   // remove the window from our save set
396   XChangeSaveSet(otk::OBDisplay::display, client->window(), SetModeDelete);
397
398   // we dont want events no more
399   XSelectInput(otk::OBDisplay::display, client->window(), NoEventMask);
400
401   frame->hide();
402   
403   // we dont want a border on the client
404   XSetWindowBorderWidth(otk::OBDisplay::display, client->window(),
405                         client->borderWidth());
406
407   delete client->frame;
408   client->frame = 0;
409
410   // remove from the screen's list
411   _clients.remove(client);
412   delete client;
413
414   // update the root properties
415   setClientList();
416 }
417
418 }