-
Notifications
You must be signed in to change notification settings - Fork 434
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support for key suppression #22
Comments
There are three major complications.
|
I am interested in resolving this issue for the windows platform and may contribute if I have time. The main information that I would like to clarify before beginning is what the general architecture for this will be. Personally I believe the best option in a practical sense would be to allow the user to register key combinations to be blocked (this could be done by simply adding suppress=True to any applicable functions). If done this way, suppression can be done only purely within keyboard, which would allow delays to be strictly controlled. Moving to the delays specifically: suppression is something that may need to be done using cython. What I'm envisioning is something like this:
Regarding your third point, that would simply be up to the user. So if the user added |
Sorry, I'm on my phone and misclicked. Please ignore the previous message. Hi xoviat. Thank you for your interest. I'm not sure I understand why cython may be needed. The library actually had this feature at the beginning, via a "blocking=True" parameter, and only ctypes was needed. Are you suggesting using cython for performance? I like your idea of the I don't understand your comment about Should both hotkeys be triggered? One could argue this is incorrect because the first one matched and requested Should only the first one trigger? But the user explicitly asked for the second hotkey to be triggered, and the user typed the key sequence. The library knew about the conflict at the time of registration, so an exception or at least a warning should be raised, instead of accepting a useless request. This is still a problem because users may be using the library in a background process, so they won't see the warning, and exceptions would only make everything stop working. I'm personally tending to this side, but I'm not happy. What do you think? |
If we're going to check all keys against the keys_to_suppress, then as you noted, we will need a very tight loop. Cython can significantly improve execution time. I am not saying that we will need this but it is an option if there is too much delay in the user input.
My point was that this is not as critical as it seems because these events will not be happening within the loop that is suppressing the keys. In other words, if 'ctrl+a, space` is registered to be suppressed, then the entire sequence of keys needs to be put on hold regardless of others keys that have been registered. With respect to program delay, that's not related specifically to this issue. I'm not specifically sure what would happen, but the answer is: whatever currently happens. The only difference that this change is going to make is whether other applications receive keyboard input. This change won't affect what |
For a possibly more pertinent example, how will this be affected if the user has different hotkeys (with suppression) set up for "ctrl+a" and "a"? |
I'll create a truth table so everyone can understand behavior clearly. The answer is: it will be suppressed under the behavior that I have described if keyboard currently calls the handler. The only relevant difference with this is timing. |
The current behavior appears to be the following:
It appears to be that only the shortest combination is recognized and all others are ignored. So, under the implementation that I am proposing, any longer key combination would simply be suppressed and would not actually call a handler. Update: looking at the API, setting
Note: "Suppress Ctrl+a" means wait for the entire key combination before allowing input. |
For my own reference: using System;
using System.Diagnostics;
using System.Windows.Forms;
using System.Runtime.InteropServices;
class InterceptKeys
{
private const int WH_KEYBOARD_LL = 13;
private const int WM_KEYDOWN = 0x0100;
private static LowLevelKeyboardProc _proc = HookCallback;
private static IntPtr _hookID = IntPtr.Zero;
public static void Main()
{
_hookID = SetHook(_proc);
Application.Run();
UnhookWindowsHookEx(_hookID);
}
private static IntPtr SetHook(LowLevelKeyboardProc proc)
{
using (Process curProcess = Process.GetCurrentProcess())
using (ProcessModule curModule = curProcess.MainModule)
{
return SetWindowsHookEx(WH_KEYBOARD_LL, proc,
GetModuleHandle(curModule.ModuleName), 0);
}
}
private delegate IntPtr LowLevelKeyboardProc(
int nCode, IntPtr wParam, IntPtr lParam);
private static IntPtr HookCallback(
int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
{
int vkCode = Marshal.ReadInt32(lParam);
Console.WriteLine((Keys)vkCode);
}
return CallNextHookEx(_hookID, nCode, wParam, lParam);
}
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr SetWindowsHookEx(int idHook,
LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool UnhookWindowsHookEx(IntPtr hhk);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,
IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern IntPtr GetModuleHandle(string lpModuleName);
} |
I really the ideas here so far. Unfortunately I'm on vacation, with little to no computer or even Internet access until the 27th of December. So I can't contribute much until then. I will still be able to answer questions, but with some hours of delay. |
I believe that my implementation is almost complete. What is a bit disturbing though is that while running the tests, the _depressed variable in
is_up comes from simply checking whether the event is a
|
I'm back from the holidays. I had been following this pull request, and I have to say it'd very impressive. I'll schedule some time to properly review it as soon as possible. Thank you |
The number of depressed keys is now updated from the high-level code. I should note that there is now a race condition (if the user releases two keys faster than the high level API can update the number of depressed keys). Although I don't anticipate it being an issue, I will note it here for archival purposes. |
So, is this officially fixed? |
Sort of. It works in many cases but not all. Covering the last few cases is not trivial. |
Is Linux support implemented or even possible? |
Linux support is not implemented, but that part should actually be trivial if you are familiar with the Linux API. All you you need to do is call |
Could you point me towards the relevant files/lines? |
@IamCarbonMan Implementing X support would be equivalent to creating a new backend, and the steps are document in #11 . As for how to communication may work, there were some suggestions here and a similar tool here. I tried tackling this problem last weekend, but didn't get very far. |
I'll look into it, I may be able to implement an X backend given some time.
…On Feb 14, 2017 3:03 PM, "BoppreH" ***@***.***> wrote:
@IamCarbonMan <https://github.com/IamCarbonMan> Implementing X support
would be equivalent to creating a new backend, and the steps are document
in #11 <#11> .
As for how to communication may work, there were some suggestions here
<#33> and a similar tool here
<https://github.com/alols/xcape/blob/master/xcape.c>.
I tried tackling this problem last weekend, but didn't get very far.
—
You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
<#22 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/APgjsCIM4mmcMt6LwIp5Gpme6IUYEsIhks5rcjLPgaJpZM4Kx9PW>
.
|
Based on this, I'm assuming there's no way to remap keys in Linux yet? E.g., user presses X, OS reads Y. Any ETA on this? |
Would be nice to have this on GNU/Linux. Currently if i want to replace some keys with other i call xmodmap from python (using sh module):
which replaces caps lock with escape and translates to bash:
|
I've been working on this for a long time to try to fix bugs, and the complexity is just crazy. Just figuring out the correct behavior is hard. To give a preview of the problem: Let's say we register the shortcut The user presses Then comes a " Now comes the " That's not good, so we decide to block the initial " Then instead of Then you realize that pressing And if someone is playing a game where And then you realize there's actually three alts: |
We could shift this problem onto the winapi with RegisterHotKey.
…On Aug 25, 2017 10:06 PM, "BoppreH" ***@***.***> wrote:
I've been working on this for a long time to try to fix bugs, and the
complexity is just crazy. Just figuring out the correct behavior is hard.
To give a preview of the problem:
Let's say we register the shortcut alt+w with key suppression. It's a
single modifier plus a single key, in a single step. one of the simplest
possible examples. Now think what happens inside the OS hook, where we have
to decide to block or allow each event that is captured.
The user presses alt. We have to either allow or block this event. We
allow it, because we don't know if the next key will be our shortcut or not.
Then comes a "w down". Hey, that's our shortcut! We block this event, and
also the "w up" that follows.
Now comes the "alt up". If we block it, the system will be left with a
held down alt, wreaking havoc. If we allow it, the underlying program
will receive a press-and-release of alt alone, sending the focus to the
menu bar.
That's not good, so we decide to block the initial "alt down".
Then instead of w comes a "tab down", for alt+tab. We have to let that
through, but we blocked the previous alt. So we send a fake "alt down",
then allow the "tab down". But when the "alt up" event comes, we have to
remember if we blocked the initial event (yes) and if we had to fake it
later (yes). In a sequence of events like alt+tab, alt+shift+m, alt+w,
alt+tab the logic gets crazy quick.
Then you realize that pressing a then alt should result in both a and alt
being allowed and the shortcut not triggered (try ctrl+a versus a+ctrl).
And if someone is playing a game where alt is bound to any important
action, they will definitely notice that holding down the key not doing
anything until its released.
And *then* you realize there's actually three alts: alt, left alt and right
alt...
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#22 (comment)>,
or mute the thread
<https://github.com/notifications/unsubscribe-auth/AOo8_GAenOFU-F8KUz1XzFYqWSPZEx3Oks5sb4vEgaJpZM4Kx9PW>
.
|
@xoviat That's a good idea, I forgot it existed. But I think it's a bit limiting (no sided modifiers, keys must have vk) and we still need to solve the problem for other platforms. I finished the code for modifiers + key, now available on the branch An excellent side effect of this implementation is that it exposes the internal suppression engine, so I added high-level functions for The bad news is that there's zero support for suppressing multi-step hotkeys or hotkeys with multiple non-modifiers (e.g. After this, the other two big projects are adding device ID detection to Windows, and a X backend. I think I'll focus on X support due to the requests for key suppression on Linux. |
b33886e in the The question now is how to implement blocking hotkeys with multiple non-modifiers together (e.g. |
Just noticed that if two separate processes are using For example: I have a hotkey win + + that maximizes a window and also applies a frame-less window style. It works fine on its own, but if another Is that something that's been taken into account for the |
What's the current state of this functionality? Thank you. |
|
Great to hear someone is working on this tough problem. Currently I use a dumb workaround in my scripts. I simply disable my keyboard using xinput and reenable it afterwards. |
I'm currently trying to implement this using an optional dependency on python-xlib. See #33. |
Also see: https://github.com/moses-palmer/pynput/blob/master/lib/pynput/keyboard/_xorg.py#L522-L528
|
The |
@adnion Are you able to share your workaround using xinput? I'd be interested to see if it works for me. Thanks |
Of course, but be aware that only the configured global hotkeys work when your keyboard is disabled. Disable Keyboard: os.system('xinput --set-prop "yourkeyboard" "Device Enabled" 0')
os.system('xinput_toggle.sh')
# add some global hotkeys below
keyboard.add_hotkey('a', do, args=["a"], suppress=True, timeout=0, trigger_on_release=False) xinput_toggle.sh #!/usr/bin/bash
# toggles keyboard that has "Keyboard" in its name
SEARCH=Keyboard
ids=$(xinput --list | awk -v search="$SEARCH" \
'$0 ~ search {match($0, /id=[0-9]+/);\
if (RSTART) \
print substr($0, RSTART+3, RLENGTH-3)\
}'\
)
for i in $ids
do
# echo $i
STATE=$(xinput list-props $i | grep "Device Enabled" | grep -o "[01]$")
if [ $STATE -eq 1 ];then
xinput --disable $i
else
xinput --enable $i
fi
done Enable Keyboard: os.system('xinput --set-prop "yourkeyboard" "Device Enabled" 1')
os.system('xinput_toggle.sh')
keyboard.unhook_all()
# add hotkey to trigger the script
# calls function do(action) with argument "powerswitch" if key 'alt gr + h' are pressed
keyboard.add_hotkey('alt gr + h', do, args=["powerswitch"], suppress=True, timeout=0, trigger_on_release=False) I use |
@wis Thanks. Confirmed. I hope this project can integrate Plover's |
I understand correctly that |
Suppression blocks only the hotkeys you manually added, and doesn't touch unrelated keys. Additonally, this topic is for a Linux issue, and there's no support for key suppression on Linux at the moment |
When I add |
Ah, that looks like a bug! The current version has a few known shortcomings. The goods news is that a replacement for the core engine is almost done, via the 'new_core' branch, and will be merged soon. |
Will these issues be solved? And is there anyone actively working on removing these issues? @boppreh |
Right now all events report after-the-fact. It should be possible to suppress keys, keeping them from being processed by applications down the line. This allows users to create global hotkeys without affecting the focused application, for example.
This is possible in Windows, but would require moving the hotkey processing back into the hook.
Linux seems to be trickier. Maybe relying on X, like https://github.com/alols/xcape ?
Finally, extreme care must be taken to not introduce input lag. Because all hotkeys will be processed in the main thread, blocking the event they analyze, it would be too easy to add precious milliseconds to every key press, which is not acceptable.
The text was updated successfully, but these errors were encountered: