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 if (resource
.mod_mask
& Mod1Mask
) s
+= "Mod1-";
1175 if (resource
.mod_mask
& Mod2Mask
) s
+= "Mod2-";
1176 if (resource
.mod_mask
& Mod3Mask
) s
+= "Mod3-";
1177 if (resource
.mod_mask
& Mod4Mask
) s
+= "Mod4-";
1178 if (resource
.mod_mask
& Mod5Mask
) s
+= "Mod5-";
1179 if (resource
.mod_mask
& ShiftMask
) s
+= "Shift-";
1180 if (resource
.mod_mask
& ControlMask
) s
+= "Control-";
1181 s
.resize(s
.size() - 1); // drop the last '-'
1182 config
.setValue("session.modifierMask", s
);
1185 saveXineramaPlacement(resource
.xinerama_placement
);
1186 saveXineramaMaximizing(resource
.xinerama_maximize
);
1187 saveXineramaSnapping(resource
.xinerama_snap
);
1190 std::for_each(screenList
.begin(), screenList
.end(),
1191 std::mem_fun(&BScreen::save_rc
));
1193 config
.setAutoSave(true);
1198 void Blackbox::load_rc(void) {
1199 if (! config
.load())
1204 if (! config
.getValue("session.colorsPerChannel",
1205 resource
.colors_per_channel
))
1206 resource
.colors_per_channel
= 4;
1207 if (resource
.colors_per_channel
< 2) resource
.colors_per_channel
= 2;
1208 else if (resource
.colors_per_channel
> 6) resource
.colors_per_channel
= 6;
1210 if (config
.getValue("session.styleFile", s
))
1211 resource
.style_file
= expandTilde(s
);
1213 resource
.style_file
= DEFAULTSTYLE
;
1215 if (! config
.getValue("session.doubleClickInterval",
1216 resource
.double_click_interval
));
1217 resource
.double_click_interval
= 250;
1219 if (! config
.getValue("session.autoRaiseDelay",
1220 resource
.auto_raise_delay
.tv_usec
))
1221 resource
.auto_raise_delay
.tv_usec
= 400;
1222 resource
.auto_raise_delay
.tv_sec
= resource
.auto_raise_delay
.tv_usec
/ 1000;
1223 resource
.auto_raise_delay
.tv_usec
-=
1224 (resource
.auto_raise_delay
.tv_sec
* 1000);
1225 resource
.auto_raise_delay
.tv_usec
*= 1000;
1227 if (! config
.getValue("session.cacheLife", resource
.cache_life
))
1228 resource
.cache_life
= 5;
1229 resource
.cache_life
*= 60000;
1231 if (! config
.getValue("session.cacheMax", resource
.cache_max
))
1232 resource
.cache_max
= 200;
1234 if (! config
.getValue("session.titlebarLayout", resource
.titlebar_layout
))
1235 resource
.titlebar_layout
= "ILMC";
1238 if (! config
.getValue("session.xineramaSupport.windowPlacement",
1239 resource
.xinerama_placement
))
1240 resource
.xinerama_placement
= false;
1242 if (! config
.getValue("session.xineramaSupport.windowMaximizing",
1243 resource
.xinerama_maximize
))
1244 resource
.xinerama_maximize
= false;
1246 if (! config
.getValue("session.xineramaSupport.windowSnapping",
1247 resource
.xinerama_snap
))
1248 resource
.xinerama_snap
= false;
1251 resource
.mod_mask
= 0;
1252 if (config
.getValue("session.modifierMask", s
)) {
1253 if (s
.find("Mod1") != string::npos
)
1254 resource
.mod_mask
|= Mod1Mask
;
1255 if (s
.find("Mod2") != string::npos
)
1256 resource
.mod_mask
|= Mod2Mask
;
1257 if (s
.find("Mod3") != string::npos
)
1258 resource
.mod_mask
|= Mod3Mask
;
1259 if (s
.find("Mod4") != string::npos
)
1260 resource
.mod_mask
|= Mod4Mask
;
1261 if (s
.find("Mod5") != string::npos
)
1262 resource
.mod_mask
|= Mod5Mask
;
1263 if (s
.find("Shift") != string::npos
)
1264 resource
.mod_mask
|= ShiftMask
;
1265 if (s
.find("Control") != string::npos
)
1266 resource
.mod_mask
|= ControlMask
;
1268 if (! resource
.mod_mask
)
1269 resource
.mod_mask
= Mod1Mask
;
1273 void Blackbox::reconfigure(void) {
1274 // don't reconfigure while saving the initial rc file, it's a waste and it
1275 // breaks somethings (workspace names)
1276 if (isStartup()) return;
1278 reconfigure_wait
= True
;
1280 if (! timer
->isTiming()) timer
->start();
1284 void Blackbox::real_reconfigure(void) {
1287 std::for_each(menuTimestamps
.begin(), menuTimestamps
.end(),
1289 menuTimestamps
.clear();
1293 std::for_each(screenList
.begin(), screenList
.end(),
1294 std::mem_fun(&BScreen::reconfigure
));
1298 void Blackbox::checkMenu(void) {
1299 bool reread
= False
;
1300 MenuTimestampList::iterator it
= menuTimestamps
.begin();
1301 for(; it
!= menuTimestamps
.end(); ++it
) {
1302 MenuTimestamp
*tmp
= *it
;
1305 if (! stat(tmp
->filename
.c_str(), &buf
)) {
1306 if (tmp
->timestamp
!= buf
.st_ctime
)
1313 if (reread
) rereadMenu();
1317 void Blackbox::rereadMenu(void) {
1318 reread_menu_wait
= True
;
1320 if (! timer
->isTiming()) timer
->start();
1324 void Blackbox::real_rereadMenu(void) {
1325 std::for_each(menuTimestamps
.begin(), menuTimestamps
.end(),
1327 menuTimestamps
.clear();
1329 std::for_each(screenList
.begin(), screenList
.end(),
1330 std::mem_fun(&BScreen::rereadMenu
));
1334 void Blackbox::saveStyleFilename(const string
& filename
) {
1335 assert(! filename
.empty());
1336 resource
.style_file
= filename
;
1337 config
.setValue("session.styleFile", resource
.style_file
);
1341 void Blackbox::addMenuTimestamp(const string
& filename
) {
1342 assert(! filename
.empty());
1345 MenuTimestampList::iterator it
= menuTimestamps
.begin();
1346 for (; it
!= menuTimestamps
.end() && ! found
; ++it
) {
1347 if ((*it
)->filename
== filename
) found
= True
;
1352 if (! stat(filename
.c_str(), &buf
)) {
1353 MenuTimestamp
*ts
= new MenuTimestamp
;
1355 ts
->filename
= filename
;
1356 ts
->timestamp
= buf
.st_ctime
;
1358 menuTimestamps
.push_back(ts
);
1364 void Blackbox::timeout(void) {
1365 if (reconfigure_wait
)
1368 if (reread_menu_wait
)
1371 reconfigure_wait
= reread_menu_wait
= False
;
1375 void Blackbox::setChangingWindow(BlackboxWindow
*win
) {
1376 // make sure one of the two is null and the other isn't
1377 assert((! changing_window
&& win
) || (! win
&& changing_window
));
1378 changing_window
= win
;
1382 void Blackbox::setFocusedWindow(BlackboxWindow
*win
) {
1383 if (focused_window
&& focused_window
== win
) // nothing to do
1386 BScreen
*old_screen
= 0;
1388 if (focused_window
) {
1389 focused_window
->setFocusFlag(False
);
1390 old_screen
= focused_window
->getScreen();
1393 if (win
&& ! win
->isIconic()) {
1394 // the active screen is the one with the last focused window...
1395 // this will keep focus on this screen no matter where the mouse goes,
1396 // so multihead keybindings will continue to work on that screen until the
1397 // user focuses a window on a different screen.
1398 active_screen
= win
->getScreen();
1399 focused_window
= win
;
1403 if (active_screen
) {
1404 // set input focus to the toolbar of the screen with mouse
1405 XSetInputFocus(getXDisplay(),
1406 active_screen
->getRootWindow(),
1407 RevertToPointerRoot
, CurrentTime
);
1409 // set input focus to the toolbar of the first managed screen
1410 XSetInputFocus(getXDisplay(),
1411 screenList
.front()->getRootWindow(),
1412 RevertToPointerRoot
, CurrentTime
);
1415 // set input focus to the toolbar of the last screen
1416 XSetInputFocus(getXDisplay(), old_screen
->getRootWindow(),
1417 RevertToPointerRoot
, CurrentTime
);
1421 if (active_screen
&& active_screen
->isScreenManaged()) {
1422 active_screen
->getToolbar()->redrawWindowLabel(True
);
1423 active_screen
->updateNetizenWindowFocus();
1426 if (old_screen
&& old_screen
!= active_screen
) {
1427 old_screen
->getToolbar()->redrawWindowLabel(True
);
1428 old_screen
->updateNetizenWindowFocus();