1
0
Fork 0
mirror of https://github.com/kbumsik/VirtScreen.git synced 2025-02-12 11:21:53 +00:00

#7: Added CLI options

This commit is contained in:
Bumsik Kim 2018-06-08 18:00:15 -04:00
parent 439769ca21
commit 30ffe32f82
No known key found for this signature in database
GPG key ID: E31041C8EC5B01C6

View file

@ -1,10 +1,19 @@
#!/usr/bin/python3
# Python standard packages
import sys, os, subprocess, signal, re, atexit, time, json, shutil
import sys
import os
import subprocess
import signal
import re
import atexit
import time
import json
import shutil
import argparse
from pathlib import Path
from enum import Enum
from typing import List, Dict
from typing import List, Dict, Callable
# PyQt5 packages
from PyQt5.QtWidgets import QApplication
from PyQt5.QtCore import QObject, QUrl, Qt, pyqtProperty, pyqtSlot, pyqtSignal, Q_ENUMS
@ -356,13 +365,23 @@ class XRandR(SubprocessWrapper):
self._update_screens()
return self.virt
def create_virtual_screen(self, width, height, portrait=False, hidpi=False) -> None:
print("creating: ", self.virt)
def create_virtual_screen(self, width, height, portrait=False, hidpi=False, pos='') -> None:
self._update_screens()
print("creating: ", self.virt)
self._add_screen_mode(width, height, portrait, hidpi)
arg_pos = ['left', 'right', 'above', 'below']
xrandr_pos = ['--left-of', '--right-of', '--above', '--below']
if pos and pos in arg_pos:
# convert pos for xrandr
pos = xrandr_pos[arg_pos.index(pos)]
pos += ' ' + self.primary.name
elif not pos:
pos = '--preferred'
else:
raise RuntimeError("Incorrect position option selected.")
self.check_output(f"xrandr --output {self.virt.name} --mode {self.mode_name}")
self.check_output("sleep 5")
self.check_output(f"xrandr --output {self.virt.name} --preferred")
self.check_output(f"xrandr --output {self.virt.name} {pos}")
self._update_screens()
def delete_virtual_screen(self) -> None:
@ -530,11 +549,11 @@ class Backend(QObject):
# Qt Slots
@pyqtSlot(str, int, int, bool, bool)
def createVirtScreen(self, device, width, height, portrait, hidpi):
def createVirtScreen(self, device, width, height, portrait, hidpi, pos=''):
self.xrandr.virt_name = device
print("Creating a Virtual Screen...")
try:
self.xrandr.create_virtual_screen(width, height, portrait, hidpi)
self.xrandr.create_virtual_screen(width, height, portrait, hidpi, pos)
except subprocess.CalledProcessError as e:
self.onError.emit(str(e.cmd) + '\n' + e.stdout.decode('utf-8'))
return
@ -672,7 +691,7 @@ class Backend(QObject):
if force:
# Usually called from atexit().
self.vncServer.kill()
time.sleep(2) # Make sure X11VNC shutdown before execute next atexit().
time.sleep(3) # Make sure X11VNC shutdown before execute next atexit().
if self._vncState in (self.VNCState.WAITING, self.VNCState.CONNECTED):
self.vncServer.kill()
else:
@ -691,41 +710,83 @@ class Backend(QObject):
# -------------------------------------------------------------------------------
# Main Code
# -------------------------------------------------------------------------------
def main():
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
app = QApplication(sys.argv)
from PyQt5.QtWidgets import QSystemTrayIcon, QMessageBox
if not QSystemTrayIcon.isSystemTrayAvailable():
QMessageBox.critical(None, "VirtScreen",
"Cannot detect system tray on this system.")
def main() -> None:
parser = argparse.ArgumentParser(
formatter_class=argparse.RawTextHelpFormatter,
description='Make your iPad/tablet/computer as a secondary monitor on Linux.\n\n'
'You can start VirtScreen in the following two modes:\n\n'
' - GUI mode: A system tray icon will appear when no argument passed.\n'
' You need to use this first to configure a virtual screen.\n'
' - CLI mode: After configured the virtual screen, you can start VirtScreen\n'
' in CLI mode if you do not want a GUI, by passing any arguments\n',
epilog='example:\n'
'virtscreen # GUI mode. You need to use this first\n'
' to configure the screen\n'
'virtscreen --auto # CLI mode. Scrren will be created using previous\n'
' settings (from both GUI mode and CLI mode)\n'
'virtscreen --left # CLI mode. On the left to the primary monitor\n'
'virtscreen --below # CLI mode. Below the primary monitor.\n'
'virtscreen --below --portrait # Below, and portrait mode.\n'
'virtscreen --below --portrait --hipdi # Below, portrait, HiDPI mode.\n')
parser.add_argument('--auto', action='store_true',
help='create a virtual screen automatically using previous\n'
'settings (from both GUI mode and CLI mode)')
parser.add_argument('--left', action='store_true',
help='a virtual screen will be created left to the primary\n'
'monitor')
parser.add_argument('--right', action='store_true',
help='right to the primary monitor')
parser.add_argument('--above', '--up', action='store_true',
help='above the primary monitor')
parser.add_argument('--below', '--down', action='store_true',
help='below the primary monitor')
parser.add_argument('--portrait', action='store_true',
help='Portrait mode. Width and height of the screen are swapped')
parser.add_argument('--hidpi', action='store_true',
help='HiDPI mode. Width and height are doubled')
args = parser.parse_args()
if any(vars(args).values()):
main_cli(args)
else:
main_gui()
print('Program should not reach here.')
sys.exit(1)
if os.environ['XDG_SESSION_TYPE'] == 'wayland':
QMessageBox.critical(None, "VirtScreen",
"Currently Wayland is not supported")
def check_env(msg: Callable[[str], None]) -> None:
if os.environ['XDG_SESSION_TYPE'].lower() == 'wayland':
msg("Currently Wayland is not supported")
sys.exit(1)
if not HOME_PATH:
QMessageBox.critical(None, "VirtScreen",
"Cannot detect home directory.")
msg("Cannot detect home directory.")
sys.exit(1)
if not os.path.exists(HOME_PATH):
try:
os.makedirs(HOME_PATH)
except:
QMessageBox.critical(None, "VirtScreen",
"Cannot create ~/.config/virtscreen")
msg("Cannot create ~/.config/virtscreen")
sys.exit(1)
if not shutil.which('x11vnc'):
QMessageBox.critical(None, "VirtScreen",
"x11vnc is not installed.")
msg("x11vnc is not installed.")
sys.exit(1)
try:
test = XRandR()
except RuntimeError as e:
QMessageBox.critical(None, "VirtScreen", str(e))
msg(str(e))
sys.exit(1)
def main_gui():
QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
app = QApplication(sys.argv)
# Check environment first
from PyQt5.QtWidgets import QMessageBox, QSystemTrayIcon
def dialog(message: str) -> None:
QMessageBox.critical(None, "VirtScreen", message)
if not QSystemTrayIcon.isSystemTrayAvailable():
dialog("Cannot detect system tray on this system.")
sys.exit(1)
check_env(dialog)
# Replace Twisted reactor with qt5reactor
import qt5reactor # pylint: disable=E0401
qt5reactor.install()
@ -744,10 +805,63 @@ def main():
engine = QQmlApplicationEngine()
engine.load(QUrl(MAIN_QML_PATH))
if not engine.rootObjects():
QMessageBox.critical(None, "VirtScreen", "Failed to load QML")
dialog("Failed to load QML")
sys.exit(1)
sys.exit(app.exec_())
reactor.run()
def main_cli(args: argparse.Namespace):
for key, value in vars(args).items():
print(key, ": ", value)
# Check the environment
check_env(print)
if not os.path.exists(CONFIG_PATH):
print("Configuration file does not exist.\n"
"Configure a virtual screen using GUI first.")
sys.exit(1)
# By instantiating the backend, additional verifications of config
# file will be done.
backend = Backend()
# Get settings
with open(CONFIG_PATH, 'r') as f:
config = json.load(f)
# Override settings from arguments
position = ''
if not args.auto:
args_virt = ['portrait', 'hidpi']
for prop in args_virt:
if vars(args)[prop]:
config['virt'][prop] = True
args_position = ['left', 'right', 'above', 'below']
tmp_args = {k: vars(args)[k] for k in args_position}
if not any(tmp_args.values()):
print("Choose a position relative to the primary monitor. (e.g. --left)")
sys.exit(1)
for key, value in tmp_args.items():
if value:
position = key
# Turn settings object into VNC arguments format
vnc_option = ''
for key, value in config['x11vncOptions'].items():
if value['available'] and value['enabled']:
vnc_option += key + ' '
if value['arg'] is not None:
vnc_option += str(value['arg']) + ' '
# Create virtscreen and Start VNC
def handle_error(msg):
print('Error: ', msg)
sys.exit(1)
backend.onError.connect(handle_error)
backend.createVirtScreen(config['virt']['device'], config['virt']['width'],
config['virt']['height'], config['virt']['portrait'],
config['virt']['hidpi'], position)
def handle_vnc_changed(state):
if state is backend.VNCState.OFF:
sys.exit(0)
backend.onVncStateChanged.connect(handle_vnc_changed)
from twisted.internet import reactor # pylint: disable=E0401
backend.startVNC(config['vnc']['port'], vnc_option)
reactor.run()
if __name__ == '__main__':
main()