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"
118 Blackbox::Blackbox(char **m_argv
, char *dpy_name
, char *rc
, char *menu
)
119 : BaseDisplay(m_argv
[0], dpy_name
) {
120 if (! XSupportsLocale())
121 fprintf(stderr
, "X server does not support locale\n");
123 if (XSetLocaleModifiers("") == NULL
)
124 fprintf(stderr
, "cannot set locale modifiers\n");
129 // try to make sure the ~/.openbox directory exists
130 mkdir(expandTilde("~/.openbox").c_str(), S_IREAD
| S_IWRITE
| S_IEXEC
|
131 S_IRGRP
| S_IWGRP
| S_IXGRP
|
132 S_IROTH
| S_IWOTH
| S_IXOTH
);
134 if (! rc
) rc
= "~/.openbox/rc";
135 rc_file
= expandTilde(rc
);
136 config
.setFile(rc_file
);
137 if (! menu
) menu
= "~/.openbox/menu";
138 menu_file
= expandTilde(menu
);
142 resource
.auto_raise_delay
.tv_sec
= resource
.auto_raise_delay
.tv_usec
= 0;
145 focused_window
= changing_window
= (BlackboxWindow
*) 0;
149 xatom
= new XAtom(getXDisplay());
151 cursor
.session
= XCreateFontCursor(getXDisplay(), XC_left_ptr
);
152 cursor
.move
= XCreateFontCursor(getXDisplay(), XC_fleur
);
153 cursor
.ll_angle
= XCreateFontCursor(getXDisplay(), XC_ll_angle
);
154 cursor
.lr_angle
= XCreateFontCursor(getXDisplay(), XC_lr_angle
);
155 cursor
.ul_angle
= XCreateFontCursor(getXDisplay(), XC_ul_angle
);
156 cursor
.ur_angle
= XCreateFontCursor(getXDisplay(), XC_ur_angle
);
158 for (unsigned int i
= 0; i
< getNumberOfScreens(); i
++) {
159 BScreen
*screen
= new BScreen(this, i
);
161 if (! screen
->isScreenManaged()) {
166 screenList
.push_back(screen
);
169 if (screenList
.empty()) {
171 i18n(blackboxSet
, blackboxNoManagableScreens
,
172 "Blackbox::Blackbox: no managable screens found, aborting.\n"));
176 // save current settings and default values
179 // set the screen with mouse to the first managed screen
180 active_screen
= screenList
.front();
183 XSynchronize(getXDisplay(), False
);
184 XSync(getXDisplay(), False
);
186 reconfigure_wait
= reread_menu_wait
= False
;
188 timer
= new BTimer(this, this);
189 timer
->setTimeout(0l);
193 Blackbox::~Blackbox(void) {
194 std::for_each(screenList
.begin(), screenList
.end(), PointerAssassin());
196 std::for_each(menuTimestamps
.begin(), menuTimestamps
.end(),
205 void Blackbox::process_event(XEvent
*e
) {
208 // strip the lock key modifiers
209 e
->xbutton
.state
&= ~(NumLockMask
| ScrollLockMask
| LockMask
);
211 last_time
= e
->xbutton
.time
;
213 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
214 Basemenu
*menu
= (Basemenu
*) 0;
215 Slit
*slit
= (Slit
*) 0;
216 Toolbar
*tbar
= (Toolbar
*) 0;
217 BScreen
*scrn
= (BScreen
*) 0;
219 if ((win
= searchWindow(e
->xbutton
.window
))) {
220 win
->buttonPressEvent(&e
->xbutton
);
222 /* XXX: is this sane on low colour desktops? */
223 if (e
->xbutton
.button
== 1)
224 win
->installColormap(True
);
225 } else if ((menu
= searchMenu(e
->xbutton
.window
))) {
226 menu
->buttonPressEvent(&e
->xbutton
);
227 } else if ((slit
= searchSlit(e
->xbutton
.window
))) {
228 slit
->buttonPressEvent(&e
->xbutton
);
229 } else if ((tbar
= searchToolbar(e
->xbutton
.window
))) {
230 tbar
->buttonPressEvent(&e
->xbutton
);
231 } else if ((scrn
= searchScreen(e
->xbutton
.window
))) {
232 scrn
->buttonPressEvent(&e
->xbutton
);
233 if (active_screen
!= scrn
) {
234 active_screen
= scrn
;
235 // first, set no focus window on the old screen
237 // and move focus to this screen
244 case ButtonRelease
: {
245 // strip the lock key modifiers
246 e
->xbutton
.state
&= ~(NumLockMask
| ScrollLockMask
| LockMask
);
248 last_time
= e
->xbutton
.time
;
250 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
251 Basemenu
*menu
= (Basemenu
*) 0;
252 Toolbar
*tbar
= (Toolbar
*) 0;
254 if ((win
= searchWindow(e
->xbutton
.window
)))
255 win
->buttonReleaseEvent(&e
->xbutton
);
256 else if ((menu
= searchMenu(e
->xbutton
.window
)))
257 menu
->buttonReleaseEvent(&e
->xbutton
);
258 else if ((tbar
= searchToolbar(e
->xbutton
.window
)))
259 tbar
->buttonReleaseEvent(&e
->xbutton
);
264 case ConfigureRequest
: {
265 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
266 Slit
*slit
= (Slit
*) 0;
268 if ((win
= searchWindow(e
->xconfigurerequest
.window
))) {
269 win
->configureRequestEvent(&e
->xconfigurerequest
);
270 } else if ((slit
= searchSlit(e
->xconfigurerequest
.window
))) {
271 slit
->configureRequestEvent(&e
->xconfigurerequest
);
273 if (validateWindow(e
->xconfigurerequest
.window
)) {
276 xwc
.x
= e
->xconfigurerequest
.x
;
277 xwc
.y
= e
->xconfigurerequest
.y
;
278 xwc
.width
= e
->xconfigurerequest
.width
;
279 xwc
.height
= e
->xconfigurerequest
.height
;
280 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
281 xwc
.sibling
= e
->xconfigurerequest
.above
;
282 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
284 XConfigureWindow(getXDisplay(), e
->xconfigurerequest
.window
,
285 e
->xconfigurerequest
.value_mask
, &xwc
);
294 fprintf(stderr
, "Blackbox::process_event(): MapRequest for 0x%lx\n",
295 e
->xmaprequest
.window
);
298 BlackboxWindow
*win
= searchWindow(e
->xmaprequest
.window
);
302 if (win
->isIconic()) {
306 if (win
->isShaded()) {
311 if (focus
&& (win
->isTransient() || win
->getScreen()->doFocusNew()) &&
313 win
->setInputFocus();
315 BScreen
*screen
= searchScreen(e
->xmaprequest
.parent
);
319 we got a map request for a window who's parent isn't root. this
320 can happen in only one circumstance:
322 a client window unmapped a managed window, and then remapped it
323 somewhere between unmapping the client window and reparenting it
326 regardless of how it happens, we need to find the screen that
329 XWindowAttributes wattrib
;
330 if (! XGetWindowAttributes(getXDisplay(), e
->xmaprequest
.window
,
332 // failed to get the window attributes, perhaps the window has
333 // now been destroyed?
337 screen
= searchScreen(wattrib
.root
);
338 assert(screen
!= 0); // this should never happen
341 screen
->manageWindow(e
->xmaprequest
.window
);
348 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
349 Slit
*slit
= (Slit
*) 0;
350 BScreen
*screen
= (BScreen
*) 0;
352 if ((win
= searchWindow(e
->xunmap
.window
))) {
353 win
->unmapNotifyEvent(&e
->xunmap
);
354 } else if ((slit
= searchSlit(e
->xunmap
.window
))) {
355 slit
->unmapNotifyEvent(&e
->xunmap
);
356 } else if ((screen
= searchSystrayWindow(e
->xunmap
.window
))) {
357 screen
->removeSystrayWindow(e
->xunmap
.window
);
363 case DestroyNotify
: {
364 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
365 Slit
*slit
= (Slit
*) 0;
366 BScreen
*screen
= (BScreen
*) 0;
367 BWindowGroup
*group
= (BWindowGroup
*) 0;
369 if ((win
= searchWindow(e
->xdestroywindow
.window
))) {
370 win
->destroyNotifyEvent(&e
->xdestroywindow
);
371 } else if ((slit
= searchSlit(e
->xdestroywindow
.window
))) {
372 slit
->removeClient(e
->xdestroywindow
.window
, False
);
373 } else if ((group
= searchGroup(e
->xdestroywindow
.window
))) {
375 } else if ((screen
= searchSystrayWindow(e
->xunmap
.window
))) {
376 screen
->removeSystrayWindow(e
->xunmap
.window
);
382 case ReparentNotify
: {
384 this event is quite rare and is usually handled in unmapNotify
385 however, if the window is unmapped when the reparent event occurs
386 the window manager never sees it because an unmap event is not sent
387 to an already unmapped window.
389 BlackboxWindow
*win
= searchWindow(e
->xreparent
.window
);
391 win
->reparentNotifyEvent(&e
->xreparent
);
393 Slit
*slit
= searchSlit(e
->xreparent
.window
);
394 if (slit
&& slit
->getWindowID() != e
->xreparent
.parent
)
395 slit
->removeClient(e
->xreparent
.window
, True
);
401 // motion notify compression...
404 while (XCheckTypedWindowEvent(getXDisplay(), e
->xmotion
.window
,
405 MotionNotify
, &realevent
)) {
409 // if we have compressed some motion events, use the last one
413 // the pointer is on the wrong screen
414 if (! e
->xmotion
.same_screen
)
417 // strip the lock key modifiers
418 e
->xmotion
.state
&= ~(NumLockMask
| ScrollLockMask
| LockMask
);
420 last_time
= e
->xmotion
.time
;
422 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
423 Basemenu
*menu
= (Basemenu
*) 0;
425 if ((win
= searchWindow(e
->xmotion
.window
)))
426 win
->motionNotifyEvent(&e
->xmotion
);
427 else if ((menu
= searchMenu(e
->xmotion
.window
)))
428 menu
->motionNotifyEvent(&e
->xmotion
);
433 case PropertyNotify
: {
434 last_time
= e
->xproperty
.time
;
436 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
437 BScreen
*screen
= (BScreen
*) 0;
439 if ((win
= searchWindow(e
->xproperty
.window
)))
440 win
->propertyNotifyEvent(&e
->xproperty
);
441 else if ((screen
= searchScreen(e
->xproperty
.window
)))
442 screen
->propertyNotifyEvent(&e
->xproperty
);
447 last_time
= e
->xcrossing
.time
;
449 BScreen
*screen
= (BScreen
*) 0;
450 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
451 Basemenu
*menu
= (Basemenu
*) 0;
452 Toolbar
*tbar
= (Toolbar
*) 0;
453 Slit
*slit
= (Slit
*) 0;
455 if (e
->xcrossing
.mode
== NotifyGrab
) break;
457 if ((e
->xcrossing
.window
== e
->xcrossing
.root
) &&
458 (screen
= searchScreen(e
->xcrossing
.window
))) {
459 screen
->getImageControl()->installRootColormap();
460 } else if ((win
= searchWindow(e
->xcrossing
.window
))) {
462 win
->enterNotifyEvent(&e
->xcrossing
);
463 } else if ((menu
= searchMenu(e
->xcrossing
.window
))) {
464 menu
->enterNotifyEvent(&e
->xcrossing
);
465 } else if ((tbar
= searchToolbar(e
->xcrossing
.window
))) {
466 tbar
->enterNotifyEvent(&e
->xcrossing
);
467 } else if ((slit
= searchSlit(e
->xcrossing
.window
))) {
468 slit
->enterNotifyEvent(&e
->xcrossing
);
474 last_time
= e
->xcrossing
.time
;
476 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
477 Basemenu
*menu
= (Basemenu
*) 0;
478 Toolbar
*tbar
= (Toolbar
*) 0;
479 Slit
*slit
= (Slit
*) 0;
481 if ((menu
= searchMenu(e
->xcrossing
.window
)))
482 menu
->leaveNotifyEvent(&e
->xcrossing
);
483 else if ((win
= searchWindow(e
->xcrossing
.window
)))
484 win
->leaveNotifyEvent(&e
->xcrossing
);
485 else if ((tbar
= searchToolbar(e
->xcrossing
.window
)))
486 tbar
->leaveNotifyEvent(&e
->xcrossing
);
487 else if ((slit
= searchSlit(e
->xcrossing
.window
)))
488 slit
->leaveNotifyEvent(&e
->xcrossing
);
493 // compress expose events
496 int ex1
, ey1
, ex2
, ey2
;
499 ex2
= ex1
+ e
->xexpose
.width
- 1;
500 ey2
= ey1
+ e
->xexpose
.height
- 1;
501 while (XCheckTypedWindowEvent(getXDisplay(), e
->xexpose
.window
,
502 Expose
, &realevent
)) {
506 ex1
= std::min(realevent
.xexpose
.x
, ex1
);
507 ey1
= std::min(realevent
.xexpose
.y
, ey1
);
508 ex2
= std::max(realevent
.xexpose
.x
+ realevent
.xexpose
.width
- 1, ex2
);
509 ey2
= std::max(realevent
.xexpose
.y
+ realevent
.xexpose
.height
- 1, ey2
);
514 // use the merged area
517 e
->xexpose
.width
= ex2
- ex1
+ 1;
518 e
->xexpose
.height
= ey2
- ey1
+ 1;
520 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
521 Basemenu
*menu
= (Basemenu
*) 0;
522 Toolbar
*tbar
= (Toolbar
*) 0;
524 if ((win
= searchWindow(e
->xexpose
.window
)))
525 win
->exposeEvent(&e
->xexpose
);
526 else if ((menu
= searchMenu(e
->xexpose
.window
)))
527 menu
->exposeEvent(&e
->xexpose
);
528 else if ((tbar
= searchToolbar(e
->xexpose
.window
)))
529 tbar
->exposeEvent(&e
->xexpose
);
535 Toolbar
*tbar
= searchToolbar(e
->xkey
.window
);
537 if (tbar
&& tbar
->isEditing())
538 tbar
->keyPressEvent(&e
->xkey
);
543 case ColormapNotify
: {
544 BScreen
*screen
= searchScreen(e
->xcolormap
.window
);
547 screen
->setRootColormapInstalled((e
->xcolormap
.state
==
548 ColormapInstalled
) ? True
: False
);
554 if (e
->xfocus
.detail
!= NotifyNonlinear
&&
555 e
->xfocus
.detail
!= NotifyAncestor
) {
557 don't process FocusIns when:
558 1. the new focus window isn't an ancestor or inferior of the old
559 focus window (NotifyNonlinear)
560 make sure to allow the FocusIn when the old focus window was an
561 ancestor but didn't have a parent, such as root (NotifyAncestor)
566 BlackboxWindow
*win
= searchWindow(e
->xfocus
.window
);
568 if (! win
->isFocused())
569 win
->setFocusFlag(True
);
572 set the event window to None. when the FocusOut event handler calls
573 this function recursively, it uses this as an indication that focus
574 has moved to a known window.
576 e
->xfocus
.window
= None
;
578 no_focus
= False
; // focusing is back on
585 if (e
->xfocus
.detail
!= NotifyNonlinear
) {
587 don't process FocusOuts when:
588 2. the new focus window isn't an ancestor or inferior of the old
589 focus window (NotifyNonlinear)
594 BlackboxWindow
*win
= searchWindow(e
->xfocus
.window
);
595 if (win
&& win
->isFocused()) {
597 before we mark "win" as unfocused, we need to verify that focus is
598 going to a known location, is in a known location, or set focus
603 // don't check the current focus if FocusOut was generated during a grab
604 bool check_focus
= (e
->xfocus
.mode
== NotifyNormal
);
607 First, check if there is a pending FocusIn event waiting. if there
608 is, process it and determine if focus has moved to another window
609 (the FocusIn event handler sets the window in the event
610 structure to None to indicate this).
612 if (XCheckTypedEvent(getXDisplay(), FocusIn
, &event
)) {
614 process_event(&event
);
615 if (event
.xfocus
.window
== None
) {
623 Second, we query the X server for the current input focus.
624 to make sure that we keep a consistent state.
626 BlackboxWindow
*focus
;
629 XGetInputFocus(getXDisplay(), &w
, &revert
);
630 focus
= searchWindow(w
);
633 focus got from "win" to "focus" under some very strange
634 circumstances, and we need to make sure that the focus indication
637 setFocusedWindow(focus
);
639 // we have no idea where focus went... so we set it to somewhere
648 case ClientMessage
: {
649 if (e
->xclient
.format
== 32) {
650 if (e
->xclient
.message_type
== xatom
->getAtom(XAtom::wm_change_state
)) {
651 // WM_CHANGE_STATE message
652 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
653 if (! win
|| ! win
->validateClient()) return;
655 if (e
->xclient
.data
.l
[0] == IconicState
)
657 if (e
->xclient
.data
.l
[0] == NormalState
)
659 } else if (e
->xclient
.message_type
==
660 xatom
->getAtom(XAtom::blackbox_change_workspace
) ||
661 e
->xclient
.message_type
==
662 xatom
->getAtom(XAtom::net_current_desktop
)) {
663 // NET_CURRENT_DESKTOP message
664 BScreen
*screen
= searchScreen(e
->xclient
.window
);
666 unsigned int workspace
= e
->xclient
.data
.l
[0];
667 if (screen
&& workspace
< screen
->getWorkspaceCount())
668 screen
->changeWorkspaceID(workspace
);
669 } else if (e
->xclient
.message_type
==
670 xatom
->getAtom(XAtom::blackbox_change_window_focus
)) {
671 // TEMP HACK TO KEEP BBKEYS WORKING
672 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
674 if (win
&& win
->isVisible() && win
->setInputFocus())
675 win
->installColormap(True
);
676 } else if (e
->xclient
.message_type
==
677 xatom
->getAtom(XAtom::net_active_window
)) {
679 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
682 BScreen
*screen
= win
->getScreen();
685 win
->deiconify(False
, False
);
686 if (! win
->isStuck() &&
687 (win
->getWorkspaceNumber() != screen
->getCurrentWorkspaceID())) {
689 screen
->changeWorkspaceID(win
->getWorkspaceNumber());
691 if (win
->isVisible() && win
->setInputFocus()) {
692 win
->getScreen()->getWorkspace(win
->getWorkspaceNumber())->
694 win
->installColormap(True
);
697 } else if (e
->xclient
.message_type
==
698 xatom
->getAtom(XAtom::blackbox_cycle_window_focus
)) {
699 // BLACKBOX_CYCLE_WINDOW_FOCUS
700 BScreen
*screen
= searchScreen(e
->xclient
.window
);
703 if (! e
->xclient
.data
.l
[0])
708 } else if (e
->xclient
.message_type
==
709 xatom
->getAtom(XAtom::net_wm_desktop
)) {
711 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
714 BScreen
*screen
= win
->getScreen();
715 unsigned long wksp
= (unsigned) e
->xclient
.data
.l
[0];
716 if (wksp
< screen
->getWorkspaceCount()) {
717 if (win
->isIconic()) win
->deiconify(False
, True
);
718 if (win
->isStuck()) win
->stick();
719 if (wksp
!= screen
->getCurrentWorkspaceID())
723 screen
->reassociateWindow(win
, wksp
, True
);
724 } else if (wksp
== 0xfffffffe || // XXX: BUG, BUT DOING THIS SO KDE WORKS FOR NOW!!
725 wksp
== 0xffffffff) {
726 if (win
->isIconic()) win
->deiconify(False
, True
);
727 if (! win
->isStuck()) win
->stick();
728 if (! win
->isVisible()) win
->show();
731 } else if (e
->xclient
.message_type
==
732 xatom
->getAtom(XAtom::blackbox_change_attributes
)) {
733 // BLACKBOX_CHANGE_ATTRIBUTES
734 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
736 if (win
&& win
->validateClient()) {
738 net
.flags
= e
->xclient
.data
.l
[0];
739 net
.attrib
= e
->xclient
.data
.l
[1];
740 net
.workspace
= e
->xclient
.data
.l
[2];
741 net
.stack
= e
->xclient
.data
.l
[3];
742 net
.decoration
= e
->xclient
.data
.l
[4];
744 win
->changeBlackboxHints(&net
);
746 } else if (e
->xclient
.message_type
==
747 xatom
->getAtom(XAtom::net_number_of_desktops
)) {
748 // NET_NUMBER_OF_DESKTOPS
749 BScreen
*screen
= searchScreen(e
->xclient
.window
);
751 if (e
->xclient
.data
.l
[0] > 0)
752 screen
->changeWorkspaceCount((unsigned) e
->xclient
.data
.l
[0]);
753 } else if (e
->xclient
.message_type
==
754 xatom
->getAtom(XAtom::net_close_window
)) {
756 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
757 if (win
&& win
->validateClient())
758 win
->close(); // could this be smarter?
759 } else if (e
->xclient
.message_type
==
760 xatom
->getAtom(XAtom::net_wm_moveresize
)) {
762 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
763 if (win
&& win
->validateClient()) {
764 int x_root
= e
->xclient
.data
.l
[0],
765 y_root
= e
->xclient
.data
.l
[1];
766 if ((Atom
) e
->xclient
.data
.l
[2] ==
767 xatom
->getAtom(XAtom::net_wm_moveresize_move
)) {
768 win
->beginMove(x_root
, y_root
);
770 if ((Atom
) e
->xclient
.data
.l
[2] ==
771 xatom
->getAtom(XAtom::net_wm_moveresize_size_topleft
))
772 win
->beginResize(x_root
, y_root
, BlackboxWindow::TopLeft
);
773 else if ((Atom
) e
->xclient
.data
.l
[2] ==
774 xatom
->getAtom(XAtom::net_wm_moveresize_size_topright
))
775 win
->beginResize(x_root
, y_root
, BlackboxWindow::TopRight
);
776 else if ((Atom
) e
->xclient
.data
.l
[2] ==
777 xatom
->getAtom(XAtom::net_wm_moveresize_size_bottomleft
))
778 win
->beginResize(x_root
, y_root
, BlackboxWindow::BottomLeft
);
779 else if ((Atom
) e
->xclient
.data
.l
[2] ==
780 xatom
->getAtom(XAtom::net_wm_moveresize_size_bottomright
))
781 win
->beginResize(x_root
, y_root
, BlackboxWindow::BottomRight
);
784 } else if (e
->xclient
.message_type
==
785 xatom
->getAtom(XAtom::net_wm_state
)) {
787 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
788 if (win
&& win
->validateClient()) {
789 const Atom action
= (Atom
) e
->xclient
.data
.l
[0];
790 const Atom state
[] = { (Atom
) e
->xclient
.data
.l
[1],
791 (Atom
) e
->xclient
.data
.l
[2] };
793 for (int i
= 0; i
< 2; ++i
) {
797 if ((Atom
) e
->xclient
.data
.l
[0] == 1) {
799 if (state
[i
] == xatom
->getAtom(XAtom::net_wm_state_modal
)) {
801 } else if (state
[i
] ==
802 xatom
->getAtom(XAtom::net_wm_state_maximized_vert
)) {
803 if (win
->isMaximizedHoriz()) {
804 win
->maximize(0); // unmaximize
805 win
->maximize(1); // full
806 } else if (! win
->isMaximized()) {
807 win
->maximize(2); // vert
809 } else if (state
[i
] ==
810 xatom
->getAtom(XAtom::net_wm_state_maximized_horz
)) {
811 if (win
->isMaximizedVert()) {
812 win
->maximize(0); // unmaximize
813 win
->maximize(1); // full
814 } else if (! win
->isMaximized()) {
815 win
->maximize(3); // horiz
817 } else if (state
[i
] ==
818 xatom
->getAtom(XAtom::net_wm_state_shaded
)) {
819 if (! win
->isShaded())
821 } else if (state
[i
] ==
822 xatom
->getAtom(XAtom::net_wm_state_skip_taskbar
)) {
823 win
->setSkipTaskbar(True
);
824 } else if (state
[i
] ==
825 xatom
->getAtom(XAtom::net_wm_state_skip_pager
)) {
826 win
->setSkipPager(True
);
827 } else if (state
[i
] ==
828 xatom
->getAtom(XAtom::net_wm_state_fullscreen
)) {
829 win
->setFullscreen(True
);
831 } else if (action
== 0) {
833 if (state
[i
] == xatom
->getAtom(XAtom::net_wm_state_modal
)) {
834 win
->setModal(False
);
835 } else if (state
[i
] ==
836 xatom
->getAtom(XAtom::net_wm_state_maximized_vert
)) {
837 if (win
->isMaximizedFull()) {
838 win
->maximize(0); // unmaximize
839 win
->maximize(3); // horiz
840 } else if (win
->isMaximizedVert()) {
841 win
->maximize(0); // unmaximize
843 } else if (state
[i
] ==
844 xatom
->getAtom(XAtom::net_wm_state_maximized_horz
)) {
845 if (win
->isMaximizedFull()) {
846 win
->maximize(0); // unmaximize
847 win
->maximize(2); // vert
848 } else if (win
->isMaximizedHoriz()) {
849 win
->maximize(0); // unmaximize
851 } else if (state
[i
] ==
852 xatom
->getAtom(XAtom::net_wm_state_shaded
)) {
855 } else if (state
[i
] ==
856 xatom
->getAtom(XAtom::net_wm_state_skip_taskbar
)) {
857 win
->setSkipTaskbar(False
);
858 } else if (state
[i
] ==
859 xatom
->getAtom(XAtom::net_wm_state_skip_pager
)) {
860 win
->setSkipPager(False
);
861 } else if (state
[i
] ==
862 xatom
->getAtom(XAtom::net_wm_state_fullscreen
)) {
863 win
->setFullscreen(False
);
865 } else if (action
== 2) {
867 if (state
[i
] == xatom
->getAtom(XAtom::net_wm_state_modal
)) {
868 win
->setModal(! win
->isModal());
869 } else if (state
[i
] ==
870 xatom
->getAtom(XAtom::net_wm_state_maximized_vert
)) {
871 if (win
->isMaximizedFull()) {
872 win
->maximize(0); // unmaximize
873 win
->maximize(3); // horiz
874 } else if (win
->isMaximizedVert()) {
875 win
->maximize(0); // unmaximize
876 } else if (win
->isMaximizedHoriz()) {
877 win
->maximize(0); // unmaximize
878 win
->maximize(1); // full
880 win
->maximize(2); // vert
882 } else if (state
[i
] ==
883 xatom
->getAtom(XAtom::net_wm_state_maximized_horz
)) {
884 if (win
->isMaximizedFull()) {
885 win
->maximize(0); // unmaximize
886 win
->maximize(2); // vert
887 } else if (win
->isMaximizedHoriz()) {
888 win
->maximize(0); // unmaximize
889 } else if (win
->isMaximizedVert()) {
890 win
->maximize(0); // unmaximize
891 win
->maximize(1); // full
893 win
->maximize(3); // horiz
895 } else if (state
[i
] ==
896 xatom
->getAtom(XAtom::net_wm_state_shaded
)) {
898 } else if (state
[i
] ==
899 xatom
->getAtom(XAtom::net_wm_state_skip_taskbar
)) {
900 win
->setSkipTaskbar(! win
->skipTaskbar());
901 } else if (state
[i
] ==
902 xatom
->getAtom(XAtom::net_wm_state_skip_pager
)) {
903 win
->setSkipPager(! win
->skipPager());
904 } else if (state
[i
] ==
905 xatom
->getAtom(XAtom::net_wm_state_fullscreen
)) {
906 win
->setFullscreen(! win
->isFullscreen());
918 case ConfigureNotify
:
920 break; // not handled, just ignore
924 if (e
->type
== getShapeEventBase()) {
925 XShapeEvent
*shape_event
= (XShapeEvent
*) e
;
926 BlackboxWindow
*win
= searchWindow(e
->xany
.window
);
929 win
->shapeEvent(shape_event
);
937 bool Blackbox::handleSignal(int sig
) {
966 bool Blackbox::validateWindow(Window window
) {
968 if (XCheckTypedWindowEvent(getXDisplay(), window
, DestroyNotify
, &event
)) {
969 XPutBackEvent(getXDisplay(), &event
);
978 BScreen
*Blackbox::searchScreen(Window window
) {
979 ScreenList::iterator it
= screenList
.begin();
981 for (; it
!= screenList
.end(); ++it
) {
983 if (s
->getRootWindow() == window
)
987 return (BScreen
*) 0;
991 BScreen
*Blackbox::searchSystrayWindow(Window window
) {
992 WindowScreenLookup::iterator it
= systraySearchList
.find(window
);
993 if (it
!= systraySearchList
.end())
1000 BlackboxWindow
*Blackbox::searchWindow(Window window
) {
1001 WindowLookup::iterator it
= windowSearchList
.find(window
);
1002 if (it
!= windowSearchList
.end())
1005 return (BlackboxWindow
*) 0;
1009 BWindowGroup
*Blackbox::searchGroup(Window window
) {
1010 GroupLookup::iterator it
= groupSearchList
.find(window
);
1011 if (it
!= groupSearchList
.end())
1014 return (BWindowGroup
*) 0;
1018 Basemenu
*Blackbox::searchMenu(Window window
) {
1019 MenuLookup::iterator it
= menuSearchList
.find(window
);
1020 if (it
!= menuSearchList
.end())
1023 return (Basemenu
*) 0;
1027 Toolbar
*Blackbox::searchToolbar(Window window
) {
1028 ToolbarLookup::iterator it
= toolbarSearchList
.find(window
);
1029 if (it
!= toolbarSearchList
.end())
1032 return (Toolbar
*) 0;
1036 Slit
*Blackbox::searchSlit(Window window
) {
1037 SlitLookup::iterator it
= slitSearchList
.find(window
);
1038 if (it
!= slitSearchList
.end())
1045 void Blackbox::saveSystrayWindowSearch(Window window
, BScreen
*screen
) {
1046 systraySearchList
.insert(WindowScreenLookupPair(window
, screen
));
1050 void Blackbox::saveWindowSearch(Window window
, BlackboxWindow
*data
) {
1051 windowSearchList
.insert(WindowLookupPair(window
, data
));
1055 void Blackbox::saveGroupSearch(Window window
, BWindowGroup
*data
) {
1056 groupSearchList
.insert(GroupLookupPair(window
, data
));
1060 void Blackbox::saveMenuSearch(Window window
, Basemenu
*data
) {
1061 menuSearchList
.insert(MenuLookupPair(window
, data
));
1065 void Blackbox::saveToolbarSearch(Window window
, Toolbar
*data
) {
1066 toolbarSearchList
.insert(ToolbarLookupPair(window
, data
));
1070 void Blackbox::saveSlitSearch(Window window
, Slit
*data
) {
1071 slitSearchList
.insert(SlitLookupPair(window
, data
));
1075 void Blackbox::removeSystrayWindowSearch(Window window
) {
1076 systraySearchList
.erase(window
);
1080 void Blackbox::removeWindowSearch(Window window
) {
1081 windowSearchList
.erase(window
);
1085 void Blackbox::removeGroupSearch(Window window
) {
1086 groupSearchList
.erase(window
);
1090 void Blackbox::removeMenuSearch(Window window
) {
1091 menuSearchList
.erase(window
);
1095 void Blackbox::removeToolbarSearch(Window window
) {
1096 toolbarSearchList
.erase(window
);
1100 void Blackbox::removeSlitSearch(Window window
) {
1101 slitSearchList
.erase(window
);
1105 void Blackbox::restart(const char *prog
) {
1109 putenv(const_cast<char *>(screenList
.front()->displayString().c_str()));
1110 execlp(prog
, prog
, NULL
);
1114 // fall back in case the above execlp doesn't work
1115 execvp(argv
[0], argv
);
1116 string name
= basename(argv
[0]);
1117 execvp(name
.c_str(), argv
);
1121 void Blackbox::shutdown(void) {
1122 BaseDisplay::shutdown();
1124 XSetInputFocus(getXDisplay(), PointerRoot
, None
, CurrentTime
);
1126 std::for_each(screenList
.begin(), screenList
.end(),
1127 std::mem_fun(&BScreen::shutdown
));
1129 XSync(getXDisplay(), False
);
1134 void Blackbox::saveXineramaPlacement(bool x
) {
1135 resource
.xinerama_placement
= x
;
1136 config
.setValue("session.xineramaSupport.windowPlacement",
1137 resource
.xinerama_placement
);
1138 reconfigure(); // make sure all screens get this change
1142 void Blackbox::saveXineramaMaximizing(bool x
) {
1143 resource
.xinerama_maximize
= x
;
1144 config
.setValue("session.xineramaSupport.windowMaximizing",
1145 resource
.xinerama_maximize
);
1146 reconfigure(); // make sure all screens get this change
1150 void Blackbox::saveXineramaSnapping(bool x
) {
1151 resource
.xinerama_snap
= x
;
1152 config
.setValue("session.xineramaSupport.windowSnapping",
1153 resource
.xinerama_snap
);
1154 reconfigure(); // make sure all screens get this change
1160 * Save all values as they are so that the defaults will be written to the rc
1163 void Blackbox::save_rc(void) {
1164 config
.setAutoSave(false);
1166 config
.setValue("session.colorsPerChannel", resource
.colors_per_channel
);
1167 config
.setValue("session.doubleClickInterval",
1168 resource
.double_click_interval
);
1169 config
.setValue("session.autoRaiseDelay",
1170 ((resource
.auto_raise_delay
.tv_sec
* 1000) +
1171 (resource
.auto_raise_delay
.tv_usec
/ 1000)));
1172 config
.setValue("session.cacheLife", resource
.cache_life
/ 60000);
1173 config
.setValue("session.cacheMax", resource
.cache_max
);
1174 config
.setValue("session.styleFile", resource
.style_file
);
1175 config
.setValue("session.titlebarLayout", resource
.titlebar_layout
);
1178 if (resource
.mod_mask
& Mod1Mask
) s
+= "Mod1-";
1179 if (resource
.mod_mask
& Mod2Mask
) s
+= "Mod2-";
1180 if (resource
.mod_mask
& Mod3Mask
) s
+= "Mod3-";
1181 if (resource
.mod_mask
& Mod4Mask
) s
+= "Mod4-";
1182 if (resource
.mod_mask
& Mod5Mask
) s
+= "Mod5-";
1183 if (resource
.mod_mask
& ShiftMask
) s
+= "Shift-";
1184 if (resource
.mod_mask
& ControlMask
) s
+= "Control-";
1185 s
.resize(s
.size() - 1); // drop the last '-'
1186 config
.setValue("session.modifierMask", s
);
1189 saveXineramaPlacement(resource
.xinerama_placement
);
1190 saveXineramaMaximizing(resource
.xinerama_maximize
);
1191 saveXineramaSnapping(resource
.xinerama_snap
);
1194 std::for_each(screenList
.begin(), screenList
.end(),
1195 std::mem_fun(&BScreen::save_rc
));
1197 config
.setAutoSave(true);
1202 void Blackbox::load_rc(void) {
1203 if (! config
.load())
1208 if (! config
.getValue("session.colorsPerChannel",
1209 resource
.colors_per_channel
))
1210 resource
.colors_per_channel
= 4;
1211 if (resource
.colors_per_channel
< 2) resource
.colors_per_channel
= 2;
1212 else if (resource
.colors_per_channel
> 6) resource
.colors_per_channel
= 6;
1214 if (config
.getValue("session.styleFile", s
))
1215 resource
.style_file
= expandTilde(s
);
1217 resource
.style_file
= DEFAULTSTYLE
;
1219 if (! config
.getValue("session.doubleClickInterval",
1220 resource
.double_click_interval
));
1221 resource
.double_click_interval
= 250;
1223 if (! config
.getValue("session.autoRaiseDelay",
1224 resource
.auto_raise_delay
.tv_usec
))
1225 resource
.auto_raise_delay
.tv_usec
= 400;
1226 resource
.auto_raise_delay
.tv_sec
= resource
.auto_raise_delay
.tv_usec
/ 1000;
1227 resource
.auto_raise_delay
.tv_usec
-=
1228 (resource
.auto_raise_delay
.tv_sec
* 1000);
1229 resource
.auto_raise_delay
.tv_usec
*= 1000;
1231 if (! config
.getValue("session.cacheLife", resource
.cache_life
))
1232 resource
.cache_life
= 5;
1233 resource
.cache_life
*= 60000;
1235 if (! config
.getValue("session.cacheMax", resource
.cache_max
))
1236 resource
.cache_max
= 200;
1238 if (! config
.getValue("session.titlebarLayout", resource
.titlebar_layout
))
1239 resource
.titlebar_layout
= "ILMC";
1242 if (! config
.getValue("session.xineramaSupport.windowPlacement",
1243 resource
.xinerama_placement
))
1244 resource
.xinerama_placement
= false;
1246 if (! config
.getValue("session.xineramaSupport.windowMaximizing",
1247 resource
.xinerama_maximize
))
1248 resource
.xinerama_maximize
= false;
1250 if (! config
.getValue("session.xineramaSupport.windowSnapping",
1251 resource
.xinerama_snap
))
1252 resource
.xinerama_snap
= false;
1255 resource
.mod_mask
= 0;
1256 if (config
.getValue("session.modifierMask", s
)) {
1257 if (s
.find("Mod1") != string::npos
)
1258 resource
.mod_mask
|= Mod1Mask
;
1259 if (s
.find("Mod2") != string::npos
)
1260 resource
.mod_mask
|= Mod2Mask
;
1261 if (s
.find("Mod3") != string::npos
)
1262 resource
.mod_mask
|= Mod3Mask
;
1263 if (s
.find("Mod4") != string::npos
)
1264 resource
.mod_mask
|= Mod4Mask
;
1265 if (s
.find("Mod5") != string::npos
)
1266 resource
.mod_mask
|= Mod5Mask
;
1267 if (s
.find("Shift") != string::npos
)
1268 resource
.mod_mask
|= ShiftMask
;
1269 if (s
.find("Control") != string::npos
)
1270 resource
.mod_mask
|= ControlMask
;
1272 if (! resource
.mod_mask
)
1273 resource
.mod_mask
= Mod1Mask
;
1277 void Blackbox::reconfigure(void) {
1278 // don't reconfigure while saving the initial rc file, it's a waste and it
1279 // breaks somethings (workspace names)
1280 if (isStartup()) return;
1282 reconfigure_wait
= True
;
1284 if (! timer
->isTiming()) timer
->start();
1288 void Blackbox::real_reconfigure(void) {
1291 std::for_each(menuTimestamps
.begin(), menuTimestamps
.end(),
1293 menuTimestamps
.clear();
1297 std::for_each(screenList
.begin(), screenList
.end(),
1298 std::mem_fun(&BScreen::reconfigure
));
1302 void Blackbox::checkMenu(void) {
1303 bool reread
= False
;
1304 MenuTimestampList::iterator it
= menuTimestamps
.begin();
1305 for(; it
!= menuTimestamps
.end(); ++it
) {
1306 MenuTimestamp
*tmp
= *it
;
1309 if (! stat(tmp
->filename
.c_str(), &buf
)) {
1310 if (tmp
->timestamp
!= buf
.st_ctime
)
1317 if (reread
) rereadMenu();
1321 void Blackbox::rereadMenu(void) {
1322 reread_menu_wait
= True
;
1324 if (! timer
->isTiming()) timer
->start();
1328 void Blackbox::real_rereadMenu(void) {
1329 std::for_each(menuTimestamps
.begin(), menuTimestamps
.end(),
1331 menuTimestamps
.clear();
1333 std::for_each(screenList
.begin(), screenList
.end(),
1334 std::mem_fun(&BScreen::rereadMenu
));
1338 void Blackbox::saveStyleFilename(const string
& filename
) {
1339 assert(! filename
.empty());
1340 resource
.style_file
= filename
;
1341 config
.setValue("session.styleFile", resource
.style_file
);
1345 void Blackbox::addMenuTimestamp(const string
& filename
) {
1346 assert(! filename
.empty());
1349 MenuTimestampList::iterator it
= menuTimestamps
.begin();
1350 for (; it
!= menuTimestamps
.end() && ! found
; ++it
) {
1351 if ((*it
)->filename
== filename
) found
= True
;
1356 if (! stat(filename
.c_str(), &buf
)) {
1357 MenuTimestamp
*ts
= new MenuTimestamp
;
1359 ts
->filename
= filename
;
1360 ts
->timestamp
= buf
.st_ctime
;
1362 menuTimestamps
.push_back(ts
);
1368 void Blackbox::timeout(void) {
1369 if (reconfigure_wait
)
1372 if (reread_menu_wait
)
1375 reconfigure_wait
= reread_menu_wait
= False
;
1379 void Blackbox::setChangingWindow(BlackboxWindow
*win
) {
1380 // make sure one of the two is null and the other isn't
1381 assert((! changing_window
&& win
) || (! win
&& changing_window
));
1382 changing_window
= win
;
1386 void Blackbox::setFocusedWindow(BlackboxWindow
*win
) {
1387 if (focused_window
&& focused_window
== win
) // nothing to do
1390 BScreen
*old_screen
= 0;
1392 if (focused_window
) {
1393 focused_window
->setFocusFlag(False
);
1394 old_screen
= focused_window
->getScreen();
1397 if (win
&& ! win
->isIconic()) {
1398 // the active screen is the one with the last focused window...
1399 // this will keep focus on this screen no matter where the mouse goes,
1400 // so multihead keybindings will continue to work on that screen until the
1401 // user focuses a window on a different screen.
1402 active_screen
= win
->getScreen();
1403 focused_window
= win
;
1407 if (active_screen
) {
1408 // set input focus to the toolbar of the screen with mouse
1409 XSetInputFocus(getXDisplay(),
1410 active_screen
->getRootWindow(),
1411 RevertToPointerRoot
, CurrentTime
);
1413 // set input focus to the toolbar of the first managed screen
1414 XSetInputFocus(getXDisplay(),
1415 screenList
.front()->getRootWindow(),
1416 RevertToPointerRoot
, CurrentTime
);
1419 // set input focus to the toolbar of the last screen
1420 XSetInputFocus(getXDisplay(), old_screen
->getRootWindow(),
1421 RevertToPointerRoot
, CurrentTime
);
1425 if (active_screen
&& active_screen
->isScreenManaged()) {
1426 active_screen
->getToolbar()->redrawWindowLabel(True
);
1427 active_screen
->updateNetizenWindowFocus();
1430 if (old_screen
&& old_screen
!= active_screen
) {
1431 old_screen
->getToolbar()->redrawWindowLabel(True
);
1432 old_screen
->updateNetizenWindowFocus();