Skip to content

Commit

Permalink
Merge pull request #18 from grafov/hyprland
Browse files Browse the repository at this point in the history
Add layout switcher for Hyprland
  • Loading branch information
grafov authored Jan 29, 2025
2 parents 84b4f5b + 2371f82 commit 757a7ee
Show file tree
Hide file tree
Showing 10 changed files with 509 additions and 132 deletions.
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
.PHONY: build
build:
go build -compiler gccgo -gccgoflags "-lX11" shift-shift.go
CGO_ENABLED=1 go build shift-shift.go

.PHONY: lint
lint:
golangci-lint run ./...
287 changes: 231 additions & 56 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,104 +1,279 @@
What is it and why?
===================
## What is it and why?

Language layout switcher for Xorg and Wayland (Sway support) with
modifiers (Shift, Control and so on) as switcher keys.
*Do you want to use Shift/Ctrl/Alt as modifiers while also switching language
layouts? This tool is for you then.*

This way allows to use the same key as a modifier and as a layout switcher without conflicts.
A language layout switcher for Xorg and Wayland (including Sway and Hyprland)
that utilizes modifier keys (such as Shift and Control) as layout switchers.
This setup enables the same key to function both as a modifier and a layout
switcher without conflicts.

The utility implements two ideas:
The utility incorporates two key concepts:

1. You could use a key as a modifier when you HOLD it and as a key switcher when you TAP it.
2. Cyclic switching by a single key is a bad idea, better to use DEDICATED keys for each language group.
1. Use a key as a modifier when held and as a layout switcher when tapped.
2. Cyclic switching with a single key is inefficient; dedicated keys for each
language group are more effective.

It maybe not true if you use a bunch of languages of same time but it
is true for the most use cases with 2-3 langs.
This approach may not be necessary when using multiple languages simultaneously,
but it is beneficial for most cases involving 2-3 languages. Frequent keyboard
layout switching, common among non-English speakers, is easier with dedicated
keys than with key combinations. For example, old Soviet computers featured a
dedicated RUS/LAT key to toggle between Latin and Cyrillic. Some computers even
offered two separate keys: RUS and LAT!

If you are often switch keyboard layouts (it real use case
for those who speaking not only English) then dedicated keys are more easy
for typing than key combos. Old Soviet computers for example had
dedicated key RUS/LAT for switch between Latin and Cyrillic.
Unfortunately, modern English-oriented keyboards lack dedicated layout-switching
keys. Modifier keys are typically used only in combination with other keys. This
utility provides a solution: these keys can perform their original functions
while also being used to switch layouts.

Sadly in modern English-oriented keyboards there are no dedicated keys
for switching layouts. Modifier keys usually used only by holding with
other key. So it looks like a good compromise: you are still able to
use them for their original purposes but when you tapping them they
work as language layout switchers.
By default, the Left Shift key switches to layout 1 and the Right Shift key to
layout 2. You can customize this behavior using command-line options. The utility
supports up to 4 groups.

By default Left Shift swithches to group 1 and Right Shift to
group 2. You may change this behavior with command line options.
Up to 4 xkb groups supported.
Additionally, you can treat any input device as a keyboard using the `-match`
option, enabling simultaneous layout switching across multiple keyboards. The
utility listens to all connected devices.

Also you could try to treat any devices as keyboards with `-match`
option. It allows to switch group simultaneously on an arbitrary
number of connected keyboards.
The program listens to keypress events from the kernel (via evdev) and sends
them to Xorg or your window manager. Different WMs have their own methods for
keyboard switching, especially on Wayland. I have added support for Wayland
window managers I have used extensively. See the examples for all variants.

Install
=======
## Install

This is a Go program. You should need Go environment to build it from sources.
### Go way

This is a Go program. You need a Go environment to build it from source.

go get github.com/grafov/shift-shift@latest

`xlib-devel` libs should be installed. Check your distro.
`xlib-devel` lib should be installed. Check your distro.

### Get sources

1. Get repository and cd to it.
2. `make build`

Usage
=====
Prerequisites are the same: `xlib-devel` lib should be installed. Check your distro.

## Usage

