Portable Linux Wine Launcher
  • JavaScript 89.9%
  • CSS 6%
  • HTML 2.6%
  • Shell 1.5%
Find a file
2026-10-01 23:14:50 +02:00
files/translations sync fixes: 2026-10-01 20:57:45 +02:00
node_modules add installer to build package 2026-09-25 20:38:23 +02:00
renderer rounded arrows on sync icon 2026-10-01 23:14:34 +02:00
src sync fixes: 2026-10-01 20:57:45 +02:00
tools add support for directly launching windows exes from file exporer using .desktop shortcut 2026-09-30 17:32:30 +02:00
.gitignore add installer to build package 2026-09-25 20:38:23 +02:00
build.sh better installer and portable mode 2026-09-25 21:15:17 +02:00
development.md optimize sync & add accept popup 2026-10-01 19:57:05 +02:00
package-lock.json add installer to build package 2026-09-25 20:38:23 +02:00
package.json bump to 0.1.3 2026-10-01 23:14:50 +02:00
README.md optimize sync & add accept popup 2026-10-01 19:57:05 +02:00
run.sh update icon & change PLGL to PLWL 2026-09-25 20:04:00 +02:00

PLWL — Portable Linux Wine Launcher

A portable launcher for Windows games and applications on Linux.

Portable layout

Everything the launcher needs lives next to the executable:

./files/
  wines/           imported Wine / Proton builds
  wineprefixes/    wine prefixes
  apps/            imported app folders (optional)
  data/            launcher config (apps.json, settings.json)
  translations/    translation files

Running

npm install
npm start

Building & installing

./build.sh produces build/PLWL-<version>.tar.gz: the Electron runtime renamed to olcxja.plwl/ (binary olcxja.plwl/olcxja.plwl) with the whole app (code, renderer and bundled files/) packed into olcxja.plwl/resources/app.asar, plus the olcxja.plwl.png icon, install.sh and createshortcut.sh:

tar -xzf PLWL-<version>.tar.gz
cd PLWL
./install.sh        # copies everything to ~/.local/bin/olcxja.plwl/,
                    # installs the icon (olcxja.plwl.png) + shortcut
                    # removes the extraction folder, launches

./createshortcut.sh # portable mode: only installs the icon + shortcut

# or run without any shortcuts:
./olcxja.plwl/olcxja.plwl

Importing from a wine prefix

Besides copy/move/leave, an app can be imported from a wine prefix: pick the prefix, then the app folder inside its drive_c. Nothing is copied — paths are stored relative to the prefix, so wine sees the app as if it were normally installed on C:\ (registry entries pointing to C:\... keep working). Both layouts work: plain wine prefixes (drive_c at the top) and Proton-managed ones (drive_c under pfx/). The app stays tied to that prefix; removing the launcher entry never deletes files from the prefix. Imports keep the folder as it was: symlinks are recreated (rewritten to relative whenever the target is part of the launcher) and file permissions are carried over, so an imported game or build runs straight away.

"Open with PLWL" in the file manager

The desktop entry (MimeType + Exec %f) makes PLWL show up in Dolphin/Nautilus under Open With for Windows executables (.exe, .msi, .bat, .cmd, .lnk). Opening a file that way shows a small popup in the launcher where you pick the Wine/Proton version and the prefix for this run — both default to the launcher's defaults, nothing is imported into the library. A second launcher instance hands the path to the running one and exits (same single-instance mechanism the shortcuts use).

LAN syncing

Two PLWL installs on the same LAN can mirror each other's content - no cloud, no accounts. Open LAN syncing in the sidebar (under Settings):

  • Device ID - 24 characters, generated once per install (current timestamp + random characters) and re-rollable at any time.
  • Sync code - 8 characters, letters and digits, case-insensitive. A device only accepts a connection when the caller proves it knows its own code; each verification costs two seconds and a device may only have one in flight, so guessing over the LAN is pointless. Both the ID and the code can be re-rolled. A correct code is still not enough: the receiving device shows an Accept / Decline popup before anything moves, and without a click within a minute the sender is turned down. Declining (or ignoring) the popup never touches this machine and does not unpair the two devices.
  • Devices on your network - other PLWL installs, found over UDP; a device shows up only while PLWL is running there, and disappears a few seconds after it is closed.

