diff --git a/.darklua.json b/.darklua.json
new file mode 100644
index 0000000..5cd0537
--- /dev/null
+++ b/.darklua.json
@@ -0,0 +1,26 @@
+{
+ "generator": { "name": "dense", "column_span": 175},
+ "bundle": {
+ "require_mode": {
+ "name" : "path",
+ "sources": {
+ "@Classes": "./src/Classes",
+ "@Configuration": "./src/Configuration",
+ "@Launcher" : "./src/Launcher",
+ "@Steam": "./src/Steam",
+ "@Utilities": "./src/Utilities",
+ "@Metadata": "./src/Metadata"
+ }
+ },
+ "excludes": ["@lune/**"]
+ },
+ "rules": [
+ "remove_comments",
+ "remove_spaces",
+ "remove_nil_declaration",
+ "compute_expression",
+ "remove_unused_if_branch",
+ "filter_after_early_return",
+ "remove_empty_do"
+ ]
+}
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..179e626
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,2 @@
+# LF normalization
+* text=auto
diff --git a/.luaurc b/.luaurc
new file mode 100644
index 0000000..dfc464c
--- /dev/null
+++ b/.luaurc
@@ -0,0 +1,14 @@
+{
+ "languageMode": "strict",
+ "lint": {
+ "*": false
+ },
+ "aliases": {
+ "Classes": "./src/Classes",
+ "Configuration": "./src/Configuration",
+ "Launcher" : "./src/Launcher",
+ "Steam": "./src/Steam",
+ "Utilities": "./src/Utilities",
+ "Metadata": "./src/Metadata"
+ }
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000..b486f95
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,13 @@
+{
+ "luau-lsp.require.mode": "relativeToFile",
+ "luau-lsp.require.directoryAliases": {
+ "@lune/": "~/.lune/.typedefs/0.8.8/",
+ "@Classes": "src/Classes",
+ "@Configuration": "src/Configuration",
+ "@Launcher" : "src/Launcher",
+ "@Steam": "src/Steam",
+ "@Utilities": "src/Utilities",
+ "@Metadata": "src/Metadata"
+},
+ "luau-lsp.sourcemap.enabled": false
+}
\ No newline at end of file
diff --git a/Makefile b/Makefile
index 970abf2..20c2f6b 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
# Metadata
-PROJECT_NAME=simplesteamtinker
-INSTALL_FOLDER_NAME=SimpleSteamTinker
+PROJECT_NAME=stweaks
+INSTALL_FOLDER_NAME=STWeaks
VERSION=indev
ifeq ($(PREFIX),)
@@ -8,56 +8,31 @@ ifeq ($(PREFIX),)
endif
BUILD_FOLDER := dist/
-.PHONY: system install uninstall clean local
-
-# -------------- System installation --------------
-# This will create a folder structure in dist that is compatible with most package managers.
-system:
- @echo "Packaging project for system installation..."
-# Executable
- mkdir -p $(BUILD_FOLDER)$(PREFIX)/bin
- install -Dm755 sst $(BUILD_FOLDER)$(PREFIX)/bin
-# Modules
- mkdir -p $(BUILD_FOLDER)$(PREFIX)/share/$(INSTALL_FOLDER_NAME)
- install -Dm644 main.lua $(BUILD_FOLDER)$(PREFIX)/share/$(INSTALL_FOLDER_NAME)/main.lua
- cp -r modules $(BUILD_FOLDER)$(PREFIX)/share/$(INSTALL_FOLDER_NAME)
-# UI
- mkdir -p $(BUILD_FOLDER)$(PREFIX)/share/$(INSTALL_FOLDER_NAME)/ui
- install -Dm644 ui/main.ui $(BUILD_FOLDER)$(PREFIX)/share/$(INSTALL_FOLDER_NAME)/ui/main.ui
-# Desktop file
- mkdir -p $(BUILD_FOLDER)$(PREFIX)/share/applications
- install -Dm644 assets/desktop/system.desktop $(BUILD_FOLDER)$(PREFIX)/share/applications/$(PROJECT_NAME).desktop
-# Icons
- mkdir -p $(BUILD_FOLDER)$(PREFIX)/share/icons/hicolor/256x256/apps
- mkdir -p $(BUILD_FOLDER)$(PREFIX)/share/icons/hicolor/scalable/apps
- install -Dm644 assets/icons/256x256.png $(BUILD_FOLDER)$(PREFIX)/share/icons/hicolor/256x256/apps/$(PROJECT_NAME).png
- install -Dm644 assets/icons/scalable.svg $(BUILD_FOLDER)$(PREFIX)/share/icons/hicolor/scalable/apps/$(PROJECT_NAME).svg
-
-# Should only be used as last resort, the end user should use a compatible package manager if possible instead.
-# This is only here for testing purposes, and also as a general "manual" on how to install the project for package maintainers.
-install: system
- @echo "Installing project to system..."
- cp -r $(BUILD_FOLDER)$(PREFIX)/bin/* $(PREFIX)/bin
- cp -r $(BUILD_FOLDER)$(PREFIX)/share/$(INSTALL_FOLDER_NAME) $(PREFIX)/share
- cp -r $(BUILD_FOLDER)$(PREFIX)/share/applications/* $(PREFIX)/share/applications
- cp -r $(BUILD_FOLDER)$(PREFIX)/share/icons/hicolor/256x256/apps/* $(PREFIX)/share/icons/hicolor/256x256/apps
- cp -r $(BUILD_FOLDER)$(PREFIX)/share/icons/hicolor/scalable/apps/* $(PREFIX)/share/icons/hicolor/scalable/apps
-# Update icon cache
- @echo "Updating icon cache..."
- gtk-update-icon-cache $(PREFIX)/share/icons/hicolor
-uninstall:
- @echo "Uninstalling project from system..."
- rm -ri $(PREFIX)/bin/sst
- rm -rI $(PREFIX)/share/$(INSTALL_FOLDER_NAME)
- rm -ri $(PREFIX)/share/applications/$(PROJECT_NAME).desktop
- rm -ri $(PREFIX)/share/icons/hicolor/256x256/apps/$(PROJECT_NAME).png
- rm -ri $(PREFIX)/share/icons/hicolor/scalable/apps/$(PROJECT_NAME).svg
-# Update icon cache
- @ echo "Updating icon cache..."
- gtk-update-icon-cache $(PREFIX)/share/icons/hicolor
-
-# -------------- Cleaning --------------
+.PHONY: build run check style clean
+
+# -------------- Packaging --------------
+$(BUILD_FOLDER)stweaks.luau: clean
+ @echo "Using DarkLua to bundle Luau code..."
+ darklua process init.luau $(BUILD_FOLDER)stweaks.luau -v
+
+build: $(BUILD_FOLDER)stweaks.luau
+ @echo "Compiling project..."
+ lune build $(BUILD_FOLDER)stweaks.luau
+
+# -------------- Run --------------
+run:
+ @echo "Running project..."
+ lune run init
+
+# -------------- Extra --------------
+check:
+ @echo "Running linter..."
+ selene src/
+
+style:
+ @echo "Running code formatter..."
+ stylua src/
clean:
@echo "Cleaning up..."
- rm -rf $(BUILD_FOLDER)
+ rm -rf "$(BUILD_FOLDER)"
diff --git a/README.md b/README.md
deleted file mode 100644
index c0c140e..0000000
--- a/README.md
+++ /dev/null
@@ -1,184 +0,0 @@
-> [!WARNING]
-> This project may be deprecated in the future, as I have been recently working on a **massive** rewrite, avoiding LGI and focusing more on portability and stability this time around.
-___
-
-> [!IMPORTANT]
-> SimpleSteamTinker is still in ***HEAVY*** development and very unfinished.
-> Expect an incomplete interface and lots of missing/unstable features.
->
-> If SteamTinkerLaunch is fast enough for you and fits your needs, you should not use this tool right now.
->
-> With that being said, I'd still very much appreciate people testing SimpleSteamTinker and reporting [issues](https://github.com/JordanViknar/SimpleSteamTinker/issues) or contributing.
-
-
-

-
SimpleSteamTinker
-
A work-in-progress simple, fast, and modern Adwaita alternative to SteamTinkerLaunch.
-
-

-
-

-

-

-
-
-

-
-
-## Description
-
-### Situation
-
-I like [SteamTinkerLaunch](https://github.com/sonic2kk/steamtinkerlaunch). It is for me one of the biggest tools to have ever been created for Linux gaming.
-
-However, it presents many flaws in my eyes : it is slow, impractical to use, and not user-friendly.
-
-I want Linux Gaming to be available to everyone, including any non-technical users. In my opinion, SteamTinkerLaunch's interface is not friendly to newcomers.
-
-Finally, my curiosity once led me to look inside its source code, only to be met with a single, extremely long (26k+ lines) Bash script. At that point, I decided there should be an alternative to it.
-
-*And thus came SimpleSteamTinker...*
-
-### Goal
-
-*TL;DR : Simple, Fast, Modern & Friendly*
-
-SimpleSteamTinker aims to be only an alternative solution to SteamTinkerLaunch, not to replace it : the more complex features it provides that aren't used by the average user won't be implemented.
-
-Using Lua, a fast and simple language, and Adwaita, a modern user-friendly interface system, the goal of this project is to have a clean and easy but *powerful* way of launching Steam games with custom options and tools.
-
-It takes inspiration from [Bottles](https://github.com/bottlesdevs/Bottles) and Adwaita applications in general.
-
-Additionally, it is considered SimpleSteamTinker should work *with* Steam rather than hack/replace its features. Anything that can be easily done through Steam will not be reimplemented (Proton version management for example. If you want to use GE-Proton, [ProtonPlus](https://github.com/Vysp3r/ProtonPlus) and [ProtonUp-Qt](https://github.com/DavidoTek/ProtonUp-Qt) are recommended).
-
-And finally, as a side goal : prove that Lua can *also* be used for developing modern applications, just like for example Python (which I often see in Adwaita apps).
-
-## Installation
-
-### Arch Linux-based distributions
-
-SimpleSteamTinker can be installed from Arch Linux, Manjaro, and variants through the provided [PKGBUILD](./install/archlinux/PKGBUILD).
-
-**Always** download the latest version of the PKGBUILD, then use **makepkg** in its directory.
-
-It will eventually be made available on the AUR once the program is complete enough.
-
-To enable a game in SimpleSteamTinker with this installation method, use this text as launch options in Steam :
-```bash
-sst %command%
-```
-
-### Packaging for other distributions
-
-Here are informations which might be useful to you :
-
-SimpleSteamTinker is meant to be installed in `/usr/share/SimpleSteamTinker` (except `sst` which goes into `/usr/bin`).
-The system desktop file goes in `/usr/share/applications` and the icons go in `/usr/share/icons` like many applications.
-
-Most of the file structure installation steps can be achieved with the Makefile.
-It is recommended to use it if possible in your packaging system.
-
-It depends on :
-- Lua 5.4
-- [LGI](https://github.com/lgi-devs/lgi) (development version **specifically**) + [GTK4](https://www.gtk.org/) + [libadwaita](https://gnome.pages.gitlab.gnome.org/libadwaita/)
-- Steam installation, with a user configured and games installed.
-- [LuaFileSystem](https://github.com/lunarmodules/luafilesystem)
-- [LuaSocket](https://github.com/lunarmodules/luasocket) + [LuaSec](https://github.com/lunarmodules/luasec)
-- [dkjson](https://dkolf.de/src/dkjson-lua.fsl/home)
-- [xclip](https://github.com/astrand/xclip) *(Might be replaced/made optional in the future.)*
-- [libnotify](https://gitlab.gnome.org/GNOME/libnotify)
-
-And optionally (if they're missing, their related features will simply be disabled) :
-
-[GameMode](https://github.com/FeralInteractive/gamemode), [MangoHud](https://github.com/flightlessmango/MangoHud), [Mesa](https://www.mesa3d.org/) *(for Zink support)*, [switcheroo-control](https://gitlab.freedesktop.org/hadess/switcheroo-control) and [Gamescope](https://github.com/ValveSoftware/gamescope).
-
-## Development
-
-Simply install the project's dependencies described earlier and clone it.
-
-To test the launch of Steam games, insert in their launch options :
-```bash
-/PATH/TO/CLONED/REPO/sst %command%
-```
-For example, in my case `/PATH/TO/CLONED/REPO/sst` is `/home/jordan/Programmation/SimpleSteamTinker/sst`.
-
-Additionally, if you want to perform UI-related changes, you'll need [Blueprint](https://gitlab.gnome.org/jwestman/blueprint-compiler).
-If, in the future, LGI becomes stable enough with GTK4 to use it directly to generate the UI, Blueprint might be dropped.
-
-*Note : A desktop file won't be provided with this method, and icons won't be installed.*
-
-## Comparison
-
-### Development
-
-| Development | SteamTinkerLaunch | SimpleSteamTinker |
-| --- | --- | --- |
-| Language | Bash | Lua |
-| Code | A single file with 26k lines | Organised in modules and commented |
-| UI | Directly generated by the code | (Mostly) done in separate GTK Blueprint files |
-| Interface system | yad + GTK3 | LGI + GTK4 + libadwaita |
-| License | GPL-3.0 | MPL-2.0 |
-
-### Interface
-
-| Features (interface) | SteamTinkerLaunch | SimpleSteamTinker |
-| --- | --- | --- |
-| Launch speed (on primary laptop) | ❌ (25 seconds) | ✅ (1 second) |
-| Game list | ❌ | ✅ |
-| Game status | ❌ | ✅ |
-| ProtonDB integration | ✅ | ✅ |
-| Launch Button | ✅ | ✅ |
-| Steam AppID | ✅ | ✅ |
-| Notifications | ✅ | ✅ |
-| Pre-game launch window | ✅ | ❌ *(Not implemented to speed up game startup and not get in the user's way. Might change in the future.)* |
-| Categories | ✅ | ❌ |
-| Custom default game config | ✅ | ❌ |
-| Game location | ~ *(impractical)* | ✅ |
-| Compatdata location | ❌ | ✅ |
-| Help pages *(ProtonDB, PCGamingWiki, etc.)* | ~ *(impractical)* | ✅ |
-
-### Tools, settings, etc.
-
-*(Note : Native Linux features/tools/settings are prioritized over alternatives designed for Windows.)*
-
-| Features (tools, settings, etc.) | SteamTinkerLaunch | SimpleSteamTinker |
-| --- | --- | --- |
-| Non-Steam game support | ✅ | ❌ *([Bottles](https://github.com/bottlesdevs/Bottles) recommended)* |
-| ~~Proton version management~~ | ✅ | ❌ *([ProtonPlus](https://github.com/Vysp3r/ProtonPlus) recommended)* |
-| dGPU management | ✅ | ✅ |
-| GameMode | ✅ | ✅ |
-| Custom launch system | ✅ | 🚧 *(Planned)* |
-| Winetricks | ✅ | ❌ *(Planned ?)* |
-| Protontricks | ❌ | ❌ *(Planned ?)* |
-| Proton settings | ✅ | ✅ |
-| DXVK settings | ✅ | 🚧 *(Planned)* |
-| VKD3D settings | ✅ | 🚧 *(Planned)* |
-| MangoHud | ✅ | ✅ |
-| Gamescope | ✅ | ✅ |
-| Shader support (ReShade/vkBasalt) | ✅ | 🚧 *(Planned)* |
-| SDL Wayland video driver | ✅ | ✅ |
-| Zink | ✅ | ✅ |
-| PulseAudio latency | ✅ | 🚧 *(Planned)* |
-| Vortex | ✅ | ❌ |
-| Mod Organizer 2 | ✅ | ❌ |
-| HedgeModManager | ✅ | ❌ |
-| geo-11 3D Driver | ✅ | ❌ |
-| SpecialK | ✅ | ❌ |
-| FlawlessWidescreen | ✅ | ❌ |
-| Stereo3D | ✅ | ❌ |
-| RADV Perftest Options | ✅ | ❌ *(Cannot test properly due to lack of an AMD GPU)* |
-| Steam Linux Runtime toggle | ✅ | ❌ |
-| steamwebhelper toggle | ✅ | ❌ |
-| obs-gamecapture | ✅ | ✅ |
-| ~~Nyrna~~ | ✅ | ❌ *(Waiting for Wayland support...)* |
-| ~~ReplaySorcery~~ | ✅ | ❌ *(Unmaintaned)* |
-| ~~Boxtron~~ | ✅ | ❌ *([ProtonPlus](https://github.com/Vysp3r/ProtonPlus) recommended)* |
-| ~~Roberta~~ | ✅ | ❌ *([ProtonPlus](https://github.com/Vysp3r/ProtonPlus) recommended)* |
-| ~~Luxtorpeda~~ | ✅ | ❌ *([ProtonPlus](https://github.com/Vysp3r/ProtonPlus) recommended)* |
-| Network monitoring and control | ✅ | 🚧 *(Planned)* |
-| Discord Rich Presence | ✅ | 🚧 *(Planned)* |
-
-## Bug Reports / Contributions / Suggestions
-You can report bugs or suggest features by making an issue, or you can contribute to this program directly by forking it and then sending a pull request.
-
-Any help will be very much appreciated. Thank you.
diff --git a/assets/desktop/system.desktop b/assets/desktop/system.desktop
deleted file mode 100755
index c2e7d71..0000000
--- a/assets/desktop/system.desktop
+++ /dev/null
@@ -1,8 +0,0 @@
-[Desktop Entry]
-Name=SimpleSteamTinker
-Icon=simplesteamtinker
-Comment=A work-in-progress simple, fast and modern Adwaita alternative to SteamTinkerLaunch.
-Exec=/usr/bin/sst
-Terminal=false
-Type=Application
-Categories=Game;
diff --git a/assets/icons/256x256.png b/assets/icons/256x256.png
deleted file mode 100644
index 1796bc0..0000000
Binary files a/assets/icons/256x256.png and /dev/null differ
diff --git a/assets/icons/scalable.svg b/assets/icons/scalable.svg
deleted file mode 100644
index 0cec0ac..0000000
--- a/assets/icons/scalable.svg
+++ /dev/null
@@ -1,144 +0,0 @@
-
-
\ No newline at end of file
diff --git a/assets/screenshots/ViewLight.ora b/assets/screenshots/ViewLight.ora
deleted file mode 100644
index 54dd31a..0000000
Binary files a/assets/screenshots/ViewLight.ora and /dev/null differ
diff --git a/assets/screenshots/ViewLight.png b/assets/screenshots/ViewLight.png
deleted file mode 100644
index 7e9f2f4..0000000
Binary files a/assets/screenshots/ViewLight.png and /dev/null differ
diff --git a/docs/STEAM_PROBLEMS_LIST.md b/docs/STEAM_PROBLEMS_LIST.md
deleted file mode 100644
index 779fa78..0000000
--- a/docs/STEAM_PROBLEMS_LIST.md
+++ /dev/null
@@ -1,47 +0,0 @@
-For *"fun"*, I have compiled a list of every single problem I consider shouldn't have happened that I've encountered anyways, going through Steam's configuration files.
-
-I put them in order, from ***most annoying*** to *least annoying*.
-
-This is meant to warn people to not create code that may cross these issues.
-
-___
-
-# Problems
-
-## Linux games don't get removed from *compat.vdf* when going back from their Windows version.
-
-### Conclusion
-
-We have no way from Steam's configuration files to know if a game that ever had a Windows version installed now has its Linux version installed.
-
-I looked everywhere I could for an alternative configuration file (or a file that could be useful through another method), but came empty-handed.
-
-### Workaround
-
-We check for Linux executables or shell scripts in every Windows game's root directory.
-
-We previously used in a loop
-
-```lua
-os.execute("file "..location.."/"..file.." | grep -e 'Linux' -e 'shell' &> /dev/null")
-```
-
-to detect those.
-
-However, ironically, this method led us into another problem later on, which required us to scrap the os.execute approach.
-
-## *libraryfolders.vdf* doesn't get immediately updated upon game installation/uninstallation.
-
-### Conclusion
-
-This problem effectively makes it completely unreliable (and thus useless) to gather the game IDs stored inside it.
-
-### Workaround
-
-As a workaround, we look in the library folders for *appmanifest_ID.acf* files, and use them to guess the installed game IDs and gather the data.
-
-## Steam can get stuck trying to start a game when using *os.execute* many times.
-
-### Workaround
-
-Use os.execute as little as possible if launching a game.
diff --git a/init.luau b/init.luau
new file mode 100644
index 0000000..bae5fd0
--- /dev/null
+++ b/init.luau
@@ -0,0 +1 @@
+return require("./src").start()
diff --git a/install/archlinux/PKGBUILD b/install/archlinux/PKGBUILD
deleted file mode 100644
index 658d7fb..0000000
--- a/install/archlinux/PKGBUILD
+++ /dev/null
@@ -1,48 +0,0 @@
-# Maintainer: JordanViknar
-
-pkgname=simplesteamtinker-git
-pkgver=r44.84be422
-pkgrel=1
-pkgdesc="A work-in-progress simple, fast and modern Adwaita alternative to SteamTinkerLaunch."
-arch=('x86_64' 'i686' 'armv7h' 'aarch64')
-url=https://github.com/JordanViknar/SimpleSteamTinker
-license=('MPL2')
-makedepends=(
- 'git'
- 'make'
-)
-depends=(
- 'lua'
- 'lua-lgi-git' # Used for GTK (git version because latest release neither supports Libadwaita nor GTK4 and is pretty old)
- 'libadwaita'
- 'gtk4'
- 'lua-filesystem' # Used for some filesystem operations
- 'lua-socket' # Used for network operations
- 'lua-sec' # Used for secure https interactions
- 'lua-dkjson' # Used for json parsing
- 'xclip' # Used for copying to clipboard
- 'libnotify' # Used for notifications
-)
-optdepends=(
- 'gamemode: Enable GameMode support'
- 'mangohud: Enable MangoHud support'
- 'mesa: Enable Zink support'
- 'switcheroo-control: Launch games with your dedicated GPU'
- 'gamescope: Enable GameScope support'
- 'protonplus: Recommended to install with SimpleSteamTinker'
-)
-provides=("${pkgname%-git}")
-conflicts=("${pkgname%-git}")
-source=('git+https://github.com/JordanViknar/SimpleSteamTinker')
-sha256sums=('SKIP')
-
-pkgver() {
- cd SimpleSteamTinker
- printf "r%s.%s" "$(git rev-list --count HEAD)" "$(git rev-parse --short HEAD)"
-}
-
-package() {
- cd SimpleSteamTinker
- make system
- cp -r dist/* "${pkgdir}/"
-}
diff --git a/main.lua b/main.lua
deleted file mode 100644
index b9fe82f..0000000
--- a/main.lua
+++ /dev/null
@@ -1,131 +0,0 @@
--- Startup time
-local totalStartupTimeVar = os.clock()
-
--- Internal Modules
-local logSystem = require("modules.general.logSystem")
-local fsUtils = require("modules.general.fsUtils")
-local systemUtils = require("modules.general.systemUtils")
-local steamUtils = require("modules.steam.steamUtils")
-local programMetadata = require("modules.extra.programMetadata")
-local gameLauncher = require("modules.gameLauncher")
-
-if programMetadata.version:find("dev") then
- logSystem.log("warning", "DEVELOPMENT VERSION")
-end
-
---[[
- Chapter 0 : Preparing the environment
- We need to create the config folder if it doesn't exist.
-]]
--- Create the config folder if it doesn't exist
-fsUtils.createOrUseDirectory(programMetadata.folders.config)
--- And the games folder inside of it
-fsUtils.createOrUseDirectory(programMetadata.folders.gamesConfig)
-
--- Create the cache folder if it doesn't exist
-fsUtils.createOrUseDirectory(programMetadata.folders.cache)
-
--- Put the arguments inside the cache folder for testing purposes
-local cacheFile = io.open(programMetadata.folders.cache.."/lastArguments.txt", "w")
-if not cacheFile then
- logSystem.log("error", "Unable to open cache file.")
-else
- cacheFile:write(table.concat(arg, "\n"))
- cacheFile:close()
-end
-
---[[
- Chapter 1 : Game management
- What's the Steam config ? Is this being started through Steam or alone ? What to do ?!
-]]
-
--- We retrieve games registered in Steam.
-local steamGames = require("modules.steam.steamConfigProvider")
-collectgarbage("collect")
-
--- We check if we're starting a Steam game or not through the arguments.
-local gameStartStatus = steamUtils.isSteamArgs(arg)
-
-if gameStartStatus == "steamGame" then
- local game = steamGames[arg[3]:gsub("AppId=", "")]
-
- -- We notify the user that SimpleSteamTinker is running.
- logSystem.log("info", "Starting "..game.name.."...")
- systemUtils.sendNotification(programMetadata.name.." is starting...", "Detected "..game.name, "information")
-
- -- For every argument that is a path, we put \ next to the spaces
- for i, v in ipairs(arg) do
- if fsUtils.exists(v) then
- arg[i] = v:gsub(" ", "\\ ")
- end
- end
-
- -- We prepare the command
- local command = table.concat(arg, " ")
-
- logSystem.log("info", "Applying settings for "..game.name.."...")
- command = gameLauncher.prepareGameLaunch(game, command)
-
- if os.execute(command) then
- os.exit(0)
- else
- systemUtils.sendNotification(programMetadata.name.." crashed !", "Something bad happened while running "..game.name..".", "error")
- os.exit(1)
- end
-elseif gameStartStatus == "SteamTinkerLaunch" or (arg[3] and steamGames[arg[3]:gsub("AppId=", "")].status == "SteamTinkerLaunch") then
- -- We notify the user that SteamTinkerLaunch is running.
- logSystem.log("error", "Can't use "..programMetadata.name.." ! SteamTinkerLaunch detected.")
- systemUtils.sendNotification("Can't use "..programMetadata.name.." !", "SteamTinkerLaunch detected.", "warning")
-
- -- For every argument that is a path, we put \ next to the spaces
- for i, v in ipairs(arg) do
- if fsUtils.exists(v) then
- arg[i] = v:gsub(" ", "\\ ")
- end
- end
-
- -- We prepare the command
- local command = table.concat(arg, " ")
-
- os.execute(command)
- os.exit(0)
-end
-logSystem.log("info", "No game to be launched detected. Launching interface...")
-
-
-
---[[
- Chapter 2 : The main window
- No games started. Time to cook some Libadwaita goodness then.
-]]
-
--- External Modules
-local lgi = require("lgi")
-local Adw = lgi.Adw
-
--- Application
-local app = Adw.Application({
- application_id = programMetadata.developer.."."..programMetadata.name,
-})
-
--- Sort Steam games alphabetically
-local newSteamGamesTable = {}
-for _, v in pairs(steamGames) do
- table.insert(newSteamGamesTable, v)
-end
-table.sort(newSteamGamesTable, function(a, b) return a.name < b.name end)
-steamGames = newSteamGamesTable
-
--- Time to start the application
-local timeStart = os.clock()
-function app:on_startup()
- require("modules.ui.mainWindow")(app, steamGames)
-end
-
-function app:on_activate()
- logSystem.log("speed", timeStart)
- logSystem.log("info", "Startup time : "..os.clock()-totalStartupTimeVar.." seconds.")
- self.active_window:present()
-end
-
-return app:run(arg)
diff --git a/modules/config/configManager.lua b/modules/config/configManager.lua
deleted file mode 100644
index 31e02da..0000000
--- a/modules/config/configManager.lua
+++ /dev/null
@@ -1,125 +0,0 @@
--- Internal Modules
-local gameConfigFolder = require("modules.extra.programMetadata").folders.gamesConfig
-local fsUtils = require("modules.general.fsUtils")
-local logSystem = require("modules.general.logSystem")
-
--- External Modules
-local json = require("dkjson")
-
-local configManager = {}
-
---[[
- Name : function configManager.createGameConfig(gameId)
- Description : Creates a config file for a game.
- Arg 1 : gameId (string) : The game's Steam ID.
- Return : The game's config.
-]]
-function configManager.createGameConfig(gameId)
- -- Grab the default template
- local gameConfig = require("modules.config.defaultConfigTemplate")
- gameConfig.steamGameId = gameId
-
- -- Write the new table to the file
- local path = string.format("%s/%s.json", gameConfigFolder, gameId)
- local file = assert(io.open(path, "w"), "Couldn't open " .. path .. " !")
- file:write(json.encode(gameConfig, { indent = false }))
- file:close()
-
- return gameConfig
-end
-
---[[
- Name : configManager.getGameConfig(gameId)
- Description : Retrieves a game's config.
- Arg 1 : gameId (string) : The game's Steam ID.
-]]
-function configManager.getGameConfig(gameId)
- -- Check if the config exists
- local path = string.format("%s/%s.json", gameConfigFolder, gameId)
- if not fsUtils.exists(path) then
- local warningMsg = "Config for game " .. gameId .. " at " .. path .. " doesn't exist. Creating it..."
- logSystem.log("warning", warningMsg)
- return configManager.createGameConfig(gameId)
- end
-
- -- Read the file
- local file = assert(io.open(path, "r"), "Couldn't open " .. path .. " !")
- local fileContent = file:read("*a")
- file:close()
-
- -- Parse the JSON
- local parsed = json.decode(fileContent)
-
- -- Update the config to be based on defaultConfigTemplate recursively
- local defaultConfig = require("modules.config.defaultConfigTemplate")
- local function updateConfig(config, template)
- for key, value in pairs(template) do
- if config[key] == nil then
- config[key] = value
- elseif type(value) == "table" then
- updateConfig(config[key], value)
- end
- end
- end
- updateConfig(parsed, defaultConfig)
-
- return parsed
-end
-
---[[
- Name : function configManager.modifyGameConfig(gameId, dataToChange)
- Description : Modifies a game's config.
- Arg 1 : gameId (string) : The game's Steam ID.
- Arg 2 : dataToChange (string) : The data to change. Example : "steamGameId"
- Arg 3 : value (any) : The value to set.
- Return : The game's config.
-]]
-function configManager.modifyGameConfig(gameId, dataToChange, value)
- -- Grab the original config data
- local gameConfig = configManager.getGameConfig(gameId)
-
- -- Parse dataToChange
- local keys = {}
- for substring in dataToChange:gmatch("[^.]+") do
- keys[#keys + 1] = substring
- end
-
- -- Modify the table
- local pointer = gameConfig
- for i = 1, #keys - 1 do
- pointer = pointer[keys[i]] or {}
- end
- pointer[keys[#keys]] = value
-
- -- Write the new table
- local path = string.format("%s/%s.json", gameConfigFolder, gameId)
- local file = assert(io.open(path, "w"), "Couldn't open " .. path .. " !")
-
- file:write(json.encode(gameConfig))
- file:close()
-
- return gameConfig
-end
-
---[[
- Name : function configManager.grabInTableFromString(table, string)
- Description : Grabs a value in a table from a string.
- Arg 1 : table (table) : The table to grab the value from.
- Arg 2 : string (string) : The string to parse. Example : "gamescope.general.resolution.internal.width"
- Return : The value.
-]]
-function configManager.grabInTableFromString(table, string)
- local keys = {}
- for substring in string:gmatch("[^.]+") do
- keys[#keys + 1] = substring
- end
-
- local pointer = table
- for i = 1, #keys do
- pointer = pointer[keys[i]] or {}
- end
-
- return pointer
-end
-
-return configManager
diff --git a/modules/config/defaultConfigTemplate.lua b/modules/config/defaultConfigTemplate.lua
deleted file mode 100644
index b605f27..0000000
--- a/modules/config/defaultConfigTemplate.lua
+++ /dev/null
@@ -1,90 +0,0 @@
-return
-
-{
- config_version = 1;
- steamGameId = nil;
-
- dgpu = {
- enabled = true;
- };
-
- -- Miscellaneous
- misc = {
- sdl_wayland = true;
- };
-
- -- Utilities
- utilities = {
- gamemode = {
- enabled = true;
- },
- mangohud = {
- enabled = false;
- },
- zink = {
- enabled = false;
- },
- obs_gamecapture = {
- enabled = false;
- },
- },
-
- -- GameScope
- gamescope = {
- enabled = false;
- general = {
- resolution = {
- enabled = false;
- internal = {
- width = 1280;
- height = 720;
- };
- external = {
- width = 1920;
- height = 1080;
- };
- };
- frame_limit = {
- enabled = false;
- normal = 60;
- unfocused = 5;
- };
- fullscreen = false;
- borderless = false;
- };
- filtering = {
- enabled = false;
- filter = "FSR"; -- "Linear", "Nearest", "FSR", "NIS", "Pixel"
- sharpness = 10; -- Ranges from 0 to 20
- }
- },
-
- -- Settings exclusive to Windows games
- proton = {
- direct3d = {
- enable_direct3d9 = true; -- Exclusive to Proton GE
- enable_direct3d10 = true;
- enable_direct3d11 = true;
- enable_direct3d12 = true; -- Exclusive to Proton GE
- use_wined3d = false;
- };
- sync = {
- enable_esync = true;
- enable_fsync = true;
- };
- nvidia = {
- enable_nvapi = false;
- hide_nvidia_gpu = false;
- };
- fsr = { -- Exclusive to Proton GE
- enabled = false;
- sharpness = 2; -- Between 0 and 5, 0 = maximum, 5 = minimum
- upscaling_mode = "none"; -- Can be "none", "performance", "balanced", "quality" or "ultra"
- resolution = {
- enabled = false;
- width = 1920;
- height = 1080;
- };
- };
- };
-}
diff --git a/modules/extra/programMetadata.lua b/modules/extra/programMetadata.lua
deleted file mode 100644
index 4df3476..0000000
--- a/modules/extra/programMetadata.lua
+++ /dev/null
@@ -1,17 +0,0 @@
-return {
- name="SimpleSteamTinker",
- description = "A work-in-progress fast, simple and modern Libadwaita alternative to SteamTinkerLaunch.",
- executable="sst",
- icon_name = "simplesteamtinker",
- version="indev",
- developer="JordanViknar",
- url = "https://github.com/JordanViknar/SimpleSteamTinker",
- installdir = os.getenv("SST_SCRIPT_PATH"),
- folders = {
- config = os.getenv("HOME").."/.config/SimpleSteamTinker",
- gamesConfig = os.getenv("HOME").."/.config/SimpleSteamTinker/games",
-
- storage = os.getenv("HOME").."/.local/share/SimpleSteamTinker",
- cache = os.getenv("HOME").."/.cache/SimpleSteamTinker"
- }
-}
diff --git a/modules/gameLauncher.lua b/modules/gameLauncher.lua
deleted file mode 100644
index 83f9fab..0000000
--- a/modules/gameLauncher.lua
+++ /dev/null
@@ -1,103 +0,0 @@
--- Internal Modules
-local logSystem = require("modules.general.logSystem")
-local systemUtils = require("modules.general.systemUtils")
-local configManager = require("modules.config.configManager")
-local programMetadata = require("modules.extra.programMetadata")
-
-local gameLauncher = {}
-
-function gameLauncher.prepareGameLaunch(game, command)
- -- First we retrive the configuration
- local gameConfig
- local status, result = pcall(function(value)
- gameConfig = configManager.getGameConfig(value)
- end, game.id)
-
- if not status then
- logSystem.log("error", "Couldn't retrieve configuration for "..game.name..". Error : "..result)
- systemUtils.sendNotification("Couldn't retrieve configuration for "..game.name.." !", result, "error")
- return command
- end
-
- -- We get the list of utilities
- local utilities = require("modules.utilitiesList")(gameConfig)
-
- -- Environment variables : Part 1
- local environmentVars = {}
-
- -- We iterate through the utilities and enable them if they are installed
- for _, utility in pairs(utilities) do
- if utility[2] --[[= Utility is enabled]] and utility[1].isInstalled then
- if utility[1].toolType == "executable" then
- command = utility[1].usage(command, gameConfig)
- elseif utility[1].toolType == "environmentVariable" then
- table.insert(environmentVars, utility[1].usage(command, gameConfig))
- end
- end
- end
-
- -- Special case for switcherooctl, some Steam launch desktop files force the dGPU on games even when disabled.
- if gameConfig.dgpu.enabled == false and utilities[3][1].isInstalled then -- Should use names instead of indexes later
- -- We add the environment variables to forcefully disable the dGPU
- table.insert(environmentVars, "DRI_PRIME=0 __NV_PRIME_RENDER_OFFLOAD=0 __VK_LAYER_NV_optimus=none __GLX_VENDOR_LIBRARY_NAME=none")
- end
-
- -- Environment variables not linked to a particular utility
- if game.os_platform == "Linux" then
- if gameConfig.misc.sdl_wayland == true then
- table.insert(environmentVars, "SDL_VIDEODRIVER=\"wayland,x11\"")
- else -- We force X11 to be used, just in case.
- table.insert(environmentVars, "SDL_VIDEODRIVER=\"x11\"")
- end
- elseif game.os_platform == "Windows" then
- -- Direct3D
- if gameConfig.proton.direct3d.enable_direct3d9 == false then table.insert(environmentVars, "PROTON_NO_D3D9=1") end
- if gameConfig.proton.direct3d.enable_direct3d10 == false then table.insert(environmentVars, "PROTON_NO_D3D10=1") end
- if gameConfig.proton.direct3d.enable_direct3d11 == false then table.insert(environmentVars, "PROTON_NO_D3D11=1") end
- if gameConfig.proton.direct3d.enable_direct3d12 == false then table.insert(environmentVars, "PROTON_NO_D3D12=1") end
- if gameConfig.proton.direct3d.use_wined3d == true then table.insert(environmentVars, "PROTON_USE_WINED3D=1") end
- -- Sync
- if gameConfig.proton.sync.enable_esync == false then table.insert(environmentVars, "PROTON_NO_ESYNC=1") end
- if gameConfig.proton.sync.enable_fsync == false then table.insert(environmentVars, "PROTON_NO_FSYNC=1") end
- -- NVIDIA
- if gameConfig.proton.nvidia.enable_nvapi == true then table.insert(environmentVars, "PROTON_ENABLE_NVAPI=1") end
- if gameConfig.proton.nvidia.hide_nvidia_gpu == true then table.insert(environmentVars, "PROTON_HIDE_NVIDIA_GPU=1") end
- -- FSR
- if gameConfig.proton.fsr.enabled == true then
- table.insert(environmentVars, string.format(
- "WINE_FULLSCREEN_FSR=1 WINE_FULLSCREEN_FSR_STRENGTH=%s WINE_FULLSCREEN_FSR_MODE=%s",
- gameConfig.proton.fsr.sharpness,
- string.lower(gameConfig.proton.fsr.upscaling_mode)
- ))
- if gameConfig.proton.fsr.resolution.enabled == true then
- table.insert(environmentVars, string.format(
- "WINE_FULLSCREEN_FSR_CUSTOM_MODE=%sx%s",
- gameConfig.proton.fsr.resolution.width,
- gameConfig.proton.fsr.resolution.height
- ))
- end
- else
- -- Explictly disable FSR if it's not enabled, recent Proton GE versions enable it by default.
- table.insert(environmentVars, "WINE_FULLSCREEN_FSR=0")
- end
- end
-
- -- We convert the environment variables table to a string
- if environmentVars ~= {} then
- local envVarString = table.concat(environmentVars, " ")
- command = "env "..envVarString.." "..command
- end
-
- -- Print the final command in cache for debugging purposes
- local cacheFile = io.open(programMetadata.folders.cache.."/lastCommand.txt", "w")
- if not cacheFile then
- logSystem.log("error", "Unable to open cache file.")
- else
- cacheFile:write(command)
- cacheFile:close()
- end
-
- return command
-end
-
-return gameLauncher
diff --git a/modules/general/fsUtils.lua b/modules/general/fsUtils.lua
deleted file mode 100644
index bbff7f5..0000000
--- a/modules/general/fsUtils.lua
+++ /dev/null
@@ -1,188 +0,0 @@
--- External Modules
-local lfs = require("lfs")
-
--- Internal Modules
-local logSystem = require("modules.general.logSystem")
-
--- Module
-local fsUtils = {}
-
---[[
- Name : function fsUtils.exists(path)
- Description : Checks if a file or directory exists.
- Arg 1 : path (string) : The path to check.
- Return : true if the file or directory exists, false otherwise.
-]]
-function fsUtils.exists(path)
- local attributes, err = lfs.attributes(path)
- if attributes then
- return true
- else
- return false, err
- end
-end
-
---[[
- Name : function fsUtils.isDirectory(path)
- Description : Checks if a path is a directory.
- Arg 1 : path (string) : The path to check.
- Return : true if the path is a directory, false otherwise.
-]]
-function fsUtils.isDirectory(path)
- local attributes, err = lfs.attributes(path)
- if attributes then
- if attributes.mode == "directory" then
- return true
- else
- return false
- end
- else
- return false, err
- end
-end
-
---[[
- Name : function fsUtils.createDirectory(path)
- Description : Creates a directory.
- Arg 1 : path (string) : The path to create.
- Return : true if the directory was created, false otherwise.
-]]
-function fsUtils.createDirectory(path)
- local success, err = lfs.mkdir(path)
- if success then
- return true
- else
- return false, err
- end
-end
-
---[[
- Name : function fsUtils.createOrUseDirectory(path)
- Description : Creates a directory if it doesn't exist, or uses it if it does.
- Arg 1 : path (string) : The path to create or use.
- Return : true if the directory was created or used, false otherwise.
-]]
-function fsUtils.createOrUseDirectory(path)
- if not fsUtils.exists(path) then
- logSystem.log("debug", "Directory "..path.." not found. Creating it...")
- return fsUtils.createDirectory(path)
- else
- return true
- end
-end
-
---[[
- Name : function fsUtils.getSize(path)
- Description : Gets the size of a file or directory.
- Arg 1 : path (string) : The path to get the size of.
- Return : The size of the file or directory.
-]]
-local function trim(s)
- return s:match'^()%s*$' and '' or s:match'^%s*(.*%S)'
-end
-function fsUtils.getSize(path)
- local handle = io.popen("du -sb '"..path:gsub("'", "'\\''").."' | cut -f1")
- if not handle then
- return 0
- end
-
- local result = handle:read("*a")
- handle:close()
-
- return tonumber(trim(result))
-end
-
---[[
- Name : function fsUtils.sizeToUnit(size)
- Description : Converts a size in bytes to a human-readable size.
- Arg 1 : size (number) : The size to convert.
- Return : The human-readable size.
-]]
-function fsUtils.sizeToUnit(size)
- local unit = "B"
- if size > 1024 then
- size = size / 1024
- unit = "KB"
- end
- if size > 1024 then
- size = size / 1024
- unit = "MB"
- end
- if size > 1024 then
- size = size / 1024
- unit = "GB"
- end
- if size > 1024 then
- size = size / 1024
- unit = "TB"
- end
- return string.format("%.2f", size).." "..unit
-end
-
---[[
- Name : function fsUtils.directoryContainsLinuxData(location)
- Description : Checks if a directory contains Linux data.
- Arg 1 : location (string) : The directory to check.
- Return : true if the directory contains Linux data, false otherwise.
-
- Note : Used to for game platform detection to work around Steam not cleaning up the compat.vdf file when going back from the Windows version of a game to the Linux version.
- Note 2 : The reason we don't use os.execute("file ...") is because Steam can get stuck while starting a game for whatever reason.
-]]
--- Alternative to using file command
-local function checkFileForKeywords(location, file)
- local filepath = location .. "/" .. file
-
- local fileHandle = io.open(filepath, "r")
- if not fileHandle then
- return false
- end
-
- local foundKeywords = false
-
- for line in fileHandle:lines() do
- if line:find("Linux") or line:find("shell") then
- foundKeywords = true
- break
- end
- end
-
- fileHandle:close()
-
- return foundKeywords
-end
-
-function fsUtils.directoryContainsLinuxData(location)
- for file in lfs.dir(location) do
- if (string.find(file,".sh") or not string.find(file,".")) and not fsUtils.isDirectory(location.."/"..file) then
- local containsKeywords = checkFileForKeywords(location, file)
- if containsKeywords then
- return true
- end
- end
- end
-
- return false
-end
-
---[[
- Name : function fsUtils.getFilenamePatternInDirectory(directory, pattern)
- Description : Returns a table containing every filename matching a pattern in a directory.
- Arg 1 : directory (string) : The directory to search in.
- Arg 2 : pattern (string) : The pattern to search for.
- Return : A table containing every filename matching the pattern.
-
- Note : Used to for game detection to work around Steam not updating the libraryfolders.vdf file immediately.
-]]
-function fsUtils.getFilenamePatternInDirectory(directory, pattern)
- local result = {}
-
- for file in lfs.dir(directory) do
- if file:match(pattern) then
- table.insert(result, file)
- end
- end
-
- return result
-end
-
-return fsUtils
diff --git a/modules/general/logSystem.lua b/modules/general/logSystem.lua
deleted file mode 100644
index 409a586..0000000
--- a/modules/general/logSystem.lua
+++ /dev/null
@@ -1,73 +0,0 @@
-local logSystem = {}
-
---[[
- Name : local function color(colorId, message)
- Description : Colors a message.
- Arg 1 : colorId (string) : The color's id.
- Arg 2 : message (string) : The message to color.
- Return : The colored message.
-]]
-local function color(colorId, message)
- local colorList = {
- ["red"] = "\27[31m",
- ["green"] = "\27[32m",
- ["yellow"] = "\27[33m",
- ["blue"] = "\27[34m",
- ["magenta"] = "\27[35m",
- ["cyan"] = "\27[36m",
- ["white"] = "\27[37m",
- ["grey"] = "\27[90m",
- ["orange"] = "\27[91m",
- ["reset"] = "\27[0m"
- }
-
- return colorList[colorId]..message..colorList["reset"]
-end
-
---[[
- Name : function logSystem.log(type, message)
- Description : Logs a message.
- Arg 1 : type (string) : The log type.
- Arg 2 : message (string) : The message to log.
- Return : nil
-]]
-function logSystem.log(type, message)
- local logReactions = {
- ["info"] = function(text)
- print("["..color("blue","Info").."] "..text)
- end,
- ["warning"] = function(text)
- print("["..color("yellow","Warning").."] "..color("yellow",text))
- end,
- ["error"] = function(text)
- print(color("red","[Error]").." "..color("red",text))
- end,
- ["debug"] = function(text)
- print("["..color("grey","Debug").."] "..color("grey",text))
- end,
- ["switch"] = function (text)
- print(color("green","[Output Switch] "..text))
- print("--------------------------")
- end,
- ["switchEnd"] = function (text)
- print("--------------------------")
- end,
- ["download"] = function (text)
- print("["..color("green","Download").."] "..text)
- end,
- ["fileRead"] = function (text)
- print("["..color("magenta","Data").."] "..text)
- end,
- ["speed"] = function (startTime)
- print("["..color("grey","Speed").."] ".."Done in "..os.clock()-startTime.." seconds.")
- end
- }
-
- if (logReactions[type]) then
- logReactions[type](message)
- else
- error("Unknown log type : "..type)
- end
-end
-
-return logSystem
diff --git a/modules/general/systemUtils.lua b/modules/general/systemUtils.lua
deleted file mode 100644
index c75b843..0000000
--- a/modules/general/systemUtils.lua
+++ /dev/null
@@ -1,72 +0,0 @@
--- Custom modules
-local logSystem = require("modules.general.logSystem")
-local fsUtils = require("modules.general.fsUtils")
-local programMetadata = require("modules.extra.programMetadata")
-
--- External modules
-local lgi = require("lgi")
-local Notify = lgi.Notify
-Notify.init(programMetadata.name)
-
--- Module
-local systemUtils = {}
-
---[[
- Name : function systemUtils.sendNotification(title, message, urgency, transient, time)
- Description : Sends a notification using notify-send.
- Arg 1 : title (string) : The notification's title.
- Arg 2 : message (string) : The notification's message.
- Arg 3 : type (string) : The notification's type. Used only for icons right now. Can be "warning", "error" or "information".
- Return : nil
-]]
-function systemUtils.sendNotification(title, message, type)
- -- Set icons
- local icon
- if type == "warning" then
- icon = "dialog-warning-symbolic"
- elseif type == "error" then
- icon = "dialog-error-symbolic"
- else
- icon = "dialog-information"
- end
-
- local notification = Notify.Notification.new(title, message, icon)
- notification:show()
-end
-
---[[
- Name : function systemUtils.copyToClipboard(text)
- Description : Copies a text to the clipboard using xclip.
- Arg 1 : text (string) : The text to copy.
- Return : true if successful, false otherwise.
-]]
-function systemUtils.copyToClipboard(text)
- local command = string.format("echo -n %q | xclip -selection clipboard", text)
-
- if not os.execute(command) then
- logSystem.log("error", "Unable to copy to clipboard using command : "..command)
- return false
- else
- return true
- end
-end
-
---[[
- Name : function systemUtils.isInstalled(command)
- Description : Checks if an command is installed.
- Arg 1 : command (string) : The command to check.
- Return : true if installed, false otherwise.
-]]
-function systemUtils.isInstalled(command)
- if command:sub(1, 1) == "/" then
- return fsUtils.exists(command)
- else
- if os.execute("which "..command.." &> /dev/null") then
- return true
- else
- return false
- end
- end
-end
-
-return systemUtils
diff --git a/modules/objects/steamGameObject.lua b/modules/objects/steamGameObject.lua
deleted file mode 100644
index 77e2b07..0000000
--- a/modules/objects/steamGameObject.lua
+++ /dev/null
@@ -1,62 +0,0 @@
--- Internal Modules
-local fsUtils = require("modules.general.fsUtils")
-
--- Constants
-local steamImagesLocation = os.getenv("HOME").."/.local/share/Steam/appcache/librarycache/"
-
--- Extra function
-local function ifExists(path)
- if fsUtils.exists(path) then
- return path
- else
- return nil
- end
-end
-
--- Object
-SteamGame = {
- metatable = {
- __index = SteamGame
- }
-}
-function SteamGame:new (id, name, library, location)
- -- Current way of detecting if an app is a game or a tool... not the greatest.
- local type = "game"
- local libraryIconPath = steamImagesLocation..id.."_library_600x900.jpg"
- if not fsUtils.exists(libraryIconPath) then
- libraryIconPath = nil
- type = "software"
- end
- if name:find("Proton") then
- type = "proton"
- end
-
- -- The actual OS detection is in steamConfigProvider.lua instead. This is here because Linux games can have compatdata left over from previously using Proton.
- local protonConfig = {}
- if fsUtils.exists(library.."/steamapps/compatdata/"..id) then
- protonConfig.compatdata = library.."/steamapps/compatdata/"..id
- end
-
- return setmetatable({
- id = id,
- name = name,
- library = library,
- location = location,
- size = nil, -- Slow, done in UI
- protondb_data = nil, -- VERY slow, done in UI
- type = type,
- os_platform = "Linux", -- Assume Linux first, normally gets reconfigured as Windows later
- proton_config = protonConfig,
- tool_status = false, -- Assume the game is not setup to use the tool, normally gets reconfigured true later
- images = {
- header = ifExists(steamImagesLocation..id.."_header.jpg"),
- icon = ifExists(steamImagesLocation..id.."_icon.jpg"),
- logo = ifExists(steamImagesLocation..id.."_logo.png"),
- library = libraryIconPath,
- library_hero = ifExists(steamImagesLocation..id.."_library_hero.jpg"),
- library_hero_blur = ifExists(steamImagesLocation..id.."_library_hero_blur.jpg"),
- }
- }, SteamGame.metatable)
-end
-
-return SteamGame
diff --git a/modules/objects/toolObject.lua b/modules/objects/toolObject.lua
deleted file mode 100644
index 0f81668..0000000
--- a/modules/objects/toolObject.lua
+++ /dev/null
@@ -1,24 +0,0 @@
--- Internal Modules
-local systemUtils = require("modules.general.systemUtils")
-local logSystem = require("modules.general.logSystem")
-
-Tool = {
- metatable = {
- __index = Tool
- }
-}
-
-function Tool:new (toolName, toolType, modification)
- assert(toolName)
- assert(toolType == "executable" or toolType == "environmentVariable", "Invalid tool type for tool "..toolName..".")
- assert(type(modification) == "function", "Invalid modification for tool "..toolName..".")
-
- return setmetatable({
- toolName = toolName,
- toolType = toolType, -- "executable" or "environmentVariable"
- isInstalled = systemUtils.isInstalled(toolName),
- usage = modification
- }, Tool.metatable)
-end
-
-return Tool
diff --git a/modules/steam/protonDBManager.lua b/modules/steam/protonDBManager.lua
deleted file mode 100644
index 1a5c448..0000000
--- a/modules/steam/protonDBManager.lua
+++ /dev/null
@@ -1,41 +0,0 @@
--- External Modules
-local http = require("socket.http")
-local ltn12 = require("ltn12")
-local json = require("dkjson")
-
--- Internal Modules
-local logSystem = require("modules.general.logSystem")
-
-local protonDBManager = {}
-
---[[
- Name : protonDBManager.getAppInfo(appId)
- Description : Retrieves the ProtonDB data for a given app ID.
- Arg 1 : appId (string) : The app ID to retrieve the data for.
- Return : The ProtonDB data for the given app ID.
-]]
-function protonDBManager.getAppInfo(appId)
- local url = "https://www.protondb.com/api/v1/reports/summaries/"..appId..".json"
- local response = {}
-
- local success, status, headers = http.request {
- url = url,
- method = "GET",
- sink = ltn12.sink.table(response)
- }
-
- if success then
- local data = json.decode(table.concat(response)) -- Parse the JSON response
- if data then
- return data
- else
- logSystem.log("error", "Failed to parse JSON response from ProtonDB for app "..appId..".")
- return "Not found"
- end
- else
- logSystem.log("error", "Failed to retrieve ProtonDB data for app "..appId..", with error : "..status..".")
- return "Unavailable"
- end
-end
-
-return protonDBManager
diff --git a/modules/steam/steamConfigProvider.lua b/modules/steam/steamConfigProvider.lua
deleted file mode 100644
index a922b79..0000000
--- a/modules/steam/steamConfigProvider.lua
+++ /dev/null
@@ -1,144 +0,0 @@
--- Internal Modules
-local logSystem = require("modules.general.logSystem")
-local vdfParser = require("modules.steam.vdfParser")
-local steamUtils = require("modules.steam.steamUtils")
-local programMetadata = require("modules.extra.programMetadata")
-local fsUtils = require("modules.general.fsUtils")
-
--- Timer
-local timeStart
-
---[[
- Chapter 1 : We recover the Steam user config so we can get the last active user.
- And also the SteamID3 to access their settings folder.
-]]
-logSystem.log("fileRead", "Detecting user config...")
-timeStart = os.clock()
-local userData = vdfParser.parseFile(os.getenv("HOME").."/.local/share/Steam/config/loginusers.vdf")
-
--- We add the SteamID3 to the user datas, and also grab the most recent user's ID while we're at it.
-local activeUserID
-for id,data in pairs(userData["users"]) do
- -- SteamID3
- data["steamID3"] = steamUtils.convertToSteamID3(id)
-
- -- Most recent
- if data["MostRecent"] == "1" then
- activeUserID = id
- end
-end
-logSystem.log("speed", timeStart)
-logSystem.log("info", "Active user : "..userData["users"][activeUserID]["AccountName"])
-
---[[
- Chapter 2 : We recover the user config to get which games have the tool enabled.
- This section would normally have quite an impact on performance.
- Thanks to some optimizations, however, the time is cut from 0.5 seconds to 0.04 seconds on my computer.
-]]
-logSystem.log("fileRead", "Detecting active user game configs...")
-timeStart = os.clock()
-
-local userAppSettings = vdfParser.parseFile(
- os.getenv("HOME").."/.local/share/Steam/userdata/"..userData["users"][activeUserID]["steamID3"].."/config/localconfig.vdf",
- {"UserLocalConfigStore","Software","Valve","Steam","apps"},
- {"CachedCommunityPreferences",
- "UIStoreLocalState",
- "CachedStorePreferences",
- "CachedNotificationPreferences",
- "SteamVoiceSettings_",
- "UIStoreLocalSteamUIState",
- "UIStoreLocalGamepadState",
- "GetEquippedProfileItemsForUser",
- "CTextFilterStore_strBannedPattern",
- "CTextFilterStore_strCleanPattern",
- "trendingstore_storage",
- "playnextstore_storage",
- "GetEquippedProfileItemsForUser"} -- Optimization : we remove big lines that we don't need.
-)["UserLocalConfigStore"]["Software"]["Valve"]["Steam"]["apps"]
-logSystem.log("speed", timeStart)
-
---[[
- Chapter 3 : We recover the Steam library config to get the list of games.
- We can't use just libraryfolders.vdf to get the game IDs, as Steam seems to not always update it immediately.
-]]
-logSystem.log("fileRead", "Detecting games...")
-timeStart = os.clock()
-
-local libraryFolders = vdfParser.parseFile(os.getenv("HOME").."/.local/share/Steam/config/libraryfolders.vdf")
-
--- Steam Game Object
-local SteamGame = require("modules.objects.steamGameObject")
--- And list
-local steamGames = {}
-
--- For each library
-for _,folderData in pairs(libraryFolders["libraryfolders"]) do
- -- We check if the folder exists
- if not fsUtils.exists(folderData["path"].."/steamapps") then
- logSystem.log("warning", "Steam library "..folderData["path"].." doesn't exist. Skipping...")
- goto continue
- end
-
- -- We get the list of every appmanifest_*.acf file in the folder
- local appManifestsFileNames = fsUtils.getFilenamePatternInDirectory(folderData["path"].."/steamapps", "appmanifest_")
- for _,appId in pairs(appManifestsFileNames) do
- -- We convert the filename to the app ID
- appId = appId:gsub("appmanifest_", ""):gsub(".acf", "")
-
- local appManifest = vdfParser.parseFile(folderData.path.."/steamapps/appmanifest_"..appId..".acf")
- -- We create a SteamGame object
- local steamGame = SteamGame:new(
- appId,
- appManifest["AppState"]["name"],
- folderData["path"],
- folderData.path.."/steamapps/common/"..appManifest["AppState"]["installdir"]
- )
- -- We add it to the list
- steamGames[appId] = steamGame
- end
-
- ::continue::
-end
-
--- And now we merge user game data and game data
-for gameID, data in pairs(userAppSettings) do
- -- We check if the game is actually installed before attempting a merge.
- if steamGames[gameID] ~= nil then
- -- Tool status
- if data["LaunchOptions"] and string.find(data["LaunchOptions"],programMetadata.executable) then
- steamGames[gameID].status = true
- end
- if data["LaunchOptions"] and string.find(data["LaunchOptions"],"steamtinkerlaunch") then
- steamGames[gameID].status = "SteamTinkerLaunch"
- end
- end
-end
-logSystem.log("speed", timeStart)
-
---[[
- Chapter 4 : We check if the game is in the compat list
-]]
-logSystem.log("fileRead","Detecting games' platforms...")
-timeStart = os.clock()
-
-local compatList = vdfParser.parseFile(
- os.getenv("HOME").."/.local/share/Steam/userdata/"..userData["users"][activeUserID]["steamID3"].."/config/compat.vdf"
-)["platform_overrides"]
-
-for gameID,_ in pairs(compatList) do
- -- Proton Status
- --[[
- WARNING : METHOD WRONGLY SETS WINDOWS STATUS FOR LINUX GAMES THAT WERE PREVIOUSLY USING PROTON
- Workaround : Check if Windows version contains Linux executable or shell script.
- ]]
- if steamGames[gameID] and steamGames.type ~= "proton" then
- if not fsUtils.directoryContainsLinuxData(steamGames[gameID].location) then
- steamGames[gameID].os_platform = "Windows"
- else
- logSystem.log("warning", "Game "..steamGames[gameID].name.." was listed as Windows game, but contains Linux data. Assuming it runs with Linux...")
- end
- end
-end
-logSystem.log("speed", timeStart)
-
-return steamGames
diff --git a/modules/steam/steamUtils.lua b/modules/steam/steamUtils.lua
deleted file mode 100644
index ed19bbb..0000000
--- a/modules/steam/steamUtils.lua
+++ /dev/null
@@ -1,44 +0,0 @@
-local steamUtils = {}
-
---[[
- This function checks if the arguments are from Steam or not.
- I suppose this is where non-Steam game support will be added later.
-]]
-function steamUtils.isSteamArgs(arguments)
- if arguments[7] ~= nil and arguments[7]:find("steamtinkerlaunch") then
- return "steamtinkerlaunch"
- elseif (
- --arguments[1] == os.getenv("HOME").."/.local/share/Steam/ubuntu12_32/reaper" and
- arguments[2] == "SteamLaunch" and
- arguments[3]:match("AppId=%d+") and
- arguments[4] == "--" and
- --arguments[5] == os.getenv("HOME").."/.local/share/Steam/ubuntu12_32/steam-launch-wrapper" and
- arguments[6] == "--"
- ) then
- return "steamGame"
- else
- return false
- end
-end
-
---[[
- Name : function steamUtils.convertToSteamID3(steamID)
- Description : Converts a SteamID64 to a SteamID3
- Arg 1 : string steamID
- Return : string steamID3
-]]
-function steamUtils.convertToSteamID3(steamID)
- local id_str = tostring(steamID)
-
- if tonumber(id_str) then
- local offset_id = tonumber(id_str) - 76561197960265728
- local account_type = offset_id % 2
- local account_id = math.floor((offset_id - account_type) / 2) + account_type
-
- return (account_id * 2) - account_type
- else
- error("Unable to decode SteamID : "..id_str)
- end
-end
-
-return steamUtils
diff --git a/modules/steam/vdfParser.lua b/modules/steam/vdfParser.lua
deleted file mode 100644
index 30e67f6..0000000
--- a/modules/steam/vdfParser.lua
+++ /dev/null
@@ -1,167 +0,0 @@
-local vdfParser = {}
-
---[[
- Name : function vdfParser.printArray(data, indent)
- Description : Prints a table in a human-readable way. Used for testing.
- Arg 1 : data (table) : The table to print.
- Arg 2 : indent (number) : The indentation level. Default : 0
-]]
-function vdfParser.printArray(data, indent)
- indent = indent or 0
- local spaces = string.rep(" ", indent)
-
- for key, value in pairs(data) do
- if type(value) == "table" then
- print(spaces .. key .. ": {")
- vdfParser.printArray(value, indent + 1)
- print(spaces .. "}")
- else
- print(spaces .. key .. ": " .. tostring(value))
- end
- end
-end
-
---[[
- Name : function vdfParser.parseString(input, stopKeyList)
- Description : Parses a VDF string into a table.
- Arg 1 : input (string) : The VDF string to parse.
- Arg 2 : stopKeyList (table) : A list of keys to stop the parsing at. Default : nil
- Return : result (table) : The parsed table.
-]]
-function vdfParser.parseString(input, stopKeyList)
- local result = {}
-
- local status = "keyWait"
-
- -- Temp Values
- local tempKey, tempValue, tempRecursiveInput = "", "", ""
-
- local indent = 0
- local previousChar = nil
-
- -- Until we reached the end of the file
- for i = 1, #input do
- local char = input:sub(i, i)
-
- -- Status :
- -- keyWait : We wait for a key
- -- readingKey : We are reading a key
- -- valueWait : We wait for a value
- -- readingValue : We are reading a value
- -- readingTableValue : We are reading a table value
-
- -- String management
- if char == '"' and previousChar ~= "\\" then
- if status == "keyWait" then
- status = "readingKey"
- elseif status == "readingKey" then
- status = "valueWait"
- elseif status == "valueWait" then
- status = "readingValue"
- elseif status == "readingValue" then
- result[tempKey] = tempValue
- -- We stop the parsing if we reached the stopKeyList entry
- if stopKeyList and tempKey == stopKeyList[1] then
- return result
- end
- tempKey = ""
- tempValue = ""
- status = "keyWait"
- elseif status == "readingTableValue" then
- tempRecursiveInput = tempRecursiveInput..char
- end
-
- -- Subtable management
- elseif char == '{' and previousChar ~= "\\" and (status == "valueWait" or status == "readingTableValue") then
- if status == "valueWait" then
- status = "readingTableValue"
- elseif status == "readingTableValue" then
- tempRecursiveInput = tempRecursiveInput..char
- indent = indent + 1
- end
- elseif char == '}' and previousChar ~= "\\" and status == "readingTableValue" then
- if indent == 0 then
- local newStopKeyList = nil
- if stopKeyList then
- newStopKeyList = {table.unpack(stopKeyList, 2)}
- if next(newStopKeyList) == nil then
- newStopKeyList = nil
- end
- end
-
- if stopKeyList == nil or stopKeyList[1] == nil or tempKey == stopKeyList[1] then
- result[tempKey] = vdfParser.parseString(tempRecursiveInput, newStopKeyList)
- end
-
- if stopKeyList and tempKey == stopKeyList[1] then
- return result
- end
-
- tempKey = ""
- tempValue = ""
- tempRecursiveInput = ""
- status = "keyWait"
- else
- tempRecursiveInput = tempRecursiveInput..char
- indent = indent - 1
- end
-
- -- While reading a key
- elseif status == "readingKey" then
- tempKey = tempKey..char
- -- While reading a value
- elseif status == "readingValue" and (stopKeyList == nil or stopKeyList[1] == nil or tempKey == stopKeyList[1]) then
- tempValue = tempValue..char
- -- While reading a table value, before sending it through the function again to parse it
- elseif status == "readingTableValue" and (stopKeyList == nil or stopKeyList[1] == nil or tempKey == stopKeyList[1]) then
- tempRecursiveInput = tempRecursiveInput..char
- end
-
- previousChar = char
- end
-
- return result
-end
-
---[[
- Name : function vdfParser.parseFile(path, stopKeyList, wordsFromLinesToRemove)
- Description : Parses a VDF file into a table.
- Arg 1 : path (string) : The path to the file to parse.
- Arg 2 : stopKeyList (table) : A list of keys to stop the parsing at. Default : nil
- Arg 3 : wordsFromLinesToRemove (table) : A list of words that, if found in a line, will remove the line. Default : nil
- Return : result (table) : The parsed table.
-
- Note : Arg 2 and Arg 3 are critical for optimization in big files. Do not hesitate to use them.
- Results such as going from 0.5 seconds to 0.04 seconds are possible.
-]]
-function vdfParser.parseFile(path, stopKeyList, wordsFromLinesToRemove)
- local file = io.open(path, "r")
- if not file then
- error("File not found : "..path)
- end
-
- local content = file:read("*all")
- file:close()
-
- -- Remove every line that contains a word from wordsFromLinesToRemove
- if wordsFromLinesToRemove then
- local lines = {}
- for line in content:gmatch("[^\r\n]+") do
- local removeLine = false
- for _, word in pairs(wordsFromLinesToRemove) do
- if line:find(word) then
- removeLine = true
- break
- end
- end
- if not removeLine then
- table.insert(lines, line)
- end
- end
- content = table.concat(lines, "\n")
- end
-
- return vdfParser.parseString(content, stopKeyList)
-end
-
-return vdfParser
diff --git a/modules/tools/gamemode.lua b/modules/tools/gamemode.lua
deleted file mode 100644
index de54ee9..0000000
--- a/modules/tools/gamemode.lua
+++ /dev/null
@@ -1,10 +0,0 @@
--- Internal Modules
-local toolObject = require("modules.objects.toolObject")
-
-local name = "gamemoderun"
-local type = "executable"
-local usage = function(command)
- return "gamemoderun "..command
-end
-
-return toolObject:new(name, type, usage)
diff --git a/modules/tools/gamescope.lua b/modules/tools/gamescope.lua
deleted file mode 100644
index bc34396..0000000
--- a/modules/tools/gamescope.lua
+++ /dev/null
@@ -1,59 +0,0 @@
--- Internal Modules
-local toolObject = require("modules.objects.toolObject")
-
-local name = "gamescope"
-local type = "executable"
-local usage = function(command, config)
- local options = ""
-
- -- Nested resolution
- if
- config.gamescope.general.resolution.enabled == true
- then
- options = string.format(
- "%s --nested-width %s --nested-height %s --output-width %s --output-height %s",
- options,
- config.gamescope.general.resolution.internal.width,
- config.gamescope.general.resolution.internal.height,
- config.gamescope.general.resolution.external.width,
- config.gamescope.general.resolution.external.height
- )
- end
-
- -- Frame limit
- if config.gamescope.general.frame_limit.enabled == true then
- options = string.format(
- "%s -r %s -o %s",
- options,
- config.gamescope.general.frame_limit.normal,
- config.gamescope.general.frame_limit.unfocused
- )
- end
-
- -- Fullscreen
- if config.gamescope.general.fullscreen == true then
- options = options.." -f"
- end
- if config.gamescope.general.borderless == true then
- options = options.." -b"
- end
-
- -- Filtering
- if config.gamescope.filtering.enabled == true then
- options = string.format(
- "%s --filter %s --sharpness %s",
- options,
- string.lower(config.gamescope.filtering.filter),
- config.gamescope.filtering.sharpness -- gamescope's sharpness is inverted
- )
- end
-
- -- The command
- return string.format(
- "gamescope %s %s",
- options,
- command
- )
-end
-
-return toolObject:new(name, type, usage)
diff --git a/modules/tools/mangohud.lua b/modules/tools/mangohud.lua
deleted file mode 100644
index 59bb082..0000000
--- a/modules/tools/mangohud.lua
+++ /dev/null
@@ -1,10 +0,0 @@
--- Internal Modules
-local toolObject = require("modules.objects.toolObject")
-
-local name = "mangohud"
-local type = "executable"
-local usage = function(command)
- return "mangohud "..command
-end
-
-return toolObject:new(name, type, usage)
diff --git a/modules/tools/obs-gamecapture.lua b/modules/tools/obs-gamecapture.lua
deleted file mode 100644
index b127f52..0000000
--- a/modules/tools/obs-gamecapture.lua
+++ /dev/null
@@ -1,17 +0,0 @@
--- Internal Modules
-local toolObject = require("modules.objects.toolObject")
-
---[[
- I was tempted to separate this tool in 2 options : one for the utility (supports Vulkan & OpenGL),
- and another for the environment variable which can be used with Vulkan games.
- However, I chose to stay simple and use what works for all : the utility.
- This is seemingly also the approach SteamTinkerLaunch goes with, judging from its wiki.
-]]
-
-local name = "obs-gamecapture"
-local type = "executable"
-local usage = function(command)
- return "obs-gamecapture "..command
-end
-
-return toolObject:new(name, type, usage)
diff --git a/modules/tools/switcherooctl.lua b/modules/tools/switcherooctl.lua
deleted file mode 100644
index 2aa25df..0000000
--- a/modules/tools/switcherooctl.lua
+++ /dev/null
@@ -1,15 +0,0 @@
--- Internal Modules
-local toolObject = require("modules.objects.toolObject")
-
-local name = "switcherooctl"
-local type = "executable"
-local usage = function(command, config)
- if config.utilities.zink.enabled == true then
- -- We let Zink handle the dGPU
- return command
- else
- return "switcherooctl launch "..command
- end
-end
-
-return toolObject:new(name, type, usage)
diff --git a/modules/tools/zink.lua b/modules/tools/zink.lua
deleted file mode 100644
index 3058c2a..0000000
--- a/modules/tools/zink.lua
+++ /dev/null
@@ -1,15 +0,0 @@
--- Internal Modules
-local toolObject = require("modules.objects.toolObject")
-
-local name = "/usr/lib/dri/zink_dri.so"
-local toolType = "environmentVariable"
-local usage = function(command, config)
- if config.dgpu.enabled and require("modules.tools.switcherooctl").isInstalled then
- -- We override switcherooctl (in its function) and let Zink handle the dGPU
- return "DRI_PRIME=1 __GLX_VENDOR_LIBRARY_NAME=mesa MESA_LOADER_DRIVER_OVERRIDE=zink GALLIUM_DRIVER=zink"
- else
- return "MESA_LOADER_DRIVER_OVERRIDE=zink"
- end
-end
-
-return toolObject:new(name, toolType, usage)
diff --git a/modules/ui/UItoSettingsList.lua b/modules/ui/UItoSettingsList.lua
deleted file mode 100644
index 29a6f8d..0000000
--- a/modules/ui/UItoSettingsList.lua
+++ /dev/null
@@ -1,52 +0,0 @@
-return {
- -- Settings
- ["dGPU_Switch"] = {type= "Switch", tool = "switcherooctl", setting = "dgpu.enabled"},
- ["SDL_Wayland_Switch"] = {type= "Switch", setting = "misc.sdl_wayland", os_platform = "Linux"},
-
- -- Utilities
- ["gamemode_Switch"] = {type= "Switch", tool = "gamemoderun", setting = "utilities.gamemode.enabled"},
- ["mangohud_Switch"] = {type= "Switch", tool = "mangohud", setting = "utilities.mangohud.enabled"},
- ["zink_Switch"] = {type= "Switch", tool = "/usr/lib/dri/zink_dri.so", setting = "utilities.zink.enabled"},
- ["obs_gamecapture_Switch"] = {type= "Switch", tool = "obs-gamecapture", setting = "utilities.obs_gamecapture.enabled"},
-
- -- Gamescope
- ["gamescope_Switch"] = {type= "Switch", tool = "gamescope", setting = "gamescope.enabled"},
- -- Resolution
- ["gamescope_Resolution_Switch"] = {type= "Switch", tool = "gamescope", setting = "gamescope.general.resolution.enabled"},
- ["gamescope_Resolution_Internal_Width_SpinRow"] = {type= "SpinRow", tool = "gamescope", setting = "gamescope.general.resolution.internal.width"},
- ["gamescope_Resolution_Internal_Height_SpinRow"] = {type= "SpinRow", tool = "gamescope", setting = "gamescope.general.resolution.internal.height"},
- ["gamescope_Resolution_External_Width_SpinRow"] = {type= "SpinRow", tool = "gamescope", setting = "gamescope.general.resolution.external.width"},
- ["gamescope_Resolution_External_Height_SpinRow"] = {type= "SpinRow", tool = "gamescope", setting = "gamescope.general.resolution.external.height"},
- -- Framerate
- ["gamescope_Framerate_Switch"] = {type= "Switch", tool = "gamescope", setting = "gamescope.general.frame_limit.enabled"},
- ["gamescope_Framerate_Normal_SpinRow"] = {type= "SpinRow", tool = "gamescope", setting = "gamescope.general.frame_limit.normal"},
- ["gamescope_Framerate_Unfocused_SpinRow"] = {type= "SpinRow", tool = "gamescope", setting = "gamescope.general.frame_limit.unfocused"},
- -- Filtering
- ["gamescope_Filtering_Switch"] = {type= "Switch", tool = "gamescope", setting = "gamescope.filtering.enabled"},
- ["gamescope_Filtering_Sharpness_SpinRow"] = {type= "SpinRow", tool = "gamescope", setting = "gamescope.filtering.sharpness"},
- ["gamescope_Filtering_Filter_ComboRow"] = {type= "ComboRow", tool = "gamescope", setting = "gamescope.filtering.filter", items = {"Linear","Nearest","FSR","NIS","Pixel"}},
- -- Extras
- ["gamescope_Borderless_Toggle"] = {type= "Toggle", tool = "gamescope", setting = "gamescope.general.borderless"},
- ["gamescope_Fullscreen_Toggle"] = {type= "Toggle", tool = "gamescope", setting = "gamescope.general.fullscreen"},
-
- -- Proton
- -- Direct3D
- ["Direct3D9_Switch"] = {type= "Switch", setting = "proton.direct3d.enable_direct3d9", os_platform = "Windows"},
- ["Direct3D10_Switch"] = {type= "Switch", setting = "proton.direct3d.enable_direct3d10", os_platform = "Windows"},
- ["Direct3D11_Switch"] = {type= "Switch", setting = "proton.direct3d.enable_direct3d11", os_platform = "Windows"},
- ["Direct3D12_Switch"] = {type= "Switch", setting = "proton.direct3d.enable_direct3d12", os_platform = "Windows"},
- ["WineD3D_Switch"] = {type= "Switch", setting = "proton.direct3d.use_wined3d", os_platform = "Windows"},
- -- Sync
- ["ESync_Toggle"] = {type= "Toggle", setting = "proton.sync.enable_esync", os_platform = "Windows"},
- ["FSync_Toggle"] = {type= "Toggle", setting = "proton.sync.enable_fsync", os_platform = "Windows"},
- -- NVIDIA
- ["Hide_NVIDIA_GPU_Switch"] = {type= "Switch", setting = "proton.nvidia.hide_nvidia_gpu", os_platform = "Windows"},
- ["Enable_NVAPI_Switch"] = {type= "Switch", setting = "proton.nvidia.enable_nvapi", os_platform = "Windows"},
- -- FSR
- ["Wine_FSR_Switch"] = {type= "Switch", setting = "proton.fsr.enabled", os_platform = "Windows"},
- ["Wine_FSR_Sharpness_SpinRow"] = {type= "SpinRow", setting = "proton.fsr.sharpness", os_platform = "Windows"},
- ["Wine_FSR_Upscaling_Resolution_Mode_ComboRow"] = {type = "ComboRow", setting = "proton.fsr.upscaling_mode", os_platform = "Windows", items = {"None", "Performance", "Balanced", "Quality", "Ultra"}},
- ["Wine_FSR_Resolution_Switch"] = {type = "Switch", setting = "proton.fsr.resolution.enabled", os_platform = "Windows"},
- ["Wine_FSR_Resolution_External_Width_SpinRow"] = {type= "SpinRow", setting = "proton.fsr.resolution.width", os_platform = "Windows"},
- ["Wine_FSR_Resolution_External_Height_SpinRow"] = {type= "SpinRow", setting = "proton.fsr.resolution.height", os_platform = "Windows"}
-}
diff --git a/modules/ui/aboutWindow.lua b/modules/ui/aboutWindow.lua
deleted file mode 100644
index 8529e8b..0000000
--- a/modules/ui/aboutWindow.lua
+++ /dev/null
@@ -1,73 +0,0 @@
--- External Modules
-local lgi = require("lgi")
-local Adw = lgi.Adw
-local Gtk = lgi.Gtk
-
--- Internal Modules
-local programMetadata = require("modules.extra.programMetadata")
-
--- Translation fake function
-local function _(text) return text end
-
-return function(application, interface, win)
- local creditLauncher = interface:get_object("aboutLauncher")
- creditLauncher.on_clicked = function()
- -- Create window with main metadata already set
- local creditWindow = Adw.AboutWindow{
- application = application,
- modal = true,
-
- application_icon = programMetadata.icon_name,
- application_name = programMetadata.name,
- version = programMetadata.version,
-
- title = _("About ")..programMetadata.name,
- developer_name = programMetadata.developer,
- website = programMetadata.url,
- issue_url = programMetadata.url.."/issues",
- license_type = Gtk.License.MPL_2_0,
-
- developers = {
- "JordanViknar https://github.com/JordanViknar",
- },
- artists = {
- "JordanViknar https://github.com/JordanViknar"
- },
- documenters = {
- "JordanViknar https://github.com/JordanViknar"
- }
- }
-
- -- Credits
- creditWindow:add_acknowledgement_section(_("Inspired by"),{
- "SteamTinkerLaunch https://github.com/sonic2kk/steamtinkerlaunch",
- "Bottles https://github.com/bottlesdevs/Bottles"
- })
- creditWindow:add_acknowledgement_section(_("Powered by"),{
- "Lua https://www.lua.org/",
- "LGI https://github.com/lgi-devs/lgi",
- "GTK https://www.gtk.org/",
- "libadwaita https://gnome.pages.gitlab.gnome.org/libadwaita/",
- "LuaFileSystem https://github.com/lunarmodules/luafilesystem",
- "LuaSocket https://github.com/lunarmodules/luasocket",
- "dkjson http://dkolf.de/src/dkjson-lua.fsl/home"
- })
- creditWindow:add_acknowledgement_section(_("Third-Party Tools and Special Thanks"),{
- "Steam https://store.steampowered.com/",
- "Proton https://github.com/ValveSoftware/proton",
- "GameMode https://github.com/FeralInteractive/gamemode",
- "MangoHud https://github.com/flightlessmango/MangoHud",
- "Zink https://docs.mesa3d.org/drivers/zink.html",
- "ProtonDB https://www.protondb.com/",
- "PCGamingWiki https://www.pcgamingwiki.com/",
- "SteamDB https://steamdb.info/",
- "switcheroo-control https://gitlab.freedesktop.org/hadess/switcheroo-control",
- "Gamescope https://github.com/ValveSoftware/gamescope"
- })
-
- creditWindow:set_comments(_("A work-in-progress fast, simple and modern Libadwaita alternative to SteamTinkerLaunch."))
-
- creditWindow:set_transient_for(win)
- creditWindow:present()
- end
-end
diff --git a/modules/ui/gameSettingsOverview.lua b/modules/ui/gameSettingsOverview.lua
deleted file mode 100644
index 82064dd..0000000
--- a/modules/ui/gameSettingsOverview.lua
+++ /dev/null
@@ -1,167 +0,0 @@
--- Internal Modules
-local lgiHelper = require("modules.ui.lgiHelper")
-local protonDBManager = require("modules.steam.protonDBManager")
-local fsUtils = require("modules.general.fsUtils")
-local logSystem = require("modules.general.logSystem")
-local systemUtils = require("modules.general.systemUtils")
-
--- External Modules
-local lgi = require("lgi")
-local Adw = lgi.Adw
-local Gio = lgi.Gio
-
-return function(application, interface, steamGameData)
- --[[
- SIDEBAR
- ]]
- -- Set the game banner in the sidebar
- interface:get_object("Sidebar_Banner"):set_filename(steamGameData.images.header)
-
- --[[
- OVERVIEW
- ]]
- -- Sets the game image in the overview area
- interface:get_object("Overview_Picture"):set_filename(steamGameData.images.library)
- -- Sets the game title in the overview area
- interface:get_object("gameTitle").label = steamGameData.name
-
- -- ProtonDB rating
- local protonRating_Label = interface:get_object("protonDBRating_Label")
- protonRating_Label.label = "Loading..."
- protonRating_Label.css_classes = {"dim-label"}
- if steamGameData.os_platform == "Windows" then
-
- local function loadProtonDBrating()
- -- If the game's rating hasn't been retrieved yet, we retrieve it.
- -- The reason why this is done HERE is because it would slow down startup otherwise.
- if steamGameData.protondb_data == nil then
- logSystem.log("download", "Loading ProtonDB rating for "..steamGameData.name.."...")
- steamGameData.protondb_data = protonDBManager.getAppInfo(steamGameData.id)
- end
-
- -- We set the rating label
- if steamGameData.protondb_data == "Not found" then
- protonRating_Label.css_classes = {"error"}
- elseif steamGameData.protondb_data == "Unavailable" then
- protonRating_Label.css_classes = {"warning"}
- elseif steamGameData.protondb_data["tier"] == "platinum" then
- protonRating_Label.css_classes = {"success"}
- elseif steamGameData.protondb_data["tier"] == "silver" or "gold" then
- protonRating_Label.css_classes = {"warning"}
- else
- protonRating_Label.css_classes = {"error"}
- end
- local rating = steamGameData.protondb_data["tier"] or steamGameData.protondb_data
- -- Set label and capitalize the first letter
- protonRating_Label.label = rating:sub(1, 1):upper()..rating:sub(2)
- end
- Gio.Async.start(loadProtonDBrating)()
-
- else
- protonRating_Label.label = "Native"
- protonRating_Label.css_classes = {"success"}
- end
-
- -- Sets up the game start button
- local gameLaunchButton = interface:get_object("gameLaunchButton")
- lgiHelper.replaceSignal(gameLaunchButton, "on_clicked", function()
- os.execute("xdg-open steam://rungameid/"..steamGameData.id.." &> /dev/null")
- end)
-
- -- Sets up the game's SimpleSteamTinker status
- local gameStatus, gameColor
- if steamGameData.status == true then
- gameStatus = "Enabled"
- gameColor = "success"
- elseif steamGameData.status == "SteamTinkerLaunch" then
- gameStatus = "SteamTinkerLaunch"
- gameColor = "warning"
- else
- gameStatus = "Disabled"
- gameColor = "error"
- end
- local gameStatus_Label = interface:get_object("gameStatus_Label")
- gameStatus_Label.label = gameStatus
- gameStatus_Label.css_classes = {gameColor}
-
- -- Sets the game ID in the overview area
- interface:get_object("gameID_Label").label = steamGameData.id
- -- Sets up the game ID copy button in the overview area
- local toastSystem = interface:get_object("toastSystem")
- local gameIDCopyButton = interface:get_object("gameID_copyButton")
- lgiHelper.replaceSignal(gameIDCopyButton, "on_clicked", function()
- if systemUtils.copyToClipboard(steamGameData.id) then
- toastSystem:add_toast(
- Adw.Toast.new("Game ID copied to clipboard !")
- )
- end
- end)
- -- Sets the game platform in the overview area
- interface:get_object("gamePlatform_Label").label = steamGameData.os_platform
-
- -- Sets the game size in the overview area
- local gameSize_Label = interface:get_object("gameSize_Label")
- gameSize_Label.label = "Loading..."
- if not steamGameData.size then
- logSystem.log("fileRead", "Detecting size for "..steamGameData.name.."...")
-
- local function insertGameSize()
- steamGameData.size = fsUtils.sizeToUnit(fsUtils.getSize(steamGameData.location))
- gameSize_Label.label = steamGameData.size
- end
- Gio.Async.start(insertGameSize)()
-
- else
- gameSize_Label.label = steamGameData.size
- end
-
- -- Sets the game folder in the overview area
- interface:get_object("gameLocation_ActionRow").subtitle = steamGameData.location
- -- Modifies the button to get to the game's folder
- local gameFolderButton = interface:get_object("gameLocationButton")
- lgiHelper.replaceSignal(gameFolderButton, "on_clicked", function()
- os.execute("xdg-open '"..steamGameData.location:gsub("'", "'\\''").."' &> /dev/null")
- end)
-
- -- Sets the compatdata folder in the overview area
- local gameCompatdata_ActionRow = interface:get_object("gameCompatdata_ActionRow")
- if steamGameData.os_platform == "Windows" then
- gameCompatdata_ActionRow.visible = true
- if steamGameData.proton_config.compatdata then
- gameCompatdata_ActionRow:set_sensitive(true)
- gameCompatdata_ActionRow.subtitle = steamGameData.proton_config.compatdata
- -- Modifies the button to get to the game's compatdata folder
- lgiHelper.replaceSignal(interface:get_object("gameCompatdataButton"), "on_clicked", function()
- os.execute("xdg-open '"..steamGameData.proton_config.compatdata:gsub("'", "'\\''").."' &> /dev/null")
- end)
- else
- gameCompatdata_ActionRow:set_sensitive(false)
- gameCompatdata_ActionRow.subtitle = "Not found. Try launching the game at least once ?"
- end
- else
- gameCompatdata_ActionRow.visible = false
- end
-
- --[[
- LINKS
- ]]
- local protonDBPage_Button = interface:get_object("protonDBPage_Button")
- lgiHelper.replaceSignal(protonDBPage_Button, "on_clicked", function()
- os.execute("xdg-open 'https://www.protondb.com/app/"..steamGameData.id.."' &> /dev/null")
- end)
-
- local steamDBPage_Button = interface:get_object("steamDBPage_Button")
- lgiHelper.replaceSignal(steamDBPage_Button, "on_clicked", function()
- os.execute("xdg-open 'https://steamdb.info/app/"..steamGameData.id.."' &> /dev/null")
- end)
-
- local PCGamingWikiPage_Button = interface:get_object("PCGamingWikiPage_Button")
- lgiHelper.replaceSignal(PCGamingWikiPage_Button, "on_clicked", function()
- os.execute("xdg-open 'https://www.pcgamingwiki.com/wiki/"..steamGameData.name:gsub(" ", "_").."' &> /dev/null")
- end)
-
- local SteambasePage_Button = interface:get_object("SteambasePage_Button")
- lgiHelper.replaceSignal(SteambasePage_Button, "on_clicked", function()
- os.execute("xdg-open 'https://steambase.io/apps/"..steamGameData.id.."' &> /dev/null")
- end)
-end
diff --git a/modules/ui/lgiHelper.lua b/modules/ui/lgiHelper.lua
deleted file mode 100644
index 75b1830..0000000
--- a/modules/ui/lgiHelper.lua
+++ /dev/null
@@ -1,77 +0,0 @@
--- External Modules
-local lgi = require("lgi")
-local GObject = lgi.GObject
-
--- Internal Modules
-local systemUtils = require("modules.general.systemUtils")
-local configManager = require("modules.config.configManager")
-
-local lgiHelper = {}
-
---[[
- Name : function lgiHelper.replaceSignal(object, signal, action)
- Description : This function helps replacing signals on widgets.
- For example, if you want to replace the "on_clicked" signal of a button.
-
- It avoids bugs like opening a game's directory also opening the previous ones.
- Arg 1 : The object to modify.
- Arg 2 : The signal to replace.
- Arg 3 : The function to run (when clicking for example).
-]]
-local signalList = {}
-function lgiHelper.replaceSignal(object, signal, action)
- lgiHelper.removeSignal(object, signal)
-
- if signalList[object] == nil then signalList[object] = {} end
- signalList[object][signal] = object[signal]:connect(action)
-end
-
---[[
- Name : function lgiHelper.removeSignal(object, signal)
- Description : Simply deactivate the function of a button.
- Arg 1 : The object to modify.
- Arg 2 : The signal to remove.
-]]
-function lgiHelper.removeSignal(object, signal)
- -- We create a table for the object if it doesn't exist.
- if signalList[object] == nil then signalList[object] = {} end
- -- If the object already has an event connected to the same signal, we disconnect it.
- if signalList[object][signal] then
- GObject.signal_handler_disconnect(object, signalList[object][signal])
- signalList[object][signal] = nil
- end
-end
-
---[[
- Name : function lgiHelper.connectUtilityToButton(id, button, utility, property, setting)
- Description : Connects a utility to a button.
- Arg 1 : string id : The game's ID.
- Arg 2 : The button to connect.
- Arg 3 : string utility : The utility's name.
- Arg 4 : string property : The property to connect.
- Arg 5 : string setting : The setting to modify.
-]]
-function lgiHelper.connectUtilityToButton(id, button, utility, property, setting)
- local signal = "on_state_set"
- lgiHelper.removeSignal(button, signal)
-
- -- If utility begins with /, then it's a file path.
- local isPresent = systemUtils.isInstalled(utility)
-
- if isPresent then
- button:set_sensitive(true)
- button:set_active(property)
-
- if signalList[button] == nil then signalList[button] = {} end
- signalList[button][signal] = button[signal]:connect(function()
- configManager.modifyGameConfig(id, setting, button:get_active())
- end)
- else
- button:set_sensitive(false)
- button:set_active(false)
- button.has_tooltip = true
- button.tooltip_text = "'"..utility.."' is not present on your system."
- end
-end
-
-return lgiHelper
diff --git a/modules/ui/mainWindow.lua b/modules/ui/mainWindow.lua
deleted file mode 100644
index da8014a..0000000
--- a/modules/ui/mainWindow.lua
+++ /dev/null
@@ -1,236 +0,0 @@
--- Internal Modules
-local programMetadata = require("modules.extra.programMetadata")
-local configManager = require("modules.config.configManager")
-local systemUtils = require("modules.general.systemUtils")
-local lgiHelper = require("modules.ui.lgiHelper")
-
--- External Modules
-local lgi = require("lgi")
-local Gtk = lgi.require("Gtk")
-local Adw = lgi.Adw
-
--- Install checks
-local installedList = {}
-
-return function(app, steamGames)
- -- We create the window
- local builder = Gtk.Builder.new_from_file(programMetadata.installdir.."ui/main.ui")
- local win = builder:get_object("mainWindow")
- win.application = app
- win.title = programMetadata.name
- win.startup_id = programMetadata.name
-
- -- Check for dev version and add relevant theme
- if programMetadata.version:find("dev") or programMetadata.version:find("a") or programMetadata.version:find("b") then
- win:add_css_class("devel")
- end
-
- --[[
- UI ELEMENTS
- ]]
- -- Page management
- local mainView = builder:get_object("mainView")
- local gameList = builder:get_object("gameList")
-
- local gameSettingsInterface = builder:get_object("gameSettings")
-
- -- Stack test
- local stack = builder:get_object("gameSettingsStack")
-
- -- For every game that has SimpleSteamMod enabled, we add a button in this list
- for _, game in pairs(steamGames) do
- if game.type == "game" then
- local statusIcon, statusLabel, statusColor
- -- Status label and icon
- if game.status == true then
- statusIcon = "emblem-default-symbolic"
- statusLabel = programMetadata.name.." is enabled for this game."
- statusColor = "success"
- elseif game.status == "SteamTinkerLaunch" then
- statusIcon = "emblem-important-symbolic"
- statusLabel = "SteamTinkerLaunch is enabled for this game instead of "..programMetadata.name.."."
- statusColor = "warning"
- else
- statusIcon = "dialog-error-symbolic"
- statusLabel = programMetadata.name.." is disabled for this game."
- statusColor = ""
- end
-
- -- OS Label
- local osLabel, osColor, osTooltip
- if game.os_platform == "Linux" then
- osLabel = "Native"
- osColor = "success"
- osTooltip = "This is a native Linux game."
- elseif game.os_platform == "Windows" then
- osLabel = "Proton"
- osColor = "accent"
- osTooltip = "This is a Windows game running through Proton."
- end
-
- -- The row
- local row = Adw.ActionRow {
- title = game.name,
- use_markup = false, -- Used to escape the ampersand
- activatable = true,
- subtitle_lines = 1
- }
-
- -- The game's image
- local image = Gtk.Image {
- file = game.images.icon,
- icon_size = Gtk.IconSize.LARGE
- }
- row:add_prefix(image)
-
- local box = Gtk.Box {
- orientation = Gtk.Orientation.HORIZONTAL,
- spacing = 12,
- Gtk.Label {
- label = osLabel,
- css_classes = { osColor, "dim-label" },
- has_tooltip = true,
- tooltip_text = osTooltip
- },
- Gtk.Image {
- icon_name = statusIcon,
- valign = Gtk.Align.CENTER,
- halign = Gtk.Align.CENTER,
- has_tooltip = true,
- tooltip_text = statusLabel,
- css_classes = { statusColor }
- }
- }
- local button = Gtk.Button {
- id = "button",
- icon_name = "go-next-symbolic",
- has_frame = false,
- valign = Gtk.Align.CENTER,
- halign = Gtk.Align.CENTER,
- css_classes = { "circular", "flat", "image-button" },
- on_clicked = function()
- -- We setup the gameSettings Overview UI for this game
- require("modules.ui.gameSettingsOverview")(app, builder, game)
-
- -- We connect the UI elements to the game settings
- local UIelements = require("modules.ui.UItoSettingsList")
- local gameSettings = configManager.getGameConfig(game.id)
-
- -- We iterate through the UI elements
- for widgetname, data in pairs(UIelements) do
- local widget = builder:get_object(widgetname)
-
- -- Split the setting into keys
- local keys = {}
- for substring in data.setting:gmatch("[^.]+") do
- keys[#keys + 1] = substring
- end
- -- Connect to the settings table
- local pointer = gameSettings
- for i = 1, #keys - 1 do
- pointer = pointer[keys[i]] or {}
- end
-
- -- We determine the right signal to use
- local signal
- if data.type == "Switch" then signal = "on_state_set"
- elseif data.type == "SpinRow" then signal = "on_changed"
- elseif data.type == "Toggle" then signal = "on_toggled"
- elseif data.type == "ComboRow" then
- signal = "on_notify" -- Not optimal, but it works. Isn't there a better way to do this ?
- else
- error("Unknown signal to use with type '"..data.type.."' for UI element '"..widgetname.."'")
- end
-
- lgiHelper.removeSignal(widget, signal)
-
- -- We hide the option if it doesn't fit the platform
- if data.os_platform and data.os_platform ~= game.os_platform then
- widget.visible = false
- goto skip
- elseif widget.visible ~= true then
- widget.visible = true
- end
-
- -- Should they be active ? Is the related tool installed ?
- if data.tool then
- if not installedList[data.tool] then
- installedList[data.tool] = systemUtils.isInstalled(data.tool)
- end
- if installedList[data.tool] == false then
- widget:set_sensitive(false)
- widget.has_tooltip = true
- widget.tooltip_text = data.tool.." is not present on your system."
- goto skip
- else
- widget:set_sensitive(true)
- end
- end
-
- -- We connect the UI element to the setting
- if data.type == "Switch" or data.type == "Toggle" then
- widget:set_active(pointer[keys[#keys]])
- lgiHelper.replaceSignal(widget, signal,function() configManager.modifyGameConfig(game.id, data.setting, widget:get_active()) end)
- elseif data.type == "SpinRow" then
- widget.value = pointer[keys[#keys]]
- lgiHelper.replaceSignal(widget, signal,function() configManager.modifyGameConfig(game.id, data.setting, math.floor(widget:get_value())) end)
- elseif data.type == "ComboRow" then
- -- We grab the model used
- local model = widget:get_model()
-
- -- We select the item that's already configured in the settings
- for index, filter in ipairs(data.items) do
- local currentFilter = configManager.grabInTableFromString(gameSettings, data.setting)
- if currentFilter == filter then
- widget:set_selected(index - 1)
- break -- No need to continue
- end
- end
-
- -- We connect to the setting
- lgiHelper.replaceSignal(widget, signal, function()
- local selected = model:get_string(widget:get_selected())
- configManager.modifyGameConfig(game.id, data.setting, selected)
- end)
- else
- error("Unknown type '"..data.type.."' for UI element '"..widgetname.."'")
- end
-
- ::skip::
- end
-
- -- Windows only pages
- local windowsPages = {"protonPage"}
- for _, page in ipairs(windowsPages) do
- page = builder:get_object(page)
- if game.os_platform == "Windows" then
- page.visible = true
- else
- page.visible = false
- end
- end
-
- -- We finally push the settings page to the user.
- mainView:push(gameSettingsInterface)
- end
- }
- box:append(button)
- row:add_suffix(box)
- row.activatable_widget = button
-
- gameList:add(row)
- end
- end
-
- --Topbar management
- builder:get_object("mainPageTopbar"):set_top_bar_style(Adw.ToolbarStyle.RAISED_BORDER)
- builder:get_object("gameSettingsTopbar"):set_top_bar_style(Adw.ToolbarStyle.RAISED_BORDER)
-
- -- Overview restoration when we exit out of the settings
- gameSettingsInterface.on_hidden = function()
- stack:set_visible_child_name("overviewPage")
- end
-
- -- Credits
- require("modules.ui.aboutWindow")(app, builder, win)
-end
diff --git a/modules/utilitiesList.lua b/modules/utilitiesList.lua
deleted file mode 100644
index e31d503..0000000
--- a/modules/utilitiesList.lua
+++ /dev/null
@@ -1,15 +0,0 @@
-return function (gameConfig)
- return
-
- -- Executables are ordered by priority (first only applies to the game, last applies to the whole sequence)
-
- {
- {require("modules.tools.zink"),gameConfig.utilities.zink.enabled},
- {require("modules.tools.gamescope"),gameConfig.gamescope.enabled},
- {require("modules.tools.switcherooctl"),gameConfig.dgpu.enabled},
- {require("modules.tools.mangohud"),gameConfig.utilities.mangohud.enabled},
- {require("modules.tools.obs-gamecapture"),gameConfig.utilities.obs_gamecapture.enabled},
- {require("modules.tools.gamemode"),gameConfig.utilities.gamemode.enabled}
- }
-
-end
diff --git a/rokit.toml b/rokit.toml
new file mode 100644
index 0000000..f98c9ba
--- /dev/null
+++ b/rokit.toml
@@ -0,0 +1,10 @@
+# This file lists tools managed by Rokit, a toolchain manager for Roblox projects.
+# For more information, see https://github.com/rojo-rbx/rokit
+
+# New tools can be added by running `rokit add ` in a terminal.
+
+[tools]
+lune = "lune-org/lune@0.8.8"
+darklua = "seaofvoices/darklua@0.13.1"
+selene = "Kampfkarren/selene@0.27.1"
+StyLua = "JohnnyMorganz/StyLua@0.20.0"
diff --git a/selene.toml b/selene.toml
new file mode 100644
index 0000000..49bce28
--- /dev/null
+++ b/selene.toml
@@ -0,0 +1,2 @@
+std = "luau"
+exclude = ["luneTypes.d.luau"]
diff --git a/src/Classes/SteamApp.luau b/src/Classes/SteamApp.luau
new file mode 100644
index 0000000..2559cd0
--- /dev/null
+++ b/src/Classes/SteamApp.luau
@@ -0,0 +1,77 @@
+local fs = require("@lune/fs")
+local process = require("@lune/process")
+
+local Metadata = require("@Metadata/")
+
+local SteamApp = {}
+
+SteamApp.Interface = {}
+SteamApp.Prototype = {}
+
+function SteamApp.Interface.new(
+ appID: number,
+ name: string,
+ library: string,
+ location: string,
+ platform: ("windows" | "linux")?,
+ launchArguments: string?
+): SteamApp
+ -- Is the app configured to use STweaks ?
+ local tweaksEnabled = false
+ if launchArguments and launchArguments:find(Metadata.executable .. " ") then
+ tweaksEnabled = true
+ end
+
+ -- Does the app manifest specify the platform ?
+ -- Could be simplified
+ local osPlatform: ("Windows" | "Linux")?
+ if platform == "windows" then
+ osPlatform = "Windows"
+ elseif platform == "linux" then
+ osPlatform = "Linux"
+ end
+
+ -- What type is the app ?
+ -- Current way of detecting if an app is a game or a tool... not the greatest.
+ local type: "Game" | "Other" | "Proton" = "Game"
+ if not fs.metadata(process.env.HOME .. "/.local/share/Steam/appcache/librarycache/" .. appID .. "_library_600x900.jpg").exists then
+ type = "Other"
+ if name:find("Proton") then
+ type = "Proton"
+ end
+ end
+
+ -- We return the object
+ return {
+ appID = appID,
+ name = name,
+ library = library,
+ location = location,
+ size = nil,
+ protonDBData = nil,
+ type = type,
+ osPlatform = osPlatform,
+ protonConfig = nil,
+ tweaksEnabled = tweaksEnabled,
+ }
+end
+
+export type SteamApp = {
+ appID: number,
+ name: string,
+ tweaksEnabled: boolean?,
+ library: string,
+ location: string,
+ type: "Game" | "Other" | "Proton",
+ osPlatform: ("Windows" | "Linux")?,
+ size: number?, -- Slow, done in UI (Currently unused)
+ protonDBData: { any }?, -- Slow, done in UI (Currently unused)
+ protonConfig: ProtonConfig?, -- (Currently unused)
+}
+
+type ProtonConfig = {
+ compatdata_path: string,
+ version: string?, -- Another file needs to be checked for this information, currently unused
+}
+
+return SteamApp.Interface
diff --git a/src/Configuration/Games/Default.luau b/src/Configuration/Games/Default.luau
new file mode 100644
index 0000000..f0b6cea
--- /dev/null
+++ b/src/Configuration/Games/Default.luau
@@ -0,0 +1,99 @@
+local SteamApp = require("@Classes/SteamApp")
+
+export type GameConfiguration = {
+ appID : number,
+ version : number,
+
+ settings : {
+ dgpu : boolean,
+ zink : boolean,
+ sdl_wayland : boolean
+ },
+
+ utilities : {
+ gamemode : boolean,
+ mangohud : {
+ enabled : boolean,
+ },
+ gamescope : {
+ enabled : boolean,
+ general : {
+ resolution : {
+ enabled : boolean;
+ internal : {
+ width : number,
+ height : number,
+ },
+ external : {
+ width : number,
+ height : number,
+ },
+ },
+ frame_limit : {
+ enabled : boolean,
+ normal : number,
+ unfocused : number,
+ },
+ fullscreen : boolean,
+ borderless : boolean
+ },
+ filtering : {
+ enabled : boolean,
+ filter : "Linear" | "Nearest" | "FSR" | "NIS" | "Pixel",
+ sharpness : number,
+ }
+ },
+ }
+}
+
+local Default = {}
+
+function Default.getDefaultGameConfiguration(appData : SteamApp.SteamApp) : GameConfiguration
+ return {
+ appID = appData.appID,
+ version = 1,
+
+ settings = {
+ dgpu = true,
+ zink = false,
+ sdl_wayland = true
+ },
+
+ utilities = {
+ gamemode = true,
+ mangohud = {
+ enabled = true,
+ },
+ gamescope = {
+ enabled = false,
+ general = {
+ resolution = {
+ enabled = false,
+ internal = {
+ width = 1280,
+ height = 720,
+ },
+ external = {
+ width = 1920,
+ height = 1080,
+ },
+ },
+ frame_limit = {
+ enabled = false,
+ normal = 60,
+ unfocused = 5,
+ },
+ fullscreen = false,
+ borderless = false
+ },
+ filtering = {
+ enabled = false,
+ filter = "FSR",
+ sharpness = 5,
+ }
+ },
+ }
+ }
+end
+
+return Default
diff --git a/src/Configuration/Games/Functions.luau b/src/Configuration/Games/Functions.luau
new file mode 100644
index 0000000..049bb5b
--- /dev/null
+++ b/src/Configuration/Games/Functions.luau
@@ -0,0 +1,41 @@
+local fs = require("@lune/fs")
+local serde = require("@lune/serde")
+
+local SteamApp = require("@Classes/SteamApp")
+local Metadata = require("@Metadata/")
+local Logging = require("@Utilities/Logging")
+
+local Default = require("./Default")
+
+local function createGameConfiguration(appData : SteamApp.SteamApp) : Default.GameConfiguration
+ local newConfig = Default.getDefaultGameConfiguration(appData)
+
+ Logging.write("info", "Creating default configuration for game " .. appData.name .. " (" .. appData.appID .. ")...")
+ local configString = serde.encode("toml", newConfig, true)
+ fs.writeFile(string.format(
+ "%s/%s.toml",
+ Metadata.folders.gamesConfig,
+ tostring(appData.appID)
+ ), configString)
+
+ return newConfig
+end
+
+local GameConfigurations = {}
+
+function GameConfigurations.getGameConfiguration(appData : SteamApp.SteamApp) : Default.GameConfiguration
+ local configPath = string.format(
+ "%s/%s.toml",
+ Metadata.folders.gamesConfig,
+ tostring(appData.appID)
+ )
+
+ if fs.metadata(configPath).exists then
+ local configString = fs.readFile(configPath)
+ return serde.decode("toml", configString)
+ else
+ return createGameConfiguration(appData)
+ end
+end
+
+return GameConfigurations
\ No newline at end of file
diff --git a/src/Configuration/init.luau b/src/Configuration/init.luau
new file mode 100644
index 0000000..61a264c
--- /dev/null
+++ b/src/Configuration/init.luau
@@ -0,0 +1,5 @@
+return {
+ Games = {
+ Functions = require("./Games/Functions")
+ }
+}
\ No newline at end of file
diff --git a/src/Launcher/Functions.luau b/src/Launcher/Functions.luau
new file mode 100644
index 0000000..f9a13aa
--- /dev/null
+++ b/src/Launcher/Functions.luau
@@ -0,0 +1,17 @@
+local SteamApp = require("../Classes/SteamApp")
+
+local GameConfiguration = require("@Configuration/").Games.Functions
+
+local Launcher = {}
+
+function Launcher.prepareLaunchCommand(appData: SteamApp.SteamApp): { string }
+ -- First and foremost, we recover the configuration of the game.
+ local ok, appConfig = pcall(GameConfiguration.getGameConfiguration, appData)
+ if not ok then
+ error("Failed to get game configuration for " .. appData.name .. " (" .. appData.appID .. ").")
+ end
+
+ error("Not implemented.")
+end
+
+return Launcher
diff --git a/src/Metadata.luau b/src/Metadata.luau
new file mode 100644
index 0000000..eec65ec
--- /dev/null
+++ b/src/Metadata.luau
@@ -0,0 +1,14 @@
+local process = require("@lune/process")
+
+return {
+ name = "STweaks",
+ description = "A work-in-progress fast, simple and modern Libadwaita alternative to SteamTinkerLaunch.",
+ version = "indev",
+ executable = "stweaks",
+ folders = {
+ config = process.env.HOME .. "/.config/Stweaks",
+ gamesConfig = process.env.HOME .. "/.config/Stweaks/Games",
+ storage = process.env.HOME .. "/.local/share/Stweaks",
+ cache = process.env.HOME .. "/.cache/Stweaks",
+ },
+}
diff --git a/src/Steam/Configuration.luau b/src/Steam/Configuration.luau
new file mode 100644
index 0000000..210405a
--- /dev/null
+++ b/src/Steam/Configuration.luau
@@ -0,0 +1,277 @@
+local fs = require("@lune/fs")
+local process = require("@lune/process")
+
+local Logging = require("@Utilities/Logging")
+local Async = require("@Utilities/Async")
+local Filesystem = require("@Utilities/Filesystem")
+local SteamUtilities = require("./Utilities")
+local VDFParser = require("./VDFParser")
+local SteamApp = require("@Classes/SteamApp")
+
+local function VDFHandler(filePath: string, errorMessage: string, logging: boolean?): any
+ if logging == nil then
+ logging = true
+ end
+
+ local timeStart
+ if logging then
+ timeStart = os.clock()
+ Logging.write("data", "Parsing VDF file : " .. filePath)
+ end
+
+ local result, parsedData = pcall(VDFParser.parseFile, filePath)
+ if not result then
+ error(errorMessage)
+ else
+ if logging then
+ Logging.write("speed", timeStart)
+ end
+ return parsedData
+ end
+end
+
+local function getMostRecentUserID(userData: loginusers_vdf): number
+ for id, data in pairs(userData.users) do
+ if data.MostRecent == 1 then
+ return id
+ end
+ end
+ error("No most recent user found in loginusers.vdf. What ?!")
+end
+
+-- --------------------------- Definitions -----------------------------
+
+-- Note : Most of the entries are unused and simply for informative purposes.
+
+type loginusers_vdf = {
+ users: {
+ [number]: {
+ AccountName: string,
+ PersonaName: string,
+ RememberPassword: number,
+ WantsOfflineMode: number,
+ SkipOfflineModeWarning: number,
+ AllowAutoLogon: number,
+ MostRecent: number,
+ Timestamp: number,
+ },
+ },
+}
+type localconfig_vdf = {
+ UserLocalConfigStore: {
+ Software: {
+ Valve: {
+ Steam: {
+ apps: {
+ [number]: {
+ cloud: {
+ last_sync_state: ("synchronized" | "changesincloud")?,
+ quota_bytes: number?,
+ quota_files: number?,
+ used_bytes: number?,
+ used_files: number?,
+ }?,
+ LastPlayed: number?,
+ Playtime: number?,
+ Playtime2wks: number?,
+ LaunchOptions: string?, -- IMPORTANT !
+ autocloud: {
+ lastlaunch: number,
+ lastexit: number,
+ }?,
+ ViewedSteamPlay: number?,
+ BadgeData: string?,
+ },
+ },
+ },
+ },
+ },
+ },
+}
+type libraryfolders_vdf = {
+ libraryfolders: {
+ [number]: {
+ path: string,
+ label: string?,
+ contentid: number,
+ totalsize: number,
+ update_clean_bytes_tally: number,
+ time_last_update_corruption: number,
+ apps: {
+ [number]: number,
+ },
+ }?,
+ },
+}
+
+type appmanifest_vdf = {
+ AppState: {
+ appid: number,
+ Universe: number,
+ name: string,
+ StateFlags: number,
+ installdir: string,
+ LastUpdated: number,
+ LastPlayed: number,
+ SizeOnDisk: number,
+ StagingSize: number,
+ buildid: number,
+ LastOwner: number,
+ UpdateResult: number?,
+ BytesToDownload: number?,
+ BytesDownloaded: number?,
+ BytesToStage: number?,
+ BytesStaged: number?,
+ TargetBuildID: number?,
+ AutoUpdateBehavior: number,
+ AllowOtherDownloadsWhileRunning: number,
+ ScheduledAutoUpdate: number,
+ InstalledDepots: {
+ [number]: {
+ manifest: number,
+ size: number,
+ },
+ },
+ InstallScripts: {
+ [number]: number,
+ }?,
+ SharedDepots: {
+ [number]: number,
+ }?,
+ UserConfig: {
+ language: string?,
+ BetaKey: string?,
+ platform_override_dest: "linux"?,
+ platform_override_source: ("linux" | "windows")?,
+ },
+ MountedConfig: {
+ language: string?,
+ BetaKey: string?,
+ platform_override_dest: "linux"?,
+ platform_override_source: ("linux" | "windows")?, -- IMPORTANT ! (Too bad it seems to never be present in Linux games, and sometimes not in Windows games... :/)
+ },
+ },
+}
+
+type compat_vdf = {
+ platform_overrides: {
+ [number]: {
+ dest: "windows" | "linux",
+ src: "windows" | "linux",
+ },
+ },
+}
+
+-- --------------------------- Script -----------------------------
+
+local Configuration = {}
+
+function Configuration.getSteamConfiguration()
+ local timeStart = os.clock()
+
+ --[[
+ Chapter 1 : We recover the Steam user config so we can get the last active user.
+ And also the SteamID3 to access their settings folder.
+ ]]
+ local userConfiguration: loginusers_vdf =
+ VDFHandler(process.env.HOME .. "/.local/share/Steam/config/loginusers.vdf", "Failed to parse loginusers.vdf file.")
+ local activeUserSteamID3 = SteamUtilities.convertToSteamID3(getMostRecentUserID(userConfiguration))
+ Logging.write("info", "Active user SteamID3 : " .. activeUserSteamID3)
+ Logging.separator()
+
+ --[[
+ Chapter 2 : We recover the user config to get which games have the tool enabled.
+
+ Note : Error handling should be added for the scenario where the configuration can't be loaded.
+ ]]
+ local localconfig_vdf: localconfig_vdf = VDFHandler(
+ process.env.HOME .. "/.local/share/Steam/userdata/" .. activeUserSteamID3 .. "/config/localconfig.vdf",
+ "Failed to parse active user localconfig.vdf file with SteamID3: " .. activeUserSteamID3
+ )
+ local userGameConfigurations = localconfig_vdf.UserLocalConfigStore.Software.Valve.Steam.apps
+ Logging.write("info", "User game configurations loaded.")
+ Logging.separator()
+
+ --[[
+ Chapter 2.5 : We recover compat.vdf to know if a game has ever used its Windows version.
+ We can't use it directly to get the platform details, as games that have previously used Proton will still be set to "windows" here.
+ Logically, a game completely absent from this list is a native Linux game.
+ ]]
+ local compat_vdf: compat_vdf = VDFHandler(
+ process.env.HOME .. "/.local/share/Steam/userdata/" .. activeUserSteamID3 .. "/config/compat.vdf",
+ "Failed to parse compat.vdf file."
+ )
+ Logging.write("info", "Compatibility data loaded.")
+ Logging.separator()
+
+ --[[
+ Chapter 3 : We recover the Steam library config to get the list of games.
+ We can't use just libraryfolders.vdf to get the game IDs, as Steam seems to not always update it immediately.
+ ]]
+ local libraries: libraryfolders_vdf =
+ VDFHandler(process.env.HOME .. "/.local/share/Steam/config/libraryfolders.vdf", "Failed to parse libraryfolders.vdf file.")
+ Logging.write("info", "Steam libraries loaded.")
+ Logging.separator()
+
+ local steamApps = {}
+ local steamAppCount = 0
+
+ Async.asyncForEach(libraries.libraryfolders, function(_, library)
+ -- We check if the folder exists
+ if not fs.metadata(library.path).exists then
+ Logging.write("warn", "Steam library " .. library.path .. " doesn't exist. Skipping...")
+ else
+ Logging.write("data", "Checking library " .. library.path)
+ local appManifestFilenames = Filesystem.getFilenamePatternInDirectory(library.path .. "/steamapps", "appmanifest_")
+
+ Async.asyncForEach(appManifestFilenames, function(_, appManifestFilename)
+ local appManifestPath: string = library.path .. "/steamapps/" .. appManifestFilename
+ local appManifest: appmanifest_vdf = VDFHandler(appManifestPath, "Failed to parse " .. appManifestPath .. ".", false)
+
+ Logging.write("info", 'Found "' .. appManifest.AppState.name .. '" (' .. appManifest.AppState.appid .. ")")
+
+ local location = library.path .. "/steamapps/common/" .. appManifest.AppState.installdir
+
+ -- Does the game have launch options set ?
+ local configuration = userGameConfigurations[appManifest.AppState.appid]
+ local launchOptions
+ if configuration then
+ launchOptions = configuration.LaunchOptions
+ end
+
+ -- Do we know the game's platform
+ local osPlatform: ("windows" | "linux")? = appManifest.AppState.MountedConfig.platform_override_source
+ if not osPlatform then
+ local compat = compat_vdf.platform_overrides[appManifest.AppState.appid]
+ -- The app being absent from the compat.vdf list means it can only be a native Linux game
+ if not compat then
+ osPlatform = "linux"
+ else
+ -- Gah ! We still don't have the platform... time to check manually.
+ Logging.write(
+ "warn",
+ 'Platform not found for "' .. appManifest.AppState.name .. '" (' .. appManifest.AppState.appid .. "). Using manual check..."
+ )
+ if not Filesystem.directoryContainsLinuxData(location) then
+ osPlatform = "windows"
+ else
+ osPlatform = "linux"
+ end
+ end
+ end
+
+ local steamApp = SteamApp.new(appManifest.AppState.appid, appManifest.AppState.name, library.path, location, osPlatform, launchOptions)
+ steamApps[steamApp.appID] = steamApp
+ steamAppCount += 1
+ end)
+ end
+ end)
+
+ Logging.write("info", "Found " .. steamAppCount .. " Steam apps.")
+ Logging.write("speed", timeStart)
+ Logging.separator()
+
+ return steamApps
+end
+
+return Configuration
diff --git a/src/Steam/Utilities.luau b/src/Steam/Utilities.luau
new file mode 100644
index 0000000..1b40e30
--- /dev/null
+++ b/src/Steam/Utilities.luau
@@ -0,0 +1,31 @@
+local process = require("@lune/process")
+
+local Utilities = {}
+
+function Utilities.isSteamLaunch(): number?
+ local appIDArgumentIndex
+ for index, argument in ipairs(process.args) do
+ -- Locate the SteamLaunch argument, our AppID will be the next argument.
+ if argument == "SteamLaunch" then
+ appIDArgumentIndex = index + 1
+ break
+ end
+ end
+
+ if appIDArgumentIndex then
+ return tonumber(process.args[appIDArgumentIndex]:match("AppId=(%d+)"))
+ else
+ return nil
+ end
+end
+
+function Utilities.convertToSteamID3(steamID64: number): number
+ local offset_id = steamID64 - 76561197960265728
+ local account_type = offset_id % 2
+ local account_id = math.floor((offset_id - account_type) / 2) + account_type
+
+ -- The -1 is meant to be a fix, but could also cause bugs by itself. Keep an eye out.
+ return (account_id * 2) - account_type - 1
+end
+
+return Utilities
diff --git a/src/Steam/VDFParser.luau b/src/Steam/VDFParser.luau
new file mode 100644
index 0000000..16a9734
--- /dev/null
+++ b/src/Steam/VDFParser.luau
@@ -0,0 +1,148 @@
+local fs = require("@lune/fs")
+
+type VDFTable = { [string]: VDFValue }
+type VDFValue = string | VDFTable
+
+local function parseVDFData(input: string): VDFTable
+ -- Trim function to remove whitespace
+ local function trim(s: string): string?
+ return s:match("^%s*(.-)%s*$")
+ end
+
+ -- Handle escape sequences
+ local function parseString(string: string, pos: number): (string, number)
+ local result = ""
+ local len = #string
+ while pos <= len do
+ local char = string:sub(pos, pos)
+ if char == "\\" then
+ pos = pos + 1
+ if pos <= len then
+ local nextChar = string:sub(pos, pos)
+ if nextChar == '"' then
+ result = result .. '"'
+ elseif nextChar == "\\" then
+ result = result .. "\\"
+ elseif nextChar == "n" then
+ result = result .. "\n"
+ elseif nextChar == "t" then
+ result = result .. "\t"
+ else
+ result = result .. "\\" .. nextChar
+ end
+ end
+ elseif char == '"' then
+ break
+ else
+ result = result .. char
+ end
+ pos = pos + 1
+ end
+ return result, pos
+ end
+
+ -- Parsing logic
+ local pos = 1
+ local len = #input
+ local stack: { VDFValue } = {}
+ local currentTable: VDFTable = {}
+ local currentKey: string? = nil
+
+ while pos <= len do
+ local char = input:sub(pos, pos)
+
+ if char == '"' then
+ -- Extract the key or value with escape sequence handling
+ pos = pos + 1
+ local parsedString
+ parsedString, pos = parseString(input, pos)
+ parsedString = trim(parsedString) or parsedString
+ pos = pos + 1 -- Skip the closing quote
+
+ if currentKey == nil then
+ -- It's a key
+ currentKey = parsedString
+ else
+ -- It's a value
+ currentTable[currentKey] = parsedString
+ currentKey = nil
+ end
+ elseif char == "{" then
+ -- Start a new table
+ local newTable: VDFTable = {}
+ if currentKey ~= nil then
+ currentTable[currentKey] = newTable
+ currentKey = nil
+ else
+ error("Malformed VDF: encountered '{' without a preceding key")
+ end
+ table.insert(stack, currentTable)
+ currentTable = newTable
+ elseif char == "}" then
+ -- End the current table
+ if #stack == 0 then
+ error("Malformed VDF: encountered '}' without a matching '{'")
+ end
+
+ local data = table.remove(stack)
+ if not data or typeof(data) == "string" then
+ error("Malformed VDF")
+ end
+ currentTable = data
+ elseif not (char == "\n" or char == "\t" or char == " ") then
+ error("Malformed VDF: unexpected character '" .. char .. "'")
+ end
+
+ pos = pos + 1
+ end
+
+ if #stack > 0 then
+ error("Malformed VDF: unclosed '{'")
+ end
+
+ return currentTable
+end
+
+local function sanitizeVDFTable(input: VDFTable)
+ local function convertValue(value: any): any
+ if type(value) == "string" then
+ -- Check for number values (integers or floats)
+ local numberValue = tonumber(value)
+ if numberValue ~= nil then
+ return numberValue
+ end
+ -- Check for empty strings
+ if value == "" then
+ return nil
+ end
+ -- If it's neither, return the original string
+ return value
+ elseif type(value) == "table" then
+ -- Recursively sanitize nested tables
+ return sanitizeVDFTable(value)
+ else
+ -- Return the value as-is for unsupported types (shouldn't happen in a VDF)
+ return value
+ end
+ end
+
+ local result = {}
+ for key, value in pairs(input) do
+ result[convertValue(key)] = convertValue(value)
+ end
+
+ return result
+end
+
+
+local VDFParser = {}
+
+function VDFParser.parseFile(path: string)
+ return VDFParser.parseString(fs.readFile(path))
+end
+
+function VDFParser.parseString(input: string)
+ return sanitizeVDFTable(parseVDFData(input))
+end
+
+return VDFParser
diff --git a/src/Utilities/Async.luau b/src/Utilities/Async.luau
new file mode 100644
index 0000000..2ea81ff
--- /dev/null
+++ b/src/Utilities/Async.luau
@@ -0,0 +1,22 @@
+local task = require("@lune/task")
+
+local Async = {}
+
+function Async.asyncForEach(tbl: { any }, func: (any, any) -> ())
+ local total = 0
+ local completed = 0
+
+ for key, item in pairs(tbl) do
+ total += 1
+ task.spawn(function()
+ func(key, item)
+ completed += 1
+ end)
+ end
+
+ while completed < total do
+ task.wait()
+ end
+end
+
+return Async
diff --git a/src/Utilities/Filesystem.luau b/src/Utilities/Filesystem.luau
new file mode 100644
index 0000000..f5d5e34
--- /dev/null
+++ b/src/Utilities/Filesystem.luau
@@ -0,0 +1,78 @@
+local fs = require("@lune/fs")
+local process = require("@lune/process")
+
+local Logging = require("./Logging")
+
+local Filesystem = {}
+
+function Filesystem.createOrUseDirectory(path: string): string
+ if not fs.isDir(path) then
+ Logging.write("debug", "Directory " .. path .. " not found. Creating it...")
+ fs.writeDir(path)
+ end
+ return path
+end
+
+function Filesystem.getSize(path: string): number
+ path = path:gsub("'", "'\\''")
+
+ local handle = process.spawn("du", { "-sb", path })
+
+ if not handle.ok then
+ error("Failed to get size of path " .. path .. ": " .. handle.stderr)
+ end
+
+ local result = tonumber(handle.stdout:match("^(%d+)")) -- Grab only the number
+ if not result then
+ error("Failed to parse size for output (" .. handle.stdout .. ")")
+ else
+ return result
+ end
+end
+
+function Filesystem.sizeToUnit(size: number): string
+ local units = { "B", "KB", "MB", "GB", "TB" }
+ local unit = units[1] -- Default to "B" (bytes)
+
+ for i = 2, #units do
+ if size >= 1024 then
+ size = size / 1024
+ unit = units[i]
+ else
+ break
+ end
+ end
+
+ return string.format("%.2f", size) .. " " .. unit
+end
+
+function Filesystem.getFilenamePatternInDirectory(directory: string, pattern: string): { string }
+ local result = {}
+
+ for _, file in fs.readDir(directory) do
+ if file:match(pattern) then
+ table.insert(result, file)
+ end
+ end
+
+ return result
+end
+
+function Filesystem.directoryContainsLinuxData(path: string): boolean
+ local files = fs.readDir(path)
+ for _, file in ipairs(files) do
+ if not fs.isDir(path) and (file:find(".sh") or not file:find(".")) then
+ file = path .. "/" .. file
+
+ Logging.write("data", "Checking file " .. file)
+ local contents = fs.readFile(file)
+
+ if contents:find("Linux") or contents:find("shell") then
+ return true
+ end
+ end
+ end
+ return false
+end
+
+return Filesystem
diff --git a/src/Utilities/Logging.luau b/src/Utilities/Logging.luau
new file mode 100644
index 0000000..4cf9aab
--- /dev/null
+++ b/src/Utilities/Logging.luau
@@ -0,0 +1,50 @@
+local stdio = require("@lune/stdio")
+
+type LogColor = "black" | "blue" | "cyan" | "green" | "purple" | "red" | "reset" | "white" | "yellow"
+type LogType = "info" | "warn" | "error" | "debug" | "data" | "speed" | "special" | "success"
+
+local function color(colorName: LogColor, text: string): string
+ return stdio.color(colorName) .. text .. stdio.color("reset")
+end
+
+local Logging = {}
+
+function Logging.write(type: LogType, message: string | number)
+ local time = os.date("%H:%M:%S")
+
+ local logFunctions = {
+ info = function(text)
+ print(time .. " - [" .. color("blue", "Info") .. "] : " .. text)
+ end,
+ warn = function(text)
+ print(time .. " - [" .. color("yellow", "Warn") .. "] : " .. color("yellow", text))
+ end,
+ error = function(text)
+ print(time .. " - " .. color("red", "[Error] : " .. text))
+ end,
+ debug = function(text)
+ print(time .. " - [" .. color("white", "Debug") .. "] : " .. color("white", text))
+ end,
+ data = function(text)
+ print(time .. " - [" .. color("purple", "Data") .. "] : " .. text)
+ end,
+ speed = function(startTime)
+ print(time .. " - [" .. color("white", "Speed") .. "] : " .. "Done in " .. os.clock() - startTime .. " seconds.")
+ end,
+ success = function(text)
+ print(time .. " - [" .. color("green", "Success") .. "] : " .. text)
+ end,
+ -- Might be used for an easter egg feature in the future...
+ special = function(text)
+ print(time .. " - [" .. color("red", "!!!!") .. "] : " .. text)
+ end,
+ }
+
+ logFunctions[type](message)
+end
+
+function Logging.separator()
+ print("---------------------------------------------------------------------------------------------")
+end
+
+return Logging
diff --git a/src/Utilities/System.luau b/src/Utilities/System.luau
new file mode 100644
index 0000000..47c6946
--- /dev/null
+++ b/src/Utilities/System.luau
@@ -0,0 +1,32 @@
+local process = require("@lune/process")
+
+local Logging = require("./Logging")
+
+local System = {}
+
+function System.sendNotification(title: string, message: string, urgency: ("normal" | "critical")?, transient: boolean?, time: number?)
+ local arguments = {
+ title,
+ message,
+ }
+ if transient then
+ table.insert(arguments, "-e")
+ end
+
+ if urgency then
+ table.insert(arguments, "-u")
+ table.insert(arguments, urgency)
+ end
+ if time then
+ table.insert(arguments, "-t")
+ table.insert(arguments, tostring(time))
+ end
+
+ local execution = process.spawn("notify-send", arguments)
+
+ if not execution.ok then
+ Logging.write("error", "Unable to send notification : " .. execution.stderr)
+ end
+end
+
+return System
diff --git a/src/init.luau b/src/init.luau
new file mode 100644
index 0000000..fc0bd39
--- /dev/null
+++ b/src/init.luau
@@ -0,0 +1,71 @@
+local fs = require("@lune/fs")
+local process = require("@lune/process")
+
+local Filesystem = require("@Utilities/Filesystem")
+local Launcher = require("@Launcher/Functions")
+local Logging = require("@Utilities/Logging")
+local Metadata = require("./Metadata")
+local Steam = {
+ Configuration = require("@Steam/Configuration"),
+ Utilities = require("@Steam/Utilities"),
+}
+local System = require("@Utilities/System")
+
+local Application = {}
+
+function Application.start()
+ -- Prepare folders if necessary
+ for _, folder in ipairs({
+ Metadata.folders.config,
+ Metadata.folders.gamesConfig,
+ Metadata.folders.cache,
+ }) do
+ Filesystem.createOrUseDirectory(folder)
+ end
+
+ -- Development version detection
+ if Metadata.version:find("dev") then
+ Logging.write("warn", "This is a development version of " .. Metadata.name .. " !")
+ -- Put the arguments inside the cache folder for testing purposes
+ fs.writeFile(Metadata.folders.cache .. "/arguments.txt", table.concat(process.args, "\n"))
+ end
+
+ -- Check if we're launched through Steam, start the game with its settings if so
+ local launchedAppID = Steam.Utilities.isSteamLaunch()
+ if launchedAppID then
+ Logging.write("info", "Launched through Steam (AppID " .. launchedAppID .. ").")
+ Logging.separator()
+
+ -- Load the Steam config
+ local ok, steamConfig = pcall(Steam.Configuration.getSteamConfiguration)
+ if not ok then
+ Logging.write("error", "Failed to load Steam games.")
+ System.sendNotification(Metadata.name, "Failed to load Steam games.", "critical", true)
+ process.exit(1)
+ end
+
+ -- Load the game's data
+ local gameData = steamConfig[launchedAppID]
+ if not gameData then
+ Logging.write("error", "No data found for game with AppID " .. launchedAppID .. ".")
+ System.sendNotification(Metadata.name, "No data found for game with AppID " .. launchedAppID .. ".", "critical", true)
+ process.exit(1)
+ end
+
+ -- Tell the user we found the game
+ Logging.write("success", 'Detected "' .. gameData.name .. '" (' .. gameData.appID .. ") !")
+ System.sendNotification(Metadata.name, "Detected " .. gameData.name .. ".", "normal", true)
+
+ -- Go into the Game Launcher module.
+ local command: { string } = Launcher.Functions.prepareLaunchCommand(gameData)
+ print(command)
+ else
+ Logging.write("info", "Launched without Steam.")
+
+ -- :( - Lune FFI is not implemented yet, so no interface.
+ System.sendNotification(Metadata.name, "No graphical interface implemented yet.", "critical", true)
+ error("No graphical interface implemented yet.")
+ end
+end
+
+return Application
diff --git a/sst b/sst
deleted file mode 100755
index 53f5969..0000000
--- a/sst
+++ /dev/null
@@ -1,27 +0,0 @@
-#!/bin/sh
-
-# Check if installed locally (main.lua is in the same directory as this script) or in the system (main.lua is in /usr/share/simplesteammod)
-if [ -f "$(dirname $0)/main.lua" ]; then
- # Installed locally
- SCRIPT_PATH="$(dirname $0)"
-elif [ -f "/usr/share/SimpleSteamTinker/main.lua" ]; then
- # Installed in the system
- SCRIPT_PATH="/usr/share/SimpleSteamTinker"
-else
- echo "main.lua not found. Please install SimpleSteamTinker and try again."
- exit 1
-fi
-
-# Set LUA_PATH to avoid missing require issues, using the detected directory
-export LUA_PATH="$SCRIPT_PATH/?.lua;$SCRIPT_PATH/?/init.lua;;"
-
-# Provide script directory
-export SST_SCRIPT_PATH="$SCRIPT_PATH/"
-
-# Check if Lua is installed and run main.lua, accounting for the script path
-if [ -x "$(command -v lua)" ]; then
- lua $SCRIPT_PATH/main.lua "$@"
-else
- echo "No Lua installation found. Please install Lua and try again."
- exit 1
-fi
diff --git a/stylua.toml b/stylua.toml
new file mode 100644
index 0000000..9dcacf6
--- /dev/null
+++ b/stylua.toml
@@ -0,0 +1,9 @@
+column_width = 160
+line_endings = "Unix"
+indent_type = "Tabs"
+indent_width = 4
+quote_style = "AutoPreferDouble"
+call_parentheses = "Always"
+
+[sort_requires]
+enabled = true
diff --git a/ui/definitions/cleanerPage.blp b/ui/definitions/cleanerPage.blp
deleted file mode 100644
index 79fa5f4..0000000
--- a/ui/definitions/cleanerPage.blp
+++ /dev/null
@@ -1,10 +0,0 @@
-using Gtk 4.0;
-
-StackPage cleaner { //[MODIFIED IN LUA]: This page is only shown if the game is using Proton.
- title: _("Cleaner");
- child:
- Label {
- label: "In the future, this page will allow you to do some cleaning up.";
- wrap: true;
- };
-}
diff --git a/ui/definitions/commandPage.blp b/ui/definitions/commandPage.blp
deleted file mode 100644
index e9b1707..0000000
--- a/ui/definitions/commandPage.blp
+++ /dev/null
@@ -1,10 +0,0 @@
-using Gtk 4.0;
-
-StackPage commandPage {
- title: _("Launch options");
- child:
- Label {
- label: "In the future, this will be a page for configuring launch options.";
- wrap: true;
- };
-}
diff --git a/ui/definitions/gamescopePage.blp b/ui/definitions/gamescopePage.blp
deleted file mode 100644
index b5a11cb..0000000
--- a/ui/definitions/gamescopePage.blp
+++ /dev/null
@@ -1,215 +0,0 @@
-using Gtk 4.0;
-using Adw 1;
-
-StackPage gamescopePage { //[MODIFIED IN LUA]: This page is only shown if the game is using Proton.
- title: "Gamescope";
- child:
- Gtk.ScrolledWindow {
- Adw.Clamp {
- Box {
- margin-bottom: 16;
- margin-end: 12;
- margin-start: 12;
- margin-top: 16;
- orientation: vertical;
- spacing: 24;
- vexpand: true;
- vexpand-set: true;
-
- Adw.PreferencesGroup {
- title: "Gamescope";
- description: _("Gamescope is a tool from Valve that allows for games to run in an isolated XWayland instance.");
- Adw.ActionRow {
- title : _("Enable Gamescope");
- subtitle: _("May cause compatibility issues with some utilities and Wayland-native games.");
- activatable-widget: gamescope_Switch;
-
- sensitive: bind gamescope_Switch.sensitive bidirectional;
- has-tooltip: bind gamescope_Switch.has-tooltip bidirectional;
- tooltip-text: bind gamescope_Switch.tooltip-text bidirectional;
-
- Gtk.Switch gamescope_Switch {
- active: false;
- sensitive: false;
- valign: center;
- }
- }
- }
-
- Adw.PreferencesGroup {
- title: _("Resolution");
- description: _("Resolution settings for both the game and the Gamescope window.");
-
- sensitive: bind gamescope_Switch.active;
-
- Adw.ExpanderRow {
- title: _("Game resolution");
- subtitle: _("The resolution the game will run at.");
-
- sensitive: bind gamescope_Resolution_Switch.active;
-
- Adw.SpinRow gamescope_Resolution_Internal_Width_SpinRow {
- title: _("Width");
- subtitle: _("The width of the game's internal resolution.");
- numeric: true;
- adjustment :
- Gtk.Adjustment {
- lower: 0;
- upper: 7680;
- step-increment: 10;
- };
- }
- Adw.SpinRow gamescope_Resolution_Internal_Height_SpinRow {
- title: _("Height");
- subtitle: _("The height of the game's internal resolution.");
- numeric: true;
- adjustment :
- Gtk.Adjustment {
- lower: 0;
- upper: 7680;
- step-increment: 10;
- };
- }
- }
-
- Adw.ExpanderRow {
- title: _("Window resolution");
- subtitle: _("The resolution of the Gamescope window.");
-
- sensitive: bind gamescope_Resolution_Switch.active;
-
- Adw.SpinRow gamescope_Resolution_External_Width_SpinRow {
- title: _("Width");
- subtitle: _("The width of the Gamescope window.");
- numeric: true;
- adjustment :
- Gtk.Adjustment {
- lower: 0;
- upper: 7680;
- step-increment: 10;
- };
- }
- Adw.SpinRow gamescope_Resolution_External_Height_SpinRow {
- title: _("Height");
- subtitle: _("The height of the Gamescope window.");
- numeric: true;
- adjustment :
- Gtk.Adjustment {
- lower: 0;
- upper: 7680;
- step-increment: 10;
- };
- }
- }
-
- [header-suffix]
- Switch gamescope_Resolution_Switch {
- active: false;
- sensitive: bind gamescope_Switch.active;
- valign: center;
- }
- }
-
-
- Adw.PreferencesGroup {
- title: _("Framerate");
- description: _("The maximum framerate the game will run at.");
-
- sensitive: bind gamescope_Switch.active;
-
- Adw.SpinRow gamescope_Framerate_Normal_SpinRow {
- title: _("Normal");
- subtitle: _("The framerate when the game is focused.");
- sensitive: bind gamescope_Framerate_Switch.active;
- numeric: true;
- adjustment :
- Gtk.Adjustment {
- lower: 0;
- upper: 244;
- step-increment: 1;
- };
- }
- Adw.SpinRow gamescope_Framerate_Unfocused_SpinRow {
- title: _("Unfocused");
- subtitle: _("The framerate when the game is not focused.");
- sensitive: bind gamescope_Framerate_Switch.active;
- numeric: true;
- adjustment :
- Gtk.Adjustment {
- lower: 0;
- upper: 244;
- step-increment: 1;
- };
- }
-
- [header-suffix]
- Switch gamescope_Framerate_Switch {
- active: false;
- sensitive: bind gamescope_Switch.active;
- valign: center;
- }
- }
-
- Adw.PreferencesGroup {
- title: _("Filtering");
- description: _("Makes the game look smoother at lower resolutions.");
-
- sensitive: bind gamescope_Switch.active;
-
- Adw.ComboRow gamescope_Filtering_Filter_ComboRow {
- title: _("Filter");
- subtitle: _("The filter to use. (NIS = Nvidia Image Scaling, FSR = FidelityFX Super Resolution)");
- sensitive: bind gamescope_Filtering_Switch.active;
- model: Gtk.StringList {
- strings ["Linear", "Nearest", "FSR", "NIS", "Pixel"]
- };
- }
- Adw.SpinRow gamescope_Filtering_Sharpness_SpinRow {
- title: _("Sharpness");
- subtitle: _("Sets the sharpness of the filter. (0 = maximum sharpness, 20 = minimum)");
- sensitive: bind gamescope_Filtering_Switch.active;
- numeric: true;
- adjustment :
- Gtk.Adjustment {
- lower: 0;
- upper: 20;
- step-increment: 1;
- };
- }
-
- [header-suffix]
- Switch gamescope_Filtering_Switch {
- active: false;
- sensitive: bind gamescope_Switch.active;
- valign: center;
- }
- }
-
- Adw.PreferencesGroup {
- title: _("Extras");
- description: _("Additional settings for GameScope.");
-
- sensitive: bind gamescope_Switch.active;
-
- Adw.ActionRow {
- title: _("Window type");
- subtitle: _("The type of window to use for the Gamescope window.");
-
- Box {
- styles ["linked"]
-
- ToggleButton gamescope_Borderless_Toggle {
- label: _("Borderless");
- valign: center;
- }
- ToggleButton gamescope_Fullscreen_Toggle {
- label: _("Fullscreen");
- valign: center;
- }
- }
- }
- }
- }
- }
- };
-}
diff --git a/ui/definitions/mainWindow.blp b/ui/definitions/mainWindow.blp
deleted file mode 100644
index b7b88ae..0000000
--- a/ui/definitions/mainWindow.blp
+++ /dev/null
@@ -1,147 +0,0 @@
-/*
- Why not use LGI directly instead ?
-
- Because LGI doesn't support well in GTK4 many things that can be easily defined here.
- Still, both are by FAR a much better solution than using XML directly.
- Plus, I feel like using a universal standard would be a good idea to make it easier for people to contribute.
-*/
-
-using Gtk 4.0;
-using Adw 1;
-
-Adw.ApplicationWindow mainWindow {
- default-height: 500;
- default-width: 700;
- height-request: 400;
- show-menubar: true;
- width-request: 360;
-
- // AdwBreakpoint used to dynamically make the sidebar appear or disappear depending on the window's width.
- Adw.Breakpoint {
- condition ("max-width: 600sp")
- setters {
- sidebarToggleButton.visible: true;
- sidebarToggleButton.active: false;
- gameSettings_SplitView.collapsed: true;
- }
- }
-
- [content]
- Adw.ToastOverlay toastSystem { //[INTERACTS WITH LUA]
- Adw.NavigationView mainView {
- /*
- MAIN PAGE
- This is the page the program starts on when it's launched.
- */
- Adw.NavigationPage mainPage {
- title: _("SimpleSteamTinker");
- Adw.ToolbarView mainPageTopbar {
- [top]
- Adw.HeaderBar {
- styles ["titlebar"]
-
- [end]
- Button aboutLauncher {
- icon-name: "help-about-symbolic";
- styles ["image-button"]
- }
- }
-
- // This is where the games are listed.
- ScrolledWindow {
- hscrollbar-policy: never;
-
- Adw.Clamp {
- margin-bottom: 16;
- margin-end: 12;
- margin-start: 12;
- margin-top: 16;
-
- Adw.PreferencesGroup gameList { //[MODIFIED IN LUA]
- description: _("Insert \"sst %command%\" as a game\'s launch options to enable it here.\nCommand may change depending on installation method.");
- title: _("Detected Steam games");
- }
- }
- }
- }
- }
-
- // This is the page that shows up when you click on a game.
- Adw.NavigationPage gameSettings { //[INTERACTS THROUGH LUA]
- title: _("Game settings");
- Adw.ToolbarView gameSettingsTopbar {
- [top]
- Adw.HeaderBar {
- styles ["titlebar"]
-
- // Button that allows you to toggle the sidebar in the game settings page.
- ToggleButton sidebarToggleButton { //[MODIFIED IN LUA][INTERACTS THROUGH LUA]
- active: true;
- visible: false;
- has-frame: false;
- icon-name: "view-sidebar-start-symbolic";
- }
- }
-
- Adw.OverlaySplitView gameSettings_SplitView {
- /*
- SIDEBAR
- */
- min-sidebar-width: 200;
- show-sidebar: bind sidebarToggleButton.active bidirectional;
- sidebar:
- Adw.NavigationPage gameSettingsSidebar {
- title: _("Sidebar");
- child:
- Adw.ToolbarView {
- Box {
- orientation: vertical;
- vexpand: true;
- vexpand-set: true;
-
- // This is the header of the sidebar of the game settings page.
- Picture Sidebar_Banner {
- halign: fill;
- valign: fill;
- }
-
- // This is the list of pages in the sidebar of the game settings page.
- StackSidebar {
- vexpand: true;
- vexpand-set: true;
- stack: gameSettingsStack;
- }
- }
- };
- };
-
- /*
- CONTENT
- */
- content:
- Adw.NavigationPage {
- title: _("Game settings");
- Stack gameSettingsStack {
- transition-type: crossfade;
-
- //[overviewPage.blp]
-
- //[settingsPage.blp]
-
- //[utilitiesPage.blp]
-
- //[commandPage.blp]
-
- //[protonPage.blp]
-
- //[gamescopePage.blp]
-
- //[cleanerPage.blp]
- }
- };
- }
- }
- }
- }
- }
-}
\ No newline at end of file
diff --git a/ui/definitions/overviewPage.blp b/ui/definitions/overviewPage.blp
deleted file mode 100644
index be9264d..0000000
--- a/ui/definitions/overviewPage.blp
+++ /dev/null
@@ -1,279 +0,0 @@
-using Gtk 4.0;
-using Adw 1;
-
-StackPage overviewPage { //[INTERACTS IN LUA]
- name: "overviewPage";
- title: _("Overview");
-
- child:
- ScrolledWindow {
- hscrollbar-policy: never;
- styles ["background"]
-
- Box {
- orientation: vertical;
-
- Picture Overview_Picture { //[MODIFIED IN LUA]
- halign: fill;
- valign: fill;
- height-request: 256;
- }
-
- Adw.Clamp {
- Box {
- margin-bottom: 16;
- margin-end: 12;
- margin-start: 12;
- margin-top: 16;
- orientation: vertical;
- spacing: 24;
- vexpand: true;
- vexpand-set: true;
-
- Box {
- orientation: vertical;
- spacing: 8;
- halign: center;
-
- // Game title
- Label gameTitle { //[MODIFIED IN LUA]
- halign: center;
- valign: end;
- wrap: true;
-
- styles [
- "title-1"
- ]
- }
-
- // ProtonDB Rating
- Box protonDBRating_Box {
- orientation: horizontal;
- spacing: 8;
- halign: center;
- Label {
- label: _("ProtonDB Rating :");
- halign: center;
- valign: end;
-
- styles [
- "dim-label"
- ]
- }
- Label protonDBRating_Label { //[MODIFIED IN LUA]
- label: _("Unavailable");
- halign: center;
- valign: end;
-
- styles [
- "dim-label"
- ]
- }
- }
- }
-
- // Launch button
- Button gameLaunchButton { //[INTERACTS THROUGH LUA]
- halign: center;
- valign: end;
-
- Adw.ButtonContent {
- icon-name: "media-playback-start-symbolic";
- label: _("Launch");
- }
-
- styles [
- "suggested-action",
- "image-button",
- "pill"
- ]
- }
-
- // SimpleSteamTinker Status
- Adw.PreferencesGroup {
- Adw.ActionRow {
- title: _("SimpleSteamTinker Status");
- subtitle: _("Based on the game's launch options.");
- subtitle-lines: 1;
-
- [suffix]
- Label gameStatus_Label { //[MODIFIED IN LUA]
- label: _("Placeholder");
- halign: end;
- wrap: true;
- styles [
- "dim-label"
- ]
- }
- }
- }
-
- // Game details
- Adw.PreferencesGroup {
- title: _("Details");
- description: _("Information about the game.");
-
- Adw.ActionRow {
- title: _("Steam AppID");
- subtitle: _("The unique ID of the game that identifies it in Steam.");
- activatable-widget: gameID_copyButton;
- [suffix]
- Box {
- spacing: 8;
- Label gameID_Label { //[MODIFIED IN LUA]
- halign: end;
- wrap: true;
- selectable: true;
- styles [
- "dim-label"
- ]
- }
- Button gameID_copyButton { //[MODIFIED IN LUA][INTERACTS WITH LUA]
- halign: end;
- icon-name: "edit-copy-symbolic";
- styles [
- "image-button",
- "flat",
- "circular"
- ]
- }
- }
- }
- Adw.ActionRow {
- title: _("Platform");
- subtitle: _("The intended platform for the installed game version.");
-
- [suffix]
- Label gamePlatform_Label { //[MODIFIED IN LUA]
- halign: end;
- wrap: true;
- styles [
- "dim-label"
- ]
- }
- }
- Adw.ActionRow {
- title: _("Size");
- subtitle: _("The amount of space the game takes up on the system.");
-
- [suffix]
- Label gameSize_Label { //[MODIFIED IN LUA]
- halign: end;
- lines: 1;
- styles [
- "dim-label"
- ]
- }
- }
- Adw.ActionRow gameLocation_ActionRow { //[MODIFIED IN LUA]
- title: _("Install location");
- tooltip-text: _("The location of the game's files on the system.");
- has-tooltip: true;
- activatable-widget: gameLocationButton;
- subtitle-lines: 2;
- [suffix]
- Button gameLocationButton { //[MODIFIED IN LUA]
- Adw.ButtonContent {
- icon-name: "folder-open-symbolic";
- }
- styles [
- "image-button",
- "flat",
- "circular"
- ]
- }
- }
- Adw.ActionRow gameCompatdata_ActionRow { //[MODIFIED IN LUA]
- title: _("Virtual filesystem location");
- tooltip-text: _("The location of the game's virtual filesystem assigned by Steam. (Only applies to Windows games)");
- has-tooltip: true;
- activatable-widget: gameCompatdataButton;
- subtitle-lines: 2;
- [suffix]
- Button gameCompatdataButton { //[MODIFIED IN LUA]
- Adw.ButtonContent {
- icon-name: "folder-open-symbolic";
- }
- styles [
- "image-button",
- "flat",
- "circular"
- ]
- }
- }
- }
-
- // Links
- Adw.PreferencesGroup {
- title: _("Online help");
- description: _("Links to useful pages related to the game.");
-
- Adw.ActionRow {
- title: _("ProtonDB");
- activatable-widget: protonDBPage_Button;
- [suffix]
- Button protonDBPage_Button { //[MODIFIED IN LUA]
- Adw.ButtonContent {
- icon-name: "help-browser-symbolic";
- }
- styles [
- "image-button",
- "flat",
- "circular"
- ]
- }
- }
-
- Adw.ActionRow {
- title: _("PCGamingWiki");
- activatable-widget: PCGamingWikiPage_Button;
- [suffix]
- Button PCGamingWikiPage_Button { //[MODIFIED IN LUA]
- Adw.ButtonContent {
- icon-name: "help-browser-symbolic";
- }
- styles [
- "image-button",
- "flat",
- "circular"
- ]
- }
- }
-
- Adw.ActionRow {
- title: _("SteamDB");
- activatable-widget: steamDBPage_Button;
- [suffix]
- Button steamDBPage_Button { //[MODIFIED IN LUA]
- Adw.ButtonContent {
- icon-name: "help-browser-symbolic";
- }
- styles [
- "image-button",
- "flat",
- "circular"
- ]
- }
- }
-
- Adw.ActionRow {
- title: _("Steambase");
- activatable-widget: SteambasePage_Button;
- [suffix]
- Button SteambasePage_Button { //[MODIFIED IN LUA]
- Adw.ButtonContent {
- icon-name: "help-browser-symbolic";
- }
- styles [
- "image-button",
- "flat",
- "circular"
- ]
- }
- }
- }
- }
- }
- }
- };
-}
diff --git a/ui/definitions/protonPage.blp b/ui/definitions/protonPage.blp
deleted file mode 100644
index 551d6c9..0000000
--- a/ui/definitions/protonPage.blp
+++ /dev/null
@@ -1,225 +0,0 @@
-using Gtk 4.0;
-using Adw 1;
-
-StackPage protonPage { //[MODIFIED IN LUA]: This page is only shown if the game is using Proton.
- title: "Proton / Wine";
-
- child:
- Gtk.ScrolledWindow {
- Adw.Clamp {
- Box {
- margin-bottom: 16;
- margin-end: 12;
- margin-start: 12;
- margin-top: 16;
- orientation: vertical;
- spacing: 24;
- vexpand: true;
- vexpand-set: true;
-
- Adw.PreferencesGroup {
- title: "Direct3D";
- description: _("Configure Direct3D settings.");
-
- Adw.ExpanderRow {
- title: _("Versions");
- subtitle: _("Control which versions of Direct3D can be used by this game. If you are unsure, leave all versions enabled.");
-
- Adw.ActionRow {
- title: _("Direct3D 9");
- subtitle: _("Only applies to GE Proton.");
- activatable-widget: Direct3D9_Switch;
- sensitive: bind Direct3D9_Switch.sensitive bidirectional;
- has-tooltip: bind Direct3D9_Switch.has-tooltip bidirectional;
- tooltip-text: bind Direct3D9_Switch.tooltip-text bidirectional;
-
- Gtk.Switch Direct3D9_Switch {
- active: true;
- valign: center;
- }
- }
- Adw.ActionRow {
- title: _("Direct3D 10");
- activatable-widget: Direct3D10_Switch;
- sensitive: bind Direct3D10_Switch.sensitive bidirectional;
- has-tooltip: bind Direct3D10_Switch.has-tooltip bidirectional;
- tooltip-text: bind Direct3D10_Switch.tooltip-text bidirectional;
-
- Gtk.Switch Direct3D10_Switch {
- active: true;
- valign: center;
- }
- }
- Adw.ActionRow {
- title: _("Direct3D 11");
- activatable-widget: Direct3D11_Switch;
- sensitive: bind Direct3D11_Switch.sensitive bidirectional;
- has-tooltip: bind Direct3D11_Switch.has-tooltip bidirectional;
- tooltip-text: bind Direct3D11_Switch.tooltip-text bidirectional;
-
- Gtk.Switch Direct3D11_Switch {
- active: true;
- valign: center;
- }
- }
- Adw.ActionRow {
- title: _("Direct3D 12");
- subtitle: _("Only applies to GE Proton.");
- activatable-widget: Direct3D12_Switch;
- sensitive: bind Direct3D12_Switch.sensitive bidirectional;
- has-tooltip: bind Direct3D12_Switch.has-tooltip bidirectional;
- tooltip-text: bind Direct3D12_Switch.tooltip-text bidirectional;
-
- Gtk.Switch Direct3D12_Switch {
- active: true;
- valign: center;
- }
- }
- }
-
- Adw.ActionRow {
- title: _("Use WineD3D instead of DXVK");
- subtitle: _("Use OpenGL-based WineD3D instead of Vulkan-based DXVK for D3D11 and D3D10.");
- activatable-widget: WineD3D_Switch;
- sensitive: bind WineD3D_Switch.sensitive bidirectional;
- has-tooltip: bind WineD3D_Switch.has-tooltip bidirectional;
- tooltip-text: bind WineD3D_Switch.tooltip-text bidirectional;
-
- Gtk.Switch WineD3D_Switch {
- active: false;
- valign: center;
- }
- }
- }
-
- Adw.PreferencesGroup {
- title: "AMD FidelityFX Super Resolution 1.0";
- description: _("Note : this feature requires GE Proton, and is not restricted to AMD GPUs.");
-
- [header-suffix]
- Switch Wine_FSR_Switch {
- active: false;
- sensitive: true;
- valign: center;
- }
-
- Adw.SpinRow Wine_FSR_Sharpness_SpinRow {
- title: _("Sharpness");
- subtitle: _("The strength of the filter. (0 = strongest, 5 = weakest)");
- sensitive: bind Wine_FSR_Switch.active;
- numeric: true;
- adjustment :
- Gtk.Adjustment {
- lower: 0;
- upper: 5;
- step-increment: 1;
- };
- }
-
- Adw.ComboRow Wine_FSR_Upscaling_Resolution_Mode_ComboRow {
- title: _("Upscaling resolution mode");
- subtitle: _("Depending on what preset value you choose and your system, Proton will set an upscaling resolution automatically.");
- sensitive: bind Wine_FSR_Switch.active;
- model: Gtk.StringList {
- strings ["None", "Performance", "Balanced", "Quality", "Ultra"]
- };
- }
-
- Adw.ExpanderRow {
- title: _("Custom resolution");
- subtitle: _("Note : overrides the upscaling resolution mode. This shouldn't be needed in most cases.");
- sensitive: bind Wine_FSR_Switch.active;
-
- Adw.ActionRow {
- title: _("Enable the usage of a custom resolution");
- Switch Wine_FSR_Resolution_Switch {
- active: false;
- sensitive: bind Wine_FSR_Switch.active;
- valign: center;
- }
- }
- Adw.SpinRow Wine_FSR_Resolution_External_Width_SpinRow {
- title: _("Width");
- sensitive: bind Wine_FSR_Resolution_Switch.active;
- numeric: true;
- adjustment :
- Gtk.Adjustment {
- lower: 0;
- upper: 7680;
- step-increment: 10;
- };
- }
- Adw.SpinRow Wine_FSR_Resolution_External_Height_SpinRow {
- title: _("Height");
- sensitive: bind Wine_FSR_Resolution_Switch.active;
- numeric: true;
- adjustment :
- Gtk.Adjustment {
- lower: 0;
- upper: 7680;
- step-increment: 10;
- };
- }
- }
- }
-
- Adw.PreferencesGroup {
- title: _("NVIDIA features");
-
- Adw.ActionRow {
- title : _("Disguise NVIDIA GPUs as AMD");
- subtitle: _("Force NVIDIA GPUs to always be reported as AMD GPUs. Some games require this if they depend on Windows-only NVIDIA driver functionality.");
- subtitle-lines: 3;
-
- activatable-widget: Hide_NVIDIA_GPU_Switch;
- sensitive: bind Hide_NVIDIA_GPU_Switch.sensitive bidirectional;
- has-tooltip: bind Hide_NVIDIA_GPU_Switch.has-tooltip bidirectional;
- tooltip-text: bind Hide_NVIDIA_GPU_Switch.tooltip-text bidirectional;
-
- Gtk.Switch Hide_NVIDIA_GPU_Switch {
- active: false;
- valign: center;
- }
- }
- Adw.ActionRow {
- title : _("Enable NVAPI");
- subtitle: _("Enable NVIDIA's NVAPI GPU support library.");
- subtitle-lines: 3;
-
- activatable-widget: Enable_NVAPI_Switch;
- sensitive: bind Enable_NVAPI_Switch.sensitive bidirectional;
- has-tooltip: bind Enable_NVAPI_Switch.has-tooltip bidirectional;
- tooltip-text: bind Enable_NVAPI_Switch.tooltip-text bidirectional;
-
- Gtk.Switch Enable_NVAPI_Switch {
- active: false;
- valign: center;
- }
- }
- }
-
- Adw.PreferencesGroup {
- title: "Wine";
-
- Adw.ActionRow {
- title: _("Synchronization");
- Box {
- styles ["linked"]
-
- ToggleButton ESync_Toggle {
- label: "ESync";
- valign: center;
- active: true;
- }
- ToggleButton FSync_Toggle {
- label: "FSync";
- valign: center;
- active: true;
- }
- }
- }
- }
- }
- }
- };
-}
diff --git a/ui/definitions/settingsPage.blp b/ui/definitions/settingsPage.blp
deleted file mode 100644
index 6c5669b..0000000
--- a/ui/definitions/settingsPage.blp
+++ /dev/null
@@ -1,59 +0,0 @@
-using Gtk 4.0;
-using Adw 1;
-
-StackPage settingsPage { //[MODIFIED IN LUA]: This page is only shown if the game is using Proton.
- title: _("Settings");
- child:
- Gtk.ScrolledWindow {
- Adw.Clamp {
- Box {
- margin-bottom: 16;
- margin-end: 12;
- margin-start: 12;
- margin-top: 16;
- orientation: vertical;
- spacing: 24;
- vexpand: true;
- vexpand-set: true;
-
- Adw.PreferencesGroup {
- title: "Recommended";
- description: _("Settings recommended for the best experience.");
- Adw.ActionRow {
- title : _("Use dedicated graphics card");
- subtitle: _("Use your dedicated graphics card to boost performance by a lot, in exchange for higher power consumption.");
- subtitle-lines: 3;
-
- activatable-widget: dGPU_Switch;
- sensitive: bind dGPU_Switch.sensitive bidirectional;
- has-tooltip: bind dGPU_Switch.has-tooltip bidirectional;
- tooltip-text: bind dGPU_Switch.tooltip-text bidirectional;
-
- Gtk.Switch dGPU_Switch {
- active: false;
- sensitive: false;
- valign: center;
- }
- }
- Adw.ActionRow {
- title : _("Enable SDL's Wayland driver");
- subtitle: _("This option can give you smoother gameplay and performance in SDL-based games on Wayland. Useless on X11.");
- subtitle-lines: 3;
-
- activatable-widget: SDL_Wayland_Switch;
- sensitive: bind SDL_Wayland_Switch.sensitive bidirectional;
- visible: bind SDL_Wayland_Switch.visible bidirectional;
- has-tooltip: bind SDL_Wayland_Switch.has-tooltip bidirectional;
- tooltip-text: bind SDL_Wayland_Switch.tooltip-text bidirectional;
-
- Gtk.Switch SDL_Wayland_Switch {
- active: false;
- sensitive: true;
- valign: center;
- }
- }
- }
- }
- }
- };
-}
diff --git a/ui/definitions/utilitiesPage.blp b/ui/definitions/utilitiesPage.blp
deleted file mode 100644
index 5e87575..0000000
--- a/ui/definitions/utilitiesPage.blp
+++ /dev/null
@@ -1,93 +0,0 @@
-using Gtk 4.0;
-using Adw 1;
-
-StackPage utilitiesPage {
- title: _("In-game utilities");
-
- child:
- Adw.ClampScrollable {
- Box {
- margin-bottom: 16;
- margin-end: 12;
- margin-start: 12;
- margin-top: 16;
- orientation: vertical;
- spacing: 24;
-
- Adw.PreferencesGroup {
- title: "Recommended";
- description: _("Utilities recommended for the best experience.");
- Adw.ActionRow {
- subtitle: _("GameMode is a tool to optimize Linux system performance on demand.");
- title: "Feral GameMode";
- activatable-widget: gamemode_Switch;
- has-tooltip: bind gamemode_Switch.has-tooltip bidirectional;
- tooltip-text: bind gamemode_Switch.tooltip-text bidirectional;
- sensitive: bind gamemode_Switch.sensitive bidirectional;
-
- Switch gamemode_Switch { //[MODIFIED IN LUA][INTERACTS WITH LUA]
- active: false;
- sensitive: false;
- valign: center;
- }
- }
- }
-
- Adw.PreferencesGroup {
- title: _("Utilities");
- description: _("Utilities that can be used while the game is running, depending on your needs.");
-
- // Adw.SwitchRow seem to be buggy-ish. The value gets inverted, and clicking directly on the button
- // doesn't send the signal I need to run the functions.
-
- Adw.ActionRow {
- subtitle: _("A Vulkan and OpenGL overlay for monitoring FPS, temperatures, CPU/GPU load and more... May not work on some native OpenGL games.");
- subtitle-lines: 2;
- title: "MangoHud";
- activatable-widget: mangohud_Switch;
- has-tooltip: bind mangohud_Switch.has-tooltip bidirectional;
- tooltip-text: bind mangohud_Switch.tooltip-text bidirectional;
- sensitive: bind mangohud_Switch.sensitive bidirectional;
-
- Switch mangohud_Switch { //[MODIFIED IN LUA][INTERACTS WITH LUA]
- active: false;
- sensitive: false;
- valign: center;
- }
- }
-
- Adw.ActionRow {
- subtitle: _("Convert OpenGL games to Vulkan.");
- subtitle-lines: 2;
- title: "Zink";
- activatable-widget: zink_Switch;
- has-tooltip: bind zink_Switch.has-tooltip bidirectional;
- tooltip-text: bind zink_Switch.tooltip-text bidirectional;
- sensitive: bind zink_Switch.sensitive bidirectional;
-
- Switch zink_Switch { //[MODIFIED IN LUA][INTERACTS WITH LUA]
- active: false;
- sensitive: false;
- valign: center;
- }
- }
-
- Adw.ActionRow {
- subtitle: _("obs-gamecapture is a tool that captures a game window and plugs it into OBS Studio as a source with minimal overhead.");
- subtitle-lines: 2;
- title: _("OBS Game Capture");
- activatable-widget: obs_gamecapture_Switch;
- has-tooltip: bind obs_gamecapture_Switch.has-tooltip bidirectional;
- tooltip-text: bind obs_gamecapture_Switch.tooltip-text bidirectional;
- sensitive: bind obs_gamecapture_Switch.sensitive bidirectional;
-
- Switch obs_gamecapture_Switch { //[MODIFIED IN LUA][INTERACTS WITH LUA]
- active: false;
- sensitive: false;
- valign: center;
- }
- }
- }
- }
- };
-}
diff --git a/ui/main.ui b/ui/main.ui
deleted file mode 100644
index e9d7cb2..0000000
--- a/ui/main.ui
+++ /dev/null
@@ -1,1190 +0,0 @@
-
-
-
-
-
diff --git a/ui/update-ui.lua b/ui/update-ui.lua
deleted file mode 100644
index b61c29e..0000000
--- a/ui/update-ui.lua
+++ /dev/null
@@ -1,81 +0,0 @@
-#!/bin/lua
-
---[[
- This script should be used after modifying any of the ui_definitions files.
- It will compile the ui_definitions files into ui files that GTK can use.
- If you do not, the modifications you've made to the UI outside of Lua code will not be applied.
-
- This is basically a workaround for LGI not working well with Gtk templates or to assemble widgets together in general right now.
-]]
-
--- Define the list of files to compile
-local baseFile = "mainWindow.blp"
-local files = {
- [baseFile] = false,
- ["overviewPage.blp"] = false,
- ["utilitiesPage.blp"] = false,
- ["settingsPage.blp"] = false,
- ["commandPage.blp"] = false,
- ["gamescopePage.blp"] = false,
- ["protonPage.blp"] = false,
- ["cleanerPage.blp"] = false,
-}
-
--- Add their contents to the list
-for file, _ in pairs(files) do
- local f = io.open("ui/definitions/"..file, "r")
- if not f then
- print("Error: Could not open file "..file)
- os.exit(1)
- end
- files[file] = f:read("*all")
- f:close()
-end
-
-local function removeUsageLines(content)
- local lines = {}
- for line in content:gmatch("[^\r\n]+") do
- if not line:find("using ") or not line:find(";") then
- table.insert(lines, line)
- end
- end
- return table.concat(lines, "\n")
-end
-
--- Inside the contents of each file, replace comments written like "//[FILENAME]" with the respective content
-for file, content in pairs(files) do
- local newContent = {}
- for line in content:gmatch("[^\r\n]+") do
-
- local fileName = line:match("//%[(.+)%]")
- if fileName then
- local fileContent = files[fileName]
- if fileContent then
- print("Inserting "..fileName.." into "..file..".")
- table.insert(newContent, removeUsageLines(fileContent))
- else
- table.insert(newContent, line)
- end
- else
- table.insert(newContent, line)
- end
-
- end
- files[file] = table.concat(newContent, "\n")
-end
-
--- Write it into a location
-local location = "/tmp/main.blp"
-local f = io.open(location, "w")
-if not f then
- print("Error: Could not open file "..location)
- os.exit(1)
-end
-f:write(files[baseFile])
-f:close()
-
--- Make the blueprint with blueprint-compiler
-local command = "blueprint-compiler compile "..location.." > ui/main.ui"
-print(command)
-os.execute(command)
-os.remove(location)
\ No newline at end of file