awesomeWM / awesome

awesome window manager
https://awesomewm.org/
GNU General Public License v2.0
6.41k stars 598 forks source link

Fake input cannot bypass Awesome keys (shortcuts) #77

Open michaelbeaumont opened 9 years ago

michaelbeaumont commented 9 years ago

When using fake inputs with or without keygrabbers, Awesome always intercepts the input. While this is the expected behavior, it doesn't cover many use cases. Awesome needs to support sending raw inputs to clients without intercepting them. The workaround when using keygrabber is to stop and restart it between fake inputs, but there is no equivalent hack for capi.key.

Original: In the docs for awful.keygrabber, it's mentioned that a keygrabber can return false to pass the keypress to the next grabber on the stack. As far as I can tell, although a stack is maintained in awful.keygrabber, keygrabber.c simply passes the keys to the last run grabber and a keygrabber will swallow a keypress no matter what.

It would make keygrabbers much more flexible if they behaved as described.

blueyed commented 9 years ago

IIRC there's a difference in behavior between using the global keygrabber from the C API vs. awful.keygrabber (which uses the C keygrabber).

michaelbeaumont commented 9 years ago

It doesn't work using either. I'd attempt to add it if it'd be a welcome patch.

michaelbeaumont commented 9 years ago

OK I figured if all keygrabbers passed the key down the stack, then at the bottom it would be sent to the window with focus as if it were pressed with no one grabbing. This won't happen with the way event.c looks at the moment.

I've got it working this way though now, is that intended behavior/Would that be welcome behavior? It's my first time working with X11, I'm not sure if there are negative performance implications in connection with grabbing and ungrabbing the keyboard and sending events...

michaelbeaumont commented 9 years ago

Actually it seems to be due to xcb/x11 that awesome can't ungrab the keyboard, send an event to the focused window and then immediately regrab the keyboard. Regrabbing the keyboard seems to make the event disappear because the window never gets it.

Elv13 commented 9 years ago

Take a look at this https://github.com/Elv13/repetitive

It has some code to bypass those issues

michaelbeaumont commented 9 years ago

Indeed, it looks like capi.root can fake input, thanks for the tip!

michaelbeaumont commented 9 years ago

Unfortunately, it seems it's all too slow for putting a keygrabber between the user and the application. The faked input is only rarely registered... I wonder if there's a way to avoid x11 eating up the key press before the application "gets" the event.

Elv13 commented 9 years ago

What is your use case for this?

michaelbeaumont commented 9 years ago

I was hoping to have a bit more flexibility with key bindings, for example if I hit shift two times quickly or if I type ww then be able to act on that. For that to be possible though, I need to be able to send those keys through the keygrabber to where they would originally have gone. Am 17.01.2015 22:23 schrieb "Emmanuel Lepage Vallée" < notifications@github.com>:

What is your use case for this?

— Reply to this email directly or view it on GitHub https://github.com/awesomeWM/awesome/issues/77#issuecomment-70384976.

Elv13 commented 9 years ago

Well, not really. You can grab the first shift as a normal key binding, then register a timer for 30 to 90ms, then if there is not a second one, send the key. You will need some extra magic to avoid triggering the key binding again when sending the fake input, but beside that it would work. Again, you can look at Repetitive code to see how to use high res timing. It wont be straightforward to implement, but it is possible without grabbing all inputs. I implemented doubleclick on some widgets using high res timestamp and timers like that.

michaelbeaumont commented 9 years ago

But I still have the same problem because awesome grabs the "Shift_L" key. Now it's just one key instead of the keyboard. But no matter what x11 will send the fake key press to awesome and awesome will take it, unless I ungrab the key.

Elv13 commented 9 years ago

Yes, that's an issue I had in the past too. You have to do some nasty hacks to solve that. Maybe we should add this option to fake_input (patches welcome)

michaelbeaumont commented 9 years ago

I tried doing it in event.c with xcb_send_event but the event never gets to the window when I have awesome regrab the keyboard right after. I don't know enough about how x11/xcb works to figure out why and the documentation in that area is pretty lacking. Maybe it'll work with xcb_test_fake_input. I'll definitely look into it again when I have time.

Elv13 commented 9 years ago

Mind if I re-open the issue and change the title?

michaelbeaumont commented 9 years ago

Definitely not, go for it!

blueyed commented 9 years ago

I agree that there should be at least an option to make awesome ignore the keys it fakes, and that option should be on by default probably.

@psychon What do you think?

My use case is sending Shift-Insert for modkey-v, and it gets picked up by awesome again through a binding for modkey-shift-insert then.

psychon commented 9 years ago

Hrm. xcb_grab_server(), then ungrabbing all keys on the target window (and the root window?), doing the fake input, grabbing the keys again and then xcb_ungrab_server() might work. However, you better don't count the number of requests that this generates...

