Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Support for ydotool #212

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ rofimoji.egg-info
dist
build
src/picker/data/copyme.py
venv/
48 changes: 45 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ You can configure `rofimoji` either with cli arguments or with a config file cal
| `--selector-args` | | | | Define arguments that `rofimoji` will pass through to the selector.<br/>Please note that you need to specify it as `--selector-args="<selector-args>"` or `--selector-args " <selector-args>"` because of a [bug in argparse](https://bugs.python.org/issue9334) |
| `--selector` | | `rofi`, `wofi`, `fuzzel`, `dmenu`, `tofi`, `bemenu`, `wmenu` | (automatically chosen) | Show the selection dialog with this application. |
| `--clipboarder` | | `xsel`, `xclip`, `wl-copy` | (automatically chosen) | Access the clipboard with this application. |
| `--typer` | | `xdotool`, `wtype` | (automatically chosen) | Type the characters using this application. |
| `--typer` | | `xdotool`, `ydotool`, `wtype` | (automatically chosen) | Type the characters using this application. |
| `--keybinding-copy`, `--keybinding-type`, `--keybinding-clipboard`, `--keybinding-unicode`, `--keybinding-copy-unicode` | | | `Alt+c`, `Alt+t`, `Alt+p`, `Alt+u`, `Alt+i` | Choose different keybindings than the default values. |

## Example config file
Expand All @@ -94,7 +94,7 @@ The options are:
| `print` | | Print the chosen characters to `stdout`. |

## Insertion method
By default, `rofimoji` types the characters using either `xdotool` or `wtype` (see [display server support](#display-server-support)). You can enforce this behavior with `--action type` (`-a type`).
By default, `rofimoji` types the characters using either `xdotool`, `ydotool`, or `wtype` (see [display server support](#display-server-support)). You can enforce this behavior with `--action type` (`-a type`).

For some applications (f.e. Firefox), this does not work reliably. To work around this, `rofimoji` can copy the emojis to your clipboard and insert them from there with `shift+insert`. Afterwards, it will restore the previous contents.
Unfortunately, it depends on the receiving application whether `shift+insert` uses the clipboard or the primary selection.
Expand Down Expand Up @@ -159,7 +159,7 @@ This also installs the python dependency `configargparse`.
What else do you need:
- Python 3.8 or higher
- A font that can display your scripts, (for emojis, [EmojiOne](https://github.com/emojione/emojione) or [Noto Emoji](https://www.google.com/get/noto/) work)
- Optionally, a tool to programmatically type characters into applications. Either `xdotool` for X11 or `wtype` for Wayland
- Optionally, a tool to programmatically type characters into applications. Either `xdotool` for X11 or `wtype`/`ydotool` for Wayland
- Optionally, a tool to copy the characters to the clipboard. `xsel` and `xclip` work on X11; `wl-copy` on Wayland

### Supported Selectors
Expand All @@ -183,3 +183,45 @@ All other selectors can be used for the basic functionality.
- [tofi](https://github.com/philj56/tofi)
- [bemenu](https://github.com/Cloudef/bemenu)
- [wmenu](https://git.sr.ht/~adnano/wmenu)

# Troubleshooting
To troubleshoot, run `rofimoji` in the command line and inspect its output.

Below is a list of error messages one can get with the typer and other components.
## Typers
> `Compositor does not support the virtual keyboard protocol`

This is an error thrown by `wtype` to indicate that the virtual keyboard protocol is not implemented. You likely use KDE Plasma or Gnome, who don't implement that protocol. Consider using a different typer such as `ydotool` or `xdotool` by passing in the `--typer` argument.

> ```
> failed to connect socket `/run/user/1000/.ydotool_socket': Connection refused
> Please check if ydotoold is running
> ```

This is an error thrown by `ydotool` to indicate it cannot find its service daemon. `ydotool` needs a daemon called `ydotoold` running in the background in order to be able to type things. You can start the `ydotoold` daemon with one of two options:

1. Create a `systemd` service (recommended)

Create the file `/etc/systemd/system/ydotoold.service`and put the following in it:
```ini
[Unit]
Description=Starts ydotoold Daemon

[Service]
Type=simple
Restart=always
RestartSec=3
ExecStartPre=/bin/sleep 2
ExecStart=/usr/bin/ydotoold --socket-path="/run/user/1000/.ydotool_socket" --socket-own="$(id -u):$(id -g)"
ExecReload=/usr/bin/kill -HUP $MAINPID
KillMode=process
TimeoutSec=180

[Install]
WantedBy=basic.target
```
Then run `sudo systemctl enable ydotoold.service && sudo systemctl start ydotoold.service` and rerun `rofimoji`.

2. Run `ydotoold` on its own (*not recommended!*)

Run `ydotoold &` (with or without sudo) and then run `rofimoji`. If it has trouble finding the socket, set the environment variable `YDOTOOL_SOCKET` when running `rofimoji`
3 changes: 1 addition & 2 deletions src/picker/abstractionhelper.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,5 @@
def is_installed(executable: str) -> bool:
return shutil.which(executable) is not None


def is_wayland() -> bool:
return "WAYLAND_DISPLAY" in os.environ
return "WAYLAND_DISPLAY" in os.environ
4 changes: 4 additions & 0 deletions src/picker/action.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from .clipboarder.clipboarder import Clipboarder
from .models import Action
from .typer.typer import Typer
from .input_event_codes import keycodes


def execute_action(
Expand Down Expand Up @@ -32,3 +33,6 @@ def execute_action(

def __get_codepoints(char: str) -> str:
return "-".join(f"{ord(c):x}" for c in char)

def __get_event_code(char: str) -> str:
return str(keycodes["KEY_" + char.upper()])
8 changes: 4 additions & 4 deletions src/picker/argument_parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def __parse_arguments(only_known: bool) -> argparse.Namespace:
metavar="FILE",
help="Read characters from this file instead, one entry per line",
)
parser.add_argument("--prompt", "-r", dest="prompt", action="store", default="😀 ", help="Set rofimoj's prompt")
parser.add_argument("--prompt", "-r", dest="prompt", action="store", default="😀 ", help="Set rofimoji's prompt")
parser.add_argument(
"--selector-args",
dest="selector_args",
Expand Down Expand Up @@ -97,14 +97,14 @@ def __parse_arguments(only_known: bool) -> argparse.Namespace:
"--hidden-descriptions",
dest="show_description",
action="store_false",
help="Show only the character without its description",
help="Show only the character without its description (Rofi only)",
)
parser.set_defaults(show_description=True)
parser.add_argument(
"--use-icons",
dest="use_icons",
action="store_true",
help="Use rofi's icon to show the character",
help="Use rofi's icon to show the character (Rofi only)",
)
parser.set_defaults(use_icons=False)
parser.add_argument(
Expand All @@ -130,7 +130,7 @@ def __parse_arguments(only_known: bool) -> argparse.Namespace:
dest="typer",
action="store",
type=str,
choices=["xdotool", "wtype"],
choices=["xdotool", "ydotool", "wtype"],
default=None,
help="Choose the application to type with",
)
Expand Down
88 changes: 88 additions & 0 deletions src/picker/input_event_codes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Consulted file /usr/include/linux/input-event-codes.h for the event codes
keycodes = {
"KEY_RESERVED":0,
"KEY_ESC":1,
"KEY_1":2,
"KEY_2":3,
"KEY_3":4,
"KEY_4":5,
"KEY_5":6,
"KEY_6":7,
"KEY_7":8,
"KEY_8":9,
"KEY_9":10,
"KEY_0":11,
"KEY_MINUS":12,
"KEY_EQUAL":13,
"KEY_BACKSPACE":14,
"KEY_TAB":15,
"KEY_Q":16,
"KEY_W":17,
"KEY_E":18,
"KEY_R":19,
"KEY_T":20,
"KEY_Y":21,
"KEY_U":22,
"KEY_I":23,
"KEY_O":24,
"KEY_P":25,
"KEY_LEFTBRACE":26,
"KEY_RIGHTBRACE":27,
"KEY_ENTER":28,
"KEY_LEFTCTRL":29,
"KEY_A":30,
"KEY_S":31,
"KEY_D":32,
"KEY_F":33,
"KEY_G":34,
"KEY_H":35,
"KEY_J":36,
"KEY_K":37,
"KEY_L":38,
"KEY_SEMICOLON":39,
"KEY_APOSTROPHE":40,
"KEY_GRAVE":41,
"KEY_LEFTSHIFT":42,
"KEY_BACKSLASH":43,
"KEY_Z":44,
"KEY_X":45,
"KEY_C":46,
"KEY_V":47,
"KEY_B":48,
"KEY_N":49,
"KEY_M":50,
"KEY_COMMA":51,
"KEY_DOT":52,
"KEY_SLASH":53,
"KEY_RIGHTSHIFT":54,
"KEY_KPASTERISK":55,
"KEY_LEFTALT":56,
"KEY_SPACE":57,
"KEY_CAPSLOCK":58,
"KEY_F1":59,
"KEY_F2":60,
"KEY_F3":61,
"KEY_F4":62,
"KEY_F5":63,
"KEY_F6":64,
"KEY_F7":65,
"KEY_F8":66,
"KEY_F9":67,
"KEY_F10":68,
"KEY_NUMLOCK":69,
"KEY_SCROLLLOCK":70,
"KEY_KP7":71,
"KEY_KP8":72,
"KEY_KP9":73,
"KEY_KPMINUS":74,
"KEY_KP4":75,
"KEY_KP5":76,
"KEY_KP6":77,
"KEY_KPPLUS":78,
"KEY_KP1":79,
"KEY_KP2":80,
"KEY_KP3":81,
"KEY_KP0":82,
"KEY_KPDOT":83,
"KEY_INSERT": 110,
}
3 changes: 2 additions & 1 deletion src/picker/typer/typer.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ def best_option(name: Optional[str] = None) -> "Typer":
from .noop import NoopTyper
from .wtype import WTypeTyper
from .xdotool import XDoToolTyper
from .ydotool import YdotoolTyper as YDoToolTyper

available_typers = [XDoToolTyper, WTypeTyper, NoopTyper]
available_typers = [XDoToolTyper, YDoToolTyper, WTypeTyper, NoopTyper]

if name is not None:
return next(typer for typer in available_typers if typer.name() == name)()
Expand Down
2 changes: 1 addition & 1 deletion src/picker/typer/wtype.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class WTypeTyper(Typer):
@staticmethod
def supported() -> bool:
return is_wayland() and is_installed("wtype")

@staticmethod
def name() -> str:
return "wtype"
Expand Down
49 changes: 49 additions & 0 deletions src/picker/typer/ydotool.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
from subprocess import run

from ..abstractionhelper import is_installed
from ..action import __get_codepoints as get_codepoints, __get_event_code as get_event_code
from .typer import Typer

class YdotoolTyper(Typer):
@staticmethod
def name():
return "ydotool"

@staticmethod
def supported():
return is_installed("ydotool")

def get_active_window(self):
return "not possible with ydotool"

def type_characters(self, characters: str, active_window: str) -> None:
# characters is assumed to be a string of emojis; for each emoji,
# get the unicode code point, then for each char in the unicode code point,
# get the event code for the char, then send the event code to ydotool
for character in characters:
# Get the unicode code point for the emoji
unicode_code_point = get_codepoints(character)

# Get keypresses for Ctrl, Shift, U, and the unicode code point
Ctrl = get_event_code("LeftCtrl") + ":1"
Shift = get_event_code("LeftShift") + ":1"
U_press = get_event_code("U") + ":1"
Ctrl_release = get_event_code("LeftCtrl") + ":0"
Shift_release = get_event_code("LeftShift") + ":0"
U_release = get_event_code("U") + ":0"
points = []

for point in unicode_code_point:
points.append(get_event_code(point) + ":1")
points.append(get_event_code(point) + ":0")

# Send the event codes to ydotool
run(["ydotool", "key", Ctrl, Shift, U_press, U_release] + points + [Shift_release, Ctrl_release])

def insert_from_clipboard(self, active_window: str) -> None:
Shift = get_event_code("LeftShift") + ":1"
Shift_release = get_event_code("LeftShift") + ":0"
Insert = get_event_code("Insert") + ":1"
Insert_release = get_event_code("Insert") + ":0"

run(["ydotool", "key", Shift, Insert, Insert_release, Shift_release])