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 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
286 Slit
*slit
= (Slit
*) 0;
288 if ((win
= searchWindow(e
->xconfigurerequest
.window
))) {
289 win
->configureRequestEvent(&e
->xconfigurerequest
);
290 } else if ((slit
= searchSlit(e
->xconfigurerequest
.window
))) {
291 slit
->configureRequestEvent(&e
->xconfigurerequest
);
293 if (validateWindow(e
->xconfigurerequest
.window
)) {
296 xwc
.x
= e
->xconfigurerequest
.x
;
297 xwc
.y
= e
->xconfigurerequest
.y
;
298 xwc
.width
= e
->xconfigurerequest
.width
;
299 xwc
.height
= e
->xconfigurerequest
.height
;
300 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
301 xwc
.sibling
= e
->xconfigurerequest
.above
;
302 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
304 XConfigureWindow(getXDisplay(), e
->xconfigurerequest
.window
,
305 e
->xconfigurerequest
.value_mask
, &xwc
);
314 fprintf(stderr
, "Blackbox::process_event(): MapRequest for 0x%lx\n",
315 e
->xmaprequest
.window
);
318 BlackboxWindow
*win
= searchWindow(e
->xmaprequest
.window
);
322 if (win
->isIconic()) {
326 if (win
->isShaded()) {
331 if (focus
&& (win
->isTransient() || win
->getScreen()->doFocusNew()) &&
333 win
->setInputFocus();
335 BScreen
*screen
= searchScreen(e
->xmaprequest
.parent
);
339 we got a map request for a window who's parent isn't root. this
340 can happen in only one circumstance:
342 a client window unmapped a managed window, and then remapped it
343 somewhere between unmapping the client window and reparenting it
346 regardless of how it happens, we need to find the screen that
349 XWindowAttributes wattrib
;
350 if (! XGetWindowAttributes(getXDisplay(), e
->xmaprequest
.window
,
352 // failed to get the window attributes, perhaps the window has
353 // now been destroyed?
357 screen
= searchScreen(wattrib
.root
);
358 assert(screen
!= 0); // this should never happen
361 screen
->manageWindow(e
->xmaprequest
.window
);
368 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
369 Slit
*slit
= (Slit
*) 0;
370 BScreen
*screen
= (BScreen
*) 0;
372 if ((win
= searchWindow(e
->xunmap
.window
))) {
373 win
->unmapNotifyEvent(&e
->xunmap
);
374 } else if ((slit
= searchSlit(e
->xunmap
.window
))) {
375 slit
->unmapNotifyEvent(&e
->xunmap
);
376 } else if ((screen
= searchSystrayWindow(e
->xunmap
.window
))) {
377 screen
->removeSystrayWindow(e
->xunmap
.window
);
383 case DestroyNotify
: {
384 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
385 Slit
*slit
= (Slit
*) 0;
386 BScreen
*screen
= (BScreen
*) 0;
387 BWindowGroup
*group
= (BWindowGroup
*) 0;
389 if ((win
= searchWindow(e
->xdestroywindow
.window
))) {
390 win
->destroyNotifyEvent(&e
->xdestroywindow
);
391 } else if ((slit
= searchSlit(e
->xdestroywindow
.window
))) {
392 slit
->removeClient(e
->xdestroywindow
.window
, False
);
393 } else if ((group
= searchGroup(e
->xdestroywindow
.window
))) {
395 } else if ((screen
= searchSystrayWindow(e
->xunmap
.window
))) {
396 screen
->removeSystrayWindow(e
->xunmap
.window
);
402 case ReparentNotify
: {
404 this event is quite rare and is usually handled in unmapNotify
405 however, if the window is unmapped when the reparent event occurs
406 the window manager never sees it because an unmap event is not sent
407 to an already unmapped window.
409 BlackboxWindow
*win
= searchWindow(e
->xreparent
.window
);
411 win
->reparentNotifyEvent(&e
->xreparent
);
413 Slit
*slit
= searchSlit(e
->xreparent
.window
);
414 if (slit
&& slit
->getWindowID() != e
->xreparent
.parent
)
415 slit
->removeClient(e
->xreparent
.window
, True
);
421 // motion notify compression...
424 while (XCheckTypedWindowEvent(getXDisplay(), e
->xmotion
.window
,
425 MotionNotify
, &realevent
)) {
429 // if we have compressed some motion events, use the last one
433 // the pointer is on the wrong screen
434 if (! e
->xmotion
.same_screen
)
437 // strip the lock key modifiers
438 e
->xmotion
.state
&= ~(NumLockMask
| ScrollLockMask
| LockMask
);
440 last_time
= e
->xmotion
.time
;
442 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
443 Basemenu
*menu
= (Basemenu
*) 0;
445 if ((win
= searchWindow(e
->xmotion
.window
)))
446 win
->motionNotifyEvent(&e
->xmotion
);
447 else if ((menu
= searchMenu(e
->xmotion
.window
)))
448 menu
->motionNotifyEvent(&e
->xmotion
);
453 case PropertyNotify
: {
454 last_time
= e
->xproperty
.time
;
456 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
457 BScreen
*screen
= (BScreen
*) 0;
459 if ((win
= searchWindow(e
->xproperty
.window
)))
460 win
->propertyNotifyEvent(&e
->xproperty
);
461 else if ((screen
= searchScreen(e
->xproperty
.window
)))
462 screen
->propertyNotifyEvent(&e
->xproperty
);
467 last_time
= e
->xcrossing
.time
;
469 BScreen
*screen
= (BScreen
*) 0;
470 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
471 Basemenu
*menu
= (Basemenu
*) 0;
472 Toolbar
*tbar
= (Toolbar
*) 0;
473 Slit
*slit
= (Slit
*) 0;
475 if (e
->xcrossing
.mode
== NotifyGrab
) break;
479 sa
.w
= e
->xcrossing
.window
;
480 sa
.enter
= sa
.leave
= False
;
481 XCheckIfEvent(getXDisplay(), &dummy
, queueScanner
, (char *) &sa
);
483 if ((e
->xcrossing
.window
== e
->xcrossing
.root
) &&
484 (screen
= searchScreen(e
->xcrossing
.window
))) {
485 screen
->getImageControl()->installRootColormap();
486 } else if ((win
= searchWindow(e
->xcrossing
.window
))) {
487 if (win
->getScreen()->isSloppyFocus() &&
488 (! win
->isFocused()) && (! no_focus
) &&
489 win
->isNormal()) { // don't focus non-normal windows with mouseover
490 if ((! sa
.leave
|| sa
.inferior
) && win
->isVisible()) {
491 if (win
->setInputFocus())
492 win
->installColormap(True
); // XXX: shouldnt we honour no install?
495 } else if ((menu
= searchMenu(e
->xcrossing
.window
))) {
496 menu
->enterNotifyEvent(&e
->xcrossing
);
497 } else if ((tbar
= searchToolbar(e
->xcrossing
.window
))) {
498 tbar
->enterNotifyEvent(&e
->xcrossing
);
499 } else if ((slit
= searchSlit(e
->xcrossing
.window
))) {
500 slit
->enterNotifyEvent(&e
->xcrossing
);
506 last_time
= e
->xcrossing
.time
;
508 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
509 Basemenu
*menu
= (Basemenu
*) 0;
510 Toolbar
*tbar
= (Toolbar
*) 0;
511 Slit
*slit
= (Slit
*) 0;
513 if ((menu
= searchMenu(e
->xcrossing
.window
)))
514 menu
->leaveNotifyEvent(&e
->xcrossing
);
515 else if ((win
= searchWindow(e
->xcrossing
.window
)))
516 win
->installColormap(False
);
517 else if ((tbar
= searchToolbar(e
->xcrossing
.window
)))
518 tbar
->leaveNotifyEvent(&e
->xcrossing
);
519 else if ((slit
= searchSlit(e
->xcrossing
.window
)))
520 slit
->leaveNotifyEvent(&e
->xcrossing
);
525 // compress expose events
528 int ex1
, ey1
, ex2
, ey2
;
531 ex2
= ex1
+ e
->xexpose
.width
- 1;
532 ey2
= ey1
+ e
->xexpose
.height
- 1;
533 while (XCheckTypedWindowEvent(getXDisplay(), e
->xexpose
.window
,
534 Expose
, &realevent
)) {
538 ex1
= std::min(realevent
.xexpose
.x
, ex1
);
539 ey1
= std::min(realevent
.xexpose
.y
, ey1
);
540 ex2
= std::max(realevent
.xexpose
.x
+ realevent
.xexpose
.width
- 1, ex2
);
541 ey2
= std::max(realevent
.xexpose
.y
+ realevent
.xexpose
.height
- 1, ey2
);
546 // use the merged area
549 e
->xexpose
.width
= ex2
- ex1
+ 1;
550 e
->xexpose
.height
= ey2
- ey1
+ 1;
552 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
553 Basemenu
*menu
= (Basemenu
*) 0;
554 Toolbar
*tbar
= (Toolbar
*) 0;
556 if ((win
= searchWindow(e
->xexpose
.window
)))
557 win
->exposeEvent(&e
->xexpose
);
558 else if ((menu
= searchMenu(e
->xexpose
.window
)))
559 menu
->exposeEvent(&e
->xexpose
);
560 else if ((tbar
= searchToolbar(e
->xexpose
.window
)))
561 tbar
->exposeEvent(&e
->xexpose
);
567 Toolbar
*tbar
= searchToolbar(e
->xkey
.window
);
569 if (tbar
&& tbar
->isEditing())
570 tbar
->keyPressEvent(&e
->xkey
);
575 case ColormapNotify
: {
576 BScreen
*screen
= searchScreen(e
->xcolormap
.window
);
579 screen
->setRootColormapInstalled((e
->xcolormap
.state
==
580 ColormapInstalled
) ? True
: False
);
586 if (e
->xfocus
.detail
!= NotifyNonlinear
&&
587 e
->xfocus
.detail
!= NotifyAncestor
) {
589 don't process FocusIns when:
590 1. the new focus window isn't an ancestor or inferior of the old
591 focus window (NotifyNonlinear)
592 make sure to allow the FocusIn when the old focus window was an
593 ancestor but didn't have a parent, such as root (NotifyAncestor)
598 BlackboxWindow
*win
= searchWindow(e
->xfocus
.window
);
600 if (! win
->isFocused())
601 win
->setFocusFlag(True
);
604 set the event window to None. when the FocusOut event handler calls
605 this function recursively, it uses this as an indication that focus
606 has moved to a known window.
608 e
->xfocus
.window
= None
;
615 if (e
->xfocus
.detail
!= NotifyNonlinear
) {
617 don't process FocusOuts when:
618 2. the new focus window isn't an ancestor or inferior of the old
619 focus window (NotifyNonlinear)
624 BlackboxWindow
*win
= searchWindow(e
->xfocus
.window
);
625 if (win
&& win
->isFocused()) {
627 before we mark "win" as unfocused, we need to verify that focus is
628 going to a known location, is in a known location, or set focus
633 // don't check the current focus if FocusOut was generated during a grab
634 bool check_focus
= (e
->xfocus
.mode
== NotifyNormal
);
637 First, check if there is a pending FocusIn event waiting. if there
638 is, process it and determine if focus has moved to another window
639 (the FocusIn event handler sets the window in the event
640 structure to None to indicate this).
642 if (XCheckTypedEvent(getXDisplay(), FocusIn
, &event
)) {
644 process_event(&event
);
645 if (event
.xfocus
.window
== None
) {
653 Second, we query the X server for the current input focus.
654 to make sure that we keep a consistent state.
656 BlackboxWindow
*focus
;
659 XGetInputFocus(getXDisplay(), &w
, &revert
);
660 focus
= searchWindow(w
);
663 focus got from "win" to "focus" under some very strange
664 circumstances, and we need to make sure that the focus indication
667 setFocusedWindow(focus
);
669 // we have no idea where focus went... so we set it to somewhere
678 case ClientMessage
: {
679 if (e
->xclient
.format
== 32) {
680 if (e
->xclient
.message_type
== xatom
->getAtom(XAtom::wm_change_state
)) {
681 // WM_CHANGE_STATE message
682 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
683 if (! win
|| ! win
->validateClient()) return;
685 if (e
->xclient
.data
.l
[0] == IconicState
)
687 if (e
->xclient
.data
.l
[0] == NormalState
)
689 } else if (e
->xclient
.message_type
==
690 xatom
->getAtom(XAtom::blackbox_change_workspace
) ||
691 e
->xclient
.message_type
==
692 xatom
->getAtom(XAtom::net_current_desktop
)) {
693 // NET_CURRENT_DESKTOP message
694 BScreen
*screen
= searchScreen(e
->xclient
.window
);
696 unsigned int workspace
= e
->xclient
.data
.l
[0];
697 if (screen
&& workspace
< screen
->getWorkspaceCount())
698 screen
->changeWorkspaceID(workspace
);
699 } else if (e
->xclient
.message_type
==
700 xatom
->getAtom(XAtom::blackbox_change_window_focus
)) {
701 // TEMP HACK TO KEEP BBKEYS WORKING
702 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
704 if (win
&& win
->isVisible() && win
->setInputFocus())
705 win
->installColormap(True
);
706 } else if (e
->xclient
.message_type
==
707 xatom
->getAtom(XAtom::net_active_window
)) {
709 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
712 BScreen
*screen
= win
->getScreen();
715 win
->deiconify(False
, True
);
716 if (! win
->isStuck() &&
717 (win
->getWorkspaceNumber() != screen
->getCurrentWorkspaceID()))
718 screen
->changeWorkspaceID(win
->getWorkspaceNumber());
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 wksp
== 0xffffffff) {
754 if (win
->isIconic()) win
->deiconify(False
, True
);
755 if (! win
->isStuck()) win
->stick();
756 if (! win
->isVisible()) win
->show();
759 } else if (e
->xclient
.message_type
==
760 xatom
->getAtom(XAtom::blackbox_change_attributes
)) {
761 // BLACKBOX_CHANGE_ATTRIBUTES
762 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
764 if (win
&& win
->validateClient()) {
766 net
.flags
= e
->xclient
.data
.l
[0];
767 net
.attrib
= e
->xclient
.data
.l
[1];
768 net
.workspace
= e
->xclient
.data
.l
[2];
769 net
.stack
= e
->xclient
.data
.l
[3];
770 net
.decoration
= e
->xclient
.data
.l
[4];
772 win
->changeBlackboxHints(&net
);
774 } else if (e
->xclient
.message_type
==
775 xatom
->getAtom(XAtom::net_number_of_desktops
)) {
776 // NET_NUMBER_OF_DESKTOPS
777 BScreen
*screen
= searchScreen(e
->xclient
.window
);
779 if (e
->xclient
.data
.l
[0] > 0)
780 screen
->changeWorkspaceCount((unsigned) e
->xclient
.data
.l
[0]);
781 } else if (e
->xclient
.message_type
==
782 xatom
->getAtom(XAtom::net_close_window
)) {
784 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
785 if (win
&& win
->validateClient())
786 win
->close(); // could this be smarter?
787 } else if (e
->xclient
.message_type
==
788 xatom
->getAtom(XAtom::net_wm_moveresize
)) {
790 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
791 if (win
&& win
->validateClient()) {
792 int x_root
= e
->xclient
.data
.l
[0],
793 y_root
= e
->xclient
.data
.l
[1];
794 if ((Atom
) e
->xclient
.data
.l
[2] ==
795 xatom
->getAtom(XAtom::net_wm_moveresize_move
)) {
796 win
->beginMove(x_root
, y_root
);
798 if ((Atom
) e
->xclient
.data
.l
[2] ==
799 xatom
->getAtom(XAtom::net_wm_moveresize_size_topleft
))
800 win
->beginResize(x_root
, y_root
, BlackboxWindow::TopLeft
);
801 else if ((Atom
) e
->xclient
.data
.l
[2] ==
802 xatom
->getAtom(XAtom::net_wm_moveresize_size_topright
))
803 win
->beginResize(x_root
, y_root
, BlackboxWindow::TopRight
);
804 else if ((Atom
) e
->xclient
.data
.l
[2] ==
805 xatom
->getAtom(XAtom::net_wm_moveresize_size_bottomleft
))
806 win
->beginResize(x_root
, y_root
, BlackboxWindow::BottomLeft
);
807 else if ((Atom
) e
->xclient
.data
.l
[2] ==
808 xatom
->getAtom(XAtom::net_wm_moveresize_size_bottomright
))
809 win
->beginResize(x_root
, y_root
, BlackboxWindow::BottomRight
);
812 } else if (e
->xclient
.message_type
==
813 xatom
->getAtom(XAtom::net_wm_state
)) {
815 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
816 if (win
&& win
->validateClient()) {
817 const Atom action
= (Atom
) e
->xclient
.data
.l
[0];
818 const Atom state
[] = { (Atom
) e
->xclient
.data
.l
[1],
819 (Atom
) e
->xclient
.data
.l
[2] };
821 for (int i
= 0; i
< 2; ++i
) {
825 if ((Atom
) e
->xclient
.data
.l
[0] == 1) {
827 if (state
[i
] == xatom
->getAtom(XAtom::net_wm_state_modal
)) {
829 } else if (state
[i
] ==
830 xatom
->getAtom(XAtom::net_wm_state_maximized_vert
)) {
831 if (win
->isMaximizedHoriz()) {
832 win
->maximize(0); // unmaximize
833 win
->maximize(1); // full
834 } else if (! win
->isMaximized()) {
835 win
->maximize(2); // vert
837 } else if (state
[i
] ==
838 xatom
->getAtom(XAtom::net_wm_state_maximized_horz
)) {
839 if (win
->isMaximizedVert()) {
840 win
->maximize(0); // unmaximize
841 win
->maximize(1); // full
842 } else if (! win
->isMaximized()) {
843 win
->maximize(3); // horiz
845 } else if (state
[i
] ==
846 xatom
->getAtom(XAtom::net_wm_state_shaded
)) {
847 if (! win
->isShaded())
849 } else if (state
[i
] ==
850 xatom
->getAtom(XAtom::net_wm_state_skip_taskbar
)) {
851 win
->setSkipTaskbar(True
);
852 } else if (state
[i
] ==
853 xatom
->getAtom(XAtom::net_wm_state_skip_pager
)) {
854 win
->setSkipPager(True
);
855 } else if (state
[i
] ==
856 xatom
->getAtom(XAtom::net_wm_state_fullscreen
)) {
857 win
->setFullscreen(True
);
859 } else if (action
== 0) {
861 if (state
[i
] == xatom
->getAtom(XAtom::net_wm_state_modal
)) {
862 win
->setModal(False
);
863 } else if (state
[i
] ==
864 xatom
->getAtom(XAtom::net_wm_state_maximized_vert
)) {
865 if (win
->isMaximizedFull()) {
866 win
->maximize(0); // unmaximize
867 win
->maximize(3); // horiz
868 } else if (win
->isMaximizedVert()) {
869 win
->maximize(0); // unmaximize
871 } else if (state
[i
] ==
872 xatom
->getAtom(XAtom::net_wm_state_maximized_horz
)) {
873 if (win
->isMaximizedFull()) {
874 win
->maximize(0); // unmaximize
875 win
->maximize(2); // vert
876 } else if (win
->isMaximizedHoriz()) {
877 win
->maximize(0); // unmaximize
879 } else if (state
[i
] ==
880 xatom
->getAtom(XAtom::net_wm_state_shaded
)) {
883 } else if (state
[i
] ==
884 xatom
->getAtom(XAtom::net_wm_state_skip_taskbar
)) {
885 win
->setSkipTaskbar(False
);
886 } else if (state
[i
] ==
887 xatom
->getAtom(XAtom::net_wm_state_skip_pager
)) {
888 win
->setSkipPager(False
);
889 } else if (state
[i
] ==
890 xatom
->getAtom(XAtom::net_wm_state_fullscreen
)) {
891 win
->setFullscreen(False
);
893 } else if (action
== 2) {
895 if (state
[i
] == xatom
->getAtom(XAtom::net_wm_state_modal
)) {
896 win
->setModal(! win
->isModal());
897 } else if (state
[i
] ==
898 xatom
->getAtom(XAtom::net_wm_state_maximized_vert
)) {
899 if (win
->isMaximizedFull()) {
900 win
->maximize(0); // unmaximize
901 win
->maximize(3); // horiz
902 } else if (win
->isMaximizedVert()) {
903 win
->maximize(0); // unmaximize
904 } else if (win
->isMaximizedHoriz()) {
905 win
->maximize(0); // unmaximize
906 win
->maximize(1); // full
908 win
->maximize(2); // vert
910 } else if (state
[i
] ==
911 xatom
->getAtom(XAtom::net_wm_state_maximized_horz
)) {
912 if (win
->isMaximizedFull()) {
913 win
->maximize(0); // unmaximize
914 win
->maximize(2); // vert
915 } else if (win
->isMaximizedHoriz()) {
916 win
->maximize(0); // unmaximize
917 } else if (win
->isMaximizedVert()) {
918 win
->maximize(0); // unmaximize
919 win
->maximize(1); // full
921 win
->maximize(3); // horiz
923 } else if (state
[i
] ==
924 xatom
->getAtom(XAtom::net_wm_state_shaded
)) {
926 } else if (state
[i
] ==
927 xatom
->getAtom(XAtom::net_wm_state_skip_taskbar
)) {
928 win
->setSkipTaskbar(! win
->skipTaskbar());
929 } else if (state
[i
] ==
930 xatom
->getAtom(XAtom::net_wm_state_skip_pager
)) {
931 win
->setSkipPager(! win
->skipPager());
932 } else if (state
[i
] ==
933 xatom
->getAtom(XAtom::net_wm_state_fullscreen
)) {
934 win
->setFullscreen(! win
->isFullscreen());
946 case ConfigureNotify
:
948 break; // not handled, just ignore
952 if (e
->type
== getShapeEventBase()) {
953 XShapeEvent
*shape_event
= (XShapeEvent
*) e
;
954 BlackboxWindow
*win
= searchWindow(e
->xany
.window
);
957 win
->shapeEvent(shape_event
);
965 bool Blackbox::handleSignal(int sig
) {
994 bool Blackbox::validateWindow(Window window
) {
996 if (XCheckTypedWindowEvent(getXDisplay(), window
, DestroyNotify
, &event
)) {
997 XPutBackEvent(getXDisplay(), &event
);
1006 BScreen
*Blackbox::searchScreen(Window window
) {
1007 ScreenList::iterator it
= screenList
.begin();
1009 for (; it
!= screenList
.end(); ++it
) {
1011 if (s
->getRootWindow() == window
)
1015 return (BScreen
*) 0;
1019 BScreen
*Blackbox::searchSystrayWindow(Window window
) {
1020 WindowScreenLookup::iterator it
= systraySearchList
.find(window
);
1021 if (it
!= systraySearchList
.end())
1024 return (BScreen
*) 0;
1028 BlackboxWindow
*Blackbox::searchWindow(Window window
) {
1029 WindowLookup::iterator it
= windowSearchList
.find(window
);
1030 if (it
!= windowSearchList
.end())
1033 return (BlackboxWindow
*) 0;
1037 BWindowGroup
*Blackbox::searchGroup(Window window
) {
1038 GroupLookup::iterator it
= groupSearchList
.find(window
);
1039 if (it
!= groupSearchList
.end())
1042 return (BWindowGroup
*) 0;
1046 Basemenu
*Blackbox::searchMenu(Window window
) {
1047 MenuLookup::iterator it
= menuSearchList
.find(window
);
1048 if (it
!= menuSearchList
.end())
1051 return (Basemenu
*) 0;
1055 Toolbar
*Blackbox::searchToolbar(Window window
) {
1056 ToolbarLookup::iterator it
= toolbarSearchList
.find(window
);
1057 if (it
!= toolbarSearchList
.end())
1060 return (Toolbar
*) 0;
1064 Slit
*Blackbox::searchSlit(Window window
) {
1065 SlitLookup::iterator it
= slitSearchList
.find(window
);
1066 if (it
!= slitSearchList
.end())
1073 void Blackbox::saveSystrayWindowSearch(Window window
, BScreen
*screen
) {
1074 systraySearchList
.insert(WindowScreenLookupPair(window
, screen
));
1078 void Blackbox::saveWindowSearch(Window window
, BlackboxWindow
*data
) {
1079 windowSearchList
.insert(WindowLookupPair(window
, data
));
1083 void Blackbox::saveGroupSearch(Window window
, BWindowGroup
*data
) {
1084 groupSearchList
.insert(GroupLookupPair(window
, data
));
1088 void Blackbox::saveMenuSearch(Window window
, Basemenu
*data
) {
1089 menuSearchList
.insert(MenuLookupPair(window
, data
));
1093 void Blackbox::saveToolbarSearch(Window window
, Toolbar
*data
) {
1094 toolbarSearchList
.insert(ToolbarLookupPair(window
, data
));
1098 void Blackbox::saveSlitSearch(Window window
, Slit
*data
) {
1099 slitSearchList
.insert(SlitLookupPair(window
, data
));
1103 void Blackbox::removeSystrayWindowSearch(Window window
) {
1104 systraySearchList
.erase(window
);
1108 void Blackbox::removeWindowSearch(Window window
) {
1109 windowSearchList
.erase(window
);
1113 void Blackbox::removeGroupSearch(Window window
) {
1114 groupSearchList
.erase(window
);
1118 void Blackbox::removeMenuSearch(Window window
) {
1119 menuSearchList
.erase(window
);
1123 void Blackbox::removeToolbarSearch(Window window
) {
1124 toolbarSearchList
.erase(window
);
1128 void Blackbox::removeSlitSearch(Window window
) {
1129 slitSearchList
.erase(window
);
1133 void Blackbox::restart(const char *prog
) {
1137 putenv(const_cast<char *>(screenList
.front()->displayString().c_str()));
1138 execlp(prog
, prog
, NULL
);
1142 // fall back in case the above execlp doesn't work
1143 execvp(argv
[0], argv
);
1144 string name
= basename(argv
[0]);
1145 execvp(name
.c_str(), argv
);
1149 void Blackbox::shutdown(void) {
1150 BaseDisplay::shutdown();
1152 XSetInputFocus(getXDisplay(), PointerRoot
, None
, CurrentTime
);
1154 std::for_each(screenList
.begin(), screenList
.end(),
1155 std::mem_fun(&BScreen::shutdown
));
1157 XSync(getXDisplay(), False
);
1162 void Blackbox::saveXineramaPlacement(bool x
) {
1163 resource
.xinerama_placement
= x
;
1164 config
.setValue("session.xineramaSupport.windowPlacement",
1165 resource
.xinerama_placement
);
1166 reconfigure(); // make sure all screens get this change
1170 void Blackbox::saveXineramaMaximizing(bool x
) {
1171 resource
.xinerama_maximize
= x
;
1172 config
.setValue("session.xineramaSupport.windowMaximizing",
1173 resource
.xinerama_maximize
);
1174 reconfigure(); // make sure all screens get this change
1178 void Blackbox::saveXineramaSnapping(bool x
) {
1179 resource
.xinerama_snap
= x
;
1180 config
.setValue("session.xineramaSupport.windowSnapping",
1181 resource
.xinerama_snap
);
1182 reconfigure(); // make sure all screens get this change
1188 * Save all values as they are so that the defaults will be written to the rc
1191 void Blackbox::save_rc(void) {
1192 config
.setAutoSave(false);
1194 config
.setValue("session.colorsPerChannel", resource
.colors_per_channel
);
1195 config
.setValue("session.doubleClickInterval",
1196 resource
.double_click_interval
);
1197 config
.setValue("session.autoRaiseDelay",
1198 ((resource
.auto_raise_delay
.tv_sec
* 1000) +
1199 (resource
.auto_raise_delay
.tv_usec
/ 1000)));
1200 config
.setValue("session.cacheLife", resource
.cache_life
/ 60000);
1201 config
.setValue("session.cacheMax", resource
.cache_max
);
1202 config
.setValue("session.styleFile", resource
.style_file
);
1203 config
.setValue("session.titlebarLayout", resource
.titlebar_layout
);
1206 saveXineramaPlacement(resource
.xinerama_placement
);
1207 saveXineramaMaximizing(resource
.xinerama_maximize
);
1208 saveXineramaSnapping(resource
.xinerama_snap
);
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";
1259 if (! config
.getValue("session.xineramaSupport.windowPlacement",
1260 resource
.xinerama_placement
))
1261 resource
.xinerama_placement
= false;
1263 if (! config
.getValue("session.xineramaSupport.windowMaximizing",
1264 resource
.xinerama_maximize
))
1265 resource
.xinerama_maximize
= false;
1267 if (! config
.getValue("session.xineramaSupport.windowSnapping",
1268 resource
.xinerama_snap
))
1269 resource
.xinerama_snap
= false;
1274 void Blackbox::reconfigure(void) {
1275 // don't reconfigure while saving the initial rc file, it's a waste and it
1276 // breaks somethings (workspace names)
1277 if (isStartup()) return;
1279 reconfigure_wait
= True
;
1281 if (! timer
->isTiming()) timer
->start();
1285 void Blackbox::real_reconfigure(void) {
1288 std::for_each(menuTimestamps
.begin(), menuTimestamps
.end(),
1290 menuTimestamps
.clear();
1294 std::for_each(screenList
.begin(), screenList
.end(),
1295 std::mem_fun(&BScreen::reconfigure
));
1299 void Blackbox::checkMenu(void) {
1300 bool reread
= False
;
1301 MenuTimestampList::iterator it
= menuTimestamps
.begin();
1302 for(; it
!= menuTimestamps
.end(); ++it
) {
1303 MenuTimestamp
*tmp
= *it
;
1306 if (! stat(tmp
->filename
.c_str(), &buf
)) {
1307 if (tmp
->timestamp
!= buf
.st_ctime
)
1314 if (reread
) rereadMenu();
1318 void Blackbox::rereadMenu(void) {
1319 reread_menu_wait
= True
;
1321 if (! timer
->isTiming()) timer
->start();
1325 void Blackbox::real_rereadMenu(void) {
1326 std::for_each(menuTimestamps
.begin(), menuTimestamps
.end(),
1328 menuTimestamps
.clear();
1330 std::for_each(screenList
.begin(), screenList
.end(),
1331 std::mem_fun(&BScreen::rereadMenu
));
1335 void Blackbox::saveStyleFilename(const string
& filename
) {
1336 assert(! filename
.empty());
1337 resource
.style_file
= filename
;
1338 config
.setValue("session.styleFile", resource
.style_file
);
1342 void Blackbox::addMenuTimestamp(const string
& filename
) {
1343 assert(! filename
.empty());
1346 MenuTimestampList::iterator it
= menuTimestamps
.begin();
1347 for (; it
!= menuTimestamps
.end() && ! found
; ++it
) {
1348 if ((*it
)->filename
== filename
) found
= True
;
1353 if (! stat(filename
.c_str(), &buf
)) {
1354 MenuTimestamp
*ts
= new MenuTimestamp
;
1356 ts
->filename
= filename
;
1357 ts
->timestamp
= buf
.st_ctime
;
1359 menuTimestamps
.push_back(ts
);
1365 void Blackbox::timeout(void) {
1366 if (reconfigure_wait
)
1369 if (reread_menu_wait
)
1372 reconfigure_wait
= reread_menu_wait
= False
;
1376 void Blackbox::setChangingWindow(BlackboxWindow
*win
) {
1377 // make sure one of the two is null and the other isn't
1378 assert((! changing_window
&& win
) || (! win
&& changing_window
));
1379 changing_window
= win
;
1383 void Blackbox::setFocusedWindow(BlackboxWindow
*win
) {
1384 if (focused_window
&& focused_window
== win
) // nothing to do
1387 BScreen
*old_screen
= 0;
1389 if (focused_window
) {
1390 focused_window
->setFocusFlag(False
);
1391 old_screen
= focused_window
->getScreen();
1394 if (win
&& ! win
->isIconic()) {
1395 // the active screen is the one with the last focused window...
1396 // this will keep focus on this screen no matter where the mouse goes,
1397 // so multihead keybindings will continue to work on that screen until the
1398 // user focuses a window on a different screen.
1399 active_screen
= win
->getScreen();
1400 focused_window
= win
;
1404 if (active_screen
) {
1405 // set input focus to the toolbar of the screen with mouse
1406 XSetInputFocus(getXDisplay(),
1407 active_screen
->getRootWindow(),
1408 RevertToPointerRoot
, CurrentTime
);
1410 // set input focus to the toolbar of the first managed screen
1411 XSetInputFocus(getXDisplay(),
1412 screenList
.front()->getRootWindow(),
1413 RevertToPointerRoot
, CurrentTime
);
1416 // set input focus to the toolbar of the last screen
1417 XSetInputFocus(getXDisplay(), old_screen
->getRootWindow(),
1418 RevertToPointerRoot
, CurrentTime
);
1422 if (active_screen
&& active_screen
->isScreenManaged()) {
1423 active_screen
->getToolbar()->redrawWindowLabel(True
);
1424 active_screen
->updateNetizenWindowFocus();
1427 if (old_screen
&& old_screen
!= active_screen
) {
1428 old_screen
->getToolbar()->redrawWindowLabel(True
);
1429 old_screen
->updateNetizenWindowFocus();