caps2esc:logo4alt

Interception tools plugin that maps caps->{esc,ctrl} and logo<->alt
git clone git://git.deurzen.net/caps2esc:logo4alt
Log | Files | Refs | README | LICENSE

commit 60ce0b8cc5e96fd6cb764bac645433105fe27ad3
Author: Francisco Lopes <francisco@oblita.com>
Date:   Mon, 24 Jul 2017 18:42:11 -0300

Initial commit

Diffstat:
A.clang-format | 17+++++++++++++++++
A.editorconfig | 12++++++++++++
A.gitignore | 114+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ACMakeLists.txt | 8++++++++
ALICENSE.md | 26++++++++++++++++++++++++++
AREADME.md | 112+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acaps2esc.c | 75+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 364 insertions(+), 0 deletions(-)

diff --git a/.clang-format b/.clang-format @@ -0,0 +1,17 @@ +BasedOnStyle: Google +Standard: Cpp11 +SortIncludes: false +AccessModifierOffset: -4 +PointerBindsToType: false +DerivePointerBinding: false +AllowShortLoopsOnASingleLine: false +AllowShortBlocksOnASingleLine : false +AllowShortIfStatementsOnASingleLine: false +AlwaysBreakTemplateDeclarations: false +AlignConsecutiveAssignments: true +AlignEscapedNewlinesLeft: true +AlignTrailingComments: true +AlignOperands: true +ColumnLimit: 80 +IndentWidth: 4 +TabWidth: 4 diff --git a/.editorconfig b/.editorconfig @@ -0,0 +1,12 @@ +root = true +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true +[*.{md}] +trim_trailing_whitespace = false +[*.{c,cpp}] +max_line_length = 80 diff --git a/.gitignore b/.gitignore @@ -0,0 +1,114 @@ + +# Created by https://www.gitignore.io/api/c,c++,vim,linux + +### C ### +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# Kernel Module Compile Results +*.mod* +*.cmd +modules.order +Module.symvers +Mkfile.old +dkms.conf + + +### C++ ### +# Prerequisites + +# Compiled Object files +*.slo + +# Precompiled Headers + +# Compiled Dynamic libraries + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai + +# Executables + + +### Vim ### +# swap +[._]*.s[a-v][a-z] +[._]*.sw[a-p] +[._]s[a-v][a-z] +[._]sw[a-p] +# session +Session.vim +# temporary +.netrwhist +*~ +# auto-generated tag files +tags + + +### Linux ### + +# temporary files which can be created if a process still has a handle open of a deleted file +.fuse_hidden* + +# KDE directory preferences +.directory + +# Linux trash folder which might appear on any partition or disk +.Trash-* + +# .nfs files are created when an open file is removed but is still being accessed +.nfs* + +# End of https://www.gitignore.io/api/c,c++,vim,linux + +### Custom ### + +.lvimrc +.tmuxp.yaml +.gdb_history +.ycm_extra_conf.py diff --git a/CMakeLists.txt b/CMakeLists.txt @@ -0,0 +1,8 @@ +cmake_minimum_required(VERSION 3.0) + +project(caps2esc) + +add_executable(caps2esc caps2esc.c) +target_compile_options(caps2esc PRIVATE -Wall -Wextra) + +install(TARGETS caps2esc RUNTIME DESTINATION bin) diff --git a/LICENSE.md b/LICENSE.md @@ -0,0 +1,26 @@ +The MIT License (MIT) +===================== + +Copyright © `2017` `Francisco Lopes da Silva` + +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. + diff --git a/README.md b/README.md @@ -0,0 +1,112 @@ +# caps2esc + +_Transforming the most useless key **ever** in the most useful one._ +<sub>_For vi/Vim/NeoVim addicts at least_.</sub> + +<a href="http://www.catonmat.net/blog/why-vim-uses-hjkl-as-arrow-keys/"> + +![ADM-3A terminal](http://www.catonmat.net/images/why-vim-uses-hjkl/lsi-adm3a-full-keyboard.jpg) + +</a> + +## What is it? + +- **Put what's useless in its place** + <sub>_By moving the CAPSLOCK function to the far ESC location_</sub> +- **Make what's useful comfortably present, just below your Pinky** + <sub>_By moving both ESC and CTRL functions to the CAPSLOCK location_</sub> + +## Why?! + +Because CAPSLOCK is just "right there" and making it CTRL when key-chording and +ESC when pressed alone is quite handy, specially in vi. + +## Dependencies + +- [Interception Tools][interception-tools] + +## Building + +``` +$ git clone git@gitlab.com:interception/linux/tools.git +$ cd tools +$ mkdir build +$ cd build +$ cmake .. +$ make +``` + +## Execution + +`caps2esc` is an [_Interception Tools_][interception-tools] plugin. A suggested +`udevmon` job configuration is: + +```yaml +- JOB: "intercept -g $DEVNODE | caps2esc | uinput -d $DEVNODE" + DEVICE: + EVENTS: + EV_KEY: [KEY_CAPSLOCK, KEY_ESC] + +``` + +For more information about the [_Interception Tools_][interception-tools], check +the project's website. + +## Installation + +I'm maintaining an Archlinux package on AUR: + +- <https://aur.archlinux.org/packages/interception-caps2esc> + +## Caveats + +As always, there's always a caveat: + +- `intercept -g` will "grab" the detected devices for exclusive access. +- If you tweak your key repeat settings, check whether they get reset. + Please check [this report][key-repeat-fix] about the resolution. + +## History + +I can't recall when I started using CAPSLOCK as both ESC and CTRL but it has +been quite some time already. It started when I was on OS X where it was quite +easy to achieve using the [Karabiner][], which already provides an option to +turn CTRL into CTRL/ESC (which can be coupled with OS X system settings that +turn CAPSLOCK into CTRL). + +Moving on, permanently making Linux my home, I searched and tweaked a similar +solution based on [xmodmap][] and [xcape][]: + +- <https://github.com/alexandre/caps2esc> + +It's a simple solution but with many annoying drawbacks I couldn't stand in the +end: + +- It resets any time a device change happens (bluetooth, usb, any) or the + laptop lid is closed or when logging off and needs to be re-executed. +- It depends on [X][]. Doesn't work on TTY (bare terminal based machine, + CTRL-ALT F2, etc). + +Meanwhile on Windows land, I had a definitive solution based on my +[Interception library][interception] that always works perfectly, no hiccups. + +It made me envy enough, so I ported the +[Windows Interception caps2esc][caps2esc-windows] sample to Linux based upon +the [_Interception Tools_][interception-tools]. + +## License + +<a href="https://gitlab.com/interception/linux/plugins/caps2esc/blob/master/LICENSE.md"> + <img src="https://upload.wikimedia.org/wikipedia/commons/thumb/0/0b/License_icon-mit-2.svg/120px-License_icon-mit-2.svg.png" alt="MIT"> +</a> + +Copyright © 2017 Francisco Lopes da Silva + +[caps2esc-windows]: https://github.com/oblitum/Interception/blob/master/samples/caps2esc/caps2esc.cpp +[karabiner]: https://pqrs.org/osx/karabiner/ +[xmodmap]: https://www.x.org/releases/X11R7.7/doc/man/man1/xmodmap.1.xhtml +[xcape]: https://github.com/alols/xcape +[x]: https://www.x.org +[interception]: https://github.com/oblitum/Interception +[interception-tools]: https://gitlab.com/interception/linux/tools +[key-repeat-fix]: https://github.com/oblitum/caps2esc/issues/1 diff --git a/caps2esc.c b/caps2esc.c @@ -0,0 +1,75 @@ +#include <stdio.h> +#include <stdlib.h> + +#include <linux/input.h> + +// clang-format off +const struct input_event +esc_up = {.type = EV_KEY, .code = KEY_ESC, .value = 0}, +ctrl_up = {.type = EV_KEY, .code = KEY_LEFTCTRL, .value = 0}, +capslock_up = {.type = EV_KEY, .code = KEY_CAPSLOCK, .value = 0}, +esc_down = {.type = EV_KEY, .code = KEY_ESC, .value = 1}, +ctrl_down = {.type = EV_KEY, .code = KEY_LEFTCTRL, .value = 1}, +capslock_down = {.type = EV_KEY, .code = KEY_CAPSLOCK, .value = 1}, +esc_repeat = {.type = EV_KEY, .code = KEY_ESC, .value = 2}, +ctrl_repeat = {.type = EV_KEY, .code = KEY_LEFTCTRL, .value = 2}, +capslock_repeat = {.type = EV_KEY, .code = KEY_CAPSLOCK, .value = 2}; +// clang-format on + +int equal(const struct input_event *first, const struct input_event *second) { + return first->type == second->type && first->code == second->code && + first->value == second->value; +} + +int read_event(struct input_event *event) { + return fread(event, sizeof(struct input_event), 1, stdin) == 1; +} + +void write_event(const struct input_event *event) { + if (fwrite(event, sizeof(struct input_event), 1, stdout) != 1) + exit(EXIT_FAILURE); +} + +int main(void) { + int capslock_is_down = 0, esc_give_up = 0; + struct input_event input; + + setbuf(stdin, NULL), setbuf(stdout, NULL); + + while (read_event(&input)) { + if (input.type != EV_KEY) { + write_event(&input); + continue; + } + + if (capslock_is_down) { + if (equal(&input, &capslock_down) || + equal(&input, &capslock_repeat)) + continue; + + if (equal(&input, &capslock_up)) { + capslock_is_down = 0; + if (esc_give_up) { + esc_give_up = 0; + write_event(&ctrl_up); + continue; + } + write_event(&esc_down); + write_event(&esc_up); + continue; + } + + if (!esc_give_up && input.value) { + esc_give_up = 1; + write_event(&ctrl_down); + } + } else if (equal(&input, &capslock_down)) { + capslock_is_down = 1; + continue; + } + + if (input.code == KEY_ESC) + input.code = KEY_CAPSLOCK; + write_event(&input); + } +}