Skip to content

Commit

Permalink
Merge pull request #194 from quexten/feature/x11-autotype
Browse files Browse the repository at this point in the history
Implement x11, mac, windows autotype
  • Loading branch information
quexten authored May 3, 2024
2 parents 1133553 + e577926 commit 63ee486
Show file tree
Hide file tree
Showing 8 changed files with 171 additions and 116 deletions.
37 changes: 29 additions & 8 deletions gui/python3-requirements.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,35 @@
{
"name": "python3-tendo",
"name": "python3-requirements",
"buildsystem": "simple",
"build-commands": [
"pip3 install --verbose --exists-action=i --no-index --find-links=\"file://${PWD}\" --prefix=${FLATPAK_DEST} \"tendo==0.3.0\" --no-build-isolation"
],
"sources": [
"build-commands": [],
"modules": [
{
"type": "file",
"url": "https://files.pythonhosted.org/packages/ce/3f/761077d55732b0b1a673b15d4fdaa947a7c1eb5c9a23b7142df557019823/tendo-0.3.0-py3-none-any.whl",
"sha256": "026b70b355ea4c9da7c2123fa2d5c280c8983c1b34e329ff49260e2e78b93be7"
"name": "python3-tendo",
"buildsystem": "simple",
"build-commands": [
"pip3 install --verbose --exists-action=i --no-index --find-links=\"file://${PWD}\" --prefix=${FLATPAK_DEST} \"tendo==0.3.0\" --no-build-isolation"
],
"sources": [
{
"type": "file",
"url": "https://files.pythonhosted.org/packages/ce/3f/761077d55732b0b1a673b15d4fdaa947a7c1eb5c9a23b7142df557019823/tendo-0.3.0-py3-none-any.whl",
"sha256": "026b70b355ea4c9da7c2123fa2d5c280c8983c1b34e329ff49260e2e78b93be7"
}
]
},
{
"name": "python3-python3-xlib",
"buildsystem": "simple",
"build-commands": [
"pip3 install --verbose --exists-action=i --no-index --find-links=\"file://${PWD}\" --prefix=${FLATPAK_DEST} \"python3-xlib==0.15\" --no-build-isolation"
],
"sources": [
{
"type": "file",
"url": "https://files.pythonhosted.org/packages/ef/c6/2c5999de3bb1533521f1101e8fe56fd9c266732f4d48011c7c69b29d12ae/python3-xlib-0.15.tar.gz",
"sha256": "dc4245f3ae4aa5949c1d112ee4723901ade37a96721ba9645f2bfa56e5b383f8"
}
]
}
]
}
3 changes: 2 additions & 1 deletion gui/requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
tendo==0.3.0
tendo==0.3.0
python3-xlib==0.15
13 changes: 7 additions & 6 deletions gui/src/gui/quickaccess.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import time
from gi.repository import Gtk, Adw, GLib, Notify, Gdk
from ..services import goldwarden
from ..services.autotype import autotype
from threading import Thread
from .resource_loader import load_template
import sys
Expand Down Expand Up @@ -71,19 +72,19 @@ def key_press(self, event, keyval, keycode, state):
# totp code
if keyval == Gdk.KEY_t or keyval == Gdk.KEY_T:
if auto_type_combo:
self.autotype(totp.totp(self.filtered_logins[self.selected_index]["totp"]))
self.run_autotype(totp.totp(self.filtered_logins[self.selected_index]["totp"]))
if copy_combo:
self.set_clipboard(totp.totp(self.filtered_logins[self.selected_index]["totp"]))

if keyval == Gdk.KEY_u or keyval == Gdk.KEY_U:
if auto_type_combo:
self.autotype(self.filtered_logins[self.selected_index]["username"])
self.run_autotype(self.filtered_logins[self.selected_index]["username"])
if copy_combo:
self.set_clipboard(self.filtered_logins[self.selected_index]["username"])

if keyval == Gdk.KEY_p or keyval == Gdk.KEY_P:
if auto_type_combo:
self.autotype(self.filtered_logins[self.selected_index]["password"])
self.run_autotype(self.filtered_logins[self.selected_index]["password"])
if copy_combo:
self.set_clipboard(self.filtered_logins[self.selected_index]["password"])

Expand All @@ -100,18 +101,18 @@ def key_press(self, event, keyval, keycode, state):

if keyval == Gdk.KEY_Return:
if auto_type_combo:
self.autotype(f"{self.filtered_logins[self.selected_index]['username']}\t{self.filtered_logins[self.selected_index]['password']}")
self.run_autotype(f"{self.filtered_logins[self.selected_index]['username']}\t{self.filtered_logins[self.selected_index]['password']}")

def update(self):
self.update_list()
self.render_list()
return True

def autotype(self, text):
def run_autotype(self, text):
def perform_autotype(text):
self.window.hide()
time.sleep(0.1)
goldwarden.autotype(text)
autotype.autotype(text)
time.sleep(0.1)
os._exit(0)
thread = Thread(target=perform_autotype, args=(text,))
Expand Down
101 changes: 0 additions & 101 deletions gui/src/linux/autotype/libportal_autotype.py

This file was deleted.

16 changes: 16 additions & 0 deletions gui/src/services/autotype/autotype.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import sys
import os

is_linux = sys.platform == 'linux'
is_wayland = os.environ.get('XDG_SESSION_TYPE') == 'wayland'

def autotype(text):
if is_linux and is_wayland:
from .libportal_autotype import autotype_libportal
autotype_libportal(text)
elif is_linux:
from .x11autotype import type
type(text)
else:
from .pyautogui_autotype import autotype_pyautogui
autotype_pyautogui(text)
5 changes: 5 additions & 0 deletions gui/src/services/autotype/libportal_autotype.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from ..goldwarden import autotype

def libportal_autotype(text):
print("autotypeing with libportal")
goldwarden.autotype(text)
5 changes: 5 additions & 0 deletions gui/src/services/autotype/pyautogui_autotype.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import pyautogui

def autotype_pyautogui(text):
print("autotypeing with pyautogui")
pyautogui.write(text, interval=0.02)
107 changes: 107 additions & 0 deletions gui/src/services/autotype/x11autotype.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
# https://github.com/gemdude46/autotype/blob/master/LICENSE
# MIT License

# Copyright (c) 2017 gemdude46

# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:

# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.

# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

import os
import time

from Xlib.display import Display
from Xlib import X
from Xlib.ext.xtest import fake_input

_display = Display(os.environ['DISPLAY'])

def type(text, delay=0.03, down_for=0):
'''Fake key presses to type out text.
Args:
text (str): The text to type out.
delay (float): The time to wait between presses. (In seconds)
down_for (float): How long to keep each key down. (In seconds)
Returns:
None
'''

# Save the users existing keyboard mapping
kbm_backup = _display.get_keyboard_mapping(8,246)

try:

text = text.replace('\n', '\r') # Because X

# Splits the text into blocks containing no more than 245 unique characters
# This is because there is a limited number of valid xmodmap keycodes
while text:
chars = set()

i = 0
while i < len(text) and len(chars) < 245:
char = ord(text[i])
chars.add(char)
i += 1

block = text[:i]
text = text[i:]

_type_lt245(block, delay, down_for)

finally:

# Restore the keyboard layout to how it was originally
_display.change_keyboard_mapping(8, kbm_backup)
_display.sync()

def _type_lt245(text, delay, down_for):

xmm = ''

chars = []

keys = []

text = [(1 << 24) + ord(i) for i in text]

for char in text:
if char not in chars:
chars.append(char)

keys.append(chars.index(char) + 8)

for i in range(8, 255):
fake_input(_display, X.KeyRelease, i)

_display.change_keyboard_mapping(8, [(i, ) * 15 for i in chars])
_display.sync()

for key in keys:

fake_input(_display, X.KeyPress, key)
_display.sync()

time.sleep(down_for)

fake_input(_display, X.KeyRelease, key)
_display.sync()

time.sleep(delay)

0 comments on commit 63ee486

Please sign in to comment.