linwin 0.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- linwin-0.4.0/.gitignore +13 -0
- linwin-0.4.0/LICENSE +21 -0
- linwin-0.4.0/PKG-INFO +152 -0
- linwin-0.4.0/README.md +123 -0
- linwin-0.4.0/config.json +50 -0
- linwin-0.4.0/linwin/__init__.py +1 -0
- linwin-0.4.0/linwin/__main__.py +19 -0
- linwin-0.4.0/linwin/icons/linwin.ico +0 -0
- linwin-0.4.0/linwin/icons/linwin.png +0 -0
- linwin-0.4.0/linwin/linux/__init__.py +0 -0
- linwin-0.4.0/linwin/linux/__main__.py +195 -0
- linwin-0.4.0/linwin/linux/app.py +15 -0
- linwin-0.4.0/linwin/linux/screens/__init__.py +0 -0
- linwin-0.4.0/linwin/linux/screens/config_editor.py +91 -0
- linwin-0.4.0/linwin/linux/screens/setup.py +195 -0
- linwin-0.4.0/linwin/linux/screens/verify.py +105 -0
- linwin-0.4.0/linwin/linux/screens/welcome.py +166 -0
- linwin-0.4.0/linwin/linux/tasks/__init__.py +0 -0
- linwin-0.4.0/linwin/linux/tasks/apt.py +41 -0
- linwin-0.4.0/linwin/linux/tasks/snaps.py +51 -0
- linwin-0.4.0/linwin/linux/tasks/systemd.py +45 -0
- linwin-0.4.0/linwin/linux/tasks/wslg.py +45 -0
- linwin-0.4.0/linwin/linux/tasks/xrdp.py +324 -0
- linwin-0.4.0/linwin/shared/__init__.py +0 -0
- linwin-0.4.0/linwin/shared/base_app.py +67 -0
- linwin-0.4.0/linwin/shared/config.py +322 -0
- linwin-0.4.0/linwin/shared/headless_protocol.py +74 -0
- linwin-0.4.0/linwin/shared/launcher.py +104 -0
- linwin-0.4.0/linwin/shared/setup_logging.py +77 -0
- linwin-0.4.0/linwin/shared/subprocess_runner.py +164 -0
- linwin-0.4.0/linwin/shared/task_result.py +19 -0
- linwin-0.4.0/linwin/shared/theme.py +133 -0
- linwin-0.4.0/linwin/shared/verify_checks.py +83 -0
- linwin-0.4.0/linwin/shared/widgets.py +346 -0
- linwin-0.4.0/linwin/windows/__init__.py +0 -0
- linwin-0.4.0/linwin/windows/__main__.py +45 -0
- linwin-0.4.0/linwin/windows/app.py +168 -0
- linwin-0.4.0/linwin/windows/screens/__init__.py +0 -0
- linwin-0.4.0/linwin/windows/screens/config_editor.py +135 -0
- linwin-0.4.0/linwin/windows/screens/drive_picker.py +140 -0
- linwin-0.4.0/linwin/windows/screens/launcher.py +223 -0
- linwin-0.4.0/linwin/windows/screens/setup.py +423 -0
- linwin-0.4.0/linwin/windows/screens/setup_proposal.py +143 -0
- linwin-0.4.0/linwin/windows/screens/status.py +286 -0
- linwin-0.4.0/linwin/windows/screens/verify.py +146 -0
- linwin-0.4.0/linwin/windows/tasks/__init__.py +0 -0
- linwin-0.4.0/linwin/windows/tasks/auto_config.py +85 -0
- linwin-0.4.0/linwin/windows/tasks/drive_scan.py +139 -0
- linwin-0.4.0/linwin/windows/tasks/features.py +87 -0
- linwin-0.4.0/linwin/windows/tasks/full_verify.py +167 -0
- linwin-0.4.0/linwin/windows/tasks/health_check.py +54 -0
- linwin-0.4.0/linwin/windows/tasks/linux_invoke.py +29 -0
- linwin-0.4.0/linwin/windows/tasks/state.py +84 -0
- linwin-0.4.0/linwin/windows/tasks/validators.py +168 -0
- linwin-0.4.0/linwin/windows/tasks/wsl_config.py +72 -0
- linwin-0.4.0/linwin/windows/tasks/wsl_install.py +204 -0
- linwin-0.4.0/pyproject.toml +54 -0
- linwin-0.4.0/run.bat +12 -0
- linwin-0.4.0/scripts/prompts.md +13 -0
- linwin-0.4.0/scripts/publish.bat +49 -0
- linwin-0.4.0/scripts/run_claude.bat +4 -0
- linwin-0.4.0/scripts/setup_dev.bat +53 -0
- linwin-0.4.0/scripts/setup_wsl.bat +57 -0
- linwin-0.4.0/scripts/ship.bat +38 -0
- linwin-0.4.0/scripts/test.bat +8 -0
- linwin-0.4.0/tests/__init__.py +0 -0
- linwin-0.4.0/tests/conftest.py +54 -0
- linwin-0.4.0/tests/helpers.py +54 -0
- linwin-0.4.0/tests/test_config.py +187 -0
- linwin-0.4.0/tests/test_coverage_gaps.py +356 -0
- linwin-0.4.0/tests/test_drive_scan.py +110 -0
- linwin-0.4.0/tests/test_features.py +22 -0
- linwin-0.4.0/tests/test_final_gaps.py +362 -0
- linwin-0.4.0/tests/test_headless.py +141 -0
- linwin-0.4.0/tests/test_linux_tasks.py +660 -0
- linwin-0.4.0/tests/test_main_entry.py +124 -0
- linwin-0.4.0/tests/test_rdp_desktop.py +509 -0
- linwin-0.4.0/tests/test_rdp_lifecycle.py +469 -0
- linwin-0.4.0/tests/test_rdp_login.py +308 -0
- linwin-0.4.0/tests/test_rdp_session.py +245 -0
- linwin-0.4.0/tests/test_screens.py +465 -0
- linwin-0.4.0/tests/test_shared_modules.py +359 -0
- linwin-0.4.0/tests/test_state.py +74 -0
- linwin-0.4.0/tests/test_subprocess_runner.py +79 -0
- linwin-0.4.0/tests/test_validators.py +30 -0
- linwin-0.4.0/tests/test_windows_tasks.py +500 -0
- linwin-0.4.0/uv.lock +857 -0
- linwin-0.4.0/wsl_ubuntu_gnome.md +156 -0
linwin-0.4.0/.gitignore
ADDED
linwin-0.4.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 James Abel
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
linwin-0.4.0/PKG-INFO
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: linwin
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: Setup WSL2 and make it easily accessible from Windows desktop
|
|
5
|
+
Project-URL: Homepage, https://github.com/jamesabel/linwin
|
|
6
|
+
Project-URL: Repository, https://github.com/jamesabel/linwin
|
|
7
|
+
Project-URL: Issues, https://github.com/jamesabel/linwin/issues
|
|
8
|
+
Author: James Abel
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: TUI,Ubuntu,WSL,WSL2,WSLg,Windows,setup,xrdp
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: System Administrators
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
23
|
+
Classifier: Topic :: System :: Installation/Setup
|
|
24
|
+
Classifier: Topic :: System :: Operating System
|
|
25
|
+
Requires-Python: >=3.11
|
|
26
|
+
Requires-Dist: pref>=0.3.1
|
|
27
|
+
Requires-Dist: textual>=0.79.0
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# linwin
|
|
31
|
+
|
|
32
|
+
Automated TUI (Text User Interface) to install Ubuntu on Windows 11 via WSL2, with the distro stored on a dedicated SSD drive. Includes WSLg for seamless Linux GUI apps on Windows and xrdp for full XFCE4 desktop access via Remote Desktop.
|
|
33
|
+
|
|
34
|
+
## Prerequisites
|
|
35
|
+
|
|
36
|
+
- **Windows 11** (or Windows 10 build 19044+)
|
|
37
|
+
- **Hardware virtualization** enabled in BIOS/UEFI (Intel VT-x or AMD-V)
|
|
38
|
+
- **Administrator access** on Windows
|
|
39
|
+
|
|
40
|
+
## Quick Start
|
|
41
|
+
|
|
42
|
+
```powershell
|
|
43
|
+
scripts\setup_wsl.bat
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
This launches an interactive terminal UI that:
|
|
47
|
+
|
|
48
|
+
- Auto-detects system hardware (RAM, CPUs, drives) and proposes an optimized configuration
|
|
49
|
+
- Scans drives and recommends the best one for WSL storage (NVMe > SSD > HDD)
|
|
50
|
+
- Runs startup verification and shows any issues before setup begins
|
|
51
|
+
- Lets you review and adjust all settings (distro, resources, packages)
|
|
52
|
+
- Enables WSL and Virtual Machine Platform features
|
|
53
|
+
- Installs Ubuntu, exports/imports to your chosen drive
|
|
54
|
+
- Writes `.wslconfig` with your resource limits
|
|
55
|
+
- Runs Linux-side setup (systemd, apt packages, snap packages, xrdp)
|
|
56
|
+
- Configures xrdp with XFCE4 desktop for Remote Desktop access
|
|
57
|
+
- Maintains a WSL keepalive process to prevent VM shutdown between sessions
|
|
58
|
+
- Verifies everything with a PASS/FAIL dashboard
|
|
59
|
+
|
|
60
|
+
The TUI handles admin elevation, the reboot boundary, and cross-WSL Linux setup automatically. After setup, a launcher screen lets you open apps, launch a terminal, or connect via Remote Desktop.
|
|
61
|
+
|
|
62
|
+
For quick reruns without dependency installation:
|
|
63
|
+
|
|
64
|
+
```powershell
|
|
65
|
+
run.bat
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Linux TUI
|
|
69
|
+
|
|
70
|
+
A standalone Linux TUI is also available for running inside WSL Ubuntu directly:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
pip3 install textual
|
|
74
|
+
python3 -m linwin.linux
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
A headless mode is available for non-interactive automation:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
python3 -m linwin.linux --headless --step enable-systemd
|
|
81
|
+
python3 -m linwin.linux --headless --step install-packages
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Keyboard Shortcuts
|
|
85
|
+
|
|
86
|
+
| Key | Action |
|
|
87
|
+
|-----|--------|
|
|
88
|
+
| 1-9 | Select numbered actions on any screen |
|
|
89
|
+
| Escape | Quit app / Cancel current screen |
|
|
90
|
+
| Ctrl+Q | Quit app |
|
|
91
|
+
| Click | Select action links and options |
|
|
92
|
+
|
|
93
|
+
## Configuration
|
|
94
|
+
|
|
95
|
+
Edit `config.json` before running, or use the built-in Configure Settings screen. On first launch, the TUI auto-detects your system profile and proposes sensible defaults:
|
|
96
|
+
|
|
97
|
+
| Field | Default | Description |
|
|
98
|
+
|-------|---------|-------------|
|
|
99
|
+
| `distroName` | `Ubuntu-22.04` | Distro to install via `wsl --install` |
|
|
100
|
+
| `distroImportName` | `Ubuntu` | Name after export/import |
|
|
101
|
+
| `wslInstallPath` | auto-detected | Where to store the distro VHD |
|
|
102
|
+
| `wslDriveLetter` | auto-detected | Best available drive (NVMe > SSD > HDD) |
|
|
103
|
+
| `wslconfig.memory` | RAM ÷ 4 | RAM limit for WSL2 (min 4 GB) |
|
|
104
|
+
| `wslconfig.processors` | CPUs ÷ 2 | CPU cores for WSL2 (min 1) |
|
|
105
|
+
| `wslconfig.swap` | RAM ÷ 8 | Swap size (min 4 GB) |
|
|
106
|
+
| `wslconfig.defaultVhdSize` | `512GB` | Max VHD size |
|
|
107
|
+
| `snaps` | *(empty)* | Snap packages to install (configure via editor) |
|
|
108
|
+
| `aptPackages` | nautilus, x11-apps, xfce4, xfce4-terminal, xrdp, dbus-x11 | Apt packages to install |
|
|
109
|
+
| `xrdpPort` | `3390` | Port for xrdp (avoids conflict with Windows RDP on 3389) |
|
|
110
|
+
|
|
111
|
+
## Project Structure
|
|
112
|
+
|
|
113
|
+
| Path | Description |
|
|
114
|
+
|------|-------------|
|
|
115
|
+
| `run.bat` | Quick launch -- runs the TUI without dependency installation |
|
|
116
|
+
| `scripts/setup_wsl.bat` | Full setup -- installs uv, Python, dependencies, then launches the TUI |
|
|
117
|
+
| `scripts/test.bat` | Run the test suite |
|
|
118
|
+
| `linwin/windows/` | Windows TUI package -- startup verification, auto-config, setup with live progress |
|
|
119
|
+
| `linwin/linux/` | Linux TUI package for WSL-side setup (also supports `--headless`) |
|
|
120
|
+
| `linwin/shared/` | Shared widgets, config, theme, and subprocess utilities |
|
|
121
|
+
| `tests/` | pytest test suite (106 tests) |
|
|
122
|
+
| `config.json` | Default configuration |
|
|
123
|
+
|
|
124
|
+
## Setup Phases
|
|
125
|
+
|
|
126
|
+
**Startup:** Auto-detects system hardware, runs verification checks, and presents a setup proposal with recommended configuration for user approval.
|
|
127
|
+
|
|
128
|
+
**Phase 1 (Windows):** Validates prerequisites, enables WSL and Virtual Machine Platform features, prompts for reboot if needed.
|
|
129
|
+
|
|
130
|
+
**Phase 2 (Windows + Linux):** Updates WSL, installs Ubuntu, exports/imports distro to target drive, writes `.wslconfig`, enables systemd, installs apt and snap packages, configures xrdp with XFCE4 desktop.
|
|
131
|
+
|
|
132
|
+
**Verification:** Checks all Windows features, distro registration, WSL version, systemd, packages, WSLg display, xrdp service, and drive mounts.
|
|
133
|
+
|
|
134
|
+
## Detailed Guide
|
|
135
|
+
|
|
136
|
+
See [wsl_ubuntu_gnome.md](wsl_ubuntu_gnome.md) for the full manual guide with GUI comparison (WSLg vs. external X servers), troubleshooting, security notes, and decision checklist.
|
|
137
|
+
|
|
138
|
+
## Launching Apps
|
|
139
|
+
|
|
140
|
+
After setup, use the launcher screen to open apps directly, or connect via Remote Desktop:
|
|
141
|
+
|
|
142
|
+
```powershell
|
|
143
|
+
mstsc /v:127.0.0.1:3390
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Individual apps can also be launched from an Ubuntu terminal:
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
nautilus & # File Manager
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Apps appear as native Windows windows via WSLg. A background keepalive process ensures the WSL VM stays running between app launches.
|
linwin-0.4.0/README.md
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
# linwin
|
|
2
|
+
|
|
3
|
+
Automated TUI (Text User Interface) to install Ubuntu on Windows 11 via WSL2, with the distro stored on a dedicated SSD drive. Includes WSLg for seamless Linux GUI apps on Windows and xrdp for full XFCE4 desktop access via Remote Desktop.
|
|
4
|
+
|
|
5
|
+
## Prerequisites
|
|
6
|
+
|
|
7
|
+
- **Windows 11** (or Windows 10 build 19044+)
|
|
8
|
+
- **Hardware virtualization** enabled in BIOS/UEFI (Intel VT-x or AMD-V)
|
|
9
|
+
- **Administrator access** on Windows
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```powershell
|
|
14
|
+
scripts\setup_wsl.bat
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
This launches an interactive terminal UI that:
|
|
18
|
+
|
|
19
|
+
- Auto-detects system hardware (RAM, CPUs, drives) and proposes an optimized configuration
|
|
20
|
+
- Scans drives and recommends the best one for WSL storage (NVMe > SSD > HDD)
|
|
21
|
+
- Runs startup verification and shows any issues before setup begins
|
|
22
|
+
- Lets you review and adjust all settings (distro, resources, packages)
|
|
23
|
+
- Enables WSL and Virtual Machine Platform features
|
|
24
|
+
- Installs Ubuntu, exports/imports to your chosen drive
|
|
25
|
+
- Writes `.wslconfig` with your resource limits
|
|
26
|
+
- Runs Linux-side setup (systemd, apt packages, snap packages, xrdp)
|
|
27
|
+
- Configures xrdp with XFCE4 desktop for Remote Desktop access
|
|
28
|
+
- Maintains a WSL keepalive process to prevent VM shutdown between sessions
|
|
29
|
+
- Verifies everything with a PASS/FAIL dashboard
|
|
30
|
+
|
|
31
|
+
The TUI handles admin elevation, the reboot boundary, and cross-WSL Linux setup automatically. After setup, a launcher screen lets you open apps, launch a terminal, or connect via Remote Desktop.
|
|
32
|
+
|
|
33
|
+
For quick reruns without dependency installation:
|
|
34
|
+
|
|
35
|
+
```powershell
|
|
36
|
+
run.bat
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Linux TUI
|
|
40
|
+
|
|
41
|
+
A standalone Linux TUI is also available for running inside WSL Ubuntu directly:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
pip3 install textual
|
|
45
|
+
python3 -m linwin.linux
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
A headless mode is available for non-interactive automation:
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
python3 -m linwin.linux --headless --step enable-systemd
|
|
52
|
+
python3 -m linwin.linux --headless --step install-packages
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Keyboard Shortcuts
|
|
56
|
+
|
|
57
|
+
| Key | Action |
|
|
58
|
+
|-----|--------|
|
|
59
|
+
| 1-9 | Select numbered actions on any screen |
|
|
60
|
+
| Escape | Quit app / Cancel current screen |
|
|
61
|
+
| Ctrl+Q | Quit app |
|
|
62
|
+
| Click | Select action links and options |
|
|
63
|
+
|
|
64
|
+
## Configuration
|
|
65
|
+
|
|
66
|
+
Edit `config.json` before running, or use the built-in Configure Settings screen. On first launch, the TUI auto-detects your system profile and proposes sensible defaults:
|
|
67
|
+
|
|
68
|
+
| Field | Default | Description |
|
|
69
|
+
|-------|---------|-------------|
|
|
70
|
+
| `distroName` | `Ubuntu-22.04` | Distro to install via `wsl --install` |
|
|
71
|
+
| `distroImportName` | `Ubuntu` | Name after export/import |
|
|
72
|
+
| `wslInstallPath` | auto-detected | Where to store the distro VHD |
|
|
73
|
+
| `wslDriveLetter` | auto-detected | Best available drive (NVMe > SSD > HDD) |
|
|
74
|
+
| `wslconfig.memory` | RAM ÷ 4 | RAM limit for WSL2 (min 4 GB) |
|
|
75
|
+
| `wslconfig.processors` | CPUs ÷ 2 | CPU cores for WSL2 (min 1) |
|
|
76
|
+
| `wslconfig.swap` | RAM ÷ 8 | Swap size (min 4 GB) |
|
|
77
|
+
| `wslconfig.defaultVhdSize` | `512GB` | Max VHD size |
|
|
78
|
+
| `snaps` | *(empty)* | Snap packages to install (configure via editor) |
|
|
79
|
+
| `aptPackages` | nautilus, x11-apps, xfce4, xfce4-terminal, xrdp, dbus-x11 | Apt packages to install |
|
|
80
|
+
| `xrdpPort` | `3390` | Port for xrdp (avoids conflict with Windows RDP on 3389) |
|
|
81
|
+
|
|
82
|
+
## Project Structure
|
|
83
|
+
|
|
84
|
+
| Path | Description |
|
|
85
|
+
|------|-------------|
|
|
86
|
+
| `run.bat` | Quick launch -- runs the TUI without dependency installation |
|
|
87
|
+
| `scripts/setup_wsl.bat` | Full setup -- installs uv, Python, dependencies, then launches the TUI |
|
|
88
|
+
| `scripts/test.bat` | Run the test suite |
|
|
89
|
+
| `linwin/windows/` | Windows TUI package -- startup verification, auto-config, setup with live progress |
|
|
90
|
+
| `linwin/linux/` | Linux TUI package for WSL-side setup (also supports `--headless`) |
|
|
91
|
+
| `linwin/shared/` | Shared widgets, config, theme, and subprocess utilities |
|
|
92
|
+
| `tests/` | pytest test suite (106 tests) |
|
|
93
|
+
| `config.json` | Default configuration |
|
|
94
|
+
|
|
95
|
+
## Setup Phases
|
|
96
|
+
|
|
97
|
+
**Startup:** Auto-detects system hardware, runs verification checks, and presents a setup proposal with recommended configuration for user approval.
|
|
98
|
+
|
|
99
|
+
**Phase 1 (Windows):** Validates prerequisites, enables WSL and Virtual Machine Platform features, prompts for reboot if needed.
|
|
100
|
+
|
|
101
|
+
**Phase 2 (Windows + Linux):** Updates WSL, installs Ubuntu, exports/imports distro to target drive, writes `.wslconfig`, enables systemd, installs apt and snap packages, configures xrdp with XFCE4 desktop.
|
|
102
|
+
|
|
103
|
+
**Verification:** Checks all Windows features, distro registration, WSL version, systemd, packages, WSLg display, xrdp service, and drive mounts.
|
|
104
|
+
|
|
105
|
+
## Detailed Guide
|
|
106
|
+
|
|
107
|
+
See [wsl_ubuntu_gnome.md](wsl_ubuntu_gnome.md) for the full manual guide with GUI comparison (WSLg vs. external X servers), troubleshooting, security notes, and decision checklist.
|
|
108
|
+
|
|
109
|
+
## Launching Apps
|
|
110
|
+
|
|
111
|
+
After setup, use the launcher screen to open apps directly, or connect via Remote Desktop:
|
|
112
|
+
|
|
113
|
+
```powershell
|
|
114
|
+
mstsc /v:127.0.0.1:3390
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
Individual apps can also be launched from an Ubuntu terminal:
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
nautilus & # File Manager
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Apps appear as native Windows windows via WSLg. A background keepalive process ensures the WSL VM stays running between app launches.
|
linwin-0.4.0/config.json
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"distroName": "Ubuntu-22.04",
|
|
3
|
+
"distroImportName": "Ubuntu",
|
|
4
|
+
"wslInstallPath": "V:\\WSL\\Ubuntu",
|
|
5
|
+
"wslDriveLetter": "V",
|
|
6
|
+
"wslconfig": {
|
|
7
|
+
"memory": "15GB",
|
|
8
|
+
"processors": 10,
|
|
9
|
+
"swap": "7GB",
|
|
10
|
+
"swapFile": "V:\\WSL\\swap.vhdx",
|
|
11
|
+
"guiApplications": true,
|
|
12
|
+
"defaultVhdSize": "512GB"
|
|
13
|
+
},
|
|
14
|
+
"snaps": [
|
|
15
|
+
{
|
|
16
|
+
"name": "pycharm-community",
|
|
17
|
+
"classic": true
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"name": "firefox",
|
|
21
|
+
"classic": false
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
"optionalApps": [
|
|
25
|
+
{
|
|
26
|
+
"id": "pycharm-community",
|
|
27
|
+
"display_name": "PyCharm Community",
|
|
28
|
+
"command": "pycharm-community",
|
|
29
|
+
"install_method": "snap",
|
|
30
|
+
"classic": true
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"id": "firefox",
|
|
34
|
+
"display_name": "Firefox",
|
|
35
|
+
"command": "firefox",
|
|
36
|
+
"install_method": "snap",
|
|
37
|
+
"classic": false
|
|
38
|
+
}
|
|
39
|
+
],
|
|
40
|
+
"aptPackages": [
|
|
41
|
+
"nautilus",
|
|
42
|
+
"x11-apps",
|
|
43
|
+
"xfce4",
|
|
44
|
+
"xfce4-terminal",
|
|
45
|
+
"xrdp",
|
|
46
|
+
"dbus-x11"
|
|
47
|
+
],
|
|
48
|
+
"enableSystemd": true,
|
|
49
|
+
"xrdpPort": 3390
|
|
50
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""linwin — TUI setup tool for WSL2 Ubuntu with WSLg GUI and xrdp desktop."""
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Entry point for ``python -m linwin``.
|
|
2
|
+
|
|
3
|
+
Delegates to the Windows or Linux TUI based on the current platform.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def main() -> None:
|
|
10
|
+
if sys.platform == "win32":
|
|
11
|
+
from linwin.windows.__main__ import main as win_main
|
|
12
|
+
win_main()
|
|
13
|
+
else:
|
|
14
|
+
from linwin.linux.__main__ import main as linux_main
|
|
15
|
+
linux_main()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
if __name__ == "__main__":
|
|
19
|
+
main()
|
|
Binary file
|
|
Binary file
|
|
File without changes
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Linux-side TUI entry point for WSL Ubuntu setup.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
python3 -m linwin.linux # Interactive TUI
|
|
6
|
+
python3 -m linwin.linux --headless --step enable-systemd # Headless: enable systemd
|
|
7
|
+
python3 -m linwin.linux --headless --step install-packages # Headless: install packages
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
import argparse
|
|
11
|
+
import asyncio
|
|
12
|
+
import json
|
|
13
|
+
import sys
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
from ..shared.headless_protocol import emit_error, emit_log, emit_task
|
|
17
|
+
from ..shared.setup_logging import setup_logging
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def find_config() -> dict:
|
|
21
|
+
"""Load config from the pref DB, or return defaults if unavailable.
|
|
22
|
+
|
|
23
|
+
Inside WSL the ``pref`` package may not be installed, so fall back
|
|
24
|
+
to defaults.
|
|
25
|
+
"""
|
|
26
|
+
try:
|
|
27
|
+
from ..shared.config import load_config
|
|
28
|
+
return load_config().to_dict()
|
|
29
|
+
except ImportError:
|
|
30
|
+
# pref not installed (running inside WSL) — use defaults.
|
|
31
|
+
from ..shared.config import SetupConfig
|
|
32
|
+
return SetupConfig().to_dict()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _run_task(task_id: str, coro, success_msg: str = "") -> bool:
|
|
36
|
+
"""Run an async task with structured headless output. Returns True on success."""
|
|
37
|
+
emit_task(task_id, "running")
|
|
38
|
+
result = asyncio.run(coro)
|
|
39
|
+
if hasattr(result, "ok"):
|
|
40
|
+
if getattr(result, "skipped", False):
|
|
41
|
+
emit_task(task_id, "done")
|
|
42
|
+
emit_log(result.message)
|
|
43
|
+
elif result.ok:
|
|
44
|
+
emit_task(task_id, "done")
|
|
45
|
+
emit_log(success_msg or result.message)
|
|
46
|
+
else:
|
|
47
|
+
emit_task(task_id, "failed")
|
|
48
|
+
emit_error(result.message)
|
|
49
|
+
return False
|
|
50
|
+
else:
|
|
51
|
+
# Boolean or other non-TaskResult return value
|
|
52
|
+
emit_task(task_id, "done")
|
|
53
|
+
return True
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def headless_enable_systemd(config: dict) -> int:
|
|
57
|
+
"""Enable systemd in wsl.conf. Delegates to the async implementation in tasks.systemd."""
|
|
58
|
+
from .tasks.systemd import enable_systemd
|
|
59
|
+
|
|
60
|
+
if not _run_task("enable_systemd", enable_systemd()):
|
|
61
|
+
return 1
|
|
62
|
+
return 0
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def headless_install_packages(config: dict) -> int:
|
|
66
|
+
"""Install apt packages, snaps, verify WSLg. Delegates to async task modules."""
|
|
67
|
+
from .tasks import apt, snaps, wslg
|
|
68
|
+
from ..shared.config import SnapPackage
|
|
69
|
+
|
|
70
|
+
exit_code = 0
|
|
71
|
+
|
|
72
|
+
# apt update & upgrade
|
|
73
|
+
if not _run_task("apt_update", apt.apt_update()):
|
|
74
|
+
exit_code = 1
|
|
75
|
+
if not _run_task("apt_upgrade", apt.apt_upgrade()):
|
|
76
|
+
exit_code = 1
|
|
77
|
+
|
|
78
|
+
# apt packages
|
|
79
|
+
for pkg in config.get("aptPackages", []):
|
|
80
|
+
if not _run_task(f"apt_{pkg}", apt.install_apt_package(pkg)):
|
|
81
|
+
exit_code = 1
|
|
82
|
+
|
|
83
|
+
# Setup snapd
|
|
84
|
+
emit_task("setup_snapd", "running")
|
|
85
|
+
systemd_ok = asyncio.run(snaps.check_systemd_running())
|
|
86
|
+
if not systemd_ok:
|
|
87
|
+
emit_task("setup_snapd", "failed")
|
|
88
|
+
emit_error("systemd not running. Snaps require systemd + WSL restart.")
|
|
89
|
+
exit_code = 1
|
|
90
|
+
else:
|
|
91
|
+
result = asyncio.run(snaps.ensure_snapd())
|
|
92
|
+
if result.ok:
|
|
93
|
+
emit_task("setup_snapd", "done")
|
|
94
|
+
emit_log(result.message)
|
|
95
|
+
else:
|
|
96
|
+
emit_task("setup_snapd", "failed")
|
|
97
|
+
emit_error(result.message)
|
|
98
|
+
exit_code = 1
|
|
99
|
+
|
|
100
|
+
# Install optional apps (snap and apt; custom skipped)
|
|
101
|
+
for app_info in config.get("optionalApps", config.get("snaps", [])):
|
|
102
|
+
method = app_info.get("install_method", "snap")
|
|
103
|
+
app_id = app_info.get("id", app_info.get("name", ""))
|
|
104
|
+
if method == "snap":
|
|
105
|
+
snap = SnapPackage(
|
|
106
|
+
name=app_id,
|
|
107
|
+
classic=app_info.get("classic", True),
|
|
108
|
+
)
|
|
109
|
+
if not _run_task(f"snap_{snap.name}", snaps.install_snap(snap)):
|
|
110
|
+
exit_code = 1
|
|
111
|
+
elif method == "apt":
|
|
112
|
+
if not _run_task(f"apt_opt_{app_id}", apt.install_apt_package(app_id)):
|
|
113
|
+
exit_code = 1
|
|
114
|
+
|
|
115
|
+
# Verify WSLg
|
|
116
|
+
emit_task("verify_wslg", "running")
|
|
117
|
+
wslg_result = asyncio.run(wslg.verify_wslg())
|
|
118
|
+
emit_log(f"DISPLAY={wslg_result.display_value or '(not set)'}")
|
|
119
|
+
emit_log(f"/mnt/wslg: {'exists' if wslg_result.wslg_dir_exists else 'not found'}")
|
|
120
|
+
wslg_ok = wslg_result.display_set and wslg_result.wslg_dir_exists
|
|
121
|
+
emit_task("verify_wslg", "done" if wslg_ok else "failed")
|
|
122
|
+
|
|
123
|
+
return exit_code
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def headless_configure_xrdp(config: dict) -> int:
|
|
127
|
+
"""Install xrdp + xfce4, configure port/session, enable service.
|
|
128
|
+
|
|
129
|
+
Delegates to async task functions in tasks.xrdp. The enable_xrdp_service
|
|
130
|
+
call internally handles SSL permissions, systemd overrides, colord polkit,
|
|
131
|
+
logind delay, user linger, and GDM masking.
|
|
132
|
+
"""
|
|
133
|
+
from .tasks import xrdp
|
|
134
|
+
|
|
135
|
+
exit_code = 0
|
|
136
|
+
port = config.get("xrdpPort", 3390)
|
|
137
|
+
|
|
138
|
+
if not _run_task("xrdp_install", xrdp.install_xrdp()):
|
|
139
|
+
exit_code = 1
|
|
140
|
+
if not _run_task("xrdp_port", xrdp.configure_xrdp_port(port)):
|
|
141
|
+
exit_code = 1
|
|
142
|
+
if not _run_task("xrdp_session", xrdp.configure_xrdp_session()):
|
|
143
|
+
exit_code = 1
|
|
144
|
+
if not _run_task("xrdp_service", xrdp.enable_xrdp_service()):
|
|
145
|
+
exit_code = 1
|
|
146
|
+
|
|
147
|
+
return exit_code
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def main() -> None:
|
|
151
|
+
parser = argparse.ArgumentParser(description="WSL Ubuntu Setup TUI (Linux)")
|
|
152
|
+
parser.add_argument("--headless", action="store_true", help="Run without TUI (structured output)")
|
|
153
|
+
parser.add_argument("--step", choices=["enable-systemd", "install-packages", "configure-xrdp"],
|
|
154
|
+
help="Step to run (headless mode)")
|
|
155
|
+
args = parser.parse_args()
|
|
156
|
+
|
|
157
|
+
log = setup_logging()
|
|
158
|
+
|
|
159
|
+
config_data = find_config()
|
|
160
|
+
|
|
161
|
+
if args.headless:
|
|
162
|
+
log.info("Headless mode, step=%s", args.step)
|
|
163
|
+
try:
|
|
164
|
+
if args.step == "enable-systemd":
|
|
165
|
+
sys.exit(headless_enable_systemd(config_data))
|
|
166
|
+
elif args.step == "install-packages":
|
|
167
|
+
sys.exit(headless_install_packages(config_data))
|
|
168
|
+
elif args.step == "configure-xrdp":
|
|
169
|
+
sys.exit(headless_configure_xrdp(config_data))
|
|
170
|
+
else:
|
|
171
|
+
print("ERROR:--step required with --headless", flush=True)
|
|
172
|
+
sys.exit(1)
|
|
173
|
+
except SystemExit:
|
|
174
|
+
sys.stdout.flush()
|
|
175
|
+
raise
|
|
176
|
+
except Exception:
|
|
177
|
+
import traceback
|
|
178
|
+
tb = traceback.format_exc()
|
|
179
|
+
emit_error(tb)
|
|
180
|
+
log.error("Unhandled exception in headless mode:\n%s", tb)
|
|
181
|
+
sys.exit(1)
|
|
182
|
+
else:
|
|
183
|
+
# Interactive TUI mode
|
|
184
|
+
from ..shared.config import SetupConfig
|
|
185
|
+
from .app import LinuxSetupApp
|
|
186
|
+
|
|
187
|
+
log.info("Linux interactive TUI starting")
|
|
188
|
+
config = SetupConfig.from_dict(config_data)
|
|
189
|
+
app = LinuxSetupApp(config)
|
|
190
|
+
app.run()
|
|
191
|
+
log.info("Linux TUI exited")
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
if __name__ == "__main__":
|
|
195
|
+
main()
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"""Linux TUI Application — main Textual app for WSL Ubuntu setup."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from ..shared.base_app import BaseSetupApp
|
|
6
|
+
from .screens.welcome import WelcomeScreen
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class LinuxSetupApp(BaseSetupApp):
|
|
10
|
+
"""Textual TUI for Ubuntu package setup inside WSL."""
|
|
11
|
+
|
|
12
|
+
TITLE = "WSL Ubuntu Setup (Linux)"
|
|
13
|
+
|
|
14
|
+
def on_mount(self) -> None:
|
|
15
|
+
self.push_screen(WelcomeScreen(self._config))
|
|
File without changes
|