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