Catching keyboard input and blocking it in AutoKey

484 views
Skip to first unread message

Rastislav Kish

unread,
Aug 13, 2020, 2:05:41 PM8/13/20
to autoke...@googlegroups.com
Hello list,

I am new here, so I'll introduce myself a bit. My name is Rastislav
Kish, I am from Slovakia and just recently, I have switched my main
operating system from Windows to Ubuntu mate 20.04 64-bit. I've used
AutoHotkey on Win previously, and i considered using AutoKey as its
Linux alternative.


But I'm facing one big issue, which is capturing keyboard input

without letting it pass.


To explain what I mean, with AutoHotkey, I used to do the following:

Press ctrl+K shortcut, autohotkey script activated.

Write a short code of action which i wanted to perform, like sbf,
standing for Software, Browsers, Firefox, what was an action launching
Firefox. The script was tracking what I was typing, so immediately after
I hit f, it recognized, that there are no other possible sequences
behind sbf and launched Firefox.


I have problems doing this in AutoKey. I have searched this group for
possible leads, finding an interesting mention of pyxhook library, which
is able to catch letters globally, however is unable to prevent them
from passing to focused window, so I can't use it.


Does anyone know of anything capable of this?


Ideal would be, if it didn't display another windows, as I want focus to
stay where it is, some of my scripts was using this for example for
Markdown to HTML conversion, where the source was the currently focused
edit field or opening a file in notepad instead of default program,
which used the currently focused entry in Windows Explorer.


Thank you in advance!


Best regards


Rastislav


Joe

unread,
Aug 13, 2020, 6:03:55 PM8/13/20
to autoke...@googlegroups.com
Welcome to our list and to Linux!

If you have a lot of AHK phrases to translate to AutoKey and you know a
little bash, I have a proof of concept script that converts AHK phrases
to AutoKey phrases. It's "proof of concept" because it only translates a
couple of the many AHK parameters and it can't handle multiline phrases
at all. Still, it might save you some grunt work - but only if you have
a lot of phrases to convert.

We don't have anything to natively track what you're typing once a
script has been invoked. However, our scripting language is full Python
3, so you can do anything limited only by your Python fu. I'm a Python
beginner, so I can't help with that.

As a simple, but clunky first attempt, you could activate your script
with a hotkey and use the dialog manager API calls to throw up an input
dialog or a selection menu. That would do what you want, but it would
clutter the screen with the dialog pop-pup and require you to press
Enter or click on "OK" to activate your chosen option.

If that's too ugly, then you have to resort to doing it yourself in
Python. If you can get a standalone Python script to grab your
keypresses (from the GUI) and perform the desired actions, you can put
the whole thing into an AutoKey script or call out to it using
subprocess(). I suggest starting out standalone because it's much easier
to develop and debug outside of AutoKey. You can use the editor of your
choice, run in a terminal, add print() statements, etc. to see what your
script is doing and you can even use an IDE for debugging. If your code
needs AutoKey API calls, you can replace them with hard-coded
stubs/results until the rest of the code works.

Joe

Johnny Rosenberg

unread,
Aug 13, 2020, 6:13:01 PM8/13/20
to autoke...@googlegroups.com
Den tors 13 aug. 2020 kl 20:05 skrev Rastislav Kish <rastisl...@gmail.com>:
Hello list,

I am new here, so I'll introduce myself a bit. My name is Rastislav
Kish, I am from Slovakia and just recently, I have switched my main
operating system from Windows to Ubuntu mate 20.04 64-bit. I've used
AutoHotkey on Win previously, and i considered using AutoKey as its
Linux alternative.


But I'm facing one big issue, which is capturing keyboard input

without letting it pass.


To explain what I mean, with AutoHotkey, I used to do the following:

Press ctrl+K shortcut, autohotkey script activated.

Write a short code of action which i wanted to perform, like sbf,
standing for Software, Browsers, Firefox, what was an action launching
Firefox. The script was tracking what I was typing, so immediately after
I hit f, it recognized, that there are no other possible sequences
behind sbf and launched Firefox.

I'm not an expert so I'm not sure how to do that. I'm pretty sure it is possible though, since tracking keys is what AutoKey does. Autokey is written in Python and so are the scripts, so if AutoKey can do it, an AutoKey script should also be able to do it. Maybe you'll find the answer if you study the AutoKey source code?