blueyed commented 9 years ago

However, you better don't count the number of requests that this generates...

Is there an actual use case (except for our tests), where you want awesome to handle the keys again, especially when it's in the context of additional modifiers, like in my case mod-shift-insert gets triggered, because I've pressed mod to call fake_keys.

From my understanding fake_keys seems to be a somewhat internal API anyway.

psychon commented 9 years ago

Nope, I can't come up with a use case right now. Still, I'd make this configurable with some argument to the function (which, if you want, defaults to "ignore awesome's keybindings").

jack836 commented 8 years ago

I am not sure weather this is appropriate for this thread, but thought it would be a 'actual use case'.

I am using awesome WM for the past two years with the default keybindings. I enjoy working (switching) with multiple windows and it was all smooth sailing until I had to work on a remote server through rsh login. Here is what I do, (i)Fireup gnome-terminal (in local machine) as a client in awesome WM, (ii) remote login to the server and span multiple panes & windows using tmux (iii) do the work. Tmux offers similar features as awesome WM and provides the same experience(visually), which is very good, but the devil's with the KEYBINDINGS.

Tmux has its own set of keybindings (customizable) but my muscle memory always tries to stick to awesome WM keybindings for the similar operations on tmux-windows. Since the visual and functional experience provided by awesome and tmux are the same it is really a pain to use two seperate keyboard shortcuts for similar tasks. For example, Press Mod4-j to switch to next window and nothing happens because you are in tmux, and imagine that it happens more than 100 times. I think human visual-processing-instructing system is designed to generate same instruction for similar kind of task and hence the difficulty. Here is a solution that i can dream for now (since I have no experience/time hacking into the lua script - but is in my TODO-SOMEDAY list)

  1. A key press(predefined) that instructs awesome WM to pass all further keystrokes to the client window on focus OR sleep to keystrokes
  2. An other key press (predefined) that wakes up awesome WM again telling to listen/respond to any further keystrokes.
    Any help or advice in this direction will be highly appreciated. I expect there must be other people too with the similar situation.
psychon commented 8 years ago

@jack836 You can relatively easily do a key binding do disable all key bindings. Something like this added to the default config:

local enable_bindings = awful.key({ modkey }, "d", function() root.keys(globalkeys) print("enabled") end)
globalkeys = awful.util.table.join(globalkeys,
  awful.key({ modkey }, "d", function() root.keys(enable_bindings) print("disabled") end))
root.keys(globalkeys)

With this, mod+d should toggle all keybindings (except for this one). As always: Completely untested, use at your own risk.

P.S.: Sorry, but yes, your problem is unrelated to this "thread".

jack836 commented 8 years ago

Ah! Got it! The issue was, I had to add the root.keys(globalkeys) at the end. Now it works fine and I like it very much. Thank you for your thoughtful reply considering my knowledge level.

Deleting the last two threads was good. I will add this topic to the mailing list and point to the solution here in case it might be useful to someone like me. Thank you for all your efforts.

On Wed, Dec 16, 2015 at 11:43 PM, Uli Schlachter notifications@github.com wrote:

@jack836 https://github.com/jack836 I now tested this and updated the code from my above comment. I changed two things: Instead of "Esc" this is now bound to mod+d (because "Esc" needs to be written "Escape" and because the default config already has some other keybinding for mod+Esc) and I added a call to root.keys(globalkeys) at the end, so that this also works if appended to the very end of the config. Besides that, this works fine for me here.

About spamming: If you don't mind, I will just delete these last two comments (and keep my previous reply). If you want somewhere to move this to... dunno, the mailing list? Reporting your own issue/thread isn't really the way to go, because this isn't about a bug in awesome.

— Reply to this email directly or view it on GitHub https://github.com/awesomeWM/awesome/issues/77#issuecomment-165129562.

qpkorr commented 5 years ago

I'm new to awesome (but loving it) but I feel like I'm being hit by this behaviour. My use case is simply that when I hit Mod4+e, in any client, I want it to 'type' in my email address. So I press Mod4, press and release 'e', and at that point the trigger fires - before I've had time to release Mod4. Problem is - the fake_input keys I'm sending appear to be interacting with the Mod4 'pressed' state, and triggering other hotkey combinations of Mod4 plus some characters in my email address. Any suggestions? Do I need to wait for a fix to this issue? Apologies if I've misunderstood my problem and it's not related to this thread!

actionless commented 5 years ago

the workaround which i am using -- i am calling this function before doing fake input:

function release_modifiers()
  root.fake_input('key_release'  , 'Super_L')
  root.fake_input('key_release'  , 'Control_L')
  root.fake_input('key_release'  , 'Super_R')
  root.fake_input('key_release'  , 'Control_R')
  -- add up more modifiers if needed
