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
;
583 if (e
->xfocus
.detail
!= NotifyNonlinear
) {
585 don't process FocusOuts when:
586 2. the new focus window isn't an ancestor or inferior of the old
587 focus window (NotifyNonlinear)
592 BlackboxWindow
*win
= searchWindow(e
->xfocus
.window
);
593 if (win
&& win
->isFocused()) {
595 before we mark "win" as unfocused, we need to verify that focus is
596 going to a known location, is in a known location, or set focus
601 // don't check the current focus if FocusOut was generated during a grab
602 bool check_focus
= (e
->xfocus
.mode
== NotifyNormal
);
605 First, check if there is a pending FocusIn event waiting. if there
606 is, process it and determine if focus has moved to another window
607 (the FocusIn event handler sets the window in the event
608 structure to None to indicate this).
610 if (XCheckTypedEvent(getXDisplay(), FocusIn
, &event
)) {
612 process_event(&event
);
613 if (event
.xfocus
.window
== None
) {
621 Second, we query the X server for the current input focus.
622 to make sure that we keep a consistent state.
624 BlackboxWindow
*focus
;
627 XGetInputFocus(getXDisplay(), &w
, &revert
);
628 focus
= searchWindow(w
);
631 focus got from "win" to "focus" under some very strange
632 circumstances, and we need to make sure that the focus indication
635 setFocusedWindow(focus
);
637 // we have no idea where focus went... so we set it to somewhere
646 case ClientMessage
: {
647 if (e
->xclient
.format
== 32) {
648 if (e
->xclient
.message_type
== xatom
->getAtom(XAtom::wm_change_state
)) {
649 // WM_CHANGE_STATE message
650 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
651 if (! win
|| ! win
->validateClient()) return;
653 if (e
->xclient
.data
.l
[0] == IconicState
)
655 if (e
->xclient
.data
.l
[0] == NormalState
)
657 } else if (e
->xclient
.message_type
==
658 xatom
->getAtom(XAtom::blackbox_change_workspace
) ||
659 e
->xclient
.message_type
==
660 xatom
->getAtom(XAtom::net_current_desktop
)) {
661 // NET_CURRENT_DESKTOP message
662 BScreen
*screen
= searchScreen(e
->xclient
.window
);
664 unsigned int workspace
= e
->xclient
.data
.l
[0];
665 if (screen
&& workspace
< screen
->getWorkspaceCount())
666 screen
->changeWorkspaceID(workspace
);
667 } else if (e
->xclient
.message_type
==
668 xatom
->getAtom(XAtom::blackbox_change_window_focus
)) {
669 // TEMP HACK TO KEEP BBKEYS WORKING
670 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
672 if (win
&& win
->isVisible() && win
->setInputFocus())
673 win
->installColormap(True
);
674 } else if (e
->xclient
.message_type
==
675 xatom
->getAtom(XAtom::net_active_window
)) {
677 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
680 BScreen
*screen
= win
->getScreen();
683 win
->deiconify(False
, False
);
684 if (! win
->isStuck() &&
685 (win
->getWorkspaceNumber() != screen
->getCurrentWorkspaceID()))
686 screen
->changeWorkspaceID(win
->getWorkspaceNumber());
687 if (win
->isVisible() && win
->setInputFocus()) {
688 win
->getScreen()->getWorkspace(win
->getWorkspaceNumber())->
690 win
->installColormap(True
);
693 } else if (e
->xclient
.message_type
==
694 xatom
->getAtom(XAtom::blackbox_cycle_window_focus
)) {
695 // BLACKBOX_CYCLE_WINDOW_FOCUS
696 BScreen
*screen
= searchScreen(e
->xclient
.window
);
699 if (! e
->xclient
.data
.l
[0])
704 } else if (e
->xclient
.message_type
==
705 xatom
->getAtom(XAtom::net_wm_desktop
)) {
707 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
710 BScreen
*screen
= win
->getScreen();
711 unsigned long wksp
= (unsigned) e
->xclient
.data
.l
[0];
712 if (wksp
< screen
->getWorkspaceCount()) {
713 if (win
->isIconic()) win
->deiconify(False
, True
);
714 if (win
->isStuck()) win
->stick();
715 if (wksp
!= screen
->getCurrentWorkspaceID())
719 screen
->reassociateWindow(win
, wksp
, True
);
720 } else if (wksp
== 0xfffffffe || // XXX: BUG, BUT DOING THIS SO KDE WORKS FOR NOW!!
721 wksp
== 0xffffffff) {
722 if (win
->isIconic()) win
->deiconify(False
, True
);
723 if (! win
->isStuck()) win
->stick();
724 if (! win
->isVisible()) win
->show();
727 } else if (e
->xclient
.message_type
==
728 xatom
->getAtom(XAtom::blackbox_change_attributes
)) {
729 // BLACKBOX_CHANGE_ATTRIBUTES
730 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
732 if (win
&& win
->validateClient()) {
734 net
.flags
= e
->xclient
.data
.l
[0];
735 net
.attrib
= e
->xclient
.data
.l
[1];
736 net
.workspace
= e
->xclient
.data
.l
[2];
737 net
.stack
= e
->xclient
.data
.l
[3];
738 net
.decoration
= e
->xclient
.data
.l
[4];
740 win
->changeBlackboxHints(&net
);
742 } else if (e
->xclient
.message_type
==
743 xatom
->getAtom(XAtom::net_number_of_desktops
)) {
744 // NET_NUMBER_OF_DESKTOPS
745 BScreen
*screen
= searchScreen(e
->xclient
.window
);
747 if (e
->xclient
.data
.l
[0] > 0)
748 screen
->changeWorkspaceCount((unsigned) e
->xclient
.data
.l
[0]);
749 } else if (e
->xclient
.message_type
==
750 xatom
->getAtom(XAtom::net_close_window
)) {
752 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
753 if (win
&& win
->validateClient())
754 win
->close(); // could this be smarter?
755 } else if (e
->xclient
.message_type
==
756 xatom
->getAtom(XAtom::net_wm_moveresize
)) {
758 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
759 if (win
&& win
->validateClient()) {
760 int x_root
= e
->xclient
.data
.l
[0],
761 y_root
= e
->xclient
.data
.l
[1];
762 if ((Atom
) e
->xclient
.data
.l
[2] ==
763 xatom
->getAtom(XAtom::net_wm_moveresize_move
)) {
764 win
->beginMove(x_root
, y_root
);
766 if ((Atom
) e
->xclient
.data
.l
[2] ==
767 xatom
->getAtom(XAtom::net_wm_moveresize_size_topleft
))
768 win
->beginResize(x_root
, y_root
, BlackboxWindow::TopLeft
);
769 else if ((Atom
) e
->xclient
.data
.l
[2] ==
770 xatom
->getAtom(XAtom::net_wm_moveresize_size_topright
))
771 win
->beginResize(x_root
, y_root
, BlackboxWindow::TopRight
);
772 else if ((Atom
) e
->xclient
.data
.l
[2] ==
773 xatom
->getAtom(XAtom::net_wm_moveresize_size_bottomleft
))
774 win
->beginResize(x_root
, y_root
, BlackboxWindow::BottomLeft
);
775 else if ((Atom
) e
->xclient
.data
.l
[2] ==
776 xatom
->getAtom(XAtom::net_wm_moveresize_size_bottomright
))
777 win
->beginResize(x_root
, y_root
, BlackboxWindow::BottomRight
);
780 } else if (e
->xclient
.message_type
==
781 xatom
->getAtom(XAtom::net_wm_state
)) {
783 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
784 if (win
&& win
->validateClient()) {
785 const Atom action
= (Atom
) e
->xclient
.data
.l
[0];
786 const Atom state
[] = { (Atom
) e
->xclient
.data
.l
[1],
787 (Atom
) e
->xclient
.data
.l
[2] };
789 for (int i
= 0; i
< 2; ++i
) {
793 if ((Atom
) e
->xclient
.data
.l
[0] == 1) {
795 if (state
[i
] == xatom
->getAtom(XAtom::net_wm_state_modal
)) {
797 } else if (state
[i
] ==
798 xatom
->getAtom(XAtom::net_wm_state_maximized_vert
)) {
799 if (win
->isMaximizedHoriz()) {
800 win
->maximize(0); // unmaximize
801 win
->maximize(1); // full
802 } else if (! win
->isMaximized()) {
803 win
->maximize(2); // vert
805 } else if (state
[i
] ==
806 xatom
->getAtom(XAtom::net_wm_state_maximized_horz
)) {
807 if (win
->isMaximizedVert()) {
808 win
->maximize(0); // unmaximize
809 win
->maximize(1); // full
810 } else if (! win
->isMaximized()) {
811 win
->maximize(3); // horiz
813 } else if (state
[i
] ==
814 xatom
->getAtom(XAtom::net_wm_state_shaded
)) {
815 if (! win
->isShaded())
817 } else if (state
[i
] ==
818 xatom
->getAtom(XAtom::net_wm_state_skip_taskbar
)) {
819 win
->setSkipTaskbar(True
);
820 } else if (state
[i
] ==
821 xatom
->getAtom(XAtom::net_wm_state_skip_pager
)) {
822 win
->setSkipPager(True
);
823 } else if (state
[i
] ==
824 xatom
->getAtom(XAtom::net_wm_state_fullscreen
)) {
825 win
->setFullscreen(True
);
827 } else if (action
== 0) {
829 if (state
[i
] == xatom
->getAtom(XAtom::net_wm_state_modal
)) {
830 win
->setModal(False
);
831 } else if (state
[i
] ==
832 xatom
->getAtom(XAtom::net_wm_state_maximized_vert
)) {
833 if (win
->isMaximizedFull()) {
834 win
->maximize(0); // unmaximize
835 win
->maximize(3); // horiz
836 } else if (win
->isMaximizedVert()) {
837 win
->maximize(0); // unmaximize
839 } else if (state
[i
] ==
840 xatom
->getAtom(XAtom::net_wm_state_maximized_horz
)) {
841 if (win
->isMaximizedFull()) {
842 win
->maximize(0); // unmaximize
843 win
->maximize(2); // vert
844 } else if (win
->isMaximizedHoriz()) {
845 win
->maximize(0); // unmaximize
847 } else if (state
[i
] ==
848 xatom
->getAtom(XAtom::net_wm_state_shaded
)) {
851 } else if (state
[i
] ==
852 xatom
->getAtom(XAtom::net_wm_state_skip_taskbar
)) {
853 win
->setSkipTaskbar(False
);
854 } else if (state
[i
] ==
855 xatom
->getAtom(XAtom::net_wm_state_skip_pager
)) {
856 win
->setSkipPager(False
);
857 } else if (state
[i
] ==
858 xatom
->getAtom(XAtom::net_wm_state_fullscreen
)) {
859 win
->setFullscreen(False
);
861 } else if (action
== 2) {
863 if (state
[i
] == xatom
->getAtom(XAtom::net_wm_state_modal
)) {
864 win
->setModal(! win
->isModal());
865 } else if (state
[i
] ==
866 xatom
->getAtom(XAtom::net_wm_state_maximized_vert
)) {
867 if (win
->isMaximizedFull()) {
868 win
->maximize(0); // unmaximize
869 win
->maximize(3); // horiz
870 } else if (win
->isMaximizedVert()) {
871 win
->maximize(0); // unmaximize
872 } else if (win
->isMaximizedHoriz()) {
873 win
->maximize(0); // unmaximize
874 win
->maximize(1); // full
876 win
->maximize(2); // vert
878 } else if (state
[i
] ==
879 xatom
->getAtom(XAtom::net_wm_state_maximized_horz
)) {
880 if (win
->isMaximizedFull()) {
881 win
->maximize(0); // unmaximize
882 win
->maximize(2); // vert
883 } else if (win
->isMaximizedHoriz()) {
884 win
->maximize(0); // unmaximize
885 } else if (win
->isMaximizedVert()) {
886 win
->maximize(0); // unmaximize
887 win
->maximize(1); // full
889 win
->maximize(3); // horiz
891 } else if (state
[i
] ==
892 xatom
->getAtom(XAtom::net_wm_state_shaded
)) {
894 } else if (state
[i
] ==
895 xatom
->getAtom(XAtom::net_wm_state_skip_taskbar
)) {
896 win
->setSkipTaskbar(! win
->skipTaskbar());
897 } else if (state
[i
] ==
898 xatom
->getAtom(XAtom::net_wm_state_skip_pager
)) {
899 win
->setSkipPager(! win
->skipPager());
900 } else if (state
[i
] ==
901 xatom
->getAtom(XAtom::net_wm_state_fullscreen
)) {
902 win
->setFullscreen(! win
->isFullscreen());
914 case ConfigureNotify
:
916 break; // not handled, just ignore
920 if (e
->type
== getShapeEventBase()) {
921 XShapeEvent
*shape_event
= (XShapeEvent
*) e
;
922 BlackboxWindow
*win
= searchWindow(e
->xany
.window
);
925 win
->shapeEvent(shape_event
);
933 bool Blackbox::handleSignal(int sig
) {
962 bool Blackbox::validateWindow(Window window
) {
964 if (XCheckTypedWindowEvent(getXDisplay(), window
, DestroyNotify
, &event
)) {
965 XPutBackEvent(getXDisplay(), &event
);
974 BScreen
*Blackbox::searchScreen(Window window
) {
975 ScreenList::iterator it
= screenList
.begin();
977 for (; it
!= screenList
.end(); ++it
) {
979 if (s
->getRootWindow() == window
)
983 return (BScreen
*) 0;
987 BScreen
*Blackbox::searchSystrayWindow(Window window
) {
988 WindowScreenLookup::iterator it
= systraySearchList
.find(window
);
989 if (it
!= systraySearchList
.end())
996 BlackboxWindow
*Blackbox::searchWindow(Window window
) {
997 WindowLookup::iterator it
= windowSearchList
.find(window
);
998 if (it
!= windowSearchList
.end())
1001 return (BlackboxWindow
*) 0;
1005 BWindowGroup
*Blackbox::searchGroup(Window window
) {
1006 GroupLookup::iterator it
= groupSearchList
.find(window
);
1007 if (it
!= groupSearchList
.end())
1010 return (BWindowGroup
*) 0;
1014 Basemenu
*Blackbox::searchMenu(Window window
) {
1015 MenuLookup::iterator it
= menuSearchList
.find(window
);
1016 if (it
!= menuSearchList
.end())
1019 return (Basemenu
*) 0;
1023 Toolbar
*Blackbox::searchToolbar(Window window
) {
1024 ToolbarLookup::iterator it
= toolbarSearchList
.find(window
);
1025 if (it
!= toolbarSearchList
.end())
1028 return (Toolbar
*) 0;
1032 Slit
*Blackbox::searchSlit(Window window
) {
1033 SlitLookup::iterator it
= slitSearchList
.find(window
);
1034 if (it
!= slitSearchList
.end())
1041 void Blackbox::saveSystrayWindowSearch(Window window
, BScreen
*screen
) {
1042 systraySearchList
.insert(WindowScreenLookupPair(window
, screen
));
1046 void Blackbox::saveWindowSearch(Window window
, BlackboxWindow
*data
) {
1047 windowSearchList
.insert(WindowLookupPair(window
, data
));
1051 void Blackbox::saveGroupSearch(Window window
, BWindowGroup
*data
) {
1052 groupSearchList
.insert(GroupLookupPair(window
, data
));
1056 void Blackbox::saveMenuSearch(Window window
, Basemenu
*data
) {
1057 menuSearchList
.insert(MenuLookupPair(window
, data
));
1061 void Blackbox::saveToolbarSearch(Window window
, Toolbar
*data
) {
1062 toolbarSearchList
.insert(ToolbarLookupPair(window
, data
));
1066 void Blackbox::saveSlitSearch(Window window
, Slit
*data
) {
1067 slitSearchList
.insert(SlitLookupPair(window
, data
));
1071 void Blackbox::removeSystrayWindowSearch(Window window
) {
1072 systraySearchList
.erase(window
);
1076 void Blackbox::removeWindowSearch(Window window
) {
1077 windowSearchList
.erase(window
);
1081 void Blackbox::removeGroupSearch(Window window
) {
1082 groupSearchList
.erase(window
);
1086 void Blackbox::removeMenuSearch(Window window
) {
1087 menuSearchList
.erase(window
);
1091 void Blackbox::removeToolbarSearch(Window window
) {
1092 toolbarSearchList
.erase(window
);
1096 void Blackbox::removeSlitSearch(Window window
) {
1097 slitSearchList
.erase(window
);
1101 void Blackbox::restart(const char *prog
) {
1105 putenv(const_cast<char *>(screenList
.front()->displayString().c_str()));
1106 execlp(prog
, prog
, NULL
);
1110 // fall back in case the above execlp doesn't work
1111 execvp(argv
[0], argv
);
1112 string name
= basename(argv
[0]);
1113 execvp(name
.c_str(), argv
);
1117 void Blackbox::shutdown(void) {
1118 BaseDisplay::shutdown();
1120 XSetInputFocus(getXDisplay(), PointerRoot
, None
, CurrentTime
);
1122 std::for_each(screenList
.begin(), screenList
.end(),
1123 std::mem_fun(&BScreen::shutdown
));
1125 XSync(getXDisplay(), False
);
1130 void Blackbox::saveXineramaPlacement(bool x
) {
1131 resource
.xinerama_placement
= x
;
1132 config
.setValue("session.xineramaSupport.windowPlacement",
1133 resource
.xinerama_placement
);
1134 reconfigure(); // make sure all screens get this change
1138 void Blackbox::saveXineramaMaximizing(bool x
) {
1139 resource
.xinerama_maximize
= x
;
1140 config
.setValue("session.xineramaSupport.windowMaximizing",
1141 resource
.xinerama_maximize
);
1142 reconfigure(); // make sure all screens get this change
1146 void Blackbox::saveXineramaSnapping(bool x
) {
1147 resource
.xinerama_snap
= x
;
1148 config
.setValue("session.xineramaSupport.windowSnapping",
1149 resource
.xinerama_snap
);
1150 reconfigure(); // make sure all screens get this change
1156 * Save all values as they are so that the defaults will be written to the rc
1159 void Blackbox::save_rc(void) {
1160 config
.setAutoSave(false);
1162 config
.setValue("session.colorsPerChannel", resource
.colors_per_channel
);
1163 config
.setValue("session.doubleClickInterval",
1164 resource
.double_click_interval
);
1165 config
.setValue("session.autoRaiseDelay",
1166 ((resource
.auto_raise_delay
.tv_sec
* 1000) +
1167 (resource
.auto_raise_delay
.tv_usec
/ 1000)));
1168 config
.setValue("session.cacheLife", resource
.cache_life
/ 60000);
1169 config
.setValue("session.cacheMax", resource
.cache_max
);
1170 config
.setValue("session.styleFile", resource
.style_file
);
1171 config
.setValue("session.titlebarLayout", resource
.titlebar_layout
);
1174 saveXineramaPlacement(resource
.xinerama_placement
);
1175 saveXineramaMaximizing(resource
.xinerama_maximize
);
1176 saveXineramaSnapping(resource
.xinerama_snap
);
1179 std::for_each(screenList
.begin(), screenList
.end(),
1180 std::mem_fun(&BScreen::save_rc
));
1182 config
.setAutoSave(true);
1187 void Blackbox::load_rc(void) {
1188 if (! config
.load())
1193 if (! config
.getValue("session.colorsPerChannel",
1194 resource
.colors_per_channel
))
1195 resource
.colors_per_channel
= 4;
1196 if (resource
.colors_per_channel
< 2) resource
.colors_per_channel
= 2;
1197 else if (resource
.colors_per_channel
> 6) resource
.colors_per_channel
= 6;
1199 if (config
.getValue("session.styleFile", s
))
1200 resource
.style_file
= expandTilde(s
);
1202 resource
.style_file
= DEFAULTSTYLE
;
1204 if (! config
.getValue("session.doubleClickInterval",
1205 resource
.double_click_interval
));
1206 resource
.double_click_interval
= 250;
1208 if (! config
.getValue("session.autoRaiseDelay",
1209 resource
.auto_raise_delay
.tv_usec
))
1210 resource
.auto_raise_delay
.tv_usec
= 400;
1211 resource
.auto_raise_delay
.tv_sec
= resource
.auto_raise_delay
.tv_usec
/ 1000;
1212 resource
.auto_raise_delay
.tv_usec
-=
1213 (resource
.auto_raise_delay
.tv_sec
* 1000);
1214 resource
.auto_raise_delay
.tv_usec
*= 1000;
1216 if (! config
.getValue("session.cacheLife", resource
.cache_life
))
1217 resource
.cache_life
= 5;
1218 resource
.cache_life
*= 60000;
1220 if (! config
.getValue("session.cacheMax", resource
.cache_max
))
1221 resource
.cache_max
= 200;
1223 if (! config
.getValue("session.titlebarLayout", resource
.titlebar_layout
))
1224 resource
.titlebar_layout
= "ILMC";
1227 if (! config
.getValue("session.xineramaSupport.windowPlacement",
1228 resource
.xinerama_placement
))
1229 resource
.xinerama_placement
= false;
1231 if (! config
.getValue("session.xineramaSupport.windowMaximizing",
1232 resource
.xinerama_maximize
))
1233 resource
.xinerama_maximize
= false;
1235 if (! config
.getValue("session.xineramaSupport.windowSnapping",
1236 resource
.xinerama_snap
))
1237 resource
.xinerama_snap
= false;
1242 void Blackbox::reconfigure(void) {
1243 // don't reconfigure while saving the initial rc file, it's a waste and it
1244 // breaks somethings (workspace names)
1245 if (isStartup()) return;
1247 reconfigure_wait
= True
;
1249 if (! timer
->isTiming()) timer
->start();
1253 void Blackbox::real_reconfigure(void) {
1256 std::for_each(menuTimestamps
.begin(), menuTimestamps
.end(),
1258 menuTimestamps
.clear();
1262 std::for_each(screenList
.begin(), screenList
.end(),
1263 std::mem_fun(&BScreen::reconfigure
));
1267 void Blackbox::checkMenu(void) {
1268 bool reread
= False
;
1269 MenuTimestampList::iterator it
= menuTimestamps
.begin();
1270 for(; it
!= menuTimestamps
.end(); ++it
) {
1271 MenuTimestamp
*tmp
= *it
;
1274 if (! stat(tmp
->filename
.c_str(), &buf
)) {
1275 if (tmp
->timestamp
!= buf
.st_ctime
)
1282 if (reread
) rereadMenu();
1286 void Blackbox::rereadMenu(void) {
1287 reread_menu_wait
= True
;
1289 if (! timer
->isTiming()) timer
->start();
1293 void Blackbox::real_rereadMenu(void) {
1294 std::for_each(menuTimestamps
.begin(), menuTimestamps
.end(),
1296 menuTimestamps
.clear();
1298 std::for_each(screenList
.begin(), screenList
.end(),
1299 std::mem_fun(&BScreen::rereadMenu
));
1303 void Blackbox::saveStyleFilename(const string
& filename
) {
1304 assert(! filename
.empty());
1305 resource
.style_file
= filename
;
1306 config
.setValue("session.styleFile", resource
.style_file
);
1310 void Blackbox::addMenuTimestamp(const string
& filename
) {
1311 assert(! filename
.empty());
1314 MenuTimestampList::iterator it
= menuTimestamps
.begin();
1315 for (; it
!= menuTimestamps
.end() && ! found
; ++it
) {
1316 if ((*it
)->filename
== filename
) found
= True
;
1321 if (! stat(filename
.c_str(), &buf
)) {
1322 MenuTimestamp
*ts
= new MenuTimestamp
;
1324 ts
->filename
= filename
;
1325 ts
->timestamp
= buf
.st_ctime
;
1327 menuTimestamps
.push_back(ts
);
1333 void Blackbox::timeout(void) {
1334 if (reconfigure_wait
)
1337 if (reread_menu_wait
)
1340 reconfigure_wait
= reread_menu_wait
= False
;
1344 void Blackbox::setChangingWindow(BlackboxWindow
*win
) {
1345 // make sure one of the two is null and the other isn't
1346 assert((! changing_window
&& win
) || (! win
&& changing_window
));
1347 changing_window
= win
;
1351 void Blackbox::setFocusedWindow(BlackboxWindow
*win
) {
1352 if (focused_window
&& focused_window
== win
) // nothing to do
1355 BScreen
*old_screen
= 0;
1357 if (focused_window
) {
1358 focused_window
->setFocusFlag(False
);
1359 old_screen
= focused_window
->getScreen();
1362 if (win
&& ! win
->isIconic()) {
1363 // the active screen is the one with the last focused window...
1364 // this will keep focus on this screen no matter where the mouse goes,
1365 // so multihead keybindings will continue to work on that screen until the
1366 // user focuses a window on a different screen.
1367 active_screen
= win
->getScreen();
1368 focused_window
= win
;
1372 if (active_screen
) {
1373 // set input focus to the toolbar of the screen with mouse
1374 XSetInputFocus(getXDisplay(),
1375 active_screen
->getRootWindow(),
1376 RevertToPointerRoot
, CurrentTime
);
1378 // set input focus to the toolbar of the first managed screen
1379 XSetInputFocus(getXDisplay(),
1380 screenList
.front()->getRootWindow(),
1381 RevertToPointerRoot
, CurrentTime
);
1384 // set input focus to the toolbar of the last screen
1385 XSetInputFocus(getXDisplay(), old_screen
->getRootWindow(),
1386 RevertToPointerRoot
, CurrentTime
);
1390 if (active_screen
&& active_screen
->isScreenManaged()) {
1391 active_screen
->getToolbar()->redrawWindowLabel(True
);
1392 active_screen
->updateNetizenWindowFocus();
1395 if (old_screen
&& old_screen
!= active_screen
) {
1396 old_screen
->getToolbar()->redrawWindowLabel(True
);
1397 old_screen
->updateNetizenWindowFocus();