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");
148 if (! rc
) rc
= "~/.openbox/rc";
149 rc_file
= expandTilde(rc
);
150 config
.setFile(rc_file
);
151 if (! menu
) menu
= "~/.openbox/menu";
152 menu_file
= expandTilde(menu
);
156 resource
.auto_raise_delay
.tv_sec
= resource
.auto_raise_delay
.tv_usec
= 0;
159 focused_window
= changing_window
= (BlackboxWindow
*) 0;
164 xatom
= new XAtom(getXDisplay());
166 cursor
.session
= XCreateFontCursor(getXDisplay(), XC_left_ptr
);
167 cursor
.move
= XCreateFontCursor(getXDisplay(), XC_fleur
);
168 cursor
.ll_angle
= XCreateFontCursor(getXDisplay(), XC_ll_angle
);
169 cursor
.lr_angle
= XCreateFontCursor(getXDisplay(), XC_lr_angle
);
170 cursor
.ul_angle
= XCreateFontCursor(getXDisplay(), XC_ul_angle
);
171 cursor
.ur_angle
= XCreateFontCursor(getXDisplay(), XC_ur_angle
);
173 for (unsigned int i
= 0; i
< getNumberOfScreens(); i
++) {
174 BScreen
*screen
= new BScreen(this, i
);
176 if (! screen
->isScreenManaged()) {
181 screenList
.push_back(screen
);
184 if (screenList
.empty()) {
186 i18n(blackboxSet
, blackboxNoManagableScreens
,
187 "Blackbox::Blackbox: no managable screens found, aborting.\n"));
191 // save current settings and default values
194 // set the screen with mouse to the first managed screen
195 active_screen
= screenList
.front();
198 XSynchronize(getXDisplay(), False
);
199 XSync(getXDisplay(), False
);
201 reconfigure_wait
= reread_menu_wait
= False
;
203 timer
= new BTimer(this, this);
204 timer
->setTimeout(0l);
208 Blackbox::~Blackbox(void) {
209 std::for_each(screenList
.begin(), screenList
.end(), PointerAssassin());
211 std::for_each(menuTimestamps
.begin(), menuTimestamps
.end(),
220 void Blackbox::process_event(XEvent
*e
) {
223 // strip the lock key modifiers
224 e
->xbutton
.state
&= ~(NumLockMask
| ScrollLockMask
| LockMask
);
226 last_time
= e
->xbutton
.time
;
228 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
229 Basemenu
*menu
= (Basemenu
*) 0;
230 Slit
*slit
= (Slit
*) 0;
231 Toolbar
*tbar
= (Toolbar
*) 0;
232 BScreen
*scrn
= (BScreen
*) 0;
234 if ((win
= searchWindow(e
->xbutton
.window
))) {
235 win
->buttonPressEvent(&e
->xbutton
);
237 /* XXX: is this sane on low colour desktops? */
238 if (e
->xbutton
.button
== 1)
239 win
->installColormap(True
);
240 } else if ((menu
= searchMenu(e
->xbutton
.window
))) {
241 menu
->buttonPressEvent(&e
->xbutton
);
242 } else if ((slit
= searchSlit(e
->xbutton
.window
))) {
243 slit
->buttonPressEvent(&e
->xbutton
);
244 } else if ((tbar
= searchToolbar(e
->xbutton
.window
))) {
245 tbar
->buttonPressEvent(&e
->xbutton
);
246 } else if ((scrn
= searchScreen(e
->xbutton
.window
))) {
247 scrn
->buttonPressEvent(&e
->xbutton
);
248 if (active_screen
!= scrn
) {
249 active_screen
= scrn
;
250 // first, set no focus window on the old screen
252 // and move focus to this screen
259 case ButtonRelease
: {
260 // strip the lock key modifiers
261 e
->xbutton
.state
&= ~(NumLockMask
| ScrollLockMask
| LockMask
);
263 last_time
= e
->xbutton
.time
;
265 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
266 Basemenu
*menu
= (Basemenu
*) 0;
267 Toolbar
*tbar
= (Toolbar
*) 0;
269 if ((win
= searchWindow(e
->xbutton
.window
)))
270 win
->buttonReleaseEvent(&e
->xbutton
);
271 else if ((menu
= searchMenu(e
->xbutton
.window
)))
272 menu
->buttonReleaseEvent(&e
->xbutton
);
273 else if ((tbar
= searchToolbar(e
->xbutton
.window
)))
274 tbar
->buttonReleaseEvent(&e
->xbutton
);
279 case ConfigureRequest
: {
280 // compress configure requests...
283 while(XCheckTypedWindowEvent(getXDisplay(), e
->xconfigurerequest
.window
,
284 ConfigureRequest
, &realevent
)) {
290 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
291 Slit
*slit
= (Slit
*) 0;
293 if ((win
= searchWindow(e
->xconfigurerequest
.window
))) {
294 win
->configureRequestEvent(&e
->xconfigurerequest
);
295 } else if ((slit
= searchSlit(e
->xconfigurerequest
.window
))) {
296 slit
->configureRequestEvent(&e
->xconfigurerequest
);
298 if (validateWindow(e
->xconfigurerequest
.window
)) {
301 xwc
.x
= e
->xconfigurerequest
.x
;
302 xwc
.y
= e
->xconfigurerequest
.y
;
303 xwc
.width
= e
->xconfigurerequest
.width
;
304 xwc
.height
= e
->xconfigurerequest
.height
;
305 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
306 xwc
.sibling
= e
->xconfigurerequest
.above
;
307 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
309 XConfigureWindow(getXDisplay(), e
->xconfigurerequest
.window
,
310 e
->xconfigurerequest
.value_mask
, &xwc
);
319 fprintf(stderr
, "Blackbox::process_event(): MapRequest for 0x%lx\n",
320 e
->xmaprequest
.window
);
323 BlackboxWindow
*win
= searchWindow(e
->xmaprequest
.window
);
326 BScreen
*screen
= searchScreen(e
->xmaprequest
.parent
);
330 we got a map request for a window who's parent isn't root. this
331 can happen in only one circumstance:
333 a client window unmapped a managed window, and then remapped it
334 somewhere between unmapping the client window and reparenting it
337 regardless of how it happens, we need to find the screen that
340 XWindowAttributes wattrib
;
341 if (! XGetWindowAttributes(getXDisplay(), e
->xmaprequest
.window
,
343 // failed to get the window attributes, perhaps the window has
344 // now been destroyed?
348 screen
= searchScreen(wattrib
.root
);
349 assert(screen
!= 0); // this should never happen
352 screen
->manageWindow(e
->xmaprequest
.window
);
359 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
360 Slit
*slit
= (Slit
*) 0;
361 BScreen
*screen
= (BScreen
*) 0;
363 if ((win
= searchWindow(e
->xunmap
.window
))) {
364 win
->unmapNotifyEvent(&e
->xunmap
);
365 } else if ((slit
= searchSlit(e
->xunmap
.window
))) {
366 slit
->unmapNotifyEvent(&e
->xunmap
);
367 } else if ((screen
= searchSystrayWindow(e
->xunmap
.window
))) {
368 screen
->removeSystrayWindow(e
->xunmap
.window
);
369 } else if ((screen
= searchDesktopWindow(e
->xunmap
.window
))) {
370 screen
->removeDesktopWindow(e
->xunmap
.window
);
376 case DestroyNotify
: {
377 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
378 Slit
*slit
= (Slit
*) 0;
379 BScreen
*screen
= (BScreen
*) 0;
380 BWindowGroup
*group
= (BWindowGroup
*) 0;
382 if ((win
= searchWindow(e
->xdestroywindow
.window
))) {
383 win
->destroyNotifyEvent(&e
->xdestroywindow
);
384 } else if ((slit
= searchSlit(e
->xdestroywindow
.window
))) {
385 slit
->removeClient(e
->xdestroywindow
.window
, False
);
386 } else if ((group
= searchGroup(e
->xdestroywindow
.window
))) {
388 } else if ((screen
= searchSystrayWindow(e
->xunmap
.window
))) {
389 screen
->removeSystrayWindow(e
->xunmap
.window
);
390 } else if ((screen
= searchDesktopWindow(e
->xunmap
.window
))) {
391 screen
->removeDesktopWindow(e
->xunmap
.window
);
397 case ReparentNotify
: {
399 this event is quite rare and is usually handled in unmapNotify
400 however, if the window is unmapped when the reparent event occurs
401 the window manager never sees it because an unmap event is not sent
402 to an already unmapped window.
404 BlackboxWindow
*win
= searchWindow(e
->xreparent
.window
);
406 win
->reparentNotifyEvent(&e
->xreparent
);
408 Slit
*slit
= searchSlit(e
->xreparent
.window
);
409 if (slit
&& slit
->getWindowID() != e
->xreparent
.parent
)
410 slit
->removeClient(e
->xreparent
.window
, True
);
416 // motion notify compression...
419 while (XCheckTypedWindowEvent(getXDisplay(), e
->xmotion
.window
,
420 MotionNotify
, &realevent
)) {
424 // if we have compressed some motion events, use the last one
428 // strip the lock key modifiers
429 e
->xbutton
.state
&= ~(NumLockMask
| ScrollLockMask
| LockMask
);
431 last_time
= e
->xmotion
.time
;
433 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
434 Basemenu
*menu
= (Basemenu
*) 0;
436 if ((win
= searchWindow(e
->xmotion
.window
)))
437 win
->motionNotifyEvent(&e
->xmotion
);
438 else if ((menu
= searchMenu(e
->xmotion
.window
)))
439 menu
->motionNotifyEvent(&e
->xmotion
);
444 case PropertyNotify
: {
445 last_time
= e
->xproperty
.time
;
447 if (e
->xproperty
.state
!= PropertyDelete
) {
448 BlackboxWindow
*win
= searchWindow(e
->xproperty
.window
);
451 win
->propertyNotifyEvent(e
->xproperty
.atom
);
458 last_time
= e
->xcrossing
.time
;
460 BScreen
*screen
= (BScreen
*) 0;
461 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
462 Basemenu
*menu
= (Basemenu
*) 0;
463 Toolbar
*tbar
= (Toolbar
*) 0;
464 Slit
*slit
= (Slit
*) 0;
466 if (e
->xcrossing
.mode
== NotifyGrab
) break;
470 sa
.w
= e
->xcrossing
.window
;
471 sa
.enter
= sa
.leave
= False
;
472 XCheckIfEvent(getXDisplay(), &dummy
, queueScanner
, (char *) &sa
);
474 if ((e
->xcrossing
.window
== e
->xcrossing
.root
) &&
475 (screen
= searchScreen(e
->xcrossing
.window
))) {
476 screen
->getImageControl()->installRootColormap();
477 } else if ((win
= searchWindow(e
->xcrossing
.window
))) {
478 if (win
->getScreen()->isSloppyFocus() &&
479 (! win
->isFocused()) && (! no_focus
)) {
480 if (((! sa
.leave
) || sa
.inferior
) && win
->isVisible()) {
481 if (win
->setInputFocus())
482 win
->installColormap(True
); // XXX: shouldnt we honour no install?
485 } else if ((menu
= searchMenu(e
->xcrossing
.window
))) {
486 menu
->enterNotifyEvent(&e
->xcrossing
);
487 } else if ((tbar
= searchToolbar(e
->xcrossing
.window
))) {
488 tbar
->enterNotifyEvent(&e
->xcrossing
);
489 } else if ((slit
= searchSlit(e
->xcrossing
.window
))) {
490 slit
->enterNotifyEvent(&e
->xcrossing
);
496 last_time
= e
->xcrossing
.time
;
498 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
499 Basemenu
*menu
= (Basemenu
*) 0;
500 Toolbar
*tbar
= (Toolbar
*) 0;
501 Slit
*slit
= (Slit
*) 0;
503 if ((menu
= searchMenu(e
->xcrossing
.window
)))
504 menu
->leaveNotifyEvent(&e
->xcrossing
);
505 else if ((win
= searchWindow(e
->xcrossing
.window
)))
506 win
->installColormap(False
);
507 else if ((tbar
= searchToolbar(e
->xcrossing
.window
)))
508 tbar
->leaveNotifyEvent(&e
->xcrossing
);
509 else if ((slit
= searchSlit(e
->xcrossing
.window
)))
510 slit
->leaveNotifyEvent(&e
->xcrossing
);
515 // compress expose events
518 int ex1
, ey1
, ex2
, ey2
;
521 ex2
= ex1
+ e
->xexpose
.width
- 1;
522 ey2
= ey1
+ e
->xexpose
.height
- 1;
523 while (XCheckTypedWindowEvent(getXDisplay(), e
->xexpose
.window
,
524 Expose
, &realevent
)) {
528 ex1
= std::min(realevent
.xexpose
.x
, ex1
);
529 ey1
= std::min(realevent
.xexpose
.y
, ey1
);
530 ex2
= std::max(realevent
.xexpose
.x
+ realevent
.xexpose
.width
- 1, ex2
);
531 ey2
= std::max(realevent
.xexpose
.y
+ realevent
.xexpose
.height
- 1, ey2
);
536 // use the merged area
539 e
->xexpose
.width
= ex2
- ex1
+ 1;
540 e
->xexpose
.height
= ey2
- ey1
+ 1;
542 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
543 Basemenu
*menu
= (Basemenu
*) 0;
544 Toolbar
*tbar
= (Toolbar
*) 0;
546 if ((win
= searchWindow(e
->xexpose
.window
)))
547 win
->exposeEvent(&e
->xexpose
);
548 else if ((menu
= searchMenu(e
->xexpose
.window
)))
549 menu
->exposeEvent(&e
->xexpose
);
550 else if ((tbar
= searchToolbar(e
->xexpose
.window
)))
551 tbar
->exposeEvent(&e
->xexpose
);
557 Toolbar
*tbar
= searchToolbar(e
->xkey
.window
);
559 if (tbar
&& tbar
->isEditing())
560 tbar
->keyPressEvent(&e
->xkey
);
565 case ColormapNotify
: {
566 BScreen
*screen
= searchScreen(e
->xcolormap
.window
);
569 screen
->setRootColormapInstalled((e
->xcolormap
.state
==
570 ColormapInstalled
) ? True
: False
);
576 if (e
->xfocus
.detail
!= NotifyNonlinear
&&
577 e
->xfocus
.detail
!= NotifyAncestor
) {
579 don't process FocusIns when:
580 1. the new focus window isn't an ancestor or inferior of the old
581 focus window (NotifyNonlinear)
582 make sure to allow the FocusIn when the old focus window was an
583 ancestor but didn't have a parent, such as root (NotifyAncestor)
588 BlackboxWindow
*win
= searchWindow(e
->xfocus
.window
);
590 if (! win
->isFocused())
591 win
->setFocusFlag(True
);
594 set the event window to None. when the FocusOut event handler calls
595 this function recursively, it uses this as an indication that focus
596 has moved to a known window.
598 e
->xfocus
.window
= None
;
605 if (e
->xfocus
.detail
!= NotifyNonlinear
) {
607 don't process FocusOuts when:
608 2. the new focus window isn't an ancestor or inferior of the old
609 focus window (NotifyNonlinear)
614 BlackboxWindow
*win
= searchWindow(e
->xfocus
.window
);
615 if (win
&& win
->isFocused()) {
617 before we mark "win" as unfocused, we need to verify that focus is
618 going to a known location, is in a known location, or set focus
623 // don't check the current focus if FocusOut was generated during a grab
624 bool check_focus
= (e
->xfocus
.mode
== NotifyNormal
);
627 First, check if there is a pending FocusIn event waiting. if there
628 is, process it and determine if focus has moved to another window
629 (the FocusIn event handler sets the window in the event
630 structure to None to indicate this).
632 if (XCheckTypedEvent(getXDisplay(), FocusIn
, &event
)) {
634 process_event(&event
);
635 if (event
.xfocus
.window
== None
) {
643 Second, we query the X server for the current input focus.
644 to make sure that we keep a consistent state.
646 BlackboxWindow
*focus
;
649 XGetInputFocus(getXDisplay(), &w
, &revert
);
650 focus
= searchWindow(w
);
653 focus got from "win" to "focus" under some very strange
654 circumstances, and we need to make sure that the focus indication
657 setFocusedWindow(focus
);
659 // we have no idea where focus went... so we set it to somewhere
668 case ClientMessage
: {
669 if (e
->xclient
.format
== 32) {
670 if (e
->xclient
.message_type
== xatom
->getAtom(XAtom::wm_change_state
)) {
671 // WM_CHANGE_STATE message
672 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
673 if (! win
|| ! win
->validateClient()) return;
675 if (e
->xclient
.data
.l
[0] == IconicState
)
677 if (e
->xclient
.data
.l
[0] == NormalState
)
679 } else if (e
->xclient
.message_type
==
680 xatom
->getAtom(XAtom::blackbox_change_workspace
) ||
681 e
->xclient
.message_type
==
682 xatom
->getAtom(XAtom::net_current_desktop
)) {
683 // NET_CURRENT_DESKTOP message
684 BScreen
*screen
= searchScreen(e
->xclient
.window
);
686 unsigned int workspace
= e
->xclient
.data
.l
[0];
687 if (screen
&& workspace
< screen
->getWorkspaceCount())
688 screen
->changeWorkspaceID(workspace
);
689 } else if (e
->xclient
.message_type
==
690 xatom
->getAtom(XAtom::blackbox_change_window_focus
) ||
691 e
->xclient
.message_type
==
692 xatom
->getAtom(XAtom::net_active_window
)) {
694 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
698 win
->deiconify(False
, True
);
699 if (win
->isVisible() && win
->setInputFocus()) {
700 //win->getScreen()->getWorkspace(win->getWorkspaceNumber())->
702 win
->installColormap(True
);
705 } else if (e
->xclient
.message_type
==
706 xatom
->getAtom(XAtom::blackbox_cycle_window_focus
)) {
707 // BLACKBOX_CYCLE_WINDOW_FOCUS
708 BScreen
*screen
= searchScreen(e
->xclient
.window
);
711 if (! e
->xclient
.data
.l
[0])
716 } else if (e
->xclient
.message_type
==
717 xatom
->getAtom(XAtom::net_wm_desktop
)) {
719 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
722 BScreen
*screen
= win
->getScreen();
723 unsigned long wksp
= (unsigned) e
->xclient
.data
.l
[0];
724 if (wksp
< screen
->getWorkspaceCount()) {
725 if (win
->isIconic()) win
->deiconify(False
, True
);
726 if (win
->isStuck()) win
->stick();
727 if (wksp
!= screen
->getCurrentWorkspaceID())
731 screen
->reassociateWindow(win
, wksp
, True
);
732 } else if (wksp
== 0xfffffffe) { // XXX: BUG, BUT DOING THIS SO KDE WORKS FOR NOW!!
733 if (win
->isIconic()) win
->deiconify(False
, True
);
734 if (! win
->isStuck()) win
->stick();
735 if (! win
->isVisible()) win
->show();
738 } else if (e
->xclient
.message_type
==
739 xatom
->getAtom(XAtom::blackbox_change_attributes
)) {
740 // BLACKBOX_CHANGE_ATTRIBUTES
741 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
743 if (win
&& win
->validateClient()) {
745 net
.flags
= e
->xclient
.data
.l
[0];
746 net
.attrib
= e
->xclient
.data
.l
[1];
747 net
.workspace
= e
->xclient
.data
.l
[2];
748 net
.stack
= e
->xclient
.data
.l
[3];
749 net
.decoration
= e
->xclient
.data
.l
[4];
751 win
->changeBlackboxHints(&net
);
753 } else if (e
->xclient
.message_type
==
754 xatom
->getAtom(XAtom::net_number_of_desktops
)) {
755 // NET_NUMBER_OF_DESKTOPS
756 BScreen
*screen
= searchScreen(e
->xclient
.window
);
758 if (e
->xclient
.data
.l
[0] > 0) {
759 if ((unsigned) e
->xclient
.data
.l
[0] < screen
->getWorkspaceCount()) {
761 for (int i
= screen
->getWorkspaceCount();
762 i
> e
->xclient
.data
.l
[0]; --i
)
763 screen
->removeLastWorkspace();
764 // removeLast already sets the current workspace to the
765 // last available one.
766 } else if ((unsigned) e
->xclient
.data
.l
[0] >
767 screen
->getWorkspaceCount()) {
769 for(int i
= screen
->getWorkspaceCount();
770 i
< e
->xclient
.data
.l
[0]; ++i
)
771 screen
->addWorkspace();
774 } else if (e
->xclient
.message_type
==
775 xatom
->getAtom(XAtom::net_close_window
)) {
777 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
778 if (win
&& win
->validateClient())
779 win
->close(); // could this be smarter?
780 } else if (e
->xclient
.message_type
==
781 xatom
->getAtom(XAtom::net_wm_moveresize
)) {
783 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
784 if (win
&& win
->validateClient()) {
785 int x_root
= e
->xclient
.data
.l
[0],
786 y_root
= e
->xclient
.data
.l
[1];
787 if ((Atom
) e
->xclient
.data
.l
[2] ==
788 xatom
->getAtom(XAtom::net_wm_moveresize_move
)) {
789 win
->beginMove(x_root
, y_root
);
791 if ((Atom
) e
->xclient
.data
.l
[2] ==
792 xatom
->getAtom(XAtom::net_wm_moveresize_size_topleft
))
793 win
->beginResize(x_root
, y_root
, BlackboxWindow::TopLeft
);
794 else if ((Atom
) e
->xclient
.data
.l
[2] ==
795 xatom
->getAtom(XAtom::net_wm_moveresize_size_topright
))
796 win
->beginResize(x_root
, y_root
, BlackboxWindow::TopRight
);
797 else if ((Atom
) e
->xclient
.data
.l
[2] ==
798 xatom
->getAtom(XAtom::net_wm_moveresize_size_bottomleft
))
799 win
->beginResize(x_root
, y_root
, BlackboxWindow::BottomLeft
);
800 else if ((Atom
) e
->xclient
.data
.l
[2] ==
801 xatom
->getAtom(XAtom::net_wm_moveresize_size_bottomright
))
802 win
->beginResize(x_root
, y_root
, BlackboxWindow::BottomRight
);
805 } else if (e
->xclient
.message_type
==
806 xatom
->getAtom(XAtom::net_wm_state
)) {
808 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
809 if (win
&& win
->validateClient()) {
810 const Atom action
= (Atom
) e
->xclient
.data
.l
[0];
811 const Atom state
[] = { (Atom
) e
->xclient
.data
.l
[1],
812 (Atom
) e
->xclient
.data
.l
[2] };
814 for (int i
= 0; i
< 2; ++i
) {
818 if ((Atom
) e
->xclient
.data
.l
[0] == 1) {
820 if (state
[i
] == xatom
->getAtom(XAtom::net_wm_state_modal
)) {
822 } else if (state
[i
] ==
823 xatom
->getAtom(XAtom::net_wm_state_maximized_vert
)) {
824 if (win
->isMaximizedHoriz()) {
825 win
->maximize(0); // unmaximize
826 win
->maximize(1); // full
827 } else if (! win
->isMaximized()) {
828 win
->maximize(2); // vert
830 } else if (state
[i
] ==
831 xatom
->getAtom(XAtom::net_wm_state_maximized_horz
)) {
832 if (win
->isMaximizedVert()) {
833 win
->maximize(0); // unmaximize
834 win
->maximize(1); // full
835 } else if (! win
->isMaximized()) {
836 win
->maximize(3); // horiz
838 } else if (state
[i
] ==
839 xatom
->getAtom(XAtom::net_wm_state_shaded
)) {
840 if (! win
->isShaded())
842 } else if (state
[i
] ==
843 xatom
->getAtom(XAtom::net_wm_state_skip_taskbar
)) {
844 win
->setSkipTaskbar(True
);
845 } else if (state
[i
] ==
846 xatom
->getAtom(XAtom::net_wm_state_skip_pager
)) {
847 win
->setSkipPager(True
);
848 } else if (state
[i
] ==
849 xatom
->getAtom(XAtom::net_wm_state_fullscreen
)) {
850 win
->setFullscreen(True
);
852 } else if (action
== 0) {
854 if (state
[i
] == xatom
->getAtom(XAtom::net_wm_state_modal
)) {
855 win
->setModal(False
);
856 } else if (state
[i
] ==
857 xatom
->getAtom(XAtom::net_wm_state_maximized_vert
)) {
858 if (win
->isMaximizedFull()) {
859 win
->maximize(0); // unmaximize
860 win
->maximize(3); // horiz
861 } else if (win
->isMaximizedVert()) {
862 win
->maximize(0); // unmaximize
864 } else if (state
[i
] ==
865 xatom
->getAtom(XAtom::net_wm_state_maximized_horz
)) {
866 if (win
->isMaximizedFull()) {
867 win
->maximize(0); // unmaximize
868 win
->maximize(2); // vert
869 } else if (win
->isMaximizedHoriz()) {
870 win
->maximize(0); // unmaximize
872 } else if (state
[i
] ==
873 xatom
->getAtom(XAtom::net_wm_state_shaded
)) {
876 } else if (state
[i
] ==
877 xatom
->getAtom(XAtom::net_wm_state_skip_taskbar
)) {
878 win
->setSkipTaskbar(False
);
879 } else if (state
[i
] ==
880 xatom
->getAtom(XAtom::net_wm_state_skip_pager
)) {
881 win
->setSkipPager(False
);
882 } else if (state
[i
] ==
883 xatom
->getAtom(XAtom::net_wm_state_fullscreen
)) {
884 win
->setFullscreen(False
);
886 } else if (action
== 2) {
888 if (state
[i
] == xatom
->getAtom(XAtom::net_wm_state_modal
)) {
889 win
->setModal(! win
->isModal());
890 } else if (state
[i
] ==
891 xatom
->getAtom(XAtom::net_wm_state_maximized_vert
)) {
892 if (win
->isMaximizedFull()) {
893 win
->maximize(0); // unmaximize
894 win
->maximize(3); // horiz
895 } else if (win
->isMaximizedVert()) {
896 win
->maximize(0); // unmaximize
897 } else if (win
->isMaximizedHoriz()) {
898 win
->maximize(0); // unmaximize
899 win
->maximize(1); // full
901 win
->maximize(2); // vert
903 } else if (state
[i
] ==
904 xatom
->getAtom(XAtom::net_wm_state_maximized_horz
)) {
905 if (win
->isMaximizedFull()) {
906 win
->maximize(0); // unmaximize
907 win
->maximize(2); // vert
908 } else if (win
->isMaximizedHoriz()) {
909 win
->maximize(0); // unmaximize
910 } else if (win
->isMaximizedVert()) {
911 win
->maximize(0); // unmaximize
912 win
->maximize(1); // full
914 win
->maximize(3); // horiz
916 } else if (state
[i
] ==
917 xatom
->getAtom(XAtom::net_wm_state_shaded
)) {
919 } else if (state
[i
] ==
920 xatom
->getAtom(XAtom::net_wm_state_skip_taskbar
)) {
921 win
->setSkipTaskbar(! win
->skipTaskbar());
922 } else if (state
[i
] ==
923 xatom
->getAtom(XAtom::net_wm_state_skip_pager
)) {
924 win
->setSkipPager(! win
->skipPager());
925 } else if (state
[i
] ==
926 xatom
->getAtom(XAtom::net_wm_state_fullscreen
)) {
927 win
->setFullscreen(! win
->isFullscreen());
939 case ConfigureNotify
:
941 break; // not handled, just ignore
945 if (e
->type
== getShapeEventBase()) {
946 XShapeEvent
*shape_event
= (XShapeEvent
*) e
;
947 BlackboxWindow
*win
= searchWindow(e
->xany
.window
);
950 win
->shapeEvent(shape_event
);
958 bool Blackbox::handleSignal(int sig
) {
984 bool Blackbox::validateWindow(Window window
) {
986 if (XCheckTypedWindowEvent(getXDisplay(), window
, DestroyNotify
, &event
)) {
987 XPutBackEvent(getXDisplay(), &event
);
996 BScreen
*Blackbox::searchScreen(Window window
) {
997 ScreenList::iterator it
= screenList
.begin();
999 for (; it
!= screenList
.end(); ++it
) {
1001 if (s
->getRootWindow() == window
)
1005 return (BScreen
*) 0;
1009 BScreen
*Blackbox::searchDesktopWindow(Window window
) {
1010 WindowScreenLookup::iterator it
= desktopSearchList
.find(window
);
1011 if (it
!= desktopSearchList
.end())
1014 return (BScreen
*) 0;
1018 BScreen
*Blackbox::searchSystrayWindow(Window window
) {
1019 WindowScreenLookup::iterator it
= systraySearchList
.find(window
);
1020 if (it
!= systraySearchList
.end())
1023 return (BScreen
*) 0;
1027 BlackboxWindow
*Blackbox::searchWindow(Window window
) {
1028 WindowLookup::iterator it
= windowSearchList
.find(window
);
1029 if (it
!= windowSearchList
.end())
1032 return (BlackboxWindow
*) 0;
1036 BWindowGroup
*Blackbox::searchGroup(Window window
) {
1037 GroupLookup::iterator it
= groupSearchList
.find(window
);
1038 if (it
!= groupSearchList
.end())
1041 return (BWindowGroup
*) 0;
1045 Basemenu
*Blackbox::searchMenu(Window window
) {
1046 MenuLookup::iterator it
= menuSearchList
.find(window
);
1047 if (it
!= menuSearchList
.end())
1050 return (Basemenu
*) 0;
1054 Toolbar
*Blackbox::searchToolbar(Window window
) {
1055 ToolbarLookup::iterator it
= toolbarSearchList
.find(window
);
1056 if (it
!= toolbarSearchList
.end())
1059 return (Toolbar
*) 0;
1063 Slit
*Blackbox::searchSlit(Window window
) {
1064 SlitLookup::iterator it
= slitSearchList
.find(window
);
1065 if (it
!= slitSearchList
.end())
1072 void Blackbox::saveDesktopWindowSearch(Window window
, BScreen
*screen
) {
1073 desktopSearchList
.insert(WindowScreenLookupPair(window
, screen
));
1077 void Blackbox::saveSystrayWindowSearch(Window window
, BScreen
*screen
) {
1078 systraySearchList
.insert(WindowScreenLookupPair(window
, screen
));
1082 void Blackbox::saveWindowSearch(Window window
, BlackboxWindow
*data
) {
1083 windowSearchList
.insert(WindowLookupPair(window
, data
));
1087 void Blackbox::saveGroupSearch(Window window
, BWindowGroup
*data
) {
1088 groupSearchList
.insert(GroupLookupPair(window
, data
));
1092 void Blackbox::saveMenuSearch(Window window
, Basemenu
*data
) {
1093 menuSearchList
.insert(MenuLookupPair(window
, data
));
1097 void Blackbox::saveToolbarSearch(Window window
, Toolbar
*data
) {
1098 toolbarSearchList
.insert(ToolbarLookupPair(window
, data
));
1102 void Blackbox::saveSlitSearch(Window window
, Slit
*data
) {
1103 slitSearchList
.insert(SlitLookupPair(window
, data
));
1107 void Blackbox::removeDesktopWindowSearch(Window window
) {
1108 desktopSearchList
.erase(window
);
1112 void Blackbox::removeSystrayWindowSearch(Window window
) {
1113 systraySearchList
.erase(window
);
1117 void Blackbox::removeWindowSearch(Window window
) {
1118 windowSearchList
.erase(window
);
1122 void Blackbox::removeGroupSearch(Window window
) {
1123 groupSearchList
.erase(window
);
1127 void Blackbox::removeMenuSearch(Window window
) {
1128 menuSearchList
.erase(window
);
1132 void Blackbox::removeToolbarSearch(Window window
) {
1133 toolbarSearchList
.erase(window
);
1137 void Blackbox::removeSlitSearch(Window window
) {
1138 slitSearchList
.erase(window
);
1142 void Blackbox::restart(const char *prog
) {
1146 putenv(const_cast<char *>(screenList
.front()->displayString().c_str()));
1147 execlp(prog
, prog
, NULL
);
1151 // fall back in case the above execlp doesn't work
1152 execvp(argv
[0], argv
);
1153 string name
= basename(argv
[0]);
1154 execvp(name
.c_str(), argv
);
1158 void Blackbox::shutdown(void) {
1159 BaseDisplay::shutdown();
1161 XSetInputFocus(getXDisplay(), PointerRoot
, None
, CurrentTime
);
1163 std::for_each(screenList
.begin(), screenList
.end(),
1164 std::mem_fun(&BScreen::shutdown
));
1166 XSync(getXDisplay(), False
);
1171 * Save all values as they are so that the defaults will be written to the rc
1174 void Blackbox::save_rc(void) {
1175 config
.setAutoSave(false);
1177 config
.setValue("session.colorsPerChannel", resource
.colors_per_channel
);
1178 config
.setValue("session.doubleClickInterval",
1179 resource
.double_click_interval
);
1180 config
.setValue("session.autoRaiseDelay",
1181 ((resource
.auto_raise_delay
.tv_sec
* 1000) +
1182 (resource
.auto_raise_delay
.tv_usec
/ 1000)));
1183 config
.setValue("session.cacheLife", resource
.cache_life
/ 60000);
1184 config
.setValue("session.cacheMax", resource
.cache_max
);
1185 config
.setValue("session.styleFile", resource
.style_file
);
1186 config
.setValue("session.titlebarLayout", resource
.titlebar_layout
);
1188 std::for_each(screenList
.begin(), screenList
.end(),
1189 std::mem_fun(&BScreen::save_rc
));
1191 config
.setAutoSave(true);
1196 void Blackbox::load_rc(void) {
1197 if (! config
.load())
1202 if (! config
.getValue("session.colorsPerChannel",
1203 resource
.colors_per_channel
))
1204 resource
.colors_per_channel
= 4;
1205 if (resource
.colors_per_channel
< 2) resource
.colors_per_channel
= 2;
1206 else if (resource
.colors_per_channel
> 6) resource
.colors_per_channel
= 6;
1208 if (config
.getValue("session.styleFile", s
))
1209 resource
.style_file
= expandTilde(s
);
1211 resource
.style_file
= DEFAULTSTYLE
;
1213 if (! config
.getValue("session.doubleClickInterval",
1214 resource
.double_click_interval
));
1215 resource
.double_click_interval
= 250;
1217 if (! config
.getValue("session.autoRaiseDelay",
1218 resource
.auto_raise_delay
.tv_usec
))
1219 resource
.auto_raise_delay
.tv_usec
= 400;
1220 resource
.auto_raise_delay
.tv_sec
= resource
.auto_raise_delay
.tv_usec
/ 1000;
1221 resource
.auto_raise_delay
.tv_usec
-=
1222 (resource
.auto_raise_delay
.tv_sec
* 1000);
1223 resource
.auto_raise_delay
.tv_usec
*= 1000;
1225 if (! config
.getValue("session.cacheLife", resource
.cache_life
))
1226 resource
.cache_life
= 5;
1227 resource
.cache_life
*= 60000;
1229 if (! config
.getValue("session.cacheMax", resource
.cache_max
))
1230 resource
.cache_max
= 200;
1232 if (! config
.getValue("session.titlebarLayout", resource
.titlebar_layout
))
1233 resource
.titlebar_layout
= "ILMC";
1237 void Blackbox::reconfigure(void) {
1238 reconfigure_wait
= True
;
1240 if (! timer
->isTiming()) timer
->start();
1244 void Blackbox::real_reconfigure(void) {
1247 std::for_each(menuTimestamps
.begin(), menuTimestamps
.end(),
1249 menuTimestamps
.clear();
1253 std::for_each(screenList
.begin(), screenList
.end(),
1254 std::mem_fun(&BScreen::reconfigure
));
1258 void Blackbox::checkMenu(void) {
1259 bool reread
= False
;
1260 MenuTimestampList::iterator it
= menuTimestamps
.begin();
1261 for(; it
!= menuTimestamps
.end(); ++it
) {
1262 MenuTimestamp
*tmp
= *it
;
1265 if (! stat(tmp
->filename
.c_str(), &buf
)) {
1266 if (tmp
->timestamp
!= buf
.st_ctime
)
1273 if (reread
) rereadMenu();
1277 void Blackbox::rereadMenu(void) {
1278 reread_menu_wait
= True
;
1280 if (! timer
->isTiming()) timer
->start();
1284 void Blackbox::real_rereadMenu(void) {
1285 std::for_each(menuTimestamps
.begin(), menuTimestamps
.end(),
1287 menuTimestamps
.clear();
1289 std::for_each(screenList
.begin(), screenList
.end(),
1290 std::mem_fun(&BScreen::rereadMenu
));
1294 void Blackbox::saveStyleFilename(const string
& filename
) {
1295 assert(! filename
.empty());
1296 resource
.style_file
= filename
;
1297 config
.setValue("session.styleFile", resource
.style_file
);
1301 void Blackbox::addMenuTimestamp(const string
& filename
) {
1302 assert(! filename
.empty());
1305 MenuTimestampList::iterator it
= menuTimestamps
.begin();
1306 for (; it
!= menuTimestamps
.end() && ! found
; ++it
) {
1307 if ((*it
)->filename
== filename
) found
= True
;
1312 if (! stat(filename
.c_str(), &buf
)) {
1313 MenuTimestamp
*ts
= new MenuTimestamp
;
1315 ts
->filename
= filename
;
1316 ts
->timestamp
= buf
.st_ctime
;
1318 menuTimestamps
.push_back(ts
);
1324 void Blackbox::timeout(void) {
1325 if (reconfigure_wait
)
1328 if (reread_menu_wait
)
1331 reconfigure_wait
= reread_menu_wait
= False
;
1335 void Blackbox::setChangingWindow(BlackboxWindow
*win
) {
1336 // make sure one of the two is null and the other isn't
1337 assert((! changing_window
&& win
) || (! win
&& changing_window
));
1338 changing_window
= win
;
1342 void Blackbox::setFocusedWindow(BlackboxWindow
*win
) {
1343 if (focused_window
&& focused_window
== win
) // nothing to do
1346 BScreen
*old_screen
= 0;
1348 if (focused_window
) {
1349 focused_window
->setFocusFlag(False
);
1350 old_screen
= focused_window
->getScreen();
1353 if (win
&& ! win
->isIconic()) {
1354 // the active screen is the one with the last focused window...
1355 // this will keep focus on this screen no matter where the mouse goes,
1356 // so multihead keybindings will continue to work on that screen until the
1357 // user focuses a window on a different screen.
1358 active_screen
= win
->getScreen();
1359 focused_window
= win
;
1363 if (active_screen
) {
1364 // set input focus to the toolbar of the screen with mouse
1365 XSetInputFocus(getXDisplay(),
1366 active_screen
->getRootWindow(),
1367 RevertToPointerRoot
, CurrentTime
);
1369 // set input focus to the toolbar of the first managed screen
1370 XSetInputFocus(getXDisplay(),
1371 screenList
.front()->getRootWindow(),
1372 RevertToPointerRoot
, CurrentTime
);
1375 // set input focus to the toolbar of the last screen
1376 XSetInputFocus(getXDisplay(), old_screen
->getRootWindow(),
1377 RevertToPointerRoot
, CurrentTime
);
1381 if (active_screen
&& active_screen
->isScreenManaged()) {
1382 active_screen
->getToolbar()->redrawWindowLabel(True
);
1383 active_screen
->updateNetizenWindowFocus();
1386 if (old_screen
&& old_screen
!= active_screen
) {
1387 old_screen
->getToolbar()->redrawWindowLabel(True
);
1388 old_screen
->updateNetizenWindowFocus();