Code Monkey home page Code Monkey logo

Comments (20)

canadaduane avatar canadaduane commented on August 28, 2024 2

Leaving this here (my current "happy config") in case future readers may benefit:

leftalt = layer(meta_mac)


[meta_mac:M]
comma = C-comma

0 = A-0
1 = A-1
2 = A-2
3 = A-3
4 = A-4
5 = A-5
6 = A-6
7 = A-7
8 = A-8
9 = A-9

a = C-a
b = C-b
# Copy
c = C-insert
d = C-d
e = C-e
f = C-f
g = C-g
h = C-h
i = C-i
j = C-j
k = C-k
l = C-l
m = C-m
n = C-n
o = C-o
p = C-p
q = C-q
r = C-r
s = C-s
t = C-t
u = C-u
# Paste
v = S-insert
w = C-w
# Cut
x = S-delete
y = C-y
z = C-z

left = home
right = end

# As soon as tab is pressed (but not yet released), we switch to the
# "app_switch_state" overlay where we can handle Meta-Backtick differently
tab = swap(app_switch_state, M-tab)

# Meta-Backtick: Switch to next window in the application group
# - A-f6 is the default binding for cycle-group in gnome
# - keybinding: `gsettings get org.gnome.desktop.wm.keybindings cycle-group`
` = A-f6

[app_switch_state:M]
# Meta-Tab: Switch to next application
# - keybinding: `gsettings get org.gnome.desktop.wm.keybindings switch-applications`
tab = M-tab
right = M-tab

# Meta-Backtick: Switch to previous application
# - keybinding: `gsettings get org.gnome.desktop.wm.keybindings switch-applications-backward`
` = M-S-tab
left = M-S-tab

from keyd.

canadaduane avatar canadaduane commented on August 28, 2024 2

I just summarized all of my findings this past week here, including a highlight on keyd: https://medium.com/@canadaduane/key-remapping-in-linux-2021-edition-47320999d2aa

from keyd.

rvaiya avatar rvaiya commented on August 28, 2024 1

I thought about this a little more and realized that you effectively want the ability to swap the current layer mid activation. I still have some reservations about whether or not this is generally useful, but I can think of at least one other case in which it might be, so I've gone ahead and implemented it. Try the latest commit with the following config

leftalt = layer(evilos_simulation_layer)

[evilos_simulation_layer:M]

c = C-insert
v = S-insert
t = C-t
w = C-w
tab = swap(tab, M-tab)

[tab:M]

