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 static StackingList::iterator
raiseWindow(StackingList
&stackingList
,
591 StackEntity
*entity
);
593 void BScreen::manageWindow(Window w
) {
594 XWMHints
*wmhints
= XGetWMHints(_blackbox
->XDisplay(), w
);
595 bool slit_client
= (wmhints
&& (wmhints
->flags
& StateHint
) &&
596 wmhints
->initial_state
== WithdrawnState
);
597 if (wmhints
) XFree(wmhints
);
600 if (!_slit
) createSlit();
605 (void) new BlackboxWindow(_blackbox
, w
, this);
606 // verify that we have managed the window
607 BlackboxWindow
*win
= _blackbox
->findWindow(w
);
610 if (win
->workspace() >= workspaceCount() &&
611 win
->workspace() != bt::BSENTINEL
)
612 win
->setWorkspace(current_workspace
);
614 Workspace
*workspace
= findWorkspace(win
->workspace());
616 win
->setWorkspace(bt::BSENTINEL
);
617 win
->setWindowNumber(bt::BSENTINEL
);
619 workspace
->addWindow(win
);
622 bool place_window
= true;
623 if (_blackbox
->startingUp()
624 || ((win
->isTransient() || win
->windowType() == WindowTypeDesktop
625 || (win
->wmNormalHints().flags
& (PPosition
|USPosition
)))
626 && win
->clientRect().intersects(screen_info
.rect())))
627 place_window
= false;
628 if (place_window
) placeWindow(win
);
630 windowList
.push_back(win
);
632 // insert window at the top of the stack
633 (void) _stackingList
.insert(win
);
634 (void) ::raiseWindow(_stackingList
, win
);
635 if (!_blackbox
->startingUp())
638 // 'show' window in the appropriate state
639 switch (_blackbox
->startingUp()
640 ? win
->currentState()
641 : win
->wmHints().initial_state
) {
657 void BScreen::releaseWindow(BlackboxWindow
*w
) {
659 updateClientListHint();
660 updateClientListStackingHint();
664 void BScreen::unmanageWindow(BlackboxWindow
*win
) {
667 if (win
->isIconic()) {
668 _iconmenu
->removeItem(win
->windowNumber());
670 Workspace
*workspace
= findWorkspace(win
->workspace());
672 workspace
->menu()->removeItem(win
->windowNumber());
673 if (workspace
->focusedWindow() == win
)
674 workspace
->clearFocusedWindow();
678 if (_blackbox
->running() && win
->isFocused()) {
679 // pass focus to the next appropriate window
680 if (focusFallback(win
)) {
681 // focus is going somewhere, but we want to avoid dangling pointers
682 _blackbox
->forgetFocusedWindow();
684 // explicitly clear the focus
685 _blackbox
->setFocusedWindow(0);
689 windowList
.remove(win
);
690 _stackingList
.remove(win
);
696 bool BScreen::focusFallback(const BlackboxWindow
*win
) {
697 Workspace
*workspace
= findWorkspace(win
->workspace());
699 workspace
= findWorkspace(current_workspace
);
701 if (workspace
->id() != current_workspace
)
704 BWindowGroup
*group
= win
->findWindowGroup();
706 // focus the top-most window in the group
707 BlackboxWindowList::const_iterator git
= group
->windows().begin(),
708 gend
= group
->windows().end();
709 StackingList::iterator it
= _stackingList
.begin(),
710 end
= _stackingList
.end();
711 for (; it
!= end
; ++it
) {
712 BlackboxWindow
* const tmp
= dynamic_cast<BlackboxWindow
*>(*it
);
715 || std::find(git
, gend
, tmp
) == gend
717 || (tmp
->workspace() != current_workspace
718 && tmp
->workspace() != bt::BSENTINEL
))
720 if (tmp
->setInputFocus())
725 const BlackboxWindow
* const zero
= 0;
728 if (win
->isTransient()) {
729 BlackboxWindow
* const tmp
= win
->findTransientFor();
732 && (tmp
->workspace() == current_workspace
733 || tmp
->workspace() == bt::BSENTINEL
)
734 && tmp
->setInputFocus())
738 // try to focus the top-most window in the same layer as win
739 StackingList::iterator it
= _stackingList
.layer(win
->layer()),
740 end
= std::find(it
, _stackingList
.end(), zero
);
741 assert(it
!= _stackingList
.end() && end
!= _stackingList
.end());
742 for (; it
!= end
; ++it
) {
743 BlackboxWindow
* const tmp
= dynamic_cast<BlackboxWindow
*>(*it
);
748 || (tmp
->workspace() != current_workspace
749 && tmp
->workspace() != bt::BSENTINEL
))
751 if (tmp
->setInputFocus())
756 // focus the top-most window in the stack
757 StackingList::iterator it
= _stackingList
.begin(),
758 end
= _stackingList
.end();
759 for (; it
!= end
; ++it
) {
760 BlackboxWindow
* const tmp
= dynamic_cast<BlackboxWindow
*>(*it
);
764 || (tmp
->workspace() != current_workspace
765 && tmp
->workspace() != bt::BSENTINEL
))
767 if (tmp
->setInputFocus())
776 Raises all windows, preserving the existing stacking order. Each
777 window is placed at the top of the layer it currently occupies (with
781 void raiseGroup(StackingList
&stackingList
, BWindowGroup
*group
) {
782 BlackboxWindowList windows
= group
->windows();
783 int layer
= StackingList::LayerNormal
;
784 for (; layer
< StackingList::LayerDesktop
; ++layer
) {
785 StackingList::iterator
786 it
, top
= stackingList
.layer(StackingList::Layer(layer
));
787 const StackingList::iterator begin
= stackingList
.begin(),
788 end
= stackingList
.end();
790 // 'top' points to the top of the layer, we need to start from the
791 // bottom of the layer
792 it
= std::find(top
, end
, (StackEntity
*) 0);
800 // walk up the layer, raising all windows in the group
801 for (--it
; it
!= begin
; --it
) {
807 BlackboxWindow
*tmp
= dynamic_cast<BlackboxWindow
*>(*it
);
809 // entity is not a window, or window is not visible on the
814 const BlackboxWindowList::iterator wend
= windows
.end();
815 BlackboxWindowList::iterator wit
;
816 if ((wit
= std::find(windows
.begin(), wend
, tmp
)) == wend
)
819 // found a window in this layer, raise it
821 stackingList
.raise(tmp
);
822 // don't bother looking at this window again
830 Raise all transients, preserving the existing stacking order.
833 void raiseTransients(StackingList::iterator top
,
834 StackingList
&stackingList
,
835 BlackboxWindowList
&transients
) {
836 // 'top' points to the top of the layer, we need to start from the bottom
837 StackingList::iterator begin
= stackingList
.begin(),
838 end
= stackingList
.end(),
839 it
= std::find(top
, end
, (StackEntity
*) 0);
841 for (--it
; it
!= begin
; --it
) {
846 BlackboxWindow
*tmp
= dynamic_cast<BlackboxWindow
*>(*it
);
849 BlackboxWindowList::iterator wit
= transients
.begin(),
850 wend
= transients
.end();
851 wit
= std::find(wit
, wend
, tmp
);
853 // found a transient in this layer, raise it
855 stackingList
.raise(tmp
);
856 // don't bother looking at this window again
857 transients
.erase(wit
);
864 Raises the specified stacking entity. If the entity is a window,
865 all transients are also raised (preserving their stacking order).
866 If the window is part of a group, the entire group is raised (also
867 preserving the stacking order) befor raising the specified window.
869 The return value indicates which windows need to be restacked:
871 - if raiseWindow() return stackingList.end(), then nothing needs to
874 - if raiseWindow() returns an iterator for a layer boundary
875 (i.e. *raiseWindow(...) == 0) then all windows in all layers need to
878 - otherwise, the return value points to the bottom most window that
879 needs to be restacked (i.e. the top of the layer down to the return
880 value need to be restacked)
883 StackingList::iterator
raiseWindow(StackingList
&stackingList
,
884 StackEntity
*entity
) {
885 BlackboxWindow
*win
= dynamic_cast<BlackboxWindow
*>(entity
);
887 if (win
->isFullScreen() && win
->layer() != StackingList::LayerFullScreen
) {
888 // move full-screen windows over all other windows when raising
889 win
->changeLayer(StackingList::LayerFullScreen
);
890 return stackingList
.end();
894 StackingList::iterator layer
= stackingList
.layer(entity
->layer());
895 if (*layer
== entity
) {
896 // already on top of the layer
897 return stackingList
.end();
900 BWindowGroup
*group
= 0;
902 group
= win
->findWindowGroup();
904 // raise all windows in the group before raising 'win'
905 ::raiseGroup(stackingList
, group
);
906 } else if (win
->isTransient()) {
907 // raise non-transient parent before raising transient
908 BlackboxWindow
*tmp
= win
->findNonTransientParent();
910 (void) ::raiseWindow(stackingList
, tmp
);
915 StackingList::iterator top
= stackingList
.raise(entity
),
916 end
= stackingList
.end();
919 // ... with all transients above it
920 BlackboxWindowList transients
= win
->buildFullTransientList();
921 if (!transients
.empty())
922 raiseTransients(top
, stackingList
, transients
);
924 // ... and group transients on top
925 if (group
&& !win
->isGroupTransient()) {
926 const BlackboxWindow
* const w
= win
->isTransient()
927 ? win
->findNonTransientParent()
929 if (!w
|| !w
->isGroupTransient()) {
930 BlackboxWindowList groupTransients
= group
->transients();
931 BlackboxWindowList::const_iterator wit
= groupTransients
.begin(),
932 wend
= groupTransients
.end();
933 for (; wit
!= wend
; ++wit
) {
934 BlackboxWindowList x
= (*wit
)->buildFullTransientList();
935 groupTransients
.splice(groupTransients
.end(), x
);
937 if (!groupTransients
.empty())
938 raiseTransients(top
, stackingList
, groupTransients
);
945 StackingList::iterator it
= std::find(top
, end
, (StackEntity
*) 0);
952 Raises the stack entity as above and then updates the stacking on
953 the X server. The EWMH stacking hint is also updated.
955 void BScreen::raiseWindow(StackEntity
*entity
) {
956 StackingList::iterator top
= ::raiseWindow(_stackingList
, entity
),
957 end
= _stackingList
.end();
959 // no need to raise entity
961 } else if (!(*top
)) {
962 // need to restack all windows
967 // find the first entity above us (if any)
968 StackEntity
*above
= 0;
969 StackingList::iterator layer
= _stackingList
.layer(entity
->layer()),
970 begin
= _stackingList
.begin(),
973 for (--it
; it
!= begin
; --it
) {
984 // found another entity above the one we are raising
985 stack
.push_back(above
->windowID());
987 // keep everying under empty_window
988 stack
.push_back(empty_window
);
991 // go to the layer boundary above 'top'
992 for (it
= top
; *it
&& it
!= begin
; --it
)
995 // put all windows from the layer boundary to 'top' into the stack
997 ++it
; // move past layer boundary
998 for (; *it
&& it
!= top
; ++it
) {
1000 stack
.push_back((*it
)->windowID());
1002 stack
.push_back((*top
)->windowID());
1004 XRestackWindows(_blackbox
->XDisplay(), &stack
[0], stack
.size());
1005 updateClientListStackingHint();
1010 void lowerGroup(StackingList
&stackingList
, BWindowGroup
*group
) {
1011 BlackboxWindowList windows
= group
->windows();
1012 int layer
= StackingList::LayerNormal
;
1013 for (; layer
< StackingList::LayerDesktop
; ++layer
) {
1014 const StackingList::iterator begin
= stackingList
.begin(),
1015 end
= stackingList
.end();
1016 StackingList::iterator it
= stackingList
.layer(StackingList::Layer(layer
)),
1017 bottom
= std::find(it
, end
, (StackEntity
*) 0);
1018 // 'it' points to the top of the layer
1019 assert(bottom
!= end
);
1026 // walk down the layer, lowering all windows in the group
1027 for (; it
!= bottom
; ++it
) {
1029 BlackboxWindow
*tmp
= dynamic_cast<BlackboxWindow
*>(*it
);
1031 // entity is not a window
1035 const BlackboxWindowList::iterator wend
= windows
.end();
1036 BlackboxWindowList::iterator wit
;
1037 if ((wit
= std::find(windows
.begin(), wend
, tmp
)) == wend
)
1040 // found a window in this layer, lower it
1042 (void) stackingList
.lower(tmp
);
1043 // don't bother looking at this window again
1050 static void lowerTransients(StackingList::iterator it
,
1051 StackingList
&stackingList
,
1052 BlackboxWindowList
&transients
) {
1053 // 'it' points to the top of the layer
1054 const StackingList::iterator end
= stackingList
.end(),
1055 bottom
= std::find(it
, end
, (StackEntity
*) 0);
1056 assert(bottom
!= end
);
1057 for (; it
!= bottom
; ++it
) {
1059 BlackboxWindow
*tmp
= dynamic_cast<BlackboxWindow
*>(*it
);
1063 const BlackboxWindowList::iterator wend
= transients
.end();
1064 BlackboxWindowList::iterator wit
;
1065 if ((wit
= std::find(transients
.begin(), wend
, tmp
)) == wend
)
1068 // found a transient in this layer, lower it
1070 StackingList::iterator l
= stackingList
.lower(tmp
);
1071 // don't bother looking at this window again
1072 transients
.erase(wit
);
1078 Lowers the specified stacking entity. If the entity is a window,
1079 all transients are also lowered (preserving their stacking order).
1080 If the window is part of a group, the entire group is lowered (also
1081 preserving the stacking order) befor lowering the specified window.
1083 The return value indicates which windows need to be restacked:
1085 - if lowerWindow() return stackingList.end(), then nothing needs to
1088 - if lowerWindow() returns an iterator for a layer boundary
1089 (i.e. *lowerWindow(...) == 0) then all windows in all layers need to
1092 - otherwise, the return value points to the top-most window that
1093 needs to be restacked (i.e. the return value down to the bottom of
1094 the layer need to be restacked)
1097 StackingList::iterator
lowerWindow(StackingList
&stackingList
,
1098 StackEntity
*entity
,
1099 bool ignore_group
= false) {
1100 StackingList::iterator it
, end
= stackingList
.end();
1101 BlackboxWindow
*win
= dynamic_cast<BlackboxWindow
*>(entity
);
1104 BWindowGroup
*group
= win
->findWindowGroup();
1105 if (!ignore_group
&& group
) {
1106 // lower all windows in the group before lowering 'win'
1107 ::lowerGroup(stackingList
, group
);
1108 it
= std::find(stackingList
.begin(), end
, (StackEntity
*) 0);
1112 const StackingList::iterator layer
= stackingList
.layer(win
->layer());
1113 if (win
->isTransient()) {
1114 BlackboxWindow
*const tmp
= win
->findNonTransientParent();
1115 if (tmp
&& !tmp
->isGroupTransient()) {
1116 // lower non-transient parent
1117 (void) ::lowerWindow(stackingList
, tmp
, true);
1124 // lower transients oinf 'win' and 'win'
1125 BlackboxWindowList transients
= win
->buildFullTransientList();
1126 if (!transients
.empty()) {
1127 ::lowerTransients(layer
, stackingList
, transients
);
1128 (void) stackingList
.lower(win
);
1133 it
= stackingList
.lower(entity
);
1136 (void) stackingList
.lower(entity
);
1141 it
= stackingList
.lower(entity
);
1148 void BScreen::lowerWindow(StackEntity
*entity
) {
1149 StackingList::iterator top
= ::lowerWindow(_stackingList
, entity
),
1150 end
= _stackingList
.end();
1152 // no need to lower entity
1154 } else if (!(*top
)) {
1155 // need to restack all windows
1160 // find the entity above us (if any)
1161 StackEntity
*above
= 0;
1162 StackingList::iterator begin
= _stackingList
.begin(),
1165 for (--it
; it
!= begin
; --it
) {
1173 // build the window stack
1176 // found another entity above the one we are lowering
1177 stack
.push_back(above
->windowID());
1179 // keep everying under empty_window
1180 stack
.push_back(empty_window
);
1183 // find the layer boundary
1184 StackingList::iterator bottom
= std::find(top
, end
, (StackEntity
*) 0);
1185 assert(bottom
!= end
);
1187 // put all windows from 'top' to the layer boundary into the stack
1188 for (it
= top
; it
!= bottom
; ++it
) {
1189 assert(*it
&& it
!= end
);
1190 stack
.push_back((*it
)->windowID());
1192 stack
.push_back((*top
)->windowID());
1194 XRestackWindows(_blackbox
->XDisplay(), &stack
[0], stack
.size());
1195 updateClientListStackingHint();
1199 void BScreen::restackWindows(void) {
1201 stack
.push_back(empty_window
);
1203 StackingList::const_iterator it
, end
= _stackingList
.end();
1204 for (it
= _stackingList
.begin(); it
!= end
; ++it
) {
1206 stack
.push_back((*it
)->windowID());
1209 XRestackWindows(_blackbox
->XDisplay(), &stack
[0], stack
.size());
1210 updateClientListStackingHint();
1214 void BScreen::propagateWindowName(const BlackboxWindow
* const win
) {
1215 if (win
->isIconic()) {
1216 _iconmenu
->changeItem(win
->windowNumber(),
1217 bt::ellideText(win
->iconTitle(), 60,
1218 bt::toUnicode("...")));
1219 } else if (win
->workspace() != bt::BSENTINEL
) {
1220 Workspace
*workspace
= findWorkspace(win
->workspace());
1221 assert(workspace
!= 0);
1222 workspace
->menu()->changeItem(win
->windowNumber(),
1223 bt::ellideText(win
->title(), 60,
1224 bt::toUnicode("...")));
1227 if (_toolbar
&& _blackbox
->focusedWindow() == win
)
1228 _toolbar
->redrawWindowLabel();
1232 void BScreen::nextFocus(void) {
1233 BlackboxWindow
*focused
= _blackbox
->focusedWindow(),
1235 BlackboxWindowList::iterator it
, end
= windowList
.end();
1237 if (focused
&& focused
->workspace() == current_workspace
&&
1238 focused
->screen()->screen_info
.screenNumber() ==
1239 screen_info
.screenNumber()) {
1240 it
= std::find(windowList
.begin(), end
, focused
);
1242 for (; it
!= end
; ++it
) {
1244 if (next
&& next
!= focused
&& next
->workspace() == current_workspace
&&
1245 next
->setInputFocus()) {
1246 // we found our new focus target
1247 next
->setInputFocus();
1255 for (it
= windowList
.begin(); it
!= end
; ++it
) {
1257 if (next
&& next
->workspace() == current_workspace
&&
1258 next
->setInputFocus()) {
1259 // we found our new focus target
1268 void BScreen::prevFocus(void) {
1269 BlackboxWindow
*focused
= _blackbox
->focusedWindow(),
1271 BlackboxWindowList::reverse_iterator it
, end
= windowList
.rend();
1273 if (focused
&& focused
->workspace() == current_workspace
&&
1274 focused
->screen()->screen_info
.screenNumber() ==
1275 screen_info
.screenNumber()) {
1276 it
= std::find(windowList
.rbegin(), end
, focused
);
1278 for (; it
!= end
; ++it
) {
1280 if (next
&& next
!= focused
&& next
->workspace() == current_workspace
&&
1281 next
->setInputFocus()) {
1282 // we found our new focus target
1283 next
->setInputFocus();
1291 for (it
= windowList
.rbegin(); it
!= end
; ++it
) {
1293 if (next
&& next
->workspace() == current_workspace
&&
1294 next
->setInputFocus()) {
1295 // we found our new focus target
1304 void BScreen::raiseFocus(void) {
1305 BlackboxWindow
*focused
= _blackbox
->focusedWindow();
1306 if (! focused
|| focused
->screen() != this)
1309 raiseWindow(focused
);
1313 void BScreen::InitMenu(void) {
1317 _rootmenu
= new Rootmenu(*_blackbox
, screen_info
.screenNumber(), this);
1318 _rootmenu
->showTitle();
1320 bool defaultMenu
= True
;
1322 if (_blackbox
->resource().menuFilename()) {
1323 FILE *menu_file
= fopen(_blackbox
->resource().menuFilename(), "r");
1326 perror(_blackbox
->resource().menuFilename());
1328 if (feof(menu_file
)) {
1329 fprintf(stderr
, "%s: menu file '%s' is empty\n",
1330 _blackbox
->applicationName().c_str(),
1331 _blackbox
->resource().menuFilename());
1333 char line
[1024], label
[1024];
1334 memset(line
, 0, 1024);
1335 memset(label
, 0, 1024);
1337 while (fgets(line
, 1024, menu_file
) && ! feof(menu_file
)) {
1341 int i
, key
= 0, index
= -1, len
= strlen(line
);
1343 for (i
= 0; i
< len
; i
++) {
1344 if (line
[i
] == '[') index
= 0;
1345 else if (line
[i
] == ']') break;
1346 else if (line
[i
] != ' ')
1348 key
+= tolower(line
[i
]);
1351 if (key
== 517) { // [begin]
1353 for (i
= index
; i
< len
; i
++) {
1356 else if (line
[i
] == ')')
1358 else if (index
++ >= 0) {
1359 if (line
[i
] == '\\' && i
< len
- 1) i
++;
1360 label
[index
- 1] = line
[i
];
1364 if (index
== -1) index
= 0;
1365 label
[index
] = '\0';
1367 _rootmenu
->setTitle(bt::toUnicode(label
));
1368 defaultMenu
= parseMenuFile(menu_file
, _rootmenu
);
1378 _rootmenu
->setTitle(bt::toUnicode("_Blackbox"));
1380 _rootmenu
->insertFunction(bt::toUnicode("xterm"),
1381 BScreen::Execute
, "xterm");
1382 _rootmenu
->insertFunction(bt::toUnicode("Restart"),
1384 _rootmenu
->insertFunction(bt::toUnicode("Exit"),
1387 _blackbox
->saveMenuFilename(_blackbox
->resource().menuFilename());
1393 size_t string_within(char begin
, char end
,
1394 const char *input
, size_t start_at
, size_t length
,
1398 size_t i
= start_at
;
1399 for (; i
< length
; ++i
) {
1400 if (input
[i
] == begin
) {
1402 } else if (input
[i
] == end
) {
1405 if (input
[i
] == '\\' && i
< length
- 1) i
++;
1406 output
[index
++] = input
[i
];
1411 output
[index
] = '\0';
1419 bool BScreen::parseMenuFile(FILE *file
, Rootmenu
*menu
) {
1420 char line
[1024], keyword
[1024], label
[1024], command
[1024];
1423 while (! (done
|| feof(file
))) {
1424 memset(line
, 0, 1024);
1425 memset(label
, 0, 1024);
1426 memset(command
, 0, 1024);
1428 if (! fgets(line
, 1024, file
))
1431 if (line
[0] == '#') // comment, skip it
1434 size_t line_length
= strlen(line
);
1435 unsigned int key
= 0;
1437 // get the keyword enclosed in []'s
1438 size_t pos
= string_within('[', ']', line
, 0, line_length
, keyword
);
1440 if (keyword
[0] == '\0') { // no keyword, no menu entry
1443 size_t len
= strlen(keyword
);
1444 for (size_t i
= 0; i
< len
; ++i
) {
1445 if (keyword
[i
] != ' ')
1446 key
+= tolower(keyword
[i
]);
1450 // get the label enclosed in ()'s
1451 pos
= string_within('(', ')', line
, pos
, line_length
, label
);
1453 // get the command enclosed in {}'s
1454 pos
= string_within('{', '}', line
, pos
, line_length
, command
);
1463 menu
->insertSeparator();
1469 menu
->insertItem(bt::toUnicode(label
));
1474 if (! (*label
&& *command
)) {
1476 "%s: [exec] error, no menu label and/or command defined\n",
1477 _blackbox
->applicationName().c_str());
1481 menu
->insertFunction(bt::toUnicode(label
), BScreen::Execute
, command
);
1486 fprintf(stderr
, "%s: [exit] error, no menu label defined\n",
1487 _blackbox
->applicationName().c_str());
1491 menu
->insertFunction(bt::toUnicode(label
), BScreen::Exit
);
1494 case 561: { // style
1495 if (! (*label
&& *command
)) {
1497 "%s: [style] error, no menu label and/or filename defined\n",
1498 _blackbox
->applicationName().c_str());
1502 std::string style
= bt::expandTilde(command
);
1503 menu
->insertFunction(bt::toUnicode(label
),
1504 BScreen::SetStyle
, style
.c_str());
1510 fprintf(stderr
, "%s: [config] error, no label defined",
1511 _blackbox
->applicationName().c_str());
1515 menu
->insertItem(bt::toUnicode(label
), configmenu
);
1518 case 740: { // include
1520 fprintf(stderr
, "%s: [include] error, no filename defined\n",
1521 _blackbox
->applicationName().c_str());
1525 std::string newfile
= bt::expandTilde(label
);
1526 FILE *submenufile
= fopen(newfile
.c_str(), "r");
1528 if (! submenufile
) {
1529 perror(newfile
.c_str());
1534 if (fstat(fileno(submenufile
), &buf
) ||
1535 ! S_ISREG(buf
.st_mode
)) {
1536 fprintf(stderr
, "%s: [include] error: '%s' is not a regular file\n",
1537 _blackbox
->applicationName().c_str(), newfile
.c_str());
1541 if (! feof(submenufile
)) {
1542 if (! parseMenuFile(submenufile
, menu
))
1543 _blackbox
->saveMenuFilename(newfile
);
1545 fclose(submenufile
);
1551 case 767: { // submenu
1553 fprintf(stderr
, "%s: [submenu] error, no menu label defined\n",
1554 _blackbox
->applicationName().c_str());
1559 new Rootmenu(*_blackbox
, screen_info
.screenNumber(), this);
1560 submenu
->showTitle();
1563 submenu
->setTitle(bt::toUnicode(command
));
1565 submenu
->setTitle(bt::toUnicode(label
));
1567 parseMenuFile(file
, submenu
);
1568 menu
->insertItem(bt::toUnicode(label
), submenu
);
1573 case 773: { // restart
1575 fprintf(stderr
, "%s: [restart] error, no menu label defined\n",
1576 _blackbox
->applicationName().c_str());
1581 menu
->insertFunction(bt::toUnicode(label
),
1582 BScreen::RestartOther
, command
);
1584 menu
->insertFunction(bt::toUnicode(label
), BScreen::Restart
);
1589 case 845: { // reconfig
1591 fprintf(stderr
, "%s: [reconfig] error, no menu label defined\n",
1592 _blackbox
->applicationName().c_str());
1596 menu
->insertFunction(bt::toUnicode(label
), BScreen::Reconfigure
);
1600 case 995: // stylesdir
1601 case 1113: { // stylesmenu
1602 bool newmenu
= ((key
== 1113) ? True
: False
);
1604 if (! *label
|| (! *command
&& newmenu
)) {
1606 "%s: [stylesdir/stylesmenu] error, no directory defined\n",
1607 _blackbox
->applicationName().c_str());
1611 char *directory
= ((newmenu
) ? command
: label
);
1613 std::string stylesdir
= bt::expandTilde(directory
);
1615 struct stat statbuf
;
1617 if (stat(stylesdir
.c_str(), &statbuf
) == -1) {
1619 "%s: [stylesdir/stylesmenu] error, '%s' does not exist\n",
1620 _blackbox
->applicationName().c_str(), stylesdir
.c_str());
1623 if (! S_ISDIR(statbuf
.st_mode
)) {
1625 "%s: [stylesdir/stylesmenu] error, '%s' is not a directory\n",
1626 _blackbox
->applicationName().c_str(), stylesdir
.c_str());
1630 Rootmenu
*stylesmenu
;
1634 new Rootmenu(*_blackbox
, screen_info
.screenNumber(),this);
1635 stylesmenu
->showTitle();
1640 DIR *d
= opendir(stylesdir
.c_str());
1642 std::vector
<std::string
> ls
;
1644 while((p
= readdir(d
)))
1645 ls
.push_back(p
->d_name
);
1649 std::sort(ls
.begin(), ls
.end());
1651 std::vector
<std::string
>::iterator it
= ls
.begin(),
1653 for (; it
!= end
; ++it
) {
1654 std::string
& fname
= *it
;
1656 if (fname
[0] == '.' || fname
[fname
.size()-1] == '~')
1659 std::string style
= stylesdir
;
1663 if (! stat(style
.c_str(), &statbuf
) && S_ISREG(statbuf
.st_mode
)) {
1664 // convert 'This_Long_Name' to 'This Long Name'
1665 std::replace(fname
.begin(), fname
.end(), '_', ' ');
1667 stylesmenu
->insertFunction(bt::toUnicode(fname
),
1668 BScreen::SetStyle
, style
);
1673 stylesmenu
->setTitle(bt::toUnicode(label
));
1674 menu
->insertItem(bt::toUnicode(label
), stylesmenu
);
1677 _blackbox
->saveMenuFilename(stylesdir
);
1681 case 1090: { // workspaces
1683 fprintf(stderr
, "%s: [workspaces] error, no menu label defined\n",
1684 _blackbox
->applicationName().c_str());
1688 menu
->insertItem(bt::toUnicode(label
), _workspacemenu
);
1694 return (menu
->count() == 0);
1698 void BScreen::shutdown(void) {
1699 XSelectInput(_blackbox
->XDisplay(), screen_info
.rootWindow(),
1701 XSync(_blackbox
->XDisplay(), False
);
1703 // unmanage all windows, but keep them in the current stacking order
1705 StackingList::const_iterator it
, end
= _stackingList
.end();
1706 for (it
= _stackingList
.begin(); it
!= end
; ++it
) {
1709 const BlackboxWindow
* const win
= dynamic_cast<BlackboxWindow
*>(*it
);
1712 stack
.push_back(win
->clientWindow());
1715 while(! windowList
.empty())
1716 unmanageWindow(windowList
.back());
1719 XRestackWindows(_blackbox
->XDisplay(), &stack
[0], stack
.size());
1726 void BScreen::showGeometry(GeometryType type
, const bt::Rect
&rect
) {
1727 if (! geom_visible
) {
1728 XMoveResizeWindow(_blackbox
->XDisplay(), geom_window
,
1729 (screen_info
.width() - geom_w
) / 2,
1730 (screen_info
.height() - geom_h
) / 2, geom_w
, geom_h
);
1731 XMapWindow(_blackbox
->XDisplay(), geom_window
);
1732 XRaiseWindow(_blackbox
->XDisplay(), geom_window
);
1734 geom_visible
= True
;
1740 sprintf(label
, "X:%4d Y:%4d", rect
.x(), rect
.y());
1743 sprintf(label
, "W:%4u H:%4u", rect
.width(), rect
.height());
1749 const WindowStyle
&style
= _resource
.windowStyle();
1750 const bt::Texture
&texture
=
1751 (style
.focus
.label
.texture() == bt::Texture::Parent_Relative
)
1753 : style
.focus
.label
;
1754 const bt::Rect
u(0, 0, geom_w
, geom_h
);
1755 const bt::Rect
t(style
.label_margin
,
1757 geom_w
- (style
.label_margin
* 2),
1758 geom_h
- (style
.label_margin
* 2));
1759 const bt::Pen
pen(screen_info
.screenNumber(), style
.focus
.text
);
1760 bt::drawTexture(screen_info
.screenNumber(), texture
, geom_window
,
1762 bt::drawText(style
.font
, pen
, geom_window
, t
, style
.alignment
,
1763 bt::toUnicode(label
));
1767 void BScreen::hideGeometry(void) {
1769 XUnmapWindow(_blackbox
->XDisplay(), geom_window
);
1770 geom_visible
= False
;
1775 void BScreen::addStrut(bt::EWMH::Strut
*strut
) {
1776 strutList
.push_back(strut
);
1777 updateAvailableArea();
1781 void BScreen::removeStrut(bt::EWMH::Strut
*strut
) {
1782 strutList
.remove(strut
);
1783 updateAvailableArea();
1787 void BScreen::updateStrut(void) {
1788 updateAvailableArea();
1792 const bt::Rect
& BScreen::availableArea(void) {
1793 if (_blackbox
->resource().fullMaximization())
1794 return screen_info
.rect(); // return the full screen
1799 void BScreen::updateAvailableArea(void) {
1802 /* these values represent offsets from the screen edge
1803 * we look for the biggest offset on each edge and then apply them
1805 * do not be confused by the similarity to the names of Rect's members
1807 bt::EWMH::Strut current
;
1809 StrutList::const_iterator sit
= strutList
.begin(), send
= strutList
.end();
1811 for(; sit
!= send
; ++sit
) {
1812 const bt::EWMH::Strut
* const strut
= *sit
;
1813 if (strut
->left
> current
.left
)
1814 current
.left
= strut
->left
;
1815 if (strut
->top
> current
.top
)
1816 current
.top
= strut
->top
;
1817 if (strut
->right
> current
.right
)
1818 current
.right
= strut
->right
;
1819 if (strut
->bottom
> current
.bottom
)
1820 current
.bottom
= strut
->bottom
;
1823 new_area
.setPos(current
.left
, current
.top
);
1824 new_area
.setSize(screen_info
.width() - (current
.left
+ current
.right
),
1825 screen_info
.height() - (current
.top
+ current
.bottom
));
1827 if (new_area
!= usableArea
) {
1828 usableArea
= new_area
;
1829 BlackboxWindowList::iterator wit
= windowList
.begin(),
1830 wend
= windowList
.end();
1831 for (; wit
!= wend
; ++wit
)
1832 if ((*wit
)->isMaximized()) (*wit
)->remaximize();
1834 updateWorkareaHint();
1839 Workspace
* BScreen::findWorkspace(unsigned int index
) const {
1840 if (index
== bt::BSENTINEL
)
1842 assert(index
< workspacesList
.size());
1843 return workspacesList
[index
];
1847 void BScreen::clientMessageEvent(const XClientMessageEvent
* const event
) {
1848 if (event
->format
!= 32) return;
1850 if (event
->message_type
== _blackbox
->ewmh().numberOfDesktops()) {
1851 unsigned int number
= event
->data
.l
[0];
1852 if (number
> workspaceCount()) {
1853 for (; number
!= workspaceCount(); --number
)
1855 } else if (number
< workspaceCount()) {
1856 for (; number
!= workspaceCount(); ++number
)
1857 removeLastWorkspace();
1859 } else if (event
->message_type
== _blackbox
->ewmh().desktopNames()) {
1861 } else if (event
->message_type
== _blackbox
->ewmh().currentDesktop()) {
1862 const unsigned int workspace
= event
->data
.l
[0];
1863 if (workspace
< workspaceCount() && workspace
!= current_workspace
)
1864 setCurrentWorkspace(workspace
);
1869 void BScreen::buttonPressEvent(const XButtonEvent
* const event
) {
1870 if (event
->button
== 1) {
1872 set this screen active. keygrabs and input focus will stay on
1873 this screen until the user focuses a window on another screen or
1874 makes another screen active.
1876 _blackbox
->setActiveScreen(this);
1877 } else if (event
->button
== 2) {
1878 _workspacemenu
->popup(event
->x_root
, event
->y_root
);
1879 } else if (event
->button
== 3) {
1880 _blackbox
->checkMenu();
1881 _rootmenu
->popup(event
->x_root
, event
->y_root
);
1882 } else if (event
->button
== 4) {
1883 setCurrentWorkspace(current_workspace
< workspacesList
.size() - 1
1884 ? current_workspace
+ 1
1886 } else if (event
->button
== 5) {
1887 setCurrentWorkspace(current_workspace
> 0
1888 ? current_workspace
- 1
1889 : workspacesList
.size() - 1);
1894 void BScreen::propertyNotifyEvent(const XPropertyEvent
* const event
) {
1895 if (event
->atom
== _blackbox
->ewmh().activeWindow() && _toolbar
)
1896 _toolbar
->redrawWindowLabel();
1900 void BScreen::unmapNotifyEvent(const XUnmapEvent
* const event
) {
1901 // handle synthetic unmap events according to ICCCM section 4.1.4
1902 BlackboxWindow
*win
= _blackbox
->findWindow(event
->window
);
1903 if (win
&& event
->event
== screen_info
.rootWindow()
1904 && !event
->from_configure
)
1905 win
->unmapNotifyEvent(event
);
1909 void BScreen::toggleFocusModel(FocusModel model
) {
1910 std::for_each(windowList
.begin(), windowList
.end(),
1911 std::mem_fun(&BlackboxWindow::ungrabButtons
));
1913 _blackbox
->resource().setFocusModel(model
);
1915 std::for_each(windowList
.begin(), windowList
.end(),
1916 std::mem_fun(&BlackboxWindow::grabButtons
));
1920 void BScreen::updateWorkareaHint(void) const {
1921 unsigned long *workarea
, *tmp
;
1923 tmp
= workarea
= new unsigned long[workspaceCount() * 4];
1925 for (unsigned int i
= 0; i
< workspaceCount(); ++i
) {
1926 tmp
[0] = usableArea
.x();
1927 tmp
[1] = usableArea
.y();
1928 tmp
[2] = usableArea
.width();
1929 tmp
[3] = usableArea
.height();
1933 _blackbox
->ewmh().setWorkarea(screen_info
.rootWindow(),
1934 workarea
, workspaceCount());
1940 void BScreen::updateDesktopNamesHint(void) const {
1941 std::vector
<bt::ustring
> names(workspacesList
.size());
1942 WorkspaceList::const_iterator it
= workspacesList
.begin();
1943 const WorkspaceList::const_iterator end
= workspacesList
.end();
1944 for (; it
!= end
; ++it
)
1945 names
.push_back((*it
)->name());
1946 _blackbox
->ewmh().setDesktopNames(screen_info
.rootWindow(), names
);
1950 void BScreen::updateClientListHint(void) const {
1951 if (windowList
.empty()) {
1952 _blackbox
->ewmh().removeProperty(screen_info
.rootWindow(),
1953 _blackbox
->ewmh().clientList());
1957 bt::EWMH::WindowList
clientList(windowList
.size());
1959 std::transform(windowList
.begin(), windowList
.end(), clientList
.begin(),
1960 std::mem_fun(&BlackboxWindow::clientWindow
));
1962 _blackbox
->ewmh().setClientList(screen_info
.rootWindow(), clientList
);
1966 void BScreen::updateClientListStackingHint(void) const {
1967 bt::EWMH::WindowList stack
;
1969 // we store windows in top-to-bottom order, but the EWMH wants
1971 StackingList::const_reverse_iterator it
= _stackingList
.rbegin(),
1972 end
= _stackingList
.rend();
1973 for (; it
!= end
; ++it
) {
1974 const BlackboxWindow
* const win
= dynamic_cast<BlackboxWindow
*>(*it
);
1975 if (win
) stack
.push_back(win
->clientWindow());
1978 if (stack
.empty()) {
1979 _blackbox
->ewmh().removeProperty(screen_info
.rootWindow(),
1980 _blackbox
->ewmh().clientListStacking());
1984 _blackbox
->ewmh().setClientListStacking(screen_info
.rootWindow(), stack
);
1988 void BScreen::readDesktopNames(void) {
1989 std::vector
<bt::ustring
> names
;
1990 if(! _blackbox
->ewmh().readDesktopNames(screen_info
.rootWindow(), names
))
1993 std::vector
<bt::ustring
>::const_iterator it
= names
.begin();
1994 const std::vector
<bt::ustring
>::const_iterator end
= names
.end();
1995 WorkspaceList::iterator wit
= workspacesList
.begin();
1996 const WorkspaceList::iterator wend
= workspacesList
.end();
1998 for (; wit
!= wend
&& it
!= end
; ++wit
, ++it
) {
1999 if ((*wit
)->name() != *it
)
2000 (*wit
)->setName(*it
);
2003 if (names
.size() < workspacesList
.size())
2004 updateDesktopNamesHint();
2008 BlackboxWindow
*BScreen::window(unsigned int workspace
, unsigned int id
) {
2009 StackingList::const_iterator it
= _stackingList
.begin(),
2010 end
= _stackingList
.end();
2011 for (; it
!= end
; ++it
) {
2012 BlackboxWindow
* const win
= dynamic_cast<BlackboxWindow
*>(*it
);
2013 if (win
&& win
->workspace() == workspace
&& win
->windowNumber() == id
)
2016 assert(false); // should not happen
2021 void BScreen::placeWindow(BlackboxWindow
*win
) {
2022 const bt::Rect
&avail
= availableArea();
2023 bt::Rect
new_win(avail
.x(), avail
.y(),
2024 win
->frameRect().width(), win
->frameRect().height());
2025 bool placed
= False
;
2027 BlackboxResource
&res
= _blackbox
->resource();
2028 switch (res
.windowPlacementPolicy()) {
2029 case RowSmartPlacement
:
2030 case ColSmartPlacement
:
2031 placed
= smartPlacement(win
->workspace(), new_win
, avail
);
2034 break; // handled below
2037 if (placed
== False
) {
2038 cascadePlacement(new_win
, avail
);
2039 cascade_x
+= _resource
.windowStyle().title_height
;
2040 cascade_y
+= _resource
.windowStyle().title_height
;
2043 if (new_win
.right() > avail
.right())
2044 new_win
.setX(avail
.left());
2045 if (new_win
.bottom() > avail
.bottom())
2046 new_win
.setY(avail
.top());
2048 win
->configure(new_win
.x(), new_win
.y(), new_win
.width(), new_win
.height());
2052 bool BScreen::cascadePlacement(bt::Rect
&win
,
2053 const bt::Rect
&avail
) {
2054 if (cascade_x
> (avail
.width() / 2) ||
2055 cascade_y
> (avail
.height() / 2))
2056 cascade_x
= cascade_y
= 32;
2058 if (cascade_x
== 32) {
2059 cascade_x
+= avail
.x();
2060 cascade_y
+= avail
.y();
2063 win
.setPos(cascade_x
, cascade_y
);
2065 if (win
.right() > avail
.right() ||
2066 win
.bottom() > avail
.bottom()) {
2067 cascade_x
= cascade_y
= 32;
2069 cascade_x
+= avail
.x();
2070 cascade_y
+= avail
.y();
2072 win
.setPos(cascade_x
, cascade_y
);
2079 bool BScreen::smartPlacement(unsigned int workspace
, bt::Rect
& rect
,
2080 const bt::Rect
& avail
) {
2082 const BlackboxResource
&res
= _blackbox
->resource();
2083 const bool row_placement
=
2084 (res
.windowPlacementPolicy() == RowSmartPlacement
);
2085 const bool leftright
=
2086 (res
.rowPlacementDirection() == LeftRight
);
2087 const bool topbottom
=
2088 (res
.colPlacementDirection() == TopBottom
);
2089 const bool ignore_shaded
= res
.placementIgnoresShaded();
2091 const int left_border
= leftright
? 0 : -2;
2092 const int top_border
= topbottom
? 0 : -2;
2093 const int right_border
= leftright
? 2 : 0;
2094 const int bottom_border
= topbottom
? 2 : 0;
2096 StackingList::const_iterator w_it
, w_end
;
2099 build a sorted vector of x and y grid boundaries
2101 note: we use one vector to reduce the number of allocations
2102 std::vector must do.. we allocate as much memory as we would need
2103 in the worst case scenario and work with that
2105 std::vector
<int> coords(_stackingList
.size() * 4 + 4);
2106 std::vector
<int>::iterator
2107 x_begin
= coords
.begin(),
2109 y_begin
= coords
.begin() + _stackingList
.size() * 2 + 2,
2113 std::vector
<int>::iterator x_it
= x_begin
, y_it
= y_begin
;
2115 *x_it
++ = avail
.left();
2116 *x_it
++ = avail
.right();
2119 *y_it
++ = avail
.top();
2120 *y_it
++ = avail
.bottom();
2124 for (w_it
= _stackingList
.begin(), w_end
= _stackingList
.end();
2125 w_it
!= w_end
; ++w_it
) {
2126 const BlackboxWindow
* const win
=
2127 dynamic_cast<const BlackboxWindow
*>(*w_it
);
2130 if (win
->windowType() == WindowTypeDesktop
)
2132 if (win
->isIconic())
2134 if (win
->workspace() != bt::BSENTINEL
&& win
->workspace() != workspace
)
2136 if (ignore_shaded
&& win
->isShaded())
2139 *x_it
++ = std::max(win
->frameRect().left() + left_border
,
2141 *x_it
++ = std::min(win
->frameRect().right() + right_border
,
2145 *y_it
++ = std::max(win
->frameRect().top() + top_border
,
2147 *y_it
++ = std::min(win
->frameRect().bottom() + bottom_border
,
2152 assert(x_end
<= y_begin
);
2155 std::sort(x_begin
, x_end
);
2156 x_end
= std::unique(x_begin
, x_end
);
2158 std::sort(y_begin
, y_end
);
2159 y_end
= std::unique(y_begin
, y_end
);
2161 // build a distribution grid
2162 unsigned int gw
= x_end
- x_begin
- 1,
2163 gh
= y_end
- y_begin
- 1;
2164 std::vector
<bool> used_grid(gw
* gh
);
2165 std::fill_n(used_grid
.begin(), used_grid
.size(), false);
2167 for (w_it
= _stackingList
.begin(), w_end
= _stackingList
.end();
2168 w_it
!= w_end
; ++w_it
) {
2169 const BlackboxWindow
* const win
=
2170 dynamic_cast<const BlackboxWindow
*>(*w_it
);
2173 if (win
->windowType() == WindowTypeDesktop
)
2175 if (win
->isIconic())
2177 if (win
->workspace() != bt::BSENTINEL
&& win
->workspace() != workspace
)
2179 if (ignore_shaded
&& win
->isShaded())
2183 std::max(win
->frameRect().left() + left_border
,
2186 std::max(win
->frameRect().top() + top_border
,
2189 std::min(win
->frameRect().right() + right_border
,
2191 const int w_bottom
=
2192 std::min(win
->frameRect().bottom() + bottom_border
,
2195 // which areas of the grid are used by this window?
2196 std::vector
<int>::iterator l_it
= std::find(x_begin
, x_end
, w_left
),
2197 r_it
= std::find(x_begin
, x_end
, w_right
),
2198 t_it
= std::find(y_begin
, y_end
, w_top
),
2199 b_it
= std::find(y_begin
, y_end
, w_bottom
);
2200 assert(l_it
!= x_end
&&
2205 const unsigned int left
= l_it
- x_begin
,
2206 right
= r_it
- x_begin
,
2207 top
= t_it
- y_begin
,
2208 bottom
= b_it
- y_begin
;
2210 for (unsigned int gy
= top
; gy
< bottom
; ++gy
)
2211 for (unsigned int gx
= left
; gx
< right
; ++gx
)
2212 used_grid
[(gy
* gw
) + gx
] = true;
2216 Attempt to fit the window into any of the empty areas in the grid.
2217 The exact order is dependent upon the users configuration (as
2221 - outer -> vertical axis
2222 - inner -> horizontal axis
2225 - outer -> horizontal axis
2226 - inner -> vertical axis
2230 int &outer
= row_placement
? gy
: gx
;
2231 int &inner
= row_placement
? gx
: gy
;
2232 const int outer_delta
= row_placement
2233 ? (topbottom
? 1 : -1)
2234 : (leftright
? 1 : -1);
2235 const int inner_delta
= row_placement
2236 ? (leftright
? 1 : -1)
2237 : (topbottom
? 1 : -1);
2238 const int outer_begin
= row_placement
2239 ? (topbottom
? 0 : static_cast<int>(gh
) - 1)
2240 : (leftright
? 0 : static_cast<int>(gw
) - 1);
2241 const int outer_end
= row_placement
2242 ? (topbottom
? static_cast<int>(gh
) : -1)
2243 : (leftright
? static_cast<int>(gw
) : -1);
2244 const int inner_begin
= row_placement
2245 ? (leftright
? 0 : static_cast<int>(gw
) - 1)
2246 : (topbottom
? 0 : static_cast<int>(gh
) - 1);
2247 const int inner_end
= row_placement
2248 ? (leftright
? static_cast<int>(gw
) : -1)
2249 : (topbottom
? static_cast<int>(gh
) : -1);
2253 for (outer
= outer_begin
; ! fit
&& outer
!= outer_end
;
2254 outer
+= outer_delta
) {
2255 for (inner
= inner_begin
; ! fit
&& inner
!= inner_end
;
2256 inner
+= inner_delta
) {
2257 // see if the window fits in a single unused area
2258 if (used_grid
[(gy
* gw
) + gx
]) continue;
2260 where
.setCoords(*(x_begin
+ gx
), *(y_begin
+ gy
),
2261 *(x_begin
+ gx
+ 1), *(y_begin
+ gy
+ 1));
2263 if (where
.width() >= rect
.width() &&
2264 where
.height() >= rect
.height()) {
2270 see if we neighboring spaces are unused
2272 TODO: we should grid fit in the same direction as above,
2273 instead of always right->left and top->bottom
2275 int gx2
= gx
, gy2
= gy
;
2277 if (rect
.width() > where
.width()) {
2278 for (gx2
= gx
+1; gx2
< static_cast<int>(gw
); ++gx2
) {
2279 if (used_grid
[(gy
* gw
) + gx2
]) {
2284 where
.setCoords(*(x_begin
+ gx
), *(y_begin
+ gy
),
2285 *(x_begin
+ gx2
+ 1), *(y_begin
+ gy2
+ 1));
2287 if (where
.width() >= rect
.width()) break;
2290 if (gx2
>= static_cast<int>(gw
)) --gx2
;
2293 if (rect
.height() > where
.height()) {
2294 for (gy2
= gy
; gy2
< static_cast<int>(gh
); ++gy2
) {
2295 if (used_grid
[(gy2
* gw
) + gx
]) {
2300 where
.setCoords(*(x_begin
+ gx
), *(y_begin
+ gy
),
2301 *(x_begin
+ gx2
+ 1), *(y_begin
+ gy2
+ 1));
2303 if (where
.height() >= rect
.height()) break;
2306 if (gy2
>= static_cast<int>(gh
)) --gy2
;
2309 if (where
.width() >= rect
.width() &&
2310 where
.height() >= rect
.height()) {
2313 // make sure all spaces are really available
2314 for (int gy3
= gy
; gy3
<= gy2
; ++gy3
) {
2315 for (int gx3
= gx
; gx3
<= gx2
; ++gx3
) {
2316 if (used_grid
[(gy3
* gw
) + gx3
]) {
2327 const int screen_area
= avail
.width() * avail
.height();
2328 const int window_area
= rect
.width() * rect
.height();
2329 if (window_area
> screen_area
/ 8) {
2330 // center windows that don't fit (except for small windows)
2332 static_cast<int>(avail
.x() + (avail
.width() - rect
.width()) / 2);
2334 static_cast<int>(avail
.y() + (avail
.height() - rect
.height()) / 2);
2341 // adjust the location() based on left/right and top/bottom placement
2343 where
.setX(where
.right() - rect
.width() + 1);
2345 where
.setY(where
.bottom() - rect
.height() + 1);
2347 rect
.setPos(where
.x(), where
.y());
2353 void BScreen::createSlit(void) {
2356 _slit
= new Slit(this);
2357 _stackingList
.insert(_slit
);
2362 void BScreen::destroySlit(void) {
2365 _stackingList
.remove(_slit
);
2371 void BScreen::createToolbar(void) {
2372 assert(_toolbar
== 0);
2374 _toolbar
= new Toolbar(this);
2375 _stackingList
.insert(_toolbar
);
2380 void BScreen::destroyToolbar(void) {
2381 assert(_toolbar
!= 0);
2383 _stackingList
.remove(_toolbar
);
2389 Windowmenu
*BScreen::windowmenu(BlackboxWindow
*win
) {
2391 _windowmenu
= new Windowmenu(*_blackbox
, screen_info
.screenNumber());
2392 _windowmenu
->setWindow(win
);
2397 void BScreen::addIcon(BlackboxWindow
*win
) {
2398 if (win
->workspace() != bt::BSENTINEL
) {
2399 Workspace
*workspace
= findWorkspace(win
->workspace());
2400 assert(workspace
!= 0);
2401 workspace
->removeWindow(win
);
2404 if (win
->isTransient()) {
2405 BlackboxWindow
* const tmp
= win
->findNonTransientParent();
2407 win
->setWindowNumber(bt::BSENTINEL
);
2412 const bt::ustring s
=
2413 bt::ellideText(win
->iconTitle(), 60, bt::toUnicode("..."));
2414 int id
= _iconmenu
->insertItem(s
);
2415 _blackbox
->ewmh().setWMVisibleIconName(win
->clientWindow(), s
);
2416 win
->setWindowNumber(id
);
2420 void BScreen::removeIcon(BlackboxWindow
*win
) {
2421 if (win
->windowNumber() != bt::BSENTINEL
) {
2422 _iconmenu
->removeItem(win
->windowNumber());
2423 _blackbox
->ewmh().removeProperty(win
->clientWindow(),
2424 _blackbox
->ewmh().wmVisibleIconName());
2427 Workspace
*workspace
= findWorkspace(current_workspace
);
2428 assert(workspace
!= 0);
2429 workspace
->addWindow(win
);
2433 BlackboxWindow
*BScreen::icon(unsigned int id
) {
2434 StackingList::const_iterator it
= _stackingList
.begin(),
2435 end
= _stackingList
.end();
2436 for (; it
!= end
; ++it
) {
2437 BlackboxWindow
* const win
= dynamic_cast<BlackboxWindow
*>(*it
);
2438 if (win
&& win
->isIconic() && win
->windowNumber() == id
)
2441 assert(false); // should not happen