- committing code from Markus Ottenbacher for cleaned-up panel navigation
[bbkeys.git] / src / ScreenHandler.cpp
blobf6311bddd331e7e5011281beb7c46a4863336f28
1 // -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; -*-
2 // -- ScreenHandler.cpp --
3 // Copyright (c) 2001 - 2003 Jason 'vanRijn' Kasper <vR at movingparts dot net>
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a
6 // copy of this software and associated documentation files (the "Software"),
7 // to deal in the Software without restriction, including without limitation
8 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 // and/or sell copies of the Software, and to permit persons to whom the
10 // Software is furnished to do so, subject to the following conditions:
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 // DEALINGS IN THE SOFTWARE.
23 // E_O_H_VR
25 #include "../config.h"
27 extern "C" {
28 #ifdef HAVE_STDIO_H
29 # include <stdio.h>
30 #endif // HAVE_STDIO_H
32 #ifdef HAVE_UNISTD_H
33 # include <sys/types.h>
34 # include <unistd.h>
35 #endif // HAVE_UNISTD_H
37 #include <X11/keysym.h>
38 #include <stdlib.h>
41 #include "ScreenHandler.h"
43 using std::cout;
45 //--------------------------------------------------------
46 // Constructor/Destructor
47 //--------------------------------------------------------
48 ScreenHandler::ScreenHandler (KeyClient * k, unsigned int number)
49 : _managed(true), _screenNumber(number),
50 _screenInfo(k->display().screenInfo(number)),
51 _clients(k->clientsList()), _active(k->activeWindow())
53 _keyClient = k;
54 _netclient = k->getNetclient();
55 _config = k->getConfig();
56 _display = k->XDisplay();
57 _last_active = _clients.end();
58 _keyGrabber = k->getKeyGrabber();
59 _keybindings = k->getKeybindings();
60 _root = _screenInfo.rootWindow();
62 // get our lockmasks from bt::Application
63 k->getLockModifiers(_numlockMask, _scrolllockMask);
65 // find a window manager supporting NETWM, waiting for it to load if we must
66 int count = 20; // try 20 times
67 _managed = false;
68 while (! (_keyClient->shuttingDown() || _managed || count <= 0)) {
69 if (! (_managed = findSupportingWM()))
70 sleep(5);
71 --count;
74 if (!_managed) {
75 cout << BBTOOL << ": " << "ScreenHandler: Unable to find a "
76 << "compatible window manager for screen: [" << number << "].\n";
77 return;
80 bt::EWMH::AtomList atoms;
81 if (_netclient->readSupported(_root, atoms)) {
82 cout << BBTOOL << ": " << "ScreenHandler: Supported atoms: [" << atoms.size() << "].\n";
83 } else {
84 cout << BBTOOL << ": " << "ScreenHandler: No supported ewmh hints. Not able to be managed.\n";
85 _managed = false;
86 return;
89 XSelectInput(_display, _root,
90 PropertyChangeMask | KeyPressMask | KeyReleaseMask);
92 // add an event handler for our root window
93 k->insertEventHandler(_root, this);
95 // get configuration options
96 _honor_modifiers = _config->getBoolValue("honormodifiers", false);
97 _raise_while_cycling = _config->getBoolValue("raisewhilecycling", true);
98 _show_cycle_menu = _config->getBoolValue("showcyclemenu", true);
99 _menu_text_justify = _config->getStringValue("menutextjustify", "left");
100 _workspace_columns = _config->getNumberValue("workspacecolumns", 0);
101 _workspace_rows = _config->getNumberValue("workspacerows", 0);
102 _debug = _config->getBoolValue("debug", false);
104 _cycling = false;
106 // our popup window list menu
107 _windowmenu = new WindowlistMenu(this);
110 ScreenHandler::~ScreenHandler ()
112 _keyClient->removeEventHandler( _root );
114 _keyGrabber->ungrabAll(_root);
116 if (_managed)
117 XSelectInput(_display, _root, None);
120 void ScreenHandler::initialize()
122 _keybindings->grabDefaults(this);
124 updateActiveDesktop();
125 updateNumDesktops();
126 updateDesktopNames();
127 updateClientList();
128 updateActiveWindow();
130 // load graphics resource from config file
131 std::string menuTextJustify =
132 _config->getStringValue("menuTextJustify", "right");
133 std::string menuTitleJustify =
134 _config->getStringValue("menuTitleJustify", "right");
136 bt::Resource res(_config->getStringValue("stylefile", DEFAULTSTYLE));
137 res.write("menu.frame.alignment", menuTextJustify.c_str());
138 res.write("menu.title.alignment", menuTitleJustify.c_str());
140 bt::MenuStyle::get(_keyClient->getMainApplication(),
141 _screenNumber)->load(res);
146 bool ScreenHandler::findSupportingWM() {
148 if (_debug)
149 cout << endl << BBTOOL << ": " << "ScreenHandler: in findSupportingWM."<< endl;
151 Window client, tmp;
152 bool res = false;
154 res = _netclient->readSupportingWMCheck(_root, &client);
155 if (!res) {
156 if (_debug)
157 cout << BBTOOL << ": " << "ScreenHandler: first readSupportingWMCheck failed." << endl;
158 return false;
161 if (_debug)
162 cout << BBTOOL << ": " << "ScreenHandler: first readSupportingWMCheck succeeded." << endl;
164 res = _netclient->readSupportingWMCheck(client, &tmp);
165 if (!res || client != tmp) {
166 if (_debug)
167 cout << BBTOOL << ": " << "ScreenHandler: second readSupportingWMCheck failed." << endl;
168 return false;
171 if (_debug)
172 cout << BBTOOL << ": " << "ScreenHandler: second readSupportingWMCheck worked." << endl;
174 // now try to get the name of the window manager, using utf8 first
175 // and falling back to ansi if that fails
178 // try ewmh
179 if (! _netclient->getValue(client, _netclient->wmName(),
180 Netclient::utf8, _wm_name)) {
181 if (_debug)
182 cout << BBTOOL << ": " << "ScreenHandler: first try at getting wmName failed." << endl;
183 // try old x stuff
184 _netclient->getValue(client, XA_WM_NAME, Netclient::ansi, _wm_name);
187 if (_wm_name.empty()) {
188 if (_debug)
189 cout << BBTOOL << ": " << "ScreenHandler: couldn't get wm's name. letting it slide this time...." << endl;
190 _wm_name = "beats the heck out of me";
193 cout << BBTOOL << ": " << "ScreenHandler: Found compatible "
194 << "window manager: [" << _wm_name << "] for screen: ["
195 << _screenNumber << "].\n";
197 return true;
201 bool ScreenHandler::grabKey(const KeyCode keyCode,
202 const int modifierMask) const {
203 return _keyGrabber->grab(keyCode, modifierMask, _root );
206 bool ScreenHandler::ungrabKey(const KeyCode keyCode,
207 const int modifierMask) const {
208 return _keyGrabber->ungrab(keyCode, modifierMask, _root );
211 void ScreenHandler::keyPressEvent (const XKeyEvent * const e)
213 unsigned int state = e->state;
215 // Mask out the lock modifiers unless our user doesn't want this
216 if (! _honor_modifiers) {
217 state= e->state & ~(LockMask|_scrolllockMask|_numlockMask);
220 // first, check to see if we're in the middle of a window cycling
221 // loop-de-loop and we're getting a cancel....
222 if (_cycling && e->keycode == XKeysymToKeycode(_display, XK_Escape)) {
224 // we've been told to cancel out of a cycleWindow loop, so we turn
225 // off cycling, ungrab the keyboard, then raise the last-active
226 // window for our user
227 _cycling = false;
228 XUngrabKeyboard(_display, CurrentTime);
230 const XWindow * la = lastActiveWindow();
232 if (la) la->focus(true);
234 return;
237 // if we've made it this far, handle the action....
238 const Action *it = _keybindings->getAction(e, state, this);
240 if (!it)
241 return;
243 switch (it->type()) {
245 case Action::chain:
246 // if we're doing a chain, then keytree has done everything for us...
247 // just return
248 return;
250 case Action::nextScreen:
251 _keyClient->cycleScreen(_screenNumber, true);
252 return;
254 case Action::prevScreen:
255 _keyClient->cycleScreen(_screenNumber, false);
256 return;
258 case Action::nextWorkspace:
259 cycleWorkspace(true, it->number() != 0 ? it->number(): 1);
260 return;
262 case Action::prevWorkspace:
263 cycleWorkspace(false, it->number() != 0 ? it->number(): 1);
264 return;
266 case Action::nextWindow:
267 cycleWindow(state, true, it->number() != 0 ? it->number(): 1);
268 return;
270 case Action::prevWindow:
271 cycleWindow(state, false, it->number() != 0 ? it->number(): 1);
272 return;
274 case Action::nextWindowOnAllWorkspaces:
275 cycleWindow(state, true, it->number() != 0 ? it->number(): 1, false, true);
276 return;
278 case Action::prevWindowOnAllWorkspaces:
279 cycleWindow(state, false, it->number() != 0 ? it->number(): 1, false, true);
280 return;
282 case Action::nextWindowOnAllScreens:
283 cycleWindow(state, true, it->number() != 0 ? it->number(): 1, true);
284 return;
286 case Action::prevWindowOnAllScreens:
287 cycleWindow(state, false, it->number() != 0 ? it->number(): 1, true);
288 return;
290 case Action::nextWindowOfClass:
291 cycleWindow(state, true, it->number() != 0 ? it->number(): 1,
292 false, false, true, it->string());
293 return;
295 case Action::prevWindowOfClass:
296 cycleWindow(state, false, it->number() != 0 ? it->number(): 1,
297 false, false, true, it->string());
298 return;
300 case Action::nextWindowOfClassOnAllWorkspaces:
301 cycleWindow(state, true, it->number() != 0 ? it->number(): 1,
302 false, true, true, it->string());
303 return;
305 case Action::prevWindowOfClassOnAllWorkspaces:
306 cycleWindow(state, false, it->number() != 0 ? it->number(): 1,
307 false, true, true, it->string());
308 return;
310 case Action::changeWorkspace:
311 changeWorkspace(it->number());
312 return;
314 case Action::upWorkspace:
315 changeWorkspaceVert(-1);
316 return;
318 case Action::downWorkspace:
319 changeWorkspaceVert(1);
320 return;
322 case Action::leftWorkspace:
323 changeWorkspaceHorz(-1);
324 return;
326 case Action::rightWorkspace:
327 changeWorkspaceHorz(1);
328 return;
330 case Action::execute:
331 execCommand(it->string());
332 return;
334 case Action::showRootMenu:
335 _netclient->sendClientMessage(_root, _netclient->xaOpenboxShowRootMenu(),
336 None);
337 return;
339 case Action::showWorkspaceMenu:
340 _netclient->sendClientMessage(_root, _netclient->xaOpenboxShowWorkspaceMenu(),
341 None);
342 return;
344 case Action::toggleGrabs: {
345 if (_grabbed) {
346 _keybindings->ungrabDefaults(this);
347 _grabbed = false;
348 } else {
349 _keybindings->grabDefaults(this);
350 _grabbed = true;
352 return;
355 default:
356 break;
359 // these actions require an active window
360 if (_active != _clients.end()) {
361 XWindow *window = *_active;
363 switch (it->type()) {
364 case Action::iconify:
365 window->iconify();
366 return;
368 case Action::close:
369 window->close();
370 return;
372 case Action::raise:
373 window->raise();
374 return;
376 case Action::lower:
377 window->lower();
378 return;
380 case Action::sendToWorkspace:
381 window->sendTo(it->number());
382 return;
384 case Action::toggleOmnipresent:
385 if (window->desktop() == 0xffffffff)
386 window->sendTo(_active_desktop);
387 else
388 window->sendTo(0xffffffff);
389 return;
391 case Action::moveWindowUp:
392 window->move(0, -(it->number() != 0 ? it->number(): 1));
393 return;
395 case Action::moveWindowDown:
396 window->move(0, it->number() != 0 ? it->number(): 1);
397 return;
399 case Action::moveWindowLeft:
400 window->move(-(it->number() != 0 ? it->number(): 1), 0);
401 return;
403 case Action::moveWindowRight:
404 window->move(it->number() != 0 ? it->number(): 1,0);
405 return;
407 case Action::resizeWindowWidth:
408 window->resizeRel(it->number(), 0);
409 return;
411 case Action::resizeWindowHeight:
412 window->resizeRel(0, it->number());
413 return;
415 case Action::toggleShade:
416 window->shade(! window->shaded());
417 return;
419 case Action::toggleMaximizeHorizontal:
420 window->toggleMaximize(XWindow::Max_Horz);
421 return;
423 case Action::toggleMaximizeVertical:
424 window->toggleMaximize(XWindow::Max_Vert);
425 return;
427 case Action::toggleMaximizeFull:
428 window->toggleMaximize(XWindow::Max_Full);
429 return;
431 case Action::toggleDecorations:
432 window->decorate(! window->decorated());
433 return;
435 default:
436 assert(false); // unhandled action type!
437 break;
442 void ScreenHandler::keyReleaseEvent (const XKeyEvent * const e)
444 // the only keyrelease event we care about (for now) is when we do window
445 // cycling and the modifier is released
446 if ( _cycling && nothingIsPressed()) {
447 // all modifiers have been released. ungrab the keyboard, move the
448 // focused window to the top of the Z-order and raise it
449 XUngrabKeyboard(_display, CurrentTime);
451 if (_active != _clients.end()) {
452 XWindow *w = *_active;
453 bool e = _last_active == _active;
454 _clients.remove(w);
455 _clients.push_front(w);
456 _active = _clients.begin();
457 if (!e) _last_active = _active;
458 w->raise();
461 _cycling = false;
465 void ScreenHandler::propertyNotifyEvent(const XPropertyEvent * const e)
467 if (e->atom == _netclient->numberOfDesktops()) {
468 updateNumDesktops();
469 } else if (e->atom == _netclient->desktopNames()) {
470 updateDesktopNames();
471 } else if (e->atom == _netclient->currentDesktop()) {
472 updateActiveDesktop();
473 } else if (e->atom == _netclient->activeWindow()) {
474 updateActiveWindow();
475 } else if (e->atom == _netclient->clientList()) {
476 updateClientList();
480 void ScreenHandler::updateNumDesktops()
482 assert(_managed);
484 if (! _netclient->readNumberOfDesktops(_root, & _num_desktops))
485 _num_desktops = 1; // assume that there is at least 1 desktop!
489 void ScreenHandler::updateDesktopNames()
491 assert(_managed);
493 if(! _netclient->readDesktopNames(_root, _desktop_names)) {
494 _desktop_names.clear();
495 return;
498 // bt::EWMH::UTF8StringList::const_iterator it = _desktop_names.begin(),
499 // end = _desktop_names.end();
501 // for (; it != end; ++it) {
502 // std::cout << BBTOOL << ": " << "name: ->" << *it << "<-\n";
503 // char default_name[80];
504 // sprintf(default_name, "Workspace %u", _id + 1);
505 // the_name = default_name;
506 // }
510 bt::ustring ScreenHandler::getDesktopName(unsigned int desktopNbr) const {
512 if (0xFFFFFFFF == desktopNbr)
513 return bt::toUnicode("");
515 if (desktopNbr > _desktop_names.size() )
516 return bt::toUnicode("error");
518 return _desktop_names[desktopNbr];
522 void ScreenHandler::updateActiveDesktop()
524 assert(_managed);
526 if (! _netclient->readCurrentDesktop(_root, & _active_desktop))
527 _active_desktop = 0; // there must be at least one desktop, and it must
528 // be the current one
532 void ScreenHandler::updateActiveWindow()
534 assert(_managed);
536 Window a = None;
537 _netclient->getValue(_root, _netclient->activeWindow(), XA_WINDOW, a);
539 if ( None == a ) {
540 return;
543 WindowList::iterator it, end = _clients.end();
544 for (it = _clients.begin(); it != end; ++it) {
545 if ( (*it)->window() == a) {
546 if ( (*it)->getScreenNumber() != _screenNumber )
547 return;
548 break;
552 _active = it;
554 if (_active != end) {
555 /* if we're not cycling and a window gets focus, add it to the top of the
556 * cycle stack.
559 if ( !_cycling) {
560 XWindow *win = *_active;
561 _clients.remove(win);
562 _clients.push_front(win);
563 _active = _clients.begin();
565 _last_active = _active;
567 if ( _debug )
568 cout <<BBTOOL << ": " << "active window now: [" << bt::toLocale((*_active)->title()) <<"]" <<endl;
575 void ScreenHandler::updateClientList()
578 assert(_managed);
580 WindowList::iterator insert_point = _active;
581 if (insert_point != _clients.end())
582 ++insert_point; // get to the item client the focused client
584 // get the client list from the root window
585 Netclient::WindowList rootclients;
586 unsigned long num = (unsigned) -1;
588 if ( ! _netclient->readClientList(_root, rootclients) ) {
589 cerr << BBTOOL << ": " << "couldn't get client list from WM.\n";
590 num = 0;
591 } else {
592 num = rootclients.size();
595 WindowList::iterator it;
596 const WindowList::iterator end = _clients.end();
597 unsigned long i;
599 for (i = 0; i < num; ++i) {
600 for (it = _clients.begin(); it != end; ++it)
601 if (**it == rootclients[i])
602 break;
603 if (it == end) { // didn't already exist
604 if (careAboutWindow(rootclients[i])) {
605 XWindow * wTmp = new XWindow( rootclients[i], _netclient, _screenInfo , *_keyClient );
606 _clients.insert(insert_point, wTmp);
611 // remove clients that no longer exist (that belong to this screen)
612 for (it = _clients.begin(); it != end;) {
613 WindowList::iterator it2 = it;
614 ++it;
616 // is on another screen?
617 if ((*it2)->getScreenNumber() != _screenNumber)
618 continue;
620 for (i = 0; i < num; ++i)
621 if (**it2 == rootclients[i])
622 break;
623 if (i == num) { // no longer exists
624 // watch for the active and last-active window
625 if (it2 == _active)
626 _active = _clients.end();
627 if (it2 == _last_active)
628 _last_active = _clients.end();
629 delete *it2;
630 _clients.erase(it2);
637 // do we care about this window as a client?
638 bool ScreenHandler::careAboutWindow(Window window) const
640 assert(_managed);
642 Atom type;
643 if (! _netclient->getValue(window, _netclient->wmWindowType(), XA_ATOM,
644 type)) {
645 return True;
648 if (type == _netclient->wmWindowTypeDock() ||
649 type == _netclient->wmWindowTypeMenu() ) {
650 return False;
651 } else {
652 return True;
656 XWindow * ScreenHandler::findWindow(Window window) const {
657 assert(_managed);
659 WindowList::const_iterator it, end = _clients.end();
660 for (it = _clients.begin(); it != end; ++it)
661 if (**it == window)
662 break;
663 if(it == end)
664 return 0;
665 return *it;
668 void ScreenHandler::execCommand(const string &cmd) const {
669 pid_t pid;
670 if ((pid = fork()) == 0) {
671 // disconnect the child from this session and the tty
672 if (setsid() == -1) {
673 cout << BBTOOL << ": " << "warning: could not start a new process group\n";
674 perror("setsid");
677 // make the command run on the correct screen
678 if (putenv(const_cast<char*>(_screenInfo.displayString().c_str()))) {
679 cout << BBTOOL << ": " << "warning: couldn't set environment variable 'DISPLAY'\n";
680 perror("putenv()");
682 execl("/bin/sh", "sh", "-c", cmd.c_str(), NULL);
683 exit(-1);
684 } else if (pid == -1) {
685 cout << BBTOOL << ": " << ": Could not fork a process for executing a command\n";
689 WindowList ScreenHandler::getCycleWindowList(unsigned int state, const bool forward,
690 const int increment, const bool allscreens,
691 const bool alldesktops, const bool sameclass,
692 const string &cn)
694 assert(_managed);
695 assert(increment > 0);
697 WindowList theList;
699 if (_clients.empty()) return theList;
701 string classname(cn);
702 if (sameclass && classname.empty() && _active != _clients.end())
703 classname = (*_active)->appClass();
706 WindowList::const_iterator it = _clients.begin();
707 const WindowList::const_iterator end = _clients.end();
709 for (; it != end; ++it) {
710 XWindow *t = *it;
712 // determine if this window is invalid for cycling to
713 if (t->iconic()) continue;
714 if (! allscreens && t->getScreenNumber() != _screenNumber) continue;
715 if (! alldesktops && ! (t->desktop() == _active_desktop ||
716 t->desktop() == 0xffffffff)) continue;
717 if (sameclass && ! classname.empty() &&
718 t->appClass() != classname) continue;
719 if (! t->canFocus()) continue;
720 if (t->skipPager()) continue;
722 // found a focusable window
723 theList.push_back(t);
726 return theList;
730 void ScreenHandler::cycleWindow(unsigned int state, const bool forward,
731 const int increment, const bool allscreens,
732 const bool alldesktops, const bool sameclass,
733 const string &cn)
735 assert(_managed);
736 assert(increment > 0);
738 if (_clients.empty()) return;
740 // if our user wants the window cycling menu to show (who wouldn't!!
741 // =:) ) and if it's not already showing...
742 if ( _show_cycle_menu && ! _windowmenu->isVisible() ) {
743 if (_debug)
744 std::cout << BBTOOL << ": " << "ScreenHandler: menu not visible. loading and showing..." << std::endl;
746 _cycling = true;
747 WindowList theList = getCycleWindowList(state, forward, increment,
748 allscreens, alldesktops,
749 sameclass, cn);
750 // can't show the window list if there's not even one window =:)
751 if (theList.size() >= 1)
752 _windowmenu->showCycleMenu(theList);
754 return;
758 string classname(cn);
759 if (sameclass && classname.empty() && _active != _clients.end())
760 classname = (*_active)->appClass();
762 WindowList::const_iterator target = _active,
763 begin = _clients.begin(),
764 end = _clients.end();
766 XWindow *t = 0;
768 for (int x = 0; x < increment; ++x) {
769 while (1) {
770 if (forward) {
771 if (target == end)
772 target = begin;
773 else
774 ++target;
775 } else {
776 if (target == begin)
777 target = end;
778 else
779 --target;
782 // must be no window to focus
783 if (target == _active)
784 return;
786 // start back at the beginning of the loop
787 if (target == end)
788 continue;
790 // determine if this window is invalid for cycling to
791 t = *target;
792 if (t->iconic()) continue;
793 if (! allscreens && t->getScreenNumber() != _screenNumber) continue;
794 if (! alldesktops && ! (t->desktop() == _active_desktop ||
795 t->desktop() == 0xffffffff)) continue;
796 if (sameclass && ! classname.empty() &&
797 t->appClass() != classname) continue;
798 if (! t->canFocus()) continue;
799 if (t->skipPager()) continue;
801 // found a good window so break out of the while, and perhaps continue
802 // with the for loop
803 break;
807 // phew. we found the window, so focus it.
808 if ( state) {
809 if (!_cycling) {
810 // grab keyboard so we can intercept KeyReleases from it
811 XGrabKeyboard(_display, _root, True, GrabModeAsync,
812 GrabModeAsync, CurrentTime);
813 _cycling = true;
815 focusWindow(t);
817 } else {
818 t->focus();
822 void ScreenHandler::focusWindow(const XWindow * win) {
824 // if the window our little user has selected is on a different
825 // workspace, go there first
826 if ( ! win->isSticky() && (_active_desktop != win->desktop() ) ) {
827 changeWorkspace(win->desktop() );
830 // if we're cycling and user wants to raise windows while cycling,
831 // raise the window, else if we're cycling without raising windows,
832 // just set focus on the window, else focus and raise the window
833 if (_cycling) {
834 if (_raise_while_cycling) {
835 win->focus();
836 } else {
837 win->focus(false);
839 } else {
840 win->focus();
845 void ScreenHandler::cycleWorkspace(const bool forward, const int increment,
846 const bool loop) const {
847 assert(_managed);
848 assert(increment > 0);
850 unsigned int destination = _active_desktop;
852 for (int x = 0; x < increment; ++x) {
853 if (forward) {
854 if (destination < _num_desktops - 1)
855 ++destination;
856 else if (loop)
857 destination = 0;
858 } else {
859 if (destination > 0)
860 --destination;
861 else if (loop)
862 destination = _num_desktops - 1;
866 if (destination != _active_desktop)
867 changeWorkspace(destination);
871 void ScreenHandler::changeWorkspace(const int num) const {
872 assert(_managed);
874 _netclient->sendClientMessage(_root, _netclient->currentDesktop(), _root, num);
877 void ScreenHandler::changeWorkspaceVert(const int num) const {
878 assert(_managed);
879 int width = _workspace_columns;
880 int height = _workspace_rows;
881 int total = (signed)_num_desktops; // starts at 1
882 int n = (signed)_active_desktop; // starts at 0
883 int wnum = 0;
884 bool moveUp = (num < 0);
886 // to understand the mathemathics in here, consider the following arrangements:
888 // | 0 1 2 columns | 0 1 2
889 // ------------------- ---------
890 // 0 | 0 1 2 0| 0 3 6
891 // 1 | 3 4 5 1| 1 4 7
892 // 2 | 6 7 2| 2 5
893 // rows
895 // left: horizontal arrangement, right: vertical
896 // last workspace missing, total number of workspaces = 8
897 // n%width = current column
898 // n%height = current row
901 if ( width > 0 ) { // width is set -> assume horizontal arrangement
902 // (default if height is given as well)
904 if ( moveUp ) { // we go up...
905 if ( n < width ) { // we're in the first row and want to change to the last
906 if ( (total-1)%width < n%width ) // last row is incomplete, our column isn't there
907 wnum = total-1 - (total-1)%width + n%width - width; // go to last but one
908 // I guess, there's a more elegant expression for this...
909 else // our column exists in the last row
910 wnum = total-1 - (total-1)%width + n%width; // go to same column in last row
911 } else wnum = n-width; // else, just go up one row
913 } else { // we go down...
915 if ( n+width > total-1 ) // next row would be out of range -> we're in the last
916 wnum = n%width; // current column in first row
917 else
918 wnum = n+width; // go down one row
921 } else if ( height > 0 ) { // height is set -> vertical arrangement
922 if ( moveUp ) {
923 if ( n%height==0 ) { // if in first row
924 if ( n + height > total - 1 ) // we're in an incomplete column
925 wnum = total-1;
926 else
927 wnum = n+height-1;
929 else // current row>1
930 wnum = n-1;
931 } else { // we're on our way down
932 if ( n==total-1 || n%height==height-1 ) // incomplete column or last row
933 wnum = n - n%height ;
934 else
935 wnum = n+1 ;
937 } else {} // no arrangement given -> do nothing
938 changeWorkspace(wnum);
941 void ScreenHandler::changeWorkspaceHorz(const int num) const {
942 assert(_managed);
943 int width = _workspace_columns;
944 int height = _workspace_rows;
945 int total = (signed)_num_desktops;
946 int n = (signed)_active_desktop;
947 int wnum = 0;
948 bool moveLeft = (num < 0);
950 if ( width > 0 ) { // width is set -> assume horizontal arrangement
951 // (default if height is given as well)
953 if ( moveLeft ) {
954 if ( n%width == 0 ) { // if in first col
955 if ( n + width > total - 1 ) // we're in an incomplete row
956 wnum = total-1;
957 else
958 wnum = n+width-1;
959 } else wnum = n-1;
961 } else { // move right
962 if ( n==total-1 || n%width==width-1 ) // incomplete row or last col
963 wnum = n - n%width ;
964 else
965 wnum = n+1 ;
968 } else if ( height > 0 ) { // height is set -> vertical arrangement
969 if ( moveLeft ) {
970 if ( n < height ) { // move first col -> last
971 if ( (total-1)%height < n%height ) // last col is incomplete
972 wnum = total-1 - (total-1)%height + n%height - height; // go to last but one
973 else // our row exists
974 wnum = total-1 - (total-1)%height + n%height; // go to same row in last col
975 } else wnum = n-height; // else, just go left one col
977 } else { // go right
978 if ( n+height > total-1 ) // we would be out of range
979 wnum = n%height; // current row in first col
980 else
981 wnum = n+height; // go down one row
984 changeWorkspace(wnum);
987 bool ScreenHandler::nothingIsPressed(void) const
989 char keys[32];
990 XQueryKeymap(_display, keys);
992 for (int i = 0; i < 32; ++i) {
993 if (keys[i] != 0)
994 return false;
997 return true;
1000 const XWindow *ScreenHandler::lastActiveWindow() const {
1001 if (_last_active != _clients.end())
1002 return *_last_active;
1004 // find a window if one exists
1005 WindowList::const_iterator it, end = _clients.end();
1006 for (it = _clients.begin(); it != end; ++it)
1007 if ((*it)->getScreenNumber() == _screenNumber && ! (*it)->iconic() &&
1008 (*it)->canFocus() &&
1009 ((*it)->desktop() == 0xffffffff ||
1010 (*it)->desktop() == _active_desktop))
1011 return *it;
1013 // no windows on this screen
1014 return 0;
1017 void ScreenHandler::p()
1019 cout << BBTOOL << ": " << "\nNOW LISTING CLIENTS!!!" << endl;
1021 WindowList::const_iterator it = _clients.begin();
1022 const WindowList::const_iterator end = _clients.end();
1024 for (; it != end; ++it)
1025 cout << BBTOOL << ": " << "desktop: ["
1026 << (*it)->desktop()
1027 << "], window: [" << bt::toLocale((*it)->title()) << "]" << endl;