Skip to content

Commit 9f0eec8

Browse files
committed
impl the js api
1 parent 3e26cb6 commit 9f0eec8

File tree

23 files changed

+1152
-577
lines changed

23 files changed

+1152
-577
lines changed

.github/workflows/release.yml

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: Release
2+
3+
on:
4+
push:
5+
tags:
6+
- v*
7+
8+
jobs:
9+
release:
10+
runs-on: ubuntu-22.04
11+
steps:
12+
- uses: actions/checkout@v3
13+
14+
- name: Set up libevdev
15+
run: sudo apt-get install libevdev-dev
16+
17+
- name: Set up Go
18+
uses: actions/setup-go@v4
19+
with:
20+
go-version: '1.23'
21+
cache-dependency-path: go.sum
22+
23+
- name: Build
24+
run: |
25+
make
26+
cp keyswift keyswift-linux-amd64
27+
28+
- name: Create release
29+
uses: softprops/action-gh-release@v2
30+
with:
31+
with:
32+
files: keyswift-linux-amd64

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,2 @@
11
.vscode
2+
keyswift

Makefile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
build:
2+
@go build -o keyswift cmd/keyswift/main.go

README.md

Lines changed: 86 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -11,49 +11,50 @@ KeySwift is a keyboard remapping tool designed for Linux gnome desktop environme
1111

