Artificial Modal Bindings for Any Program
Since I'm pretty used to using modal programs, it's always annoying when I have to use something that doesn't have customizable keyboard shortcuts or a modal interface. For example, most of my text input is done in vim or with pentadactyl's modal interface for text entry boxes, but sometimes I have to use Libre Office/ Open Office. Having a navigation layer can help with selection and movement, but it's really not nearly as easy as just using single keypresses like in vim.
Today I decided to try to simulate a modal interface. The result is actually totally usable and works pretty well. It obsoletes a lot of functionality of a navigation layer in certain instances. For example, it makes it so that it doesn't really matter that it's not easy to map control inside a normal layer. Selecting text and copying or cutting text can now be as simple as entering a virtual visual mode and jumping by words or lines with w and n and e instead of all the keys that would need to be pressed to do this normally.
As a test, I decided to set up modes for Libre Writer. They'll work for anything that takes the same generic shortcuts though. What I think might be really interesting is to do something like this for photoshop or the GIMP. This idea could potentially be used for any gui program with no modal mappings but a bunch of control alt shift nonsense (i.e. file manager, music player .. it could even be used to bolster vimium so that d would work on something like a new tab).
Video Demonstration
https://www.youtube.com/watch?v=iB1fCASlpY8
Successfully Tested Features:
•hnei and basic navigation
•gg and G
•things taking a count (i.e 10dw is possible; 10j is possible; setup is long though)
•copy, cut, and paste (y(..), d(w,d,etc.), p)
•saving, undoing, searching, etc.
•visual mode
•visual block mode
•pretty much anything that can be done with default keyboard shortcuts (in this case: https://help.libreoffice.org/Writer/Sho … or_Writer)
The basic principle is to use a hotkey program to fake keyboard input in order to map single letters to longer keyboard shortcuts involving things like shift and control.
Tools:
•sxhkd or xchainkeys (for binding and modes)*
•something like xdo, xdotool, xvkbd, xte, etc. (for faking keyboard input)
I've found the best way is to use xchainkeys and xsendkey (and a bit of xdotool and xte as workarounds for staying in the modes).
* However xpybind might be even a better way to do it (have not tried)
Sxhkd v xchainkeys:
Both programs can be used to enter modes using a hotkey where all keyboard input is captured and interpreted based on your bindings. The main downside of sxhkd that makes it not really usable is that once you reach a certain depth, letters can't be reused (i.e. you can't move by word with w and delete word with dw; binding something to dw at this depth will result in just using the previous depth w/moving forward by a word). The other problem is that escaping isn't customizable (meaning that Escape will always exit the mode entirely). Xchainkeys, on the other hand, allows for pretty much unlimited depth and the ability to set up a specific key to abort the chain. This means that a specific key like q (or i.. for insert) can be chosen to exit normal mode and Escape can be used to return to normal mode from visual mode for example. It should be noted, however, that if abort is set to automatic, then any keys that are not bound for that depth will exit the chain.
The only downside of xchainkeys is that fact that if abort is set to manual, that the popup showing the keys pressed will always stay visible. This does NOT effect the bindings (they still work), but it makes the cursor invisible in Libre (and you might find it annoying, but it's pretty small). What I've done to make the cursor somewhat visible when moving in "normal mode" is to turn abort to automatic and set it up to reenter the mode after every hotkey (which is why you'll see a bunch of these: " && xdotool key super+o"). This is the only thing that sxhkd has over xchainkeys (cursor remains visible).
My decision to use super+o (W-o) was totally random. I'd recommend a dedicated key. You can use symbols on layers to enter modes, but they cause hitting that key unmodified to glitch up.
I think I'm using xdotool to send the re-entering keypresses because xchainkeys will interpret keys sent by it but not by xsendkey, but I'm tired and don't really remember.
For more information about xchainkeys (basic syntax): https://code.google.com/p/xchainkeys/
Sxhkd and xchainkeys have a lot of other cool possibilities for use including setting up virtual layers (because anything below 100 is not enough) and virtual modifiers (any symbol can act as a prefix key or load a layer/mode and lock it; i.e. hotkey to either load an xmodmap configuration file or a virtual layer for gaming with wasd/esdf) and layer and keyboard locks.
Here's what a basic implementation of this concept for Libre Office looks like (my ~/.config/xchainkeys/xcainkeys.conf):
It's pretty much self explanatory for the most part and commented. Let me know what you think.
## License
## This software is licensed under the CC0 1.0 Public Domain Declaration, as
## released by Creative Commons <https://creativecommons.org/publicdomain/zero/1.0/>.
## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
## FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
## (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
## LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
## SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
W-o :enter timeout=0 abort=automatic
W-o r :reload
# to do without gui popup, have to re-enter the mode at the end of every command like so:
# W-o h :exec xsendkey Left && xdotool key super+o
# benefit is that cursor doesn't totally dissapear
# Normal Mode {{{
# let escape act as normal and don't exit
W-o q :abort
W-o Escape :exec xsendkey Escape && xdotool key super+o
W-o Mode_switch :exec xdotool key Escape && xdotool key super+o
# basic movement
W-o h :exec xsendkey Left && xdotool key super+o
W-o n :exec xsendkey Down && xdotool key super+o
W-o e :exec xsendkey Up && xdotool key super+o
W-o i :exec xsendkey Right && xdotool key super+o
# take a count; could add for everything
W-o 5 e :exec xsendkey Up && xsendkey Up && xsendkey Up && xsendkey Up && xsendkey Up && xdotool key super+o
# by word
W-o w :exec xsendkey Control+Right && xdotool key super+o
W-o b :exec xsendkey Control+Left && xdotool key super+o
# gg and G
W-o g g :exec xsendkey Control+Home && xdotool key super+o
# o will be shifted unless do this and won't re-enter mode
# sleeping prevents the o from being shifted
W-o S-g :exec xsendkey Control+End && sleep 0.2 && xdotool key super+o
# keyup on shift with xte stopped working so sleep now; maybe can lower to 0.1
# undo and redo
W-o u :exec xsendkey Control+z && xdotool key super+o
W-o S-u :exec xsendkey Control+y && sleep 0.2 && xdotool key super+o
# save (this pulls you out)
W-o s :exec xsendkey Control+s
# search (this also requires you to exit the mode... unless you want to set up virtual keyboard input for every letter)
W-o slash :exec xsendkey Control+f
# next word suggestion
W-o Tab :exec xsendkey Control+Tab && xdotool key super+o
# cut, copy, and paste
# won't delete blank lines, use x
W-o d d :exec xsendkey End && xsendkey Shift+Home && xsendkey Delete && xdotool key super+o
W-o x :exec xsendkey Delete && xdotool key super+o
# this won't work in the middle of the word (alternatively and do a control left before, but them if you're at the start of the word it will delete the previous word)
W-o d w :exec xsendkey Control+Shift+Right && xsendkey Control+x && xdotool key super+o
W-o S-d :exec xsendkey Shift+End && xsendkey Control+x && sleep 0.2 && xdotool key super+o
# maybe more vim like D:
# xsendkey Shift+End && xsendkey BackSpace && xsendkey Right && xsendkey Return && xsendkey Up && xsendkey End
W-o y w :exec xsendkey Control+Shift+Right && xsendkey Control+c && xdotool key super+o
W-o S-y :exec xsendkey Shift+End && xsendkey Control+c && sleep 0.2 && xdotool key super+o
W-o p :exec xsendkey Control+v && xdotool key super+o
#}}}
# Visual mode# {{{
W-o v :enter timout=0 abort=manual
# exit visual mode; return to normal mode
W-o v v :abort
W-o v Escape :exec xsendkey Escape && xdotool key v && xdotool key super+o
W-o v Mode_switch :exec xsendkey Escape && xdotool key v && xdotool key super+o
# basic movemennts
W-o v h :exec xsendkey Shift+Left
W-o v n :exec xsendkey Shift+Down
W-o v e :exec xsendkey Shift+Up
W-o v i :exec xsendkey Shift+Right
# by word
W-o v w :exec xsendkey Control+Shift+Right
W-o v b :exec xsendkey Control+Shift+Left
# gg and G
W-o v g g :exec xsendkey Control+Shift+Home && xdotool key super+o && xdotool key v
W-o v S-g :exec xsendkey Control+Shift+End
# yank and return to normal
W-o v y :exec xsendkey Control+c && xsendkey Escape && xdotool key v && xdotool key super+o
# cut and return to normal
W-o v d :exec xsendkey Control+x && xsendkey Escape && xdotool key v && xdotool key super+o
# }}}
# Visual block mode# {{{
W-o S-v :enter timout=0 abort=manual
W-o S-v v :abort
W-o S-v Escape :exec xsendkey Escape && xdotool key v && xdotool key super+o
W-o S-v Mode_switch :exec xsendkey Escape && xdotool key v && xdotool key super+o
# make this better
W-o S-v n :exec xsendkey Shift+End && xsendkey Control+Shift+Down
W-o S-v e :exec xsendkey Shift+Home && xsendkey Control+Shift+Up
# could also do counts here of course.. too lazy to do right now
W-o S-v S-g :exec xsendkey Control+Shift+End
# yank
W-o S-v y :exec xsendkey Control+c && xsendkey Escape && xdotool key v && xdotool key super+o
# cut
W-o S-v d :exec xsendkey Control+x && xsendkey Escape && xdotool key v && xdotool key super+o
# }}}
# command mode example
W-o semicolon w :exec xsendkey Control+s
EDIT: I added a video and fixed some of the settings (using shifted mappings has the glitch of shifting what you send at the end, exiting the mode; i was using xte to keyup the shift, but this stopped working so I added sleep).
EDIT: I added the suggested license.