tab = M-tab
` = M-S-tab

from keyd.

rvaiya avatar rvaiya commented on August 28, 2024 1

I appreciate the favourable mention. Your article seems quite comprehensive and introduced me to several tools I was previously unaware of. Now I can finally cross having Dwayne Johnson write an article about me off my bucket list ;).

from keyd.

canadaduane avatar canadaduane commented on August 28, 2024

Thinking a little further about the internal state of this kbd script, I think this is what I'm asking of keyd (not sure if it's currently possible):

Clipboard Key Simulation Sequence:
Alt Pressed -> do nothing
C Pressed && !AppSwitchMode -> send Ctrl-Insert
C Released -> do nothing
V Pressed && !AppSwitchMode -> send Shift-Insert
V Released -> do nothing
Alt Released -> done
(NOTE: it's also possible the Alt Released event happens BEFORE V Released, but there would be no difference in outcome).

App Switch Simulation Sequence:
Alt Pressed -> do nothing
Tab Pressed -> AppSwitchMode = true; send Meta Pressed; send Tab Pressed
Tab Released -> send Tab Released
Tab Pressed -> send Tab Pressed
Tab Released -> send Tab Released
Grave Pressed -> send Grave Pressed
Grave Released -> send Grave Released
Alt Released -> AppSwitchMode = false; send Meta Released
(NOTE: it's also possible the Alt Released event happens BEFORE Grave Released, but there would be no difference in outcome).

from keyd.

rvaiya avatar rvaiya commented on August 28, 2024

I'm thinking of Alt as the "do everything" modifier, like Cmd is in Mac OS.

The way to achieve this is to define a custom layer and assign it to the alt key. However you probably already know that there are some hurdles higher up the stack since there is less consistency among the different linux UI toolkits than there is on macOS (C-c doesn't work everywhere, visual selections automatically copy things to the primary selection, there are multiple clipboards, etc).

When I press Alt-C I want to simulate the Linux system-wide "Copy" shortcut key: Ctrl-Insert When I press Alt-V I want to simulate the Linux system-wide "Paste" shortcut key: Shift-Insert

This can be achieved as follows

leftalt = layer(myalt)

[myalt] 
c = C-c 
v = S-insert 

When I press and hold Alt, followed by any sequence of Tab or Grave (tilde) I want to simulate Meta-Tab (for each Tab press/release) or Meta-Shift-Tab (for each Grave press/release), until Alt is released. (In this "held" state, after an initial Tab or Grave has been pressed, it's ok--maybe even desirable?--to do nothing when C or V are pressed, even though technically Alt is also being pressed and that could be considered an Alt-C or Alt-V sequence).

Do you mean alt or meta? My understanding is that your window manager expects meta-tab for app switching, in which case the following config should work:

leftalt = layer(myalt)

[myalt:M]

c = C-c 
v = S-insert
` = M-S-tab 

The trick is to make myalt a modifier layer based on meta and then redfine the keys of interest. This means that alt+tab will translate into meta+tab. Additionally keyd should not release meta until you release alt or press c or v (which will release meta send the defined keysequences and then send another meta down event). This should produce the desired effect. Note that for all keys which you do not explicitly define within the layer alt will also function as meta.

from keyd.

rvaiya avatar rvaiya commented on August 28, 2024

I think this is what I'm asking of keyd (not sure if it's currently possible):

The example in the above post achieves this with the exception of being completely inert when alt is pressed on its own. My advice would be to redefine your launcher key from meta to another key like f13 and then overload your actual meta key as described at the bottom of #47. Altogether it should look something like this

leftmeta = overload(M, f13)
rightmeta = overload(M, f13)

leftalt = layer(myalt)

[myalt:M]

c = C-c
v = S-insert
` = M-S-tab 

In general it is not a good idea to map modifiers by themselves at the level of the window manager since this will interfere with programs which rely on detecting individual key up/down events. It is better to express things in terms of key combinations and then use keyd if you want to overload one of your modifiers to do something when tapped. It also worth noting that many window managers allow you to drag windows while meta is held which is a feature you necessarily forfeit when you overload it.

from keyd.

rvaiya avatar rvaiya commented on August 28, 2024

It looks like the meta key is the default gnome overlay key. You can modify it to f13 like so, which in addition to the above config should give you what you want.

gsettings set org.gnome.mutter overlay-key 'XF86Tools'

from keyd.

canadaduane avatar canadaduane commented on August 28, 2024

Thanks for this! It's close, but I think that inert initial Alt is going to be a thorn.

However you probably already know that there are some hurdles higher up the stack since there is less consistency among the different linux UI toolkits than there is on macOS (C-c doesn't work everywhere, visual selections automatically copy things to the primary selection, there are multiple clipboards, etc).

Thanks, yes, there are definitely some hurdles. Interesting trivia: while C-c is not universal, did you know C-insert (for copy) is universal (as far as I can tell)? Although it isn't included in the gnome terminal preferences (for example), remarkably, it still works there.

I modified your sample config slightly and here's where I am now:

leftmeta = overload(M, f13)
rightmeta = overload(M, f13)

leftalt = layer(myalt)

[myalt:M]
` = M-S-tab 
c = C-insert
v = S-insert
x = S-delete

