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()) {
528 Workspace
*workspace
= findWorkspace(current_workspace
);
529 assert(workspace
!= 0);
531 if (workspace
->focusedWindow()) {
532 // focus the window that last had focus
533 workspace
->focusedWindow()->setInputFocus();
535 // focus the top-most window in the stack
536 for (it
= _stackingList
.begin(); it
!= end
; ++it
) {
537 BlackboxWindow
* const tmp
= dynamic_cast<BlackboxWindow
*>(*it
);
540 || (tmp
->workspace() != current_workspace
541 && tmp
->workspace() != bt::BSENTINEL
))
543 if (tmp
->setInputFocus())
550 _blackbox
->ewmh().setCurrentDesktop(screen_info
.rootWindow(),
553 XUnmapWindow(_blackbox
->XDisplay(), empty_window
);
555 _blackbox
->XUngrabServer();
559 void BScreen::nextWorkspace(void) {
560 if (currentWorkspace() < (workspaceCount() - 1))
561 setCurrentWorkspace(currentWorkspace() + 1);
563 setCurrentWorkspace(0);
567 void BScreen::prevWorkspace(void) {
568 if (currentWorkspace() > 0)
569 setCurrentWorkspace(currentWorkspace() - 1);
571 setCurrentWorkspace(workspaceCount() - 1);
575 void BScreen::addWindow(Window w
) {
577 BlackboxWindow
* const win
= _blackbox
->findWindow(w
);
580 updateClientListHint();
582 // focus the new window if appropriate
583 switch (win
->windowType()) {
584 case WindowTypeDesktop
:
586 // these types should not be focused when managed
590 if (!_blackbox
->startingUp() &&
591 (!_blackbox
->activeScreen() || _blackbox
->activeScreen() == this) &&
592 (win
->isTransient() || _blackbox
->resource().focusNewWindows())) {
593 XSync(_blackbox
->XDisplay(), False
); // make sure the frame is mapped..
594 win
->setInputFocus();
601 static StackingList::iterator
raiseWindow(StackingList
&stackingList
,
602 StackEntity
*entity
);
604 void BScreen::manageWindow(Window w
) {
605 XWMHints
*wmhints
= XGetWMHints(_blackbox
->XDisplay(), w
);
606 bool slit_client
= (wmhints
&& (wmhints
->flags
& StateHint
) &&
607 wmhints
->initial_state
== WithdrawnState
);
608 if (wmhints
) XFree(wmhints
);
611 if (!_slit
) createSlit();
616 (void) new BlackboxWindow(_blackbox
, w
, this);
617 // verify that we have managed the window
618 BlackboxWindow
*win
= _blackbox
->findWindow(w
);
621 if (win
->workspace() >= workspaceCount() &&
622 win
->workspace() != bt::BSENTINEL
)
623 win
->setWorkspace(current_workspace
);
625 Workspace
*workspace
= findWorkspace(win
->workspace());
627 win
->setWorkspace(bt::BSENTINEL
);
628 win
->setWindowNumber(bt::BSENTINEL
);
630 workspace
->addWindow(win
);
633 if (!_blackbox
->startingUp())
636 windowList
.push_back(win
);
638 // insert window at the top of the stack
639 (void) _stackingList
.insert(win
);
640 (void) ::raiseWindow(_stackingList
, win
);
641 if (!_blackbox
->startingUp())
644 // 'show' window in the appropriate state
645 switch (_blackbox
->startingUp()
646 ? win
->currentState()
647 : win
->wmHints().initial_state
) {
663 void BScreen::releaseWindow(BlackboxWindow
*w
) {
665 updateClientListHint();
666 updateClientListStackingHint();
670 void BScreen::unmanageWindow(BlackboxWindow
*win
) {
673 if (win
->isIconic()) {
674 _iconmenu
->removeItem(win
->windowNumber());
676 Workspace
*workspace
= findWorkspace(win
->workspace());
678 workspace
->menu()->removeItem(win
->windowNumber());
679 if (workspace
->focusedWindow() == win
)
680 workspace
->clearFocusedWindow();
684 if (_blackbox
->running() && win
->isFocused()) {
685 // pass focus to the next appropriate window
686 if (focusFallback(win
)) {
687 // focus is going somewhere, but we want to avoid dangling pointers
688 _blackbox
->forgetFocusedWindow();
690 // explicitly clear the focus
691 _blackbox
->setFocusedWindow(0);
695 windowList
.remove(win
);
696 _stackingList
.remove(win
);
698 if (_windowmenu
&& _windowmenu
->window() == win
)
705 bool BScreen::focusFallback(const BlackboxWindow
*win
) {
706 Workspace
*workspace
= findWorkspace(win
->workspace());
708 workspace
= findWorkspace(current_workspace
);
710 if (workspace
->id() != current_workspace
)
713 BWindowGroup
*group
= win
->findWindowGroup();
715 // focus the top-most window in the group
716 BlackboxWindowList::const_iterator git
= group
->windows().begin(),
717 gend
= group
->windows().end();
718 StackingList::iterator it
= _stackingList
.begin(),
719 end
= _stackingList
.end();
720 for (; it
!= end
; ++it
) {
721 BlackboxWindow
* const tmp
= dynamic_cast<BlackboxWindow
*>(*it
);
724 || std::find(git
, gend
, tmp
) == gend
726 || (tmp
->workspace() != current_workspace
727 && tmp
->workspace() != bt::BSENTINEL
))
729 if (tmp
->setInputFocus())
734 const BlackboxWindow
* const zero
= 0;
737 if (win
->isTransient()) {
738 BlackboxWindow
* const tmp
= win
->findTransientFor();
741 && (tmp
->workspace() == current_workspace
742 || tmp
->workspace() == bt::BSENTINEL
)
743 && tmp
->setInputFocus())
747 // try to focus the top-most window in the same layer as win
748 StackingList::iterator it
= _stackingList
.layer(win
->layer()),
749 end
= std::find(it
, _stackingList
.end(), zero
);
750 assert(it
!= _stackingList
.end() && end
!= _stackingList
.end());
751 for (; it
!= end
; ++it
) {
752 BlackboxWindow
* const tmp
= dynamic_cast<BlackboxWindow
*>(*it
);
757 || (tmp
->workspace() != current_workspace
758 && tmp
->workspace() != bt::BSENTINEL
))
760 if (tmp
->setInputFocus())
765 // focus the top-most window in the stack
766 StackingList::iterator it
= _stackingList
.begin(),
767 end
= _stackingList
.end();
768 for (; it
!= end
; ++it
) {
769 BlackboxWindow
* const tmp
= dynamic_cast<BlackboxWindow
*>(*it
);
773 || (tmp
->workspace() != current_workspace
774 && tmp
->workspace() != bt::BSENTINEL
))
776 if (tmp
->setInputFocus())
785 Raises all windows, preserving the existing stacking order. Each
786 window is placed at the top of the layer it currently occupies (with
790 void raiseGroup(StackingList
&stackingList
, BWindowGroup
*group
) {
791 BlackboxWindowList windows
= group
->windows();
792 int layer
= StackingList::LayerNormal
;
793 for (; layer
< StackingList::LayerDesktop
; ++layer
) {
794 StackingList::iterator
795 it
, top
= stackingList
.layer(StackingList::Layer(layer
));
796 const StackingList::iterator begin
= stackingList
.begin(),
797 end
= stackingList
.end();
799 // 'top' points to the top of the layer, we need to start from the
800 // bottom of the layer
801 it
= std::find(top
, end
, (StackEntity
*) 0);
809 // walk up the layer, raising all windows in the group
810 for (--it
; it
!= begin
; --it
) {
816 BlackboxWindow
*tmp
= dynamic_cast<BlackboxWindow
*>(*it
);
818 // entity is not a window, or window is not visible on the
823 const BlackboxWindowList::iterator wend
= windows
.end();
824 BlackboxWindowList::iterator wit
;
825 if ((wit
= std::find(windows
.begin(), wend
, tmp
)) == wend
)
828 // found a window in this layer, raise it
830 stackingList
.raise(tmp
);
831 // don't bother looking at this window again
839 Raise all transients, preserving the existing stacking order.
842 void raiseTransients(StackingList::iterator top
,
843 StackingList
&stackingList
,
844 BlackboxWindowList
&transients
) {
845 // 'top' points to the top of the layer, we need to start from the bottom
846 StackingList::iterator begin
= stackingList
.begin(),
847 end
= stackingList
.end(),
848 it
= std::find(top
, end
, (StackEntity
*) 0);
850 for (--it
; it
!= begin
; --it
) {
855 BlackboxWindow
*tmp
= dynamic_cast<BlackboxWindow
*>(*it
);
858 BlackboxWindowList::iterator wit
= transients
.begin(),
859 wend
= transients
.end();
860 wit
= std::find(wit
, wend
, tmp
);
862 // found a transient in this layer, raise it
864 stackingList
.raise(tmp
);
865 // don't bother looking at this window again
866 transients
.erase(wit
);
873 Raises the specified stacking entity. If the entity is a window,
874 all transients are also raised (preserving their stacking order).
875 If the window is part of a group, the entire group is raised (also
876 preserving the stacking order) befor raising the specified window.
878 The return value indicates which windows need to be restacked:
880 - if raiseWindow() return stackingList.end(), then nothing needs to
883 - if raiseWindow() returns an iterator for a layer boundary
884 (i.e. *raiseWindow(...) == 0) then all windows in all layers need to
887 - otherwise, the return value points to the bottom most window that
888 needs to be restacked (i.e. the top of the layer down to the return
889 value need to be restacked)
892 StackingList::iterator
raiseWindow(StackingList
&stackingList
,
893 StackEntity
*entity
) {
894 BlackboxWindow
*win
= dynamic_cast<BlackboxWindow
*>(entity
);
896 if (win
->isFullScreen() && win
->layer() != StackingList::LayerFullScreen
) {
897 // move full-screen windows over all other windows when raising
898 win
->changeLayer(StackingList::LayerFullScreen
);
899 return stackingList
.end();
903 StackingList::iterator layer
= stackingList
.layer(entity
->layer());
904 if (*layer
== entity
) {
905 // already on top of the layer
906 return stackingList
.end();
909 BWindowGroup
*group
= 0;
911 group
= win
->findWindowGroup();
913 // raise all windows in the group before raising 'win'
914 ::raiseGroup(stackingList
, group
);
915 } else if (win
->isTransient()) {
916 // raise non-transient parent before raising transient
917 BlackboxWindow
*tmp
= win
->findNonTransientParent();
919 (void) ::raiseWindow(stackingList
, tmp
);
924 StackingList::iterator top
= stackingList
.raise(entity
),
925 end
= stackingList
.end();
928 // ... with all transients above it
929 BlackboxWindowList transients
= win
->buildFullTransientList();
930 if (!transients
.empty())
931 raiseTransients(top
, stackingList
, transients
);
933 // ... and group transients on top
934 if (group
&& !win
->isGroupTransient()) {
935 const BlackboxWindow
* const w
= win
->isTransient()
936 ? win
->findNonTransientParent()
938 if (!w
|| !w
->isGroupTransient()) {
939 BlackboxWindowList groupTransients
= group
->transients();
940 BlackboxWindowList::const_iterator wit
= groupTransients
.begin(),
941 wend
= groupTransients
.end();
942 for (; wit
!= wend
; ++wit
) {
943 BlackboxWindowList x
= (*wit
)->buildFullTransientList();
944 groupTransients
.splice(groupTransients
.end(), x
);
946 if (!groupTransients
.empty())
947 raiseTransients(top
, stackingList
, groupTransients
);
954 StackingList::iterator it
= std::find(top
, end
, (StackEntity
*) 0);
961 Raises the stack entity as above and then updates the stacking on
962 the X server. The EWMH stacking hint is also updated.
964 void BScreen::raiseWindow(StackEntity
*entity
) {
965 StackingList::iterator top
= ::raiseWindow(_stackingList
, entity
),
966 end
= _stackingList
.end();
968 // no need to raise entity
970 } else if (!(*top
)) {
971 // need to restack all windows
976 // find the first entity above us (if any)
977 StackEntity
*above
= 0;
978 StackingList::iterator layer
= _stackingList
.layer(entity
->layer()),
979 begin
= _stackingList
.begin(),
982 for (--it
; it
!= begin
; --it
) {
993 // found another entity above the one we are raising
994 stack
.push_back(above
->windowID());
996 // keep everying under empty_window
997 stack
.push_back(empty_window
);
1000 // go to the layer boundary above 'top'
1001 for (it
= top
; *it
&& it
!= begin
; --it
)
1004 // put all windows from the layer boundary to 'top' into the stack
1006 ++it
; // move past layer boundary
1007 for (; *it
&& it
!= top
; ++it
) {
1009 stack
.push_back((*it
)->windowID());
1011 stack
.push_back((*top
)->windowID());
1013 XRestackWindows(_blackbox
->XDisplay(), &stack
[0], stack
.size());
1014 updateClientListStackingHint();
1019 void lowerGroup(StackingList
&stackingList
, BWindowGroup
*group
) {
1020 BlackboxWindowList windows
= group
->windows();
1021 int layer
= StackingList::LayerNormal
;
1022 for (; layer
< StackingList::LayerDesktop
; ++layer
) {
1023 const StackingList::iterator begin
= stackingList
.begin(),
1024 end
= stackingList
.end();
1025 StackingList::iterator it
= stackingList
.layer(StackingList::Layer(layer
)),
1026 bottom
= std::find(it
, end
, (StackEntity
*) 0);
1027 // 'it' points to the top of the layer
1028 assert(bottom
!= end
);
1035 // walk down the layer, lowering all windows in the group
1036 for (; it
!= bottom
; ++it
) {
1038 BlackboxWindow
*tmp
= dynamic_cast<BlackboxWindow
*>(*it
);
1040 // entity is not a window
1044 const BlackboxWindowList::iterator wend
= windows
.end();
1045 BlackboxWindowList::iterator wit
;
1046 if ((wit
= std::find(windows
.begin(), wend
, tmp
)) == wend
)
1049 // found a window in this layer, lower it
1051 (void) stackingList
.lower(tmp
);
1052 // don't bother looking at this window again
1059 static void lowerTransients(StackingList::iterator it
,
1060 StackingList
&stackingList
,
1061 BlackboxWindowList
&transients
) {
1062 // 'it' points to the top of the layer
1063 const StackingList::iterator end
= stackingList
.end(),
1064 bottom
= std::find(it
, end
, (StackEntity
*) 0);
1065 assert(bottom
!= end
);
1066 for (; it
!= bottom
; ++it
) {
1068 BlackboxWindow
*tmp
= dynamic_cast<BlackboxWindow
*>(*it
);
1072 const BlackboxWindowList::iterator wend
= transients
.end();
1073 BlackboxWindowList::iterator wit
;
1074 if ((wit
= std::find(transients
.begin(), wend
, tmp
)) == wend
)
1077 // found a transient in this layer, lower it
1079 StackingList::iterator l
= stackingList
.lower(tmp
);
1080 // don't bother looking at this window again
1081 transients
.erase(wit
);
1087 Lowers the specified stacking entity. If the entity is a window,
1088 all transients are also lowered (preserving their stacking order).
1089 If the window is part of a group, the entire group is lowered (also
1090 preserving the stacking order) befor lowering the specified window.
1092 The return value indicates which windows need to be restacked:
1094 - if lowerWindow() return stackingList.end(), then nothing needs to
1097 - if lowerWindow() returns an iterator for a layer boundary
1098 (i.e. *lowerWindow(...) == 0) then all windows in all layers need to
1101 - otherwise, the return value points to the top-most window that
1102 needs to be restacked (i.e. the return value down to the bottom of
1103 the layer need to be restacked)
1106 StackingList::iterator
lowerWindow(StackingList
&stackingList
,
1107 StackEntity
*entity
,
1108 bool ignore_group
= false) {
1109 StackingList::iterator it
, end
= stackingList
.end();
1110 BlackboxWindow
*win
= dynamic_cast<BlackboxWindow
*>(entity
);
1113 BWindowGroup
*group
= win
->findWindowGroup();
1114 if (!ignore_group
&& group
) {
1115 // lower all windows in the group before lowering 'win'
1116 ::lowerGroup(stackingList
, group
);
1117 it
= std::find(stackingList
.begin(), end
, (StackEntity
*) 0);
1121 const StackingList::iterator layer
= stackingList
.layer(win
->layer());
1122 if (win
->isTransient()) {
1123 BlackboxWindow
*const tmp
= win
->findNonTransientParent();
1124 if (tmp
&& !tmp
->isGroupTransient()) {
1125 // lower non-transient parent
1126 (void) ::lowerWindow(stackingList
, tmp
, true);
1133 // lower transients oinf 'win' and 'win'
1134 BlackboxWindowList transients
= win
->buildFullTransientList();
1135 if (!transients
.empty()) {
1136 ::lowerTransients(layer
, stackingList
, transients
);
1137 (void) stackingList
.lower(win
);
1142 it
= stackingList
.lower(entity
);
1145 (void) stackingList
.lower(entity
);
1150 it
= stackingList
.lower(entity
);
1157 void BScreen::lowerWindow(StackEntity
*entity
) {
1158 StackingList::iterator top
= ::lowerWindow(_stackingList
, entity
),
1159 end
= _stackingList
.end();
1161 // no need to lower entity
1163 } else if (!(*top
)) {
1164 // need to restack all windows
1169 // find the entity above us (if any)
1170 StackEntity
*above
= 0;
1171 StackingList::iterator begin
= _stackingList
.begin(),
1174 for (--it
; it
!= begin
; --it
) {
1182 // build the window stack
1185 // found another entity above the one we are lowering
1186 stack
.push_back(above
->windowID());
1188 // keep everying under empty_window
1189 stack
.push_back(empty_window
);
1192 // find the layer boundary
1193 StackingList::iterator bottom
= std::find(top
, end
, (StackEntity
*) 0);
1194 assert(bottom
!= end
);
1196 // put all windows from 'top' to the layer boundary into the stack
1197 for (it
= top
; it
!= bottom
; ++it
) {
1198 assert(*it
&& it
!= end
);
1199 stack
.push_back((*it
)->windowID());
1201 stack
.push_back((*top
)->windowID());
1203 XRestackWindows(_blackbox
->XDisplay(), &stack
[0], stack
.size());
1204 updateClientListStackingHint();
1208 void BScreen::restackWindows(void) {
1210 stack
.push_back(empty_window
);
1212 StackingList::const_iterator it
, end
= _stackingList
.end();
1213 for (it
= _stackingList
.begin(); it
!= end
; ++it
) {
1215 stack
.push_back((*it
)->windowID());
1218 XRestackWindows(_blackbox
->XDisplay(), &stack
[0], stack
.size());
1219 updateClientListStackingHint();
1223 void BScreen::propagateWindowName(const BlackboxWindow
* const win
) {
1224 if (win
->isIconic()) {
1225 _iconmenu
->changeItem(win
->windowNumber(),
1226 bt::ellideText(win
->iconTitle(), 60,
1227 bt::toUnicode("...")));
1228 } else if (win
->workspace() != bt::BSENTINEL
) {
1229 Workspace
*workspace
= findWorkspace(win
->workspace());
1230 assert(workspace
!= 0);
1231 workspace
->menu()->changeItem(win
->windowNumber(),
1232 bt::ellideText(win
->title(), 60,
1233 bt::toUnicode("...")));
1236 if (_toolbar
&& _blackbox
->focusedWindow() == win
)
1237 _toolbar
->redrawWindowLabel();
1241 void BScreen::nextFocus(void) {
1242 BlackboxWindow
*focused
= _blackbox
->focusedWindow(),
1244 BlackboxWindowList::iterator it
, end
= windowList
.end();
1246 if (focused
&& focused
->workspace() == current_workspace
&&
1247 focused
->screen()->screen_info
.screenNumber() ==
1248 screen_info
.screenNumber()) {
1249 it
= std::find(windowList
.begin(), end
, focused
);
1251 for (; it
!= end
; ++it
) {
1253 if (next
&& next
!= focused
&& next
->workspace() == current_workspace
&&
1254 next
->setInputFocus()) {
1255 // we found our new focus target
1256 next
->setInputFocus();
1264 for (it
= windowList
.begin(); it
!= end
; ++it
) {
1266 if (next
&& next
->workspace() == current_workspace
&&
1267 next
->setInputFocus()) {
1268 // we found our new focus target
1277 void BScreen::prevFocus(void) {
1278 BlackboxWindow
*focused
= _blackbox
->focusedWindow(),
1280 BlackboxWindowList::reverse_iterator it
, end
= windowList
.rend();
1282 if (focused
&& focused
->workspace() == current_workspace
&&
1283 focused
->screen()->screen_info
.screenNumber() ==
1284 screen_info
.screenNumber()) {
1285 it
= std::find(windowList
.rbegin(), end
, focused
);
1287 for (; it
!= end
; ++it
) {
1289 if (next
&& next
!= focused
&& next
->workspace() == current_workspace
&&
1290 next
->setInputFocus()) {
1291 // we found our new focus target
1292 next
->setInputFocus();
1300 for (it
= windowList
.rbegin(); it
!= end
; ++it
) {
1302 if (next
&& next
->workspace() == current_workspace
&&
1303 next
->setInputFocus()) {
1304 // we found our new focus target
1313 void BScreen::raiseFocus(void) {
1314 BlackboxWindow
*focused
= _blackbox
->focusedWindow();
1315 if (! focused
|| focused
->screen() != this)
1318 raiseWindow(focused
);
1322 void BScreen::InitMenu(void) {
1326 _rootmenu
= new Rootmenu(*_blackbox
, screen_info
.screenNumber(), this);
1327 _rootmenu
->showTitle();
1329 bool defaultMenu
= True
;
1331 if (_blackbox
->resource().menuFilename()) {
1332 const char * const filename
= _blackbox
->resource().menuFilename();
1333 bool pipe_menu
= filename
[0] == '|';
1334 FILE *menu_file
= pipe_menu
1335 ? popen(filename
+ 1, "r")
1336 : fopen(filename
, "r");
1338 perror(_blackbox
->resource().menuFilename());
1340 if (feof(menu_file
)) {
1341 fprintf(stderr
, "%s: menu file '%s' is empty\n",
1342 _blackbox
->applicationName().c_str(),
1343 _blackbox
->resource().menuFilename());
1345 char line
[1024], label
[1024];
1346 memset(line
, 0, 1024);
1347 memset(label
, 0, 1024);
1349 while (fgets(line
, 1024, menu_file
) && ! feof(menu_file
)) {
1353 int i
, key
= 0, index
= -1, len
= strlen(line
);
1355 for (i
= 0; i
< len
; i
++) {
1356 if (line
[i
] == '[') index
= 0;
1357 else if (line
[i
] == ']') break;
1358 else if (line
[i
] != ' ')
1360 key
+= tolower(line
[i
]);
1363 if (key
== 517) { // [begin]
1365 for (i
= index
; i
< len
; i
++) {
1368 else if (line
[i
] == ')')
1370 else if (index
++ >= 0) {
1371 if (line
[i
] == '\\' && i
< len
- 1) i
++;
1372 label
[index
- 1] = line
[i
];
1376 if (index
== -1) index
= 0;
1377 label
[index
] = '\0';
1379 _rootmenu
->setTitle(bt::toUnicode(label
));
1380 defaultMenu
= parseMenuFile(menu_file
, _rootmenu
);
1393 _rootmenu
->setTitle(bt::toUnicode("_Blackbox"));
1395 _rootmenu
->insertFunction(bt::toUnicode("xterm"),
1396 BScreen::Execute
, "xterm");
1397 _rootmenu
->insertFunction(bt::toUnicode("Restart"),
1399 _rootmenu
->insertFunction(bt::toUnicode("Exit"),
1402 _blackbox
->saveMenuFilename(_blackbox
->resource().menuFilename());
1408 size_t string_within(char begin
, char end
,
1409 const char *input
, size_t start_at
, size_t length
,
1413 size_t i
= start_at
;
1414 for (; i
< length
; ++i
) {
1415 if (input
[i
] == begin
) {
1417 } else if (input
[i
] == end
) {
1420 if (input
[i
] == '\\' && i
< length
- 1) i
++;
1421 output
[index
++] = input
[i
];
1426 output
[index
] = '\0';
1434 bool BScreen::parseMenuFile(FILE *file
, Rootmenu
*menu
) {
1435 char line
[1024], keyword
[1024], label
[1024], command
[1024];
1438 while (! (done
|| feof(file
))) {
1439 memset(line
, 0, 1024);
1440 memset(label
, 0, 1024);
1441 memset(command
, 0, 1024);
1443 if (! fgets(line
, 1024, file
))
1446 if (line
[0] == '#') // comment, skip it
1449 size_t line_length
= strlen(line
);
1450 unsigned int key
= 0;
1452 // get the keyword enclosed in []'s
1453 size_t pos
= string_within('[', ']', line
, 0, line_length
, keyword
);
1455 if (keyword
[0] == '\0') { // no keyword, no menu entry
1458 size_t len
= strlen(keyword
);
1459 for (size_t i
= 0; i
< len
; ++i
) {
1460 if (keyword
[i
] != ' ')
1461 key
+= tolower(keyword
[i
]);
1465 // get the label enclosed in ()'s
1466 pos
= string_within('(', ')', line
, pos
, line_length
, label
);
1468 // get the command enclosed in {}'s
1469 pos
= string_within('{', '}', line
, pos
, line_length
, command
);
1478 menu
->insertSeparator();
1484 menu
->setItemEnabled(menu
->insertItem(bt::toUnicode(label
)), false);
1488 if (! (*label
&& *command
)) {
1490 "%s: [exec] error, no menu label and/or command defined\n",
1491 _blackbox
->applicationName().c_str());
1495 menu
->insertFunction(bt::toUnicode(label
), BScreen::Execute
, command
);
1500 fprintf(stderr
, "%s: [exit] error, no menu label defined\n",
1501 _blackbox
->applicationName().c_str());
1505 menu
->insertFunction(bt::toUnicode(label
), BScreen::Exit
);
1508 case 561: { // style
1509 if (! (*label
&& *command
)) {
1511 "%s: [style] error, no menu label and/or filename defined\n",
1512 _blackbox
->applicationName().c_str());
1516 std::string style
= bt::expandTilde(command
);
1517 menu
->insertFunction(bt::toUnicode(label
),
1518 BScreen::SetStyle
, style
.c_str());
1524 fprintf(stderr
, "%s: [config] error, no label defined",
1525 _blackbox
->applicationName().c_str());
1529 menu
->insertItem(bt::toUnicode(label
), configmenu
);
1532 case 740: { // include
1534 fprintf(stderr
, "%s: [include] error, no filename defined\n",
1535 _blackbox
->applicationName().c_str());
1539 bool pipe_menu
= label
[0] == '|';
1540 std::string newfile
= bt::expandTilde(pipe_menu
? label
+ 1 : label
);
1541 FILE *submenufile
= pipe_menu
1542 ? popen(newfile
.c_str(), "r")
1543 : fopen(newfile
.c_str(), "r");
1545 if (! submenufile
) {
1546 perror(newfile
.c_str());
1552 && (fstat(fileno(submenufile
), &buf
) || ! S_ISREG(buf
.st_mode
))) {
1553 fprintf(stderr
, "%s: [include] error: '%s' is not a regular file\n",
1554 _blackbox
->applicationName().c_str(), newfile
.c_str());
1555 fclose(submenufile
);
1559 if (! feof(submenufile
)) {
1560 if (! parseMenuFile(submenufile
, menu
))
1561 _blackbox
->saveMenuFilename(newfile
);
1564 pclose(submenufile
);
1566 fclose(submenufile
);
1572 case 767: { // submenu
1574 fprintf(stderr
, "%s: [submenu] error, no menu label defined\n",
1575 _blackbox
->applicationName().c_str());
1580 new Rootmenu(*_blackbox
, screen_info
.screenNumber(), this);
1581 submenu
->showTitle();
1584 submenu
->setTitle(bt::toUnicode(command
));
1586 submenu
->setTitle(bt::toUnicode(label
));
1588 parseMenuFile(file
, submenu
);
1589 menu
->insertItem(bt::toUnicode(label
), submenu
);
1594 case 773: { // restart
1596 fprintf(stderr
, "%s: [restart] error, no menu label defined\n",
1597 _blackbox
->applicationName().c_str());
1602 menu
->insertFunction(bt::toUnicode(label
),
1603 BScreen::RestartOther
, command
);
1605 menu
->insertFunction(bt::toUnicode(label
), BScreen::Restart
);
1610 case 845: { // reconfig
1612 fprintf(stderr
, "%s: [reconfig] error, no menu label defined\n",
1613 _blackbox
->applicationName().c_str());
1617 menu
->insertFunction(bt::toUnicode(label
), BScreen::Reconfigure
);
1621 case 995: // stylesdir
1622 case 1113: { // stylesmenu
1623 bool newmenu
= ((key
== 1113) ? True
: False
);
1625 if (! *label
|| (! *command
&& newmenu
)) {
1627 "%s: [stylesdir/stylesmenu] error, no directory defined\n",
1628 _blackbox
->applicationName().c_str());
1632 char *directory
= ((newmenu
) ? command
: label
);
1634 std::string stylesdir
= bt::expandTilde(directory
);
1636 struct stat statbuf
;
1638 if (stat(stylesdir
.c_str(), &statbuf
) == -1) {
1640 "%s: [stylesdir/stylesmenu] error, '%s' does not exist\n",
1641 _blackbox
->applicationName().c_str(), stylesdir
.c_str());
1644 if (! S_ISDIR(statbuf
.st_mode
)) {
1646 "%s: [stylesdir/stylesmenu] error, '%s' is not a directory\n",
1647 _blackbox
->applicationName().c_str(), stylesdir
.c_str());
1651 Rootmenu
*stylesmenu
;
1655 new Rootmenu(*_blackbox
, screen_info
.screenNumber(),this);
1656 stylesmenu
->showTitle();
1661 DIR *d
= opendir(stylesdir
.c_str());
1663 std::vector
<std::string
> ls
;
1665 while((p
= readdir(d
)))
1666 ls
.push_back(p
->d_name
);
1670 std::sort(ls
.begin(), ls
.end());
1672 std::vector
<std::string
>::iterator it
= ls
.begin(),
1674 for (; it
!= end
; ++it
) {
1675 std::string
& fname
= *it
;
1677 if (fname
[0] == '.' || fname
[fname
.size()-1] == '~')
1680 std::string style
= stylesdir
;
1684 if (! stat(style
.c_str(), &statbuf
) && S_ISREG(statbuf
.st_mode
)) {
1685 // convert 'This_Long_Name' to 'This Long Name'
1686 std::replace(fname
.begin(), fname
.end(), '_', ' ');
1688 stylesmenu
->insertFunction(bt::toUnicode(fname
),
1689 BScreen::SetStyle
, style
);
1694 stylesmenu
->setTitle(bt::toUnicode(label
));
1695 menu
->insertItem(bt::toUnicode(label
), stylesmenu
);
1698 _blackbox
->saveMenuFilename(stylesdir
);
1702 case 1090: { // workspaces
1704 fprintf(stderr
, "%s: [workspaces] error, no menu label defined\n",
1705 _blackbox
->applicationName().c_str());
1709 menu
->insertItem(bt::toUnicode(label
), _workspacemenu
);
1715 return (menu
->count() == 0);
1719 void BScreen::shutdown(void) {
1720 XSelectInput(_blackbox
->XDisplay(), screen_info
.rootWindow(),
1722 XSync(_blackbox
->XDisplay(), False
);
1724 // unmanage all windows, but keep them in the current stacking order
1726 StackingList::const_iterator it
, end
= _stackingList
.end();
1727 for (it
= _stackingList
.begin(); it
!= end
; ++it
) {
1730 const BlackboxWindow
* const win
= dynamic_cast<BlackboxWindow
*>(*it
);
1733 stack
.push_back(win
->clientWindow());
1736 while(! windowList
.empty())
1737 unmanageWindow(windowList
.back());
1740 XRestackWindows(_blackbox
->XDisplay(), &stack
[0], stack
.size());
1747 void BScreen::showGeometry(GeometryType type
, const bt::Rect
&rect
) {
1748 if (! geom_visible
) {
1749 XMoveResizeWindow(_blackbox
->XDisplay(), geom_window
,
1750 (screen_info
.width() - geom_w
) / 2,
1751 (screen_info
.height() - geom_h
) / 2, geom_w
, geom_h
);
1752 XMapWindow(_blackbox
->XDisplay(), geom_window
);
1753 XRaiseWindow(_blackbox
->XDisplay(), geom_window
);
1755 geom_visible
= True
;
1761 sprintf(label
, "X:%4d Y:%4d", rect
.x(), rect
.y());
1764 sprintf(label
, "W:%4u H:%4u", rect
.width(), rect
.height());
1770 const WindowStyle
&style
= _resource
.windowStyle();
1771 const bt::Texture
&texture
=
1772 (style
.focus
.label
.texture() == bt::Texture::Parent_Relative
)
1774 : style
.focus
.label
;
1775 const bt::Rect
u(0, 0, geom_w
, geom_h
);
1776 const bt::Rect
t(style
.label_margin
,
1778 geom_w
- (style
.label_margin
* 2),
1779 geom_h
- (style
.label_margin
* 2));
1780 const bt::Pen
pen(screen_info
.screenNumber(), style
.focus
.text
);
1781 bt::drawTexture(screen_info
.screenNumber(), texture
, geom_window
,
1783 bt::drawText(style
.font
, pen
, geom_window
, t
, style
.alignment
,
1784 bt::toUnicode(label
));
1788 void BScreen::hideGeometry(void) {
1790 XUnmapWindow(_blackbox
->XDisplay(), geom_window
);
1791 geom_visible
= False
;
1796 void BScreen::addStrut(bt::EWMH::Strut
*strut
) {
1797 strutList
.push_back(strut
);
1798 updateAvailableArea();
1802 void BScreen::removeStrut(bt::EWMH::Strut
*strut
) {
1803 strutList
.remove(strut
);
1804 updateAvailableArea();
1808 void BScreen::updateStrut(void) {
1809 updateAvailableArea();
1813 const bt::Rect
& BScreen::availableArea(void) {
1814 if (_blackbox
->resource().fullMaximization())
1815 return screen_info
.rect(); // return the full screen
1820 void BScreen::updateAvailableArea(void) {
1823 /* these values represent offsets from the screen edge
1824 * we look for the biggest offset on each edge and then apply them
1826 * do not be confused by the similarity to the names of Rect's members
1828 bt::EWMH::Strut current
;
1830 StrutList::const_iterator sit
= strutList
.begin(), send
= strutList
.end();
1832 for(; sit
!= send
; ++sit
) {
1833 const bt::EWMH::Strut
* const strut
= *sit
;
1834 if (strut
->left
> current
.left
)
1835 current
.left
= strut
->left
;
1836 if (strut
->top
> current
.top
)
1837 current
.top
= strut
->top
;
1838 if (strut
->right
> current
.right
)
1839 current
.right
= strut
->right
;
1840 if (strut
->bottom
> current
.bottom
)
1841 current
.bottom
= strut
->bottom
;
1844 new_area
.setPos(current
.left
, current
.top
);
1845 new_area
.setSize(screen_info
.width() - (current
.left
+ current
.right
),
1846 screen_info
.height() - (current
.top
+ current
.bottom
));
1848 if (new_area
!= usableArea
) {
1849 usableArea
= new_area
;
1850 BlackboxWindowList::iterator wit
= windowList
.begin(),
1851 wend
= windowList
.end();
1852 for (; wit
!= wend
; ++wit
)
1853 if ((*wit
)->isMaximized()) (*wit
)->remaximize();
1855 updateWorkareaHint();
1860 Workspace
* BScreen::findWorkspace(unsigned int index
) const {
1861 if (index
== bt::BSENTINEL
)
1863 assert(index
< workspacesList
.size());
1864 return workspacesList
[index
];
1868 void BScreen::clientMessageEvent(const XClientMessageEvent
* const event
) {
1869 if (event
->format
!= 32) return;
1871 if (event
->message_type
== _blackbox
->ewmh().numberOfDesktops()) {
1872 unsigned int number
= event
->data
.l
[0];
1873 if (number
> workspaceCount()) {
1874 for (; number
!= workspaceCount(); --number
)
1876 } else if (number
< workspaceCount()) {
1877 for (; number
!= workspaceCount(); ++number
)
1878 removeLastWorkspace();
1880 } else if (event
->message_type
== _blackbox
->ewmh().desktopNames()) {
1882 } else if (event
->message_type
== _blackbox
->ewmh().currentDesktop()) {
1883 const unsigned int workspace
= event
->data
.l
[0];
1884 if (workspace
< workspaceCount() && workspace
!= current_workspace
)
1885 setCurrentWorkspace(workspace
);
1890 void BScreen::buttonPressEvent(const XButtonEvent
* const event
) {
1892 set this screen active. keygrabs and input focus will stay on
1893 this screen until the user focuses a window on another screen or
1894 makes another screen active.
1896 _blackbox
->setActiveScreen(this);
1898 if (event
->button
== 2) {
1899 _workspacemenu
->popup(event
->x_root
, event
->y_root
);
1900 } else if (event
->button
== 3) {
1901 _blackbox
->checkMenu();
1902 _rootmenu
->popup(event
->x_root
, event
->y_root
);
1903 } else if (event
->button
== 4 &&
1904 _blackbox
->resource().changeWorkspaceWithMouseWheel()) {
1906 } else if (event
->button
== 5 &&
1907 _blackbox
->resource().changeWorkspaceWithMouseWheel()) {
1913 void BScreen::propertyNotifyEvent(const XPropertyEvent
* const event
) {
1914 if (event
->atom
== _blackbox
->ewmh().activeWindow() && _toolbar
)
1915 _toolbar
->redrawWindowLabel();
1919 void BScreen::unmapNotifyEvent(const XUnmapEvent
* const event
) {
1920 // handle synthetic unmap events according to ICCCM section 4.1.4
1921 BlackboxWindow
*win
= _blackbox
->findWindow(event
->window
);
1922 if (win
&& event
->event
== screen_info
.rootWindow()
1923 && !event
->from_configure
)
1924 win
->unmapNotifyEvent(event
);
1928 void BScreen::toggleFocusModel(FocusModel model
) {
1929 std::for_each(windowList
.begin(), windowList
.end(),
1930 std::mem_fun(&BlackboxWindow::ungrabButtons
));
1932 _blackbox
->resource().setFocusModel(model
);
1934 std::for_each(windowList
.begin(), windowList
.end(),
1935 std::mem_fun(&BlackboxWindow::grabButtons
));
1939 void BScreen::updateWorkareaHint(void) const {
1940 unsigned long *workarea
, *tmp
;
1942 tmp
= workarea
= new unsigned long[workspaceCount() * 4];
1944 for (unsigned int i
= 0; i
< workspaceCount(); ++i
) {
1945 tmp
[0] = usableArea
.x();
1946 tmp
[1] = usableArea
.y();
1947 tmp
[2] = usableArea
.width();
1948 tmp
[3] = usableArea
.height();
1952 _blackbox
->ewmh().setWorkarea(screen_info
.rootWindow(),
1953 workarea
, workspaceCount());
1959 void BScreen::updateDesktopNamesHint(void) const {
1960 std::vector
<bt::ustring
> names(workspacesList
.size());
1961 WorkspaceList::const_iterator it
= workspacesList
.begin();
1962 const WorkspaceList::const_iterator end
= workspacesList
.end();
1963 for (; it
!= end
; ++it
)
1964 names
.push_back((*it
)->name());
1965 _blackbox
->ewmh().setDesktopNames(screen_info
.rootWindow(), names
);
1969 void BScreen::updateClientListHint(void) const {
1970 if (windowList
.empty()) {
1971 _blackbox
->ewmh().removeProperty(screen_info
.rootWindow(),
1972 _blackbox
->ewmh().clientList());
1976 bt::EWMH::WindowList
clientList(windowList
.size());
1978 std::transform(windowList
.begin(), windowList
.end(), clientList
.begin(),
1979 std::mem_fun(&BlackboxWindow::clientWindow
));
1981 _blackbox
->ewmh().setClientList(screen_info
.rootWindow(), clientList
);
1985 void BScreen::updateClientListStackingHint(void) const {
1986 bt::EWMH::WindowList stack
;
1988 // we store windows in top-to-bottom order, but the EWMH wants
1990 StackingList::const_reverse_iterator it
= _stackingList
.rbegin(),
1991 end
= _stackingList
.rend();
1992 for (; it
!= end
; ++it
) {
1993 const BlackboxWindow
* const win
= dynamic_cast<BlackboxWindow
*>(*it
);
1994 if (win
) stack
.push_back(win
->clientWindow());
1997 if (stack
.empty()) {
1998 _blackbox
->ewmh().removeProperty(screen_info
.rootWindow(),
1999 _blackbox
->ewmh().clientListStacking());
2003 _blackbox
->ewmh().setClientListStacking(screen_info
.rootWindow(), stack
);
2007 void BScreen::readDesktopNames(void) {
2008 std::vector
<bt::ustring
> names
;
2009 if(! _blackbox
->ewmh().readDesktopNames(screen_info
.rootWindow(), names
))
2012 std::vector
<bt::ustring
>::const_iterator it
= names
.begin();
2013 const std::vector
<bt::ustring
>::const_iterator end
= names
.end();
2014 WorkspaceList::iterator wit
= workspacesList
.begin();
2015 const WorkspaceList::iterator wend
= workspacesList
.end();
2017 for (; wit
!= wend
&& it
!= end
; ++wit
, ++it
) {
2018 if ((*wit
)->name() != *it
)
2019 (*wit
)->setName(*it
);
2022 if (names
.size() < workspacesList
.size())
2023 updateDesktopNamesHint();
2027 BlackboxWindow
*BScreen::window(unsigned int workspace
, unsigned int id
) {
2028 StackingList::const_iterator it
= _stackingList
.begin(),
2029 end
= _stackingList
.end();
2030 for (; it
!= end
; ++it
) {
2031 BlackboxWindow
* const win
= dynamic_cast<BlackboxWindow
*>(*it
);
2032 if (win
&& win
->workspace() == workspace
&& win
->windowNumber() == id
)
2035 assert(false); // should not happen
2040 void BScreen::placeWindow(BlackboxWindow
*win
) {
2041 bt::Rect r
= win
->frameRect();
2043 // if the client/user has explicitly placed the window, honor it
2044 if (win
->wmNormalHints().flags
& (PPosition
| USPosition
)) {
2045 // make sure that the window is not placed outside of the workarea
2046 win
->configure(r
.inside(usableArea
));
2050 switch (win
->windowType()) {
2051 case WindowTypeDesktop
:
2052 win
->configure(screen_info
.rect());
2054 case WindowTypeDialog
: {
2055 BlackboxWindow
*w
= win
->findTransientFor();
2056 bt::Rect p
= w
? w
->frameRect() : usableArea
;
2057 const int x
= static_cast<int>(p
.x() + (p
.width() - r
.width()) / 2);
2058 const int y
= static_cast<int>(p
.y() + (p
.height() - r
.height()) / 2);
2063 bool placed
= false;
2064 BlackboxResource
&res
= _blackbox
->resource();
2065 switch (res
.windowPlacementPolicy()) {
2066 case RowSmartPlacement
:
2067 case ColSmartPlacement
:
2068 placed
= smartPlacement(win
->workspace(), r
, usableArea
);
2070 case CenterPlacement
:
2071 placed
= centerPlacement(r
, usableArea
);
2074 break; // handled below
2077 cascadePlacement(r
, usableArea
);
2078 cascade_x
+= _resource
.windowStyle().title_height
;
2079 cascade_y
+= _resource
.windowStyle().title_height
;
2085 win
->configure(r
.inside(usableArea
));
2089 bool BScreen::cascadePlacement(bt::Rect
&win
,
2090 const bt::Rect
&avail
) {
2091 if (cascade_x
> (avail
.width() / 2) ||
2092 cascade_y
> (avail
.height() / 2))
2093 cascade_x
= cascade_y
= 32;
2095 if (cascade_x
== 32) {
2096 cascade_x
+= avail
.x();
2097 cascade_y
+= avail
.y();
2100 win
.setPos(cascade_x
, cascade_y
);
2102 if (win
.right() > avail
.right() ||
2103 win
.bottom() > avail
.bottom()) {
2104 cascade_x
= cascade_y
= 32;
2106 cascade_x
+= avail
.x();
2107 cascade_y
+= avail
.y();
2109 win
.setPos(cascade_x
, cascade_y
);
2116 bool BScreen::centerPlacement(bt::Rect
&rect
, const bt::Rect
&avail
)
2119 static_cast<int>(avail
.x() + (avail
.width() - rect
.width()) / 2);
2121 static_cast<int>(avail
.y() + (avail
.height() - rect
.height()) / 2);
2127 bool BScreen::smartPlacement(unsigned int workspace
, bt::Rect
& rect
,
2128 const bt::Rect
& avail
) {
2130 const BlackboxResource
&res
= _blackbox
->resource();
2131 const bool row_placement
=
2132 (res
.windowPlacementPolicy() == RowSmartPlacement
);
2133 const bool leftright
=
2134 (res
.rowPlacementDirection() == LeftRight
);
2135 const bool topbottom
=
2136 (res
.colPlacementDirection() == TopBottom
);
2137 const bool ignore_shaded
= res
.placementIgnoresShaded();
2139 const int left_border
= leftright
? 0 : -1;
2140 const int top_border
= topbottom
? 0 : -1;
2141 const int right_border
= leftright
? 1 : 0;
2142 const int bottom_border
= topbottom
? 1 : 0;
2144 StackingList::const_iterator w_it
, w_end
;
2147 build a sorted vector of x and y grid boundaries
2149 note: we use one vector to reduce the number of allocations
2150 std::vector must do.. we allocate as much memory as we would need
2151 in the worst case scenario and work with that
2153 std::vector
<int> coords(_stackingList
.size() * 4 + 4);
2154 std::vector
<int>::iterator
2155 x_begin
= coords
.begin(),
2157 y_begin
= coords
.begin() + _stackingList
.size() * 2 + 2,
2161 std::vector
<int>::iterator x_it
= x_begin
, y_it
= y_begin
;
2163 *x_it
++ = avail
.left();
2164 *x_it
++ = avail
.right();
2167 *y_it
++ = avail
.top();
2168 *y_it
++ = avail
.bottom();
2172 for (w_it
= _stackingList
.begin(), w_end
= _stackingList
.end();
2173 w_it
!= w_end
; ++w_it
) {
2174 const BlackboxWindow
* const win
=
2175 dynamic_cast<const BlackboxWindow
*>(*w_it
);
2178 if (win
->windowType() == WindowTypeDesktop
)
2180 if (win
->isIconic())
2182 if (win
->workspace() != bt::BSENTINEL
&& win
->workspace() != workspace
)
2184 if (ignore_shaded
&& win
->isShaded())
2187 *x_it
++ = std::max(win
->frameRect().left() + left_border
,
2189 *x_it
++ = std::min(win
->frameRect().right() + right_border
,
2193 *y_it
++ = std::max(win
->frameRect().top() + top_border
,
2195 *y_it
++ = std::min(win
->frameRect().bottom() + bottom_border
,
2200 assert(x_end
<= y_begin
);
2203 std::sort(x_begin
, x_end
);
2204 x_end
= std::unique(x_begin
, x_end
);
2206 std::sort(y_begin
, y_end
);
2207 y_end
= std::unique(y_begin
, y_end
);
2209 // build a distribution grid
2210 unsigned int gw
= x_end
- x_begin
- 1,
2211 gh
= y_end
- y_begin
- 1;
2212 std::vector
<bool> used_grid(gw
* gh
);
2213 std::fill_n(used_grid
.begin(), used_grid
.size(), false);
2215 for (w_it
= _stackingList
.begin(), w_end
= _stackingList
.end();
2216 w_it
!= w_end
; ++w_it
) {
2217 const BlackboxWindow
* const win
=
2218 dynamic_cast<const BlackboxWindow
*>(*w_it
);
2221 if (win
->windowType() == WindowTypeDesktop
)
2223 if (win
->isIconic())
2225 if (win
->workspace() != bt::BSENTINEL
&& win
->workspace() != workspace
)
2227 if (ignore_shaded
&& win
->isShaded())
2231 std::max(win
->frameRect().left() + left_border
,
2234 std::max(win
->frameRect().top() + top_border
,
2237 std::min(win
->frameRect().right() + right_border
,
2239 const int w_bottom
=
2240 std::min(win
->frameRect().bottom() + bottom_border
,
2243 // which areas of the grid are used by this window?
2244 std::vector
<int>::iterator l_it
= std::find(x_begin
, x_end
, w_left
),
2245 r_it
= std::find(x_begin
, x_end
, w_right
),
2246 t_it
= std::find(y_begin
, y_end
, w_top
),
2247 b_it
= std::find(y_begin
, y_end
, w_bottom
);
2248 assert(l_it
!= x_end
&&
2253 const unsigned int left
= l_it
- x_begin
,
2254 right
= r_it
- x_begin
,
2255 top
= t_it
- y_begin
,
2256 bottom
= b_it
- y_begin
;
2258 for (unsigned int gy
= top
; gy
< bottom
; ++gy
)
2259 for (unsigned int gx
= left
; gx
< right
; ++gx
)
2260 used_grid
[(gy
* gw
) + gx
] = true;
2264 Attempt to fit the window into any of the empty areas in the grid.
2265 The exact order is dependent upon the users configuration (as
2269 - outer -> vertical axis
2270 - inner -> horizontal axis
2273 - outer -> horizontal axis
2274 - inner -> vertical axis
2278 int &outer
= row_placement
? gy
: gx
;
2279 int &inner
= row_placement
? gx
: gy
;
2280 const int outer_delta
= row_placement
2281 ? (topbottom
? 1 : -1)
2282 : (leftright
? 1 : -1);
2283 const int inner_delta
= row_placement
2284 ? (leftright
? 1 : -1)
2285 : (topbottom
? 1 : -1);
2286 const int outer_begin
= row_placement
2287 ? (topbottom
? 0 : static_cast<int>(gh
) - 1)
2288 : (leftright
? 0 : static_cast<int>(gw
) - 1);
2289 const int outer_end
= row_placement
2290 ? (topbottom
? static_cast<int>(gh
) : -1)
2291 : (leftright
? static_cast<int>(gw
) : -1);
2292 const int inner_begin
= row_placement
2293 ? (leftright
? 0 : static_cast<int>(gw
) - 1)
2294 : (topbottom
? 0 : static_cast<int>(gh
) - 1);
2295 const int inner_end
= row_placement
2296 ? (leftright
? static_cast<int>(gw
) : -1)
2297 : (topbottom
? static_cast<int>(gh
) : -1);
2301 for (outer
= outer_begin
; ! fit
&& outer
!= outer_end
;
2302 outer
+= outer_delta
) {
2303 for (inner
= inner_begin
; ! fit
&& inner
!= inner_end
;
2304 inner
+= inner_delta
) {
2305 // see if the window fits in a single unused area
2306 if (used_grid
[(gy
* gw
) + gx
]) continue;
2308 where
.setCoords(*(x_begin
+ gx
), *(y_begin
+ gy
),
2309 *(x_begin
+ gx
+ 1), *(y_begin
+ gy
+ 1));
2311 if (where
.width() >= rect
.width() &&
2312 where
.height() >= rect
.height()) {
2318 see if we neighboring spaces are unused
2320 TODO: we should grid fit in the same direction as above,
2321 instead of always right->left and top->bottom
2323 int gx2
= gx
, gy2
= gy
;
2325 if (rect
.width() > where
.width()) {
2326 for (gx2
= gx
+1; gx2
< static_cast<int>(gw
); ++gx2
) {
2327 if (used_grid
[(gy
* gw
) + gx2
]) {
2332 where
.setCoords(*(x_begin
+ gx
), *(y_begin
+ gy
),
2333 *(x_begin
+ gx2
+ 1), *(y_begin
+ gy2
+ 1));
2335 if (where
.width() >= rect
.width()) break;
2338 if (gx2
>= static_cast<int>(gw
)) --gx2
;
2341 if (rect
.height() > where
.height()) {
2342 for (gy2
= gy
; gy2
< static_cast<int>(gh
); ++gy2
) {
2343 if (used_grid
[(gy2
* gw
) + gx
]) {
2348 where
.setCoords(*(x_begin
+ gx
), *(y_begin
+ gy
),
2349 *(x_begin
+ gx2
+ 1), *(y_begin
+ gy2
+ 1));
2351 if (where
.height() >= rect
.height()) break;
2354 if (gy2
>= static_cast<int>(gh
)) --gy2
;
2357 if (where
.width() >= rect
.width() &&
2358 where
.height() >= rect
.height()) {
2361 // make sure all spaces are really available
2362 for (int gy3
= gy
; gy3
<= gy2
; ++gy3
) {
2363 for (int gx3
= gx
; gx3
<= gx2
; ++gx3
) {
2364 if (used_grid
[(gy3
* gw
) + gx3
]) {
2375 const int screen_area
= avail
.width() * avail
.height();
2376 const int window_area
= rect
.width() * rect
.height();
2377 if (window_area
> screen_area
/ 8) {
2378 // center windows that don't fit (except for small windows)
2379 return centerPlacement(rect
, avail
);
2384 // adjust the location() based on left/right and top/bottom placement
2386 where
.setX(where
.right() - rect
.width() + 1);
2388 where
.setY(where
.bottom() - rect
.height() + 1);
2390 rect
.setPos(where
.x(), where
.y());
2396 void BScreen::createSlit(void) {
2399 _slit
= new Slit(this);
2400 _stackingList
.insert(_slit
);
2405 void BScreen::destroySlit(void) {
2408 _stackingList
.remove(_slit
);
2414 void BScreen::createToolbar(void) {
2415 assert(_toolbar
== 0);
2417 _toolbar
= new Toolbar(this);
2418 _stackingList
.insert(_toolbar
);
2423 void BScreen::destroyToolbar(void) {
2424 assert(_toolbar
!= 0);
2426 _stackingList
.remove(_toolbar
);
2432 Windowmenu
*BScreen::windowmenu(BlackboxWindow
*win
) {
2434 _windowmenu
= new Windowmenu(*_blackbox
, screen_info
.screenNumber());
2435 _windowmenu
->setWindow(win
);
2440 void BScreen::addIcon(BlackboxWindow
*win
) {
2441 if (win
->workspace() != bt::BSENTINEL
) {
2442 Workspace
*workspace
= findWorkspace(win
->workspace());
2443 assert(workspace
!= 0);
2444 workspace
->removeWindow(win
);
2447 if (win
->isTransient()) {
2448 BlackboxWindow
* const tmp
= win
->findNonTransientParent();
2450 win
->setWindowNumber(bt::BSENTINEL
);
2455 const bt::ustring s
=
2456 bt::ellideText(win
->iconTitle(), 60, bt::toUnicode("..."));
2457 int id
= _iconmenu
->insertItem(s
);
2458 _blackbox
->ewmh().setWMVisibleIconName(win
->clientWindow(), s
);
2459 win
->setWindowNumber(id
);
2463 void BScreen::removeIcon(BlackboxWindow
*win
) {
2464 if (win
->windowNumber() != bt::BSENTINEL
) {
2465 _iconmenu
->removeItem(win
->windowNumber());
2466 _blackbox
->ewmh().removeProperty(win
->clientWindow(),
2467 _blackbox
->ewmh().wmVisibleIconName());
2470 Workspace
*workspace
= findWorkspace(current_workspace
);
2471 assert(workspace
!= 0);
2472 workspace
->addWindow(win
);
2476 BlackboxWindow
*BScreen::icon(unsigned int id
) {
2477 StackingList::const_iterator it
= _stackingList
.begin(),
2478 end
= _stackingList
.end();
2479 for (; it
!= end
; ++it
) {
2480 BlackboxWindow
* const win
= dynamic_cast<BlackboxWindow
*>(*it
);
2481 if (win
&& win
->isIconic() && win
->windowNumber() == id
)
2484 assert(false); // should not happen