correctly set size/state on construction... this fixes bug 1108138
[blackbox.git] / src / Screen.cc
blobe9aa5aac7137b130838bc543f5a3d610fa3eea3d
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 static StackingList::iterator raiseWindow(StackingList &stackingList,
591 StackEntity *entity);
593 void BScreen::manageWindow(Window w) {
594 XWMHints *wmhints = XGetWMHints(_blackbox->XDisplay(), w);
595 bool slit_client = (wmhints && (wmhints->flags & StateHint) &&
596 wmhints->initial_state == WithdrawnState);
597 if (wmhints) XFree(wmhints);
599 if (slit_client) {
600 if (!_slit) createSlit();
601 _slit->addClient(w);
602 return;
605 (void) new BlackboxWindow(_blackbox, w, this);
606 // verify that we have managed the window
607 BlackboxWindow *win = _blackbox->findWindow(w);
608 if (! win) return;
610 if (win->workspace() >= workspaceCount() &&
611 win->workspace() != bt::BSENTINEL)
612 win->setWorkspace(current_workspace);
614 Workspace *workspace = findWorkspace(win->workspace());
615 if (!workspace) {
616 win->setWorkspace(bt::BSENTINEL);
617 win->setWindowNumber(bt::BSENTINEL);
618 } else {
619 workspace->addWindow(win);
622 bool place_window = true;
623 if (_blackbox->startingUp()
624 || ((win->isTransient() || win->windowType() == WindowTypeDesktop
625 || (win->wmNormalHints().flags & (PPosition|USPosition)))
626 && win->clientRect().intersects(screen_info.rect())))
627 place_window = false;
628 if (place_window) placeWindow(win);
630 windowList.push_back(win);
632 // insert window at the top of the stack
633 (void) _stackingList.insert(win);
634 (void) ::raiseWindow(_stackingList, win);
635 if (!_blackbox->startingUp())
636 restackWindows();
638 // 'show' window in the appropriate state
639 switch (_blackbox->startingUp()
640 ? win->currentState()
641 : win->wmHints().initial_state) {
642 case IconicState:
643 win->iconify();
644 break;
646 case WithdrawnState:
647 win->hide();
648 break;
650 default:
651 win->show();
652 break;
653 } // switch
657 void BScreen::releaseWindow(BlackboxWindow *w) {
658 unmanageWindow(w);
659 updateClientListHint();
660 updateClientListStackingHint();
664 void BScreen::unmanageWindow(BlackboxWindow *win) {
665 win->restore();
667 if (win->isIconic()) {
668 _iconmenu->removeItem(win->windowNumber());
669 } else {
670 Workspace *workspace = findWorkspace(win->workspace());
671 if (workspace) {
672 workspace->menu()->removeItem(win->windowNumber());
673 if (workspace->focusedWindow() == win)
674 workspace->clearFocusedWindow();
678 if (_blackbox->running() && win->isFocused()) {
679 // pass focus to the next appropriate window
680 if (focusFallback(win)) {
681 // focus is going somewhere, but we want to avoid dangling pointers
682 _blackbox->forgetFocusedWindow();
683 } else {
684 // explicitly clear the focus
685 _blackbox->setFocusedWindow(0);
689 windowList.remove(win);
690 _stackingList.remove(win);
692 delete win;
696 bool BScreen::focusFallback(const BlackboxWindow *win) {
697 Workspace *workspace = findWorkspace(win->workspace());
698 if (!workspace)
699 workspace = findWorkspace(current_workspace);
701 if (workspace->id() != current_workspace)
702 return false;
704 BWindowGroup *group = win->findWindowGroup();
705 if (group) {
706 // focus the top-most window in the group
707 BlackboxWindowList::const_iterator git = group->windows().begin(),
708 gend = group->windows().end();
709 StackingList::iterator it = _stackingList.begin(),
710 end = _stackingList.end();
711 for (; it != end; ++it) {
712 BlackboxWindow * const tmp = dynamic_cast<BlackboxWindow *>(*it);
713 if (!tmp
714 || tmp == win
715 || std::find(git, gend, tmp) == gend
716 || !tmp->isVisible()
717 || (tmp->workspace() != current_workspace
718 && tmp->workspace() != bt::BSENTINEL))
719 continue;
720 if (tmp->setInputFocus())
721 return true;
725 const BlackboxWindow * const zero = 0;
727 if (win) {
728 if (win->isTransient()) {
729 BlackboxWindow * const tmp = win->findTransientFor();
730 if (tmp
731 && tmp->isVisible()
732 && (tmp->workspace() == current_workspace
733 || tmp->workspace() == bt::BSENTINEL)
734 && tmp->setInputFocus())
735 return true;
738 // try to focus the top-most window in the same layer as win
739 StackingList::iterator it = _stackingList.layer(win->layer()),
740 end = std::find(it, _stackingList.end(), zero);
741 assert(it != _stackingList.end() && end != _stackingList.end());
742 for (; it != end; ++it) {
743 BlackboxWindow * const tmp = dynamic_cast<BlackboxWindow *>(*it);
744 if (!tmp)
745 break;
746 if (tmp == win
747 || !tmp->isVisible()
748 || (tmp->workspace() != current_workspace
749 && tmp->workspace() != bt::BSENTINEL))
750 continue;
751 if (tmp->setInputFocus())
752 return true;
756 // focus the top-most window in the stack
757 StackingList::iterator it = _stackingList.begin(),
758 end = _stackingList.end();
759 for (; it != end; ++it) {
760 BlackboxWindow * const tmp = dynamic_cast<BlackboxWindow *>(*it);
761 if (!tmp
762 || tmp == win
763 || !tmp->isVisible()
764 || (tmp->workspace() != current_workspace
765 && tmp->workspace() != bt::BSENTINEL))
766 continue;
767 if (tmp->setInputFocus())
768 return true;
771 return false;
776 Raises all windows, preserving the existing stacking order. Each
777 window is placed at the top of the layer it currently occupies (with
778 transients above).
780 static
781 void raiseGroup(StackingList &stackingList, BWindowGroup *group) {
782 BlackboxWindowList windows = group->windows();
783 int layer = StackingList::LayerNormal;
784 for (; layer < StackingList::LayerDesktop; ++layer) {
785 StackingList::iterator
786 it, top = stackingList.layer(StackingList::Layer(layer));
787 const StackingList::iterator begin = stackingList.begin(),
788 end = stackingList.end();
790 // 'top' points to the top of the layer, we need to start from the
791 // bottom of the layer
792 it = std::find(top, end, (StackEntity *) 0);
793 assert(it != end);
795 if (!(*top)) {
796 // nothing in layer
797 break;
800 // walk up the layer, raising all windows in the group
801 for (--it; it != begin; --it) {
802 if (!(*it)) {
803 // top of layer
804 break;
807 BlackboxWindow *tmp = dynamic_cast<BlackboxWindow *>(*it);
808 if (!tmp) {
809 // entity is not a window, or window is not visible on the
810 // current_workspace
811 continue;
814 const BlackboxWindowList::iterator wend = windows.end();
815 BlackboxWindowList::iterator wit;
816 if ((wit = std::find(windows.begin(), wend, tmp)) == wend)
817 continue;
819 // found a window in this layer, raise it
820 ++it;
821 stackingList.raise(tmp);
822 // don't bother looking at this window again
823 windows.erase(wit);
830 Raise all transients, preserving the existing stacking order.
832 static
833 void raiseTransients(StackingList::iterator top,
834 StackingList &stackingList,
835 BlackboxWindowList &transients) {
836 // 'top' points to the top of the layer, we need to start from the bottom
837 StackingList::iterator begin = stackingList.begin(),
838 end = stackingList.end(),
839 it = std::find(top, end, (StackEntity *) 0);
840 assert(it != end);
841 for (--it; it != begin; --it) {
842 if (it == top) {
843 // top of layer
844 break;
846 BlackboxWindow *tmp = dynamic_cast<BlackboxWindow *>(*it);
847 if (!tmp)
848 continue;
849 BlackboxWindowList::iterator wit = transients.begin(),
850 wend = transients.end();
851 wit = std::find(wit, wend, tmp);
852 if (wit != wend) {
853 // found a transient in this layer, raise it
854 ++it;
855 stackingList.raise(tmp);
856 // don't bother looking at this window again
857 transients.erase(wit);
864 Raises the specified stacking entity. If the entity is a window,
865 all transients are also raised (preserving their stacking order).
866 If the window is part of a group, the entire group is raised (also
867 preserving the stacking order) befor raising the specified window.
869 The return value indicates which windows need to be restacked:
871 - if raiseWindow() return stackingList.end(), then nothing needs to
872 be restacked.
874 - if raiseWindow() returns an iterator for a layer boundary
875 (i.e. *raiseWindow(...) == 0) then all windows in all layers need to
876 be restacked
878 - otherwise, the return value points to the bottom most window that
879 needs to be restacked (i.e. the top of the layer down to the return
880 value need to be restacked)
882 static
883 StackingList::iterator raiseWindow(StackingList &stackingList,
884 StackEntity *entity) {
885 BlackboxWindow *win = dynamic_cast<BlackboxWindow *>(entity);
886 if (win) {
887 if (win->isFullScreen() && win->layer() != StackingList::LayerFullScreen) {
888 // move full-screen windows over all other windows when raising
889 win->changeLayer(StackingList::LayerFullScreen);
890 return stackingList.end();
894 StackingList::iterator layer = stackingList.layer(entity->layer());
895 if (*layer == entity) {
896 // already on top of the layer
897 return stackingList.end();
900 BWindowGroup *group = 0;
901 if (win) {
902 group = win->findWindowGroup();
903 if (group) {
904 // raise all windows in the group before raising 'win'
905 ::raiseGroup(stackingList, group);
906 } else if (win->isTransient()) {
907 // raise non-transient parent before raising transient
908 BlackboxWindow *tmp = win->findNonTransientParent();
909 if (tmp)
910 (void) ::raiseWindow(stackingList, tmp);
914 // raise the entity
915 StackingList::iterator top = stackingList.raise(entity),
916 end = stackingList.end();
917 assert(top != end);
918 if (win) {
919 // ... with all transients above it
920 BlackboxWindowList transients = win->buildFullTransientList();
921 if (!transients.empty())
922 raiseTransients(top, stackingList, transients);
924 // ... and group transients on top
925 if (group && !win->isGroupTransient()) {
926 const BlackboxWindow * const w = win->isTransient()
927 ? win->findNonTransientParent()
928 : win;
929 if (!w || !w->isGroupTransient()) {
930 BlackboxWindowList groupTransients = group->transients();
931 BlackboxWindowList::const_iterator wit = groupTransients.begin(),
932 wend = groupTransients.end();
933 for (; wit != wend; ++wit) {
934 BlackboxWindowList x = (*wit)->buildFullTransientList();
935 groupTransients.splice(groupTransients.end(), x);
937 if (!groupTransients.empty())
938 raiseTransients(top, stackingList, groupTransients);
942 if (!group)
943 return top;
945 StackingList::iterator it = std::find(top, end, (StackEntity *) 0);
946 assert(it != end);
947 return it;
952 Raises the stack entity as above and then updates the stacking on
953 the X server. The EWMH stacking hint is also updated.
955 void BScreen::raiseWindow(StackEntity *entity) {
956 StackingList::iterator top = ::raiseWindow(_stackingList, entity),
957 end = _stackingList.end();
958 if (top == end) {
959 // no need to raise entity
960 return;
961 } else if (!(*top)) {
962 // need to restack all windows
963 restackWindows();
964 return;
967 // find the first entity above us (if any)
968 StackEntity *above = 0;
969 StackingList::iterator layer = _stackingList.layer(entity->layer()),
970 begin = _stackingList.begin(),
971 it = layer;
972 if (it != begin) {
973 for (--it; it != begin; --it) {
974 if (*it) {
975 above = *it;
976 break;
981 // build the stack
982 WindowStack stack;
983 if (above) {
984 // found another entity above the one we are raising
985 stack.push_back(above->windowID());
986 } else {
987 // keep everying under empty_window
988 stack.push_back(empty_window);
991 // go to the layer boundary above 'top'
992 for (it = top; *it && it != begin; --it)
995 // put all windows from the layer boundary to 'top' into the stack
996 if (!(*it))
997 ++it; // move past layer boundary
998 for (; *it && it != top; ++it) {
999 assert(it != end);
1000 stack.push_back((*it)->windowID());
1002 stack.push_back((*top)->windowID());
1004 XRestackWindows(_blackbox->XDisplay(), &stack[0], stack.size());
1005 updateClientListStackingHint();
1009 static
1010 void lowerGroup(StackingList &stackingList, BWindowGroup *group) {
1011 BlackboxWindowList windows = group->windows();
1012 int layer = StackingList::LayerNormal;
1013 for (; layer < StackingList::LayerDesktop; ++layer) {
1014 const StackingList::iterator begin = stackingList.begin(),
1015 end = stackingList.end();
1016 StackingList::iterator it = stackingList.layer(StackingList::Layer(layer)),
1017 bottom = std::find(it, end, (StackEntity *) 0);
1018 // 'it' points to the top of the layer
1019 assert(bottom != end);
1021 if (!(*it)) {
1022 // nothing in layer
1023 break;
1026 // walk down the layer, lowering all windows in the group
1027 for (; it != bottom; ++it) {
1028 assert(it != end);
1029 BlackboxWindow *tmp = dynamic_cast<BlackboxWindow *>(*it);
1030 if (!tmp) {
1031 // entity is not a window
1032 continue;
1035 const BlackboxWindowList::iterator wend = windows.end();
1036 BlackboxWindowList::iterator wit;
1037 if ((wit = std::find(windows.begin(), wend, tmp)) == wend)
1038 continue;
1040 // found a window in this layer, lower it
1041 --it;
1042 (void) stackingList.lower(tmp);
1043 // don't bother looking at this window again
1044 windows.erase(wit);
1050 static void lowerTransients(StackingList::iterator it,
1051 StackingList &stackingList,
1052 BlackboxWindowList &transients) {
1053 // 'it' points to the top of the layer
1054 const StackingList::iterator end = stackingList.end(),
1055 bottom = std::find(it, end, (StackEntity *) 0);
1056 assert(bottom != end);
1057 for (; it != bottom; ++it) {
1058 assert(it != end);
1059 BlackboxWindow *tmp = dynamic_cast<BlackboxWindow *>(*it);
1060 if (!tmp)
1061 continue;
1063 const BlackboxWindowList::iterator wend = transients.end();
1064 BlackboxWindowList::iterator wit;
1065 if ((wit = std::find(transients.begin(), wend, tmp)) == wend)
1066 continue;
1068 // found a transient in this layer, lower it
1069 --it;
1070 StackingList::iterator l = stackingList.lower(tmp);
1071 // don't bother looking at this window again
1072 transients.erase(wit);
1078 Lowers the specified stacking entity. If the entity is a window,
1079 all transients are also lowered (preserving their stacking order).
1080 If the window is part of a group, the entire group is lowered (also
1081 preserving the stacking order) befor lowering the specified window.
1083 The return value indicates which windows need to be restacked:
1085 - if lowerWindow() return stackingList.end(), then nothing needs to
1086 be restacked.
1088 - if lowerWindow() returns an iterator for a layer boundary
1089 (i.e. *lowerWindow(...) == 0) then all windows in all layers need to
1090 be restacked
1092 - otherwise, the return value points to the top-most window that
1093 needs to be restacked (i.e. the return value down to the bottom of
1094 the layer need to be restacked)
1096 static
1097 StackingList::iterator lowerWindow(StackingList &stackingList,
1098 StackEntity *entity,
1099 bool ignore_group = false) {
1100 StackingList::iterator it, end = stackingList.end();
1101 BlackboxWindow *win = dynamic_cast<BlackboxWindow *>(entity);
1102 if (win) {
1103 it = end;
1104 BWindowGroup *group = win->findWindowGroup();
1105 if (!ignore_group && group) {
1106 // lower all windows in the group before lowering 'win'
1107 ::lowerGroup(stackingList, group);
1108 it = std::find(stackingList.begin(), end, (StackEntity *) 0);
1109 assert(it != end);
1112 const StackingList::iterator layer = stackingList.layer(win->layer());
1113 if (win->isTransient()) {
1114 BlackboxWindow *const tmp = win->findNonTransientParent();
1115 if (tmp && !tmp->isGroupTransient()) {
1116 // lower non-transient parent
1117 (void) ::lowerWindow(stackingList, tmp, true);
1118 if (it == end)
1119 it = layer;
1121 return it;
1124 // lower transients oinf 'win' and 'win'
1125 BlackboxWindowList transients = win->buildFullTransientList();
1126 if (!transients.empty()) {
1127 ::lowerTransients(layer, stackingList, transients);
1128 (void) stackingList.lower(win);
1129 if (it == end)
1130 it = layer;
1131 } else {
1132 if (it == end) {
1133 it = stackingList.lower(entity);
1134 assert(it != end);
1135 } else {
1136 (void) stackingList.lower(entity);
1139 } else {
1140 // lower the entity
1141 it = stackingList.lower(entity);
1142 assert(it != end);
1144 return it;
1148 void BScreen::lowerWindow(StackEntity *entity) {
1149 StackingList::iterator top = ::lowerWindow(_stackingList, entity),
1150 end = _stackingList.end();
1151 if (top == end) {
1152 // no need to lower entity
1153 return;
1154 } else if (!(*top)) {
1155 // need to restack all windows
1156 restackWindows();
1157 return;
1160 // find the entity above us (if any)
1161 StackEntity *above = 0;
1162 StackingList::iterator begin = _stackingList.begin(),
1163 it = top;
1164 if (it != begin) {
1165 for (--it; it != begin; --it) {
1166 if (*it) {
1167 above = *it;
1168 break;
1173 // build the window stack
1174 WindowStack stack;
1175 if (above) {
1176 // found another entity above the one we are lowering
1177 stack.push_back(above->windowID());
1178 } else {
1179 // keep everying under empty_window
1180 stack.push_back(empty_window);
1183 // find the layer boundary
1184 StackingList::iterator bottom = std::find(top, end, (StackEntity *) 0);
1185 assert(bottom != end);
1187 // put all windows from 'top' to the layer boundary into the stack
1188 for (it = top; it != bottom; ++it) {
1189 assert(*it && it != end);
1190 stack.push_back((*it)->windowID());
1192 stack.push_back((*top)->windowID());
1194 XRestackWindows(_blackbox->XDisplay(), &stack[0], stack.size());
1195 updateClientListStackingHint();
1199 void BScreen::restackWindows(void) {
1200 WindowStack stack;
1201 stack.push_back(empty_window);
1203 StackingList::const_iterator it, end = _stackingList.end();
1204 for (it = _stackingList.begin(); it != end; ++it) {
1205 if (*it)
1206 stack.push_back((*it)->windowID());
1209 XRestackWindows(_blackbox->XDisplay(), &stack[0], stack.size());
1210 updateClientListStackingHint();
1214 void BScreen::propagateWindowName(const BlackboxWindow * const win) {
1215 if (win->isIconic()) {
1216 _iconmenu->changeItem(win->windowNumber(),
1217 bt::ellideText(win->iconTitle(), 60,
1218 bt::toUnicode("...")));
1219 } else if (win->workspace() != bt::BSENTINEL) {
1220 Workspace *workspace = findWorkspace(win->workspace());
1221 assert(workspace != 0);
1222 workspace->menu()->changeItem(win->windowNumber(),
1223 bt::ellideText(win->title(), 60,
1224 bt::toUnicode("...")));
1227 if (_toolbar && _blackbox->focusedWindow() == win)
1228 _toolbar->redrawWindowLabel();
1232 void BScreen::nextFocus(void) {
1233 BlackboxWindow *focused = _blackbox->focusedWindow(),
1234 *next = 0;
1235 BlackboxWindowList::iterator it, end = windowList.end();
1237 if (focused && focused->workspace() == current_workspace &&
1238 focused->screen()->screen_info.screenNumber() ==
1239 screen_info.screenNumber()) {
1240 it = std::find(windowList.begin(), end, focused);
1241 assert(it != end);
1242 for (; it != end; ++it) {
1243 next = *it;
1244 if (next && next != focused && next->workspace() == current_workspace &&
1245 next->setInputFocus()) {
1246 // we found our new focus target
1247 next->setInputFocus();
1248 raiseWindow(next);
1249 break;
1251 next = 0;
1254 if (!next) {
1255 for (it = windowList.begin(); it != end; ++it) {
1256 next = *it;
1257 if (next && next->workspace() == current_workspace &&
1258 next->setInputFocus()) {
1259 // we found our new focus target
1260 raiseWindow(next);
1261 break;
1268 void BScreen::prevFocus(void) {
1269 BlackboxWindow *focused = _blackbox->focusedWindow(),
1270 *next = 0;
1271 BlackboxWindowList::reverse_iterator it, end = windowList.rend();
1273 if (focused && focused->workspace() == current_workspace &&
1274 focused->screen()->screen_info.screenNumber() ==
1275 screen_info.screenNumber()) {
1276 it = std::find(windowList.rbegin(), end, focused);
1277 assert(it != end);
1278 for (; it != end; ++it) {
1279 next = *it;
1280 if (next && next != focused && next->workspace() == current_workspace &&
1281 next->setInputFocus()) {
1282 // we found our new focus target
1283 next->setInputFocus();
1284 raiseWindow(next);
1285 break;
1287 next = 0;
1290 if (!next) {
1291 for (it = windowList.rbegin(); it != end; ++it) {
1292 next = *it;
1293 if (next && next->workspace() == current_workspace &&
1294 next->setInputFocus()) {
1295 // we found our new focus target
1296 raiseWindow(next);
1297 break;
1304 void BScreen::raiseFocus(void) {
1305 BlackboxWindow *focused = _blackbox->focusedWindow();
1306 if (! focused || focused->screen() != this)
1307 return;
1309 raiseWindow(focused);
1313 void BScreen::InitMenu(void) {
1314 if (_rootmenu) {
1315 _rootmenu->clear();
1316 } else {
1317 _rootmenu = new Rootmenu(*_blackbox, screen_info.screenNumber(), this);
1318 _rootmenu->showTitle();
1320 bool defaultMenu = True;
1322 if (_blackbox->resource().menuFilename()) {
1323 FILE *menu_file = fopen(_blackbox->resource().menuFilename(), "r");
1325 if (!menu_file) {
1326 perror(_blackbox->resource().menuFilename());
1327 } else {
1328 if (feof(menu_file)) {
1329 fprintf(stderr, "%s: menu file '%s' is empty\n",
1330 _blackbox->applicationName().c_str(),
1331 _blackbox->resource().menuFilename());
1332 } else {
1333 char line[1024], label[1024];
1334 memset(line, 0, 1024);
1335 memset(label, 0, 1024);
1337 while (fgets(line, 1024, menu_file) && ! feof(menu_file)) {
1338 if (line[0] == '#')
1339 continue;
1341 int i, key = 0, index = -1, len = strlen(line);
1343 for (i = 0; i < len; i++) {
1344 if (line[i] == '[') index = 0;
1345 else if (line[i] == ']') break;
1346 else if (line[i] != ' ')
1347 if (index++ >= 0)
1348 key += tolower(line[i]);
1351 if (key == 517) { // [begin]
1352 index = -1;
1353 for (i = index; i < len; i++) {
1354 if (line[i] == '(')
1355 index = 0;
1356 else if (line[i] == ')')
1357 break;
1358 else if (index++ >= 0) {
1359 if (line[i] == '\\' && i < len - 1) i++;
1360 label[index - 1] = line[i];
1364 if (index == -1) index = 0;
1365 label[index] = '\0';
1367 _rootmenu->setTitle(bt::toUnicode(label));
1368 defaultMenu = parseMenuFile(menu_file, _rootmenu);
1369 break;
1373 fclose(menu_file);
1377 if (defaultMenu) {
1378 _rootmenu->setTitle(bt::toUnicode("_Blackbox"));
1380 _rootmenu->insertFunction(bt::toUnicode("xterm"),
1381 BScreen::Execute, "xterm");
1382 _rootmenu->insertFunction(bt::toUnicode("Restart"),
1383 BScreen::Restart);
1384 _rootmenu->insertFunction(bt::toUnicode("Exit"),
1385 BScreen::Exit);
1386 } else {
1387 _blackbox->saveMenuFilename(_blackbox->resource().menuFilename());
1392 static
1393 size_t string_within(char begin, char end,
1394 const char *input, size_t start_at, size_t length,
1395 char *output) {
1396 bool parse = False;
1397 size_t index = 0;
1398 size_t i = start_at;
1399 for (; i < length; ++i) {
1400 if (input[i] == begin) {
1401 parse = True;
1402 } else if (input[i] == end) {
1403 break;
1404 } else if (parse) {
1405 if (input[i] == '\\' && i < length - 1) i++;
1406 output[index++] = input[i];
1410 if (parse)
1411 output[index] = '\0';
1412 else
1413 output[0] = '\0';
1415 return i;
1419 bool BScreen::parseMenuFile(FILE *file, Rootmenu *menu) {
1420 char line[1024], keyword[1024], label[1024], command[1024];
1421 bool done = False;
1423 while (! (done || feof(file))) {
1424 memset(line, 0, 1024);
1425 memset(label, 0, 1024);
1426 memset(command, 0, 1024);
1428 if (! fgets(line, 1024, file))
1429 continue;
1431 if (line[0] == '#') // comment, skip it
1432 continue;
1434 size_t line_length = strlen(line);
1435 unsigned int key = 0;
1437 // get the keyword enclosed in []'s
1438 size_t pos = string_within('[', ']', line, 0, line_length, keyword);
1440 if (keyword[0] == '\0') { // no keyword, no menu entry
1441 continue;
1442 } else {
1443 size_t len = strlen(keyword);
1444 for (size_t i = 0; i < len; ++i) {
1445 if (keyword[i] != ' ')
1446 key += tolower(keyword[i]);
1450 // get the label enclosed in ()'s
1451 pos = string_within('(', ')', line, pos, line_length, label);
1453 // get the command enclosed in {}'s
1454 pos = string_within('{', '}', line, pos, line_length, command);
1456 switch (key) {
1457 case 311: // end
1458 done = True;
1460 break;
1462 case 328: // sep
1463 menu->insertSeparator();
1464 break;
1466 case 333: // nop
1467 if (! *label)
1468 label[0] = '\0';
1469 menu->insertItem(bt::toUnicode(label));
1471 break;
1473 case 421: // exec
1474 if (! (*label && *command)) {
1475 fprintf(stderr,
1476 "%s: [exec] error, no menu label and/or command defined\n",
1477 _blackbox->applicationName().c_str());
1478 continue;
1481 menu->insertFunction(bt::toUnicode(label), BScreen::Execute, command);
1482 break;
1484 case 442: // exit
1485 if (! *label) {
1486 fprintf(stderr, "%s: [exit] error, no menu label defined\n",
1487 _blackbox->applicationName().c_str());
1488 continue;
1491 menu->insertFunction(bt::toUnicode(label), BScreen::Exit);
1492 break;
1494 case 561: { // style
1495 if (! (*label && *command)) {
1496 fprintf(stderr,
1497 "%s: [style] error, no menu label and/or filename defined\n",
1498 _blackbox->applicationName().c_str());
1499 continue;
1502 std::string style = bt::expandTilde(command);
1503 menu->insertFunction(bt::toUnicode(label),
1504 BScreen::SetStyle, style.c_str());
1505 break;
1508 case 630: // config
1509 if (! *label) {
1510 fprintf(stderr, "%s: [config] error, no label defined",
1511 _blackbox->applicationName().c_str());
1512 continue;
1515 menu->insertItem(bt::toUnicode(label), configmenu);
1516 break;
1518 case 740: { // include
1519 if (! *label) {
1520 fprintf(stderr, "%s: [include] error, no filename defined\n",
1521 _blackbox->applicationName().c_str());
1522 continue;
1525 std::string newfile = bt::expandTilde(label);
1526 FILE *submenufile = fopen(newfile.c_str(), "r");
1528 if (! submenufile) {
1529 perror(newfile.c_str());
1530 continue;
1533 struct stat buf;
1534 if (fstat(fileno(submenufile), &buf) ||
1535 ! S_ISREG(buf.st_mode)) {
1536 fprintf(stderr, "%s: [include] error: '%s' is not a regular file\n",
1537 _blackbox->applicationName().c_str(), newfile.c_str());
1538 break;
1541 if (! feof(submenufile)) {
1542 if (! parseMenuFile(submenufile, menu))
1543 _blackbox->saveMenuFilename(newfile);
1545 fclose(submenufile);
1549 break;
1551 case 767: { // submenu
1552 if (! *label) {
1553 fprintf(stderr, "%s: [submenu] error, no menu label defined\n",
1554 _blackbox->applicationName().c_str());
1555 continue;
1558 Rootmenu *submenu =
1559 new Rootmenu(*_blackbox, screen_info.screenNumber(), this);
1560 submenu->showTitle();
1562 if (*command)
1563 submenu->setTitle(bt::toUnicode(command));
1564 else
1565 submenu->setTitle(bt::toUnicode(label));
1567 parseMenuFile(file, submenu);
1568 menu->insertItem(bt::toUnicode(label), submenu);
1571 break;
1573 case 773: { // restart
1574 if (! *label) {
1575 fprintf(stderr, "%s: [restart] error, no menu label defined\n",
1576 _blackbox->applicationName().c_str());
1577 continue;
1580 if (*command) {
1581 menu->insertFunction(bt::toUnicode(label),
1582 BScreen::RestartOther, command);
1583 } else {
1584 menu->insertFunction(bt::toUnicode(label), BScreen::Restart);
1586 break;
1589 case 845: { // reconfig
1590 if (! *label) {
1591 fprintf(stderr, "%s: [reconfig] error, no menu label defined\n",
1592 _blackbox->applicationName().c_str());
1593 continue;
1596 menu->insertFunction(bt::toUnicode(label), BScreen::Reconfigure);
1597 break;
1600 case 995: // stylesdir
1601 case 1113: { // stylesmenu
1602 bool newmenu = ((key == 1113) ? True : False);
1604 if (! *label || (! *command && newmenu)) {
1605 fprintf(stderr,
1606 "%s: [stylesdir/stylesmenu] error, no directory defined\n",
1607 _blackbox->applicationName().c_str());
1608 continue;
1611 char *directory = ((newmenu) ? command : label);
1613 std::string stylesdir = bt::expandTilde(directory);
1615 struct stat statbuf;
1617 if (stat(stylesdir.c_str(), &statbuf) == -1) {
1618 fprintf(stderr,
1619 "%s: [stylesdir/stylesmenu] error, '%s' does not exist\n",
1620 _blackbox->applicationName().c_str(), stylesdir.c_str());
1621 continue;
1623 if (! S_ISDIR(statbuf.st_mode)) {
1624 fprintf(stderr,
1625 "%s: [stylesdir/stylesmenu] error, '%s' is not a directory\n",
1626 _blackbox->applicationName().c_str(), stylesdir.c_str());
1627 continue;
1630 Rootmenu *stylesmenu;
1632 if (newmenu) {
1633 stylesmenu =
1634 new Rootmenu(*_blackbox, screen_info.screenNumber(),this);
1635 stylesmenu->showTitle();
1636 } else {
1637 stylesmenu = menu;
1640 DIR *d = opendir(stylesdir.c_str());
1641 struct dirent *p;
1642 std::vector<std::string> ls;
1644 while((p = readdir(d)))
1645 ls.push_back(p->d_name);
1647 closedir(d);
1649 std::sort(ls.begin(), ls.end());
1651 std::vector<std::string>::iterator it = ls.begin(),
1652 end = ls.end();
1653 for (; it != end; ++it) {
1654 std::string& fname = *it;
1656 if (fname[0] == '.' || fname[fname.size()-1] == '~')
1657 continue;
1659 std::string style = stylesdir;
1660 style += '/';
1661 style += fname;
1663 if (! stat(style.c_str(), &statbuf) && S_ISREG(statbuf.st_mode)) {
1664 // convert 'This_Long_Name' to 'This Long Name'
1665 std::replace(fname.begin(), fname.end(), '_', ' ');
1667 stylesmenu->insertFunction(bt::toUnicode(fname),
1668 BScreen::SetStyle, style);
1672 if (newmenu) {
1673 stylesmenu->setTitle(bt::toUnicode(label));
1674 menu->insertItem(bt::toUnicode(label), stylesmenu);
1677 _blackbox->saveMenuFilename(stylesdir);
1679 break;
1681 case 1090: { // workspaces
1682 if (! *label) {
1683 fprintf(stderr, "%s: [workspaces] error, no menu label defined\n",
1684 _blackbox->applicationName().c_str());
1685 continue;
1688 menu->insertItem(bt::toUnicode(label), _workspacemenu);
1689 break;
1691 } // switch
1694 return (menu->count() == 0);
1698 void BScreen::shutdown(void) {
1699 XSelectInput(_blackbox->XDisplay(), screen_info.rootWindow(),
1700 NoEventMask);
1701 XSync(_blackbox->XDisplay(), False);
1703 // unmanage all windows, but keep them in the current stacking order
1704 WindowStack stack;
1705 StackingList::const_iterator it, end = _stackingList.end();
1706 for (it = _stackingList.begin(); it != end; ++it) {
1707 if (!(*it))
1708 continue;
1709 const BlackboxWindow * const win = dynamic_cast<BlackboxWindow *>(*it);
1710 if (!win)
1711 continue;
1712 stack.push_back(win->clientWindow());
1715 while(! windowList.empty())
1716 unmanageWindow(windowList.back());
1718 if (!stack.empty())
1719 XRestackWindows(_blackbox->XDisplay(), &stack[0], stack.size());
1721 if (_slit)
1722 _slit->shutdown();
1726 void BScreen::showGeometry(GeometryType type, const bt::Rect &rect) {
1727 if (! geom_visible) {
1728 XMoveResizeWindow(_blackbox->XDisplay(), geom_window,
1729 (screen_info.width() - geom_w) / 2,
1730 (screen_info.height() - geom_h) / 2, geom_w, geom_h);
1731 XMapWindow(_blackbox->XDisplay(), geom_window);
1732 XRaiseWindow(_blackbox->XDisplay(), geom_window);
1734 geom_visible = True;
1737 char label[80];
1738 switch (type) {
1739 case Position:
1740 sprintf(label, "X:%4d Y:%4d", rect.x(), rect.y());
1741 break;
1742 case Size:
1743 sprintf(label, "W:%4u H:%4u", rect.width(), rect.height());
1744 break;
1745 default:
1746 assert(0);
1749 const WindowStyle &style = _resource.windowStyle();
1750 const bt::Texture &texture =
1751 (style.focus.label.texture() == bt::Texture::Parent_Relative)
1752 ? style.focus.title
1753 : style.focus.label;
1754 const bt::Rect u(0, 0, geom_w, geom_h);
1755 const bt::Rect t(style.label_margin,
1756 style.label_margin,
1757 geom_w - (style.label_margin * 2),
1758 geom_h - (style.label_margin * 2));
1759 const bt::Pen pen(screen_info.screenNumber(), style.focus.text);
1760 bt::drawTexture(screen_info.screenNumber(), texture, geom_window,
1761 u, u, geom_pixmap);
1762 bt::drawText(style.font, pen, geom_window, t, style.alignment,
1763 bt::toUnicode(label));
1767 void BScreen::hideGeometry(void) {
1768 if (geom_visible) {
1769 XUnmapWindow(_blackbox->XDisplay(), geom_window);
1770 geom_visible = False;
1775 void BScreen::addStrut(bt::EWMH::Strut *strut) {
1776 strutList.push_back(strut);
1777 updateAvailableArea();
1781 void BScreen::removeStrut(bt::EWMH::Strut *strut) {
1782 strutList.remove(strut);
1783 updateAvailableArea();
1787 void BScreen::updateStrut(void) {
1788 updateAvailableArea();
1792 const bt::Rect& BScreen::availableArea(void) {
1793 if (_blackbox->resource().fullMaximization())
1794 return screen_info.rect(); // return the full screen
1795 return usableArea;
1799 void BScreen::updateAvailableArea(void) {
1800 bt::Rect new_area;
1802 /* these values represent offsets from the screen edge
1803 * we look for the biggest offset on each edge and then apply them
1804 * all at once
1805 * do not be confused by the similarity to the names of Rect's members
1807 bt::EWMH::Strut current;
1809 StrutList::const_iterator sit = strutList.begin(), send = strutList.end();
1811 for(; sit != send; ++sit) {
1812 const bt::EWMH::Strut* const strut = *sit;
1813 if (strut->left > current.left)
1814 current.left = strut->left;
1815 if (strut->top > current.top)
1816 current.top = strut->top;
1817 if (strut->right > current.right)
1818 current.right = strut->right;
1819 if (strut->bottom > current.bottom)
1820 current.bottom = strut->bottom;
1823 new_area.setPos(current.left, current.top);
1824 new_area.setSize(screen_info.width() - (current.left + current.right),
1825 screen_info.height() - (current.top + current.bottom));
1827 if (new_area != usableArea) {
1828 usableArea = new_area;
1829 BlackboxWindowList::iterator wit = windowList.begin(),
1830 wend = windowList.end();
1831 for (; wit != wend; ++wit)
1832 if ((*wit)->isMaximized()) (*wit)->remaximize();
1834 updateWorkareaHint();
1839 Workspace* BScreen::findWorkspace(unsigned int index) const {
1840 if (index == bt::BSENTINEL)
1841 return 0;
1842 assert(index < workspacesList.size());
1843 return workspacesList[index];
1847 void BScreen::clientMessageEvent(const XClientMessageEvent * const event) {
1848 if (event->format != 32) return;
1850 if (event->message_type == _blackbox->ewmh().numberOfDesktops()) {
1851 unsigned int number = event->data.l[0];
1852 if (number > workspaceCount()) {
1853 for (; number != workspaceCount(); --number)
1854 addWorkspace();
1855 } else if (number < workspaceCount()) {
1856 for (; number != workspaceCount(); ++number)
1857 removeLastWorkspace();
1859 } else if (event->message_type == _blackbox->ewmh().desktopNames()) {
1860 readDesktopNames();
1861 } else if (event->message_type == _blackbox->ewmh().currentDesktop()) {
1862 const unsigned int workspace = event->data.l[0];
1863 if (workspace < workspaceCount() && workspace != current_workspace)
1864 setCurrentWorkspace(workspace);
1869 void BScreen::buttonPressEvent(const XButtonEvent * const event) {
1870 if (event->button == 1) {
1872 set this screen active. keygrabs and input focus will stay on
1873 this screen until the user focuses a window on another screen or
1874 makes another screen active.
1876 _blackbox->setActiveScreen(this);
1877 } else if (event->button == 2) {
1878 _workspacemenu->popup(event->x_root, event->y_root);
1879 } else if (event->button == 3) {
1880 _blackbox->checkMenu();
1881 _rootmenu->popup(event->x_root, event->y_root);
1882 } else if (event->button == 4) {
1883 setCurrentWorkspace(current_workspace < workspacesList.size() - 1
1884 ? current_workspace + 1
1885 : 0);
1886 } else if (event->button == 5) {
1887 setCurrentWorkspace(current_workspace > 0
1888 ? current_workspace - 1
1889 : workspacesList.size() - 1);
1894 void BScreen::propertyNotifyEvent(const XPropertyEvent * const event) {
1895 if (event->atom == _blackbox->ewmh().activeWindow() && _toolbar)
1896 _toolbar->redrawWindowLabel();
1900 void BScreen::unmapNotifyEvent(const XUnmapEvent * const event) {
1901 // handle synthetic unmap events according to ICCCM section 4.1.4
1902 BlackboxWindow *win = _blackbox->findWindow(event->window);
1903 if (win && event->event == screen_info.rootWindow()
1904 && !event->from_configure)
1905 win->unmapNotifyEvent(event);
1909 void BScreen::toggleFocusModel(FocusModel model) {
1910 std::for_each(windowList.begin(), windowList.end(),
1911 std::mem_fun(&BlackboxWindow::ungrabButtons));
1913 _blackbox->resource().setFocusModel(model);
1915 std::for_each(windowList.begin(), windowList.end(),
1916 std::mem_fun(&BlackboxWindow::grabButtons));
1920 void BScreen::updateWorkareaHint(void) const {
1921 unsigned long *workarea, *tmp;
1923 tmp = workarea = new unsigned long[workspaceCount() * 4];
1925 for (unsigned int i = 0; i < workspaceCount(); ++i) {
1926 tmp[0] = usableArea.x();
1927 tmp[1] = usableArea.y();
1928 tmp[2] = usableArea.width();
1929 tmp[3] = usableArea.height();
1930 tmp += 4;
1933 _blackbox->ewmh().setWorkarea(screen_info.rootWindow(),
1934 workarea, workspaceCount());
1936 delete [] workarea;
1940 void BScreen::updateDesktopNamesHint(void) const {
1941 std::vector<bt::ustring> names(workspacesList.size());
1942 WorkspaceList::const_iterator it = workspacesList.begin();
1943 const WorkspaceList::const_iterator end = workspacesList.end();
1944 for (; it != end; ++it)
1945 names.push_back((*it)->name());
1946 _blackbox->ewmh().setDesktopNames(screen_info.rootWindow(), names);
1950 void BScreen::updateClientListHint(void) const {
1951 if (windowList.empty()) {
1952 _blackbox->ewmh().removeProperty(screen_info.rootWindow(),
1953 _blackbox->ewmh().clientList());
1954 return;
1957 bt::EWMH::WindowList clientList(windowList.size());
1959 std::transform(windowList.begin(), windowList.end(), clientList.begin(),
1960 std::mem_fun(&BlackboxWindow::clientWindow));
1962 _blackbox->ewmh().setClientList(screen_info.rootWindow(), clientList);
1966 void BScreen::updateClientListStackingHint(void) const {
1967 bt::EWMH::WindowList stack;
1969 // we store windows in top-to-bottom order, but the EWMH wants
1970 // bottom-to-top...
1971 StackingList::const_reverse_iterator it = _stackingList.rbegin(),
1972 end = _stackingList.rend();
1973 for (; it != end; ++it) {
1974 const BlackboxWindow * const win = dynamic_cast<BlackboxWindow *>(*it);
1975 if (win) stack.push_back(win->clientWindow());
1978 if (stack.empty()) {
1979 _blackbox->ewmh().removeProperty(screen_info.rootWindow(),
1980 _blackbox->ewmh().clientListStacking());
1981 return;
1984 _blackbox->ewmh().setClientListStacking(screen_info.rootWindow(), stack);
1988 void BScreen::readDesktopNames(void) {
1989 std::vector<bt::ustring> names;
1990 if(! _blackbox->ewmh().readDesktopNames(screen_info.rootWindow(), names))
1991 return;
1993 std::vector<bt::ustring>::const_iterator it = names.begin();
1994 const std::vector<bt::ustring>::const_iterator end = names.end();
1995 WorkspaceList::iterator wit = workspacesList.begin();
1996 const WorkspaceList::iterator wend = workspacesList.end();
1998 for (; wit != wend && it != end; ++wit, ++it) {
1999 if ((*wit)->name() != *it)
2000 (*wit)->setName(*it);
2003 if (names.size() < workspacesList.size())
2004 updateDesktopNamesHint();
2008 BlackboxWindow *BScreen::window(unsigned int workspace, unsigned int id) {
2009 StackingList::const_iterator it = _stackingList.begin(),
2010 end = _stackingList.end();
2011 for (; it != end; ++it) {
2012 BlackboxWindow * const win = dynamic_cast<BlackboxWindow *>(*it);
2013 if (win && win->workspace() == workspace && win->windowNumber() == id)
2014 return win;
2016 assert(false); // should not happen
2017 return 0;
2021 void BScreen::placeWindow(BlackboxWindow *win) {
2022 const bt::Rect &avail = availableArea();
2023 bt::Rect new_win(avail.x(), avail.y(),
2024 win->frameRect().width(), win->frameRect().height());
2025 bool placed = False;
2027 BlackboxResource &res = _blackbox->resource();
2028 switch (res.windowPlacementPolicy()) {
2029 case RowSmartPlacement:
2030 case ColSmartPlacement:
2031 placed = smartPlacement(win->workspace(), new_win, avail);
2032 break;
2033 default:
2034 break; // handled below
2035 } // switch
2037 if (placed == False) {
2038 cascadePlacement(new_win, avail);
2039 cascade_x += _resource.windowStyle().title_height;
2040 cascade_y += _resource.windowStyle().title_height;
2043 if (new_win.right() > avail.right())
2044 new_win.setX(avail.left());
2045 if (new_win.bottom() > avail.bottom())
2046 new_win.setY(avail.top());
2048 win->configure(new_win.x(), new_win.y(), new_win.width(), new_win.height());
2052 bool BScreen::cascadePlacement(bt::Rect &win,
2053 const bt::Rect &avail) {
2054 if (cascade_x > (avail.width() / 2) ||
2055 cascade_y > (avail.height() / 2))
2056 cascade_x = cascade_y = 32;
2058 if (cascade_x == 32) {
2059 cascade_x += avail.x();
2060 cascade_y += avail.y();
2063 win.setPos(cascade_x, cascade_y);
2065 if (win.right() > avail.right() ||
2066 win.bottom() > avail.bottom()) {
2067 cascade_x = cascade_y = 32;
2069 cascade_x += avail.x();
2070 cascade_y += avail.y();
2072 win.setPos(cascade_x, cascade_y);
2075 return True;
2079 bool BScreen::smartPlacement(unsigned int workspace, bt::Rect& rect,
2080 const bt::Rect& avail) {
2081 // constants
2082 const BlackboxResource &res = _blackbox->resource();
2083 const bool row_placement =
2084 (res.windowPlacementPolicy() == RowSmartPlacement);
2085 const bool leftright =
2086 (res.rowPlacementDirection() == LeftRight);
2087 const bool topbottom =
2088 (res.colPlacementDirection() == TopBottom);
2089 const bool ignore_shaded = res.placementIgnoresShaded();
2091 const int left_border = leftright ? 0 : -2;
2092 const int top_border = topbottom ? 0 : -2;
2093 const int right_border = leftright ? 2 : 0;
2094 const int bottom_border = topbottom ? 2 : 0;
2096 StackingList::const_iterator w_it, w_end;
2099 build a sorted vector of x and y grid boundaries
2101 note: we use one vector to reduce the number of allocations
2102 std::vector must do.. we allocate as much memory as we would need
2103 in the worst case scenario and work with that
2105 std::vector<int> coords(_stackingList.size() * 4 + 4);
2106 std::vector<int>::iterator
2107 x_begin = coords.begin(),
2108 x_end = x_begin,
2109 y_begin = coords.begin() + _stackingList.size() * 2 + 2,
2110 y_end = y_begin;
2113 std::vector<int>::iterator x_it = x_begin, y_it = y_begin;
2115 *x_it++ = avail.left();
2116 *x_it++ = avail.right();
2117 x_end += 2;
2119 *y_it++ = avail.top();
2120 *y_it++ = avail.bottom();
2121 y_end += 2;
2124 for (w_it = _stackingList.begin(), w_end = _stackingList.end();
2125 w_it != w_end; ++w_it) {
2126 const BlackboxWindow * const win =
2127 dynamic_cast<const BlackboxWindow *>(*w_it);
2128 if (!win) continue;
2130 if (win->windowType() == WindowTypeDesktop)
2131 continue;
2132 if (win->isIconic())
2133 continue;
2134 if (win->workspace() != bt::BSENTINEL && win->workspace() != workspace)
2135 continue;
2136 if (ignore_shaded && win->isShaded())
2137 continue;
2139 *x_it++ = std::max(win->frameRect().left() + left_border,
2140 avail.left());
2141 *x_it++ = std::min(win->frameRect().right() + right_border,
2142 avail.right());
2143 x_end += 2;
2145 *y_it++ = std::max(win->frameRect().top() + top_border,
2146 avail.top());
2147 *y_it++ = std::min(win->frameRect().bottom() + bottom_border,
2148 avail.bottom());
2149 y_end += 2;
2152 assert(x_end <= y_begin);
2155 std::sort(x_begin, x_end);
2156 x_end = std::unique(x_begin, x_end);
2158 std::sort(y_begin, y_end);
2159 y_end = std::unique(y_begin, y_end);
2161 // build a distribution grid
2162 unsigned int gw = x_end - x_begin - 1,
2163 gh = y_end - y_begin - 1;
2164 std::vector<bool> used_grid(gw * gh);
2165 std::fill_n(used_grid.begin(), used_grid.size(), false);
2167 for (w_it = _stackingList.begin(), w_end = _stackingList.end();
2168 w_it != w_end; ++w_it) {
2169 const BlackboxWindow * const win =
2170 dynamic_cast<const BlackboxWindow *>(*w_it);
2171 if (!win) continue;
2173 if (win->windowType() == WindowTypeDesktop)
2174 continue;
2175 if (win->isIconic())
2176 continue;
2177 if (win->workspace() != bt::BSENTINEL && win->workspace() != workspace)
2178 continue;
2179 if (ignore_shaded && win->isShaded())
2180 continue;
2182 const int w_left =
2183 std::max(win->frameRect().left() + left_border,
2184 avail.left());
2185 const int w_top =
2186 std::max(win->frameRect().top() + top_border,
2187 avail.top());
2188 const int w_right =
2189 std::min(win->frameRect().right() + right_border,
2190 avail.right());
2191 const int w_bottom =
2192 std::min(win->frameRect().bottom() + bottom_border,
2193 avail.bottom());
2195 // which areas of the grid are used by this window?
2196 std::vector<int>::iterator l_it = std::find(x_begin, x_end, w_left),
2197 r_it = std::find(x_begin, x_end, w_right),
2198 t_it = std::find(y_begin, y_end, w_top),
2199 b_it = std::find(y_begin, y_end, w_bottom);
2200 assert(l_it != x_end &&
2201 r_it != x_end &&
2202 t_it != y_end &&
2203 b_it != y_end);
2205 const unsigned int left = l_it - x_begin,
2206 right = r_it - x_begin,
2207 top = t_it - y_begin,
2208 bottom = b_it - y_begin;
2210 for (unsigned int gy = top; gy < bottom; ++gy)
2211 for (unsigned int gx = left; gx < right; ++gx)
2212 used_grid[(gy * gw) + gx] = true;
2216 Attempt to fit the window into any of the empty areas in the grid.
2217 The exact order is dependent upon the users configuration (as
2218 shown below).
2220 row placement:
2221 - outer -> vertical axis
2222 - inner -> horizontal axis
2224 col placement:
2225 - outer -> horizontal axis
2226 - inner -> vertical axis
2229 int gx, gy;
2230 int &outer = row_placement ? gy : gx;
2231 int &inner = row_placement ? gx : gy;
2232 const int outer_delta = row_placement
2233 ? (topbottom ? 1 : -1)
2234 : (leftright ? 1 : -1);
2235 const int inner_delta = row_placement
2236 ? (leftright ? 1 : -1)
2237 : (topbottom ? 1 : -1);
2238 const int outer_begin = row_placement
2239 ? (topbottom ? 0 : static_cast<int>(gh) - 1)
2240 : (leftright ? 0 : static_cast<int>(gw) - 1);
2241 const int outer_end = row_placement
2242 ? (topbottom ? static_cast<int>(gh) : -1)
2243 : (leftright ? static_cast<int>(gw) : -1);
2244 const int inner_begin = row_placement
2245 ? (leftright ? 0 : static_cast<int>(gw) - 1)
2246 : (topbottom ? 0 : static_cast<int>(gh) - 1);
2247 const int inner_end = row_placement
2248 ? (leftright ? static_cast<int>(gw) : -1)
2249 : (topbottom ? static_cast<int>(gh) : -1);
2251 bt::Rect where;
2252 bool fit = false;
2253 for (outer = outer_begin; ! fit && outer != outer_end;
2254 outer += outer_delta) {
2255 for (inner = inner_begin; ! fit && inner != inner_end;
2256 inner += inner_delta) {
2257 // see if the window fits in a single unused area
2258 if (used_grid[(gy * gw) + gx]) continue;
2260 where.setCoords(*(x_begin + gx), *(y_begin + gy),
2261 *(x_begin + gx + 1), *(y_begin + gy + 1));
2263 if (where.width() >= rect.width() &&
2264 where.height() >= rect.height()) {
2265 fit = true;
2266 break;
2270 see if we neighboring spaces are unused
2272 TODO: we should grid fit in the same direction as above,
2273 instead of always right->left and top->bottom
2275 int gx2 = gx, gy2 = gy;
2277 if (rect.width() > where.width()) {
2278 for (gx2 = gx+1; gx2 < static_cast<int>(gw); ++gx2) {
2279 if (used_grid[(gy * gw) + gx2]) {
2280 --gx2;
2281 break;
2284 where.setCoords(*(x_begin + gx), *(y_begin + gy),
2285 *(x_begin + gx2 + 1), *(y_begin + gy2 + 1));
2287 if (where.width() >= rect.width()) break;
2290 if (gx2 >= static_cast<int>(gw)) --gx2;
2293 if (rect.height() > where.height()) {
2294 for (gy2 = gy; gy2 < static_cast<int>(gh); ++gy2) {
2295 if (used_grid[(gy2 * gw) + gx]) {
2296 --gy2;
2297 break;
2300 where.setCoords(*(x_begin + gx), *(y_begin + gy),
2301 *(x_begin + gx2 + 1), *(y_begin + gy2 + 1));
2303 if (where.height() >= rect.height()) break;
2306 if (gy2 >= static_cast<int>(gh)) --gy2;
2309 if (where.width() >= rect.width() &&
2310 where.height() >= rect.height()) {
2311 fit = true;
2313 // make sure all spaces are really available
2314 for (int gy3 = gy; gy3 <= gy2; ++gy3) {
2315 for (int gx3 = gx; gx3 <= gx2; ++gx3) {
2316 if (used_grid[(gy3 * gw) + gx3]) {
2317 fit = false;
2318 break;
2326 if (! fit) {
2327 const int screen_area = avail.width() * avail.height();
2328 const int window_area = rect.width() * rect.height();
2329 if (window_area > screen_area / 8) {
2330 // center windows that don't fit (except for small windows)
2331 const int x =
2332 static_cast<int>(avail.x() + (avail.width() - rect.width()) / 2);
2333 const int y =
2334 static_cast<int>(avail.y() + (avail.height() - rect.height()) / 2);
2335 rect.setPos(x, y);
2336 return true;
2338 return false;
2341 // adjust the location() based on left/right and top/bottom placement
2342 if (! leftright)
2343 where.setX(where.right() - rect.width() + 1);
2344 if (! topbottom)
2345 where.setY(where.bottom() - rect.height() + 1);
2347 rect.setPos(where.x(), where.y());
2349 return true;
2353 void BScreen::createSlit(void) {
2354 assert(_slit == 0);
2356 _slit = new Slit(this);
2357 _stackingList.insert(_slit);
2358 restackWindows();
2362 void BScreen::destroySlit(void) {
2363 assert(_slit != 0);
2365 _stackingList.remove(_slit);
2366 delete _slit;
2367 _slit = 0;
2371 void BScreen::createToolbar(void) {
2372 assert(_toolbar == 0);
2374 _toolbar = new Toolbar(this);
2375 _stackingList.insert(_toolbar);
2376 restackWindows();
2380 void BScreen::destroyToolbar(void) {
2381 assert(_toolbar != 0);
2383 _stackingList.remove(_toolbar);
2384 delete _toolbar;
2385 _toolbar = 0;
2389 Windowmenu *BScreen::windowmenu(BlackboxWindow *win) {
2390 if (!_windowmenu)
2391 _windowmenu = new Windowmenu(*_blackbox, screen_info.screenNumber());
2392 _windowmenu->setWindow(win);
2393 return _windowmenu;
2397 void BScreen::addIcon(BlackboxWindow *win) {
2398 if (win->workspace() != bt::BSENTINEL) {
2399 Workspace *workspace = findWorkspace(win->workspace());
2400 assert(workspace != 0);
2401 workspace->removeWindow(win);
2404 if (win->isTransient()) {
2405 BlackboxWindow * const tmp = win->findNonTransientParent();
2406 if (tmp) {
2407 win->setWindowNumber(bt::BSENTINEL);
2408 return;
2412 const bt::ustring s =
2413 bt::ellideText(win->iconTitle(), 60, bt::toUnicode("..."));
2414 int id = _iconmenu->insertItem(s);
2415 _blackbox->ewmh().setWMVisibleIconName(win->clientWindow(), s);
2416 win->setWindowNumber(id);
2420 void BScreen::removeIcon(BlackboxWindow *win) {
2421 if (win->windowNumber() != bt::BSENTINEL) {
2422 _iconmenu->removeItem(win->windowNumber());
2423 _blackbox->ewmh().removeProperty(win->clientWindow(),
2424 _blackbox->ewmh().wmVisibleIconName());
2427 Workspace *workspace = findWorkspace(current_workspace);
2428 assert(workspace != 0);
2429 workspace->addWindow(win);
2433 BlackboxWindow *BScreen::icon(unsigned int id) {
2434 StackingList::const_iterator it = _stackingList.begin(),
2435 end = _stackingList.end();
2436 for (; it != end; ++it) {
2437 BlackboxWindow * const win = dynamic_cast<BlackboxWindow *>(*it);
2438 if (win && win->isIconic() && win->windowNumber() == id)
2439 return win;
2441 assert(false); // should not happen
2442 return 0;