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 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | CMakeLists.txt | | | 8 | ++++++++ |
A | LICENSE.md | | | 26 | ++++++++++++++++++++++++++ |
A | README.md | | | 112 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | caps2esc.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);
+ }
+}