1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // blackbox.cc for Blackbox - an X11 Window manager
3 // Copyright (c) 2001 - 2002 Sean 'Shaleh' Perry <shaleh@debian.org>
4 // Copyright (c) 1997 - 2000 Brad Hughes (bhughes@tcac.net)
6 // Permission is hereby granted, free of charge, to any person obtaining a
7 // copy of this software and associated documentation files (the "Software"),
8 // to deal in the Software without restriction, including without limitation
9 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
10 // and/or sell copies of the Software, and to permit persons to whom the
11 // Software is furnished to do so, subject to the following conditions:
13 // The above copyright notice and this permission notice shall be included in
14 // all copies or substantial portions of the Software.
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22 // DEALINGS IN THE SOFTWARE.
25 # include "../config.h"
26 #endif // HAVE_CONFIG_H
30 #include <X11/Xutil.h>
31 #include <X11/Xatom.h>
32 #include <X11/cursorfont.h>
33 #include <X11/keysym.h>
36 #include <X11/extensions/shape.h>
41 #endif // HAVE_STDIO_H
45 #endif // HAVE_STDLIB_H
49 #endif // HAVE_STRING_H
52 # include <sys/types.h>
54 #endif // HAVE_UNISTD_H
56 #ifdef HAVE_SYS_PARAM_H
57 # include <sys/param.h>
58 #endif // HAVE_SYS_PARAM_H
60 #ifdef HAVE_SYS_SELECT_H
61 # include <sys/select.h>
62 #endif // HAVE_SYS_SELECT_H
66 #endif // HAVE_SIGNAL_H
68 #ifdef HAVE_SYS_SIGNAL_H
69 # include <sys/signal.h>
70 #endif // HAVE_SYS_SIGNAL_H
72 #ifdef HAVE_SYS_STAT_H
73 # include <sys/types.h>
74 # include <sys/stat.h>
75 #endif // HAVE_SYS_STAT_H
77 #ifdef TIME_WITH_SYS_TIME
78 # include <sys/time.h>
80 #else // !TIME_WITH_SYS_TIME
81 # ifdef HAVE_SYS_TIME_H
82 # include <sys/time.h>
83 # else // !HAVE_SYS_TIME_H
85 # endif // HAVE_SYS_TIME_H
86 #endif // TIME_WITH_SYS_TIME
90 #endif // HAVE_LIBGEN_H
100 #include "blackbox.hh"
101 #include "Basemenu.hh"
102 #include "Clientmenu.hh"
103 #include "GCCache.hh"
105 #include "Rootmenu.hh"
108 #include "Toolbar.hh"
111 #include "Workspace.hh"
112 #include "Workspacemenu.hh"
115 // X event scanner for enter/leave notifies - adapted from twm
118 bool leave
, inferior
, enter
;
121 static Bool
queueScanner(Display
*, XEvent
*e
, char *args
) {
122 scanargs
*scan
= (scanargs
*) args
;
123 if ((e
->type
== LeaveNotify
) &&
124 (e
->xcrossing
.window
== scan
->w
) &&
125 (e
->xcrossing
.mode
== NotifyNormal
)) {
127 scan
->inferior
= (e
->xcrossing
.detail
== NotifyInferior
);
128 } else if ((e
->type
== EnterNotify
) && (e
->xcrossing
.mode
== NotifyUngrab
)) {
138 Blackbox::Blackbox(char **m_argv
, char *dpy_name
, char *rc
, char *menu
)
139 : BaseDisplay(m_argv
[0], dpy_name
) {
140 if (! XSupportsLocale())
141 fprintf(stderr
, "X server does not support locale\n");
143 if (XSetLocaleModifiers("") == NULL
)
144 fprintf(stderr
, "cannot set locale modifiers\n");
149 // try to make sure the ~/.openbox directory exists
150 mkdir(expandTilde("~/.openbox").c_str(), S_IREAD
| S_IWRITE
| S_IEXEC
|
151 S_IRGRP
| S_IWGRP
| S_IXGRP
|
152 S_IROTH
| S_IWOTH
| S_IXOTH
);
154 if (! rc
) rc
= "~/.openbox/rc";
155 rc_file
= expandTilde(rc
);
156 config
.setFile(rc_file
);
157 if (! menu
) menu
= "~/.openbox/menu";
158 menu_file
= expandTilde(menu
);
162 resource
.auto_raise_delay
.tv_sec
= resource
.auto_raise_delay
.tv_usec
= 0;
165 focused_window
= changing_window
= (BlackboxWindow
*) 0;
169 xatom
= new XAtom(getXDisplay());
171 cursor
.session
= XCreateFontCursor(getXDisplay(), XC_left_ptr
);
172 cursor
.move
= XCreateFontCursor(getXDisplay(), XC_fleur
);
173 cursor
.ll_angle
= XCreateFontCursor(getXDisplay(), XC_ll_angle
);
174 cursor
.lr_angle
= XCreateFontCursor(getXDisplay(), XC_lr_angle
);
175 cursor
.ul_angle
= XCreateFontCursor(getXDisplay(), XC_ul_angle
);
176 cursor
.ur_angle
= XCreateFontCursor(getXDisplay(), XC_ur_angle
);
178 for (unsigned int i
= 0; i
< getNumberOfScreens(); i
++) {
179 BScreen
*screen
= new BScreen(this, i
);
181 if (! screen
->isScreenManaged()) {
186 screenList
.push_back(screen
);
189 if (screenList
.empty()) {
191 i18n(blackboxSet
, blackboxNoManagableScreens
,
192 "Blackbox::Blackbox: no managable screens found, aborting.\n"));
196 // save current settings and default values
199 // set the screen with mouse to the first managed screen
200 active_screen
= screenList
.front();
203 XSynchronize(getXDisplay(), False
);
204 XSync(getXDisplay(), False
);
206 reconfigure_wait
= reread_menu_wait
= False
;
208 timer
= new BTimer(this, this);
209 timer
->setTimeout(0l);
213 Blackbox::~Blackbox(void) {
214 std::for_each(screenList
.begin(), screenList
.end(), PointerAssassin());
216 std::for_each(menuTimestamps
.begin(), menuTimestamps
.end(),
225 void Blackbox::process_event(XEvent
*e
) {
228 // strip the lock key modifiers
229 e
->xbutton
.state
&= ~(NumLockMask
| ScrollLockMask
| LockMask
);
231 last_time
= e
->xbutton
.time
;
233 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
234 Basemenu
*menu
= (Basemenu
*) 0;
235 Slit
*slit
= (Slit
*) 0;
236 Toolbar
*tbar
= (Toolbar
*) 0;
237 BScreen
*scrn
= (BScreen
*) 0;
239 if ((win
= searchWindow(e
->xbutton
.window
))) {
240 win
->buttonPressEvent(&e
->xbutton
);
242 /* XXX: is this sane on low colour desktops? */
243 if (e
->xbutton
.button
== 1)
244 win
->installColormap(True
);
245 } else if ((menu
= searchMenu(e
->xbutton
.window
))) {
246 menu
->buttonPressEvent(&e
->xbutton
);
247 } else if ((slit
= searchSlit(e
->xbutton
.window
))) {
248 slit
->buttonPressEvent(&e
->xbutton
);
249 } else if ((tbar
= searchToolbar(e
->xbutton
.window
))) {
250 tbar
->buttonPressEvent(&e
->xbutton
);
251 } else if ((scrn
= searchScreen(e
->xbutton
.window
))) {
252 scrn
->buttonPressEvent(&e
->xbutton
);
253 if (active_screen
!= scrn
) {
254 active_screen
= scrn
;
255 // first, set no focus window on the old screen
257 // and move focus to this screen
264 case ButtonRelease
: {
265 // strip the lock key modifiers
266 e
->xbutton
.state
&= ~(NumLockMask
| ScrollLockMask
| LockMask
);
268 last_time
= e
->xbutton
.time
;
270 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
271 Basemenu
*menu
= (Basemenu
*) 0;
272 Toolbar
*tbar
= (Toolbar
*) 0;
274 if ((win
= searchWindow(e
->xbutton
.window
)))
275 win
->buttonReleaseEvent(&e
->xbutton
);
276 else if ((menu
= searchMenu(e
->xbutton
.window
)))
277 menu
->buttonReleaseEvent(&e
->xbutton
);
278 else if ((tbar
= searchToolbar(e
->xbutton
.window
)))
279 tbar
->buttonReleaseEvent(&e
->xbutton
);
284 case ConfigureRequest
: {
285 // compress configure requests...
288 while(XCheckTypedWindowEvent(getXDisplay(), e
->xconfigurerequest
.window
,
289 ConfigureRequest
, &realevent
)) {
295 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
296 Slit
*slit
= (Slit
*) 0;
298 if ((win
= searchWindow(e
->xconfigurerequest
.window
))) {
299 win
->configureRequestEvent(&e
->xconfigurerequest
);
300 } else if ((slit
= searchSlit(e
->xconfigurerequest
.window
))) {
301 slit
->configureRequestEvent(&e
->xconfigurerequest
);
303 if (validateWindow(e
->xconfigurerequest
.window
)) {
306 xwc
.x
= e
->xconfigurerequest
.x
;
307 xwc
.y
= e
->xconfigurerequest
.y
;
308 xwc
.width
= e
->xconfigurerequest
.width
;
309 xwc
.height
= e
->xconfigurerequest
.height
;
310 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
311 xwc
.sibling
= e
->xconfigurerequest
.above
;
312 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
314 XConfigureWindow(getXDisplay(), e
->xconfigurerequest
.window
,
315 e
->xconfigurerequest
.value_mask
, &xwc
);
324 fprintf(stderr
, "Blackbox::process_event(): MapRequest for 0x%lx\n",
325 e
->xmaprequest
.window
);
328 BlackboxWindow
*win
= searchWindow(e
->xmaprequest
.window
);
332 if (win
->isIconic()) {
336 if (win
->isShaded()) {
341 if (focus
&& (win
->isTransient() || win
->getScreen()->doFocusNew()))
342 win
->setInputFocus();
344 BScreen
*screen
= searchScreen(e
->xmaprequest
.parent
);
348 we got a map request for a window who's parent isn't root. this
349 can happen in only one circumstance:
351 a client window unmapped a managed window, and then remapped it
352 somewhere between unmapping the client window and reparenting it
355 regardless of how it happens, we need to find the screen that
358 XWindowAttributes wattrib
;
359 if (! XGetWindowAttributes(getXDisplay(), e
->xmaprequest
.window
,
361 // failed to get the window attributes, perhaps the window has
362 // now been destroyed?
366 screen
= searchScreen(wattrib
.root
);
367 assert(screen
!= 0); // this should never happen
370 screen
->manageWindow(e
->xmaprequest
.window
);
377 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
378 Slit
*slit
= (Slit
*) 0;
379 BScreen
*screen
= (BScreen
*) 0;
381 if ((win
= searchWindow(e
->xunmap
.window
))) {
382 win
->unmapNotifyEvent(&e
->xunmap
);
383 } else if ((slit
= searchSlit(e
->xunmap
.window
))) {
384 slit
->unmapNotifyEvent(&e
->xunmap
);
385 } else if ((screen
= searchSystrayWindow(e
->xunmap
.window
))) {
386 screen
->removeSystrayWindow(e
->xunmap
.window
);
387 } else if ((screen
= searchDesktopWindow(e
->xunmap
.window
))) {
388 screen
->removeDesktopWindow(e
->xunmap
.window
);
394 case DestroyNotify
: {
395 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
396 Slit
*slit
= (Slit
*) 0;
397 BScreen
*screen
= (BScreen
*) 0;
398 BWindowGroup
*group
= (BWindowGroup
*) 0;
400 if ((win
= searchWindow(e
->xdestroywindow
.window
))) {
401 win
->destroyNotifyEvent(&e
->xdestroywindow
);
402 } else if ((slit
= searchSlit(e
->xdestroywindow
.window
))) {
403 slit
->removeClient(e
->xdestroywindow
.window
, False
);
404 } else if ((group
= searchGroup(e
->xdestroywindow
.window
))) {
406 } else if ((screen
= searchSystrayWindow(e
->xunmap
.window
))) {
407 screen
->removeSystrayWindow(e
->xunmap
.window
);
408 } else if ((screen
= searchDesktopWindow(e
->xunmap
.window
))) {
409 screen
->removeDesktopWindow(e
->xunmap
.window
);
415 case ReparentNotify
: {
417 this event is quite rare and is usually handled in unmapNotify
418 however, if the window is unmapped when the reparent event occurs
419 the window manager never sees it because an unmap event is not sent
420 to an already unmapped window.
422 BlackboxWindow
*win
= searchWindow(e
->xreparent
.window
);
424 win
->reparentNotifyEvent(&e
->xreparent
);
426 Slit
*slit
= searchSlit(e
->xreparent
.window
);
427 if (slit
&& slit
->getWindowID() != e
->xreparent
.parent
)
428 slit
->removeClient(e
->xreparent
.window
, True
);
434 // motion notify compression...
437 while (XCheckTypedWindowEvent(getXDisplay(), e
->xmotion
.window
,
438 MotionNotify
, &realevent
)) {
442 // if we have compressed some motion events, use the last one
446 // strip the lock key modifiers
447 e
->xbutton
.state
&= ~(NumLockMask
| ScrollLockMask
| LockMask
);
449 last_time
= e
->xmotion
.time
;
451 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
452 Basemenu
*menu
= (Basemenu
*) 0;
454 if ((win
= searchWindow(e
->xmotion
.window
)))
455 win
->motionNotifyEvent(&e
->xmotion
);
456 else if ((menu
= searchMenu(e
->xmotion
.window
)))
457 menu
->motionNotifyEvent(&e
->xmotion
);
462 case PropertyNotify
: {
463 last_time
= e
->xproperty
.time
;
465 BlackboxWindow
*win
= searchWindow(e
->xproperty
.window
);
467 win
->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 if (((! sa
.leave
) || sa
.inferior
) && win
->isVisible()) {
495 if (win
->setInputFocus())
496 win
->installColormap(True
); // XXX: shouldnt we honour no install?
499 } else if ((menu
= searchMenu(e
->xcrossing
.window
))) {
500 menu
->enterNotifyEvent(&e
->xcrossing
);
501 } else if ((tbar
= searchToolbar(e
->xcrossing
.window
))) {
502 tbar
->enterNotifyEvent(&e
->xcrossing
);
503 } else if ((slit
= searchSlit(e
->xcrossing
.window
))) {
504 slit
->enterNotifyEvent(&e
->xcrossing
);
510 last_time
= e
->xcrossing
.time
;
512 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
513 Basemenu
*menu
= (Basemenu
*) 0;
514 Toolbar
*tbar
= (Toolbar
*) 0;
515 Slit
*slit
= (Slit
*) 0;
517 if ((menu
= searchMenu(e
->xcrossing
.window
)))
518 menu
->leaveNotifyEvent(&e
->xcrossing
);
519 else if ((win
= searchWindow(e
->xcrossing
.window
)))
520 win
->installColormap(False
);
521 else if ((tbar
= searchToolbar(e
->xcrossing
.window
)))
522 tbar
->leaveNotifyEvent(&e
->xcrossing
);
523 else if ((slit
= searchSlit(e
->xcrossing
.window
)))
524 slit
->leaveNotifyEvent(&e
->xcrossing
);
529 // compress expose events
532 int ex1
, ey1
, ex2
, ey2
;
535 ex2
= ex1
+ e
->xexpose
.width
- 1;
536 ey2
= ey1
+ e
->xexpose
.height
- 1;
537 while (XCheckTypedWindowEvent(getXDisplay(), e
->xexpose
.window
,
538 Expose
, &realevent
)) {
542 ex1
= std::min(realevent
.xexpose
.x
, ex1
);
543 ey1
= std::min(realevent
.xexpose
.y
, ey1
);
544 ex2
= std::max(realevent
.xexpose
.x
+ realevent
.xexpose
.width
- 1, ex2
);
545 ey2
= std::max(realevent
.xexpose
.y
+ realevent
.xexpose
.height
- 1, ey2
);
550 // use the merged area
553 e
->xexpose
.width
= ex2
- ex1
+ 1;
554 e
->xexpose
.height
= ey2
- ey1
+ 1;
556 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
557 Basemenu
*menu
= (Basemenu
*) 0;
558 Toolbar
*tbar
= (Toolbar
*) 0;
560 if ((win
= searchWindow(e
->xexpose
.window
)))
561 win
->exposeEvent(&e
->xexpose
);
562 else if ((menu
= searchMenu(e
->xexpose
.window
)))
563 menu
->exposeEvent(&e
->xexpose
);
564 else if ((tbar
= searchToolbar(e
->xexpose
.window
)))
565 tbar
->exposeEvent(&e
->xexpose
);
571 Toolbar
*tbar
= searchToolbar(e
->xkey
.window
);
573 if (tbar
&& tbar
->isEditing())
574 tbar
->keyPressEvent(&e
->xkey
);
579 case ColormapNotify
: {
580 BScreen
*screen
= searchScreen(e
->xcolormap
.window
);
583 screen
->setRootColormapInstalled((e
->xcolormap
.state
==
584 ColormapInstalled
) ? True
: False
);
590 if (e
->xfocus
.detail
!= NotifyNonlinear
&&
591 e
->xfocus
.detail
!= NotifyAncestor
) {
593 don't process FocusIns when:
594 1. the new focus window isn't an ancestor or inferior of the old
595 focus window (NotifyNonlinear)
596 make sure to allow the FocusIn when the old focus window was an
597 ancestor but didn't have a parent, such as root (NotifyAncestor)
602 BlackboxWindow
*win
= searchWindow(e
->xfocus
.window
);
604 if (! win
->isFocused())
605 win
->setFocusFlag(True
);
608 set the event window to None. when the FocusOut event handler calls
609 this function recursively, it uses this as an indication that focus
610 has moved to a known window.
612 e
->xfocus
.window
= None
;
619 if (e
->xfocus
.detail
!= NotifyNonlinear
) {
621 don't process FocusOuts when:
622 2. the new focus window isn't an ancestor or inferior of the old
623 focus window (NotifyNonlinear)
628 BlackboxWindow
*win
= searchWindow(e
->xfocus
.window
);
629 if (win
&& win
->isFocused()) {
631 before we mark "win" as unfocused, we need to verify that focus is
632 going to a known location, is in a known location, or set focus
637 // don't check the current focus if FocusOut was generated during a grab
638 bool check_focus
= (e
->xfocus
.mode
== NotifyNormal
);
641 First, check if there is a pending FocusIn event waiting. if there
642 is, process it and determine if focus has moved to another window
643 (the FocusIn event handler sets the window in the event
644 structure to None to indicate this).
646 if (XCheckTypedEvent(getXDisplay(), FocusIn
, &event
)) {
648 process_event(&event
);
649 if (event
.xfocus
.window
== None
) {
657 Second, we query the X server for the current input focus.
658 to make sure that we keep a consistent state.
660 BlackboxWindow
*focus
;
663 XGetInputFocus(getXDisplay(), &w
, &revert
);
664 focus
= searchWindow(w
);
667 focus got from "win" to "focus" under some very strange
668 circumstances, and we need to make sure that the focus indication
671 setFocusedWindow(focus
);
673 // we have no idea where focus went... so we set it to somewhere
682 case ClientMessage
: {
683 if (e
->xclient
.format
== 32) {
684 if (e
->xclient
.message_type
== xatom
->getAtom(XAtom::wm_change_state
)) {
685 // WM_CHANGE_STATE message
686 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
687 if (! win
|| ! win
->validateClient()) return;
689 if (e
->xclient
.data
.l
[0] == IconicState
)
691 if (e
->xclient
.data
.l
[0] == NormalState
)
693 } else if (e
->xclient
.message_type
==
694 xatom
->getAtom(XAtom::blackbox_change_workspace
) ||
695 e
->xclient
.message_type
==
696 xatom
->getAtom(XAtom::net_current_desktop
)) {
697 // NET_CURRENT_DESKTOP message
698 BScreen
*screen
= searchScreen(e
->xclient
.window
);
700 unsigned int workspace
= e
->xclient
.data
.l
[0];
701 if (screen
&& workspace
< screen
->getWorkspaceCount())
702 screen
->changeWorkspaceID(workspace
);
703 } else if (e
->xclient
.message_type
==
704 xatom
->getAtom(XAtom::blackbox_change_window_focus
) ||
705 e
->xclient
.message_type
==
706 xatom
->getAtom(XAtom::net_active_window
)) {
708 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
712 win
->deiconify(False
, True
);
713 if (win
->isVisible() && win
->setInputFocus()) {
714 //win->getScreen()->getWorkspace(win->getWorkspaceNumber())->
716 win
->installColormap(True
);
719 } else if (e
->xclient
.message_type
==
720 xatom
->getAtom(XAtom::blackbox_cycle_window_focus
)) {
721 // BLACKBOX_CYCLE_WINDOW_FOCUS
722 BScreen
*screen
= searchScreen(e
->xclient
.window
);
725 if (! e
->xclient
.data
.l
[0])
730 } else if (e
->xclient
.message_type
==
731 xatom
->getAtom(XAtom::net_wm_desktop
)) {
733 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
736 BScreen
*screen
= win
->getScreen();
737 unsigned long wksp
= (unsigned) e
->xclient
.data
.l
[0];
738 if (wksp
< screen
->getWorkspaceCount()) {
739 if (win
->isIconic()) win
->deiconify(False
, True
);
740 if (win
->isStuck()) win
->stick();
741 if (wksp
!= screen
->getCurrentWorkspaceID())
745 screen
->reassociateWindow(win
, wksp
, True
);
746 } else if (wksp
== 0xfffffffe) { // XXX: BUG, BUT DOING THIS SO KDE WORKS FOR NOW!!
747 if (win
->isIconic()) win
->deiconify(False
, True
);
748 if (! win
->isStuck()) win
->stick();
749 if (! win
->isVisible()) win
->show();
752 } else if (e
->xclient
.message_type
==
753 xatom
->getAtom(XAtom::blackbox_change_attributes
)) {
754 // BLACKBOX_CHANGE_ATTRIBUTES
755 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
757 if (win
&& win
->validateClient()) {
759 net
.flags
= e
->xclient
.data
.l
[0];
760 net
.attrib
= e
->xclient
.data
.l
[1];
761 net
.workspace
= e
->xclient
.data
.l
[2];
762 net
.stack
= e
->xclient
.data
.l
[3];
763 net
.decoration
= e
->xclient
.data
.l
[4];
765 win
->changeBlackboxHints(&net
);
767 } else if (e
->xclient
.message_type
==
768 xatom
->getAtom(XAtom::net_number_of_desktops
)) {
769 // NET_NUMBER_OF_DESKTOPS
770 BScreen
*screen
= searchScreen(e
->xclient
.window
);
772 if (e
->xclient
.data
.l
[0] > 0) {
773 if ((unsigned) e
->xclient
.data
.l
[0] < screen
->getWorkspaceCount()) {
775 for (int i
= screen
->getWorkspaceCount();
776 i
> e
->xclient
.data
.l
[0]; --i
)
777 screen
->removeLastWorkspace();
778 // removeLast already sets the current workspace to the
779 // last available one.
780 } else if ((unsigned) e
->xclient
.data
.l
[0] >
781 screen
->getWorkspaceCount()) {
783 for(int i
= screen
->getWorkspaceCount();
784 i
< e
->xclient
.data
.l
[0]; ++i
)
785 screen
->addWorkspace();
788 } else if (e
->xclient
.message_type
==
789 xatom
->getAtom(XAtom::net_close_window
)) {
791 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
792 if (win
&& win
->validateClient())
793 win
->close(); // could this be smarter?
794 } else if (e
->xclient
.message_type
==
795 xatom
->getAtom(XAtom::net_wm_moveresize
)) {
797 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
798 if (win
&& win
->validateClient()) {
799 int x_root
= e
->xclient
.data
.l
[0],
800 y_root
= e
->xclient
.data
.l
[1];
801 if ((Atom
) e
->xclient
.data
.l
[2] ==
802 xatom
->getAtom(XAtom::net_wm_moveresize_move
)) {
803 win
->beginMove(x_root
, y_root
);
805 if ((Atom
) e
->xclient
.data
.l
[2] ==
806 xatom
->getAtom(XAtom::net_wm_moveresize_size_topleft
))
807 win
->beginResize(x_root
, y_root
, BlackboxWindow::TopLeft
);
808 else if ((Atom
) e
->xclient
.data
.l
[2] ==
809 xatom
->getAtom(XAtom::net_wm_moveresize_size_topright
))
810 win
->beginResize(x_root
, y_root
, BlackboxWindow::TopRight
);
811 else if ((Atom
) e
->xclient
.data
.l
[2] ==
812 xatom
->getAtom(XAtom::net_wm_moveresize_size_bottomleft
))
813 win
->beginResize(x_root
, y_root
, BlackboxWindow::BottomLeft
);
814 else if ((Atom
) e
->xclient
.data
.l
[2] ==
815 xatom
->getAtom(XAtom::net_wm_moveresize_size_bottomright
))
816 win
->beginResize(x_root
, y_root
, BlackboxWindow::BottomRight
);
819 } else if (e
->xclient
.message_type
==
820 xatom
->getAtom(XAtom::net_wm_state
)) {
822 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
823 if (win
&& win
->validateClient()) {
824 const Atom action
= (Atom
) e
->xclient
.data
.l
[0];
825 const Atom state
[] = { (Atom
) e
->xclient
.data
.l
[1],
826 (Atom
) e
->xclient
.data
.l
[2] };
828 for (int i
= 0; i
< 2; ++i
) {
832 if ((Atom
) e
->xclient
.data
.l
[0] == 1) {
834 if (state
[i
] == xatom
->getAtom(XAtom::net_wm_state_modal
)) {
836 } else if (state
[i
] ==
837 xatom
->getAtom(XAtom::net_wm_state_maximized_vert
)) {
838 if (win
->isMaximizedHoriz()) {
839 win
->maximize(0); // unmaximize
840 win
->maximize(1); // full
841 } else if (! win
->isMaximized()) {
842 win
->maximize(2); // vert
844 } else if (state
[i
] ==
845 xatom
->getAtom(XAtom::net_wm_state_maximized_horz
)) {
846 if (win
->isMaximizedVert()) {
847 win
->maximize(0); // unmaximize
848 win
->maximize(1); // full
849 } else if (! win
->isMaximized()) {
850 win
->maximize(3); // horiz
852 } else if (state
[i
] ==
853 xatom
->getAtom(XAtom::net_wm_state_shaded
)) {
854 if (! win
->isShaded())
856 } else if (state
[i
] ==
857 xatom
->getAtom(XAtom::net_wm_state_skip_taskbar
)) {
858 win
->setSkipTaskbar(True
);
859 } else if (state
[i
] ==
860 xatom
->getAtom(XAtom::net_wm_state_skip_pager
)) {
861 win
->setSkipPager(True
);
862 } else if (state
[i
] ==
863 xatom
->getAtom(XAtom::net_wm_state_fullscreen
)) {
864 win
->setFullscreen(True
);
866 } else if (action
== 0) {
868 if (state
[i
] == xatom
->getAtom(XAtom::net_wm_state_modal
)) {
869 win
->setModal(False
);
870 } else if (state
[i
] ==
871 xatom
->getAtom(XAtom::net_wm_state_maximized_vert
)) {
872 if (win
->isMaximizedFull()) {
873 win
->maximize(0); // unmaximize
874 win
->maximize(3); // horiz
875 } else if (win
->isMaximizedVert()) {
876 win
->maximize(0); // unmaximize
878 } else if (state
[i
] ==
879 xatom
->getAtom(XAtom::net_wm_state_maximized_horz
)) {
880 if (win
->isMaximizedFull()) {
881 win
->maximize(0); // unmaximize
882 win
->maximize(2); // vert
883 } else if (win
->isMaximizedHoriz()) {
884 win
->maximize(0); // unmaximize
886 } else if (state
[i
] ==
887 xatom
->getAtom(XAtom::net_wm_state_shaded
)) {
890 } else if (state
[i
] ==
891 xatom
->getAtom(XAtom::net_wm_state_skip_taskbar
)) {
892 win
->setSkipTaskbar(False
);
893 } else if (state
[i
] ==
894 xatom
->getAtom(XAtom::net_wm_state_skip_pager
)) {
895 win
->setSkipPager(False
);
896 } else if (state
[i
] ==
897 xatom
->getAtom(XAtom::net_wm_state_fullscreen
)) {
898 win
->setFullscreen(False
);
900 } else if (action
== 2) {
902 if (state
[i
] == xatom
->getAtom(XAtom::net_wm_state_modal
)) {
903 win
->setModal(! win
->isModal());
904 } else if (state
[i
] ==
905 xatom
->getAtom(XAtom::net_wm_state_maximized_vert
)) {
906 if (win
->isMaximizedFull()) {
907 win
->maximize(0); // unmaximize
908 win
->maximize(3); // horiz
909 } else if (win
->isMaximizedVert()) {
910 win
->maximize(0); // unmaximize
911 } else if (win
->isMaximizedHoriz()) {
912 win
->maximize(0); // unmaximize
913 win
->maximize(1); // full
915 win
->maximize(2); // vert
917 } else if (state
[i
] ==
918 xatom
->getAtom(XAtom::net_wm_state_maximized_horz
)) {
919 if (win
->isMaximizedFull()) {
920 win
->maximize(0); // unmaximize
921 win
->maximize(2); // vert
922 } else if (win
->isMaximizedHoriz()) {
923 win
->maximize(0); // unmaximize
924 } else if (win
->isMaximizedVert()) {
925 win
->maximize(0); // unmaximize
926 win
->maximize(1); // full
928 win
->maximize(3); // horiz
930 } else if (state
[i
] ==
931 xatom
->getAtom(XAtom::net_wm_state_shaded
)) {
933 } else if (state
[i
] ==
934 xatom
->getAtom(XAtom::net_wm_state_skip_taskbar
)) {
935 win
->setSkipTaskbar(! win
->skipTaskbar());
936 } else if (state
[i
] ==
937 xatom
->getAtom(XAtom::net_wm_state_skip_pager
)) {
938 win
->setSkipPager(! win
->skipPager());
939 } else if (state
[i
] ==
940 xatom
->getAtom(XAtom::net_wm_state_fullscreen
)) {
941 win
->setFullscreen(! win
->isFullscreen());
953 case ConfigureNotify
:
955 break; // not handled, just ignore
959 if (e
->type
== getShapeEventBase()) {
960 XShapeEvent
*shape_event
= (XShapeEvent
*) e
;
961 BlackboxWindow
*win
= searchWindow(e
->xany
.window
);
964 win
->shapeEvent(shape_event
);
972 bool Blackbox::handleSignal(int sig
) {
998 bool Blackbox::validateWindow(Window window
) {
1000 if (XCheckTypedWindowEvent(getXDisplay(), window
, DestroyNotify
, &event
)) {
1001 XPutBackEvent(getXDisplay(), &event
);
1010 BScreen
*Blackbox::searchScreen(Window window
) {
1011 ScreenList::iterator it
= screenList
.begin();
1013 for (; it
!= screenList
.end(); ++it
) {
1015 if (s
->getRootWindow() == window
)
1019 return (BScreen
*) 0;
1023 BScreen
*Blackbox::searchDesktopWindow(Window window
) {
1024 WindowScreenLookup::iterator it
= desktopSearchList
.find(window
);
1025 if (it
!= desktopSearchList
.end())
1028 return (BScreen
*) 0;
1032 BScreen
*Blackbox::searchSystrayWindow(Window window
) {
1033 WindowScreenLookup::iterator it
= systraySearchList
.find(window
);
1034 if (it
!= systraySearchList
.end())
1037 return (BScreen
*) 0;
1041 BlackboxWindow
*Blackbox::searchWindow(Window window
) {
1042 WindowLookup::iterator it
= windowSearchList
.find(window
);
1043 if (it
!= windowSearchList
.end())
1046 return (BlackboxWindow
*) 0;
1050 BWindowGroup
*Blackbox::searchGroup(Window window
) {
1051 GroupLookup::iterator it
= groupSearchList
.find(window
);
1052 if (it
!= groupSearchList
.end())
1055 return (BWindowGroup
*) 0;
1059 Basemenu
*Blackbox::searchMenu(Window window
) {
1060 MenuLookup::iterator it
= menuSearchList
.find(window
);
1061 if (it
!= menuSearchList
.end())
1064 return (Basemenu
*) 0;
1068 Toolbar
*Blackbox::searchToolbar(Window window
) {
1069 ToolbarLookup::iterator it
= toolbarSearchList
.find(window
);
1070 if (it
!= toolbarSearchList
.end())
1073 return (Toolbar
*) 0;
1077 Slit
*Blackbox::searchSlit(Window window
) {
1078 SlitLookup::iterator it
= slitSearchList
.find(window
);
1079 if (it
!= slitSearchList
.end())
1086 void Blackbox::saveDesktopWindowSearch(Window window
, BScreen
*screen
) {
1087 desktopSearchList
.insert(WindowScreenLookupPair(window
, screen
));
1091 void Blackbox::saveSystrayWindowSearch(Window window
, BScreen
*screen
) {
1092 systraySearchList
.insert(WindowScreenLookupPair(window
, screen
));
1096 void Blackbox::saveWindowSearch(Window window
, BlackboxWindow
*data
) {
1097 windowSearchList
.insert(WindowLookupPair(window
, data
));
1101 void Blackbox::saveGroupSearch(Window window
, BWindowGroup
*data
) {
1102 groupSearchList
.insert(GroupLookupPair(window
, data
));
1106 void Blackbox::saveMenuSearch(Window window
, Basemenu
*data
) {
1107 menuSearchList
.insert(MenuLookupPair(window
, data
));
1111 void Blackbox::saveToolbarSearch(Window window
, Toolbar
*data
) {
1112 toolbarSearchList
.insert(ToolbarLookupPair(window
, data
));
1116 void Blackbox::saveSlitSearch(Window window
, Slit
*data
) {
1117 slitSearchList
.insert(SlitLookupPair(window
, data
));
1121 void Blackbox::removeDesktopWindowSearch(Window window
) {
1122 desktopSearchList
.erase(window
);
1126 void Blackbox::removeSystrayWindowSearch(Window window
) {
1127 systraySearchList
.erase(window
);
1131 void Blackbox::removeWindowSearch(Window window
) {
1132 windowSearchList
.erase(window
);
1136 void Blackbox::removeGroupSearch(Window window
) {
1137 groupSearchList
.erase(window
);
1141 void Blackbox::removeMenuSearch(Window window
) {
1142 menuSearchList
.erase(window
);
1146 void Blackbox::removeToolbarSearch(Window window
) {
1147 toolbarSearchList
.erase(window
);
1151 void Blackbox::removeSlitSearch(Window window
) {
1152 slitSearchList
.erase(window
);
1156 void Blackbox::restart(const char *prog
) {
1160 putenv(const_cast<char *>(screenList
.front()->displayString().c_str()));
1161 execlp(prog
, prog
, NULL
);
1165 // fall back in case the above execlp doesn't work
1166 execvp(argv
[0], argv
);
1167 string name
= basename(argv
[0]);
1168 execvp(name
.c_str(), argv
);
1172 void Blackbox::shutdown(void) {
1173 BaseDisplay::shutdown();
1175 XSetInputFocus(getXDisplay(), PointerRoot
, None
, CurrentTime
);
1177 std::for_each(screenList
.begin(), screenList
.end(),
1178 std::mem_fun(&BScreen::shutdown
));
1180 XSync(getXDisplay(), False
);
1185 * Save all values as they are so that the defaults will be written to the rc
1188 void Blackbox::save_rc(void) {
1189 config
.setAutoSave(false);
1191 config
.setValue("session.colorsPerChannel", resource
.colors_per_channel
);
1192 config
.setValue("session.doubleClickInterval",
1193 resource
.double_click_interval
);
1194 config
.setValue("session.autoRaiseDelay",
1195 ((resource
.auto_raise_delay
.tv_sec
* 1000) +
1196 (resource
.auto_raise_delay
.tv_usec
/ 1000)));
1197 config
.setValue("session.cacheLife", resource
.cache_life
/ 60000);
1198 config
.setValue("session.cacheMax", resource
.cache_max
);
1199 config
.setValue("session.styleFile", resource
.style_file
);
1200 config
.setValue("session.titlebarLayout", resource
.titlebar_layout
);
1202 std::for_each(screenList
.begin(), screenList
.end(),
1203 std::mem_fun(&BScreen::save_rc
));
1205 config
.setAutoSave(true);
1210 void Blackbox::load_rc(void) {
1211 if (! config
.load())
1216 if (! config
.getValue("session.colorsPerChannel",
1217 resource
.colors_per_channel
))
1218 resource
.colors_per_channel
= 4;
1219 if (resource
.colors_per_channel
< 2) resource
.colors_per_channel
= 2;
1220 else if (resource
.colors_per_channel
> 6) resource
.colors_per_channel
= 6;
1222 if (config
.getValue("session.styleFile", s
))
1223 resource
.style_file
= expandTilde(s
);
1225 resource
.style_file
= DEFAULTSTYLE
;
1227 if (! config
.getValue("session.doubleClickInterval",
1228 resource
.double_click_interval
));
1229 resource
.double_click_interval
= 250;
1231 if (! config
.getValue("session.autoRaiseDelay",
1232 resource
.auto_raise_delay
.tv_usec
))
1233 resource
.auto_raise_delay
.tv_usec
= 400;
1234 resource
.auto_raise_delay
.tv_sec
= resource
.auto_raise_delay
.tv_usec
/ 1000;
1235 resource
.auto_raise_delay
.tv_usec
-=
1236 (resource
.auto_raise_delay
.tv_sec
* 1000);
1237 resource
.auto_raise_delay
.tv_usec
*= 1000;
1239 if (! config
.getValue("session.cacheLife", resource
.cache_life
))
1240 resource
.cache_life
= 5;
1241 resource
.cache_life
*= 60000;
1243 if (! config
.getValue("session.cacheMax", resource
.cache_max
))
1244 resource
.cache_max
= 200;
1246 if (! config
.getValue("session.titlebarLayout", resource
.titlebar_layout
))
1247 resource
.titlebar_layout
= "ILMC";
1251 void Blackbox::reconfigure(void) {
1252 reconfigure_wait
= True
;
1254 if (! timer
->isTiming()) timer
->start();
1258 void Blackbox::real_reconfigure(void) {
1261 std::for_each(menuTimestamps
.begin(), menuTimestamps
.end(),
1263 menuTimestamps
.clear();
1267 std::for_each(screenList
.begin(), screenList
.end(),
1268 std::mem_fun(&BScreen::reconfigure
));
1272 void Blackbox::checkMenu(void) {
1273 bool reread
= False
;
1274 MenuTimestampList::iterator it
= menuTimestamps
.begin();
1275 for(; it
!= menuTimestamps
.end(); ++it
) {
1276 MenuTimestamp
*tmp
= *it
;
1279 if (! stat(tmp
->filename
.c_str(), &buf
)) {
1280 if (tmp
->timestamp
!= buf
.st_ctime
)
1287 if (reread
) rereadMenu();
1291 void Blackbox::rereadMenu(void) {
1292 reread_menu_wait
= True
;
1294 if (! timer
->isTiming()) timer
->start();
1298 void Blackbox::real_rereadMenu(void) {
1299 std::for_each(menuTimestamps
.begin(), menuTimestamps
.end(),
1301 menuTimestamps
.clear();
1303 std::for_each(screenList
.begin(), screenList
.end(),
1304 std::mem_fun(&BScreen::rereadMenu
));
1308 void Blackbox::saveStyleFilename(const string
& filename
) {
1309 assert(! filename
.empty());
1310 resource
.style_file
= filename
;
1311 config
.setValue("session.styleFile", resource
.style_file
);
1315 void Blackbox::addMenuTimestamp(const string
& filename
) {
1316 assert(! filename
.empty());
1319 MenuTimestampList::iterator it
= menuTimestamps
.begin();
1320 for (; it
!= menuTimestamps
.end() && ! found
; ++it
) {
1321 if ((*it
)->filename
== filename
) found
= True
;
1326 if (! stat(filename
.c_str(), &buf
)) {
1327 MenuTimestamp
*ts
= new MenuTimestamp
;
1329 ts
->filename
= filename
;
1330 ts
->timestamp
= buf
.st_ctime
;
1332 menuTimestamps
.push_back(ts
);
1338 void Blackbox::timeout(void) {
1339 if (reconfigure_wait
)
1342 if (reread_menu_wait
)
1345 reconfigure_wait
= reread_menu_wait
= False
;
1349 void Blackbox::setChangingWindow(BlackboxWindow
*win
) {
1350 // make sure one of the two is null and the other isn't
1351 assert((! changing_window
&& win
) || (! win
&& changing_window
));
1352 changing_window
= win
;
1356 void Blackbox::setFocusedWindow(BlackboxWindow
*win
) {
1357 if (focused_window
&& focused_window
== win
) // nothing to do
1360 BScreen
*old_screen
= 0;
1362 if (focused_window
) {
1363 focused_window
->setFocusFlag(False
);
1364 old_screen
= focused_window
->getScreen();
1367 if (win
&& ! win
->isIconic()) {
1368 // the active screen is the one with the last focused window...
1369 // this will keep focus on this screen no matter where the mouse goes,
1370 // so multihead keybindings will continue to work on that screen until the
1371 // user focuses a window on a different screen.
1372 active_screen
= win
->getScreen();
1373 focused_window
= win
;
1377 if (active_screen
) {
1378 // set input focus to the toolbar of the screen with mouse
1379 XSetInputFocus(getXDisplay(),
1380 active_screen
->getRootWindow(),
1381 RevertToPointerRoot
, CurrentTime
);
1383 // set input focus to the toolbar of the first managed screen
1384 XSetInputFocus(getXDisplay(),
1385 screenList
.front()->getRootWindow(),
1386 RevertToPointerRoot
, CurrentTime
);
1389 // set input focus to the toolbar of the last screen
1390 XSetInputFocus(getXDisplay(), old_screen
->getRootWindow(),
1391 RevertToPointerRoot
, CurrentTime
);
1395 if (active_screen
&& active_screen
->isScreenManaged()) {
1396 active_screen
->getToolbar()->redrawWindowLabel(True
);
1397 active_screen
->updateNetizenWindowFocus();
1400 if (old_screen
&& old_screen
!= active_screen
) {
1401 old_screen
->getToolbar()->redrawWindowLabel(True
);
1402 old_screen
->updateNetizenWindowFocus();