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
99 #include "blackbox.hh"
100 #include "basemenu.hh"
101 #include "clientmenu.hh"
102 #include "gccache.hh"
104 #include "rootmenu.hh"
107 #include "toolbar.hh"
110 #include "workspace.hh"
111 #include "workspacemenu.hh"
117 Blackbox::Blackbox(char **m_argv
, char *dpy_name
, char *rc
, char *menu
)
118 : BaseDisplay(m_argv
[0], dpy_name
) {
119 if (! XSupportsLocale())
120 fprintf(stderr
, "X server does not support locale\n");
122 if (XSetLocaleModifiers("") == NULL
)
123 fprintf(stderr
, "cannot set locale modifiers\n");
128 // try to make sure the ~/.openbox directory exists
129 mkdir(expandTilde("~/.openbox").c_str(), S_IREAD
| S_IWRITE
| S_IEXEC
|
130 S_IRGRP
| S_IWGRP
| S_IXGRP
|
131 S_IROTH
| S_IWOTH
| S_IXOTH
);
133 if (! rc
) rc
= "~/.openbox/rc";
134 rc_file
= expandTilde(rc
);
135 config
.setFile(rc_file
);
139 //have to come up with something better than this
141 if (! config
.getValue("session.menuFile", rcmenu
))
142 rcmenu
= "~/.openbox/menu";
146 menu_file
= expandTilde(rcmenu
.c_str());
150 resource
.auto_raise_delay
.tv_sec
= resource
.auto_raise_delay
.tv_usec
= 0;
153 focused_window
= changing_window
= (BlackboxWindow
*) 0;
157 xatom
= new XAtom(getXDisplay());
159 cursor
.session
= XCreateFontCursor(getXDisplay(), XC_left_ptr
);
160 cursor
.move
= XCreateFontCursor(getXDisplay(), XC_fleur
);
161 cursor
.ll_angle
= XCreateFontCursor(getXDisplay(), XC_ll_angle
);
162 cursor
.lr_angle
= XCreateFontCursor(getXDisplay(), XC_lr_angle
);
163 cursor
.ul_angle
= XCreateFontCursor(getXDisplay(), XC_ul_angle
);
164 cursor
.ur_angle
= XCreateFontCursor(getXDisplay(), XC_ur_angle
);
166 for (unsigned int i
= 0; i
< getNumberOfScreens(); i
++) {
167 BScreen
*screen
= new BScreen(this, i
);
169 if (! screen
->isScreenManaged()) {
174 screenList
.push_back(screen
);
177 if (screenList
.empty()) {
179 i18n(blackboxSet
, blackboxNoManagableScreens
,
180 "Blackbox::Blackbox: no managable screens found, aborting.\n"));
184 // save current settings and default values
187 // set the screen with mouse to the first managed screen
188 active_screen
= screenList
.front();
191 XSynchronize(getXDisplay(), False
);
192 XSync(getXDisplay(), False
);
194 reconfigure_wait
= reread_menu_wait
= False
;
196 timer
= new BTimer(this, this);
197 timer
->setTimeout(0l);
201 Blackbox::~Blackbox(void) {
202 std::for_each(screenList
.begin(), screenList
.end(), PointerAssassin());
204 std::for_each(menuTimestamps
.begin(), menuTimestamps
.end(),
213 void Blackbox::process_event(XEvent
*e
) {
216 // strip the lock key modifiers
217 e
->xbutton
.state
&= ~(NumLockMask
| ScrollLockMask
| LockMask
);
219 last_time
= e
->xbutton
.time
;
221 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
222 Basemenu
*menu
= (Basemenu
*) 0;
223 Slit
*slit
= (Slit
*) 0;
224 Toolbar
*tbar
= (Toolbar
*) 0;
225 BScreen
*scrn
= (BScreen
*) 0;
227 if ((win
= searchWindow(e
->xbutton
.window
))) {
228 win
->buttonPressEvent(&e
->xbutton
);
230 /* XXX: is this sane on low colour desktops? */
231 if (e
->xbutton
.button
== 1)
232 win
->installColormap(True
);
233 } else if ((menu
= searchMenu(e
->xbutton
.window
))) {
234 menu
->buttonPressEvent(&e
->xbutton
);
235 } else if ((slit
= searchSlit(e
->xbutton
.window
))) {
236 slit
->buttonPressEvent(&e
->xbutton
);
237 } else if ((tbar
= searchToolbar(e
->xbutton
.window
))) {
238 tbar
->buttonPressEvent(&e
->xbutton
);
239 } else if ((scrn
= searchScreen(e
->xbutton
.window
))) {
240 scrn
->buttonPressEvent(&e
->xbutton
);
241 if (active_screen
!= scrn
) {
242 active_screen
= scrn
;
243 // first, set no focus window on the old screen
245 // and move focus to this screen
252 case ButtonRelease
: {
253 // strip the lock key modifiers
254 e
->xbutton
.state
&= ~(NumLockMask
| ScrollLockMask
| LockMask
);
256 last_time
= e
->xbutton
.time
;
258 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
259 Basemenu
*menu
= (Basemenu
*) 0;
260 Toolbar
*tbar
= (Toolbar
*) 0;
262 if ((win
= searchWindow(e
->xbutton
.window
)))
263 win
->buttonReleaseEvent(&e
->xbutton
);
264 else if ((menu
= searchMenu(e
->xbutton
.window
)))
265 menu
->buttonReleaseEvent(&e
->xbutton
);
266 else if ((tbar
= searchToolbar(e
->xbutton
.window
)))
267 tbar
->buttonReleaseEvent(&e
->xbutton
);
272 case ConfigureRequest
: {
273 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
274 Slit
*slit
= (Slit
*) 0;
276 if ((win
= searchWindow(e
->xconfigurerequest
.window
))) {
277 win
->configureRequestEvent(&e
->xconfigurerequest
);
278 } else if ((slit
= searchSlit(e
->xconfigurerequest
.window
))) {
279 slit
->configureRequestEvent(&e
->xconfigurerequest
);
281 if (validateWindow(e
->xconfigurerequest
.window
)) {
284 xwc
.x
= e
->xconfigurerequest
.x
;
285 xwc
.y
= e
->xconfigurerequest
.y
;
286 xwc
.width
= e
->xconfigurerequest
.width
;
287 xwc
.height
= e
->xconfigurerequest
.height
;
288 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
289 xwc
.sibling
= e
->xconfigurerequest
.above
;
290 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
292 XConfigureWindow(getXDisplay(), e
->xconfigurerequest
.window
,
293 e
->xconfigurerequest
.value_mask
, &xwc
);
302 fprintf(stderr
, "Blackbox::process_event(): MapRequest for 0x%lx\n",
303 e
->xmaprequest
.window
);
306 BlackboxWindow
*win
= searchWindow(e
->xmaprequest
.window
);
310 if (win
->isIconic()) {
314 if (win
->isShaded()) {
319 if (focus
&& (win
->isTransient() || win
->getScreen()->doFocusNew()) &&
321 win
->setInputFocus();
323 BScreen
*screen
= searchScreen(e
->xmaprequest
.parent
);
327 we got a map request for a window who's parent isn't root. this
328 can happen in only one circumstance:
330 a client window unmapped a managed window, and then remapped it
331 somewhere between unmapping the client window and reparenting it
334 regardless of how it happens, we need to find the screen that
337 XWindowAttributes wattrib
;
338 if (! XGetWindowAttributes(getXDisplay(), e
->xmaprequest
.window
,
340 // failed to get the window attributes, perhaps the window has
341 // now been destroyed?
345 screen
= searchScreen(wattrib
.root
);
346 assert(screen
!= 0); // this should never happen
349 screen
->manageWindow(e
->xmaprequest
.window
);
356 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
357 Slit
*slit
= (Slit
*) 0;
358 BScreen
*screen
= (BScreen
*) 0;
360 if ((win
= searchWindow(e
->xunmap
.window
))) {
361 win
->unmapNotifyEvent(&e
->xunmap
);
362 } else if ((slit
= searchSlit(e
->xunmap
.window
))) {
363 slit
->unmapNotifyEvent(&e
->xunmap
);
364 } else if ((screen
= searchSystrayWindow(e
->xunmap
.window
))) {
365 screen
->removeSystrayWindow(e
->xunmap
.window
);
371 case DestroyNotify
: {
372 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
373 Slit
*slit
= (Slit
*) 0;
374 BScreen
*screen
= (BScreen
*) 0;
375 BWindowGroup
*group
= (BWindowGroup
*) 0;
377 if ((win
= searchWindow(e
->xdestroywindow
.window
))) {
378 win
->destroyNotifyEvent(&e
->xdestroywindow
);
379 } else if ((slit
= searchSlit(e
->xdestroywindow
.window
))) {
380 slit
->removeClient(e
->xdestroywindow
.window
, False
);
381 } else if ((group
= searchGroup(e
->xdestroywindow
.window
))) {
383 } else if ((screen
= searchSystrayWindow(e
->xunmap
.window
))) {
384 screen
->removeSystrayWindow(e
->xunmap
.window
);
390 case ReparentNotify
: {
392 this event is quite rare and is usually handled in unmapNotify
393 however, if the window is unmapped when the reparent event occurs
394 the window manager never sees it because an unmap event is not sent
395 to an already unmapped window.
397 BlackboxWindow
*win
= searchWindow(e
->xreparent
.window
);
399 win
->reparentNotifyEvent(&e
->xreparent
);
401 Slit
*slit
= searchSlit(e
->xreparent
.window
);
402 if (slit
&& slit
->getWindowID() != e
->xreparent
.parent
)
403 slit
->removeClient(e
->xreparent
.window
, True
);
409 // motion notify compression...
412 while (XCheckTypedWindowEvent(getXDisplay(), e
->xmotion
.window
,
413 MotionNotify
, &realevent
)) {
417 // if we have compressed some motion events, use the last one
421 // the pointer is on the wrong screen
422 if (! e
->xmotion
.same_screen
)
425 // strip the lock key modifiers
426 e
->xmotion
.state
&= ~(NumLockMask
| ScrollLockMask
| LockMask
);
428 last_time
= e
->xmotion
.time
;
430 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
431 Basemenu
*menu
= (Basemenu
*) 0;
433 if ((win
= searchWindow(e
->xmotion
.window
)))
434 win
->motionNotifyEvent(&e
->xmotion
);
435 else if ((menu
= searchMenu(e
->xmotion
.window
)))
436 menu
->motionNotifyEvent(&e
->xmotion
);
441 case PropertyNotify
: {
442 last_time
= e
->xproperty
.time
;
444 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
445 BScreen
*screen
= (BScreen
*) 0;
447 if ((win
= searchWindow(e
->xproperty
.window
)))
448 win
->propertyNotifyEvent(&e
->xproperty
);
449 else if ((screen
= searchScreen(e
->xproperty
.window
)))
450 screen
->propertyNotifyEvent(&e
->xproperty
);
455 last_time
= e
->xcrossing
.time
;
457 BScreen
*screen
= (BScreen
*) 0;
458 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
459 Basemenu
*menu
= (Basemenu
*) 0;
460 Toolbar
*tbar
= (Toolbar
*) 0;
461 Slit
*slit
= (Slit
*) 0;
463 if (e
->xcrossing
.mode
== NotifyGrab
) break;
465 if ((e
->xcrossing
.window
== e
->xcrossing
.root
) &&
466 (screen
= searchScreen(e
->xcrossing
.window
))) {
467 screen
->getImageControl()->installRootColormap();
468 } else if ((win
= searchWindow(e
->xcrossing
.window
))) {
470 win
->enterNotifyEvent(&e
->xcrossing
);
471 } else if ((menu
= searchMenu(e
->xcrossing
.window
))) {
472 menu
->enterNotifyEvent(&e
->xcrossing
);
473 } else if ((tbar
= searchToolbar(e
->xcrossing
.window
))) {
474 tbar
->enterNotifyEvent(&e
->xcrossing
);
475 } else if ((slit
= searchSlit(e
->xcrossing
.window
))) {
476 slit
->enterNotifyEvent(&e
->xcrossing
);
482 last_time
= e
->xcrossing
.time
;
484 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
485 Basemenu
*menu
= (Basemenu
*) 0;
486 Toolbar
*tbar
= (Toolbar
*) 0;
487 Slit
*slit
= (Slit
*) 0;
489 if ((menu
= searchMenu(e
->xcrossing
.window
)))
490 menu
->leaveNotifyEvent(&e
->xcrossing
);
491 else if ((win
= searchWindow(e
->xcrossing
.window
)))
492 win
->leaveNotifyEvent(&e
->xcrossing
);
493 else if ((tbar
= searchToolbar(e
->xcrossing
.window
)))
494 tbar
->leaveNotifyEvent(&e
->xcrossing
);
495 else if ((slit
= searchSlit(e
->xcrossing
.window
)))
496 slit
->leaveNotifyEvent(&e
->xcrossing
);
501 // compress expose events
504 int ex1
, ey1
, ex2
, ey2
;
507 ex2
= ex1
+ e
->xexpose
.width
- 1;
508 ey2
= ey1
+ e
->xexpose
.height
- 1;
509 while (XCheckTypedWindowEvent(getXDisplay(), e
->xexpose
.window
,
510 Expose
, &realevent
)) {
514 ex1
= std::min(realevent
.xexpose
.x
, ex1
);
515 ey1
= std::min(realevent
.xexpose
.y
, ey1
);
516 ex2
= std::max(realevent
.xexpose
.x
+ realevent
.xexpose
.width
- 1, ex2
);
517 ey2
= std::max(realevent
.xexpose
.y
+ realevent
.xexpose
.height
- 1, ey2
);
522 // use the merged area
525 e
->xexpose
.width
= ex2
- ex1
+ 1;
526 e
->xexpose
.height
= ey2
- ey1
+ 1;
528 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
529 Basemenu
*menu
= (Basemenu
*) 0;
530 Toolbar
*tbar
= (Toolbar
*) 0;
532 if ((win
= searchWindow(e
->xexpose
.window
)))
533 win
->exposeEvent(&e
->xexpose
);
534 else if ((menu
= searchMenu(e
->xexpose
.window
)))
535 menu
->exposeEvent(&e
->xexpose
);
536 else if ((tbar
= searchToolbar(e
->xexpose
.window
)))
537 tbar
->exposeEvent(&e
->xexpose
);
543 Toolbar
*tbar
= searchToolbar(e
->xkey
.window
);
545 if (tbar
&& tbar
->isEditing())
546 tbar
->keyPressEvent(&e
->xkey
);
551 case ColormapNotify
: {
552 BScreen
*screen
= searchScreen(e
->xcolormap
.window
);
555 screen
->setRootColormapInstalled((e
->xcolormap
.state
==
556 ColormapInstalled
) ? True
: False
);
562 if (e
->xfocus
.detail
!= NotifyNonlinear
&&
563 e
->xfocus
.detail
!= NotifyAncestor
) {
565 don't process FocusIns when:
566 1. the new focus window isn't an ancestor or inferior of the old
567 focus window (NotifyNonlinear)
568 make sure to allow the FocusIn when the old focus window was an
569 ancestor but didn't have a parent, such as root (NotifyAncestor)
574 BlackboxWindow
*win
= searchWindow(e
->xfocus
.window
);
576 if (! win
->isFocused())
577 win
->setFocusFlag(True
);
580 set the event window to None. when the FocusOut event handler calls
581 this function recursively, it uses this as an indication that focus
582 has moved to a known window.
584 e
->xfocus
.window
= None
;
586 no_focus
= False
; // focusing is back on
593 if (e
->xfocus
.detail
!= NotifyNonlinear
) {
595 don't process FocusOuts when:
596 2. the new focus window isn't an ancestor or inferior of the old
597 focus window (NotifyNonlinear)
602 BlackboxWindow
*win
= searchWindow(e
->xfocus
.window
);
603 if (win
&& win
->isFocused()) {
605 before we mark "win" as unfocused, we need to verify that focus is
606 going to a known location, is in a known location, or set focus
611 // don't check the current focus if FocusOut was generated during a grab
612 bool check_focus
= (e
->xfocus
.mode
== NotifyNormal
);
615 First, check if there is a pending FocusIn event waiting. if there
616 is, process it and determine if focus has moved to another window
617 (the FocusIn event handler sets the window in the event
618 structure to None to indicate this).
620 if (XCheckTypedEvent(getXDisplay(), FocusIn
, &event
)) {
622 process_event(&event
);
623 if (event
.xfocus
.window
== None
) {
631 Second, we query the X server for the current input focus.
632 to make sure that we keep a consistent state.
634 BlackboxWindow
*focus
;
637 XGetInputFocus(getXDisplay(), &w
, &revert
);
638 focus
= searchWindow(w
);
641 focus got from "win" to "focus" under some very strange
642 circumstances, and we need to make sure that the focus indication
645 setFocusedWindow(focus
);
647 // we have no idea where focus went... so we set it to somewhere
656 case ClientMessage
: {
657 if (e
->xclient
.format
== 32) {
658 if (e
->xclient
.message_type
== xatom
->getAtom(XAtom::wm_change_state
)) {
659 // WM_CHANGE_STATE message
660 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
661 if (! win
|| ! win
->validateClient()) return;
663 if (e
->xclient
.data
.l
[0] == IconicState
)
665 if (e
->xclient
.data
.l
[0] == NormalState
)
667 } else if (e
->xclient
.message_type
==
668 xatom
->getAtom(XAtom::blackbox_change_workspace
) ||
669 e
->xclient
.message_type
==
670 xatom
->getAtom(XAtom::net_current_desktop
)) {
671 // NET_CURRENT_DESKTOP message
672 BScreen
*screen
= searchScreen(e
->xclient
.window
);
674 unsigned int workspace
= e
->xclient
.data
.l
[0];
675 if (screen
&& workspace
< screen
->getWorkspaceCount())
676 screen
->changeWorkspaceID(workspace
);
677 } else if (e
->xclient
.message_type
==
678 xatom
->getAtom(XAtom::blackbox_change_window_focus
)) {
679 // TEMP HACK TO KEEP BBKEYS WORKING
680 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
682 if (win
&& win
->isVisible() && win
->setInputFocus())
683 win
->installColormap(True
);
684 } else if (e
->xclient
.message_type
==
685 xatom
->getAtom(XAtom::net_active_window
)) {
687 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
690 BScreen
*screen
= win
->getScreen();
693 win
->deiconify(False
, False
);
694 if (! win
->isStuck() &&
695 (win
->getWorkspaceNumber() != screen
->getCurrentWorkspaceID())) {
697 screen
->changeWorkspaceID(win
->getWorkspaceNumber());
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 wksp
== 0xffffffff) {
734 if (win
->isIconic()) win
->deiconify(False
, True
);
735 if (! win
->isStuck()) win
->stick();
736 if (! win
->isVisible()) win
->show();
739 } else if (e
->xclient
.message_type
==
740 xatom
->getAtom(XAtom::blackbox_change_attributes
)) {
741 // BLACKBOX_CHANGE_ATTRIBUTES
742 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
744 if (win
&& win
->validateClient()) {
746 net
.flags
= e
->xclient
.data
.l
[0];
747 net
.attrib
= e
->xclient
.data
.l
[1];
748 net
.workspace
= e
->xclient
.data
.l
[2];
749 net
.stack
= e
->xclient
.data
.l
[3];
750 net
.decoration
= e
->xclient
.data
.l
[4];
752 win
->changeBlackboxHints(&net
);
754 } else if (e
->xclient
.message_type
==
755 xatom
->getAtom(XAtom::net_number_of_desktops
)) {
756 // NET_NUMBER_OF_DESKTOPS
757 BScreen
*screen
= searchScreen(e
->xclient
.window
);
759 if (e
->xclient
.data
.l
[0] > 0)
760 screen
->changeWorkspaceCount((unsigned) e
->xclient
.data
.l
[0]);
761 } else if (e
->xclient
.message_type
==
762 xatom
->getAtom(XAtom::net_close_window
)) {
764 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
765 if (win
&& win
->validateClient())
766 win
->close(); // could this be smarter?
767 } else if (e
->xclient
.message_type
==
768 xatom
->getAtom(XAtom::net_wm_moveresize
)) {
770 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
771 if (win
&& win
->validateClient()) {
772 int x_root
= e
->xclient
.data
.l
[0],
773 y_root
= e
->xclient
.data
.l
[1];
774 if ((Atom
) e
->xclient
.data
.l
[2] ==
775 xatom
->getAtom(XAtom::net_wm_moveresize_move
)) {
776 win
->beginMove(x_root
, y_root
);
778 if ((Atom
) e
->xclient
.data
.l
[2] ==
779 xatom
->getAtom(XAtom::net_wm_moveresize_size_topleft
))
780 win
->beginResize(x_root
, y_root
, BlackboxWindow::TopLeft
);
781 else if ((Atom
) e
->xclient
.data
.l
[2] ==
782 xatom
->getAtom(XAtom::net_wm_moveresize_size_topright
))
783 win
->beginResize(x_root
, y_root
, BlackboxWindow::TopRight
);
784 else if ((Atom
) e
->xclient
.data
.l
[2] ==
785 xatom
->getAtom(XAtom::net_wm_moveresize_size_bottomleft
))
786 win
->beginResize(x_root
, y_root
, BlackboxWindow::BottomLeft
);
787 else if ((Atom
) e
->xclient
.data
.l
[2] ==
788 xatom
->getAtom(XAtom::net_wm_moveresize_size_bottomright
))
789 win
->beginResize(x_root
, y_root
, BlackboxWindow::BottomRight
);
792 } else if (e
->xclient
.message_type
==
793 xatom
->getAtom(XAtom::net_wm_state
)) {
795 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
796 if (win
&& win
->validateClient()) {
797 const Atom action
= (Atom
) e
->xclient
.data
.l
[0];
798 const Atom state
[] = { (Atom
) e
->xclient
.data
.l
[1],
799 (Atom
) e
->xclient
.data
.l
[2] };
801 for (int i
= 0; i
< 2; ++i
) {
805 if ((Atom
) e
->xclient
.data
.l
[0] == 1) {
807 if (state
[i
] == xatom
->getAtom(XAtom::net_wm_state_modal
)) {
809 } else if (state
[i
] ==
810 xatom
->getAtom(XAtom::net_wm_state_maximized_vert
)) {
811 if (win
->isMaximizedHoriz()) {
812 win
->maximize(0); // unmaximize
813 win
->maximize(1); // full
814 } else if (! win
->isMaximized()) {
815 win
->maximize(2); // vert
817 } else if (state
[i
] ==
818 xatom
->getAtom(XAtom::net_wm_state_maximized_horz
)) {
819 if (win
->isMaximizedVert()) {
820 win
->maximize(0); // unmaximize
821 win
->maximize(1); // full
822 } else if (! win
->isMaximized()) {
823 win
->maximize(3); // horiz
825 } else if (state
[i
] ==
826 xatom
->getAtom(XAtom::net_wm_state_shaded
)) {
827 if (! win
->isShaded())
829 } else if (state
[i
] ==
830 xatom
->getAtom(XAtom::net_wm_state_skip_taskbar
)) {
831 win
->setSkipTaskbar(True
);
832 } else if (state
[i
] ==
833 xatom
->getAtom(XAtom::net_wm_state_skip_pager
)) {
834 win
->setSkipPager(True
);
835 } else if (state
[i
] ==
836 xatom
->getAtom(XAtom::net_wm_state_fullscreen
)) {
837 win
->setFullscreen(True
);
839 } else if (action
== 0) {
841 if (state
[i
] == xatom
->getAtom(XAtom::net_wm_state_modal
)) {
842 win
->setModal(False
);
843 } else if (state
[i
] ==
844 xatom
->getAtom(XAtom::net_wm_state_maximized_vert
)) {
845 if (win
->isMaximizedFull()) {
846 win
->maximize(0); // unmaximize
847 win
->maximize(3); // horiz
848 } else if (win
->isMaximizedVert()) {
849 win
->maximize(0); // unmaximize
851 } else if (state
[i
] ==
852 xatom
->getAtom(XAtom::net_wm_state_maximized_horz
)) {
853 if (win
->isMaximizedFull()) {
854 win
->maximize(0); // unmaximize
855 win
->maximize(2); // vert
856 } else if (win
->isMaximizedHoriz()) {
857 win
->maximize(0); // unmaximize
859 } else if (state
[i
] ==
860 xatom
->getAtom(XAtom::net_wm_state_shaded
)) {
863 } else if (state
[i
] ==
864 xatom
->getAtom(XAtom::net_wm_state_skip_taskbar
)) {
865 win
->setSkipTaskbar(False
);
866 } else if (state
[i
] ==
867 xatom
->getAtom(XAtom::net_wm_state_skip_pager
)) {
868 win
->setSkipPager(False
);
869 } else if (state
[i
] ==
870 xatom
->getAtom(XAtom::net_wm_state_fullscreen
)) {
871 win
->setFullscreen(False
);
873 } else if (action
== 2) {
875 if (state
[i
] == xatom
->getAtom(XAtom::net_wm_state_modal
)) {
876 win
->setModal(! win
->isModal());
877 } else if (state
[i
] ==
878 xatom
->getAtom(XAtom::net_wm_state_maximized_vert
)) {
879 if (win
->isMaximizedFull()) {
880 win
->maximize(0); // unmaximize
881 win
->maximize(3); // horiz
882 } else if (win
->isMaximizedVert()) {
883 win
->maximize(0); // unmaximize
884 } else if (win
->isMaximizedHoriz()) {
885 win
->maximize(0); // unmaximize
886 win
->maximize(1); // full
888 win
->maximize(2); // vert
890 } else if (state
[i
] ==
891 xatom
->getAtom(XAtom::net_wm_state_maximized_horz
)) {
892 if (win
->isMaximizedFull()) {
893 win
->maximize(0); // unmaximize
894 win
->maximize(2); // vert
895 } else if (win
->isMaximizedHoriz()) {
896 win
->maximize(0); // unmaximize
897 } else if (win
->isMaximizedVert()) {
898 win
->maximize(0); // unmaximize
899 win
->maximize(1); // full
901 win
->maximize(3); // horiz
903 } else if (state
[i
] ==
904 xatom
->getAtom(XAtom::net_wm_state_shaded
)) {
906 } else if (state
[i
] ==
907 xatom
->getAtom(XAtom::net_wm_state_skip_taskbar
)) {
908 win
->setSkipTaskbar(! win
->skipTaskbar());
909 } else if (state
[i
] ==
910 xatom
->getAtom(XAtom::net_wm_state_skip_pager
)) {
911 win
->setSkipPager(! win
->skipPager());
912 } else if (state
[i
] ==
913 xatom
->getAtom(XAtom::net_wm_state_fullscreen
)) {
914 win
->setFullscreen(! win
->isFullscreen());
919 } else if (e
->xclient
.message_type
==
920 xatom
->getAtom(XAtom::openbox_show_root_menu
) ||
921 e
->xclient
.message_type
==
922 xatom
->getAtom(XAtom::openbox_show_workspace_menu
)) {
923 // find the screen the mouse is on
925 ScreenList::iterator it
, end
= screenList
.end();
926 for (it
= screenList
.begin(); it
!= end
; ++it
) {
930 if (XQueryPointer(getXDisplay(), (*it
)->getRootWindow(),
931 &w
, &w
, &x
, &y
, &i
, &i
, &m
))
935 if (e
->xclient
.message_type
==
936 xatom
->getAtom(XAtom::openbox_show_root_menu
))
937 (*it
)->showRootMenu(x
, y
);
939 (*it
)->showWorkspaceMenu(x
, y
);
948 case ConfigureNotify
:
950 break; // not handled, just ignore
954 if (e
->type
== getShapeEventBase()) {
955 XShapeEvent
*shape_event
= (XShapeEvent
*) e
;
956 BlackboxWindow
*win
= searchWindow(e
->xany
.window
);
958 if (win
&& shape_event
->kind
== ShapeBounding
)
959 win
->shapeEvent(shape_event
);
967 bool Blackbox::handleSignal(int sig
) {
996 bool Blackbox::validateWindow(Window window
) {
998 if (XCheckTypedWindowEvent(getXDisplay(), window
, DestroyNotify
, &event
)) {
999 XPutBackEvent(getXDisplay(), &event
);
1008 BScreen
*Blackbox::searchScreen(Window window
) {
1009 ScreenList::iterator it
= screenList
.begin();
1011 for (; it
!= screenList
.end(); ++it
) {
1013 if (s
->getRootWindow() == window
)
1017 return (BScreen
*) 0;
1021 BScreen
*Blackbox::searchSystrayWindow(Window window
) {
1022 WindowScreenLookup::iterator it
= systraySearchList
.find(window
);
1023 if (it
!= systraySearchList
.end())
1026 return (BScreen
*) 0;
1030 BlackboxWindow
*Blackbox::searchWindow(Window window
) {
1031 WindowLookup::iterator it
= windowSearchList
.find(window
);
1032 if (it
!= windowSearchList
.end())
1035 return (BlackboxWindow
*) 0;
1039 BWindowGroup
*Blackbox::searchGroup(Window window
) {
1040 GroupLookup::iterator it
= groupSearchList
.find(window
);
1041 if (it
!= groupSearchList
.end())
1044 return (BWindowGroup
*) 0;
1048 Basemenu
*Blackbox::searchMenu(Window window
) {
1049 MenuLookup::iterator it
= menuSearchList
.find(window
);
1050 if (it
!= menuSearchList
.end())
1053 return (Basemenu
*) 0;
1057 Toolbar
*Blackbox::searchToolbar(Window window
) {
1058 ToolbarLookup::iterator it
= toolbarSearchList
.find(window
);
1059 if (it
!= toolbarSearchList
.end())
1062 return (Toolbar
*) 0;
1066 Slit
*Blackbox::searchSlit(Window window
) {
1067 SlitLookup::iterator it
= slitSearchList
.find(window
);
1068 if (it
!= slitSearchList
.end())
1075 void Blackbox::saveSystrayWindowSearch(Window window
, BScreen
*screen
) {
1076 systraySearchList
.insert(WindowScreenLookupPair(window
, screen
));
1080 void Blackbox::saveWindowSearch(Window window
, BlackboxWindow
*data
) {
1081 windowSearchList
.insert(WindowLookupPair(window
, data
));
1085 void Blackbox::saveGroupSearch(Window window
, BWindowGroup
*data
) {
1086 groupSearchList
.insert(GroupLookupPair(window
, data
));
1090 void Blackbox::saveMenuSearch(Window window
, Basemenu
*data
) {
1091 menuSearchList
.insert(MenuLookupPair(window
, data
));
1095 void Blackbox::saveToolbarSearch(Window window
, Toolbar
*data
) {
1096 toolbarSearchList
.insert(ToolbarLookupPair(window
, data
));
1100 void Blackbox::saveSlitSearch(Window window
, Slit
*data
) {
1101 slitSearchList
.insert(SlitLookupPair(window
, data
));
1105 void Blackbox::removeSystrayWindowSearch(Window window
) {
1106 systraySearchList
.erase(window
);
1110 void Blackbox::removeWindowSearch(Window window
) {
1111 windowSearchList
.erase(window
);
1115 void Blackbox::removeGroupSearch(Window window
) {
1116 groupSearchList
.erase(window
);
1120 void Blackbox::removeMenuSearch(Window window
) {
1121 menuSearchList
.erase(window
);
1125 void Blackbox::removeToolbarSearch(Window window
) {
1126 toolbarSearchList
.erase(window
);
1130 void Blackbox::removeSlitSearch(Window window
) {
1131 slitSearchList
.erase(window
);
1135 void Blackbox::restart(const char *prog
) {
1139 putenv(const_cast<char *>(screenList
.front()->displayString().c_str()));
1140 execlp(prog
, prog
, NULL
);
1144 // fall back in case the above execlp doesn't work
1145 execvp(argv
[0], argv
);
1146 string name
= basename(argv
[0]);
1147 execvp(name
.c_str(), argv
);
1151 void Blackbox::shutdown(void) {
1152 BaseDisplay::shutdown();
1154 XSetInputFocus(getXDisplay(), PointerRoot
, None
, CurrentTime
);
1156 std::for_each(screenList
.begin(), screenList
.end(),
1157 std::mem_fun(&BScreen::shutdown
));
1159 XSync(getXDisplay(), False
);
1164 void Blackbox::saveXineramaPlacement(bool x
) {
1165 resource
.xinerama_placement
= x
;
1166 config
.setValue("session.xineramaSupport.windowPlacement",
1167 resource
.xinerama_placement
);
1168 reconfigure(); // make sure all screens get this change
1172 void Blackbox::saveXineramaMaximizing(bool x
) {
1173 resource
.xinerama_maximize
= x
;
1174 config
.setValue("session.xineramaSupport.windowMaximizing",
1175 resource
.xinerama_maximize
);
1176 reconfigure(); // make sure all screens get this change
1180 void Blackbox::saveXineramaSnapping(bool x
) {
1181 resource
.xinerama_snap
= x
;
1182 config
.setValue("session.xineramaSupport.windowSnapping",
1183 resource
.xinerama_snap
);
1184 reconfigure(); // make sure all screens get this change
1190 * Save all values as they are so that the defaults will be written to the rc
1193 void Blackbox::save_rc(void) {
1194 config
.setAutoSave(false);
1196 config
.setValue("session.colorsPerChannel", resource
.colors_per_channel
);
1197 config
.setValue("session.doubleClickInterval",
1198 resource
.double_click_interval
);
1199 config
.setValue("session.autoRaiseDelay",
1200 ((resource
.auto_raise_delay
.tv_sec
* 1000) +
1201 (resource
.auto_raise_delay
.tv_usec
/ 1000)));
1202 config
.setValue("session.cacheLife", resource
.cache_life
/ 60000);
1203 config
.setValue("session.cacheMax", resource
.cache_max
);
1204 config
.setValue("session.styleFile", resource
.style_file
);
1205 config
.setValue("session.titlebarLayout", resource
.titlebar_layout
);
1208 if (resource
.mod_mask
& Mod1Mask
) s
+= "Mod1-";
1209 if (resource
.mod_mask
& Mod2Mask
) s
+= "Mod2-";
1210 if (resource
.mod_mask
& Mod3Mask
) s
+= "Mod3-";
1211 if (resource
.mod_mask
& Mod4Mask
) s
+= "Mod4-";
1212 if (resource
.mod_mask
& Mod5Mask
) s
+= "Mod5-";
1213 if (resource
.mod_mask
& ShiftMask
) s
+= "Shift-";
1214 if (resource
.mod_mask
& ControlMask
) s
+= "Control-";
1215 s
.resize(s
.size() - 1); // drop the last '-'
1216 config
.setValue("session.modifierMask", s
);
1219 saveXineramaPlacement(resource
.xinerama_placement
);
1220 saveXineramaMaximizing(resource
.xinerama_maximize
);
1221 saveXineramaSnapping(resource
.xinerama_snap
);
1224 std::for_each(screenList
.begin(), screenList
.end(),
1225 std::mem_fun(&BScreen::save_rc
));
1227 config
.setAutoSave(true);
1232 void Blackbox::load_rc(void) {
1233 if (! config
.load())
1238 if (! config
.getValue("session.colorsPerChannel",
1239 resource
.colors_per_channel
))
1240 resource
.colors_per_channel
= 4;
1241 if (resource
.colors_per_channel
< 2) resource
.colors_per_channel
= 2;
1242 else if (resource
.colors_per_channel
> 6) resource
.colors_per_channel
= 6;
1244 if (config
.getValue("session.styleFile", s
))
1245 resource
.style_file
= expandTilde(s
);
1247 resource
.style_file
= DEFAULTSTYLE
;
1249 if (! config
.getValue("session.doubleClickInterval",
1250 resource
.double_click_interval
));
1251 resource
.double_click_interval
= 250;
1253 if (! config
.getValue("session.autoRaiseDelay",
1254 resource
.auto_raise_delay
.tv_usec
))
1255 resource
.auto_raise_delay
.tv_usec
= 400;
1256 resource
.auto_raise_delay
.tv_sec
= resource
.auto_raise_delay
.tv_usec
/ 1000;
1257 resource
.auto_raise_delay
.tv_usec
-=
1258 (resource
.auto_raise_delay
.tv_sec
* 1000);
1259 resource
.auto_raise_delay
.tv_usec
*= 1000;
1261 if (! config
.getValue("session.cacheLife", resource
.cache_life
))
1262 resource
.cache_life
= 5;
1263 resource
.cache_life
*= 60000;
1265 if (! config
.getValue("session.cacheMax", resource
.cache_max
))
1266 resource
.cache_max
= 200;
1268 if (! config
.getValue("session.titlebarLayout", resource
.titlebar_layout
))
1269 resource
.titlebar_layout
= "ILMC";
1272 if (! config
.getValue("session.xineramaSupport.windowPlacement",
1273 resource
.xinerama_placement
))
1274 resource
.xinerama_placement
= false;
1276 if (! config
.getValue("session.xineramaSupport.windowMaximizing",
1277 resource
.xinerama_maximize
))
1278 resource
.xinerama_maximize
= false;
1280 if (! config
.getValue("session.xineramaSupport.windowSnapping",
1281 resource
.xinerama_snap
))
1282 resource
.xinerama_snap
= false;
1285 resource
.mod_mask
= 0;
1286 if (config
.getValue("session.modifierMask", s
)) {
1287 if (s
.find("Mod1") != string::npos
)
1288 resource
.mod_mask
|= Mod1Mask
;
1289 if (s
.find("Mod2") != string::npos
)
1290 resource
.mod_mask
|= Mod2Mask
;
1291 if (s
.find("Mod3") != string::npos
)
1292 resource
.mod_mask
|= Mod3Mask
;
1293 if (s
.find("Mod4") != string::npos
)
1294 resource
.mod_mask
|= Mod4Mask
;
1295 if (s
.find("Mod5") != string::npos
)
1296 resource
.mod_mask
|= Mod5Mask
;
1297 if (s
.find("Shift") != string::npos
)
1298 resource
.mod_mask
|= ShiftMask
;
1299 if (s
.find("Control") != string::npos
)
1300 resource
.mod_mask
|= ControlMask
;
1302 if (! resource
.mod_mask
)
1303 resource
.mod_mask
= Mod1Mask
;
1307 void Blackbox::reconfigure(void) {
1308 // don't reconfigure while saving the initial rc file, it's a waste and it
1309 // breaks somethings (workspace names)
1310 if (isStartup()) return;
1312 reconfigure_wait
= True
;
1314 if (! timer
->isTiming()) timer
->start();
1318 void Blackbox::real_reconfigure(void) {
1321 std::for_each(menuTimestamps
.begin(), menuTimestamps
.end(),
1323 menuTimestamps
.clear();
1327 std::for_each(screenList
.begin(), screenList
.end(),
1328 std::mem_fun(&BScreen::reconfigure
));
1332 void Blackbox::checkMenu(void) {
1333 bool reread
= False
;
1334 MenuTimestampList::iterator it
= menuTimestamps
.begin();
1335 for(; it
!= menuTimestamps
.end(); ++it
) {
1336 MenuTimestamp
*tmp
= *it
;
1339 if (! stat(tmp
->filename
.c_str(), &buf
)) {
1340 if (tmp
->timestamp
!= buf
.st_ctime
)
1347 if (reread
) rereadMenu();
1351 void Blackbox::rereadMenu(void) {
1352 reread_menu_wait
= True
;
1354 if (! timer
->isTiming()) timer
->start();
1358 void Blackbox::real_rereadMenu(void) {
1359 std::for_each(menuTimestamps
.begin(), menuTimestamps
.end(),
1361 menuTimestamps
.clear();
1363 std::for_each(screenList
.begin(), screenList
.end(),
1364 std::mem_fun(&BScreen::rereadMenu
));
1368 void Blackbox::saveStyleFilename(const string
& filename
) {
1369 assert(! filename
.empty());
1370 resource
.style_file
= filename
;
1371 config
.setValue("session.styleFile", resource
.style_file
);
1375 void Blackbox::addMenuTimestamp(const string
& filename
) {
1376 assert(! filename
.empty());
1379 MenuTimestampList::iterator it
= menuTimestamps
.begin();
1380 for (; it
!= menuTimestamps
.end() && ! found
; ++it
) {
1381 if ((*it
)->filename
== filename
) found
= True
;
1386 if (! stat(filename
.c_str(), &buf
)) {
1387 MenuTimestamp
*ts
= new MenuTimestamp
;
1389 ts
->filename
= filename
;
1390 ts
->timestamp
= buf
.st_ctime
;
1392 menuTimestamps
.push_back(ts
);
1398 void Blackbox::timeout(void) {
1399 if (reconfigure_wait
)
1402 if (reread_menu_wait
)
1405 reconfigure_wait
= reread_menu_wait
= False
;
1409 void Blackbox::setChangingWindow(BlackboxWindow
*win
) {
1410 // make sure one of the two is null and the other isn't
1411 assert((! changing_window
&& win
) || (! win
&& changing_window
));
1412 changing_window
= win
;
1416 void Blackbox::setFocusedWindow(BlackboxWindow
*win
) {
1417 if (focused_window
&& focused_window
== win
) // nothing to do
1420 BScreen
*old_screen
= 0;
1422 if (focused_window
) {
1423 focused_window
->setFocusFlag(False
);
1424 old_screen
= focused_window
->getScreen();
1427 if (win
&& ! win
->isIconic()) {
1428 // the active screen is the one with the last focused window...
1429 // this will keep focus on this screen no matter where the mouse goes,
1430 // so multihead keybindings will continue to work on that screen until the
1431 // user focuses a window on a different screen.
1432 active_screen
= win
->getScreen();
1433 focused_window
= win
;
1437 if (active_screen
) {
1438 // set input focus to the toolbar of the screen with mouse
1439 XSetInputFocus(getXDisplay(),
1440 active_screen
->getRootWindow(),
1441 RevertToPointerRoot
, CurrentTime
);
1443 // set input focus to the toolbar of the first managed screen
1444 XSetInputFocus(getXDisplay(),
1445 screenList
.front()->getRootWindow(),
1446 RevertToPointerRoot
, CurrentTime
);
1449 // set input focus to the toolbar of the last screen
1450 XSetInputFocus(getXDisplay(), old_screen
->getRootWindow(),
1451 RevertToPointerRoot
, CurrentTime
);
1455 if (active_screen
&& active_screen
->isScreenManaged()) {
1456 active_screen
->getToolbar()->redrawWindowLabel(True
);
1457 active_screen
->updateNetizenWindowFocus();
1460 if (old_screen
&& old_screen
!= active_screen
) {
1461 old_screen
->getToolbar()->redrawWindowLabel(True
);
1462 old_screen
->updateNetizenWindowFocus();