From 46261a8284730a16d664fa89423fc5728ed284b4 Mon Sep 17 00:00:00 2001 From: Mathias Gumz <akira at fluxbox dot org> Date: Fri, 18 Dec 2009 08:05:07 +0100 Subject: implemented 'MoveN' and 'ClickN' support in keys file. the hardcoded 'OnTitlebar Mouse1 :Raise' (see Window.cc, FluxboxWindow::buttonPressEvent()) is disabled for now, should be added to fluxbox-update_configs --- ChangeLog | 4 ++ doc/asciidoc/fluxbox-keys.txt | 22 ++++++--- doc/fluxbox-keys.5.in | 31 +++++++++--- src/CurrentWindowCmd.cc | 47 ++++++++++++++---- src/FbTk/KeyUtil.cc | 56 +++++++++++---------- src/Keys.cc | 110 +++++++++++++++++++++++++----------------- src/Window.cc | 43 ++++++++++++++--- src/Window.hh | 1 + 8 files changed, 216 insertions(+), 98 deletions(-) diff --git a/ChangeLog b/ChangeLog index 3087640..cc9e8a9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,6 +1,10 @@ (Format: Year/Month/Day) Changes for 1.1.2 +*09/12/18: + * Implemented new 'MoveN' and 'ClickN' support for keys file (Mathias) + Keys.cc Window.cc/hh CurrentWindowCmd.cc FbTk/KeyUtil.cc + *09/12/15: * Updated fluxbox-keys documentation, added 'Fullscreen' (thanx Paul Tagliamonte) diff --git a/doc/asciidoc/fluxbox-keys.txt b/doc/asciidoc/fluxbox-keys.txt index ed69a57..3f3431d 100644 --- a/doc/asciidoc/fluxbox-keys.txt +++ b/doc/asciidoc/fluxbox-keys.txt @@ -55,7 +55,7 @@ are most commonly used: where *Mod1* is the Alt key on the PC keyboard and *Mod4* is usually a key branded with a familiar company logo. -There are also some special modifiers that refer to mouse button presses::: +There are also some special modifiers that refer to mouse button events::: *OnDesktop*;; The mouse cursor is over the desktop (root window), and not any window. @@ -84,13 +84,21 @@ the key, and see the name in the output. If you have some "special" keys that do not produce a key name in the output of *xev(1)*, you can just use the keycode (NOT the keysym!) in your keys file. -Commands can also be bound to mouse button presses, for which the proper "key" -name is *Mouse*'n' where 'n' is the number of the mouse button. For example, -*Mouse1* is the primary button, and *Mouse4* / *Mouse5* are the scroll wheel -events, in normal configurations. *xev(1)* can also be used to tell the button -number. +Commands can also be bound to mouse events ('N' denotes the number of the +button, eg. '1' is the primary button, '4'/'5' are the wheel buttons): + +*MouseN*;; + The mouse button 'N' is pressed down and holded. +*ClickN*;; + The mouse button 'N' is clicked (pressed and released with no + movement in between) +*MoveN*;; + The mouse button 'N' is currently holded, the binded action is triggered + as often as the mouse moves. + + +There are some special "keys" that let you bind events to non-keyboard events: -There are some special "keys" that let you bind events to non-keyboard events::: *ChangeWorkspace*;; Fires when the workspace changes. This can be used to change backgrounds or do anything else you like when you switch to a new workspace. See the diff --git a/doc/fluxbox-keys.5.in b/doc/fluxbox-keys.5.in index 6e641f9..2b29891 100644 --- a/doc/fluxbox-keys.5.in +++ b/doc/fluxbox-keys.5.in @@ -2,12 +2,12 @@ .\" Title: fluxbox-keys .\" Author: [see the "AUTHORS" section] .\" Generator: DocBook XSL Stylesheets v1.75.1 <http://docbook.sf.net/> -.\" Date: 12/14/2009 +.\" Date: 12/17/2009 .\" Manual: Fluxbox Manual .\" Source: fluxbox-keys.txt .\" Language: English .\" -.TH "FLUXBOX\-KEYS" "5" "12/14/2009" "fluxbox\-keys\&.txt" "Fluxbox Manual" +.TH "FLUXBOX\-KEYS" "5" "12/17/2009" "fluxbox\-keys\&.txt" "Fluxbox Manual" .\" ----------------------------------------------------------------- .\" * set default formatting .\" ----------------------------------------------------------------- @@ -53,7 +53,7 @@ You can get a list of possible modifiers by calling \(oqxmodmap \-pm\(cq\&. This .sp where \fBMod1\fR is the Alt key on the PC keyboard and \fBMod4\fR is usually a key branded with a familiar company logo\&. .PP -There are also some special modifiers that refer to mouse button presses +There are also some special modifiers that refer to mouse button events .RS 4 .PP \fBOnDesktop\fR @@ -90,10 +90,30 @@ You may specify a key by its key name (for example, \fBa\fR or \fBspace\fR) or b .sp If you don\(cqt know the name of a key, you can run \fBxev(1)\fR in a terminal, push the key, and see the name in the output\&. If you have some "special" keys that do not produce a key name in the output of \fBxev(1)\fR, you can just use the keycode (NOT the keysym!) in your keys file\&. .sp -Commands can also be bound to mouse button presses, for which the proper "key" name is \fBMouse\fR\fIn\fR where \fIn\fR is the number of the mouse button\&. For example, \fBMouse1\fR is the primary button, and \fBMouse4\fR / \fBMouse5\fR are the scroll wheel events, in normal configurations\&. \fBxev(1)\fR can also be used to tell the button number\&. +Commands can also be bound to mouse events (\fIN\fR denotes the number of the button, eg\&. \fI1\fR is the primary button, \fI4\fR/\fI5\fR are the wheel buttons): .PP -There are some special "keys" that let you bind events to non\-keyboard events +\fBMouseN\fR .RS 4 +The mouse button +\fIN\fR +is pressed down and holded\&. +.RE +.PP +\fBClickN\fR +.RS 4 +The mouse button +\fIN\fR +is clicked (pressed and released with no movement in between) +.RE +.PP +\fBMoveN\fR +.RS 4 +The mouse button +\fIN\fR +is currently holded, the binded action is triggered as often as the mouse moves\&. +.RE +.sp +There are some special "keys" that let you bind events to non\-keyboard events: .PP \fBChangeWorkspace\fR .RS 4 @@ -101,7 +121,6 @@ Fires when the workspace changes\&. This can be used to change backgrounds or do \fBEXAMPLES\fR below for one idea\&. .RE -.RE .if n \{\ .sp .\} diff --git a/src/CurrentWindowCmd.cc b/src/CurrentWindowCmd.cc index b73de01..f896009 100644 --- a/src/CurrentWindowCmd.cc +++ b/src/CurrentWindowCmd.cc @@ -262,11 +262,26 @@ void GoToTabCmd::real_execute() { REGISTER_COMMAND(startmoving, StartMovingCmd, void); void StartMovingCmd::real_execute() { + + int x; + int y; const XEvent &last = Fluxbox::instance()->lastEvent(); - if (last.type == ButtonPress) { - const XButtonEvent &be = last.xbutton; - fbwindow().startMoving(be.x_root, be.y_root); + switch (last.type) { + case ButtonPress: + x = last.xbutton.x_root; + y = last.xbutton.y_root; + break; + + case MotionNotify: + x = last.xmotion.x_root; + y = last.xmotion.y_root; + break; + + default: + return; } + + fbwindow().startMoving(x, y); } FbTk::Command<void> *StartResizingCmd::parse(const string &cmd, const string &args, @@ -305,15 +320,27 @@ FbTk::Command<void> *StartResizingCmd::parse(const string &cmd, const string &ar REGISTER_COMMAND_PARSER(startresizing, StartResizingCmd::parse, void); void StartResizingCmd::real_execute() { + + int x; + int y; const XEvent &last = Fluxbox::instance()->lastEvent(); - if (last.type == ButtonPress) { - const XButtonEvent &be = last.xbutton; - int x = be.x_root - fbwindow().x() - - fbwindow().frame().window().borderWidth(); - int y = be.y_root - fbwindow().y() - - fbwindow().frame().window().borderWidth(); - fbwindow().startResizing(x, y, fbwindow().getResizeDirection(x, y, m_mode)); + switch (last.type) { + case ButtonPress: + x = last.xbutton.x_root; + y = last.xbutton.y_root; + break; + case MotionNotify: + x = last.xmotion.x_root; + y = last.xmotion.y_root; + break; + default: + return; } + + x -= fbwindow().x() - fbwindow().frame().window().borderWidth(); + y -= fbwindow().y() - fbwindow().frame().window().borderWidth(); + + fbwindow().startResizing(x, y, fbwindow().getResizeDirection(x, y, m_mode)); } REGISTER_COMMAND(starttabbing, StartTabbingCmd, void); diff --git a/src/FbTk/KeyUtil.cc b/src/FbTk/KeyUtil.cc index 0578b9c..6ffe2a2 100644 --- a/src/FbTk/KeyUtil.cc +++ b/src/FbTk/KeyUtil.cc @@ -42,16 +42,16 @@ struct t_modlist{ }; const struct t_modlist modlist[] = { - {"SHIFT", ShiftMask}, - {"LOCK", LockMask}, - {"CONTROL", ControlMask}, - {"MOD1", Mod1Mask}, - {"MOD2", Mod2Mask}, - {"MOD3", Mod3Mask}, - {"MOD4", Mod4Mask}, - {"MOD5", Mod5Mask}, - {"ALT", Mod1Mask}, - {"CTRL", ControlMask}, + {"shift", ShiftMask}, + {"lock", LockMask}, + {"control", ControlMask}, + {"mod1", Mod1Mask}, + {"mod2", Mod2Mask}, + {"mod3", Mod3Mask}, + {"mod4", Mod4Mask}, + {"mod5", Mod5Mask}, + {"alt", Mod1Mask}, + {"ctrl", ControlMask}, {0, 0} }; @@ -88,7 +88,7 @@ void KeyUtil::loadModmap() { XFreeModifiermap(m_modmap); m_modmap = XGetModifierMapping(App::instance()->display()); - + // find modifiers and set them for (int i=0, realkey=0; i<8; ++i) { for (int key=0; key<m_modmap->max_keypermod; ++key, ++realkey) { @@ -96,7 +96,7 @@ void KeyUtil::loadModmap() { if (m_modmap->modifiermap[realkey] == 0) continue; - KeySym ks = XKeycodeToKeysym(App::instance()->display(), + KeySym ks = XKeycodeToKeysym(App::instance()->display(), m_modmap->modifiermap[realkey], 0); switch (ks) { @@ -154,12 +154,18 @@ void KeyUtil::grabButton(unsigned int button, unsigned int mod, Window win, */ unsigned int KeyUtil::getKey(const char *keystr) { - if (!keystr) - return 0; - KeySym sym = XStringToKeysym(keystr); - if (sym==NoSymbol) - return 0; - return XKeysymToKeycode(App::instance()->display(), sym); + + KeyCode code = 0; + + if (keystr) { + + KeySym sym = XStringToKeysym(keystr); + if (sym != NoSymbol) { + code = XKeysymToKeycode(App::instance()->display(), sym); + } + } + + return code; } @@ -169,14 +175,14 @@ unsigned int KeyUtil::getKey(const char *keystr) { unsigned int KeyUtil::getModifier(const char *modstr) { if (!modstr) return 0; - + // find mod mask string for (unsigned int i=0; modlist[i].str !=0; i++) { - if (modlist[i] == modstr) - return modlist[i].mask; + if (modlist[i] == modstr) + return modlist[i].mask; } - - return 0; + + return 0; } /// Ungrabs the keys @@ -193,7 +199,7 @@ void KeyUtil::ungrabButtons(Window win) { unsigned int KeyUtil::keycodeToModmask(unsigned int keycode) { XModifierKeymap *modmap = instance().m_modmap; - if (!modmap) + if (!modmap) return 0; // search through modmap for this keycode @@ -204,7 +210,7 @@ unsigned int KeyUtil::keycodeToModmask(unsigned int keycode) { if (modmap->modifiermap[modmap->max_keypermod*mod + key] == keycode) { return modlist[mod].mask; } - } + } } // no luck return 0; diff --git a/src/Keys.cc b/src/Keys.cc index 0b8efe5..935d54b 100644 --- a/src/Keys.cc +++ b/src/Keys.cc @@ -106,6 +106,28 @@ using std::pair; using FbTk::STLUtil::destroyAndClearSecond; +namespace { + +// candidate for FbTk::StringUtil ? +int extractKeyFromString(const std::string& in, const char* start_pattern, unsigned int& key) { + + int ret = 0; + + if (strstr(in.c_str(), start_pattern) != 0) { + + unsigned int tmp_key = 0; + if (FbTk::StringUtil::extractNumber(in.substr(strlen(start_pattern)), tmp_key)) { + + key = tmp_key; + ret = 1; + } + } + + return ret; +} + +} // end of anonymouse namespace + // helper class 'keytree' class Keys::t_key { public: @@ -254,10 +276,12 @@ void Keys::grabWindow(Window win) { if ((win_it->second & Keys::GLOBAL) > 0 && (*it)->type == KeyPress) FbTk::KeyUtil::grabKey((*it)->key, (*it)->mod, win); // ON_DESKTOP buttons don't need to be grabbed - else if ((win_it->second & (*it)->context & ~Keys::ON_DESKTOP) > 0 && - (*it)->type == ButtonPress) - FbTk::KeyUtil::grabButton((*it)->key, (*it)->mod, win, - ButtonPressMask|ButtonReleaseMask); + else if ((win_it->second & (*it)->context & ~Keys::ON_DESKTOP) > 0) { + + if ((*it)->type == ButtonPress || (*it)->type == ButtonRelease || (*it)->type == MotionNotify) { + FbTk::KeyUtil::grabButton((*it)->key, (*it)->mod, win, ButtonPressMask|ButtonReleaseMask|ButtonMotionMask); + } + } } } @@ -363,85 +387,84 @@ bool Keys::addBinding(const string &linebuffer) { // for each argument for (; argc < val.size(); argc++) { - if (val[argc][0] != ':') { // parse key(s) + std::string arg = FbTk::StringUtil::toLower(val[argc]); + + if (arg[0] != ':') { // parse key(s) - int tmpmod = FbTk::KeyUtil::getModifier(val[argc].c_str()); + int tmpmod = FbTk::KeyUtil::getModifier(arg.c_str()); if(tmpmod) mod |= tmpmod; //If it's a modifier - else if (strcasecmp("ondesktop", val[argc].c_str()) == 0) + else if (arg == "ondesktop") context |= ON_DESKTOP; - else if (strcasecmp("ontoolbar", val[argc].c_str()) == 0) + else if (arg == "ontoolbar") context |= ON_TOOLBAR; - else if (strcasecmp("onwindow", val[argc].c_str()) == 0) + else if (arg == "onwindow") context |= ON_WINDOW; - else if (strcasecmp("ontitlebar", val[argc].c_str()) == 0) + else if (arg == "ontitlebar") context |= ON_TITLEBAR; - else if (strcasecmp("double", val[argc].c_str()) == 0) + else if (arg == "double") isdouble = true; - else if (strcasecmp("NONE",val[argc].c_str())) { - // check if it's a mouse button - if (strcasecmp("focusin", val[argc].c_str()) == 0) { + else if (arg != "none") { + if (arg == "focusin") { context = ON_WINDOW; mod = key = 0; type = FocusIn; - } else if (strcasecmp("focusout", val[argc].c_str()) == 0) { + } else if (arg == "focusout") { context = ON_WINDOW; mod = key = 0; type = FocusOut; - } else if (strcasecmp("changeworkspace", - val[argc].c_str()) == 0) { + } else if (arg == "changeworkspace") { context = ON_DESKTOP; mod = key = 0; type = FocusIn; - } else if (strcasecmp("mouseover", val[argc].c_str()) == 0) { + } else if (arg == "mouseover") { type = EnterNotify; if (!(context & (ON_WINDOW|ON_TOOLBAR))) context |= ON_WINDOW; key = 0; - } else if (strcasecmp("mouseout", val[argc].c_str()) == 0) { + } else if (arg == "mouseout") { type = LeaveNotify; if (!(context & (ON_WINDOW|ON_TOOLBAR))) context |= ON_WINDOW; key = 0; - } else if (strcasecmp(val[argc].substr(0,5).c_str(), - "mouse") == 0 && - val[argc].length() > 5) { + + // check if it's a mouse button + } else if (extractKeyFromString(arg, "mouse", key)) { type = ButtonPress; - key = atoi(val[argc].substr(5, - val[argc].length()-5).c_str()); + // fluxconf mangles things like OnWindow Mouse# to Mouse#ow - if (strstr(val[argc].c_str(), "top")) + if (strstr(arg.c_str(), "top")) context = ON_DESKTOP; - else if (strstr(val[argc].c_str(), "ebar")) + else if (strstr(arg.c_str(), "ebar")) context = ON_TITLEBAR; - else if (strstr(val[argc].c_str(), "bar")) + else if (strstr(arg.c_str(), "bar")) context = ON_TOOLBAR; - else if (strstr(val[argc].c_str(), "ow")) + else if (strstr(arg.c_str(), "ow")) context = ON_WINDOW; + } else if (extractKeyFromString(arg, "click", key)) { + type = ButtonRelease; + } else if (extractKeyFromString(arg, "move", key)) { + type = MotionNotify; + + } else if (key = FbTk::KeyUtil::getKey(val[argc].c_str())) { // convert from string symbol + type = KeyPress; + // keycode covers the following three two-byte cases: // 0x - hex // +[1-9] - number between +1 and +9 // numbers 10 and above // - } else if (!val[argc].empty() && ((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 - key = FbTk::KeyUtil::getKey(val[argc].c_str()); + } else { + FbTk::StringUtil::extractNumber(arg, key); type = KeyPress; } - if (key == 0 && (type == KeyPress || type == ButtonPress)) + if (key == 0 && (type == KeyPress || type == ButtonPress || type == ButtonRelease)) return false; + if (type != ButtonPress) isdouble = false; + if (!first_new_key) { first_new_keylist = current_key; current_key = current_key->find(type, mod, key, context, @@ -470,7 +493,7 @@ bool Keys::addBinding(const string &linebuffer) { return false; const char *str = FbTk::StringUtil::strcasestr(linebuffer.c_str(), - val[argc].c_str()); + val[argc].c_str()); if (str) // +1 to skip ':' current_key->m_command = FbTk::CommandParser<void>::instance().parse(str + 1); @@ -504,10 +527,11 @@ bool Keys::doAction(int type, unsigned int mods, unsigned int key, bool isdouble = false; if (type == ButtonPress) { - if (time > last_button_time) + if (time > last_button_time) { double_click = (time - last_button_time < Fluxbox::instance()->getDoubleClickInterval()) && last_button == key; + } last_button_time = time; last_button = key; isdouble = double_click; diff --git a/src/Window.cc b/src/Window.cc index c03447c..0fb674c 100644 --- a/src/Window.cc +++ b/src/Window.cc @@ -280,6 +280,7 @@ FluxboxWindow::FluxboxWindow(WinClient &client): m_button_grab_x(0), m_button_grab_y(0), m_last_move_x(0), m_last_move_y(0), m_last_resize_h(1), m_last_resize_w(1), + m_last_pressed_button(0), m_workspace_number(0), m_current_state(0), m_old_decoration_mask(0), @@ -1073,7 +1074,6 @@ void FluxboxWindow::grabButtons() { GrabModeSync, GrabModeSync, None, None); XUngrabButton(display, Button1, Mod1Mask|Mod2Mask|Mod3Mask, frame().window().window()); - } @@ -1478,7 +1478,7 @@ void FluxboxWindow::setFullscreenLayer() { FluxboxWindow *foc = FocusControl::focusedFbWindow(); // if another window on the same head is focused, make sure we can see it if (isFocused() || !foc || &foc->screen() != &screen() || - getOnHead() != foc->getOnHead() || + getOnHead() != foc->getOnHead() || (foc->winClient().isTransient() && foc->winClient().transientFor()->fbwindow() == this)) { moveToLayer(::Layer::ABOVE_DOCK); @@ -2370,13 +2370,16 @@ bool FluxboxWindow::isTyping() const { void FluxboxWindow::buttonPressEvent(XButtonEvent &be) { m_last_button_x = be.x_root; m_last_button_y = be.y_root; + m_last_pressed_button = be.button; bool onTitlebar = frame().insideTitlebar( be.window ) && frame().handle().window() != be.window; +#if 0 // disabled if (onTitlebar && be.button == 1) raise(); +#endif // check keys file first Keys *k = Fluxbox::instance()->keys(); @@ -2412,19 +2415,27 @@ void FluxboxWindow::buttonPressEvent(XButtonEvent &be) { void FluxboxWindow::buttonReleaseEvent(XButtonEvent &re) { + if (m_last_pressed_button == re.button) { + m_last_pressed_button = 0; + } + if (isMoving()) stopMoving(); else if (isResizing()) stopResizing(); else if (m_attaching_tab) attachTo(re.x_root, re.y_root); - else - frame().tabcontainer().tryButtonReleaseEvent(re); + else if (!frame().tabcontainer().tryButtonReleaseEvent(re)) { + if (m_last_button_x == re.x_root && m_last_button_y == re.y_root) { + Fluxbox::instance()->keys()->doAction(re.type, re.state, re.button, Keys::ON_WINDOW, &winClient(), re.time); + } + } } void FluxboxWindow::motionNotifyEvent(XMotionEvent &me) { + if (isMoving() && me.window == parent()) { me.window = frame().window().window(); } @@ -2452,6 +2463,13 @@ void FluxboxWindow::motionNotifyEvent(XMotionEvent &me) { } } + + // in case someone put MoveX :StartMoving etc into keys, we have + // to activate it before doing the actual motionNotify code + Fluxbox::instance()->keys()->doAction(me.type, me.state, m_last_pressed_button, + inside_titlebar ? Keys::ON_TITLEBAR : Keys::ON_WINDOW, + &winClient(), me.time); + if (moving || ((me.state & Button1Mask) && functions.move && inside_titlebar && !isResizing() && m_attaching_tab == 0)) { @@ -2818,8 +2836,14 @@ void FluxboxWindow::setDecorationMask(unsigned int mask, bool apply) { } void FluxboxWindow::startMoving(int x, int y) { - if (s_num_grabs > 0) + + if (isMoving()) { + return; + } + + if (s_num_grabs > 0) { return; + } if (isMaximized() && screen().getMaxDisableMove()) return; @@ -3062,6 +3086,7 @@ void FluxboxWindow::doSnapping(int &orig_left, int &orig_top) { FluxboxWindow::ReferenceCorner FluxboxWindow::getResizeDirection(int x, int y, ResizeModel model) const { + int cx = frame().width() / 2; int cy = frame().height() / 2; if (model == CENTERRESIZE) @@ -3088,6 +3113,9 @@ FluxboxWindow::ReferenceCorner FluxboxWindow::getResizeDirection(int x, int y, void FluxboxWindow::startResizing(int x, int y, ReferenceCorner dir) { + if (isResizing()) + return; + if (s_num_grabs > 0 || isShaded() || isIconic() ) return; @@ -3550,7 +3578,7 @@ void FluxboxWindow::updateButtons() { need_update = true; } } - + } if (!need_update) @@ -3680,6 +3708,7 @@ void FluxboxWindow::grabPointer(Window grab_window, Window confine_to, Cursor cursor, Time time) { + XGrabPointer(FbTk::App::instance()->display(), grab_window, owner_events, @@ -3870,7 +3899,7 @@ void FluxboxWindow::setWindowType(WindowState::WindowType type) { */ } -void FluxboxWindow::focusedWindowChanged(BScreen &screen, +void FluxboxWindow::focusedWindowChanged(BScreen &screen, FluxboxWindow *focused_win, WinClient* client) { if (focused_win) { setFullscreenLayer(); diff --git a/src/Window.hh b/src/Window.hh index 92d20d7..9cefd55 100644 --- a/src/Window.hh +++ b/src/Window.hh @@ -547,6 +547,7 @@ private: int m_last_resize_x, m_last_resize_y; // handles last button press event for resize int m_last_move_x, m_last_move_y; // handles last pos for non opaque moving int m_last_resize_h, m_last_resize_w; // handles height/width for resize "window" + int m_last_pressed_button; timeval m_last_keypress_time; -- cgit v0.11.2