1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // Screen.cc for Blackbox - an X11 Window manager
3 // Copyright (c) 2001 - 2005 Sean 'Shaleh' Perry <shaleh@debian.org>
4 // Copyright (c) 1997 - 2000, 2002 - 2005
5 // Bradley T Hughes <bhughes at trolltech.com>
7 // Permission is hereby granted, free of charge, to any person obtaining a
8 // copy of this software and associated documentation files (the "Software"),
9 // to deal in the Software without restriction, including without limitation
10 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
11 // and/or sell copies of the Software, and to permit persons to whom the
12 // Software is furnished to do so, subject to the following conditions:
14 // The above copyright notice and this permission notice shall be included in
15 // all copies or substantial portions of the Software.
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
20 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
22 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
23 // DEALINGS IN THE SOFTWARE.
26 #include "Clientmenu.hh"
27 #include "Configmenu.hh"
28 #include "Iconmenu.hh"
29 #include "Rootmenu.hh"
31 #include "Slitmenu.hh"
33 #include "Toolbarmenu.hh"
35 #include "WindowGroup.hh"
36 #include "Windowmenu.hh"
37 #include "Workspace.hh"
38 #include "Workspacemenu.hh"
41 #include <PixmapCache.hh>
44 #include <X11/Xutil.h>
45 #include <sys/types.h>
52 static bool running
= true;
53 static int anotherWMRunning(Display
*, XErrorEvent
*) {
59 BScreen::BScreen(Blackbox
*bb
, unsigned int scrn
) :
60 screen_info(bb
->display().screenInfo(scrn
)), _blackbox(bb
),
61 _resource(bb
->resource().screenResource(scrn
))
64 XErrorHandler old
= XSetErrorHandler((XErrorHandler
) anotherWMRunning
);
65 XSelectInput(screen_info
.display().XDisplay(),
66 screen_info
.rootWindow(),
69 SubstructureRedirectMask
|
72 XSync(screen_info
.display().XDisplay(), False
);
73 XSetErrorHandler((XErrorHandler
) old
);
78 "%s: another window manager is already running on display '%s'\n",
79 _blackbox
->applicationName().c_str(),
80 DisplayString(_blackbox
->XDisplay()));
84 static const char *visual_classes
[] = {
92 printf("%s: managing screen %u using %s visual 0x%lx, depth %d\n",
93 _blackbox
->applicationName().c_str(), screen_info
.screenNumber(),
94 visual_classes
[screen_info
.visual()->c_class
],
95 XVisualIDFromVisual(screen_info
.visual()), screen_info
.depth());
97 _blackbox
->insertEventHandler(screen_info
.rootWindow(), this);
99 cascade_x
= cascade_y
= ~0;
104 XDefineCursor(_blackbox
->XDisplay(), screen_info
.rootWindow(),
105 _blackbox
->resource().cursors().pointer
);
107 // start off full screen, top left.
108 usableArea
.setSize(screen_info
.width(), screen_info
.height());
113 geom_visible
= False
;
118 XCreateSimpleWindow(_blackbox
->XDisplay(), screen_info
.rootWindow(),
119 0, 0, screen_info
.width(), screen_info
.height(), 0,
121 XSetWindowBackgroundPixmap(_blackbox
->XDisplay(), empty_window
, None
);
124 XCreateSimpleWindow(_blackbox
->XDisplay(), screen_info
.rootWindow(),
125 screen_info
.width(), screen_info
.height(), 1, 1,
127 XSelectInput(_blackbox
->XDisplay(), no_focus_window
, NoEventMask
);
128 XMapWindow(_blackbox
->XDisplay(), no_focus_window
);
131 new Iconmenu(*_blackbox
, screen_info
.screenNumber(), this);
133 new Slitmenu(*_blackbox
, screen_info
.screenNumber(), this);
135 new Toolbarmenu(*_blackbox
, screen_info
.screenNumber(), this);
137 new Workspacemenu(*_blackbox
, screen_info
.screenNumber(), this);
140 new Configmenu(*_blackbox
, screen_info
.screenNumber(), this);
142 if (_resource
.workspaceCount() == 0) // there is always 1 workspace
143 _resource
.setWorkspaceCount(1);
145 _workspacemenu
->insertIconMenu(_iconmenu
);
146 for (unsigned int i
= 0; i
< _resource
.workspaceCount(); ++i
) {
147 Workspace
*wkspc
= new Workspace(this, i
);
148 workspacesList
.push_back(wkspc
);
149 _workspacemenu
->insertWorkspace(wkspc
);
152 current_workspace
= workspacesList
.front()->id();
153 _workspacemenu
->setWorkspaceChecked(current_workspace
, true);
155 // the Slit will be created on demand
159 if (_resource
.toolbarOptions().enabled
) {
160 _toolbar
= new Toolbar(this);
161 _stackingList
.insert(_toolbar
);
166 const bt::EWMH
& ewmh
= _blackbox
->ewmh();
168 ewmh requires the window manager to set a property on a window it creates
169 which is the id of that window. We must also set an equivalent property
170 on the root window. Then we must set _NET_WM_NAME on the child window
171 to be the name of the wm.
173 ewmh
.setSupportingWMCheck(screen_info
.rootWindow(), geom_window
);
174 ewmh
.setSupportingWMCheck(geom_window
, geom_window
);
175 ewmh
.setWMName(geom_window
, bt::toUnicode("Blackbox"));
177 ewmh
.setCurrentDesktop(screen_info
.rootWindow(), 0);
178 ewmh
.setNumberOfDesktops(screen_info
.rootWindow(),
179 workspacesList
.size());
180 ewmh
.setDesktopGeometry(screen_info
.rootWindow(),
181 screen_info
.width(), screen_info
.height());
182 ewmh
.setDesktopViewport(screen_info
.rootWindow(), 0, 0);
183 ewmh
.setActiveWindow(screen_info
.rootWindow(), None
);
184 updateWorkareaHint();
185 updateDesktopNamesHint();
189 ewmh
.clientListStacking(),
190 ewmh
.numberOfDesktops(),
191 // _NET_DESKTOP_GEOMETRY is not supported
192 // _NET_DESKTOP_VIEWPORT is not supported
193 ewmh
.currentDesktop(),
197 // _NET_VIRTUAL_ROOTS is not supported
198 // _NET_SHOWING_DESKTOP is not supported
201 ewmh
.moveresizeWindow(),
202 // _NET_WM_MOVERESIZE is not supported
205 ewmh
.wmVisibleName(),
207 ewmh
.wmVisibleIconName(),
211 ewmh
.wmWindowTypeDesktop(),
212 ewmh
.wmWindowTypeDock(),
213 ewmh
.wmWindowTypeToolbar(),
214 ewmh
.wmWindowTypeMenu(),
215 ewmh
.wmWindowTypeUtility(),
216 ewmh
.wmWindowTypeSplash(),
217 ewmh
.wmWindowTypeDialog(),
218 ewmh
.wmWindowTypeNormal(),
222 // _NET_WM_STATE_STICKY is not supported
223 ewmh
.wmStateMaximizedVert(),
224 ewmh
.wmStateMaximizedHorz(),
225 ewmh
.wmStateShaded(),
226 ewmh
.wmStateSkipTaskbar(),
227 ewmh
.wmStateSkipPager(),
228 ewmh
.wmStateHidden(),
229 ewmh
.wmStateFullscreen(),
233 ewmh
.wmAllowedActions(),
235 ewmh
.wmActionResize(),
236 ewmh
.wmActionMinimize(),
237 ewmh
.wmActionShade(),
238 // _NET_WM_ACTION_STICK is not supported
239 ewmh
.wmActionMaximizeHorz(),
240 ewmh
.wmActionMaximizeVert(),
241 ewmh
.wmActionFullscreen(),
242 ewmh
.wmActionChangeDesktop(),
243 ewmh
.wmActionClose(),
246 // _NET_WM_STRUT_PARTIAL is not supported
247 // _NET_WM_ICON_GEOMETRY is not supported
248 // _NET_WM_ICON is not supported
249 // _NET_WM_PID is not supported
250 // _NET_WM_HANDLED_ICONS is not supported
251 // _NET_WM_USER_TIME is not supported
253 // _NET_WM_PING is not supported
256 ewmh
.setSupported(screen_info
.rootWindow(), supported
,
257 sizeof(supported
) / sizeof(Atom
));
259 _blackbox
->XGrabServer();
261 unsigned int i
, j
, nchild
;
262 Window r
, p
, *children
;
263 XQueryTree(_blackbox
->XDisplay(), screen_info
.rootWindow(), &r
, &p
,
266 // preen the window list of all icon windows... for better dockapp support
267 for (i
= 0; i
< nchild
; i
++) {
268 if (children
[i
] == None
|| children
[i
] == no_focus_window
)
271 XWMHints
*wmhints
= XGetWMHints(_blackbox
->XDisplay(),
275 if ((wmhints
->flags
& IconWindowHint
) &&
276 (wmhints
->icon_window
!= children
[i
])) {
277 for (j
= 0; j
< nchild
; j
++) {
278 if (children
[j
] == wmhints
->icon_window
) {
289 // manage shown windows
290 for (i
= 0; i
< nchild
; ++i
) {
291 if (children
[i
] == None
|| children
[i
] == no_focus_window
)
294 XWindowAttributes attrib
;
295 if (XGetWindowAttributes(_blackbox
->XDisplay(), children
[i
], &attrib
)) {
296 if (attrib
.override_redirect
) continue;
298 if (attrib
.map_state
!= IsUnmapped
) {
299 manageWindow(children
[i
]);
306 _blackbox
->XUngrabServer();
308 updateClientListHint();
313 BScreen::~BScreen(void) {
314 if (! managed
) return;
316 _blackbox
->removeEventHandler(screen_info
.rootWindow());
318 bt::PixmapCache::release(geom_pixmap
);
320 if (geom_window
!= None
)
321 XDestroyWindow(_blackbox
->XDisplay(), geom_window
);
322 XDestroyWindow(_blackbox
->XDisplay(), empty_window
);
324 std::for_each(workspacesList
.begin(), workspacesList
.end(),
325 bt::PointerAssassin());
330 delete _workspacemenu
;
338 _blackbox
->ewmh().removeProperty(screen_info
.rootWindow(),
339 _blackbox
->ewmh().supportingWMCheck());
340 _blackbox
->ewmh().removeProperty(screen_info
.rootWindow(),
341 _blackbox
->ewmh().supported());
342 _blackbox
->ewmh().removeProperty(screen_info
.rootWindow(),
343 _blackbox
->ewmh().numberOfDesktops());
344 _blackbox
->ewmh().removeProperty(screen_info
.rootWindow(),
345 _blackbox
->ewmh().desktopGeometry());
346 _blackbox
->ewmh().removeProperty(screen_info
.rootWindow(),
347 _blackbox
->ewmh().desktopViewport());
348 _blackbox
->ewmh().removeProperty(screen_info
.rootWindow(),
349 _blackbox
->ewmh().currentDesktop());
350 _blackbox
->ewmh().removeProperty(screen_info
.rootWindow(),
351 _blackbox
->ewmh().activeWindow());
352 _blackbox
->ewmh().removeProperty(screen_info
.rootWindow(),
353 _blackbox
->ewmh().workarea());
357 void BScreen::updateGeomWindow(void) {
358 const WindowStyle
&style
= _resource
.windowStyle();
360 bt::textRect(screen_info
.screenNumber(), style
.font
,
361 bt::toUnicode("m:mmmm m:mmmm"));
363 geom_w
= geomr
.width() + (style
.label_margin
* 2);
364 geom_h
= geomr
.height() + (style
.label_margin
* 2);
366 if (geom_window
== None
) {
367 XSetWindowAttributes setattrib
;
368 unsigned long mask
= CWColormap
| CWSaveUnder
;
369 setattrib
.colormap
= screen_info
.colormap();
370 setattrib
.save_under
= True
;
373 XCreateWindow(_blackbox
->XDisplay(), screen_info
.rootWindow(),
374 0, 0, geom_w
, geom_h
, 0, screen_info
.depth(), InputOutput
,
375 screen_info
.visual(), mask
, &setattrib
);
378 const bt::Texture
&texture
=
379 (style
.focus
.label
.texture() == bt::Texture::Parent_Relative
)
382 geom_w
+= (texture
.borderWidth() * 2);
383 geom_h
+= (texture
.borderWidth() * 2);
384 geom_pixmap
= bt::PixmapCache::find(screen_info
.screenNumber(),
392 void BScreen::reconfigure(void) {
397 if (_toolbar
) _toolbar
->reconfigure();
398 if (_slit
) _slit
->reconfigure();
401 BlackboxWindowList::iterator it
= windowList
.begin(),
402 end
= windowList
.end();
403 for (; it
!= end
; ++it
)
404 if (*it
) (*it
)->reconfigure();
409 configmenu
->reconfigure();
410 _rootmenu
->reconfigure();
411 _workspacemenu
->reconfigure();
413 _windowmenu
->reconfigure();
416 WorkspaceList::iterator it
= workspacesList
.begin(),
417 end
= workspacesList
.end();
418 for (; it
!= end
; ++it
)
419 (*it
)->menu()->reconfigure();
424 void BScreen::rereadMenu(void) {
426 _rootmenu
->reconfigure();
430 void BScreen::LoadStyle(void) {
431 _resource
.loadStyle(this, _blackbox
->resource().styleFilename());
433 if (! _resource
.rootCommand().empty())
434 bt::bexec(_resource
.rootCommand(), screen_info
.displayString());
438 void BScreen::addWorkspace(void) {
439 Workspace
*wkspc
= new Workspace(this, workspacesList
.size());
440 workspacesList
.push_back(wkspc
);
441 _workspacemenu
->insertWorkspace(wkspc
);
443 _blackbox
->ewmh().setNumberOfDesktops(screen_info
.rootWindow(),
444 workspacesList
.size());
445 updateDesktopNamesHint();
449 void BScreen::removeLastWorkspace(void) {
450 if (workspacesList
.size() == 1)
453 Workspace
*workspace
= workspacesList
.back();
455 BlackboxWindowList::iterator it
= windowList
.begin();
456 const BlackboxWindowList::iterator end
= windowList
.end();
457 for (; it
!= end
; ++it
) {
458 BlackboxWindow
* const win
= *it
;
459 if (win
->workspace() == workspace
->id())
460 win
->setWorkspace(workspace
->id() - 1);
463 if (current_workspace
== workspace
->id())
464 setCurrentWorkspace(workspace
->id() - 1);
466 _workspacemenu
->removeWorkspace(workspace
->id());
467 workspacesList
.pop_back();
470 _blackbox
->ewmh().setNumberOfDesktops(screen_info
.rootWindow(),
471 workspacesList
.size());
472 updateDesktopNamesHint();
476 void BScreen::setCurrentWorkspace(unsigned int id
) {
477 if (id
== current_workspace
)
480 assert(id
< workspacesList
.size());
482 _blackbox
->XGrabServer();
484 // show the empty window... this will prevent unnecessary exposure
485 // of the root window
486 XMapWindow(_blackbox
->XDisplay(), empty_window
);
488 BlackboxWindow
* const focused_window
= _blackbox
->focusedWindow();
491 _workspacemenu
->setWorkspaceChecked(current_workspace
, false);
493 // withdraw windows in reverse order to minimize the number of
495 StackingList::const_reverse_iterator it
= _stackingList
.rbegin();
496 const StackingList::const_reverse_iterator end
= _stackingList
.rend();
497 for (; it
!= end
; ++it
) {
498 BlackboxWindow
*win
= dynamic_cast<BlackboxWindow
*>(*it
);
499 if (win
&& win
->workspace() == current_workspace
)
503 Workspace
*workspace
= findWorkspace(current_workspace
);
504 assert(workspace
!= 0);
505 if (focused_window
&& focused_window
->workspace() != bt::BSENTINEL
) {
506 // remember the window that last had focus
507 workspace
->setFocusedWindow(focused_window
);
509 workspace
->clearFocusedWindow();
513 current_workspace
= id
;
516 _workspacemenu
->setWorkspaceChecked(current_workspace
, true);
518 StackingList::const_iterator it
= _stackingList
.begin();
519 const StackingList::const_iterator end
= _stackingList
.end();
520 for (; it
!= end
; ++it
) {
521 BlackboxWindow
*win
= dynamic_cast<BlackboxWindow
*>(*it
);
522 if (win
&& win
->workspace() == current_workspace
)
526 const BlackboxResource
&res
= _blackbox
->resource();
527 if (res
.focusLastWindowOnWorkspace()
529 || focused_window
->workspace() != bt::BSENTINEL
530 // these window types shouldn't keep focus
531 || focused_window
->windowType() == WindowTypeDesktop
532 || focused_window
->windowType() == WindowTypeDock
)) {
533 Workspace
*workspace
= findWorkspace(current_workspace
);
534 assert(workspace
!= 0);
536 if (workspace
->focusedWindow()) {
537 // focus the window that last had focus
538 workspace
->focusedWindow()->setInputFocus();
540 // focus the top-most window in the stack
541 for (it
= _stackingList
.begin(); it
!= end
; ++it
) {
542 BlackboxWindow
* const tmp
= dynamic_cast<BlackboxWindow
*>(*it
);
545 || (tmp
->workspace() != current_workspace
546 && tmp
->workspace() != bt::BSENTINEL
))
548 if (tmp
->setInputFocus())
555 _blackbox
->ewmh().setCurrentDesktop(screen_info
.rootWindow(),
558 XUnmapWindow(_blackbox
->XDisplay(), empty_window
);
560 _blackbox
->XUngrabServer();
564 void BScreen::addWindow(Window w
) {
566 BlackboxWindow
* const win
= _blackbox
->findWindow(w
);
569 updateClientListHint();
571 // focus the new window if appropriate
572 switch (win
->windowType()) {
573 case WindowTypeDesktop
:
575 // these types should not be focused when managed
579 if (!_blackbox
->startingUp() &&
580 (!_blackbox
->activeScreen() || _blackbox
->activeScreen() == this) &&
581 (win
->isTransient() || _blackbox
->resource().focusNewWindows())) {
582 XSync(_blackbox
->XDisplay(), False
); // make sure the frame is mapped..
583 win
->setInputFocus();
590 void BScreen::manageWindow(Window w
) {
591 XWMHints
*wmhints
= XGetWMHints(_blackbox
->XDisplay(), w
);
592 bool slit_client
= (wmhints
&& (wmhints
->flags
& StateHint
) &&
593 wmhints
->initial_state
== WithdrawnState
);
594 if (wmhints
) XFree(wmhints
);
597 if (!_slit
) createSlit();
602 (void) new BlackboxWindow(_blackbox
, w
, this);
603 // verify that we have managed the window
604 BlackboxWindow
*win
= _blackbox
->findWindow(w
);
607 if (win
->workspace() >= workspaceCount() &&
608 win
->workspace() != bt::BSENTINEL
)
609 win
->setWorkspace(current_workspace
);
611 Workspace
*workspace
= findWorkspace(win
->workspace());
613 win
->setWorkspace(bt::BSENTINEL
);
614 win
->setWindowNumber(bt::BSENTINEL
);
616 workspace
->addWindow(win
);
619 bool place_window
= true;
620 if (_blackbox
->startingUp()
621 || ((win
->isTransient() || win
->windowType() == WindowTypeDesktop
622 || (win
->wmNormalHints().flags
& (PPosition
|USPosition
)))
623 && win
->clientRect().intersects(screen_info
.rect())))
624 place_window
= false;
625 if (place_window
) placeWindow(win
);
627 windowList
.push_back(win
);
629 // insert window at the top of the stack
630 _stackingList
.insert(win
);
631 if (!_blackbox
->startingUp())
634 // 'show' window in the appropriate state
635 switch (_blackbox
->startingUp()
636 ? win
->currentState()
637 : win
->wmHints().initial_state
) {
653 void BScreen::releaseWindow(BlackboxWindow
*w
) {
655 updateClientListHint();
656 updateClientListStackingHint();
660 void BScreen::unmanageWindow(BlackboxWindow
*win
) {
663 if (win
->isIconic()) {
664 _iconmenu
->removeItem(win
->windowNumber());
666 Workspace
*workspace
= findWorkspace(win
->workspace());
668 workspace
->menu()->removeItem(win
->windowNumber());
669 if (workspace
->focusedWindow() == win
)
670 workspace
->clearFocusedWindow();
674 if (_blackbox
->running() && win
->isFocused()) {
675 // pass focus to the next appropriate window
676 if (focusFallback(win
)) {
677 // focus is going somewhere, but we want to avoid dangling pointers
678 _blackbox
->forgetFocusedWindow();
680 // explicitly clear the focus
681 _blackbox
->setFocusedWindow(0);
685 windowList
.remove(win
);
686 _stackingList
.remove(win
);
692 bool BScreen::focusFallback(const BlackboxWindow
*win
) {
693 Workspace
*workspace
= findWorkspace(win
->workspace());
695 workspace
= findWorkspace(current_workspace
);
697 if (workspace
->id() != current_workspace
)
700 BWindowGroup
*group
= win
->findWindowGroup();
702 // focus the top-most window in the group
703 BlackboxWindowList::const_iterator git
= group
->windows().begin(),
704 gend
= group
->windows().end();
705 StackingList::iterator it
= _stackingList
.begin(),
706 end
= _stackingList
.end();
707 for (; it
!= end
; ++it
) {
708 BlackboxWindow
* const tmp
= dynamic_cast<BlackboxWindow
*>(*it
);
711 || std::find(git
, gend
, tmp
) == gend
713 || (tmp
->workspace() != current_workspace
714 && tmp
->workspace() != bt::BSENTINEL
))
716 if (tmp
->setInputFocus())
721 const BlackboxWindow
* const zero
= 0;
724 if (win
->isTransient()) {
725 BlackboxWindow
* const tmp
= win
->findTransientFor();
728 && (tmp
->workspace() == current_workspace
729 || tmp
->workspace() == bt::BSENTINEL
)
730 && tmp
->setInputFocus())
734 // try to focus the top-most window in the same layer as win
735 StackingList::iterator it
= _stackingList
.layer(win
->layer()),
736 end
= std::find(it
, _stackingList
.end(), zero
);
737 assert(it
!= _stackingList
.end() && end
!= _stackingList
.end());
738 for (; it
!= end
; ++it
) {
739 BlackboxWindow
* const tmp
= dynamic_cast<BlackboxWindow
*>(*it
);
744 || (tmp
->workspace() != current_workspace
745 && tmp
->workspace() != bt::BSENTINEL
))
747 if (tmp
->setInputFocus())
752 // focus the top-most window in the stack
753 StackingList::iterator it
= _stackingList
.begin(),
754 end
= _stackingList
.end();
755 for (; it
!= end
; ++it
) {
756 BlackboxWindow
* const tmp
= dynamic_cast<BlackboxWindow
*>(*it
);
760 || (tmp
->workspace() != current_workspace
761 && tmp
->workspace() != bt::BSENTINEL
))
763 if (tmp
->setInputFocus())
772 Raises all windows, preserving the existing stacking order. Each
773 window is placed at the top of the layer it currently occupies (with
777 void raiseGroup(StackingList
&stackingList
, BWindowGroup
*group
) {
778 BlackboxWindowList windows
= group
->windows();
779 int layer
= StackingList::LayerNormal
;
780 for (; layer
< StackingList::LayerDesktop
; ++layer
) {
781 StackingList::iterator
782 it
, top
= stackingList
.layer(StackingList::Layer(layer
));
783 const StackingList::iterator begin
= stackingList
.begin(),
784 end
= stackingList
.end();
786 // 'top' points to the top of the layer, we need to start from the
787 // bottom of the layer
788 it
= std::find(top
, end
, (StackEntity
*) 0);
796 // walk up the layer, raising all windows in the group
797 for (--it
; it
!= begin
; --it
) {
803 BlackboxWindow
*tmp
= dynamic_cast<BlackboxWindow
*>(*it
);
805 // entity is not a window, or window is not visible on the
810 const BlackboxWindowList::iterator wend
= windows
.end();
811 BlackboxWindowList::iterator wit
;
812 if ((wit
= std::find(windows
.begin(), wend
, tmp
)) == wend
)
815 // found a window in this layer, raise it
817 stackingList
.raise(tmp
);
818 // don't bother looking at this window again
826 Raise all transients, preserving the existing stacking order.
829 void raiseTransients(StackingList::iterator top
,
830 StackingList
&stackingList
,
831 BlackboxWindowList
&transients
) {
832 // 'top' points to the top of the layer, we need to start from the bottom
833 StackingList::iterator begin
= stackingList
.begin(),
834 end
= stackingList
.end(),
835 it
= std::find(top
, end
, (StackEntity
*) 0);
837 for (--it
; it
!= begin
; --it
) {
842 BlackboxWindow
*tmp
= dynamic_cast<BlackboxWindow
*>(*it
);
845 BlackboxWindowList::iterator wit
= transients
.begin(),
846 wend
= transients
.end();
847 wit
= std::find(wit
, wend
, tmp
);
849 // found a transient in this layer, raise it
851 stackingList
.raise(tmp
);
852 // don't bother looking at this window again
853 transients
.erase(wit
);
860 Raises the specified stacking entity. If the entity is a window,
861 all transients are also raised (preserving their stacking order).
862 If the window is part of a group, the entire group is raised (also
863 preserving the stacking order) befor raising the specified window.
865 The return value indicates which windows need to be restacked:
867 - if raiseWindow() return stackingList.end(), then nothing needs to
870 - if raiseWindow() returns an iterator for a layer boundary
871 (i.e. *raiseWindow(...) == 0) then all windows in all layers need to
874 - otherwise, the return value points to the bottom most window that
875 needs to be restacked (i.e. the top of the layer down to the return
876 value need to be restacked)
879 StackingList::iterator
raiseWindow(StackingList
&stackingList
,
880 StackEntity
*entity
) {
881 BlackboxWindow
*win
= dynamic_cast<BlackboxWindow
*>(entity
);
883 if (win
->isFullScreen() && win
->layer() != StackingList::LayerFullScreen
) {
884 // move full-screen windows over all other windows when raising
885 win
->changeLayer(StackingList::LayerFullScreen
);
886 return stackingList
.end();
890 StackingList::iterator layer
= stackingList
.layer(entity
->layer());
891 if (*layer
== entity
) {
892 // already on top of the layer
893 return stackingList
.end();
896 BWindowGroup
*group
= 0;
898 group
= win
->findWindowGroup();
900 // raise all windows in the group before raising 'win'
901 ::raiseGroup(stackingList
, group
);
902 } else if (win
->isTransient()) {
903 // raise non-transient parent before raising transient
904 BlackboxWindow
*tmp
= win
->findNonTransientParent();
906 (void) ::raiseWindow(stackingList
, tmp
);
911 StackingList::iterator top
= stackingList
.raise(entity
),
912 end
= stackingList
.end();
915 // ... with all transients above it
916 BlackboxWindowList transients
= win
->buildFullTransientList();
917 if (!transients
.empty())
918 raiseTransients(top
, stackingList
, transients
);
920 // ... and group transients on top
921 if (group
&& !win
->isGroupTransient()) {
922 const BlackboxWindow
* const w
= win
->isTransient()
923 ? win
->findNonTransientParent()
925 if (!w
|| !w
->isGroupTransient()) {
926 BlackboxWindowList groupTransients
= group
->transients();
927 BlackboxWindowList::const_iterator wit
= groupTransients
.begin(),
928 wend
= groupTransients
.end();
929 for (; wit
!= wend
; ++wit
) {
930 BlackboxWindowList x
= (*wit
)->buildFullTransientList();
931 groupTransients
.splice(groupTransients
.end(), x
);
933 if (!groupTransients
.empty())
934 raiseTransients(top
, stackingList
, groupTransients
);
941 StackingList::iterator it
= std::find(top
, end
, (StackEntity
*) 0);
948 Raises the stack entity as above and then updates the stacking on
949 the X server. The EWMH stacking hint is also updated.
951 void BScreen::raiseWindow(StackEntity
*entity
) {
952 StackingList::iterator top
= ::raiseWindow(_stackingList
, entity
),
953 end
= _stackingList
.end();
955 // no need to raise entity
957 } else if (!(*top
)) {
958 // need to restack all windows
963 // find the first entity above us (if any)
964 StackEntity
*above
= 0;
965 StackingList::iterator layer
= _stackingList
.layer(entity
->layer()),
966 begin
= _stackingList
.begin(),
968 for (--it
; it
!= begin
; --it
) {
978 // found another entity above the one we are raising
979 stack
.push_back(above
->windowID());
981 // keep everying under empty_window
982 stack
.push_back(empty_window
);
985 // go to the layer boundary above 'top'
987 for (--it
; *it
&& it
!= begin
; --it
)
990 // put all windows from the layer boundary to 'top' into the stack
991 for (++it
; *it
&& it
!= top
; ++it
) {
993 stack
.push_back((*it
)->windowID());
995 stack
.push_back((*top
)->windowID());
997 XRestackWindows(_blackbox
->XDisplay(), &stack
[0], stack
.size());
998 updateClientListStackingHint();
1003 void lowerGroup(StackingList
&stackingList
, BWindowGroup
*group
) {
1004 BlackboxWindowList windows
= group
->windows();
1005 int layer
= StackingList::LayerNormal
;
1006 for (; layer
< StackingList::LayerDesktop
; ++layer
) {
1007 const StackingList::iterator begin
= stackingList
.begin(),
1008 end
= stackingList
.end();
1009 StackingList::iterator it
= stackingList
.layer(StackingList::Layer(layer
)),
1010 bottom
= std::find(it
, end
, (StackEntity
*) 0);
1011 // 'it' points to the top of the layer
1012 assert(bottom
!= end
);
1019 // walk down the layer, lowering all windows in the group
1020 for (; it
!= bottom
; ++it
) {
1022 BlackboxWindow
*tmp
= dynamic_cast<BlackboxWindow
*>(*it
);
1024 // entity is not a window
1028 const BlackboxWindowList::iterator wend
= windows
.end();
1029 BlackboxWindowList::iterator wit
;
1030 if ((wit
= std::find(windows
.begin(), wend
, tmp
)) == wend
)
1033 // found a window in this layer, lower it
1035 (void) stackingList
.lower(tmp
);
1036 // don't bother looking at this window again
1043 static void lowerTransients(StackingList::iterator it
,
1044 StackingList
&stackingList
,
1045 BlackboxWindowList
&transients
) {
1046 // 'it' points to the top of the layer
1047 const StackingList::iterator end
= stackingList
.end(),
1048 bottom
= std::find(it
, end
, (StackEntity
*) 0);
1049 assert(bottom
!= end
);
1050 for (; it
!= bottom
; ++it
) {
1052 BlackboxWindow
*tmp
= dynamic_cast<BlackboxWindow
*>(*it
);
1056 const BlackboxWindowList::iterator wend
= transients
.end();
1057 BlackboxWindowList::iterator wit
;
1058 if ((wit
= std::find(transients
.begin(), wend
, tmp
)) == wend
)
1061 // found a transient in this layer, lower it
1063 StackingList::iterator l
= stackingList
.lower(tmp
);
1064 // don't bother looking at this window again
1065 transients
.erase(wit
);
1071 Lowers the specified stacking entity. If the entity is a window,
1072 all transients are also lowered (preserving their stacking order).
1073 If the window is part of a group, the entire group is lowered (also
1074 preserving the stacking order) befor lowering the specified window.
1076 The return value indicates which windows need to be restacked:
1078 - if lowerWindow() return stackingList.end(), then nothing needs to
1081 - if lowerWindow() returns an iterator for a layer boundary
1082 (i.e. *lowerWindow(...) == 0) then all windows in all layers need to
1085 - otherwise, the return value points to the top-most window that
1086 needs to be restacked (i.e. the return value down to the bottom of
1087 the layer need to be restacked)
1090 StackingList::iterator
lowerWindow(StackingList
&stackingList
,
1091 StackEntity
*entity
,
1092 bool ignore_group
= false) {
1093 StackingList::iterator it
, end
= stackingList
.end();
1094 BlackboxWindow
*win
= dynamic_cast<BlackboxWindow
*>(entity
);
1097 BWindowGroup
*group
= win
->findWindowGroup();
1098 if (!ignore_group
&& group
) {
1099 // lower all windows in the group before lowering 'win'
1100 ::lowerGroup(stackingList
, group
);
1101 it
= std::find(stackingList
.begin(), end
, (StackEntity
*) 0);
1105 const StackingList::iterator layer
= stackingList
.layer(win
->layer());
1106 if (win
->isTransient()) {
1107 BlackboxWindow
*const tmp
= win
->findNonTransientParent();
1108 if (tmp
&& !tmp
->isGroupTransient()) {
1109 // lower non-transient parent
1110 (void) ::lowerWindow(stackingList
, tmp
, true);
1117 // lower transients oinf 'win' and 'win'
1118 BlackboxWindowList transients
= win
->buildFullTransientList();
1119 if (!transients
.empty()) {
1120 ::lowerTransients(layer
, stackingList
, transients
);
1121 (void) stackingList
.lower(win
);
1126 it
= stackingList
.lower(entity
);
1129 (void) stackingList
.lower(entity
);
1134 it
= stackingList
.lower(entity
);
1141 void BScreen::lowerWindow(StackEntity
*entity
) {
1142 StackingList::iterator top
= ::lowerWindow(_stackingList
, entity
),
1143 end
= _stackingList
.end();
1145 // no need to lower entity
1147 } else if (!(*top
)) {
1148 // need to restack all windows
1153 // find the entity above us (if any)
1154 StackEntity
*above
= 0;
1155 StackingList::iterator begin
= _stackingList
.begin(),
1158 for (--it
; it
!= begin
; --it
) {
1166 // build the window stack
1169 // found another entity above the one we are lowering
1170 stack
.push_back(above
->windowID());
1172 // keep everying under empty_window
1173 stack
.push_back(empty_window
);
1176 // find the layer boundary
1177 StackingList::iterator bottom
= std::find(top
, end
, (StackEntity
*) 0);
1178 assert(bottom
!= end
);
1180 // put all windows from 'top' to the layer boundary into the stack
1181 for (it
= top
; it
!= bottom
; ++it
) {
1182 assert(*it
&& it
!= end
);
1183 stack
.push_back((*it
)->windowID());
1185 stack
.push_back((*top
)->windowID());
1187 XRestackWindows(_blackbox
->XDisplay(), &stack
[0], stack
.size());
1188 updateClientListStackingHint();
1192 void BScreen::restackWindows(void) {
1194 stack
.push_back(empty_window
);
1196 StackingList::const_iterator it
, end
= _stackingList
.end();
1197 for (it
= _stackingList
.begin(); it
!= end
; ++it
) {
1199 stack
.push_back((*it
)->windowID());
1202 XRestackWindows(_blackbox
->XDisplay(), &stack
[0], stack
.size());
1203 updateClientListStackingHint();
1207 void BScreen::propagateWindowName(const BlackboxWindow
* const win
) {
1208 if (win
->isIconic()) {
1209 _iconmenu
->changeItem(win
->windowNumber(),
1210 bt::ellideText(win
->iconTitle(), 60,
1211 bt::toUnicode("...")));
1212 } else if (win
->workspace() != bt::BSENTINEL
) {
1213 Workspace
*workspace
= findWorkspace(win
->workspace());
1214 assert(workspace
!= 0);
1215 workspace
->menu()->changeItem(win
->windowNumber(),
1216 bt::ellideText(win
->title(), 60,
1217 bt::toUnicode("...")));
1220 if (_toolbar
&& _blackbox
->focusedWindow() == win
)
1221 _toolbar
->redrawWindowLabel();
1225 void BScreen::nextFocus(void) {
1226 BlackboxWindow
*focused
= _blackbox
->focusedWindow(),
1228 BlackboxWindowList::iterator it
, end
= windowList
.end();
1230 if (focused
&& focused
->workspace() == current_workspace
&&
1231 focused
->screen()->screen_info
.screenNumber() ==
1232 screen_info
.screenNumber()) {
1233 it
= std::find(windowList
.begin(), end
, focused
);
1235 for (; it
!= end
; ++it
) {
1237 if (next
&& next
!= focused
&& next
->workspace() == current_workspace
&&
1238 next
->setInputFocus()) {
1239 // we found our new focus target
1240 next
->setInputFocus();
1248 for (it
= windowList
.begin(); it
!= end
; ++it
) {
1250 if (next
&& next
->workspace() == current_workspace
&&
1251 next
->setInputFocus()) {
1252 // we found our new focus target
1261 void BScreen::prevFocus(void) {
1262 BlackboxWindow
*focused
= _blackbox
->focusedWindow(),
1264 BlackboxWindowList::reverse_iterator it
, end
= windowList
.rend();
1266 if (focused
&& focused
->workspace() == current_workspace
&&
1267 focused
->screen()->screen_info
.screenNumber() ==
1268 screen_info
.screenNumber()) {
1269 it
= std::find(windowList
.rbegin(), end
, focused
);
1271 for (; it
!= end
; ++it
) {
1273 if (next
&& next
!= focused
&& next
->workspace() == current_workspace
&&
1274 next
->setInputFocus()) {
1275 // we found our new focus target
1276 next
->setInputFocus();
1284 for (it
= windowList
.rbegin(); it
!= end
; ++it
) {
1286 if (next
&& next
->workspace() == current_workspace
&&
1287 next
->setInputFocus()) {
1288 // we found our new focus target
1297 void BScreen::raiseFocus(void) {
1298 BlackboxWindow
*focused
= _blackbox
->focusedWindow();
1299 if (! focused
|| focused
->screen() != this)
1302 raiseWindow(focused
);
1306 void BScreen::InitMenu(void) {
1310 _rootmenu
= new Rootmenu(*_blackbox
, screen_info
.screenNumber(), this);
1311 _rootmenu
->showTitle();
1313 bool defaultMenu
= True
;
1315 if (_blackbox
->resource().menuFilename()) {
1316 FILE *menu_file
= fopen(_blackbox
->resource().menuFilename(), "r");
1319 perror(_blackbox
->resource().menuFilename());
1321 if (feof(menu_file
)) {
1322 fprintf(stderr
, "%s: menu file '%s' is empty\n",
1323 _blackbox
->applicationName().c_str(),
1324 _blackbox
->resource().menuFilename());
1326 char line
[1024], label
[1024];
1327 memset(line
, 0, 1024);
1328 memset(label
, 0, 1024);
1330 while (fgets(line
, 1024, menu_file
) && ! feof(menu_file
)) {
1334 int i
, key
= 0, index
= -1, len
= strlen(line
);
1336 for (i
= 0; i
< len
; i
++) {
1337 if (line
[i
] == '[') index
= 0;
1338 else if (line
[i
] == ']') break;
1339 else if (line
[i
] != ' ')
1341 key
+= tolower(line
[i
]);
1344 if (key
== 517) { // [begin]
1346 for (i
= index
; i
< len
; i
++) {
1349 else if (line
[i
] == ')')
1351 else if (index
++ >= 0) {
1352 if (line
[i
] == '\\' && i
< len
- 1) i
++;
1353 label
[index
- 1] = line
[i
];
1357 if (index
== -1) index
= 0;
1358 label
[index
] = '\0';
1360 _rootmenu
->setTitle(bt::toUnicode(label
));
1361 defaultMenu
= parseMenuFile(menu_file
, _rootmenu
);
1371 _rootmenu
->setTitle(bt::toUnicode("_Blackbox"));
1373 _rootmenu
->insertFunction(bt::toUnicode("xterm"),
1374 BScreen::Execute
, "xterm");
1375 _rootmenu
->insertFunction(bt::toUnicode("Restart"),
1377 _rootmenu
->insertFunction(bt::toUnicode("Exit"),
1380 _blackbox
->saveMenuFilename(_blackbox
->resource().menuFilename());
1386 size_t string_within(char begin
, char end
,
1387 const char *input
, size_t start_at
, size_t length
,
1391 size_t i
= start_at
;
1392 for (; i
< length
; ++i
) {
1393 if (input
[i
] == begin
) {
1395 } else if (input
[i
] == end
) {
1398 if (input
[i
] == '\\' && i
< length
- 1) i
++;
1399 output
[index
++] = input
[i
];
1404 output
[index
] = '\0';
1412 bool BScreen::parseMenuFile(FILE *file
, Rootmenu
*menu
) {
1413 char line
[1024], keyword
[1024], label
[1024], command
[1024];
1416 while (! (done
|| feof(file
))) {
1417 memset(line
, 0, 1024);
1418 memset(label
, 0, 1024);
1419 memset(command
, 0, 1024);
1421 if (! fgets(line
, 1024, file
))
1424 if (line
[0] == '#') // comment, skip it
1427 size_t line_length
= strlen(line
);
1428 unsigned int key
= 0;
1430 // get the keyword enclosed in []'s
1431 size_t pos
= string_within('[', ']', line
, 0, line_length
, keyword
);
1433 if (keyword
[0] == '\0') { // no keyword, no menu entry
1436 size_t len
= strlen(keyword
);
1437 for (size_t i
= 0; i
< len
; ++i
) {
1438 if (keyword
[i
] != ' ')
1439 key
+= tolower(keyword
[i
]);
1443 // get the label enclosed in ()'s
1444 pos
= string_within('(', ')', line
, pos
, line_length
, label
);
1446 // get the command enclosed in {}'s
1447 pos
= string_within('{', '}', line
, pos
, line_length
, command
);
1456 menu
->insertSeparator();
1462 menu
->insertItem(bt::toUnicode(label
));
1467 if (! (*label
&& *command
)) {
1469 "%s: [exec] error, no menu label and/or command defined\n",
1470 _blackbox
->applicationName().c_str());
1474 menu
->insertFunction(bt::toUnicode(label
), BScreen::Execute
, command
);
1479 fprintf(stderr
, "%s: [exit] error, no menu label defined\n",
1480 _blackbox
->applicationName().c_str());
1484 menu
->insertFunction(bt::toUnicode(label
), BScreen::Exit
);
1487 case 561: { // style
1488 if (! (*label
&& *command
)) {
1490 "%s: [style] error, no menu label and/or filename defined\n",
1491 _blackbox
->applicationName().c_str());
1495 std::string style
= bt::expandTilde(command
);
1496 menu
->insertFunction(bt::toUnicode(label
),
1497 BScreen::SetStyle
, style
.c_str());
1503 fprintf(stderr
, "%s: [config] error, no label defined",
1504 _blackbox
->applicationName().c_str());
1508 menu
->insertItem(bt::toUnicode(label
), configmenu
);
1511 case 740: { // include
1513 fprintf(stderr
, "%s: [include] error, no filename defined\n",
1514 _blackbox
->applicationName().c_str());
1518 std::string newfile
= bt::expandTilde(label
);
1519 FILE *submenufile
= fopen(newfile
.c_str(), "r");
1521 if (! submenufile
) {
1522 perror(newfile
.c_str());
1527 if (fstat(fileno(submenufile
), &buf
) ||
1528 ! S_ISREG(buf
.st_mode
)) {
1529 fprintf(stderr
, "%s: [include] error: '%s' is not a regular file\n",
1530 _blackbox
->applicationName().c_str(), newfile
.c_str());
1534 if (! feof(submenufile
)) {
1535 if (! parseMenuFile(submenufile
, menu
))
1536 _blackbox
->saveMenuFilename(newfile
);
1538 fclose(submenufile
);
1544 case 767: { // submenu
1546 fprintf(stderr
, "%s: [submenu] error, no menu label defined\n",
1547 _blackbox
->applicationName().c_str());
1552 new Rootmenu(*_blackbox
, screen_info
.screenNumber(), this);
1553 submenu
->showTitle();
1556 submenu
->setTitle(bt::toUnicode(command
));
1558 submenu
->setTitle(bt::toUnicode(label
));
1560 parseMenuFile(file
, submenu
);
1561 menu
->insertItem(bt::toUnicode(label
), submenu
);
1566 case 773: { // restart
1568 fprintf(stderr
, "%s: [restart] error, no menu label defined\n",
1569 _blackbox
->applicationName().c_str());
1574 menu
->insertFunction(bt::toUnicode(label
),
1575 BScreen::RestartOther
, command
);
1577 menu
->insertFunction(bt::toUnicode(label
), BScreen::Restart
);
1582 case 845: { // reconfig
1584 fprintf(stderr
, "%s: [reconfig] error, no menu label defined\n",
1585 _blackbox
->applicationName().c_str());
1589 menu
->insertFunction(bt::toUnicode(label
), BScreen::Reconfigure
);
1593 case 995: // stylesdir
1594 case 1113: { // stylesmenu
1595 bool newmenu
= ((key
== 1113) ? True
: False
);
1597 if (! *label
|| (! *command
&& newmenu
)) {
1599 "%s: [stylesdir/stylesmenu] error, no directory defined\n",
1600 _blackbox
->applicationName().c_str());
1604 char *directory
= ((newmenu
) ? command
: label
);
1606 std::string stylesdir
= bt::expandTilde(directory
);
1608 struct stat statbuf
;
1610 if (stat(stylesdir
.c_str(), &statbuf
) == -1) {
1612 "%s: [stylesdir/stylesmenu] error, '%s' does not exist\n",
1613 _blackbox
->applicationName().c_str(), stylesdir
.c_str());
1616 if (! S_ISDIR(statbuf
.st_mode
)) {
1618 "%s: [stylesdir/stylesmenu] error, '%s' is not a directory\n",
1619 _blackbox
->applicationName().c_str(), stylesdir
.c_str());
1623 Rootmenu
*stylesmenu
;
1627 new Rootmenu(*_blackbox
, screen_info
.screenNumber(),this);
1628 stylesmenu
->showTitle();
1633 DIR *d
= opendir(stylesdir
.c_str());
1635 std::vector
<std::string
> ls
;
1637 while((p
= readdir(d
)))
1638 ls
.push_back(p
->d_name
);
1642 std::sort(ls
.begin(), ls
.end());
1644 std::vector
<std::string
>::iterator it
= ls
.begin(),
1646 for (; it
!= end
; ++it
) {
1647 std::string
& fname
= *it
;
1649 if (fname
[0] == '.' || fname
[fname
.size()-1] == '~')
1652 std::string style
= stylesdir
;
1656 if (! stat(style
.c_str(), &statbuf
) && S_ISREG(statbuf
.st_mode
)) {
1657 // convert 'This_Long_Name' to 'This Long Name'
1658 std::replace(fname
.begin(), fname
.end(), '_', ' ');
1660 stylesmenu
->insertFunction(bt::toUnicode(fname
),
1661 BScreen::SetStyle
, style
);
1666 stylesmenu
->setTitle(bt::toUnicode(label
));
1667 menu
->insertItem(bt::toUnicode(label
), stylesmenu
);
1670 _blackbox
->saveMenuFilename(stylesdir
);
1674 case 1090: { // workspaces
1676 fprintf(stderr
, "%s: [workspaces] error, no menu label defined\n",
1677 _blackbox
->applicationName().c_str());
1681 menu
->insertItem(bt::toUnicode(label
), _workspacemenu
);
1687 return (menu
->count() == 0);
1691 void BScreen::shutdown(void) {
1692 XSelectInput(_blackbox
->XDisplay(), screen_info
.rootWindow(),
1694 XSync(_blackbox
->XDisplay(), False
);
1696 // unmanage all windows, but keep them in the current stacking order
1698 StackingList::const_iterator it
, end
= _stackingList
.end();
1699 for (it
= _stackingList
.begin(); it
!= end
; ++it
) {
1702 const BlackboxWindow
* const win
= dynamic_cast<BlackboxWindow
*>(*it
);
1705 stack
.push_back(win
->clientWindow());
1708 while(! windowList
.empty())
1709 unmanageWindow(windowList
.back());
1712 XRestackWindows(_blackbox
->XDisplay(), &stack
[0], stack
.size());
1719 void BScreen::showGeometry(GeometryType type
, const bt::Rect
&rect
) {
1720 if (! geom_visible
) {
1721 XMoveResizeWindow(_blackbox
->XDisplay(), geom_window
,
1722 (screen_info
.width() - geom_w
) / 2,
1723 (screen_info
.height() - geom_h
) / 2, geom_w
, geom_h
);
1724 XMapWindow(_blackbox
->XDisplay(), geom_window
);
1725 XRaiseWindow(_blackbox
->XDisplay(), geom_window
);
1727 geom_visible
= True
;
1733 sprintf(label
, "X:%4d Y:%4d", rect
.x(), rect
.y());
1736 sprintf(label
, "W:%4u H:%4u", rect
.width(), rect
.height());
1742 const WindowStyle
&style
= _resource
.windowStyle();
1743 const bt::Texture
&texture
=
1744 (style
.focus
.label
.texture() == bt::Texture::Parent_Relative
)
1746 : style
.focus
.label
;
1747 const bt::Rect
u(0, 0, geom_w
, geom_h
);
1748 const bt::Rect
t(style
.label_margin
,
1750 geom_w
- (style
.label_margin
* 2),
1751 geom_h
- (style
.label_margin
* 2));
1752 const bt::Pen
pen(screen_info
.screenNumber(), style
.focus
.text
);
1753 bt::drawTexture(screen_info
.screenNumber(), texture
, geom_window
,
1755 bt::drawText(style
.font
, pen
, geom_window
, t
, style
.alignment
,
1756 bt::toUnicode(label
));
1760 void BScreen::hideGeometry(void) {
1762 XUnmapWindow(_blackbox
->XDisplay(), geom_window
);
1763 geom_visible
= False
;
1768 void BScreen::addStrut(bt::EWMH::Strut
*strut
) {
1769 strutList
.push_back(strut
);
1770 updateAvailableArea();
1774 void BScreen::removeStrut(bt::EWMH::Strut
*strut
) {
1775 strutList
.remove(strut
);
1776 updateAvailableArea();
1780 void BScreen::updateStrut(void) {
1781 updateAvailableArea();
1785 const bt::Rect
& BScreen::availableArea(void) {
1786 if (_blackbox
->resource().fullMaximization())
1787 return screen_info
.rect(); // return the full screen
1792 void BScreen::updateAvailableArea(void) {
1795 /* these values represent offsets from the screen edge
1796 * we look for the biggest offset on each edge and then apply them
1798 * do not be confused by the similarity to the names of Rect's members
1800 bt::EWMH::Strut current
;
1802 StrutList::const_iterator sit
= strutList
.begin(), send
= strutList
.end();
1804 for(; sit
!= send
; ++sit
) {
1805 const bt::EWMH::Strut
* const strut
= *sit
;
1806 if (strut
->left
> current
.left
)
1807 current
.left
= strut
->left
;
1808 if (strut
->top
> current
.top
)
1809 current
.top
= strut
->top
;
1810 if (strut
->right
> current
.right
)
1811 current
.right
= strut
->right
;
1812 if (strut
->bottom
> current
.bottom
)
1813 current
.bottom
= strut
->bottom
;
1816 new_area
.setPos(current
.left
, current
.top
);
1817 new_area
.setSize(screen_info
.width() - (current
.left
+ current
.right
),
1818 screen_info
.height() - (current
.top
+ current
.bottom
));
1820 if (new_area
!= usableArea
) {
1821 usableArea
= new_area
;
1822 BlackboxWindowList::iterator wit
= windowList
.begin(),
1823 wend
= windowList
.end();
1824 for (; wit
!= wend
; ++wit
)
1825 if ((*wit
)->isMaximized()) (*wit
)->remaximize();
1827 updateWorkareaHint();
1832 Workspace
* BScreen::findWorkspace(unsigned int index
) const {
1833 if (index
== bt::BSENTINEL
)
1835 assert(index
< workspacesList
.size());
1836 return workspacesList
[index
];
1840 void BScreen::clientMessageEvent(const XClientMessageEvent
* const event
) {
1841 if (event
->format
!= 32) return;
1843 if (event
->message_type
== _blackbox
->ewmh().numberOfDesktops()) {
1844 unsigned int number
= event
->data
.l
[0];
1845 if (number
> workspaceCount()) {
1846 for (; number
!= workspaceCount(); --number
)
1848 } else if (number
< workspaceCount()) {
1849 for (; number
!= workspaceCount(); ++number
)
1850 removeLastWorkspace();
1852 } else if (event
->message_type
== _blackbox
->ewmh().desktopNames()) {
1854 } else if (event
->message_type
== _blackbox
->ewmh().currentDesktop()) {
1855 const unsigned int workspace
= event
->data
.l
[0];
1856 if (workspace
< workspaceCount() && workspace
!= current_workspace
)
1857 setCurrentWorkspace(workspace
);
1862 void BScreen::buttonPressEvent(const XButtonEvent
* const event
) {
1863 if (event
->button
== 1) {
1865 set this screen active. keygrabs and input focus will stay on
1866 this screen until the user focuses a window on another screen or
1867 makes another screen active.
1869 _blackbox
->setActiveScreen(this);
1870 } else if (event
->button
== 2) {
1871 _workspacemenu
->popup(event
->x_root
, event
->y_root
);
1872 } else if (event
->button
== 3) {
1873 _blackbox
->checkMenu();
1874 _rootmenu
->popup(event
->x_root
, event
->y_root
);
1875 } else if (event
->button
== 4) {
1876 setCurrentWorkspace(current_workspace
< workspacesList
.size() - 1
1877 ? current_workspace
+ 1
1879 } else if (event
->button
== 5) {
1880 setCurrentWorkspace(current_workspace
> 0
1881 ? current_workspace
- 1
1882 : workspacesList
.size() - 1);
1887 void BScreen::propertyNotifyEvent(const XPropertyEvent
* const event
) {
1888 if (event
->atom
== _blackbox
->ewmh().activeWindow() && _toolbar
)
1889 _toolbar
->redrawWindowLabel();
1893 void BScreen::unmapNotifyEvent(const XUnmapEvent
* const event
) {
1894 // handle synthetic unmap events according to ICCCM section 4.1.4
1895 BlackboxWindow
*win
= _blackbox
->findWindow(event
->window
);
1896 if (win
&& event
->event
== screen_info
.rootWindow()
1897 && !event
->from_configure
)
1898 win
->unmapNotifyEvent(event
);
1902 void BScreen::toggleFocusModel(FocusModel model
) {
1903 std::for_each(windowList
.begin(), windowList
.end(),
1904 std::mem_fun(&BlackboxWindow::ungrabButtons
));
1906 _blackbox
->resource().setFocusModel(model
);
1908 std::for_each(windowList
.begin(), windowList
.end(),
1909 std::mem_fun(&BlackboxWindow::grabButtons
));
1913 void BScreen::updateWorkareaHint(void) const {
1914 unsigned long *workarea
, *tmp
;
1916 tmp
= workarea
= new unsigned long[workspaceCount() * 4];
1918 for (unsigned int i
= 0; i
< workspaceCount(); ++i
) {
1919 tmp
[0] = usableArea
.x();
1920 tmp
[1] = usableArea
.y();
1921 tmp
[2] = usableArea
.width();
1922 tmp
[3] = usableArea
.height();
1926 _blackbox
->ewmh().setWorkarea(screen_info
.rootWindow(),
1927 workarea
, workspaceCount());
1933 void BScreen::updateDesktopNamesHint(void) const {
1934 std::vector
<bt::ustring
> names(workspacesList
.size());
1935 WorkspaceList::const_iterator it
= workspacesList
.begin();
1936 const WorkspaceList::const_iterator end
= workspacesList
.end();
1937 for (; it
!= end
; ++it
)
1938 names
.push_back((*it
)->name());
1939 _blackbox
->ewmh().setDesktopNames(screen_info
.rootWindow(), names
);
1943 void BScreen::updateClientListHint(void) const {
1944 if (windowList
.empty()) {
1945 _blackbox
->ewmh().removeProperty(screen_info
.rootWindow(),
1946 _blackbox
->ewmh().clientList());
1950 bt::EWMH::WindowList
clientList(windowList
.size());
1952 std::transform(windowList
.begin(), windowList
.end(), clientList
.begin(),
1953 std::mem_fun(&BlackboxWindow::clientWindow
));
1955 _blackbox
->ewmh().setClientList(screen_info
.rootWindow(), clientList
);
1959 void BScreen::updateClientListStackingHint(void) const {
1960 bt::EWMH::WindowList stack
;
1962 // we store windows in top-to-bottom order, but the EWMH wants
1964 StackingList::const_reverse_iterator it
= _stackingList
.rbegin(),
1965 end
= _stackingList
.rend();
1966 for (; it
!= end
; ++it
) {
1967 const BlackboxWindow
* const win
= dynamic_cast<BlackboxWindow
*>(*it
);
1968 if (win
) stack
.push_back(win
->clientWindow());
1971 if (stack
.empty()) {
1972 _blackbox
->ewmh().removeProperty(screen_info
.rootWindow(),
1973 _blackbox
->ewmh().clientListStacking());
1977 _blackbox
->ewmh().setClientListStacking(screen_info
.rootWindow(), stack
);
1981 void BScreen::readDesktopNames(void) {
1982 std::vector
<bt::ustring
> names
;
1983 if(! _blackbox
->ewmh().readDesktopNames(screen_info
.rootWindow(), names
))
1986 std::vector
<bt::ustring
>::const_iterator it
= names
.begin();
1987 const std::vector
<bt::ustring
>::const_iterator end
= names
.end();
1988 WorkspaceList::iterator wit
= workspacesList
.begin();
1989 const WorkspaceList::iterator wend
= workspacesList
.end();
1991 for (; wit
!= wend
&& it
!= end
; ++wit
, ++it
) {
1992 if ((*wit
)->name() != *it
)
1993 (*wit
)->setName(*it
);
1996 if (names
.size() < workspacesList
.size())
1997 updateDesktopNamesHint();
2001 BlackboxWindow
*BScreen::window(unsigned int workspace
, unsigned int id
) {
2002 StackingList::const_iterator it
= _stackingList
.begin(),
2003 end
= _stackingList
.end();
2004 for (; it
!= end
; ++it
) {
2005 BlackboxWindow
* const win
= dynamic_cast<BlackboxWindow
*>(*it
);
2006 if (win
&& win
->workspace() == workspace
&& win
->windowNumber() == id
)
2009 assert(false); // should not happen
2014 void BScreen::placeWindow(BlackboxWindow
*win
) {
2015 const bt::Rect
&avail
= availableArea();
2016 bt::Rect
new_win(avail
.x(), avail
.y(),
2017 win
->frameRect().width(), win
->frameRect().height());
2018 bool placed
= False
;
2020 BlackboxResource
&res
= _blackbox
->resource();
2021 switch (res
.windowPlacementPolicy()) {
2022 case RowSmartPlacement
:
2023 case ColSmartPlacement
:
2024 placed
= smartPlacement(win
->workspace(), new_win
, avail
);
2027 break; // handled below
2030 if (placed
== False
) {
2031 cascadePlacement(new_win
, avail
);
2032 cascade_x
+= _resource
.windowStyle().title_height
;
2033 cascade_y
+= _resource
.windowStyle().title_height
;
2036 if (new_win
.right() > avail
.right())
2037 new_win
.setX(avail
.left());
2038 if (new_win
.bottom() > avail
.bottom())
2039 new_win
.setY(avail
.top());
2041 win
->configure(new_win
.x(), new_win
.y(), new_win
.width(), new_win
.height());
2045 bool BScreen::cascadePlacement(bt::Rect
&win
,
2046 const bt::Rect
&avail
) {
2047 if (cascade_x
> (avail
.width() / 2) ||
2048 cascade_y
> (avail
.height() / 2))
2049 cascade_x
= cascade_y
= 32;
2051 if (cascade_x
== 32) {
2052 cascade_x
+= avail
.x();
2053 cascade_y
+= avail
.y();
2056 win
.setPos(cascade_x
, cascade_y
);
2058 if (win
.right() > avail
.right() ||
2059 win
.bottom() > avail
.bottom()) {
2060 cascade_x
= cascade_y
= 32;
2062 cascade_x
+= avail
.x();
2063 cascade_y
+= avail
.y();
2065 win
.setPos(cascade_x
, cascade_y
);
2072 bool BScreen::smartPlacement(unsigned int workspace
, bt::Rect
& rect
,
2073 const bt::Rect
& avail
) {
2075 const BlackboxResource
&res
= _blackbox
->resource();
2076 const bool row_placement
=
2077 (res
.windowPlacementPolicy() == RowSmartPlacement
);
2078 const bool leftright
=
2079 (res
.rowPlacementDirection() == LeftRight
);
2080 const bool topbottom
=
2081 (res
.colPlacementDirection() == TopBottom
);
2082 const bool ignore_shaded
= res
.placementIgnoresShaded();
2084 const int left_border
= leftright
? 0 : -2;
2085 const int top_border
= topbottom
? 0 : -2;
2086 const int right_border
= leftright
? 2 : 0;
2087 const int bottom_border
= topbottom
? 2 : 0;
2089 StackingList::const_iterator w_it
, w_end
;
2092 build a sorted vector of x and y grid boundaries
2094 note: we use one vector to reduce the number of allocations
2095 std::vector must do.. we allocate as much memory as we would need
2096 in the worst case scenario and work with that
2098 std::vector
<int> coords(_stackingList
.size() * 4 + 4);
2099 std::vector
<int>::iterator
2100 x_begin
= coords
.begin(),
2102 y_begin
= coords
.begin() + _stackingList
.size() * 2 + 2,
2106 std::vector
<int>::iterator x_it
= x_begin
, y_it
= y_begin
;
2108 *x_it
++ = avail
.left();
2109 *x_it
++ = avail
.right();
2112 *y_it
++ = avail
.top();
2113 *y_it
++ = avail
.bottom();
2117 for (w_it
= _stackingList
.begin(), w_end
= _stackingList
.end();
2118 w_it
!= w_end
; ++w_it
) {
2119 const BlackboxWindow
* const win
=
2120 dynamic_cast<const BlackboxWindow
*>(*w_it
);
2123 if (win
->windowType() == WindowTypeDesktop
)
2125 if (win
->isIconic())
2127 if (win
->workspace() != bt::BSENTINEL
&& win
->workspace() != workspace
)
2129 if (ignore_shaded
&& win
->isShaded())
2132 *x_it
++ = std::max(win
->frameRect().left() + left_border
,
2134 *x_it
++ = std::min(win
->frameRect().right() + right_border
,
2138 *y_it
++ = std::max(win
->frameRect().top() + top_border
,
2140 *y_it
++ = std::min(win
->frameRect().bottom() + bottom_border
,
2145 assert(x_end
<= y_begin
);
2148 std::sort(x_begin
, x_end
);
2149 x_end
= std::unique(x_begin
, x_end
);
2151 std::sort(y_begin
, y_end
);
2152 y_end
= std::unique(y_begin
, y_end
);
2154 // build a distribution grid
2155 unsigned int gw
= x_end
- x_begin
- 1,
2156 gh
= y_end
- y_begin
- 1;
2157 std::vector
<bool> used_grid(gw
* gh
);
2158 std::fill_n(used_grid
.begin(), used_grid
.size(), false);
2160 for (w_it
= _stackingList
.begin(), w_end
= _stackingList
.end();
2161 w_it
!= w_end
; ++w_it
) {
2162 const BlackboxWindow
* const win
=
2163 dynamic_cast<const BlackboxWindow
*>(*w_it
);
2166 if (win
->windowType() == WindowTypeDesktop
)
2168 if (win
->isIconic())
2170 if (win
->workspace() != bt::BSENTINEL
&& win
->workspace() != workspace
)
2172 if (ignore_shaded
&& win
->isShaded())
2176 std::max(win
->frameRect().left() + left_border
,
2179 std::max(win
->frameRect().top() + top_border
,
2182 std::min(win
->frameRect().right() + right_border
,
2184 const int w_bottom
=
2185 std::min(win
->frameRect().bottom() + bottom_border
,
2188 // which areas of the grid are used by this window?
2189 std::vector
<int>::iterator l_it
= std::find(x_begin
, x_end
, w_left
),
2190 r_it
= std::find(x_begin
, x_end
, w_right
),
2191 t_it
= std::find(y_begin
, y_end
, w_top
),
2192 b_it
= std::find(y_begin
, y_end
, w_bottom
);
2193 assert(l_it
!= x_end
&&
2198 const unsigned int left
= l_it
- x_begin
,
2199 right
= r_it
- x_begin
,
2200 top
= t_it
- y_begin
,
2201 bottom
= b_it
- y_begin
;
2203 for (unsigned int gy
= top
; gy
< bottom
; ++gy
)
2204 for (unsigned int gx
= left
; gx
< right
; ++gx
)
2205 used_grid
[(gy
* gw
) + gx
] = true;
2209 Attempt to fit the window into any of the empty areas in the grid.
2210 The exact order is dependent upon the users configuration (as
2214 - outer -> vertical axis
2215 - inner -> horizontal axis
2218 - outer -> horizontal axis
2219 - inner -> vertical axis
2223 int &outer
= row_placement
? gy
: gx
;
2224 int &inner
= row_placement
? gx
: gy
;
2225 const int outer_delta
= row_placement
2226 ? (topbottom
? 1 : -1)
2227 : (leftright
? 1 : -1);
2228 const int inner_delta
= row_placement
2229 ? (leftright
? 1 : -1)
2230 : (topbottom
? 1 : -1);
2231 const int outer_begin
= row_placement
2232 ? (topbottom
? 0 : static_cast<int>(gh
) - 1)
2233 : (leftright
? 0 : static_cast<int>(gw
) - 1);
2234 const int outer_end
= row_placement
2235 ? (topbottom
? static_cast<int>(gh
) : -1)
2236 : (leftright
? static_cast<int>(gw
) : -1);
2237 const int inner_begin
= row_placement
2238 ? (leftright
? 0 : static_cast<int>(gw
) - 1)
2239 : (topbottom
? 0 : static_cast<int>(gh
) - 1);
2240 const int inner_end
= row_placement
2241 ? (leftright
? static_cast<int>(gw
) : -1)
2242 : (topbottom
? static_cast<int>(gh
) : -1);
2246 for (outer
= outer_begin
; ! fit
&& outer
!= outer_end
;
2247 outer
+= outer_delta
) {
2248 for (inner
= inner_begin
; ! fit
&& inner
!= inner_end
;
2249 inner
+= inner_delta
) {
2250 // see if the window fits in a single unused area
2251 if (used_grid
[(gy
* gw
) + gx
]) continue;
2253 where
.setCoords(*(x_begin
+ gx
), *(y_begin
+ gy
),
2254 *(x_begin
+ gx
+ 1), *(y_begin
+ gy
+ 1));
2256 if (where
.width() >= rect
.width() &&
2257 where
.height() >= rect
.height()) {
2263 see if we neighboring spaces are unused
2265 TODO: we should grid fit in the same direction as above,
2266 instead of always right->left and top->bottom
2268 int gx2
= gx
, gy2
= gy
;
2270 if (rect
.width() > where
.width()) {
2271 for (gx2
= gx
+1; gx2
< static_cast<int>(gw
); ++gx2
) {
2272 if (used_grid
[(gy
* gw
) + gx2
]) {
2277 where
.setCoords(*(x_begin
+ gx
), *(y_begin
+ gy
),
2278 *(x_begin
+ gx2
+ 1), *(y_begin
+ gy2
+ 1));
2280 if (where
.width() >= rect
.width()) break;
2283 if (gx2
>= static_cast<int>(gw
)) --gx2
;
2286 if (rect
.height() > where
.height()) {
2287 for (gy2
= gy
; gy2
< static_cast<int>(gh
); ++gy2
) {
2288 if (used_grid
[(gy2
* gw
) + gx
]) {
2293 where
.setCoords(*(x_begin
+ gx
), *(y_begin
+ gy
),
2294 *(x_begin
+ gx2
+ 1), *(y_begin
+ gy2
+ 1));
2296 if (where
.height() >= rect
.height()) break;
2299 if (gy2
>= static_cast<int>(gh
)) --gy2
;
2302 if (where
.width() >= rect
.width() &&
2303 where
.height() >= rect
.height()) {
2306 // make sure all spaces are really available
2307 for (int gy3
= gy
; gy3
<= gy2
; ++gy3
) {
2308 for (int gx3
= gx
; gx3
<= gx2
; ++gx3
) {
2309 if (used_grid
[(gy3
* gw
) + gx3
]) {
2320 const int screen_area
= avail
.width() * avail
.height();
2321 const int window_area
= rect
.width() * rect
.height();
2322 if (window_area
> screen_area
/ 8) {
2323 // center windows that don't fit (except for small windows)
2325 static_cast<int>(avail
.x() + (avail
.width() - rect
.width()) / 2);
2327 static_cast<int>(avail
.y() + (avail
.height() - rect
.height()) / 2);
2334 // adjust the location() based on left/right and top/bottom placement
2336 where
.setX(where
.right() - rect
.width() + 1);
2338 where
.setY(where
.bottom() - rect
.height() + 1);
2340 rect
.setPos(where
.x(), where
.y());
2346 void BScreen::createSlit(void) {
2349 _slit
= new Slit(this);
2350 _stackingList
.insert(_slit
);
2355 void BScreen::destroySlit(void) {
2358 _stackingList
.remove(_slit
);
2364 void BScreen::createToolbar(void) {
2365 assert(_toolbar
== 0);
2367 _toolbar
= new Toolbar(this);
2368 _stackingList
.insert(_toolbar
);
2373 void BScreen::destroyToolbar(void) {
2374 assert(_toolbar
!= 0);
2376 _stackingList
.remove(_toolbar
);
2382 Windowmenu
*BScreen::windowmenu(BlackboxWindow
*win
) {
2384 _windowmenu
= new Windowmenu(*_blackbox
, screen_info
.screenNumber());
2385 _windowmenu
->setWindow(win
);
2390 void BScreen::addIcon(BlackboxWindow
*win
) {
2391 if (win
->workspace() != bt::BSENTINEL
) {
2392 Workspace
*workspace
= findWorkspace(win
->workspace());
2393 assert(workspace
!= 0);
2394 workspace
->removeWindow(win
);
2397 if (win
->isTransient()) {
2398 BlackboxWindow
* const tmp
= win
->findNonTransientParent();
2400 win
->setWindowNumber(bt::BSENTINEL
);
2405 const bt::ustring s
=
2406 bt::ellideText(win
->iconTitle(), 60, bt::toUnicode("..."));
2407 int id
= _iconmenu
->insertItem(s
);
2408 _blackbox
->ewmh().setWMVisibleIconName(win
->clientWindow(), s
);
2409 win
->setWindowNumber(id
);
2413 void BScreen::removeIcon(BlackboxWindow
*win
) {
2414 if (win
->windowNumber() != bt::BSENTINEL
) {
2415 _iconmenu
->removeItem(win
->windowNumber());
2416 _blackbox
->ewmh().removeProperty(win
->clientWindow(),
2417 _blackbox
->ewmh().wmVisibleIconName());
2420 Workspace
*workspace
= findWorkspace(current_workspace
);
2421 assert(workspace
!= 0);
2422 workspace
->addWindow(win
);
2426 BlackboxWindow
*BScreen::icon(unsigned int id
) {
2427 StackingList::const_iterator it
= _stackingList
.begin(),
2428 end
= _stackingList
.end();
2429 for (; it
!= end
; ++it
) {
2430 BlackboxWindow
* const win
= dynamic_cast<BlackboxWindow
*>(*it
);
2431 if (win
&& win
->isIconic() && win
->windowNumber() == id
)
2434 assert(false); // should not happen