diff --git a/.github/workflows/pythonapp.yml b/.github/workflows/pythonapp.yml
index f5848116..467d6e81 100644
--- a/.github/workflows/pythonapp.yml
+++ b/.github/workflows/pythonapp.yml
@@ -17,23 +17,21 @@ jobs:
quality:
runs-on: ubuntu-20.04
steps:
- - uses: actions/checkout@v1
- - name: Set up Python 3.8
+ - uses: actions/checkout@v2
+ - name: Set up Python 3.x
uses: actions/setup-python@v1
with:
- python-version: 3.8
+ python-version: 3.x
- name: Install dependencies
run: |
- python -m pip install --upgrade pip
+ python -m pip install --upgrade pip wheel
pip install -r requirements.txt
+ pip3 install black
- name: Lint with flake8
run: |
+ black --check guiscrcpy --exclude guiscrcpy/ui
pip install flake8
- # stop the build if there are Python syntax errors or undefined names
- flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
- # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
- flake8 --per-file-ignores='setup.py:E501'
- name: Test setup.py
run: |
diff --git a/.gitignore b/.gitignore
index 74d9298a..396e23e7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -27,3 +27,4 @@ guiscrcpy.AppDir.BUILD/
guiscrcpy.AppDir/
scrcpy-squashfs-root/
squashfs-root/
+/deb_dist/
diff --git a/guiscrcpy/__main__.py b/guiscrcpy/__main__.py
index 17594876..755df510 100755
--- a/guiscrcpy/__main__.py
+++ b/guiscrcpy/__main__.py
@@ -19,4 +19,5 @@
"""
from guiscrcpy.cli import cli
+
cli()
diff --git a/guiscrcpy/cli.py b/guiscrcpy/cli.py
index c8749450..257ff339 100644
--- a/guiscrcpy/cli.py
+++ b/guiscrcpy/cli.py
@@ -5,13 +5,16 @@
import click
import colorama
+from click import Context
from qtpy.QtWidgets import QApplication
from qtpy import QtCore, QtWidgets
from .lib.utils import format_colors as fc, show_message_box
from .launcher import bootstrap
-from .lib.check import AndroidDebugBridge, ScrcpyNotFoundError, \
- AdbNotFoundError, ScrcpyServerNotFoundError
+from .lib.bridge.exceptions import ScrcpyNotFoundError
+from .lib.bridge.exceptions import AdbNotFoundError
+from .lib.bridge.exceptions import ScrcpyServerNotFoundError
+from .lib.bridge import AndroidDebugBridge
from .lib.config import InterfaceConfig, InvalidConfigurationError
from .version import VERSION
from . import __doc__ as lic
@@ -24,7 +27,7 @@
# srevin: bad snap!
_sys_argv = list()
for arg in sys.argv:
- if not arg.startswith('/snap'):
+ if not arg.startswith("/snap"):
_sys_argv.append(arg)
sys.argv = _sys_argv
@@ -33,25 +36,27 @@ def show_version(ctx, param, value): # noqa:
"""Prints the version of the utility"""
if not value or ctx.resilient_parsing:
return
- click.echo(fc('{g}guiscrcpy {rst}v{v}', v=VERSION))
- if os.getenv('APPIMAGE'):
+ click.echo(fc("{g}guiscrcpy {rst}v{v}", v=VERSION))
+ if os.getenv("APPIMAGE"):
click.echo("Running from AppImage")
- if os.getenv('APPDIR'):
- click.echo("AppDir: {}".format(os.getenv('APPDIR')))
+ if os.getenv("APPDIR"):
+ click.echo("AppDir: {}".format(os.getenv("APPDIR")))
import inspect
from PyQt5 import Qt
+
_pyqt5_version = [
- '%s = %s' % (k, v) for k, v in
- vars(Qt).items() if
- k.lower().find('version') >= 0 and not inspect.isbuiltin(v)
+ "%s = %s" % (k, v)
+ for k, v in vars(Qt).items()
+ if k.lower().find("version") >= 0 and not inspect.isbuiltin(v)
]
print()
print("== PyQt5 Version ==")
- print('\n'.join(sorted(_pyqt5_version)))
+ print("\n".join(sorted(_pyqt5_version)))
print()
if platform.system() == "Linux":
print("== CairoSVG version ==")
from cairosvg import VERSION as CAIRO_VERSION # noqa:
+
print("CairoSVG == {}".format(CAIRO_VERSION))
print()
@@ -68,32 +73,55 @@ def show_license(ctx, param, value): # noqa:
@click.group(invoke_without_command=True)
@click.pass_context
-@click.option('--version', is_flag=True,
- callback=show_version,
- expose_value=False, is_eager=True)
-@click.option('--license', '--lic', is_flag=True,
- callback=show_license,
- expose_value=False, is_eager=True)
-@click.option('-T',
- '--theme', 'theme',
- default='Breeze',
- help="Set the default theme (based on PyQt5 "
- "themes - Fusion, Breeze, Windows) "
- "(stored in configuration, override by --theme-no-cfg)")
-@click.option('-W',
- '--hide-wm-frame/--show-wm-frame',
- 'hide_wm_frame',
- default=True,
- help="Show window manager border frame.")
-@click.option('--debug-disable-scrcpy',
- 'debug__disable_scrcpy',
- default=False, is_flag=True,
- help="Do not launch scrcpy even when 'Start Scrcpy' is pressed")
-@click.option('-A', '--always-on-top/--disable-always-on-top', 'aot',
- default=True, help="Forces the panels to be always of top")
-def cli(ctx, hide_wm_frame=True, aot=True, theme='Breeze',
- debug__disable_scrcpy=False):
- """ guiscrcpy: Graphical user interface for scrcpy"""
+@click.option(
+ "--version", is_flag=True, callback=show_version, expose_value=False, is_eager=True
+)
+@click.option(
+ "--license",
+ "--lic",
+ is_flag=True,
+ callback=show_license,
+ expose_value=False,
+ is_eager=True,
+)
+@click.option(
+ "-T",
+ "--theme",
+ "theme",
+ default="Breeze",
+ help="Set the default theme (based on PyQt5 "
+ "themes - Fusion, Breeze, Windows) "
+ "(stored in configuration, override by --theme-no-cfg)",
+)
+@click.option(
+ "-W",
+ "--hide-wm-frame/--show-wm-frame",
+ "hide_wm_frame",
+ default=True,
+ help="Show window manager border frame.",
+)
+@click.option(
+ "--debug-disable-scrcpy",
+ "debug_disable_scrcpy",
+ default=False,
+ is_flag=True,
+ help="Do not launch scrcpy even when 'Start Scrcpy' is pressed",
+)
+@click.option(
+ "-A",
+ "--always-on-top/--disable-always-on-top",
+ "aot",
+ default=True,
+ help="Forces the panels to be always of top",
+)
+def cli(
+ ctx: Context,
+ hide_wm_frame: bool = True,
+ aot: bool = True,
+ theme: str = "Breeze",
+ debug_disable_scrcpy: bool = False,
+):
+ """guiscrcpy: Graphical user interface for scrcpy"""
print(fc("\n{b}guiscrcpy {v}{rst}", v=VERSION))
print(fc("by @srevinsaju"))
print(fc("{x}https://github.com/srevinsaju/guiscrcpy{rst}\n\n"))
@@ -110,33 +138,38 @@ def cli(ctx, hide_wm_frame=True, aot=True, theme='Breeze',
QApplication.setAttribute(QtCore.Qt.AA_UseHighDpiPixmaps, True)
# init core
app = QtWidgets.QApplication(sys.argv)
- cfgmgr = InterfaceConfig()
+ config_manager = InterfaceConfig()
bootstrap(
- app,
- cfgmgr,
+ app=app,
+ config_manager=config_manager,
theme=theme,
aot=aot,
hide_wm_frame=hide_wm_frame,
- debug__no_scrcpy=debug__disable_scrcpy)
+ debug_no_scrcpy=debug_disable_scrcpy,
+ )
except ScrcpyNotFoundError:
_msg_box = show_message_box(
text="Scrcpy not found",
info_text="guiscrcpy could not find scrcpy. "
- "Make sure you select scrcpy in the dialog box "
- "or add scrcpy to PATH. You can get the latest release "
- "of scrcpy from "
- "Genymobile/scrcpy 's GitHub Releases "
- "and select the
scrcpy{ext} when you run "
- "guiscrcpy next time".format(
- ext=".exe" if platform.system() == "Windows" else ""))
+ "Make sure you select scrcpy in the dialog box "
+ "or add scrcpy to PATH. You can get the latest release "
+ "of scrcpy from "
+ "Genymobile/scrcpy 's GitHub Releases "
+ "and select the scrcpy{ext} when you run "
+ "guiscrcpy next time".format(
+ ext=".exe" if platform.system() == "Windows" else ""
+ ),
+ )
_msg_box.exec_()
print("Aborting!")
sys.exit(-1)
except AdbNotFoundError:
_msg_box = show_message_box(
- text="ADB not found", info_text="guiscrcpy could not find adb. "
- "Make sure you select adb in the dialog box or add adb to PATH")
+ text="ADB not found",
+ info_text="guiscrcpy could not find adb. "
+ "Make sure you select adb in the dialog box or add adb to PATH",
+ )
_msg_box.exec_()
print("Aborting!")
sys.exit(-1)
@@ -144,12 +177,12 @@ def cli(ctx, hide_wm_frame=True, aot=True, theme='Breeze',
_msg_box = show_message_box(
text="Invalid configuration error",
info_text="The configuration file for guiscrcpy is invalid. "
- "This is possibly because a new version of guiscrcpy "
- "was installed, or the old paths to `adb` and `scrcpy` "
- "as defined in the configuration file, no longer exists "
- "in the same path. To fix this error,"
- "Run "
- "guiscrcpy config -r on your terminal."
+ "This is possibly because a new version of guiscrcpy "
+ "was installed, or the old paths to `adb` and `scrcpy` "
+ "as defined in the configuration file, no longer exists "
+ "in the same path. To fix this error,"
+ "Run "
+ "guiscrcpy config -r on your terminal.",
)
_msg_box.exec_()
print("Aborting!")
@@ -158,12 +191,12 @@ def cli(ctx, hide_wm_frame=True, aot=True, theme='Breeze',
_msg_box = show_message_box(
text="Scrcpy server not found error",
info_text="The configuration file for guiscrcpy is invalid. "
- "This is possibly because a new version of guiscrcpy "
- "was installed, or the old paths to `adb` and `scrcpy` "
- "as defined in the configuration file, no longer exists "
- "in the same path. To fix this error,"
- "Run "
- "guiscrcpy config -r on your terminal."
+ "This is possibly because a new version of guiscrcpy "
+ "was installed, or the old paths to `adb` and `scrcpy` "
+ "as defined in the configuration file, no longer exists "
+ "in the same path. To fix this error,"
+ "Run "
+ "guiscrcpy config -r on your terminal.",
)
_msg_box.exec_()
print("Aborting!")
@@ -174,10 +207,10 @@ def cli(ctx, hide_wm_frame=True, aot=True, theme='Breeze',
_msg_box = show_message_box(
text="Error: Unhandled exception",
info_text="{error_message} "
- "Please report this, if its a bug, to "
- "guiscrcpy bug tracker as it will help to improve "
- "the next release.".format(error_message=error_message)
+ "Please report this, if its a bug, to "
+ "guiscrcpy bug tracker as it will help to improve "
+ "the next release.".format(error_message=error_message),
)
_msg_box.exec_()
print("Aborting!")
@@ -185,23 +218,32 @@ def cli(ctx, hide_wm_frame=True, aot=True, theme='Breeze',
@cli.command()
-@click.option('--device-id', 'device_id',
- help="Sets the device-id for mapper to configure "
- " (optional, needed for multiple devices)")
-@click.option('-r', '--reset', 'reset', is_flag=True,
- help="Reset guiscrcpy's mapper configuration file")
+@click.option(
+ "--device-id",
+ "device_id",
+ help="Sets the device-id for mapper to configure "
+ " (optional, needed for multiple devices)",
+)
+@click.option(
+ "-r",
+ "--reset",
+ "reset",
+ is_flag=True,
+ help="Reset guiscrcpy's mapper configuration file",
+)
def mapper(device_id=None, reset=False):
"""Run the guiscrcpy mapper"""
- cfgmgr = InterfaceConfig()
- config = cfgmgr.get_config()
+ config_manager = InterfaceConfig()
+ config = config_manager.get_config()
mapper_cfg_path = os.path.join(
- cfgmgr.get_cfgpath(), 'guiscrcpy.mapper.json'
+ config_manager.get_cfgpath(), "guiscrcpy.mapper.json"
)
if reset:
# A ternary version of removing a file if it exists
# https://stackoverflow.com/q/10840533/
- mapper_configuration_file_exists = os.remove(mapper_cfg_path) if \
- os.path.exists(mapper_cfg_path) else None
+ mapper_configuration_file_exists = (
+ os.remove(mapper_cfg_path) if os.path.exists(mapper_cfg_path) else None
+ )
if mapper_configuration_file_exists:
print("guiscrcpy mapper configuration file has been removed.")
print("Removed {}".format(mapper_configuration_file_exists))
@@ -209,10 +251,10 @@ def mapper(device_id=None, reset=False):
print("guiscrcpy mapper configuration is not created yet.")
return
- if os.getenv('GUISCRCPY_ADB'):
- adb_path = os.getenv('GUISCRCPY_ADB')
+ if os.getenv("GUISCRCPY_ADB"):
+ adb_path = os.getenv("GUISCRCPY_ADB")
else:
- adb_path = config['adb']
+ adb_path = config["adb"]
adb = AndroidDebugBridge(adb_path)
adb_devices_list = adb.devices()
if len(adb_devices_list) == 0:
@@ -221,18 +263,19 @@ def mapper(device_id=None, reset=False):
elif len(adb_devices_list) == 1:
mapper_device_id = adb_devices_list[0][0]
elif not device_id:
- print("Please pass the --device-id to initialize "
- "the mapper")
+ print("Please pass the --device-id to initialize " "the mapper")
sys.exit(1)
else:
mapper_device_id = device_id
from guiscrcpy.lib.mapper.mapper import Mapper
+
# Initialize the mapper if it is called.
- adb = AndroidDebugBridge(path=cfgmgr.get_config().get('adb'))
+ adb = AndroidDebugBridge(path=config_manager.get_config().get("adb"))
mp = Mapper(mapper_device_id, adb=adb, config_path=mapper_cfg_path)
if not os.path.exists(
- os.path.join(cfgmgr.get_cfgpath(), 'guiscrcpy.mapper.json')):
+ os.path.join(config_manager.get_cfgpath(), "guiscrcpy.mapper.json")
+ ):
print("guiscrcpy.mapper.json does not exist. ")
print("Initializing Mapper Configuration for the first time use.")
mp.initialize(initialize_qt=True)
@@ -242,54 +285,55 @@ def mapper(device_id=None, reset=False):
print("Your keyboard is being listened by guiscrcpy-mapper")
print("pressing any key will trigger the position.")
print()
- print('If you would like to register new keys, pass --mapper-reset')
+ print("If you would like to register new keys, pass --mapper-reset")
print("\nInitializing\n\n")
mp.listen_keypress()
print("Done!")
-@cli.command('adb')
-@click.argument('args', nargs=-1)
+@cli.command("adb")
+@click.argument("args", nargs=-1)
def adb_cli(args):
"""Create an interface with the Android Debugging bridge"""
- cfgmgr = InterfaceConfig()
- config = cfgmgr.get_config()
- if os.getenv('GUISCRCPY_ADB'):
- adb_path = os.getenv('GUISCRCPY_ADB')
+ config_manager = InterfaceConfig()
+ config = config_manager.get_config()
+ if os.getenv("GUISCRCPY_ADB"):
+ adb_path = os.getenv("GUISCRCPY_ADB")
else:
- adb_path = config['adb']
+ adb_path = config["adb"]
print("Interfacing guiscrcpy-adb")
- os.system('{} {}'.format(adb_path, ' '.join(args)))
+ os.system("{} {}".format(adb_path, " ".join(args)))
pass
@cli.command()
-@click.argument('args', nargs=-1)
+@click.argument("args", nargs=-1)
def scrcpy(args):
"""Create an interface with scrcpy"""
- cfgmgr = InterfaceConfig()
- config = cfgmgr.get_config()
- if os.getenv('GUISCRCPY_SCRCPY'):
- scrcpy_path = os.getenv('GUISCRCPY_SCRCPY')
+ config_manager = InterfaceConfig()
+ config = config_manager.get_config()
+ if os.getenv("GUISCRCPY_SCRCPY"):
+ scrcpy_path = os.getenv("GUISCRCPY_SCRCPY")
else:
- scrcpy_path = config['scrcpy']
+ scrcpy_path = config["scrcpy"]
print("Interfacing guiscrcpy-scrcpy")
- os.system('{} {}'.format(scrcpy_path, ' '.join(args)))
+ os.system("{} {}".format(scrcpy_path, " ".join(args)))
pass
-@cli.command('config')
-@click.option('-r', '--reset', 'reset', is_flag=True,
- help="Reset the configuration files")
+@cli.command("config")
+@click.option(
+ "-r", "--reset", "reset", is_flag=True, help="Reset the configuration files"
+)
def _config(reset=False):
"""View / Edit the configuration file"""
- cfgmgr = InterfaceConfig(load=False)
+ config_manager = InterfaceConfig(load=False)
if reset:
- cfgmgr.reset_config()
+ config_manager.reset_config()
click.echo("Configuration file resetted successfully.")
sys.exit(0)
- cfgmgr.load_config()
- print(cfgmgr)
+ config_manager.load_config()
+ print(config_manager)
@cli.command()
diff --git a/guiscrcpy/constants.py b/guiscrcpy/constants.py
index 59f1cc31..e8d6c779 100644
--- a/guiscrcpy/constants.py
+++ b/guiscrcpy/constants.py
@@ -1,8 +1,8 @@
FONTS = [
- 'NotoSans-BoldItalic.ttf',
- 'NotoSans-Italic.ttf',
- 'TitilliumWeb-Bold.ttf',
- 'NotoSans-Bold.ttf',
- 'NotoSans-Regular.ttf',
- 'TitilliumWeb-Regular.ttf',
+ "NotoSans-BoldItalic.ttf",
+ "NotoSans-Italic.ttf",
+ "TitilliumWeb-Bold.ttf",
+ "NotoSans-Bold.ttf",
+ "NotoSans-Regular.ttf",
+ "TitilliumWeb-Regular.ttf",
]
diff --git a/guiscrcpy/install/finder.py b/guiscrcpy/install/finder.py
index 07c3461b..4a429bce 100644
--- a/guiscrcpy/install/finder.py
+++ b/guiscrcpy/install/finder.py
@@ -4,14 +4,13 @@
def open_exe_name_dialog(parent, appname):
options = QFileDialog.Options()
options |= QFileDialog.DontUseNativeDialog
- file_name, _ = \
- QFileDialog.getOpenFileName(
- parent,
- "{} could not be found. Please locate it manually".format(appname),
- "",
- "Valid {} executable (*);;".format(appname),
- options=options
- )
+ file_name, _ = QFileDialog.getOpenFileName(
+ parent,
+ "{} could not be found. Please locate it manually".format(appname),
+ "",
+ "Valid {} executable (*);;".format(appname),
+ options=options,
+ )
if file_name:
print(file_name)
return file_name
diff --git a/guiscrcpy/launcher.py b/guiscrcpy/launcher.py
index fd63fcd3..37062741 100644
--- a/guiscrcpy/launcher.py
+++ b/guiscrcpy/launcher.py
@@ -45,6 +45,10 @@
from qtpy.QtGui import QPixmap, QIcon, QFont, QFontDatabase
from qtpy.QtWidgets import QMainWindow, QListWidgetItem, QMenu
from qtpy.QtWidgets import QMessageBox
+
+from .lib.bridge.audio.sndcpy import SndcpyBridge
+from .lib.bridge.audio.usbaudio import USBAudioBridge
+from .lib.config import InterfaceConfig
from .lib.utils import format_colors as fc
from .constants import FONTS
from .install.finder import open_exe_name_dialog
@@ -60,8 +64,9 @@
from .ux.swipe import SwipeUX
from .ux.toolkit import InterfaceToolkit
from .version import VERSION
-from .lib.check import AndroidDebugBridge, ScrcpyBridge, \
- ScrcpyServerNotFoundError
+from .lib.bridge import AndroidDebugBridge, ScrcpyBridge
+from .lib.bridge.exceptions import ScrcpyServerNotFoundError
+
environment = platform.System()
@@ -70,6 +75,7 @@
if environment.system() == "Linux":
try:
from cairosvg import svg2png # noqa:
+
has_cairo = True
except Exception as e:
print("Failed to load cairo:", e)
@@ -84,19 +90,26 @@ class InterfaceGuiscrcpy(QMainWindow, Ui_MainWindow):
"""
# noinspection PyArgumentList
- def __init__(self, cfgmgr, adb, scrcpy, force_window_frame=False,
- panels_not_always_on_top=False, debug__no_scrcpy=False):
+ def __init__(
+ self,
+ config_manager: InterfaceConfig,
+ adb: AndroidDebugBridge,
+ scrcpy: ScrcpyBridge,
+ force_window_frame: bool = False,
+ panels_not_always_on_top: bool = False,
+ debug_no_scrcpy: bool = False,
+ ):
QMainWindow.__init__(self)
Ui_MainWindow.__init__(self)
self.setupUi(self)
self._adb = adb
self._scrcpy = scrcpy
- self.cfgmgr = cfgmgr
- config = self.cfgmgr.get_config()
+ self.config_manager = config_manager
+ config = self.config_manager.get_config()
self._config = config
self.panels_not_always_on_top = panels_not_always_on_top
self.force_window_frame = force_window_frame
- self.debug__no_scrcpy = debug__no_scrcpy
+ self.debug__no_scrcpy = debug_no_scrcpy
self.cmx = None
self.sm = None
self.mp = None
@@ -106,48 +119,50 @@ def __init__(self, cfgmgr, adb, scrcpy, force_window_frame=False,
self.side_instance = None
self.child_windows = list()
self.options = ""
- log("Options received by class are : {} {} {} {} {} ".format(
- config['bitrate'],
- config['dimension'],
- config['swtouches'],
- config['dispRO'],
- config['fullscreen'],
- ))
+ log(
+ "Options received by class are : {} {} {} {} {} ".format(
+ config["bitrate"],
+ config["dimension"],
+ config["swtouches"],
+ config["dispRO"],
+ config["fullscreen"],
+ )
+ )
# ====================================================================
# Rotation; read config, update UI
self.device_rotation.setCurrentIndex(config.get("rotation", 0))
- self.dial.setValue(int(config['bitrate']))
- if config['swtouches']:
+ self.dial.setValue(int(config["bitrate"]))
+ if config["swtouches"]:
self.showTouches.setChecked(True)
else:
self.showTouches.setChecked(False)
- if config['dispRO']:
+ if config["dispRO"]:
self.displayForceOn.setChecked(True)
else:
self.displayForceOn.setChecked(False)
# panels
- if config['panels'].get('swipe'):
+ if config["panels"].get("swipe"):
self.check_swipe_panel.setChecked(True)
else:
self.check_swipe_panel.setChecked(False)
- if config['panels'].get('tookit'):
+ if config["panels"].get("tookit"):
self.check_side_panel.setChecked(True)
else:
self.check_side_panel.setChecked(False)
- if config['panels'].get('bottom'):
+ if config["panels"].get("bottom"):
self.check_bottom_panel.setChecked(True)
else:
self.check_bottom_panel.setChecked(False)
# dimension
- if config['dimension'] is not None:
+ if config["dimension"] is not None:
self.dimensionDefaultCheckbox.setChecked(False)
try:
- self.dimensionSlider.setValue(config['dimension'])
+ self.dimensionSlider.setValue(config["dimension"])
except TypeError:
self.dimensionDefaultCheckbox.setChecked(True)
- if config['fullscreen']:
+ if config["fullscreen"]:
self.fullscreen.setChecked(True)
else:
self.fullscreen.setChecked(False)
@@ -159,8 +174,7 @@ def __init__(self, cfgmgr, adb, scrcpy, force_window_frame=False,
self.display_public_message("SCRCPY SERVER NOT RUNNING")
# CONNECT DIMENSION CHECK BOX TO STATE CHANGE
- self.dimensionDefaultCheckbox.stateChanged.connect(
- self.__dimension_change_cb)
+ self.dimensionDefaultCheckbox.stateChanged.connect(self.__dimension_change_cb)
self.build_label.setText("Build {} by srevinsaju".format(VERSION))
# DIAL CTRL GRP
@@ -171,15 +185,15 @@ def __init__(self, cfgmgr, adb, scrcpy, force_window_frame=False,
# MAIN EXECUTE ACTION
self.executeaction.clicked.connect(self.start_act)
try:
- if config['extra']:
- self.flaglineedit.setText(config['extra'])
+ if config["extra"]:
+ self.flaglineedit.setText(config["extra"])
except Exception as err:
log(f"Exception: flaglineedit.text(config[extra]) {err}")
self.quit.clicked.connect(self.quit_window)
self.dimensionText.setText("DEFAULT")
- config['bitrate'] = int(self.dial.value())
- self.bitrateText.setText(" " + str(config['bitrate']) + "KB/s")
+ config["bitrate"] = int(self.dial.value())
+ self.bitrateText.setText(" " + str(config["bitrate"]) + "KB/s")
self.pushButton.setText("RESET")
self.pushButton.clicked.connect(self.reset)
self.abtgit.clicked.connect(self.launch_web_github)
@@ -189,26 +203,23 @@ def __init__(self, cfgmgr, adb, scrcpy, force_window_frame=False,
self.settings_button.clicked.connect(self.settings_mgr)
# self.devices_view.itemChanged.connect(self.update_rotation_combo_cb)
self.devices_view.itemClicked.connect(self.update_rotation_combo_cb)
- self.refreshdevices.clicked.connect(
- self.scan_devices_update_list_view
- )
- self.restart_adb_server.clicked.connect(
- self.restart_adb_server_guiscrcpy)
+ self.refreshdevices.clicked.connect(self.scan_devices_update_list_view)
+ self.restart_adb_server.clicked.connect(self.restart_adb_server_guiscrcpy)
self.devices_view.itemClicked.connect(self.more_options_device_view)
self.devices_view.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
self.scan_config_devices_update_list_view()
self.refresh_devices()
@property
- def adb(self):
+ def adb(self) -> AndroidDebugBridge:
return self._adb
@property
- def scrcpy(self):
+ def scrcpy(self) -> ScrcpyBridge:
return self._scrcpy
@property
- def config(self):
+ def config(self) -> dict:
return self._config
def restart_adb_server_guiscrcpy(self):
@@ -229,38 +240,46 @@ def update_rotation_combo_cb(self):
"""
if self.devices_view.currentItem():
_, device_id = self.current_device_identifier()
- _rotation = self.config.get('device').get(device_id, dict()).get(
- 'rotation', self.device_rotation.currentIndex())
+ _rotation = (
+ self.config.get("device")
+ .get(device_id, dict())
+ .get("rotation", self.device_rotation.currentIndex())
+ )
else:
- _rotation = self.config.get("rotation",
- self.device_rotation.currentIndex())
+ _rotation = self.config.get("rotation", self.device_rotation.currentIndex())
self.device_rotation.setCurrentIndex(_rotation)
def settings_mgr(self):
from guiscrcpy.ux.settings import InterfaceSettings
+
self.sm = InterfaceSettings(self)
self.sm.init()
self.sm.show()
def network_mgr(self):
from guiscrcpy.ux.network import InterfaceNetwork
+
self.nm = InterfaceNetwork(self.adb)
self.nm.init()
self.nm.show()
def bootstrap_mapper(self):
- mapper_config_path = os.path.join(self.cfgmgr.get_cfgpath(),
- "guiscrcpy.mapper.json")
+ mapper_config_path = os.path.join(
+ self.config_manager.get_cfgpath(), "guiscrcpy.mapper.json"
+ )
if os.path.exists(mapper_config_path):
from guiscrcpy.lib.mapper.mapper import MapperAsync
+
_, identifier = self.current_device_identifier()
- self.mp = MapperAsync(self, device_id=identifier,
- config_path=mapper_config_path,
- adb=self.adb, initialize=False)
- self.mp.start()
- self.private_message_box_adb.setText(
- "guiscrcpy-mapper has started"
+ self.mp = MapperAsync(
+ self,
+ device_id=identifier,
+ config_path=mapper_config_path,
+ adb=self.adb,
+ initialize=False,
)
+ self.mp.start()
+ self.private_message_box_adb.setText("guiscrcpy-mapper has started")
else:
message_box = QMessageBox()
message_box.setText(
@@ -275,15 +294,16 @@ def bootstrap_mapper(self):
user_message_box_response = message_box.exec()
values_devices_list = self.scan_devices_update_list_view()
self.check_devices_status_and_select_first_if_only_one(
- values_devices_list=values_devices_list)
+ values_devices_list=values_devices_list
+ )
# TODO: allow enabling mapper from inside
if user_message_box_response == QMessageBox.Yes:
- self.private_message_box_adb.setText(
- "Initializing mapper...")
- print("Make sure your phone is connected and display is "
- "switched on")
- print("Reset mapper if you missed any "
- "steps by 'guiscrcpy --mapper-reset'")
+ self.private_message_box_adb.setText("Initializing mapper...")
+ print("Make sure your phone is connected and display is " "switched on")
+ print(
+ "Reset mapper if you missed any "
+ "steps by 'guiscrcpy --mapper-reset'"
+ )
print()
print(
"If at first you don't succeed... "
@@ -293,25 +313,26 @@ def bootstrap_mapper(self):
_, identifier = self.current_device_identifier()
executable = get_self()
from .lib.utils import shellify as sx
+
subprocess.Popen(
- sx('{} mapper'.format(executable)),
+ sx("{} mapper".format(executable)),
stdout=sys.stdout,
stdin=sys.stdin,
stderr=sys.stderr,
- cwd=os.getcwd())
+ cwd=os.getcwd(),
+ )
print("Mapper started")
- self.private_message_box_adb.setText(
- "Mapper initialized")
+ self.private_message_box_adb.setText("Mapper initialized")
- @staticmethod
- def launch_usb_audio():
- for path in environment.paths():
- if os.path.exists(os.path.join(path, 'usbaudio')):
- path_to_usbaudio = os.path.join(path, 'usbaudio')
- break
+ def launch_usb_audio(self):
+ android_version = self.adb.get_target_android_version()
+ if android_version == 10:
+ audio_bridge = SndcpyBridge()
else:
- return
- Popen(path_to_usbaudio, stdout=PIPE, stderr=PIPE)
+ audio_bridge = USBAudioBridge()
+
+ # FIXME: provide the right device id
+ audio_bridge.run(device_id=None)
@staticmethod
def launch_web_github():
@@ -338,7 +359,7 @@ def reset(self):
Remove configuration files; Reset the mapper and guiscrcpy.json
:return:
"""
- self.cfgmgr.reset_config()
+ self.config_manager.reset_config()
log("CONFIGURATION FILE REMOVED SUCCESSFULLY")
log("RESTART")
message_box = QMessageBox().window()
@@ -370,16 +391,16 @@ def forget_paired_device(self):
"""
try:
_, identifier = self.current_device_identifier()
- popped_device = self.config['device'].pop(identifier)
+ popped_device = self.config["device"].pop(identifier)
self.refresh_devices()
- self.cfgmgr.update_config(self.config)
- self.cfgmgr.write_file()
+ self.config_manager.update_config(self.config)
+ self.config_manager.write_file()
return popped_device
except KeyError:
return False
def more_options_device_view(self, button):
- if 'Disconnect' in button.text():
+ if "Disconnect" in button.text():
menu = QMenu("Menu", self)
menu.addAction("Pair / Ping", self.ping_paired_device)
menu.addAction("Attempt TCPIP on device", self.tcpip_paired_device)
@@ -390,56 +411,56 @@ def more_options_device_view(self, button):
menu.addAction("Attempt reconnection", self.ping_paired_device)
menu.addAction("Refresh", self.refresh_devices)
_, identifier = self.current_device_identifier()
- if platform.System.system() == "Linux" and identifier.count('.') >= 3:
+ if platform.System.system() == "Linux" and identifier.count(".") >= 3:
menu.addAction(
"Add Desktop Shortcut to this device",
- self.create_desktop_shortcut_linux_os
+ self.create_desktop_shortcut_linux_os,
)
menu.exec_(
self.devices_view.mapToGlobal(
QPoint(
self.devices_view.visualItemRect(button).x() + 22,
- self.devices_view.visualItemRect(button).y() + 22
+ self.devices_view.visualItemRect(button).y() + 22,
)
)
)
- def create_desktop_shortcut_linux_os(self):
+ def create_desktop_shortcut_linux_os(self) -> bool:
"""
Creates a desktop shortcut for Linux OS
- :return:
+ :return: bool
"""
# just a check before anything further happens because of an
# unrelated OS
if environment.system() != "Linux":
- log("Tried to run create_desktop_shortcut_linux_os on an "
- "unsupported OS.")
+ log(
+ "Tried to run create_desktop_shortcut_linux_os on an " "unsupported OS."
+ )
return False
# get device specific configuration
model, identifier = self.current_device_identifier()
- picture_file_path = self.cfgmgr.get_cfgpath()
- __sha_shift = self.config.get('sha_shift', 5)
- sha = hashlib.sha256(
- str(identifier).encode()
- ).hexdigest()[__sha_shift:__sha_shift + 6]
+ picture_file_path = self.config_manager.get_cfgpath()
+ __sha_shift = self.config.get("sha_shift", 5)
+ sha = hashlib.sha256(str(identifier).encode()).hexdigest()[
+ __sha_shift : __sha_shift + 6
+ ]
log(f"Creating desktop shortcut sha: {sha}")
- path_to_image = os.path.join(picture_file_path, identifier + '.png')
+ path_to_image = os.path.join(picture_file_path, identifier + ".png")
if has_cairo:
svg2png(
bytestring=desktop_device_shortcut_svg().format(f"#{sha}"),
- write_to=path_to_image
+ write_to=path_to_image,
)
else:
- print("Trying to use Plain SVG as renderer"
- " instead of cairo")
- with open(path_to_image, 'w') as fp:
+ print("Trying to use Plain SVG as renderer" " instead of cairo")
+ with open(path_to_image, "w") as fp:
svg_str = desktop_device_shortcut_svg().format(f"#{sha}")
fp.write(svg_str)
# go through all args; break when we find guiscrcpy
for args_i in range(len(sys.argv)):
- if 'guiscrcpy' in sys.argv[args_i]:
+ if "guiscrcpy" in sys.argv[args_i]:
aend = args_i + 1
break
else:
@@ -451,75 +472,73 @@ def create_desktop_shortcut_linux_os(self):
# experimental support for AppImages / snaps
# I am not sure; if it would work indeed
for i in sys_args_desktop:
- if i.endswith('.py'):
+ if i.endswith(".py"):
needs_python = True
break
else:
needs_python = False
if needs_python:
- sys_args_desktop = ['python3'] + sys_args_desktop
+ sys_args_desktop = ["python3"] + sys_args_desktop
# convert the list into a string
- sys_args_desktop = ' '.join(sys_args_desktop)
- auto_connect_run_command = \
- "{executable} --connect={ip} " \
+ sys_args_desktop = " ".join(sys_args_desktop)
+ auto_connect_run_command = (
+ "{executable} --connect={ip} "
"--start --start-scrcpy-device-id={ip}".format(
- executable=sys_args_desktop,
- ip=identifier
+ executable=sys_args_desktop, ip=identifier
)
+ )
# create the desktop file using linux's desktop file gen method
path_to_desktop_file = platform.System().create_desktop(
desktop_file=GUISCRCPY_DEVICE.format(
identifier=model,
command=auto_connect_run_command,
- icon_path=path_to_image
+ icon_path=path_to_image,
),
- desktop_file_name=f'{model}.guiscrcpy.desktop'
+ desktop_file_name=f"{model}.guiscrcpy.desktop",
)
# announce it to developers / users
log(f"Path to desktop file : {path_to_desktop_file}")
print("Desktop file generated successfully")
self.display_public_message("Desktop file has been created")
+ return True
def is_connection_success_handler(self, output: Popen, ip=None):
out, err = output.communicate()
- if 'failed' in out.decode() or 'failed' in err.decode():
+ if "failed" in out.decode() or "failed" in err.decode():
self.display_public_message(
"Failed to connect to {}. See the logs for more "
"information".format(ip)
)
print("adb:", out.decode(), err.decode())
else:
- self.display_public_message(
- "Connection command completed successfully"
- )
+ self.display_public_message("Connection command completed successfully")
def ping_paired_device(self, device_id=None):
# update the configuration file first
if not device_id:
_, identifier = self.current_device_identifier()
- if identifier.count('.') == 3:
+ if identifier.count(".") == 3:
wifi_device = True
else:
wifi_device = False
try:
- self.config['device'][identifier]['wifi'] = wifi_device
+ self.config["device"][identifier]["wifi"] = wifi_device
except KeyError:
- log(f"Failed writing the configuration "
- f"'wifi' key to {identifier}")
+ log(f"Failed writing the configuration " f"'wifi' key to {identifier}")
if wifi_device:
ip = self.current_device_identifier()[1]
- output = self.adb.command('connect {}'.format(ip))
+ output = self.adb.command("connect {}".format(ip))
self.is_connection_success_handler(output, ip=ip)
else:
- self.adb.command('reconnect offline')
+ self.adb.command("reconnect offline")
# As we have attempted to connect; refresh the panel
self.refresh_devices()
else:
- output = self.adb.command('connect {}'.format(device_id))
+ output = self.adb.command("connect {}".format(device_id))
self.is_connection_success_handler(output, ip=device_id)
def tcpip_paired_device(self):
@@ -530,15 +549,12 @@ def tcpip_paired_device(self):
__exit_code = self.adb.tcpip(identifier=identifier)
if __exit_code != 0:
self.display_public_message(
- "TCP/IP failed on device. "
- "Please reconnect USB and try again"
+ "TCP/IP failed on device. " "Please reconnect USB and try again"
)
else:
- self.display_public_message(
- "TCP/IP completed successfully."
- )
+ self.display_public_message("TCP/IP completed successfully.")
time.sleep(0.1) # wait for everything to get settled
- if identifier.count('.') >= 3:
+ if identifier.count(".") >= 3:
self.ping_paired_device(device_id=identifier)
else:
self.ping_paired_device()
@@ -546,14 +562,16 @@ def tcpip_paired_device(self):
def current_device_identifier(self, need_status=False):
if self.devices_view.currentItem():
if need_status:
- return \
- self.devices_view.currentItem().text().split()[0], \
- self.devices_view.currentItem().text().split()[1], \
- self.devices_view.currentItem().text().split()[2]
+ return (
+ self.devices_view.currentItem().text().split()[0],
+ self.devices_view.currentItem().text().split()[1],
+ self.devices_view.currentItem().text().split()[2],
+ )
else:
- return \
- self.devices_view.currentItem().text().split()[0], \
- self.devices_view.currentItem().text().split()[1]
+ return (
+ self.devices_view.currentItem().text().split()[0],
+ self.devices_view.currentItem().text().split()[1],
+ )
else:
raise ValueError("No item is selected in QListView")
@@ -563,31 +581,31 @@ def scan_config_devices_update_list_view(self):
:return:
"""
self.devices_view.clear()
- paired_devices = self.config['device']
+ paired_devices = self.config["device"]
for i in paired_devices:
- if paired_devices[i].get('wifi'):
- icon = ':/icons/icons/portrait_mobile_disconnect.svg'
+ if paired_devices[i].get("wifi"):
+ icon = ":/icons/icons/portrait_mobile_disconnect.svg"
devices_view_list_item = QListWidgetItem(
QIcon(icon),
"{device}\n{mode}\n{status}".format(
- device=paired_devices[i].get('model'),
+ device=paired_devices[i].get("model"),
mode=i,
- status='Disconnected'
- )
+ status="Disconnected",
+ ),
)
- __sha_shift = self.config.get('sha_shift', 5)
- __sha = hashlib.sha256(
- str(i).encode()).hexdigest()[__sha_shift:__sha_shift + 6]
+ __sha_shift = self.config.get("sha_shift", 5)
+ __sha = hashlib.sha256(str(i).encode()).hexdigest()[
+ __sha_shift : __sha_shift + 6
+ ]
devices_view_list_item.setToolTip(
"Device: {d} \n"
"Status: {s}".format(
d=i,
- s="Disconnected. Right click 'ping' to attempt "
- "reconnect",
- color=__sha
+ s="Disconnected. Right click 'ping' to attempt " "reconnect",
+ color=__sha,
)
)
- devices_view_list_item.setFont(QFont('Noto Sans', 8))
+ devices_view_list_item.setFont(QFont("Noto Sans", 8))
self.devices_view.addItem(devices_view_list_item)
return paired_devices
@@ -605,45 +623,44 @@ def scan_devices_update_list_view(self):
__devices = self.adb.devices_detailed()
log(__devices)
for i in __devices:
- device_is_wifi = \
- i['identifier'].count('.') >= 3 and (':' in i['identifier'])
+ device_is_wifi = i["identifier"].count(".") >= 3 and (
+ ":" in i["identifier"]
+ )
- if i['identifier'] not in self.config['device'].keys():
+ if i["identifier"] not in self.config["device"].keys():
device_paired_and_exists = False
- self.config['device'][i['identifier']] = {
- 'rotation': 0
- }
+ self.config["device"][i["identifier"]] = {"rotation": 0}
else:
device_paired_and_exists = True
if device_is_wifi:
- _icon_suffix = '_wifi'
+ _icon_suffix = "_wifi"
else:
- _icon_suffix = '_usb'
+ _icon_suffix = "_usb"
- icon = ':/icons/icons/portrait_mobile_white{}.svg'.format(
- _icon_suffix
- )
+ icon = ":/icons/icons/portrait_mobile_white{}.svg".format(_icon_suffix)
- if i['status'] == 'offline':
- icon = ':/icons/icons/portrait_mobile_error.svg'
- elif i['status'] == 'unauthorized':
- icon = ':/icons/icons/portrait_mobile_warning.svg'
+ if i["status"] == "offline":
+ icon = ":/icons/icons/portrait_mobile_error.svg"
+ elif i["status"] == "unauthorized":
+ icon = ":/icons/icons/portrait_mobile_warning.svg"
- if i['status'] == 'no_permission':
+ if i["status"] == "no_permission":
log("pairfilter: 5")
# https://stackoverflow.com/questions/
# 53887322/adb-devices-no-permissions-user-in-
# plugdev-group-are-your-udev-rules-wrong
- udev_error = "Error connecting to device. Your udev rules are"\
- " incorrect. See https://stackoverflow.com/questions"\
- "/53887322/adb-devices-no-permissions-user-in-plugdev-"\
+ udev_error = (
+ "Error connecting to device. Your udev rules are"
+ " incorrect. See https://stackoverflow.com/questions"
+ "/53887322/adb-devices-no-permissions-user-in-plugdev-"
"group-are-your-udev-rules-wrong"
+ )
self.display_public_message(udev_error)
print(udev_error)
return []
# Check if device is unauthorized
- elif i['status'] == "unauthorized":
+ elif i["status"] == "unauthorized":
log("unauthorized device detected: Click Allow on your device")
log("pairfilter: 4")
# The device is connected; and might/might't paired in the past
@@ -660,8 +677,7 @@ def scan_devices_update_list_view(self):
# Remove other devices with the same id and offline and
# unauthorized
self.remove_device_device_view(
- i['identifier'],
- statuses=['offline', 'unauthorized']
+ i["identifier"], statuses=["offline", "unauthorized"]
)
# Unauthorized device cannot be considered as a paired device
devices_view_list_item = QListWidgetItem()
@@ -673,7 +689,7 @@ def scan_devices_update_list_view(self):
devices_view_list_item = QListWidgetItem()
else:
for paired_device in paired_devices:
- if paired_device.text().split()[0] == i['model']:
+ if paired_device.text().split()[0] == i["model"]:
log("pairfilter: 1")
paired = True
devices_view_list_item = paired_device
@@ -685,16 +701,13 @@ def scan_devices_update_list_view(self):
# identifier and remove them; based on this same
# assumption
self.remove_device_device_view(
- i['identifier'],
- statuses=['offline', 'unauthorized']
+ i["identifier"], statuses=["offline", "unauthorized"]
)
break
- elif paired_device.text().split()[1] ==\
- i['identifier']:
+ elif paired_device.text().split()[1] == i["identifier"]:
log("pairfilter: 2")
self.remove_device_device_view(
- i['identifier'],
- statuses=['offline', 'unauthorized']
+ i["identifier"], statuses=["offline", "unauthorized"]
)
devices_view_list_item = QListWidgetItem()
paired = False
@@ -708,15 +721,13 @@ def scan_devices_update_list_view(self):
devices_view_list_item.setText(
"{device}\n{mode}\n{status}".format(
- device=i['model'],
- mode=i['identifier'],
- status=i['status']
+ device=i["model"], mode=i["identifier"], status=i["status"]
)
)
- __sha_shift = self.config.get('sha_shift', 5)
- __sha = hashlib.sha256(
- str(i['identifier']).encode()
- ).hexdigest()[__sha_shift:__sha_shift + 6]
+ __sha_shift = self.config.get("sha_shift", 5)
+ __sha = hashlib.sha256(str(i["identifier"]).encode()).hexdigest()[
+ __sha_shift : __sha_shift + 6
+ ]
devices_view_list_item.setToolTip(
"Device: "
""
@@ -727,18 +738,18 @@ def scan_devices_update_list_view(self):
"Status: {s}\n "
"Transport ID: {t}\n "
"Paired: {p}".format(
- d=i['identifier'],
- m=i['model'],
- a=i['product'],
- s=i['status'],
- t=i['transport_id'],
+ d=i["identifier"],
+ m=i["model"],
+ a=i["product"],
+ s=i["status"],
+ t=i["transport_id"],
p=paired,
color=__sha,
- inv_color=str(hex(0xFFFFFF - int(__sha, 16))[2:])
+ inv_color=str(hex(0xFFFFFF - int(__sha, 16))[2:]),
)
)
- devices_view_list_item.setFont(QFont('Noto Sans', 8))
+ devices_view_list_item.setFont(QFont("Noto Sans", 8))
log(f"Pairing status: {device_paired_and_exists}")
if device_paired_and_exists and device_is_wifi:
# we need to only neglect wifi devices
@@ -752,7 +763,7 @@ def scan_devices_update_list_view(self):
self.devices_view.addItem(devices_view_list_item)
return __devices
- def remove_device_device_view(self, identifier: str = '', statuses=()):
+ def remove_device_device_view(self, identifier: str = "", statuses=()):
"""
Removes all QListWidgetItems from the device_view for all matching
identifier
@@ -763,36 +774,33 @@ def remove_device_device_view(self, identifier: str = '', statuses=()):
for index in range(self.devices_view.count() - 1, -1, -1):
for status in statuses:
if self.devices_view.item(index):
- if str(identifier) in self.devices_view.item(index).text()\
- and \
- str(status) in \
- self.devices_view.item(index).text():
+ if (
+ str(identifier) in self.devices_view.item(index).text()
+ and str(status) in self.devices_view.item(index).text()
+ ):
self.devices_view.takeItem(index)
return
def __dimension_change_cb(self):
if self.dimensionDefaultCheckbox.isChecked():
self.dimensionSlider.setEnabled(False)
- self.config['dimension'] = None
+ self.config["dimension"] = None
self.dimensionText.setInputMask("")
self.dimensionText.setText("DEFAULT")
else:
self.dimensionSlider.setEnabled(True)
- self.config['dimension'] = int(self.dimensionSlider.value())
- self.dimensionText.setText(
- " " + str(self.config['dimension']) + "px")
- self.dimensionSlider.sliderMoved.connect(
- self.__slider_change_cb)
- self.dimensionSlider.sliderReleased.connect(
- self.__slider_change_cb)
+ self.config["dimension"] = int(self.dimensionSlider.value())
+ self.dimensionText.setText(" " + str(self.config["dimension"]) + "px")
+ self.dimensionSlider.sliderMoved.connect(self.__slider_change_cb)
+ self.dimensionSlider.sliderReleased.connect(self.__slider_change_cb)
def __slider_change_cb(self):
- self.config['dimension'] = int(self.dimensionSlider.value())
- self.dimensionText.setText(str(self.config['dimension']) + "px")
+ self.config["dimension"] = int(self.dimensionSlider.value())
+ self.dimensionText.setText(str(self.config["dimension"]) + "px")
def __dial_change_cb(self):
- self.config['bitrate'] = int(self.dial.value())
- self.bitrateText.setText(str(self.config['bitrate']) + "KB/s")
+ self.config["bitrate"] = int(self.dial.value())
+ self.bitrateText.setText(str(self.config["bitrate"]) + "KB/s")
def progress(self, val):
self.progressBar.setValue(val)
@@ -803,7 +811,7 @@ def progress(self, val):
@staticmethod
def is_device_unusable(status):
- if any(('unauth' in status, 'offline' in status)):
+ if any(("unauth" in status, "offline" in status)):
return True
else:
return False
@@ -814,12 +822,13 @@ def show_device_status_failure(self, status):
)
def __reset_message_box_stylesheet(self):
- stylesheet = \
- "background-color: qlineargradient(" \
- "spread:pad, x1:0, y1:0, x2:1, y2:1, " \
- "stop:0 rgba(0, 255, 255, 255), " \
- "stop:1 rgba(0, 255, 152, 255)); " \
+ stylesheet = (
+ "background-color: qlineargradient("
+ "spread:pad, x1:0, y1:0, x2:1, y2:1, "
+ "stop:0 rgba(0, 255, 255, 255), "
+ "stop:1 rgba(0, 255, 152, 255)); "
"border-radius: 10px;"
+ )
self.private_message_box_adb.setStyleSheet(stylesheet)
def __select_first_device(self):
@@ -835,8 +844,7 @@ def display_public_message(self, message):
"""
self.private_message_box_adb.setText(message)
- def check_devices_status_and_select_first_if_only_one(
- self, values_devices_list):
+ def check_devices_status_and_select_first_if_only_one(self, values_devices_list):
"""
Checks the devices in the Grid View, and then checks if any device
is available or offline accordingly display the error message. If
@@ -853,8 +861,7 @@ def check_devices_status_and_select_first_if_only_one(
# Could not detect any device
self.display_public_message("Could not find any devices")
return 0
- elif self.devices_view.currentIndex() is None and \
- len(values_devices_list) != 1:
+ elif self.devices_view.currentIndex() is None and len(values_devices_list) != 1:
# No device is selected and more than one device found
self.display_public_message("Please select a device below.")
return 0
@@ -873,8 +880,9 @@ def check_devices_status_and_select_first_if_only_one(
# get the status and identifier of the device;
# return if device is not in a connectable state
try:
- _, device_id, _stat = \
- self.current_device_identifier(need_status=True)
+ _, device_id, _stat = self.current_device_identifier(
+ need_status=True
+ )
if self.is_device_unusable(_stat):
self.show_device_status_failure(_stat)
return 0
@@ -889,8 +897,7 @@ def check_devices_status_and_select_first_if_only_one(
self.display_public_message("Please select a device below.")
return 0
else:
- _, device_id, _stat = self.current_device_identifier(
- need_status=True)
+ _, device_id, _stat = self.current_device_identifier(need_status=True)
if self.is_device_unusable(_stat):
self.show_device_status_failure(_stat)
return 0
@@ -921,8 +928,7 @@ def start_act(self):
# ====================================================================
# 3: Check devices
values_devices_list = self.scan_devices_update_list_view()
- _e = self.check_devices_status_and_select_first_if_only_one(
- values_devices_list)
+ _e = self.check_devices_status_and_select_first_if_only_one(values_devices_list)
if _e is None or isinstance(_e, int):
return _e
device_id, more_devices, _stat = _e
@@ -934,17 +940,17 @@ def start_act(self):
if self.dimensionDefaultCheckbox.isChecked():
self.dimensionSlider.setEnabled(False)
self.dimensionText.setText("DEFAULT")
- self.config['dimension'] = None
+ self.config["dimension"] = None
else:
self.dimensionSlider.setEnabled(True)
- self.config['dimension'] = int(self.dimensionSlider.value())
- self.dimensionSlider.setValue(self.config['dimension'])
- self.dimensionText.setText(str(self.config['dimension']) + "px")
+ self.config["dimension"] = int(self.dimensionSlider.value())
+ self.dimensionSlider.setValue(self.config["dimension"])
+ self.dimensionText.setText(str(self.config["dimension"]) + "px")
# edit configuration files to update dimension key
- if self.config['dimension'] is None:
+ if self.config["dimension"] is None:
self.options = " "
- elif self.config['dimension'] is not None:
- self.options = " -m " + str(self.config['dimension'])
+ elif self.config["dimension"] is not None:
+ self.options = " -m " + str(self.config["dimension"])
else:
self.options = ""
progress = self.progress(progress)
@@ -955,18 +961,18 @@ def start_act(self):
self.options += " --always-on-top"
if self.fullscreen.isChecked():
self.options += " -f"
- self.config['fullscreen'] = True
+ self.config["fullscreen"] = True
else:
- self.config['fullscreen'] = False
+ self.config["fullscreen"] = False
progress = self.progress(progress)
# ====================================================================
# 6: Check if show touches / recording are on
if self.showTouches.isChecked():
self.options += " --show-touches"
- self.config['swtouches'] = True
+ self.config["swtouches"] = True
else:
- self.config['swtouches'] = False
+ self.config["swtouches"] = False
progress = self.progress(progress)
# ====================================================================
@@ -979,23 +985,25 @@ def start_act(self):
# 8: Check if the display is forced to be on
if self.displayForceOn.isChecked():
self.options += " -S"
- self.config['dispRO'] = True
+ self.config["dispRO"] = True
else:
- self.config['dispRO'] = False
+ self.config["dispRO"] = False
progress = self.progress(progress)
# ====================================================================
# 9: Parse bitrate
# Bitrate is parsed, by editing the bitrate mask
- if self.bitrateText.text().split()[1][0] in ['K', 'M', 'T']:
+ if self.bitrateText.text().split()[1][0] in ["K", "M", "T"]:
bitrate_multiplier = str(self.bitrateText.text().split()[1][0])
elif self.bitrateText.text().split()[1][0] == "B":
bitrate_multiplier = "B"
else:
# do not proceed. Invalid file size multiplier
- multiplier_error = f"Invalid file size multiplier \
- '{str(self.bitrateText.text().split()[1][0])}'. " \
- f"Please use only K, M, T only"
+ multiplier_error = (
+ f"Invalid file size multiplier \
+ '{str(self.bitrateText.text().split()[1][0])}'. "
+ f"Please use only K, M, T only"
+ )
print(multiplier_error)
self.display_public_message(multiplier_error)
return False
@@ -1004,7 +1012,7 @@ def start_act(self):
else:
bitrate_integer = 8000
self.options += " -b {}{}".format(bitrate_integer, bitrate_multiplier)
- self.config['bitrate'] = bitrate_integer
+ self.config["bitrate"] = bitrate_integer
progress = self.progress(progress)
# ====================================================================
@@ -1014,83 +1022,88 @@ def start_act(self):
self.progressBar.setValue(50)
log("Flags passed to scrcpy engine : " + self.options)
self.progressBar.setValue(60)
- self.config['extra'] = self.flaglineedit.text()
+ self.config["extra"] = self.flaglineedit.text()
progress = self.progress(progress)
# ====================================================================
# 11: Initialize User Experience Mapper
ux = UXMapper(
- adb=self.adb,
- device_id=device_id,
- sha_shift=self.config.get('sha_shift', 5)
+ adb=self.adb, device_id=device_id, sha_shift=self.config.get("sha_shift", 5)
)
progress = self.progress(progress)
- always_on_top = \
- self.config.get('panels_always_on_top', False) or \
- not self.panels_not_always_on_top
+ always_on_top = (
+ self.config.get("panels_always_on_top", False)
+ or not self.panels_not_always_on_top
+ )
# ====================================================================
# 12: Init side_panel if necessary
if self.check_side_panel.isChecked():
- self.config['panels']['toolkit'] = True
+ self.config["panels"]["toolkit"] = True
side_instance = InterfaceToolkit(
parent=self,
ux_mapper=ux,
frame=self.force_window_frame,
- always_on_top=always_on_top
+ always_on_top=always_on_top,
)
for instance in self.child_windows:
- if instance.ux.get_sha() == side_instance.ux.get_sha() and \
- instance.name == side_instance.name and \
- not instance.isHidden():
+ if (
+ instance.ux.get_sha() == side_instance.ux.get_sha()
+ and instance.name == side_instance.name
+ and not instance.isHidden()
+ ):
break
else:
side_instance.init()
self.child_windows.append(side_instance)
else:
- self.config['panels']['toolkit'] = False
+ self.config["panels"]["toolkit"] = False
progress = self.progress(progress)
# ====================================================================
# 13: Init bottom_panel if necessary
if self.check_bottom_panel.isChecked():
- self.config['panels']['bottom'] = True
+ self.config["panels"]["bottom"] = True
panel_instance = Panel(
parent=self,
ux_mapper=ux,
frame=self.force_window_frame,
- always_on_top=always_on_top
+ always_on_top=always_on_top,
)
for instance in self.child_windows:
- if instance.ux.get_sha() == panel_instance.ux.get_sha() and \
- instance.name == panel_instance.name and \
- not instance.isHidden():
+ if (
+ instance.ux.get_sha() == panel_instance.ux.get_sha()
+ and instance.name == panel_instance.name
+ and not instance.isHidden()
+ ):
break
else:
panel_instance.init()
self.child_windows.append(panel_instance)
else:
- self.config['panels']['bottom'] = False
+ self.config["panels"]["bottom"] = False
progress = self.progress(progress)
# ====================================================================
# 14: Init swipe panel if necessary
if self.check_swipe_panel.isChecked():
- self.config['panels']['swipe'] = True
+ self.config["panels"]["swipe"] = True
swipe_instance = SwipeUX(
ux_wrapper=ux,
frame=self.force_window_frame,
- always_on_top=always_on_top
+ always_on_top=always_on_top,
) # Load swipe UI
for instance in self.child_windows:
- if instance.ux.get_sha() == swipe_instance.ux.get_sha() and \
- instance.name == swipe_instance.name and \
- not instance.isHidden():
+ if (
+ instance.ux.get_sha() == swipe_instance.ux.get_sha()
+ and instance.name == swipe_instance.name
+ and not instance.isHidden()
+ ):
break
else:
swipe_instance.init()
self.child_windows.append(swipe_instance)
else:
- self.config['panels']['swipe'] = False
+ self.config["panels"]["swipe"] = False
progress = self.progress(progress)
# ====================================================================
@@ -1112,26 +1125,23 @@ def start_act(self):
rotation_parameter = "--rotation"
if rotation_index != -1:
self.options += " {} {}".format(rotation_parameter, rotation_index)
- self.config['device'][identifier]['rotation'] = \
- rotation_index + 1
+ self.config["device"][identifier]["rotation"] = rotation_index + 1
else:
- self.config['device'][identifier]['rotation'] = 0
+ self.config["device"][identifier]["rotation"] = 0
# ====================================================================
# 18: Update device specific configuration
- if identifier.count('.') >= 3 and identifier[-1].isdigit():
- self.config['device'][identifier]['wifi'] = True
- self.config['device'][identifier]['model'] = model
+ if identifier.count(".") >= 3 and identifier[-1].isdigit():
+ self.config["device"][identifier]["wifi"] = True
+ self.config["device"][identifier]["model"] = model
# ====================================================================
# 16: Parse scrcpy arguments
if self.cmx is not None:
- self.config['cmx'] = ' '.join(map(str, self.cmx))
+ self.config["cmx"] = " ".join(map(str, self.cmx))
arguments_scrcpy = "{} {} {}".format(
- self.options,
- self.config['extra'],
- self.config['cmx']
+ self.options, self.config["extra"], self.config["cmx"]
)
progress = self.progress(progress)
@@ -1146,8 +1156,7 @@ def start_act(self):
# tell end users that the color of the device is this
self.display_public_message(
- f"Device {device_id} is connected; (color id matches "
- f"toolkit color)"
+ f"Device {device_id} is connected; (color id matches " f"toolkit color)"
)
log("Device connection completed successfully.")
log("Private message box updated successfully")
@@ -1158,8 +1167,7 @@ def start_act(self):
if not self.debug__no_scrcpy:
# for debugging purposes, its important to not start scrcpy
# every time
- self.scrcpy.start(arguments_scrcpy, stdout=sys.stdout,
- stderr=sys.stderr)
+ self.scrcpy.start(arguments_scrcpy, stdout=sys.stdout, stderr=sys.stderr)
progress = self.progress(progress)
# ====================================================================
@@ -1171,56 +1179,60 @@ def start_act(self):
# ====================================================================
# 22: Update configuration
- self.cfgmgr.update_config(self.config)
- self.cfgmgr.write_file()
+ self.config_manager.update_config(self.config)
+ self.config_manager.write_file()
progress = self.progress(progress)
return self.progress(progress)
def set_scrcpy_server_path(config):
- scrcpy_server_path_env = os.getenv('SCRCPY_SERVER_PATH', None)
+ scrcpy_server_path_env = os.getenv("SCRCPY_SERVER_PATH", None)
if scrcpy_server_path_env:
if not os.path.exists(scrcpy_server_path_env):
- server_path = open_exe_name_dialog(None, 'scrcpy-server')
+ server_path = open_exe_name_dialog(None, "scrcpy-server")
if server_path is None:
- raise ScrcpyServerNotFoundError(
- "User did not select scrcpy server")
- config['scrcpy-server'] = server_path
- os.environ['SCRCPY_SERVER_PATH'] = server_path
+ raise ScrcpyServerNotFoundError("User did not select scrcpy server")
+ config["scrcpy-server"] = server_path
+ os.environ["SCRCPY_SERVER_PATH"] = server_path
elif (
- (scrcpy_server_path_env is None) and (
- (isinstance(config.get('scrcpy-server'), str) and
- not os.path.exists(config.get('scrcpy-server'))) or
- config.get('scrcpy-server') is None)
- ) and (
- platform.System().system() == 'Windows'
- ):
- server_path = open_exe_name_dialog(None, 'scrcpy-server')
+ (scrcpy_server_path_env is None)
+ and (
+ (
+ isinstance(config.get("scrcpy-server"), str)
+ and not os.path.exists(config.get("scrcpy-server"))
+ )
+ or config.get("scrcpy-server") is None
+ )
+ ) and (platform.System().system() == "Windows"):
+ server_path = open_exe_name_dialog(None, "scrcpy-server")
if server_path is None:
- raise ScrcpyServerNotFoundError(
- "User did not select scrcpy server")
- config['scrcpy-server'] = server_path
- os.environ['SCRCPY_SERVER_PATH'] = server_path
+ raise ScrcpyServerNotFoundError("User did not select scrcpy server")
+ config["scrcpy-server"] = server_path
+ os.environ["SCRCPY_SERVER_PATH"] = server_path
elif platform.System().system() == "Windows":
- os.environ['SCRCPY_SERVER_PATH'] = config['scrcpy-server']
+ os.environ["SCRCPY_SERVER_PATH"] = config["scrcpy-server"]
return config
-def bootstrap(app, cfgmgr, theme='Breeze', aot=True, debug__no_scrcpy=False,
- hide_wm_frame=True):
+def bootstrap(
+ app: QtWidgets.QApplication,
+ config_manager: InterfaceConfig,
+ theme: str = "Breeze",
+ aot: bool = True,
+ debug_no_scrcpy: bool = False,
+ hide_wm_frame: bool = True,
+):
"""
Launch the guiscrcpy window
:return:
"""
- config = cfgmgr.get_config()
+ config = config_manager.get_config()
# load fonts
font_database = QFontDatabase()
for font in FONTS:
- s = font_database.addApplicationFont(':/font/fonts/{ttf}'.format(
- ttf=font
- ))
+ s = font_database.addApplicationFont(":/font/fonts/{ttf}".format(ttf=font))
if s == -1: # loading the font failed
# https://doc.qt.io/qt-5/qfontdatabase.html
print(fc("{y}Failed to load {ttf} font.{rst}", ttf=font))
@@ -1228,7 +1240,7 @@ def bootstrap(app, cfgmgr, theme='Breeze', aot=True, debug__no_scrcpy=False,
# set theme
app.setStyle(theme)
# apply stylesheet
- if theme == 'Breeze':
+ if theme == "Breeze":
# The Qdarkstylesheet is based on Breeze, lets load them on default
app.setStyleSheet(dark_stylesheet())
@@ -1241,16 +1253,19 @@ def bootstrap(app, cfgmgr, theme='Breeze', aot=True, debug__no_scrcpy=False,
# on windows, users are likely not to add the scrcpy-server to the
# SCRCPY_SERVER_PATH
- cfgmgr.update_config(set_scrcpy_server_path(config))
- cfgmgr.write_file()
- adb = AndroidDebugBridge(cfgmgr.get_config().get('adb'))
- scrcpy = ScrcpyBridge(cfgmgr.get_config().get('scrcpy'))
- cfgmgr['adb'] = adb.get_path()
- cfgmgr['scrcpy'] = scrcpy.get_path()
+ config_manager.update_config(set_scrcpy_server_path(config))
+ config_manager.write_file()
+ adb = AndroidDebugBridge(config_manager.get_config().get("adb"))
+ scrcpy = ScrcpyBridge(config_manager.get_config().get("scrcpy"))
+ config_manager["adb"] = adb.get_path()
+ config_manager["scrcpy"] = scrcpy.get_path()
guiscrcpy = InterfaceGuiscrcpy(
- cfgmgr=cfgmgr, adb=adb, scrcpy=scrcpy,
+ config_manager=config_manager,
+ adb=adb,
+ scrcpy=scrcpy,
force_window_frame=not hide_wm_frame,
- panels_not_always_on_top=not aot, debug__no_scrcpy=debug__no_scrcpy
+ panels_not_always_on_top=not aot,
+ debug_no_scrcpy=debug_no_scrcpy,
)
guiscrcpy.show()
app.processEvents()
diff --git a/guiscrcpy/lib/bridge/__init__.py b/guiscrcpy/lib/bridge/__init__.py
new file mode 100644
index 00000000..e91082c7
--- /dev/null
+++ b/guiscrcpy/lib/bridge/__init__.py
@@ -0,0 +1,4 @@
+from .adb import AndroidDebugBridge
+from .scrcpy import ScrcpyBridge
+
+__all__ = (AndroidDebugBridge, ScrcpyBridge)
diff --git a/guiscrcpy/lib/bridge/adb.py b/guiscrcpy/lib/bridge/adb.py
new file mode 100644
index 00000000..526d5eff
--- /dev/null
+++ b/guiscrcpy/lib/bridge/adb.py
@@ -0,0 +1,149 @@
+import logging
+import sys
+from subprocess import Popen, PIPE, TimeoutExpired, call
+
+from .base import Bridge
+from ..check import _get_dimension_raw_noexcept, get
+from ..utils import decode_process, _
+
+
+class AndroidDebugBridge(Bridge):
+ name = "adb"
+
+ def get_target_android_version(self, device_id=None):
+ api = 5
+ _proc = self.shell("getprop ro.build.version.release", device_id=device_id)
+ _ecode = _proc.wait(10)
+ if not _ecode:
+ api = int(_proc.stdout.read().decode())
+ return api
+
+ def shell_input(self, command, device_id=None):
+ path = self.path
+ if device_id:
+ Popen(
+ _("{} -s {} shell input {}".format(path, device_id, command)),
+ stdout=PIPE,
+ stderr=PIPE,
+ )
+ else:
+ Popen(
+ _("{} shell input {}".format(path, command)),
+ stdout=PIPE,
+ stderr=PIPE,
+ )
+
+ def kill_adb_server(self):
+ self.command(self.path, "kill-server")
+
+ def get_dimensions(self, device_id=None):
+ shell_adb = _get_dimension_raw_noexcept(path=self.path, device_id=device_id)
+ try:
+ if shell_adb.wait(timeout=3) != 0:
+ print(
+ "E: Command 'adb shell wm size' exited with {}".format(
+ shell_adb.returncode
+ )
+ )
+ return False
+ except TimeoutExpired:
+ print("E: adb falied; timeout exceeded 10s, killing and " "respawining adb")
+ self.kill_adb_server()
+ if isinstance(device_id, str) and device_id.count(".") >= 3:
+ self.command(self.path, "connect {}".format(device_id))
+ shell_adb = _get_dimension_raw_noexcept(path=self.path, device_id=device_id)
+ if shell_adb.wait(timeout=8) != 0:
+ print(
+ "E: Command 'adb shell wm size' exited with {}".format(
+ shell_adb.returncode
+ )
+ )
+ return False
+ raw_dimensions = shell_adb.stdout.read().decode().strip("\n")
+ for i in ["Override size", "Physical size"]:
+ if i in raw_dimensions:
+ out = raw_dimensions[raw_dimensions.find(i) :]
+ out_decoded = out.split(":")[1].strip()
+ dimension_values = out_decoded.split("x")
+ return dimension_values
+
+ # As the for loop did not find any device; and hence we have reached
+ # this line. Announce to the user regarding the same
+ logging.error(
+ "AndroidDeviceError: adb shell wm size did not return "
+ "'Physical Size' or 'Override Size'"
+ )
+ return False
+
+ def shell(self, command, device_id=None):
+ if device_id:
+ po = Popen(
+ _("{} -s {} shell {}".format(self.path, device_id, command)),
+ stdout=PIPE,
+ stderr=PIPE,
+ )
+ else:
+ po = Popen(
+ _("{} shell {}".format(self.path, command)), stdout=PIPE, stderr=PIPE
+ )
+ return po
+
+ def command(self, command, device_id=None):
+ if device_id:
+ adb_shell_output = Popen(
+ _("{} -s {} {}".format(self.path, device_id, command)),
+ stdout=PIPE,
+ stderr=PIPE,
+ )
+ else:
+ adb_shell_output = Popen(
+ _("{} {}".format(self.path, command)), stdout=PIPE, stderr=PIPE
+ )
+ return adb_shell_output
+
+ def tcpip(self, port=5555, identifier=""):
+ if identifier:
+ command = "{path} -s {identifier} -d tcpip {port}"
+ else:
+ command = "{path} -d tcpip {port}"
+ exit_code = call(
+ _(command.format(path=self.path, port=port, identifier=identifier)),
+ stdout=sys.stdout,
+ stderr=sys.stdout,
+ )
+ return exit_code
+
+ def devices(self):
+ proc = Popen(_(self.path + " devices"), stdout=PIPE)
+ output = [[y.strip() for y in x.split("\t")] for x in decode_process(proc)[1:]][
+ :-1
+ ]
+
+ logging.debug("ADB: {}".format(output))
+ return output
+
+ def devices_detailed(self):
+ proc = Popen(_(self.path + " devices -l"), stdout=PIPE)
+ output = [[y.strip() for y in x.split()] for x in decode_process(proc)[1:]][:-1]
+ devices_found = []
+ for device in output:
+ # https://github.com/srevinsaju/guiscrcpy/issues/117
+ if "udev" in device and "permission" in device:
+ # This is an error with some linux and Windows OSes
+ # This happens because the udev is not configured
+ # and linux adb does not have access to reading the device
+ # the status hence should be 'no_permission'
+ status = "no_permission"
+ else:
+ status = device[1]
+ description = {
+ "identifier": device[0],
+ "status": status,
+ "product": get(device, 2, ":").split(":")[-1],
+ "model": get(device, 3, ":").split(":")[-1],
+ "device": get(device, 4, ":").split(":")[-1],
+ "transport_id": get(device, 5, ":").split(":")[-1],
+ }
+ devices_found.append(description)
+ logging.debug("ADB: {}".format(devices_found))
+ return devices_found
diff --git a/guiscrcpy/lib/bridge/audio/__init__.py b/guiscrcpy/lib/bridge/audio/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/guiscrcpy/lib/bridge/audio/base.py b/guiscrcpy/lib/bridge/audio/base.py
new file mode 100644
index 00000000..d40160bd
--- /dev/null
+++ b/guiscrcpy/lib/bridge/audio/base.py
@@ -0,0 +1,6 @@
+from ..base import Bridge
+
+
+class AudioBridge(Bridge):
+ def run(self):
+ raise NotImplementedError
diff --git a/guiscrcpy/lib/bridge/audio/sndcpy.py b/guiscrcpy/lib/bridge/audio/sndcpy.py
new file mode 100644
index 00000000..0324e2be
--- /dev/null
+++ b/guiscrcpy/lib/bridge/audio/sndcpy.py
@@ -0,0 +1,47 @@
+import sys
+import subprocess
+import time
+
+from .base import AudioBridge
+from ...utils import shellify as _, show_message_box
+
+
+class SndcpyBridge(AudioBridge):
+ name = "sndcpy"
+
+ def run(self, device_id=None):
+ if device_id is None:
+ command = "{sndcpy}"
+ else:
+ command = "{sndcpy} {device_id}"
+ _proc = subprocess.Popen(
+ _(command.format(sndcpy=self.get_path(), device_id=device_id)),
+ stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE,
+ stdin=subprocess.PIPE,
+ )
+ waiting_since = 0
+ while True:
+ if waiting_since > 30:
+ # we should stop now
+ _out = show_message_box(
+ text="sndcpy Failed to connect",
+ info_text="We couldn't establish a proper connection "
+ "to your device. Check if sndcpy is given stream permissions. "
+ "See https://github.com/rom1v/sndcpy for more information.",
+ )
+ _out.exec_()
+ line = _proc.stdout.readline()
+ if not line:
+ break
+ if "Press Enter" in line.decode():
+ _out = show_message_box(
+ text="Sndcpy",
+ info_text="Click Accept on your Android device, and then click 'ok'",
+ )
+ _out.exec_()
+ _proc.stdin.write(b"\n\r\n\r")
+ _proc.stdin.close()
+ break
+ time.sleep(0.1)
+ waiting_since += 0.1
diff --git a/guiscrcpy/lib/bridge/audio/usbaudio.py b/guiscrcpy/lib/bridge/audio/usbaudio.py
new file mode 100644
index 00000000..14cd64c2
--- /dev/null
+++ b/guiscrcpy/lib/bridge/audio/usbaudio.py
@@ -0,0 +1,21 @@
+import sys
+import subprocess
+
+
+from .base import AudioBridge
+from ...utils import shellify as _
+
+
+class USBAudioBridge(AudioBridge):
+ name = "usbaudio"
+
+ def run(self, device_id=None):
+ if device_id is None:
+ command = "{usbaudio}"
+ else:
+ command = "{usbaudio} --serial {device_id}"
+ subprocess.Popen(
+ _(command.format(usbaudio=self.get_path(), device_id=device_id)),
+ stdout=sys.stdout,
+ stderr=sys.stderr,
+ )
diff --git a/guiscrcpy/lib/bridge/base.py b/guiscrcpy/lib/bridge/base.py
new file mode 100644
index 00000000..16af9841
--- /dev/null
+++ b/guiscrcpy/lib/bridge/base.py
@@ -0,0 +1,25 @@
+import shutil
+
+
+from ...install.finder import open_exe_name_dialog
+
+
+class Bridge:
+ name = None
+
+ def __init__(self, path=None):
+ if path is not None:
+ self.path = path
+ elif shutil.which(self.name):
+ self.path = shutil.which(self.name)
+ else:
+ self.path = open_exe_name_dialog(None, self.name)
+ if self.path is None:
+ raise FileNotFoundError(f"Could not find '{self.name}' on $PATH.")
+ self.post_init()
+
+ def post_init(self):
+ pass
+
+ def get_path(self):
+ return self.path
diff --git a/guiscrcpy/lib/bridge/exceptions.py b/guiscrcpy/lib/bridge/exceptions.py
new file mode 100644
index 00000000..97f4ea25
--- /dev/null
+++ b/guiscrcpy/lib/bridge/exceptions.py
@@ -0,0 +1,14 @@
+class ScrcpyNotFoundError(FileNotFoundError):
+ pass
+
+
+class ScrcpyServerNotFoundError(FileNotFoundError):
+ pass
+
+
+class AdbNotFoundError(FileNotFoundError):
+ pass
+
+
+class AdbRuntimeError(RuntimeError):
+ pass
diff --git a/guiscrcpy/lib/bridge/scrcpy.py b/guiscrcpy/lib/bridge/scrcpy.py
new file mode 100644
index 00000000..cba58024
--- /dev/null
+++ b/guiscrcpy/lib/bridge/scrcpy.py
@@ -0,0 +1,24 @@
+import os
+from subprocess import Popen, PIPE
+
+from .base import Bridge
+from ...lib.utils import shellify as _
+
+
+class ScrcpyBridge(Bridge):
+ name = "scrcpy"
+
+ def post_init(self):
+ if os.getenv("SCRCPY_LDD"):
+ if os.getenv("LD_LIBRARY_PATH"):
+ os.environ["LD_LIBRARY_PATH"] += os.getenv("SCRCPY_LDD")
+ else:
+ os.environ["LD_LIBRARY_PATH"] = os.getenv("SCRCPY_LDD")
+
+ def start(self, args, stdout=PIPE, stderr=PIPE):
+ proc = Popen(
+ _("{} {}".format(self.path, args)),
+ stdout=stdout,
+ stderr=stderr,
+ )
+ return proc
diff --git a/guiscrcpy/lib/check.py b/guiscrcpy/lib/check.py
index dd2dfd76..f03e2fc5 100644
--- a/guiscrcpy/lib/check.py
+++ b/guiscrcpy/lib/check.py
@@ -17,16 +17,9 @@
along with this program. If not, see .
"""
-import os
-import logging
-import shutil
-from subprocess import Popen, PIPE, call, TimeoutExpired
+from subprocess import Popen, PIPE
-from ..lib.utils import decode_process, shellify as _
-from ..platform.platform import System
-from ..install.finder import open_exe_name_dialog
-
-environment = System()
+from ..lib.utils import shellify as _
def get(ls, idx, default=""):
@@ -40,205 +33,9 @@ def _get_dimension_raw_noexcept(path, device_id=None):
if device_id:
shell_adb = Popen(
_("{} -s {} shell wm size".format(path, device_id)),
- stdout=PIPE, stderr=PIPE)
- else:
- shell_adb = Popen(_("{} shell wm size".format(path)),
- stdout=PIPE, stderr=PIPE)
- return shell_adb
-
-
-class ScrcpyNotFoundError(FileNotFoundError):
- pass
-
-
-class ScrcpyServerNotFoundError(FileNotFoundError):
- pass
-
-
-class ScrcpyBridge:
- def __init__(self, path=None):
- if path is not None:
- self.path = path
- elif shutil.which('scrcpy'):
- self.path = shutil.which('scrcpy')
- else:
- self.path = open_exe_name_dialog(None, 'scrcpy')
- if self.path is None:
- raise ScrcpyNotFoundError("Could not find `scrcpy` on PATH. Make "
- "sure scrcpy is installed and "
- "accessible from the terminal.")
- if os.getenv('SCRCPY_LDD'):
- if os.getenv('LD_LIBRARY_PATH'):
- os.environ['LD_LIBRARY_PATH'] += os.getenv('SCRCPY_LDD')
- else:
- os.environ['LD_LIBRARY_PATH'] = os.getenv('SCRCPY_LDD')
-
- def start(self, args, stdout=PIPE, stderr=PIPE):
- proc = Popen(
- _("{} {}".format(self.path, args)),
- stdout=stdout,
- stderr=stderr,
- )
- return proc
-
- def get_path(self):
- return self.path
-
-
-class AdbNotFoundError(FileNotFoundError):
- pass
-
-
-class AdbRuntimeError(RuntimeError):
- pass
-
-
-class AndroidDebugBridge:
- def __init__(self, path=None):
- if path is not None:
- self.path = path
- elif shutil.which('adb') is not None:
- self.path = shutil.which('adb')
- else:
- self.path = open_exe_name_dialog(None, 'adb')
- if self.path is None:
- AdbNotFoundError("Could not find `adb` on PATH. "
- "Make sure adb is installed accessible "
- "from the terminal")
-
- def get_target_android_version(self, device_id=None):
- api = 5
- _proc = self.shell(
- "getprop ro.build.version.release", device_id=device_id)
- _ecode = _proc.wait(10)
- if not _ecode:
- api = int(_proc.stdout.read().decode())
- return api
-
- def shell_input(self, command, device_id=None):
- path = self.path
- if device_id:
- Popen(
- _("{} -s {} shell input {}".format(path, device_id, command)),
- stdout=PIPE,
- stderr=PIPE,
- )
- else:
- Popen(
- _("{} shell input {}".format(path, command)),
- stdout=PIPE,
- stderr=PIPE,
- )
-
- def get_path(self):
- return self.path
-
- def kill_adb_server(self):
- self.command(self.path, "kill-server")
-
- def get_dimensions(self, device_id=None):
- shell_adb = _get_dimension_raw_noexcept(
- path=self.path, device_id=device_id
- )
- try:
- if shell_adb.wait(timeout=3) != 0:
- print("E: Command 'adb shell wm size' exited with {}".format(
- shell_adb.returncode))
- return False
- except TimeoutExpired:
- print("E: adb falied; timeout exceeded 10s, killing and "
- "respawining adb")
- self.kill_adb_server()
- if isinstance(device_id, str) and device_id.count('.') >= 3:
- self.command(self.path, "connect {}".format(device_id))
- shell_adb = _get_dimension_raw_noexcept(
- path=self.path, device_id=device_id
- )
- if shell_adb.wait(timeout=8) != 0:
- print("E: Command 'adb shell wm size' exited with {}".format(
- shell_adb.returncode))
- return False
- raw_dimensions = shell_adb.stdout.read().decode().strip('\n')
- for i in ['Override size', 'Physical size']:
- if i in raw_dimensions:
- out = raw_dimensions[raw_dimensions.find(i):]
- out_decoded = out.split(':')[1].strip()
- dimension_values = out_decoded.split('x')
- return dimension_values
-
- # As the for loop did not find any device; and hence we have reached
- # this line. Announce to the user regarding the same
- logging.error(
- "AndroidDeviceError: adb shell wm size did not return "
- "'Physical Size' or 'Override Size'"
- )
- return False
-
- def shell(self, command, device_id=None):
- if device_id:
- po = Popen(_("{} -s {} shell {}".format(
- self.path, device_id, command)),
- stdout=PIPE, stderr=PIPE)
- else:
- po = Popen(_("{} shell {}".format(self.path, command)),
- stdout=PIPE, stderr=PIPE)
- return po
-
- def command(self, command, device_id=None):
- if device_id:
- adb_shell_output = Popen(
- _("{} -s {} {}".format(self.path, device_id, command)),
- stdout=PIPE,
- stderr=PIPE)
- else:
- adb_shell_output = Popen(_("{} {}".format(self.path, command)),
- stdout=PIPE, stderr=PIPE)
- return adb_shell_output
-
- def tcpip(self, port=5555, identifier=""):
- if identifier:
- command = "{path} -s {identifier} -d tcpip {port}"
- else:
- command = "{path} -d tcpip {port}"
- exit_code = call(
- _(command.format(path=self.path, port=port,
- identifier=identifier)),
stdout=PIPE,
- stderr=PIPE
+ stderr=PIPE,
)
- return exit_code
-
- def devices(self):
- proc = Popen(_(self.path + " devices"), stdout=PIPE)
- output = [[y.strip() for y in x.split('\t')]
- for x in decode_process(proc)[1:]][:-1]
-
- logging.debug("ADB: {}".format(output))
- return output
-
- def devices_detailed(self):
- proc = Popen(_(self.path + " devices -l"), stdout=PIPE)
- output = [[y.strip() for y in x.split()]
- for x in decode_process(proc)[1:]][:-1]
- devices_found = []
- for device in output:
- # https://github.com/srevinsaju/guiscrcpy/issues/117
- if 'udev' in device and 'permission' in device:
- # This is an error with some linux and Windows OSes
- # This happens because the udev is not configured
- # and linux adb does not have access to reading the device
- # the status hence should be 'no_permission'
- status = 'no_permission'
- else:
- status = device[1]
- description = {
- 'identifier': device[0],
- 'status': status,
- 'product': get(device, 2, ':').split(':')[-1],
- 'model': get(device, 3, ':').split(':')[-1],
- 'device': get(device, 4, ':').split(':')[-1],
- 'transport_id': get(device, 5, ':').split(':')[-1]
- }
- devices_found.append(description)
- logging.debug("ADB: {}".format(devices_found))
- return devices_found
+ else:
+ shell_adb = Popen(_("{} shell wm size".format(path)), stdout=PIPE, stderr=PIPE)
+ return shell_adb
diff --git a/guiscrcpy/lib/config.py b/guiscrcpy/lib/config.py
index 91ab9080..39749bb9 100644
--- a/guiscrcpy/lib/config.py
+++ b/guiscrcpy/lib/config.py
@@ -36,28 +36,24 @@ def __init__(self, load=True):
self.cfgpath = self.os.cfgpath()
self.paths = self.os.paths()
self.config = {
- 'paths': self.paths,
- 'scrcpy': None,
- 'adb': None,
- 'panels': {
- 'swipe': True,
- 'bottom': True,
- 'toolkit': True
- },
- 'mapper': '',
- 'sha_shift': 5,
- 'scrcpy-server': None,
- 'dimension': None,
- 'swtouches': False,
- 'bitrate': 8000,
- 'fullscreen': False,
- 'dispRO': False,
- 'extra': "",
- 'cmx': "",
- 'device': {},
- 'theme': 'Breeze'
+ "paths": self.paths,
+ "scrcpy": None,
+ "adb": None,
+ "panels": {"swipe": True, "bottom": True, "toolkit": True},
+ "mapper": "",
+ "sha_shift": 5,
+ "scrcpy-server": None,
+ "dimension": None,
+ "swtouches": False,
+ "bitrate": 8000,
+ "fullscreen": False,
+ "dispRO": False,
+ "extra": "",
+ "cmx": "",
+ "device": {},
+ "theme": "Breeze",
}
- self.json_file = 'guiscrcpy.json'
+ self.json_file = "guiscrcpy.json"
if load:
self.load_config()
@@ -68,42 +64,41 @@ def load_config(self):
def validate(self):
# check scrcpy and adb are not None, else replace it with original
# values
- if os.getenv('APPIMAGE') is not None:
+ if os.getenv("APPIMAGE") is not None:
# no need further configuration for adb, scrcpy and scrcpy_server
- self.config['adb'] = os.getenv('GUISCRCPY_ADB')
- self.config['scrcpy'] = os.getenv('GUISCRCPY_SCRCPY')
+ self.config["adb"] = os.getenv("GUISCRCPY_ADB")
+ self.config["scrcpy"] = os.getenv("GUISCRCPY_SCRCPY")
return True
- if self.config['adb'] is None:
- adb_path = shutil.which('adb')
- self.config['adb'] = adb_path
+ if self.config["adb"] is None:
+ adb_path = shutil.which("adb")
+ self.config["adb"] = adb_path
else:
- _adb_path = self.config['adb']
+ _adb_path = self.config["adb"]
if not os.path.exists(_adb_path):
raise InvalidConfigurationError(
"The configuration key 'adb' is "
"invalid. {} does not exist. "
"If you did not set it on purpose, "
"run `guiscrcpy config -r` to reset "
- "the configuration".format(
- self.config['adb'])
+ "the configuration".format(self.config["adb"])
)
- if self.config['scrcpy'] is None:
- scrcpy_path = shutil.which('scrcpy')
- self.config['scrcpy'] = scrcpy_path
+ if self.config["scrcpy"] is None:
+ scrcpy_path = shutil.which("scrcpy")
+ self.config["scrcpy"] = scrcpy_path
else:
- _scrcpy_path = self.config['scrcpy']
+ _scrcpy_path = self.config["scrcpy"]
if not os.path.exists(_scrcpy_path):
raise InvalidConfigurationError(
"The configuration key 'scrcpy' is "
"invalid. {} does not exist. "
"If you did not set it on purpose, "
"run `guiscrcpy config -r` to reset "
- "the configuration".format(
- self.config['scrcpy'])
+ "the configuration".format(self.config["scrcpy"])
)
- if (self.config['scrcpy-server'] is not None) and (
- platform.System() == "Windows"):
- os.environ['SCRCPY_SERVER_PATH'] = self.config['scrcpy-server']
+ if (self.config["scrcpy-server"] is not None) and (
+ platform.System() == "Windows"
+ ):
+ os.environ["SCRCPY_SERVER_PATH"] = self.config["scrcpy-server"]
return True
def __setitem__(self, key, value):
@@ -117,14 +112,14 @@ def get_config(self):
return self.config
def get_scrcpy(self):
- if self.config['scrcpy'] is not None:
- return self.config['scrcpy']
+ if self.config["scrcpy"] is not None:
+ return self.config["scrcpy"]
else:
return None
def get_adb(self):
- if self.config['adb'] is not None:
- return self.config['adb']
+ if self.config["adb"] is not None:
+ return self.config["adb"]
else:
return None
@@ -132,12 +127,12 @@ def get_cfgpath(self):
return self.cfgpath
def read_file(self):
- with open(os.path.join(self.cfgpath, self.json_file), 'r') as f:
+ with open(os.path.join(self.cfgpath, self.json_file), "r") as f:
config = json.load(f)
self.update_config(config)
def write_file(self):
- with open(os.path.join(self.cfgpath, self.json_file), 'w') as f:
+ with open(os.path.join(self.cfgpath, self.json_file), "w") as f:
json.dump(self.config, f, indent=4, sort_keys=True)
def check_file(self):
@@ -160,6 +155,5 @@ def reset_config(self):
def __repr__(self):
return 'GuiscrcpyConfig({}, "{}")'.format(
- json.dumps(self.config, indent=4),
- self.cfgpath
+ json.dumps(self.config, indent=4), self.cfgpath
)
diff --git a/guiscrcpy/lib/mapper/mapper.py b/guiscrcpy/lib/mapper/mapper.py
index 44c835d7..ee1009e0 100644
--- a/guiscrcpy/lib/mapper/mapper.py
+++ b/guiscrcpy/lib/mapper/mapper.py
@@ -34,7 +34,7 @@
fixed_pos = [0.0, 0.0]
final_pos = [0.0, 0.0]
-json_file = 'guiscrcpy.mapper.json'
+json_file = "guiscrcpy.mapper.json"
def log(category, message):
@@ -48,7 +48,7 @@ def __init__(self, device_id, adb, config_path=None):
self.app = None
self.adb = adb
log("mapper", "Waiting for device...")
- self.adb.command('wait-for-any-device')
+ self.adb.command("wait-for-any-device")
log("mapper", "Device connection established...")
self.window = None
self.guiscrcpy_mapper_json = config_path
@@ -66,17 +66,21 @@ def check_orientation(self):
e_code = proc.wait(5)
except subprocess.TimeoutExpired:
e_code = 0
- log("mapper", "Failed to detect orientation instantly. Expect"
- "invalid orientations.")
+ log(
+ "mapper",
+ "Failed to detect orientation instantly. Expect"
+ "invalid orientations.",
+ )
if e_code != 0:
# process failed
raise AdbRuntimeError(
- 'adb failed with {ecode} when trying to '
- 'execute command '
- '`adb shell dumpsys input`'.format(ecode=e_code))
+ "adb failed with {ecode} when trying to "
+ "execute command "
+ "`adb shell dumpsys input`".format(ecode=e_code)
+ )
out, err = proc.communicate()
out, err = out.decode(), err.decode()
- if 'SurfaceOrientation' in out:
+ if "SurfaceOrientation" in out:
# SurfaceOrientation gives the idea if the device is
# landscape or portait. SurfaceOrientation: 1 mentions that
# the mobile is oriented in the landscape orientation
@@ -89,12 +93,12 @@ def check_orientation(self):
print("Detected Landscape orientation...")
return 1
else:
- print("Failed to detect orientation from device. "
- "Fallback to 0")
+ print("Failed to detect orientation from device. " "Fallback to 0")
return 0
else:
- print("Failed to detect Orientation. SurfaceOrientation"
- " key was not found")
+ print(
+ "Failed to detect Orientation. SurfaceOrientation" " key was not found"
+ )
return 0
def set_device_id(self, device_id):
@@ -124,32 +128,29 @@ def get_screenshot(self):
print("Please wait. A full definition screenshot is being captured")
adb_screencap_process = self.adb.command(
- 'shell screencap -p /sdcard/{uid}.png'.format(
- uid=uid
- ),
- device_id=self._device_id
+ "shell screencap -p /sdcard/{uid}.png".format(uid=uid),
+ device_id=self._device_id,
)
adb_screencap_process_ecode = adb_screencap_process.wait(500)
if adb_screencap_process_ecode != 0:
print("Screenshot failed. Exiting")
- print(adb_screencap_process.stdout.read().decode('utf-8'))
+ print(adb_screencap_process.stdout.read().decode("utf-8"))
return
# sleep for two seconds so that the image is processed
time.sleep(2)
# pull screenshot from android using `adb pull`
adb_pull_process = self.adb.command(
- 'pull /sdcard/{uid}.png {dest}'.format(
- uid=uid,
- dest=os.path.dirname(self.guiscrcpy_mapper_json)
+ "pull /sdcard/{uid}.png {dest}".format(
+ uid=uid, dest=os.path.dirname(self.guiscrcpy_mapper_json)
),
- device_id=self._device_id
+ device_id=self._device_id,
)
adb_pull_process_ecode = adb_pull_process.wait(500)
if adb_pull_process_ecode != 0:
print("Screenshot pull failed. Exiting")
- print(adb_pull_process.stdout.read().decode('utf-8'))
+ print(adb_pull_process.stdout.read().decode("utf-8"))
return
# sleep for 1 second to get capture time
@@ -157,13 +158,16 @@ def get_screenshot(self):
# remove data from user sdcard
self.adb.command(
- "shell rm /sdcard/{uid}.png".format(uid=uid),
- device_id=self._device_id
+ "shell rm /sdcard/{uid}.png".format(uid=uid), device_id=self._device_id
+ )
+ print(
+ "[LOG] Screenshot captured. Saved to {cfgpath}".format(
+ cfgpath=os.path.dirname(self.guiscrcpy_mapper_json)
+ )
+ )
+ return os.path.join(
+ os.path.dirname(self.guiscrcpy_mapper_json), "{uid}.png".format(uid=uid)
)
- print("[LOG] Screenshot captured. Saved to {cfgpath}".format(
- cfgpath=os.path.dirname(self.guiscrcpy_mapper_json)))
- return os.path.join(os.path.dirname(self.guiscrcpy_mapper_json),
- '{uid}.png'.format(uid=uid))
# The following functions handle key events on the mapper
def on_key_press(self, key):
@@ -172,10 +176,10 @@ def on_key_press(self, key):
print("[KEY] Hotkey command executing")
position_to_tap = self.config.get(key.char)
c = self.adb.command(
- 'shell input tap {} {}'.format(*position_to_tap),
- device_id=self.get_device_id()
+ "shell input tap {} {}".format(*position_to_tap),
+ device_id=self.get_device_id(),
)
- print(c.stdout.read().decode('utf-8'))
+ print(c.stdout.read().decode("utf-8"))
print("[KEY][COMPLETE]")
except AttributeError:
@@ -194,10 +198,7 @@ def listen_keypress(self):
:return:
:rtype:
"""
- print(
- "[SERVER] LISTENING VALUES:"
- "Your keys are being listened by server. "
- )
+ print("[SERVER] LISTENING VALUES:" "Your keys are being listened by server. ")
try:
with keyboard.Listener(on_press=self.on_key_press) as listener:
listener.join()
@@ -208,11 +209,11 @@ def listen_keypress(self):
def read_configuration(self):
if not os.path.exists(self.guiscrcpy_mapper_json):
self.create_configuration()
- with open(self.guiscrcpy_mapper_json, 'r', encoding='utf-8') as f:
+ with open(self.guiscrcpy_mapper_json, "r", encoding="utf-8") as f:
self.config.update(json.load(f))
def create_configuration(self):
- with open(self.guiscrcpy_mapper_json, 'w') as w:
+ with open(self.guiscrcpy_mapper_json, "w") as w:
json.dump(self.config, w)
print("Wrote configuration file.")
@@ -228,7 +229,7 @@ def initialize(self, initialize_qt=False):
"""
print("Setting up guiscrcpy-mapper for the first time use...")
print("Intializing GUI window")
- if __name__ == '__main__' or initialize_qt:
+ if __name__ == "__main__" or initialize_qt:
print("Creating QtCore window Application instance")
self.app = QtWidgets.QApplication([])
self.window = MapperUI(
@@ -236,15 +237,14 @@ def initialize(self, initialize_qt=False):
self.get_screenshot(),
self.dimensions,
fixed_pos=fixed_pos,
- final_pos=final_pos
+ final_pos=final_pos,
)
self.app.processEvents()
self.app.exec_()
class MapperAsync(QThread):
- def __init__(self, parent, device_id, adb, initialize=True,
- config_path=None):
+ def __init__(self, parent, device_id, adb, initialize=True, config_path=None):
QThread.__init__(self, parent)
self.parent = parent
self.adb = adb
@@ -253,8 +253,7 @@ def __init__(self, parent, device_id, adb, initialize=True,
self._config_path = config_path
def run(self):
- mp = Mapper(self.device_id, adb=self.adb,
- config_path=self._config_path)
+ mp = Mapper(self.device_id, adb=self.adb, config_path=self._config_path)
if self.initialize:
mp.initialize(initialize_qt=False)
else:
diff --git a/guiscrcpy/lib/mapper/ux.py b/guiscrcpy/lib/mapper/ux.py
index 7277bc62..b25cbde1 100644
--- a/guiscrcpy/lib/mapper/ux.py
+++ b/guiscrcpy/lib/mapper/ux.py
@@ -30,8 +30,15 @@ class MapperUI(QtWidgets.QWidget):
configuration an button
mapping
"""
- def __init__(self, core, screenshot_path, dimensions,
- fixed_pos=[0.0, 0.0], final_pos=[0.0, 0.0]):
+
+ def __init__(
+ self,
+ core,
+ screenshot_path,
+ dimensions,
+ fixed_pos=[0.0, 0.0],
+ final_pos=[0.0, 0.0],
+ ):
self.fixed_pos = fixed_pos
self.final_pos = final_pos
self.core = core
@@ -50,17 +57,16 @@ def build_user_interface(self):
self.widget.setGeometry(QtCore.QRect(0, 0, 351, 34))
self.widget.setObjectName("widget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.widget)
- self.horizontalLayout.setSizeConstraint(
- QtWidgets.QLayout.SetMaximumSize)
+ self.horizontalLayout.setSizeConstraint(QtWidgets.QLayout.SetMaximumSize)
self.horizontalLayout.setContentsMargins(0, 0, 0, 0)
self.horizontalLayout.setObjectName("horizontalLayout")
self.lineEdit = QtWidgets.QLineEdit(self.widget)
size_policy = QtWidgets.QSizePolicy(
- QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed)
+ QtWidgets.QSizePolicy.Minimum, QtWidgets.QSizePolicy.Fixed
+ )
size_policy.setHorizontalStretch(0)
size_policy.setVerticalStretch(0)
- size_policy.setHeightForWidth(
- self.lineEdit.sizePolicy().hasHeightForWidth())
+ size_policy.setHeightForWidth(self.lineEdit.sizePolicy().hasHeightForWidth())
self.lineEdit.setSizePolicy(size_policy)
self.lineEdit.setMinimumSize(QtCore.QSize(25, 25))
self.lineEdit.setMaximumSize(QtCore.QSize(25, 16777215))
@@ -101,13 +107,9 @@ def set_screenshot_to_label(self, screenshot_path):
"""
self.pixmap = QtGui.QPixmap(screenshot_path)
self.label.resize(
- int(0.5 * self.pixmap.width()),
- int(0.5 * self.pixmap.height())
- )
- self.resize(
- int(0.5 * self.pixmap.width()),
- int(0.5 * self.pixmap.height())
+ int(0.5 * self.pixmap.width()), int(0.5 * self.pixmap.height())
)
+ self.resize(int(0.5 * self.pixmap.width()), int(0.5 * self.pixmap.height()))
self.show()
self.resize(self.label.size())
@@ -117,8 +119,7 @@ def set_screenshot_to_label(self, screenshot_path):
int(0.5 * self.pixmap.width()), int(0.5 * self.pixmap.height())
)
self.setMaximumSize(
- int(0.5 * self.pixmap.width()),
- int(0.5 * self.pixmap.height())
+ int(0.5 * self.pixmap.width()), int(0.5 * self.pixmap.height())
)
self.label.installEventFilter(self)
layout = QtWidgets.QVBoxLayout(self)
@@ -126,8 +127,7 @@ def set_screenshot_to_label(self, screenshot_path):
self.pushButton.setText("OK")
self.label0.setWordWrap(True)
self.label0.setText(
- "Click the point, and enter char in textbox and "
- "press OK to continue."
+ "Click the point, and enter char in textbox and " "press OK to continue."
)
def register_key(self):
@@ -136,8 +136,10 @@ def register_key(self):
fixx = relx * int(self.dimensions[0])
fixy = rely * int(self.dimensions[1])
char = self.lineEdit.text()[:1]
- print("Successfully registered {ch} "
- "with position ({x}, {y})".format(ch=char, x=fixx, y=fixy))
+ print(
+ "Successfully registered {ch} "
+ "with position ({x}, {y})".format(ch=char, x=fixx, y=fixy)
+ )
self.core.add_position(char, (fixx, fixy))
self.label0.setText(
"SUCCESS! "
@@ -148,8 +150,7 @@ def register_key(self):
def eventFilter(self, source, event):
if source is self.label and event.type() == QtCore.QEvent.Resize:
self.label.setPixmap(
- self.pixmap.scaled(self.label.size(),
- QtCore.Qt.KeepAspectRatio)
+ self.pixmap.scaled(self.label.size(), QtCore.Qt.KeepAspectRatio)
)
return super(MapperUI, self).eventFilter(source, event)
@@ -160,7 +161,8 @@ def mousePressEvent(self, event):
self.fixed_pos[1] = int(event.pos().y())
print(self.last_found_point, "LAST")
self.last_found_point = self.label.mapFromParent(
- event.pos()) # this is working fine now
+ event.pos()
+ ) # this is working fine now
# self.label.setPixmap(QPixmap.fromImage(self.image))
def mouseMoveEvent(self, event):
@@ -170,7 +172,8 @@ def mouseMoveEvent(self, event):
# painter.drawLine(
# self.label.mapFromParent(event.pos()),self.last_found_point)
self.last_found_point = self.label.mapFromParent(
- event.pos()) # this is working fine now
+ event.pos()
+ ) # this is working fine now
print(self.last_found_point, "MOVE")
self.fixed_pos[0] = int(event.pos().x())
self.fixed_pos[1] = int(event.pos().y())
@@ -184,17 +187,15 @@ def mouseReleaseEvent(self, event):
def closeEvent(self, event):
# do stuff
message_box = QMessageBox()
- message_box.setText(
- "Save changes and exit?"
- )
- vals = ["{} → {}".format(
- x, self.core.config[x]) for x in self.core.config]
+ message_box.setText("Save changes and exit?")
+ vals = ["{} → {}".format(x, self.core.config[x]) for x in self.core.config]
message_box.setInformativeText(
"Mapper has unsaved mappings: {val}. Do you want to save the "
- "current mappings?".format(val=', '.join(vals))
+ "current mappings?".format(val=", ".join(vals))
)
message_box.setStandardButtons(
- QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)
+ QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel
+ )
user_message_box_response = message_box.exec()
if user_message_box_response == QMessageBox.Yes:
print("Registration process completed.")
diff --git a/guiscrcpy/lib/process.py b/guiscrcpy/lib/process.py
index c71ef6ee..ff5bfcc7 100644
--- a/guiscrcpy/lib/process.py
+++ b/guiscrcpy/lib/process.py
@@ -19,6 +19,7 @@
try:
import psutil
+
psutil_present = True
except ModuleNotFoundError:
psutil_present = False
@@ -32,10 +33,6 @@ def is_running(process_name):
# Check if process name contains the given name string.
if process_name.lower() in proc.name().lower():
return True
- except (
- psutil.NoSuchProcess,
- psutil.AccessDenied,
- psutil.ZombieProcess
- ):
+ except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess):
pass
return False
diff --git a/guiscrcpy/lib/toolkit.py b/guiscrcpy/lib/toolkit.py
index 732edd2c..93a4fe3d 100644
--- a/guiscrcpy/lib/toolkit.py
+++ b/guiscrcpy/lib/toolkit.py
@@ -31,8 +31,7 @@
import pyautogui as auto
from pygetwindow import getWindowsWithTitle
except ModuleNotFoundError as e:
- logging.debug("pygetwindow, pyautogui "
- "failed with error code {}".format(e))
+ logging.debug("pygetwindow, pyautogui " "failed with error code {}".format(e))
auto = None
getWindowsWithTitle = None
else:
@@ -46,26 +45,29 @@ def wmctrl_xdotool_linux_send_key(key):
wmctrl = shutil.which("wmctrl")
xdotool = shutil.which("xdotool")
if not wmctrl or not xdotool:
- print("E: Could not find {} on PATH. Make sure "
- "that a compatible package is installed "
- "on your system for this function")
+ print(
+ "E: Could not find {} on PATH. Make sure "
+ "that a compatible package is installed "
+ "on your system for this function"
+ )
return
- _proc = subprocess.Popen(shlex.split("wmctrl -x -a scrcpy"),
- stdout=sys.stdout,
- stderr=sys.stderr)
+ _proc = subprocess.Popen(
+ shlex.split("wmctrl -x -a scrcpy"), stdout=sys.stdout, stderr=sys.stderr
+ )
if _proc.wait() == 0:
_xdotool_proc = subprocess.Popen(
- shlex.split("xdotool key --clearmodifiers {}+{}".format(
- os.getenv("GUISCRCPY_MODIFIER") or "alt", key)),
+ shlex.split(
+ "xdotool key --clearmodifiers {}+{}".format(
+ os.getenv("GUISCRCPY_MODIFIER") or "alt", key
+ )
+ ),
stdout=sys.stdout,
stderr=sys.stderr,
)
if _xdotool_proc.wait() != 0:
- print("E (xdotool): Failed to send key {} to "
- "scrcpy window.".format(key))
+ print("E (xdotool): Failed to send key {} to " "scrcpy window.".format(key))
else:
- print("E (wmctrl): Failed to get scrcpy window. "
- "(Is scrcpy running?)")
+ print("E (wmctrl): Failed to get scrcpy window. " "(Is scrcpy running?)")
class UXMapper:
@@ -83,12 +85,14 @@ def __init__(self, adb, device_id=None, sha_shift=5):
logging.debug("Calculating Screen Size")
self.android_dimensions = adb.get_dimensions(device_id=device_id)
if not self.android_dimensions:
- print("E: guiscrcpy has crashed because of a failure in the "
- "execution of `adb shell wm size`. This might be because "
- "of an improper connection of adb. Please reconnect your "
- "device (disconnect from WiFi / Reconnect USB) or try \n\n"
- "guiscrcpy --killserver\n\nas a command line to restart adb "
- "server")
+ print(
+ "E: guiscrcpy has crashed because of a failure in the "
+ "execution of `adb shell wm size`. This might be because "
+ "of an improper connection of adb. Please reconnect your "
+ "device (disconnect from WiFi / Reconnect USB) or try \n\n"
+ "guiscrcpy --killserver\n\nas a command line to restart adb "
+ "server"
+ )
self.deviceId = device_id
# each device connected is uniquely identified by the tools by
@@ -101,7 +105,7 @@ def get_sha(self):
A method which returns the unique UUID of the the device
:return: The hexdigest of a salted hash
"""
- return self.__sha[self.sha_shift:self.sha_shift + 6]
+ return self.__sha[self.sha_shift : self.sha_shift + 6]
def do_swipe(self, x1=10, y1=10, x2=10, y2=10):
"""
@@ -112,8 +116,9 @@ def do_swipe(self, x1=10, y1=10, x2=10, y2=10):
:param y2: y2 coordinate
:return: Boolean True, in success
"""
- self.adb.shell_input("swipe {} {} {} {}".format(x1, y1, x2, y2),
- device_id=self.deviceId)
+ self.adb.shell_input(
+ "swipe {} {} {} {}".format(x1, y1, x2, y2), device_id=self.deviceId
+ )
return True
def do_keyevent(self, key):
@@ -122,8 +127,7 @@ def do_keyevent(self, key):
:param key: The ADB predefined keycode
:return:
"""
- self.adb.shell_input("keyevent {}".format(key),
- device_id=self.deviceId)
+ self.adb.shell_input("keyevent {}".format(key), device_id=self.deviceId)
return True
def copy_devpc(self):
@@ -164,17 +168,17 @@ def key_switch(self):
def reorient_portrait(self):
logging.debug("Passing REORIENT [POTRAIT]")
- self.adb.shell("settings put system accelerometer_rotation 0",
- device_id=self.deviceId)
- self.adb.shell("settings put system rotation 1",
- device_id=self.deviceId)
+ self.adb.shell(
+ "settings put system accelerometer_rotation 0", device_id=self.deviceId
+ )
+ self.adb.shell("settings put system rotation 1", device_id=self.deviceId)
def reorient_landscape(self):
logging.debug("Passing REORIENT [LANDSCAPE]")
- self.adb.shell("settings put system accelerometer_rotation 0",
- device_id=self.deviceId)
- self.adb.shell("settings put system rotation 1",
- device_id=self.deviceId)
+ self.adb.shell(
+ "settings put system accelerometer_rotation 0", device_id=self.deviceId
+ )
+ self.adb.shell("settings put system rotation 1", device_id=self.deviceId)
def expand_notifications(self):
logging.debug("Passing NOTIF EXPAND")
diff --git a/guiscrcpy/lib/utils.py b/guiscrcpy/lib/utils.py
index c038b6ea..1194a83b 100644
--- a/guiscrcpy/lib/utils.py
+++ b/guiscrcpy/lib/utils.py
@@ -32,25 +32,25 @@
environment = System()
COLORS = {
- 'g': Fore.GREEN,
- 'rst': Fore.RESET,
- 'y': Fore.YELLOW,
- 'r': Fore.RED,
- 'b': Fore.BLUE,
- 'B': Fore.LIGHTBLUE_EX,
- 'x': Fore.LIGHTBLACK_EX
+ "g": Fore.GREEN,
+ "rst": Fore.RESET,
+ "y": Fore.YELLOW,
+ "r": Fore.RED,
+ "b": Fore.BLUE,
+ "B": Fore.LIGHTBLUE_EX,
+ "x": Fore.LIGHTBLACK_EX,
}
def log(*args, **kwargs):
- if os.getenv('GUISCRCPY_DEBUG', False):
+ if os.getenv("GUISCRCPY_DEBUG", False):
print(*args, **kwargs)
else:
logging.debug(str(args))
def shellify(args):
- if environment.system() == 'Windows':
+ if environment.system() == "Windows":
return args
else:
return shlex.split(args)
@@ -63,7 +63,7 @@ def decode_process(process):
try:
output = process.stdout.readlines()
for i in range(len(output)):
- output[i] = output[i].decode('utf-8')
+ output[i] = output[i].decode("utf-8")
except NameError:
logging.error("No stdout in process.")
output = ""
@@ -77,13 +77,12 @@ def check_existence(paths, filename="", directory=True, path=False):
if directory and os.path.isdir(j):
return [j]
else:
- if environment.system() == 'Windows':
- append = '.exe'
+ if environment.system() == "Windows":
+ append = ".exe"
else:
- append = ''
+ append = ""
- if (isinstance(filename, list)) or \
- (isinstance(filename, tuple)):
+ if (isinstance(filename, list)) or (isinstance(filename, tuple)):
for exe in filename:
if os.path.exists(os.path.join(j, exe + append)):
return [os.path.join(j, exe + append)]
@@ -95,11 +94,12 @@ def check_existence(paths, filename="", directory=True, path=False):
logging.debug("{} doesn't exist".format(i))
if path:
- new_paths = os.getenv('PATH').split(os.pathsep)
+ new_paths = os.getenv("PATH").split(os.pathsep)
found_path = check_existence(
- new_paths, filename=filename, directory=directory, path=False)
+ new_paths, filename=filename, directory=directory, path=False
+ )
if found_path:
- return found_path + ['path']
+ return found_path + ["path"]
else:
return False
else:
@@ -112,26 +112,26 @@ def get_self():
:return:
:rtype:
"""
- if os.getenv('APPIMAGE'):
+ if os.getenv("APPIMAGE"):
# Running from AppImage
- return os.getenv('APPIMAGE')
- elif getattr(sys, 'frozen', False):
+ return os.getenv("APPIMAGE")
+ elif getattr(sys, "frozen", False):
# running in precompiled bundle
return sys.executable
- elif shutil.which('guiscrcpy'):
+ elif shutil.which("guiscrcpy"):
# guiscrcpy is added to PATH
- return shutil.which('guiscrcpy')
- elif any([os.path.exists(os.path.join(x, 'guiscrcpy'))
- for x in sys.path]) and \
- any([sys.executable.endswith(py) for py in
- ['python', 'python3']]):
+ return shutil.which("guiscrcpy")
+ elif any([os.path.exists(os.path.join(x, "guiscrcpy")) for x in sys.path]) and any(
+ [sys.executable.endswith(py) for py in ["python", "python3"]]
+ ):
# guiscrcpy is installed as a pip package, but not added to PATH
- return '{py} -m guiscrcpy'.format(py=sys.executable)
- elif os.getenv('SNAP'):
+ return "{py} -m guiscrcpy".format(py=sys.executable)
+ elif os.getenv("SNAP"):
# running from SNAP
- return '/snap/bin/guiscrcpy'
- raise RuntimeError("Could not detect if guiscrcpy was run from "
- "snap, appimage or python wheel")
+ return "/snap/bin/guiscrcpy"
+ raise RuntimeError(
+ "Could not detect if guiscrcpy was run from " "snap, appimage or python wheel"
+ )
def format_colors(string, **kwargs):
@@ -148,12 +148,15 @@ def show_message_box(text, info_text="", buttons=QMessageBox.Ok):
"""
message_box = QMessageBox()
try:
- message_box.setIconPixmap(QPixmap(
- ":/res/ui/guiscrcpy_logo.png").scaledToHeight(100))
+ message_box.setIconPixmap(
+ QPixmap(":/res/ui/guiscrcpy_logo.png").scaledToHeight(100)
+ )
except Exception as e:
- print("WARN: {e}: loading guiscrcpy "
- "message box pixmap failed. "
- "Ignoring".format(e=e))
+ print(
+ "WARN: {e}: loading guiscrcpy "
+ "message box pixmap failed. "
+ "Ignoring".format(e=e)
+ )
message_box.setText("{} ".format(text))
message_box.setTextFormat(QtCore.Qt.RichText)
message_box.setInformativeText(info_text)
diff --git a/guiscrcpy/network/network.py b/guiscrcpy/network/network.py
index f04e9032..b4a8b515 100644
--- a/guiscrcpy/network/network.py
+++ b/guiscrcpy/network/network.py
@@ -5,7 +5,6 @@
class NetworkManager:
-
@staticmethod
def get_my_ip():
"""
@@ -27,8 +26,8 @@ def map_network(self, pool_size=255):
ip_list = list()
# get my IP and compose a base like 192.168.1.xxx
- ip_parts = self.get_my_ip().split('.')
- base_ip = ip_parts[0] + '.' + ip_parts[1] + '.' + ip_parts[2] + '.'
+ ip_parts = self.get_my_ip().split(".")
+ base_ip = ip_parts[0] + "." + ip_parts[1] + "." + ip_parts[2] + "."
max_threads = 50
@@ -62,8 +61,8 @@ def check_adb_port(ip):
return ip_list
-if __name__ == '__main__':
- print('Mapping...')
+if __name__ == "__main__":
+ print("Mapping...")
nm = NetworkManager()
lst = nm.map_network()
print(lst)
diff --git a/guiscrcpy/platform/darwin.py b/guiscrcpy/platform/darwin.py
index e65b75a1..8f790046 100644
--- a/guiscrcpy/platform/darwin.py
+++ b/guiscrcpy/platform/darwin.py
@@ -24,8 +24,7 @@
class Darwin:
def __init__(self):
logging.error(
- "MacOS is untested. "
- "guiscrcpy is trying to use Linux config on Mac"
+ "MacOS is untested. " "guiscrcpy is trying to use Linux config on Mac"
)
def cfgpath(self):
@@ -33,33 +32,30 @@ def cfgpath(self):
@staticmethod
def make_config():
- if os.getenv('XDG_CONFIG_HOME') is None:
+ if os.getenv("XDG_CONFIG_HOME") is None:
path = os.path.expanduser("~/.config/guiscrcpy/")
else:
- path = os.getenv('XDG_CONFIG_HOME').split(":")[0] + "/guiscrcpy"
+ path = os.getenv("XDG_CONFIG_HOME").split(":")[0] + "/guiscrcpy"
if not os.path.exists(path):
try:
os.makedirs(path)
except Exception as e:
logging.error(
"Error creating configuration filename in dir {path}. "
- "Error code:{e}"
- .format(
- path=path,
- e=e
- ))
+ "Error code:{e}".format(path=path, e=e)
+ )
return path
@staticmethod
def system():
- return 'Darwin'
+ return "Darwin"
def increment(self):
pass
@staticmethod
def paths():
- return ['bin', '/usr/bin', '~/.local/bin', '~/bin', '/usr/local/bin']
+ return ["bin", "/usr/bin", "~/.local/bin", "~/bin", "/usr/local/bin"]
@staticmethod
def install_fonts():
diff --git a/guiscrcpy/platform/linux.py b/guiscrcpy/platform/linux.py
index d29758b8..6495dfc4 100644
--- a/guiscrcpy/platform/linux.py
+++ b/guiscrcpy/platform/linux.py
@@ -22,8 +22,7 @@
from guiscrcpy.version import VERSION
-desktop = \
- """
+desktop = """
[Desktop Entry]
Version={v}
Name=guiscrcpy
@@ -46,26 +45,22 @@ def cfgpath(self):
@staticmethod
def make_config():
- if os.getenv('XDG_CONFIG_HOME') is None:
+ if os.getenv("XDG_CONFIG_HOME") is None:
path = os.path.expanduser("~/.config/guiscrcpy/")
else:
- path = os.getenv('XDG_CONFIG_HOME').split(":")[0] + "/guiscrcpy"
+ path = os.getenv("XDG_CONFIG_HOME").split(":")[0] + "/guiscrcpy"
if not os.path.exists(path):
try:
os.makedirs(path)
except Exception as e:
logging.error(
"Error creating configuration filename in dir {path}. "
- "Error code:{e}"
- .format(
- path=path,
- e=e
- ))
+ "Error code:{e}".format(path=path, e=e)
+ )
return path
@staticmethod
- def create_desktop(desktop_file=None,
- desktop_file_name='guiscrcpy.desktop'):
+ def create_desktop(desktop_file=None, desktop_file_name="guiscrcpy.desktop"):
"""
Create Desktop filename for Linux in ~/.local level
:return:
@@ -76,26 +71,23 @@ def create_desktop(desktop_file=None,
desk = desktop.format(
v=VERSION,
icon_path=os.path.join(
- os.path.abspath(
- os.path.dirname(
- os.path.dirname(__file__)
- )
- ),
- 'ui', 'ui', 'guiscrcpy_logo.png'
- )
+ os.path.abspath(os.path.dirname(os.path.dirname(__file__))),
+ "ui",
+ "ui",
+ "guiscrcpy_logo.png",
+ ),
)
- if os.getenv('XDG_DESKTOP_DIR'):
- desktop_dir = os.getenv('XDG_DESKTOP_DIR')
+ if os.getenv("XDG_DESKTOP_DIR"):
+ desktop_dir = os.getenv("XDG_DESKTOP_DIR")
else:
- if os.path.exists(os.path.expanduser('~/Desktop')):
- desktop_dir = os.path.expanduser('~/Desktop')
- elif os.path.exists(os.path.expanduser('~/desktop')):
- desktop_dir = os.path.expanduser('~/desktop')
+ if os.path.exists(os.path.expanduser("~/Desktop")):
+ desktop_dir = os.path.expanduser("~/Desktop")
+ elif os.path.exists(os.path.expanduser("~/desktop")):
+ desktop_dir = os.path.expanduser("~/desktop")
else:
desktop_dir = False
if desktop_dir:
- with open(os.path.join(desktop_dir, desktop_file_name),
- 'w') as w:
+ with open(os.path.join(desktop_dir, desktop_file_name), "w") as w:
w.write(desk)
return desktop_dir
else:
@@ -108,11 +100,11 @@ def install_fonts():
@staticmethod
def system():
- return 'Linux'
+ return "Linux"
def increment(self):
pass
@staticmethod
def paths():
- return ['bin', '/usr/bin', '~/.local/bin', '~/bin', '/usr/local/bin']
+ return ["bin", "/usr/bin", "~/.local/bin", "~/bin", "/usr/local/bin"]
diff --git a/guiscrcpy/platform/platform.py b/guiscrcpy/platform/platform.py
index d57d8053..d0ec972d 100644
--- a/guiscrcpy/platform/platform.py
+++ b/guiscrcpy/platform/platform.py
@@ -20,7 +20,7 @@
import logging
import platform
-if platform.system() == 'Windows':
+if platform.system() == "Windows":
# Windows OS > 7
from guiscrcpy.platform.windows import Windows as System
elif platform.system() == "Linux":
diff --git a/guiscrcpy/platform/windows.py b/guiscrcpy/platform/windows.py
index f25d465d..5f1a45cc 100644
--- a/guiscrcpy/platform/windows.py
+++ b/guiscrcpy/platform/windows.py
@@ -29,8 +29,7 @@ def __init__(self):
@staticmethod
def make_config():
- path = os.path.expanduser(os.path.join(
- "~", "AppData", "Local", "guiscrcpy"))
+ path = os.path.expanduser(os.path.join("~", "AppData", "Local", "guiscrcpy"))
if not os.path.exists(path):
try:
os.makedirs(path)
@@ -43,7 +42,7 @@ def make_config():
@staticmethod
def system():
- return 'Windows'
+ return "Windows"
def cfgpath(self):
return self.make_config()
@@ -53,7 +52,7 @@ def increment(self):
@staticmethod
def paths():
- return ['bin']
+ return ["bin"]
@staticmethod
def install_fonts():
diff --git a/guiscrcpy/platform/windows_tools/tools.py b/guiscrcpy/platform/windows_tools/tools.py
index 3777c406..491f45c8 100644
--- a/guiscrcpy/platform/windows_tools/tools.py
+++ b/guiscrcpy/platform/windows_tools/tools.py
@@ -12,15 +12,15 @@
UserFolders = namedtuple("UserFolders", ("home", "desktop", "startmenu"))
-scut_ext = 'lnk'
-ico_ext = 'ico'
+scut_ext = "lnk"
+ico_ext = "ico"
def get_conda_active_env():
- '''Return name of active conda environment or empty string'''
+ """Return name of active conda environment or empty string"""
conda_env = None
try:
- conda_env = os.environ['CONDA_DEFAULT_ENV']
+ conda_env = os.environ["CONDA_DEFAULT_ENV"]
except KeyError:
print("No conda env active, defaulting to base")
conda_env = ""
@@ -36,7 +36,9 @@ def get_conda_active_env():
echo # run in conda environment "%CONDA_DEFAULT_ENV%":
echo # %*
%*
-""".format(conda_env)
+""".format(
+ conda_env
+)
_WSHELL = win32com.client.Dispatch("Wscript.Shell")
@@ -44,23 +46,24 @@ def get_conda_active_env():
# Windows Special Folders
# see: https://docs.microsoft.com/en-us/windows/win32/shell/csidl
+
def get_homedir():
- '''Return home directory:
+ """Return home directory:
note that we return CSIDL_PROFILE, not
CSIDL_APPDATA, CSIDL_LOCAL_APPDATA, or CSIDL_COMMON_APPDATA
- '''
+ """
return shell.SHGetFolderPath(0, shellcon.CSIDL_PROFILE, None, 0)
def get_desktop():
- '''Return user Desktop folder'''
+ """Return user Desktop folder"""
return shell.SHGetFolderPath(0, shellcon.CSIDL_DESKTOP, None, 0)
def get_startmenu():
- '''Return user Start Menu Programs folder
+ """Return user Start Menu Programs folder
note that we return CSIDL_PROGRAMS not CSIDL_COMMON_PROGRAMS
- '''
+ """
return shell.SHGetFolderPath(0, shellcon.CSIDL_PROGRAMS, None, 0)
@@ -79,22 +82,28 @@ def make_shortcut():
userfolders = get_folders()
desktop, startmenu = True, True
- for (create, folder) in ((desktop, userfolders.desktop),
- (startmenu, userfolders.startmenu)):
+ for (create, folder) in (
+ (desktop, userfolders.desktop),
+ (startmenu, userfolders.startmenu),
+ ):
if not os.path.exists(folder):
os.makedirs(folder)
dest = os.path.join(folder, "guiscrcpy.lnk")
wscript = _WSHELL.CreateShortCut(dest)
- wscript.Targetpath = 'guiscrcpy.exe'
+ wscript.Targetpath = "guiscrcpy.exe"
wscript.WorkingDirectory = userfolders.home
wscript.WindowStyle = 0
wscript.Description = "An Open Source Android Screen Mirroring System"
- wscript.IconLocation = os.path.join(os.path.abspath(
- os.path.dirname(os.path.dirname(os.path.dirname(__file__)))),
- 'ui', 'icons',
- 'guiscrcpy_logo_SRj_icon.ico')
+ wscript.IconLocation = os.path.join(
+ os.path.abspath(
+ os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
+ ),
+ "ui",
+ "icons",
+ "guiscrcpy_logo_SRj_icon.ico",
+ )
wscript.save()
return True
diff --git a/guiscrcpy/theme/decorate.py b/guiscrcpy/theme/decorate.py
index f093bc45..ee19411e 100644
--- a/guiscrcpy/theme/decorate.py
+++ b/guiscrcpy/theme/decorate.py
@@ -23,21 +23,22 @@
class Header:
def __init__(self, commit):
print(
- ColorTerms.UNDERLINE +
- " " +
- ColorTerms.ENDC)
+ ColorTerms.UNDERLINE
+ + " "
+ + ColorTerms.ENDC
+ )
print()
print("guiscrcpy")
print("by srevinsaju")
print(ColorTerms.OKBLUE + commit + ColorTerms.ENDC)
print(
- ColorTerms.OKBLUE +
- "Licensed under GNU GPL v3 (c) 2020 " +
- ColorTerms.ENDC)
+ ColorTerms.OKBLUE + "Licensed under GNU GPL v3 (c) 2020 " + ColorTerms.ENDC
+ )
print(
- ColorTerms.UNDERLINE +
- " " +
- ColorTerms.ENDC)
+ ColorTerms.UNDERLINE
+ + " "
+ + ColorTerms.ENDC
+ )
print(ColorTerms.OKBLUE + "" + ColorTerms.ENDC)
print()
diff --git a/guiscrcpy/theme/desktop_shortcut.py b/guiscrcpy/theme/desktop_shortcut.py
index 2fd474c6..721c3a42 100644
--- a/guiscrcpy/theme/desktop_shortcut.py
+++ b/guiscrcpy/theme/desktop_shortcut.py
@@ -1,7 +1,7 @@
#!/usr/bin/env/python\n
# flake8: noqa
def desktop_device_shortcut_svg():
- a="""
+ a = """
30:
- self.nm_det.setText('Device connect failed: Timeout')
+ self.nm_det.setText("Device connect failed: Timeout")
else:
time.sleep(1)
- self.nm_det.setText(
- "Connected to IP:{}:{}".format(ip, self.spinBox.value()))
+ self.nm_det.setText("Connected to IP:{}:{}".format(ip, self.spinBox.value()))
def refresh(self):
self.listView.clear()
diff --git a/guiscrcpy/ux/panel.py b/guiscrcpy/ux/panel.py
index c14e93c9..0d8a90f3 100644
--- a/guiscrcpy/ux/panel.py
+++ b/guiscrcpy/ux/panel.py
@@ -28,8 +28,7 @@
class Panel(QMainWindow, Ui_HorizontalPanel):
# there was a Dialog in the bracket
- def __init__(self, parent=None, ux_mapper=None, frame=False,
- always_on_top=True):
+ def __init__(self, parent=None, ux_mapper=None, frame=False, always_on_top=True):
"""
The bottom panel subwindow class for guiscrcpy
:param parent: The caller of the function
@@ -91,14 +90,18 @@ def quit_window(self):
# This method checks if we are the last member of the windows
# spawned and we ourselves are not a member of ourself by
# checking the uuid generated on creation
- if not instance.isHidden() \
- and instance.name != "swipe" and instance.uid != \
- self.uid:
+ if (
+ not instance.isHidden()
+ and instance.name != "swipe"
+ and instance.uid != self.uid
+ ):
self.hide()
break
else:
for instance in self.parent.child_windows: # noqa
- if instance.name == 'swipe' and instance.ux.get_sha() == \
- self.ux.get_sha():
+ if (
+ instance.name == "swipe"
+ and instance.ux.get_sha() == self.ux.get_sha()
+ ):
instance.hide()
self.hide()
diff --git a/guiscrcpy/ux/settings.py b/guiscrcpy/ux/settings.py
index 1cb470d7..d9f39618 100644
--- a/guiscrcpy/ux/settings.py
+++ b/guiscrcpy/ux/settings.py
@@ -32,18 +32,17 @@ def __init__(self, parent):
self.parent = parent
self.commands = []
self.checkboxes = {
- self.a1: [[None], '--always-on-top', 0],
- self.a2: [[self.a2c1, self.a2c2], '--crop {}:{}', 1],
- self.a3: [[self.a3e1], '--max-fps {}'],
- self.a4: [[None], '--prefer-text'],
- self.a5: [[self.a5c1], '--push-target {}'],
- self.a6: [[self.a6c1], '--record {}'],
- self.a8: [[self.a8c1], '--serial {}'],
- self.a9: [[None], '--window-borderless'],
- self.b0: [[self.b0c1], '--window-title {}'],
- self.b1: [[self.b1c1, self.b1c1], '--window-x {} --window-y {}'],
- self.b2: [[self.b2c1, self.b2c2],
- '--window-width {} --window-height {}'],
+ self.a1: [[None], "--always-on-top", 0],
+ self.a2: [[self.a2c1, self.a2c2], "--crop {}:{}", 1],
+ self.a3: [[self.a3e1], "--max-fps {}"],
+ self.a4: [[None], "--prefer-text"],
+ self.a5: [[self.a5c1], "--push-target {}"],
+ self.a6: [[self.a6c1], "--record {}"],
+ self.a8: [[self.a8c1], "--serial {}"],
+ self.a9: [[None], "--window-borderless"],
+ self.b0: [[self.b0c1], "--window-title {}"],
+ self.b1: [[self.b1c1, self.b1c1], "--window-x {} --window-y {}"],
+ self.b2: [[self.b2c1, self.b2c2], "--window-width {} --window-height {}"],
}
def init(self):
@@ -54,9 +53,9 @@ def init(self):
def file_chooser(self):
dialog = QFileDialog()
dialog.setFilter(dialog.filter() | QtCore.QDir.Hidden)
- dialog.setDefaultSuffix('mp4')
+ dialog.setDefaultSuffix("mp4")
dialog.setAcceptMode(QFileDialog.AcceptSave)
- dialog.setNameFilters(['H.264 (*.mp4)', 'MKV (*.mkv)'])
+ dialog.setNameFilters(["H.264 (*.mp4)", "MKV (*.mkv)"])
if dialog.exec_() == QDialog.Accepted:
self.a6c1.setText(dialog.selectedFiles()[0])
diff --git a/guiscrcpy/ux/swipe.py b/guiscrcpy/ux/swipe.py
index 0f1ccb88..92ac8cf1 100644
--- a/guiscrcpy/ux/swipe.py
+++ b/guiscrcpy/ux/swipe.py
@@ -106,8 +106,7 @@ def __init__(self, ux_wrapper=None, frame=False, always_on_top=True):
self.lol.setText("")
self.lol.setObjectName("lol")
self.lol.setStyleSheet(
- f"background-color: #{hexdigest};"
- f"border-radius: 12px; "
+ f"background-color: #{hexdigest};" f"border-radius: 12px; "
)
self.swirt = QtWidgets.QPushButton(self.centralwidget)
self.swirt.setGeometry(QtCore.QRect(40, 20, 30, 30))
diff --git a/guiscrcpy/ux/toolkit.py b/guiscrcpy/ux/toolkit.py
index 24b3ffcd..c352c5db 100644
--- a/guiscrcpy/ux/toolkit.py
+++ b/guiscrcpy/ux/toolkit.py
@@ -29,11 +29,7 @@
class InterfaceToolkit(QMainWindow, Ui_ToolbarPanel):
- def __init__(self,
- ux_mapper=None,
- parent=None,
- frame=False,
- always_on_top=True):
+ def __init__(self, ux_mapper=None, parent=None, frame=False, always_on_top=True):
"""
Side panel toolkit for guiscrcpy main window
:param ux_mapper:
@@ -61,9 +57,11 @@ def __init__(self,
self.ux = UXMapper()
def init(self):
- if platform.system() != "Linux" or (shutil.which("wmctrl")
- and shutil.which("xdotool")
- and platform.system() == "Linux"):
+ if platform.system() != "Linux" or (
+ shutil.which("wmctrl")
+ and shutil.which("xdotool")
+ and platform.system() == "Linux"
+ ):
self.clipD2PC.clicked.connect(self.ux.copy_devpc)
self.clipPC2D.clicked.connect(self.ux.copy_pc2dev)
self.fullscreenUI.clicked.connect(self.ux.fullscreen)
@@ -79,7 +77,8 @@ def init(self):
'This function is disabled because "wmctrl"'
' or "xdotool" is not found on your installation.'
"Keyboard shortcut can be used instead"
- ": {} ".format(helper))
+ ": {} ".format(helper)
+ )
self.back.clicked.connect(self.ux.key_back)
self.screenfreeze.clicked.connect(self.quit_window)
@@ -119,13 +118,18 @@ def quit_window(self):
# This method checks if we are the last member of the windows
# spawned and we ourselves are not a member of ourself by
# checking the uuid generated on creation
- if (not instance.isHidden() and instance.name != "swipe"
- and instance.uid != self.uid):
+ if (
+ not instance.isHidden()
+ and instance.name != "swipe"
+ and instance.uid != self.uid
+ ):
self.hide()
break
else:
for instance in self.parent.child_windows: # noqa
- if (instance.name == "swipe"
- and instance.ux.get_sha() == self.ux.get_sha()):
+ if (
+ instance.name == "swipe"
+ and instance.ux.get_sha() == self.ux.get_sha()
+ ):
instance.hide()
self.hide()