added mouse bindings to the keys file
@@ -1,5 +1,18 @@
(Format: Year/Month/Day) Changes for 1.0rc3: +*07/02/09: + * Introduced support for mouse buttons in the keys file (Mark) + - Mouse buttons are bound to new `keys' Mouse1, Mouse2, etc. + - Also a new modifier `OnDesktop' to specify actions that should take place + when you click on the desktop -- without this, mouse bindings are global + - Obviously, more modifiers are planned, so don't ask + - Introduced new command HideMenus + - Added a utility to the project that automatically updates configuration + files when we change the syntax -- your current mouse bindings on the + desktop will be added to your keys file automatically + FbCommands.cc/hh FbCommandFactory.cc Keys.cc/hh Screen.cc/hh fluxbox.cc + FbTk/KeyUtil.cc/hh WorkspaceCmd.cc data/init.in data/keys util/Makefile.am + Added util/fluxbox-update_configs.cc *07/02/06: * Wrong window was being raised when attaching an unfocused window (Mark) Window.cc
@@ -33,3 +33,4 @@ session.colorsPerChannel: 4
session.doubleClickInterval: 250 session.cacheMax: 200 session.imageDither: True +session.configVersion: 1
@@ -77,6 +77,7 @@ "focusdown",
"focusleft", "focusright", "fullscreen", + "hidemenus", "iconify", "keymode", "killwindow",@@ -433,6 +434,8 @@ else if (command == "arrangewindows")
return new ArrangeWindowsCmd(); else if (command == "showdesktop") return new ShowDesktopCmd(); + else if (command == "hidemenus") + return new HideMenuCmd(); else if (command == "rootmenu") return new ShowRootMenuCmd(); else if (command == "workspacemenu")
@@ -260,6 +260,15 @@ void KeyModeCmd::execute() {
Fluxbox::instance()->keys()->keyMode(m_keymode); } +void HideMenuCmd::execute() { + BScreen *screen = Fluxbox::instance()->mouseScreen(); + screen->hideMenus(); + if (screen->rootMenu().isVisible()) + screen->rootMenu().hide(); + if (screen->workspaceMenu().isVisible()) + screen->workspaceMenu().hide(); +} + void ShowRootMenuCmd::execute() { BScreen *screen = Fluxbox::instance()->mouseScreen(); if (screen == 0)
@@ -113,6 +113,11 @@ std::string m_keymode;
std::string m_end_args; }; +class HideMenuCmd: public FbTk::Command { +public: + void execute(); +}; + class ShowRootMenuCmd: public FbTk::Command { public: void execute();
@@ -138,7 +138,7 @@ // Grab with numlock, capslock and scrlock
for (int i = 0; i < 8; i++) { XGrabButton(display, button, mod | (i & 1 ? capsmod : 0) | (i & 2 ? nummod : 0) | (i & 4 ? scrollmod : 0), - win, True, event_mask, GrabModeAsync, GrabModeAsync, + win, False, event_mask, GrabModeAsync, GrabModeAsync, None, cursor); }@@ -178,6 +178,11 @@ /// Ungrabs the keys
void KeyUtil::ungrabKeys(Window win) { Display * display = App::instance()->display(); XUngrabKey(display, AnyKey, AnyModifier, win); +} + +void KeyUtil::ungrabButtons(Window win) { + Display * display = App::instance()->display(); + XUngrabButton(display, AnyButton, AnyModifier, win); } unsigned int KeyUtil::keycodeToModmask(unsigned int keycode) {
@@ -62,6 +62,7 @@ /**
ungrabs all keys */ static void ungrabKeys(Window win); + static void ungrabButtons(Window win); /** Strip out modifiers we want to ignore
@@ -114,6 +114,7 @@ }
Keys::~Keys() { ungrabKeys(); + ungrabButtons(); deleteTree(); }@@ -140,6 +141,23 @@ for (; it != it_end; ++it)
FbTk::KeyUtil::ungrabKeys(*it); } +void Keys::grabButton(unsigned int button, unsigned int mod) { + std::list<Window>::iterator it = m_window_list.begin(); + std::list<Window>::iterator it_end = m_window_list.end(); + + for (; it != it_end; ++it) + FbTk::KeyUtil::grabButton(button, mod, *it, + ButtonPressMask|ButtonReleaseMask); +} + +void Keys::ungrabButtons() { + std::list<Window>::iterator it = m_window_list.begin(); + std::list<Window>::iterator it_end = m_window_list.end(); + + for (; it != it_end; ++it) + FbTk::KeyUtil::ungrabButtons(*it); +} + /** Load and grab keys TODO: error checking@@ -152,7 +170,7 @@
//free memory of previous grabs deleteTree(); - m_map["default:"] = new t_key(0,0); + m_map["default:"] = new t_key(0,0,0,0); FbTk::App::instance()->sync(false);@@ -211,6 +229,7 @@ if (val[0][0] == '#' || val[0][0] == '!' ) //the line is commented
return true; // still a valid line. unsigned int key = 0, mod = 0; + int type = 0, context = 0; size_t argc = 0; t_key *current_key=m_map["default:"]; t_key *first_new_keylist = current_key, *first_new_key=0;@@ -219,7 +238,7 @@ if (val[0][val[0].length()-1] == ':') {
argc++; keyspace_t::iterator it = m_map.find(val[0]); if (it == m_map.end()) - m_map[val[0]] = new t_key(0,0); + m_map[val[0]] = new t_key(0,0,0,0); current_key = m_map[val[0]]; } // for each argument@@ -230,41 +249,53 @@
int tmpmod = FbTk::KeyUtil::getModifier(val[argc].c_str()); if(tmpmod) mod |= tmpmod; //If it's a modifier + else if (strcasecmp("ondesktop", val[argc].c_str()) == 0) + context |= ON_DESKTOP; else if (strcasecmp("NONE",val[argc].c_str())) { + // check if it's a mouse button + if (!strcasecmp(val[argc].substr(0,5).c_str(), "mouse") && + val[argc].length() > 5) { + type = ButtonPress; + key = atoi(val[argc].substr(5,val[argc].length()-5).c_str()); // keycode covers the following three two-byte cases: // 0x - hex // +[1-9] - number between +1 and +9 // numbers 10 and above // - if (val[argc].size() > 1 && (isdigit(val[argc][0]) && - (isdigit(val[argc][1]) || val[argc][1] == 'x') || - val[argc][0] == '+' && isdigit(val[argc][1])) ) { + } else if (val[argc].size() > 1 && (isdigit(val[argc][0]) && + (isdigit(val[argc][1]) || val[argc][1] == 'x') || + val[argc][0] == '+' && isdigit(val[argc][1])) ) { key = strtoul(val[argc].c_str(), NULL, 0); + type = KeyPress; if (errno == EINVAL || errno == ERANGE) key = 0; - } else // convert from string symbol + } else { // convert from string symbol key = FbTk::KeyUtil::getKey(val[argc].c_str()); + type = KeyPress; + } if (key == 0) return false; if (!first_new_key) { first_new_keylist = current_key; - current_key = current_key->find(key, mod); + current_key = current_key->find(type, mod, key, context); if (!current_key) { - first_new_key = new t_key(key, mod); + first_new_key = new t_key(type, mod, key, context); current_key = first_new_key; } else if (*current_key->m_command) // already being used return false; } else { - t_key *temp_key = new t_key(key, mod); + t_key *temp_key = new t_key(type, mod, key, context); current_key->keylist.push_back(temp_key); current_key = temp_key; } mod = 0; key = 0; + type = 0; + context = 0; } } else { // parse command line@@ -291,36 +322,43 @@ return false;
} // return true if bound to a command, else false -bool Keys::doAction(XKeyEvent &ke) { - - ke.state = FbTk::KeyUtil::instance().cleanMods(ke.state); +bool Keys::doAction(int type, unsigned int mods, unsigned int key) { static t_key* next_key = m_keylist; if (!next_key) next_key = m_keylist; - t_key *temp_key = next_key->find(ke); + mods = FbTk::KeyUtil::instance().cleanMods(mods); + // at the moment, any key/button that gets here is on root window + // context will need to be added as an argument to doAction, though + t_key *temp_key = next_key->find(type, mods, key, ON_DESKTOP|GLOBAL); // need to save this for emacs-style keybindings static t_key *saved_keymode = 0; + // grab "None Escape" to exit keychain in the middle + unsigned int esc = FbTk::KeyUtil::getKey("Escape"); + if (temp_key && temp_key->keylist.size()) { // emacs-style - saved_keymode = m_keylist; + if (!saved_keymode) + saved_keymode = m_keylist; next_key = temp_key; setKeyMode(next_key); - // grab "None Escape" to exit keychain in the middle - unsigned int esc = FbTk::KeyUtil::getKey("Escape"); grabKey(esc,0); return true; } if (!temp_key || *temp_key->m_command == 0) { - next_key = 0; - if (saved_keymode) { - setKeyMode(saved_keymode); - saved_keymode = 0; + if (type == KeyPress && key == esc && mods == 0) { + // if we're in the middle of an emacs-style keychain, exit it + next_key = 0; + if (saved_keymode) { + setKeyMode(saved_keymode); + saved_keymode = 0; + } } return false; } + temp_key->m_command->execute(); if (saved_keymode) { if (next_key == m_keylist) // don't reset keymode if command changed it@@ -349,22 +387,33 @@ }
void Keys::setKeyMode(t_key *keyMode) { ungrabKeys(); + ungrabButtons(); keylist_t::iterator it = keyMode->keylist.begin(); keylist_t::iterator it_end = keyMode->keylist.end(); - for (; it != it_end; ++it) - grabKey((*it)->key,(*it)->mod); + for (; it != it_end; ++it) { + if ((*it)->type == KeyPress) + grabKey((*it)->key,(*it)->mod); + else if ((*it)->context == GLOBAL) + grabButton((*it)->key,(*it)->mod); + // we must use root window's event mask to get ON_DESKTOP events + } m_keylist = keyMode; } -Keys::t_key::t_key(unsigned int key_, unsigned int mod_, FbTk::RefCount<FbTk::Command> command) { +Keys::t_key::t_key(int type_, unsigned int mod_, unsigned int key_, + int context_, FbTk::RefCount<FbTk::Command> command) { key = key_; mod = mod_; + type = type_; + context = context_ ? context_ : GLOBAL; m_command = command; } Keys::t_key::t_key(t_key *k) { key = k->key; mod = k->mod; + type = k->type; + context = k->context; m_command = k->m_command; }
@@ -38,6 +38,21 @@
class Keys:private FbTk::NotCopyable { public: + // contexts for events + // it's ok if there is overlap; it will be worked out in t_key::find() + // eventHandlers should submit bitwise-or of contexts the event happened in + enum { + GLOBAL = 0x01, + ON_DESKTOP = 0x02, + ON_TOOLBAR = 0x04, + ON_ICONBUTTON = 0x08, + ON_TITLEBAR = 0x10, + ON_WINDOW = 0x20, + ON_TAB = 0x40, + ON_SLIT = 0x80 + // and so on... + }; + /** Constructor @param display display connection@@ -65,7 +80,7 @@
/** do action from XKeyEvent; return false if not bound to anything */ - bool doAction(XKeyEvent &ke); + bool doAction(int type, unsigned int mods, unsigned int key); /** Reload configuration from filename@@ -79,6 +94,8 @@ void deleteTree();
void grabKey(unsigned int key, unsigned int mod); void ungrabKeys(); + void grabButton(unsigned int button, unsigned int mod); + void ungrabButtons(); std::string m_filename;@@ -87,33 +104,27 @@ typedef std::vector<t_key*> keylist_t;
class t_key { public: - t_key(unsigned int key, unsigned int mod, + t_key(int type, unsigned int mod, unsigned int key, int context, FbTk::RefCount<FbTk::Command> command = FbTk::RefCount<FbTk::Command>(0)); t_key(t_key *k); ~t_key(); - t_key *find(unsigned int key_, unsigned int mod_) { + t_key *find(int type_, unsigned int mod_, unsigned int key_, + int context_) { for (size_t i = 0; i < keylist.size(); i++) { - if (keylist[i]->key == key_ && keylist[i]->mod == FbTk::KeyUtil::instance().isolateModifierMask(mod_)) - return keylist[i]; - } - return 0; - } - t_key *find(XKeyEvent &ke) { - for (size_t i = 0; i < keylist.size(); i++) { - if (keylist[i]->key == ke.keycode && - keylist[i]->mod == FbTk::KeyUtil::instance().isolateModifierMask(ke.state)) + if (keylist[i]->type == type_ && keylist[i]->key == key_ && + (keylist[i]->context & context_) > 0 && keylist[i]->mod == + FbTk::KeyUtil::instance().isolateModifierMask(mod_)) return keylist[i]; } return 0; } - bool operator == (XKeyEvent &ke) const { - return (mod == FbTk::KeyUtil::instance().isolateModifierMask(ke.state) && key == ke.keycode); - } FbTk::RefCount<FbTk::Command> m_command; - unsigned int key; + int context; // ON_TITLEBAR, etc.: bitwise-or of all desired contexts + int type; // KeyPress or ButtonPress + unsigned int key; // key code or button number unsigned int mod; keylist_t keylist; };
@@ -2060,7 +2060,7 @@
/** Called when a set of watched modifiers has been released */ -void BScreen::notifyReleasedKeys(XKeyEvent &ke) { +void BScreen::notifyReleasedKeys() { focusControl().stopCyclingFocus(); }
@@ -294,7 +294,7 @@ /// show geomentry with "width x height"-text, not size of window
void showGeometry(int width, int height); void hideGeometry(); - void notifyReleasedKeys(XKeyEvent &ke); + void notifyReleasedKeys(); void setLayer(FbTk::XLayerItem &item, int layernum); // remove? no, items are never removed from their layer until they die
@@ -42,44 +42,48 @@ #include <algorithm>
#include <functional> void NextWindowCmd::execute() { - BScreen *screen = Fluxbox::instance()->keyScreen(); + Fluxbox *fb = Fluxbox::instance(); + BScreen *screen = fb->keyScreen(); if (screen != 0) { - Fluxbox *fb = Fluxbox::instance(); - // special case for commands from key events - if (fb->lastEvent().type == KeyPress) { - unsigned int mods = FbTk::KeyUtil::instance().cleanMods(fb->lastEvent().xkey.state); - mods = FbTk::KeyUtil::instance().isolateModifierMask(mods); - if (mods == 0) // can't stacked cycle unless there is a mod to grab - screen->focusControl().nextFocus(m_option | FocusControl::CYCLELINEAR); - else { - // if stacked cycling, then set a watch for - // the release of exactly these modifiers - Fluxbox::instance()->watchKeyRelease(*screen, mods); - screen->focusControl().nextFocus(m_option); - } - } else + // get modifiers from event that causes this for focus order cycling + unsigned int mods = 0; + XEvent ev = fb->lastEvent(); + if (ev.type == KeyPress) { + mods = FbTk::KeyUtil::instance().cleanMods(ev.xkey.state); + } else if (ev.type == ButtonPress) { + mods = FbTk::KeyUtil::instance().cleanMods(ev.xbutton.state); + } + if (mods == 0) // can't stacked cycle unless there is a mod to grab screen->focusControl().nextFocus(m_option | FocusControl::CYCLELINEAR); + else { + // if stacked cycling, then set a watch for + // the release of exactly these modifiers + fb->watchKeyRelease(*screen, mods); + screen->focusControl().nextFocus(m_option); + } } } void PrevWindowCmd::execute() { - BScreen *screen = Fluxbox::instance()->keyScreen(); + Fluxbox *fb = Fluxbox::instance(); + BScreen *screen = fb->keyScreen(); if (screen != 0) { - Fluxbox *fb = Fluxbox::instance(); - // special case for commands from key events - if (fb->lastEvent().type == KeyPress) { - unsigned int mods = FbTk::KeyUtil::instance().cleanMods(fb->lastEvent().xkey.state); - mods = FbTk::KeyUtil::instance().isolateModifierMask(mods); - if (mods == 0) // can't stacked cycle unless there is a mod to grab - screen->focusControl().prevFocus(m_option | FocusControl::CYCLELINEAR); - else { - // if stacked cycling, then set a watch for - // the release of exactly these modifiers - Fluxbox::instance()->watchKeyRelease(*screen, mods); - screen->focusControl().prevFocus(m_option); - } - } else - screen->focusControl().nextFocus(m_option | FocusControl::CYCLELINEAR); + // get modifiers from event that causes this for focus order cycling + unsigned int mods = 0; + XEvent ev = fb->lastEvent(); + if (ev.type == KeyPress) { + mods = FbTk::KeyUtil::instance().cleanMods(ev.xkey.state); + } else if (ev.type == ButtonPress) { + mods = FbTk::KeyUtil::instance().cleanMods(ev.xbutton.state); + } + if (mods == 0) // can't stacked cycle unless there is a mod to grab + screen->focusControl().prevFocus(m_option | FocusControl::CYCLELINEAR); + else { + // if stacked cycling, then set a watch for + // the release of exactly these modifiers + fb->watchKeyRelease(*screen, mods); + screen->focusControl().prevFocus(m_option); + } } }
@@ -649,6 +649,28 @@ // copy init file
if (create_init) FbTk::FileUtil::copyFile(DEFAULT_INITFILE, init_file.c_str()); +#define CONFIG_VERSION 1 + FbTk::Resource<int> config_version(m_resourcemanager, 0, + "session.configVersion", "Session.ConfigVersion"); + if (*config_version < CONFIG_VERSION) { + // configs are out of date, so run fluxbox-update_configs + + string commandargs = "fluxbox-update_configs -rc "; + commandargs += init_file; + +#ifdef HAVE_GETPID + // add the fluxbox pid so fbuc can have us reload rc if necessary + pid_t bpid = getpid(); + char intbuff[64]; + sprintf(intbuff, "%d", bpid); + commandargs += " -pid "; + commandargs += intbuff; +#endif // HAVE_GETPID + + FbCommands::ExecuteCmd fbuc(commandargs, 0); + fbuc.execute(); + } + } void Fluxbox::handleEvent(XEvent * const e) {@@ -936,59 +958,28 @@ }
} void Fluxbox::handleButtonEvent(XButtonEvent &be) { + m_last_time = be.time; - switch (be.type) { - case ButtonPress: { - m_last_time = be.time; + BScreen *screen = searchScreen(be.window); + if (be.type == ButtonRelease || !screen) + // no bindings for this type yet + return; - BScreen *screen = searchScreen(be.window); - if (screen == 0) - break; // end case + if (be.button == 1 && !screen->isRootColormapInstalled()) + screen->imageControl().installRootColormap(); - screen->hideMenus(); - - // strip num/caps/scroll-lock and - // see if we're using any other modifier, - // if we're we shouldn't show the root menu - // this could happen if we're resizing aterm for instance - if (FbTk::KeyUtil::instance().cleanMods(be.state) != 0) - return; - - if (be.button == 1) { - if (! screen->isRootColormapInstalled()) - screen->imageControl().installRootColormap(); - // hide menus - if (screen->rootMenu().isVisible()) - screen->rootMenu().hide(); - if (screen->workspaceMenu().isVisible()) - screen->workspaceMenu().hide(); - - } else if (be.button == 2) { - FbCommands::ShowWorkspaceMenuCmd cmd; - cmd.execute(); - } else if (be.button == 3) { - FbCommands::ShowRootMenuCmd cmd; - cmd.execute(); - } else if (screen->isDesktopWheeling() && be.button == 4) { - if(screen->isReverseWheeling()) { - screen->prevWorkspace(1); - } else { - screen->nextWorkspace(1); - } - } else if (screen->isDesktopWheeling() && be.button == 5) { - if(screen->isReverseWheeling()) { - screen->nextWorkspace(1); - } else { - screen->prevWorkspace(1); - } - } - - } break; - case ButtonRelease: - m_last_time = be.time; - break; - default: - break; + // see if we need to keep watching for key releases + BScreen *old_watching_screen = m_watching_screen; + m_watching_screen = 0; + if (!m_key->doAction(be.type, be.state, be.button)) + // no command run, so could still be cycling + m_watching_screen = old_watching_screen; + else if (old_watching_screen && + m_watching_screen != old_watching_screen) { + // no longer need to watch old screen, so stop cycling + old_watching_screen->notifyReleasedKeys(); + if (!m_watching_screen) + XUngrabKeyboard(FbTk::App::instance()->display(), CurrentTime); } }@@ -1126,11 +1117,12 @@ switch (ke.type) {
case KeyPress: // see if we need to keep watching for key releases m_watching_screen = 0; - if (!m_key->doAction(ke)) // could still be cycling + if (!m_key->doAction(ke.type, ke.state, ke.keycode)) + // no command run, so could still be cycling m_watching_screen = old_watching_screen; else if (old_watching_screen && m_watching_screen != old_watching_screen) { - old_watching_screen->notifyReleasedKeys(ke); + old_watching_screen->notifyReleasedKeys(); if (!m_watching_screen) XUngrabKeyboard(FbTk::App::instance()->display(), CurrentTime); }@@ -1148,7 +1140,7 @@ state &= ~FbTk::KeyUtil::instance().keycodeToModmask(ke.keycode);
if ((m_watch_keyrelease & state) == 0) { - m_watching_screen->notifyReleasedKeys(ke); + m_watching_screen->notifyReleasedKeys(); XUngrabKeyboard(FbTk::App::instance()->display(), CurrentTime); // once they are released, we drop the watch
@@ -2,9 +2,12 @@ # util/Makefile.am for Fluxbox
SUBDIRS= fbrun INCLUDES= -I$(top_srcdir)/src -I$(top_srcdir)/src/FbTk bin_SCRIPTS= fbsetbg fluxbox-generate_menu startfluxbox -bin_PROGRAMS= fbsetroot +bin_PROGRAMS= fbsetroot fluxbox-update_configs fbsetroot_SOURCES= fbsetroot.cc fbsetroot.hh fbsetroot_LDADD=../src/FbRootWindow.o ../src/FbAtoms.o \ + ../src/FbTk/libFbTk.a +fluxbox_update_configs_SOURCES= fluxbox-update_configs.cc +fluxbox_update_configs_LDADD= ../src/defaults.o ../src/Resources.o \ ../src/FbTk/libFbTk.a MAINTAINERCLEANFILES= Makefile.in@@ -22,6 +25,9 @@
fbsetroot.o: fbsetroot.cc ../config.h $(srcdir)/fbsetroot.hh \ $(top_srcdir)/src/FbRootWindow.hh $(top_srcdir)/src/FbAtoms.hh +fluxbox-update_configs.o: fluxbox-update_configs.cc ../config.h \ + $(top_srcdir)/src/defaults.hh + startfluxbox: startfluxbox.in @regex_cmd@ -e "s,@pkgdatadir@,$(pkgdatadir),g" \ -e "s,@pkgbindir@,$(bindir),g" \@@ -40,3 +46,7 @@ ../src/FbRootWindow.o:
cd ../src && ${MAKE} FbRootWindow.o ../src/FbAtoms.o: cd ../src && ${MAKE} FbAtoms.o +../src/defaults.o: + cd ../src && ${MAKE} defaults.o +../src/Resources.o: + cd ../src && ${MAKE} Resources.o
@@ -0,0 +1,197 @@
+// fluxbox-update_configs.cc +// Copyright (c) 2007 Fluxbox Team (fluxgen at fluxbox dot org) +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. + +#include "../src/FbTk/I18n.hh" +#include "../src/FbTk/Resource.hh" +#include "../src/FbTk/StringUtil.hh" + +#include "../src/defaults.hh" + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif // HAVE_CONFIG_H + +#ifdef HAVE_SIGNAL_H +#include <signal.h> +#endif // HAVE_SIGNAL_H + +//use GNU extensions +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif // _GNU_SOURCE + +#ifdef HAVE_CSTDIO + #include <cstdio> +#else + #include <stdio.h> +#endif +#ifdef HAVE_CSTDLIB + #include <cstdlib> +#else + #include <stdlib.h> +#endif +#ifdef HAVE_CSTRING + #include <cstring> +#else + #include <string.h> +#endif +#include <iostream> +#include <fstream> + +using std::cout; +using std::cerr; +using std::endl; +using std::string; +using std::ifstream; +using std::ofstream; + +#define VERSION 1 + +int run_updates(int old_version, FbTk::ResourceManager rm) { + int new_version = old_version; + + if (old_version < 1) { // add mouse events to keys file + FbTk::Resource<string> rc_keyfile(rm, DEFAULTKEYSFILE, + "session.keyFile", "Session.KeyFile"); + string keyfilename = FbTk::StringUtil::expandFilename(*rc_keyfile); + + // ok, I don't know anything about file handling in c++ + // what's it to you?!?! + // I assume there should be some error handling in here, but I sure + // don't know how, and I don't have documentation + + ifstream in_keyfile(keyfilename.c_str()); + string whole_keyfile = ""; + + while (!in_keyfile.eof()) { + string linebuffer; + + getline(in_keyfile, linebuffer); + whole_keyfile += linebuffer + "\n"; + } + in_keyfile.close(); + + ofstream out_keyfile(keyfilename.c_str()); + // let's put our new keybindings first, so they're easy to find + out_keyfile << "!mouse actions added by fluxbox-update_configs" << endl + << "OnDesktop Mouse1 :hideMenus" << endl + << "OnDesktop Mouse2 :workspaceMenu" << endl + << "OnDesktop Mouse3 :rootMenu" << endl; + + // scrolling on desktop needs to match user's desktop wheeling settings + // hmmm, what are the odds that somebody wants this to be different on + // different screens? the ability is going away until we make per-screen + // keys files, anyway, so let's just use the first screen's setting + FbTk::Resource<bool> rc_wheeling(rm, true, + "session.screen0.desktopwheeling", + "Session.Screen0.DesktopWheeling"); + FbTk::Resource<bool> rc_reverse(rm, false, + "session.screen0.reversewheeling", + "Session.Screen0.ReverseWheeling"); + if (*rc_wheeling) { + if (*rc_reverse) { // if you ask me, this should have been default + out_keyfile << "OnDesktop Mouse4 :prevWorkspace" << endl + << "OnDesktop Mouse5 :nextWorkspace" << endl; + } else { + out_keyfile << "OnDesktop Mouse4 :nextWorkspace" << endl + << "OnDesktop Mouse5 :prevWorkspace" << endl; + } + } + out_keyfile << endl; // just for good looks + + // now, restore user's old keybindings + out_keyfile << whole_keyfile; + new_version = 1; + } + + return new_version; +} + +int main(int argc, char **argv) { + string rc_filename; + int i = 1; + pid_t fb_pid = 0; + + FbTk::NLSInit("fluxbox.cat"); + _FB_USES_NLS; + + for (; i < argc; i++) { + if (strcmp(argv[i], "-rc") == 0) { + // look for alternative rc file to use + + if ((++i) >= argc) { + cerr<<_FB_CONSOLETEXT(main, RCRequiresArg, + "error: '-rc' requires an argument", "the -rc option requires a file argument")<<endl; + exit(1); + } + + rc_filename = argv[i]; + } else if (strcmp(argv[i], "-pid") == 0) { + if ((++i) >= argc) { + // need translations for this, too + cerr<<"the -pid option requires a numeric argument"<<endl; + } else + fb_pid = atoi(argv[i]); + } else if (strcmp(argv[i], "-help") == 0 || strcmp(argv[i], "-h") == 0) { + // no NLS translations yet -- we'll just have to use English for now + cout << " -rc <string>\t\t\tuse alternate resource file.\n" + << " -pid <int>\t\t\ttell fluxbox to reload configuration.\n" + << " -help\t\t\t\tdisplay this help text and exit.\n\n" + << endl; + exit(0); + } + } + + FbTk::ResourceManager resource_manager(rc_filename.c_str(),false); + if (rc_filename.empty() || !resource_manager.load(rc_filename.c_str())) { + // couldn't load rc file + if (!rc_filename.empty()) { + cerr<<_FB_CONSOLETEXT(Fluxbox, CantLoadRCFile, "Failed to load database", "Failed trying to read rc file")<<":"<<rc_filename<<endl; + cerr<<_FB_CONSOLETEXT(Fluxbox, CantLoadRCFileTrying, "Retrying with", "Retrying rc file loading with (the following file)")<<": "<<DEFAULT_INITFILE<<endl; + } + // couldn't load default rc file, either + if (!resource_manager.load(DEFAULT_INITFILE)) { + cerr<<_FB_CONSOLETEXT(Fluxbox, CantLoadRCFile, "Failed to load database", "")<<": "<<DEFAULT_INITFILE<<endl; + exit(1); // this is a fatal error for us + } + } + + // run updates here + // I feel like putting this in a separate function for no apparent reason + + FbTk::Resource<int> config_version(resource_manager, 0, + "session.configVersion", "Session.ConfigVersion"); + int old_version = *config_version; + int new_version = run_updates(old_version, resource_manager); + if (new_version > old_version) { + config_version = new_version; + resource_manager.save(rc_filename.c_str(), rc_filename.c_str()); + +#ifdef HAVE_SIGNAL_H + // if we were given a fluxbox pid, send it a reconfigure signal + if (fb_pid > 0) + kill(fb_pid, SIGUSR2); +#endif // HAVE_SIGNAL_H + + } + + return 0; +}