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