From 58e19dc91eba51739d4b8ed2dfdbb49e28d96379 Mon Sep 17 00:00:00 2001 From: rathnor Date: Tue, 15 Apr 2003 00:50:25 +0000 Subject: add most recently used window cycling (Simon) It is now the default cycling action --- ChangeLog | 3 + RoadMap | 2 +- src/Keys.cc | 48 +++++++++++---- src/Keys.hh | 17 +++++- src/Screen.cc | 189 ++++++++++++++++++++++++++++++++++++++++++++------------- src/Screen.hh | 25 ++++++-- src/Window.cc | 5 +- src/fluxbox.cc | 74 +++++++++++++++++----- src/fluxbox.hh | 6 +- 9 files changed, 289 insertions(+), 80 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6792791..12766c7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,8 @@ (Format: Year/Month/Day) Changes for 0.9.1: +*03/04/15: + * Add most recently used/stacked window cycling, set as default (Simon) + Window.cc Screen.hh/cc fluxbox.hh/cc Keys.hh/cc *03/04/14: * Don't create menuconfig during install and style cleanups (Han) fluxbox_generate_menu diff --git a/RoadMap b/RoadMap index 2facc92..5e672cb 100644 --- a/RoadMap +++ b/RoadMap @@ -82,7 +82,7 @@ Major Features: * Toolbar modes (Simon) = Tabs embedded in titlebar (Henrik) = Tabgroup rewrite (Henrik) - = Most recently used window cycling (Simon) + * Most recently used window cycling (Simon) Minor Features: - per workspace backgrounds (Simon) - more keybinding actions (Both) diff --git a/src/Keys.cc b/src/Keys.cc index 8d705c1..02cfabd 100644 --- a/src/Keys.cc +++ b/src/Keys.cc @@ -19,7 +19,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -//$Id: Keys.cc,v 1.24 2003/04/14 12:10:16 fluxgen Exp $ +//$Id: Keys.cc,v 1.25 2003/04/15 00:50:24 rathnor Exp $ #include "Keys.hh" @@ -140,14 +140,18 @@ Keys::Keys(const char *filename): m_numlock_mod(0), m_scrolllock_mod(0), m_abortkey(0), - m_display(FbTk::App::instance()->display()) + m_display(FbTk::App::instance()->display()), + m_modmap(0) { - determineModmap(); + loadModmap(); if (filename != 0) load(filename); } Keys::~Keys() { + if (m_modmap) { + XFreeModifiermap(m_modmap); + } ungrabKeys(); deleteTree(); } @@ -458,7 +462,7 @@ unsigned int Keys::getKey(const char *keystr) { Keys::KeyAction Keys::getAction(XKeyEvent *ke) { static t_key *next_key = 0; //remove numlock, capslock and scrolllock - ke->state &= ~Mod2Mask & ~Mod5Mask & ~LockMask; + ke->state = cleanMods(ke->state); if (m_abortkey && *m_abortkey==ke) { //abort current keychain next_key = 0; @@ -636,9 +640,14 @@ Keys::t_key::~t_key() { } /** - determines modifier mapping for caps, num and scroll lock -*/ -void Keys::determineModmap() { + * load state relating to the modifier map + */ +void Keys::loadModmap() { + if (m_modmap) { + XFreeModifiermap(m_modmap); + } + m_modmap = XGetModifierMapping(m_display); + // mask to use for modifier int mods[] = { ShiftMask, @@ -652,15 +661,14 @@ void Keys::determineModmap() { 0 }; - XModifierKeymap *map = XGetModifierMapping(m_display); // find modifiers and set them for (int i=0, realkey=0; i<8; ++i) { - for (int key=0; keymax_keypermod; ++key, ++realkey) { + for (int key=0; keymax_keypermod; ++key, ++realkey) { - if (map->modifiermap[realkey] == 0) + if (m_modmap->modifiermap[realkey] == 0) continue; - KeySym ks = XKeycodeToKeysym(m_display, map->modifiermap[realkey], 0); + KeySym ks = XKeycodeToKeysym(m_display, m_modmap->modifiermap[realkey], 0); switch (ks) { case XK_Caps_Lock: @@ -675,5 +683,21 @@ void Keys::determineModmap() { } } } - XFreeModifiermap(map); +} + +unsigned int Keys::keycodeToModmask(unsigned int keycode) { + if (!m_modmap) return 0; + + // search through modmap for this keycode + for (int mod=0; mod < 8; mod++) { + for (int key=0; key < m_modmap->max_keypermod; ++key) { + // modifiermap is an array with 8 sets of keycodes + // each max_keypermod long, but in a linear array. + if (m_modmap->modifiermap[m_modmap->max_keypermod*mod + key] == keycode) { + return (1<keycode mapping }; #endif // _KEYS_HH_ diff --git a/src/Screen.cc b/src/Screen.cc index af703fb..0d374ef 100644 --- a/src/Screen.cc +++ b/src/Screen.cc @@ -22,7 +22,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -// $Id: Screen.cc,v 1.119 2003/04/14 14:49:47 fluxgen Exp $ +// $Id: Screen.cc,v 1.120 2003/04/15 00:50:24 rathnor Exp $ #include "Screen.hh" @@ -407,6 +407,7 @@ BScreen::BScreen(ResourceManager &rm, m_workspacenames_sig(*this), // workspace names signal m_currentworkspace_sig(*this), // current workspace signal m_layermanager(num_layers), + cycling_focus(false), theme(0), m_windowtheme(scrn), m_menutheme(new FbTk::MenuTheme(scrn)), resource(rm, screenname, altscreenname), @@ -449,6 +450,7 @@ BScreen::BScreen(ResourceManager &rm, (unsigned char *) &bpid, 1); #endif // HAVE_GETPID + cycling_window = focused_list.end(); XDefineCursor(disp, getRootWindow(), fluxbox->getSessionCursor()); @@ -841,6 +843,15 @@ void BScreen::removeWindow(FluxboxWindow *win) { (*it)->removeWindow(win); } + +void BScreen::removeClient(WinClient &client) { + WinClient *cyc = *cycling_window; + focused_list.remove(&client); + if (cyc == &client) { + cycling_window = focused_list.end(); + } +} + FluxboxWindow *BScreen::getIcon(unsigned int index) { if (index < iconList.size()) return iconList[index]; @@ -1130,12 +1141,17 @@ FluxboxWindow *BScreen::createWindow(Window client) { delete win; return 0; } else { + // always put on end of focused list, if it gets focused it'll get pushed up + // there is only the one win client at this stage + focused_list.push_back(&win->winClient()); + + //TODO: is next line needed? Fluxbox::instance()->saveWindowSearch(client, win); Fluxbox::instance()->attachSignals(*win); setupWindowActions(*win); } if (win->getWorkspaceNumber() == getCurrentWorkspaceID() || win->isStuck()) { - win->show(); + win->show(); } XSync(FbTk::App::instance()->display(), False); return win; @@ -1153,6 +1169,9 @@ FluxboxWindow *BScreen::createWindow(WinClient &client) { delete win; return 0; } + // don't add to focused_list, as it should already be in there (since the + // WinClient already exists). + Fluxbox::instance()->saveWindowSearch(client.window(), win); Fluxbox::instance()->attachSignals(*win); setupWindowActions(*win); @@ -1343,27 +1362,58 @@ void BScreen::nextFocus(int opts) { } if (num_windows >= 1) { - Workspace *wksp = getCurrentWorkspace(); - Workspace::Windows &wins = wksp->getWindowList(); - Workspace::Windows::iterator it = wins.begin(); + if (!(opts & CYCLELINEAR)) { + if (!cycling_focus) { + cycling_focus = True; + cycling_window = focused_list.begin(); + } + // if it is stacked, we want the highest window in the focused list + // that is on the same workspace + FocusedWindows::iterator it = cycling_window; + FocusedWindows::iterator it_end = focused_list.end(); + + while (true) { + ++it; + if (it == it_end) { + it = focused_list.begin(); + } + // give up [do nothing] if we reach the current focused again + if ((*it) == (*cycling_window)) { + break; + } - if (!have_focused) { - focused = (*it); - } else { - for (; (*it) != focused; ++it) //get focused window iterator - continue; + FluxboxWindow *fbwin = (*it)->m_win; + if (fbwin && !fbwin->isIconic() && + (fbwin->isStuck() + || fbwin->getWorkspaceNumber() == getCurrentWorkspaceID())) { + // either on this workspace, or stuck + if (! (doSkipWindow(fbwin, opts) || !fbwin->setInputFocus()) ) + break; + } + } + cycling_window = it; + } else { // not stacked cycling + Workspace *wksp = getCurrentWorkspace(); + Workspace::Windows &wins = wksp->getWindowList(); + Workspace::Windows::iterator it = wins.begin(); + + if (!have_focused) { + focused = (*it); + } else { + for (; (*it) != focused; ++it) //get focused window iterator + continue; + } + do { + ++it; + if (it == wins.end()) + it = wins.begin(); + // see if the window should be skipped + if (! (doSkipWindow((*it), opts) || !(*it)->setInputFocus()) ) + break; + } while ((*it) != focused); + if ((*it) != focused && it != wins.end()) + (*it)->raise(); } - do { - ++it; - if (it == wins.end()) - it = wins.begin(); - // see if the window should be skipped - if (! (doSkipWindow((*it), opts) || !(*it)->setInputFocus()) ) - break; - } while ((*it) != focused); - - if ((*it) != focused && it != wins.end()) - (*it)->raise(); } @@ -1385,29 +1435,62 @@ void BScreen::prevFocus(int opts) { } if (num_windows >= 1) { - Workspace *wksp = getCurrentWorkspace(); - Workspace::Windows &wins = wksp->getWindowList(); - Workspace::Windows::iterator it = wins.begin(); + if (!(opts & CYCLELINEAR)) { + if (!cycling_focus) { + cycling_focus = True; + cycling_window = focused_list.end(); + } + // if it is stacked, we want the highest window in the focused list + // that is on the same workspace + FocusedWindows::iterator it = cycling_window; + FocusedWindows::iterator it_end = focused_list.end(); + + while (true) { + --it; + if (it == it_end) { + it = focused_list.end(); + --it; + } + // give up [do nothing] if we reach the current focused again + if ((*it) == (*cycling_window)) { + break; + } - if (!have_focused) { - focused = (*it); - } else { - for (; (*it) != focused; ++it) //get focused window iterator - continue; + FluxboxWindow *fbwin = (*it)->m_win; + if (fbwin && !fbwin->isIconic() && + (fbwin->isStuck() + || fbwin->getWorkspaceNumber() == getCurrentWorkspaceID())) { + // either on this workspace, or stuck + if (! (doSkipWindow(fbwin, opts) || !fbwin->setInputFocus()) ) + break; + } + } + cycling_window = it; + } else { // not stacked cycling + + Workspace *wksp = getCurrentWorkspace(); + Workspace::Windows &wins = wksp->getWindowList(); + Workspace::Windows::iterator it = wins.begin(); + + if (!have_focused) { + focused = (*it); + } else { + for (; (*it) != focused; ++it) //get focused window iterator + continue; + } + + do { + if (it == wins.begin()) + it = wins.end(); + --it; + // see if the window should be skipped + if (! (doSkipWindow((*it), opts) || !(*it)->setInputFocus()) ) + break; + } while ((*it) != focused); + + if ((*it) != focused && it != wins.end()) + (*it)->raise(); } - - do { - if (it == wins.begin()) - it = wins.end(); - --it; - // see if the window should be skipped - if (! (doSkipWindow((*it), opts) || !(*it)->setInputFocus()) ) - break; - } while ((*it) != focused); - - if ((*it) != focused && it != wins.end()) - (*it)->raise(); - } } @@ -1428,6 +1511,15 @@ void BScreen::raiseFocus() { fb->getFocusedWindow()->raise(); } +void BScreen::setFocusedWindow(WinClient &winclient) { + // raise newly focused window to the top of the focused list + if (!cycling_focus) { // don't change the order if we're cycling + focused_list.remove(&winclient); + focused_list.push_front(&winclient); + cycling_window = focused_list.begin(); + } +} + void BScreen::initMenu() { I18n *i18n = I18n::instance(); @@ -2060,6 +2152,19 @@ bool BScreen::doSkipWindow(const FluxboxWindow *w, int opts) { } /** + Called when a set of watched modifiers has been released +*/ +void BScreen::notifyReleasedKeys(XKeyEvent &ke) { + if (cycling_focus) { + cycling_focus = false; + // put currently focused window to top + WinClient *client = *cycling_window; + focused_list.erase(cycling_window); + focused_list.push_front(client); + } +} + +/** Access and clear the auto-group window */ FluxboxWindow* BScreen::useAutoGroupWindow() { diff --git a/src/Screen.hh b/src/Screen.hh index b4adcbf..8be5d42 100644 --- a/src/Screen.hh +++ b/src/Screen.hh @@ -22,7 +22,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -// $Id: Screen.hh,v 1.74 2003/04/14 12:13:36 fluxgen Exp $ +// $Id: Screen.hh,v 1.75 2003/04/15 00:50:24 rathnor Exp $ #ifndef SCREEN_HH #define SCREEN_HH @@ -151,6 +151,7 @@ public: unsigned int getMaxBottom() const; typedef std::vector Icons; + typedef std::list FocusedWindows; /// @return number of workspaces inline unsigned int getCount() const { return workspacesList.size(); } @@ -158,6 +159,8 @@ public: inline unsigned int getIconCount() const { return iconList.size(); } inline const Icons &getIconList() const { return iconList; } inline Icons &getIconList() { return iconList; } + inline const FocusedWindows &getFocusedList() const { return focused_list; } + inline FocusedWindows &getFocusedList() { return focused_list; } const Workspaces &getWorkspacesList() const { return workspacesList; } const WorkspaceNames &getWorkspaceNames() const { return workspaceNames; } /** @@ -258,6 +261,7 @@ public: void removeIcon(FluxboxWindow *win); // remove window void removeWindow(FluxboxWindow *win); + void removeClient(WinClient &client); std::string getNameOfWorkspace(unsigned int workspace) const; void changeWorkspaceID(unsigned int); @@ -269,6 +273,8 @@ public: void prevFocus(int options); void nextFocus(int options); void raiseFocus(); + void setFocusedWindow(WinClient &winclient); + void reconfigure(); void rereadMenu(); void shutdown(); @@ -276,6 +282,8 @@ public: void showGeometry(unsigned int, unsigned int); void hideGeometry(); + void notifyReleasedKeys(XKeyEvent &ke); + void setLayer(FbTk::XLayerItem &item, int layernum); // remove? no, items are never removed from their layer until they die @@ -306,7 +314,7 @@ public: WINDOWLOWER, WINDOWSTICK, WINDOWKILL, SETSTYLE, WINDOWTAB}; // prevFocus/nextFocus option bits enum { CYCLESKIPLOWERTABS = 0x01, CYCLESKIPSTUCK = 0x02, CYCLESKIPSHADED = 0x04, - CYCLEDEFAULT = 0x00 }; + CYCLELINEAR = 0x08, CYCLEDEFAULT = 0x00 }; class ScreenSubject:public FbTk::Subject { public: @@ -334,10 +342,8 @@ private: m_currentworkspace_sig; ///< current workspace signal FbTk::MultLayers m_layermanager; - //!! - Theme *theme; ///< obsolete - Bool root_colormap_installed, managed, geom_visible; + Bool root_colormap_installed, managed, geom_visible, cycling_focus; GC opGC; Pixmap geom_pixmap; FbTk::FbWindow geom_window; @@ -353,6 +359,12 @@ private: Rootmenus rootmenuList; Netizens netizenList; Icons iconList; + + // This list keeps the order of window focusing for this screen + // Screen global so it works for sticky windows too. + FocusedWindows focused_list; + FocusedWindows::iterator cycling_window; + #ifdef SLIT std::auto_ptr m_slit; #endif // SLIT @@ -368,6 +380,9 @@ private: Window auto_group_window; + //!! + Theme *theme; ///< obsolete + FbWinFrameTheme m_windowtheme; std::auto_ptr m_menutheme; diff --git a/src/Window.cc b/src/Window.cc index 2e582ca..4047f1b 100644 --- a/src/Window.cc +++ b/src/Window.cc @@ -22,7 +22,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -// $Id: Window.cc,v 1.131 2003/04/14 14:58:12 fluxgen Exp $ +// $Id: Window.cc,v 1.132 2003/04/15 00:50:25 rathnor Exp $ #include "Window.hh" @@ -1143,7 +1143,8 @@ bool FluxboxWindow::setInputFocus() { } m_frame.setFocus(true); - + screen.setFocusedWindow(*m_client); + Fluxbox::instance()->setFocusedWindow(this); if (send_focus_message) diff --git a/src/fluxbox.cc b/src/fluxbox.cc index 2194b87..158e0db 100644 --- a/src/fluxbox.cc +++ b/src/fluxbox.cc @@ -22,7 +22,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -// $Id: fluxbox.cc,v 1.106 2003/04/14 15:28:52 fluxgen Exp $ +// $Id: fluxbox.cc,v 1.107 2003/04/15 00:50:25 rathnor Exp $ #include "fluxbox.hh" @@ -373,6 +373,7 @@ Fluxbox::Fluxbox(int m_argc, char **m_argv, const char *dpy_name, const char *rc m_rc_cache_max(m_resourcemanager, 200, "session.cacheMax", "Session.CacheMax"), focused_window(0), masked_window(0), timer(this), + watching_screen(0), watch_keyrelease(0), no_focus(false), rc_file(rc ? rc : ""), argv(m_argv), argc(m_argc), @@ -699,6 +700,15 @@ void Fluxbox::handleEvent(XEvent * const e) { case UnmapNotify: handleUnmapNotify(e->xunmap); break; + case MappingNotify: + // Update stored modifier mapping +#ifdef DEBUG + cerr<<__FILE__<<"("<<__FUNCTION__<<"): MappingNotify"<loadModmap(); + } + break; case CreateNotify: break; case DestroyNotify: { @@ -767,6 +777,7 @@ void Fluxbox::handleEvent(XEvent * const e) { } break; + case KeyRelease: case KeyPress: handleKeyEvent(e->xkey); break; @@ -1136,9 +1147,19 @@ void Fluxbox::handleKeyEvent(XKeyEvent &ke) { } break; case Keys::NEXTWINDOW: //activate next window + if (!watching_screen && !(key->getParam() & BScreen::CYCLELINEAR)) { + // if stacked cycling, then set a watch for + // the release of exactly these modifiers + watchKeyRelease(screen, Keys::cleanMods(ke.state)); + } screen->nextFocus(key->getParam()); break; case Keys::PREVWINDOW: //activate prev window + if (!watching_screen && !(key->getParam() & BScreen::CYCLELINEAR)) { + // if stacked cycling, then set a watch for + // the release of exactly these modifiers + watchKeyRelease(screen, Keys::cleanMods(ke.state)); + } screen->prevFocus(key->getParam()); break; case Keys::NEXTTAB: @@ -1242,7 +1263,30 @@ void Fluxbox::handleKeyEvent(XKeyEvent &ke) { break; } - + case KeyRelease: + { + // we ignore most key releases unless we need to use + // a release to stop something (e.g. window cycling). + + // we notify if _all_ of the watched modifiers are released + if (watching_screen && watch_keyrelease) { + // mask the mod of the released key out + // won't mask anything if it isn't a mod + ke.state &= ~key->keycodeToModmask(ke.keycode); + + if ((watch_keyrelease & ke.state) == 0) { + + watching_screen->notifyReleasedKeys(ke); + XUngrabKeyboard(getXDisplay(), CurrentTime); + + // once they are released, we drop the watch + watching_screen = 0; + watch_keyrelease = 0; + } + } + + break; + } default: break; } @@ -1470,10 +1514,7 @@ void Fluxbox::update(FbTk::Subject *changedsub) { } // make sure each workspace get this BScreen &scr = win.getScreen(); - for (int workspace = 0; workspace < scr.getNumberOfWorkspaces(); - ++workspace) { - scr.getWorkspace(workspace)->removeWindow(&win); - } + scr.removeWindow(&win); } else if ((&(win.workspaceSig())) == changedsub) { // workspace signal for (size_t i=0; i(changedsub); WinClient &client = subj->winClient(); - //!! TODO we shouldn't call update netizen on every screen - // just the screen it was located on - ScreenList::iterator screen_it = screenList.begin(); - const ScreenList::iterator screen_it_end = screenList.end(); - for (; screen_it != screen_it_end; ++screen_it) - (*screen_it)->updateNetizenWindowDel(client.window()); + if (client.fbwindow()) { + BScreen &screen = client.fbwindow()->getScreen(); + screen.updateNetizenWindowDel(client.window()); + screen.removeClient(client); + } removeWindowSearch(client.window()); //!! TODO @@ -2232,13 +2272,12 @@ void Fluxbox::setFocusedWindow(FluxboxWindow *win) { if (focused_window != 0) { old_win = focused_window; old_screen = &old_win->getScreen(); - + old_tbar = old_screen->getToolbar(); old_wkspc = old_screen->getWorkspace(old_win->getWorkspaceNumber()); old_win->setFocusFlag(False); old_wkspc->menu().setItemSelected(old_win->getWindowNumber(), false); - } if (win && ! win->isIconic()) { @@ -2272,3 +2311,10 @@ void Fluxbox::setFocusedWindow(FluxboxWindow *win) { old_screen->updateNetizenWindowFocus(); } + +void Fluxbox::watchKeyRelease(BScreen *screen, unsigned int mods) { + watching_screen = screen; + watch_keyrelease = mods; + XGrabKeyboard(getXDisplay(),screen->getRootWindow(), True, + GrabModeAsync, GrabModeAsync, CurrentTime); +} diff --git a/src/fluxbox.hh b/src/fluxbox.hh index e214979..c5db866 100644 --- a/src/fluxbox.hh +++ b/src/fluxbox.hh @@ -22,7 +22,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. -// $Id: fluxbox.hh,v 1.47 2003/04/14 15:25:14 fluxgen Exp $ +// $Id: fluxbox.hh,v 1.48 2003/04/15 00:50:25 rathnor Exp $ #ifndef FLUXBOX_HH #define FLUXBOX_HH @@ -141,6 +141,8 @@ public: { masked = w; masked_window = bw; } inline void setNoFocus(Bool f) { no_focus = f; } + void watchKeyRelease(BScreen *screen, unsigned int mods); + void setFocusedWindow(FluxboxWindow *w); void shutdown(); void load_rc(BScreen *); @@ -236,6 +238,8 @@ private: FluxboxWindow *focused_window, *masked_window; FbTk::Timer timer; + BScreen *watching_screen; + unsigned int watch_keyrelease; #ifdef HAVE_GETPID Atom fluxbox_pid; -- cgit v0.11.2