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