version bump
[blackbox.git] / src / Screen.cc
blob838d16815ec26cdf4f5ce5dce63536065f94f9ae
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 Workspace *workspace = findWorkspace(current_workspace);
529 assert(workspace != 0);
531 if (workspace->focusedWindow()) {
532 // focus the window that last had focus
533 workspace->focusedWindow()->setInputFocus();
534 } else {
535 // focus the top-most window in the stack
536 for (it = _stackingList.begin(); it != end; ++it) {
537 BlackboxWindow * const tmp = dynamic_cast<BlackboxWindow *>(*it);
538 if (!tmp
539 || !tmp->isVisible()
540 || (tmp->workspace() != current_workspace
541 && tmp->workspace() != bt::BSENTINEL))
542 continue;
543 if (tmp->setInputFocus())
544 break;
550 _blackbox->ewmh().setCurrentDesktop(screen_info.rootWindow(),
551 current_workspace);
553 XUnmapWindow(_blackbox->XDisplay(), empty_window);
555 _blackbox->XUngrabServer();
559 void BScreen::nextWorkspace(void) {
560 if (currentWorkspace() < (workspaceCount() - 1))
561 setCurrentWorkspace(currentWorkspace() + 1);
562 else
563 setCurrentWorkspace(0);
567 void BScreen::prevWorkspace(void) {
568 if (currentWorkspace() > 0)
569 setCurrentWorkspace(currentWorkspace() - 1);
570 else
571 setCurrentWorkspace(workspaceCount() - 1);
575 void BScreen::addWindow(Window w) {
576 manageWindow(w);
577 BlackboxWindow * const win = _blackbox->findWindow(w);
578 if (!win) return;
580 updateClientListHint();
582 // focus the new window if appropriate
583 switch (win->windowType()) {
584 case WindowTypeDesktop:
585 case WindowTypeDock:
586 // these types should not be focused when managed
587 break;
589 default:
590 if (!_blackbox->startingUp() &&
591 (!_blackbox->activeScreen() || _blackbox->activeScreen() == this) &&
592 (win->isTransient() || _blackbox->resource().focusNewWindows())) {
593 XSync(_blackbox->XDisplay(), False); // make sure the frame is mapped..
594 win->setInputFocus();
595 break;
601 static StackingList::iterator raiseWindow(StackingList &stackingList,
602 StackEntity *entity);
604 void BScreen::manageWindow(Window w) {
605 XWMHints *wmhints = XGetWMHints(_blackbox->XDisplay(), w);
606 bool slit_client = (wmhints && (wmhints->flags & StateHint) &&
607 wmhints->initial_state == WithdrawnState);
608 if (wmhints) XFree(wmhints);
610 if (slit_client) {
611 if (!_slit) createSlit();
612 _slit->addClient(w);
613 return;
616 (void) new BlackboxWindow(_blackbox, w, this);
617 // verify that we have managed the window
618 BlackboxWindow *win = _blackbox->findWindow(w);
619 if (! win) return;
621 if (win->workspace() >= workspaceCount() &&
622 win->workspace() != bt::BSENTINEL)
623 win->setWorkspace(current_workspace);
625 Workspace *workspace = findWorkspace(win->workspace());
626 if (!workspace) {
627 win->setWorkspace(bt::BSENTINEL);
628 win->setWindowNumber(bt::BSENTINEL);
629 } else {
630 workspace->addWindow(win);
633 if (!_blackbox->startingUp())
634 placeWindow(win);
636 windowList.push_back(win);
638 // insert window at the top of the stack
639 (void) _stackingList.insert(win);
640 (void) ::raiseWindow(_stackingList, win);
641 if (!_blackbox->startingUp())
642 restackWindows();
644 // 'show' window in the appropriate state
645 switch (_blackbox->startingUp()
646 ? win->currentState()
647 : win->wmHints().initial_state) {
648 case IconicState:
649 win->iconify();
650 break;
652 case WithdrawnState:
653 win->hide();
654 break;
656 default:
657 win->show();
658 break;
659 } // switch
663 void BScreen::releaseWindow(BlackboxWindow *w) {
664 unmanageWindow(w);
665 updateClientListHint();
666 updateClientListStackingHint();
670 void BScreen::unmanageWindow(BlackboxWindow *win) {
671 win->restore();
673 if (win->isIconic()) {
674 _iconmenu->removeItem(win->windowNumber());
675 } else {
676 Workspace *workspace = findWorkspace(win->workspace());
677 if (workspace) {
678 workspace->menu()->removeItem(win->windowNumber());
679 if (workspace->focusedWindow() == win)
680 workspace->clearFocusedWindow();
684 if (_blackbox->running() && win->isFocused()) {
685 // pass focus to the next appropriate window
686 if (focusFallback(win)) {
687 // focus is going somewhere, but we want to avoid dangling pointers
688 _blackbox->forgetFocusedWindow();
689 } else {
690 // explicitly clear the focus
691 _blackbox->setFocusedWindow(0);
695 windowList.remove(win);
696 _stackingList.remove(win);
698 if (_windowmenu && _windowmenu->window() == win)
699 _windowmenu->hide();
701 delete win;
705 bool BScreen::focusFallback(const BlackboxWindow *win) {
706 Workspace *workspace = findWorkspace(win->workspace());
707 if (!workspace)
708 workspace = findWorkspace(current_workspace);
710 if (workspace->id() != current_workspace)
711 return false;
713 BWindowGroup *group = win->findWindowGroup();
714 if (group) {
715 // focus the top-most window in the group
716 BlackboxWindowList::const_iterator git = group->windows().begin(),
717 gend = group->windows().end();
718 StackingList::iterator it = _stackingList.begin(),
719 end = _stackingList.end();
720 for (; it != end; ++it) {
721 BlackboxWindow * const tmp = dynamic_cast<BlackboxWindow *>(*it);
722 if (!tmp
723 || tmp == win
724 || std::find(git, gend, tmp) == gend
725 || !tmp->isVisible()
726 || (tmp->workspace() != current_workspace
727 && tmp->workspace() != bt::BSENTINEL))
728 continue;
729 if (tmp->setInputFocus())
730 return true;
734 const BlackboxWindow * const zero = 0;
736 if (win) {
737 if (win->isTransient()) {
738 BlackboxWindow * const tmp = win->findTransientFor();
739 if (tmp
740 && tmp->isVisible()
741 && (tmp->workspace() == current_workspace
742 || tmp->workspace() == bt::BSENTINEL)
743 && tmp->setInputFocus())
744 return true;
747 // try to focus the top-most window in the same layer as win
748 StackingList::iterator it = _stackingList.layer(win->layer()),
749 end = std::find(it, _stackingList.end(), zero);
750 assert(it != _stackingList.end() && end != _stackingList.end());
751 for (; it != end; ++it) {
752 BlackboxWindow * const tmp = dynamic_cast<BlackboxWindow *>(*it);
753 if (!tmp)
754 break;
755 if (tmp == win
756 || !tmp->isVisible()
757 || (tmp->workspace() != current_workspace
758 && tmp->workspace() != bt::BSENTINEL))
759 continue;
760 if (tmp->setInputFocus())
761 return true;
765 // focus the top-most window in the stack
766 StackingList::iterator it = _stackingList.begin(),
767 end = _stackingList.end();
768 for (; it != end; ++it) {
769 BlackboxWindow * const tmp = dynamic_cast<BlackboxWindow *>(*it);
770 if (!tmp
771 || tmp == win
772 || !tmp->isVisible()
773 || (tmp->workspace() != current_workspace
774 && tmp->workspace() != bt::BSENTINEL))
775 continue;
776 if (tmp->setInputFocus())
777 return true;
780 return false;
785 Raises all windows, preserving the existing stacking order. Each
786 window is placed at the top of the layer it currently occupies (with
787 transients above).
789 static
790 void raiseGroup(StackingList &stackingList, BWindowGroup *group) {
791 BlackboxWindowList windows = group->windows();
792 int layer = StackingList::LayerNormal;
793 for (; layer < StackingList::LayerDesktop; ++layer) {
794 StackingList::iterator
795 it, top = stackingList.layer(StackingList::Layer(layer));
796 const StackingList::iterator begin = stackingList.begin(),
797 end = stackingList.end();
799 // 'top' points to the top of the layer, we need to start from the
800 // bottom of the layer
801 it = std::find(top, end, (StackEntity *) 0);
802 assert(it != end);
804 if (!(*top)) {
805 // nothing in layer
806 break;
809 // walk up the layer, raising all windows in the group
810 for (--it; it != begin; --it) {
811 if (!(*it)) {
812 // top of layer
813 break;
816 BlackboxWindow *tmp = dynamic_cast<BlackboxWindow *>(*it);
817 if (!tmp) {
818 // entity is not a window, or window is not visible on the
819 // current_workspace
820 continue;
823 const BlackboxWindowList::iterator wend = windows.end();
824 BlackboxWindowList::iterator wit;
825 if ((wit = std::find(windows.begin(), wend, tmp)) == wend)
826 continue;
828 // found a window in this layer, raise it
829 ++it;
830 stackingList.raise(tmp);
831 // don't bother looking at this window again
832 windows.erase(wit);
839 Raise all transients, preserving the existing stacking order.
841 static
842 void raiseTransients(StackingList::iterator top,
843 StackingList &stackingList,
844 BlackboxWindowList &transients) {
845 // 'top' points to the top of the layer, we need to start from the bottom
846 StackingList::iterator begin = stackingList.begin(),
847 end = stackingList.end(),
848 it = std::find(top, end, (StackEntity *) 0);
849 assert(it != end);
850 for (--it; it != begin; --it) {
851 if (it == top) {
852 // top of layer
853 break;
855 BlackboxWindow *tmp = dynamic_cast<BlackboxWindow *>(*it);
856 if (!tmp)
857 continue;
858 BlackboxWindowList::iterator wit = transients.begin(),
859 wend = transients.end();
860 wit = std::find(wit, wend, tmp);
861 if (wit != wend) {
862 // found a transient in this layer, raise it
863 ++it;
864 stackingList.raise(tmp);
865 // don't bother looking at this window again
866 transients.erase(wit);
873 Raises the specified stacking entity. If the entity is a window,
874 all transients are also raised (preserving their stacking order).
875 If the window is part of a group, the entire group is raised (also
876 preserving the stacking order) befor raising the specified window.
878 The return value indicates which windows need to be restacked:
880 - if raiseWindow() return stackingList.end(), then nothing needs to
881 be restacked.
883 - if raiseWindow() returns an iterator for a layer boundary
884 (i.e. *raiseWindow(...) == 0) then all windows in all layers need to
885 be restacked
887 - otherwise, the return value points to the bottom most window that
888 needs to be restacked (i.e. the top of the layer down to the return
889 value need to be restacked)
891 static
892 StackingList::iterator raiseWindow(StackingList &stackingList,
893 StackEntity *entity) {
894 BlackboxWindow *win = dynamic_cast<BlackboxWindow *>(entity);
895 if (win) {
896 if (win->isFullScreen() && win->layer() != StackingList::LayerFullScreen) {
897 // move full-screen windows over all other windows when raising
898 win->changeLayer(StackingList::LayerFullScreen);
899 return stackingList.end();
903 StackingList::iterator layer = stackingList.layer(entity->layer());
904 if (*layer == entity) {
905 // already on top of the layer
906 return stackingList.end();
909 BWindowGroup *group = 0;
910 if (win) {
911 group = win->findWindowGroup();
912 if (group) {
913 // raise all windows in the group before raising 'win'
914 ::raiseGroup(stackingList, group);
915 } else if (win->isTransient()) {
916 // raise non-transient parent before raising transient
917 BlackboxWindow *tmp = win->findNonTransientParent();
918 if (tmp)
919 (void) ::raiseWindow(stackingList, tmp);
923 // raise the entity
924 StackingList::iterator top = stackingList.raise(entity),
925 end = stackingList.end();
926 assert(top != end);
927 if (win) {
928 // ... with all transients above it
929 BlackboxWindowList transients = win->buildFullTransientList();
930 if (!transients.empty())
931 raiseTransients(top, stackingList, transients);
933 // ... and group transients on top
934 if (group && !win->isGroupTransient()) {
935 const BlackboxWindow * const w = win->isTransient()
936 ? win->findNonTransientParent()
937 : win;
938 if (!w || !w->isGroupTransient()) {
939 BlackboxWindowList groupTransients = group->transients();
940 BlackboxWindowList::const_iterator wit = groupTransients.begin(),
941 wend = groupTransients.end();
942 for (; wit != wend; ++wit) {
943 BlackboxWindowList x = (*wit)->buildFullTransientList();
944 groupTransients.splice(groupTransients.end(), x);
946 if (!groupTransients.empty())
947 raiseTransients(top, stackingList, groupTransients);
951 if (!group)
952 return top;
954 StackingList::iterator it = std::find(top, end, (StackEntity *) 0);
955 assert(it != end);
956 return it;
961 Raises the stack entity as above and then updates the stacking on
962 the X server. The EWMH stacking hint is also updated.
964 void BScreen::raiseWindow(StackEntity *entity) {
965 StackingList::iterator top = ::raiseWindow(_stackingList, entity),
966 end = _stackingList.end();
967 if (top == end) {
968 // no need to raise entity
969 return;
970 } else if (!(*top)) {
971 // need to restack all windows
972 restackWindows();
973 return;
976 // find the first entity above us (if any)
977 StackEntity *above = 0;
978 StackingList::iterator layer = _stackingList.layer(entity->layer()),
979 begin = _stackingList.begin(),
980 it = layer;
981 if (it != begin) {
982 for (--it; it != begin; --it) {
983 if (*it) {
984 above = *it;
985 break;
990 // build the stack
991 WindowStack stack;
992 if (above) {
993 // found another entity above the one we are raising
994 stack.push_back(above->windowID());
995 } else {
996 // keep everying under empty_window
997 stack.push_back(empty_window);
1000 // go to the layer boundary above 'top'
1001 for (it = top; *it && it != begin; --it)
1004 // put all windows from the layer boundary to 'top' into the stack
1005 if (!(*it))
1006 ++it; // move past layer boundary
1007 for (; *it && it != top; ++it) {
1008 assert(it != end);
1009 stack.push_back((*it)->windowID());
1011 stack.push_back((*top)->windowID());
1013 XRestackWindows(_blackbox->XDisplay(), &stack[0], stack.size());
1014 updateClientListStackingHint();
1018 static
1019 void lowerGroup(StackingList &stackingList, BWindowGroup *group) {
1020 BlackboxWindowList windows = group->windows();
1021 int layer = StackingList::LayerNormal;
1022 for (; layer < StackingList::LayerDesktop; ++layer) {
1023 const StackingList::iterator begin = stackingList.begin(),
1024 end = stackingList.end();
1025 StackingList::iterator it = stackingList.layer(StackingList::Layer(layer)),
1026 bottom = std::find(it, end, (StackEntity *) 0);
1027 // 'it' points to the top of the layer
1028 assert(bottom != end);
1030 if (!(*it)) {
1031 // nothing in layer
1032 break;
1035 // walk down the layer, lowering all windows in the group
1036 for (; it != bottom; ++it) {
1037 assert(it != end);
1038 BlackboxWindow *tmp = dynamic_cast<BlackboxWindow *>(*it);
1039 if (!tmp) {
1040 // entity is not a window
1041 continue;
1044 const BlackboxWindowList::iterator wend = windows.end();
1045 BlackboxWindowList::iterator wit;
1046 if ((wit = std::find(windows.begin(), wend, tmp)) == wend)
1047 continue;
1049 // found a window in this layer, lower it
1050 --it;
1051 (void) stackingList.lower(tmp);
1052 // don't bother looking at this window again
1053 windows.erase(wit);
1059 static void lowerTransients(StackingList::iterator it,
1060 StackingList &stackingList,
1061 BlackboxWindowList &transients) {
1062 // 'it' points to the top of the layer
1063 const StackingList::iterator end = stackingList.end(),
1064 bottom = std::find(it, end, (StackEntity *) 0);
1065 assert(bottom != end);
1066 for (; it != bottom; ++it) {
1067 assert(it != end);
1068 BlackboxWindow *tmp = dynamic_cast<BlackboxWindow *>(*it);
1069 if (!tmp)
1070 continue;
1072 const BlackboxWindowList::iterator wend = transients.end();
1073 BlackboxWindowList::iterator wit;
1074 if ((wit = std::find(transients.begin(), wend, tmp)) == wend)
1075 continue;
1077 // found a transient in this layer, lower it
1078 --it;
1079 StackingList::iterator l = stackingList.lower(tmp);
1080 // don't bother looking at this window again
1081 transients.erase(wit);
1087 Lowers the specified stacking entity. If the entity is a window,
1088 all transients are also lowered (preserving their stacking order).
1089 If the window is part of a group, the entire group is lowered (also
1090 preserving the stacking order) befor lowering the specified window.
1092 The return value indicates which windows need to be restacked:
1094 - if lowerWindow() return stackingList.end(), then nothing needs to
1095 be restacked.
1097 - if lowerWindow() returns an iterator for a layer boundary
1098 (i.e. *lowerWindow(...) == 0) then all windows in all layers need to
1099 be restacked
1101 - otherwise, the return value points to the top-most window that
1102 needs to be restacked (i.e. the return value down to the bottom of
1103 the layer need to be restacked)
1105 static
1106 StackingList::iterator lowerWindow(StackingList &stackingList,
1107 StackEntity *entity,
1108 bool ignore_group = false) {
1109 StackingList::iterator it, end = stackingList.end();
1110 BlackboxWindow *win = dynamic_cast<BlackboxWindow *>(entity);
1111 if (win) {
1112 it = end;
1113 BWindowGroup *group = win->findWindowGroup();
1114 if (!ignore_group && group) {
1115 // lower all windows in the group before lowering 'win'
1116 ::lowerGroup(stackingList, group);
1117 it = std::find(stackingList.begin(), end, (StackEntity *) 0);
1118 assert(it != end);
1121 const StackingList::iterator layer = stackingList.layer(win->layer());
1122 if (win->isTransient()) {
1123 BlackboxWindow *const tmp = win->findNonTransientParent();
1124 if (tmp && !tmp->isGroupTransient()) {
1125 // lower non-transient parent
1126 (void) ::lowerWindow(stackingList, tmp, true);
1127 if (it == end)
1128 it = layer;
1130 return it;
1133 // lower transients oinf 'win' and 'win'
1134 BlackboxWindowList transients = win->buildFullTransientList();
1135 if (!transients.empty()) {
1136 ::lowerTransients(layer, stackingList, transients);
1137 (void) stackingList.lower(win);
1138 if (it == end)
1139 it = layer;
1140 } else {
1141 if (it == end) {
1142 it = stackingList.lower(entity);
1143 assert(it != end);
1144 } else {
1145 (void) stackingList.lower(entity);
1148 } else {
1149 // lower the entity
1150 it = stackingList.lower(entity);
1151 assert(it != end);
1153 return it;
1157 void BScreen::lowerWindow(StackEntity *entity) {
1158 StackingList::iterator top = ::lowerWindow(_stackingList, entity),
1159 end = _stackingList.end();
1160 if (top == end) {
1161 // no need to lower entity
1162 return;
1163 } else if (!(*top)) {
1164 // need to restack all windows
1165 restackWindows();
1166 return;
1169 // find the entity above us (if any)
1170 StackEntity *above = 0;
1171 StackingList::iterator begin = _stackingList.begin(),
1172 it = top;
1173 if (it != begin) {
1174 for (--it; it != begin; --it) {
1175 if (*it) {
1176 above = *it;
1177 break;
1182 // build the window stack
1183 WindowStack stack;
1184 if (above) {
1185 // found another entity above the one we are lowering
1186 stack.push_back(above->windowID());
1187 } else {
1188 // keep everying under empty_window
1189 stack.push_back(empty_window);
1192 // find the layer boundary
1193 StackingList::iterator bottom = std::find(top, end, (StackEntity *) 0);
1194 assert(bottom != end);
1196 // put all windows from 'top' to the layer boundary into the stack
1197 for (it = top; it != bottom; ++it) {
1198 assert(*it && it != end);
1199 stack.push_back((*it)->windowID());
1201 stack.push_back((*top)->windowID());
1203 XRestackWindows(_blackbox->XDisplay(), &stack[0], stack.size());
1204 updateClientListStackingHint();
1208 void BScreen::restackWindows(void) {
1209 WindowStack stack;
1210 stack.push_back(empty_window);
1212 StackingList::const_iterator it, end = _stackingList.end();
1213 for (it = _stackingList.begin(); it != end; ++it) {
1214 if (*it)
1215 stack.push_back((*it)->windowID());
1218 XRestackWindows(_blackbox->XDisplay(), &stack[0], stack.size());
1219 updateClientListStackingHint();
1223 void BScreen::propagateWindowName(const BlackboxWindow * const win) {
1224 if (win->isIconic()) {
1225 _iconmenu->changeItem(win->windowNumber(),
1226 bt::ellideText(win->iconTitle(), 60,
1227 bt::toUnicode("...")));
1228 } else if (win->workspace() != bt::BSENTINEL) {
1229 Workspace *workspace = findWorkspace(win->workspace());
1230 assert(workspace != 0);
1231 workspace->menu()->changeItem(win->windowNumber(),
1232 bt::ellideText(win->title(), 60,
1233 bt::toUnicode("...")));
1236 if (_toolbar && _blackbox->focusedWindow() == win)
1237 _toolbar->redrawWindowLabel();
1241 void BScreen::nextFocus(void) {
1242 BlackboxWindow *focused = _blackbox->focusedWindow(),
1243 *next = 0;
1244 BlackboxWindowList::iterator it, end = windowList.end();
1246 if (focused && focused->workspace() == current_workspace &&
1247 focused->screen()->screen_info.screenNumber() ==
1248 screen_info.screenNumber()) {
1249 it = std::find(windowList.begin(), end, focused);
1250 assert(it != end);
1251 for (; it != end; ++it) {
1252 next = *it;
1253 if (next && next != focused && next->workspace() == current_workspace &&
1254 next->setInputFocus()) {
1255 // we found our new focus target
1256 next->setInputFocus();
1257 raiseWindow(next);
1258 break;
1260 next = 0;
1263 if (!next) {
1264 for (it = windowList.begin(); it != end; ++it) {
1265 next = *it;
1266 if (next && next->workspace() == current_workspace &&
1267 next->setInputFocus()) {
1268 // we found our new focus target
1269 raiseWindow(next);
1270 break;
1277 void BScreen::prevFocus(void) {
1278 BlackboxWindow *focused = _blackbox->focusedWindow(),
1279 *next = 0;
1280 BlackboxWindowList::reverse_iterator it, end = windowList.rend();
1282 if (focused && focused->workspace() == current_workspace &&
1283 focused->screen()->screen_info.screenNumber() ==
1284 screen_info.screenNumber()) {
1285 it = std::find(windowList.rbegin(), end, focused);
1286 assert(it != end);
1287 for (; it != end; ++it) {
1288 next = *it;
1289 if (next && next != focused && next->workspace() == current_workspace &&
1290 next->setInputFocus()) {
1291 // we found our new focus target
1292 next->setInputFocus();
1293 raiseWindow(next);
1294 break;
1296 next = 0;
1299 if (!next) {
1300 for (it = windowList.rbegin(); it != end; ++it) {
1301 next = *it;
1302 if (next && next->workspace() == current_workspace &&
1303 next->setInputFocus()) {
1304 // we found our new focus target
1305 raiseWindow(next);
1306 break;
1313 void BScreen::raiseFocus(void) {
1314 BlackboxWindow *focused = _blackbox->focusedWindow();
1315 if (! focused || focused->screen() != this)
1316 return;
1318 raiseWindow(focused);
1322 void BScreen::InitMenu(void) {
1323 if (_rootmenu) {
1324 _rootmenu->clear();
1325 } else {
1326 _rootmenu = new Rootmenu(*_blackbox, screen_info.screenNumber(), this);
1327 _rootmenu->showTitle();
1329 bool defaultMenu = True;
1331 if (_blackbox->resource().menuFilename()) {
1332 const char * const filename = _blackbox->resource().menuFilename();
1333 bool pipe_menu = filename[0] == '|';
1334 FILE *menu_file = pipe_menu
1335 ? popen(filename + 1, "r")
1336 : fopen(filename, "r");
1337 if (!menu_file) {
1338 perror(_blackbox->resource().menuFilename());
1339 } else {
1340 if (feof(menu_file)) {
1341 fprintf(stderr, "%s: menu file '%s' is empty\n",
1342 _blackbox->applicationName().c_str(),
1343 _blackbox->resource().menuFilename());
1344 } else {
1345 char line[1024], label[1024];
1346 memset(line, 0, 1024);
1347 memset(label, 0, 1024);
1349 while (fgets(line, 1024, menu_file) && ! feof(menu_file)) {
1350 if (line[0] == '#')
1351 continue;
1353 int i, key = 0, index = -1, len = strlen(line);
1355 for (i = 0; i < len; i++) {
1356 if (line[i] == '[') index = 0;
1357 else if (line[i] == ']') break;
1358 else if (line[i] != ' ')
1359 if (index++ >= 0)
1360 key += tolower(line[i]);
1363 if (key == 517) { // [begin]
1364 index = -1;
1365 for (i = index; i < len; i++) {
1366 if (line[i] == '(')
1367 index = 0;
1368 else if (line[i] == ')')
1369 break;
1370 else if (index++ >= 0) {
1371 if (line[i] == '\\' && i < len - 1) i++;
1372 label[index - 1] = line[i];
1376 if (index == -1) index = 0;
1377 label[index] = '\0';
1379 _rootmenu->setTitle(bt::toUnicode(label));
1380 defaultMenu = parseMenuFile(menu_file, _rootmenu);
1381 break;
1385 if (pipe_menu)
1386 pclose(menu_file);
1387 else
1388 fclose(menu_file);
1392 if (defaultMenu) {
1393 _rootmenu->setTitle(bt::toUnicode("_Blackbox"));
1395 _rootmenu->insertFunction(bt::toUnicode("xterm"),
1396 BScreen::Execute, "xterm");
1397 _rootmenu->insertFunction(bt::toUnicode("Restart"),
1398 BScreen::Restart);
1399 _rootmenu->insertFunction(bt::toUnicode("Exit"),
1400 BScreen::Exit);
1401 } else {
1402 _blackbox->saveMenuFilename(_blackbox->resource().menuFilename());
1407 static
1408 size_t string_within(char begin, char end,
1409 const char *input, size_t start_at, size_t length,
1410 char *output) {
1411 bool parse = False;
1412 size_t index = 0;
1413 size_t i = start_at;
1414 for (; i < length; ++i) {
1415 if (input[i] == begin) {
1416 parse = True;
1417 } else if (input[i] == end) {
1418 break;
1419 } else if (parse) {
1420 if (input[i] == '\\' && i < length - 1) i++;
1421 output[index++] = input[i];
1425 if (parse)
1426 output[index] = '\0';
1427 else
1428 output[0] = '\0';
1430 return i;
1434 bool BScreen::parseMenuFile(FILE *file, Rootmenu *menu) {
1435 char line[1024], keyword[1024], label[1024], command[1024];
1436 bool done = False;
1438 while (! (done || feof(file))) {
1439 memset(line, 0, 1024);
1440 memset(label, 0, 1024);
1441 memset(command, 0, 1024);
1443 if (! fgets(line, 1024, file))
1444 continue;
1446 if (line[0] == '#') // comment, skip it
1447 continue;
1449 size_t line_length = strlen(line);
1450 unsigned int key = 0;
1452 // get the keyword enclosed in []'s
1453 size_t pos = string_within('[', ']', line, 0, line_length, keyword);
1455 if (keyword[0] == '\0') { // no keyword, no menu entry
1456 continue;
1457 } else {
1458 size_t len = strlen(keyword);
1459 for (size_t i = 0; i < len; ++i) {
1460 if (keyword[i] != ' ')
1461 key += tolower(keyword[i]);
1465 // get the label enclosed in ()'s
1466 pos = string_within('(', ')', line, pos, line_length, label);
1468 // get the command enclosed in {}'s
1469 pos = string_within('{', '}', line, pos, line_length, command);
1471 switch (key) {
1472 case 311: // end
1473 done = True;
1475 break;
1477 case 328: // sep
1478 menu->insertSeparator();
1479 break;
1481 case 333: // nop
1482 if (! *label)
1483 label[0] = '\0';
1484 menu->setItemEnabled(menu->insertItem(bt::toUnicode(label)), false);
1485 break;
1487 case 421: // exec
1488 if (! (*label && *command)) {
1489 fprintf(stderr,
1490 "%s: [exec] error, no menu label and/or command defined\n",
1491 _blackbox->applicationName().c_str());
1492 continue;
1495 menu->insertFunction(bt::toUnicode(label), BScreen::Execute, command);
1496 break;
1498 case 442: // exit
1499 if (! *label) {
1500 fprintf(stderr, "%s: [exit] error, no menu label defined\n",
1501 _blackbox->applicationName().c_str());
1502 continue;
1505 menu->insertFunction(bt::toUnicode(label), BScreen::Exit);
1506 break;
1508 case 561: { // style
1509 if (! (*label && *command)) {
1510 fprintf(stderr,
1511 "%s: [style] error, no menu label and/or filename defined\n",
1512 _blackbox->applicationName().c_str());
1513 continue;
1516 std::string style = bt::expandTilde(command);
1517 menu->insertFunction(bt::toUnicode(label),
1518 BScreen::SetStyle, style.c_str());
1519 break;
1522 case 630: // config
1523 if (! *label) {
1524 fprintf(stderr, "%s: [config] error, no label defined",
1525 _blackbox->applicationName().c_str());
1526 continue;
1529 menu->insertItem(bt::toUnicode(label), configmenu);
1530 break;
1532 case 740: { // include
1533 if (! *label) {
1534 fprintf(stderr, "%s: [include] error, no filename defined\n",
1535 _blackbox->applicationName().c_str());
1536 continue;
1539 bool pipe_menu = label[0] == '|';
1540 std::string newfile = bt::expandTilde(pipe_menu ? label + 1 : label);
1541 FILE *submenufile = pipe_menu
1542 ? popen(newfile.c_str(), "r")
1543 : fopen(newfile.c_str(), "r");
1545 if (! submenufile) {
1546 perror(newfile.c_str());
1547 continue;
1550 struct stat buf;
1551 if (!pipe_menu
1552 && (fstat(fileno(submenufile), &buf) || ! S_ISREG(buf.st_mode))) {
1553 fprintf(stderr, "%s: [include] error: '%s' is not a regular file\n",
1554 _blackbox->applicationName().c_str(), newfile.c_str());
1555 fclose(submenufile);
1556 break;
1559 if (! feof(submenufile)) {
1560 if (! parseMenuFile(submenufile, menu))
1561 _blackbox->saveMenuFilename(newfile);
1563 if (pipe_menu)
1564 pclose(submenufile);
1565 else
1566 fclose(submenufile);
1570 break;
1572 case 767: { // submenu
1573 if (! *label) {
1574 fprintf(stderr, "%s: [submenu] error, no menu label defined\n",
1575 _blackbox->applicationName().c_str());
1576 continue;
1579 Rootmenu *submenu =
1580 new Rootmenu(*_blackbox, screen_info.screenNumber(), this);
1581 submenu->showTitle();
1583 if (*command)
1584 submenu->setTitle(bt::toUnicode(command));
1585 else
1586 submenu->setTitle(bt::toUnicode(label));
1588 parseMenuFile(file, submenu);
1589 menu->insertItem(bt::toUnicode(label), submenu);
1592 break;
1594 case 773: { // restart
1595 if (! *label) {
1596 fprintf(stderr, "%s: [restart] error, no menu label defined\n",
1597 _blackbox->applicationName().c_str());
1598 continue;
1601 if (*command) {
1602 menu->insertFunction(bt::toUnicode(label),
1603 BScreen::RestartOther, command);
1604 } else {
1605 menu->insertFunction(bt::toUnicode(label), BScreen::Restart);
1607 break;
1610 case 845: { // reconfig
1611 if (! *label) {
1612 fprintf(stderr, "%s: [reconfig] error, no menu label defined\n",
1613 _blackbox->applicationName().c_str());
1614 continue;
1617 menu->insertFunction(bt::toUnicode(label), BScreen::Reconfigure);
1618 break;
1621 case 995: // stylesdir
1622 case 1113: { // stylesmenu
1623 bool newmenu = ((key == 1113) ? True : False);
1625 if (! *label || (! *command && newmenu)) {
1626 fprintf(stderr,
1627 "%s: [stylesdir/stylesmenu] error, no directory defined\n",
1628 _blackbox->applicationName().c_str());
1629 continue;
1632 char *directory = ((newmenu) ? command : label);
1634 std::string stylesdir = bt::expandTilde(directory);
1636 struct stat statbuf;
1638 if (stat(stylesdir.c_str(), &statbuf) == -1) {
1639 fprintf(stderr,
1640 "%s: [stylesdir/stylesmenu] error, '%s' does not exist\n",
1641 _blackbox->applicationName().c_str(), stylesdir.c_str());
1642 continue;
1644 if (! S_ISDIR(statbuf.st_mode)) {
1645 fprintf(stderr,
1646 "%s: [stylesdir/stylesmenu] error, '%s' is not a directory\n",
1647 _blackbox->applicationName().c_str(), stylesdir.c_str());
1648 continue;
1651 Rootmenu *stylesmenu;
1653 if (newmenu) {
1654 stylesmenu =
1655 new Rootmenu(*_blackbox, screen_info.screenNumber(),this);
1656 stylesmenu->showTitle();
1657 } else {
1658 stylesmenu = menu;
1661 DIR *d = opendir(stylesdir.c_str());
1662 struct dirent *p;
1663 std::vector<std::string> ls;
1665 while((p = readdir(d)))
1666 ls.push_back(p->d_name);
1668 closedir(d);
1670 std::sort(ls.begin(), ls.end());
1672 std::vector<std::string>::iterator it = ls.begin(),
1673 end = ls.end();
1674 for (; it != end; ++it) {
1675 std::string& fname = *it;
1677 if (fname[0] == '.' || fname[fname.size()-1] == '~')
1678 continue;
1680 std::string style = stylesdir;
1681 style += '/';
1682 style += fname;
1684 if (! stat(style.c_str(), &statbuf) && S_ISREG(statbuf.st_mode)) {
1685 // convert 'This_Long_Name' to 'This Long Name'
1686 std::replace(fname.begin(), fname.end(), '_', ' ');
1688 stylesmenu->insertFunction(bt::toUnicode(fname),
1689 BScreen::SetStyle, style);
1693 if (newmenu) {
1694 stylesmenu->setTitle(bt::toUnicode(label));
1695 menu->insertItem(bt::toUnicode(label), stylesmenu);
1698 _blackbox->saveMenuFilename(stylesdir);
1700 break;
1702 case 1090: { // workspaces
1703 if (! *label) {
1704 fprintf(stderr, "%s: [workspaces] error, no menu label defined\n",
1705 _blackbox->applicationName().c_str());
1706 continue;
1709 menu->insertItem(bt::toUnicode(label), _workspacemenu);
1710 break;
1712 } // switch
1715 return (menu->count() == 0);
1719 void BScreen::shutdown(void) {
1720 XSelectInput(_blackbox->XDisplay(), screen_info.rootWindow(),
1721 NoEventMask);
1722 XSync(_blackbox->XDisplay(), False);
1724 // unmanage all windows, but keep them in the current stacking order
1725 WindowStack stack;
1726 StackingList::const_iterator it, end = _stackingList.end();
1727 for (it = _stackingList.begin(); it != end; ++it) {
1728 if (!(*it))
1729 continue;
1730 const BlackboxWindow * const win = dynamic_cast<BlackboxWindow *>(*it);
1731 if (!win)
1732 continue;
1733 stack.push_back(win->clientWindow());
1736 while(! windowList.empty())
1737 unmanageWindow(windowList.back());
1739 if (!stack.empty())
1740 XRestackWindows(_blackbox->XDisplay(), &stack[0], stack.size());
1742 if (_slit)
1743 _slit->shutdown();
1747 void BScreen::showGeometry(GeometryType type, const bt::Rect &rect) {
1748 if (! geom_visible) {
1749 XMoveResizeWindow(_blackbox->XDisplay(), geom_window,
1750 (screen_info.width() - geom_w) / 2,
1751 (screen_info.height() - geom_h) / 2, geom_w, geom_h);
1752 XMapWindow(_blackbox->XDisplay(), geom_window);
1753 XRaiseWindow(_blackbox->XDisplay(), geom_window);
1755 geom_visible = True;
1758 char label[80];
1759 switch (type) {
1760 case Position:
1761 sprintf(label, "X:%4d Y:%4d", rect.x(), rect.y());
1762 break;
1763 case Size:
1764 sprintf(label, "W:%4u H:%4u", rect.width(), rect.height());
1765 break;
1766 default:
1767 assert(0);
1770 const WindowStyle &style = _resource.windowStyle();
1771 const bt::Texture &texture =
1772 (style.focus.label.texture() == bt::Texture::Parent_Relative)
1773 ? style.focus.title
1774 : style.focus.label;
1775 const bt::Rect u(0, 0, geom_w, geom_h);
1776 const bt::Rect t(style.label_margin,
1777 style.label_margin,
1778 geom_w - (style.label_margin * 2),
1779 geom_h - (style.label_margin * 2));
1780 const bt::Pen pen(screen_info.screenNumber(), style.focus.text);
1781 bt::drawTexture(screen_info.screenNumber(), texture, geom_window,
1782 u, u, geom_pixmap);
1783 bt::drawText(style.font, pen, geom_window, t, style.alignment,
1784 bt::toUnicode(label));
1788 void BScreen::hideGeometry(void) {
1789 if (geom_visible) {
1790 XUnmapWindow(_blackbox->XDisplay(), geom_window);
1791 geom_visible = False;
1796 void BScreen::addStrut(bt::EWMH::Strut *strut) {
1797 strutList.push_back(strut);
1798 updateAvailableArea();
1802 void BScreen::removeStrut(bt::EWMH::Strut *strut) {
1803 strutList.remove(strut);
1804 updateAvailableArea();
1808 void BScreen::updateStrut(void) {
1809 updateAvailableArea();
1813 const bt::Rect& BScreen::availableArea(void) {
1814 if (_blackbox->resource().fullMaximization())
1815 return screen_info.rect(); // return the full screen
1816 return usableArea;
1820 void BScreen::updateAvailableArea(void) {
1821 bt::Rect new_area;
1823 /* these values represent offsets from the screen edge
1824 * we look for the biggest offset on each edge and then apply them
1825 * all at once
1826 * do not be confused by the similarity to the names of Rect's members
1828 bt::EWMH::Strut current;
1830 StrutList::const_iterator sit = strutList.begin(), send = strutList.end();
1832 for(; sit != send; ++sit) {
1833 const bt::EWMH::Strut* const strut = *sit;
1834 if (strut->left > current.left)
1835 current.left = strut->left;
1836 if (strut->top > current.top)
1837 current.top = strut->top;
1838 if (strut->right > current.right)
1839 current.right = strut->right;
1840 if (strut->bottom > current.bottom)
1841 current.bottom = strut->bottom;
1844 new_area.setPos(current.left, current.top);
1845 new_area.setSize(screen_info.width() - (current.left + current.right),
1846 screen_info.height() - (current.top + current.bottom));
1848 if (new_area != usableArea) {
1849 usableArea = new_area;
1850 BlackboxWindowList::iterator wit = windowList.begin(),
1851 wend = windowList.end();
1852 for (; wit != wend; ++wit)
1853 if ((*wit)->isMaximized()) (*wit)->remaximize();
1855 updateWorkareaHint();
1860 Workspace* BScreen::findWorkspace(unsigned int index) const {
1861 if (index == bt::BSENTINEL)
1862 return 0;
1863 assert(index < workspacesList.size());
1864 return workspacesList[index];
1868 void BScreen::clientMessageEvent(const XClientMessageEvent * const event) {
1869 if (event->format != 32) return;
1871 if (event->message_type == _blackbox->ewmh().numberOfDesktops()) {
1872 unsigned int number = event->data.l[0];
1873 if (number > workspaceCount()) {
1874 for (; number != workspaceCount(); --number)
1875 addWorkspace();
1876 } else if (number < workspaceCount()) {
1877 for (; number != workspaceCount(); ++number)
1878 removeLastWorkspace();
1880 } else if (event->message_type == _blackbox->ewmh().desktopNames()) {
1881 readDesktopNames();
1882 } else if (event->message_type == _blackbox->ewmh().currentDesktop()) {
1883 const unsigned int workspace = event->data.l[0];
1884 if (workspace < workspaceCount() && workspace != current_workspace)
1885 setCurrentWorkspace(workspace);
1890 void BScreen::buttonPressEvent(const XButtonEvent * const event) {
1892 set this screen active. keygrabs and input focus will stay on
1893 this screen until the user focuses a window on another screen or
1894 makes another screen active.
1896 _blackbox->setActiveScreen(this);
1898 if (event->button == 2) {
1899 _workspacemenu->popup(event->x_root, event->y_root);
1900 } else if (event->button == 3) {
1901 _blackbox->checkMenu();
1902 _rootmenu->popup(event->x_root, event->y_root);
1903 } else if (event->button == 4 &&
1904 _blackbox->resource().changeWorkspaceWithMouseWheel()) {
1905 nextWorkspace();
1906 } else if (event->button == 5 &&
1907 _blackbox->resource().changeWorkspaceWithMouseWheel()) {
1908 prevWorkspace();
1913 void BScreen::propertyNotifyEvent(const XPropertyEvent * const event) {
1914 if (event->atom == _blackbox->ewmh().activeWindow() && _toolbar)
1915 _toolbar->redrawWindowLabel();
1919 void BScreen::unmapNotifyEvent(const XUnmapEvent * const event) {
1920 // handle synthetic unmap events according to ICCCM section 4.1.4
1921 BlackboxWindow *win = _blackbox->findWindow(event->window);
1922 if (win && event->event == screen_info.rootWindow()
1923 && !event->from_configure)
1924 win->unmapNotifyEvent(event);
1928 void BScreen::toggleFocusModel(FocusModel model) {
1929 std::for_each(windowList.begin(), windowList.end(),
1930 std::mem_fun(&BlackboxWindow::ungrabButtons));
1932 _blackbox->resource().setFocusModel(model);
1934 std::for_each(windowList.begin(), windowList.end(),
1935 std::mem_fun(&BlackboxWindow::grabButtons));
1939 void BScreen::updateWorkareaHint(void) const {
1940 unsigned long *workarea, *tmp;
1942 tmp = workarea = new unsigned long[workspaceCount() * 4];
1944 for (unsigned int i = 0; i < workspaceCount(); ++i) {
1945 tmp[0] = usableArea.x();
1946 tmp[1] = usableArea.y();
1947 tmp[2] = usableArea.width();
1948 tmp[3] = usableArea.height();
1949 tmp += 4;
1952 _blackbox->ewmh().setWorkarea(screen_info.rootWindow(),
1953 workarea, workspaceCount());
1955 delete [] workarea;
1959 void BScreen::updateDesktopNamesHint(void) const {
1960 std::vector<bt::ustring> names(workspacesList.size());
1961 WorkspaceList::const_iterator it = workspacesList.begin();
1962 const WorkspaceList::const_iterator end = workspacesList.end();
1963 for (; it != end; ++it)
1964 names.push_back((*it)->name());
1965 _blackbox->ewmh().setDesktopNames(screen_info.rootWindow(), names);
1969 void BScreen::updateClientListHint(void) const {
1970 if (windowList.empty()) {
1971 _blackbox->ewmh().removeProperty(screen_info.rootWindow(),
1972 _blackbox->ewmh().clientList());
1973 return;
1976 bt::EWMH::WindowList clientList(windowList.size());
1978 std::transform(windowList.begin(), windowList.end(), clientList.begin(),
1979 std::mem_fun(&BlackboxWindow::clientWindow));
1981 _blackbox->ewmh().setClientList(screen_info.rootWindow(), clientList);
1985 void BScreen::updateClientListStackingHint(void) const {
1986 bt::EWMH::WindowList stack;
1988 // we store windows in top-to-bottom order, but the EWMH wants
1989 // bottom-to-top...
1990 StackingList::const_reverse_iterator it = _stackingList.rbegin(),
1991 end = _stackingList.rend();
1992 for (; it != end; ++it) {
1993 const BlackboxWindow * const win = dynamic_cast<BlackboxWindow *>(*it);
1994 if (win) stack.push_back(win->clientWindow());
1997 if (stack.empty()) {
1998 _blackbox->ewmh().removeProperty(screen_info.rootWindow(),
1999 _blackbox->ewmh().clientListStacking());
2000 return;
2003 _blackbox->ewmh().setClientListStacking(screen_info.rootWindow(), stack);
2007 void BScreen::readDesktopNames(void) {
2008 std::vector<bt::ustring> names;
2009 if(! _blackbox->ewmh().readDesktopNames(screen_info.rootWindow(), names))
2010 return;
2012 std::vector<bt::ustring>::const_iterator it = names.begin();
2013 const std::vector<bt::ustring>::const_iterator end = names.end();
2014 WorkspaceList::iterator wit = workspacesList.begin();
2015 const WorkspaceList::iterator wend = workspacesList.end();
2017 for (; wit != wend && it != end; ++wit, ++it) {
2018 if ((*wit)->name() != *it)
2019 (*wit)->setName(*it);
2022 if (names.size() < workspacesList.size())
2023 updateDesktopNamesHint();
2027 BlackboxWindow *BScreen::window(unsigned int workspace, unsigned int id) {
2028 StackingList::const_iterator it = _stackingList.begin(),
2029 end = _stackingList.end();
2030 for (; it != end; ++it) {
2031 BlackboxWindow * const win = dynamic_cast<BlackboxWindow *>(*it);
2032 if (win && win->workspace() == workspace && win->windowNumber() == id)
2033 return win;
2035 assert(false); // should not happen
2036 return 0;
2040 void BScreen::placeWindow(BlackboxWindow *win) {
2041 bt::Rect r = win->frameRect();
2043 // if the client/user has explicitly placed the window, honor it
2044 if (win->wmNormalHints().flags & (PPosition | USPosition)) {
2045 // make sure that the window is not placed outside of the workarea
2046 win->configure(r.inside(usableArea));
2047 return;
2050 switch (win->windowType()) {
2051 case WindowTypeDesktop:
2052 win->configure(screen_info.rect());
2053 return;
2054 case WindowTypeDialog: {
2055 BlackboxWindow *w = win->findTransientFor();
2056 bt::Rect p = w ? w->frameRect() : usableArea;
2057 const int x = static_cast<int>(p.x() + (p.width() - r.width()) / 2);
2058 const int y = static_cast<int>(p.y() + (p.height() - r.height()) / 2);
2059 r.setPos(x, y);
2060 break;
2062 default: {
2063 bool placed = false;
2064 BlackboxResource &res = _blackbox->resource();
2065 switch (res.windowPlacementPolicy()) {
2066 case RowSmartPlacement:
2067 case ColSmartPlacement:
2068 placed = smartPlacement(win->workspace(), r, usableArea);
2069 break;
2070 case CenterPlacement:
2071 placed = centerPlacement(r, usableArea);
2072 break;
2073 default:
2074 break; // handled below
2075 } // switch
2076 if (!placed) {
2077 cascadePlacement(r, usableArea);
2078 cascade_x += _resource.windowStyle().title_height;
2079 cascade_y += _resource.windowStyle().title_height;
2081 break;
2083 } // switch
2085 win->configure(r.inside(usableArea));
2089 bool BScreen::cascadePlacement(bt::Rect &win,
2090 const bt::Rect &avail) {
2091 if (cascade_x > (avail.width() / 2) ||
2092 cascade_y > (avail.height() / 2))
2093 cascade_x = cascade_y = 32;
2095 if (cascade_x == 32) {
2096 cascade_x += avail.x();
2097 cascade_y += avail.y();
2100 win.setPos(cascade_x, cascade_y);
2102 if (win.right() > avail.right() ||
2103 win.bottom() > avail.bottom()) {
2104 cascade_x = cascade_y = 32;
2106 cascade_x += avail.x();
2107 cascade_y += avail.y();
2109 win.setPos(cascade_x, cascade_y);
2112 return True;
2116 bool BScreen::centerPlacement(bt::Rect &rect, const bt::Rect &avail)
2118 const int x =
2119 static_cast<int>(avail.x() + (avail.width() - rect.width()) / 2);
2120 const int y =
2121 static_cast<int>(avail.y() + (avail.height() - rect.height()) / 2);
2122 rect.setPos(x, y);
2123 return true;
2127 bool BScreen::smartPlacement(unsigned int workspace, bt::Rect& rect,
2128 const bt::Rect& avail) {
2129 // constants
2130 const BlackboxResource &res = _blackbox->resource();
2131 const bool row_placement =
2132 (res.windowPlacementPolicy() == RowSmartPlacement);
2133 const bool leftright =
2134 (res.rowPlacementDirection() == LeftRight);
2135 const bool topbottom =
2136 (res.colPlacementDirection() == TopBottom);
2137 const bool ignore_shaded = res.placementIgnoresShaded();
2139 const int left_border = leftright ? 0 : -1;
2140 const int top_border = topbottom ? 0 : -1;
2141 const int right_border = leftright ? 1 : 0;
2142 const int bottom_border = topbottom ? 1 : 0;
2144 StackingList::const_iterator w_it, w_end;
2147 build a sorted vector of x and y grid boundaries
2149 note: we use one vector to reduce the number of allocations
2150 std::vector must do.. we allocate as much memory as we would need
2151 in the worst case scenario and work with that
2153 std::vector<int> coords(_stackingList.size() * 4 + 4);
2154 std::vector<int>::iterator
2155 x_begin = coords.begin(),
2156 x_end = x_begin,
2157 y_begin = coords.begin() + _stackingList.size() * 2 + 2,
2158 y_end = y_begin;
2161 std::vector<int>::iterator x_it = x_begin, y_it = y_begin;
2163 *x_it++ = avail.left();
2164 *x_it++ = avail.right();
2165 x_end += 2;
2167 *y_it++ = avail.top();
2168 *y_it++ = avail.bottom();
2169 y_end += 2;
2172 for (w_it = _stackingList.begin(), w_end = _stackingList.end();
2173 w_it != w_end; ++w_it) {
2174 const BlackboxWindow * const win =
2175 dynamic_cast<const BlackboxWindow *>(*w_it);
2176 if (!win) continue;
2178 if (win->windowType() == WindowTypeDesktop)
2179 continue;
2180 if (win->isIconic())
2181 continue;
2182 if (win->workspace() != bt::BSENTINEL && win->workspace() != workspace)
2183 continue;
2184 if (ignore_shaded && win->isShaded())
2185 continue;
2187 *x_it++ = std::max(win->frameRect().left() + left_border,
2188 avail.left());
2189 *x_it++ = std::min(win->frameRect().right() + right_border,
2190 avail.right());
2191 x_end += 2;
2193 *y_it++ = std::max(win->frameRect().top() + top_border,
2194 avail.top());
2195 *y_it++ = std::min(win->frameRect().bottom() + bottom_border,
2196 avail.bottom());
2197 y_end += 2;
2200 assert(x_end <= y_begin);
2203 std::sort(x_begin, x_end);
2204 x_end = std::unique(x_begin, x_end);
2206 std::sort(y_begin, y_end);
2207 y_end = std::unique(y_begin, y_end);
2209 // build a distribution grid
2210 unsigned int gw = x_end - x_begin - 1,
2211 gh = y_end - y_begin - 1;
2212 std::vector<bool> used_grid(gw * gh);
2213 std::fill_n(used_grid.begin(), used_grid.size(), false);
2215 for (w_it = _stackingList.begin(), w_end = _stackingList.end();
2216 w_it != w_end; ++w_it) {
2217 const BlackboxWindow * const win =
2218 dynamic_cast<const BlackboxWindow *>(*w_it);
2219 if (!win) continue;
2221 if (win->windowType() == WindowTypeDesktop)
2222 continue;
2223 if (win->isIconic())
2224 continue;
2225 if (win->workspace() != bt::BSENTINEL && win->workspace() != workspace)
2226 continue;
2227 if (ignore_shaded && win->isShaded())
2228 continue;
2230 const int w_left =
2231 std::max(win->frameRect().left() + left_border,
2232 avail.left());
2233 const int w_top =
2234 std::max(win->frameRect().top() + top_border,
2235 avail.top());
2236 const int w_right =
2237 std::min(win->frameRect().right() + right_border,
2238 avail.right());
2239 const int w_bottom =
2240 std::min(win->frameRect().bottom() + bottom_border,
2241 avail.bottom());
2243 // which areas of the grid are used by this window?
2244 std::vector<int>::iterator l_it = std::find(x_begin, x_end, w_left),
2245 r_it = std::find(x_begin, x_end, w_right),
2246 t_it = std::find(y_begin, y_end, w_top),
2247 b_it = std::find(y_begin, y_end, w_bottom);
2248 assert(l_it != x_end &&
2249 r_it != x_end &&
2250 t_it != y_end &&
2251 b_it != y_end);
2253 const unsigned int left = l_it - x_begin,
2254 right = r_it - x_begin,
2255 top = t_it - y_begin,
2256 bottom = b_it - y_begin;
2258 for (unsigned int gy = top; gy < bottom; ++gy)
2259 for (unsigned int gx = left; gx < right; ++gx)
2260 used_grid[(gy * gw) + gx] = true;
2264 Attempt to fit the window into any of the empty areas in the grid.
2265 The exact order is dependent upon the users configuration (as
2266 shown below).
2268 row placement:
2269 - outer -> vertical axis
2270 - inner -> horizontal axis
2272 col placement:
2273 - outer -> horizontal axis
2274 - inner -> vertical axis
2277 int gx, gy;
2278 int &outer = row_placement ? gy : gx;
2279 int &inner = row_placement ? gx : gy;
2280 const int outer_delta = row_placement
2281 ? (topbottom ? 1 : -1)
2282 : (leftright ? 1 : -1);
2283 const int inner_delta = row_placement
2284 ? (leftright ? 1 : -1)
2285 : (topbottom ? 1 : -1);
2286 const int outer_begin = row_placement
2287 ? (topbottom ? 0 : static_cast<int>(gh) - 1)
2288 : (leftright ? 0 : static_cast<int>(gw) - 1);
2289 const int outer_end = row_placement
2290 ? (topbottom ? static_cast<int>(gh) : -1)
2291 : (leftright ? static_cast<int>(gw) : -1);
2292 const int inner_begin = row_placement
2293 ? (leftright ? 0 : static_cast<int>(gw) - 1)
2294 : (topbottom ? 0 : static_cast<int>(gh) - 1);
2295 const int inner_end = row_placement
2296 ? (leftright ? static_cast<int>(gw) : -1)
2297 : (topbottom ? static_cast<int>(gh) : -1);
2299 bt::Rect where;
2300 bool fit = false;
2301 for (outer = outer_begin; ! fit && outer != outer_end;
2302 outer += outer_delta) {
2303 for (inner = inner_begin; ! fit && inner != inner_end;
2304 inner += inner_delta) {
2305 // see if the window fits in a single unused area
2306 if (used_grid[(gy * gw) + gx]) continue;
2308 where.setCoords(*(x_begin + gx), *(y_begin + gy),
2309 *(x_begin + gx + 1), *(y_begin + gy + 1));
2311 if (where.width() >= rect.width() &&
2312 where.height() >= rect.height()) {
2313 fit = true;
2314 break;
2318 see if we neighboring spaces are unused
2320 TODO: we should grid fit in the same direction as above,
2321 instead of always right->left and top->bottom
2323 int gx2 = gx, gy2 = gy;
2325 if (rect.width() > where.width()) {
2326 for (gx2 = gx+1; gx2 < static_cast<int>(gw); ++gx2) {
2327 if (used_grid[(gy * gw) + gx2]) {
2328 --gx2;
2329 break;
2332 where.setCoords(*(x_begin + gx), *(y_begin + gy),
2333 *(x_begin + gx2 + 1), *(y_begin + gy2 + 1));
2335 if (where.width() >= rect.width()) break;
2338 if (gx2 >= static_cast<int>(gw)) --gx2;
2341 if (rect.height() > where.height()) {
2342 for (gy2 = gy; gy2 < static_cast<int>(gh); ++gy2) {
2343 if (used_grid[(gy2 * gw) + gx]) {
2344 --gy2;
2345 break;
2348 where.setCoords(*(x_begin + gx), *(y_begin + gy),
2349 *(x_begin + gx2 + 1), *(y_begin + gy2 + 1));
2351 if (where.height() >= rect.height()) break;
2354 if (gy2 >= static_cast<int>(gh)) --gy2;
2357 if (where.width() >= rect.width() &&
2358 where.height() >= rect.height()) {
2359 fit = true;
2361 // make sure all spaces are really available
2362 for (int gy3 = gy; gy3 <= gy2; ++gy3) {
2363 for (int gx3 = gx; gx3 <= gx2; ++gx3) {
2364 if (used_grid[(gy3 * gw) + gx3]) {
2365 fit = false;
2366 break;
2374 if (! fit) {
2375 const int screen_area = avail.width() * avail.height();
2376 const int window_area = rect.width() * rect.height();
2377 if (window_area > screen_area / 8) {
2378 // center windows that don't fit (except for small windows)
2379 return centerPlacement(rect, avail);
2381 return false;
2384 // adjust the location() based on left/right and top/bottom placement
2385 if (! leftright)
2386 where.setX(where.right() - rect.width() + 1);
2387 if (! topbottom)
2388 where.setY(where.bottom() - rect.height() + 1);
2390 rect.setPos(where.x(), where.y());
2392 return true;
2396 void BScreen::createSlit(void) {
2397 assert(_slit == 0);
2399 _slit = new Slit(this);
2400 _stackingList.insert(_slit);
2401 restackWindows();
2405 void BScreen::destroySlit(void) {
2406 assert(_slit != 0);
2408 _stackingList.remove(_slit);
2409 delete _slit;
2410 _slit = 0;
2414 void BScreen::createToolbar(void) {
2415 assert(_toolbar == 0);
2417 _toolbar = new Toolbar(this);
2418 _stackingList.insert(_toolbar);
2419 restackWindows();
2423 void BScreen::destroyToolbar(void) {
2424 assert(_toolbar != 0);
2426 _stackingList.remove(_toolbar);
2427 delete _toolbar;
2428 _toolbar = 0;
2432 Windowmenu *BScreen::windowmenu(BlackboxWindow *win) {
2433 if (!_windowmenu)
2434 _windowmenu = new Windowmenu(*_blackbox, screen_info.screenNumber());
2435 _windowmenu->setWindow(win);
2436 return _windowmenu;
2440 void BScreen::addIcon(BlackboxWindow *win) {
2441 if (win->workspace() != bt::BSENTINEL) {
2442 Workspace *workspace = findWorkspace(win->workspace());
2443 assert(workspace != 0);
2444 workspace->removeWindow(win);
2447 if (win->isTransient()) {
2448 BlackboxWindow * const tmp = win->findNonTransientParent();
2449 if (tmp) {
2450 win->setWindowNumber(bt::BSENTINEL);
2451 return;
2455 const bt::ustring s =
2456 bt::ellideText(win->iconTitle(), 60, bt::toUnicode("..."));
2457 int id = _iconmenu->insertItem(s);
2458 _blackbox->ewmh().setWMVisibleIconName(win->clientWindow(), s);
2459 win->setWindowNumber(id);
2463 void BScreen::removeIcon(BlackboxWindow *win) {
2464 if (win->windowNumber() != bt::BSENTINEL) {
2465 _iconmenu->removeItem(win->windowNumber());
2466 _blackbox->ewmh().removeProperty(win->clientWindow(),
2467 _blackbox->ewmh().wmVisibleIconName());
2470 Workspace *workspace = findWorkspace(current_workspace);
2471 assert(workspace != 0);
2472 workspace->addWindow(win);
2476 BlackboxWindow *BScreen::icon(unsigned int id) {
2477 StackingList::const_iterator it = _stackingList.begin(),
2478 end = _stackingList.end();
2479 for (; it != end; ++it) {
2480 BlackboxWindow * const win = dynamic_cast<BlackboxWindow *>(*it);
2481 if (win && win->isIconic() && win->windowNumber() == id)
2482 return win;
2484 assert(false); // should not happen
2485 return 0;