1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // blackbox.cc for Blackbox - an X11 Window manager
3 // Copyright (c) 2001 - 2002 Sean 'Shaleh' Perry <shaleh@debian.org>
4 // Copyright (c) 1997 - 2000 Brad Hughes (bhughes@tcac.net)
6 // Permission is hereby granted, free of charge, to any person obtaining a
7 // copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the
11 // Software is furnished to do so, subject to the following conditions:
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 // DEALINGS IN THE SOFTWARE.
25 # include "../config.h"
26 #endif // HAVE_CONFIG_H
30 #include <X11/Xutil.h>
31 #include <X11/Xatom.h>
32 #include <X11/cursorfont.h>
33 #include <X11/keysym.h>
36 #include <X11/extensions/shape.h>
41 #endif // HAVE_STDIO_H
45 #endif // HAVE_STDLIB_H
49 #endif // HAVE_STRING_H
52 # include <sys/types.h>
54 #endif // HAVE_UNISTD_H
56 #ifdef HAVE_SYS_PARAM_H
57 # include <sys/param.h>
58 #endif // HAVE_SYS_PARAM_H
60 #ifdef HAVE_SYS_SELECT_H
61 # include <sys/select.h>
62 #endif // HAVE_SYS_SELECT_H
66 #endif // HAVE_SIGNAL_H
68 #ifdef HAVE_SYS_SIGNAL_H
69 # include <sys/signal.h>
70 #endif // HAVE_SYS_SIGNAL_H
72 #ifdef HAVE_SYS_STAT_H
73 # include <sys/types.h>
74 # include <sys/stat.h>
75 #endif // HAVE_SYS_STAT_H
77 #ifdef TIME_WITH_SYS_TIME
78 # include <sys/time.h>
80 #else // !TIME_WITH_SYS_TIME
81 # ifdef HAVE_SYS_TIME_H
82 # include <sys/time.h>
83 # else // !HAVE_SYS_TIME_H
85 # endif // HAVE_SYS_TIME_H
86 #endif // TIME_WITH_SYS_TIME
90 #endif // HAVE_LIBGEN_H
100 #include "blackbox.hh"
101 #include "Basemenu.hh"
102 #include "Clientmenu.hh"
103 #include "GCCache.hh"
105 #include "Rootmenu.hh"
108 #include "Toolbar.hh"
111 #include "Workspace.hh"
112 #include "Workspacemenu.hh"
115 // X event scanner for enter/leave notifies - adapted from twm
118 bool leave
, inferior
, enter
;
121 static Bool
queueScanner(Display
*, XEvent
*e
, char *args
) {
122 scanargs
*scan
= (scanargs
*) args
;
123 if ((e
->type
== LeaveNotify
) &&
124 (e
->xcrossing
.window
== scan
->w
) &&
125 (e
->xcrossing
.mode
== NotifyNormal
)) {
127 scan
->inferior
= (e
->xcrossing
.detail
== NotifyInferior
);
128 } else if ((e
->type
== EnterNotify
) && (e
->xcrossing
.mode
== NotifyUngrab
)) {
138 Blackbox::Blackbox(char **m_argv
, char *dpy_name
, char *rc
, char *menu
)
139 : BaseDisplay(m_argv
[0], dpy_name
) {
140 if (! XSupportsLocale())
141 fprintf(stderr
, "X server does not support locale\n");
143 if (XSetLocaleModifiers("") == NULL
)
144 fprintf(stderr
, "cannot set locale modifiers\n");
149 // try to make sure the ~/.openbox directory exists
150 mkdir(expandTilde("~/.openbox").c_str(), S_IREAD
| S_IWRITE
| S_IEXEC
|
151 S_IRGRP
| S_IWGRP
| S_IXGRP
|
152 S_IROTH
| S_IWOTH
| S_IXOTH
);
154 if (! rc
) rc
= "~/.openbox/rc";
155 rc_file
= expandTilde(rc
);
156 config
.setFile(rc_file
);
157 if (! menu
) menu
= "~/.openbox/menu";
158 menu_file
= expandTilde(menu
);
162 resource
.auto_raise_delay
.tv_sec
= resource
.auto_raise_delay
.tv_usec
= 0;
165 focused_window
= changing_window
= (BlackboxWindow
*) 0;
170 xatom
= new XAtom(getXDisplay());
172 cursor
.session
= XCreateFontCursor(getXDisplay(), XC_left_ptr
);
173 cursor
.move
= XCreateFontCursor(getXDisplay(), XC_fleur
);
174 cursor
.ll_angle
= XCreateFontCursor(getXDisplay(), XC_ll_angle
);
175 cursor
.lr_angle
= XCreateFontCursor(getXDisplay(), XC_lr_angle
);
176 cursor
.ul_angle
= XCreateFontCursor(getXDisplay(), XC_ul_angle
);
177 cursor
.ur_angle
= XCreateFontCursor(getXDisplay(), XC_ur_angle
);
179 for (unsigned int i
= 0; i
< getNumberOfScreens(); i
++) {
180 BScreen
*screen
= new BScreen(this, i
);
182 if (! screen
->isScreenManaged()) {
187 screenList
.push_back(screen
);
190 if (screenList
.empty()) {
192 i18n(blackboxSet
, blackboxNoManagableScreens
,
193 "Blackbox::Blackbox: no managable screens found, aborting.\n"));
197 // save current settings and default values
200 // set the screen with mouse to the first managed screen
201 active_screen
= screenList
.front();
204 XSynchronize(getXDisplay(), False
);
205 XSync(getXDisplay(), False
);
207 reconfigure_wait
= reread_menu_wait
= False
;
209 timer
= new BTimer(this, this);
210 timer
->setTimeout(0l);
214 Blackbox::~Blackbox(void) {
215 std::for_each(screenList
.begin(), screenList
.end(), PointerAssassin());
217 std::for_each(menuTimestamps
.begin(), menuTimestamps
.end(),
226 void Blackbox::process_event(XEvent
*e
) {
229 // strip the lock key modifiers
230 e
->xbutton
.state
&= ~(NumLockMask
| ScrollLockMask
| LockMask
);
232 last_time
= e
->xbutton
.time
;
234 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
235 Basemenu
*menu
= (Basemenu
*) 0;
236 Slit
*slit
= (Slit
*) 0;
237 Toolbar
*tbar
= (Toolbar
*) 0;
238 BScreen
*scrn
= (BScreen
*) 0;
240 if ((win
= searchWindow(e
->xbutton
.window
))) {
241 win
->buttonPressEvent(&e
->xbutton
);
243 /* XXX: is this sane on low colour desktops? */
244 if (e
->xbutton
.button
== 1)
245 win
->installColormap(True
);
246 } else if ((menu
= searchMenu(e
->xbutton
.window
))) {
247 menu
->buttonPressEvent(&e
->xbutton
);
248 } else if ((slit
= searchSlit(e
->xbutton
.window
))) {
249 slit
->buttonPressEvent(&e
->xbutton
);
250 } else if ((tbar
= searchToolbar(e
->xbutton
.window
))) {
251 tbar
->buttonPressEvent(&e
->xbutton
);
252 } else if ((scrn
= searchScreen(e
->xbutton
.window
))) {
253 scrn
->buttonPressEvent(&e
->xbutton
);
254 if (active_screen
!= scrn
) {
255 active_screen
= scrn
;
256 // first, set no focus window on the old screen
258 // and move focus to this screen
265 case ButtonRelease
: {
266 // strip the lock key modifiers
267 e
->xbutton
.state
&= ~(NumLockMask
| ScrollLockMask
| LockMask
);
269 last_time
= e
->xbutton
.time
;
271 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
272 Basemenu
*menu
= (Basemenu
*) 0;
273 Toolbar
*tbar
= (Toolbar
*) 0;
275 if ((win
= searchWindow(e
->xbutton
.window
)))
276 win
->buttonReleaseEvent(&e
->xbutton
);
277 else if ((menu
= searchMenu(e
->xbutton
.window
)))
278 menu
->buttonReleaseEvent(&e
->xbutton
);
279 else if ((tbar
= searchToolbar(e
->xbutton
.window
)))
280 tbar
->buttonReleaseEvent(&e
->xbutton
);
285 case ConfigureRequest
: {
286 // compress configure requests...
289 while(XCheckTypedWindowEvent(getXDisplay(), e
->xconfigurerequest
.window
,
290 ConfigureRequest
, &realevent
)) {
296 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
297 Slit
*slit
= (Slit
*) 0;
299 if ((win
= searchWindow(e
->xconfigurerequest
.window
))) {
300 win
->configureRequestEvent(&e
->xconfigurerequest
);
301 } else if ((slit
= searchSlit(e
->xconfigurerequest
.window
))) {
302 slit
->configureRequestEvent(&e
->xconfigurerequest
);
304 if (validateWindow(e
->xconfigurerequest
.window
)) {
307 xwc
.x
= e
->xconfigurerequest
.x
;
308 xwc
.y
= e
->xconfigurerequest
.y
;
309 xwc
.width
= e
->xconfigurerequest
.width
;
310 xwc
.height
= e
->xconfigurerequest
.height
;
311 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
312 xwc
.sibling
= e
->xconfigurerequest
.above
;
313 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
315 XConfigureWindow(getXDisplay(), e
->xconfigurerequest
.window
,
316 e
->xconfigurerequest
.value_mask
, &xwc
);
325 fprintf(stderr
, "Blackbox::process_event(): MapRequest for 0x%lx\n",
326 e
->xmaprequest
.window
);
329 BlackboxWindow
*win
= searchWindow(e
->xmaprequest
.window
);
332 if (win
->isIconic()) {
334 win
->setInputFocus();
337 BScreen
*screen
= searchScreen(e
->xmaprequest
.parent
);
341 we got a map request for a window who's parent isn't root. this
342 can happen in only one circumstance:
344 a client window unmapped a managed window, and then remapped it
345 somewhere between unmapping the client window and reparenting it
348 regardless of how it happens, we need to find the screen that
351 XWindowAttributes wattrib
;
352 if (! XGetWindowAttributes(getXDisplay(), e
->xmaprequest
.window
,
354 // failed to get the window attributes, perhaps the window has
355 // now been destroyed?
359 screen
= searchScreen(wattrib
.root
);
360 assert(screen
!= 0); // this should never happen
363 screen
->manageWindow(e
->xmaprequest
.window
);
370 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
371 Slit
*slit
= (Slit
*) 0;
372 BScreen
*screen
= (BScreen
*) 0;
374 if ((win
= searchWindow(e
->xunmap
.window
))) {
375 win
->unmapNotifyEvent(&e
->xunmap
);
376 } else if ((slit
= searchSlit(e
->xunmap
.window
))) {
377 slit
->unmapNotifyEvent(&e
->xunmap
);
378 } else if ((screen
= searchSystrayWindow(e
->xunmap
.window
))) {
379 screen
->removeSystrayWindow(e
->xunmap
.window
);
380 } else if ((screen
= searchDesktopWindow(e
->xunmap
.window
))) {
381 screen
->removeDesktopWindow(e
->xunmap
.window
);
387 case DestroyNotify
: {
388 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
389 Slit
*slit
= (Slit
*) 0;
390 BScreen
*screen
= (BScreen
*) 0;
391 BWindowGroup
*group
= (BWindowGroup
*) 0;
393 if ((win
= searchWindow(e
->xdestroywindow
.window
))) {
394 win
->destroyNotifyEvent(&e
->xdestroywindow
);
395 } else if ((slit
= searchSlit(e
->xdestroywindow
.window
))) {
396 slit
->removeClient(e
->xdestroywindow
.window
, False
);
397 } else if ((group
= searchGroup(e
->xdestroywindow
.window
))) {
399 } else if ((screen
= searchSystrayWindow(e
->xunmap
.window
))) {
400 screen
->removeSystrayWindow(e
->xunmap
.window
);
401 } else if ((screen
= searchDesktopWindow(e
->xunmap
.window
))) {
402 screen
->removeDesktopWindow(e
->xunmap
.window
);
408 case ReparentNotify
: {
410 this event is quite rare and is usually handled in unmapNotify
411 however, if the window is unmapped when the reparent event occurs
412 the window manager never sees it because an unmap event is not sent
413 to an already unmapped window.
415 BlackboxWindow
*win
= searchWindow(e
->xreparent
.window
);
417 win
->reparentNotifyEvent(&e
->xreparent
);
419 Slit
*slit
= searchSlit(e
->xreparent
.window
);
420 if (slit
&& slit
->getWindowID() != e
->xreparent
.parent
)
421 slit
->removeClient(e
->xreparent
.window
, True
);
427 // motion notify compression...
430 while (XCheckTypedWindowEvent(getXDisplay(), e
->xmotion
.window
,
431 MotionNotify
, &realevent
)) {
435 // if we have compressed some motion events, use the last one
439 // strip the lock key modifiers
440 e
->xbutton
.state
&= ~(NumLockMask
| ScrollLockMask
| LockMask
);
442 last_time
= e
->xmotion
.time
;
444 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
445 Basemenu
*menu
= (Basemenu
*) 0;
447 if ((win
= searchWindow(e
->xmotion
.window
)))
448 win
->motionNotifyEvent(&e
->xmotion
);
449 else if ((menu
= searchMenu(e
->xmotion
.window
)))
450 menu
->motionNotifyEvent(&e
->xmotion
);
455 case PropertyNotify
: {
456 last_time
= e
->xproperty
.time
;
458 BlackboxWindow
*win
= searchWindow(e
->xproperty
.window
);
460 win
->propertyNotifyEvent(&e
->xproperty
);
465 last_time
= e
->xcrossing
.time
;
467 BScreen
*screen
= (BScreen
*) 0;
468 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
469 Basemenu
*menu
= (Basemenu
*) 0;
470 Toolbar
*tbar
= (Toolbar
*) 0;
471 Slit
*slit
= (Slit
*) 0;
473 if (e
->xcrossing
.mode
== NotifyGrab
) break;
477 sa
.w
= e
->xcrossing
.window
;
478 sa
.enter
= sa
.leave
= False
;
479 XCheckIfEvent(getXDisplay(), &dummy
, queueScanner
, (char *) &sa
);
481 if ((e
->xcrossing
.window
== e
->xcrossing
.root
) &&
482 (screen
= searchScreen(e
->xcrossing
.window
))) {
483 screen
->getImageControl()->installRootColormap();
484 } else if ((win
= searchWindow(e
->xcrossing
.window
))) {
485 if (win
->getScreen()->isSloppyFocus() &&
486 (! win
->isFocused()) && (! no_focus
)) {
487 if (((! sa
.leave
) || sa
.inferior
) && win
->isVisible()) {
488 if (win
->setInputFocus())
489 win
->installColormap(True
); // XXX: shouldnt we honour no install?
492 } else if ((menu
= searchMenu(e
->xcrossing
.window
))) {
493 menu
->enterNotifyEvent(&e
->xcrossing
);
494 } else if ((tbar
= searchToolbar(e
->xcrossing
.window
))) {
495 tbar
->enterNotifyEvent(&e
->xcrossing
);
496 } else if ((slit
= searchSlit(e
->xcrossing
.window
))) {
497 slit
->enterNotifyEvent(&e
->xcrossing
);
503 last_time
= e
->xcrossing
.time
;
505 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
506 Basemenu
*menu
= (Basemenu
*) 0;
507 Toolbar
*tbar
= (Toolbar
*) 0;
508 Slit
*slit
= (Slit
*) 0;
510 if ((menu
= searchMenu(e
->xcrossing
.window
)))
511 menu
->leaveNotifyEvent(&e
->xcrossing
);
512 else if ((win
= searchWindow(e
->xcrossing
.window
)))
513 win
->installColormap(False
);
514 else if ((tbar
= searchToolbar(e
->xcrossing
.window
)))
515 tbar
->leaveNotifyEvent(&e
->xcrossing
);
516 else if ((slit
= searchSlit(e
->xcrossing
.window
)))
517 slit
->leaveNotifyEvent(&e
->xcrossing
);
522 // compress expose events
525 int ex1
, ey1
, ex2
, ey2
;
528 ex2
= ex1
+ e
->xexpose
.width
- 1;
529 ey2
= ey1
+ e
->xexpose
.height
- 1;
530 while (XCheckTypedWindowEvent(getXDisplay(), e
->xexpose
.window
,
531 Expose
, &realevent
)) {
535 ex1
= std::min(realevent
.xexpose
.x
, ex1
);
536 ey1
= std::min(realevent
.xexpose
.y
, ey1
);
537 ex2
= std::max(realevent
.xexpose
.x
+ realevent
.xexpose
.width
- 1, ex2
);
538 ey2
= std::max(realevent
.xexpose
.y
+ realevent
.xexpose
.height
- 1, ey2
);
543 // use the merged area
546 e
->xexpose
.width
= ex2
- ex1
+ 1;
547 e
->xexpose
.height
= ey2
- ey1
+ 1;
549 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
550 Basemenu
*menu
= (Basemenu
*) 0;
551 Toolbar
*tbar
= (Toolbar
*) 0;
553 if ((win
= searchWindow(e
->xexpose
.window
)))
554 win
->exposeEvent(&e
->xexpose
);
555 else if ((menu
= searchMenu(e
->xexpose
.window
)))
556 menu
->exposeEvent(&e
->xexpose
);
557 else if ((tbar
= searchToolbar(e
->xexpose
.window
)))
558 tbar
->exposeEvent(&e
->xexpose
);
564 Toolbar
*tbar
= searchToolbar(e
->xkey
.window
);
566 if (tbar
&& tbar
->isEditing())
567 tbar
->keyPressEvent(&e
->xkey
);
572 case ColormapNotify
: {
573 BScreen
*screen
= searchScreen(e
->xcolormap
.window
);
576 screen
->setRootColormapInstalled((e
->xcolormap
.state
==
577 ColormapInstalled
) ? True
: False
);
583 if (e
->xfocus
.detail
!= NotifyNonlinear
&&
584 e
->xfocus
.detail
!= NotifyAncestor
) {
586 don't process FocusIns when:
587 1. the new focus window isn't an ancestor or inferior of the old
588 focus window (NotifyNonlinear)
589 make sure to allow the FocusIn when the old focus window was an
590 ancestor but didn't have a parent, such as root (NotifyAncestor)
595 BlackboxWindow
*win
= searchWindow(e
->xfocus
.window
);
597 if (! win
->isFocused())
598 win
->setFocusFlag(True
);
601 set the event window to None. when the FocusOut event handler calls
602 this function recursively, it uses this as an indication that focus
603 has moved to a known window.
605 e
->xfocus
.window
= None
;
612 if (e
->xfocus
.detail
!= NotifyNonlinear
) {
614 don't process FocusOuts when:
615 2. the new focus window isn't an ancestor or inferior of the old
616 focus window (NotifyNonlinear)
621 BlackboxWindow
*win
= searchWindow(e
->xfocus
.window
);
622 if (win
&& win
->isFocused()) {
624 before we mark "win" as unfocused, we need to verify that focus is
625 going to a known location, is in a known location, or set focus
630 // don't check the current focus if FocusOut was generated during a grab
631 bool check_focus
= (e
->xfocus
.mode
== NotifyNormal
);
634 First, check if there is a pending FocusIn event waiting. if there
635 is, process it and determine if focus has moved to another window
636 (the FocusIn event handler sets the window in the event
637 structure to None to indicate this).
639 if (XCheckTypedEvent(getXDisplay(), FocusIn
, &event
)) {
641 process_event(&event
);
642 if (event
.xfocus
.window
== None
) {
650 Second, we query the X server for the current input focus.
651 to make sure that we keep a consistent state.
653 BlackboxWindow
*focus
;
656 XGetInputFocus(getXDisplay(), &w
, &revert
);
657 focus
= searchWindow(w
);
660 focus got from "win" to "focus" under some very strange
661 circumstances, and we need to make sure that the focus indication
664 setFocusedWindow(focus
);
666 // we have no idea where focus went... so we set it to somewhere
675 case ClientMessage
: {
676 if (e
->xclient
.format
== 32) {
677 if (e
->xclient
.message_type
== xatom
->getAtom(XAtom::wm_change_state
)) {
678 // WM_CHANGE_STATE message
679 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
680 if (! win
|| ! win
->validateClient()) return;
682 if (e
->xclient
.data
.l
[0] == IconicState
)
684 if (e
->xclient
.data
.l
[0] == NormalState
)
686 } else if (e
->xclient
.message_type
==
687 xatom
->getAtom(XAtom::blackbox_change_workspace
) ||
688 e
->xclient
.message_type
==
689 xatom
->getAtom(XAtom::net_current_desktop
)) {
690 // NET_CURRENT_DESKTOP message
691 BScreen
*screen
= searchScreen(e
->xclient
.window
);
693 unsigned int workspace
= e
->xclient
.data
.l
[0];
694 if (screen
&& workspace
< screen
->getWorkspaceCount())
695 screen
->changeWorkspaceID(workspace
);
696 } else if (e
->xclient
.message_type
==
697 xatom
->getAtom(XAtom::blackbox_change_window_focus
) ||
698 e
->xclient
.message_type
==
699 xatom
->getAtom(XAtom::net_active_window
)) {
701 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
705 win
->deiconify(False
, True
);
706 if (win
->isVisible() && win
->setInputFocus()) {
707 //win->getScreen()->getWorkspace(win->getWorkspaceNumber())->
709 win
->installColormap(True
);
712 } else if (e
->xclient
.message_type
==
713 xatom
->getAtom(XAtom::blackbox_cycle_window_focus
)) {
714 // BLACKBOX_CYCLE_WINDOW_FOCUS
715 BScreen
*screen
= searchScreen(e
->xclient
.window
);
718 if (! e
->xclient
.data
.l
[0])
723 } else if (e
->xclient
.message_type
==
724 xatom
->getAtom(XAtom::net_wm_desktop
)) {
726 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
729 BScreen
*screen
= win
->getScreen();
730 unsigned long wksp
= (unsigned) e
->xclient
.data
.l
[0];
731 if (wksp
< screen
->getWorkspaceCount()) {
732 if (win
->isIconic()) win
->deiconify(False
, True
);
733 if (win
->isStuck()) win
->stick();
734 if (wksp
!= screen
->getCurrentWorkspaceID())
738 screen
->reassociateWindow(win
, wksp
, True
);
739 } else if (wksp
== 0xfffffffe) { // XXX: BUG, BUT DOING THIS SO KDE WORKS FOR NOW!!
740 if (win
->isIconic()) win
->deiconify(False
, True
);
741 if (! win
->isStuck()) win
->stick();
742 if (! win
->isVisible()) win
->show();
745 } else if (e
->xclient
.message_type
==
746 xatom
->getAtom(XAtom::blackbox_change_attributes
)) {
747 // BLACKBOX_CHANGE_ATTRIBUTES
748 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
750 if (win
&& win
->validateClient()) {
752 net
.flags
= e
->xclient
.data
.l
[0];
753 net
.attrib
= e
->xclient
.data
.l
[1];
754 net
.workspace
= e
->xclient
.data
.l
[2];
755 net
.stack
= e
->xclient
.data
.l
[3];
756 net
.decoration
= e
->xclient
.data
.l
[4];
758 win
->changeBlackboxHints(&net
);
760 } else if (e
->xclient
.message_type
==
761 xatom
->getAtom(XAtom::net_number_of_desktops
)) {
762 // NET_NUMBER_OF_DESKTOPS
763 BScreen
*screen
= searchScreen(e
->xclient
.window
);
765 if (e
->xclient
.data
.l
[0] > 0) {
766 if ((unsigned) e
->xclient
.data
.l
[0] < screen
->getWorkspaceCount()) {
768 for (int i
= screen
->getWorkspaceCount();
769 i
> e
->xclient
.data
.l
[0]; --i
)
770 screen
->removeLastWorkspace();
771 // removeLast already sets the current workspace to the
772 // last available one.
773 } else if ((unsigned) e
->xclient
.data
.l
[0] >
774 screen
->getWorkspaceCount()) {
776 for(int i
= screen
->getWorkspaceCount();
777 i
< e
->xclient
.data
.l
[0]; ++i
)
778 screen
->addWorkspace();
781 } else if (e
->xclient
.message_type
==
782 xatom
->getAtom(XAtom::net_close_window
)) {
784 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
785 if (win
&& win
->validateClient())
786 win
->close(); // could this be smarter?
787 } else if (e
->xclient
.message_type
==
788 xatom
->getAtom(XAtom::net_wm_moveresize
)) {
790 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
791 if (win
&& win
->validateClient()) {
792 int x_root
= e
->xclient
.data
.l
[0],
793 y_root
= e
->xclient
.data
.l
[1];
794 if ((Atom
) e
->xclient
.data
.l
[2] ==
795 xatom
->getAtom(XAtom::net_wm_moveresize_move
)) {
796 win
->beginMove(x_root
, y_root
);
798 if ((Atom
) e
->xclient
.data
.l
[2] ==
799 xatom
->getAtom(XAtom::net_wm_moveresize_size_topleft
))
800 win
->beginResize(x_root
, y_root
, BlackboxWindow::TopLeft
);
801 else if ((Atom
) e
->xclient
.data
.l
[2] ==
802 xatom
->getAtom(XAtom::net_wm_moveresize_size_topright
))
803 win
->beginResize(x_root
, y_root
, BlackboxWindow::TopRight
);
804 else if ((Atom
) e
->xclient
.data
.l
[2] ==
805 xatom
->getAtom(XAtom::net_wm_moveresize_size_bottomleft
))
806 win
->beginResize(x_root
, y_root
, BlackboxWindow::BottomLeft
);
807 else if ((Atom
) e
->xclient
.data
.l
[2] ==
808 xatom
->getAtom(XAtom::net_wm_moveresize_size_bottomright
))
809 win
->beginResize(x_root
, y_root
, BlackboxWindow::BottomRight
);
812 } else if (e
->xclient
.message_type
==
813 xatom
->getAtom(XAtom::net_wm_state
)) {
815 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
816 if (win
&& win
->validateClient()) {
817 const Atom action
= (Atom
) e
->xclient
.data
.l
[0];
818 const Atom state
[] = { (Atom
) e
->xclient
.data
.l
[1],
819 (Atom
) e
->xclient
.data
.l
[2] };
821 for (int i
= 0; i
< 2; ++i
) {
825 if ((Atom
) e
->xclient
.data
.l
[0] == 1) {
827 if (state
[i
] == xatom
->getAtom(XAtom::net_wm_state_modal
)) {
829 } else if (state
[i
] ==
830 xatom
->getAtom(XAtom::net_wm_state_maximized_vert
)) {
831 if (win
->isMaximizedHoriz()) {
832 win
->maximize(0); // unmaximize
833 win
->maximize(1); // full
834 } else if (! win
->isMaximized()) {
835 win
->maximize(2); // vert
837 } else if (state
[i
] ==
838 xatom
->getAtom(XAtom::net_wm_state_maximized_horz
)) {
839 if (win
->isMaximizedVert()) {
840 win
->maximize(0); // unmaximize
841 win
->maximize(1); // full
842 } else if (! win
->isMaximized()) {
843 win
->maximize(3); // horiz
845 } else if (state
[i
] ==
846 xatom
->getAtom(XAtom::net_wm_state_shaded
)) {
847 if (! win
->isShaded())
849 } else if (state
[i
] ==
850 xatom
->getAtom(XAtom::net_wm_state_skip_taskbar
)) {
851 win
->setSkipTaskbar(True
);
852 } else if (state
[i
] ==
853 xatom
->getAtom(XAtom::net_wm_state_skip_pager
)) {
854 win
->setSkipPager(True
);
855 } else if (state
[i
] ==
856 xatom
->getAtom(XAtom::net_wm_state_fullscreen
)) {
857 win
->setFullscreen(True
);
859 } else if (action
== 0) {
861 if (state
[i
] == xatom
->getAtom(XAtom::net_wm_state_modal
)) {
862 win
->setModal(False
);
863 } else if (state
[i
] ==
864 xatom
->getAtom(XAtom::net_wm_state_maximized_vert
)) {
865 if (win
->isMaximizedFull()) {
866 win
->maximize(0); // unmaximize
867 win
->maximize(3); // horiz
868 } else if (win
->isMaximizedVert()) {
869 win
->maximize(0); // unmaximize
871 } else if (state
[i
] ==
872 xatom
->getAtom(XAtom::net_wm_state_maximized_horz
)) {
873 if (win
->isMaximizedFull()) {
874 win
->maximize(0); // unmaximize
875 win
->maximize(2); // vert
876 } else if (win
->isMaximizedHoriz()) {
877 win
->maximize(0); // unmaximize
879 } else if (state
[i
] ==
880 xatom
->getAtom(XAtom::net_wm_state_shaded
)) {
883 } else if (state
[i
] ==
884 xatom
->getAtom(XAtom::net_wm_state_skip_taskbar
)) {
885 win
->setSkipTaskbar(False
);
886 } else if (state
[i
] ==
887 xatom
->getAtom(XAtom::net_wm_state_skip_pager
)) {
888 win
->setSkipPager(False
);
889 } else if (state
[i
] ==
890 xatom
->getAtom(XAtom::net_wm_state_fullscreen
)) {
891 win
->setFullscreen(False
);
893 } else if (action
== 2) {
895 if (state
[i
] == xatom
->getAtom(XAtom::net_wm_state_modal
)) {
896 win
->setModal(! win
->isModal());
897 } else if (state
[i
] ==
898 xatom
->getAtom(XAtom::net_wm_state_maximized_vert
)) {
899 if (win
->isMaximizedFull()) {
900 win
->maximize(0); // unmaximize
901 win
->maximize(3); // horiz
902 } else if (win
->isMaximizedVert()) {
903 win
->maximize(0); // unmaximize
904 } else if (win
->isMaximizedHoriz()) {
905 win
->maximize(0); // unmaximize
906 win
->maximize(1); // full
908 win
->maximize(2); // vert
910 } else if (state
[i
] ==
911 xatom
->getAtom(XAtom::net_wm_state_maximized_horz
)) {
912 if (win
->isMaximizedFull()) {
913 win
->maximize(0); // unmaximize
914 win
->maximize(2); // vert
915 } else if (win
->isMaximizedHoriz()) {
916 win
->maximize(0); // unmaximize
917 } else if (win
->isMaximizedVert()) {
918 win
->maximize(0); // unmaximize
919 win
->maximize(1); // full
921 win
->maximize(3); // horiz
923 } else if (state
[i
] ==
924 xatom
->getAtom(XAtom::net_wm_state_shaded
)) {
926 } else if (state
[i
] ==
927 xatom
->getAtom(XAtom::net_wm_state_skip_taskbar
)) {
928 win
->setSkipTaskbar(! win
->skipTaskbar());
929 } else if (state
[i
] ==
930 xatom
->getAtom(XAtom::net_wm_state_skip_pager
)) {
931 win
->setSkipPager(! win
->skipPager());
932 } else if (state
[i
] ==
933 xatom
->getAtom(XAtom::net_wm_state_fullscreen
)) {
934 win
->setFullscreen(! win
->isFullscreen());
946 case ConfigureNotify
:
948 break; // not handled, just ignore
952 if (e
->type
== getShapeEventBase()) {
953 XShapeEvent
*shape_event
= (XShapeEvent
*) e
;
954 BlackboxWindow
*win
= searchWindow(e
->xany
.window
);
957 win
->shapeEvent(shape_event
);
965 bool Blackbox::handleSignal(int sig
) {
991 bool Blackbox::validateWindow(Window window
) {
993 if (XCheckTypedWindowEvent(getXDisplay(), window
, DestroyNotify
, &event
)) {
994 XPutBackEvent(getXDisplay(), &event
);
1003 BScreen
*Blackbox::searchScreen(Window window
) {
1004 ScreenList::iterator it
= screenList
.begin();
1006 for (; it
!= screenList
.end(); ++it
) {
1008 if (s
->getRootWindow() == window
)
1012 return (BScreen
*) 0;
1016 BScreen
*Blackbox::searchDesktopWindow(Window window
) {
1017 WindowScreenLookup::iterator it
= desktopSearchList
.find(window
);
1018 if (it
!= desktopSearchList
.end())
1021 return (BScreen
*) 0;
1025 BScreen
*Blackbox::searchSystrayWindow(Window window
) {
1026 WindowScreenLookup::iterator it
= systraySearchList
.find(window
);
1027 if (it
!= systraySearchList
.end())
1030 return (BScreen
*) 0;
1034 BlackboxWindow
*Blackbox::searchWindow(Window window
) {
1035 WindowLookup::iterator it
= windowSearchList
.find(window
);
1036 if (it
!= windowSearchList
.end())
1039 return (BlackboxWindow
*) 0;
1043 BWindowGroup
*Blackbox::searchGroup(Window window
) {
1044 GroupLookup::iterator it
= groupSearchList
.find(window
);
1045 if (it
!= groupSearchList
.end())
1048 return (BWindowGroup
*) 0;
1052 Basemenu
*Blackbox::searchMenu(Window window
) {
1053 MenuLookup::iterator it
= menuSearchList
.find(window
);
1054 if (it
!= menuSearchList
.end())
1057 return (Basemenu
*) 0;
1061 Toolbar
*Blackbox::searchToolbar(Window window
) {
1062 ToolbarLookup::iterator it
= toolbarSearchList
.find(window
);
1063 if (it
!= toolbarSearchList
.end())
1066 return (Toolbar
*) 0;
1070 Slit
*Blackbox::searchSlit(Window window
) {
1071 SlitLookup::iterator it
= slitSearchList
.find(window
);
1072 if (it
!= slitSearchList
.end())
1079 void Blackbox::saveDesktopWindowSearch(Window window
, BScreen
*screen
) {
1080 desktopSearchList
.insert(WindowScreenLookupPair(window
, screen
));
1084 void Blackbox::saveSystrayWindowSearch(Window window
, BScreen
*screen
) {
1085 systraySearchList
.insert(WindowScreenLookupPair(window
, screen
));
1089 void Blackbox::saveWindowSearch(Window window
, BlackboxWindow
*data
) {
1090 windowSearchList
.insert(WindowLookupPair(window
, data
));
1094 void Blackbox::saveGroupSearch(Window window
, BWindowGroup
*data
) {
1095 groupSearchList
.insert(GroupLookupPair(window
, data
));
1099 void Blackbox::saveMenuSearch(Window window
, Basemenu
*data
) {
1100 menuSearchList
.insert(MenuLookupPair(window
, data
));
1104 void Blackbox::saveToolbarSearch(Window window
, Toolbar
*data
) {
1105 toolbarSearchList
.insert(ToolbarLookupPair(window
, data
));
1109 void Blackbox::saveSlitSearch(Window window
, Slit
*data
) {
1110 slitSearchList
.insert(SlitLookupPair(window
, data
));
1114 void Blackbox::removeDesktopWindowSearch(Window window
) {
1115 desktopSearchList
.erase(window
);
1119 void Blackbox::removeSystrayWindowSearch(Window window
) {
1120 systraySearchList
.erase(window
);
1124 void Blackbox::removeWindowSearch(Window window
) {
1125 windowSearchList
.erase(window
);
1129 void Blackbox::removeGroupSearch(Window window
) {
1130 groupSearchList
.erase(window
);
1134 void Blackbox::removeMenuSearch(Window window
) {
1135 menuSearchList
.erase(window
);
1139 void Blackbox::removeToolbarSearch(Window window
) {
1140 toolbarSearchList
.erase(window
);
1144 void Blackbox::removeSlitSearch(Window window
) {
1145 slitSearchList
.erase(window
);
1149 void Blackbox::restart(const char *prog
) {
1153 putenv(const_cast<char *>(screenList
.front()->displayString().c_str()));
1154 execlp(prog
, prog
, NULL
);
1158 // fall back in case the above execlp doesn't work
1159 execvp(argv
[0], argv
);
1160 string name
= basename(argv
[0]);
1161 execvp(name
.c_str(), argv
);
1165 void Blackbox::shutdown(void) {
1166 BaseDisplay::shutdown();
1168 XSetInputFocus(getXDisplay(), PointerRoot
, None
, CurrentTime
);
1170 std::for_each(screenList
.begin(), screenList
.end(),
1171 std::mem_fun(&BScreen::shutdown
));
1173 XSync(getXDisplay(), False
);
1178 * Save all values as they are so that the defaults will be written to the rc
1181 void Blackbox::save_rc(void) {
1182 config
.setAutoSave(false);
1184 config
.setValue("session.colorsPerChannel", resource
.colors_per_channel
);
1185 config
.setValue("session.doubleClickInterval",
1186 resource
.double_click_interval
);
1187 config
.setValue("session.autoRaiseDelay",
1188 ((resource
.auto_raise_delay
.tv_sec
* 1000) +
1189 (resource
.auto_raise_delay
.tv_usec
/ 1000)));
1190 config
.setValue("session.cacheLife", resource
.cache_life
/ 60000);
1191 config
.setValue("session.cacheMax", resource
.cache_max
);
1192 config
.setValue("session.styleFile", resource
.style_file
);
1193 config
.setValue("session.titlebarLayout", resource
.titlebar_layout
);
1195 std::for_each(screenList
.begin(), screenList
.end(),
1196 std::mem_fun(&BScreen::save_rc
));
1198 config
.setAutoSave(true);
1203 void Blackbox::load_rc(void) {
1204 if (! config
.load())
1209 if (! config
.getValue("session.colorsPerChannel",
1210 resource
.colors_per_channel
))
1211 resource
.colors_per_channel
= 4;
1212 if (resource
.colors_per_channel
< 2) resource
.colors_per_channel
= 2;
1213 else if (resource
.colors_per_channel
> 6) resource
.colors_per_channel
= 6;
1215 if (config
.getValue("session.styleFile", s
))
1216 resource
.style_file
= expandTilde(s
);
1218 resource
.style_file
= DEFAULTSTYLE
;
1220 if (! config
.getValue("session.doubleClickInterval",
1221 resource
.double_click_interval
));
1222 resource
.double_click_interval
= 250;
1224 if (! config
.getValue("session.autoRaiseDelay",
1225 resource
.auto_raise_delay
.tv_usec
))
1226 resource
.auto_raise_delay
.tv_usec
= 400;
1227 resource
.auto_raise_delay
.tv_sec
= resource
.auto_raise_delay
.tv_usec
/ 1000;
1228 resource
.auto_raise_delay
.tv_usec
-=
1229 (resource
.auto_raise_delay
.tv_sec
* 1000);
1230 resource
.auto_raise_delay
.tv_usec
*= 1000;
1232 if (! config
.getValue("session.cacheLife", resource
.cache_life
))
1233 resource
.cache_life
= 5;
1234 resource
.cache_life
*= 60000;
1236 if (! config
.getValue("session.cacheMax", resource
.cache_max
))
1237 resource
.cache_max
= 200;
1239 if (! config
.getValue("session.titlebarLayout", resource
.titlebar_layout
))
1240 resource
.titlebar_layout
= "ILMC";
1244 void Blackbox::reconfigure(void) {
1245 reconfigure_wait
= True
;
1247 if (! timer
->isTiming()) timer
->start();
1251 void Blackbox::real_reconfigure(void) {
1254 std::for_each(menuTimestamps
.begin(), menuTimestamps
.end(),
1256 menuTimestamps
.clear();
1260 std::for_each(screenList
.begin(), screenList
.end(),
1261 std::mem_fun(&BScreen::reconfigure
));
1265 void Blackbox::checkMenu(void) {
1266 bool reread
= False
;
1267 MenuTimestampList::iterator it
= menuTimestamps
.begin();
1268 for(; it
!= menuTimestamps
.end(); ++it
) {
1269 MenuTimestamp
*tmp
= *it
;
1272 if (! stat(tmp
->filename
.c_str(), &buf
)) {
1273 if (tmp
->timestamp
!= buf
.st_ctime
)
1280 if (reread
) rereadMenu();
1284 void Blackbox::rereadMenu(void) {
1285 reread_menu_wait
= True
;
1287 if (! timer
->isTiming()) timer
->start();
1291 void Blackbox::real_rereadMenu(void) {
1292 std::for_each(menuTimestamps
.begin(), menuTimestamps
.end(),
1294 menuTimestamps
.clear();
1296 std::for_each(screenList
.begin(), screenList
.end(),
1297 std::mem_fun(&BScreen::rereadMenu
));
1301 void Blackbox::saveStyleFilename(const string
& filename
) {
1302 assert(! filename
.empty());
1303 resource
.style_file
= filename
;
1304 config
.setValue("session.styleFile", resource
.style_file
);
1308 void Blackbox::addMenuTimestamp(const string
& filename
) {
1309 assert(! filename
.empty());
1312 MenuTimestampList::iterator it
= menuTimestamps
.begin();
1313 for (; it
!= menuTimestamps
.end() && ! found
; ++it
) {
1314 if ((*it
)->filename
== filename
) found
= True
;
1319 if (! stat(filename
.c_str(), &buf
)) {
1320 MenuTimestamp
*ts
= new MenuTimestamp
;
1322 ts
->filename
= filename
;
1323 ts
->timestamp
= buf
.st_ctime
;
1325 menuTimestamps
.push_back(ts
);
1331 void Blackbox::timeout(void) {
1332 if (reconfigure_wait
)
1335 if (reread_menu_wait
)
1338 reconfigure_wait
= reread_menu_wait
= False
;
1342 void Blackbox::setChangingWindow(BlackboxWindow
*win
) {
1343 // make sure one of the two is null and the other isn't
1344 assert((! changing_window
&& win
) || (! win
&& changing_window
));
1345 changing_window
= win
;
1349 void Blackbox::setFocusedWindow(BlackboxWindow
*win
) {
1350 if (focused_window
&& focused_window
== win
) // nothing to do
1353 BScreen
*old_screen
= 0;
1355 if (focused_window
) {
1356 focused_window
->setFocusFlag(False
);
1357 old_screen
= focused_window
->getScreen();
1360 if (win
&& ! win
->isIconic()) {
1361 // the active screen is the one with the last focused window...
1362 // this will keep focus on this screen no matter where the mouse goes,
1363 // so multihead keybindings will continue to work on that screen until the
1364 // user focuses a window on a different screen.
1365 active_screen
= win
->getScreen();
1366 focused_window
= win
;
1370 if (active_screen
) {
1371 // set input focus to the toolbar of the screen with mouse
1372 XSetInputFocus(getXDisplay(),
1373 active_screen
->getRootWindow(),
1374 RevertToPointerRoot
, CurrentTime
);
1376 // set input focus to the toolbar of the first managed screen
1377 XSetInputFocus(getXDisplay(),
1378 screenList
.front()->getRootWindow(),
1379 RevertToPointerRoot
, CurrentTime
);
1382 // set input focus to the toolbar of the last screen
1383 XSetInputFocus(getXDisplay(), old_screen
->getRootWindow(),
1384 RevertToPointerRoot
, CurrentTime
);
1388 if (active_screen
&& active_screen
->isScreenManaged()) {
1389 active_screen
->getToolbar()->redrawWindowLabel(True
);
1390 active_screen
->updateNetizenWindowFocus();
1393 if (old_screen
&& old_screen
!= active_screen
) {
1394 old_screen
->getToolbar()->redrawWindowLabel(True
);
1395 old_screen
->updateNetizenWindowFocus();