I'm 80% of the way there--so close!--but I can't quite cross the finishing line:

  • Some apps react strangely to the Meta key being pressed & released, even when the system has the app launcher assigned to f13:
    • For example, gnome-terminal makes the cursor "hollow" when Meta is pressed, and "filled" when released, creating a blinking effect (this is fairly innocuous, but it made me feel like something was "off" when I first saw it).
    • Worse, when entering a URL in Firefox, if a synthetic Meta key is pressed and released, the quick search box opens up and then closes. Text areas with focus also temporarily lose focus while Meta is pressed.

How difficult would it be to make an "inert-until-used" style of modifier?

from keyd.

rvaiya avatar rvaiya commented on August 28, 2024

did you know C-insert (for copy) is universal (as far as I can tell)?

Interesting, I was unaware of this. It seems to be a convention like S-insert, though some programs ignore it (e.g urxvt).

Some apps react strangely to the Meta key being pressed & released, even when the system has the app launcher assigned to f13

I assume you are referring to the meta keycode and not the physical key.

Worse, when entering a URL in Firefox, if a synthetic Meta key is pressed and released, the quick search box opens up and then closes. Text areas with focus also temporarily lose focus while Meta is pressed.

I can't seem to replicate this. What key sequence are you executing and what are you expecting to happen? Meta on its own (alt in the above config) doesn't seem to trigger anything for me on firefox.

How difficult would it be to make an "inert-until-used" style of modifier?

It depends on what you mean by this. If you simply mean a layer which only sends modifiers when keys are pressed then this is the default behaviour for non modifier layers.

E.G

leftalt = layer(mylayer)

[mylayer]

tab = M-tab
c = C-c

will only send M-tab when alt+tab is pressed and will do nothing when alt is pressed alone. The problem arises when you rely on software which interprets individual key down/up events. In the case of app switching the window manager will treat the sequences <meta down> <tab down> <tab up> <meta up> <meta down> <tab down> <tab up> <meta up> and <meta down> <tab down> <tab up> <tab down> <tab up> <meta up> differently.

Let's take the above config. You effectively want the following physical key sequence

<alt down> 
<tab down> <tab up>
<tab down> <tab up>
<alt up>

to map to the following key sequence

<meta down> 
<tab down> <tab up> 
<tab down> <tab up>
<meta up>

however you also want

<alt down> 
<c down> <c up> 
<c down> <c up>
<alt up>

to map to

<control down> 
<c down> <c up>
<control up>

<control down> 
<c down> <c up> 
<control up>

How do you distinguish between the two cases? What should the following sequence do?

<alt down> 
<tab down> <tab up> 
<c down> <c up>
<alt up>

presumably you want

<meta down> 
<tab down> <tab up> 
<meta up> 

<control down>
<c down> <c up>
<control up>

but after which physical key event should keyd send <meta up>? If one of the modifiers is still active it will also apply to any subsequent mouse operations. What happens if a layer contains both a and S-a? When should shift be released in the physical event sequence? Leaving active modifiers in the wake of keystrokes produces unintuitive results and increases complexity. Ideally the user should not have to reason about any of these things.

keyd tries to do the most intuitive thing by only sending modifiers associated with the defined keysequence when the key sequence is struck. Unfortunately this breaks the case of meta+tab since the window manager relies on the notion of a modifier as 'a thing which is held to effect a mode switch' and thus interprets each tab in the modified state ('mode') as operating within the same context as the last one. This is in contrast to a layer based approach, which focuses on output rather than state. The shift modifier is a good example of this since it is concerned with symbol output and is effectively stateless (and so is more like a layer than a true modifier).

Trying to reconcile these two disparate concepts (modifiers and layers) is futile. Most of the time the distinction is irrelevant and the following definitions will work as expected

[ctrl]

a = C-a
b = C-b

[meta]

a = M-a
b = M-b

