Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,246 changes: 659 additions & 587 deletions poetry.lock

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ numpy = "^2"
adbutils = "^2"

# Optional dependencies for ui
PySide6 = { version = "^6.0.0", optional = true }
PySide6 = { version = "^6.8.1", optional = true }
packaging = "^24.2"

[tool.poetry.extras]
ui = ["PySide6"]
Expand Down
6 changes: 6 additions & 0 deletions scrcpy/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,12 @@
TYPE_SET_CLIPBOARD = 9
TYPE_SET_SCREEN_POWER_MODE = 10
TYPE_ROTATE_DEVICE = 11
TYPE_UHID_CREATE = 12
TYPE_UHID_INPUT = 13
TYPE_UHID_DESTROY = 14
TYPE_OPEN_HARD_KEYBOARD_SETTINGS = 15
TYPE_START_APP = 16
TYPE_RESET_VIDEO = 17

# Lock screen orientation
LOCK_SCREEN_ORIENTATION_UNLOCKED = -1
Expand Down
8 changes: 8 additions & 0 deletions scrcpy/control.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,14 @@ def collapse_panels(self) -> bytes:
"""
return b""

@inject(const.TYPE_START_APP)
def start_app(self, package_name: str) -> bytes:
"""
Start app
"""
utf8_package_name = package_name.encode("utf-8")
return struct.pack(">b", len(utf8_package_name)) + utf8_package_name

def get_clipboard(self) -> str:
"""
Get clipboard
Expand Down
36 changes: 34 additions & 2 deletions scrcpy/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ def __init__(
connection_timeout: int = 3000,
encoder_name: Optional[str] = None,
codec_name: Optional[str] = None,
new_display: Union[str, bool] = False,
start_app: Optional[str] = None,
):
"""
Create a scrcpy client, this client won't be started until you call the start function
Expand Down Expand Up @@ -70,6 +72,27 @@ def __init__(
]
assert codec_name in [None, "h264", "h265", "av1"]

# Check new_display format
if new_display:
if new_display == True:
# Use default value, no need to check
new_display = ""
else:
# Check format [<width>x<height>][/<dpi>]
parts = new_display.split("/")
if len(parts) > 2:
raise ValueError("Invalid new_display format. Expected format: [<width>x<height>][/<dpi>]")

# Check resolution part
if parts[0]:
width, height = map(int, parts[0].split("x"))
assert width > 0 and height > 0, "Invalid resolution format. Expected format: <width>x<height>"

# Check DPI part
if len(parts) == 2:
dpi = int(parts[1])
assert dpi > 0, "DPI must be a positive integer"

# Params
self.flip = flip
self.max_width = max_width
Expand All @@ -81,6 +104,9 @@ def __init__(
self.connection_timeout = connection_timeout
self.encoder_name = encoder_name
self.codec_name = codec_name
self.new_display = new_display
self.start_app = start_app


# Connect to device
if device is None:
Expand Down Expand Up @@ -143,7 +169,7 @@ def __deploy_server(self) -> None:
"""
Deploy server to android device
"""
jar_name = "scrcpy-server.jar"
jar_name = "scrcpy-server-v3.1"
server_file_path = os.path.join(
os.path.abspath(os.path.dirname(__file__)), jar_name
)
Expand All @@ -153,7 +179,7 @@ def __deploy_server(self) -> None:
"app_process",
"/",
"com.genymobile.scrcpy.Server",
"2.4", # Scrcpy server version
"3.1", # Scrcpy server version
"log_level=info",
f"max_size={self.max_width}",
f"max_fps={self.max_fps}",
Expand All @@ -171,6 +197,8 @@ def __deploy_server(self) -> None:
"power_off_on_close=false",
"clipboard_autosync=false",
]
if self.new_display or self.new_display == '':
commands.append(f"new_display={self.new_display}")

self.__server_stream: AdbConnection = self.device.shell(
commands,
Expand All @@ -195,6 +223,9 @@ def start(self, threaded: bool = False, daemon_threaded: bool = False) -> None:
self.alive = True
self.__send_to_listeners(EVENT_INIT)

if self.start_app:
self.control.start_app(self.start_app)

if threaded or daemon_threaded:
self.stream_loop_thread = threading.Thread(
target=self.__stream_loop, daemon=daemon_threaded
Expand Down Expand Up @@ -243,6 +274,7 @@ def __stream_loop(self) -> None:
frame = frame.to_ndarray(format="bgr24")
if self.flip:
frame = frame[:, ::-1, :]
frame = np.ascontiguousarray(frame)
self.last_frame = frame
self.resolution = (frame.shape[1], frame.shape[0])
self.__send_to_listeners(EVENT_FRAME, frame)
Expand Down
Binary file added scrcpy/scrcpy-server-v3.1
Binary file not shown.
29 changes: 26 additions & 3 deletions scrcpy_ui/main.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from argparse import ArgumentParser
from typing import Optional
from typing import Optional, Union

from adbutils import adb
from PySide6.QtGui import QImage, QKeyEvent, QMouseEvent, QPixmap, Qt
Expand All @@ -20,6 +20,8 @@ def __init__(
max_width: Optional[int],
serial: Optional[str] = None,
encoder_name: Optional[str] = None,
new_display: Union[str, bool] = False,
start_app: Optional[str] = None,
):
super(MainWindow, self).__init__()
self.ui = Ui_MainWindow()
Expand All @@ -40,6 +42,8 @@ def __init__(
bitrate=1000000000,
encoder_name=encoder_name,
max_fps=60,
new_display=new_display,
start_app=start_app,
)
self.client.add_listener(scrcpy.EVENT_INIT, self.on_init)
self.client.add_listener(scrcpy.EVENT_FRAME, self.on_frame)
Expand Down Expand Up @@ -183,10 +187,29 @@ def main():
type=str,
help="Select device manually (device serial required)",
)
parser.add_argument("--encoder_name", type=str, help="Encoder name to use")
parser.add_argument("--encoder-name", type=str, help="Encoder name to use")
parser.add_argument(
"--new-display",
type=str,
# default="",
metavar="[<width>x<height>][/<dpi>]",
help="Create a new display with the specified resolution and density. If not provided, they default to the main display dimensions and DPI.\n"
"Examples:\n"
" --new-display=1920x1080 # create display with 1920x1080 resolution\n"
" --new-display=1920x1080/420 # create display with 1920x1080 resolution and 420 DPI\n"
" --new-display # use main display size and density\n"
" --new-display=/240 # use main display size with 240 DPI",
nargs="?",
const="",
)
parser.add_argument(
"--start-app",
type=str,
help="Start app package name",
)
args = parser.parse_args()

m = MainWindow(args.max_width, args.device, args.encoder_name)
m = MainWindow(args.max_width, args.device, args.encoder_name, args.new_display, args.start_app)
m.show()

m.client.start()
Expand Down