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::Color::clearCache();
373 bt::Font::clearCache();
374 bt::PixmapCache::clearCache();
378 Blackbox::Blackbox(char **m_argv
, const char *dpy_name
,
379 const std::string
& rc
, bool multi_head
)
380 : bt::Application(m_argv
[0], dpy_name
, multi_head
),
381 grab_count(0u), _resource(rc
)
383 if (! XSupportsLocale())
384 fprintf(stderr
, "X server does not support locale\n");
386 if (XSetLocaleModifiers("") == NULL
)
387 fprintf(stderr
, "cannot set locale modifiers\n");
392 focused_window
= (BlackboxWindow
*) 0;
393 _ewmh
= (bt::EWMH
*) 0;
397 if (! multi_head
|| display().screenCount() == 1)
398 screen_list_count
= 1;
400 screen_list_count
= display().screenCount();
402 _resource
.load(*this);
404 screen_list
= new BScreen
*[screen_list_count
];
405 unsigned int managed
= 0;
406 for (unsigned int i
= 0; i
< screen_list_count
; ++i
) {
407 BScreen
*screen
= new BScreen(this, i
);
409 if (! screen
->isScreenManaged()) {
414 screen_list
[i
] = screen
;
419 fprintf(stderr
, "%s: no managable screens found, exiting...\n",
420 applicationName().c_str());
424 screen_list_count
= managed
;
426 // start with the first managed screen as the active screen
427 setActiveScreen(screen_list
[0]);
429 XSynchronize(XDisplay(), false);
430 XSync(XDisplay(), false);
432 timer
= new bt::Timer(this, this);
433 timer
->setTimeout(0l);
437 Blackbox::~Blackbox(void) {
438 std::for_each(screen_list
, screen_list
+ screen_list_count
,
439 bt::PointerAssassin());
441 delete [] screen_list
;
442 std::for_each(menuTimestamps
.begin(), menuTimestamps
.end(),
443 bt::PointerAssassin());
450 void Blackbox::XGrabServer(void) {
451 if (grab_count
++ == 0)
452 ::XGrabServer(XDisplay());
456 void Blackbox::XUngrabServer(void) {
457 if (--grab_count
== 0)
458 ::XUngrabServer(XDisplay());
462 BScreen
*Blackbox::findScreen(Window window
) const {
463 for (unsigned int i
= 0; i
< screen_list_count
; ++i
)
464 if (screen_list
[i
]->screenInfo().rootWindow() == window
)
465 return screen_list
[i
];
470 void Blackbox::setActiveScreen(BScreen
*screen
) {
471 if (active_screen
&& active_screen
== screen
) // nothing to do
475 active_screen
= screen
;
477 // install screen colormap
478 XInstallColormap(XDisplay(), active_screen
->screenInfo().colormap());
480 if (! focused_window
|| focused_window
->screen() != active_screen
)
485 BScreen
* Blackbox::screenNumber(unsigned int n
) {
486 assert(n
< screen_list_count
);
487 return screen_list
[n
];
491 BlackboxWindow
*Blackbox::findWindow(Window window
) const {
492 WindowLookup::const_iterator it
= windowSearchList
.find(window
);
493 if (it
!= windowSearchList
.end())
499 void Blackbox::insertWindow(Window window
, BlackboxWindow
*data
)
500 { windowSearchList
.insert(WindowLookupPair(window
, data
)); }
503 void Blackbox::removeWindow(Window window
)
504 { windowSearchList
.erase(window
); }
507 BWindowGroup
*Blackbox::findWindowGroup(Window window
) const {
508 GroupLookup::const_iterator it
= groupSearchList
.find(window
);
509 if (it
!= groupSearchList
.end())
515 void Blackbox::insertWindowGroup(Window window
, BWindowGroup
*data
)
516 { groupSearchList
.insert(GroupLookupPair(window
, data
)); }
519 void Blackbox::removeWindowGroup(Window window
)
520 { groupSearchList
.erase(window
); }
523 void Blackbox::setFocusedWindow(BlackboxWindow
*win
) {
524 if (focused_window
&& focused_window
== win
) // nothing to do
527 if (win
&& !win
->isIconic()) {
528 // the active screen is the one with the newly focused window...
529 active_screen
= win
->screen();
530 focused_window
= win
;
534 assert(active_screen
!= 0);
535 XSetInputFocus(XDisplay(), active_screen
->noFocusWindow(),
536 RevertToPointerRoot
, XTime());
539 updateActiveWindow();
543 void Blackbox::restart(const std::string
&prog
) {
544 setRunState(bt::Application::SHUTDOWN
);
547 since we don't allow control to return to the eventloop, we need
548 to call shutdown() explicitly
552 if (! prog
.empty()) {
553 putenv(const_cast<char *>
554 (display().screenInfo(0).displayString().c_str()));
555 execlp(prog
.c_str(), prog
.c_str(), NULL
);
556 perror(prog
.c_str());
559 // fall back in case the above execlp doesn't work
560 execvp(argv
[0], argv
);
561 std::string name
= bt::basename(argv
[0]);
562 execvp(name
.c_str(), argv
);
566 void Blackbox::reconfigure(void) {
567 if (! timer
->isTiming())
572 void Blackbox::saveMenuFilename(const std::string
& filename
) {
573 assert(!filename
.empty());
576 MenuTimestampList::iterator it
= menuTimestamps
.begin();
577 for (; it
!= menuTimestamps
.end() && !found
; ++it
)
578 found
= (*it
)->filename
== filename
;
583 if (stat(filename
.c_str(), &buf
) != 0)
584 return; // file doesn't exist
586 MenuTimestamp
*ts
= new MenuTimestamp
;
587 ts
->filename
= filename
;
588 ts
->timestamp
= buf
.st_ctime
;
589 menuTimestamps
.push_back(ts
);
593 void Blackbox::checkMenu(void) {
595 MenuTimestampList::iterator it
= menuTimestamps
.begin();
596 for(; it
!= menuTimestamps
.end(); ++it
) {
597 MenuTimestamp
*tmp
= *it
;
600 if (! stat(tmp
->filename
.c_str(), &buf
)) {
601 if (tmp
->timestamp
!= buf
.st_ctime
)
613 void Blackbox::rereadMenu(void) {
614 std::for_each(menuTimestamps
.begin(), menuTimestamps
.end(),
615 bt::PointerAssassin());
616 menuTimestamps
.clear();
618 std::for_each(screen_list
, screen_list
+ screen_list_count
,
619 std::mem_fun(&BScreen::rereadMenu
));