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
);
392 case DestroyNotify
: {
393 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
394 Slit
*slit
= (Slit
*) 0;
395 BScreen
*screen
= (BScreen
*) 0;
396 BWindowGroup
*group
= (BWindowGroup
*) 0;
398 if ((win
= searchWindow(e
->xdestroywindow
.window
))) {
399 win
->destroyNotifyEvent(&e
->xdestroywindow
);
400 } else if ((slit
= searchSlit(e
->xdestroywindow
.window
))) {
401 slit
->removeClient(e
->xdestroywindow
.window
, False
);
402 } else if ((group
= searchGroup(e
->xdestroywindow
.window
))) {
404 } else if ((screen
= searchSystrayWindow(e
->xunmap
.window
))) {
405 screen
->removeSystrayWindow(e
->xunmap
.window
);
411 case ReparentNotify
: {
413 this event is quite rare and is usually handled in unmapNotify
414 however, if the window is unmapped when the reparent event occurs
415 the window manager never sees it because an unmap event is not sent
416 to an already unmapped window.
418 BlackboxWindow
*win
= searchWindow(e
->xreparent
.window
);
420 win
->reparentNotifyEvent(&e
->xreparent
);
422 Slit
*slit
= searchSlit(e
->xreparent
.window
);
423 if (slit
&& slit
->getWindowID() != e
->xreparent
.parent
)
424 slit
->removeClient(e
->xreparent
.window
, True
);
430 // motion notify compression...
433 while (XCheckTypedWindowEvent(getXDisplay(), e
->xmotion
.window
,
434 MotionNotify
, &realevent
)) {
438 // if we have compressed some motion events, use the last one
442 // strip the lock key modifiers
443 e
->xbutton
.state
&= ~(NumLockMask
| ScrollLockMask
| LockMask
);
445 last_time
= e
->xmotion
.time
;
447 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
448 Basemenu
*menu
= (Basemenu
*) 0;
450 if ((win
= searchWindow(e
->xmotion
.window
)))
451 win
->motionNotifyEvent(&e
->xmotion
);
452 else if ((menu
= searchMenu(e
->xmotion
.window
)))
453 menu
->motionNotifyEvent(&e
->xmotion
);
458 case PropertyNotify
: {
459 last_time
= e
->xproperty
.time
;
461 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
462 BScreen
*screen
= (BScreen
*) 0;
464 if ((win
= searchWindow(e
->xproperty
.window
)))
465 win
->propertyNotifyEvent(&e
->xproperty
);
466 else if ((screen
= searchScreen(e
->xproperty
.window
)))
467 screen
->propertyNotifyEvent(&e
->xproperty
);
472 last_time
= e
->xcrossing
.time
;
474 BScreen
*screen
= (BScreen
*) 0;
475 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
476 Basemenu
*menu
= (Basemenu
*) 0;
477 Toolbar
*tbar
= (Toolbar
*) 0;
478 Slit
*slit
= (Slit
*) 0;
480 if (e
->xcrossing
.mode
== NotifyGrab
) break;
484 sa
.w
= e
->xcrossing
.window
;
485 sa
.enter
= sa
.leave
= False
;
486 XCheckIfEvent(getXDisplay(), &dummy
, queueScanner
, (char *) &sa
);
488 if ((e
->xcrossing
.window
== e
->xcrossing
.root
) &&
489 (screen
= searchScreen(e
->xcrossing
.window
))) {
490 screen
->getImageControl()->installRootColormap();
491 } else if ((win
= searchWindow(e
->xcrossing
.window
))) {
492 if (win
->getScreen()->isSloppyFocus() &&
493 (! win
->isFocused()) && (! no_focus
) &&
494 win
->isNormal()) { // don't focus non-normal windows with mouseover
495 if (((! sa
.leave
) || sa
.inferior
) && win
->isVisible()) {
496 if (win
->setInputFocus())
497 win
->installColormap(True
); // XXX: shouldnt we honour no install?
500 } else if ((menu
= searchMenu(e
->xcrossing
.window
))) {
501 menu
->enterNotifyEvent(&e
->xcrossing
);
502 } else if ((tbar
= searchToolbar(e
->xcrossing
.window
))) {
503 tbar
->enterNotifyEvent(&e
->xcrossing
);
504 } else if ((slit
= searchSlit(e
->xcrossing
.window
))) {
505 slit
->enterNotifyEvent(&e
->xcrossing
);
511 last_time
= e
->xcrossing
.time
;
513 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
514 Basemenu
*menu
= (Basemenu
*) 0;
515 Toolbar
*tbar
= (Toolbar
*) 0;
516 Slit
*slit
= (Slit
*) 0;
518 if ((menu
= searchMenu(e
->xcrossing
.window
)))
519 menu
->leaveNotifyEvent(&e
->xcrossing
);
520 else if ((win
= searchWindow(e
->xcrossing
.window
)))
521 win
->installColormap(False
);
522 else if ((tbar
= searchToolbar(e
->xcrossing
.window
)))
523 tbar
->leaveNotifyEvent(&e
->xcrossing
);
524 else if ((slit
= searchSlit(e
->xcrossing
.window
)))
525 slit
->leaveNotifyEvent(&e
->xcrossing
);
530 // compress expose events
533 int ex1
, ey1
, ex2
, ey2
;
536 ex2
= ex1
+ e
->xexpose
.width
- 1;
537 ey2
= ey1
+ e
->xexpose
.height
- 1;
538 while (XCheckTypedWindowEvent(getXDisplay(), e
->xexpose
.window
,
539 Expose
, &realevent
)) {
543 ex1
= std::min(realevent
.xexpose
.x
, ex1
);
544 ey1
= std::min(realevent
.xexpose
.y
, ey1
);
545 ex2
= std::max(realevent
.xexpose
.x
+ realevent
.xexpose
.width
- 1, ex2
);
546 ey2
= std::max(realevent
.xexpose
.y
+ realevent
.xexpose
.height
- 1, ey2
);
551 // use the merged area
554 e
->xexpose
.width
= ex2
- ex1
+ 1;
555 e
->xexpose
.height
= ey2
- ey1
+ 1;
557 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
558 Basemenu
*menu
= (Basemenu
*) 0;
559 Toolbar
*tbar
= (Toolbar
*) 0;
561 if ((win
= searchWindow(e
->xexpose
.window
)))
562 win
->exposeEvent(&e
->xexpose
);
563 else if ((menu
= searchMenu(e
->xexpose
.window
)))
564 menu
->exposeEvent(&e
->xexpose
);
565 else if ((tbar
= searchToolbar(e
->xexpose
.window
)))
566 tbar
->exposeEvent(&e
->xexpose
);
572 Toolbar
*tbar
= searchToolbar(e
->xkey
.window
);
574 if (tbar
&& tbar
->isEditing())
575 tbar
->keyPressEvent(&e
->xkey
);
580 case ColormapNotify
: {
581 BScreen
*screen
= searchScreen(e
->xcolormap
.window
);
584 screen
->setRootColormapInstalled((e
->xcolormap
.state
==
585 ColormapInstalled
) ? True
: False
);
591 if (e
->xfocus
.detail
!= NotifyNonlinear
&&
592 e
->xfocus
.detail
!= NotifyAncestor
) {
594 don't process FocusIns when:
595 1. the new focus window isn't an ancestor or inferior of the old
596 focus window (NotifyNonlinear)
597 make sure to allow the FocusIn when the old focus window was an
598 ancestor but didn't have a parent, such as root (NotifyAncestor)
603 BlackboxWindow
*win
= searchWindow(e
->xfocus
.window
);
605 if (! win
->isFocused())
606 win
->setFocusFlag(True
);
609 set the event window to None. when the FocusOut event handler calls
610 this function recursively, it uses this as an indication that focus
611 has moved to a known window.
613 e
->xfocus
.window
= None
;
620 if (e
->xfocus
.detail
!= NotifyNonlinear
) {
622 don't process FocusOuts when:
623 2. the new focus window isn't an ancestor or inferior of the old
624 focus window (NotifyNonlinear)
629 BlackboxWindow
*win
= searchWindow(e
->xfocus
.window
);
630 if (win
&& win
->isFocused()) {
632 before we mark "win" as unfocused, we need to verify that focus is
633 going to a known location, is in a known location, or set focus
638 // don't check the current focus if FocusOut was generated during a grab
639 bool check_focus
= (e
->xfocus
.mode
== NotifyNormal
);
642 First, check if there is a pending FocusIn event waiting. if there
643 is, process it and determine if focus has moved to another window
644 (the FocusIn event handler sets the window in the event
645 structure to None to indicate this).
647 if (XCheckTypedEvent(getXDisplay(), FocusIn
, &event
)) {
649 process_event(&event
);
650 if (event
.xfocus
.window
== None
) {
658 Second, we query the X server for the current input focus.
659 to make sure that we keep a consistent state.
661 BlackboxWindow
*focus
;
664 XGetInputFocus(getXDisplay(), &w
, &revert
);
665 focus
= searchWindow(w
);
668 focus got from "win" to "focus" under some very strange
669 circumstances, and we need to make sure that the focus indication
672 setFocusedWindow(focus
);
674 // we have no idea where focus went... so we set it to somewhere
683 case ClientMessage
: {
684 if (e
->xclient
.format
== 32) {
685 if (e
->xclient
.message_type
== xatom
->getAtom(XAtom::wm_change_state
)) {
686 // WM_CHANGE_STATE message
687 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
688 if (! win
|| ! win
->validateClient()) return;
690 if (e
->xclient
.data
.l
[0] == IconicState
)
692 if (e
->xclient
.data
.l
[0] == NormalState
)
694 } else if (e
->xclient
.message_type
==
695 xatom
->getAtom(XAtom::blackbox_change_workspace
) ||
696 e
->xclient
.message_type
==
697 xatom
->getAtom(XAtom::net_current_desktop
)) {
698 // NET_CURRENT_DESKTOP message
699 BScreen
*screen
= searchScreen(e
->xclient
.window
);
701 unsigned int workspace
= e
->xclient
.data
.l
[0];
702 if (screen
&& workspace
< screen
->getWorkspaceCount())
703 screen
->changeWorkspaceID(workspace
);
704 } else if (e
->xclient
.message_type
==
705 xatom
->getAtom(XAtom::blackbox_change_window_focus
)) {
706 // TEMP HACK TO KEEP BBKEYS WORKING
707 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
709 if (win
&& win
->isVisible() && win
->setInputFocus())
710 win
->installColormap(True
);
711 } else if (e
->xclient
.message_type
==
712 xatom
->getAtom(XAtom::net_active_window
)) {
714 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
717 BScreen
*screen
= win
->getScreen();
720 win
->deiconify(False
, True
);
723 if (win
->getWorkspaceNumber() != screen
->getCurrentWorkspaceID())
724 screen
->changeWorkspaceID(win
->getWorkspaceNumber());
725 if (win
->isVisible() && win
->setInputFocus()) {
726 win
->getScreen()->getWorkspace(win
->getWorkspaceNumber())->
728 win
->installColormap(True
);
731 } else if (e
->xclient
.message_type
==
732 xatom
->getAtom(XAtom::blackbox_cycle_window_focus
)) {
733 // BLACKBOX_CYCLE_WINDOW_FOCUS
734 BScreen
*screen
= searchScreen(e
->xclient
.window
);
737 if (! e
->xclient
.data
.l
[0])
742 } else if (e
->xclient
.message_type
==
743 xatom
->getAtom(XAtom::net_wm_desktop
)) {
745 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
748 BScreen
*screen
= win
->getScreen();
749 unsigned long wksp
= (unsigned) e
->xclient
.data
.l
[0];
750 if (wksp
< screen
->getWorkspaceCount()) {
751 if (win
->isIconic()) win
->deiconify(False
, True
);
752 if (win
->isStuck()) win
->stick();
753 if (wksp
!= screen
->getCurrentWorkspaceID())
757 screen
->reassociateWindow(win
, wksp
, True
);
758 } else if (wksp
== 0xfffffffe || // XXX: BUG, BUT DOING THIS SO KDE WORKS FOR NOW!!
759 wksp
== 0xffffffff) {
760 if (win
->isIconic()) win
->deiconify(False
, True
);
761 if (! win
->isStuck()) win
->stick();
762 if (! win
->isVisible()) win
->show();
765 } else if (e
->xclient
.message_type
==
766 xatom
->getAtom(XAtom::blackbox_change_attributes
)) {
767 // BLACKBOX_CHANGE_ATTRIBUTES
768 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
770 if (win
&& win
->validateClient()) {
772 net
.flags
= e
->xclient
.data
.l
[0];
773 net
.attrib
= e
->xclient
.data
.l
[1];
774 net
.workspace
= e
->xclient
.data
.l
[2];
775 net
.stack
= e
->xclient
.data
.l
[3];
776 net
.decoration
= e
->xclient
.data
.l
[4];
778 win
->changeBlackboxHints(&net
);
780 } else if (e
->xclient
.message_type
==
781 xatom
->getAtom(XAtom::net_number_of_desktops
)) {
782 // NET_NUMBER_OF_DESKTOPS
783 BScreen
*screen
= searchScreen(e
->xclient
.window
);
785 if (e
->xclient
.data
.l
[0] > 0)
786 screen
->changeWorkspaceCount((unsigned) e
->xclient
.data
.l
[0]);
787 } else if (e
->xclient
.message_type
==
788 xatom
->getAtom(XAtom::net_close_window
)) {
790 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
791 if (win
&& win
->validateClient())
792 win
->close(); // could this be smarter?
793 } else if (e
->xclient
.message_type
==
794 xatom
->getAtom(XAtom::net_wm_moveresize
)) {
796 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
797 if (win
&& win
->validateClient()) {
798 int x_root
= e
->xclient
.data
.l
[0],
799 y_root
= e
->xclient
.data
.l
[1];
800 if ((Atom
) e
->xclient
.data
.l
[2] ==
801 xatom
->getAtom(XAtom::net_wm_moveresize_move
)) {
802 win
->beginMove(x_root
, y_root
);
804 if ((Atom
) e
->xclient
.data
.l
[2] ==
805 xatom
->getAtom(XAtom::net_wm_moveresize_size_topleft
))
806 win
->beginResize(x_root
, y_root
, BlackboxWindow::TopLeft
);
807 else if ((Atom
) e
->xclient
.data
.l
[2] ==
808 xatom
->getAtom(XAtom::net_wm_moveresize_size_topright
))
809 win
->beginResize(x_root
, y_root
, BlackboxWindow::TopRight
);
810 else if ((Atom
) e
->xclient
.data
.l
[2] ==
811 xatom
->getAtom(XAtom::net_wm_moveresize_size_bottomleft
))
812 win
->beginResize(x_root
, y_root
, BlackboxWindow::BottomLeft
);
813 else if ((Atom
) e
->xclient
.data
.l
[2] ==
814 xatom
->getAtom(XAtom::net_wm_moveresize_size_bottomright
))
815 win
->beginResize(x_root
, y_root
, BlackboxWindow::BottomRight
);
818 } else if (e
->xclient
.message_type
==
819 xatom
->getAtom(XAtom::net_wm_state
)) {
821 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
822 if (win
&& win
->validateClient()) {
823 const Atom action
= (Atom
) e
->xclient
.data
.l
[0];
824 const Atom state
[] = { (Atom
) e
->xclient
.data
.l
[1],
825 (Atom
) e
->xclient
.data
.l
[2] };
827 for (int i
= 0; i
< 2; ++i
) {
831 if ((Atom
) e
->xclient
.data
.l
[0] == 1) {
833 if (state
[i
] == xatom
->getAtom(XAtom::net_wm_state_modal
)) {
835 } else if (state
[i
] ==
836 xatom
->getAtom(XAtom::net_wm_state_maximized_vert
)) {
837 if (win
->isMaximizedHoriz()) {
838 win
->maximize(0); // unmaximize
839 win
->maximize(1); // full
840 } else if (! win
->isMaximized()) {
841 win
->maximize(2); // vert
843 } else if (state
[i
] ==
844 xatom
->getAtom(XAtom::net_wm_state_maximized_horz
)) {
845 if (win
->isMaximizedVert()) {
846 win
->maximize(0); // unmaximize
847 win
->maximize(1); // full
848 } else if (! win
->isMaximized()) {
849 win
->maximize(3); // horiz
851 } else if (state
[i
] ==
852 xatom
->getAtom(XAtom::net_wm_state_shaded
)) {
853 if (! win
->isShaded())
855 } else if (state
[i
] ==
856 xatom
->getAtom(XAtom::net_wm_state_skip_taskbar
)) {
857 win
->setSkipTaskbar(True
);
858 } else if (state
[i
] ==
859 xatom
->getAtom(XAtom::net_wm_state_skip_pager
)) {
860 win
->setSkipPager(True
);
861 } else if (state
[i
] ==
862 xatom
->getAtom(XAtom::net_wm_state_fullscreen
)) {
863 win
->setFullscreen(True
);
865 } else if (action
== 0) {
867 if (state
[i
] == xatom
->getAtom(XAtom::net_wm_state_modal
)) {
868 win
->setModal(False
);
869 } else if (state
[i
] ==
870 xatom
->getAtom(XAtom::net_wm_state_maximized_vert
)) {
871 if (win
->isMaximizedFull()) {
872 win
->maximize(0); // unmaximize
873 win
->maximize(3); // horiz
874 } else if (win
->isMaximizedVert()) {
875 win
->maximize(0); // unmaximize
877 } else if (state
[i
] ==
878 xatom
->getAtom(XAtom::net_wm_state_maximized_horz
)) {
879 if (win
->isMaximizedFull()) {
880 win
->maximize(0); // unmaximize
881 win
->maximize(2); // vert
882 } else if (win
->isMaximizedHoriz()) {
883 win
->maximize(0); // unmaximize
885 } else if (state
[i
] ==
886 xatom
->getAtom(XAtom::net_wm_state_shaded
)) {
889 } else if (state
[i
] ==
890 xatom
->getAtom(XAtom::net_wm_state_skip_taskbar
)) {
891 win
->setSkipTaskbar(False
);
892 } else if (state
[i
] ==
893 xatom
->getAtom(XAtom::net_wm_state_skip_pager
)) {
894 win
->setSkipPager(False
);
895 } else if (state
[i
] ==
896 xatom
->getAtom(XAtom::net_wm_state_fullscreen
)) {
897 win
->setFullscreen(False
);
899 } else if (action
== 2) {
901 if (state
[i
] == xatom
->getAtom(XAtom::net_wm_state_modal
)) {
902 win
->setModal(! win
->isModal());
903 } else if (state
[i
] ==
904 xatom
->getAtom(XAtom::net_wm_state_maximized_vert
)) {
905 if (win
->isMaximizedFull()) {
906 win
->maximize(0); // unmaximize
907 win
->maximize(3); // horiz
908 } else if (win
->isMaximizedVert()) {
909 win
->maximize(0); // unmaximize
910 } else if (win
->isMaximizedHoriz()) {
911 win
->maximize(0); // unmaximize
912 win
->maximize(1); // full
914 win
->maximize(2); // vert
916 } else if (state
[i
] ==
917 xatom
->getAtom(XAtom::net_wm_state_maximized_horz
)) {
918 if (win
->isMaximizedFull()) {
919 win
->maximize(0); // unmaximize
920 win
->maximize(2); // vert
921 } else if (win
->isMaximizedHoriz()) {
922 win
->maximize(0); // unmaximize
923 } else if (win
->isMaximizedVert()) {
924 win
->maximize(0); // unmaximize
925 win
->maximize(1); // full
927 win
->maximize(3); // horiz
929 } else if (state
[i
] ==
930 xatom
->getAtom(XAtom::net_wm_state_shaded
)) {
932 } else if (state
[i
] ==
933 xatom
->getAtom(XAtom::net_wm_state_skip_taskbar
)) {
934 win
->setSkipTaskbar(! win
->skipTaskbar());
935 } else if (state
[i
] ==
936 xatom
->getAtom(XAtom::net_wm_state_skip_pager
)) {
937 win
->setSkipPager(! win
->skipPager());
938 } else if (state
[i
] ==
939 xatom
->getAtom(XAtom::net_wm_state_fullscreen
)) {
940 win
->setFullscreen(! win
->isFullscreen());
952 case ConfigureNotify
:
954 break; // not handled, just ignore
958 if (e
->type
== getShapeEventBase()) {
959 XShapeEvent
*shape_event
= (XShapeEvent
*) e
;
960 BlackboxWindow
*win
= searchWindow(e
->xany
.window
);
963 win
->shapeEvent(shape_event
);
971 bool Blackbox::handleSignal(int sig
) {
1000 bool Blackbox::validateWindow(Window window
) {
1002 if (XCheckTypedWindowEvent(getXDisplay(), window
, DestroyNotify
, &event
)) {
1003 XPutBackEvent(getXDisplay(), &event
);
1012 BScreen
*Blackbox::searchScreen(Window window
) {
1013 ScreenList::iterator it
= screenList
.begin();
1015 for (; it
!= screenList
.end(); ++it
) {
1017 if (s
->getRootWindow() == window
)
1021 return (BScreen
*) 0;
1025 BScreen
*Blackbox::searchSystrayWindow(Window window
) {
1026 WindowScreenLookup::iterator it
= systraySearchList
.find(window
);
1027 if (it
!= systraySearchList
.end())
1030 return (BScreen
*) 0;
1034 BlackboxWindow
*Blackbox::searchWindow(Window window
) {
1035 WindowLookup::iterator it
= windowSearchList
.find(window
);
1036 if (it
!= windowSearchList
.end())
1039 return (BlackboxWindow
*) 0;
1043 BWindowGroup
*Blackbox::searchGroup(Window window
) {
1044 GroupLookup::iterator it
= groupSearchList
.find(window
);
1045 if (it
!= groupSearchList
.end())
1048 return (BWindowGroup
*) 0;
1052 Basemenu
*Blackbox::searchMenu(Window window
) {
1053 MenuLookup::iterator it
= menuSearchList
.find(window
);
1054 if (it
!= menuSearchList
.end())
1057 return (Basemenu
*) 0;
1061 Toolbar
*Blackbox::searchToolbar(Window window
) {
1062 ToolbarLookup::iterator it
= toolbarSearchList
.find(window
);
1063 if (it
!= toolbarSearchList
.end())
1066 return (Toolbar
*) 0;
1070 Slit
*Blackbox::searchSlit(Window window
) {
1071 SlitLookup::iterator it
= slitSearchList
.find(window
);
1072 if (it
!= slitSearchList
.end())
1079 void Blackbox::saveSystrayWindowSearch(Window window
, BScreen
*screen
) {
1080 systraySearchList
.insert(WindowScreenLookupPair(window
, screen
));
1084 void Blackbox::saveWindowSearch(Window window
, BlackboxWindow
*data
) {
1085 windowSearchList
.insert(WindowLookupPair(window
, data
));
1089 void Blackbox::saveGroupSearch(Window window
, BWindowGroup
*data
) {
1090 groupSearchList
.insert(GroupLookupPair(window
, data
));
1094 void Blackbox::saveMenuSearch(Window window
, Basemenu
*data
) {
1095 menuSearchList
.insert(MenuLookupPair(window
, data
));
1099 void Blackbox::saveToolbarSearch(Window window
, Toolbar
*data
) {
1100 toolbarSearchList
.insert(ToolbarLookupPair(window
, data
));
1104 void Blackbox::saveSlitSearch(Window window
, Slit
*data
) {
1105 slitSearchList
.insert(SlitLookupPair(window
, data
));
1109 void Blackbox::removeSystrayWindowSearch(Window window
) {
1110 systraySearchList
.erase(window
);
1114 void Blackbox::removeWindowSearch(Window window
) {
1115 windowSearchList
.erase(window
);
1119 void Blackbox::removeGroupSearch(Window window
) {
1120 groupSearchList
.erase(window
);
1124 void Blackbox::removeMenuSearch(Window window
) {
1125 menuSearchList
.erase(window
);
1129 void Blackbox::removeToolbarSearch(Window window
) {
1130 toolbarSearchList
.erase(window
);
1134 void Blackbox::removeSlitSearch(Window window
) {
1135 slitSearchList
.erase(window
);
1139 void Blackbox::restart(const char *prog
) {
1143 putenv(const_cast<char *>(screenList
.front()->displayString().c_str()));
1144 execlp(prog
, prog
, NULL
);
1148 // fall back in case the above execlp doesn't work
1149 execvp(argv
[0], argv
);
1150 string name
= basename(argv
[0]);
1151 execvp(name
.c_str(), argv
);
1155 void Blackbox::shutdown(void) {
1156 BaseDisplay::shutdown();
1158 XSetInputFocus(getXDisplay(), PointerRoot
, None
, CurrentTime
);
1160 std::for_each(screenList
.begin(), screenList
.end(),
1161 std::mem_fun(&BScreen::shutdown
));
1163 XSync(getXDisplay(), False
);
1168 void Blackbox::saveXineramaPlacement(bool x
) {
1169 resource
.xinerama_placement
= x
;
1170 config
.setValue("session.xineramaSupport.windowPlacement",
1171 resource
.xinerama_placement
);
1172 reconfigure(); // make sure all screens get this change
1176 void Blackbox::saveXineramaMaximizing(bool x
) {
1177 resource
.xinerama_maximize
= x
;
1178 config
.setValue("session.xineramaSupport.windowMaximizing",
1179 resource
.xinerama_maximize
);
1180 reconfigure(); // make sure all screens get this change
1184 void Blackbox::saveXineramaSnapping(bool x
) {
1185 resource
.xinerama_snap
= x
;
1186 config
.setValue("session.xineramaSupport.windowSnapping",
1187 resource
.xinerama_snap
);
1188 reconfigure(); // make sure all screens get this change
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
);
1212 saveXineramaPlacement(resource
.xinerama_placement
);
1213 saveXineramaMaximizing(resource
.xinerama_maximize
);
1214 saveXineramaSnapping(resource
.xinerama_snap
);
1217 std::for_each(screenList
.begin(), screenList
.end(),
1218 std::mem_fun(&BScreen::save_rc
));
1220 config
.setAutoSave(true);
1225 void Blackbox::load_rc(void) {
1226 if (! config
.load())
1231 if (! config
.getValue("session.colorsPerChannel",
1232 resource
.colors_per_channel
))
1233 resource
.colors_per_channel
= 4;
1234 if (resource
.colors_per_channel
< 2) resource
.colors_per_channel
= 2;
1235 else if (resource
.colors_per_channel
> 6) resource
.colors_per_channel
= 6;
1237 if (config
.getValue("session.styleFile", s
))
1238 resource
.style_file
= expandTilde(s
);
1240 resource
.style_file
= DEFAULTSTYLE
;
1242 if (! config
.getValue("session.doubleClickInterval",
1243 resource
.double_click_interval
));
1244 resource
.double_click_interval
= 250;
1246 if (! config
.getValue("session.autoRaiseDelay",
1247 resource
.auto_raise_delay
.tv_usec
))
1248 resource
.auto_raise_delay
.tv_usec
= 400;
1249 resource
.auto_raise_delay
.tv_sec
= resource
.auto_raise_delay
.tv_usec
/ 1000;
1250 resource
.auto_raise_delay
.tv_usec
-=
1251 (resource
.auto_raise_delay
.tv_sec
* 1000);
1252 resource
.auto_raise_delay
.tv_usec
*= 1000;
1254 if (! config
.getValue("session.cacheLife", resource
.cache_life
))
1255 resource
.cache_life
= 5;
1256 resource
.cache_life
*= 60000;
1258 if (! config
.getValue("session.cacheMax", resource
.cache_max
))
1259 resource
.cache_max
= 200;
1261 if (! config
.getValue("session.titlebarLayout", resource
.titlebar_layout
))
1262 resource
.titlebar_layout
= "ILMC";
1265 if (! config
.getValue("session.xineramaSupport.windowPlacement",
1266 resource
.xinerama_placement
))
1267 resource
.xinerama_placement
= false;
1269 if (! config
.getValue("session.xineramaSupport.windowMaximizing",
1270 resource
.xinerama_maximize
))
1271 resource
.xinerama_maximize
= false;
1273 if (! config
.getValue("session.xineramaSupport.windowSnapping",
1274 resource
.xinerama_snap
))
1275 resource
.xinerama_snap
= false;
1280 void Blackbox::reconfigure(void) {
1281 // don't reconfigure while saving the initial rc file, it's a waste and it
1282 // breaks somethings (workspace names)
1283 if (isStartup()) return;
1285 reconfigure_wait
= True
;
1287 if (! timer
->isTiming()) timer
->start();
1291 void Blackbox::real_reconfigure(void) {
1294 std::for_each(menuTimestamps
.begin(), menuTimestamps
.end(),
1296 menuTimestamps
.clear();
1300 std::for_each(screenList
.begin(), screenList
.end(),
1301 std::mem_fun(&BScreen::reconfigure
));
1305 void Blackbox::checkMenu(void) {
1306 bool reread
= False
;
1307 MenuTimestampList::iterator it
= menuTimestamps
.begin();
1308 for(; it
!= menuTimestamps
.end(); ++it
) {
1309 MenuTimestamp
*tmp
= *it
;
1312 if (! stat(tmp
->filename
.c_str(), &buf
)) {
1313 if (tmp
->timestamp
!= buf
.st_ctime
)
1320 if (reread
) rereadMenu();
1324 void Blackbox::rereadMenu(void) {
1325 reread_menu_wait
= True
;
1327 if (! timer
->isTiming()) timer
->start();
1331 void Blackbox::real_rereadMenu(void) {
1332 std::for_each(menuTimestamps
.begin(), menuTimestamps
.end(),
1334 menuTimestamps
.clear();
1336 std::for_each(screenList
.begin(), screenList
.end(),
1337 std::mem_fun(&BScreen::rereadMenu
));
1341 void Blackbox::saveStyleFilename(const string
& filename
) {
1342 assert(! filename
.empty());
1343 resource
.style_file
= filename
;
1344 config
.setValue("session.styleFile", resource
.style_file
);
1348 void Blackbox::addMenuTimestamp(const string
& filename
) {
1349 assert(! filename
.empty());
1352 MenuTimestampList::iterator it
= menuTimestamps
.begin();
1353 for (; it
!= menuTimestamps
.end() && ! found
; ++it
) {
1354 if ((*it
)->filename
== filename
) found
= True
;
1359 if (! stat(filename
.c_str(), &buf
)) {
1360 MenuTimestamp
*ts
= new MenuTimestamp
;
1362 ts
->filename
= filename
;
1363 ts
->timestamp
= buf
.st_ctime
;
1365 menuTimestamps
.push_back(ts
);
1371 void Blackbox::timeout(void) {
1372 if (reconfigure_wait
)
1375 if (reread_menu_wait
)
1378 reconfigure_wait
= reread_menu_wait
= False
;
1382 void Blackbox::setChangingWindow(BlackboxWindow
*win
) {
1383 // make sure one of the two is null and the other isn't
1384 assert((! changing_window
&& win
) || (! win
&& changing_window
));
1385 changing_window
= win
;
1389 void Blackbox::setFocusedWindow(BlackboxWindow
*win
) {
1390 if (focused_window
&& focused_window
== win
) // nothing to do
1393 BScreen
*old_screen
= 0;
1395 if (focused_window
) {
1396 focused_window
->setFocusFlag(False
);
1397 old_screen
= focused_window
->getScreen();
1400 if (win
&& ! win
->isIconic()) {
1401 // the active screen is the one with the last focused window...
1402 // this will keep focus on this screen no matter where the mouse goes,
1403 // so multihead keybindings will continue to work on that screen until the
1404 // user focuses a window on a different screen.
1405 active_screen
= win
->getScreen();
1406 focused_window
= win
;
1410 if (active_screen
) {
1411 // set input focus to the toolbar of the screen with mouse
1412 XSetInputFocus(getXDisplay(),
1413 active_screen
->getRootWindow(),
1414 RevertToPointerRoot
, CurrentTime
);
1416 // set input focus to the toolbar of the first managed screen
1417 XSetInputFocus(getXDisplay(),
1418 screenList
.front()->getRootWindow(),
1419 RevertToPointerRoot
, CurrentTime
);
1422 // set input focus to the toolbar of the last screen
1423 XSetInputFocus(getXDisplay(), old_screen
->getRootWindow(),
1424 RevertToPointerRoot
, CurrentTime
);
1428 if (active_screen
&& active_screen
->isScreenManaged()) {
1429 active_screen
->getToolbar()->redrawWindowLabel(True
);
1430 active_screen
->updateNetizenWindowFocus();
1433 if (old_screen
&& old_screen
!= active_screen
) {
1434 old_screen
->getToolbar()->redrawWindowLabel(True
);
1435 old_screen
->updateNetizenWindowFocus();