1
// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2
// blackbox.cc for Blackbox - an X11 Window manager
3
// Copyright (c) 2001 - 2005 Sean 'Shaleh' Perry <shaleh at debian.org>
4
// Copyright (c) 1997 - 2000, 2002 - 2005
5
// Bradley T Hughes <bhughes at trolltech.com>
7
// Permission is hereby granted, free of charge, to any person obtaining a
8
// copy of this software and associated documentation files (the "Software"),
9
// to deal in the Software without restriction, including without limitation
10
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
11
// and/or sell copies of the Software, and to permit persons to whom the
12
// Software is furnished to do so, subject to the following conditions:
14
// The above copyright notice and this permission notice shall be included in
15
// all copies or substantial portions of the Software.
17
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23
// DEALINGS IN THE SOFTWARE.
25
#include "blackbox.hh"
31
#include <PixmapCache.hh>
34
#include <X11/Xresource.h>
35
#include <sys/types.h>
41
// #define FOCUS_DEBUG
43
static const char *Mode[] = {
50
static const char *Detail[] = {
63
void Blackbox::save_rc(void)
64
{ _resource.save(*this); }
67
void Blackbox::load_rc(void)
68
{ _resource.load(*this); }
71
void Blackbox::reload_rc(void) {
77
void Blackbox::init_icccm(void) {
79
"WM_COLORMAP_WINDOWS",
88
XInternAtoms(XDisplay(), atoms, 7, false, atoms_return);
89
xa_wm_colormap_windows = atoms_return[0];
90
xa_wm_protocols = atoms_return[1];
91
xa_wm_state = atoms_return[2];
92
xa_wm_change_state = atoms_return[3];
93
xa_wm_delete_window = atoms_return[4];
94
xa_wm_take_focus = atoms_return[5];
95
motif_wm_hints = atoms_return[6];
97
_ewmh = new bt::EWMH(display());
101
void Blackbox::updateActiveWindow() const {
102
Window active = (focused_window) ? focused_window->clientWindow() : None;
103
for (unsigned int i = 0; i < display().screenCount(); ++i)
104
_ewmh->setActiveWindow(display().screenInfo(i).rootWindow(), active);
108
void Blackbox::shutdown(void) {
109
bt::Application::shutdown();
113
XSetInputFocus(XDisplay(), PointerRoot, RevertToPointerRoot, XTime());
115
std::for_each(screen_list, screen_list + screen_list_count,
116
std::mem_fun(&BScreen::shutdown));
118
XSync(XDisplay(), false);
124
static Bool scanForFocusIn(Display *, XEvent *e, XPointer) {
125
if (e->type == FocusIn
126
&& e->xfocus.mode != NotifyGrab
127
&& (e->xfocus.detail == NotifyNonlinearVirtual
128
|| e->xfocus.detail == NotifyVirtual)) {
135
void Blackbox::process_event(XEvent *e) {
139
fprintf(stderr, "Blackbox::process_event(): MapRequest for 0x%lx\n",
140
e->xmaprequest.window);
143
BlackboxWindow *win = findWindow(e->xmaprequest.window);
146
if ((!activeScreen() || activeScreen() == win->screen()) &&
147
(win->isTransient() || _resource.focusNewWindows()))
150
BScreen *screen = findScreen(e->xmaprequest.parent);
154
we got a map request for a window who's parent isn't root. this
155
can happen in only one circumstance:
157
a client window unmapped a managed window, and then remapped it
158
somewhere between unmapping the client window and reparenting it
161
regardless of how it happens, we need to find the screen that
164
XWindowAttributes wattrib;
165
if (! XGetWindowAttributes(XDisplay(), e->xmaprequest.window,
167
// failed to get the window attributes, perhaps the window has
168
// now been destroyed?
172
screen = findScreen(wattrib.root);
173
assert(screen != 0); // this should never happen
175
screen->addWindow(e->xmaprequest.window);
181
case ConfigureRequest: {
182
BlackboxWindow *win = findWindow(e->xconfigurerequest.window);
184
// a window wants to resize
185
win->configureRequestEvent(&e->xconfigurerequest);
190
dynamic_cast<Slit *>(findEventHandler(e->xconfigurerequest.parent));
192
// something in the slit wants to resize
193
slit->configureRequestEvent(&e->xconfigurerequest);
198
handle configure requests for windows that have no EventHandlers
199
by simply configuring them as requested.
201
note: the event->window parameter points to the window being
202
configured, and event->parent points to the window that received
203
the event (in this case, the root window, since
204
SubstructureRedirect has been selected).
207
xwc.x = e->xconfigurerequest.x;
208
xwc.y = e->xconfigurerequest.y;
209
xwc.width = e->xconfigurerequest.width;
210
xwc.height = e->xconfigurerequest.height;
211
xwc.border_width = e->xconfigurerequest.border_width;
212
xwc.sibling = e->xconfigurerequest.above;
213
xwc.stack_mode = e->xconfigurerequest.detail;
214
XConfigureWindow(XDisplay(),
215
e->xconfigurerequest.window,
216
e->xconfigurerequest.value_mask,
223
printf("FocusIn : window %8lx mode %s detail %s\n",
224
e->xfocus.window, Mode[e->xfocus.mode], Detail[e->xfocus.detail]);
227
if (e->xfocus.mode == NotifyGrab
228
|| (e->xfocus.detail != NotifyNonlinearVirtual
229
&& e->xfocus.detail != NotifyVirtual)) {
231
don't process FocusIns when:
232
1. they are the result of a grab
233
2. the new focus window isn't an ancestor or inferior of the
234
old focus window (NotifyNonlinearVirtual and NotifyVirtual)
239
BlackboxWindow *win = findWindow(e->xfocus.window);
240
if (!win || win->isFocused())
244
printf(" win %p got focus\n", win);
246
win->setFocused(true);
247
setFocusedWindow(win);
250
set the event window to None. when the FocusOut event handler calls
251
this function recursively, it uses this as an indication that focus
252
has moved to a known window.
254
e->xfocus.window = None;
261
printf("FocusOut: window %8lx mode %s detail %s\n",
262
e->xfocus.window, Mode[e->xfocus.mode], Detail[e->xfocus.detail]);
265
if (e->xfocus.mode == NotifyGrab
266
|| (e->xfocus.detail != NotifyNonlinearVirtual
267
&& e->xfocus.detail != NotifyVirtual)) {
269
don't process FocusOuts when:
270
1. they are the result of a grab
271
2. the new focus window isn't an ancestor or inferior of the
272
old focus window (NotifyNonlinearVirtual and NotifyNonlinearVirtual)
277
BlackboxWindow *win = findWindow(e->xfocus.window);
278
if (!win || !win->isFocused())
281
bool lost_focus = true; // did the window really lose focus?
282
bool no_focus = true; // did another window get focus?
285
if (XCheckIfEvent(XDisplay(), &event, scanForFocusIn, NULL)) {
286
process_event(&event);
288
if (event.xfocus.window == None)
291
XWindowAttributes attr;
294
XGetInputFocus(XDisplay(), &w, &unused);
296
&& XGetWindowAttributes(XDisplay(), w, &attr)
297
&& attr.override_redirect) {
299
printf(" focused moved to an override_redirect window\n");
301
lost_focus = (e->xfocus.mode == NotifyNormal);
307
printf(" win %p lost focus\n", win);
309
win->setFocused(false);
313
printf(" no window has focus\n");
323
// Send the event through the default EventHandlers.
324
bt::Application::process_event(e);
330
bool Blackbox::process_signal(int sig) {
345
return bt::Application::process_signal(sig);
352
void Blackbox::timeout(bt::Timer *) {
353
XrmDatabase new_blackboxrc = (XrmDatabase) 0;
355
std::string style = "session.styleFile: ";
356
style += _resource.styleFilename();
357
XrmPutLineResource(&new_blackboxrc, style.c_str());
359
XrmDatabase old_blackboxrc = XrmGetFileDatabase(_resource.rcFilename());
361
XrmMergeDatabases(new_blackboxrc, &old_blackboxrc);
362
XrmPutFileDatabase(old_blackboxrc, _resource.rcFilename());
363
if (old_blackboxrc) XrmDestroyDatabase(old_blackboxrc);
365
std::for_each(menuTimestamps.begin(), menuTimestamps.end(),
366
bt::PointerAssassin());
367
menuTimestamps.clear();
369
std::for_each(screen_list, screen_list + screen_list_count,
370
std::mem_fun(&BScreen::reconfigure));
372
bt::Font::clearCache();
373
bt::PixmapCache::clearCache();
374
bt::Pen::clearCache();
376
// clear the color cache here to allow the pen cache to deallocate
378
bt::Color::clearCache();
382
Blackbox::Blackbox(char **m_argv, const char *dpy_name,
383
const std::string& rc, bool multi_head)
384
: bt::Application(m_argv[0], dpy_name, multi_head),
385
grab_count(0u), _resource(rc)
387
if (! XSupportsLocale())
388
fprintf(stderr, "X server does not support locale\n");
390
if (XSetLocaleModifiers("") == NULL)
391
fprintf(stderr, "cannot set locale modifiers\n");
396
focused_window = (BlackboxWindow *) 0;
397
_ewmh = (bt::EWMH*) 0;
401
if (! multi_head || display().screenCount() == 1)
402
screen_list_count = 1;
404
screen_list_count = display().screenCount();
406
_resource.load(*this);
408
screen_list = new BScreen*[screen_list_count];
409
unsigned int managed = 0;
410
for (unsigned int i = 0; i < screen_list_count; ++i) {
411
BScreen *screen = new BScreen(this, i);
413
if (! screen->isScreenManaged()) {
418
screen_list[i] = screen;
423
fprintf(stderr, "%s: no managable screens found, exiting...\n",
424
applicationName().c_str());
428
screen_list_count = managed;
430
// start with the first managed screen as the active screen
431
setActiveScreen(screen_list[0]);
433
XSynchronize(XDisplay(), false);
434
XSync(XDisplay(), false);
436
timer = new bt::Timer(this, this);
437
timer->setTimeout(0l);
441
Blackbox::~Blackbox(void) {
442
std::for_each(screen_list, screen_list + screen_list_count,
443
bt::PointerAssassin());
445
delete [] screen_list;
446
std::for_each(menuTimestamps.begin(), menuTimestamps.end(),
447
bt::PointerAssassin());
454
void Blackbox::XGrabServer(void) {
455
if (grab_count++ == 0)
456
::XGrabServer(XDisplay());
460
void Blackbox::XUngrabServer(void) {
461
if (--grab_count == 0)
462
::XUngrabServer(XDisplay());
466
BScreen *Blackbox::findScreen(Window window) const {
467
for (unsigned int i = 0; i < screen_list_count; ++i)
468
if (screen_list[i]->screenInfo().rootWindow() == window)
469
return screen_list[i];
474
void Blackbox::setActiveScreen(BScreen *screen) {
475
if (active_screen && active_screen == screen) // nothing to do
479
active_screen = screen;
481
// install screen colormap
482
XInstallColormap(XDisplay(), active_screen->screenInfo().colormap());
484
if (! focused_window || focused_window->screen() != active_screen)
489
BScreen* Blackbox::screenNumber(unsigned int n) {
490
assert(n < screen_list_count);
491
return screen_list[n];
495
BlackboxWindow *Blackbox::findWindow(Window window) const {
496
WindowLookup::const_iterator it = windowSearchList.find(window);
497
if (it != windowSearchList.end())
503
void Blackbox::insertWindow(Window window, BlackboxWindow *data)
504
{ windowSearchList.insert(WindowLookupPair(window, data)); }
507
void Blackbox::removeWindow(Window window)
508
{ windowSearchList.erase(window); }
511
BWindowGroup *Blackbox::findWindowGroup(Window window) const {
512
GroupLookup::const_iterator it = groupSearchList.find(window);
513
if (it != groupSearchList.end())
519
void Blackbox::insertWindowGroup(Window window, BWindowGroup *data)
520
{ groupSearchList.insert(GroupLookupPair(window, data)); }
523
void Blackbox::removeWindowGroup(Window window)
524
{ groupSearchList.erase(window); }
527
void Blackbox::setFocusedWindow(BlackboxWindow *win) {
528
if (focused_window && focused_window == win) // nothing to do
531
if (win && !win->isIconic()) {
532
// the active screen is the one with the newly focused window...
533
active_screen = win->screen();
534
focused_window = win;
538
assert(active_screen != 0);
539
XSetInputFocus(XDisplay(), active_screen->noFocusWindow(),
540
RevertToPointerRoot, XTime());
543
updateActiveWindow();
547
void Blackbox::restart(const std::string &prog) {
548
setRunState(bt::Application::SHUTDOWN);
551
since we don't allow control to return to the eventloop, we need
552
to call shutdown() explicitly
556
if (! prog.empty()) {
557
putenv(const_cast<char *>
558
(display().screenInfo(0).displayString().c_str()));
559
execlp(prog.c_str(), prog.c_str(), NULL);
560
perror(prog.c_str());
563
// fall back in case the above execlp doesn't work
564
execvp(argv[0], argv);
565
std::string name = bt::basename(argv[0]);
566
execvp(name.c_str(), argv);
570
void Blackbox::reconfigure(void) {
571
if (! timer->isTiming())
576
void Blackbox::saveMenuFilename(const std::string& filename) {
577
assert(!filename.empty());
580
MenuTimestampList::iterator it = menuTimestamps.begin();
581
for (; it != menuTimestamps.end() && !found; ++it)
582
found = (*it)->filename == filename;
587
if (stat(filename.c_str(), &buf) != 0)
588
return; // file doesn't exist
590
MenuTimestamp *ts = new MenuTimestamp;
591
ts->filename = filename;
592
ts->timestamp = buf.st_ctime;
593
menuTimestamps.push_back(ts);
597
void Blackbox::checkMenu(void) {
599
MenuTimestampList::iterator it = menuTimestamps.begin();
600
for(; it != menuTimestamps.end(); ++it) {
601
MenuTimestamp *tmp = *it;
604
if (! stat(tmp->filename.c_str(), &buf)) {
605
if (tmp->timestamp != buf.st_ctime)
617
void Blackbox::rereadMenu(void) {
618
std::for_each(menuTimestamps.begin(), menuTimestamps.end(),
619
bt::PointerAssassin());
620
menuTimestamps.clear();
622
std::for_each(screen_list, screen_list + screen_list_count,
623
std::mem_fun(&BScreen::rereadMenu));