Any other better keybinding methods?

My keyboard habits are as follows:
Switch Caps Lock and Esc
Swap left Alt and left Ctrl
then:
L_Ctrl + H -> Left
L_Ctrl + J -> Down
L_Ctrl + K -> Up
L_Ctrl + L -> Right
L_Ctrl + B -> Ctrl + Left
L_Ctrl + W -> Ctrl + Right
L_Ctrl + num_0 -> Home
L_Ctrl + num_4 -> End

Previously, with setxkbmap, this tool could only finish swap keys. with xmodmap, this tool could only modify the mapping of a single key, but multiple keys could not be implemented.Changing left Ctrl to Mode_Switch does part of the feature, but the original left Ctrl key doesn’t work. If I want to switch input methods with left Ctrl+space, I can’t do it, because the keyboard layout has been broken.
I have also used xbindkeys, sxhkd combined with xdotool, xvkbd, xev, but the experience was very poor. For example, I use xbindkeys with xdotool, if I press Left Ctrl+ H, I can change to Left. However, if I press L_Ctrl+ H all the time, I can only produce a Left key instead of a continuous Left

xvkbd with --xsendevent options can work well in some apps witch is not based on gtk3.

I eventually discovered xkeysnail, a tool that almost perfectly answered my needs, but had a major problem with “libinput Disable While Typing Enabled” function is failed on my touchpad.

my xnailkeys config is

import re
from xkeysnail.transform import *

define_modmap({
    # 交换 ESC 和 Caps Lock
    Key.CAPSLOCK: Key.ESC,
    Key.ESC: Key.CAPSLOCK,

    # 交换 左Alt 和 左Ctrl
    Key.LEFT_CTRL: Key.LEFT_ALT,
    Key.LEFT_ALT: Key.LEFT_CTRL
})

define_keymap(None, {
    K("LC-h"): K("LEFT"),     
    K("LC-j"): K("DOWN"),    
    K("LC-k"): K("UP"),       
    K("LC-l"): K("RIGHT"),    
    K("LC-b"): K("C-LEFT"),    
    K("LC-w"): K("C-RIGHT"),  
    K("LC-KEY_0"): K("home"), 
    K("LC-KEY_4"): K("end")   
}, "vim-like keys")

define_keymap(re.compile("firefox"), {
    K("RC-KEY_0"): K("LM-KEY_0"),
    K("RC-KEY_1"): K("LM-KEY_1"),
    K("RC-KEY_2"): K("LM-KEY_2"),
    K("RC-KEY_3"): K("LM-KEY_3"),
    K("RC-KEY_4"): K("LM-KEY_4"),
    K("RC-KEY_5"): K("LM-KEY_5"),
    K("RC-KEY_6"): K("LM-KEY_6"),
    K("RC-KEY_7"): K("LM-KEY_7"),
    K("RC-KEY_8"): K("LM-KEY_8"),
    K("RC-KEY_9"): K("LM-KEY_9"),
}, "firefox")

Is there any other way?