1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // blackbox.cc for Blackbox - an X11 Window manager
3 // Copyright (c) 2001 - 2002 Sean 'Shaleh' Perry <shaleh@debian.org>
4 // Copyright (c) 1997 - 2000 Brad Hughes (bhughes@tcac.net)
6 // Permission is hereby granted, free of charge, to any person obtaining a
7 // copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the
11 // Software is furnished to do so, subject to the following conditions:
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 // DEALINGS IN THE SOFTWARE.
25 # include "../config.h"
26 #endif // HAVE_CONFIG_H
30 #include <X11/Xutil.h>
31 #include <X11/Xatom.h>
32 #include <X11/cursorfont.h>
33 #include <X11/keysym.h>
36 #include <X11/extensions/shape.h>
41 #endif // HAVE_STDIO_H
45 #endif // HAVE_STDLIB_H
49 #endif // HAVE_STRING_H
52 # include <sys/types.h>
54 #endif // HAVE_UNISTD_H
56 #ifdef HAVE_SYS_PARAM_H
57 # include <sys/param.h>
58 #endif // HAVE_SYS_PARAM_H
60 #ifdef HAVE_SYS_SELECT_H
61 # include <sys/select.h>
62 #endif // HAVE_SYS_SELECT_H
66 #endif // HAVE_SIGNAL_H
68 #ifdef HAVE_SYS_SIGNAL_H
69 # include <sys/signal.h>
70 #endif // HAVE_SYS_SIGNAL_H
72 #ifdef HAVE_SYS_STAT_H
73 # include <sys/types.h>
74 # include <sys/stat.h>
75 #endif // HAVE_SYS_STAT_H
77 #ifdef TIME_WITH_SYS_TIME
78 # include <sys/time.h>
80 #else // !TIME_WITH_SYS_TIME
81 # ifdef HAVE_SYS_TIME_H
82 # include <sys/time.h>
83 # else // !HAVE_SYS_TIME_H
85 # endif // HAVE_SYS_TIME_H
86 #endif // TIME_WITH_SYS_TIME
90 #endif // HAVE_LIBGEN_H
100 #include "blackbox.hh"
101 #include "Basemenu.hh"
102 #include "Clientmenu.hh"
103 #include "GCCache.hh"
105 #include "Rootmenu.hh"
108 #include "Toolbar.hh"
111 #include "Workspace.hh"
112 #include "Workspacemenu.hh"
115 // X event scanner for enter/leave notifies - adapted from twm
118 bool leave
, inferior
, enter
;
121 static Bool
queueScanner(Display
*, XEvent
*e
, char *args
) {
122 scanargs
*scan
= (scanargs
*) args
;
123 if ((e
->type
== LeaveNotify
) &&
124 (e
->xcrossing
.window
== scan
->w
) &&
125 (e
->xcrossing
.mode
== NotifyNormal
)) {
127 scan
->inferior
= (e
->xcrossing
.detail
== NotifyInferior
);
128 } else if ((e
->type
== EnterNotify
) && (e
->xcrossing
.mode
== NotifyUngrab
)) {
138 Blackbox::Blackbox(char **m_argv
, char *dpy_name
, char *rc
, char *menu
)
139 : BaseDisplay(m_argv
[0], dpy_name
) {
140 if (! XSupportsLocale())
141 fprintf(stderr
, "X server does not support locale\n");
143 if (XSetLocaleModifiers("") == NULL
)
144 fprintf(stderr
, "cannot set locale modifiers\n");
149 // try to make sure the ~/.openbox directory exists
150 mkdir(expandTilde("~/.openbox").c_str(), S_IREAD
| S_IWRITE
| S_IEXEC
|
151 S_IRGRP
| S_IWGRP
| S_IXGRP
|
152 S_IROTH
| S_IWOTH
| S_IXOTH
);
154 if (! rc
) rc
= "~/.openbox/rc";
155 rc_file
= expandTilde(rc
);
156 config
.setFile(rc_file
);
157 if (! menu
) menu
= "~/.openbox/menu";
158 menu_file
= expandTilde(menu
);
162 resource
.auto_raise_delay
.tv_sec
= resource
.auto_raise_delay
.tv_usec
= 0;
165 focused_window
= changing_window
= (BlackboxWindow
*) 0;
169 xatom
= new XAtom(getXDisplay());
171 cursor
.session
= XCreateFontCursor(getXDisplay(), XC_left_ptr
);
172 cursor
.move
= XCreateFontCursor(getXDisplay(), XC_fleur
);
173 cursor
.ll_angle
= XCreateFontCursor(getXDisplay(), XC_ll_angle
);
174 cursor
.lr_angle
= XCreateFontCursor(getXDisplay(), XC_lr_angle
);
175 cursor
.ul_angle
= XCreateFontCursor(getXDisplay(), XC_ul_angle
);
176 cursor
.ur_angle
= XCreateFontCursor(getXDisplay(), XC_ur_angle
);
178 for (unsigned int i
= 0; i
< getNumberOfScreens(); i
++) {
179 BScreen
*screen
= new BScreen(this, i
);
181 if (! screen
->isScreenManaged()) {
186 screenList
.push_back(screen
);
189 if (screenList
.empty()) {
191 i18n(blackboxSet
, blackboxNoManagableScreens
,
192 "Blackbox::Blackbox: no managable screens found, aborting.\n"));
196 // save current settings and default values
199 // set the screen with mouse to the first managed screen
200 active_screen
= screenList
.front();
203 XSynchronize(getXDisplay(), False
);
204 XSync(getXDisplay(), False
);
206 reconfigure_wait
= reread_menu_wait
= False
;
208 timer
= new BTimer(this, this);
209 timer
->setTimeout(0l);
213 Blackbox::~Blackbox(void) {
214 std::for_each(screenList
.begin(), screenList
.end(), PointerAssassin());
216 std::for_each(menuTimestamps
.begin(), menuTimestamps
.end(),
225 void Blackbox::process_event(XEvent
*e
) {
228 // strip the lock key modifiers
229 e
->xbutton
.state
&= ~(NumLockMask
| ScrollLockMask
| LockMask
);
231 last_time
= e
->xbutton
.time
;
233 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
234 Basemenu
*menu
= (Basemenu
*) 0;
235 Slit
*slit
= (Slit
*) 0;
236 Toolbar
*tbar
= (Toolbar
*) 0;
237 BScreen
*scrn
= (BScreen
*) 0;
239 if ((win
= searchWindow(e
->xbutton
.window
))) {
240 win
->buttonPressEvent(&e
->xbutton
);
242 /* XXX: is this sane on low colour desktops? */
243 if (e
->xbutton
.button
== 1)
244 win
->installColormap(True
);
245 } else if ((menu
= searchMenu(e
->xbutton
.window
))) {
246 menu
->buttonPressEvent(&e
->xbutton
);
247 } else if ((slit
= searchSlit(e
->xbutton
.window
))) {
248 slit
->buttonPressEvent(&e
->xbutton
);
249 } else if ((tbar
= searchToolbar(e
->xbutton
.window
))) {
250 tbar
->buttonPressEvent(&e
->xbutton
);
251 } else if ((scrn
= searchScreen(e
->xbutton
.window
))) {
252 scrn
->buttonPressEvent(&e
->xbutton
);
253 if (active_screen
!= scrn
) {
254 active_screen
= scrn
;
255 // first, set no focus window on the old screen
257 // and move focus to this screen
264 case ButtonRelease
: {
265 // strip the lock key modifiers
266 e
->xbutton
.state
&= ~(NumLockMask
| ScrollLockMask
| LockMask
);
268 last_time
= e
->xbutton
.time
;
270 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
271 Basemenu
*menu
= (Basemenu
*) 0;
272 Toolbar
*tbar
= (Toolbar
*) 0;
274 if ((win
= searchWindow(e
->xbutton
.window
)))
275 win
->buttonReleaseEvent(&e
->xbutton
);
276 else if ((menu
= searchMenu(e
->xbutton
.window
)))
277 menu
->buttonReleaseEvent(&e
->xbutton
);
278 else if ((tbar
= searchToolbar(e
->xbutton
.window
)))
279 tbar
->buttonReleaseEvent(&e
->xbutton
);
284 case ConfigureRequest
: {
285 // compress configure requests...
288 while(XCheckTypedWindowEvent(getXDisplay(), e
->xconfigurerequest
.window
,
289 ConfigureRequest
, &realevent
)) {
295 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
296 Slit
*slit
= (Slit
*) 0;
298 if ((win
= searchWindow(e
->xconfigurerequest
.window
))) {
299 win
->configureRequestEvent(&e
->xconfigurerequest
);
300 } else if ((slit
= searchSlit(e
->xconfigurerequest
.window
))) {
301 slit
->configureRequestEvent(&e
->xconfigurerequest
);
303 if (validateWindow(e
->xconfigurerequest
.window
)) {
306 xwc
.x
= e
->xconfigurerequest
.x
;
307 xwc
.y
= e
->xconfigurerequest
.y
;
308 xwc
.width
= e
->xconfigurerequest
.width
;
309 xwc
.height
= e
->xconfigurerequest
.height
;
310 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
311 xwc
.sibling
= e
->xconfigurerequest
.above
;
312 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
314 XConfigureWindow(getXDisplay(), e
->xconfigurerequest
.window
,
315 e
->xconfigurerequest
.value_mask
, &xwc
);
324 fprintf(stderr
, "Blackbox::process_event(): MapRequest for 0x%lx\n",
325 e
->xmaprequest
.window
);
328 BlackboxWindow
*win
= searchWindow(e
->xmaprequest
.window
);
332 if (win
->isIconic()) {
336 if (win
->isShaded()) {
341 if (focus
&& (win
->isTransient() || win
->getScreen()->doFocusNew()))
342 win
->setInputFocus();
344 BScreen
*screen
= searchScreen(e
->xmaprequest
.parent
);
348 we got a map request for a window who's parent isn't root. this
349 can happen in only one circumstance:
351 a client window unmapped a managed window, and then remapped it
352 somewhere between unmapping the client window and reparenting it
355 regardless of how it happens, we need to find the screen that
358 XWindowAttributes wattrib
;
359 if (! XGetWindowAttributes(getXDisplay(), e
->xmaprequest
.window
,
361 // failed to get the window attributes, perhaps the window has
362 // now been destroyed?
366 screen
= searchScreen(wattrib
.root
);
367 assert(screen
!= 0); // this should never happen
370 screen
->manageWindow(e
->xmaprequest
.window
);
377 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
378 Slit
*slit
= (Slit
*) 0;
379 BScreen
*screen
= (BScreen
*) 0;
381 if ((win
= searchWindow(e
->xunmap
.window
))) {
382 win
->unmapNotifyEvent(&e
->xunmap
);
383 } else if ((slit
= searchSlit(e
->xunmap
.window
))) {
384 slit
->unmapNotifyEvent(&e
->xunmap
);
385 } else if ((screen
= searchSystrayWindow(e
->xunmap
.window
))) {
386 screen
->removeSystrayWindow(e
->xunmap
.window
);
387 } else if ((screen
= searchDesktopWindow(e
->xunmap
.window
))) {
388 screen
->removeDesktopWindow(e
->xunmap
.window
);
394 case DestroyNotify
: {
395 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
396 Slit
*slit
= (Slit
*) 0;
397 BScreen
*screen
= (BScreen
*) 0;
398 BWindowGroup
*group
= (BWindowGroup
*) 0;
400 if ((win
= searchWindow(e
->xdestroywindow
.window
))) {
401 win
->destroyNotifyEvent(&e
->xdestroywindow
);
402 } else if ((slit
= searchSlit(e
->xdestroywindow
.window
))) {
403 slit
->removeClient(e
->xdestroywindow
.window
, False
);
404 } else if ((group
= searchGroup(e
->xdestroywindow
.window
))) {
406 } else if ((screen
= searchSystrayWindow(e
->xunmap
.window
))) {
407 screen
->removeSystrayWindow(e
->xunmap
.window
);
408 } else if ((screen
= searchDesktopWindow(e
->xunmap
.window
))) {
409 screen
->removeDesktopWindow(e
->xunmap
.window
);
415 case ReparentNotify
: {
417 this event is quite rare and is usually handled in unmapNotify
418 however, if the window is unmapped when the reparent event occurs
419 the window manager never sees it because an unmap event is not sent
420 to an already unmapped window.
422 BlackboxWindow
*win
= searchWindow(e
->xreparent
.window
);
424 win
->reparentNotifyEvent(&e
->xreparent
);
426 Slit
*slit
= searchSlit(e
->xreparent
.window
);
427 if (slit
&& slit
->getWindowID() != e
->xreparent
.parent
)
428 slit
->removeClient(e
->xreparent
.window
, True
);
434 // motion notify compression...
437 while (XCheckTypedWindowEvent(getXDisplay(), e
->xmotion
.window
,
438 MotionNotify
, &realevent
)) {
442 // if we have compressed some motion events, use the last one
446 // strip the lock key modifiers
447 e
->xbutton
.state
&= ~(NumLockMask
| ScrollLockMask
| LockMask
);
449 last_time
= e
->xmotion
.time
;
451 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
452 Basemenu
*menu
= (Basemenu
*) 0;
454 if ((win
= searchWindow(e
->xmotion
.window
)))
455 win
->motionNotifyEvent(&e
->xmotion
);
456 else if ((menu
= searchMenu(e
->xmotion
.window
)))
457 menu
->motionNotifyEvent(&e
->xmotion
);
462 case PropertyNotify
: {
463 last_time
= e
->xproperty
.time
;
465 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
466 BScreen
*screen
= (BScreen
*) 0;
468 if ((win
= searchWindow(e
->xproperty
.window
)))
469 win
->propertyNotifyEvent(&e
->xproperty
);
470 else if ((screen
= searchScreen(e
->xproperty
.window
)))
471 screen
->propertyNotifyEvent(&e
->xproperty
);
476 last_time
= e
->xcrossing
.time
;
478 BScreen
*screen
= (BScreen
*) 0;
479 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
480 Basemenu
*menu
= (Basemenu
*) 0;
481 Toolbar
*tbar
= (Toolbar
*) 0;
482 Slit
*slit
= (Slit
*) 0;
484 if (e
->xcrossing
.mode
== NotifyGrab
) break;
488 sa
.w
= e
->xcrossing
.window
;
489 sa
.enter
= sa
.leave
= False
;
490 XCheckIfEvent(getXDisplay(), &dummy
, queueScanner
, (char *) &sa
);
492 if ((e
->xcrossing
.window
== e
->xcrossing
.root
) &&
493 (screen
= searchScreen(e
->xcrossing
.window
))) {
494 screen
->getImageControl()->installRootColormap();
495 } else if ((win
= searchWindow(e
->xcrossing
.window
))) {
496 if (win
->getScreen()->isSloppyFocus() &&
497 (! win
->isFocused()) && (! no_focus
)) {
498 if (((! sa
.leave
) || sa
.inferior
) && win
->isVisible()) {
499 if (win
->setInputFocus())
500 win
->installColormap(True
); // XXX: shouldnt we honour no install?
503 } else if ((menu
= searchMenu(e
->xcrossing
.window
))) {
504 menu
->enterNotifyEvent(&e
->xcrossing
);
505 } else if ((tbar
= searchToolbar(e
->xcrossing
.window
))) {
506 tbar
->enterNotifyEvent(&e
->xcrossing
);
507 } else if ((slit
= searchSlit(e
->xcrossing
.window
))) {
508 slit
->enterNotifyEvent(&e
->xcrossing
);
514 last_time
= e
->xcrossing
.time
;
516 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
517 Basemenu
*menu
= (Basemenu
*) 0;
518 Toolbar
*tbar
= (Toolbar
*) 0;
519 Slit
*slit
= (Slit
*) 0;
521 if ((menu
= searchMenu(e
->xcrossing
.window
)))
522 menu
->leaveNotifyEvent(&e
->xcrossing
);
523 else if ((win
= searchWindow(e
->xcrossing
.window
)))
524 win
->installColormap(False
);
525 else if ((tbar
= searchToolbar(e
->xcrossing
.window
)))
526 tbar
->leaveNotifyEvent(&e
->xcrossing
);
527 else if ((slit
= searchSlit(e
->xcrossing
.window
)))
528 slit
->leaveNotifyEvent(&e
->xcrossing
);
533 // compress expose events
536 int ex1
, ey1
, ex2
, ey2
;
539 ex2
= ex1
+ e
->xexpose
.width
- 1;
540 ey2
= ey1
+ e
->xexpose
.height
- 1;
541 while (XCheckTypedWindowEvent(getXDisplay(), e
->xexpose
.window
,
542 Expose
, &realevent
)) {
546 ex1
= std::min(realevent
.xexpose
.x
, ex1
);
547 ey1
= std::min(realevent
.xexpose
.y
, ey1
);
548 ex2
= std::max(realevent
.xexpose
.x
+ realevent
.xexpose
.width
- 1, ex2
);
549 ey2
= std::max(realevent
.xexpose
.y
+ realevent
.xexpose
.height
- 1, ey2
);
554 // use the merged area
557 e
->xexpose
.width
= ex2
- ex1
+ 1;
558 e
->xexpose
.height
= ey2
- ey1
+ 1;
560 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
561 Basemenu
*menu
= (Basemenu
*) 0;
562 Toolbar
*tbar
= (Toolbar
*) 0;
564 if ((win
= searchWindow(e
->xexpose
.window
)))
565 win
->exposeEvent(&e
->xexpose
);
566 else if ((menu
= searchMenu(e
->xexpose
.window
)))
567 menu
->exposeEvent(&e
->xexpose
);
568 else if ((tbar
= searchToolbar(e
->xexpose
.window
)))
569 tbar
->exposeEvent(&e
->xexpose
);
575 Toolbar
*tbar
= searchToolbar(e
->xkey
.window
);
577 if (tbar
&& tbar
->isEditing())
578 tbar
->keyPressEvent(&e
->xkey
);
583 case ColormapNotify
: {
584 BScreen
*screen
= searchScreen(e
->xcolormap
.window
);
587 screen
->setRootColormapInstalled((e
->xcolormap
.state
==
588 ColormapInstalled
) ? True
: False
);
594 if (e
->xfocus
.detail
!= NotifyNonlinear
&&
595 e
->xfocus
.detail
!= NotifyAncestor
) {
597 don't process FocusIns when:
598 1. the new focus window isn't an ancestor or inferior of the old
599 focus window (NotifyNonlinear)
600 make sure to allow the FocusIn when the old focus window was an
601 ancestor but didn't have a parent, such as root (NotifyAncestor)
606 BlackboxWindow
*win
= searchWindow(e
->xfocus
.window
);
608 if (! win
->isFocused())
609 win
->setFocusFlag(True
);
612 set the event window to None. when the FocusOut event handler calls
613 this function recursively, it uses this as an indication that focus
614 has moved to a known window.
616 e
->xfocus
.window
= None
;
623 if (e
->xfocus
.detail
!= NotifyNonlinear
) {
625 don't process FocusOuts when:
626 2. the new focus window isn't an ancestor or inferior of the old
627 focus window (NotifyNonlinear)
632 BlackboxWindow
*win
= searchWindow(e
->xfocus
.window
);
633 if (win
&& win
->isFocused()) {
635 before we mark "win" as unfocused, we need to verify that focus is
636 going to a known location, is in a known location, or set focus
641 // don't check the current focus if FocusOut was generated during a grab
642 bool check_focus
= (e
->xfocus
.mode
== NotifyNormal
);
645 First, check if there is a pending FocusIn event waiting. if there
646 is, process it and determine if focus has moved to another window
647 (the FocusIn event handler sets the window in the event
648 structure to None to indicate this).
650 if (XCheckTypedEvent(getXDisplay(), FocusIn
, &event
)) {
652 process_event(&event
);
653 if (event
.xfocus
.window
== None
) {
661 Second, we query the X server for the current input focus.
662 to make sure that we keep a consistent state.
664 BlackboxWindow
*focus
;
667 XGetInputFocus(getXDisplay(), &w
, &revert
);
668 focus
= searchWindow(w
);
671 focus got from "win" to "focus" under some very strange
672 circumstances, and we need to make sure that the focus indication
675 setFocusedWindow(focus
);
677 // we have no idea where focus went... so we set it to somewhere
686 case ClientMessage
: {
687 if (e
->xclient
.format
== 32) {
688 if (e
->xclient
.message_type
== xatom
->getAtom(XAtom::wm_change_state
)) {
689 // WM_CHANGE_STATE message
690 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
691 if (! win
|| ! win
->validateClient()) return;
693 if (e
->xclient
.data
.l
[0] == IconicState
)
695 if (e
->xclient
.data
.l
[0] == NormalState
)
697 } else if (e
->xclient
.message_type
==
698 xatom
->getAtom(XAtom::blackbox_change_workspace
) ||
699 e
->xclient
.message_type
==
700 xatom
->getAtom(XAtom::net_current_desktop
)) {
701 // NET_CURRENT_DESKTOP message
702 BScreen
*screen
= searchScreen(e
->xclient
.window
);
704 unsigned int workspace
= e
->xclient
.data
.l
[0];
705 if (screen
&& workspace
< screen
->getWorkspaceCount())
706 screen
->changeWorkspaceID(workspace
);
707 } else if (e
->xclient
.message_type
==
708 xatom
->getAtom(XAtom::blackbox_change_window_focus
) ||
709 e
->xclient
.message_type
==
710 xatom
->getAtom(XAtom::net_active_window
)) {
712 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
716 win
->deiconify(False
, True
);
719 if (win
->isVisible() && win
->setInputFocus()) {
720 win
->getScreen()->getWorkspace(win
->getWorkspaceNumber())->
722 win
->installColormap(True
);
725 } else if (e
->xclient
.message_type
==
726 xatom
->getAtom(XAtom::blackbox_cycle_window_focus
)) {
727 // BLACKBOX_CYCLE_WINDOW_FOCUS
728 BScreen
*screen
= searchScreen(e
->xclient
.window
);
731 if (! e
->xclient
.data
.l
[0])
736 } else if (e
->xclient
.message_type
==
737 xatom
->getAtom(XAtom::net_wm_desktop
)) {
739 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
742 BScreen
*screen
= win
->getScreen();
743 unsigned long wksp
= (unsigned) e
->xclient
.data
.l
[0];
744 if (wksp
< screen
->getWorkspaceCount()) {
745 if (win
->isIconic()) win
->deiconify(False
, True
);
746 if (win
->isStuck()) win
->stick();
747 if (wksp
!= screen
->getCurrentWorkspaceID())
751 screen
->reassociateWindow(win
, wksp
, True
);
752 } else if (wksp
== 0xfffffffe) { // XXX: BUG, BUT DOING THIS SO KDE WORKS FOR NOW!!
753 if (win
->isIconic()) win
->deiconify(False
, True
);
754 if (! win
->isStuck()) win
->stick();
755 if (! win
->isVisible()) win
->show();
758 } else if (e
->xclient
.message_type
==
759 xatom
->getAtom(XAtom::blackbox_change_attributes
)) {
760 // BLACKBOX_CHANGE_ATTRIBUTES
761 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
763 if (win
&& win
->validateClient()) {
765 net
.flags
= e
->xclient
.data
.l
[0];
766 net
.attrib
= e
->xclient
.data
.l
[1];
767 net
.workspace
= e
->xclient
.data
.l
[2];
768 net
.stack
= e
->xclient
.data
.l
[3];
769 net
.decoration
= e
->xclient
.data
.l
[4];
771 win
->changeBlackboxHints(&net
);
773 } else if (e
->xclient
.message_type
==
774 xatom
->getAtom(XAtom::net_number_of_desktops
)) {
775 // NET_NUMBER_OF_DESKTOPS
776 BScreen
*screen
= searchScreen(e
->xclient
.window
);
778 if (e
->xclient
.data
.l
[0] > 0) {
779 if ((unsigned) e
->xclient
.data
.l
[0] < screen
->getWorkspaceCount()) {
781 for (int i
= screen
->getWorkspaceCount();
782 i
> e
->xclient
.data
.l
[0]; --i
)
783 screen
->removeLastWorkspace();
784 // removeLast already sets the current workspace to the
785 // last available one.
786 } else if ((unsigned) e
->xclient
.data
.l
[0] >
787 screen
->getWorkspaceCount()) {
789 for(int i
= screen
->getWorkspaceCount();
790 i
< e
->xclient
.data
.l
[0]; ++i
)
791 screen
->addWorkspace();
794 } else if (e
->xclient
.message_type
==
795 xatom
->getAtom(XAtom::net_close_window
)) {
797 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
798 if (win
&& win
->validateClient())
799 win
->close(); // could this be smarter?
800 } else if (e
->xclient
.message_type
==
801 xatom
->getAtom(XAtom::net_wm_moveresize
)) {
803 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
804 if (win
&& win
->validateClient()) {
805 int x_root
= e
->xclient
.data
.l
[0],
806 y_root
= e
->xclient
.data
.l
[1];
807 if ((Atom
) e
->xclient
.data
.l
[2] ==
808 xatom
->getAtom(XAtom::net_wm_moveresize_move
)) {
809 win
->beginMove(x_root
, y_root
);
811 if ((Atom
) e
->xclient
.data
.l
[2] ==
812 xatom
->getAtom(XAtom::net_wm_moveresize_size_topleft
))
813 win
->beginResize(x_root
, y_root
, BlackboxWindow::TopLeft
);
814 else if ((Atom
) e
->xclient
.data
.l
[2] ==
815 xatom
->getAtom(XAtom::net_wm_moveresize_size_topright
))
816 win
->beginResize(x_root
, y_root
, BlackboxWindow::TopRight
);
817 else if ((Atom
) e
->xclient
.data
.l
[2] ==
818 xatom
->getAtom(XAtom::net_wm_moveresize_size_bottomleft
))
819 win
->beginResize(x_root
, y_root
, BlackboxWindow::BottomLeft
);
820 else if ((Atom
) e
->xclient
.data
.l
[2] ==
821 xatom
->getAtom(XAtom::net_wm_moveresize_size_bottomright
))
822 win
->beginResize(x_root
, y_root
, BlackboxWindow::BottomRight
);
825 } else if (e
->xclient
.message_type
==
826 xatom
->getAtom(XAtom::net_wm_state
)) {
828 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
829 if (win
&& win
->validateClient()) {
830 const Atom action
= (Atom
) e
->xclient
.data
.l
[0];
831 const Atom state
[] = { (Atom
) e
->xclient
.data
.l
[1],
832 (Atom
) e
->xclient
.data
.l
[2] };
834 for (int i
= 0; i
< 2; ++i
) {
838 if ((Atom
) e
->xclient
.data
.l
[0] == 1) {
840 if (state
[i
] == xatom
->getAtom(XAtom::net_wm_state_modal
)) {
842 } else if (state
[i
] ==
843 xatom
->getAtom(XAtom::net_wm_state_maximized_vert
)) {
844 if (win
->isMaximizedHoriz()) {
845 win
->maximize(0); // unmaximize
846 win
->maximize(1); // full
847 } else if (! win
->isMaximized()) {
848 win
->maximize(2); // vert
850 } else if (state
[i
] ==
851 xatom
->getAtom(XAtom::net_wm_state_maximized_horz
)) {
852 if (win
->isMaximizedVert()) {
853 win
->maximize(0); // unmaximize
854 win
->maximize(1); // full
855 } else if (! win
->isMaximized()) {
856 win
->maximize(3); // horiz
858 } else if (state
[i
] ==
859 xatom
->getAtom(XAtom::net_wm_state_shaded
)) {
860 if (! win
->isShaded())
862 } else if (state
[i
] ==
863 xatom
->getAtom(XAtom::net_wm_state_skip_taskbar
)) {
864 win
->setSkipTaskbar(True
);
865 } else if (state
[i
] ==
866 xatom
->getAtom(XAtom::net_wm_state_skip_pager
)) {
867 win
->setSkipPager(True
);
868 } else if (state
[i
] ==
869 xatom
->getAtom(XAtom::net_wm_state_fullscreen
)) {
870 win
->setFullscreen(True
);
872 } else if (action
== 0) {
874 if (state
[i
] == xatom
->getAtom(XAtom::net_wm_state_modal
)) {
875 win
->setModal(False
);
876 } else if (state
[i
] ==
877 xatom
->getAtom(XAtom::net_wm_state_maximized_vert
)) {
878 if (win
->isMaximizedFull()) {
879 win
->maximize(0); // unmaximize
880 win
->maximize(3); // horiz
881 } else if (win
->isMaximizedVert()) {
882 win
->maximize(0); // unmaximize
884 } else if (state
[i
] ==
885 xatom
->getAtom(XAtom::net_wm_state_maximized_horz
)) {
886 if (win
->isMaximizedFull()) {
887 win
->maximize(0); // unmaximize
888 win
->maximize(2); // vert
889 } else if (win
->isMaximizedHoriz()) {
890 win
->maximize(0); // unmaximize
892 } else if (state
[i
] ==
893 xatom
->getAtom(XAtom::net_wm_state_shaded
)) {
896 } else if (state
[i
] ==
897 xatom
->getAtom(XAtom::net_wm_state_skip_taskbar
)) {
898 win
->setSkipTaskbar(False
);
899 } else if (state
[i
] ==
900 xatom
->getAtom(XAtom::net_wm_state_skip_pager
)) {
901 win
->setSkipPager(False
);
902 } else if (state
[i
] ==
903 xatom
->getAtom(XAtom::net_wm_state_fullscreen
)) {
904 win
->setFullscreen(False
);
906 } else if (action
== 2) {
908 if (state
[i
] == xatom
->getAtom(XAtom::net_wm_state_modal
)) {
909 win
->setModal(! win
->isModal());
910 } else if (state
[i
] ==
911 xatom
->getAtom(XAtom::net_wm_state_maximized_vert
)) {
912 if (win
->isMaximizedFull()) {
913 win
->maximize(0); // unmaximize
914 win
->maximize(3); // horiz
915 } else if (win
->isMaximizedVert()) {
916 win
->maximize(0); // unmaximize
917 } else if (win
->isMaximizedHoriz()) {
918 win
->maximize(0); // unmaximize
919 win
->maximize(1); // full
921 win
->maximize(2); // vert
923 } else if (state
[i
] ==
924 xatom
->getAtom(XAtom::net_wm_state_maximized_horz
)) {
925 if (win
->isMaximizedFull()) {
926 win
->maximize(0); // unmaximize
927 win
->maximize(2); // vert
928 } else if (win
->isMaximizedHoriz()) {
929 win
->maximize(0); // unmaximize
930 } else if (win
->isMaximizedVert()) {
931 win
->maximize(0); // unmaximize
932 win
->maximize(1); // full
934 win
->maximize(3); // horiz
936 } else if (state
[i
] ==
937 xatom
->getAtom(XAtom::net_wm_state_shaded
)) {
939 } else if (state
[i
] ==
940 xatom
->getAtom(XAtom::net_wm_state_skip_taskbar
)) {
941 win
->setSkipTaskbar(! win
->skipTaskbar());
942 } else if (state
[i
] ==
943 xatom
->getAtom(XAtom::net_wm_state_skip_pager
)) {
944 win
->setSkipPager(! win
->skipPager());
945 } else if (state
[i
] ==
946 xatom
->getAtom(XAtom::net_wm_state_fullscreen
)) {
947 win
->setFullscreen(! win
->isFullscreen());
959 case ConfigureNotify
:
961 break; // not handled, just ignore
965 if (e
->type
== getShapeEventBase()) {
966 XShapeEvent
*shape_event
= (XShapeEvent
*) e
;
967 BlackboxWindow
*win
= searchWindow(e
->xany
.window
);
970 win
->shapeEvent(shape_event
);
978 bool Blackbox::handleSignal(int sig
) {
1007 bool Blackbox::validateWindow(Window window
) {
1009 if (XCheckTypedWindowEvent(getXDisplay(), window
, DestroyNotify
, &event
)) {
1010 XPutBackEvent(getXDisplay(), &event
);
1019 BScreen
*Blackbox::searchScreen(Window window
) {
1020 ScreenList::iterator it
= screenList
.begin();
1022 for (; it
!= screenList
.end(); ++it
) {
1024 if (s
->getRootWindow() == window
)
1028 return (BScreen
*) 0;
1032 BScreen
*Blackbox::searchDesktopWindow(Window window
) {
1033 WindowScreenLookup::iterator it
= desktopSearchList
.find(window
);
1034 if (it
!= desktopSearchList
.end())
1037 return (BScreen
*) 0;
1041 BScreen
*Blackbox::searchSystrayWindow(Window window
) {
1042 WindowScreenLookup::iterator it
= systraySearchList
.find(window
);
1043 if (it
!= systraySearchList
.end())
1046 return (BScreen
*) 0;
1050 BlackboxWindow
*Blackbox::searchWindow(Window window
) {
1051 WindowLookup::iterator it
= windowSearchList
.find(window
);
1052 if (it
!= windowSearchList
.end())
1055 return (BlackboxWindow
*) 0;
1059 BWindowGroup
*Blackbox::searchGroup(Window window
) {
1060 GroupLookup::iterator it
= groupSearchList
.find(window
);
1061 if (it
!= groupSearchList
.end())
1064 return (BWindowGroup
*) 0;
1068 Basemenu
*Blackbox::searchMenu(Window window
) {
1069 MenuLookup::iterator it
= menuSearchList
.find(window
);
1070 if (it
!= menuSearchList
.end())
1073 return (Basemenu
*) 0;
1077 Toolbar
*Blackbox::searchToolbar(Window window
) {
1078 ToolbarLookup::iterator it
= toolbarSearchList
.find(window
);
1079 if (it
!= toolbarSearchList
.end())
1082 return (Toolbar
*) 0;
1086 Slit
*Blackbox::searchSlit(Window window
) {
1087 SlitLookup::iterator it
= slitSearchList
.find(window
);
1088 if (it
!= slitSearchList
.end())
1095 void Blackbox::saveDesktopWindowSearch(Window window
, BScreen
*screen
) {
1096 desktopSearchList
.insert(WindowScreenLookupPair(window
, screen
));
1100 void Blackbox::saveSystrayWindowSearch(Window window
, BScreen
*screen
) {
1101 systraySearchList
.insert(WindowScreenLookupPair(window
, screen
));
1105 void Blackbox::saveWindowSearch(Window window
, BlackboxWindow
*data
) {
1106 windowSearchList
.insert(WindowLookupPair(window
, data
));
1110 void Blackbox::saveGroupSearch(Window window
, BWindowGroup
*data
) {
1111 groupSearchList
.insert(GroupLookupPair(window
, data
));
1115 void Blackbox::saveMenuSearch(Window window
, Basemenu
*data
) {
1116 menuSearchList
.insert(MenuLookupPair(window
, data
));
1120 void Blackbox::saveToolbarSearch(Window window
, Toolbar
*data
) {
1121 toolbarSearchList
.insert(ToolbarLookupPair(window
, data
));
1125 void Blackbox::saveSlitSearch(Window window
, Slit
*data
) {
1126 slitSearchList
.insert(SlitLookupPair(window
, data
));
1130 void Blackbox::removeDesktopWindowSearch(Window window
) {
1131 desktopSearchList
.erase(window
);
1135 void Blackbox::removeSystrayWindowSearch(Window window
) {
1136 systraySearchList
.erase(window
);
1140 void Blackbox::removeWindowSearch(Window window
) {
1141 windowSearchList
.erase(window
);
1145 void Blackbox::removeGroupSearch(Window window
) {
1146 groupSearchList
.erase(window
);
1150 void Blackbox::removeMenuSearch(Window window
) {
1151 menuSearchList
.erase(window
);
1155 void Blackbox::removeToolbarSearch(Window window
) {
1156 toolbarSearchList
.erase(window
);
1160 void Blackbox::removeSlitSearch(Window window
) {
1161 slitSearchList
.erase(window
);
1165 void Blackbox::restart(const char *prog
) {
1169 putenv(const_cast<char *>(screenList
.front()->displayString().c_str()));
1170 execlp(prog
, prog
, NULL
);
1174 // fall back in case the above execlp doesn't work
1175 execvp(argv
[0], argv
);
1176 string name
= basename(argv
[0]);
1177 execvp(name
.c_str(), argv
);
1181 void Blackbox::shutdown(void) {
1182 BaseDisplay::shutdown();
1184 XSetInputFocus(getXDisplay(), PointerRoot
, None
, CurrentTime
);
1186 std::for_each(screenList
.begin(), screenList
.end(),
1187 std::mem_fun(&BScreen::shutdown
));
1189 XSync(getXDisplay(), False
);
1194 * Save all values as they are so that the defaults will be written to the rc
1197 void Blackbox::save_rc(void) {
1198 config
.setAutoSave(false);
1200 config
.setValue("session.colorsPerChannel", resource
.colors_per_channel
);
1201 config
.setValue("session.doubleClickInterval",
1202 resource
.double_click_interval
);
1203 config
.setValue("session.autoRaiseDelay",
1204 ((resource
.auto_raise_delay
.tv_sec
* 1000) +
1205 (resource
.auto_raise_delay
.tv_usec
/ 1000)));
1206 config
.setValue("session.cacheLife", resource
.cache_life
/ 60000);
1207 config
.setValue("session.cacheMax", resource
.cache_max
);
1208 config
.setValue("session.styleFile", resource
.style_file
);
1209 config
.setValue("session.titlebarLayout", resource
.titlebar_layout
);
1211 std::for_each(screenList
.begin(), screenList
.end(),
1212 std::mem_fun(&BScreen::save_rc
));
1214 config
.setAutoSave(true);
1219 void Blackbox::load_rc(void) {
1220 if (! config
.load())
1225 if (! config
.getValue("session.colorsPerChannel",
1226 resource
.colors_per_channel
))
1227 resource
.colors_per_channel
= 4;
1228 if (resource
.colors_per_channel
< 2) resource
.colors_per_channel
= 2;
1229 else if (resource
.colors_per_channel
> 6) resource
.colors_per_channel
= 6;
1231 if (config
.getValue("session.styleFile", s
))
1232 resource
.style_file
= expandTilde(s
);
1234 resource
.style_file
= DEFAULTSTYLE
;
1236 if (! config
.getValue("session.doubleClickInterval",
1237 resource
.double_click_interval
));
1238 resource
.double_click_interval
= 250;
1240 if (! config
.getValue("session.autoRaiseDelay",
1241 resource
.auto_raise_delay
.tv_usec
))
1242 resource
.auto_raise_delay
.tv_usec
= 400;
1243 resource
.auto_raise_delay
.tv_sec
= resource
.auto_raise_delay
.tv_usec
/ 1000;
1244 resource
.auto_raise_delay
.tv_usec
-=
1245 (resource
.auto_raise_delay
.tv_sec
* 1000);
1246 resource
.auto_raise_delay
.tv_usec
*= 1000;
1248 if (! config
.getValue("session.cacheLife", resource
.cache_life
))
1249 resource
.cache_life
= 5;
1250 resource
.cache_life
*= 60000;
1252 if (! config
.getValue("session.cacheMax", resource
.cache_max
))
1253 resource
.cache_max
= 200;
1255 if (! config
.getValue("session.titlebarLayout", resource
.titlebar_layout
))
1256 resource
.titlebar_layout
= "ILMC";
1260 void Blackbox::reconfigure(void) {
1261 reconfigure_wait
= True
;
1263 if (! timer
->isTiming()) timer
->start();
1267 void Blackbox::real_reconfigure(void) {
1270 std::for_each(menuTimestamps
.begin(), menuTimestamps
.end(),
1272 menuTimestamps
.clear();
1276 std::for_each(screenList
.begin(), screenList
.end(),
1277 std::mem_fun(&BScreen::reconfigure
));
1281 void Blackbox::checkMenu(void) {
1282 bool reread
= False
;
1283 MenuTimestampList::iterator it
= menuTimestamps
.begin();
1284 for(; it
!= menuTimestamps
.end(); ++it
) {
1285 MenuTimestamp
*tmp
= *it
;
1288 if (! stat(tmp
->filename
.c_str(), &buf
)) {
1289 if (tmp
->timestamp
!= buf
.st_ctime
)
1296 if (reread
) rereadMenu();
1300 void Blackbox::rereadMenu(void) {
1301 reread_menu_wait
= True
;
1303 if (! timer
->isTiming()) timer
->start();
1307 void Blackbox::real_rereadMenu(void) {
1308 std::for_each(menuTimestamps
.begin(), menuTimestamps
.end(),
1310 menuTimestamps
.clear();
1312 std::for_each(screenList
.begin(), screenList
.end(),
1313 std::mem_fun(&BScreen::rereadMenu
));
1317 void Blackbox::saveStyleFilename(const string
& filename
) {
1318 assert(! filename
.empty());
1319 resource
.style_file
= filename
;
1320 config
.setValue("session.styleFile", resource
.style_file
);
1324 void Blackbox::addMenuTimestamp(const string
& filename
) {
1325 assert(! filename
.empty());
1328 MenuTimestampList::iterator it
= menuTimestamps
.begin();
1329 for (; it
!= menuTimestamps
.end() && ! found
; ++it
) {
1330 if ((*it
)->filename
== filename
) found
= True
;
1335 if (! stat(filename
.c_str(), &buf
)) {
1336 MenuTimestamp
*ts
= new MenuTimestamp
;
1338 ts
->filename
= filename
;
1339 ts
->timestamp
= buf
.st_ctime
;
1341 menuTimestamps
.push_back(ts
);
1347 void Blackbox::timeout(void) {
1348 if (reconfigure_wait
)
1351 if (reread_menu_wait
)
1354 reconfigure_wait
= reread_menu_wait
= False
;
1358 void Blackbox::setChangingWindow(BlackboxWindow
*win
) {
1359 // make sure one of the two is null and the other isn't
1360 assert((! changing_window
&& win
) || (! win
&& changing_window
));
1361 changing_window
= win
;
1365 void Blackbox::setFocusedWindow(BlackboxWindow
*win
) {
1366 if (focused_window
&& focused_window
== win
) // nothing to do
1369 BScreen
*old_screen
= 0;
1371 if (focused_window
) {
1372 focused_window
->setFocusFlag(False
);
1373 old_screen
= focused_window
->getScreen();
1376 if (win
&& ! win
->isIconic()) {
1377 // the active screen is the one with the last focused window...
1378 // this will keep focus on this screen no matter where the mouse goes,
1379 // so multihead keybindings will continue to work on that screen until the
1380 // user focuses a window on a different screen.
1381 active_screen
= win
->getScreen();
1382 focused_window
= win
;
1386 if (active_screen
) {
1387 // set input focus to the toolbar of the screen with mouse
1388 XSetInputFocus(getXDisplay(),
1389 active_screen
->getRootWindow(),
1390 RevertToPointerRoot
, CurrentTime
);
1392 // set input focus to the toolbar of the first managed screen
1393 XSetInputFocus(getXDisplay(),
1394 screenList
.front()->getRootWindow(),
1395 RevertToPointerRoot
, CurrentTime
);
1398 // set input focus to the toolbar of the last screen
1399 XSetInputFocus(getXDisplay(), old_screen
->getRootWindow(),
1400 RevertToPointerRoot
, CurrentTime
);
1404 if (active_screen
&& active_screen
->isScreenManaged()) {
1405 active_screen
->getToolbar()->redrawWindowLabel(True
);
1406 active_screen
->updateNetizenWindowFocus();
1409 if (old_screen
&& old_screen
!= active_screen
) {
1410 old_screen
->getToolbar()->redrawWindowLabel(True
);
1411 old_screen
->updateNetizenWindowFocus();