diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index f570dd9..811e1b4 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -12,7 +12,7 @@ repos:
- id: remove-crlf
- id: forbid-tabs
- id: remove-tabs
- args: [ --whitespaces-count, "2" ]
+ args: [ --whitespaces-count, "4" ]
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.6.0
@@ -43,7 +43,8 @@ repos:
- repo: https://github.com/scop/pre-commit-shfmt
rev: v3.8.0-1
hooks:
- - id: shfmt
+ - id: shfmt
+ args: ["-i", "4", "-ci", "-s"]
- repo: https://github.com/shellcheck-py/shellcheck-py
rev: v0.10.0.1
diff --git a/README.md b/README.md
index a86767b..acb2e1c 100644
--- a/README.md
+++ b/README.md
@@ -1,114 +1,121 @@
# WinApps
+*The WinApps project, forked from Fmstrat's [original repository](https://github.com/Fmstrat/winapps).*
-The WinApps main project, [originally created by Fmstrat](https://github.com/Fmstrat/winapps)
+Run Windows applications (including [Microsoft 365](https://www.microsoft365.com/) and [Adobe Creative Cloud](https://www.adobe.com/creativecloud.html)) on GNU+Linux with `KDE` or `GNOME`, integrated seamlessly as if they were native to the OS.
-Run Windows apps such as Microsoft Office/Adobe in Linux (Ubuntu/Fedora) and GNOME/KDE as if they were a part of the native OS,
-including Nautilus integration for right-clicking on files of specific mime types to open them.
+
-
+## Underlying Mechanism
+WinApps works by:
+1. Running Windows in a `Docker` or `libvirt + KVM/QEMU` virtual machine (deprecated).
+2. Querying Windows for all installed applications.
+3. Creating shortcuts to selected Windows applications on the host GNU/Linux OS.
+4. Using [`FreeRDP`](https://www.freerdp.com/) as a backend to seamlessly render Windows applications alongside GNU/Linux applications.
-## How it works
+## Additional Features
+- The GNU/Linux `/home` directory is accessible within Windows via the `\\tsclient\home` mount.
+- Integration with `Nautilus`, allowing you to right-click files to open them with specific Windows applications based on the file MIME type.
-WinApps was created as an easy, one-command way to include apps running inside a VM (or on any RDP server) directly into GNOME as if they were native applications. WinApps works by:
+## Supported Applications
+**WinApps supports *ALL* Windows applications.**
-- Running a Windows RDP server in a background VM container
-- Checking the RDP server for installed applications such as Microsoft Office
-- If those programs are installed, it creates shortcuts leveraging FreeRDP for both the CLI and the GNOME tray
-- Files in your home directory are accessible via the `\\tsclient\home` mount inside the VM
-- You can right-click on any files in your home directory to open with an application, too
+Universal application support is achieved by:
+1. Scanning Windows for any officially supported applications (list below).
+2. Scanning Windows for any other `.exe` files listed within the Windows Registry.
-## Currently supported applications
+Officially supported applications benefit from high-resolution icons and pre-populated MIME types. This enables file managers to determine which Windows applications should open files based on file extensions. Icons for other detected applications are pulled from `.exe` files.
-### WinApps supports **_ANY_** installed application on your system.
+Contributing to the list of supported applications is encouraged through submission of pull requests! Please help us grow the WinApps community.
-It does this by:
-
-1. Scanning your system for the officially configured applications (below)
-2. Scanning your system for any other EXE files with install records in the Windows Registry
-
-Any officially configured applications will have support for high-resolution icons and mime types for automatically detecting what files can be opened by each application. Any other detected executable files will leverage the icons pulled from the EXE.
-
-Note: The officially configured application list below is fueled by the community, and therefore some apps may be untested by the WinApps team.
+*Please note that the provided list of officially supported applications is community-driven. As such, some applications may not be tested and verified by the WinApps team.*
+### Officially Supported Applications
| Adobe Acrobat Pro (X) |
- Adobe Acrobat Reader (DC) |
- ||
| Adobe After Effects (CC) |
- Adobe Audition (CC) |
- ||
| Adobe Bridge (CS6, CC) |
- Adobe Creative Cloud (CC) |
- ||
| Adobe Illustrator (CC) |
- Adobe InDesign (CC) |
- ||
| Adobe Lightroom (CC) |
- Command Prompt (cmd.exe) |
- ||
| Explorer (File Manager) |
- Internet Explorer (11) |
- ||
| Microsoft Access (2016, 2019, o365) |
- Microsoft Excel (2016, 2019, o365) |
- ||
| Microsoft Word (2016, 2019, o365) |
- Microsoft OneNote (2016, 2019, o365) |
- ||
| Microsoft Outlook (2016, 2019, o365) |
- Microsoft PowerPoint (2016, 2019, o365) |
- ||
| Microsoft Publisher (2016, 2019, o365) |
- PowerShell | -||
| Windows (Full RDP session) |
- - | ||
| Adobe Acrobat Pro (X) |
+ Adobe After Effects (CC) |
+ ||
| Adobe Audition (CC) |
+ Adobe Bridge (CS6, CC) |
+ ||
| Adobe Creative Cloud (CC) |
+ Adobe Illustrator (CC) |
+ ||
| Adobe InDesign (CC) |
+ Adobe Lightroom (CC) |
+ ||
| Command Prompt (cmd.exe) |
+ Explorer (File Manager) |
+ ||
| Internet Explorer (11) |
+ Microsoft Access (2016, 2019, o365) |
+ ||
| Microsoft Excel (2016, 2019, o365) |
+ Microsoft Word (2016, 2019, o365) |
+ ||
| Microsoft OneNote (2016, 2019, o365) |
+ Microsoft Outlook (2016, 2019, o365) |
+ ||
| Microsoft PowerPoint (2016, 2019, o365) |
+ Microsoft Publisher (2016, 2019, o365) |
+ ||
| PowerShell | +Windows (Full RDP session) |
+
-## Adding pre-defined applications
+## Adding Additional Pre-defined Applications
+Adding your own applications with custom icons and MIME types to the installer is easy. Simply copy one of the application configurations in the `apps` folder located within the WinApps repository, and:
+1. Modify the name and variables to reflect the appropriate/desired values for your application.
+2. Replace `icon.svg` with an SVG for your application (ensuring the icon is appropriately licensed).
+3. Remove and reinstall WinApps.
+4. (Optional, but strongly encouraged) Submit a pull request to add your application to WinApps as an officially supported application once you have tested your configuration files to verify functionality.
-Adding applications with custom icons and mime types to the installer is easy. Simply copy one of the application configurations in the `apps` folder, and:
-
-- Edit the variables for the application
-- Replace the `icon.svg` with an SVG for the application (appropriately licensed)
-- Re-run the installer
-- Submit a Pull Request to add it to WinApps officially
-
-When running the installer, it will check for if any configured apps are installed, and if they are,
-it will create the appropriate shortcuts on the host OS.
-
-## Running applications manually
-
-WinApps offers a manual mode for running applications that aren't configured. This is completed with the `manual` flag.
-Executables that are in the path don't require full path definition.
+## Running Applications Manually
+WinApps offers a manual mode for running applications that were not configured by the WinApps installer. This is completed with the `manual` flag. Executables that are in the Windows PATH do not require full path definition.
```bash
./bin/winapps manual "C:\my\directory\executableNotInPath.exe"
./bin/winapps manual executableInPath.exe
```
-## Checking for new application support
-
-The installer can be run multiple times, so simply run the below again, and it will remove any current installations and update for the latest applications.
-
-```bash
-./installer.sh
-```
-
-## Optional installer command line arguments
-
-The following optional commands can be used to manage your application configurations without prompts:
-
-```bash
-./installer.sh --user # Configure applications for the current user
-./installer.sh --system # Configure applications for the entire system
-./installer.sh --user --uninstall # Remove all configured applications for the current user
-./installer.sh --system --uninstall # Remove all configured applications for the entire system
-./installer.sh --user --setupAllOfficiallySupportedApps # Configures all officially supported applications for the current user
-./installer.sh --system --setupAllOfficiallySupportedApps # Configures all officially supported applications for the entire system
-```
+## Updating WinApps
+The installer can be run multiple times. To update your installation of WinApps:
+1. Run the WinApps installer to remove WinApps from your system.
+2. Pull the latest changes from the WinApps GitHub repository.
+3. Re-install WinApps using the WinApps installer.
## Shout-outs
-
-- Some icons pulled from
- - Fluent UI React - Icons under [MIT License](https://github.com/Fmstrat/fluent-ui-react/blob/master/LICENSE.md)
- - Fluent UI - Icons under [MIT License](https://github.com/Fmstrat/fluentui/blob/master/LICENSE) with [restricted use](https://static2.sharepointonline.com/files/fabric/assets/microsoft_fabric_assets_license_agreement_nov_2019.pdf)
- - PKief's VSCode Material Icon Theme - Icons under [MIT License](https://github.com/Fmstrat/vscode-material-icon-theme/blob/master/LICENSE.md)
- - DiemenDesign's LibreICONS - Icons under [MIT License](https://github.com/Fmstrat/LibreICONS/blob/master/LICENSE)
+Some icons used for the officially supported applications were sourced from:
+- Fluent UI React - Icons under [MIT License](https://github.com/Fmstrat/fluent-ui-react/blob/master/LICENSE.md)
+- Fluent UI - Icons under [MIT License](https://github.com/Fmstrat/fluentui/blob/master/LICENSE) with [restricted use](https://static2.sharepointonline.com/files/fabric/assets/microsoft_fabric_assets_license_agreement_nov_2019.pdf)
+- PKief's VSCode Material Icon Theme - Icons under [MIT License](https://github.com/Fmstrat/vscode-material-icon-theme/blob/master/LICENSE.md)
+- DiemenDesign's LibreICONS - Icons under [MIT License](https://github.com/Fmstrat/LibreICONS/blob/master/LICENSE)
diff --git a/bin/winapps b/bin/winapps
index 228f397..7504ddf 100755
--- a/bin/winapps
+++ b/bin/winapps
@@ -1,209 +1,329 @@
#!/usr/bin/env bash
-if [ ! -f "$HOME/.config/winapps/winapps.conf" ] && [ ! -f "$HOME/.winapps" ]; then
- echo "You need to create a ~/.config/winapps/winapps.conf configuration. Exiting..."
- exit
-fi
+### GLOBAL CONSTANTS ###
+# ANSI ESCAPE SEQUENCES
+readonly ERROR_TEXT="\033[1;41;37m" # Bold + White + Red Background
+readonly CLEAR_TEXT="\033[0m" # Clear
-DIR="$(dirname "$(readlink -f "$0")")"
-RUN="$(date)-$RANDOM"
+# ERROR CODES
+readonly EC_MISSING_CONFIG=1
+readonly EC_MISSING_FREERDP=2
+readonly EC_NOT_IN_GROUP=3
+readonly EC_VM_NOT_RUNNING=4
+readonly EC_VM_NO_IP=5
+readonly EC_VM_BAD_PORT=6
+readonly EC_UNSUPPORTED_APP=7
-if [ ! -d "$HOME/.local/share/winapps" ]; then
- mkdir -p "$HOME/.local/share/winapps"
-fi
+# PATHS
+readonly APPDATA_PATH="${HOME}/.local/share/winapps"
+readonly SYS_APP_PATH="/usr/local/share/winapps"
+readonly LASTRUN_PATH="${APPDATA_PATH}/lastrun"
+readonly LOG_PATH="${APPDATA_PATH}/winapps.log"
+readonly CONFIG_PATH="${HOME}/.config/winapps/winapps.conf"
+# shellcheck disable=SC2155 # Silence warnings regarding masking return values through simultaneous declaration and assignment.
+readonly SCRIPT_DIR_PATH="$(cd "$(dirname "${BASH_SOURCE[0]}")" &>/dev/null && pwd)"
+# OTHER
+readonly VM_NAME="RDPWindows"
+readonly RDP_PORT=3389
+# shellcheck disable=SC2155 # Silence warnings regarding masking return values through simultaneous declaration and assignment.
+readonly RUN="$(date)-${RANDOM}"
+
+### GLOBAL VARIABLES ###
+# WINAPPS CONFIGURATION FILE
+RDP_USER=""
+RDP_PASS=""
+RDP_DOMAIN=""
+RDP_IP=""
+RDP_FLAGS=""
+FREERDP_COMMAND=""
RDP_SCALE=100
+MULTIMON="false"
+DEBUG="true"
+MULTI_FLAG=""
-if [ -f "$HOME/.config/winapps/winapps.conf" ]; then
- # shellcheck source=/dev/null
- . "$HOME/.config/winapps/winapps.conf"
-else
- # shellcheck source=/dev/null
- . "$HOME/.winapps"
-fi
+### FUNCTIONS ###
+# Name: 'waThrowExit'
+# Role: Throw an error message and exit the script.
+function waThrowExit() {
+ # Declare variables.
+ local ERR_CODE="$1"
+ # Throw error.
+ case "$ERR_CODE" in
+ "$EC_MISSING_CONFIG")
+ # Missing WinApps configuration file.
+ dprint "ERROR: MISSING WINAPPS CONFIGURATION FILE. EXITING."
+ echo -e "${ERROR_TEXT}ERROR: MISSING WINAPPS CONFIGURATION FILE.${CLEAR_TEXT}"
+ echo "Please create a WinApps configuration file at '${CONFIG_PATH}'".
+ ;;
+ "$EC_MISSING_FREERDP")
+ dprint "ERROR: FREERDP VERSION 3 IS NOT INSTALLED. EXITING."
+ echo -e "${ERROR_TEXT}ERROR: FREERDP VERSION 3 IS NOT INSTALLED.${CLEAR_TEXT}"
+ ;;
+ "$EC_NOT_IN_GROUP")
+ dprint "ERROR: USER NOT PART OF REQUIRED GROUPS. EXITING."
+ echo -e "${ERROR_TEXT}ERROR: USER NOT PART OF REQUIRED GROUPS.${CLEAR_TEXT}"
+ echo "Please run:"
+ echo " sudo usermod -a -G libvirt $(whoami)"
+ echo " sudo usermod -a -G kvm $(whoami)"
+ ;;
+ "$EC_VM_NOT_RUNNING")
+ dprint "ERROR: VM NOT RUNNING. EXITING."
+ echo -e "${ERROR_TEXT}ERROR: VM NOT RUNNING.${CLEAR_TEXT}"
+ echo "Please ensure the Windows VM is powered on."
+ ;;
+ "$EC_VM_NO_IP")
+ dprint "ERROR: VM UNREACHABLE. EXITING."
+ echo -e "${ERROR_TEXT}ERROR: VM UNREACHABLE.${CLEAR_TEXT}"
+ echo "Please ensure the Windows VM is assigned an IP address."
+ ;;
+ "$EC_VM_BAD_PORT")
+ dprint "ERROR: RDP PORT CLOSED. EXITING."
+ echo -e "${ERROR_TEXT}ERROR: RDP PORT CLOSED.${CLEAR_TEXT}"
+ echo "Please ensure Remote Desktop is correctly configured on the Windows VM."
+ ;;
+ "$EC_UNSUPPORTED_APP")
+ dprint "ERROR: APPLICATION NOT FOUND. EXITING."
+ echo -e "${ERROR_TEXT}ERROR: APPLICATION NOT FOUND.${CLEAR_TEXT}"
+ echo "Please ensure the program is correctly configured as an officially supported application."
+ ;;
+ esac
+
+ # Provide generic advice.
+ echo "Check the WinApps project README for more information."
+
+ # Terminate the script.
+ echo "Exiting with status '${ERR_CODE}'."
+ exit "$ERR_CODE"
+}
+
+# Name: 'dprint'
+# Role: Conditionally print debug messages to a log file, creating it if it does not exist.
function dprint() {
- if [ "$DEBUG" = "true" ]; then
- echo "[$RUN] $1" >>"$HOME/.local/share/winapps/winapps.log"
+ [ "$DEBUG" = "true" ] && echo "[$RUN] $1" >>"$LOG_PATH"
+}
+
+# Name: 'waLoadConfig'
+# Role: Load the variables within the WinApps configuration file.
+function waLoadConfig() {
+ # Load WinApps configuration file.
+ if [ -f "$CONFIG_PATH" ]; then
+ # shellcheck source=/dev/null # Exclude WinApps configuration file from being checked by ShellCheck.
+ source "$CONFIG_PATH"
+ else
+ waThrowExit $EC_MISSING_CONFIG
+ fi
+
+ # Update 'MULTI_FLAG' based on 'MULTIMON'.
+ MULTI_FLAG=$([[ $MULTIMON == "true" ]] && echo "/multimon" || echo "+span")
+
+ # Append additional flags or parameters to FreeRDP.
+ [[ -n $RDP_FLAGS ]] && FREERDP_COMMAND="${FREERDP_COMMAND} ${RDP_FLAGS}"
+}
+
+# Name: 'waLastRun'
+# Role: Determine the last time this script was run.
+function waLastRun() {
+ # Declare variables.
+ local LAST_RUN_UNIX_TIME=0
+ local CURR_RUN_UNIX_TIME=0
+
+ # Store the time this script was run last as a unix timestamp.
+ if [ -f "$LASTRUN_PATH" ]; then
+ LAST_RUN_UNIX_TIME=$(stat -t -c %Y "$LASTRUN_PATH")
+ dprint "LAST_RUN: ${LAST_RUN_UNIX_TIME}"
+ fi
+
+ # Update the file modification time with the current time.
+ touch "$LASTRUN_PATH"
+ CURR_RUN_UNIX_TIME=$(stat -t -c %Y "$LASTRUN_PATH")
+ dprint "THIS_RUN: ${CURR_RUN_UNIX_TIME}"
+}
+
+function waGetFreeRDPCommand() {
+ # Attempt to set a FreeRDP command if the command variable is empty.
+ if [ -z "$FREERDP_COMMAND" ]; then
+ # Check for 'xfreerdp'.
+ if command -v xfreerdp &>/dev/null; then
+ # Check FreeRDP major version is 3 or greater.
+ FREERDP_MAJOR_VERSION=$(xfreerdp --version | head -n 1 | grep -o -m 1 '\b[0-9]\S*' | cut -d'.' -f1)
+ if [[ $FREERDP_MAJOR_VERSION =~ ^[0-9]+$ ]] && ((FREERDP_MAJOR_VERSION >= 3)); then
+ FREERDP_COMMAND="xfreerdp"
+ fi
+ # Check for 'xfreerdp3'.
+ elif command -v xfreerdp3 &>/dev/null; then
+ # Check FreeRDP major version is 3 or greater.
+ FREERDP_MAJOR_VERSION=$(xfreerdp3 --version | head -n 1 | grep -o -m 1 '\b[0-9]\S*' | cut -d'.' -f1)
+ if [[ $FREERDP_MAJOR_VERSION =~ ^[0-9]+$ ]] && ((FREERDP_MAJOR_VERSION >= 3)); then
+ FREERDP_COMMAND="xfreerdp3"
+ fi
+ fi
+
+ # Check for FreeRDP Flatpak (fallback option).
+ if [ -z "$FREERDP_COMMAND" ]; then
+ if command -v flatpak &>/dev/null; then
+ if flatpak list --columns=application | grep -q "^com.freerdp.FreeRDP$"; then
+ # Check FreeRDP major version is 3 or greater.
+ FREERDP_MAJOR_VERSION=$(flatpak list --columns=application,version | grep "^com.freerdp.FreeRDP" | awk '{print $2}' | cut -d'.' -f1)
+ if [[ $FREERDP_MAJOR_VERSION =~ ^[0-9]+$ ]] && ((FREERDP_MAJOR_VERSION >= 3)); then
+ FREERDP_COMMAND="flatpak run --command=xfreerdp com.freerdp.FreeRDP"
+ fi
+ fi
+ fi
+ fi
+ fi
+
+ if command -v "$FREERDP_COMMAND" &>/dev/null || [ "$FREERDP_COMMAND" = "flatpak run --command=xfreerdp com.freerdp.FreeRDP" ]; then
+ dprint "Using FreeRDP command '${FREERDP_COMMAND}'."
+ else
+ waThrowExit "$EC_MISSING_FREERDP"
fi
}
+# Name: 'waCheckGroupMembership'
+# Role: Ensures the current user is part of the required groups.
+function waCheckGroupMembership() {
+ # Identify groups the current user belongs to.
+ # shellcheck disable=SC2155 # Silence warnings regarding masking return values through simultaneous declaration and assignment.
+ local USER_GROUPS=$(groups "$(whoami)")
+
+ if ! (echo "$USER_GROUPS" | grep -q -E "\blibvirt\b") || ! (echo "$USER_GROUPS" | grep -q -E "\bkvm\b"); then
+ waThrowExit "$EC_NOT_IN_GROUP"
+ fi
+}
+
+# Name: 'waCheckVMRunning'
+# Role: Throw an error if the Windows VM is not running.
+function waCheckVMRunning() {
+ ! virsh list --state-running --name | grep -q "^${VM_NAME}$" && waThrowExit "$EC_VM_NOT_RUNNING"
+}
+
+# Name: 'waCheckVMContactable'
+# Role: Assesses whether the Windows VM can be contacted.
+function waCheckVMContactable() {
+ # Declare variables.
+ local VM_MAC="" # Stores the MAC address of the Windows VM.
+
+ # Obtain Windows VM IP Address
+ if [ -z "$RDP_IP" ]; then
+ VM_MAC=$(virsh domiflist "$VM_NAME" | grep -oE "([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})") # VM MAC address.
+ RDP_IP=$(arp -n | grep "$VM_MAC" | grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}") # VM IP address.
+ [ -z "$RDP_IP" ] && waThrowExit "$EC_VM_NO_IP"
+ fi
+
+ # Check for an open RDP port.
+ timeout 5 nc -z "$RDP_IP" "$RDP_PORT" &>/dev/null || waThrowExit "$EC_VM_BAD_PORT"
+}
+
+function waRunCommand() {
+ # Declare variables.
+ local ICON=""
+ local FILE_PATH=""
+
+ # Run option.
+ if [ "$1" = "windows" ]; then
+ # Open Windows VM.
+ dprint "WINDOWS"
+ $FREERDP_COMMAND \
+ /d:"$RDP_DOMAIN" \
+ /u:"$RDP_USER" \
+ /p:"$RDP_PASS" \
+ /scale:"$RDP_SCALE" \
+ +dynamic-resolution \
+ +auto-reconnect \
+ +home-drive \
+ /wm-class:"Microsoft Windows" \
+ /v:"$RDP_IP" &>/dev/null &
+ elif [ "$1" = "manual" ]; then
+ # Open specified application.
+ dprint "MANUAL: ${2}"
+ $FREERDP_COMMAND \
+ /cert:tofu \
+ /d:"$RDP_DOMAIN" \
+ /u:"$RDP_USER" \
+ /p:"$RDP_PASS" \
+ /scale:"$RDP_SCALE" \
+ +auto-reconnect \
+ +clipboard \
+ +home-drive \
+ -wallpaper \
+ +dynamic-resolution \
+ "$MULTI_FLAG" \
+ /app:program:"$2" \
+ /v:"$RDP_IP" &>/dev/null &
+ else
+ # Script summoned from right-click menu with officially supported application name plus/minus a file path.
+ if [ -e "${SCRIPT_DIR_PATH}/../apps/${1}/info" ]; then
+ # shellcheck source=/dev/null # Exclude this file from being checked by ShellCheck.
+ source "${SCRIPT_DIR_PATH}/../apps/${1}/info"
+ ICON="${SCRIPT_DIR_PATH}/../apps/${1}/icon.svg"
+ elif [ -e "${APPDATA_PATH}/apps/${1}/info" ]; then
+ # shellcheck source=/dev/null # Exclude this file from being checked by ShellCheck.
+ source "${APPDATA_PATH}/apps/${1}/info"
+ ICON="${APPDATA_PATH}/apps/${1}/icon.svg"
+ elif [ -e "${SYS_APP_PATH}/apps/${1}/info" ]; then
+ # shellcheck source=/dev/null # Exclude this file from being checked by ShellCheck.
+ source "${SYS_APP_PATH}/apps/${1}/info"
+ ICON="${SYS_APP_PATH}/apps/${1}/icon.svg"
+ else
+ waThrowExit "$EC_UNSUPPORTED_APP"
+ fi
+
+ # Check if a file path was specified, and pass this to the application.
+ if [ -z "$2" ]; then
+ # No file path specified.
+ $FREERDP_COMMAND \
+ /d:"$RDP_DOMAIN" \
+ /u:"$RDP_USER" \
+ /p:"$RDP_PASS" \
+ /scale:"$RDP_SCALE" \
+ +auto-reconnect \
+ +clipboard \
+ +home-drive \
+ -wallpaper \
+ +dynamic-resolution \
+ "$MULTI_FLAG" \
+ /wm-class:"$FULL_NAME" \
+ /app:program:"$WIN_EXECUTABLE",icon:"$ICON",name:"$FULL_NAME" \
+ /v:"$RDP_IP" &>/dev/null &
+ else
+ # Convert path from UNIX to Windows style.
+ FILE_PATH=$(echo "$2" | sed 's|'"${HOME}"'|\\\\tsclient\\home|;s|/|\\|g;s|\\|\\\\|g')
+ dprint "UNIX_FILE_PATH: ${2}"
+ dprint "WINDOWS_FILE_PATH: ${FILE_PATH}"
+
+ $FREERDP_COMMAND \
+ /cert:tofu \
+ /d:"$RDP_DOMAIN" \
+ /u:"$RDP_USER" \
+ /p:"$RDP_PASS" \
+ /scale:"$RDP_SCALE" \
+ +auto-reconnect \
+ +clipboard \
+ +home-drive \
+ -wallpaper \
+ +dynamic-resolution \
+ "$MULTI_FLAG" \
+ /wm-class:"$FULL_NAME" \
+ /app:program:"$WIN_EXECUTABLE",icon:"$ICON",name:$"FULL_NAME",cmd:\""$FILE_PATH"\" \
+ /v:"$RDP_IP" &>/dev/null &
+ fi
+ fi
+}
+
+### MAIN LOGIC ###
+#set -x # Enable for debugging.
dprint "START"
-
-if [ -f "$HOME/.local/share/winapps/run" ]; then
- LAST_RAN=$(stat -t -c %Y "$HOME/.local/share/winapps/run")
- dprint "LAST_RAN:${LAST_RAN}"
- touch "$HOME/.local/share/winapps/run"
- THIS_RUN=$(stat -t -c %Y "$HOME/.local/share/winapps/run")
- dprint "THIS_RUN:$THIS_RUN"
- if ((THIS_RUN - LAST_RAN < 2)); then
- exit
- fi
-else
- touch "$HOME/.local/share/winapps/run"
-fi
-
-if [ -z "${FREERDP_COMMAND}" ]; then
- if command -v xfreerdp &> /dev/null
- then
- FREERDP_COMMAND="xfreerdp"
- elif command -v xfreerdp3 &> /dev/null
- then
- FREERDP_COMMAND="xfreerdp3"
- fi
-elif command -v "$FREERDP_COMMAND" &> /dev/null
-then
- dprint "Using custom freerdp command $FREERDP_COMMAND"
-else
- echo "You have supplied a custom FreeRDP command, but the command is not available."
- exit
-fi
-
-if [ -z "$RDP_IP" ]; then
- if groups | grep -vq libvirt; then
- echo "You are not a member of the libvirt group. Run the below then reboot."
- echo " sudo usermod -a -G libvirt $(whoami)"
- echo " sudo usermod -a -G kvm $(whoami)"
- exit
- fi
- if ! virsh list --state-running --name | grep -q '^RDPWindows$'; then
- echo "RDPWindows is not running. Please run:"
- echo " virsh start RDPWindows"
- exit
- fi
- RDP_IP=$(virsh net-dhcp-leases default | grep "RDPWindows" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}')
-fi
-
-dprint "1:$1"
-dprint "2:$2"
-# this is just for debug logging anyways
-# shellcheck disable=SC2145
-dprint "@:${@}"
-
-MULTI_FLAG="+span"
-if [ "$MULTIMON" = "true" ]; then
- MULTI_FLAG="/multimon"
-fi
-
-# Append additional flags or parameters to FreeRDP
-if [[ -n "$RDP_FLAGS" ]]; then
- FREERDP_COMMAND="$FREERDP_COMMAND $RDP_FLAGS"
-fi
-
-if [ "$1" = "check" ]; then
- # Open File Explorer
- dprint "CHECK"
- COMMAND=(
- "${FREERDP_COMMAND}"
- "/d:${RDP_DOMAIN}"
- "/u:${RDP_USER}"
- "/p:${RDP_PASS}"
- "/scale:${RDP_SCALE}"
- "+auto-reconnect"
- "+home-drive"
- "-wallpaper"
- "+dynamic-resolution"
- "${MULTI_FLAG}"
- "/app:program:explorer.exe"
- "/v:${RDP_IP}"
- )
- "${COMMAND[@]}"
-elif [ "$1" = "windows" ]; then
- # Open Virtual Machine
- dprint "WINDOWS"
- COMMAND=(
- "${FREERDP_COMMAND}"
- "/d:${RDP_DOMAIN}"
- "/u:${RDP_USER}"
- "/p:${RDP_PASS}"
- "/scale:${RDP_SCALE}"
- "+dynamic-resolution"
- "+auto-reconnect"
- "+home-drive"
- "/wm-class:\"Microsoft Windows\""
- "/v:${RDP_IP}"
- )
- # Run the command in the background, redirecting both stdout and stderr to /dev/null
- "${COMMAND[@]}" 1>/dev/null 2>&1 &
-elif [ "$1" = "manual" ]; then
- # Open Specified Application
- dprint "MANUAL:${2}"
- COMMAND=(
- "${FREERDP_COMMAND}"
- "/d:${RDP_DOMAIN}"
- "/u:${RDP_USER}"
- "/p:${RDP_PASS}"
- "/scale:${RDP_SCALE}"
- "+auto-reconnect"
- "+home-drive"
- "+dynamic-resolution"
- "${MULTI_FLAG}"
- "/app:program:${2}"
- "/v:${RDP_IP}"
- )
- # Run the command in the background, redirecting both stdout and stderr to /dev/null
- "${COMMAND[@]}" 1>/dev/null 2>&1 &
-elif [ "$1" != "install" ]; then
- dprint "DIR:${DIR}"
- if [ -e "${DIR}/../apps/$1/info" ]; then
- # shellcheck disable=SC1090
- . "${DIR}/../apps/$1/info"
- ICON="${DIR}/../apps/$1/icon.svg"
- elif [ -e "$HOME/.local/share/winapps/apps/$1/info" ]; then
- # shellcheck disable=SC1090
- . "$HOME/.local/share/winapps/apps/$1/info"
- ICON="$HOME/.local/share/winapps/apps/$1/icon.svg"
- elif [ -e "/usr/local/share/winapps/apps/$1/info" ]; then
- # shellcheck disable=SC1090
- . "/usr/local/share/winapps/apps/$1/info"
- ICON="/usr/local/share/winapps/apps/$1/icon.svg"
- else
- echo "You need to run 'installer.sh' first."
- exit 1
- fi
- if [ -n "$2" ]; then
- dprint "HOME:$HOME"
- FILE=$(echo "$2" | sed 's|'"$HOME"'|\\\\tsclient\\home|;s|/|\\|g;s|\\|\\\\|g')
- dprint "FILE:${FILE}"
- # shellcheck disable=SC2140
- COMMAND=(
- "${FREERDP_COMMAND}"
- "/d:${RDP_DOMAIN}"
- "/u:${RDP_USER}"
- "/p:${RDP_PASS}"
- "/scale:${RDP_SCALE}"
- "+auto-reconnect"
- "+clipboard"
- "+home-drive"
- "-wallpaper"
- "+dynamic-resolution"
- "${MULTI_FLAG}"
- "/wm-class:${FULL_NAME}"
- "/app:program:${WIN_EXECUTABLE},icon:${ICON},name:${FULL_NAME},cmd:\"${FILE}\""
- "/v:${RDP_IP}"
- )
- # Run the command in the background, redirecting both stdout and stderr to /dev/null
- echo "${COMMAND[@]}" #1>/dev/null 2>&1 &
- else
- COMMAND=(
- "${FREERDP_COMMAND}"
- "/d:${RDP_DOMAIN}"
- "/u:${RDP_USER}"
- "/p:${RDP_PASS}"
- "/scale:${RDP_SCALE}"
- "+auto-reconnect"
- "+clipboard"
- "+home-drive"
- "-wallpaper"
- "+dynamic-resolution"
- "${MULTI_FLAG}"
- "/wm-class:${FULL_NAME}"
- "/app:program:${WIN_EXECUTABLE},icon:${ICON},name:${FULL_NAME}"
- "/v:${RDP_IP}"
- )
- # Run the command in the background, redirecting both stdout and stderr to /dev/null
- "${COMMAND[@]}" 1>/dev/null 2>&1 &
- fi
-fi
-
+dprint "SCRIPT_DIR: ${SCRIPT_DIR_PATH}"
+dprint "SCRIPT_ARGS: ${*}"
+dprint "HOME_DIR: ${HOME}"
+mkdir -p "$APPDATA_PATH"
+waLastRun
+waLoadConfig
+waGetFreeRDPCommand
+waCheckGroupMembership
+waCheckVMRunning
+waCheckVMContactable
+waRunCommand "$@"
dprint "END"
diff --git a/demo/installer.gif b/demo/installer.gif
index b152494..5123715 100644
Binary files a/demo/installer.gif and b/demo/installer.gif differ
diff --git a/icons/windows.svg b/icons/windows.svg
index ea5b408..ec8f033 100644
--- a/icons/windows.svg
+++ b/icons/windows.svg
@@ -1,3 +1,14 @@
-