Skip to content
Merged
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
15 changes: 9 additions & 6 deletions .github/workflows/lint-and-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,12 @@ concurrency:

jobs:
ruff:
runs-on: windows-latest
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
# Ruff is version and platform sensible
matrix:
os: [windows-latest, ubuntu-22.04]
python-version: ["3.10", "3.11", "3.12"]
steps:
- name: Checkout ${{ github.repository }}/${{ github.ref }}
Expand All @@ -59,11 +60,12 @@ jobs:
shell: pwsh
- run: ruff check .
Pyright:
runs-on: windows-latest
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
# Pyright is version and platform sensible
matrix:
os: [windows-latest, ubuntu-22.04]
python-version: ["3.10", "3.11", "3.12"]
steps:
- name: Checkout ${{ github.repository }}/${{ github.ref }}
Expand All @@ -82,12 +84,13 @@ jobs:
working-directory: src/
python-version: ${{ matrix.python-version }}
Build:
runs-on: windows-latest
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
# Only the Python version we plan on shipping matters.
matrix:
python-version: ["3.11", "3.12"]
os: [windows-latest, ubuntu-22.04]
python-version: ["3.11"]
steps:
- name: Checkout ${{ github.repository }}/${{ github.ref }}
uses: actions/checkout@v3
Expand All @@ -104,13 +107,13 @@ jobs:
- name: Upload Build Artifact
uses: actions/upload-artifact@v3
with:
name: AutoSplit (Python ${{ matrix.python-version }})
name: AutoSplit for ${{ matrix.os }} (Python ${{ matrix.python-version }})
path: dist/AutoSplit*
if-no-files-found: error
- name: Upload Build logs
uses: actions/upload-artifact@v3
with:
name: Build logs (Python ${{ matrix.python-version }})
name: Build logs for ${{ matrix.os }} (Python ${{ matrix.python-version }})
path: |
build/AutoSplit/*.toc
build/AutoSplit/*.txt
Expand Down
36 changes: 36 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,30 @@ This program can be used to automatically start, split, and reset your preferred
- You can also check out the [latest dev builds](/../../actions/workflows/lint-and-build.yml?query=event%3Apush+is%3Asuccess) (requires a GitHub account)
(If you don't have a GitHub account, you can try [nightly.link](https://nightly.link/Toufool/AutoSplit/workflows/lint-and-build/dev))

- Linux users must ensure they are in the `tty` and `input` groups and have write access to `/dev/uinput`. You can run the following commands to do so:

<!-- https://github.com/boppreh/keyboard/issues/312#issuecomment-1189734564 -->
<!-- Keep in sync with scripts/install.ps1 and src/error_messages.py -->
```shell
sudo usermod -a -G tty,input $USER
sudo touch /dev/uinput
sudo chmod +0666 /dev/uinput
echo 'KERNEL=="uinput", TAG+="uaccess""' | sudo tee /etc/udev/rules.d/50-uinput.rules
echo 'SUBSYSTEM=="input", MODE="0666" GROUP="plugdev"' | sudo tee /etc/udev/rules.d/12-input.rules
echo 'SUBSYSTEM=="misc", MODE="0666" GROUP="plugdev"' | sudo tee -a /etc/udev/rules.d/12-input.rules
echo 'SUBSYSTEM=="tty", MODE="0666" GROUP="plugdev"' | sudo tee -a /etc/udev/rules.d/12-input.rules
loginctl terminate-user $USER
```

<!-- Keep in sync with src/error_messages.py -->
All screen capture method are incompatible with Wayland. Follow [this guide](https://linuxconfig.org/how-to-enable-disable-wayland-on-ubuntu-22-04-desktop) to disable it.

### Compatibility

- Windows 10 and 11.
- Linux (Only tested on Ubuntu 22.04)
- Wayland is not currently supported
- WSL2/WSLg requires an additional Desktop Environment, external X11 server, and/or systemd
- Python 3.10+ (Not required for normal use. Refer to the [build instructions](/docs/build%20instructions.md) if you'd like run the application directly in Python).

## OPTIONS
Expand Down Expand Up @@ -70,6 +91,8 @@ This program can be used to automatically start, split, and reset your preferred
#### Capture Method
<!-- Keep all descriptions in sync with in-code descriptions in src/capture_method/*CaptureMethod.py-->

##### Windows

- **Windows Graphics Capture** (fast, most compatible, capped at 60fps)
Only available in Windows 10.0.17134 and up.
Due to current technical limitations, Windows versions below 10.0.0.17763 require having at least one audio or video Capture Device connected and enabled.
Expand All @@ -88,6 +111,19 @@ This program can be used to automatically start, split, and reset your preferred
- **Force Full Content Rendering** (very slow, can affect rendering)
Uses BitBlt behind the scene, but passes a special flag to PrintWindow to force rendering the entire desktop.
About 10-15x slower than BitBlt based on original window size and can mess up some applications' rendering pipelines.

##### Linux

- **X11 XCB** (fast, requires XCB)
Uses the XCB library to take screenshots of the X11 server.
- **Scrot** (very slow, may leave files)
Uses Scrot (SCReenshOT) to take screenshots.
Leaves behind a screenshot file if interrupted.
<!-- Keep in sync with src/menu_bar.py -->
"scrot" must be installed: `sudo apt-get install scrot`

##### All platforms

- **Video Capture Device**
Uses a Video Capture Device, like a webcam, virtual cam, or capture card.

Expand Down
5 changes: 5 additions & 0 deletions docs/build instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@

- Microsoft Visual C++ 14.0 or greater may be required to build the executable. Get it with [Microsoft C++ Build Tools](https://visualstudio.microsoft.com/visual-cpp-build-tools/).

### Linux

- You need to be part of the `input` and `tty` groups, as well as have permissions on a few files and folders.
If you are missing from either groups, the install script will take care of it on its first run, but you'll need to restart your session.

### All platforms

- [Python](https://www.python.org/downloads/) 3.10+.
Expand Down
27 changes: 23 additions & 4 deletions scripts/build.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,31 @@ $arguments = @(
# if requirements.txt was used directly to help ensure consistency when building locally.
#
# Installed by PyAutoGUI
'--exclude=pyscreeze',
'--exclude=pygetwindow',
'--exclude=pymsgbox',
'--exclude=pytweening',
'--exclude=mouseinfo',
# Used by D3DShot
'--exclude=PIL')
'--exclude=mouseinfo')
if ($IsWindows) {
$arguments += @(
# Installed by PyAutoGUI, but used by linux
'--exclude=pyscreeze'
# Used by D3DShot
'--exclude=PIL')
}
if ($IsLinux) {
$arguments += @(
# Required on the CI for PyWinCtl
'--hidden-import pynput.keyboard._xorg',
'--hidden-import pynput.mouse._xorg')
}

Start-Process -Wait -NoNewWindow pyinstaller -ArgumentList $arguments

If ($IsLinux) {
Move-Item -Force $PSScriptRoot/../dist/AutoSplit $PSScriptRoot/../dist/AutoSplit.elf
If ($?) {
Write-Host 'Added .elf extension'
}
chmod +x $PSScriptRoot/../dist/AutoSplit.elf
Write-Host 'Added execute permission'
}
9 changes: 6 additions & 3 deletions scripts/designer.ps1
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
$python = $IsLinux ? 'python3' : 'python'
$qt6_applications_import = 'import qt6_applications; print(qt6_applications.__path__[0])'
$qt6_applications_path = python -c $qt6_applications_import

$qt6_applications_path = &"$python" -c $qt6_applications_import
if ($null -eq $qt6_applications_path) {
Write-Host 'Designer not found, installing qt6_applications'
python -m pip install qt6_applications
&"$python" -m pip install qt6_applications
}
$qt6_applications_path = python -c $qt6_applications_import

$qt6_applications_path = &"$python" -c $qt6_applications_import
& "$qt6_applications_path/Qt/bin/designer" `
"$PSScriptRoot/../res/design.ui" `
"$PSScriptRoot/../res/about.ui" `
Expand Down
67 changes: 57 additions & 10 deletions scripts/install.ps1
Original file line number Diff line number Diff line change
@@ -1,7 +1,45 @@
$python = $IsLinux ? 'python3' : 'python'

# Validating user groups on Linux
If ($IsLinux) {
$groups = groups
if ($groups.Contains('input') -and $groups.Contains('tty')) {
Write-Host "User $Env:USER is already part of groups input and tty. No actions taken."
}
Else {
# https://github.com/boppreh/keyboard/issues/312#issuecomment-1189734564
Write-Host "User $Env:USER isn't part of groups input and tty. It is required to install the keyboard module."
# Keep in sync with README.md and src/error_messages.py
sudo usermod -a -G 'tty,input' $Env:USER
sudo touch /dev/uinput
sudo chmod +0666 /dev/uinput
If (-not $Env:GITHUB_JOB) {
Write-Output 'KERNEL=="uinput", TAG+="uaccess""' | sudo tee /etc/udev/rules.d/50-uinput.rules
Write-Output 'SUBSYSTEM=="input", MODE="0666" GROUP="plugdev"' | sudo tee /etc/udev/rules.d/12-input.rules
Write-Output 'SUBSYSTEM=="misc", MODE="0666" GROUP="plugdev"' | sudo tee -a /etc/udev/rules.d/12-input.rules
Write-Output 'SUBSYSTEM=="tty", MODE="0666" GROUP="plugdev"' | sudo tee -a /etc/udev/rules.d/12-input.rules
}
Write-Host 'You have been added automatically,' `
"but still need to manually terminate your session with 'loginctl terminate-user $Env:USER'" `
'for the changes to take effect outside of this script.'
If (-not $Env:GITHUB_JOB) {
Write-Host -NoNewline 'Press any key to continue...';
$null = $Host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown');
}
}
}

# Installing Python dependencies
$dev = If ($Env:GITHUB_JOB -eq 'Build') { '' } Else { '-dev' }
If ($IsLinux) {
If (-not $Env:GITHUB_JOB -or $Env:GITHUB_JOB -eq 'Build') {
sudo apt-get update
# python3-tk for splash screen, npm for pyright, the rest for PySide6
sudo apt-get install -y python3-pip python3-tk npm libegl1 libxkbcommon0
}
}
# Ensures installation tools are up to date. This also aliases pip to pip3 on MacOS.
python -m pip install wheel pip setuptools --upgrade
&"$python" -m pip install wheel pip setuptools --upgrade
pip install -r "$PSScriptRoot/requirements$dev.txt" --upgrade
# These libraries install extra requirements we don't want
# Open suggestion for support in requirements files: https://github.com/pypa/pip/issues/9948 & https://github.com/pypa/pip/pull/10837
Expand All @@ -15,21 +53,30 @@ pip install PyAutoGUI "D3DShot>=0.1.5 ; sys_platform == 'win32'" --no-deps --upg
# Prevent PyAutoGUI and pywinctl from setting Process DPI Awareness, which Qt tries to do then throws warnings about it.
# The unittest workaround significantly increases build time, boot time and build size with PyInstaller.
# https://github.com/asweigart/pyautogui/issues/663#issuecomment-1296719464
$libPath = python -c 'import pyautogui as _; print(_.__path__[0])'
$libPath = &"$python" -c 'import pyautogui as _; print(_.__path__[0])'
(Get-Content "$libPath/_pyautogui_win.py").replace('ctypes.windll.user32.SetProcessDPIAware()', 'pass') |
Set-Content "$libPath/_pyautogui_win.py"
$libPath = python -c 'import pymonctl as _; print(_.__path__[0])'
(Get-Content "$libPath/_pymonctl_win.py").replace('ctypes.windll.shcore.SetProcessDpiAwareness(2)', 'pass') |
Set-Content "$libPath/_pymonctl_win.py"
$libPath = python -c 'import pywinbox as _; print(_.__path__[0])'
(Get-Content "$libPath/_pywinbox_win.py").replace('ctypes.windll.shcore.SetProcessDpiAwareness(2)', 'pass') |
Set-Content "$libPath/_pywinbox_win.py"
If ($IsWindows) {
$libPath = &"$python" -c 'import pymonctl as _; print(_.__path__[0])'
(Get-Content "$libPath/_pymonctl_win.py").replace('ctypes.windll.shcore.SetProcessDpiAwareness(2)', 'pass') |
Set-Content "$libPath/_pymonctl_win.py"
$libPath = &"$python" -c 'import pywinbox as _; print(_.__path__[0])'
(Get-Content "$libPath/_pywinbox_win.py").replace('ctypes.windll.shcore.SetProcessDpiAwareness(2)', 'pass') |
Set-Content "$libPath/_pywinbox_win.py"
}
# Because Ubuntu 22.04 is forced to use an older version of PySide6, we do a dirty typing patch
# https://bugreports.qt.io/browse/QTBUG-114635
If ($IsLinux) {
$libPath = &"$python" -c 'import PySide6 as _; print(_.__path__[0])'
(Get-Content "$libPath/QtWidgets.pyi").replace('-> Tuple:', '-> Tuple[str, ...]:') |
Set-Content "$libPath/QtWidgets.pyi"
}
# Uninstall optional dependencies if PyAutoGUI or D3DShot was installed outside this script
# pyscreeze -> pyscreenshot -> mss deps call SetProcessDpiAwareness, used to be installed on Windows
# Pillow, pygetwindow, pymsgbox, pytweening, MouseInfo are picked up by PySide6
# (also --exclude from build script, but more consistent with unfrozen run)
python -m pip uninstall pyscreeze pyscreenshot mss Pillow pygetwindow pymsgbox pytweening MouseInfo -y

pip uninstall pyscreenshot mss pygetwindow pymsgbox pytweening MouseInfo -y
If ($IsWindows) { pip uninstall pyscreeze Pillow -y }

# Don't compile resources on the Build CI job as it'll do so in build script
If ($dev) {
Expand Down
28 changes: 28 additions & 0 deletions scripts/python_build_from_source_linux.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
cd ..

# Update package lists
sudo apt update

# Install dependent libraries:
sudo apt install build-essential zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libsqlite3-dev libreadline-dev libffi-dev curl libbz2-dev tk-dev

# Download Python binary package:
wget https://www.python.org/ftp/python/3.10.13/Python-3.10.13.tgz

# Unzip the package:
tar -xzf Python-3.10.13.tgz

# Execute configure script
cd Python-3.10.13
./configure --enable-optimizations --enable-shared

# Build Python 3.10
make -j 2

# Install Python 3.10
sudo make install

# Verify the installation
python3.10 -V

echo "If Python version did not print, you may need to stop active processes"
1 change: 1 addition & 0 deletions scripts/requirements-dev.txt
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,6 @@ types-keyboard
types-psutil
types-PyAutoGUI
types-pyinstaller
types-python-xlib ; sys_platform == 'linux'
types-pywin32>=306.0.0.8 ; sys_platform == 'win32'
types-toml
7 changes: 6 additions & 1 deletion scripts/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ psutil>=5.9.6 # Python 3.12 fixes
# PyAutoGUI # See install.ps1
PyWinCtl>=0.0.42 # py.typed
# When needed, dev builds can be found at https://download.qt.io/snapshots/ci/pyside/dev?C=M;O=D
PySide6-Essentials>=6.6.0 # Python 3.12 support
PySide6-Essentials>=6.6.0 ; sys_platform == 'win32' # Python 3.12 support
PySide6-Essentials<6.5.1 ; sys_platform == 'linux' # Wayland issue on Ubuntu 22.04 https://bugreports.qt.io/browse/QTBUG-114635
scipy>=1.11.2 # Python 3.12 support
toml
typing-extensions>=4.4.0 # @override decorator support
Expand All @@ -26,3 +27,7 @@ pygrabber>=0.2 ; sys_platform == 'win32' # Completed types
pywin32>=301 ; sys_platform == 'win32'
winsdk>=1.0.0b10 ; sys_platform == 'win32' # Python 3.12 support
# D3DShot # See install.ps1
#
# Linux-only dependencies
pyscreeze ; sys_platform == 'linux'
python-xlib ; sys_platform == 'linux'
3 changes: 2 additions & 1 deletion scripts/start.ps1
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
param ([string]$p1)
& "$PSScriptRoot/compile_resources.ps1"
python "$PSScriptRoot/../src/AutoSplit.py" $p1
$python = $IsLinux ? 'python3' : 'python'
&"$python" "$PSScriptRoot/../src/AutoSplit.py" $p1
17 changes: 13 additions & 4 deletions src/AutoSplit.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,14 @@
from PySide6.QtTest import QTest
from PySide6.QtWidgets import QApplication, QFileDialog, QLabel, QMainWindow, QMessageBox
from typing_extensions import override
from win32comext.shell import shell as shell32

import error_messages
import user_profile
from AutoControlledThread import AutoControlledThread
from AutoSplitImage import AutoSplitImage, ImageType
from capture_method import CaptureMethodBase, CaptureMethodEnum
from gen import about, design, settings, update_checker
from hotkeys import HOTKEYS, after_setting_hotkey, send_command
from hotkeys import HOTKEYS, KEYBOARD_GROUPS_ISSUE, KEYBOARD_UINPUT_ISSUE, after_setting_hotkey, send_command
from menu_bar import (
about_qt,
about_qt_for_python,
Expand All @@ -43,15 +42,18 @@
FROZEN,
ONE_SECOND,
QTIMER_FPS_LIMIT,
RUNNING_WAYLAND,
auto_split_directory,
decimal,
flatten,
is_valid_image,
open_file,
)

myappid = f"Toufool.AutoSplit.v{AUTOSPLIT_VERSION}"
shell32.SetCurrentProcessExplicitAppUserModelID(myappid)
if sys.platform == "win32":
from win32comext.shell import shell as shell32
myappid = f"Toufool.AutoSplit.v{AUTOSPLIT_VERSION}"
shell32.SetCurrentProcessExplicitAppUserModelID(myappid)


class AutoSplit(QMainWindow, design.Ui_MainWindow):
Expand Down Expand Up @@ -912,6 +914,7 @@ def seconds_remaining_text(seconds: float):
return f"{seconds:.1f} second{'' if 0 < seconds <= 1 else 's'} remaining"


# TODO: Add Linux support
def is_already_open():
# When running directly in Python, any AutoSplit process means it's already open
# When bundled, we must ignore itself and the splash screen
Expand All @@ -936,6 +939,12 @@ def main():

if is_already_open():
error_messages.already_open()
if KEYBOARD_GROUPS_ISSUE:
error_messages.linux_groups()
if KEYBOARD_UINPUT_ISSUE:
error_messages.linux_uinput()
if RUNNING_WAYLAND:
error_messages.linux_wayland()

AutoSplit()

Expand Down
4 changes: 4 additions & 0 deletions src/capture_method/BitBltCaptureMethod.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import sys

if sys.platform != "win32":
raise OSError
import ctypes

import numpy as np
Expand Down
Loading