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
98 #include "blackbox.hh"
99 #include "Basemenu.hh"
100 #include "Clientmenu.hh"
101 #include "GCCache.hh"
103 #include "Rootmenu.hh"
106 #include "Toolbar.hh"
109 #include "Workspace.hh"
110 #include "Workspacemenu.hh"
113 // X event scanner for enter/leave notifies - adapted from twm
116 bool leave
, inferior
, enter
;
119 static Bool
queueScanner(Display
*, XEvent
*e
, char *args
) {
120 scanargs
*scan
= (scanargs
*) args
;
121 if ((e
->type
== LeaveNotify
) &&
122 (e
->xcrossing
.window
== scan
->w
) &&
123 (e
->xcrossing
.mode
== NotifyNormal
)) {
125 scan
->inferior
= (e
->xcrossing
.detail
== NotifyInferior
);
126 } else if ((e
->type
== EnterNotify
) && (e
->xcrossing
.mode
== NotifyUngrab
)) {
136 Blackbox::Blackbox(char **m_argv
, char *dpy_name
, char *rc
, char *menu
)
137 : BaseDisplay(m_argv
[0], dpy_name
) {
138 if (! XSupportsLocale())
139 fprintf(stderr
, "X server does not support locale\n");
141 if (XSetLocaleModifiers("") == NULL
)
142 fprintf(stderr
, "cannot set locale modifiers\n");
146 if (! rc
) rc
= "~/.openbox/rc";
147 rc_file
= expandTilde(rc
);
148 config
.setFile(rc_file
);
149 if (! menu
) menu
= "~/.openbox/menu";
150 menu_file
= expandTilde(menu
);
154 resource
.auto_raise_delay
.tv_sec
= resource
.auto_raise_delay
.tv_usec
= 0;
157 focused_window
= (BlackboxWindow
*) 0;
164 cursor
.session
= XCreateFontCursor(getXDisplay(), XC_left_ptr
);
165 cursor
.move
= XCreateFontCursor(getXDisplay(), XC_fleur
);
166 cursor
.ll_angle
= XCreateFontCursor(getXDisplay(), XC_ll_angle
);
167 cursor
.lr_angle
= XCreateFontCursor(getXDisplay(), XC_lr_angle
);
169 for (unsigned int i
= 0; i
< getNumberOfScreens(); i
++) {
170 BScreen
*screen
= new BScreen(this, i
);
172 if (! screen
->isScreenManaged()) {
177 screenList
.push_back(screen
);
180 if (screenList
.empty()) {
182 i18n(blackboxSet
, blackboxNoManagableScreens
,
183 "Blackbox::Blackbox: no managable screens found, aborting.\n"));
187 // save current settings and default values
190 // set the screen with mouse to the first managed screen
191 active_screen
= screenList
.front();
194 XSynchronize(getXDisplay(), False
);
195 XSync(getXDisplay(), False
);
197 reconfigure_wait
= reread_menu_wait
= False
;
199 timer
= new BTimer(this, this);
200 timer
->setTimeout(0l);
204 Blackbox::~Blackbox(void) {
205 std::for_each(screenList
.begin(), screenList
.end(), PointerAssassin());
207 std::for_each(menuTimestamps
.begin(), menuTimestamps
.end(),
214 void Blackbox::process_event(XEvent
*e
) {
217 // strip the lock key modifiers
218 e
->xbutton
.state
&= ~(NumLockMask
| ScrollLockMask
| LockMask
);
220 last_time
= e
->xbutton
.time
;
222 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
223 Basemenu
*menu
= (Basemenu
*) 0;
224 Slit
*slit
= (Slit
*) 0;
225 Toolbar
*tbar
= (Toolbar
*) 0;
226 BScreen
*scrn
= (BScreen
*) 0;
228 if ((win
= searchWindow(e
->xbutton
.window
))) {
229 win
->buttonPressEvent(&e
->xbutton
);
231 /* XXX: is this sane on low colour desktops? */
232 if (e
->xbutton
.button
== 1)
233 win
->installColormap(True
);
234 } else if ((menu
= searchMenu(e
->xbutton
.window
))) {
235 menu
->buttonPressEvent(&e
->xbutton
);
236 } else if ((slit
= searchSlit(e
->xbutton
.window
))) {
237 slit
->buttonPressEvent(&e
->xbutton
);
238 } else if ((tbar
= searchToolbar(e
->xbutton
.window
))) {
239 tbar
->buttonPressEvent(&e
->xbutton
);
240 } else if ((scrn
= searchScreen(e
->xbutton
.window
))) {
241 scrn
->buttonPressEvent(&e
->xbutton
);
242 if (active_screen
!= scrn
) {
243 active_screen
= scrn
;
244 // first, set no focus window on the old screen
246 // and move focus to this screen
253 case ButtonRelease
: {
254 // strip the lock key modifiers
255 e
->xbutton
.state
&= ~(NumLockMask
| ScrollLockMask
| LockMask
);
257 last_time
= e
->xbutton
.time
;
259 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
260 Basemenu
*menu
= (Basemenu
*) 0;
261 Toolbar
*tbar
= (Toolbar
*) 0;
263 if ((win
= searchWindow(e
->xbutton
.window
)))
264 win
->buttonReleaseEvent(&e
->xbutton
);
265 else if ((menu
= searchMenu(e
->xbutton
.window
)))
266 menu
->buttonReleaseEvent(&e
->xbutton
);
267 else if ((tbar
= searchToolbar(e
->xbutton
.window
)))
268 tbar
->buttonReleaseEvent(&e
->xbutton
);
273 case ConfigureRequest
: {
274 // compress configure requests...
277 while(XCheckTypedWindowEvent(getXDisplay(), e
->xconfigurerequest
.window
,
278 ConfigureRequest
, &realevent
)) {
284 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
285 Slit
*slit
= (Slit
*) 0;
287 if ((win
= searchWindow(e
->xconfigurerequest
.window
))) {
288 win
->configureRequestEvent(&e
->xconfigurerequest
);
289 } else if ((slit
= searchSlit(e
->xconfigurerequest
.window
))) {
290 slit
->configureRequestEvent(&e
->xconfigurerequest
);
292 if (validateWindow(e
->xconfigurerequest
.window
)) {
295 xwc
.x
= e
->xconfigurerequest
.x
;
296 xwc
.y
= e
->xconfigurerequest
.y
;
297 xwc
.width
= e
->xconfigurerequest
.width
;
298 xwc
.height
= e
->xconfigurerequest
.height
;
299 xwc
.border_width
= e
->xconfigurerequest
.border_width
;
300 xwc
.sibling
= e
->xconfigurerequest
.above
;
301 xwc
.stack_mode
= e
->xconfigurerequest
.detail
;
303 XConfigureWindow(getXDisplay(), e
->xconfigurerequest
.window
,
304 e
->xconfigurerequest
.value_mask
, &xwc
);
313 fprintf(stderr
, "Blackbox::process_event(): MapRequest for 0x%lx\n",
314 e
->xmaprequest
.window
);
317 BlackboxWindow
*win
= searchWindow(e
->xmaprequest
.window
);
320 BScreen
*screen
= searchScreen(e
->xmaprequest
.parent
);
324 we got a map request for a window who's parent isn't root. this
325 can happen in only one circumstance:
327 a client window unmapped a managed window, and then remapped it
328 somewhere between unmapping the client window and reparenting it
331 regardless of how it happens, we need to find the screen that
334 XWindowAttributes wattrib
;
335 if (! XGetWindowAttributes(getXDisplay(), e
->xmaprequest
.window
,
337 // failed to get the window attributes, perhaps the window has
338 // now been destroyed?
342 screen
= searchScreen(wattrib
.root
);
343 assert(screen
!= 0); // this should never happen
346 screen
->manageWindow(e
->xmaprequest
.window
);
353 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
354 Slit
*slit
= (Slit
*) 0;
356 if ((win
= searchWindow(e
->xunmap
.window
))) {
357 win
->unmapNotifyEvent(&e
->xunmap
);
358 } else if ((slit
= searchSlit(e
->xunmap
.window
))) {
359 slit
->unmapNotifyEvent(&e
->xunmap
);
365 case DestroyNotify
: {
366 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
367 Slit
*slit
= (Slit
*) 0;
368 BWindowGroup
*group
= (BWindowGroup
*) 0;
370 if ((win
= searchWindow(e
->xdestroywindow
.window
))) {
371 win
->destroyNotifyEvent(&e
->xdestroywindow
);
372 } else if ((slit
= searchSlit(e
->xdestroywindow
.window
))) {
373 slit
->removeClient(e
->xdestroywindow
.window
, False
);
374 } else if ((group
= searchGroup(e
->xdestroywindow
.window
))) {
381 case ReparentNotify
: {
383 this event is quite rare and is usually handled in unmapNotify
384 however, if the window is unmapped when the reparent event occurs
385 the window manager never sees it because an unmap event is not sent
386 to an already unmapped window.
388 BlackboxWindow
*win
= searchWindow(e
->xreparent
.window
);
390 win
->reparentNotifyEvent(&e
->xreparent
);
392 Slit
*slit
= searchSlit(e
->xreparent
.window
);
393 if (slit
&& slit
->getWindowID() != e
->xreparent
.parent
)
394 slit
->removeClient(e
->xreparent
.window
, True
);
400 // motion notify compression...
403 while (XCheckTypedWindowEvent(getXDisplay(), e
->xmotion
.window
,
404 MotionNotify
, &realevent
)) {
408 // if we have compressed some motion events, use the last one
412 // strip the lock key modifiers
413 e
->xbutton
.state
&= ~(NumLockMask
| ScrollLockMask
| LockMask
);
415 last_time
= e
->xmotion
.time
;
417 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
418 Basemenu
*menu
= (Basemenu
*) 0;
420 if ((win
= searchWindow(e
->xmotion
.window
)))
421 win
->motionNotifyEvent(&e
->xmotion
);
422 else if ((menu
= searchMenu(e
->xmotion
.window
)))
423 menu
->motionNotifyEvent(&e
->xmotion
);
428 case PropertyNotify
: {
429 last_time
= e
->xproperty
.time
;
431 if (e
->xproperty
.state
!= PropertyDelete
) {
432 BlackboxWindow
*win
= searchWindow(e
->xproperty
.window
);
435 win
->propertyNotifyEvent(e
->xproperty
.atom
);
442 last_time
= e
->xcrossing
.time
;
444 BScreen
*screen
= (BScreen
*) 0;
445 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
446 Basemenu
*menu
= (Basemenu
*) 0;
447 Toolbar
*tbar
= (Toolbar
*) 0;
448 Slit
*slit
= (Slit
*) 0;
450 if (e
->xcrossing
.mode
== NotifyGrab
) break;
454 sa
.w
= e
->xcrossing
.window
;
455 sa
.enter
= sa
.leave
= False
;
456 XCheckIfEvent(getXDisplay(), &dummy
, queueScanner
, (char *) &sa
);
458 if ((e
->xcrossing
.window
== e
->xcrossing
.root
) &&
459 (screen
= searchScreen(e
->xcrossing
.window
))) {
460 screen
->getImageControl()->installRootColormap();
461 } else if ((win
= searchWindow(e
->xcrossing
.window
))) {
462 if (win
->getScreen()->isSloppyFocus() &&
463 (! win
->isFocused()) && (! no_focus
)) {
464 if (((! sa
.leave
) || sa
.inferior
) && win
->isVisible()) {
465 if (win
->setInputFocus())
466 win
->installColormap(True
); // XXX: shouldnt we honour no install?
469 } else if ((menu
= searchMenu(e
->xcrossing
.window
))) {
470 menu
->enterNotifyEvent(&e
->xcrossing
);
471 } else if ((tbar
= searchToolbar(e
->xcrossing
.window
))) {
472 tbar
->enterNotifyEvent(&e
->xcrossing
);
473 } else if ((slit
= searchSlit(e
->xcrossing
.window
))) {
474 slit
->enterNotifyEvent(&e
->xcrossing
);
480 last_time
= e
->xcrossing
.time
;
482 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
483 Basemenu
*menu
= (Basemenu
*) 0;
484 Toolbar
*tbar
= (Toolbar
*) 0;
485 Slit
*slit
= (Slit
*) 0;
487 if ((menu
= searchMenu(e
->xcrossing
.window
)))
488 menu
->leaveNotifyEvent(&e
->xcrossing
);
489 else if ((win
= searchWindow(e
->xcrossing
.window
)))
490 win
->installColormap(False
);
491 else if ((tbar
= searchToolbar(e
->xcrossing
.window
)))
492 tbar
->leaveNotifyEvent(&e
->xcrossing
);
493 else if ((slit
= searchSlit(e
->xcrossing
.window
)))
494 slit
->leaveNotifyEvent(&e
->xcrossing
);
499 // compress expose events
502 int ex1
, ey1
, ex2
, ey2
;
505 ex2
= ex1
+ e
->xexpose
.width
- 1;
506 ey2
= ey1
+ e
->xexpose
.height
- 1;
507 while (XCheckTypedWindowEvent(getXDisplay(), e
->xexpose
.window
,
508 Expose
, &realevent
)) {
512 ex1
= std::min(realevent
.xexpose
.x
, ex1
);
513 ey1
= std::min(realevent
.xexpose
.y
, ey1
);
514 ex2
= std::max(realevent
.xexpose
.x
+ realevent
.xexpose
.width
- 1, ex2
);
515 ey2
= std::max(realevent
.xexpose
.y
+ realevent
.xexpose
.height
- 1, ey2
);
520 // use the merged area
523 e
->xexpose
.width
= ex2
- ex1
+ 1;
524 e
->xexpose
.height
= ey2
- ey1
+ 1;
526 BlackboxWindow
*win
= (BlackboxWindow
*) 0;
527 Basemenu
*menu
= (Basemenu
*) 0;
528 Toolbar
*tbar
= (Toolbar
*) 0;
530 if ((win
= searchWindow(e
->xexpose
.window
)))
531 win
->exposeEvent(&e
->xexpose
);
532 else if ((menu
= searchMenu(e
->xexpose
.window
)))
533 menu
->exposeEvent(&e
->xexpose
);
534 else if ((tbar
= searchToolbar(e
->xexpose
.window
)))
535 tbar
->exposeEvent(&e
->xexpose
);
541 Toolbar
*tbar
= searchToolbar(e
->xkey
.window
);
543 if (tbar
&& tbar
->isEditing())
544 tbar
->keyPressEvent(&e
->xkey
);
549 case ColormapNotify
: {
550 BScreen
*screen
= searchScreen(e
->xcolormap
.window
);
553 screen
->setRootColormapInstalled((e
->xcolormap
.state
==
554 ColormapInstalled
) ? True
: False
);
560 if (e
->xfocus
.detail
!= NotifyNonlinear
) {
562 don't process FocusIns when:
563 1. the new focus window isn't an ancestor or inferior of the old
564 focus window (NotifyNonlinear)
569 BlackboxWindow
*win
= searchWindow(e
->xfocus
.window
);
571 if (! win
->isFocused())
572 win
->setFocusFlag(True
);
575 set the event window to None. when the FocusOut event handler calls
576 this function recursively, it uses this as an indication that focus
577 has moved to a known window.
579 e
->xfocus
.window
= None
;
586 if (e
->xfocus
.detail
!= NotifyNonlinear
) {
588 don't process FocusOuts when:
589 2. the new focus window isn't an ancestor or inferior of the old
590 focus window (NotifyNonlinear)
595 BlackboxWindow
*win
= searchWindow(e
->xfocus
.window
);
596 if (win
&& win
->isFocused()) {
598 before we mark "win" as unfocused, we need to verify that focus is
599 going to a known location, is in a known location, or set focus
604 // don't check the current focus if FocusOut was generated during a grab
605 bool check_focus
= (e
->xfocus
.mode
== NotifyNormal
);
608 First, check if there is a pending FocusIn event waiting. if there
609 is, process it and determine if focus has moved to another window
610 (the FocusIn event handler sets the window in the event
611 structure to None to indicate this).
613 if (XCheckTypedEvent(getXDisplay(), FocusIn
, &event
)) {
615 process_event(&event
);
616 if (event
.xfocus
.window
== None
) {
624 Second, we query the X server for the current input focus.
625 to make sure that we keep a consistent state.
627 BlackboxWindow
*focus
;
630 XGetInputFocus(getXDisplay(), &w
, &revert
);
631 focus
= searchWindow(w
);
634 focus got from "win" to "focus" under some very strange
635 circumstances, and we need to make sure that the focus indication
638 setFocusedWindow(focus
);
640 // we have no idea where focus went... so we set it to somewhere
649 case ClientMessage
: {
650 if (e
->xclient
.format
== 32) {
651 if (e
->xclient
.message_type
== getWMChangeStateAtom()) {
652 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
653 if (! win
|| ! win
->validateClient()) return;
655 if (e
->xclient
.data
.l
[0] == IconicState
)
657 if (e
->xclient
.data
.l
[0] == NormalState
)
659 } else if(e
->xclient
.message_type
== getBlackboxChangeWorkspaceAtom()) {
660 BScreen
*screen
= searchScreen(e
->xclient
.window
);
662 if (screen
&& e
->xclient
.data
.l
[0] >= 0 &&
663 e
->xclient
.data
.l
[0] <
664 static_cast<signed>(screen
->getWorkspaceCount()))
665 screen
->changeWorkspaceID(e
->xclient
.data
.l
[0]);
666 } else if (e
->xclient
.message_type
== getBlackboxChangeWindowFocusAtom()) {
667 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
669 if (win
&& win
->isVisible() && win
->setInputFocus())
670 win
->installColormap(True
);
671 } else if (e
->xclient
.message_type
== getBlackboxCycleWindowFocusAtom()) {
672 BScreen
*screen
= searchScreen(e
->xclient
.window
);
675 if (! e
->xclient
.data
.l
[0])
680 } else if (e
->xclient
.message_type
== getBlackboxChangeAttributesAtom()) {
681 BlackboxWindow
*win
= searchWindow(e
->xclient
.window
);
683 if (win
&& win
->validateClient()) {
685 net
.flags
= e
->xclient
.data
.l
[0];
686 net
.attrib
= e
->xclient
.data
.l
[1];
687 net
.workspace
= e
->xclient
.data
.l
[2];
688 net
.stack
= e
->xclient
.data
.l
[3];
689 net
.decoration
= e
->xclient
.data
.l
[4];
691 win
->changeBlackboxHints(&net
);
700 case ConfigureNotify
:
702 break; // not handled, just ignore
706 if (e
->type
== getShapeEventBase()) {
707 XShapeEvent
*shape_event
= (XShapeEvent
*) e
;
708 BlackboxWindow
*win
= searchWindow(e
->xany
.window
);
711 win
->shapeEvent(shape_event
);
719 bool Blackbox::handleSignal(int sig
) {
745 void Blackbox::init_icccm(void) {
746 xa_wm_colormap_windows
=
747 XInternAtom(getXDisplay(), "WM_COLORMAP_WINDOWS", False
);
748 xa_wm_protocols
= XInternAtom(getXDisplay(), "WM_PROTOCOLS", False
);
749 xa_wm_state
= XInternAtom(getXDisplay(), "WM_STATE", False
);
750 xa_wm_change_state
= XInternAtom(getXDisplay(), "WM_CHANGE_STATE", False
);
751 xa_wm_delete_window
= XInternAtom(getXDisplay(), "WM_DELETE_WINDOW", False
);
752 xa_wm_take_focus
= XInternAtom(getXDisplay(), "WM_TAKE_FOCUS", False
);
753 motif_wm_hints
= XInternAtom(getXDisplay(), "_MOTIF_WM_HINTS", False
);
755 blackbox_hints
= XInternAtom(getXDisplay(), "_BLACKBOX_HINTS", False
);
756 blackbox_attributes
=
757 XInternAtom(getXDisplay(), "_BLACKBOX_ATTRIBUTES", False
);
758 blackbox_change_attributes
=
759 XInternAtom(getXDisplay(), "_BLACKBOX_CHANGE_ATTRIBUTES", False
);
760 blackbox_structure_messages
=
761 XInternAtom(getXDisplay(), "_BLACKBOX_STRUCTURE_MESSAGES", False
);
762 blackbox_notify_startup
=
763 XInternAtom(getXDisplay(), "_BLACKBOX_NOTIFY_STARTUP", False
);
764 blackbox_notify_window_add
=
765 XInternAtom(getXDisplay(), "_BLACKBOX_NOTIFY_WINDOW_ADD", False
);
766 blackbox_notify_window_del
=
767 XInternAtom(getXDisplay(), "_BLACKBOX_NOTIFY_WINDOW_DEL", False
);
768 blackbox_notify_current_workspace
=
769 XInternAtom(getXDisplay(), "_BLACKBOX_NOTIFY_CURRENT_WORKSPACE", False
);
770 blackbox_notify_workspace_count
=
771 XInternAtom(getXDisplay(), "_BLACKBOX_NOTIFY_WORKSPACE_COUNT", False
);
772 blackbox_notify_window_focus
=
773 XInternAtom(getXDisplay(), "_BLACKBOX_NOTIFY_WINDOW_FOCUS", False
);
774 blackbox_notify_window_raise
=
775 XInternAtom(getXDisplay(), "_BLACKBOX_NOTIFY_WINDOW_RAISE", False
);
776 blackbox_notify_window_lower
=
777 XInternAtom(getXDisplay(), "_BLACKBOX_NOTIFY_WINDOW_LOWER", False
);
778 blackbox_change_workspace
=
779 XInternAtom(getXDisplay(), "_BLACKBOX_CHANGE_WORKSPACE", False
);
780 blackbox_change_window_focus
=
781 XInternAtom(getXDisplay(), "_BLACKBOX_CHANGE_WINDOW_FOCUS", False
);
782 blackbox_cycle_window_focus
=
783 XInternAtom(getXDisplay(), "_BLACKBOX_CYCLE_WINDOW_FOCUS", False
);
786 net_supported
= XInternAtom(getXDisplay(), "_NET_SUPPORTED", False
);
787 net_client_list
= XInternAtom(getXDisplay(), "_NET_CLIENT_LIST", False
);
788 net_client_list_stacking
=
789 XInternAtom(getXDisplay(), "_NET_CLIENT_LIST_STACKING", False
);
790 net_number_of_desktops
=
791 XInternAtom(getXDisplay(), "_NET_NUMBER_OF_DESKTOPS", False
);
792 net_desktop_geometry
=
793 XInternAtom(getXDisplay(), "_NET_DESKTOP_GEOMETRY", False
);
794 net_desktop_viewport
=
795 XInternAtom(getXDisplay(), "_NET_DESKTOP_VIEWPORT", False
);
796 net_current_desktop
=
797 XInternAtom(getXDisplay(), "_NET_CURRENT_DESKTOP", False
);
798 net_desktop_names
= XInternAtom(getXDisplay(), "_NET_DESKTOP_NAMES", False
);
799 net_active_window
= XInternAtom(getXDisplay(), "_NET_ACTIVE_WINDOW", False
);
800 net_workarea
= XInternAtom(getXDisplay(), "_NET_WORKAREA", False
);
801 net_supporting_wm_check
=
802 XInternAtom(getXDisplay(), "_NET_SUPPORTING_WM_CHECK", False
);
803 net_virtual_roots
= XInternAtom(getXDisplay(), "_NET_VIRTUAL_ROOTS", False
);
804 net_close_window
= XInternAtom(getXDisplay(), "_NET_CLOSE_WINDOW", False
);
805 net_wm_moveresize
= XInternAtom(getXDisplay(), "_NET_WM_MOVERESIZE", False
);
806 net_properties
= XInternAtom(getXDisplay(), "_NET_PROPERTIES", False
);
807 net_wm_name
= XInternAtom(getXDisplay(), "_NET_WM_NAME", False
);
808 net_wm_desktop
= XInternAtom(getXDisplay(), "_NET_WM_DESKTOP", False
);
810 XInternAtom(getXDisplay(), "_NET_WM_WINDOW_TYPE", False
);
811 net_wm_state
= XInternAtom(getXDisplay(), "_NET_WM_STATE", False
);
812 net_wm_strut
= XInternAtom(getXDisplay(), "_NET_WM_STRUT", False
);
813 net_wm_icon_geometry
=
814 XInternAtom(getXDisplay(), "_NET_WM_ICON_GEOMETRY", False
);
815 net_wm_icon
= XInternAtom(getXDisplay(), "_NET_WM_ICON", False
);
816 net_wm_pid
= XInternAtom(getXDisplay(), "_NET_WM_PID", False
);
817 net_wm_handled_icons
=
818 XInternAtom(getXDisplay(), "_NET_WM_HANDLED_ICONS", False
);
819 net_wm_ping
= XInternAtom(getXDisplay(), "_NET_WM_PING", False
);
823 blackbox_pid
= XInternAtom(getXDisplay(), "_BLACKBOX_PID", False
);
824 #endif // HAVE_GETPID
828 bool Blackbox::validateWindow(Window window
) {
830 if (XCheckTypedWindowEvent(getXDisplay(), window
, DestroyNotify
, &event
)) {
831 XPutBackEvent(getXDisplay(), &event
);
840 BScreen
*Blackbox::searchScreen(Window window
) {
841 ScreenList::iterator it
= screenList
.begin();
843 for (; it
!= screenList
.end(); ++it
) {
845 if (s
->getRootWindow() == window
)
849 return (BScreen
*) 0;
853 BlackboxWindow
*Blackbox::searchWindow(Window window
) {
854 WindowLookup::iterator it
= windowSearchList
.find(window
);
855 if (it
!= windowSearchList
.end())
858 return (BlackboxWindow
*) 0;
862 BWindowGroup
*Blackbox::searchGroup(Window window
) {
863 GroupLookup::iterator it
= groupSearchList
.find(window
);
864 if (it
!= groupSearchList
.end())
867 return (BWindowGroup
*) 0;
871 Basemenu
*Blackbox::searchMenu(Window window
) {
872 MenuLookup::iterator it
= menuSearchList
.find(window
);
873 if (it
!= menuSearchList
.end())
876 return (Basemenu
*) 0;
880 Toolbar
*Blackbox::searchToolbar(Window window
) {
881 ToolbarLookup::iterator it
= toolbarSearchList
.find(window
);
882 if (it
!= toolbarSearchList
.end())
889 Slit
*Blackbox::searchSlit(Window window
) {
890 SlitLookup::iterator it
= slitSearchList
.find(window
);
891 if (it
!= slitSearchList
.end())
898 void Blackbox::saveWindowSearch(Window window
, BlackboxWindow
*data
) {
899 windowSearchList
.insert(WindowLookupPair(window
, data
));
903 void Blackbox::saveGroupSearch(Window window
, BWindowGroup
*data
) {
904 groupSearchList
.insert(GroupLookupPair(window
, data
));
908 void Blackbox::saveMenuSearch(Window window
, Basemenu
*data
) {
909 menuSearchList
.insert(MenuLookupPair(window
, data
));
913 void Blackbox::saveToolbarSearch(Window window
, Toolbar
*data
) {
914 toolbarSearchList
.insert(ToolbarLookupPair(window
, data
));
918 void Blackbox::saveSlitSearch(Window window
, Slit
*data
) {
919 slitSearchList
.insert(SlitLookupPair(window
, data
));
923 void Blackbox::removeWindowSearch(Window window
) {
924 windowSearchList
.erase(window
);
928 void Blackbox::removeGroupSearch(Window window
) {
929 groupSearchList
.erase(window
);
933 void Blackbox::removeMenuSearch(Window window
) {
934 menuSearchList
.erase(window
);
938 void Blackbox::removeToolbarSearch(Window window
) {
939 toolbarSearchList
.erase(window
);
943 void Blackbox::removeSlitSearch(Window window
) {
944 slitSearchList
.erase(window
);
948 void Blackbox::restart(const char *prog
) {
952 execlp(prog
, prog
, NULL
);
956 // fall back in case the above execlp doesn't work
957 execvp(argv
[0], argv
);
958 string name
= basename(argv
[0]);
959 execvp(name
.c_str(), argv
);
963 void Blackbox::shutdown(void) {
964 BaseDisplay::shutdown();
966 XSetInputFocus(getXDisplay(), PointerRoot
, None
, CurrentTime
);
968 std::for_each(screenList
.begin(), screenList
.end(),
969 std::mem_fun(&BScreen::shutdown
));
971 XSync(getXDisplay(), False
);
976 * Save all values as they are so that the defaults will be written to the rc
979 void Blackbox::save_rc(void) {
980 config
.setAutoSave(false);
982 config
.setValue("session.colorsPerChannel", resource
.colors_per_channel
);
983 config
.setValue("session.doubleClickInterval",
984 resource
.double_click_interval
);
985 config
.setValue("session.autoRaiseDelay",
986 ((resource
.auto_raise_delay
.tv_sec
* 1000) +
987 (resource
.auto_raise_delay
.tv_usec
/ 1000)));
988 config
.setValue("session.cacheLife", resource
.cache_life
/ 60000);
989 config
.setValue("session.cacheMax", resource
.cache_max
);
990 config
.setValue("session.styleFile", resource
.style_file
);
991 config
.setValue("session.titlebarLayout", resource
.titlebar_layout
);
993 std::for_each(screenList
.begin(), screenList
.end(),
994 std::mem_fun(&BScreen::save_rc
));
996 config
.setAutoSave(true);
1001 void Blackbox::load_rc(void) {
1002 if (! config
.load())
1007 if (! config
.getValue("session.colorsPerChannel",
1008 resource
.colors_per_channel
))
1009 resource
.colors_per_channel
= 4;
1010 if (resource
.colors_per_channel
< 2) resource
.colors_per_channel
= 2;
1011 else if (resource
.colors_per_channel
> 6) resource
.colors_per_channel
= 6;
1013 if (config
.getValue("session.styleFile", s
))
1014 resource
.style_file
= expandTilde(s
);
1016 resource
.style_file
= DEFAULTSTYLE
;
1018 if (! config
.getValue("session.doubleClickInterval",
1019 resource
.double_click_interval
));
1020 resource
.double_click_interval
= 250;
1022 if (! config
.getValue("session.autoRaiseDelay",
1023 resource
.auto_raise_delay
.tv_usec
))
1024 resource
.auto_raise_delay
.tv_usec
= 400;
1025 resource
.auto_raise_delay
.tv_sec
= resource
.auto_raise_delay
.tv_usec
/ 1000;
1026 resource
.auto_raise_delay
.tv_usec
-=
1027 (resource
.auto_raise_delay
.tv_sec
* 1000);
1028 resource
.auto_raise_delay
.tv_usec
*= 1000;
1030 if (! config
.getValue("session.cacheLife", resource
.cache_life
))
1031 resource
.cache_life
= 5;
1032 resource
.cache_life
*= 60000;
1034 if (! config
.getValue("session.cacheMax", resource
.cache_max
))
1035 resource
.cache_max
= 200;
1037 if (! config
.getValue("session.titlebarLayout", resource
.titlebar_layout
))
1038 resource
.titlebar_layout
= "ILMC";
1042 void Blackbox::reconfigure(void) {
1043 reconfigure_wait
= True
;
1045 if (! timer
->isTiming()) timer
->start();
1049 void Blackbox::real_reconfigure(void) {
1052 std::for_each(menuTimestamps
.begin(), menuTimestamps
.end(),
1054 menuTimestamps
.clear();
1058 std::for_each(screenList
.begin(), screenList
.end(),
1059 std::mem_fun(&BScreen::reconfigure
));
1063 void Blackbox::checkMenu(void) {
1064 bool reread
= False
;
1065 MenuTimestampList::iterator it
= menuTimestamps
.begin();
1066 for(; it
!= menuTimestamps
.end(); ++it
) {
1067 MenuTimestamp
*tmp
= *it
;
1070 if (! stat(tmp
->filename
.c_str(), &buf
)) {
1071 if (tmp
->timestamp
!= buf
.st_ctime
)
1078 if (reread
) rereadMenu();
1082 void Blackbox::rereadMenu(void) {
1083 reread_menu_wait
= True
;
1085 if (! timer
->isTiming()) timer
->start();
1089 void Blackbox::real_rereadMenu(void) {
1090 std::for_each(menuTimestamps
.begin(), menuTimestamps
.end(),
1092 menuTimestamps
.clear();
1094 std::for_each(screenList
.begin(), screenList
.end(),
1095 std::mem_fun(&BScreen::rereadMenu
));
1099 void Blackbox::saveStyleFilename(const string
& filename
) {
1100 assert(! filename
.empty());
1101 resource
.style_file
= filename
;
1102 config
.setValue("session.styleFile", resource
.style_file
);
1106 void Blackbox::addMenuTimestamp(const string
& filename
) {
1107 assert(! filename
.empty());
1110 MenuTimestampList::iterator it
= menuTimestamps
.begin();
1111 for (; it
!= menuTimestamps
.end() && ! found
; ++it
) {
1112 if ((*it
)->filename
== filename
) found
= True
;
1117 if (! stat(filename
.c_str(), &buf
)) {
1118 MenuTimestamp
*ts
= new MenuTimestamp
;
1120 ts
->filename
= filename
;
1121 ts
->timestamp
= buf
.st_ctime
;
1123 menuTimestamps
.push_back(ts
);
1129 void Blackbox::timeout(void) {
1130 if (reconfigure_wait
)
1133 if (reread_menu_wait
)
1136 reconfigure_wait
= reread_menu_wait
= False
;
1140 void Blackbox::setFocusedWindow(BlackboxWindow
*win
) {
1141 if (focused_window
&& focused_window
== win
) // nothing to do
1144 BScreen
*old_screen
= 0;
1146 if (focused_window
) {
1147 focused_window
->setFocusFlag(False
);
1148 old_screen
= focused_window
->getScreen();
1151 if (win
&& ! win
->isIconic()) {
1152 // the active screen is the one with the last focused window...
1153 // this will keep focus on this screen no matter where the mouse goes,
1154 // so multihead keybindings will continue to work on that screen until the
1155 // user focuses a window on a different screen.
1156 active_screen
= win
->getScreen();
1157 focused_window
= win
;
1161 if (active_screen
) {
1162 // set input focus to the toolbar of the screen with mouse
1163 XSetInputFocus(getXDisplay(),
1164 active_screen
->getRootWindow(),
1165 RevertToPointerRoot
, CurrentTime
);
1167 // set input focus to the toolbar of the first managed screen
1168 XSetInputFocus(getXDisplay(),
1169 screenList
.front()->getRootWindow(),
1170 RevertToPointerRoot
, CurrentTime
);
1173 // set input focus to the toolbar of the last screen
1174 XSetInputFocus(getXDisplay(), old_screen
->getRootWindow(),
1175 RevertToPointerRoot
, CurrentTime
);
1179 if (active_screen
&& active_screen
->isScreenManaged()) {
1180 active_screen
->getToolbar()->redrawWindowLabel(True
);
1181 active_screen
->updateNetizenWindowFocus();
1184 if (old_screen
&& old_screen
!= active_screen
) {
1185 old_screen
->getToolbar()->redrawWindowLabel(True
);
1186 old_screen
->updateNetizenWindowFocus();