end
qpkorr commented 4 years ago

Thanks @actionless - I tried that, but for some reason the key_release didn't seem to work for me. No idea why not. Instead, I ended up just adding a half second delay between registering the key press (Mod4+e) and actually starting to 'type' the intended text (using fake_input). The half second is long enough for me to have physically released the Mod4 key, and everything works as intended.

michael-nhat commented 3 years ago

Thanks @actionless - I tried that, but for some reason the key_release didn't seem to work for me. No idea why not. Instead, I ended up just adding a half second delay between registering the key press (Mod4+e) and actually starting to 'type' the intended text (using fake_input). The half second is long enough for me to have physically released the Mod4 key, and everything works as intended.

I have 3 day hard searching the problem like you, in your case, using timer is good choice. But I want remap Modkey + x to send page up to Chrome, but it's seem chrome don't want to page up when modkey still press. So I have to remap modkey to another key and make it don't repeat. If you want it typing immediately, you should probaly do like me! Haha, it take me soo long, but I solved it!

qpkorr commented 3 years ago

Thanks for sharing @NhatGit - and well done for solving properly! (or as well as Awesome currently allows?) I'm still pretty ignorant around linux and don't really understand how you solved it - any chance you could share some of the code you're using to help me try something similar? Thanks in advance if you can!

michael-nhat commented 3 years ago

@qpkorr The method I haved used have disavantage, keyboard layout often reset to defaul for some reason (sleep, restart, ... I use manjaro) and I have reconfig a lots. Recently I use xkeysnail with python lib xdo, to solve some problem, it is more flexible and power than the shortcut function of awesome. But I would like deeply create deeper shortcut programing in Linux (may be by C/C++), xkeysnail is something not good enough, just to deal with temporary situations! You could google to explore xkeysnail, make it run at startup by anyway you know. Xkeysnail permit a shortcut could call a function in python, so you could play what you want in python, so it is simple and fun. Read the guide on its github page thoroughly, there examples also very easily to understand. I don't think I need show my clumsy code :))

qpkorr commented 3 years ago

@NhatGit Thanks for that! Great to know there's a python option out there - which sounds like it might work quite well. Whilst it would be nice to have faster keyboard shortcuts (without the half second delay I use to make awesome work properly) those half-seconds may not yet justify the switch for me. But if I need to do more complex mappings than awesome will support, hopefully I'll remember this suggestion! Thank you!

psychon commented 3 years ago

Recently I use xkeysnail with python lib xdo, to solve some problem, it is more flexible and power than the shortcut function of awesome.

Which made me wonder "How is that possible?!". Well... https://github.com/mooz/xkeysnail

  • Pros [...]
    • Runs in low-level layer (evdev and uinput), making remapping work in almost all the places
  • Cons
    • Runs in root-mode (requires sudo)

Ah, okay. I doubt we'd want to support that in AwesomeWM. This sounds a lot like this does not use X11 at all, but instead talks directly to the kernel to get input events. No idea how that would make it possible to "grab" keys (I would expect both Xorg and xkeysnail to get the same keybindings).

Also... how does this help with the issue at hand? This sounds like it can bind keybindings. However, it cannot bypass keybindings...? Or can it? How?

I'm confused. Sorry for my random ramblings.

Edit: Looks like this uses the "release modifiers before "faking" input"-approach: https://github.com/mooz/xkeysnail/blob/bf3c93b4fe6efd42893db4e6588e5ef1c4909cfb/xkeysnail/output.py#L69-L99

Edit: This seems to grab existing keyboards and "fake" a new keyboard device. Events read from existing keyboards are passed to the new uinput device. That way, it is possible to have actions like "act differently on the next key".

actionless commented 3 years ago

Edit: Looks like this uses the "release modifiers before "faking" input"-approach:

isn't it the same as releasing them using awesome's fake input?

also forgot to mention what i also wrap it with timer.delayed_call (otherwise it sometimes was not working)

actionless commented 3 years ago

ah and a side note, my use case for this (also will be curious to know why other need this):

when i press hotkey in awesome it click Ctrl+L to focus on URL in browser and copy it

psychon commented 3 years ago

isn't it the same as releasing them using awesome's fake input?

Yup. But xkeysnail sees key presses, so it know "the left control key is pressed". AwesomeWM only sees "the ctrl modifier is active"; but does not know which key activated this modifier.

However, xkeysnail has to hardcode assumptions about the keyboard layout: https://github.com/mooz/xkeysnail/blob/bf3c93b4fe6efd42893db4e6588e5ef1c4909cfb/xkeysnail/key.py#L748-L762 It is possible to change the keyboard layout enough so that these assumptions are no longer correct / xkeysnail would break.