But do you really need to use Ctrl+k for this? Here's another way to do the same thing:
replace Ctrl+k with another character, let's say a period. Then create a script for every program you want to launch. Here's an example for launching Firefox:
New Script
Name it something like ”Launch Firefox” (or whatever you want).
Type the following code:
import subprocess
subprocess.call(['C:\\Temp\\a b c\\Notepad.exe', 'C:\\test.txt'])

At ”Abbreviations”, click Set.
In the dialogue, create the shortcut ”.sbf”.
Make sure the rest looks as the following:
☒ Remove the typed abbreviation
☐ Omit trigger character
☐ Ignore case of typed abbreviation (actually this one could also be checked, it's up to you)
☐ Trigger when typed as part of a word
☒ Trigger immediately (don't require a trigger character)

Now hit OK and then save your script.
Now, where ever window is active, it shouldn't matter, type:
.sbf
Firefox should now launch right after you hit the ”f”. No need for ↵, ↹ or anything else. At least this worked for me.

Another way to look at it is that most GNU/Linux-distributions already are able to launch software with keyboard shortcuts, that is without the need of AutoKey. I don't currently use Mate so I can't tell you exactly how, but at least in Xfce it's fairly easy. I think it's equally easy in Mate. Personally I usually only setup the most commonly used software, for instance Super+f for File manager, Super+t for Terminal and so on. There are also software dedicated for this. It was a very long time I used such software, so I don't know which ones are available these days, but usually you hit some key to launch the launcher, like Super, and then you just start typing the name of your software. Of course it involves a small dialogue to open, so maybe it's not for you, I don't know.

Another possibility is probably to just write a script in Bash that launches your software.
read -n1 Key
The key you hit is stored in the variable Key and you don't need to press ↵.

I guess that could be used for a script that does the work.
When finished, associate that new script with Ctrl+k and then you should be able to use it the way you described, I think. I haven't tested this last one though.


Kind regards

Johnny Rosenberg



I have problems doing this in AutoKey. I have searched this group for
possible leads, finding an interesting mention of pyxhook library, which
is able to catch letters globally, however is unable to prevent them
from passing to focused window, so I can't use it.


Does anyone know of anything capable of this?


Ideal would be, if it didn't display another windows, as I want focus to
stay where it is, some of my scripts was using this for example for
Markdown to HTML conversion, where the source was the currently focused
edit field or opening a file in notepad instead of default program,
which used the currently focused entry in Windows Explorer.


Thank you in advance!


Best regards


Rastislav


--
You received this message because you are subscribed to the Google Groups "autokey-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to autokey-user...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/autokey-users/df14f362-a317-104a-d44f-6bf73b778409%40gmail.com.

Little Girl

unread,
Aug 14, 2020, 4:10:26 PM8/14/20
to autokey-users
I want to make sure I understand the desired behavior. If I'm reading what you wrote correctly, you want to have program one running and it will have the focus. You then want AutoKey to be launched and to listen for your keystrokes. You then want to type sbf to trigger AutoKey to open your browser, but you don't want program one to be aware that you did that, so you don't want the sbf to be entered into program one to begin with. Is that correct?

If it's okay for the sbf to be entered into program one and immediately removed by AutoKey, this will be a very standard setting for an ordinary abbreviation and you could create as many similar abbreviations as you like for the other tasks. In fact, you can leave the ctrl+k part out of it entirely and just have AutoKey running in the background listening.

As an example, you could create a new script and put this into it, replacing "about:home" with a specific URL if you like:

import webbrowser
webbrowser
.get("firefox %s").open("about:home")


Then set its abbreviation to sbf, put a check in the "Remove typed abbreviation" box, put a check in the "Trigger immediately (don't require a trigger character)" box, and click the OK button.

If, however, it's not okay for the sbf to be entered into program one and immediately removed by AutoKey, that would complicate matters. Part of the problem would be that as far as AutoKey or a script that you write is concerned, you might wish to type sba or sbc or whatever in program one, so until you get all three letters typed, no script in the world will know your intent. The only way around that would be to write a script that intercepts all of your input before program one is ever allowed to receive it and only allows anything other than the specified three-letter incantation to go through to program one while sending only the three-letter incantation over to AutoKey. I'm sure it can be done, but probably only by someone who's comfortable moving around in those kinds of coding gymnastics. I suspect I'd write a rather big and ugly script if I were to attempt it.

Rastislav Kish

unread,
Aug 14, 2020, 6:12:58 PM8/14/20
to autoke...@googlegroups.com

Hello,

exactly. I need to type the letters without the program being aware of that.

The reason is, that programs are quite sensitive to key presses. Various letters can not just toggle on or off functions of a program, but also directly interfere with the task I want to launch.

For example, combination fon, standing for functions, open with, notepad, was launching my script, which detected the currently focused file in Windows Explorer and opened it in notepad. Similarly fow would do the same just with Word, fog was opening the file in Goldwave etc.

However, if the input after pressing ctrl+K was not blocked, this wouldn't be possible, as pressing letters in any filesystem explorer would firstly select a completely different file.


Autohotkey had a very useful input function for this, which after launching blocked the keyboard completely and was reading input from it, until either an entry in matchlist was reached, or it was also possible to set a callback, where one could manually determine after receiving a letter whether to stop or not.


I personally don't have problems with code gymnastics, if I just knew what to do. :)

I don't know how exactly broadcasting events in Linux works, so may be it's about the time to find out.


Anyway, thank you all for your answers, the primary goal was to find out whether this doesn't exist already, so I wouldn't need to reinvent the wheel. I believe too that there must be at least some cheat to do this, but this is probably a question for more technically targeted place, such as Stack overflow.


Best regards


Rastislav


Dňa 14. 8. 2020 o 22:10 Little Girl napísal(a):
--
You received this message because you are subscribed to the Google Groups "autokey-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to autokey-user...@googlegroups.com.

Johnny Rosenberg

unread,
Aug 15, 2020, 3:04:07 AM8/15/20
to autoke...@googlegroups.com
Correction of my previous reply, see below:

I found that snippet online and I corrected it before I tested it, but for some reason I seem to have pasted it here in an early stage. If course c:, as was pointed out to me in a private email, won't work on Linux. Here's what worked in this specific case:

import subprocess
subprocess.call(['firefox'])

Having the whole path is probably recommended, for instance:

import subprocess
subprocess.call(['/usr/bin/firefox'])

 
Kind regards

Johnny Rosenberg

Little Girl

unread,
Aug 19, 2020, 6:14:25 PM8/19/20
to autokey-users
Hey there,

Just for the heck of it, I thought I'd mess around a bit and come up with a proof of concept that doesn't quite do everything you want, but it does some of it and should head you in the right direction. It solves one issue in that if you set Ctrl + K as the Hotkey for it, it will listen for your keystrokes and intercept the moment that incantation is typed and then react to whatever you type next without sending your keystrokes back to the currently-focused window. What I don't know how to do yet is to get Python to figure out which file you currently have open and then open it in Notepad. Maybe someone else can jump in and help with that, but I'll probably still mess around with it if you don't mind my probably awkward and inelegant code.

Without further ado, here's what I've got so far and it's working smoothly at my end (except for the Chrome.exe part, since I'm running the script in GNU/Linux and not Windows:

#############################################################
# ABOUT THIS SCRIPT:
# If you set the Hotkey for this script as Ctrl+K,
# you can press that key combination to activate a GUI dialog
# that asks for input.
# You can then type specific key combinations and press the
# Enter key to launch specific browsers.
# In this example, any of these things can happen:
#   * pressing Esc or clicking the Cancel button triggers a notification
#   * pressing the Enter key or clicking the OK button triggers a notification
#   * typing sbc opens Chrome
#   * typing sbcb opens Chromium Browser
#   * typing sbf opens Firefox
#   * typing sbv opens Vivaldi
#   * if something else happens, an error message is triggered
#############################################################

#Ask for input, offer a default example, and check the exit code of the command:
retCode
, userInput = dialog.input_dialog(title="Input required",
    message
="Enter a browser shortcut:", default="example")

#If the exit code was 0, take the specified action:
if retCode == 0:

   
#If sbc was typed, open Chrome:
   
if userInput == "sbc":
        subprocess
.Popen(["chrome.exe"])#Windows only

   
#If sbcb was typed, open Chromium:
   
elif userInput == "sbcb":
        subprocess
.Popen(["chromium-browser"])

   
#If sbf was typed, open Firefox:
   
elif userInput == "sbf":
        subprocess
.Popen(["firefox"])

   
#If sbv was typed, open Vivaldi:
   
elif userInput == "sbv":
        subprocess
.Popen(["vivaldi"])

   
#If any other input was received:
   
else:
        invalid
= "You typed: " + userInput + "\n"
        prompt
= "Please enter a valid shortcut."
        dialog
.info_dialog(title="Invalid", message=invalid + prompt, width="250")

#If the exit code was 1, notify the user that the dialog was cancelled:
elif retCode == 1:
    cancelled
= "You pressed the Esc key or the Cancel button."
    dialog
.info_dialog(title="Cancelled", message=cancelled, width="200")

#If the exit code was anything other than 0 or 1, warn the user of an error:
else:
    error
= "Something went wrong."
    dialog
.info_dialog(title="Error", message=error, width="200")



Johnny Rosenberg

unread,
Aug 23, 2020, 3:58:00 PM8/23/20
to autoke...@googlegroups.com
Den tors 20 aug. 2020 kl 00:14 skrev Little Girl <littl...@gmail.com>:
Hey there,

Just for the heck of it, I thought I'd mess around a bit and come up with a proof of concept that doesn't quite do everything you want, but it does some of it and should head you in the right direction. It solves one issue in that if you set Ctrl + K as the Hotkey for it, it will listen for your keystrokes and intercept the moment that incantation is typed and then react to whatever you type next without sending your keystrokes back to the currently-focused window. What I don't know how to do yet is to get Python to figure out which file you currently have open and then open it in Notepad. Maybe someone else can jump in and help with that, but I'll probably still mess around with it if you don't mind my probably awkward and inelegant code.

Without further ado, here's what I've got so far and it's working smoothly at my end (except for the Chrome.exe part, since I'm running the script in GNU/Linux and not Windows:

The original poster said he uses ”Ubuntu mate 20.04 64-bit”, so I guess he will experience the same thing then.


Kind regards

Johnny Rosenberg
--
You received this message because you are subscribed to the Google Groups "autokey-users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to autokey-user...@googlegroups.com.

Rastislav Kish

unread,
Sep 21, 2020, 4:22:55 PM9/21/20
to autoke...@googlegroups.com
Hi folks,

well, it's been some time since I asked this question.

In first place, I would like to thank you all for your help in this
topic, I greatly appreciate it. :)


After some researching, it seems I've finally found a way to do this, so
I've told myself that I'll report back for case that someone other is
also interested.


To recapitulate the problem, I was searching for a way to remake
Autohotkey's input function, which on Windows blocks keyboard and
collects input from it, until a match is hit or escape key is pressed.


Finally, I've found a way to do this on Linux.

The important library is called evdev, you can install it like:

sudo pip3 install evdev

yes, the sudo part is necessary.


Evdev, if I understand it right, is a Linux component responsible for
distributing events. As a library, it allows programmers to read from
devices and also write to them.


Here is a short Python example, which after pressing ctrl+K blocks the
keyboard and prints pressed keys to the screen, until esc is pressed.

I've included comments for better readability.


# Inport required stuff

from evdev import InputDevice, ecodes, categorize, UInput
from evdev.events import KeyEvent

# Open the keyboard device. Path will most likely differ on your
computer, see:
# https://python-evdev.readthedocs.io/en/latest/tutorial.html
# For an example on how to list devices available on your PC.

dev=InputDevice("/dev/input/event4")

# Prepare some variables

control=False # Whether ctrl is pressed
capturing=False # Wether we're capturing input from blocked keyboard

# We enter an infinite loop, receiving each event of the device to event
variable
for event in dev.read_loop():
    # First, let categorize function specify the received event
somewhat more (evdev is responsible for more than just keyboard events,
so we need to convert the general InpuEvent class to one of its subclasses)
    event=categorize(event)
    # alternative form if event.type==ecodes.EV_KEY:, would make
further work bit more complex than actually necessary

    if isinstance(event, KeyEvent):

        # categorize recognized the event as KeyEvent, thus it's our
time to react. First, let's check, whether ctrl was pressed or released
and if yes, update our control variable accordingly
        if "CTRL" in event.keycode:
            control=event.keystate==KeyEvent.key_down
            continue

        # Next we only want to process key down events
        if event.keystate==KeyEvent.key_down:

            if event.keycode=="KEY_ESC": # keycode is of type str,
which however contains name of a constant from evdev.ecodes
                print(event.scancode) # This is key's numeral form,
just for demonstration here. In fact, it perhaps should be used for
comparisons instead of strings, but I didn't do so here for simplicity.

                # We need to react to esc differently if we're
capturing and if not.
                if capturing:
                    dev.ungrab()
                    capturing=False
                    continue
                else:
                    break # Ends the loop and thus also the program

            if control and event.keycode=="KEY_K":
                print("Shortcut detected")

                # ctrl+K was detected, it's time to grab the keyboard.
                # There is just one thing we need to do first. After we
grab the device, other applications won't receive events about releasing
ctrl and K. Thus we need to do so before we take control.

                with UInput() as ui:
                    ui.write(ecodes.EV_KEY, ecodes.KEY_K, 0)
                    ui.write(ecodes.EV_KEY, ecodes.KEY_LEFTCTRL, 0)
                    ui.syn()
                dev.grab()
                capturing=True
                continue

            if capturing:
                print(event.keycode)


Few notes:

* This example is not fully complete. What is required for universal
approach is:

a. An ability to select the keyboard device automatically

b. Some kind of storing which control was pressed for its correct
release after pressing ctrl+K. Left is currently assumed, but someone
might use right control.


I've omitted these for simplicity, dealing with them should be quite easy.


* Currently, the program tracks key presses for ctrl+K detection, but
doesn't block the shortcut from passing to active programs. Thus, for
example in Pluma, I always activate some kind of search I guess, as the
shortcut is assigned for that function there. Now, the question is,
whether it is possible to do something about this. I've found out that
even if I grab the keyboard, all events I raise myself are delivered to
other applications. Thus I could in theory grab it in the very
beginning, and just forward everything in case I don't need capturing,
until ctrl+K is pressed, which could be suppressed. I'm currently
researching, how invasive would such a method be and whether it'd
introduce any performance hits to system.


* Because Evdev is a lowlevel api, all pressed keys are reported in
manners of hardware buttons, not alphabet characters. Like, there is
some mapping, like KEY_A for a key, but recognizing capital A would
require some additional processing. Two approaches are available:

* Some dictionary processing, with manually entering transformations
specifically for my keyboard layout

* Use of GDK, which has methods to deal with this stuff.


This problem was also omitted here for simplicity.


* The program has to run as root. Yes, you're supposed to launch it as:

sudo python3 main.py

otherwise Evdev, even if you install it as non-root, won't have access
to keyboard device. For this reason, I wouldn't myself use the
automation code here, as it doesn't need root privileges.

Instead, my plan is to create a named pipe, with a program similar to
the one above acting as a server, and my automation code as a client.
Every time a key sequence would be detected, a signal would be send
through the pipe, so the client could react without having any special
privileges.


If you're not familiar with using pipes in Python, a brief summary can
be found for example on:

https://www.tutorialspoint.com/How-to-create-and-use-a-named-pipe-in-Python


Well, I guess that's it for now. I hope this answer helps someone, now,
or in the future. I will be developing my project further and if
everything goes as planned, I'll release it as a some kind of framework,
so people won't need to worry about Evdev, GDK, pipes or anything like that.

I will report back again in that case, providing links and
documentation. I've just written this because I know myself, and my rate
of unreleased almost finished projects, so people can at least find the
way if I don't finish it.


Best regards


Rastislav


Dňa 13. 8. 2020 o 20:05 Rastislav Kish napísal(a):

Joe

unread,
Sep 21, 2020, 5:15:06 PM9/21/20
to autoke...@googlegroups.com
That's quite a piece of work. Thanks for sharing it.

As a Python beginner, I look forward to your framework. It will
significantly extend AutoKey's capabilities.

When you get it to work, let us know here and on Gitter
https://gitter.im/autokey/autokey where the devs will see it.  Maybe
they'll want to integrate it into AutoKey somehow. Then, if you want to,
you can also add something to our wiki so normal users will find it.

Joe
Reply all
Reply to author
Forward
0 new messages