fix raising/lowering of group transients
[blackbox.git] / src / Screen.cc
blob2e3d791a43a3df503e86705341ca14fa5ddedc79
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>
6 //
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.
25 #include "Screen.hh"
26 #include "Clientmenu.hh"
27 #include "Configmenu.hh"
28 #include "Iconmenu.hh"
29 #include "Rootmenu.hh"
30 #include "Slit.hh"
31 #include "Slitmenu.hh"
32 #include "Toolbar.hh"
33 #include "Toolbarmenu.hh"
34 #include "Window.hh"
35 #include "WindowGroup.hh"
36 #include "Windowmenu.hh"
37 #include "Workspace.hh"
38 #include "Workspacemenu.hh"
40 #include <Pen.hh>
41 #include <PixmapCache.hh>
42 #include <Unicode.hh>
44 #include <X11/Xutil.h>
45 #include <sys/types.h>
46 #include <sys/stat.h>
47 #include <assert.h>
48 #include <ctype.h>
49 #include <dirent.h>
52 static bool running = true;
53 static int anotherWMRunning(Display *, XErrorEvent *) {
54 running = false;
55 return -1;
59 BScreen::BScreen(Blackbox *bb, unsigned int scrn) :
60 screen_info(bb->display().screenInfo(scrn)), _blackbox(bb),
61 _resource(bb->resource().screenResource(scrn))
63 running = true;
64 XErrorHandler old = XSetErrorHandler((XErrorHandler) anotherWMRunning);
65 XSelectInput(screen_info.display().XDisplay(),
66 screen_info.rootWindow(),
67 PropertyChangeMask |
68 StructureNotifyMask |
69 SubstructureRedirectMask |
70 ButtonPressMask);
72 XSync(screen_info.display().XDisplay(), False);
73 XSetErrorHandler((XErrorHandler) old);
75 managed = running;
76 if (! managed) {
77 fprintf(stderr,
78 "%s: another window manager is already running on display '%s'\n",
79 _blackbox->applicationName().c_str(),
80 DisplayString(_blackbox->XDisplay()));
81 return;
84 static const char *visual_classes[] = {
85 "StaticGray",
86 "GrayScale",
87 "StaticColor",
88 "PseudoColor",
89 "TrueColor",
90 "DirectColor"
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;
101 _rootmenu = 0;
102 _windowmenu = 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());
110 LoadStyle();
112 geom_pixmap = None;
113 geom_visible = False;
114 geom_window = None;
115 updateGeomWindow();
117 empty_window =
118 XCreateSimpleWindow(_blackbox->XDisplay(), screen_info.rootWindow(),
119 0, 0, screen_info.width(), screen_info.height(), 0,
120 0l, 0l);
121 XSetWindowBackgroundPixmap(_blackbox->XDisplay(), empty_window, None);
123 no_focus_window =
124 XCreateSimpleWindow(_blackbox->XDisplay(), screen_info.rootWindow(),
125 screen_info.width(), screen_info.height(), 1, 1,
126 0, 0l, 0l);
127 XSelectInput(_blackbox->XDisplay(), no_focus_window, NoEventMask);
128 XMapWindow(_blackbox->XDisplay(), no_focus_window);
130 _iconmenu =
131 new Iconmenu(*_blackbox, screen_info.screenNumber(), this);
132 _slitmenu =
133 new Slitmenu(*_blackbox, screen_info.screenNumber(), this);
134 _toolbarmenu =
135 new Toolbarmenu(*_blackbox, screen_info.screenNumber(), this);
136 _workspacemenu =
137 new Workspacemenu(*_blackbox, screen_info.screenNumber(), this);
139 configmenu =
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
156 _slit = 0;
158 _toolbar = 0;
159 if (_resource.toolbarOptions().enabled) {
160 _toolbar = new Toolbar(this);
161 _stackingList.insert(_toolbar);
164 InitMenu();
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();
187 Atom supported[] = {
188 ewmh.clientList(),
189 ewmh.clientListStacking(),
190 ewmh.numberOfDesktops(),
191 // _NET_DESKTOP_GEOMETRY is not supported
192 // _NET_DESKTOP_VIEWPORT is not supported
193 ewmh.currentDesktop(),
194 ewmh.desktopNames(),
195 ewmh.activeWindow(),
196 ewmh.workarea(),
197 // _NET_VIRTUAL_ROOTS is not supported
198 // _NET_SHOWING_DESKTOP is not supported
200 ewmh.closeWindow(),
201 ewmh.moveresizeWindow(),
202 // _NET_WM_MOVERESIZE is not supported
204 ewmh.wmName(),
205 ewmh.wmVisibleName(),
206 ewmh.wmIconName(),
207 ewmh.wmVisibleIconName(),
208 ewmh.wmDesktop(),
210 ewmh.wmWindowType(),
211 ewmh.wmWindowTypeDesktop(),
212 ewmh.wmWindowTypeDock(),
213 ewmh.wmWindowTypeToolbar(),
214 ewmh.wmWindowTypeMenu(),
215 ewmh.wmWindowTypeUtility(),
216 ewmh.wmWindowTypeSplash(),
217 ewmh.wmWindowTypeDialog(),
218 ewmh.wmWindowTypeNormal(),
220 ewmh.wmState(),
221 ewmh.wmStateModal(),
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(),
230 ewmh.wmStateAbove(),
231 ewmh.wmStateBelow(),
233 ewmh.wmAllowedActions(),
234 ewmh.wmActionMove(),
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(),
245 ewmh.wmStrut()
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,
264 &children, &nchild);
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)
269 continue;
271 XWMHints *wmhints = XGetWMHints(_blackbox->XDisplay(),
272 children[i]);
274 if (wmhints) {
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) {
279 children[j] = None;
280 break;
285 XFree(wmhints);
289 // manage shown windows
290 for (i = 0; i < nchild; ++i) {
291 if (children[i] == None || children[i] == no_focus_window)
292 continue;
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]);
304 XFree(children);
306 _blackbox->XUngrabServer();
308 updateClientListHint();
309 restackWindows();
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());
327 delete _rootmenu;
328 delete configmenu;
330 delete _workspacemenu;
331 delete _iconmenu;
333 delete _windowmenu;
335 delete _slit;
336 delete _toolbar;
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();
359 bt::Rect geomr =
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;
372 geom_window =
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)
380 ? style.focus.title
381 : style.focus.label;
382 geom_w += (texture.borderWidth() * 2);
383 geom_h += (texture.borderWidth() * 2);
384 geom_pixmap = bt::PixmapCache::find(screen_info.screenNumber(),
385 texture,
386 geom_w,
387 geom_h,
388 geom_pixmap);
392 void BScreen::reconfigure(void) {
393 LoadStyle();
395 updateGeomWindow();
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();
407 InitMenu();
409 configmenu->reconfigure();
410 _rootmenu->reconfigure();
411 _workspacemenu->reconfigure();
412 if (_windowmenu)
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) {
425 InitMenu();
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)
451 return;
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();
468 delete workspace;
470 _blackbox->ewmh().setNumberOfDesktops(screen_info.rootWindow(),
471 workspacesList.size());
472 updateDesktopNamesHint();
476 void BScreen::setCurrentWorkspace(unsigned int id) {
477 if (id == current_workspace)
478 return;
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
494 // Expose events
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)
500 win->hide();
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);
508 } else {
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)
523 win->show();
526 const BlackboxResource &res = _blackbox->resource();
527 if (res.focusLastWindowOnWorkspace()
528 && (!focused_window
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();
539 } else {
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);
543 if (!tmp
544 || !tmp->isVisible()
545 || (tmp->workspace() != current_workspace
546 && tmp->workspace() != bt::BSENTINEL))
547 continue;
548 if (tmp->setInputFocus())
549 break;
555 _blackbox->ewmh().setCurrentDesktop(screen_info.rootWindow(),
556 current_workspace);
558 XUnmapWindow(_blackbox->XDisplay(), empty_window);
560 _blackbox->XUngrabServer();
564 void BScreen::addWindow(Window w) {
565 manageWindow(w);
566 BlackboxWindow * const win = _blackbox->findWindow(w);
567 if (!win) return;
569 updateClientListHint();
571 // focus the new window if appropriate
572 switch (win->windowType()) {
573 case WindowTypeDesktop:
574 case WindowTypeDock:
575 // these types should not be focused when managed
576 break;
578 default:
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();
584 break;
590 void BScreen::manageWindow(Window w) {
591 XWMHints *wmhints = XGetWMHints(_blackbox->XDisplay(), w);
592 bool slit_client = (wmhints && (wmhints->flags & StateHint) &&
593 wmhints->initial_state == WithdrawnState);
594 if (wmhints) XFree(wmhints);
596 if (slit_client) {
597 if (!_slit) createSlit();
598 _slit->addClient(w);
599 return;
602 (void) new BlackboxWindow(_blackbox, w, this);
603 // verify that we have managed the window
604 BlackboxWindow *win = _blackbox->findWindow(w);
605 if (! win) return;
607 if (win->workspace() >= workspaceCount() &&
608 win->workspace() != bt::BSENTINEL)
609 win->setWorkspace(current_workspace);
611 Workspace *workspace = findWorkspace(win->workspace());
612 if (!workspace) {
613 win->setWorkspace(bt::BSENTINEL);
614 win->setWindowNumber(bt::BSENTINEL);
615 } else {
616 workspace->addWindow(win);
619 bool place_window = true;
620 if (_blackbox->startingUp()
621 || ((win->isTransient() || win->windowType() == WindowTypeDesktop
622 || (win->wmNormalHints().flags & (PPosition|USPosition)))
623 && win->clientRect().intersects(screen_info.rect())))
624 place_window = false;
625 if (place_window) placeWindow(win);
627 windowList.push_back(win);
629 // insert window at the top of the stack
630 _stackingList.insert(win);
631 if (!_blackbox->startingUp())
632 restackWindows();
634 // 'show' window in the appropriate state
635 switch (_blackbox->startingUp()
636 ? win->currentState()
637 : win->wmHints().initial_state) {
638 case IconicState:
639 win->iconify();
640 break;
642 case WithdrawnState:
643 win->hide();
644 break;
646 default:
647 win->show();
648 break;
649 } // switch
653 void BScreen::releaseWindow(BlackboxWindow *w) {
654 unmanageWindow(w);
655 updateClientListHint();
656 updateClientListStackingHint();
660 void BScreen::unmanageWindow(BlackboxWindow *win) {
661 win->restore();
663 if (win->isIconic()) {
664 _iconmenu->removeItem(win->windowNumber());
665 } else {
666 Workspace *workspace = findWorkspace(win->workspace());
667 if (workspace) {
668 workspace->menu()->removeItem(win->windowNumber());
669 if (workspace->focusedWindow() == win)
670 workspace->clearFocusedWindow();
674 if (_blackbox->running() && win->isFocused()) {
675 // pass focus to the next appropriate window
676 if (focusFallback(win)) {
677 // focus is going somewhere, but we want to avoid dangling pointers
678 _blackbox->forgetFocusedWindow();
679 } else {
680 // explicitly clear the focus
681 _blackbox->setFocusedWindow(0);
685 windowList.remove(win);
686 _stackingList.remove(win);
688 delete win;
692 bool BScreen::focusFallback(const BlackboxWindow *win) {
693 Workspace *workspace = findWorkspace(win->workspace());
694 if (!workspace)
695 workspace = findWorkspace(current_workspace);
697 if (workspace->id() != current_workspace)
698 return false;
700 BWindowGroup *group = win->findWindowGroup();
701 if (group) {
702 // focus the top-most window in the group
703 BlackboxWindowList::const_iterator git = group->windows().begin(),
704 gend = group->windows().end();
705 StackingList::iterator it = _stackingList.begin(),
706 end = _stackingList.end();
707 for (; it != end; ++it) {
708 BlackboxWindow * const tmp = dynamic_cast<BlackboxWindow *>(*it);
709 if (!tmp
710 || tmp == win
711 || std::find(git, gend, tmp) == gend
712 || !tmp->isVisible()
713 || (tmp->workspace() != current_workspace
714 && tmp->workspace() != bt::BSENTINEL))
715 continue;
716 if (tmp->setInputFocus())
717 return true;
721 const BlackboxWindow * const zero = 0;
723 if (win) {
724 if (win->isTransient()) {
725 BlackboxWindow * const tmp = win->findTransientFor();
726 if (tmp
727 && tmp->isVisible()
728 && (tmp->workspace() == current_workspace
729 || tmp->workspace() == bt::BSENTINEL)
730 && tmp->setInputFocus())
731 return true;
734 // try to focus the top-most window in the same layer as win
735 StackingList::iterator it = _stackingList.layer(win->layer()),
736 end = std::find(it, _stackingList.end(), zero);
737 assert(it != _stackingList.end() && end != _stackingList.end());
738 for (; it != end; ++it) {
739 BlackboxWindow * const tmp = dynamic_cast<BlackboxWindow *>(*it);
740 if (!tmp)
741 break;
742 if (tmp == win
743 || !tmp->isVisible()
744 || (tmp->workspace() != current_workspace
745 && tmp->workspace() != bt::BSENTINEL))
746 continue;
747 if (tmp->setInputFocus())
748 return true;
752 // focus the top-most window in the stack
753 StackingList::iterator it = _stackingList.begin(),
754 end = _stackingList.end();
755 for (; it != end; ++it) {
756 BlackboxWindow * const tmp = dynamic_cast<BlackboxWindow *>(*it);
757 if (!tmp
758 || tmp == win
759 || !tmp->isVisible()
760 || (tmp->workspace() != current_workspace
761 && tmp->workspace() != bt::BSENTINEL))
762 continue;
763 if (tmp->setInputFocus())
764 return true;
767 return false;
772 Raises all windows, preserving the existing stacking order. Each
773 window is placed at the top of the layer it currently occupies (with
774 transients above).
776 static
777 void raiseGroup(StackingList &stackingList, BWindowGroup *group) {
778 BlackboxWindowList windows = group->windows();
779 int layer = StackingList::LayerNormal;
780 for (; layer < StackingList::LayerDesktop; ++layer) {
781 StackingList::iterator
782 it, top = stackingList.layer(StackingList::Layer(layer));
783 const StackingList::iterator begin = stackingList.begin(),
784 end = stackingList.end();
786 // 'top' points to the top of the layer, we need to start from the
787 // bottom of the layer
788 it = std::find(top, end, (StackEntity *) 0);
789 assert(it != end);
791 if (!(*top)) {
792 // nothing in layer
793 break;
796 // walk up the layer, raising all windows in the group
797 for (--it; it != begin; --it) {
798 if (!(*it)) {
799 // top of layer
800 break;
803 BlackboxWindow *tmp = dynamic_cast<BlackboxWindow *>(*it);
804 if (!tmp) {
805 // entity is not a window, or window is not visible on the
806 // current_workspace
807 continue;
810 const BlackboxWindowList::iterator wend = windows.end();
811 BlackboxWindowList::iterator wit;
812 if ((wit = std::find(windows.begin(), wend, tmp)) == wend)
813 continue;
815 // found a window in this layer, raise it
816 ++it;
817 stackingList.raise(tmp);
818 // don't bother looking at this window again
819 windows.erase(wit);
826 Raise all transients, preserving the existing stacking order.
828 static
829 void raiseTransients(StackingList::iterator top,
830 StackingList &stackingList,
831 BlackboxWindowList &transients) {
832 // 'top' points to the top of the layer, we need to start from the bottom
833 StackingList::iterator begin = stackingList.begin(),
834 end = stackingList.end(),
835 it = std::find(top, end, (StackEntity *) 0);
836 assert(it != end);
837 for (--it; it != begin; --it) {
838 if (it == top) {
839 // top of layer
840 break;
842 BlackboxWindow *tmp = dynamic_cast<BlackboxWindow *>(*it);
843 if (!tmp)
844 continue;
845 BlackboxWindowList::iterator wit = transients.begin(),
846 wend = transients.end();
847 wit = std::find(wit, wend, tmp);
848 if (wit != wend) {
849 // found a transient in this layer, raise it
850 ++it;
851 stackingList.raise(tmp);
852 // don't bother looking at this window again
853 transients.erase(wit);
860 Raises the specified stacking entity. If the entity is a window,
861 all transients are also raised (preserving their stacking order).
862 If the window is part of a group, the entire group is raised (also
863 preserving the stacking order) befor raising the specified window.
865 The return value indicates which windows need to be restacked:
867 - if raiseWindow() return stackingList.end(), then nothing needs to
868 be restacked.
870 - if raiseWindow() returns an iterator for a layer boundary
871 (i.e. *raiseWindow(...) == 0) then all windows in all layers need to
872 be restacked
874 - otherwise, the return value points to the bottom most window that
875 needs to be restacked (i.e. the top of the layer down to the return
876 value need to be restacked)
878 static
879 StackingList::iterator raiseWindow(StackingList &stackingList,
880 StackEntity *entity) {
881 BlackboxWindow *win = dynamic_cast<BlackboxWindow *>(entity);
882 if (win) {
883 if (win->isFullScreen() && win->layer() != StackingList::LayerFullScreen) {
884 // move full-screen windows over all other windows when raising
885 win->changeLayer(StackingList::LayerFullScreen);
886 return stackingList.end();
890 StackingList::iterator layer = stackingList.layer(entity->layer());
891 if (*layer == entity) {
892 // already on top of the layer
893 return stackingList.end();
896 BWindowGroup *group = 0;
897 if (win) {
898 group = win->findWindowGroup();
899 if (group) {
900 // raise all windows in the group before raising 'win'
901 ::raiseGroup(stackingList, group);
902 } else if (win->isTransient()) {
903 // raise non-transient parent before raising transient
904 BlackboxWindow *tmp = win->findNonTransientParent();
905 if (tmp)
906 (void) ::raiseWindow(stackingList, tmp);
910 // raise the entity
911 StackingList::iterator top = stackingList.raise(entity),
912 end = stackingList.end();
913 assert(top != end);
914 if (win) {
915 // ... with all transients above it
916 BlackboxWindowList transients = win->buildFullTransientList();
917 if (!transients.empty())
918 raiseTransients(top, stackingList, transients);
920 // ... and group transients on top
921 if (group && !win->isGroupTransient()) {
922 const BlackboxWindow * const w = win->isTransient()
923 ? win->findNonTransientParent()
924 : win;
925 if (!w || !w->isGroupTransient()) {
926 BlackboxWindowList groupTransients = group->transients();
927 BlackboxWindowList::const_iterator wit = groupTransients.begin(),
928 wend = groupTransients.end();
929 for (; wit != wend; ++wit) {
930 BlackboxWindowList x = (*wit)->buildFullTransientList();
931 groupTransients.splice(groupTransients.end(), x);
933 if (!groupTransients.empty())
934 raiseTransients(top, stackingList, groupTransients);
938 if (!group)
939 return top;
941 StackingList::iterator it = std::find(top, end, (StackEntity *) 0);
942 assert(it != end);
943 return it;
948 Raises the stack entity as above and then updates the stacking on
949 the X server. The EWMH stacking hint is also updated.
951 void BScreen::raiseWindow(StackEntity *entity) {
952 StackingList::iterator top = ::raiseWindow(_stackingList, entity),
953 end = _stackingList.end();
954 if (top == end) {
955 // no need to raise entity
956 return;
957 } else if (!(*top)) {
958 // need to restack all windows
959 restackWindows();
960 return;
963 // find the first entity above us (if any)
964 StackEntity *above = 0;
965 StackingList::iterator layer = _stackingList.layer(entity->layer()),
966 begin = _stackingList.begin(),
967 it = layer;
968 for (--it; it != begin; --it) {
969 if (*it) {
970 above = *it;
971 break;
975 // build the stack
976 WindowStack stack;
977 if (above) {
978 // found another entity above the one we are raising
979 stack.push_back(above->windowID());
980 } else {
981 // keep everying under empty_window
982 stack.push_back(empty_window);
985 // go to the layer boundary above 'top'
986 it = top;
987 for (--it; *it && it != begin; --it)
990 // put all windows from the layer boundary to 'top' into the stack
991 for (++it; *it && it != top; ++it) {
992 assert(it != end);
993 stack.push_back((*it)->windowID());
995 stack.push_back((*top)->windowID());
997 XRestackWindows(_blackbox->XDisplay(), &stack[0], stack.size());
998 updateClientListStackingHint();
1002 static
1003 void lowerGroup(StackingList &stackingList, BWindowGroup *group) {
1004 BlackboxWindowList windows = group->windows();
1005 int layer = StackingList::LayerNormal;
1006 for (; layer < StackingList::LayerDesktop; ++layer) {
1007 const StackingList::iterator begin = stackingList.begin(),
1008 end = stackingList.end();
1009 StackingList::iterator it = stackingList.layer(StackingList::Layer(layer)),
1010 bottom = std::find(it, end, (StackEntity *) 0);
1011 // 'it' points to the top of the layer
1012 assert(bottom != end);
1014 if (!(*it)) {
1015 // nothing in layer
1016 break;
1019 // walk down the layer, lowering all windows in the group
1020 for (; it != bottom; ++it) {
1021 assert(it != end);
1022 BlackboxWindow *tmp = dynamic_cast<BlackboxWindow *>(*it);
1023 if (!tmp) {
1024 // entity is not a window
1025 continue;
1028 const BlackboxWindowList::iterator wend = windows.end();
1029 BlackboxWindowList::iterator wit;
1030 if ((wit = std::find(windows.begin(), wend, tmp)) == wend)
1031 continue;
1033 // found a window in this layer, lower it
1034 --it;
1035 (void) stackingList.lower(tmp);
1036 // don't bother looking at this window again
1037 windows.erase(wit);
1043 static void lowerTransients(StackingList::iterator it,
1044 StackingList &stackingList,
1045 BlackboxWindowList &transients) {
1046 // 'it' points to the top of the layer
1047 const StackingList::iterator end = stackingList.end(),
1048 bottom = std::find(it, end, (StackEntity *) 0);
1049 assert(bottom != end);
1050 for (; it != bottom; ++it) {
1051 assert(it != end);
1052 BlackboxWindow *tmp = dynamic_cast<BlackboxWindow *>(*it);
1053 if (!tmp)
1054 continue;
1056 const BlackboxWindowList::iterator wend = transients.end();
1057 BlackboxWindowList::iterator wit;
1058 if ((wit = std::find(transients.begin(), wend, tmp)) == wend)
1059 continue;
1061 // found a transient in this layer, lower it
1062 --it;
1063 StackingList::iterator l = stackingList.lower(tmp);
1064 // don't bother looking at this window again
1065 transients.erase(wit);
1071 Lowers the specified stacking entity. If the entity is a window,
1072 all transients are also lowered (preserving their stacking order).
1073 If the window is part of a group, the entire group is lowered (also
1074 preserving the stacking order) befor lowering the specified window.
1076 The return value indicates which windows need to be restacked:
1078 - if lowerWindow() return stackingList.end(), then nothing needs to
1079 be restacked.
1081 - if lowerWindow() returns an iterator for a layer boundary
1082 (i.e. *lowerWindow(...) == 0) then all windows in all layers need to
1083 be restacked
1085 - otherwise, the return value points to the top-most window that
1086 needs to be restacked (i.e. the return value down to the bottom of
1087 the layer need to be restacked)
1089 static
1090 StackingList::iterator lowerWindow(StackingList &stackingList,
1091 StackEntity *entity,
1092 bool ignore_group = false) {
1093 StackingList::iterator it, end = stackingList.end();
1094 BlackboxWindow *win = dynamic_cast<BlackboxWindow *>(entity);
1095 if (win) {
1096 it = end;
1097 BWindowGroup *group = win->findWindowGroup();
1098 if (!ignore_group && group) {
1099 // lower all windows in the group before lowering 'win'
1100 ::lowerGroup(stackingList, group);
1101 it = std::find(stackingList.begin(), end, (StackEntity *) 0);
1102 assert(it != end);
1105 const StackingList::iterator layer = stackingList.layer(win->layer());
1106 if (win->isTransient()) {
1107 BlackboxWindow *const tmp = win->findNonTransientParent();
1108 if (tmp && !tmp->isGroupTransient()) {
1109 // lower non-transient parent
1110 (void) ::lowerWindow(stackingList, tmp, true);
1111 if (it == end)
1112 it = layer;
1114 return it;
1117 // lower transients oinf 'win' and 'win'
1118 BlackboxWindowList transients = win->buildFullTransientList();
1119 if (!transients.empty()) {
1120 ::lowerTransients(layer, stackingList, transients);
1121 (void) stackingList.lower(win);
1122 if (it == end)
1123 it = layer;
1124 } else {
1125 if (it == end) {
1126 it = stackingList.lower(entity);
1127 assert(it != end);
1128 } else {
1129 (void) stackingList.lower(entity);
1132 } else {
1133 // lower the entity
1134 it = stackingList.lower(entity);
1135 assert(it != end);
1137 return it;
1141 void BScreen::lowerWindow(StackEntity *entity) {
1142 StackingList::iterator top = ::lowerWindow(_stackingList, entity),
1143 end = _stackingList.end();
1144 if (top == end) {
1145 // no need to lower entity
1146 return;
1147 } else if (!(*top)) {
1148 // need to restack all windows
1149 restackWindows();
1150 return;
1153 // find the entity above us (if any)
1154 StackEntity *above = 0;
1155 StackingList::iterator begin = _stackingList.begin(),
1156 it = top;
1157 if (it != begin) {
1158 for (--it; it != begin; --it) {
1159 if (*it) {
1160 above = *it;
1161 break;
1166 // build the window stack
1167 WindowStack stack;
1168 if (above) {
1169 // found another entity above the one we are lowering
1170 stack.push_back(above->windowID());
1171 } else {
1172 // keep everying under empty_window
1173 stack.push_back(empty_window);
1176 // find the layer boundary
1177 StackingList::iterator bottom = std::find(top, end, (StackEntity *) 0);
1178 assert(bottom != end);
1180 // put all windows from 'top' to the layer boundary into the stack
1181 for (it = top; it != bottom; ++it) {
1182 assert(*it && it != end);
1183 stack.push_back((*it)->windowID());
1185 stack.push_back((*top)->windowID());
1187 XRestackWindows(_blackbox->XDisplay(), &stack[0], stack.size());
1188 updateClientListStackingHint();
1192 void BScreen::restackWindows(void) {
1193 WindowStack stack;
1194 stack.push_back(empty_window);
1196 StackingList::const_iterator it, end = _stackingList.end();
1197 for (it = _stackingList.begin(); it != end; ++it) {
1198 if (*it)
1199 stack.push_back((*it)->windowID());
1202 XRestackWindows(_blackbox->XDisplay(), &stack[0], stack.size());
1203 updateClientListStackingHint();
1207 void BScreen::propagateWindowName(const BlackboxWindow * const win) {
1208 if (win->isIconic()) {
1209 _iconmenu->changeItem(win->windowNumber(),
1210 bt::ellideText(win->iconTitle(), 60,
1211 bt::toUnicode("...")));
1212 } else if (win->workspace() != bt::BSENTINEL) {
1213 Workspace *workspace = findWorkspace(win->workspace());
1214 assert(workspace != 0);
1215 workspace->menu()->changeItem(win->windowNumber(),
1216 bt::ellideText(win->title(), 60,
1217 bt::toUnicode("...")));
1220 if (_toolbar && _blackbox->focusedWindow() == win)
1221 _toolbar->redrawWindowLabel();
1225 void BScreen::nextFocus(void) {
1226 BlackboxWindow *focused = _blackbox->focusedWindow(),
1227 *next = 0;
1228 BlackboxWindowList::iterator it, end = windowList.end();
1230 if (focused && focused->workspace() == current_workspace &&
1231 focused->screen()->screen_info.screenNumber() ==
1232 screen_info.screenNumber()) {
1233 it = std::find(windowList.begin(), end, focused);
1234 assert(it != end);
1235 for (; it != end; ++it) {
1236 next = *it;
1237 if (next && next != focused && next->workspace() == current_workspace &&
1238 next->setInputFocus()) {
1239 // we found our new focus target
1240 next->setInputFocus();
1241 raiseWindow(next);
1242 break;
1244 next = 0;
1247 if (!next) {
1248 for (it = windowList.begin(); it != end; ++it) {
1249 next = *it;
1250 if (next && next->workspace() == current_workspace &&
1251 next->setInputFocus()) {
1252 // we found our new focus target
1253 raiseWindow(next);
1254 break;
1261 void BScreen::prevFocus(void) {
1262 BlackboxWindow *focused = _blackbox->focusedWindow(),
1263 *next = 0;
1264 BlackboxWindowList::reverse_iterator it, end = windowList.rend();
1266 if (focused && focused->workspace() == current_workspace &&
1267 focused->screen()->screen_info.screenNumber() ==
1268 screen_info.screenNumber()) {
1269 it = std::find(windowList.rbegin(), end, focused);
1270 assert(it != end);
1271 for (; it != end; ++it) {
1272 next = *it;
1273 if (next && next != focused && next->workspace() == current_workspace &&
1274 next->setInputFocus()) {
1275 // we found our new focus target
1276 next->setInputFocus();
1277 raiseWindow(next);
1278 break;
1280 next = 0;
1283 if (!next) {
1284 for (it = windowList.rbegin(); it != end; ++it) {
1285 next = *it;
1286 if (next && next->workspace() == current_workspace &&
1287 next->setInputFocus()) {
1288 // we found our new focus target
1289 raiseWindow(next);
1290 break;
1297 void BScreen::raiseFocus(void) {
1298 BlackboxWindow *focused = _blackbox->focusedWindow();
1299 if (! focused || focused->screen() != this)
1300 return;
1302 raiseWindow(focused);
1306 void BScreen::InitMenu(void) {
1307 if (_rootmenu) {
1308 _rootmenu->clear();
1309 } else {
1310 _rootmenu = new Rootmenu(*_blackbox, screen_info.screenNumber(), this);
1311 _rootmenu->showTitle();
1313 bool defaultMenu = True;
1315 if (_blackbox->resource().menuFilename()) {
1316 FILE *menu_file = fopen(_blackbox->resource().menuFilename(), "r");
1318 if (!menu_file) {
1319 perror(_blackbox->resource().menuFilename());
1320 } else {
1321 if (feof(menu_file)) {
1322 fprintf(stderr, "%s: menu file '%s' is empty\n",
1323 _blackbox->applicationName().c_str(),
1324 _blackbox->resource().menuFilename());
1325 } else {
1326 char line[1024], label[1024];
1327 memset(line, 0, 1024);
1328 memset(label, 0, 1024);
1330 while (fgets(line, 1024, menu_file) && ! feof(menu_file)) {
1331 if (line[0] == '#')
1332 continue;
1334 int i, key = 0, index = -1, len = strlen(line);
1336 for (i = 0; i < len; i++) {
1337 if (line[i] == '[') index = 0;
1338 else if (line[i] == ']') break;
1339 else if (line[i] != ' ')
1340 if (index++ >= 0)
1341 key += tolower(line[i]);
1344 if (key == 517) { // [begin]
1345 index = -1;
1346 for (i = index; i < len; i++) {
1347 if (line[i] == '(')
1348 index = 0;
1349 else if (line[i] == ')')
1350 break;
1351 else if (index++ >= 0) {
1352 if (line[i] == '\\' && i < len - 1) i++;
1353 label[index - 1] = line[i];
1357 if (index == -1) index = 0;
1358 label[index] = '\0';
1360 _rootmenu->setTitle(bt::toUnicode(label));
1361 defaultMenu = parseMenuFile(menu_file, _rootmenu);
1362 break;
1366 fclose(menu_file);
1370 if (defaultMenu) {
1371 _rootmenu->setTitle(bt::toUnicode("_Blackbox"));
1373 _rootmenu->insertFunction(bt::toUnicode("xterm"),
1374 BScreen::Execute, "xterm");
1375 _rootmenu->insertFunction(bt::toUnicode("Restart"),
1376 BScreen::Restart);
1377 _rootmenu->insertFunction(bt::toUnicode("Exit"),
1378 BScreen::Exit);
1379 } else {
1380 _blackbox->saveMenuFilename(_blackbox->resource().menuFilename());
1385 static
1386 size_t string_within(char begin, char end,
1387 const char *input, size_t start_at, size_t length,
1388 char *output) {
1389 bool parse = False;
1390 size_t index = 0;
1391 size_t i = start_at;
1392 for (; i < length; ++i) {
1393 if (input[i] == begin) {
1394 parse = True;
1395 } else if (input[i] == end) {
1396 break;
1397 } else if (parse) {
1398 if (input[i] == '\\' && i < length - 1) i++;
1399 output[index++] = input[i];
1403 if (parse)
1404 output[index] = '\0';
1405 else
1406 output[0] = '\0';
1408 return i;
1412 bool BScreen::parseMenuFile(FILE *file, Rootmenu *menu) {
1413 char line[1024], keyword[1024], label[1024], command[1024];
1414 bool done = False;
1416 while (! (done || feof(file))) {
1417 memset(line, 0, 1024);
1418 memset(label, 0, 1024);
1419 memset(command, 0, 1024);
1421 if (! fgets(line, 1024, file))
1422 continue;
1424 if (line[0] == '#') // comment, skip it
1425 continue;
1427 size_t line_length = strlen(line);
1428 unsigned int key = 0;
1430 // get the keyword enclosed in []'s
1431 size_t pos = string_within('[', ']', line, 0, line_length, keyword);
1433 if (keyword[0] == '\0') { // no keyword, no menu entry
1434 continue;
1435 } else {
1436 size_t len = strlen(keyword);
1437 for (size_t i = 0; i < len; ++i) {
1438 if (keyword[i] != ' ')
1439 key += tolower(keyword[i]);
1443 // get the label enclosed in ()'s
1444 pos = string_within('(', ')', line, pos, line_length, label);
1446 // get the command enclosed in {}'s
1447 pos = string_within('{', '}', line, pos, line_length, command);
1449 switch (key) {
1450 case 311: // end
1451 done = True;
1453 break;
1455 case 328: // sep
1456 menu->insertSeparator();
1457 break;
1459 case 333: // nop
1460 if (! *label)
1461 label[0] = '\0';
1462 menu->insertItem(bt::toUnicode(label));
1464 break;
1466 case 421: // exec
1467 if (! (*label && *command)) {
1468 fprintf(stderr,
1469 "%s: [exec] error, no menu label and/or command defined\n",
1470 _blackbox->applicationName().c_str());
1471 continue;
1474 menu->insertFunction(bt::toUnicode(label), BScreen::Execute, command);
1475 break;
1477 case 442: // exit
1478 if (! *label) {
1479 fprintf(stderr, "%s: [exit] error, no menu label defined\n",
1480 _blackbox->applicationName().c_str());
1481 continue;
1484 menu->insertFunction(bt::toUnicode(label), BScreen::Exit);
1485 break;
1487 case 561: { // style
1488 if (! (*label && *command)) {
1489 fprintf(stderr,
1490 "%s: [style] error, no menu label and/or filename defined\n",
1491 _blackbox->applicationName().c_str());
1492 continue;
1495 std::string style = bt::expandTilde(command);
1496 menu->insertFunction(bt::toUnicode(label),
1497 BScreen::SetStyle, style.c_str());
1498 break;
1501 case 630: // config
1502 if (! *label) {
1503 fprintf(stderr, "%s: [config] error, no label defined",
1504 _blackbox->applicationName().c_str());
1505 continue;
1508 menu->insertItem(bt::toUnicode(label), configmenu);
1509 break;
1511 case 740: { // include
1512 if (! *label) {
1513 fprintf(stderr, "%s: [include] error, no filename defined\n",
1514 _blackbox->applicationName().c_str());
1515 continue;
1518 std::string newfile = bt::expandTilde(label);
1519 FILE *submenufile = fopen(newfile.c_str(), "r");
1521 if (! submenufile) {
1522 perror(newfile.c_str());
1523 continue;
1526 struct stat buf;
1527 if (fstat(fileno(submenufile), &buf) ||
1528 ! S_ISREG(buf.st_mode)) {
1529 fprintf(stderr, "%s: [include] error: '%s' is not a regular file\n",
1530 _blackbox->applicationName().c_str(), newfile.c_str());
1531 break;
1534 if (! feof(submenufile)) {
1535 if (! parseMenuFile(submenufile, menu))
1536 _blackbox->saveMenuFilename(newfile);
1538 fclose(submenufile);
1542 break;
1544 case 767: { // submenu
1545 if (! *label) {
1546 fprintf(stderr, "%s: [submenu] error, no menu label defined\n",
1547 _blackbox->applicationName().c_str());
1548 continue;
1551 Rootmenu *submenu =
1552 new Rootmenu(*_blackbox, screen_info.screenNumber(), this);
1553 submenu->showTitle();
1555 if (*command)
1556 submenu->setTitle(bt::toUnicode(command));
1557 else
1558 submenu->setTitle(bt::toUnicode(label));
1560 parseMenuFile(file, submenu);
1561 menu->insertItem(bt::toUnicode(label), submenu);
1564 break;
1566 case 773: { // restart
1567 if (! *label) {
1568 fprintf(stderr, "%s: [restart] error, no menu label defined\n",
1569 _blackbox->applicationName().c_str());
1570 continue;
1573 if (*command) {
1574 menu->insertFunction(bt::toUnicode(label),
1575 BScreen::RestartOther, command);
1576 } else {
1577 menu->insertFunction(bt::toUnicode(label), BScreen::Restart);
1579 break;
1582 case 845: { // reconfig
1583 if (! *label) {
1584 fprintf(stderr, "%s: [reconfig] error, no menu label defined\n",
1585 _blackbox->applicationName().c_str());
1586 continue;
1589 menu->insertFunction(bt::toUnicode(label), BScreen::Reconfigure);
1590 break;
1593 case 995: // stylesdir
1594 case 1113: { // stylesmenu
1595 bool newmenu = ((key == 1113) ? True : False);
1597 if (! *label || (! *command && newmenu)) {
1598 fprintf(stderr,
1599 "%s: [stylesdir/stylesmenu] error, no directory defined\n",
1600 _blackbox->applicationName().c_str());
1601 continue;
1604 char *directory = ((newmenu) ? command : label);
1606 std::string stylesdir = bt::expandTilde(directory);
1608 struct stat statbuf;
1610 if (stat(stylesdir.c_str(), &statbuf) == -1) {
1611 fprintf(stderr,
1612 "%s: [stylesdir/stylesmenu] error, '%s' does not exist\n",
1613 _blackbox->applicationName().c_str(), stylesdir.c_str());
1614 continue;
1616 if (! S_ISDIR(statbuf.st_mode)) {
1617 fprintf(stderr,
1618 "%s: [stylesdir/stylesmenu] error, '%s' is not a directory\n",
1619 _blackbox->applicationName().c_str(), stylesdir.c_str());
1620 continue;
1623 Rootmenu *stylesmenu;
1625 if (newmenu) {
1626 stylesmenu =
1627 new Rootmenu(*_blackbox, screen_info.screenNumber(),this);
1628 stylesmenu->showTitle();
1629 } else {
1630 stylesmenu = menu;
1633 DIR *d = opendir(stylesdir.c_str());
1634 struct dirent *p;
1635 std::vector<std::string> ls;
1637 while((p = readdir(d)))
1638 ls.push_back(p->d_name);
1640 closedir(d);
1642 std::sort(ls.begin(), ls.end());
1644 std::vector<std::string>::iterator it = ls.begin(),
1645 end = ls.end();
1646 for (; it != end; ++it) {
1647 std::string& fname = *it;
1649 if (fname[0] == '.' || fname[fname.size()-1] == '~')
1650 continue;
1652 std::string style = stylesdir;
1653 style += '/';
1654 style += fname;
1656 if (! stat(style.c_str(), &statbuf) && S_ISREG(statbuf.st_mode)) {
1657 // convert 'This_Long_Name' to 'This Long Name'
1658 std::replace(fname.begin(), fname.end(), '_', ' ');
1660 stylesmenu->insertFunction(bt::toUnicode(fname),
1661 BScreen::SetStyle, style);
1665 if (newmenu) {
1666 stylesmenu->setTitle(bt::toUnicode(label));
1667 menu->insertItem(bt::toUnicode(label), stylesmenu);
1670 _blackbox->saveMenuFilename(stylesdir);
1672 break;
1674 case 1090: { // workspaces
1675 if (! *label) {
1676 fprintf(stderr, "%s: [workspaces] error, no menu label defined\n",
1677 _blackbox->applicationName().c_str());
1678 continue;
1681 menu->insertItem(bt::toUnicode(label), _workspacemenu);
1682 break;
1684 } // switch
1687 return (menu->count() == 0);
1691 void BScreen::shutdown(void) {
1692 XSelectInput(_blackbox->XDisplay(), screen_info.rootWindow(),
1693 NoEventMask);
1694 XSync(_blackbox->XDisplay(), False);
1696 // unmanage all windows, but keep them in the current stacking order
1697 WindowStack stack;
1698 StackingList::const_iterator it, end = _stackingList.end();
1699 for (it = _stackingList.begin(); it != end; ++it) {
1700 if (!(*it))
1701 continue;
1702 const BlackboxWindow * const win = dynamic_cast<BlackboxWindow *>(*it);
1703 if (!win)
1704 continue;
1705 stack.push_back(win->clientWindow());
1708 while(! windowList.empty())
1709 unmanageWindow(windowList.back());
1711 if (!stack.empty())
1712 XRestackWindows(_blackbox->XDisplay(), &stack[0], stack.size());
1714 if (_slit)
1715 _slit->shutdown();
1719 void BScreen::showGeometry(GeometryType type, const bt::Rect &rect) {
1720 if (! geom_visible) {
1721 XMoveResizeWindow(_blackbox->XDisplay(), geom_window,
1722 (screen_info.width() - geom_w) / 2,
1723 (screen_info.height() - geom_h) / 2, geom_w, geom_h);
1724 XMapWindow(_blackbox->XDisplay(), geom_window);
1725 XRaiseWindow(_blackbox->XDisplay(), geom_window);
1727 geom_visible = True;
1730 char label[80];
1731 switch (type) {
1732 case Position:
1733 sprintf(label, "X:%4d Y:%4d", rect.x(), rect.y());
1734 break;
1735 case Size:
1736 sprintf(label, "W:%4u H:%4u", rect.width(), rect.height());
1737 break;
1738 default:
1739 assert(0);
1742 const WindowStyle &style = _resource.windowStyle();
1743 const bt::Texture &texture =
1744 (style.focus.label.texture() == bt::Texture::Parent_Relative)
1745 ? style.focus.title
1746 : style.focus.label;
1747 const bt::Rect u(0, 0, geom_w, geom_h);
1748 const bt::Rect t(style.label_margin,
1749 style.label_margin,
1750 geom_w - (style.label_margin * 2),
1751 geom_h - (style.label_margin * 2));
1752 const bt::Pen pen(screen_info.screenNumber(), style.focus.text);
1753 bt::drawTexture(screen_info.screenNumber(), texture, geom_window,
1754 u, u, geom_pixmap);
1755 bt::drawText(style.font, pen, geom_window, t, style.alignment,
1756 bt::toUnicode(label));
1760 void BScreen::hideGeometry(void) {
1761 if (geom_visible) {
1762 XUnmapWindow(_blackbox->XDisplay(), geom_window);
1763 geom_visible = False;
1768 void BScreen::addStrut(bt::EWMH::Strut *strut) {
1769 strutList.push_back(strut);
1770 updateAvailableArea();
1774 void BScreen::removeStrut(bt::EWMH::Strut *strut) {
1775 strutList.remove(strut);
1776 updateAvailableArea();
1780 void BScreen::updateStrut(void) {
1781 updateAvailableArea();
1785 const bt::Rect& BScreen::availableArea(void) {
1786 if (_blackbox->resource().fullMaximization())
1787 return screen_info.rect(); // return the full screen
1788 return usableArea;
1792 void BScreen::updateAvailableArea(void) {
1793 bt::Rect new_area;
1795 /* these values represent offsets from the screen edge
1796 * we look for the biggest offset on each edge and then apply them
1797 * all at once
1798 * do not be confused by the similarity to the names of Rect's members
1800 bt::EWMH::Strut current;
1802 StrutList::const_iterator sit = strutList.begin(), send = strutList.end();
1804 for(; sit != send; ++sit) {
1805 const bt::EWMH::Strut* const strut = *sit;
1806 if (strut->left > current.left)
1807 current.left = strut->left;
1808 if (strut->top > current.top)
1809 current.top = strut->top;
1810 if (strut->right > current.right)
1811 current.right = strut->right;
1812 if (strut->bottom > current.bottom)
1813 current.bottom = strut->bottom;
1816 new_area.setPos(current.left, current.top);
1817 new_area.setSize(screen_info.width() - (current.left + current.right),
1818 screen_info.height() - (current.top + current.bottom));
1820 if (new_area != usableArea) {
1821 usableArea = new_area;
1822 BlackboxWindowList::iterator wit = windowList.begin(),
1823 wend = windowList.end();
1824 for (; wit != wend; ++wit)
1825 if ((*wit)->isMaximized()) (*wit)->remaximize();
1827 updateWorkareaHint();
1832 Workspace* BScreen::findWorkspace(unsigned int index) const {
1833 if (index == bt::BSENTINEL)
1834 return 0;
1835 assert(index < workspacesList.size());
1836 return workspacesList[index];
1840 void BScreen::clientMessageEvent(const XClientMessageEvent * const event) {
1841 if (event->format != 32) return;
1843 if (event->message_type == _blackbox->ewmh().numberOfDesktops()) {
1844 unsigned int number = event->data.l[0];
1845 if (number > workspaceCount()) {
1846 for (; number != workspaceCount(); --number)
1847 addWorkspace();
1848 } else if (number < workspaceCount()) {
1849 for (; number != workspaceCount(); ++number)
1850 removeLastWorkspace();
1852 } else if (event->message_type == _blackbox->ewmh().desktopNames()) {
1853 readDesktopNames();
1854 } else if (event->message_type == _blackbox->ewmh().currentDesktop()) {
1855 const unsigned int workspace = event->data.l[0];
1856 if (workspace < workspaceCount() && workspace != current_workspace)
1857 setCurrentWorkspace(workspace);
1862 void BScreen::buttonPressEvent(const XButtonEvent * const event) {
1863 if (event->button == 1) {
1865 set this screen active. keygrabs and input focus will stay on
1866 this screen until the user focuses a window on another screen or
1867 makes another screen active.
1869 _blackbox->setActiveScreen(this);
1870 } else if (event->button == 2) {
1871 _workspacemenu->popup(event->x_root, event->y_root);
1872 } else if (event->button == 3) {
1873 _blackbox->checkMenu();
1874 _rootmenu->popup(event->x_root, event->y_root);
1875 } else if (event->button == 4) {
1876 setCurrentWorkspace(current_workspace < workspacesList.size() - 1
1877 ? current_workspace + 1
1878 : 0);
1879 } else if (event->button == 5) {
1880 setCurrentWorkspace(current_workspace > 0
1881 ? current_workspace - 1
1882 : workspacesList.size() - 1);
1887 void BScreen::propertyNotifyEvent(const XPropertyEvent * const event) {
1888 if (event->atom == _blackbox->ewmh().activeWindow() && _toolbar)
1889 _toolbar->redrawWindowLabel();
1893 void BScreen::unmapNotifyEvent(const XUnmapEvent * const event) {
1894 // handle synthetic unmap events according to ICCCM section 4.1.4
1895 BlackboxWindow *win = _blackbox->findWindow(event->window);
1896 if (win && event->event == screen_info.rootWindow()
1897 && !event->from_configure)
1898 win->unmapNotifyEvent(event);
1902 void BScreen::toggleFocusModel(FocusModel model) {
1903 std::for_each(windowList.begin(), windowList.end(),
1904 std::mem_fun(&BlackboxWindow::ungrabButtons));
1906 _blackbox->resource().setFocusModel(model);
1908 std::for_each(windowList.begin(), windowList.end(),
1909 std::mem_fun(&BlackboxWindow::grabButtons));
1913 void BScreen::updateWorkareaHint(void) const {
1914 unsigned long *workarea, *tmp;
1916 tmp = workarea = new unsigned long[workspaceCount() * 4];
1918 for (unsigned int i = 0; i < workspaceCount(); ++i) {
1919 tmp[0] = usableArea.x();
1920 tmp[1] = usableArea.y();
1921 tmp[2] = usableArea.width();
1922 tmp[3] = usableArea.height();
1923 tmp += 4;
1926 _blackbox->ewmh().setWorkarea(screen_info.rootWindow(),
1927 workarea, workspaceCount());
1929 delete [] workarea;
1933 void BScreen::updateDesktopNamesHint(void) const {
1934 std::vector<bt::ustring> names(workspacesList.size());
1935 WorkspaceList::const_iterator it = workspacesList.begin();
1936 const WorkspaceList::const_iterator end = workspacesList.end();
1937 for (; it != end; ++it)
1938 names.push_back((*it)->name());
1939 _blackbox->ewmh().setDesktopNames(screen_info.rootWindow(), names);
1943 void BScreen::updateClientListHint(void) const {
1944 if (windowList.empty()) {
1945 _blackbox->ewmh().removeProperty(screen_info.rootWindow(),
1946 _blackbox->ewmh().clientList());
1947 return;
1950 bt::EWMH::WindowList clientList(windowList.size());
1952 std::transform(windowList.begin(), windowList.end(), clientList.begin(),
1953 std::mem_fun(&BlackboxWindow::clientWindow));
1955 _blackbox->ewmh().setClientList(screen_info.rootWindow(), clientList);
1959 void BScreen::updateClientListStackingHint(void) const {
1960 bt::EWMH::WindowList stack;
1962 // we store windows in top-to-bottom order, but the EWMH wants
1963 // bottom-to-top...
1964 StackingList::const_reverse_iterator it = _stackingList.rbegin(),
1965 end = _stackingList.rend();
1966 for (; it != end; ++it) {
1967 const BlackboxWindow * const win = dynamic_cast<BlackboxWindow *>(*it);
1968 if (win) stack.push_back(win->clientWindow());
1971 if (stack.empty()) {
1972 _blackbox->ewmh().removeProperty(screen_info.rootWindow(),
1973 _blackbox->ewmh().clientListStacking());
1974 return;
1977 _blackbox->ewmh().setClientListStacking(screen_info.rootWindow(), stack);
1981 void BScreen::readDesktopNames(void) {
1982 std::vector<bt::ustring> names;
1983 if(! _blackbox->ewmh().readDesktopNames(screen_info.rootWindow(), names))
1984 return;
1986 std::vector<bt::ustring>::const_iterator it = names.begin();
1987 const std::vector<bt::ustring>::const_iterator end = names.end();
1988 WorkspaceList::iterator wit = workspacesList.begin();
1989 const WorkspaceList::iterator wend = workspacesList.end();
1991 for (; wit != wend && it != end; ++wit, ++it) {
1992 if ((*wit)->name() != *it)
1993 (*wit)->setName(*it);
1996 if (names.size() < workspacesList.size())
1997 updateDesktopNamesHint();
2001 BlackboxWindow *BScreen::window(unsigned int workspace, unsigned int id) {
2002 StackingList::const_iterator it = _stackingList.begin(),
2003 end = _stackingList.end();
2004 for (; it != end; ++it) {
2005 BlackboxWindow * const win = dynamic_cast<BlackboxWindow *>(*it);
2006 if (win && win->workspace() == workspace && win->windowNumber() == id)
2007 return win;
2009 assert(false); // should not happen
2010 return 0;
2014 void BScreen::placeWindow(BlackboxWindow *win) {
2015 const bt::Rect &avail = availableArea();
2016 bt::Rect new_win(avail.x(), avail.y(),
2017 win->frameRect().width(), win->frameRect().height());
2018 bool placed = False;
2020 BlackboxResource &res = _blackbox->resource();
2021 switch (res.windowPlacementPolicy()) {
2022 case RowSmartPlacement:
2023 case ColSmartPlacement:
2024 placed = smartPlacement(win->workspace(), new_win, avail);
2025 break;
2026 default:
2027 break; // handled below
2028 } // switch
2030 if (placed == False) {
2031 cascadePlacement(new_win, avail);
2032 cascade_x += _resource.windowStyle().title_height;
2033 cascade_y += _resource.windowStyle().title_height;
2036 if (new_win.right() > avail.right())
2037 new_win.setX(avail.left());
2038 if (new_win.bottom() > avail.bottom())
2039 new_win.setY(avail.top());
2041 win->configure(new_win.x(), new_win.y(), new_win.width(), new_win.height());
2045 bool BScreen::cascadePlacement(bt::Rect &win,
2046 const bt::Rect &avail) {
2047 if (cascade_x > (avail.width() / 2) ||
2048 cascade_y > (avail.height() / 2))
2049 cascade_x = cascade_y = 32;
2051 if (cascade_x == 32) {
2052 cascade_x += avail.x();
2053 cascade_y += avail.y();
2056 win.setPos(cascade_x, cascade_y);
2058 if (win.right() > avail.right() ||
2059 win.bottom() > avail.bottom()) {
2060 cascade_x = cascade_y = 32;
2062 cascade_x += avail.x();
2063 cascade_y += avail.y();
2065 win.setPos(cascade_x, cascade_y);
2068 return True;
2072 bool BScreen::smartPlacement(unsigned int workspace, bt::Rect& rect,
2073 const bt::Rect& avail) {
2074 // constants
2075 const BlackboxResource &res = _blackbox->resource();
2076 const bool row_placement =
2077 (res.windowPlacementPolicy() == RowSmartPlacement);
2078 const bool leftright =
2079 (res.rowPlacementDirection() == LeftRight);
2080 const bool topbottom =
2081 (res.colPlacementDirection() == TopBottom);
2082 const bool ignore_shaded = res.placementIgnoresShaded();
2084 const int left_border = leftright ? 0 : -2;
2085 const int top_border = topbottom ? 0 : -2;
2086 const int right_border = leftright ? 2 : 0;
2087 const int bottom_border = topbottom ? 2 : 0;
2089 StackingList::const_iterator w_it, w_end;
2092 build a sorted vector of x and y grid boundaries
2094 note: we use one vector to reduce the number of allocations
2095 std::vector must do.. we allocate as much memory as we would need
2096 in the worst case scenario and work with that
2098 std::vector<int> coords(_stackingList.size() * 4 + 4);
2099 std::vector<int>::iterator
2100 x_begin = coords.begin(),
2101 x_end = x_begin,
2102 y_begin = coords.begin() + _stackingList.size() * 2 + 2,
2103 y_end = y_begin;
2106 std::vector<int>::iterator x_it = x_begin, y_it = y_begin;
2108 *x_it++ = avail.left();
2109 *x_it++ = avail.right();
2110 x_end += 2;
2112 *y_it++ = avail.top();
2113 *y_it++ = avail.bottom();
2114 y_end += 2;
2117 for (w_it = _stackingList.begin(), w_end = _stackingList.end();
2118 w_it != w_end; ++w_it) {
2119 const BlackboxWindow * const win =
2120 dynamic_cast<const BlackboxWindow *>(*w_it);
2121 if (!win) continue;
2123 if (win->windowType() == WindowTypeDesktop)
2124 continue;
2125 if (win->isIconic())
2126 continue;
2127 if (win->workspace() != bt::BSENTINEL && win->workspace() != workspace)
2128 continue;
2129 if (ignore_shaded && win->isShaded())
2130 continue;
2132 *x_it++ = std::max(win->frameRect().left() + left_border,
2133 avail.left());
2134 *x_it++ = std::min(win->frameRect().right() + right_border,
2135 avail.right());
2136 x_end += 2;
2138 *y_it++ = std::max(win->frameRect().top() + top_border,
2139 avail.top());
2140 *y_it++ = std::min(win->frameRect().bottom() + bottom_border,
2141 avail.bottom());
2142 y_end += 2;
2145 assert(x_end <= y_begin);
2148 std::sort(x_begin, x_end);
2149 x_end = std::unique(x_begin, x_end);
2151 std::sort(y_begin, y_end);
2152 y_end = std::unique(y_begin, y_end);
2154 // build a distribution grid
2155 unsigned int gw = x_end - x_begin - 1,
2156 gh = y_end - y_begin - 1;
2157 std::vector<bool> used_grid(gw * gh);
2158 std::fill_n(used_grid.begin(), used_grid.size(), false);
2160 for (w_it = _stackingList.begin(), w_end = _stackingList.end();
2161 w_it != w_end; ++w_it) {
2162 const BlackboxWindow * const win =
2163 dynamic_cast<const BlackboxWindow *>(*w_it);
2164 if (!win) continue;
2166 if (win->windowType() == WindowTypeDesktop)
2167 continue;
2168 if (win->isIconic())
2169 continue;
2170 if (win->workspace() != bt::BSENTINEL && win->workspace() != workspace)
2171 continue;
2172 if (ignore_shaded && win->isShaded())
2173 continue;
2175 const int w_left =
2176 std::max(win->frameRect().left() + left_border,
2177 avail.left());
2178 const int w_top =
2179 std::max(win->frameRect().top() + top_border,
2180 avail.top());
2181 const int w_right =
2182 std::min(win->frameRect().right() + right_border,
2183 avail.right());
2184 const int w_bottom =
2185 std::min(win->frameRect().bottom() + bottom_border,
2186 avail.bottom());
2188 // which areas of the grid are used by this window?
2189 std::vector<int>::iterator l_it = std::find(x_begin, x_end, w_left),
2190 r_it = std::find(x_begin, x_end, w_right),
2191 t_it = std::find(y_begin, y_end, w_top),
2192 b_it = std::find(y_begin, y_end, w_bottom);
2193 assert(l_it != x_end &&
2194 r_it != x_end &&
2195 t_it != y_end &&
2196 b_it != y_end);
2198 const unsigned int left = l_it - x_begin,
2199 right = r_it - x_begin,
2200 top = t_it - y_begin,
2201 bottom = b_it - y_begin;
2203 for (unsigned int gy = top; gy < bottom; ++gy)
2204 for (unsigned int gx = left; gx < right; ++gx)
2205 used_grid[(gy * gw) + gx] = true;
2209 Attempt to fit the window into any of the empty areas in the grid.
2210 The exact order is dependent upon the users configuration (as
2211 shown below).
2213 row placement:
2214 - outer -> vertical axis
2215 - inner -> horizontal axis
2217 col placement:
2218 - outer -> horizontal axis
2219 - inner -> vertical axis
2222 int gx, gy;
2223 int &outer = row_placement ? gy : gx;
2224 int &inner = row_placement ? gx : gy;
2225 const int outer_delta = row_placement
2226 ? (topbottom ? 1 : -1)
2227 : (leftright ? 1 : -1);
2228 const int inner_delta = row_placement
2229 ? (leftright ? 1 : -1)
2230 : (topbottom ? 1 : -1);
2231 const int outer_begin = row_placement
2232 ? (topbottom ? 0 : static_cast<int>(gh) - 1)
2233 : (leftright ? 0 : static_cast<int>(gw) - 1);
2234 const int outer_end = row_placement
2235 ? (topbottom ? static_cast<int>(gh) : -1)
2236 : (leftright ? static_cast<int>(gw) : -1);
2237 const int inner_begin = row_placement
2238 ? (leftright ? 0 : static_cast<int>(gw) - 1)
2239 : (topbottom ? 0 : static_cast<int>(gh) - 1);
2240 const int inner_end = row_placement
2241 ? (leftright ? static_cast<int>(gw) : -1)
2242 : (topbottom ? static_cast<int>(gh) : -1);
2244 bt::Rect where;
2245 bool fit = false;
2246 for (outer = outer_begin; ! fit && outer != outer_end;
2247 outer += outer_delta) {
2248 for (inner = inner_begin; ! fit && inner != inner_end;
2249 inner += inner_delta) {
2250 // see if the window fits in a single unused area
2251 if (used_grid[(gy * gw) + gx]) continue;
2253 where.setCoords(*(x_begin + gx), *(y_begin + gy),
2254 *(x_begin + gx + 1), *(y_begin + gy + 1));
2256 if (where.width() >= rect.width() &&
2257 where.height() >= rect.height()) {
2258 fit = true;
2259 break;
2263 see if we neighboring spaces are unused
2265 TODO: we should grid fit in the same direction as above,
2266 instead of always right->left and top->bottom
2268 int gx2 = gx, gy2 = gy;
2270 if (rect.width() > where.width()) {
2271 for (gx2 = gx+1; gx2 < static_cast<int>(gw); ++gx2) {
2272 if (used_grid[(gy * gw) + gx2]) {
2273 --gx2;
2274 break;
2277 where.setCoords(*(x_begin + gx), *(y_begin + gy),
2278 *(x_begin + gx2 + 1), *(y_begin + gy2 + 1));
2280 if (where.width() >= rect.width()) break;
2283 if (gx2 >= static_cast<int>(gw)) --gx2;
2286 if (rect.height() > where.height()) {
2287 for (gy2 = gy; gy2 < static_cast<int>(gh); ++gy2) {
2288 if (used_grid[(gy2 * gw) + gx]) {
2289 --gy2;
2290 break;
2293 where.setCoords(*(x_begin + gx), *(y_begin + gy),
2294 *(x_begin + gx2 + 1), *(y_begin + gy2 + 1));
2296 if (where.height() >= rect.height()) break;
2299 if (gy2 >= static_cast<int>(gh)) --gy2;
2302 if (where.width() >= rect.width() &&
2303 where.height() >= rect.height()) {
2304 fit = true;
2306 // make sure all spaces are really available
2307 for (int gy3 = gy; gy3 <= gy2; ++gy3) {
2308 for (int gx3 = gx; gx3 <= gx2; ++gx3) {
2309 if (used_grid[(gy3 * gw) + gx3]) {
2310 fit = false;
2311 break;
2319 if (! fit) {
2320 const int screen_area = avail.width() * avail.height();
2321 const int window_area = rect.width() * rect.height();
2322 if (window_area > screen_area / 8) {
2323 // center windows that don't fit (except for small windows)
2324 const int x =
2325 static_cast<int>(avail.x() + (avail.width() - rect.width()) / 2);
2326 const int y =
2327 static_cast<int>(avail.y() + (avail.height() - rect.height()) / 2);
2328 rect.setPos(x, y);
2329 return true;
2331 return false;
2334 // adjust the location() based on left/right and top/bottom placement
2335 if (! leftright)
2336 where.setX(where.right() - rect.width() + 1);
2337 if (! topbottom)
2338 where.setY(where.bottom() - rect.height() + 1);
2340 rect.setPos(where.x(), where.y());
2342 return true;
2346 void BScreen::createSlit(void) {
2347 assert(_slit == 0);
2349 _slit = new Slit(this);
2350 _stackingList.insert(_slit);
2351 restackWindows();
2355 void BScreen::destroySlit(void) {
2356 assert(_slit != 0);
2358 _stackingList.remove(_slit);
2359 delete _slit;
2360 _slit = 0;
2364 void BScreen::createToolbar(void) {
2365 assert(_toolbar == 0);
2367 _toolbar = new Toolbar(this);
2368 _stackingList.insert(_toolbar);
2369 restackWindows();
2373 void BScreen::destroyToolbar(void) {
2374 assert(_toolbar != 0);
2376 _stackingList.remove(_toolbar);
2377 delete _toolbar;
2378 _toolbar = 0;
2382 Windowmenu *BScreen::windowmenu(BlackboxWindow *win) {
2383 if (!_windowmenu)
2384 _windowmenu = new Windowmenu(*_blackbox, screen_info.screenNumber());
2385 _windowmenu->setWindow(win);
2386 return _windowmenu;
2390 void BScreen::addIcon(BlackboxWindow *win) {
2391 if (win->workspace() != bt::BSENTINEL) {
2392 Workspace *workspace = findWorkspace(win->workspace());
2393 assert(workspace != 0);
2394 workspace->removeWindow(win);
2397 if (win->isTransient()) {
2398 BlackboxWindow * const tmp = win->findNonTransientParent();
2399 if (tmp) {
2400 win->setWindowNumber(bt::BSENTINEL);
2401 return;
2405 const bt::ustring s =
2406 bt::ellideText(win->iconTitle(), 60, bt::toUnicode("..."));
2407 int id = _iconmenu->insertItem(s);
2408 _blackbox->ewmh().setWMVisibleIconName(win->clientWindow(), s);
2409 win->setWindowNumber(id);
2413 void BScreen::removeIcon(BlackboxWindow *win) {
2414 if (win->windowNumber() != bt::BSENTINEL) {
2415 _iconmenu->removeItem(win->windowNumber());
2416 _blackbox->ewmh().removeProperty(win->clientWindow(),
2417 _blackbox->ewmh().wmVisibleIconName());
2420 Workspace *workspace = findWorkspace(current_workspace);
2421 assert(workspace != 0);
2422 workspace->addWindow(win);
2426 BlackboxWindow *BScreen::icon(unsigned int id) {
2427 StackingList::const_iterator it = _stackingList.begin(),
2428 end = _stackingList.end();
2429 for (; it != end; ++it) {
2430 BlackboxWindow * const win = dynamic_cast<BlackboxWindow *>(*it);
2431 if (win && win->isIconic() && win->windowNumber() == id)
2432 return win;
2434 assert(false); // should not happen
2435 return 0;