Pick a device and choose what to push: portable apps, apps imported from a prefix, Wine/Proton builds, prefixes and Default settings. Only what is ticked travels - a sync without Default settings never touches the other machine's defaults. The code has to be typed once - an accepted code is stored next to that device ID, so every later sync needs no typing. If the other side re-rolled its code, the stored one is forgotten and the launcher asks for the new one instead of failing silently.

Both machines show what is happening. The sender gets a progress popup and the receiving device gets the same popup titled Receiving from X…, driven by the transfer as it arrives. Both show the current phase and unit, the files being moved, a sent/total counter per category (Apps: 2/5 · Prefixes: 1/2) and the transferred vs. planned size. The bar follows bytes, not file counts, and it belongs to the phase being worked on: hashing fills its own bar and the transfer starts again from empty, so nothing ever looks finished while it has just begun (the byte line next to it keeps the overall transferred vs. planned total). Both popups warn that the launcher must stay open, and a running sync is not cancellable from the outside: clicking the backdrop or pressing Escape does nothing. If the other device goes quiet for a few seconds (it hashes before it sends) the popup says Waiting for the other device… rather than closing; it closes itself when the transfer ends, or after fifteen minutes of complete silence when the other side has vanished.

The sending device is the source of truth: files that differ are overwritten, missing ones are written, and anything living only on the receiving side inside the synced trees (files/apps/, files/wines/, files/wineprefixes/, files/data/icons/) is deleted. Files are compared with a fast, collision-resistant hash (BLAKE2b-512, SHA-512/SHA-256 fallback); Force sync all files skips the comparison and overwrites everything instead. Hashes are remembered per file (size + modification time), so a repeat sync of a library that did not change reads nothing at all.

Wine builds and prefixes survive the trip. Permissions travel with a sync, so the proton, wine and wineserver binaries are still executable on the other machine (a build whose executable bit was lost is additionally repaired at startup, without re-importing it), and symlinks are sent as RELATIVE paths whenever the file they point at belongs to the launcher: a Proton prefix is full of links into its build, and the receiving side gets links that resolve to its own copy instead of the sender's /home/... folders. Links that leave the launcher on purpose (dosdevices drive mappings, a mounted drive, the user's home folder) stay absolute, and links into a folder that does not exist yet on the other machine are still written relative, so they start working the moment that folder appears. Wine and Proton rewrite a prefix's links with local absolute targets while they update it; PLWL relativizes them again on the wire, on import and before every run.

A sync also carries how the apps are set up: the Wine/Proton build and prefix each app runs in, its arguments, environment variables and DLL overrides, plus playtime and last played. The launcher's default Wine build and its default prefix travel with the Default settings category, and only if that build or prefix exists on the other machine - an id it does not have is ignored instead of leaving a dangling default behind. Playtime only ever grows - the higher counter wins - so pushing from a freshly installed machine cannot wipe a library's play history. Everything else stays local: language and window settings, logs, translations, and external (leave) apps - prefixes are always kept usable, so app entries pointing inside a prefix keep their prefix-relative paths and the receiving side never loses its own Main prefix.

Wine & Proton support

  • System Wine — uses the wine binary from PATH.
  • Imported builds — drop any wine or Proton build into files/wines/. The folder may contain the build directly, or one level of nesting (zip-extracted layouts are detected automatically). After the copy, PLWL rewires the build's absolute symlinks to relative ones and restores the executable bit on the binaries it needs to run (proton, wine, wineserver, winetricks), so a build that arrived from a zip or another machine works without further fiddling.
  • Proton without Steam — fully supported. Proton is invoked like Steam does it: STEAM_COMPAT_CLIENT_INSTALL_PATH points to a fake steam client folder, STEAM_COMPAT_DATA_PATH is a per-build compat folder, and the prefix lives at <compat>/pfx which PLWL links to its own prefix folder. No Steam installation required (GE builds work out of the box; for a few Valve builds that refuse to run without a client, PLWL fakes a minimal steam folder in files/data/fake-steam).

Translations

All UI text comes from JSON files in files/translations/. The data object carries the language names; texts maps keys to strings, with {0}, {1} ... placeholders filled at runtime.

The translations bundled with a release are launcher-owned: on every start they are refreshed into files/translations/ whenever their content changed, so launcher updates deliver new/changed keys to existing installs. Extra language files the bundle does not ship (user-added) are never touched.

Development

Codebase overview, conventions and rules for contributors: development.md.