Closed G-M0N3Y-2503 closed 7 months ago
Maybe for the Virtual key senario more thought is required. What works for me on Linux is the following:
[device="Razer Razer Basilisk V3 Pro"]
VolumeProfile = Virtual1
MediaProfile = Virtual2
Profiles = MediaProfile | VolumeProfile
[system="Linux" device="Razer Razer Basilisk V3 Pro"]
SetNoneProfile = $(notify-send -t 500 -a "keymapper" "Profile:\n [None] Volume Media") ^
SetVolumeProfile = VolumeProfile $(notify-send -t 500 -a "keymapper" "Profile:\n None [Volume] Media") ^
SetMedifProfile = MediaProfile $(notify-send -t 500 -a "keymapper" "Profile:\n None Volume [Media]") ^
# Reddit
[modifier="!Profiles" class=/chrome/i title=/reddit/i device="Razer Razer Basilisk V3 Pro"]
ButtonForward >> J
ButtonBack >> K
# Default
[device="Razer Razer Basilisk V3 Pro"]
!Profiles ControlLeft >> SetVolumeProfile
VolumeProfile{ControlLeft} >> VolumeProfile SetMedifProfile
MediaProfile{ControlLeft} >> MediaProfile SetNoneProfile
[modifier="VolumeProfile" device="Razer Razer Basilisk V3 Pro"]
ButtonForward >> AudioVolumeUp
ButtonBack >> AudioVolumeDown
AltLeft >> (Meta){AudioVolumeMute}
[modifier="MediaProfile" device="Razer Razer Basilisk V3 Pro"]
ButtonForward >> MediaTrackNext
ButtonBack >> MediaTrackPrevious
AltLeft >> MediaPlayPause
[device="Razer Razer Basilisk V3 Pro"]
AltLeft >> LaunchApp1
Any >> Any
So It notifies me exactly what mapping I have for the virtual keys but not when I am on Reddit vs some other site.
@G-M0N3Y-2503 Cool trick, thanks for sharing!
For Windows and Linux the following gets across the line for the notification part at least.
MediaProfileMsg = "Profile: None Volume [Media]"
VolumeProfileMsg = "Profile: None [Volume] Media"
NoneProfileMsg = "Profile: [None] Volume Media"
[system="Linux" device="Razer Razer Basilisk V3 Pro"]
MediaProfileNotify = $(notify-send -t 500 -a "keymapper" MediaProfileMsg) ^
VolumeProfileNotify = $(notify-send -t 500 -a "keymapper" VolumeProfileMsg) ^
NoneProfileNotify = $(notify-send -t 500 -a "keymapper" NoneProfileMsg) ^
[system="Windows"]
MediaProfileNotify = $(msg.exe * /TIME:1 MediaProfileMsg) ^
VolumeProfileNotify = $(msg.exe * /TIME:1 VolumeProfileMsg) ^
NoneProfileNotify = $(msg.exe * /TIME:1 NoneProfileMsg) ^
No device filter on Windows which makes it difficult for cross-platform.
Hi, I am planning to implement a few new features to simplify this:
ContextActive
which exists separately for each context and is toggled when the context becomes active/inactive.With these something like the following configuration should be possible:
[system="Linux"]
notify(TEXT) = $(notify-send -t 500 -a "keymapper" TEXT)
[system="Windows"]
notify(TEXT) = $(msg.exe * /TIME:1 TEXT)
[default]
context(NAME) = notify("entered NAME") ^ notify("left NAME")
[class=/chrome/i title=/reddit/i]
ContextActive >> context("Reddit")
That sounds awesome! It'd be good to explicitly know when you're changing context!
But to avoid conflation on one part of my shown config, just to be safe. It allows me to change the map tempararily while in a different context, like changing music while in VSCode where I more often use the default map. These changes will help clean that up a and let me have context specific maps to cycle through. But just noting it is different.
This is already testable, if you are willing to build the next
branch from source.
It is not yet documented but this is the config I used for testing:
log = $(print "$0" >> ~/keymapper.log)
context = 50ms log["entered $0"] ^ log["left $0"]
[title="GPUpad"]
ContextActive >> context["GPUpad"]
[title="Thunar"]
ContextActive >> context["Thunar"]
It should be quite self-explanatory. It logs whenever one of the applications gets/loses focus to a file.
@houmain I tried building it on my Ubuntu Focal based system but the build fails. I guess my toolchain is too obsolete?
$ cmake --version
cmake version 3.25.0
$ gcc --version
gcc (Ubuntu 9.4.0-1ubuntu1~20.04.2) 9.4.0
Error:
+ cmake -B ./build -DCMAKE_INSTALL_PREFIX=dist
-- Configuring done
-- Generating done
-- Build files have been written to: /home/ristomatti/src/keymapper/build
+ cmake --build ./build --config Release
[ 3%] Building CXX object CMakeFiles/keymapper.dir/src/config/ParseConfig.cpp.o
/home/ristomatti/src/keymapper/src/config/ParseConfig.cpp: In member function ‘std::string ParseConfig::preprocess_ident(std::string) const’:
/home/ristomatti/src/keymapper/src/config/ParseConfig.cpp:466:65: error: could not convert ‘{ident.std::__cxx11::basic_string<char>::begin().__gnu_cxx::__normal_iterator<char*, std::__cxx11::basic_string<char> >::operator+(((__gnu_cxx::__normal_iterator<char*, std::__cxx11::basic_string<char> >::difference_type)pos)).__gnu_cxx::__normal_iterator<char*, std::__cxx11::basic_string<char> >::operator+(1), ident.std::__cxx11::basic_string<char>::end()}’ from ‘<brace-enclosed initializer list>’ to ‘std::string_view’ {aka ‘std::basic_string_view<char>’}
466 | get_argument_list({ ident.begin() + pos + 1, ident.end() }));
| ^
| |
| <brace-enclosed initializer list>
In file included from /usr/include/x86_64-linux-gnu/c++/9/bits/c++allocator.h:33,
from /usr/include/c++/9/bits/allocator.h:46,
from /usr/include/c++/9/string:41,
from /home/ristomatti/src/keymapper/src/runtime/Key.h:3,
from /home/ristomatti/src/keymapper/src/runtime/KeyEvent.h:3,
from /home/ristomatti/src/keymapper/src/config/ParseConfig.h:3,
from /home/ristomatti/src/keymapper/src/config/ParseConfig.cpp:2:
/usr/include/c++/9/ext/new_allocator.h: In instantiation of ‘void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = std::basic_string_view<char>; _Args = {const char*&, const char*}; _Tp = std::basic_string_view<char>]’:
/usr/include/c++/9/bits/alloc_traits.h:483:4: required from ‘static void std::allocator_traits<std::allocator<_CharT> >::construct(std::allocator_traits<std::allocator<_CharT> >::allocator_type&, _Up*, _Args&& ...) [with _Up = std::basic_string_view<char>; _Args = {const char*&, const char*}; _Tp = std::basic_string_view<char>; std::allocator_traits<std::allocator<_CharT> >::allocator_type = std::allocator<std::basic_string_view<char> >]’
/usr/include/c++/9/bits/vector.tcc:115:30: required from ‘std::vector<_Tp, _Alloc>::reference std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {const char*&, const char*}; _Tp = std::basic_string_view<char>; _Alloc = std::allocator<std::basic_string_view<char> >; std::vector<_Tp, _Alloc>::reference = std::basic_string_view<char>&]’
/home/ristomatti/src/keymapper/src/config/ParseConfig.cpp:80:62: required from here
/usr/include/c++/9/ext/new_allocator.h:146:4: error: invalid conversion from ‘const char*’ to ‘std::basic_string_view<char>::size_type’ {aka ‘long unsigned int’} [-fpermissive]
146 | { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
| |
| const char*
In file included from /usr/include/c++/9/bits/basic_string.h:48,
from /usr/include/c++/9/string:55,
from /home/ristomatti/src/keymapper/src/runtime/Key.h:3,
from /home/ristomatti/src/keymapper/src/runtime/KeyEvent.h:3,
from /home/ristomatti/src/keymapper/src/config/ParseConfig.h:3,
from /home/ristomatti/src/keymapper/src/config/ParseConfig.cpp:2:
/usr/include/c++/9/string_view:129:56: note: initializing argument 2 of ‘constexpr std::basic_string_view<_CharT, _Traits>::basic_string_view(const _CharT*, std::basic_string_view<_CharT, _Traits>::size_type) [with _CharT = char; _Traits = std::char_traits<char>; std::basic_string_view<_CharT, _Traits>::size_type = long unsigned int]’
129 | basic_string_view(const _CharT* __str, size_type __len) noexcept
| ~~~~~~~~~~^~~~~
make[2]: *** [CMakeFiles/keymapper.dir/build.make:272: CMakeFiles/keymapper.dir/src/config/ParseConfig.cpp.o] Error 1
make[1]: *** [CMakeFiles/Makefile2:85: CMakeFiles/keymapper.dir/all] Error 2
make: *** [Makefile:156: all] Error 2
Oops, I unintentionally used a C++20 feature. This is fixed now. Please try again.
Here is another example, with multiple arguments:
modify = $0{ $1 }
A >> modify[ShiftLeft, X]
So nice! This will definitely help with debugging. I can get ContextActive
to trigger on class
, title
and modifier
scoped contexts but not on a device
scoped context. Is this a bug or expected behavior
Test config below. When the window gets focused "entered kitty default", but if I press F12, "k15" gets typed.
log = $(echo "$0" >> ~/tmp/keymapper.log)
context = 50ms log["entered $0"] ^ log["left $0"]
KeyboardK15Pro = /Keychron K15 Pro Keyboard$/i
[device=KeyboardK15Pro class="kitty"]
ContextActive >> context["kitty k15 pro"]
F12 >> 'k15'
[class="kitty"]
ContextActive >> context["kitty default"]
F12 >> 'default'
Here is another example, with multiple arguments:
modify = $0{ $1 } A >> modify[ShiftLeft, X]
Multiple arguments seems to work also, perfect.
I see this example uncovered another yet undocumented and also rather unexpected feature! Do you have a specific use case in mind for this? My imagination is lacking with this one. :thinking:
... but not on a
device
scoped context. Is this a bug or expected behavior?
That is not intentional. I will fix that soon. Thanks!
I see this example uncovered another yet undocumented and also rather unexpected feature!
It is actually not another new feature. Just the possibility to pass arguments to aliases/macros.
Do you have a specific use case in mind for this? My imagination is lacking with this one. 🤔
Not yet, maybe someone finds another use case beside debugging. 😄
@houmain I came across a bug with the parameterized aliases. The parameter apparently cannot be an alias itself.
Repro with some context of what I'm doing:
log = $(echo "$0" >> ~/tmp/keymapper.log)
# Logitech G502 X
ButtonSide = 277
DoubleTap = $0 !250ms $0
[default]
ButtonSideDouble = ButtonSide !200ms ButtonSide
ButtonForward{150ms} >> log["mouse_drag"]
ButtonBack{150ms} >> log["mouse_drag_resize"]
# ButtonSideDouble >> log["wm_float_window"] # OK
# DoubleTap[277] >> log["wm_float_window"] # OK
DoubleTap[ButtonSide] >> log["wm_float_window"] # Invalid key "ButtonSide"
ButtonSide >>
[modifier="ButtonSide"]
DoubleTap[ButtonLeft] >> log["wm_resize_window"] # OK
ButtonLeft >> log["wm_center_window"]
DoubleTap[ButtonRight] >> log["wm_close_window"] # OK
ButtonRight >> log["wm_resize_window_wide"]
ButtonMiddle >> log["wm_sticky_toggle"]
Hi ristomatti,
Aliases should work now as parameters. Also the ContextActive in device contexts should work now.
This will be available in the upcoming 4.0.0 release which is already documented and testable in the next
branch. I incremented the major version since I refactored the code a lot. So it would be great if you could try it out with your sophisticated configuration, thanks!
Sorry to report but the fix doesn't seem to work fully and the changes caused some regression with the mappings the above example config was based on. The actual snippet from my config:
MouseG502X = /Logitech USB Receiver|G502 X LIGHTSPEED/i
ButtonSide = 277
[device=MouseG502X]
ButtonSideDouble = ButtonSide !200ms ButtonSide
ButtonForward{150ms} >> mouse_drag
ButtonBack{150ms} >> mouse_drag_resize
# ButtonSideDouble >> wm_float_window
ButtonSideDouble >> log["wm_float_window"] # <- modified for testing
ButtonSide >>
[device=MouseG502X modifier="ButtonSide"]
# Added as a test
ContextActive >> log["MouseG502X ButtonSide"] # <- added for testing
DoubleTap[ButtonLeft] >> wm_resize_window
ButtonLeft >> wm_center_window
# DoubleTap[ButtonRight] >> wm_close_window
DoubleTap[ButtonRight] >> log["wm_close_window"] # <- modified for testing
ButtonRight >> wm_resize_window_wide
ButtonMiddle >> wm_sticky_toggle
# ... lots of other config ...
[default]
mouse_drag >> (AltLeft ButtonLeft)
mouse_drag_resize >> (AltLeft ButtonRight)
wm_resize_window >> $(i3-msg "resize set 1000px 1200px") ^
wm_resize_window_wide >> $(i3-msg "resize set 1800px 1200px") ^
wm_center_window >> $(i3-msg "move absolute position center") ^
wm_close_window >> $(i3-msg '[con_id="__focused__"] kill') ^
wm_float_window >> $(i3-msg "floating toggle") ^
wm_sticky_toggle >> $(i3-msg "sticky toggle") ^
These mappings stopped working:
[device=MouseG502X modifier="ButtonSide"]
ButtonSideDouble >> wm_float_window
ButtonForward{150ms} >> mouse_drag
ButtonBack{150ms} >> mouse_drag_resize
Neither of these seem to trigger:
[device=MouseG502X]
ButtonSideDouble >> log["wm_float_window"]
[device=MouseG502X modifier="ButtonSide"]
ContextActive >> log["MouseG502X ButtonSide"]
This however does work as expected but it did previously as well:
[device=MouseG502X modifier="ButtonSide"]
DoubleTap[ButtonRight] >> log["wm_close_window"]
I incremented the major version since I refactored the code a lot
I can see that. https://github.com/houmain/keymapper/compare/3.5.3...next :exploding_head:
Thanks for trying it out so quickly. I am glad I did not release it yet. I will analyze this asap!
I tried this again with the new updates you mentioned here https://github.com/houmain/keymapper/issues/105#issuecomment-2008003564.
I was able to get ContextActive
to trigger from this:
[device=MouseG502X modifier="ButtonSide"]
ContextActive >> log["MouseG502X ButtonSide"]
I don't know if it's got anything to do with the changes but I got it to trigger by a slight modification below. It seemed to have the downside I couldn't get a mapping like ButtonSide !200ms ButtonSide >> do_stuff
to work.
ButtonSideMod = Virtual7
[device=MouseG502X]
ContextActive >> context["MouseG502X"]
ButtonSide >> ButtonSideMod ^ ButtonSideMod
[device=MouseG502X modifier="ButtonSideMod"]
ContextActive >> context["MouseG502X ButtonSide"]
I did some more testing/debugging around the mouse button mappings. The remaining issue is that my mappings for window move/drag from any part of the window when holding mouse back/forward buttons does not work anymore:
# [device=MouseG502X] <- commented out for this to work as a test config
[default]
ButtonForward{150ms} >> mouse_drag
ButtonBack{150ms} >> mouse_drag_resize
# This would work but it'd break normal use as back/forward buttons
# ButtonForward >> mouse_drag
# ButtonBack >> mouse_drag_resize
[default]
mouse_drag >> (AltLeft ButtonLeft)
mouse_drag_resize >> (AltLeft ButtonRight)
I think I could reproduce it with this configuration. There is still a problem when a key is used at the same time in a context modifier and has a timeout:
X{!1000ms} >> B
[modifier="X"]
ContextActive >> A ^
We're you able to reproduce the last example about the mouse keys held down? I forgot to include this minimum config I was able to reproduce it. It seems to be affect any mapping that relies on holding a button down:
1{250ms} >> 2
Previously this would allow using 1
as normal but act as if 2
was held down if kept pressed longer than 150ms.
Hi ristomatti, With the latest commits I can no longer reproduce any problem. I tested it with the configurations above and this one:
log = $(notify-send -t 2000 -a "keymapper" "$0")
MouseG502X = /G300/i
ButtonSide = ButtonBack
[device=MouseG502X]
ButtonSideDouble = ButtonSide !200ms ButtonSide
ButtonSideDouble >> log["wm_float_window"]
[device=MouseG502X modifier="ButtonBack"]
ContextActive >> log["ButtonSide"]
Could you please also give the latest version an try?
Could you please also give the latest version an try?
For sure. Luckily it's a 4 days weekend due to Easter as I see my inbox is full with notifications from this repo. 😁
I'm still seeing a change with the mouse handling, e.g. my old config doesn't work. I have not yet pinpointed if it's due to relying on some undocumented behavior. Here's an example config with only the bits of config affected:
# Debug logging
log = $(echo "$0" >> ~/tmp/keymapper.log)
context = 50ms log["entered $0"] ^ log["left $0"]
# Custom keys
ButtonSide = 277
# Context matchers
Mouse = /Mouse|Logitech USB Receiver|G502/i
MouseG502X = /Logitech USB Receiver|G502 X LIGHTSPEED/i
# Pameterized aliases
DoubleTap = $0 !250ms $0{!250ms}
Hold = $0{200ms}
[device=Mouse]
ContextActive >> context["Mouse"] # triggers, previosly didn't
ButtonForward{150ms} >> mouse_drag # doesn't trigger
ButtonBack{150ms} >> mouse_drag_resize # doesn't trigger
[device=MouseG502X modifier="ButtonSide"]
ContextActive >> context["G502X ButtonSide"] # triggers, previosly didn't
DoubleTap[ButtonLeft] >> log["wm_resize_window"] # triggers
DoubleTap[ButtonRight] >> log["wm_close_window"] # triggers
Hold[ButtonMiddle] >> log["wm_sticky_toggle"] # doesn't trigger
ButtonLeft >> log["wm_center_window"] # doesn't trigger
ButtonRight >> log["wm_resize_window_wide"] # doesn't trigger
ButtonMiddle >> log["wm_float_window"] # triggers
[default]
mouse_drag >> (AltLeft ButtonLeft)
mouse_drag_resize >> (AltLeft ButtonRight)
Previously all worked except the device context ContextActive
logs.
I just noticed that that if I don't define a separate ButtonSide
context, these both trigger.
[device=MouseG502X]
ButtonSide{DoubleTap[ButtonLeft]} >> log["wm_resize_window"]
ButtonSide{ButtonLeft} >> log["wm_center_window"]
Further findings, this config doesn't work either for me:
log = $(echo "$0" >> ~/tmp/keymapper.log)
Hold = $0{200ms}
Hold[F4] >> log["F4 held"] # doesn't trigger
Actually it seems key hold mappings do not work at all. Even this does not work on my system. :thinking:
A{200ms} >> B
@houmain Out of curiosity, I traced back the commit after which X{200ms} >> Y
mappings stop working. It's this one https://github.com/houmain/keymapper/commit/0979310df0264afefab40ce0f85fd623da9394c3 which first introduced keymapperctl
.
Sorry for the late reply! I was AFK this week. The last commit should fix the problem.
@houmain No sweat! Unfortunately the change didn't seem to have any effect. Here's the test config I used:
# Debug logging
log = $(echo "$0" >> ~/tmp/keymapper.log)
context = 50ms log["entered $0"] ^ log["left $0"]
# Custom keys
ButtonSide = 277
# Context matchers
MouseG502X = /Logitech USB Receiver|G502 X LIGHTSPEED/i
# Pameterized aliases
DoubleTap = $0 !250ms $0{!250ms}
Hold = $0{200ms}
[device=MouseG502X]
ContextActive >> context["MouseG502X"]
# ❌ - no trigger
ButtonForward{150ms} >> mouse_drag
ButtonBack{150ms} >> mouse_drag_resize
[device=MouseG502X modifier="ButtonSide"]
# ✅ - logs "entered X" when button held down, "left X" when released
ContextActive >> context["MouseG502X ButtonSide"]
# ✅ - both trigger consistently
DoubleTap[ButtonLeft] >> log["wm_resize_window"]
DoubleTap[ButtonRight] >> log["wm_close_window"]
# ❌ - no trigger
Hold[ButtonMiddle] >> log["wm_sticky_toggle"]
# ❌ - these two behave similarly as "A{!100ms} B", e.g. in sequence, but not
# when ButtonSide held down
ButtonLeft >> log["wm_center_window"]
ButtonRight >> log["wm_resize_window_wide"]
# ✅ - triggers consistently
ButtonMiddle >> log["wm_float_window"]
[default]
mouse_drag >> (AltLeft ButtonLeft)
mouse_drag_resize >> (AltLeft ButtonRight)
$ inxi -SGx
System: Host: x1e Kernel: 5.15.0-94-generic x86_64 bits: 64 compiler: N/A Desktop: Gnome 3.36.9
Distro: Linux Mint 20 Ulyana base: Ubuntu 20.04 focal
Graphics: Device-1: NVIDIA vendor: Lenovo driver: nvidia v: 520.61.05 bus ID: 01:00.0
Display: x11 server: X.Org 1.20.13 driver: nvidia unloaded: fbdev,modesetting,nouveau,vesa
resolution: 3440x1440~60Hz
OpenGL: renderer: NVIDIA GeForce GTX 1650 Ti with Max-Q Design/PCIe/SSE2 v: 4.6.0 NVIDIA 520.61.05
direct render: Yes
I tested your configuration and found another bug, thanks! I am again quite confident that it should work now.
Great work! Everything the above config works and so does every other mapping I've tried so far. I found one regression though unless it's by design in the new version. Aliases don't work within shell commands. E.g.
LOG_FILE = "$HOME/tmp/keymapper.log"
log = $(echo "$0" >> LOG_FILE)
# ...various other commands that log to the same file
Now the logs go to ~/LOG_FILE
. I don't have that many aliases I use as variables on the commands so I can just inline them. I have a vague memory you at some point added a workaround to prevent alias substitution within quotes(?)
Similarly I had this state indicator logic (which I'm definitely going to redo using keymapperctl
)
# Create state directory if it doesn't exist
ensureStateDirExists = test -f STATE_DIR || mkdir -p STATE_DIR
# Status bar input mode indicator handling
auto_shift_status_on = $(ensureStateDirExists && touch AUTO_SHIFT_FILE)
auto_shift_status_off = $(test -f AUTO_SHIFT_FILE && rm AUTO_SHIFT_FILE)
direct_umlauts_status_on = $(ensureStateDirExists && touch DIRECT_UMLAUTS_FILE)
direct_umlauts_status_off = $(test -f DIRECT_UMLAUTS_FILE && rm DIRECT_UMLAUTS_FILE)
caps_word_status_on = $(ensureStateDirExists && touch CAPS_WORD_FILE)
caps_word_status_off = $(test -f CAPS_WORD_FILE && rm CAPS_WORD_FILE)
Here ensureStateDirExists
gives "command not found" error when running with verbose logging.
Now with everything inlined, the indicators work as well. 🙂
I restored the substitution within terminal commands.
Confirm working again. Your reaction times are insane! :zap:
It would be good to be able to configure a system notification to pop up that helps explain the key map that is active.
For me the notible key maps change when any of the virtual keys are pressed or when a context change matches a new block.
Perhaps a good first step for the notification message would be the virtual keys alias and the context values.
Linux Workaround