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