Skip to content

Waydroid

Installation

# sudo pacman -S linux-xanmod-anbox linux-xanmod-anbox-headers
sudo pacman -S linux-zen linux-zen-headers
sudo rm -rf /var/lib/waydroid /home/.waydroid ~/waydroid ~/.share/waydroid ~/.local/share/applications/*aydroid* ~/.local/share/waydroid
sudo pacman -S waydroid
sudo waydroid init -s GAPPS

# waydroid-extras: https://aur.archlinux.org/waydroid-script-git.git
# For amd use libndk
sudo waydroid-extras install libndk widevine magisk

sudo systemctl enable waydroid-container
sudo systemctl start waydroid-container

# Using cage since I use xorg.
cage waydroid show-full-ui

Google Play certification

https://docs.waydro.id/faq/google-play-certification

Shadowfight Arena 4

Setup udev waydroid to detect Joystick

waydroid prop set persist.waydroid.udev true
waydroid prop set persist.waydroid.uevent true

Emulate Joystick

git clone https://github.com/iosonofabio/virtual_gamepad
paru -S python-uinput python-pynput

# For script to work load uinput
sudo modprobe uinput
# Load module on boot
sudo echo uinput >> /etc/modules-load.d/uinput.conf

# To use script without sudo:
sudo groupadd uinput
sudo usermod -aG uinput "$USER"
sudo chmod g+rw /dev/uinput
sudo chgrp uinput /dev/uinput

For permissions to persist across reboot:

/etc/udev/rules.d/99-uinput.rules
KERNEL=="uinput", GROUP="uinput", MODE="0660"

Play

cage waydroid show-full-ui
python virtual_gamepad/virtual_gamepad.py

virtual_gamepad.py with hjkl bindings

from collections import defaultdict
import pynput
import uinput
import time

events = (
    uinput.BTN_A,
    uinput.BTN_B,
    uinput.BTN_X,
    uinput.BTN_Y,
    uinput.BTN_TL,
    uinput.BTN_TR,
    uinput.BTN_THUMBL,
    uinput.BTN_THUMBR,
    uinput.ABS_X + (0, 255, 0, 0),
    uinput.ABS_Y + (0, 255, 0, 0),
)
device = uinput.Device(
    events,
    vendor=0x045e,
    product=0x028e,
    version=0x110,
    name="Microsoft X-Box 360 pad",
)

# Center joystick
# syn=False to emit an "atomic" (128, 128) event.
device.emit(uinput.ABS_X, 128, syn=False)
device.emit(uinput.ABS_Y, 128)

keymap = {
    'right': 'l',
    'left': 'h',
    'up': 'k',
    'down': 'j',
    'kick': 'd',
    'fight': 'f',
    'shadow': 's',
    'range': 'a',
    'jumpleft': 'i',
    'jumpright': 'o',
    'rollleft': 'n',
    'rollleft2': 'b',
    'rollright': 'm',
    'range': 'a',
    'confirm': '0',
    '?': '-',
    'requests/cancel': '9',
    'zoomout': '8',
    'back': 'p',
}
keys = list(keymap.values())

def find_key(key):
    #if key == keyboard.Key.esc:
    #    return False  # stop listener
    try:
        k = key.char  # single-char keys
    except:
        k = key.name  # other keys
    if k not in keys:
        return True

    return k

    #print('Key pressed: ' + k)
    #return False  # stop listener; remove this if want more keys 


def on_press(key):
    k = find_key(key)
    if k is True:
        return True

    if k == keymap['kick']:
        device.emit(uinput.BTN_A, 1)
    elif k == keymap['fight']:
        device.emit(uinput.BTN_B, 1)
    elif k == keymap['shadow']:
        device.emit(uinput.BTN_X, 1)
    elif k == keymap['range']:
        device.emit(uinput.BTN_Y, 1)
    elif k == keymap['confirm']:
        device.emit(uinput.BTN_TL, 1)
    elif k == keymap['?']:
        device.emit(uinput.BTN_TR, 1)
    elif k == keymap['requests/cancel']:
        device.emit(uinput.BTN_THUMBL, 1)
    elif k == keymap['zoomout']:
        device.emit(uinput.BTN_THUMBR, 1)
    elif k == keymap['up']:
        device.emit(uinput.ABS_Y, 0)                    # Zero Y
    elif k == keymap['jumpleft']:
        device.emit(uinput.ABS_Y, 0)                    # Zero Y
        device.emit(uinput.ABS_X, 0)                    # Zero X
    elif k == keymap['jumpright']:
        device.emit(uinput.ABS_Y, 0)                    # Zero Y
        device.emit(uinput.ABS_X, 255)                    # Zero X
    elif k == keymap['down']:
        device.emit(uinput.ABS_Y, 255)                  # Max Y
    elif k == keymap['rollleft']:
        device.emit(uinput.ABS_Y, 255)                  # Max Y
        device.emit(uinput.ABS_X, 0)                    # Zero X
    elif k == keymap['rollleft2']:
        device.emit(uinput.ABS_Y, 255)                  # Max Y
        device.emit(uinput.ABS_X, 0)                    # Zero X
    elif k == keymap['rollright']:
        device.emit(uinput.ABS_Y, 255)                  # Max Y
        device.emit(uinput.ABS_X, 255)                  # Max X
    elif k == keymap['left']:
        device.emit(uinput.ABS_X, 0)                    # Zero X
    elif k == keymap['right']:
        device.emit(uinput.ABS_X, 255)                  # Max X

    return True


def on_release(key):
    k = find_key(key)
    if k is True:
        return True

    if k == keymap['kick']:
        device.emit(uinput.BTN_A, 0)
    elif k == keymap['fight']:
        device.emit(uinput.BTN_B, 0)
    elif k == keymap['shadow']:
        device.emit(uinput.BTN_X, 0)
    elif k == keymap['range']:
        device.emit(uinput.BTN_Y, 0)
    elif k == keymap['confirm']:
        device.emit(uinput.BTN_TL, 0)
    elif k == keymap['?']:
        device.emit(uinput.BTN_TR, 0)
    elif k == keymap['requests/cancel']:
        device.emit(uinput.BTN_THUMBL, 0)
    elif k == keymap['zoomout']:
        device.emit(uinput.BTN_THUMBR, 0)
    elif k == keymap['up']:
        device.emit(uinput.ABS_Y, 128)                # Center Y
    elif k == keymap['jumpleft']:
        device.emit(uinput.ABS_Y, 128)                    # Zero Y
        device.emit(uinput.ABS_X, 128)                    # Zero X
    elif k == keymap['jumpright']:
        device.emit(uinput.ABS_Y, 128)                    # Zero Y
        device.emit(uinput.ABS_X, 128)                    # Zero X
    elif k == keymap['rollright']:
        device.emit(uinput.ABS_Y, 128)                    # Zero Y
        device.emit(uinput.ABS_X, 128)                    # Zero X
    elif k == keymap['rollleft']:
        device.emit(uinput.ABS_Y, 128)                    # Zero Y
        device.emit(uinput.ABS_X, 128)                    # Zero X
    elif k == keymap['rollleft2']:
        device.emit(uinput.ABS_Y, 128)                    # Zero Y
        device.emit(uinput.ABS_X, 128)                    # Zero X
    elif k == keymap['down']:
        device.emit(uinput.ABS_Y, 128)                # Center Y
    elif k == keymap['left']:
        device.emit(uinput.ABS_X, 128)                # Center Y
    elif k == keymap['right']:
        device.emit(uinput.ABS_X, 128)                # Center Y

    #time.sleep(.02)    # Poll every 20ms (otherwise CPU load gets too high)

    return True


if True:
    listener = pynput.keyboard.Listener(
        on_press=on_press,
        on_release=on_release,
        )
    listener.start()  # start to listen on a separate thread
    listener.join()  # remove if main thread is polling self.keys

taunts with xdotool and xbindkeys

I don't think there's a way to get taunts working just with joystick. You need to make use of the mouse and screen. I did this using xdotool and xbindkeys.

~/.xbindkeysrc
"~/bin/taunts.sh z"
    m:0x0 + c:52
    z

"~/bin/taunts.sh x"
    m:0x0 + c:53
    x

"~/bin/taunts.sh c"
    m:0x0 + c:54
    c

"~/bin/taunts.sh v"
    m:0x0 + c:55
    v
~/bin/taunts.sh
#!/bin/bash

if [[ $1 == "z" ]];
then
  xdotool mousemove 3326 937
  xdotool mousedown 1
  xdotool mousemove 3285 921
  sleep 0.1
  xdotool mouseup 1
elif [[ $1 == "x" ]];
then
  xdotool mousemove 3326 937
  xdotool mousedown 1
  xdotool mousemove 3326 852
  sleep 0.1
  xdotool mouseup 1
elif [[ $1 == "c" ]];
then
  xdotool mousemove 3326 937
  xdotool mousedown 1
  xdotool mousemove 3435 921
  sleep 0.1
  xdotool mouseup 1
elif [[ $1 == "v" ]];
then
  xdotool mousemove 3326 937
  xdotool mousedown 1
  xdotool mousemove 3285 1001
  sleep 0.1
  xdotool mouseup 1
fi

I found the appropriate location for mouse using xdotool getmouselocation --shell.

I run xbindkeys with xbindkeys -n so that I simply ctrl-c the script to revert keybindings.


Comments