but when software inevitably relies on modifier specific behaviour (e.g meta+tab in your window manager, anything involving two or more modifiers) the notion of a modifier as 'just another layer' breaks down.

Unlike most other remapping solutions keyd at least acknowledges this distinction by allowing the user to define hybrids (modifier layers) which behave like normal modifiers in all instances except where keys are explicitly overriden. To make effective use of this you have to treat modifiers as first class citizens instead of trying to completely replace them with layers. Software like your window manager relies on the stateful behaviour afforded by modifiers in order to function properly and is likely not the last piece of software you will encounter which does. Since many of the things done by the macOS command key concern window management it makes sense in this case to start with the meta modifier (conventionally the linux window management key) and then make modifications as necessary.

Sorry for the lengthy exposition. I considered these issues at length when designing keyd but never bothered to document them anywhere and thought we might both profit from the effort.

from keyd.

canadaduane avatar canadaduane commented on August 28, 2024

Thank you for the thorough reply & clarity of thought!

How do you distinguish between the two cases? What should the following sequence do?
<alt down>
<tab down>
<c down>
<alt up>

This is a good question, and this is also why I added && !AppSwitchMode to my original "Clipboard Key Simulation Sequence". In other words, what I think we need here is a state variable that lets us commit to one of the two ambiguous states and leave the other behind. So in this case, pressing <tab down> would be a "point of no return" that says we are now in the "AppSwitchMode" state--in this world, each key release does NOT result in a modifier release. If, on the other hand, the user had pressed <c down> after <alt down> then we commit to being in the "NOT AppSwitchMode" state--in this world, every (non-modifier) key press is wrapped in <alt down> <key down> <key up> <alt up>.

I wrote the code that does the above logic in a custom C script called Meta Mac for interception. So I guess the question is whether or not it's suitable to add this kind of "state tracker" to keyd.

What you've accomplished with keyd is remarkable, and having written the above custom code, I know a little more intimately how difficult it is to get all of the logic and timing correct. Hence my interest in porting the Meta Mac capabilities to keyd so that I (and others moving from Mac OS) can take advantage of the far greater flexibility offered by your approach.

from keyd.

rvaiya avatar rvaiya commented on August 28, 2024

So I guess the question is whether or not it's suitable to add this kind of "state tracker" to keyd.

It would certainly be possible to implement something like this to accomodate this specific use case, however I am not convinced this is generically useful enough to warrant addition of additional state and config options. The end result would very closely approximate what can already be achieved using modifier layers. meta on its own (or even in combination) almost never does anything in other applications so I am sceptical that the additional meta events are causing the problem.

I am amenable to considering such an addition if I can reproduce the issue and it can't be obviated by changing a binding somewhere in your DE. I have tried to reproduce the issue with gnome and firefox using the following config

leftmeta = overload(M, f13)
rightmeta = overload(M, f13)

leftalt = layer(myalt)