```
$ shift-shift -h
Usage of shift-shift:
$ Usage of ./shift-shift:
-1 string
key used for switching to 1st xkb group (default "LEFTSHIFT")
key used for switching to 1st xkb group (default "LEFTSHIFT")
-2 string
key used for switching to 2nd xkb group (default "RIGHTSHIFT")
key used for switching to 2nd xkb group (default "RIGHTSHIFT")
-3 string
key used for switching to 3rd xkb group
key used for switching to 3rd xkb group
-4 string
key used for switching to 4th xkb group
key used for switching to 4th xkb group
-double-keystroke
require pressing the same key twice to switch the layout
require pressing the same key twice to switch the layout
-double-keystroke-timeout int
second keystroke timeout in milliseconds (default 500)
second keystroke timeout in milliseconds (default 500)
-list
list all devices that found by evdev (not only keyboards)
list all devices that found by evdev (not only keyboards)
-list-hypr
list all keyboards recognized by Hyprland
-list-sway
list all devices recognized by Sway (not only keyboards)
list all devices recognized by Sway (not only keyboards)
-match string
regexp used to match keyboard device (default "keyboard")
regexp used to match input keyboard device as it listed by evdev (default "keyboard")
-match-wm string
optional regexp used to match device in WM, if not set evdev regexp used (default "keyboard")
-print
print pressed keys for debug (verbose output)
print pressed keys for debug (verbose output)
-quiet
be silent
be silent
-scan-once
scan for keyboards only at startup (less power consumption)
scan for keyboards only at startup (less power consumption)
-switcher string
select method of switching (possible values are "auto", "xkb", "sway") (default "auto")
select method of switching (possible values are "auto", "xkb", "sway", "hypr") (default "auto")
```

On start the program tries to find devices where name contains "keyboard" string. The substring could be customized with `-match` option. [The syntax](https://pkg.go.dev/regexp/syntax) of regular expressions could be used. For example:
## Configuration

### 1. Match keyboard in evdev output
First step is common for all environments. Just try to find your keyboard device. It may look like this:

```
$ shift-shift -match "(?i)ergodox|ergohaven"
$ shift-shift -list
/dev/input/event3 PC Speaker
/dev/input/event4 HDA ATI HDMI HDMI/DP,pcm=3
/dev/input/event6 HDA ATI HDMI HDMI/DP,pcm=8
/dev/input/event8 HD-Audio Generic Rear Mic
/dev/input/event12 HD-Audio Generic Front Headphone
/dev/input/event20 ZSA Technology Labs ErgoDox EZ System Control
/dev/input/event21 ZSA Technology Labs ErgoDox EZ Consumer Control
/dev/input/event23 VIRPIL Controls/20210102 R-VPC Stick MT-50
/dev/input/event7 HDA ATI HDMI HDMI/DP,pcm=9
/dev/input/event18 ZSA Technology Labs ErgoDox EZ
/dev/input/event22 ZSA Technology Labs ErgoDox EZ Keyboard
/dev/input/event24 ATMEL/VIRPIL/190930 BRD Rudder V3
/dev/input/event5 HDA ATI HDMI HDMI/DP,pcm=7
/dev/input/event17 Kensington Kensington Expert Mouse
/dev/input/event19 ZSA Technology Labs ErgoDox EZ
...
```

Check the list of evdev detected devices with:
And consists of any devices recognized by kernel on your machine. Just select any that looks like a keyboard :)

**Note:** you need setup proper access for reading `/dev/input/*`
devices. As a fallback try to run with `sudo` or similar tool.