1212
Download the latest release from the [Releases](https://github.com/jialeicui/keyswift/releases) page.
1313

14-
Or install from source:
14+
Or build from source:
15+
16+
**Dependencies**:
17+
- libevdev-dev
18+
- golang
19+
1520

1621
```bash
1722
git clone https://github.com/jialeicui/keyswift.git
1823
cd keyswift
19-
make install
24+
make
2025
```
2126

2227
## Configuration
2328

24-
Create a configuration file at `~/.config/keyswift/config.yml`. Here's an example:
25-
26-
```yaml
27-
# yaml-language-server: $schema=schema.json
28-
# KeySwift Configuration File
29-
30-
mode_actions:
31-
default:
32-
if: "true"
33-
triggers:
34-
- source_event:
35-
key_press_event:
36-
key: "esc"
37-
action:
38-
set_value:
39-
normal: true
40-
- source_event:
41-
window_focus_event:
42-
window_class:
43-
- kitty
44-
action:
45-
set_value:
46-
terminal: true
47-
normal:
48-
if: normal
49-
triggers:
50-
- source_event:
51-
key_press_event:
52-
key: "j"
53-
action:
54-
map_to_keys:
55-
keys:
56-
- key: "down"
29+
Create a configuration file at `~/.config/keyswift/config.js`. Here's an example:
30+
31+
```js
32+
const curWindowClass = KeySwift.getActiveWindowClass();
33+
const Terminals = ["kitty", "Gnome-terminal", "org.gnome.Terminal"];
34+
const inTerminal = Terminals.includes(curWindowClass);
35+
36+
KeySwift.onKeyPress(["cmd", "v"], () => {
37+
if (curWindowClass === "com.mitchellh.ghostty") {
38+
KeySwift.sendKeys(["shift", "ctrl", "v"]);
39+
return
40+
}
41+
42+
if (inTerminal) {
43+
KeySwift.sendKeys(["cmd", "shift", "v"]);
44+
}
45+
});
46+
```
47+
48+
You can also see more examples in the [examples](examples) directory.
49+
50+
KeySwift's config is implemented based on [QuickJS](https://bellard.org/quickjs), and all available objects and functions are as follows:
51+
52+
```js
53+
const KeySwift = {
54+
getActiveWindowClass: () => string,
55+
sendKeys: (keys: string[]) => void,
56+
onKeyPress: (keys: string[], callback: () => void) => void,
57+
}
5758
```
5859

5960
## Acknowledgments
@@ -66,6 +67,56 @@ KeySwift was inspired by several excellent projects:
6667

6768
Thank you to the maintainers of these projects for your contributions to open-source keyboard customization tools!
6869

70+
This project also draws inspiration from [AutoHotkey](https://www.autohotkey.com)'s design philosophy. Thanks to this amazing project
71+
72+
## Tips
73+
74+
### How to get the active window class
75+
76+
You can use the `cmd+i` shortcut to print the active window class to the console with the following configuration:
77+
78+
```js
79+
KeySwift.onKeyPress(["cmd", "i"], () => {
80+
const curWindowClass = KeySwift.getActiveWindowClass();
81+
console.log(curWindowClass);
82+
});
83+
```
84+
### How to run the program
85+
86+
1. Install the keyswift gnome extension
87+
88+
```bash
89+
# Clone the repository
90+
git clone https://github.com/jialeicui/keyswift-gnome-ext.git ~/.local/share/gnome-shell/extensions/keyswift@jialeicui.github.io
91+
# Enable the extension via gnome-extensions-app or gnome-extensions cli
92+
gnome-extensions enable keyswift@jialeicui.github.io
93+
# You may need to restart the gnome-shell
94+
```
95+
96+
2. Get the input device permission for current user
97+
98+
```bash
99+
sudo gpasswd -a $USER input
100+
echo 'KERNEL=="uinput", GROUP="input", TAG+="uaccess"' | sudo tee /etc/udev/rules.d/input.rules
101+
# You may need to restart the system to take effect
102+
```
103+
104+
3. Run the program
105+
106+
```bash
107+
# XXX is the substring of the keyboard device name
108+
./keyswift -keyboards XXX -config ~/.config/keyswift/config.js
109+
```
110+
- if you have multiple keyboards, you can use comma to separate them
111+
- if you don't know the device name, you can leave it blank and the program will print all the keyboard device names and you can select one of them
112+
113+
**NOTE**: KeySwift does not support running with sudo, so you need to run the program with current user.
114+
115+
## TODO
116+
117+
- [ ] Support KDE
118+
- [ ] Support Mouse
119+
69120
## Getting Help
70121

71122
If you encounter any issues or have questions, please [open an issue](https://github.com/jialeicui/keyswift/issues) on the GitHub repository.

cmd/keyswift/main.go

Lines changed: 17 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,18 @@ import (
1212
"github.com/jialeicui/golibevdev"
1313
"github.com/samber/lo"
1414

15-
"github.com/jialeicui/keyswift/pkg/conf"
16-
"github.com/jialeicui/keyswift/pkg/dev"
15+
"github.com/jialeicui/keyswift/pkg/bus"
1716
"github.com/jialeicui/keyswift/pkg/evdev"
18-
"github.com/jialeicui/keyswift/pkg/keys"
19-
"github.com/jialeicui/keyswift/pkg/mode"
17+
"github.com/jialeicui/keyswift/pkg/handler"
18+
"github.com/jialeicui/keyswift/pkg/utils"
2019
"github.com/jialeicui/keyswift/pkg/wininfo/dbus"
2120
)
2221

2322
var (
24-
flagKeyboards = flag.String("keyboards", "Apple", "Comma-separated list of keyboard device name substrings")
25-
flagConfig = flag.String("config", "", "Configuration file path (defaults to $XDG_CONFIG_HOME/keyswift/config.js)")
26-
// TODO change this to false
27-
flagVerbose = flag.Bool("verbose", true, "Enable verbose logging")
23+
flagKeyboards = flag.String("keyboards", "HHKB", "Comma-separated list of keyboard device name substrings")
24+
flagConfig = flag.String("config", "", "Configuration file path (defaults to $XDG_CONFIG_HOME/keyswift/config.js)")
25+
flagVerbose = flag.Bool("verbose", false, "Enable verbose logging")
26+
flagOutputDeviceName = flag.String("output-device-name", "keyswift", "Name of the virtual keyboard device")
2827
)
2928

3029
func main() {
@@ -40,7 +39,7 @@ func main() {
4039
// Load configuration
4140
configPath := *flagConfig
4241
if configPath == "" {
43-
configPath = conf.DefaultConfigPath()
42+
configPath = utils.DefaultConfigPath()
4443
}
4544

4645
// Initialize window info service
@@ -53,27 +52,26 @@ func main() {
5352
slog.Info("Window Monitor service is running...")
5453

5554
// Initialize virtual keyboard for output
56-
out, err := golibevdev.NewVirtualKeyboard("keyswift")
55+
out, err := golibevdev.NewVirtualKeyboard(*flagOutputDeviceName)
5756
if err != nil {
5857
slog.Error("Failed to create virtual keyboard", "error", err)
5958
os.Exit(1)
6059
}
6160
defer out.Close()
62-
gnomeKeys := keys.NewGnomeKeys(out)
6361

6462
script, err := os.ReadFile(configPath)
6563
if err != nil {
6664
slog.Error("Failed to read configuration file", "error", err)
6765
os.Exit(1)
6866
}
6967

70-
// Initialize mode manager
71-
modeManager, err := mode.NewManager(string(script), gnomeKeys, windowMonitor)
68+
// Initialize bus manager
69+
busMgr, err := bus.New(string(script), windowMonitor, out)
7270
if err != nil {
73-
slog.Error("Failed to initialize mode manager", "error", err)
71+
slog.Error("Failed to initialize bus manager", "error", err)
7472
os.Exit(1)
7573
}
76-
slog.Info("Mode manager initialized")
74+
slog.Info("bus manager initialized")
7775

7876
// Find input devices
7977
devs, err := evdev.NewOverviewImpl().ListInputDevices()
@@ -93,7 +91,7 @@ func main() {
9391
}
9492

9593
matches := lo.Filter(devs, func(item *evdev.InputDevice, _ int) bool {
96-
return strings.Contains(item.Name, pattern)
94+
return strings.Contains(item.Name, pattern) && item.Name != *flagOutputDeviceName
9795
})
9896

9997
matchedDevices = append(matchedDevices, matches...)
@@ -114,7 +112,7 @@ func main() {
114112
}
115113

116114
// Initialize and set up the device manager
117-
deviceManager := dev.NewInputDeviceManager()
115+
deviceManager := handler.New()
118116
defer deviceManager.Close()
119117

120118
// Add all matched devices to the manager
@@ -140,8 +138,8 @@ func main() {
140138
}()
141139

142140
// Start processing events from all devices
143-
slog.Info(fmt.Sprintf("Processing events from %d devices... Press Ctrl+C to exit\n", len(deviceManager.GetDevices())))
144-
deviceManager.ProcessEvents(out, modeManager)
141+
slog.Info(fmt.Sprintf("Processing events from %d devices... Press Ctrl+C to exit", len(deviceManager.GetDevices())))
142+
deviceManager.ProcessEvents(out, busMgr)
145143

146144
// Wait for all processing to complete (typically won't reach here except on error)
147145
deviceManager.Wait()

0 commit comments

Comments
 (0)