[myalt:M]
` = M-S-tab
c = C-insert
v = S-insert
x = S-delete

paired with

gsettings set org.gnome.mutter overlay-key 'XF86Tools'

to no avail.

from keyd.

rvaiya avatar rvaiya commented on August 28, 2024

I also don't see any issue with gnome-terminal which I would expect to ignore an isolated meta key. My suspicion is that there is some residual meta binding in gnome that is causing a problem. The cursor does disappear whenever you paste something in gnome-terminal but this is normal behaviour and also happens when you press Shift+Insert.

from keyd.

canadaduane avatar canadaduane commented on August 28, 2024

I think you're right! I'm not seeing the same odd effects in apps as I was before. I believe it was because I had been testing A as the modifier parent layout at the time (on my system both alt+tab and meta+tab are bound to app switching by default). So it must have been alt down/up cycle that was causing flashing and dropdown trigger effects in Firefox / gnome-terminal, and when using meta as the modifier parent (with the mutter overlay trigger reassigned), that problem is resolved.

Two remaining issues:

  1. it seems that pressing alt now doesn't trigger 'f13' when pressed/released on its own. I tested with xev. I'm not sure why that would be the case. I copied your config exactly to test it. Is it working for you?
  2. where I had hoped to go next is to emulate the way that Mac OS treats Cmd-` (backtick/tilde) as having different effects when used directly vs when in "AppSwitchMode":
  • When pressing Cmd-` directly, Mac OS switches between windows of the same app. This is equivalent to A-` (or M-` on my system--they both seem to do the same thing).
  • When in AppSwitchMode (i.e. after Cmd-tab is pressed at least once), then Cmd-` changes its effect to choose the "previous app" in the app switcher overlay. This is equivalent to A-S-tab (or M-S-tab on my system also).

It's possible that the answer is just "modify the app switcher code." I still hold on to hope that I can get nearly 100% of the way to the finish line without modifying source code :D

from keyd.

canadaduane avatar canadaduane commented on August 28, 2024

it seems that pressing alt now doesn't trigger 'f13' when pressed/released on its own.

The following config seems to fix the above issue:

leftalt = overload(macmeta, f13)

[macmeta:M]
` = M-S-tab
c = C-insert
v = S-insert
x = S-delete

from keyd.

rvaiya avatar rvaiya commented on August 28, 2024

it seems that pressing alt now doesn't trigger 'f13'

I was under the impression that you didn't want the alt key to trigger the launcher since this is not how macOS behaves. The point of leftmeta = overload(M, f13) is to preserve the functionality of the meta as a launcher while precluding alt from doing this. If you don't mind alt having the same effect then you don't need to overload anything. You can just create a modifier layer based on meta and everything should work without issue (assuming the default value of org.gnome.mutter overlay-key)

Thus your entire config simply becomes

leftalt = layer(myalt)

[myalt:M]
` = M-S-tab
c = C-insert
v = S-insert
x = S-delete

where I had hoped to go next is to emulate the way that Mac OS treats Cmd-` (backtick/tilde) ...
It's possible that the answer is just "modify the app switcher code."

Indeed :P. This sort of detail is best done at the application level. Trying to fool the application to do what you want by maintaining internal state in your keyboard and using keystrokes as an API is a bad idea. This state properly belongs in the application launcher.

https://gitlab.gnome.org/GNOME/gnome-shell/-/blob/main/js/ui/windowManager.js#L1685 doesn't look too scary, maybe you can fix it yourself or bug upstream. It seems like they were trying to copy macOS bindings anyway so it is surprising that they have not already implemented this.

There is also a project called kinto which tries to provide macOS bindings by making DE specific tweaks in conjunction with other remapping software, though I'm not sure how it fares in this particular instance.

Finally you could always just do 1 = M-` if your OCD permits it ;).

from keyd.

canadaduane avatar canadaduane commented on August 28, 2024

I see! Thanks for the explanation (and the laugh :)

It's clear there is more state here that does seem odd to put in a keyboard layer manager, but I will poke around a bit more with keyd and kmonad to see how far I can take this.

kinto.sh uses xkeysnail which I am lead to believe will not work on Wayland. I'm on X11 now, but I'm planning to switch in the next 6 months or so and hoped to create a more robust solution (the fewer tweaks the better).

I sure appreciate your help!

Feel free to close this if you'd like. I don't consider it "fixed" but I do understand if you consider it "resolved" :)

from keyd.

canadaduane avatar canadaduane commented on August 28, 2024

Whoa! What are you doing, making my dreams come true? :)

Thanks, I will pull and build now!

from keyd.

canadaduane avatar canadaduane commented on August 28, 2024

It works!!!

Thanks so much. Brilliant change, too. I feel like this will help others in corner-case situations where they are entering a new "state" of the keyboard mid-chord.

from keyd.

rvaiya avatar rvaiya commented on August 28, 2024

#111 is another interesting (ab)use of swap.

from keyd.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.