You should add it `-match` key. You are free to use regular expression or just a substring for matching. Check [The
syntax of regexps](https://pkg.go.dev/regexp/syntax) for reference.

```
$ shift-shift -list
-match="^ZSA.*Keyboard$"
```

Hint. You could match several keyboards at once. They will used when connected:

```
$ shift-shift -match "Ergodox|Ergohaven"
```

**Wayland/Sway note.** In the same time `-match` option applied to the
list of Sway input devices. You could check them with:
Well, we've matched device with evdev.

### 2. Match switcher

Next step is match it to a switcher. It
depends of your environment, see variants below.

#### 2.1 WM or DE under XOrg

```
$ shift-shift -match="^ZSA.*Keyboard$" -switcher=xkb
```

And it will run switcher especially for XOrg.

#### 2.2 Sway WM

In most of cases you should just add "-switcher=sway" to command line and it will works.

``` shell
$ shift-shift -match="^ZSA.*Keyboard$" -switcher=sway
```

You could list keyboards as they seen by Sway:

``` shell
$ shift-shift -list-sway

{
"identifier": "12951:18804:ZSA_Technology_Labs_ErgoDox_EZ_Keyboard",
"name": "ZSA Technology Labs ErgoDox EZ Keyboard",
"type": "keyboard",
"repeat_delay": 350,
"repeat_rate": 50,
"xkb_layout_names": [
"English (US)",
"Russian (typewriter)"
],
"xkb_active_layout_index": 0,
"xkb_active_layout_name": "English (US)",
"libinput": {
"send_events": "enabled"
},
"vendor": 12951,
"product": 18804
},
{
"identifier": "12951:18804:ZSA_Technology_Labs_ErgoDox_EZ_Consumer_Control",
"name": "ZSA Technology Labs ErgoDox EZ Consumer Control",
...
```
**Note:** you need setup proper access for reading `/dev/input/*`
devices. As a fallback try to run with `sudo` or similar tool.
It will display your list of devices (but not only keyboards).
Sometimes Sway may name the keyboard differently. In such cases, you can match
the keyboard using a regular expression similar to the evdev example above.
Then, add the result to the "-match-wm" key:
```
$ shift-shift -match="^ZSA.*Keyboard$" -switcher=sway -match-wm="^ZSA.*Keyboard$"
```
When "-match-wm" omited its value set to value of "-match".
#### 2.3 Hyprland WM
In most of cases you should just add "-switcher=hypr" to command line and it will works.
``` shell
$ shift-shift -match="(?i)ergodox" -switcher=hypr
```
You could list keyboards as they seen by Hyprland:
``` shell
$ shift-shift -list-hypr

model: name:zsa-technology-labs-ergodox-ez layout:us,ru options:caps:none,misc:typo,lv3:capslock_switch keymap:English (US)
```
Naming in Hyprland differs from evdev. You can match the keyboard using a
regular expression, similar to evdev as described above. Then, add the result to
the "-match-wm" key:
``` shell
$ shift-shift -match="^ZSA.*Keyboard$" -switcher=hypr -match-wm="^zsa-technology-labs-ergodox-ez$"
```
When "-match-wm" omited its value set to value of "-match".
#### 3. Print for detected keyboards
For debugging you could run with "-print" option. For example the output for Hyprland config above:
```
2025/01/30 01:12:28 use Hyprland switcher
2025/01/30 01:12:28 Hyprland keyboard matched zsa-technology-labs-ergodox-ez
2025/01/30 01:12:29 evdev keyboard found at /dev/input/event21: ZSA Technology Labs ErgoDox EZ Consumer Control
2025/01/30 01:12:29 evdev keyboard found at /dev/input/event22: ZSA Technology Labs ErgoDox EZ Keyboard

```
#### 4. Keys for switching
By default, the left Shift key is used for layout 0, and the right Shift key for
layout 1. You can use up to four layouts, naming them with the keys "-1" through
"-4". In the example below, the Ctrls key is used:
``` shell
$ shift-shift -match="^ZSA.*Keyboard$" -switcher=hypr -match-wm="^zsa-technology-labs-ergodox-ez$" -1 LEFT_CTRL -2 RIGHT_CTRL -print
```
The output with "-print" when you press any of switch keys:
```
2025/01/30 01:12:29 ZSA Technology Labs ErgoDox EZ type:1 code:90 pressed
2025/01/30 01:12:29 ZSA Technology Labs ErgoDox EZ type:1 code:90 released
2025/01/30 01:12:29 ZSA Technology Labs ErgoDox EZ switches group to 1
2025/01/30 01:12:29 switch hyprland kbd "zsa-technology-labs-ergodox-ez" to group 0
2025/01/30 01:12:30 ZSA Technology Labs ErgoDox EZ type:1 code:91 pressed
2025/01/30 01:12:30 ZSA Technology Labs ErgoDox EZ type:1 code:91 released
2025/01/30 01:12:30 ZSA Technology Labs ErgoDox EZ switches group to 2
```
For reference full list of key names recognized by evdev:
``` shell
$ cat /usr/include/linux/input-event-codes.h
```
#### 5. Final setup
Just remove "-print" once everything is working. Then, add the final command to
your window manager's autostart.
$ shift-shift -match="^ZSA.*Keyboard$" -switcher=hypr -match-wm="^zsa-technology-labs-ergodox-ez$" -1 LEFT_CTRL -2 RIGHT_CTRL -print
Thanks
======
## Thanks
Thanks to people who contributed bugreports and improvements for
`shift-shift`, especially to
Expand Down
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
module github.com/grafov/shift-shift

go 1.20
go 1.21

require github.com/grafov/evdev v1.0.0

require github.com/thiagokokada/hyprland-go v0.4.0
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
github.com/grafov/evdev v1.0.0 h1:/rsOssITVhM7GPMyvlT34kiAOnvYzKJnKvuOqaMYW7c=
github.com/grafov/evdev v1.0.0/go.mod h1:dcJfZnwr3VySHuphTV+Q0JcbP3AWR9W4WqbLeA3bG6U=
github.com/thiagokokada/hyprland-go v0.4.0 h1:SJQYVWYde240BjseQr7QRKRDCMdieKlaMO5RNWG2vh8=
github.com/thiagokokada/hyprland-go v0.4.0/go.mod h1:gUGbdxhD7QdvPpdEwsB09x0HzICPkhKAuCeY+LVx5YM=
Loading

0 comments on commit 757a7ee

Please sign in to comment.