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"
116 // X event scanner for enter/leave notifies - adapted from twm
119 bool leave
, inferior
, enter
;
122 static Bool
queueScanner(Display
*, XEvent
*e
, char *args
) {
123 scanargs
*scan
= (scanargs
*) args
;
124 if ((e
->type
== LeaveNotify
) &&
125 (e
->xcrossing
.window
== scan
->w
) &&
126 (e
->xcrossing
.mode
== NotifyNormal
)) {
128 scan
->inferior
= (e
->xcrossing
.detail
== NotifyInferior
);
129 } else if ((e
->type
== EnterNotify
) && (e
->xcrossing
.mode
== NotifyUngrab
)) {
139 Blackbox::Blackbox(char **m_argv
, char *dpy_name
, char *rc
, char *menu
)
140 : BaseDisplay(m_argv
[0], dpy_name
) {
141 if (! XSupportsLocale())
142 fprintf(stderr
, "X server does not support locale\n");
144 if (XSetLocaleModifiers("") == NULL
)
145 fprintf(stderr
, "cannot set locale modifiers\n");
149 if (! rc
) rc
= "~/.openbox/rc";
150 rc_file
= expandTilde(rc
);
151 config
.setFile(rc_file
);
152 if (! menu
) menu
= "~/.openbox/menu";
153 menu_file
= expandTilde(menu
);
157 resource
.auto_raise_delay
.tv_sec
= resource
.auto_raise_delay
.tv_usec
= 0;
160 focused_window
= changing_window
= (BlackboxWindow
*) 0;
165 xatom
= new XAtom(getXDisplay());
166 input
= new BInput(this);
168 cursor
.session
= XCreateFontCursor(getXDisplay(), XC_left_ptr
);
169 cursor
.move
= XCreateFontCursor(getXDisplay(), XC_fleur
);
170 cursor
.ll_angle
= XCreateFontCursor(getXDisplay(), XC_ll_angle
);
171 cursor
.lr_angle
= XCreateFontCursor(getXDisplay(), XC_lr_angle
);
172 cursor
.ul_angle
= XCreateFontCursor(getXDisplay(), XC_ul_angle
);
173 cursor
.ur_angle
= XCreateFontCursor(getXDisplay(), XC_ur_angle
);
175 for (unsigned int i
= 0; i
< getNumberOfScreens(); i
++) {
176 BScreen
*screen
= new BScreen(this, i
);
178 if (! screen
->isScreenManaged()) {
183 screenList
.push_back(screen
);
186 if (screenList
.empty()) {
188 i18n(blackboxSet
, blackboxNoManagableScreens
,
189 "Blackbox::Blackbox: no managable screens found, aborting.\n"));
193 // save current settings and default values
196 // set the screen with mouse to the first managed screen
197 active_screen
= screenList
.front();
200 XSynchronize(getXDisplay(), False
);
201 XSync(getXDisplay(), False
);
203 reconfigure_wait
= reread_menu_wait
= False
;
205 timer
= new BTimer(this, this);
206 timer
->setTimeout(0l);
210 Blackbox::~Blackbox(void) {
211 std::for_each(screenList
.begin(), screenList
.end(), PointerAssassin());
213 std::for_each(menuTimestamps
.begin(), menuTimestamps
.end(),
222 void Blackbox::process_event(XEvent
*e
) {
225 // strip the lock key modifiers
226 e
->xbutton
.state
&= ~(NumLockMask
| ScrollLockMask
| LockMask
);
228 last_time
= e
->xbutton
.time
;
230 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
231 Basemenu
*menu
= (Basemenu
*) 0;
232 Slit
*slit
= (Slit
*) 0;
233 Toolbar
*tbar
= (Toolbar
*) 0;
234 BScreen
*scrn
= (BScreen
*) 0;
236 if ((win
= searchWindow(e
->xbutton
.window
))) {
237 win
->buttonPressEvent(&e
->xbutton
);
239 /* XXX: is this sane on low colour desktops? */
240 if (e
->xbutton
.button
== 1)
241 win
->installColormap(True
);
242 } else if ((menu
= searchMenu(e
->xbutton
.window
))) {
243 menu
->buttonPressEvent(&e
->xbutton
);
244 } else if ((slit
= searchSlit(e
->xbutton
.window
))) {
245 slit
->buttonPressEvent(&e
->xbutton
);
246 } else if ((tbar
= searchToolbar(e
->xbutton
.window
))) {
247 tbar
->buttonPressEvent(&e
->xbutton
);
248 } else if ((scrn
= searchScreen(e
->xbutton
.window
))) {
249 scrn
->buttonPressEvent(&e
->xbutton
);
250 if (active_screen
!= scrn
) {
251 active_screen
= scrn
;
252 // first, set no focus window on the old screen
254 // and move focus to this screen
261 case ButtonRelease
: {
262 // strip the lock key modifiers
263 e
->xbutton
.state
&= ~(NumLockMask
| ScrollLockMask
| LockMask
);
265 last_time
= e
->xbutton
.time
;
267 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
268 Basemenu
*menu
= (Basemenu
*) 0;
269 Toolbar
*tbar
= (Toolbar
*) 0;
271 if ((win
= searchWindow(e
->xbutton
.window
)))
272 win
->buttonReleaseEvent(&e
->xbutton
);
273 else if ((menu
= searchMenu(e
->xbutton
.window
)))
274 menu
->buttonReleaseEvent(&e
->xbutton
);
275 else if ((tbar
= searchToolbar(e
->xbutton
.window
)))
276 tbar
->buttonReleaseEvent(&e
->xbutton
);
281 case ConfigureRequest
: {
282 // compress configure requests...
285 while(XCheckTypedWindowEvent(getXDisplay(), e
->xconfigurerequest
.window
,
286 ConfigureRequest
, &realevent
)) {
292 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
293 Slit
*slit
= (Slit
*) 0;
295 if ((win
= searchWindow(e
->xconfigurerequest
.window
))) {
296 win
->configureRequestEvent(&e
->xconfigurerequest
);
297 } else if ((slit
= searchSlit(e
->xconfigurerequest
.window
))) {
298 slit
->configureRequestEvent(&e
->xconfigurerequest
);
300 if (validateWindow(e
->xconfigurerequest
.window
)) {
303 xwc
.x
= e
->xconfigurerequest
.x
;
304 xwc
.y
= e
->xconfigurerequest
.y
;
305 xwc
.width
= e
->xconfigurerequest
.width
;
306 xwc
.height
= e
->xconfigurerequest
.height
;
307 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
308 xwc
.sibling
= e
->xconfigurerequest
.above
;
309 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
311 XConfigureWindow(getXDisplay(), e
->xconfigurerequest
.window
,
312 e
->xconfigurerequest
.value_mask
, &xwc
);
321 fprintf(stderr
, "Blackbox::process_event(): MapRequest for 0x%lx\n",
322 e
->xmaprequest
.window
);
325 BlackboxWindow
*win
= searchWindow(e
->xmaprequest
.window
);
328 BScreen
*screen
= searchScreen(e
->xmaprequest
.parent
);
332 we got a map request for a window who's parent isn't root. this
333 can happen in only one circumstance:
335 a client window unmapped a managed window, and then remapped it
336 somewhere between unmapping the client window and reparenting it
339 regardless of how it happens, we need to find the screen that
342 XWindowAttributes wattrib
;
343 if (! XGetWindowAttributes(getXDisplay(), e
->xmaprequest
.window
,
345 // failed to get the window attributes, perhaps the window has
346 // now been destroyed?
350 screen
= searchScreen(wattrib
.root
);
351 assert(screen
!= 0); // this should never happen
354 screen
->manageWindow(e
->xmaprequest
.window
);
361 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
362 Slit
*slit
= (Slit
*) 0;
363 BScreen
*screen
= (BScreen
*) 0;
365 if ((win
= searchWindow(e
->xunmap
.window
))) {
366 win
->unmapNotifyEvent(&e
->xunmap
);
367 } else if ((slit
= searchSlit(e
->xunmap
.window
))) {
368 slit
->unmapNotifyEvent(&e
->xunmap
);
369 } else if ((screen
= searchSystrayWindow(e
->xunmap
.window
))) {
370 screen
->removeSystrayWindow(e
->xunmap
.window
);
371 } else if ((screen
= searchDesktopWindow(e
->xunmap
.window
))) {
372 screen
->removeDesktopWindow(e
->xunmap
.window
);
378 case DestroyNotify
: {
379 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
380 Slit
*slit
= (Slit
*) 0;
381 BScreen
*screen
= (BScreen
*) 0;
382 BWindowGroup
*group
= (BWindowGroup
*) 0;
384 if ((win
= searchWindow(e
->xdestroywindow
.window
))) {
385 win
->destroyNotifyEvent(&e
->xdestroywindow
);
386 } else if ((slit
= searchSlit(e
->xdestroywindow
.window
))) {
387 slit
->removeClient(e
->xdestroywindow
.window
, False
);
388 } else if ((group
= searchGroup(e
->xdestroywindow
.window
))) {
390 } else if ((screen
= searchSystrayWindow(e
->xunmap
.window
))) {
391 screen
->removeSystrayWindow(e
->xunmap
.window
);
392 } else if ((screen
= searchDesktopWindow(e
->xunmap
.window
))) {
393 screen
->removeDesktopWindow(e
->xunmap
.window
);
399 case ReparentNotify
: {
401 this event is quite rare and is usually handled in unmapNotify
402 however, if the window is unmapped when the reparent event occurs
403 the window manager never sees it because an unmap event is not sent
404 to an already unmapped window.
406 BlackboxWindow
*win
= searchWindow(e
->xreparent
.window
);
408 win
->reparentNotifyEvent(&e
->xreparent
);
410 Slit
*slit
= searchSlit(e
->xreparent
.window
);
411 if (slit
&& slit
->getWindowID() != e
->xreparent
.parent
)
412 slit
->removeClient(e
->xreparent
.window
, True
);
418 // motion notify compression...
421 while (XCheckTypedWindowEvent(getXDisplay(), e
->xmotion
.window
,
422 MotionNotify
, &realevent
)) {
426 // if we have compressed some motion events, use the last one
430 // strip the lock key modifiers
431 e
->xbutton
.state
&= ~(NumLockMask
| ScrollLockMask
| LockMask
);
433 last_time
= e
->xmotion
.time
;
435 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
436 Basemenu
*menu
= (Basemenu
*) 0;
438 if ((win
= searchWindow(e
->xmotion
.window
)))
439 win
->motionNotifyEvent(&e
->xmotion
);
440 else if ((menu
= searchMenu(e
->xmotion
.window
)))
441 menu
->motionNotifyEvent(&e
->xmotion
);
446 case PropertyNotify
: {
447 last_time
= e
->xproperty
.time
;
449 if (e
->xproperty
.state
!= PropertyDelete
) {
450 BlackboxWindow
*win
= searchWindow(e
->xproperty
.window
);
453 win
->propertyNotifyEvent(e
->xproperty
.atom
);
460 last_time
= e
->xcrossing
.time
;
462 BScreen
*screen
= (BScreen
*) 0;
463 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
464 Basemenu
*menu
= (Basemenu
*) 0;
465 Toolbar
*tbar
= (Toolbar
*) 0;
466 Slit
*slit
= (Slit
*) 0;
468 if (e
->xcrossing
.mode
== NotifyGrab
) break;
472 sa
.w
= e
->xcrossing
.window
;
473 sa
.enter
= sa
.leave
= False
;
474 XCheckIfEvent(getXDisplay(), &dummy
, queueScanner
, (char *) &sa
);
476 if ((e
->xcrossing
.window
== e
->xcrossing
.root
) &&
477 (screen
= searchScreen(e
->xcrossing
.window
))) {
478 screen
->getImageControl()->installRootColormap();
479 } else if ((win
= searchWindow(e
->xcrossing
.window
))) {
480 if (win
->getScreen()->isSloppyFocus() &&
481 (! win
->isFocused()) && (! no_focus
)) {
482 if (((! sa
.leave
) || sa
.inferior
) && win
->isVisible()) {
483 if (win
->setInputFocus())
484 win
->installColormap(True
); // XXX: shouldnt we honour no install?
487 } else if ((menu
= searchMenu(e
->xcrossing
.window
))) {
488 menu
->enterNotifyEvent(&e
->xcrossing
);
489 } else if ((tbar
= searchToolbar(e
->xcrossing
.window
))) {
490 tbar
->enterNotifyEvent(&e
->xcrossing
);
491 } else if ((slit
= searchSlit(e
->xcrossing
.window
))) {
492 slit
->enterNotifyEvent(&e
->xcrossing
);
498 last_time
= e
->xcrossing
.time
;
500 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
501 Basemenu
*menu
= (Basemenu
*) 0;
502 Toolbar
*tbar
= (Toolbar
*) 0;
503 Slit
*slit
= (Slit
*) 0;
505 if ((menu
= searchMenu(e
->xcrossing
.window
)))
506 menu
->leaveNotifyEvent(&e
->xcrossing
);
507 else if ((win
= searchWindow(e
->xcrossing
.window
)))
508 win
->installColormap(False
);
509 else if ((tbar
= searchToolbar(e
->xcrossing
.window
)))
510 tbar
->leaveNotifyEvent(&e
->xcrossing
);
511 else if ((slit
= searchSlit(e
->xcrossing
.window
)))
512 slit
->leaveNotifyEvent(&e
->xcrossing
);
517 // compress expose events
520 int ex1
, ey1
, ex2
, ey2
;
523 ex2
= ex1
+ e
->xexpose
.width
- 1;
524 ey2
= ey1
+ e
->xexpose
.height
- 1;
525 while (XCheckTypedWindowEvent(getXDisplay(), e
->xexpose
.window
,
526 Expose
, &realevent
)) {
530 ex1
= std::min(realevent
.xexpose
.x
, ex1
);
531 ey1
= std::min(realevent
.xexpose
.y
, ey1
);
532 ex2
= std::max(realevent
.xexpose
.x
+ realevent
.xexpose
.width
- 1, ex2
);
533 ey2
= std::max(realevent
.xexpose
.y
+ realevent
.xexpose
.height
- 1, ey2
);
538 // use the merged area
541 e
->xexpose
.width
= ex2
- ex1
+ 1;
542 e
->xexpose
.height
= ey2
- ey1
+ 1;
544 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
545 Basemenu
*menu
= (Basemenu
*) 0;
546 Toolbar
*tbar
= (Toolbar
*) 0;
548 if ((win
= searchWindow(e
->xexpose
.window
)))
549 win
->exposeEvent(&e
->xexpose
);
550 else if ((menu
= searchMenu(e
->xexpose
.window
)))
551 menu
->exposeEvent(&e
->xexpose
);
552 else if ((tbar
= searchToolbar(e
->xexpose
.window
)))
553 tbar
->exposeEvent(&e
->xexpose
);
559 Toolbar
*tbar
= searchToolbar(e
->xkey
.window
);
561 if (tbar
&& tbar
->isEditing())
562 tbar
->keyPressEvent(&e
->xkey
);
567 case ColormapNotify
: {
568 BScreen
*screen
= searchScreen(e
->xcolormap
.window
);
571 screen
->setRootColormapInstalled((e
->xcolormap
.state
==
572 ColormapInstalled
) ? True
: False
);
578 if (e
->xfocus
.detail
!= NotifyNonlinear
&&
579 e
->xfocus
.detail
!= NotifyAncestor
) {
581 don't process FocusIns when:
582 1. the new focus window isn't an ancestor or inferior of the old
583 focus window (NotifyNonlinear)
584 make sure to allow the FocusIn when the old focus window was an
585 ancestor but didn't have a parent, such as root (NotifyAncestor)
590 BlackboxWindow
*win
= searchWindow(e
->xfocus
.window
);
592 if (! win
->isFocused())
593 win
->setFocusFlag(True
);
596 set the event window to None. when the FocusOut event handler calls
597 this function recursively, it uses this as an indication that focus
598 has moved to a known window.
600 e
->xfocus
.window
= None
;
607 if (e
->xfocus
.detail
!= NotifyNonlinear
) {
609 don't process FocusOuts when:
610 2. the new focus window isn't an ancestor or inferior of the old
611 focus window (NotifyNonlinear)
616 BlackboxWindow
*win
= searchWindow(e
->xfocus
.window
);
617 if (win
&& win
->isFocused()) {
619 before we mark "win" as unfocused, we need to verify that focus is
620 going to a known location, is in a known location, or set focus
625 // don't check the current focus if FocusOut was generated during a grab
626 bool check_focus
= (e
->xfocus
.mode
== NotifyNormal
);
629 First, check if there is a pending FocusIn event waiting. if there
630 is, process it and determine if focus has moved to another window
631 (the FocusIn event handler sets the window in the event
632 structure to None to indicate this).
634 if (XCheckTypedEvent(getXDisplay(), FocusIn
, &event
)) {
636 process_event(&event
);
637 if (event
.xfocus
.window
== None
) {
645 Second, we query the X server for the current input focus.
646 to make sure that we keep a consistent state.
648 BlackboxWindow
*focus
;
651 XGetInputFocus(getXDisplay(), &w
, &revert
);
652 focus
= searchWindow(w
);
655 focus got from "win" to "focus" under some very strange
656 circumstances, and we need to make sure that the focus indication
659 setFocusedWindow(focus
);
661 // we have no idea where focus went... so we set it to somewhere
670 case ClientMessage
: {
671 if (e
->xclient
.format
== 32) {
672 if (e
->xclient
.message_type
== xatom
->getAtom(XAtom::wm_change_state
)) {
673 // WM_CHANGE_STATE message
674 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
675 if (! win
|| ! win
->validateClient()) return;
677 if (e
->xclient
.data
.l
[0] == IconicState
)
679 if (e
->xclient
.data
.l
[0] == NormalState
)
681 } else if (e
->xclient
.message_type
==
682 xatom
->getAtom(XAtom::blackbox_change_workspace
) ||
683 e
->xclient
.message_type
==
684 xatom
->getAtom(XAtom::net_current_desktop
)) {
685 // NET_CURRENT_DESKTOP message
686 BScreen
*screen
= searchScreen(e
->xclient
.window
);
688 unsigned int workspace
= e
->xclient
.data
.l
[0];
689 if (screen
&& workspace
< screen
->getWorkspaceCount())
690 screen
->changeWorkspaceID(workspace
);
691 } else if (e
->xclient
.message_type
==
692 xatom
->getAtom(XAtom::blackbox_change_window_focus
) ||
693 e
->xclient
.message_type
==
694 xatom
->getAtom(XAtom::net_active_window
)) {
696 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
700 win
->deiconify(False
, True
);
701 if (win
->isVisible() && win
->setInputFocus()) {
702 //win->getScreen()->getWorkspace(win->getWorkspaceNumber())->
704 win
->installColormap(True
);
707 } else if (e
->xclient
.message_type
==
708 xatom
->getAtom(XAtom::blackbox_cycle_window_focus
)) {
709 // BLACKBOX_CYCLE_WINDOW_FOCUS
710 BScreen
*screen
= searchScreen(e
->xclient
.window
);
713 if (! e
->xclient
.data
.l
[0])
718 } else if (e
->xclient
.message_type
==
719 xatom
->getAtom(XAtom::net_wm_desktop
)) {
721 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
724 BScreen
*screen
= win
->getScreen();
725 unsigned long wksp
= (unsigned) e
->xclient
.data
.l
[0];
726 if (wksp
< screen
->getWorkspaceCount()) {
727 if (win
->isIconic()) win
->deiconify(False
, True
);
728 if (win
->isStuck()) win
->stick();
729 if (wksp
!= screen
->getCurrentWorkspaceID())
733 screen
->reassociateWindow(win
, wksp
, True
);
734 } else if (wksp
== 0xfffffffe) { // XXX: BUG, BUT DOING THIS SO KDE WORKS FOR NOW!!
735 if (win
->isIconic()) win
->deiconify(False
, True
);
736 if (! win
->isStuck()) win
->stick();
737 if (! win
->isVisible()) win
->show();
740 } else if (e
->xclient
.message_type
==
741 xatom
->getAtom(XAtom::blackbox_change_attributes
)) {
742 // BLACKBOX_CHANGE_ATTRIBUTES
743 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
745 if (win
&& win
->validateClient()) {
747 net
.flags
= e
->xclient
.data
.l
[0];
748 net
.attrib
= e
->xclient
.data
.l
[1];
749 net
.workspace
= e
->xclient
.data
.l
[2];
750 net
.stack
= e
->xclient
.data
.l
[3];
751 net
.decoration
= e
->xclient
.data
.l
[4];
753 win
->changeBlackboxHints(&net
);
755 } else if (e
->xclient
.message_type
==
756 xatom
->getAtom(XAtom::net_number_of_desktops
)) {
757 // NET_NUMBER_OF_DESKTOPS
758 BScreen
*screen
= searchScreen(e
->xclient
.window
);
760 if (e
->xclient
.data
.l
[0] > 0) {
761 if ((unsigned) e
->xclient
.data
.l
[0] < screen
->getWorkspaceCount()) {
763 for (int i
= screen
->getWorkspaceCount();
764 i
> e
->xclient
.data
.l
[0]; --i
)
765 screen
->removeLastWorkspace();
766 // removeLast already sets the current workspace to the
767 // last available one.
768 } else if ((unsigned) e
->xclient
.data
.l
[0] >
769 screen
->getWorkspaceCount()) {
771 for(int i
= screen
->getWorkspaceCount();
772 i
< e
->xclient
.data
.l
[0]; ++i
)
773 screen
->addWorkspace();
776 } else if (e
->xclient
.message_type
==
777 xatom
->getAtom(XAtom::net_close_window
)) {
779 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
780 if (win
&& win
->validateClient())
781 win
->close(); // could this be smarter?
782 } else if (e
->xclient
.message_type
==
783 xatom
->getAtom(XAtom::net_wm_moveresize
)) {
785 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
786 if (win
&& win
->validateClient()) {
787 int x_root
= e
->xclient
.data
.l
[0],
788 y_root
= e
->xclient
.data
.l
[1];
789 if ((Atom
) e
->xclient
.data
.l
[2] ==
790 xatom
->getAtom(XAtom::net_wm_moveresize_move
)) {
791 win
->beginMove(x_root
, y_root
);
793 if ((Atom
) e
->xclient
.data
.l
[2] ==
794 xatom
->getAtom(XAtom::net_wm_moveresize_size_topleft
))
795 win
->beginResize(x_root
, y_root
, BlackboxWindow::TopLeft
);
796 else if ((Atom
) e
->xclient
.data
.l
[2] ==
797 xatom
->getAtom(XAtom::net_wm_moveresize_size_topright
))
798 win
->beginResize(x_root
, y_root
, BlackboxWindow::TopRight
);
799 else if ((Atom
) e
->xclient
.data
.l
[2] ==
800 xatom
->getAtom(XAtom::net_wm_moveresize_size_bottomleft
))
801 win
->beginResize(x_root
, y_root
, BlackboxWindow::BottomLeft
);
802 else if ((Atom
) e
->xclient
.data
.l
[2] ==
803 xatom
->getAtom(XAtom::net_wm_moveresize_size_bottomright
))
804 win
->beginResize(x_root
, y_root
, BlackboxWindow::BottomRight
);
807 } else if (e
->xclient
.message_type
==
808 xatom
->getAtom(XAtom::net_wm_state
)) {
810 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
811 if (win
&& win
->validateClient()) {
812 const Atom action
= (Atom
) e
->xclient
.data
.l
[0];
813 const Atom state
[] = { (Atom
) e
->xclient
.data
.l
[1],
814 (Atom
) e
->xclient
.data
.l
[2] };
816 for (int i
= 0; i
< 2; ++i
) {
820 if ((Atom
) e
->xclient
.data
.l
[0] == 1) {
822 if (state
[i
] == xatom
->getAtom(XAtom::net_wm_state_modal
)) {
824 } else if (state
[i
] ==
825 xatom
->getAtom(XAtom::net_wm_state_maximized_vert
)) {
826 if (win
->isMaximizedHoriz()) {
827 win
->maximize(0); // unmaximize
828 win
->maximize(1); // full
829 } else if (! win
->isMaximized()) {
830 win
->maximize(2); // vert
832 } else if (state
[i
] ==
833 xatom
->getAtom(XAtom::net_wm_state_maximized_horz
)) {
834 if (win
->isMaximizedVert()) {
835 win
->maximize(0); // unmaximize
836 win
->maximize(1); // full
837 } else if (! win
->isMaximized()) {
838 win
->maximize(3); // horiz
840 } else if (state
[i
] ==
841 xatom
->getAtom(XAtom::net_wm_state_shaded
)) {
842 if (! win
->isShaded())
844 } else if (state
[i
] ==
845 xatom
->getAtom(XAtom::net_wm_state_skip_taskbar
)) {
846 win
->setSkipTaskbar(True
);
847 } else if (state
[i
] ==
848 xatom
->getAtom(XAtom::net_wm_state_skip_pager
)) {
849 win
->setSkipPager(True
);
850 } else if (state
[i
] ==
851 xatom
->getAtom(XAtom::net_wm_state_fullscreen
)) {
852 win
->setFullscreen(True
);
854 } else if (action
== 0) {
856 if (state
[i
] == xatom
->getAtom(XAtom::net_wm_state_modal
)) {
857 win
->setModal(False
);
858 } else if (state
[i
] ==
859 xatom
->getAtom(XAtom::net_wm_state_maximized_vert
)) {
860 if (win
->isMaximizedFull()) {
861 win
->maximize(0); // unmaximize
862 win
->maximize(3); // horiz
863 } else if (win
->isMaximizedVert()) {
864 win
->maximize(0); // unmaximize
866 } else if (state
[i
] ==
867 xatom
->getAtom(XAtom::net_wm_state_maximized_horz
)) {
868 if (win
->isMaximizedFull()) {
869 win
->maximize(0); // unmaximize
870 win
->maximize(2); // vert
871 } else if (win
->isMaximizedHoriz()) {
872 win
->maximize(0); // unmaximize
874 } else if (state
[i
] ==
875 xatom
->getAtom(XAtom::net_wm_state_shaded
)) {
878 } else if (state
[i
] ==
879 xatom
->getAtom(XAtom::net_wm_state_skip_taskbar
)) {
880 win
->setSkipTaskbar(False
);
881 } else if (state
[i
] ==
882 xatom
->getAtom(XAtom::net_wm_state_skip_pager
)) {
883 win
->setSkipPager(False
);
884 } else if (state
[i
] ==
885 xatom
->getAtom(XAtom::net_wm_state_fullscreen
)) {
886 win
->setFullscreen(False
);
888 } else if (action
== 2) {
890 if (state
[i
] == xatom
->getAtom(XAtom::net_wm_state_modal
)) {
891 win
->setModal(! win
->isModal());
892 } else if (state
[i
] ==
893 xatom
->getAtom(XAtom::net_wm_state_maximized_vert
)) {
894 if (win
->isMaximizedFull()) {
895 win
->maximize(0); // unmaximize
896 win
->maximize(3); // horiz
897 } else if (win
->isMaximizedVert()) {
898 win
->maximize(0); // unmaximize
899 } else if (win
->isMaximizedHoriz()) {
900 win
->maximize(0); // unmaximize
901 win
->maximize(1); // full
903 win
->maximize(2); // vert
905 } else if (state
[i
] ==
906 xatom
->getAtom(XAtom::net_wm_state_maximized_horz
)) {
907 if (win
->isMaximizedFull()) {
908 win
->maximize(0); // unmaximize
909 win
->maximize(2); // vert
910 } else if (win
->isMaximizedHoriz()) {
911 win
->maximize(0); // unmaximize
912 } else if (win
->isMaximizedVert()) {
913 win
->maximize(0); // unmaximize
914 win
->maximize(1); // full
916 win
->maximize(3); // horiz
918 } else if (state
[i
] ==
919 xatom
->getAtom(XAtom::net_wm_state_shaded
)) {
921 } else if (state
[i
] ==
922 xatom
->getAtom(XAtom::net_wm_state_skip_taskbar
)) {
923 win
->setSkipTaskbar(! win
->skipTaskbar());
924 } else if (state
[i
] ==
925 xatom
->getAtom(XAtom::net_wm_state_skip_pager
)) {
926 win
->setSkipPager(! win
->skipPager());
927 } else if (state
[i
] ==
928 xatom
->getAtom(XAtom::net_wm_state_fullscreen
)) {
929 win
->setFullscreen(! win
->isFullscreen());
941 case ConfigureNotify
:
943 break; // not handled, just ignore
947 if (e
->type
== getShapeEventBase()) {
948 XShapeEvent
*shape_event
= (XShapeEvent
*) e
;
949 BlackboxWindow
*win
= searchWindow(e
->xany
.window
);
952 win
->shapeEvent(shape_event
);
960 bool Blackbox::handleSignal(int sig
) {
986 bool Blackbox::validateWindow(Window window
) {
988 if (XCheckTypedWindowEvent(getXDisplay(), window
, DestroyNotify
, &event
)) {
989 XPutBackEvent(getXDisplay(), &event
);
998 BScreen
*Blackbox::searchScreen(Window window
) {
999 ScreenList::iterator it
= screenList
.begin();
1001 for (; it
!= screenList
.end(); ++it
) {
1003 if (s
->getRootWindow() == window
)
1007 return (BScreen
*) 0;
1011 BScreen
*Blackbox::searchDesktopWindow(Window window
) {
1012 WindowScreenLookup::iterator it
= desktopSearchList
.find(window
);
1013 if (it
!= desktopSearchList
.end())
1016 return (BScreen
*) 0;
1020 BScreen
*Blackbox::searchSystrayWindow(Window window
) {
1021 WindowScreenLookup::iterator it
= systraySearchList
.find(window
);
1022 if (it
!= systraySearchList
.end())
1025 return (BScreen
*) 0;
1029 BlackboxWindow
*Blackbox::searchWindow(Window window
) {
1030 WindowLookup::iterator it
= windowSearchList
.find(window
);
1031 if (it
!= windowSearchList
.end())
1034 return (BlackboxWindow
*) 0;
1038 BWindowGroup
*Blackbox::searchGroup(Window window
) {
1039 GroupLookup::iterator it
= groupSearchList
.find(window
);
1040 if (it
!= groupSearchList
.end())
1043 return (BWindowGroup
*) 0;
1047 Basemenu
*Blackbox::searchMenu(Window window
) {
1048 MenuLookup::iterator it
= menuSearchList
.find(window
);
1049 if (it
!= menuSearchList
.end())
1052 return (Basemenu
*) 0;
1056 Toolbar
*Blackbox::searchToolbar(Window window
) {
1057 ToolbarLookup::iterator it
= toolbarSearchList
.find(window
);
1058 if (it
!= toolbarSearchList
.end())
1061 return (Toolbar
*) 0;
1065 Slit
*Blackbox::searchSlit(Window window
) {
1066 SlitLookup::iterator it
= slitSearchList
.find(window
);
1067 if (it
!= slitSearchList
.end())
1074 void Blackbox::saveDesktopWindowSearch(Window window
, BScreen
*screen
) {
1075 desktopSearchList
.insert(WindowScreenLookupPair(window
, screen
));
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::removeDesktopWindowSearch(Window window
) {
1110 desktopSearchList
.erase(window
);
1114 void Blackbox::removeSystrayWindowSearch(Window window
) {
1115 systraySearchList
.erase(window
);
1119 void Blackbox::removeWindowSearch(Window window
) {
1120 windowSearchList
.erase(window
);
1124 void Blackbox::removeGroupSearch(Window window
) {
1125 groupSearchList
.erase(window
);
1129 void Blackbox::removeMenuSearch(Window window
) {
1130 menuSearchList
.erase(window
);
1134 void Blackbox::removeToolbarSearch(Window window
) {
1135 toolbarSearchList
.erase(window
);
1139 void Blackbox::removeSlitSearch(Window window
) {
1140 slitSearchList
.erase(window
);
1144 void Blackbox::restart(const char *prog
) {
1148 execlp(prog
, prog
, NULL
);
1152 // fall back in case the above execlp doesn't work
1153 execvp(argv
[0], argv
);
1154 string name
= basename(argv
[0]);
1155 execvp(name
.c_str(), argv
);
1159 void Blackbox::shutdown(void) {
1160 BaseDisplay::shutdown();
1162 XSetInputFocus(getXDisplay(), PointerRoot
, None
, CurrentTime
);
1164 std::for_each(screenList
.begin(), screenList
.end(),
1165 std::mem_fun(&BScreen::shutdown
));
1167 XSync(getXDisplay(), False
);
1172 * Save all values as they are so that the defaults will be written to the rc
1175 void Blackbox::save_rc(void) {
1176 config
.setAutoSave(false);
1178 config
.setValue("session.colorsPerChannel", resource
.colors_per_channel
);
1179 config
.setValue("session.doubleClickInterval",
1180 resource
.double_click_interval
);
1181 config
.setValue("session.autoRaiseDelay",
1182 ((resource
.auto_raise_delay
.tv_sec
* 1000) +
1183 (resource
.auto_raise_delay
.tv_usec
/ 1000)));
1184 config
.setValue("session.cacheLife", resource
.cache_life
/ 60000);
1185 config
.setValue("session.cacheMax", resource
.cache_max
);
1186 config
.setValue("session.styleFile", resource
.style_file
);
1187 config
.setValue("session.titlebarLayout", resource
.titlebar_layout
);
1189 std::for_each(screenList
.begin(), screenList
.end(),
1190 std::mem_fun(&BScreen::save_rc
));
1192 config
.setAutoSave(true);
1197 void Blackbox::load_rc(void) {
1198 if (! config
.load())
1203 if (! config
.getValue("session.colorsPerChannel",
1204 resource
.colors_per_channel
))
1205 resource
.colors_per_channel
= 4;
1206 if (resource
.colors_per_channel
< 2) resource
.colors_per_channel
= 2;
1207 else if (resource
.colors_per_channel
> 6) resource
.colors_per_channel
= 6;
1209 if (config
.getValue("session.styleFile", s
))
1210 resource
.style_file
= expandTilde(s
);
1212 resource
.style_file
= DEFAULTSTYLE
;
1214 if (! config
.getValue("session.doubleClickInterval",
1215 resource
.double_click_interval
));
1216 resource
.double_click_interval
= 250;
1218 if (! config
.getValue("session.autoRaiseDelay",
1219 resource
.auto_raise_delay
.tv_usec
))
1220 resource
.auto_raise_delay
.tv_usec
= 400;
1221 resource
.auto_raise_delay
.tv_sec
= resource
.auto_raise_delay
.tv_usec
/ 1000;
1222 resource
.auto_raise_delay
.tv_usec
-=
1223 (resource
.auto_raise_delay
.tv_sec
* 1000);
1224 resource
.auto_raise_delay
.tv_usec
*= 1000;
1226 if (! config
.getValue("session.cacheLife", resource
.cache_life
))
1227 resource
.cache_life
= 5;
1228 resource
.cache_life
*= 60000;
1230 if (! config
.getValue("session.cacheMax", resource
.cache_max
))
1231 resource
.cache_max
= 200;
1233 if (! config
.getValue("session.titlebarLayout", resource
.titlebar_layout
))
1234 resource
.titlebar_layout
= "ILMC";
1238 void Blackbox::reconfigure(void) {
1239 reconfigure_wait
= True
;
1241 if (! timer
->isTiming()) timer
->start();
1245 void Blackbox::real_reconfigure(void) {
1248 std::for_each(menuTimestamps
.begin(), menuTimestamps
.end(),
1250 menuTimestamps
.clear();
1254 std::for_each(screenList
.begin(), screenList
.end(),
1255 std::mem_fun(&BScreen::reconfigure
));
1259 void Blackbox::checkMenu(void) {
1260 bool reread
= False
;
1261 MenuTimestampList::iterator it
= menuTimestamps
.begin();
1262 for(; it
!= menuTimestamps
.end(); ++it
) {
1263 MenuTimestamp
*tmp
= *it
;
1266 if (! stat(tmp
->filename
.c_str(), &buf
)) {
1267 if (tmp
->timestamp
!= buf
.st_ctime
)
1274 if (reread
) rereadMenu();
1278 void Blackbox::rereadMenu(void) {
1279 reread_menu_wait
= True
;
1281 if (! timer
->isTiming()) timer
->start();
1285 void Blackbox::real_rereadMenu(void) {
1286 std::for_each(menuTimestamps
.begin(), menuTimestamps
.end(),
1288 menuTimestamps
.clear();
1290 std::for_each(screenList
.begin(), screenList
.end(),
1291 std::mem_fun(&BScreen::rereadMenu
));
1295 void Blackbox::saveStyleFilename(const string
& filename
) {
1296 assert(! filename
.empty());
1297 resource
.style_file
= filename
;
1298 config
.setValue("session.styleFile", resource
.style_file
);
1302 void Blackbox::addMenuTimestamp(const string
& filename
) {
1303 assert(! filename
.empty());
1306 MenuTimestampList::iterator it
= menuTimestamps
.begin();
1307 for (; it
!= menuTimestamps
.end() && ! found
; ++it
) {
1308 if ((*it
)->filename
== filename
) found
= True
;
1313 if (! stat(filename
.c_str(), &buf
)) {
1314 MenuTimestamp
*ts
= new MenuTimestamp
;
1316 ts
->filename
= filename
;
1317 ts
->timestamp
= buf
.st_ctime
;
1319 menuTimestamps
.push_back(ts
);
1325 void Blackbox::timeout(void) {
1326 if (reconfigure_wait
)
1329 if (reread_menu_wait
)
1332 reconfigure_wait
= reread_menu_wait
= False
;
1336 void Blackbox::setChangingWindow(BlackboxWindow
*win
) {
1337 // make sure one of the two is null and the other isn't
1338 assert((! changing_window
&& win
) || (! win
&& changing_window
));
1339 changing_window
= win
;
1343 void Blackbox::setFocusedWindow(BlackboxWindow
*win
) {
1344 if (focused_window
&& focused_window
== win
) // nothing to do
1347 BScreen
*old_screen
= 0;
1349 if (focused_window
) {
1350 focused_window
->setFocusFlag(False
);
1351 old_screen
= focused_window
->getScreen();
1354 if (win
&& ! win
->isIconic()) {
1355 // the active screen is the one with the last focused window...
1356 // this will keep focus on this screen no matter where the mouse goes,
1357 // so multihead keybindings will continue to work on that screen until the
1358 // user focuses a window on a different screen.
1359 active_screen
= win
->getScreen();
1360 focused_window
= win
;
1364 if (active_screen
) {
1365 // set input focus to the toolbar of the screen with mouse
1366 XSetInputFocus(getXDisplay(),
1367 active_screen
->getRootWindow(),
1368 RevertToPointerRoot
, CurrentTime
);
1370 // set input focus to the toolbar of the first managed screen
1371 XSetInputFocus(getXDisplay(),
1372 screenList
.front()->getRootWindow(),
1373 RevertToPointerRoot
, CurrentTime
);
1376 // set input focus to the toolbar of the last screen
1377 XSetInputFocus(getXDisplay(), old_screen
->getRootWindow(),
1378 RevertToPointerRoot
, CurrentTime
);
1382 if (active_screen
&& active_screen
->isScreenManaged()) {
1383 active_screen
->getToolbar()->redrawWindowLabel(True
);
1384 active_screen
->updateNetizenWindowFocus();
1387 if (old_screen
&& old_screen
!= active_screen
) {
1388 old_screen
->getToolbar()->redrawWindowLabel(True
);
1389 old_screen
->updateNetizenWindowFocus();