├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── pull_request_template.md └── workflows │ └── main.yml ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── SECURITY.md ├── com.vysp3r.ProtonPlus.local.yml ├── com.vysp3r.ProtonPlus.yml ├── data ├── com.vysp3r.ProtonPlus.desktop.in ├── com.vysp3r.ProtonPlus.gschema.xml ├── com.vysp3r.ProtonPlus.metainfo.xml.in ├── css │ ├── css.gresource.xml │ ├── meson.build │ └── style.css ├── icons │ ├── circle-check.svg │ ├── circle-chevron-up.svg │ ├── dots.svg │ ├── download.svg │ ├── icons.gresource.xml │ ├── info-circle.svg │ ├── journal-text.svg │ ├── meson.build │ ├── trash.svg │ ├── world-www.svg │ └── x.svg ├── images │ ├── images.gresource.xml │ ├── launchers │ │ ├── bottles.png │ │ ├── hgl.png │ │ ├── lutris.png │ │ └── steam.png │ └── meson.build ├── logo │ ├── ProtonPlus.xcf │ ├── com.vysp3r.ProtonPlus.svg │ └── icons │ │ └── hicolor │ │ ├── 128x128 │ │ └── apps │ │ │ └── com.vysp3r.ProtonPlus.png │ │ ├── 16x16 │ │ └── apps │ │ │ └── com.vysp3r.ProtonPlus.png │ │ ├── 256x256 │ │ └── apps │ │ │ └── com.vysp3r.ProtonPlus.png │ │ ├── 32x32 │ │ └── apps │ │ │ └── com.vysp3r.ProtonPlus.png │ │ ├── 48x48 │ │ └── apps │ │ │ └── com.vysp3r.ProtonPlus.png │ │ ├── 512x512 │ │ └── apps │ │ │ └── com.vysp3r.ProtonPlus.png │ │ └── 64x64 │ │ └── apps │ │ └── com.vysp3r.ProtonPlus.png ├── meson.build ├── previews │ └── Preview-1.png └── ui │ ├── gtk │ └── help-overlay.ui │ ├── meson.build │ └── ui.gresource.xml ├── meson.build ├── po ├── LINGUAS ├── POTFILES ├── be.po ├── com.vysp3r.ProtonPlus.pot ├── cs.po ├── de.po ├── es.po ├── fi.po ├── fr.po ├── id.po ├── it.po ├── meson.build ├── pl.po ├── pt.po ├── ru.po ├── sv.po ├── zh.po └── zh_TW.po ├── scripts ├── build-flathub.sh ├── build-local.sh ├── build-native.sh ├── flathub-linter.sh ├── generate-icons.sh ├── rebuild-translations.sh └── set-version.py └── src ├── config.vapi ├── main.vala ├── meson.build ├── models ├── group.vala ├── launcher.vala ├── launchers │ ├── bottles.vala │ ├── hgl.vala │ ├── lutris.vala │ └── steam.vala ├── parameters.vala ├── release.vala ├── releases │ ├── basic.vala │ ├── github-action.vala │ ├── steamtinkerlaunch.vala │ └── upgrade.vala ├── runner.vala └── runners │ ├── basic.vala │ ├── boxtron.vala │ ├── dxvk-async-sarek.vala │ ├── dxvk-doitsujin.vala │ ├── dxvk-gpl-async-ph42on.vala │ ├── dxvk-sarek.vala │ ├── github-action.vala │ ├── github.vala │ ├── gitlab.vala │ ├── kron4ek-wine-builds-staging-tkg.vala │ ├── kron4ek-wine-builds-staging.vala │ ├── kron4ek-wine-builds-vanilla.vala │ ├── luxtorpeda.vala │ ├── northstar-proton.vala │ ├── other.vala │ ├── proton-cachyos.vala │ ├── proton-em.vala │ ├── proton-ge-rstp.vala │ ├── proton-ge.vala │ ├── proton-sarek-async.vala │ ├── proton-sarek.vala │ ├── proton-tkg.vala │ ├── roberta.vala │ ├── steamtinkerlaunch.vala │ ├── vkd3d-lutris.vala │ └── vkd3d-proton.vala ├── utils ├── filesystem.vala ├── parser.vala ├── system.vala └── web.vala └── widgets ├── application.vala ├── busy-dialog.vala ├── busy-dialogs ├── install-dialog.vala ├── remove-dialog.vala └── upgrade-dialog.vala ├── description-dialog.vala ├── info-box.vala ├── launcher-box.vala ├── load-more-row.vala ├── release-row.vala ├── release-rows ├── basic.vala └── steamtinkerlaunch.vala ├── runner-group.vala ├── runner-row.vala ├── sidebar-row.vala ├── sidebar.vala ├── status-box.vala └── window.vala /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[BUG]" 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. Fedora 36] 28 | - DE: [e.g. GNOME 42] 29 | - Type: [source or Flatpak] 30 | 31 | **Additional context** 32 | Add any other context about the problem here. 33 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "[FEATURE]" 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | *Thank you for contributing to ProtonPlus! So that your Pull Request can be handled effectively, please populate the following fields (delete sections that are not applicable)* 2 | 3 | ### Category 4 | > One of: Bugfix / Feature / Code style update / Refactoring Only / Build related changes / Documentation / Other (Please specify!) 5 | 6 | ### Overview 7 | > Briefly outline your new changes... 8 | 9 | ### Issue Number _(if applicable)_ 10 | > Related issue: #00 11 | 12 | ### New Vars _(if applicable)_ 13 | > If you've added any new build scripts, environmental variables, config file options, dependency please outline here. 14 | 15 | ### Screenshot _(if applicable)_ 16 | > If you've introduced any significant UI changes, please include a screenshot. 17 | 18 | -------------------------------------------------------------------------------- /.github/workflows/main.yml: -------------------------------------------------------------------------------- 1 | name: Build & Check 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | build: 10 | name: Build 11 | runs-on: ubuntu-latest 12 | steps: 13 | - name: Install dependencies 14 | run: | 15 | sudo apt update 16 | sudo apt install gettext valac meson libadwaita-1-dev libarchive-dev libgee-0.8-dev libgtk-4-dev libjson-glib-dev libsoup-3.0-dev desktop-file-utils appstream-util 17 | - name: Checkout pull request 18 | uses: actions/checkout@v4.2.2 19 | with: 20 | ref: ${{ github.event.pull_request.head.sha }} 21 | - name: Build 22 | run: | 23 | mkdir build 24 | meson --prefix=/usr build 25 | meson compile -C build 26 | - name: Check 27 | run: 'meson test -C build --print-errorlogs || :' 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Temporary build files. 2 | _build/ 3 | .flatpak-builder/ 4 | .flatpak/ 5 | build-dir/ 6 | build-flatpak/ 7 | build-native/ 8 | build/ 9 | 10 | # Miscellaneous. 11 | .vscode/ 12 | gdb.sh 13 | com.vysp3r.ProtonPlus.flatpak -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | protonplus@vysp3r.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to ProtonPlus 2 | 3 | First off, thanks for taking the time to contribute! ❤️ 4 | 5 | All types of contributions are encouraged and valued. See the [Table of Contents](#table-of-contents) for different ways to help and details about how this project handles them. Please make sure to read the relevant section before making your contribution. It will make it a lot easier for us maintainers and smooth out the experience for all involved. The community looks forward to your contributions. 🎉 6 | 7 | > And if you like the project, but just don't have time to contribute, that's fine. There are other easy ways to support the project and show your appreciation, which we would also be very happy about: 8 | > - Star the project 9 | > - Tweet about it 10 | > - Refer this project in your project's readme 11 | > - Mention the project at local meetups and tell your friends/colleagues 12 | 13 | ## Table of Contents 14 | 15 | - [I Have a Question](#i-have-a-question) 16 | - [I Want To Contribute](#i-want-to-contribute) 17 | - [Reporting Bugs](#reporting-bugs) 18 | - [Suggesting Enhancements](#suggesting-enhancements) 19 | - [Styleguides](#styleguides) 20 | - [Commit Messages](#commit-messages) 21 | 22 | 23 | ## Code of Conduct 24 | 25 | This project and everyone participating in it is governed by the 26 | [ProtonPlus Code of Conduct](https://github.com/Vysp3r/ProtonPlus/blob/master/CODE_OF_CONDUCT.md). 27 | By participating, you are expected to uphold this code. Please report unacceptable behavior 28 | to [@Vysp3r](https://github.com/Vysp3r). 29 | 30 | ## I Have a Question 31 | 32 | > If you want to ask a question, we assume that you have read the available [Documentation](https://github.com/Vysp3r/ProtonPlus/#readme). 33 | 34 | Before you ask a question, it is best to search for existing [Issues](https://github.com/Vysp3r/ProtonPlus/issues) that might help you. In case you have found a suitable issue and still need clarification, you can write your question in this issue. It is also advisable to search the internet for answers first. 35 | 36 | If you then still feel the need to ask a question and need clarification, we recommend the following: 37 | 38 | - Open an [Issue](https://github.com/Vysp3r/ProtonPlus/issues/new). 39 | - Provide as much context as you can about what you're running into. 40 | 41 | We will then take care of the issue as soon as possible. 42 | 43 | ## I Want To Contribute 44 | 45 | > ### Legal Notice 46 | > When contributing to this project, you must agree that you have authored 100% of the content, that you have the necessary rights to the content and that the content you contribute may be provided under the project license. 47 | 48 | ### Reporting Bugs 49 | 50 | #### Before Submitting a Bug Report 51 | 52 | A good bug report shouldn't leave others needing to chase you up for more information. Therefore, we ask you to investigate carefully, collect information and describe the issue in detail in your report. Please complete the following steps in advance to help us fix any potential bug as fast as possible. 53 | 54 | - Make sure that you are using the latest version. 55 | - Determine if your bug is really a bug and not an error on your side e.g. using incompatible environment components/versions (Make sure that you have read the [documentation](https://github.com/Vysp3r/ProtonPlus/#readme). If you are looking for support, you might want to check [this section](#i-have-a-question)). 56 | - To see if other users have experienced (and potentially already solved) the same issue you are having, check if there is not already a bug report existing for your bug or error in the [bug tracker](https://github.com/Vysp3r/ProtonPlus/issues?q=is%3Aopen+is%3Aissue+label%3A%22%F0%9F%90%9B+Bug%22). 57 | - Also make sure to search the internet (including Stack Overflow) to see if users outside of the GitHub community have discussed the issue. 58 | - Collect information about the bug: 59 | - Stack trace (Traceback) 60 | - OS, Platform and Version (Windows, Linux, macOS, x86, ARM) 61 | - Possibly your input and the output 62 | - Can you reliably reproduce the issue? And can you also reproduce it with older versions? 63 | 64 | #### How Do I Submit a Good Bug Report? 65 | 66 | > You must never report security related issues, vulnerabilities or bugs including sensitive information to the issue tracker, or elsewhere in public. Instead sensitive bugs must be sent by email to vyspr@tuta.io. 67 | 68 | We use GitHub issues to track bugs and errors. If you run into an issue with the project: 69 | 70 | - Open an [Issue](https://github.com/Vysp3r/ProtonPlus/issues/new). 71 | - Explain the behavior you would expect and the actual behavior. 72 | - Please provide as much context as possible and describe the *reproduction steps* that someone else can follow to recreate the issue on their own. This usually includes your code. For good bug reports you should isolate the problem and create a reduced test case. 73 | - Provide the information you collected in the previous section. 74 | 75 | 76 | ### Suggesting Enhancements 77 | 78 | This section guides you through submitting an enhancement suggestion for ProtonPlus, **including completely new features and minor improvements to existing functionality**. Following these guidelines will help maintainers and the community to understand your suggestion and find related suggestions. 79 | 80 | #### Before Submitting an Enhancement 81 | 82 | - Make sure that you are using the latest version. 83 | - Read the [documentation](https://github.com/Vysp3r/ProtonPlus/#readme) carefully and find out if the functionality is already covered, maybe by an individual configuration. 84 | - Perform a [search](https://github.com/Vysp3r/ProtonPlus/issues) to see if the enhancement has already been suggested. If it has, add a comment to the existing issue instead of opening a new one. 85 | - Find out whether your idea fits with the scope and aims of the project. It's up to you to make a strong case to convince the project's developers of the merits of this feature. Keep in mind that we want features that will be useful to the majority of our users and not just a small subset. If you're just targeting a minority of users, consider writing an add-on/plugin library. 86 | 87 | #### How Do I Submit a Good Enhancement Suggestion? 88 | 89 | Enhancement suggestions are tracked as [GitHub issues](https://github.com/Vysp3r/ProtonPlus/issues). 90 | 91 | - Use a **clear and descriptive title** for the issue to identify the suggestion. 92 | - Provide a **step-by-step description of the suggested enhancement** in as many details as possible. 93 | - **Describe the current behavior** and **explain which behavior you expected to see instead** and why. At this point you can also tell which alternatives do not work for you. 94 | - You may want to **include screenshots and animated GIFs** which help you demonstrate the steps or point out the part which the suggestion is related to. You can use [this tool](https://github.com/ShareX/ShareX) to record GIFs on Windows, and [this tool](https://github.com/phw/peek) on linux. 95 | - **Explain why this enhancement would be useful** to most ProtonPlus users. You may also want to point out the other projects that solved it better and which could serve as inspiration. 96 | 97 | ## Styleguides 98 | ### Commit Messages 99 | 100 | ProtonPlus uses [GitMoji](https://gitmoji.dev/). 101 | We would appreciate it if everyone keeps their commit messages withing these rulings. 102 | 103 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | 3 |

4 | ProtonPlus 5 |

6 | 7 |

8 | A modern compatibility tools manager for Linux. 9 |

10 | 11 |

12 | 13 | Stars 14 | 15 | 16 | Latest Release 17 | 18 | 19 | Flathub Downloads 20 | 21 | 22 | License 23 | 24 | 25 | Telegram 26 | 27 |

28 | 29 |

30 | Don't forget to star the repo if you are enjoying the project! 31 |

32 | 33 | [Preview 1](https://flathub.org/apps/details/com.vysp3r.ProtonPlus) 34 | 35 | ## 📦️ Installation methods 36 | 37 | 38 | Download on Flathub 39 | 40 | 41 |

42 | 43 | > [!WARNING] 44 | > The main installation method is Flathub 45 | 46 | ### [Arch Linux (AUR)](https://aur.archlinux.org/packages/protonplus) (Maintained by yochananmarqos) 47 | 48 | ### [Fedora (COPR)](https://copr.fedorainfracloud.org/coprs/wehagy/protonplus/) (Maintained by wehagy) 49 | 50 | ### [NixOS (MyNixOS)](https://mynixos.com/nixpkgs/package/protonplus) (Maintained by Seth) 51 | 52 | ### [Ubuntu (Pacstall)](https://pacstall.dev/packages/protonplus) (Maintained by xdavius) 53 | 54 | ### [openSUSE](https://software.opensuse.org/package/ProtonPlus) (Maintained by rrahl0) 55 | 56 | ## 🏗️ Building from source 57 | 58 | **Requirements** 59 | 60 | - [git](https://github.com/git/git) 61 | - [ninja](https://github.com/ninja-build/ninja) 62 | - [meson >= 1.0.0](https://github.com/mesonbuild/meson) 63 | - [gtk4](https://gitlab.gnome.org/GNOME/gtk/) 64 | - [libadwaita >= 1.5](https://gitlab.gnome.org/GNOME/libadwaita) 65 | - [json-glib](https://gitlab.gnome.org/GNOME/json-glib) 66 | - [libsoup](https://gitlab.gnome.org/GNOME/libsoup) 67 | - [libarchive](https://github.com/libarchive/libarchive) 68 | - [desktop-file-utils](https://gitlab.freedesktop.org/xdg/desktop-file-utils) 69 | - [libgee](https://gitlab.gnome.org/GNOME/libgee) 70 | 71 |
72 | Linux 73 | 74 | 1. Install all dependencies (I am on Fedora, so for you this line might be different) 75 | ```bash 76 | sudo dnf install \ 77 | git \ 78 | gettext \ 79 | 'meson >= 1.0.0'\ 80 | vala \ 81 | desktop-file-utils \ 82 | libappstream-glib \ 83 | 'pkgconfig(gee-0.8)' \ 84 | 'pkgconfig(glib-2.0)' \ 85 | 'pkgconfig(gtk4)' \ 86 | 'pkgconfig(json-glib-1.0)' \ 87 | 'pkgconfig(libadwaita-1) >= 1.5' \ 88 | 'pkgconfig(libarchive)' \ 89 | 'pkgconfig(libsoup-3.0)' 90 | ``` 91 | 92 | 2. Clone the GitHub repo and change to repo directory 93 | ```bash 94 | git clone https://github.com/Vysp3r/ProtonPlus.git && \ 95 | cd ProtonPlus 96 | ``` 97 | 98 | 3. Build the local source code as a native application 99 | ```bash 100 | ./scripts/build-native.sh 101 | 102 | # Alternative: Runs application after the build. 103 | ./scripts/build-native.sh run 104 | ``` 105 | 106 | 4. (Optional) Install the application 107 | ```bash 108 | cd build-native 109 | ninja install 110 | ``` 111 | 112 | 5. Run the application 113 | ```bash 114 | cd src && \ 115 | ./com.vysp3r.ProtonPlus 116 | ``` 117 |
118 | 119 |
120 | Linux (Flatpak Builder) 121 | 122 | 1. Install all dependencies (I am on Fedora, so for you this line might be different) 123 | ```bash 124 | sudo dnf install \ 125 | git \ 126 | flatpak 127 | ``` 128 | 129 | 2. Add the flathub repo to your system if not added before 130 | ```bash 131 | flatpak --if-not-exists remote-add \ 132 | flathub https://flathub.org/repo/flathub.flatpakrepo 133 | ``` 134 | 135 | 3. Install the necessary runtimes and build tools for Flatpak 136 | ```bash 137 | flatpak install \ 138 | runtime/org.gnome.Sdk/x86_64/48 \ 139 | runtime/org.gnome.Platform/x86_64/48 \ 140 | runtime/org.freedesktop.Sdk.Extension.vala/x86_64/24.08 \ 141 | org.flatpak.Builder 142 | ``` 143 | 144 | 4. Clone the GitHub repo and change to repo directory 145 | ```bash 146 | git clone https://github.com/Vysp3r/ProtonPlus.git && \ 147 | cd ProtonPlus 148 | ``` 149 | 150 | 5. Build the local source code as a Flatpak and install for the current user 151 | ```bash 152 | ./scripts/build-local.sh 153 | 154 | # Alternative: Runs application after the build. 155 | ./scripts/build-local.sh run 156 | ``` 157 | 158 | 6. Run the application 159 | ```bash 160 | flatpak --user run \ 161 | com.vysp3r.ProtonPlus 162 | ``` 163 |
164 | 165 | ## 🌐 Translate 166 | 167 | **You can translate ProtonPlus on [Weblate](https://hosted.weblate.org/projects/protonplus/protonplus/) or by modifying the files directly** 168 | 169 | ## 🙌 Contribute 170 | 171 | **Please read our [Contribution Guidelines](/CONTRIBUTING.md)** 172 | 173 | All contributions are highly appreciated. 174 | 175 | ## ✨️ Contributors 176 | 177 | [![Contributors](https://contrib.rocks/image?repo=Vysp3r/ProtonPlus)](https://github.com/Vysp3r/ProtonPlus/graphs/contributors) 178 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Supported Versions 4 | 5 | All versions are currently supported! 6 | 7 | ## Reporting a Vulnerability 8 | 9 | To report a vulnerability, email me at dev@vysp3r.com. Do not create an issue for that! 10 | -------------------------------------------------------------------------------- /com.vysp3r.ProtonPlus.local.yml: -------------------------------------------------------------------------------- 1 | id: com.vysp3r.ProtonPlus 2 | runtime: org.gnome.Platform 3 | runtime-version: '48' 4 | sdk: org.gnome.Sdk 5 | sdk-extensions: 6 | - org.freedesktop.Sdk.Extension.vala 7 | command: protonplus 8 | finish-args: 9 | - --device=dri 10 | - --share=ipc 11 | - --share=network 12 | - --socket=fallback-x11 13 | - --socket=wayland 14 | # Host-spawn access is needed to check if host contains dependencies, 15 | # and to execute various compatibility tool installers on the host. 16 | - --talk-name=org.freedesktop.Flatpak 17 | # Required GNOME/GTK permissions. 18 | - --talk-name=org.gtk.vfs.* 19 | - --filesystem=xdg-run/gvfsd 20 | # Home is required due to "~/stl" installations on Steam Deck. 21 | - --filesystem=home 22 | # Request access to supported Flatpak apps (they are not included by "home"). 23 | - --filesystem=~/.var/app/com.heroicgameslauncher.hgl/config/heroic 24 | - --filesystem=~/.var/app/com.usebottles.bottles/data/bottles 25 | - --filesystem=~/.var/app/com.valvesoftware.Steam/data/Steam 26 | - --filesystem=~/.var/app/net.lutris.Lutris/data/lutris 27 | build-options: 28 | append-path: /usr/lib/sdk/vala/bin 29 | prepend-ld-library-path: /usr/lib/sdk/vala/lib 30 | cleanup: 31 | - /include 32 | - /lib/pkgconfig 33 | - /share/pkgconfig 34 | - /share/aclocal 35 | - /man 36 | - /share/man 37 | - /share/gtk-doc 38 | - '*.la' 39 | - '*.a' 40 | modules: 41 | - name: ProtonPlus 42 | builddir: true 43 | buildsystem: meson 44 | sources: 45 | - type: dir 46 | path: . 47 | -------------------------------------------------------------------------------- /com.vysp3r.ProtonPlus.yml: -------------------------------------------------------------------------------- 1 | id: com.vysp3r.ProtonPlus 2 | runtime: org.gnome.Platform 3 | runtime-version: '48' 4 | sdk: org.gnome.Sdk 5 | sdk-extensions: 6 | - org.freedesktop.Sdk.Extension.vala 7 | command: protonplus 8 | finish-args: 9 | - --device=dri 10 | - --share=ipc 11 | - --share=network 12 | - --socket=fallback-x11 13 | - --socket=wayland 14 | # Host-spawn access is needed to check if host contains dependencies, 15 | # and to execute various compatibility tool installers on the host. 16 | - --talk-name=org.freedesktop.Flatpak 17 | # Required GNOME/GTK permissions. 18 | - --talk-name=org.gtk.vfs.* 19 | - --filesystem=xdg-run/gvfsd 20 | # Home is required due to "~/stl" installations on Steam Deck. 21 | - --filesystem=home 22 | # Request access to supported Flatpak apps (they are not included by "home"). 23 | - --filesystem=~/.var/app/com.heroicgameslauncher.hgl/config/heroic 24 | - --filesystem=~/.var/app/com.usebottles.bottles/data/bottles 25 | - --filesystem=~/.var/app/com.valvesoftware.Steam/data/Steam 26 | - --filesystem=~/.var/app/net.lutris.Lutris/data/lutris 27 | build-options: 28 | append-path: /usr/lib/sdk/vala/bin 29 | prepend-ld-library-path: /usr/lib/sdk/vala/lib 30 | cleanup: 31 | - /include 32 | - /lib/pkgconfig 33 | - /man 34 | - /share/gtk-doc 35 | - /share/man 36 | - /share/pkgconfig 37 | - /share/vala 38 | - '*.la' 39 | - '*.a' 40 | modules: 41 | - name: ProtonPlus 42 | builddir: true 43 | buildsystem: meson 44 | sources: 45 | - type: git 46 | url: https://github.com/Vysp3r/ProtonPlus.git 47 | tag: v0.4.31 48 | x-checker-data: 49 | type: git 50 | tag-pattern: ^v(\d+\.\d+\.\d+(?:-[0-9A-Za-z.\-]+)?)$ 51 | version-scheme: semantic 52 | is-main-source: true 53 | -------------------------------------------------------------------------------- /data/com.vysp3r.ProtonPlus.desktop.in: -------------------------------------------------------------------------------- 1 | [Desktop Entry] 2 | Name=ProtonPlus 3 | Exec=protonplus 4 | Icon=com.vysp3r.ProtonPlus 5 | Terminal=false 6 | Type=Application 7 | Categories=Game;Utility; 8 | StartupNotify=true 9 | -------------------------------------------------------------------------------- /data/com.vysp3r.ProtonPlus.gschema.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 950 6 | 7 | 8 | 600 9 | 10 | 11 | false 12 | 13 | 14 | false 15 | 16 | 17 | -------------------------------------------------------------------------------- /data/css/css.gresource.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | style.css 5 | 6 | -------------------------------------------------------------------------------- /data/css/meson.build: -------------------------------------------------------------------------------- 1 | css_gresource = gnome.compile_resources( 2 | 'gresource_css', 3 | 'css.gresource.xml' 4 | ) -------------------------------------------------------------------------------- /data/css/style.css: -------------------------------------------------------------------------------- 1 | .sidebar-row { 2 | padding: 7px; 3 | } 4 | 5 | .dialog-list { 6 | background: transparent; 7 | } 8 | 9 | .dialog { 10 | padding: 10px; 11 | } -------------------------------------------------------------------------------- /data/icons/circle-check.svg: -------------------------------------------------------------------------------- 1 | 2 | 20 | 22 | 40 | 45 | 49 | 53 | 54 | -------------------------------------------------------------------------------- /data/icons/circle-chevron-up.svg: -------------------------------------------------------------------------------- 1 | 2 | 20 | 22 | 40 | 45 | 49 | 53 | 54 | -------------------------------------------------------------------------------- /data/icons/dots.svg: -------------------------------------------------------------------------------- 1 | 2 | 20 | 22 | 40 | 45 | 49 | 53 | 57 | 58 | -------------------------------------------------------------------------------- /data/icons/download.svg: -------------------------------------------------------------------------------- 1 | 2 | 20 | 22 | 40 | 45 | 49 | 53 | 57 | 58 | -------------------------------------------------------------------------------- /data/icons/icons.gresource.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | circle-check.svg 5 | circle-chevron-up.svg 6 | trash.svg 7 | download.svg 8 | x.svg 9 | info-circle.svg 10 | dots.svg 11 | world-www.svg 12 | journal-text.svg 13 | 14 | -------------------------------------------------------------------------------- /data/icons/info-circle.svg: -------------------------------------------------------------------------------- 1 | 2 | 20 | 22 | 40 | 45 | 49 | 53 | 57 | 58 | -------------------------------------------------------------------------------- /data/icons/journal-text.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /data/icons/meson.build: -------------------------------------------------------------------------------- 1 | icons_gresource = gnome.compile_resources( 2 | 'gresource_icons', 3 | 'icons.gresource.xml' 4 | ) -------------------------------------------------------------------------------- /data/icons/trash.svg: -------------------------------------------------------------------------------- 1 | 2 | 20 | 22 | 40 | 45 | 49 | 53 | 57 | 61 | 65 | 66 | -------------------------------------------------------------------------------- /data/icons/world-www.svg: -------------------------------------------------------------------------------- 1 | 2 | 20 | 22 | 40 | 45 | 49 | 53 | 57 | 61 | 65 | 69 | 73 | 77 | 81 | 82 | -------------------------------------------------------------------------------- /data/icons/x.svg: -------------------------------------------------------------------------------- 1 | 2 | 20 | 22 | 40 | 45 | 49 | 53 | 54 | -------------------------------------------------------------------------------- /data/images/images.gresource.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | launchers/bottles.png 5 | launchers/hgl.png 6 | launchers/lutris.png 7 | launchers/steam.png 8 | 9 | -------------------------------------------------------------------------------- /data/images/launchers/bottles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vysp3r/ProtonPlus/eb67d7c33cf33d4ded407496caeb37f961b49944/data/images/launchers/bottles.png -------------------------------------------------------------------------------- /data/images/launchers/hgl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vysp3r/ProtonPlus/eb67d7c33cf33d4ded407496caeb37f961b49944/data/images/launchers/hgl.png -------------------------------------------------------------------------------- /data/images/launchers/lutris.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vysp3r/ProtonPlus/eb67d7c33cf33d4ded407496caeb37f961b49944/data/images/launchers/lutris.png -------------------------------------------------------------------------------- /data/images/launchers/steam.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vysp3r/ProtonPlus/eb67d7c33cf33d4ded407496caeb37f961b49944/data/images/launchers/steam.png -------------------------------------------------------------------------------- /data/images/meson.build: -------------------------------------------------------------------------------- 1 | images_gresource = gnome.compile_resources( 2 | 'gresource_images', 3 | 'images.gresource.xml' 4 | ) -------------------------------------------------------------------------------- /data/logo/ProtonPlus.xcf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vysp3r/ProtonPlus/eb67d7c33cf33d4ded407496caeb37f961b49944/data/logo/ProtonPlus.xcf -------------------------------------------------------------------------------- /data/logo/icons/hicolor/128x128/apps/com.vysp3r.ProtonPlus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vysp3r/ProtonPlus/eb67d7c33cf33d4ded407496caeb37f961b49944/data/logo/icons/hicolor/128x128/apps/com.vysp3r.ProtonPlus.png -------------------------------------------------------------------------------- /data/logo/icons/hicolor/16x16/apps/com.vysp3r.ProtonPlus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vysp3r/ProtonPlus/eb67d7c33cf33d4ded407496caeb37f961b49944/data/logo/icons/hicolor/16x16/apps/com.vysp3r.ProtonPlus.png -------------------------------------------------------------------------------- /data/logo/icons/hicolor/256x256/apps/com.vysp3r.ProtonPlus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vysp3r/ProtonPlus/eb67d7c33cf33d4ded407496caeb37f961b49944/data/logo/icons/hicolor/256x256/apps/com.vysp3r.ProtonPlus.png -------------------------------------------------------------------------------- /data/logo/icons/hicolor/32x32/apps/com.vysp3r.ProtonPlus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vysp3r/ProtonPlus/eb67d7c33cf33d4ded407496caeb37f961b49944/data/logo/icons/hicolor/32x32/apps/com.vysp3r.ProtonPlus.png -------------------------------------------------------------------------------- /data/logo/icons/hicolor/48x48/apps/com.vysp3r.ProtonPlus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vysp3r/ProtonPlus/eb67d7c33cf33d4ded407496caeb37f961b49944/data/logo/icons/hicolor/48x48/apps/com.vysp3r.ProtonPlus.png -------------------------------------------------------------------------------- /data/logo/icons/hicolor/512x512/apps/com.vysp3r.ProtonPlus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vysp3r/ProtonPlus/eb67d7c33cf33d4ded407496caeb37f961b49944/data/logo/icons/hicolor/512x512/apps/com.vysp3r.ProtonPlus.png -------------------------------------------------------------------------------- /data/logo/icons/hicolor/64x64/apps/com.vysp3r.ProtonPlus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vysp3r/ProtonPlus/eb67d7c33cf33d4ded407496caeb37f961b49944/data/logo/icons/hicolor/64x64/apps/com.vysp3r.ProtonPlus.png -------------------------------------------------------------------------------- /data/meson.build: -------------------------------------------------------------------------------- 1 | subdir('ui') 2 | subdir('css') 3 | subdir('icons') 4 | subdir('images') 5 | 6 | install_subdir( 7 | join_paths('logo', 'icons'), 8 | install_dir: join_paths(get_option('prefix'), get_option('datadir'), 'icons'), 9 | strip_directory: true 10 | ) 11 | 12 | desktop_file = i18n.merge_file( 13 | input: 'com.vysp3r.ProtonPlus.desktop.in', 14 | output: 'com.vysp3r.ProtonPlus.desktop', 15 | type: 'desktop', 16 | po_dir: '../po', 17 | install: true, 18 | install_dir: join_paths(get_option('datadir'), 'applications') 19 | ) 20 | 21 | desktop_utils = find_program('desktop-file-validate', required: false) 22 | if desktop_utils.found() 23 | test('Validate desktop file', desktop_utils, args: [desktop_file]) 24 | endif 25 | 26 | appstream_file = i18n.merge_file( 27 | input: 'com.vysp3r.ProtonPlus.metainfo.xml.in', 28 | output: 'com.vysp3r.ProtonPlus.metainfo.xml', 29 | po_dir: '../po', 30 | install: true, 31 | install_dir: join_paths(get_option('datadir'), 'metainfo') 32 | ) 33 | 34 | appstream_util = find_program('appstream-util', required: false) 35 | if appstream_util.found() 36 | test('Validate appstream file', appstream_util, args: ['validate-relax', '--nonet', appstream_file]) 37 | endif 38 | 39 | install_data('com.vysp3r.ProtonPlus.gschema.xml', 40 | install_dir: join_paths(get_option('datadir'), 'glib-2.0/schemas') 41 | ) 42 | 43 | compile_schemas = find_program('glib-compile-schemas', required: false) 44 | if compile_schemas.found() 45 | test('Validate schema file', 46 | compile_schemas, 47 | args: ['--strict', '--dry-run', meson.current_source_dir()]) 48 | endif 49 | -------------------------------------------------------------------------------- /data/previews/Preview-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Vysp3r/ProtonPlus/eb67d7c33cf33d4ded407496caeb37f961b49944/data/previews/Preview-1.png -------------------------------------------------------------------------------- /data/ui/gtk/help-overlay.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | True 5 | 6 | 7 | shortcuts 8 | 10 9 | 10 | 11 | General 12 | 13 | 14 | Show Shortcuts 15 | win.show-help-overlay 16 | 17 | 18 | 19 | 20 | Quit 21 | app.quit 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /data/ui/meson.build: -------------------------------------------------------------------------------- 1 | ui_gresource = gnome.compile_resources( 2 | 'ui-resource', 3 | 'ui.gresource.xml' 4 | ) 5 | -------------------------------------------------------------------------------- /data/ui/ui.gresource.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | gtk/help-overlay.ui 5 | 6 | 7 | -------------------------------------------------------------------------------- /meson.build: -------------------------------------------------------------------------------- 1 | project( 2 | 'com.vysp3r.ProtonPlus', ['c', 'vala'], 3 | version: '0.4.31', 4 | meson_version: '>= 1.0.0', 5 | default_options: [ 'warning_level=2', 'werror=false', ], 6 | ) 7 | 8 | i18n = import('i18n') 9 | gnome = import('gnome') 10 | valac = meson.get_compiler('vala') 11 | 12 | src_dir = meson.project_source_root() / 'src' 13 | 14 | config_h = configuration_data() 15 | config_h.set_quoted('APP_NAME', meson.project_name().split('.')[2]) 16 | config_h.set_quoted('APP_ID', meson.project_name()) 17 | config_h.set_quoted('APP_VERSION', meson.project_version()) 18 | config_h.set_quoted('LOCALE_DIR', join_paths (get_option('prefix'), get_option('localedir'))) 19 | config_h.set_quoted('RESOURCE_BASE', '/com/vysp3r/ProtonPlus') 20 | configure_file(output : 'config.h', configuration : config_h) 21 | 22 | config_dep = valac.find_library ('config', dirs: src_dir) 23 | config_inc = include_directories('.') 24 | 25 | add_global_arguments('-DGETTEXT_PACKAGE="@0@"'.format(meson.project_name()), language: 'c') 26 | 27 | subdir('data') 28 | subdir('src') 29 | subdir('po') 30 | 31 | gnome.post_install( 32 | glib_compile_schemas: true, 33 | gtk_update_icon_cache: true, 34 | update_desktop_database: true, 35 | ) -------------------------------------------------------------------------------- /po/LINGUAS: -------------------------------------------------------------------------------- 1 | cs 2 | de 3 | es 4 | fr 5 | it 6 | ru 7 | id 8 | pt 9 | zh 10 | be 11 | fi 12 | pl 13 | sv 14 | zh_TW -------------------------------------------------------------------------------- /po/POTFILES: -------------------------------------------------------------------------------- 1 | src/widgets/status-box.vala 2 | src/widgets/sidebar.vala 3 | src/widgets/info-box.vala 4 | src/widgets/description-dialog.vala 5 | src/widgets/busy-dialog.vala 6 | src/widgets/release-row.vala 7 | src/widgets/runner-row.vala 8 | src/widgets/runner-group.vala 9 | src/widgets/launcher-box.vala 10 | src/widgets/load-more-row.vala 11 | src/widgets/window.vala 12 | src/widgets/application.vala 13 | src/widgets/release-rows/basic.vala 14 | src/widgets/release-rows/steamtinkerlaunch.vala 15 | src/widgets/busy-dialogs/install-dialog.vala 16 | src/widgets/busy-dialogs/remove-dialog.vala 17 | src/widgets/busy-dialogs/upgrade-dialog.vala 18 | src/models/release.vala 19 | src/models/group.vala 20 | src/models/parameters.vala 21 | src/models/launcher.vala 22 | src/models/runner.vala 23 | src/models/launchers/bottles.vala 24 | src/models/launchers/hgl.vala 25 | src/models/launchers/lutris.vala 26 | src/models/launchers/steam.vala 27 | src/models/runners/basic.vala 28 | src/models/runners/github.vala 29 | src/models/runners/github-action.vala 30 | src/models/runners/gitlab.vala 31 | src/models/runners/boxtron.vala 32 | src/models/runners/dxvk-doitsujin.vala 33 | src/models/runners/dxvk-sarek.vala 34 | src/models/runners/dxvk-async-sarek.vala 35 | src/models/runners/dxvk-gpl-async-ph42on.vala 36 | src/models/runners/kron4ek-wine-builds-staging-tkg.vala 37 | src/models/runners/kron4ek-wine-builds-staging.vala 38 | src/models/runners/kron4ek-wine-builds-vanilla.vala 39 | src/models/runners/luxtorpeda.vala 40 | src/models/runners/northstar-proton.vala 41 | src/models/runners/proton-ge-rstp.vala 42 | src/models/runners/proton-ge.vala 43 | src/models/runners/proton-tkg.vala 44 | src/models/runners/roberta.vala 45 | src/models/runners/proton-cachyos.vala 46 | src/models/runners/proton-em.vala 47 | src/models/runners/proton-sarek.vala 48 | src/models/runners/proton-sarek-async.vala 49 | src/models/runners/vkd3d-lutris.vala 50 | src/models/runners/vkd3d-proton.vala 51 | src/models/runners/other.vala 52 | src/models/runners/steamtinkerlaunch.vala 53 | src/models/releases/basic.vala 54 | src/models/releases/basic.vala 55 | src/models/releases/github-action.vala 56 | src/models/releases/upgrade.vala 57 | src/models/releases/steamtinkerlaunch.vala 58 | src/utils/filesystem.vala 59 | src/utils/parser.vala 60 | src/utils/web.vala 61 | src/utils/system.vala 62 | src/main.vala -------------------------------------------------------------------------------- /po/com.vysp3r.ProtonPlus.pot: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the com.vysp3r.ProtonPlus package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: com.vysp3r.ProtonPlus\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2025-05-25 18:12-0400\n" 12 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 13 | "Last-Translator: FULL NAME \n" 14 | "Language-Team: LANGUAGE \n" 15 | "Language: \n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=CHARSET\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | 20 | #: src/widgets/info-box.vala:16 21 | msgid "_Installed Only" 22 | msgstr "" 23 | 24 | #: src/widgets/info-box.vala:23 25 | msgid "_Keyboard Shortcuts" 26 | msgstr "" 27 | 28 | #: src/widgets/info-box.vala:24 29 | msgid "_About ProtonPlus" 30 | msgstr "" 31 | 32 | #: src/widgets/info-box.vala:31 33 | msgid "Main Menu" 34 | msgstr "" 35 | 36 | #: src/widgets/description-dialog.vala:14 37 | msgid "More information" 38 | msgstr "" 39 | 40 | #: src/widgets/description-dialog.vala:18 41 | msgid "Open" 42 | msgstr "" 43 | 44 | #: src/widgets/description-dialog.vala:22 45 | msgid "Open in your web browser" 46 | msgstr "" 47 | 48 | #: src/widgets/busy-dialog.vala:23 49 | msgid "Show/hide logs" 50 | msgstr "" 51 | 52 | #: src/widgets/busy-dialog.vala:120 53 | msgid "Closing in" 54 | msgstr "" 55 | 56 | #: src/widgets/release-row.vala:10 src/widgets/runner-group.vala:50 57 | #: src/widgets/runner-group.vala:62 src/widgets/release-rows/basic.vala:34 58 | #: src/widgets/release-rows/steamtinkerlaunch.vala:110 59 | #, c-format 60 | msgid "Delete %s" 61 | msgstr "" 62 | 63 | #: src/widgets/release-row.vala:14 64 | #, c-format 65 | msgid "Install %s" 66 | msgstr "" 67 | 68 | #: src/widgets/release-row.vala:18 69 | msgid "Show more information" 70 | msgstr "" 71 | 72 | #: src/widgets/runner-group.vala:62 src/widgets/release-rows/basic.vala:34 73 | #: src/widgets/release-rows/steamtinkerlaunch.vala:110 74 | #, c-format 75 | msgid "You're about to remove %s from your system." 76 | msgstr "" 77 | 78 | #: src/widgets/runner-group.vala:62 src/widgets/release-rows/basic.vala:34 79 | #: src/widgets/release-rows/steamtinkerlaunch.vala:110 80 | msgid "Are you sure you want this?" 81 | msgstr "" 82 | 83 | #: src/widgets/runner-group.vala:64 src/widgets/release-rows/basic.vala:36 84 | #: src/widgets/release-rows/steamtinkerlaunch.vala:114 85 | msgid "No" 86 | msgstr "" 87 | 88 | #: src/widgets/runner-group.vala:65 src/widgets/release-rows/basic.vala:37 89 | #: src/widgets/release-rows/steamtinkerlaunch.vala:115 90 | msgid "Yes" 91 | msgstr "" 92 | 93 | #: src/widgets/load-more-row.vala:8 src/widgets/load-more-row.vala:17 94 | msgid "Load more" 95 | msgstr "" 96 | 97 | #: src/widgets/window.vala:50 98 | #, c-format 99 | msgid "Welcome to %s" 100 | msgstr "" 101 | 102 | #: src/widgets/window.vala:50 103 | msgid "Install Steam, Lutris, Bottles or Heroic Games Launcher to get started." 104 | msgstr "" 105 | 106 | #: src/widgets/application.vala:78 107 | msgid "A modern compatibility tools manager for Linux." 108 | msgstr "" 109 | 110 | #: src/widgets/application.vala:84 111 | msgid "Special thanks to" 112 | msgstr "" 113 | 114 | #: src/widgets/release-rows/steamtinkerlaunch.vala:60 115 | msgid "Missing dependencies!" 116 | msgstr "" 117 | 118 | #: src/widgets/release-rows/steamtinkerlaunch.vala:60 119 | #, c-format 120 | msgid "You are missing the following dependencies for %s:" 121 | msgstr "" 122 | 123 | #: src/widgets/release-rows/steamtinkerlaunch.vala:60 124 | msgid "Installation will be canceled." 125 | msgstr "" 126 | 127 | #: src/widgets/release-rows/steamtinkerlaunch.vala:62 128 | #: src/widgets/release-rows/steamtinkerlaunch.vala:76 129 | #: src/widgets/release-rows/steamtinkerlaunch.vala:175 130 | msgid "OK" 131 | msgstr "" 132 | 133 | #: src/widgets/release-rows/steamtinkerlaunch.vala:73 134 | #, c-format 135 | msgid "Existing installation of %s" 136 | msgstr "" 137 | 138 | #: src/widgets/release-rows/steamtinkerlaunch.vala:73 139 | #, c-format 140 | msgid "" 141 | "It looks like you currently have another version of %s which was not " 142 | "installed by ProtonPlus." 143 | msgstr "" 144 | 145 | #: src/widgets/release-rows/steamtinkerlaunch.vala:73 146 | #, c-format 147 | msgid "Do you want to delete it and install %s with ProtonPlus?" 148 | msgstr "" 149 | 150 | #: src/widgets/release-rows/steamtinkerlaunch.vala:75 151 | msgid "Cancel" 152 | msgstr "" 153 | 154 | #: src/widgets/release-rows/steamtinkerlaunch.vala:108 155 | msgid "Check this to also remove your configuration files." 156 | msgstr "" 157 | 158 | #: src/widgets/release-rows/steamtinkerlaunch.vala:165 159 | #: src/widgets/release-rows/steamtinkerlaunch.vala:169 160 | #, c-format 161 | msgid "%s is not supported" 162 | msgstr "" 163 | 164 | #: src/widgets/release-rows/steamtinkerlaunch.vala:165 165 | #, c-format 166 | msgid "To install %s for the %s, please run the following command:" 167 | msgstr "" 168 | 169 | #: src/widgets/release-rows/steamtinkerlaunch.vala:169 170 | #, c-format 171 | msgid "There's currently no known way for us to install %s for the %s." 172 | msgstr "" 173 | 174 | #: src/widgets/release-rows/steamtinkerlaunch.vala:195 175 | #, c-format 176 | msgid "%s is up-to-date" 177 | msgstr "" 178 | 179 | #: src/widgets/release-rows/steamtinkerlaunch.vala:195 180 | #, c-format 181 | msgid "Update %s to the latest version" 182 | msgstr "" 183 | 184 | #: src/widgets/busy-dialogs/install-dialog.vala:6 185 | msgid "Installing" 186 | msgstr "" 187 | 188 | #: src/widgets/busy-dialogs/remove-dialog.vala:6 189 | msgid "Removing" 190 | msgstr "" 191 | 192 | #: src/widgets/busy-dialogs/upgrade-dialog.vala:6 193 | msgid "Upgrading" 194 | msgstr "" 195 | 196 | #: src/models/release.vala:31 197 | #, c-format 198 | msgid "The installation of %s has begun." 199 | msgstr "" 200 | 201 | #: src/models/release.vala:34 202 | #, c-format 203 | msgid "The removal of %s has begun." 204 | msgstr "" 205 | 206 | #: src/models/release.vala:37 207 | #, c-format 208 | msgid "The upgrade of %s has begun." 209 | msgstr "" 210 | 211 | #: src/models/release.vala:57 212 | #, c-format 213 | msgid "An unexpected error occurred while installing %s." 214 | msgstr "" 215 | 216 | #: src/models/release.vala:59 217 | #, c-format 218 | msgid "The installation of %s is complete." 219 | msgstr "" 220 | 221 | #: src/models/release.vala:79 222 | #, c-format 223 | msgid "The removal of %s is complete." 224 | msgstr "" 225 | 226 | #: src/models/release.vala:79 227 | #, c-format 228 | msgid "An unexpected error occurred while removing %s." 229 | msgstr "" 230 | 231 | #: src/models/launchers/bottles.vala:29 src/models/launchers/steam.vala:33 232 | msgid "Runners" 233 | msgstr "" 234 | 235 | #: src/models/launchers/bottles.vala:29 src/models/launchers/hgl.vala:32 236 | #: src/models/launchers/lutris.vala:36 237 | msgid "Compatibility tools for running Windows software on Linux." 238 | msgstr "" 239 | 240 | #: src/models/launchers/bottles.vala:32 src/models/launchers/lutris.vala:39 241 | msgid "Vulkan-based implementation of Direct3D 8, 9, 10 and 11 for Linux/Wine." 242 | msgstr "" 243 | 244 | #: src/models/launchers/hgl.vala:29 src/models/launchers/lutris.vala:33 245 | msgid "Compatibility tools by Valve for running Windows software on Linux." 246 | msgstr "" 247 | 248 | #: src/models/launchers/lutris.vala:42 249 | msgid "" 250 | "Variant of Wine's VKD3D which aims to implement the full Direct3D 12 API on " 251 | "top of Vulkan." 252 | msgstr "" 253 | 254 | #: src/models/runners/boxtron.vala:6 255 | msgid "Steam compatibility tool for running DOS games using DOSBox for Linux." 256 | msgstr "" 257 | 258 | #: src/models/runners/dxvk-sarek.vala:6 259 | #: src/models/runners/dxvk-async-sarek.vala:6 260 | msgid "DXVK Builds that work with pre-Vulkan 1.3 versions" 261 | msgstr "" 262 | 263 | #: src/models/runners/kron4ek-wine-builds-staging-tkg.vala:7 264 | msgid "Wine build with the Staging patchset and many other useful patches." 265 | msgstr "" 266 | 267 | #: src/models/runners/kron4ek-wine-builds-staging.vala:7 268 | msgid "Wine build with the Staging patchset." 269 | msgstr "" 270 | 271 | #: src/models/runners/kron4ek-wine-builds-vanilla.vala:7 272 | msgid "Wine build compiled from the official WineHQ sources." 273 | msgstr "" 274 | 275 | #: src/models/runners/luxtorpeda.vala:6 276 | msgid "" 277 | "Luxtorpeda provides Linux-native game engines for certain Windows-only games." 278 | msgstr "" 279 | 280 | #: src/models/runners/northstar-proton.vala:6 281 | msgid "Custom Proton build for running the Northstar client for Titanfall 2." 282 | msgstr "" 283 | 284 | #: src/models/runners/proton-ge-rstp.vala:6 285 | msgid "" 286 | "Steam compatibility tool based on Proton-GE with additional patches to " 287 | "improve RTSP codecs for VRChat." 288 | msgstr "" 289 | 290 | #: src/models/runners/proton-ge.vala:6 291 | msgid "" 292 | "Steam compatibility tool for running Windows games with improvements over " 293 | "Valve's default Proton." 294 | msgstr "" 295 | 296 | #: src/models/runners/proton-tkg.vala:6 297 | msgid "Custom Proton build for running Windows games, based on Wine-tkg." 298 | msgstr "" 299 | 300 | #: src/models/runners/roberta.vala:6 301 | msgid "" 302 | "Steam compatibility tool for running adventure games using ScummVM for Linux." 303 | msgstr "" 304 | 305 | #: src/models/runners/proton-cachyos.vala:14 306 | msgid "" 307 | "Steam compatibility tool from the CachyOS Linux distribution for running " 308 | "Windows games with improvements over Valve's default Proton." 309 | msgstr "" 310 | 311 | #: src/models/runners/proton-em.vala:6 312 | msgid "" 313 | "Steam compatibility tool for running Windows games with improvements over " 314 | "Valve's default Proton. By Etaash Mathamsetty adding FSR4 support and wine " 315 | "wayland tweaks." 316 | msgstr "" 317 | 318 | #: src/models/runners/proton-sarek.vala:6 319 | #: src/models/runners/proton-sarek-async.vala:6 320 | #, c-format 321 | msgid "" 322 | "Steam compatibility tool based on Proton-GE with modifications for very old " 323 | "GPUs, with %s." 324 | msgstr "" 325 | 326 | #: src/models/runners/steamtinkerlaunch.vala:6 327 | msgid "" 328 | "Steam tool for easy, graphical configuration of your other compatibility " 329 | "tools for both Windows games and native Linux games." 330 | msgstr "" 331 | 332 | #: src/models/releases/basic.vala:46 src/models/releases/github-action.vala:14 333 | #: src/models/releases/steamtinkerlaunch.vala:259 334 | msgid "Downloading..." 335 | msgstr "" 336 | 337 | #: src/models/releases/basic.vala:55 src/models/releases/github-action.vala:23 338 | #: src/models/releases/steamtinkerlaunch.vala:266 339 | msgid "Extracting..." 340 | msgstr "" 341 | 342 | #: src/models/releases/basic.vala:64 src/models/releases/github-action.vala:37 343 | msgid "Renaming..." 344 | msgstr "" 345 | 346 | #: src/models/releases/basic.vala:73 src/models/releases/github-action.vala:46 347 | msgid "Running post installation script..." 348 | msgstr "" 349 | 350 | #: src/models/releases/basic.vala:84 351 | msgid "Deleting..." 352 | msgstr "" 353 | 354 | #: src/models/releases/basic.vala:91 355 | msgid "Running post removal script..." 356 | msgstr "" 357 | 358 | #: src/models/releases/upgrade.vala:8 359 | #, c-format 360 | msgid "The upgrade of %s is complete." 361 | msgstr "" 362 | 363 | #: src/models/releases/upgrade.vala:8 364 | #, c-format 365 | msgid "An unexpected error occurred while upgrading %s." 366 | msgstr "" 367 | -------------------------------------------------------------------------------- /po/fi.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the com.vysp3r.ProtonPlus package. 4 | # Vysp3r , 2024. 5 | msgid "" 6 | msgstr "" 7 | "Project-Id-Version: com.vysp3r.ProtonPlus\n" 8 | "Report-Msgid-Bugs-To: \n" 9 | "POT-Creation-Date: 2025-05-25 18:12-0400\n" 10 | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" 11 | "Last-Translator: Automatically generated\n" 12 | "Language-Team: none\n" 13 | "Language: fi\n" 14 | "MIME-Version: 1.0\n" 15 | "Content-Type: text/plain; charset=UTF-8\n" 16 | "Content-Transfer-Encoding: 8bit\n" 17 | 18 | #: src/widgets/info-box.vala:16 19 | msgid "_Installed Only" 20 | msgstr "" 21 | 22 | #: src/widgets/info-box.vala:23 23 | msgid "_Keyboard Shortcuts" 24 | msgstr "" 25 | 26 | #: src/widgets/info-box.vala:24 27 | msgid "_About ProtonPlus" 28 | msgstr "" 29 | 30 | #: src/widgets/info-box.vala:31 31 | msgid "Main Menu" 32 | msgstr "" 33 | 34 | #: src/widgets/description-dialog.vala:14 35 | msgid "More information" 36 | msgstr "" 37 | 38 | #: src/widgets/description-dialog.vala:18 39 | msgid "Open" 40 | msgstr "" 41 | 42 | #: src/widgets/description-dialog.vala:22 43 | msgid "Open in your web browser" 44 | msgstr "" 45 | 46 | #: src/widgets/busy-dialog.vala:23 47 | msgid "Show/hide logs" 48 | msgstr "" 49 | 50 | #: src/widgets/busy-dialog.vala:120 51 | msgid "Closing in" 52 | msgstr "" 53 | 54 | #: src/widgets/release-row.vala:10 src/widgets/runner-group.vala:50 55 | #: src/widgets/runner-group.vala:62 src/widgets/release-rows/basic.vala:34 56 | #: src/widgets/release-rows/steamtinkerlaunch.vala:110 57 | #, c-format 58 | msgid "Delete %s" 59 | msgstr "" 60 | 61 | #: src/widgets/release-row.vala:14 62 | #, c-format 63 | msgid "Install %s" 64 | msgstr "" 65 | 66 | #: src/widgets/release-row.vala:18 67 | msgid "Show more information" 68 | msgstr "" 69 | 70 | #: src/widgets/runner-group.vala:62 src/widgets/release-rows/basic.vala:34 71 | #: src/widgets/release-rows/steamtinkerlaunch.vala:110 72 | #, c-format 73 | msgid "You're about to remove %s from your system." 74 | msgstr "" 75 | 76 | #: src/widgets/runner-group.vala:62 src/widgets/release-rows/basic.vala:34 77 | #: src/widgets/release-rows/steamtinkerlaunch.vala:110 78 | msgid "Are you sure you want this?" 79 | msgstr "" 80 | 81 | #: src/widgets/runner-group.vala:64 src/widgets/release-rows/basic.vala:36 82 | #: src/widgets/release-rows/steamtinkerlaunch.vala:114 83 | msgid "No" 84 | msgstr "" 85 | 86 | #: src/widgets/runner-group.vala:65 src/widgets/release-rows/basic.vala:37 87 | #: src/widgets/release-rows/steamtinkerlaunch.vala:115 88 | msgid "Yes" 89 | msgstr "" 90 | 91 | #: src/widgets/load-more-row.vala:8 src/widgets/load-more-row.vala:17 92 | msgid "Load more" 93 | msgstr "" 94 | 95 | #: src/widgets/window.vala:50 96 | #, c-format 97 | msgid "Welcome to %s" 98 | msgstr "" 99 | 100 | #: src/widgets/window.vala:50 101 | msgid "Install Steam, Lutris, Bottles or Heroic Games Launcher to get started." 102 | msgstr "" 103 | 104 | #: src/widgets/application.vala:78 105 | msgid "A modern compatibility tools manager for Linux." 106 | msgstr "" 107 | 108 | #: src/widgets/application.vala:84 109 | msgid "Special thanks to" 110 | msgstr "" 111 | 112 | #: src/widgets/release-rows/steamtinkerlaunch.vala:60 113 | msgid "Missing dependencies!" 114 | msgstr "" 115 | 116 | #: src/widgets/release-rows/steamtinkerlaunch.vala:60 117 | #, c-format 118 | msgid "You are missing the following dependencies for %s:" 119 | msgstr "" 120 | 121 | #: src/widgets/release-rows/steamtinkerlaunch.vala:60 122 | msgid "Installation will be canceled." 123 | msgstr "" 124 | 125 | #: src/widgets/release-rows/steamtinkerlaunch.vala:62 126 | #: src/widgets/release-rows/steamtinkerlaunch.vala:76 127 | #: src/widgets/release-rows/steamtinkerlaunch.vala:175 128 | msgid "OK" 129 | msgstr "" 130 | 131 | #: src/widgets/release-rows/steamtinkerlaunch.vala:73 132 | #, c-format 133 | msgid "Existing installation of %s" 134 | msgstr "" 135 | 136 | #: src/widgets/release-rows/steamtinkerlaunch.vala:73 137 | #, c-format 138 | msgid "" 139 | "It looks like you currently have another version of %s which was not " 140 | "installed by ProtonPlus." 141 | msgstr "" 142 | 143 | #: src/widgets/release-rows/steamtinkerlaunch.vala:73 144 | #, c-format 145 | msgid "Do you want to delete it and install %s with ProtonPlus?" 146 | msgstr "" 147 | 148 | #: src/widgets/release-rows/steamtinkerlaunch.vala:75 149 | msgid "Cancel" 150 | msgstr "" 151 | 152 | #: src/widgets/release-rows/steamtinkerlaunch.vala:108 153 | msgid "Check this to also remove your configuration files." 154 | msgstr "" 155 | 156 | #: src/widgets/release-rows/steamtinkerlaunch.vala:165 157 | #: src/widgets/release-rows/steamtinkerlaunch.vala:169 158 | #, c-format 159 | msgid "%s is not supported" 160 | msgstr "" 161 | 162 | #: src/widgets/release-rows/steamtinkerlaunch.vala:165 163 | #, c-format 164 | msgid "To install %s for the %s, please run the following command:" 165 | msgstr "" 166 | 167 | #: src/widgets/release-rows/steamtinkerlaunch.vala:169 168 | #, c-format 169 | msgid "There's currently no known way for us to install %s for the %s." 170 | msgstr "" 171 | 172 | #: src/widgets/release-rows/steamtinkerlaunch.vala:195 173 | #, c-format 174 | msgid "%s is up-to-date" 175 | msgstr "" 176 | 177 | #: src/widgets/release-rows/steamtinkerlaunch.vala:195 178 | #, c-format 179 | msgid "Update %s to the latest version" 180 | msgstr "" 181 | 182 | #: src/widgets/busy-dialogs/install-dialog.vala:6 183 | msgid "Installing" 184 | msgstr "" 185 | 186 | #: src/widgets/busy-dialogs/remove-dialog.vala:6 187 | msgid "Removing" 188 | msgstr "" 189 | 190 | #: src/widgets/busy-dialogs/upgrade-dialog.vala:6 191 | msgid "Upgrading" 192 | msgstr "" 193 | 194 | #: src/models/release.vala:31 195 | #, c-format 196 | msgid "The installation of %s has begun." 197 | msgstr "" 198 | 199 | #: src/models/release.vala:34 200 | #, c-format 201 | msgid "The removal of %s has begun." 202 | msgstr "" 203 | 204 | #: src/models/release.vala:37 205 | #, c-format 206 | msgid "The upgrade of %s has begun." 207 | msgstr "" 208 | 209 | #: src/models/release.vala:57 210 | #, c-format 211 | msgid "An unexpected error occurred while installing %s." 212 | msgstr "" 213 | 214 | #: src/models/release.vala:59 215 | #, c-format 216 | msgid "The installation of %s is complete." 217 | msgstr "" 218 | 219 | #: src/models/release.vala:79 220 | #, c-format 221 | msgid "The removal of %s is complete." 222 | msgstr "" 223 | 224 | #: src/models/release.vala:79 225 | #, c-format 226 | msgid "An unexpected error occurred while removing %s." 227 | msgstr "" 228 | 229 | #: src/models/launchers/bottles.vala:29 src/models/launchers/steam.vala:33 230 | msgid "Runners" 231 | msgstr "" 232 | 233 | #: src/models/launchers/bottles.vala:29 src/models/launchers/hgl.vala:32 234 | #: src/models/launchers/lutris.vala:36 235 | msgid "Compatibility tools for running Windows software on Linux." 236 | msgstr "" 237 | 238 | #: src/models/launchers/bottles.vala:32 src/models/launchers/lutris.vala:39 239 | msgid "Vulkan-based implementation of Direct3D 8, 9, 10 and 11 for Linux/Wine." 240 | msgstr "" 241 | 242 | #: src/models/launchers/hgl.vala:29 src/models/launchers/lutris.vala:33 243 | msgid "Compatibility tools by Valve for running Windows software on Linux." 244 | msgstr "" 245 | 246 | #: src/models/launchers/lutris.vala:42 247 | msgid "" 248 | "Variant of Wine's VKD3D which aims to implement the full Direct3D 12 API on " 249 | "top of Vulkan." 250 | msgstr "" 251 | 252 | #: src/models/runners/boxtron.vala:6 253 | msgid "Steam compatibility tool for running DOS games using DOSBox for Linux." 254 | msgstr "" 255 | 256 | #: src/models/runners/dxvk-sarek.vala:6 257 | #: src/models/runners/dxvk-async-sarek.vala:6 258 | msgid "DXVK Builds that work with pre-Vulkan 1.3 versions" 259 | msgstr "" 260 | 261 | #: src/models/runners/kron4ek-wine-builds-staging-tkg.vala:7 262 | msgid "Wine build with the Staging patchset and many other useful patches." 263 | msgstr "" 264 | 265 | #: src/models/runners/kron4ek-wine-builds-staging.vala:7 266 | msgid "Wine build with the Staging patchset." 267 | msgstr "" 268 | 269 | #: src/models/runners/kron4ek-wine-builds-vanilla.vala:7 270 | msgid "Wine build compiled from the official WineHQ sources." 271 | msgstr "" 272 | 273 | #: src/models/runners/luxtorpeda.vala:6 274 | msgid "" 275 | "Luxtorpeda provides Linux-native game engines for certain Windows-only games." 276 | msgstr "" 277 | 278 | #: src/models/runners/northstar-proton.vala:6 279 | msgid "Custom Proton build for running the Northstar client for Titanfall 2." 280 | msgstr "" 281 | 282 | #: src/models/runners/proton-ge-rstp.vala:6 283 | msgid "" 284 | "Steam compatibility tool based on Proton-GE with additional patches to " 285 | "improve RTSP codecs for VRChat." 286 | msgstr "" 287 | 288 | #: src/models/runners/proton-ge.vala:6 289 | msgid "" 290 | "Steam compatibility tool for running Windows games with improvements over " 291 | "Valve's default Proton." 292 | msgstr "" 293 | 294 | #: src/models/runners/proton-tkg.vala:6 295 | msgid "Custom Proton build for running Windows games, based on Wine-tkg." 296 | msgstr "" 297 | 298 | #: src/models/runners/roberta.vala:6 299 | msgid "" 300 | "Steam compatibility tool for running adventure games using ScummVM for Linux." 301 | msgstr "" 302 | 303 | #: src/models/runners/proton-cachyos.vala:14 304 | msgid "" 305 | "Steam compatibility tool from the CachyOS Linux distribution for running " 306 | "Windows games with improvements over Valve's default Proton." 307 | msgstr "" 308 | 309 | #: src/models/runners/proton-em.vala:6 310 | msgid "" 311 | "Steam compatibility tool for running Windows games with improvements over " 312 | "Valve's default Proton. By Etaash Mathamsetty adding FSR4 support and wine " 313 | "wayland tweaks." 314 | msgstr "" 315 | 316 | #: src/models/runners/proton-sarek.vala:6 317 | #: src/models/runners/proton-sarek-async.vala:6 318 | #, c-format 319 | msgid "" 320 | "Steam compatibility tool based on Proton-GE with modifications for very old " 321 | "GPUs, with %s." 322 | msgstr "" 323 | 324 | #: src/models/runners/steamtinkerlaunch.vala:6 325 | msgid "" 326 | "Steam tool for easy, graphical configuration of your other compatibility " 327 | "tools for both Windows games and native Linux games." 328 | msgstr "" 329 | 330 | #: src/models/releases/basic.vala:46 src/models/releases/github-action.vala:14 331 | #: src/models/releases/steamtinkerlaunch.vala:259 332 | msgid "Downloading..." 333 | msgstr "" 334 | 335 | #: src/models/releases/basic.vala:55 src/models/releases/github-action.vala:23 336 | #: src/models/releases/steamtinkerlaunch.vala:266 337 | msgid "Extracting..." 338 | msgstr "" 339 | 340 | #: src/models/releases/basic.vala:64 src/models/releases/github-action.vala:37 341 | msgid "Renaming..." 342 | msgstr "" 343 | 344 | #: src/models/releases/basic.vala:73 src/models/releases/github-action.vala:46 345 | msgid "Running post installation script..." 346 | msgstr "" 347 | 348 | #: src/models/releases/basic.vala:84 349 | msgid "Deleting..." 350 | msgstr "" 351 | 352 | #: src/models/releases/basic.vala:91 353 | msgid "Running post removal script..." 354 | msgstr "" 355 | 356 | #: src/models/releases/upgrade.vala:8 357 | #, c-format 358 | msgid "The upgrade of %s is complete." 359 | msgstr "" 360 | 361 | #: src/models/releases/upgrade.vala:8 362 | #, c-format 363 | msgid "An unexpected error occurred while upgrading %s." 364 | msgstr "" 365 | -------------------------------------------------------------------------------- /po/meson.build: -------------------------------------------------------------------------------- 1 | i18n.gettext( 2 | meson.project_name(), 3 | preset: 'glib' 4 | ) -------------------------------------------------------------------------------- /po/zh_TW.po: -------------------------------------------------------------------------------- 1 | # SOME DESCRIPTIVE TITLE. 2 | # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER 3 | # This file is distributed under the same license as the com.vysp3r.ProtonPlus package. 4 | # FIRST AUTHOR , YEAR. 5 | # 6 | #, fuzzy 7 | msgid "" 8 | msgstr "" 9 | "Project-Id-Version: com.vysp3r.ProtonPlus\n" 10 | "Report-Msgid-Bugs-To: \n" 11 | "POT-Creation-Date: 2025-05-25 18:12-0400\n" 12 | "PO-Revision-Date: 2025-05-25 01:30+0800\n" 13 | "Last-Translator: nick.exe \n" 14 | "Language-Team: CodeBay.IN\n" 15 | "Language: zh_TW\n" 16 | "MIME-Version: 1.0\n" 17 | "Content-Type: text/plain; charset=UTF-8\n" 18 | "Content-Transfer-Encoding: 8bit\n" 19 | "X-Generator: Poedit 3.6\n" 20 | 21 | #: src/widgets/info-box.vala:16 22 | msgid "_Installed Only" 23 | msgstr "僅列出已安裝的(_I)" 24 | 25 | #: src/widgets/info-box.vala:23 26 | msgid "_Keyboard Shortcuts" 27 | msgstr "鍵盤捷徑鍵(_K)" 28 | 29 | #: src/widgets/info-box.vala:24 30 | msgid "_About ProtonPlus" 31 | msgstr "關於 ProtonPlus(_A)" 32 | 33 | #: src/widgets/info-box.vala:31 34 | msgid "Main Menu" 35 | msgstr "主選單" 36 | 37 | #: src/widgets/description-dialog.vala:14 38 | msgid "More information" 39 | msgstr "更多資訊" 40 | 41 | #: src/widgets/description-dialog.vala:18 42 | msgid "Open" 43 | msgstr "開啟" 44 | 45 | #: src/widgets/description-dialog.vala:22 46 | msgid "Open in your web browser" 47 | msgstr "於瀏覽器中開啟" 48 | 49 | #: src/widgets/busy-dialog.vala:23 50 | msgid "Show/hide logs" 51 | msgstr "顯示/隱藏日誌" 52 | 53 | #: src/widgets/busy-dialog.vala:120 54 | msgid "Closing in" 55 | msgstr "即將關閉..." 56 | 57 | #: src/widgets/release-row.vala:10 src/widgets/runner-group.vala:50 58 | #: src/widgets/runner-group.vala:62 src/widgets/release-rows/basic.vala:34 59 | #: src/widgets/release-rows/steamtinkerlaunch.vala:110 60 | #, c-format 61 | msgid "Delete %s" 62 | msgstr "移除 %s" 63 | 64 | #: src/widgets/release-row.vala:14 65 | #, c-format 66 | msgid "Install %s" 67 | msgstr "安裝 %s" 68 | 69 | #: src/widgets/release-row.vala:18 70 | msgid "Show more information" 71 | msgstr "顯示更多資訊" 72 | 73 | #: src/widgets/runner-group.vala:62 src/widgets/release-rows/basic.vala:34 74 | #: src/widgets/release-rows/steamtinkerlaunch.vala:110 75 | #, c-format 76 | msgid "You're about to remove %s from your system." 77 | msgstr "您即將從系統中移除 %s。" 78 | 79 | #: src/widgets/runner-group.vala:62 src/widgets/release-rows/basic.vala:34 80 | #: src/widgets/release-rows/steamtinkerlaunch.vala:110 81 | msgid "Are you sure you want this?" 82 | msgstr "您確定要繼續嗎?" 83 | 84 | #: src/widgets/runner-group.vala:64 src/widgets/release-rows/basic.vala:36 85 | #: src/widgets/release-rows/steamtinkerlaunch.vala:114 86 | msgid "No" 87 | msgstr "否" 88 | 89 | #: src/widgets/runner-group.vala:65 src/widgets/release-rows/basic.vala:37 90 | #: src/widgets/release-rows/steamtinkerlaunch.vala:115 91 | msgid "Yes" 92 | msgstr "是" 93 | 94 | #: src/widgets/load-more-row.vala:8 src/widgets/load-more-row.vala:17 95 | msgid "Load more" 96 | msgstr "載入更多" 97 | 98 | #: src/widgets/window.vala:50 99 | #, c-format 100 | msgid "Welcome to %s" 101 | msgstr "歡迎來到 %s" 102 | 103 | #: src/widgets/window.vala:50 104 | msgid "Install Steam, Lutris, Bottles or Heroic Games Launcher to get started." 105 | msgstr "請安裝 Steam、Lutris、Bottles 或 Heroic Games Launcher 以開始使用。" 106 | 107 | #: src/widgets/application.vala:78 108 | msgid "A modern compatibility tools manager for Linux." 109 | msgstr "適用於 Linux 的現代相容性工具管理程式。" 110 | 111 | #: src/widgets/application.vala:84 112 | msgid "Special thanks to" 113 | msgstr "特別感謝" 114 | 115 | #: src/widgets/release-rows/steamtinkerlaunch.vala:60 116 | msgid "Missing dependencies!" 117 | msgstr "缺少相依性工具!" 118 | 119 | #: src/widgets/release-rows/steamtinkerlaunch.vala:60 120 | #, c-format 121 | msgid "You are missing the following dependencies for %s:" 122 | msgstr "您缺少下列 %s 的相依性工具:" 123 | 124 | #: src/widgets/release-rows/steamtinkerlaunch.vala:60 125 | msgid "Installation will be canceled." 126 | msgstr "安裝將被取消。" 127 | 128 | #: src/widgets/release-rows/steamtinkerlaunch.vala:62 129 | #: src/widgets/release-rows/steamtinkerlaunch.vala:76 130 | #: src/widgets/release-rows/steamtinkerlaunch.vala:175 131 | msgid "OK" 132 | msgstr "確定" 133 | 134 | #: src/widgets/release-rows/steamtinkerlaunch.vala:73 135 | #, c-format 136 | msgid "Existing installation of %s" 137 | msgstr "已存在 %s 的安裝" 138 | 139 | #: src/widgets/release-rows/steamtinkerlaunch.vala:73 140 | #, c-format 141 | msgid "" 142 | "It looks like you currently have another version of %s which was not " 143 | "installed by ProtonPlus." 144 | msgstr "看起來您目前有另一個版本的 %s,但不是透過 ProtonPlus 安裝的。" 145 | 146 | #: src/widgets/release-rows/steamtinkerlaunch.vala:73 147 | #, c-format 148 | msgid "Do you want to delete it and install %s with ProtonPlus?" 149 | msgstr "您要移除它並使用 ProtonPlus 安裝 %s 嗎?" 150 | 151 | #: src/widgets/release-rows/steamtinkerlaunch.vala:75 152 | msgid "Cancel" 153 | msgstr "取消" 154 | 155 | #: src/widgets/release-rows/steamtinkerlaunch.vala:108 156 | msgid "Check this to also remove your configuration files." 157 | msgstr "勾選此選項也會移除設定檔。" 158 | 159 | #: src/widgets/release-rows/steamtinkerlaunch.vala:165 160 | #: src/widgets/release-rows/steamtinkerlaunch.vala:169 161 | #, c-format 162 | msgid "%s is not supported" 163 | msgstr "不支援 %s" 164 | 165 | #: src/widgets/release-rows/steamtinkerlaunch.vala:165 166 | #, c-format 167 | msgid "To install %s for the %s, please run the following command:" 168 | msgstr "若要為 %2$s 安裝 %1$s,請運行以下命令:" 169 | 170 | #: src/widgets/release-rows/steamtinkerlaunch.vala:169 171 | #, c-format 172 | msgid "There's currently no known way for us to install %s for the %s." 173 | msgstr "目前尚無已知的方法來為 %2$s 安裝 %1$s。" 174 | 175 | #: src/widgets/release-rows/steamtinkerlaunch.vala:195 176 | #, c-format 177 | msgid "%s is up-to-date" 178 | msgstr "%s 已為最新版本" 179 | 180 | #: src/widgets/release-rows/steamtinkerlaunch.vala:195 181 | #, c-format 182 | msgid "Update %s to the latest version" 183 | msgstr "更新 %s 至最新版本" 184 | 185 | #: src/widgets/busy-dialogs/install-dialog.vala:6 186 | msgid "Installing" 187 | msgstr "正在安裝" 188 | 189 | #: src/widgets/busy-dialogs/remove-dialog.vala:6 190 | msgid "Removing" 191 | msgstr "正在移除" 192 | 193 | #: src/widgets/busy-dialogs/upgrade-dialog.vala:6 194 | msgid "Upgrading" 195 | msgstr "正在更新" 196 | 197 | #: src/models/release.vala:31 198 | #, c-format 199 | msgid "The installation of %s has begun." 200 | msgstr "%s 的安裝已開始。" 201 | 202 | #: src/models/release.vala:34 203 | #, c-format 204 | msgid "The removal of %s has begun." 205 | msgstr "%s 的移除已開始。" 206 | 207 | #: src/models/release.vala:37 208 | #, c-format 209 | msgid "The upgrade of %s has begun." 210 | msgstr "%s 的更新已開始。" 211 | 212 | #: src/models/release.vala:57 213 | #, c-format 214 | msgid "An unexpected error occurred while installing %s." 215 | msgstr "安裝 %s 時發生意外錯誤。" 216 | 217 | #: src/models/release.vala:59 218 | #, c-format 219 | msgid "The installation of %s is complete." 220 | msgstr "%s 的安裝已完成。" 221 | 222 | #: src/models/release.vala:79 223 | #, c-format 224 | msgid "The removal of %s is complete." 225 | msgstr "%s 的移除已完成。" 226 | 227 | #: src/models/release.vala:79 228 | #, c-format 229 | msgid "An unexpected error occurred while removing %s." 230 | msgstr "移除 %s 時發生意外錯誤。" 231 | 232 | #: src/models/launchers/bottles.vala:29 src/models/launchers/steam.vala:33 233 | msgid "Runners" 234 | msgstr "運行器" 235 | 236 | #: src/models/launchers/bottles.vala:29 src/models/launchers/hgl.vala:32 237 | #: src/models/launchers/lutris.vala:36 238 | msgid "Compatibility tools for running Windows software on Linux." 239 | msgstr "用於在 Linux 上運行 Windows 軟體的相容性工具。" 240 | 241 | #: src/models/launchers/bottles.vala:32 src/models/launchers/lutris.vala:39 242 | msgid "Vulkan-based implementation of Direct3D 8, 9, 10 and 11 for Linux/Wine." 243 | msgstr "適用於 Linux/Wine 的,基於 Vulkan 的 Direct3D 8、9、10 和 11 的實作。" 244 | 245 | #: src/models/launchers/hgl.vala:29 src/models/launchers/lutris.vala:33 246 | msgid "Compatibility tools by Valve for running Windows software on Linux." 247 | msgstr "由 Valve 提供的,用於在 Linux 上運行 Windows 軟體的相容性工具。" 248 | 249 | #: src/models/launchers/lutris.vala:42 250 | msgid "" 251 | "Variant of Wine's VKD3D which aims to implement the full Direct3D 12 API on " 252 | "top of Vulkan." 253 | msgstr "Wine VKD3D 的特化版,旨在 Vulkan 之上實作完整的 Direct3D 12 API。" 254 | 255 | #: src/models/runners/boxtron.vala:6 256 | msgid "Steam compatibility tool for running DOS games using DOSBox for Linux." 257 | msgstr "用於使用 Linux 版 DOSBox 運行 DOS 遊戲的 Steam 相容性工具。" 258 | 259 | #: src/models/runners/dxvk-sarek.vala:6 260 | #: src/models/runners/dxvk-async-sarek.vala:6 261 | msgid "DXVK Builds that work with pre-Vulkan 1.3 versions" 262 | msgstr "適用於 Vulkan 1.3 之前的 DXVK 建構版本。" 263 | 264 | #: src/models/runners/kron4ek-wine-builds-staging-tkg.vala:7 265 | msgid "Wine build with the Staging patchset and many other useful patches." 266 | msgstr "包含 Staging 修補程式集,以及多項實用修補程式的 Wine 建構版本。" 267 | 268 | #: src/models/runners/kron4ek-wine-builds-staging.vala:7 269 | msgid "Wine build with the Staging patchset." 270 | msgstr "包含 Staging 修補程式集的 Wine 建構版本。" 271 | 272 | #: src/models/runners/kron4ek-wine-builds-vanilla.vala:7 273 | msgid "Wine build compiled from the official WineHQ sources." 274 | msgstr "從官方 WineHQ 原始碼編譯的 Wine 建構版本。" 275 | 276 | #: src/models/runners/luxtorpeda.vala:6 277 | msgid "" 278 | "Luxtorpeda provides Linux-native game engines for certain Windows-only games." 279 | msgstr "Luxtorpeda 能為特定 Windows 專屬遊戲提供 Linux 原生遊戲引擎。" 280 | 281 | #: src/models/runners/northstar-proton.vala:6 282 | msgid "Custom Proton build for running the Northstar client for Titanfall 2." 283 | msgstr "用於運行 Titanfall 2 Northstar 用戶端的自訂 Proton 建構版本。" 284 | 285 | #: src/models/runners/proton-ge-rstp.vala:6 286 | msgid "" 287 | "Steam compatibility tool based on Proton-GE with additional patches to " 288 | "improve RTSP codecs for VRChat." 289 | msgstr "" 290 | "基於 Proton-GE 的 Steam 相容性工具,包含額外的修補程式以改善 VRChat 的 RTSP " 291 | "編解碼器。" 292 | 293 | #: src/models/runners/proton-ge.vala:6 294 | msgid "" 295 | "Steam compatibility tool for running Windows games with improvements over " 296 | "Valve's default Proton." 297 | msgstr "" 298 | "用於運行 Windows 遊戲的 Steam 相容性工具,並在 Valve 預設的 Proton 上做了改" 299 | "進。" 300 | 301 | #: src/models/runners/proton-tkg.vala:6 302 | msgid "Custom Proton build for running Windows games, based on Wine-tkg." 303 | msgstr "基於 Wine-tkg 的,用於運行 Windows 遊戲的自訂 Proton 建構版本。" 304 | 305 | #: src/models/runners/roberta.vala:6 306 | msgid "" 307 | "Steam compatibility tool for running adventure games using ScummVM for Linux." 308 | msgstr "用於使用 Linux 版 ScummVM 運行冒險遊戲的 Steam 相容性工具。" 309 | 310 | #: src/models/runners/proton-cachyos.vala:14 311 | msgid "" 312 | "Steam compatibility tool from the CachyOS Linux distribution for running " 313 | "Windows games with improvements over Valve's default Proton." 314 | msgstr "" 315 | "CachyOS Linux 發行版的 Steam 相容性工具,用於運行 Windows 遊戲,並在 Valve 預" 316 | "設的 Proton 上做了改進。" 317 | 318 | #: src/models/runners/proton-em.vala:6 319 | msgid "" 320 | "Steam compatibility tool for running Windows games with improvements over " 321 | "Valve's default Proton. By Etaash Mathamsetty adding FSR4 support and wine " 322 | "wayland tweaks." 323 | msgstr "" 324 | "用於運行 Windows 遊戲的 Steam 相容性工具,並在 Valve 預設的 Proton 上做了改" 325 | "進。由 Etaash Mathamsetty 新增 FSR4 支援和 Wine Wayland 調整。" 326 | 327 | #: src/models/runners/proton-sarek.vala:6 328 | #: src/models/runners/proton-sarek-async.vala:6 329 | #, c-format 330 | msgid "" 331 | "Steam compatibility tool based on Proton-GE with modifications for very old " 332 | "GPUs, with %s." 333 | msgstr "" 334 | "基於 Proton-GE 的 Steam 相容性工具,包含對非常舊的 GPU 進行的修改,並包含 " 335 | "%s。" 336 | 337 | #: src/models/runners/steamtinkerlaunch.vala:6 338 | msgid "" 339 | "Steam tool for easy, graphical configuration of your other compatibility " 340 | "tools for both Windows games and native Linux games." 341 | msgstr "" 342 | "讓您能以圖形介面輕鬆設定的 Steam 工具,專門調整用於 Windows 遊戲和原生 Linux " 343 | "遊戲的各種相容性工具。" 344 | 345 | #: src/models/releases/basic.vala:46 src/models/releases/github-action.vala:14 346 | #: src/models/releases/steamtinkerlaunch.vala:259 347 | msgid "Downloading..." 348 | msgstr "正在下載..." 349 | 350 | #: src/models/releases/basic.vala:55 src/models/releases/github-action.vala:23 351 | #: src/models/releases/steamtinkerlaunch.vala:266 352 | msgid "Extracting..." 353 | msgstr "正在解壓縮..." 354 | 355 | #: src/models/releases/basic.vala:64 src/models/releases/github-action.vala:37 356 | msgid "Renaming..." 357 | msgstr "正在重新命名..." 358 | 359 | #: src/models/releases/basic.vala:73 src/models/releases/github-action.vala:46 360 | msgid "Running post installation script..." 361 | msgstr "正在運行安裝後的腳本..." 362 | 363 | #: src/models/releases/basic.vala:84 364 | msgid "Deleting..." 365 | msgstr "正在移除..." 366 | 367 | #: src/models/releases/basic.vala:91 368 | msgid "Running post removal script..." 369 | msgstr "正在運行移除後的腳本..." 370 | 371 | #: src/models/releases/upgrade.vala:8 372 | #, c-format 373 | msgid "The upgrade of %s is complete." 374 | msgstr "%s 的更新已完成。" 375 | 376 | #: src/models/releases/upgrade.vala:8 377 | #, c-format 378 | msgid "An unexpected error occurred while upgrading %s." 379 | msgstr "更新 %s 時發生意外錯誤。" 380 | -------------------------------------------------------------------------------- /scripts/build-flathub.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -ex 4 | 5 | SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" 6 | cd "${SCRIPT_DIR}/.." 7 | 8 | BUILD_VARIANT="flathub" 9 | BUILD_MANIFEST="com.vysp3r.ProtonPlus.yml" 10 | BUILD_DIR="build-flatpak/${BUILD_VARIANT}/build" 11 | BUILD_OSTREE_REPO="build-flatpak/${BUILD_VARIANT}/repo" 12 | flatpak run org.flatpak.Builder --verbose \ 13 | --sandbox --force-clean --ccache --user --install \ 14 | "${BUILD_DIR}" \ 15 | "${BUILD_MANIFEST}" 16 | 17 | if [[ "$1" == "run" ]]; then 18 | flatpak run --user com.vysp3r.ProtonPlus 19 | fi 20 | -------------------------------------------------------------------------------- /scripts/build-local.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -ex 4 | 5 | SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" 6 | cd "${SCRIPT_DIR}/.." 7 | 8 | BUILD_VARIANT="local" 9 | BUILD_MANIFEST="com.vysp3r.ProtonPlus.local.yml" 10 | BUILD_DIR="build-flatpak/${BUILD_VARIANT}/build" 11 | BUILD_OSTREE_REPO="build-flatpak/${BUILD_VARIANT}/repo" 12 | flatpak run org.flatpak.Builder --verbose \ 13 | --sandbox --force-clean --ccache --user --install \ 14 | "${BUILD_DIR}" \ 15 | "${BUILD_MANIFEST}" 16 | 17 | if [[ "$1" == "run" ]]; then 18 | flatpak run --user com.vysp3r.ProtonPlus 19 | fi 20 | -------------------------------------------------------------------------------- /scripts/build-native.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" 6 | cd "${SCRIPT_DIR}/.." 7 | 8 | BUILD_DIR="build-native" 9 | meson "${BUILD_DIR}" --wipe --prefix=/usr 10 | cd "${BUILD_DIR}" 11 | ninja 12 | 13 | if [[ "$1" == "run" ]]; then 14 | cd src 15 | ./com.vysp3r.ProtonPlus 16 | fi 17 | -------------------------------------------------------------------------------- /scripts/flathub-linter.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -ex 4 | 5 | SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" 6 | cd "${SCRIPT_DIR}/.." 7 | 8 | # We must perform a Flatpak build *and* export to a ostree "repo" directory. 9 | # NOTE: We will perform a LOCAL build so that we check the LOCAL manifest. 10 | # NOTE: We don't trigger INSTALL in this case, since we're just linting. 11 | BUILD_VARIANT="local" 12 | BUILD_MANIFEST="com.vysp3r.ProtonPlus.local.yml" 13 | BUILD_DIR="build-flatpak/${BUILD_VARIANT}/build" 14 | BUILD_OSTREE_REPO="build-flatpak/${BUILD_VARIANT}/repo" 15 | flatpak run org.flatpak.Builder --verbose \ 16 | --sandbox --force-clean --ccache \ 17 | --repo="${BUILD_OSTREE_REPO}" \ 18 | "${BUILD_DIR}" \ 19 | "${BUILD_MANIFEST}" 20 | 21 | # Allow command failures after this point, since linters may exit with errors. 22 | set +e 23 | 24 | # Now we can run the Flathub linters. 25 | echo -e "\n\n\n\nLinter Results:\n" 26 | flatpak run --command=flatpak-builder-lint org.flatpak.Builder manifest "${BUILD_MANIFEST}" 27 | flatpak run --command=flatpak-builder-lint org.flatpak.Builder appstream "${BUILD_DIR}/export/share/metainfo/com.vysp3r.ProtonPlus.metainfo.xml" 28 | flatpak run --command=flatpak-builder-lint org.flatpak.Builder repo "${BUILD_OSTREE_REPO}" 29 | 30 | set +x 31 | echo -e "\nSome linter errors regarding external icons, screenshots or screenshot files may happen in a local build but not on Flathub. Those can be safely ignored." 32 | echo -e "\nAlways ignore the following linter errors:\n- \"appid-filename-mismatch: com.vysp3r.ProtonPlus.local\" (because we perform linting of the local development source code)\n- \"finish-args-flatpak-spawn-access\" (it's the only way for us to install Runners on the Host)" 33 | -------------------------------------------------------------------------------- /scripts/generate-icons.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" 6 | cd "${SCRIPT_DIR}/.." 7 | 8 | SVG_DIR="data/logo" 9 | EXPORT_DIR="data/logo/icons/hicolor" 10 | ICON_SIZES=(512 256 128 64 48 32 16) 11 | 12 | for svg_file in "${SVG_DIR}"/*.svg; do 13 | svg_name="$(basename "${svg_file}")" 14 | 15 | for size in "${ICON_SIZES[@]}"; do 16 | echo "${svg_name} @ ${size}" 17 | 18 | png_output_dir="${EXPORT_DIR}/${size}x${size}/apps" 19 | if [[ ! -d "${png_output_dir}" ]]; then 20 | mkdir -p "${png_output_dir}" 21 | fi 22 | 23 | png_file="${png_output_dir}/${svg_name%.*}.png" 24 | 25 | echo "-> ${png_file}" 26 | 27 | inkscape --export-type="png" \ 28 | --export-filename="${png_file}" \ 29 | --export-area-page \ 30 | --export-width="${size}" \ 31 | --export-height="${size}" \ 32 | "${svg_file}" 33 | 34 | optipng -o7 "${png_file}" 35 | done 36 | done 37 | -------------------------------------------------------------------------------- /scripts/rebuild-translations.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -e 4 | 5 | SCRIPT_DIR="$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )" 6 | cd "${SCRIPT_DIR}/.." 7 | 8 | # We must first perform a native build. 9 | BUILD_DIR="build-native" 10 | meson "${BUILD_DIR}" --wipe --prefix=/usr 11 | cd "${BUILD_DIR}" 12 | ninja 13 | 14 | # Now we can update the translations. 15 | ninja com.vysp3r.ProtonPlus-update-po 16 | -------------------------------------------------------------------------------- /scripts/set-version.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | from pathlib import Path 4 | import re 5 | import sys 6 | 7 | 8 | SCRIPT_DIR = Path(__file__).parent.absolute() 9 | 10 | 11 | def die(msg: str) -> None: 12 | print(msg) 13 | exit(1) 14 | 15 | 16 | def file_rgx_replace( 17 | rgx: str, replacement: str, file: Path, max_count: int = 1, debug: bool = False 18 | ) -> None: 19 | print(f'Patching "{file.name}"...') 20 | text = file.read_text(encoding="utf-8") 21 | text = re.sub(rgx, replacement, text, max_count) 22 | if debug: 23 | print(text) 24 | else: 25 | file.write_text(text, encoding="utf-8") 26 | 27 | 28 | new_version: str | None = None 29 | if len(sys.argv) < 2: 30 | die("Usage: set-version.py \nExample: set-version.py 0.5.23") 31 | 32 | new_version = sys.argv[1] 33 | if re.search(r"^[0-9.\-]+$", new_version) is None: 34 | die(f'Invalid version specifier: "{new_version}"') 35 | 36 | file_rgx_replace( # Application version constant. 37 | r"(\bversion: ')[0-9.\-]+", 38 | rf"version: '{new_version}", 39 | SCRIPT_DIR / "../meson.build", 40 | ) 41 | 42 | file_rgx_replace( # Flathub manifest. 43 | r"(\burl: .+?\/ProtonPlus\.git[\s]*\btag:) v[0-9.\-]+", 44 | rf"\1 v{new_version}", 45 | SCRIPT_DIR / "../com.vysp3r.ProtonPlus.yml", 46 | ) 47 | 48 | print( 49 | "Remember to perform the following actions manually:\n" 50 | f'- Add the version number ("{new_version}") and release notes to "data/com.vysp3r.ProtonPlus.metainfo.xml.in".\n' 51 | f'- Then create and publish a new Git tag: "v{new_version}"' 52 | ) 53 | -------------------------------------------------------------------------------- /src/config.vapi: -------------------------------------------------------------------------------- 1 | [CCode (cprefix = "", lower_case_cprefix = "", cheader_filename = "config.h")] 2 | namespace ProtonPlus.Config { 3 | public const string APP_NAME; 4 | public const string APP_ID; 5 | public const string APP_VERSION; 6 | public const string LOCALE_DIR; 7 | public const string RESOURCE_BASE; 8 | } -------------------------------------------------------------------------------- /src/main.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus { 2 | public static int main (string[] args) { 3 | if (!Thread.supported ()) { 4 | message ("Threads are not supported!"); 5 | return -1; 6 | } 7 | 8 | Intl.bindtextdomain (Config.APP_ID, Config.LOCALE_DIR); 9 | Intl.bind_textdomain_codeset (Config.APP_ID, "UTF-8"); 10 | Intl.textdomain (Config.APP_ID); 11 | 12 | var application = new Widgets.Application (); 13 | return application.run (args); 14 | } 15 | } -------------------------------------------------------------------------------- /src/meson.build: -------------------------------------------------------------------------------- 1 | protonplus_sources = files( 2 | 'main.vala', 3 | 4 | 'widgets/application.vala', 5 | 'widgets/window.vala', 6 | 'widgets/info-box.vala', 7 | 'widgets/status-box.vala', 8 | 'widgets/sidebar.vala', 9 | 'widgets/sidebar-row.vala', 10 | 'widgets/release-row.vala', 11 | 'widgets/busy-dialog.vala', 12 | 'widgets/load-more-row.vala', 13 | 'widgets/launcher-box.vala', 14 | 'widgets/runner-group.vala', 15 | 'widgets/runner-row.vala', 16 | 'widgets/description-dialog.vala', 17 | 18 | 'widgets/release-rows/steamtinkerlaunch.vala', 19 | 'widgets/release-rows/basic.vala', 20 | 21 | 'widgets/busy-dialogs/install-dialog.vala', 22 | 'widgets/busy-dialogs/upgrade-dialog.vala', 23 | 'widgets/busy-dialogs/remove-dialog.vala', 24 | 25 | 'models/group.vala', 26 | 'models/launcher.vala', 27 | 'models/release.vala', 28 | 'models/runner.vala', 29 | 'models/parameters.vala', 30 | 31 | 'models/launchers/bottles.vala', 32 | 'models/launchers/hgl.vala', 33 | 'models/launchers/lutris.vala', 34 | 'models/launchers/steam.vala', 35 | 36 | 'models/runners/basic.vala', 37 | 'models/runners/github.vala', 38 | 'models/runners/github-action.vala', 39 | 'models/runners/gitlab.vala', 40 | 'models/runners/proton-ge.vala', 41 | 'models/runners/luxtorpeda.vala', 42 | 'models/runners/boxtron.vala', 43 | 'models/runners/roberta.vala', 44 | 'models/runners/northstar-proton.vala', 45 | 'models/runners/proton-ge-rstp.vala', 46 | 'models/runners/proton-tkg.vala', 47 | 'models/runners/proton-cachyos.vala', 48 | 'models/runners/proton-em.vala', 49 | 'models/runners/proton-sarek.vala', 50 | 'models/runners/proton-sarek-async.vala', 51 | 'models/runners/steamtinkerlaunch.vala', 52 | 'models/runners/kron4ek-wine-builds-staging-tkg.vala', 53 | 'models/runners/kron4ek-wine-builds-staging.vala', 54 | 'models/runners/kron4ek-wine-builds-vanilla.vala', 55 | 'models/runners/other.vala', 56 | 'models/runners/dxvk-async-sarek.vala', 57 | 'models/runners/dxvk-gpl-async-ph42on.vala', 58 | 'models/runners/dxvk-sarek.vala', 59 | 'models/runners/dxvk-doitsujin.vala', 60 | 'models/runners/vkd3d-lutris.vala', 61 | 'models/runners/vkd3d-proton.vala', 62 | 63 | 'models/releases/basic.vala', 64 | 'models/releases/github-action.vala', 65 | 'models/releases/upgrade.vala', 66 | 'models/releases/steamtinkerlaunch.vala', 67 | 68 | 'utils/filesystem.vala', 69 | 'utils/web.vala', 70 | 'utils/parser.vala', 71 | 'utils/system.vala', 72 | ) 73 | 74 | protonplus_deps = [ 75 | config_dep, 76 | dependency('gtk4'), 77 | dependency('glib-2.0'), 78 | dependency('libadwaita-1', version: '>= 1.5'), 79 | dependency('json-glib-1.0'), 80 | dependency('libsoup-3.0'), 81 | dependency('gee-0.8'), 82 | dependency('libarchive') 83 | ] 84 | 85 | executable( 86 | meson.project_name().split('.')[2].to_lower(), 87 | ui_gresource, 88 | css_gresource, 89 | icons_gresource, 90 | images_gresource, 91 | protonplus_sources, 92 | dependencies: protonplus_deps, 93 | include_directories: config_inc, 94 | install: true 95 | ) 96 | -------------------------------------------------------------------------------- /src/models/group.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models { 2 | public class Group : Object { 3 | public string title { get; set; } 4 | public string description { get; set; } 5 | public string directory { get; set; } 6 | public Launcher launcher { get; set; } 7 | 8 | public List runners; 9 | 10 | public Group (string title, string description, string directory, Launcher launcher) { 11 | this.title = title; 12 | this.description = description; 13 | this.directory = directory; 14 | this.launcher = launcher; 15 | 16 | if (!FileUtils.test (launcher.directory + directory, FileTest.IS_DIR)) { 17 | Utils.Filesystem.create_directory.begin (launcher.directory + directory, (obj, res) => { 18 | Utils.Filesystem.create_directory.end (res); 19 | }); 20 | } 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/models/launcher.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models { 2 | public class Launcher : Object { 3 | public string title; 4 | public string icon_path; 5 | public string directory; 6 | public bool installed; 7 | 8 | public Group[] groups; 9 | 10 | public InstallationTypes installation_type; 11 | public enum InstallationTypes { 12 | SYSTEM, 13 | FLATPAK, 14 | SNAP 15 | } 16 | 17 | public delegate bool callback (Release release); 18 | public signal bool install (Release release); 19 | public signal bool uninstall (Release release); 20 | 21 | public Launcher (string title, InstallationTypes installation_type, string icon_path, string[] directories) { 22 | this.title = title; 23 | this.installation_type = installation_type; 24 | this.icon_path = icon_path; 25 | this.directory = ""; 26 | 27 | foreach (var directory in directories) { 28 | var current_path = Environment.get_home_dir () + directory; 29 | if (FileUtils.test (current_path, FileTest.IS_DIR)) { 30 | if (title == "Steam") { 31 | if (FileUtils.test (current_path + "/steamclient.dll", FileTest.IS_REGULAR) && FileUtils.test (current_path + "/steamclient64.dll", FileTest.IS_REGULAR)) { 32 | this.directory = current_path; 33 | break; 34 | } 35 | } else { 36 | this.directory = current_path; 37 | break; 38 | } 39 | } 40 | } 41 | 42 | installed = directory.length > 0; 43 | } 44 | 45 | public string get_installation_type_title () { 46 | switch (installation_type) { 47 | case InstallationTypes.SYSTEM: 48 | return "System"; 49 | case InstallationTypes.FLATPAK: 50 | return "Flatpak"; 51 | case InstallationTypes.SNAP: 52 | return "Snap"; 53 | default: 54 | return "Invalid type"; 55 | } 56 | } 57 | 58 | public static List get_all () { 59 | var launchers = new List (); 60 | 61 | launchers.append (new Launchers.Steam (InstallationTypes.SYSTEM)); 62 | launchers.append (new Launchers.Steam (InstallationTypes.FLATPAK)); 63 | launchers.append (new Launchers.Steam (InstallationTypes.SNAP)); 64 | 65 | launchers.append (new Launchers.Lutris (InstallationTypes.SYSTEM)); 66 | launchers.append (new Launchers.Lutris (InstallationTypes.FLATPAK)); 67 | 68 | launchers.append (new Launchers.Bottles (InstallationTypes.SYSTEM)); 69 | launchers.append (new Launchers.Bottles (InstallationTypes.FLATPAK)); 70 | 71 | launchers.append (new Launchers.HeroicGamesLauncher (InstallationTypes.SYSTEM)); 72 | launchers.append (new Launchers.HeroicGamesLauncher (InstallationTypes.FLATPAK)); 73 | 74 | launchers.foreach ((launcher) => { 75 | if (!launcher.installed)launchers.remove (launcher); 76 | }); 77 | 78 | return (owned) launchers; 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /src/models/launchers/bottles.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models.Launchers { 2 | public class Bottles : Launcher { 3 | public Bottles (Launcher.InstallationTypes installation_type) { 4 | string[] directories = null; 5 | 6 | switch (installation_type) { 7 | case Launcher.InstallationTypes.SYSTEM: 8 | directories = new string[] { "/.local/share/bottles" }; 9 | break; 10 | case Launcher.InstallationTypes.FLATPAK: 11 | directories = new string[] { "/.var/app/com.usebottles.bottles/data/bottles" }; 12 | break; 13 | case Launcher.InstallationTypes.SNAP: 14 | break; 15 | } 16 | 17 | base ("Bottles", installation_type, Config.RESOURCE_BASE + "/bottles.png", directories); 18 | 19 | if (installed) { 20 | groups = get_groups (); 21 | install.connect ((release) => true); 22 | uninstall.connect ((release) => true); 23 | } 24 | } 25 | 26 | Group[] get_groups () { 27 | var groups = new Group[2]; 28 | 29 | groups[0] = new Group (_("Runners"), _("Compatibility tools for running Windows software on Linux."), "/runners", this); 30 | groups[0].runners = get_runners (groups[0]); 31 | 32 | groups[1] = new Group ("DXVK", _("Vulkan-based implementation of Direct3D 8, 9, 10 and 11 for Linux/Wine."), "/dxvk", this); 33 | groups[1].runners = get_dxvk_runners (groups[1]); 34 | 35 | return groups; 36 | } 37 | 38 | List get_runners (Group group) { 39 | var runners = new List (); 40 | 41 | runners.append (new Runners.Proton_GE (group)); 42 | runners.append (new Runners.Proton_CachyOS (group)); 43 | runners.append (new Runners.Proton_EM (group)); 44 | runners.append (new Runners.Other (group)); 45 | 46 | return runners; 47 | } 48 | 49 | List get_dxvk_runners (Group group) { 50 | var runners = new List (); 51 | 52 | runners.append (new Runners.DXVK_doitsujin (group)); 53 | 54 | return runners; 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /src/models/launchers/hgl.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models.Launchers { 2 | public class HeroicGamesLauncher : Launcher { 3 | public HeroicGamesLauncher (Launcher.InstallationTypes installation_type) { 4 | string[] directories = null; 5 | 6 | switch (installation_type) { 7 | case Launcher.InstallationTypes.SYSTEM: 8 | directories = new string[] { "/.config/heroic" }; 9 | break; 10 | case Launcher.InstallationTypes.FLATPAK: 11 | directories = new string[] { "/.var/app/com.heroicgameslauncher.hgl/config/heroic" }; 12 | break; 13 | case Launcher.InstallationTypes.SNAP: 14 | break; 15 | } 16 | 17 | base ("Heroic Games Launcher", installation_type, Config.RESOURCE_BASE + "/hgl.png", directories); 18 | 19 | if (installed) { 20 | groups = get_groups (); 21 | install.connect ((release) => install_script (release)); 22 | uninstall.connect ((release) => uninstall_script (release)); 23 | } 24 | } 25 | 26 | Group[] get_groups () { 27 | var groups = new Group[2]; 28 | 29 | groups[0] = new Group ("Proton", _("Compatibility tools by Valve for running Windows software on Linux."), "/tools/proton", this); 30 | groups[0].runners = get_proton_runners (groups[0]); 31 | 32 | groups[1] = new Group ("Wine", _("Compatibility tools for running Windows software on Linux."), "/tools/wine", this); 33 | groups[1].runners = get_wine_runners (groups[1]); 34 | 35 | return groups; 36 | } 37 | 38 | List get_wine_runners (Group group) { 39 | var runners = new List (); 40 | 41 | runners.append (new Runners.Wine_Vanilla_Kron4ek (group)); 42 | runners.append (new Runners.Wine_Staging_Kron4ek (group)); 43 | runners.append (new Runners.Wine_Staging_Tkg_Kron4ek (group)); 44 | 45 | return runners; 46 | } 47 | 48 | List get_proton_runners (Group group) { 49 | var runners = new List (); 50 | 51 | runners.append (new Runners.Proton_GE (group)); 52 | runners.append (new Runners.Proton_CachyOS (group)); 53 | runners.append (new Runners.Proton_EM (group)); 54 | runners.append (new Runners.Proton_Tkg (group)); 55 | 56 | return runners; 57 | } 58 | 59 | bool install_script (Release release) { 60 | try { 61 | string path = "/.config"; 62 | if (release.runner.group.launcher.installation_type == Launcher.InstallationTypes.FLATPAK)path = "/.var/app/com.heroicgameslauncher.hgl/config"; 63 | path = Environment.get_home_dir () + path + "/heroic/store/wine-downloader-info.json"; 64 | 65 | Json.Node root_node = Json.from_string (Utils.Filesystem.get_file_content (path)); 66 | Json.Object root_object = root_node.get_object (); 67 | 68 | var wine_release_array = root_object.get_array_member ("wine-releases"); 69 | if (wine_release_array == null) 70 | return false; 71 | 72 | bool found = false; 73 | 74 | var runner = release.runner as Runners.Basic; 75 | 76 | var installDirBase = release.runner.group.launcher.directory + release.runner.group.directory + "/"; 77 | 78 | for (var i = 0; i < wine_release_array.get_length (); i++) { 79 | var obj = wine_release_array.get_object_element (i); 80 | 81 | if (obj.get_string_member ("version").contains (runner.get_directory_name (release.title))) { 82 | obj.set_boolean_member ("isInstalled", true); 83 | obj.set_boolean_member ("hasUpdate", false); 84 | obj.set_string_member ("installDir", installDirBase + obj.get_string_member ("version")); 85 | 86 | obj.set_int_member ("disksize", (int64) Utils.Filesystem.get_directory_size (obj.get_string_member ("installDir"))); 87 | 88 | found = true; 89 | } 90 | } 91 | 92 | if (!found) { 93 | var obj = new Json.Object (); 94 | 95 | obj.set_string_member ("version", runner.get_directory_name (release.title)); 96 | obj.set_string_member ("type", release.runner.title); 97 | obj.set_string_member ("date", ""); 98 | obj.set_string_member ("checksum", ""); 99 | obj.set_string_member ("download", ""); 100 | obj.set_int_member ("downsize", 0); 101 | 102 | obj.set_boolean_member ("isInstalled", true); 103 | obj.set_boolean_member ("hasUpdate", false); 104 | obj.set_string_member ("installDir", installDirBase + obj.get_string_member ("version")); 105 | 106 | obj.set_int_member ("disksize", (int64) Utils.Filesystem.get_directory_size (obj.get_string_member ("installDir"))); 107 | 108 | wine_release_array.add_object_element (obj); 109 | } 110 | 111 | Utils.Filesystem.modify_file (path, Json.to_string (root_node, true)); 112 | 113 | return true; 114 | } catch (Error e) { 115 | message (e.message); 116 | 117 | return false; 118 | } 119 | } 120 | 121 | bool uninstall_script (Release release) { 122 | try { 123 | string path = "/.config"; 124 | if (release.runner.group.launcher.installation_type == Launcher.InstallationTypes.FLATPAK)path = "/.var/app/com.heroicgameslauncher.hgl/config"; 125 | path = Environment.get_home_dir () + path + "/heroic/store/wine-downloader-info.json"; 126 | 127 | Json.Node root_node = Json.from_string (Utils.Filesystem.get_file_content (path)); 128 | Json.Object root_object = root_node.get_object (); 129 | 130 | var wine_release_array = root_object.get_array_member ("wine-releases"); 131 | if (wine_release_array == null) 132 | return false; 133 | 134 | for (var i = 0; i < wine_release_array.get_length (); i++) { 135 | var obj = wine_release_array.get_object_element (i); 136 | 137 | var runner = release.runner as Runners.Basic; 138 | 139 | if (obj.get_string_member ("version").contains (runner.get_directory_name (release.title))) { 140 | obj.remove_member ("isInstalled"); 141 | obj.remove_member ("hasUpdate"); 142 | obj.remove_member ("installDir"); 143 | obj.set_int_member ("disksize", 0); 144 | } 145 | } 146 | 147 | Utils.Filesystem.modify_file (path, Json.to_string (root_node, true)); 148 | 149 | return true; 150 | } catch (Error e) { 151 | message (e.message); 152 | 153 | return false; 154 | } 155 | } 156 | } 157 | } -------------------------------------------------------------------------------- /src/models/launchers/lutris.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models.Launchers { 2 | public class Lutris : Launcher { 3 | public Lutris (Launcher.InstallationTypes installation_type) { 4 | string[] directories = null; 5 | 6 | switch (installation_type) { 7 | case Launcher.InstallationTypes.SYSTEM: 8 | directories = new string[] { "/.local/share/lutris" }; 9 | break; 10 | case Launcher.InstallationTypes.FLATPAK: 11 | directories = new string[] { "/.var/app/net.lutris.Lutris/data/lutris" }; 12 | break; 13 | case Launcher.InstallationTypes.SNAP: 14 | break; 15 | } 16 | 17 | base ("Lutris", installation_type, Config.RESOURCE_BASE + "/lutris.png", directories); 18 | 19 | if (installed) { 20 | groups = get_groups (); 21 | install.connect ((release) => true); 22 | uninstall.connect ((release) => true); 23 | 24 | Utils.Filesystem.create_directory.begin (directory + "/runners/proton", (obj, res) => { 25 | Utils.Filesystem.create_directory.end (res); 26 | }); 27 | } 28 | } 29 | 30 | Group[] get_groups () { 31 | var groups = new Group[4]; 32 | 33 | groups[0] = new Group ("Proton", _("Compatibility tools by Valve for running Windows software on Linux."), "/runners/proton", this); 34 | groups[0].runners = get_proton_runners (groups[0]); 35 | 36 | groups[1] = new Group ("Wine", _("Compatibility tools for running Windows software on Linux."), "/runners/wine", this); 37 | groups[1].runners = get_wine_runners (groups[1]); 38 | 39 | groups[2] = new Group ("DXVK", _("Vulkan-based implementation of Direct3D 8, 9, 10 and 11 for Linux/Wine."), "/runtime/dxvk", this); 40 | groups[2].runners = get_dxvk_runners (groups[2]); 41 | 42 | groups[3] = new Group ("VKD3D", _("Variant of Wine's VKD3D which aims to implement the full Direct3D 12 API on top of Vulkan."), "/runtime/vkd3d", this); 43 | groups[3].runners = get_vkd3d_runners (groups[3]); 44 | 45 | return groups; 46 | } 47 | 48 | List get_proton_runners (Group group) { 49 | var runners = new List (); 50 | 51 | runners.append (new Runners.Proton_GE (group)); 52 | runners.append (new Runners.Proton_CachyOS (group)); 53 | runners.append (new Runners.Proton_EM (group)); 54 | runners.append (new Runners.Proton_Sarek (group)); 55 | runners.append (new Runners.Proton_Sarek_Async (group)); 56 | runners.append (new Runners.Proton_Tkg (group)); 57 | 58 | return runners; 59 | } 60 | 61 | List get_wine_runners (Group group) { 62 | var runners = new List (); 63 | 64 | runners.append (new Runners.Wine_Vanilla_Kron4ek (group)); 65 | runners.append (new Runners.Wine_Staging_Kron4ek (group)); 66 | runners.append (new Runners.Wine_Staging_Tkg_Kron4ek (group)); 67 | 68 | return runners; 69 | } 70 | 71 | List get_dxvk_runners (Group group) { 72 | var runners = new List (); 73 | 74 | runners.append (new Runners.DXVK_doitsujin (group)); 75 | runners.append (new Runners.DXVK_GPL_Async_Ph42oN (group)); 76 | runners.append (new Runners.DXVK_Sarek (group)); 77 | runners.append (new Runners.DXVK_Async_Sarek (group)); 78 | 79 | return runners; 80 | } 81 | 82 | List get_vkd3d_runners (Group group) { 83 | var runners = new List (); 84 | 85 | runners.append (new Runners.VKD3D_Lutris (group)); 86 | runners.append (new Runners.VKD3D_Proton (group)); 87 | 88 | return runners; 89 | } 90 | } 91 | } -------------------------------------------------------------------------------- /src/models/launchers/steam.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models.Launchers { 2 | public class Steam : Launcher { 3 | public Steam (Launcher.InstallationTypes installation_type) { 4 | string[] directories = null; 5 | 6 | switch (installation_type) { 7 | case Launcher.InstallationTypes.SYSTEM: 8 | directories = new string[] { "/.local/share/Steam", 9 | "/.steam/root", 10 | "/.steam/steam", 11 | "/.steam/debian-installation" }; 12 | break; 13 | case Launcher.InstallationTypes.FLATPAK: 14 | directories = new string[] { "/.var/app/com.valvesoftware.Steam/data/Steam" }; 15 | break; 16 | case Launcher.InstallationTypes.SNAP: 17 | directories = new string[] { "/snap/steam/common/.steam/root" }; 18 | break; 19 | } 20 | 21 | base ("Steam", installation_type, Config.RESOURCE_BASE + "/steam.png", directories); 22 | 23 | if (installed) { 24 | groups = get_groups (); 25 | install.connect ((release) => true); 26 | uninstall.connect ((release) => true); 27 | } 28 | } 29 | 30 | Group[] get_groups () { 31 | var groups = new Group[1]; 32 | 33 | groups[0] = new Group (_("Runners"), "", "/compatibilitytools.d", this); 34 | groups[0].runners = get_runners (groups[0]); 35 | 36 | return groups; 37 | } 38 | 39 | public static List get_runners (Group group) { 40 | var runners = new List (); 41 | 42 | runners.append (new Runners.Proton_GE (group)); 43 | runners.append (new Runners.Proton_CachyOS (group)); 44 | runners.append (new Runners.Proton_EM (group)); 45 | runners.append (new Runners.Proton_Sarek (group)); 46 | runners.append (new Runners.Proton_Sarek_Async (group)); 47 | runners.append (new Runners.Proton_GE_RSTP (group)); 48 | runners.append (new Runners.Proton_Tkg (group)); 49 | runners.append (new Runners.Northstar_Proton (group)); 50 | runners.append (new Runners.Luxtorpeda (group)); 51 | runners.append (new Runners.Boxtron (group)); 52 | runners.append (new Runners.Roberta (group)); 53 | runners.append (new Runners.SteamTinkerLaunch (group)); 54 | 55 | return runners; 56 | } 57 | } 58 | } -------------------------------------------------------------------------------- /src/models/parameters.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models { 2 | public class Parameters : Object { 3 | 4 | } 5 | } -------------------------------------------------------------------------------- /src/models/release.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models { 2 | public abstract class Release: Object { 3 | public Runner runner { get; set; } 4 | public string title { get; set; } 5 | public string displayed_title { get; set; } 6 | public string description { get; set; } 7 | public string release_date { get; set; } 8 | public string download_url { get; set; } 9 | public string page_url { get; set; } 10 | public bool canceled { get; set; } 11 | public string progress { get; set; } 12 | public signal void send_message (string message); 13 | 14 | public State state { get; set; } 15 | public enum State { 16 | NOT_INSTALLED, 17 | UPDATE_AVAILABLE, 18 | UP_TO_DATE, 19 | BUSY_INSTALLING, 20 | BUSY_REMOVING, 21 | BUSY_UPGRADING, 22 | } 23 | 24 | construct { 25 | notify["state"].connect (state_changed); 26 | } 27 | 28 | void state_changed () { 29 | switch (state) { 30 | case State.BUSY_INSTALLING: 31 | send_message (_("The installation of %s has begun.").printf (title)); 32 | break; 33 | case State.BUSY_REMOVING: 34 | send_message (_("The removal of %s has begun.").printf (title)); 35 | break; 36 | case State.BUSY_UPGRADING: 37 | send_message (_("The upgrade of %s has begun.").printf (title)); 38 | break; 39 | default: 40 | break; 41 | } 42 | } 43 | 44 | public async bool install () { 45 | var busy_upgrading = state == State.BUSY_UPGRADING; 46 | 47 | if (!busy_upgrading) 48 | state = State.BUSY_INSTALLING; 49 | 50 | // Attempt the installation. 51 | var install_success = yield _start_install (); 52 | 53 | if (!install_success) { 54 | yield remove (new Models.Parameters ()); // Refreshes install state too. 55 | 56 | if (!busy_upgrading) 57 | send_message (_("An unexpected error occurred while installing %s.").printf (title)); 58 | } else if (!busy_upgrading) 59 | send_message (_("The installation of %s is complete.").printf (title)); 60 | 61 | if (!busy_upgrading) 62 | refresh_state (); // Force UI state refresh. 63 | 64 | return install_success; 65 | } 66 | 67 | protected abstract async bool _start_install (); 68 | 69 | public async bool remove (R parameters) { 70 | var busy_upgrading_or_instaling = state == State.BUSY_UPGRADING || state == State.BUSY_INSTALLING; 71 | 72 | if (!busy_upgrading_or_instaling) 73 | state = State.BUSY_REMOVING; 74 | 75 | // Attempt the removal. 76 | var remove_success = yield _start_remove (parameters); 77 | 78 | if (!busy_upgrading_or_instaling) { 79 | send_message ((remove_success ? _("The removal of %s is complete.") : _("An unexpected error occurred while removing %s.")).printf (title)); 80 | refresh_state (); // Force UI state refresh. 81 | } 82 | 83 | return remove_success; 84 | } 85 | 86 | protected abstract async bool _start_remove (R parameters); 87 | 88 | protected abstract void refresh_state (); 89 | } 90 | } -------------------------------------------------------------------------------- /src/models/releases/basic.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models.Releases { 2 | public class Basic : Release { 3 | public string install_location { get; set; } 4 | public int64 download_size { get; set; } 5 | 6 | public Basic.simple (Runners.Basic runner, string title, string install_location) { 7 | this.runner = runner; 8 | this.title = title; 9 | this.install_location = install_location; 10 | } 11 | 12 | public Basic.github (Runners.Basic runner, string title, string description, string release_date, int64 download_size, string download_url, string page_url) { 13 | this.description = description; 14 | this.download_size = download_size; 15 | 16 | shared (runner, title, release_date, download_url, page_url); 17 | } 18 | 19 | public Basic.gitlab (Runners.Basic runner, string title, string description, string release_date, string download_url, string page_url) { 20 | this.description = description; 21 | 22 | shared (runner, title, release_date, download_url, page_url); 23 | } 24 | 25 | internal void shared (Runners.Basic runner, string title, string release_date, string download_url, string page_url) { 26 | this.runner = runner; 27 | this.title = title; 28 | this.displayed_title = title; 29 | this.description = description; 30 | this.release_date = release_date; 31 | this.download_url = download_url; 32 | this.page_url = page_url; 33 | 34 | install_location = runner.group.launcher.directory + runner.group.directory + "/" + runner.get_directory_name (title); 35 | 36 | refresh_state (); 37 | } 38 | 39 | protected override void refresh_state () { 40 | canceled = false; 41 | 42 | state = FileUtils.test (install_location, FileTest.IS_DIR) ? State.UP_TO_DATE : State.NOT_INSTALLED; 43 | } 44 | 45 | protected override async bool _start_install () { 46 | send_message (_("Downloading...")); 47 | 48 | string path = runner.group.launcher.directory + runner.group.directory + "/" + title + ".tar.gz"; 49 | 50 | var download_valid = yield Utils.Web.Download (download_url, path, () => canceled, (is_percent, progress) => this.progress = is_percent? @"$progress%" : Utils.Filesystem.convert_bytes_to_string (progress)); 51 | 52 | if (!download_valid) 53 | return false; 54 | 55 | send_message (_("Extracting...")); 56 | 57 | string directory = runner.group.launcher.directory + "/" + runner.group.directory + "/"; 58 | 59 | string source_path = yield Utils.Filesystem.extract (directory, title, ".tar.gz", () => canceled); 60 | 61 | if (source_path == "") 62 | return false; 63 | 64 | send_message (_("Renaming...")); 65 | 66 | var runner = this.runner as Runners.Basic; 67 | 68 | var renaming_valid = yield Utils.Filesystem.rename (source_path, directory + runner.get_directory_name (title)); 69 | 70 | if (!renaming_valid) 71 | return false; 72 | 73 | send_message (_("Running post installation script...")); 74 | 75 | var install_script_success = runner.group.launcher.install (this); 76 | 77 | if (!install_script_success) 78 | return false; 79 | 80 | return true; 81 | } 82 | 83 | protected override async bool _start_remove (Parameters parameters) { 84 | send_message (_("Deleting...")); 85 | 86 | var deleted = yield Utils.Filesystem.delete_directory (install_location); 87 | 88 | if (!deleted) 89 | return false; 90 | 91 | send_message (_("Running post removal script...")); 92 | 93 | var uninstall_script_success = runner.group.launcher.uninstall (this); 94 | 95 | if (!uninstall_script_success) 96 | return false; 97 | 98 | return true; 99 | } 100 | } 101 | } -------------------------------------------------------------------------------- /src/models/releases/github-action.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models.Releases { 2 | public class GitHubAction : Basic { 3 | public string artifacts_url { get; set; } 4 | 5 | public GitHubAction (Runners.Basic runner, string title, string release_date, string download_url, string page_url, string artifacts_url) { 6 | this.artifacts_url = artifacts_url; 7 | 8 | shared (runner, title, release_date, download_url, page_url); 9 | 10 | displayed_title = @"$title ($release_date)"; 11 | } 12 | 13 | protected override async bool _start_install () { 14 | send_message (_("Downloading...")); 15 | 16 | string path = runner.group.launcher.directory + runner.group.directory + "/" + title + ".tar.gz"; 17 | 18 | var download_valid = yield Utils.Web.Download (download_url, path, () => canceled, (is_percent, progress) => this.progress = is_percent? @"$progress%" : Utils.Filesystem.convert_bytes_to_string (progress)); 19 | 20 | if (!download_valid) 21 | return false; 22 | 23 | send_message (_("Extracting...")); 24 | 25 | string directory = runner.group.launcher.directory + "/" + runner.group.directory + "/"; 26 | 27 | string source_path = yield Utils.Filesystem.extract (directory, title, ".tar.gz", () => canceled); 28 | 29 | if (source_path == "") 30 | return false; 31 | 32 | source_path = yield Utils.Filesystem.extract (directory, source_path.substring (0, source_path.length - 4).replace (directory, ""), ".tar", () => canceled); 33 | 34 | if (source_path == "") 35 | return false; 36 | 37 | send_message (_("Renaming...")); 38 | 39 | var runner = this.runner as Runners.Basic; 40 | 41 | var renaming_valid = yield Utils.Filesystem.rename (source_path, directory + runner.get_directory_name (title)); 42 | 43 | if (!renaming_valid) 44 | return false; 45 | 46 | send_message (_("Running post installation script...")); 47 | 48 | var install_script_success = runner.group.launcher.install (this); 49 | 50 | if (!install_script_success) 51 | return false; 52 | 53 | return true; 54 | } 55 | } 56 | } -------------------------------------------------------------------------------- /src/models/releases/upgrade.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models.Releases { 2 | public abstract class Upgrade: Release { 3 | public async bool upgrade () { 4 | state = State.BUSY_UPGRADING; 5 | 6 | var upgrade_success = yield _start_upgrade (); 7 | 8 | send_message ((upgrade_success ? _("The upgrade of %s is complete.") : _("An unexpected error occurred while upgrading %s.")).printf (title)); 9 | 10 | refresh_state (); 11 | 12 | return upgrade_success; 13 | } 14 | 15 | protected abstract async bool _start_upgrade (); 16 | } 17 | } -------------------------------------------------------------------------------- /src/models/runner.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models { 2 | public abstract class Runner : Object { 3 | public string title { get; set; } 4 | public string description { get; set; } 5 | public Group group { get; set; } 6 | public bool has_more { get; set; } // TODO Find a better name 7 | public List releases; 8 | 9 | public abstract async List load(); 10 | } 11 | } -------------------------------------------------------------------------------- /src/models/runners/basic.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models.Runners { 2 | public abstract class Basic : Runner { 3 | internal int asset_position { get; set; } // TODO Describe this 4 | internal string endpoint { get; set; } // TODO Describe this 5 | internal int page { get; set; } // TODO Describe this 6 | 7 | construct { 8 | page = 1; 9 | } 10 | 11 | public abstract string get_directory_name (string release_name); 12 | } 13 | } -------------------------------------------------------------------------------- /src/models/runners/boxtron.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models.Runners { 2 | public class Boxtron : GitHub { 3 | public Boxtron(Group group) { 4 | Object(group: group, 5 | title: "Boxtron", 6 | description: _("Steam compatibility tool for running DOS games using DOSBox for Linux."), 7 | endpoint: "https://api.github.com/repos/dreamer/boxtron/releases", 8 | asset_position: 0); 9 | } 10 | 11 | public override string get_directory_name(string release_name) { 12 | return @"$title $release_name"; 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/models/runners/dxvk-async-sarek.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models.Runners { 2 | public class DXVK_Async_Sarek : GitHub { 3 | public DXVK_Async_Sarek (Group group) { 4 | Object (group: group, 5 | title: "DXVK Async (Sarek)", 6 | description: _("DXVK Builds that work with pre-Vulkan 1.3 versions"), 7 | endpoint: "https://api.github.com/repos/pythonlover02/DXVK-Sarek/releases", 8 | asset_position: 0); 9 | } 10 | 11 | public override string get_directory_name (string release_name) { 12 | return release_name + "-async"; 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/models/runners/dxvk-doitsujin.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models.Runners { 2 | public class DXVK_doitsujin : GitHub { 3 | public DXVK_doitsujin (Group group) { 4 | Object (group: group, 5 | title: "DXVK (doitsujin)", 6 | description: "", 7 | endpoint: "https://api.github.com/repos/doitsujin/dxvk/releases", 8 | asset_position: 0); 9 | } 10 | 11 | public override string get_directory_name (string release_name) { 12 | return release_name.replace ("v", "dxvk-"); 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/models/runners/dxvk-gpl-async-ph42on.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models.Runners { 2 | public class DXVK_GPL_Async_Ph42oN : GitLab { 3 | public DXVK_GPL_Async_Ph42oN (Group group) { 4 | Object (group: group, 5 | title: "DXVK GPL+Async (Ph42oN)", 6 | description: "", 7 | endpoint: "https://gitlab.com/api/v4/projects/Ph42oN%2Fdxvk-gplasync/releases", 8 | asset_position: 0); 9 | } 10 | 11 | public override string get_directory_name (string release_name) { 12 | return @"dxvk-gplasync-" + release_name; 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/models/runners/dxvk-sarek.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models.Runners { 2 | public class DXVK_Sarek : GitHub { 3 | public DXVK_Sarek (Group group) { 4 | Object (group: group, 5 | title: "DXVK (Sarek)", 6 | description: _("DXVK Builds that work with pre-Vulkan 1.3 versions"), 7 | endpoint: "https://api.github.com/repos/pythonlover02/DXVK-Sarek/releases", 8 | asset_position: 1); 9 | } 10 | 11 | public override string get_directory_name (string release_name) { 12 | return release_name; 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/models/runners/github-action.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models.Runners { 2 | public abstract class GitHubAction : Basic { 3 | internal string url_template { get; set; } 4 | 5 | public override async List load () { 6 | var temp_releases = new List (); 7 | 8 | var json = yield Utils.Web.GET (endpoint + "?per_page=25&page=" + page ++.to_string (), false); 9 | 10 | if (json == null || json.contains ("https://docs.github.com/rest/overview/resources-in-the-rest-api#rate-limiting")) 11 | return temp_releases; 12 | 13 | var root_node = Utils.Parser.get_node_from_json (json); 14 | 15 | var root_object = root_node.get_object (); 16 | 17 | if (!root_object.has_member ("workflow_runs") || root_object.get_member ("workflow_runs").get_node_type () != Json.NodeType.ARRAY) 18 | return temp_releases; 19 | 20 | var runs_array = root_object.get_array_member ("workflow_runs"); 21 | if (runs_array == null) 22 | return temp_releases; 23 | 24 | for (var i = 0; i < runs_array.get_length (); i++) { 25 | var object = runs_array.get_object_element (i); 26 | 27 | string title = object.get_int_member ("run_number").to_string (); 28 | string page_url = object.get_string_member ("html_url"); 29 | string release_date = object.get_string_member ("created_at").split ("T")[0]; 30 | string download_url = url_template.replace ("{id}", object.get_int_member ("id").to_string ()); 31 | string artifacts_url = object.get_string_member ("artifacts_url"); 32 | 33 | if (object.get_string_member_with_default ("status", "") == "completed" && object.get_string_member_with_default ("conclusion", "") == "success") { 34 | var release = new Releases.GitHubAction (this, title, release_date, download_url, page_url, artifacts_url); 35 | 36 | releases.append (release); 37 | temp_releases.append (release); 38 | } 39 | } 40 | 41 | return temp_releases; 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /src/models/runners/github.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models.Runners { 2 | public abstract class GitHub : Basic { 3 | internal bool use_name_instead_of_tag_name { get; set; } // TODO Describe this 4 | internal string[] request_asset_exclude { get; set; } // TODO Describe this 5 | 6 | public override async List load () { 7 | var temp_releases = new List (); 8 | 9 | var json = yield Utils.Web.GET (endpoint + "?per_page=25&page=" + page ++.to_string (), false); 10 | 11 | if (json == null || json.contains ("https://docs.github.com/rest/overview/resources-in-the-rest-api#rate-limiting")) 12 | return temp_releases; 13 | 14 | var root_node = Utils.Parser.get_node_from_json (json); 15 | 16 | if (root_node == null) 17 | return temp_releases; 18 | 19 | if (root_node.get_node_type () != Json.NodeType.ARRAY) 20 | return temp_releases; 21 | 22 | var root_array = root_node.get_array (); 23 | if (root_array == null) 24 | return temp_releases; 25 | 26 | for (var i = 0; i < root_array.get_length (); i++) { 27 | var object = root_array.get_object_element (i); 28 | 29 | var asset_array = object.get_array_member ("assets"); 30 | if (asset_array == null) 31 | continue; 32 | 33 | string title = use_name_instead_of_tag_name ? object.get_string_member ("name") : object.get_string_member ("tag_name"); 34 | string description = object.get_string_member ("body").strip (); 35 | string page_url = object.get_string_member ("html_url"); 36 | string release_date = object.get_string_member ("created_at").split ("T")[0]; 37 | 38 | if (request_asset_exclude != null) { 39 | var excluded = false; 40 | 41 | foreach (var excluded_asset in request_asset_exclude) { 42 | if (title.contains (excluded_asset)) { 43 | excluded = true; 44 | break; 45 | } 46 | } 47 | 48 | if (excluded) 49 | continue; 50 | } 51 | 52 | if (asset_array.get_length () - 1 >= asset_position) { 53 | var asset_object = asset_array.get_object_element (asset_position); 54 | 55 | string download_url = asset_object.get_string_member ("browser_download_url"); 56 | int64 download_size = asset_object.get_int_member ("size"); 57 | 58 | var release = new Releases.Basic.github (this, title, description, release_date, download_size, download_url, page_url); 59 | 60 | releases.append (release); 61 | temp_releases.append (release); 62 | } 63 | } 64 | 65 | has_more = temp_releases.length () == 25; 66 | 67 | return temp_releases; 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /src/models/runners/gitlab.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models.Runners { 2 | public abstract class GitLab : Basic { 3 | internal bool use_name_instead_of_tag_name { get; set; } // TODO Describe this 4 | internal string[] request_asset_exclude { get; set; } // TODO Describe this 5 | 6 | public override async List load () { 7 | var temp_releases = new List (); 8 | 9 | var json = yield Utils.Web.GET (endpoint + "?per_page=25&page=" + page ++.to_string (), false); 10 | 11 | if (json == null) 12 | return temp_releases; 13 | 14 | var root_node = Utils.Parser.get_node_from_json (json); 15 | 16 | if (root_node == null || root_node.get_node_type () != Json.NodeType.ARRAY) 17 | return temp_releases; 18 | 19 | var root_array = root_node.get_array (); 20 | if (root_array == null) 21 | return temp_releases; 22 | 23 | for (var i = 0; i < root_array.get_length (); i++) { 24 | var object = root_array.get_object_element (i); 25 | 26 | string title = use_name_instead_of_tag_name ? object.get_string_member ("name") : object.get_string_member ("tag_name"); 27 | string description = object.get_string_member ("description").strip ();; 28 | string release_date = object.get_string_member ("created_at").split ("T")[0]; 29 | 30 | if (request_asset_exclude != null) { 31 | var excluded = false; 32 | 33 | foreach (var excluded_asset in request_asset_exclude) { 34 | if (title.contains (excluded_asset)) { 35 | excluded = true; 36 | break; 37 | } 38 | } 39 | 40 | if (excluded) 41 | continue; 42 | } 43 | 44 | var page_url_object = object.get_object_member ("_links"); 45 | if (page_url_object == null) 46 | continue; 47 | 48 | string page_url = page_url_object.get_string_member ("self"); 49 | 50 | var assets_object = object.get_object_member ("assets"); 51 | if (assets_object == null) 52 | continue; 53 | 54 | var link_array = assets_object.get_array_member ("links"); 55 | if (link_array == null) 56 | continue; 57 | 58 | if (link_array.get_length () - 1 >= asset_position) { 59 | var link_object = link_array.get_object_element (asset_position); 60 | 61 | var download_url = link_object.get_string_member ("direct_asset_url").replace ("?ref_type=heads", ""); 62 | 63 | var release = new Releases.Basic.gitlab (this, title, description, release_date, download_url, page_url); 64 | 65 | releases.append (release); 66 | temp_releases.append (release); 67 | } 68 | } 69 | 70 | return temp_releases; 71 | } 72 | } 73 | } -------------------------------------------------------------------------------- /src/models/runners/kron4ek-wine-builds-staging-tkg.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models.Runners { 2 | public class Wine_Staging_Tkg_Kron4ek : GitHub { 3 | public Wine_Staging_Tkg_Kron4ek (Group group) { 4 | var request_asset_exclude = new string[] { "proton", ".0." }; 5 | Object (group: group, 6 | title: "Wine-Staging-Tkg (Kron4ek)", 7 | description: _("Wine build with the Staging patchset and many other useful patches."), 8 | endpoint: "https://api.github.com/repos/Kron4ek/Wine-Builds/releases", 9 | asset_position: 3, 10 | request_asset_exclude: request_asset_exclude); 11 | } 12 | 13 | public override string get_directory_name (string release_name) { 14 | return @"wine-$release_name-staging-tkg-amd64"; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/models/runners/kron4ek-wine-builds-staging.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models.Runners { 2 | public class Wine_Staging_Kron4ek : GitHub { 3 | public Wine_Staging_Kron4ek (Group group) { 4 | var request_asset_exclude = new string[] { "proton", ".0." }; 5 | Object (group: group, 6 | title: "Wine-Staging (Kron4ek)", 7 | description: _("Wine build with the Staging patchset."), 8 | endpoint: "https://api.github.com/repos/Kron4ek/Wine-Builds/releases", 9 | asset_position: 2, 10 | request_asset_exclude: request_asset_exclude); 11 | // https://api.github.com/repos/Kron4ek/Wine-Builds/releases 12 | // 2 13 | // Models.Runner.title_types.KRON4EK_STAGING 14 | // kron4ek_staging.request_asset_exclude = { "proton", ".0." }; 15 | } 16 | 17 | public override string get_directory_name (string release_name) { 18 | return @"wine-$release_name-staging-amd64"; 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /src/models/runners/kron4ek-wine-builds-vanilla.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models.Runners { 2 | public class Wine_Vanilla_Kron4ek : GitHub { 3 | public Wine_Vanilla_Kron4ek (Group group) { 4 | var request_asset_exclude = new string[] { "proton", ".0." }; 5 | Object (group: group, 6 | title: "Wine-Vanilla (Kron4ek)", 7 | description: _("Wine build compiled from the official WineHQ sources."), 8 | endpoint: "https://api.github.com/repos/Kron4ek/Wine-Builds/releases", 9 | asset_position: 1, 10 | request_asset_exclude: request_asset_exclude); 11 | } 12 | 13 | public override string get_directory_name (string release_name) { 14 | return @"wine-$release_name-amd64"; 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/models/runners/luxtorpeda.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models.Runners { 2 | public class Luxtorpeda : GitHub { 3 | public Luxtorpeda (Group group) { 4 | Object (group: group, 5 | title: "Luxtorpeda", 6 | description: _("Luxtorpeda provides Linux-native game engines for certain Windows-only games."), 7 | endpoint: "https://api.github.com/repos/luxtorpeda-dev/luxtorpeda/releases", 8 | asset_position: 0); 9 | } 10 | 11 | public override string get_directory_name (string release_name) { 12 | return @"$title $release_name"; 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/models/runners/northstar-proton.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models.Runners { 2 | public class Northstar_Proton : GitHub { 3 | public Northstar_Proton (Group group) { 4 | Object (group: group, 5 | title: "NorthstarProton", 6 | description: _("Custom Proton build for running the Northstar client for Titanfall 2."), 7 | endpoint: "https://api.github.com/repos/cyrv6737/NorthstarProton/releases", 8 | asset_position: 0); 9 | } 10 | 11 | public override string get_directory_name (string release_name) { 12 | return @"$title $release_name"; 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/models/runners/other.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models.Runners { 2 | public class Other : GitHub { 3 | public Other (Group group) { 4 | Object (group: group, 5 | title: "Other", 6 | description: "", 7 | endpoint: "https://api.github.com/repos/bottlesdevs/wine/releases", 8 | asset_position: 0, 9 | use_name_instead_of_tag_name: true); 10 | } 11 | 12 | public override string get_directory_name (string release_name) { 13 | return release_name.down ().replace (" ", "-"); 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/models/runners/proton-cachyos.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models.Runners { 2 | public class Proton_CachyOS : GitHub { 3 | public Proton_CachyOS (Group group) { 4 | int position = 1; 5 | foreach (var item in Utils.System.HWCAPS) { 6 | if (item == "x86_64_v3") { 7 | position = 3; 8 | break; 9 | } 10 | } 11 | 12 | Object (group: group, 13 | title: "Proton-CachyOS", 14 | description: _("Steam compatibility tool from the CachyOS Linux distribution for running Windows games with improvements over Valve's default Proton."), 15 | endpoint: "https://api.github.com/repos/CachyOS/proton-cachyos/releases", 16 | asset_position: position); 17 | } 18 | 19 | public override string get_directory_name (string release_name) { 20 | return @"$title $release_name"; 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/models/runners/proton-em.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models.Runners { 2 | public class Proton_EM : GitHub { 3 | public Proton_EM (Group group) { 4 | Object (group: group, 5 | title: "Proton-EM", 6 | description: _("Steam compatibility tool for running Windows games with improvements over Valve's default Proton. By Etaash Mathamsetty adding FSR4 support and wine wayland tweaks."), 7 | endpoint: "https://api.github.com/repos/Etaash-mathamsetty/Proton/releases", 8 | asset_position: 1); 9 | } 10 | 11 | public override string get_directory_name (string release_name) { 12 | return release_name; 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/models/runners/proton-ge-rstp.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models.Runners { 2 | public class Proton_GE_RSTP : GitHub { 3 | public Proton_GE_RSTP (Group group) { 4 | Object (group: group, 5 | title: "Proton-GE RTSP", 6 | description: _("Steam compatibility tool based on Proton-GE with additional patches to improve RTSP codecs for VRChat."), 7 | endpoint: "https://api.github.com/repos/SpookySkeletons/proton-ge-rtsp/releases", 8 | asset_position: 0); 9 | } 10 | 11 | public override string get_directory_name (string release_name) { 12 | return @"$title $release_name"; 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/models/runners/proton-ge.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models.Runners { 2 | public class Proton_GE : GitHub { 3 | public Proton_GE (Group group) { 4 | Object (group: group, 5 | title: "Proton-GE", 6 | description: _("Steam compatibility tool for running Windows games with improvements over Valve's default Proton."), 7 | endpoint: "https://api.github.com/repos/GloriousEggroll/proton-ge-custom/releases", 8 | asset_position: 1); 9 | } 10 | 11 | public override string get_directory_name (string release_name) { 12 | switch (group.launcher.title) { 13 | case "Steam": 14 | if (title.contains (".")) { 15 | return "Proton-" + release_name; 16 | } 17 | return release_name; 18 | case "Bottles": 19 | return release_name.down (); 20 | 21 | case "Heroic Games Launcher": 22 | return @"Proton-$release_name"; 23 | default: 24 | return release_name; 25 | } 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/models/runners/proton-sarek-async.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models.Runners { 2 | public class Proton_Sarek_Async : GitHub { 3 | public Proton_Sarek_Async (Group group) { 4 | Object (group: group, 5 | title: "Proton-Sarek (Async)", 6 | description: _("Steam compatibility tool based on Proton-GE with modifications for very old GPUs, with %s.").printf ("DXVK-Async"), 7 | endpoint: "https://api.github.com/repos/pythonlover02/Proton-Sarek/releases", 8 | asset_position: 0, 9 | request_asset_exclude: new string[] { "Sarek9-13" }); 10 | } 11 | 12 | public override string get_directory_name (string release_name) { 13 | return release_name + "-async"; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/models/runners/proton-sarek.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models.Runners { 2 | public class Proton_Sarek : GitHub { 3 | public Proton_Sarek (Group group) { 4 | Object (group: group, 5 | title: "Proton-Sarek", 6 | description: _("Steam compatibility tool based on Proton-GE with modifications for very old GPUs, with %s.").printf ("DXVK"), 7 | endpoint: "https://api.github.com/repos/pythonlover02/Proton-Sarek/releases", 8 | asset_position: 1, 9 | request_asset_exclude: new string[] { "Sarek9-13" }); 10 | } 11 | 12 | public override string get_directory_name (string release_name) { 13 | return release_name; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/models/runners/proton-tkg.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models.Runners { 2 | public class Proton_Tkg : GitHubAction { 3 | public Proton_Tkg (Group group) { 4 | Object (group: group, 5 | title: "Proton-Tkg", 6 | description: _("Custom Proton build for running Windows games, based on Wine-tkg."), 7 | endpoint: "https://api.github.com/repos/Frogging-Family/wine-tkg-git/actions/workflows/29873769/runs", 8 | asset_position: 0, 9 | url_template: "https://nightly.link/Frogging-Family/wine-tkg-git/actions/runs/{id}/proton-tkg-build.zip"); 10 | } 11 | 12 | public override string get_directory_name (string release_name) { 13 | return @"$title-$release_name"; 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /src/models/runners/roberta.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models.Runners { 2 | public class Roberta : GitHub { 3 | public Roberta (Group group) { 4 | Object (group: group, 5 | title: "Roberta", 6 | description: _("Steam compatibility tool for running adventure games using ScummVM for Linux."), 7 | endpoint: "https://api.github.com/repos/dreamer/roberta/releases", 8 | asset_position: 0); 9 | } 10 | 11 | public override string get_directory_name (string release_name) { 12 | return @"$title $release_name"; 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/models/runners/steamtinkerlaunch.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models.Runners { 2 | public class SteamTinkerLaunch : Runner { 3 | public SteamTinkerLaunch (Models.Group group) { 4 | Object (group: group, 5 | title: "SteamTinkerLaunch", 6 | description: _("Steam tool for easy, graphical configuration of your other compatibility tools for both Windows games and native Linux games.")); 7 | } 8 | 9 | public override async List load () { 10 | releases = new List (); 11 | 12 | var release = new Releases.SteamTinkerLaunch (this); 13 | 14 | releases.append (release); 15 | 16 | return (owned) releases; 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/models/runners/vkd3d-lutris.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models.Runners { 2 | public class VKD3D_Lutris : GitHub { 3 | public VKD3D_Lutris (Group group) { 4 | Object (group: group, 5 | title: "VKD3D-Lutris", 6 | description: "", 7 | endpoint: "https://api.github.com/repos/lutris/vkd3d/releases", 8 | asset_position: 0); 9 | } 10 | 11 | public override string get_directory_name (string release_name) { 12 | switch (group.launcher.title) { 13 | case "Lutris": 14 | return release_name; 15 | case "Heroic Games Launcher": 16 | return release_name.replace ("v", "vkd3d-lutris-"); 17 | default: 18 | return release_name; 19 | } 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /src/models/runners/vkd3d-proton.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Models.Runners { 2 | public class VKD3D_Proton : GitHub { 3 | public VKD3D_Proton (Group group) { 4 | Object (group: group, 5 | title: "VKD3D-Proton", 6 | description: "", 7 | endpoint: "https://api.github.com/repos/HansKristian-Work/vkd3d-proton/releases", 8 | asset_position: 0); 9 | } 10 | 11 | public override string get_directory_name (string release_name) { 12 | return release_name.replace ("v", "vkd3d-proton-"); 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/utils/parser.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Utils { 2 | public class Parser { 3 | public static Json.Node? get_node_from_json (string json) { 4 | try { 5 | return Json.from_string (json); 6 | } catch (Error e) { 7 | message (e.message); 8 | return null; 9 | } 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /src/utils/system.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Utils { 2 | public class System { 3 | public static bool IS_STEAM_OS = false; 4 | public static bool IS_GAMESCOPE = false; 5 | public static bool IS_FLATPAK = false; 6 | public static List HWCAPS = new List (); 7 | 8 | public static void initialize () { 9 | IS_FLATPAK = FileUtils.test ("/.flatpak-info", FileTest.IS_REGULAR); 10 | IS_GAMESCOPE = Environment.get_variable ("DESKTOP_SESSION") == "gamescope-wayland"; 11 | IS_STEAM_OS = get_distribution_name ().ascii_down () == "steamos"; 12 | HWCAPS = get_hwcaps (); 13 | } 14 | 15 | public static string run_command (string command) { 16 | try { 17 | string command_line = ""; 18 | if (IS_FLATPAK)command_line += "flatpak-spawn --host "; 19 | command_line += command; 20 | 21 | string stdout = ""; 22 | var valid = Process.spawn_command_line_sync (command_line, out stdout, null, null); 23 | if (!valid)return ""; 24 | 25 | return stdout; 26 | } catch (Error e) { 27 | message (e.message); 28 | return ""; 29 | } 30 | } 31 | 32 | public static List get_hwcaps () { 33 | var hwcaps = new List (); 34 | hwcaps.append ("x86_64"); 35 | 36 | // flags according to https://gitlab.com/x86-psABIs/x86-64-ABI/-/blob/master/x86-64-ABI/low-level-sys-info.tex 37 | var flags_v2 = new List (); 38 | flags_v2.append ("sse4_1"); 39 | flags_v2.append ("sse4_2"); 40 | flags_v2.append ("ssse3"); 41 | 42 | var flags_v3 = new List (); 43 | foreach (var flag in flags_v2) 44 | flags_v3.append (flag); 45 | flags_v3.append ("avx"); 46 | flags_v3.append ("avx2"); 47 | 48 | var flags_v4 = new List (); 49 | foreach (var flag in flags_v3) 50 | flags_v4.append (flag); 51 | flags_v4.append ("avx512f"); 52 | flags_v4.append ("avx512bw"); 53 | flags_v4.append ("avx512cd"); 54 | flags_v4.append ("avx512dq"); 55 | flags_v4.append ("avx512vl"); 56 | 57 | string flags = ""; 58 | 59 | try { 60 | // Open the file for reading 61 | File file = File.new_for_path ("/proc/cpuinfo"); 62 | InputStream input_stream = file.read (); 63 | DataInputStream dis = new DataInputStream (input_stream); 64 | 65 | // Read lines from the input stream 66 | string? line; 67 | while ((line = dis.read_line ()) != null) { 68 | if (line.slice (0, 5) == "flags") { 69 | flags = line; 70 | break; 71 | } 72 | } 73 | 74 | // Close the input stream 75 | input_stream.close (); 76 | } catch (Error e) { 77 | message ("Error: %s\n", e.message); 78 | } 79 | 80 | int count = 0; 81 | foreach (var flag in flags_v4) { 82 | if (flags.contains (flag)) 83 | count++; 84 | } 85 | if (flags_v4.length () == count) 86 | hwcaps.append ("x86_64_v4"); 87 | 88 | count = 0; 89 | foreach (var flag in flags_v3) { 90 | if (flags.contains (flag)) 91 | count++; 92 | } 93 | if (flags_v3.length () == count) 94 | hwcaps.append ("x86_64_v3"); 95 | 96 | count = 0; 97 | foreach (var flag in flags_v2) { 98 | if (flags.contains (flag)) 99 | count++; 100 | } 101 | if (flags_v2.length () == count) 102 | hwcaps.append ("x86_64_v2"); 103 | 104 | return (owned) hwcaps; 105 | } 106 | 107 | public static bool check_dependency (string name) { 108 | return run_command (@"which $name") == "" ? false : true; 109 | } 110 | 111 | static string get_distribution_name () { 112 | var distro_info = run_command ("cat /etc/lsb-release /etc/os-release").split ("\n", 1)[0]; 113 | 114 | var distro_name = "Unknown"; 115 | MatchInfo m; 116 | if (/^NAME="\s*(.+?)\s*"/m.match (distro_info, 0, out m)) 117 | distro_name = m.fetch (1); 118 | 119 | return distro_name; 120 | } 121 | 122 | public static void open_url (string url) { 123 | var uri_launcher = new Gtk.UriLauncher (url); 124 | uri_launcher.launch.begin (Widgets.Application.window, null, (obj, res) => { 125 | try { 126 | uri_launcher.launch.end (res); 127 | } catch (Error error) { 128 | message (error.message); 129 | } 130 | }); 131 | } 132 | } 133 | } -------------------------------------------------------------------------------- /src/utils/web.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Utils { 2 | public class Web { 3 | public static string get_user_agent () { 4 | return Config.APP_NAME + "/" + Config.APP_VERSION; 5 | } 6 | 7 | public static async string ? GET (string url, bool stl = false) { 8 | try { 9 | var session = new Soup.Session (); 10 | session.set_user_agent (get_user_agent ()); 11 | 12 | var message = new Soup.Message ("GET", url); 13 | 14 | Bytes bytes = yield session.send_and_read_async (message, Priority.DEFAULT, null); 15 | 16 | return (string) bytes.get_data (); 17 | } catch (Error e) { 18 | message (e.message); 19 | return null; 20 | } 21 | } 22 | 23 | public delegate bool cancel_callback (); 24 | 25 | public delegate void progress_callback (bool is_percent, int64 progress); 26 | 27 | public static async bool Download (string url, string path, cancel_callback cancel_callback, progress_callback progress_callback) { 28 | try { 29 | var session = new Soup.Session (); 30 | session.set_user_agent (get_user_agent ()); 31 | 32 | var soup_message = new Soup.Message ("GET", url); 33 | 34 | var input_stream = yield session.send_async (soup_message, Priority.DEFAULT, null); 35 | 36 | if (soup_message.status_code != 200) { 37 | message (soup_message.reason_phrase); 38 | return false; 39 | } 40 | 41 | var file = File.new_for_path (path); 42 | if (file.query_exists ()) 43 | yield file.delete_async (Priority.DEFAULT, null); 44 | 45 | FileOutputStream output_stream = yield file.create_async (FileCreateFlags.REPLACE_DESTINATION, Priority.DEFAULT, null); 46 | 47 | // Prefer real Content-Length header from the server if it exists. 48 | // NOTE: Servers typically return "0" when it doesn't know, for 49 | // live-generated files (such as GitHub's commit-based source 50 | // archives). However, GitHub's server then caches the generated 51 | // result for a few minutes to avoid extra work. So the first 52 | // download of a GitHub source archive will have an unknown "0" 53 | // length, but any download requests after that it will see the 54 | // filesize for a few minutes, until GitHub clears their cache. 55 | int64 server_download_size = soup_message.get_response_headers ().get_content_length (); 56 | 57 | const size_t chunk_size = 4096; 58 | bool is_percent = server_download_size > 0; 59 | int64 bytes_downloaded = 0; 60 | 61 | if (progress_callback != null) 62 | progress_callback (is_percent, 0); // Set initial progress state. 63 | 64 | var is_canceled = false; 65 | 66 | while (true) { 67 | if (cancel_callback ()) { 68 | is_canceled = true; 69 | break; 70 | } 71 | 72 | var chunk = yield input_stream.read_bytes_async (chunk_size); 73 | 74 | if (chunk.get_size () == 0) 75 | break; 76 | 77 | bytes_downloaded += output_stream.write (chunk.get_data ()); 78 | 79 | if (progress_callback != null) { 80 | // Use "bytes downloaded" when total size is unknown. 81 | int64 progress = !is_percent ? bytes_downloaded : (int64) (((double) bytes_downloaded / server_download_size) * 100); 82 | progress_callback (is_percent, progress); 83 | } 84 | } 85 | 86 | yield output_stream.close_async (); 87 | 88 | session.abort (); 89 | 90 | if (is_canceled && file.query_exists ()) 91 | yield file.delete_async (Priority.DEFAULT, null); 92 | 93 | return !is_canceled; 94 | } catch (Error e) { 95 | message (e.message); 96 | return false; 97 | } 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /src/widgets/application.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Widgets { 2 | public class Application : Adw.Application { 3 | public static Window window { get; set; } 4 | 5 | construct { 6 | application_id = Config.APP_ID; 7 | flags |= ApplicationFlags.FLAGS_NONE; 8 | 9 | ActionEntry[] action_entries = { 10 | { "about", this.on_about_action }, 11 | { "quit", this.quit } 12 | }; 13 | this.add_action_entries (action_entries, this); 14 | this.set_accels_for_action ("app.quit", { "Q" }); 15 | } 16 | 17 | public override void activate () { 18 | base.activate (); 19 | 20 | window = new Window (); 21 | 22 | var display = Gdk.Display.get_default (); 23 | 24 | Gtk.IconTheme.get_for_display (display).add_resource_path ("/com/vysp3r/ProtonPlus/icons"); 25 | 26 | var css_provider = new Gtk.CssProvider (); 27 | css_provider.load_from_resource ("/com/vysp3r/ProtonPlus/css/style.css"); 28 | 29 | Gtk.StyleContext.add_provider_for_display (display, css_provider, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION); 30 | 31 | Utils.System.initialize (); 32 | 33 | window.initialize (); 34 | 35 | var settings = new Settings ("com.vysp3r.ProtonPlus.State"); 36 | settings.bind ("width", 37 | window, 38 | "default-width", 39 | SettingsBindFlags.DEFAULT); 40 | settings.bind ("height", 41 | window, 42 | "default-height", 43 | SettingsBindFlags.DEFAULT); 44 | settings.bind ("is-maximized", 45 | window, 46 | "maximized", 47 | SettingsBindFlags.DEFAULT); 48 | settings.bind ("is-fullscreen", 49 | window, 50 | "fullscreened", 51 | SettingsBindFlags.DEFAULT); 52 | 53 | if (Utils.System.IS_GAMESCOPE) 54 | window.fullscreen (); 55 | 56 | window.present (); 57 | } 58 | 59 | void on_about_action () { 60 | const string[] devs = { 61 | "Charles Malouin (Vysp3r) https://github.com/Vysp3r", 62 | "Johnny Arcitec https://github.com/Arcitec", 63 | "windblows95 https://github.com/windblows95", 64 | null 65 | }; 66 | 67 | const string[] thanks = { 68 | "GNOME Project https://www.gnome.org/", 69 | "ProtonUp-Qt Project https://davidotek.github.io/protonup-qt/", 70 | "LUG Helper Project https://github.com/starcitizen-lug/lug-helper", 71 | null 72 | }; 73 | 74 | var about_dialog = new Adw.AboutDialog (); 75 | about_dialog.set_application_name (Config.APP_NAME); 76 | about_dialog.set_application_icon (Config.APP_ID); 77 | about_dialog.set_version ("v" + Config.APP_VERSION); 78 | about_dialog.set_comments (_("A modern compatibility tools manager for Linux.")); 79 | about_dialog.add_link ("GitHub", "https://github.com/Vysp3r/ProtonPlus"); 80 | about_dialog.add_link ("Website", "https://protonplus.vysp3r.com/"); 81 | about_dialog.set_issue_url ("https://github.com/Vysp3r/ProtonPlus/issues/new/choose"); 82 | about_dialog.set_copyright ("© 2022-2025 Vysp3r"); 83 | about_dialog.set_license_type (Gtk.License.GPL_3_0); 84 | about_dialog.set_developers (devs); 85 | about_dialog.add_credit_section (_("Special thanks to"), thanks); 86 | about_dialog.present (window); 87 | } 88 | } 89 | } -------------------------------------------------------------------------------- /src/widgets/busy-dialog.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Widgets { 2 | public abstract class BusyDialog : Adw.Dialog { 3 | protected Adw.ToolbarView toolbar_view { get; set; } 4 | protected Adw.WindowTitle window_title { get; set; } 5 | protected Adw.HeaderBar header_bar { get; set; } 6 | protected Gtk.Box content_box { get; set; } 7 | protected Gtk.ScrolledWindow scrolled_window { get; set; } 8 | protected Gtk.Box container { get; set; } 9 | protected Gtk.Label progress_label { get; set; } 10 | protected Gtk.Label closing_label { get; set; } 11 | protected Gtk.ProgressBar progress_bar { get; set; } 12 | 13 | protected Models.Release release { get; set; } 14 | protected bool pulse { get; set; } 15 | protected bool finished { get; set; } 16 | protected bool stop { get; set; } 17 | protected int count { get; set; } 18 | 19 | construct { 20 | window_title = new Adw.WindowTitle ("", ""); 21 | 22 | var log_button = new Gtk.Button.from_icon_name ("journal-text-symbolic"); 23 | log_button.set_tooltip_text (_("Show/hide logs")); 24 | log_button.clicked.connect (() => { 25 | if (scrolled_window.get_parent () == null) 26 | content_box.prepend (scrolled_window); 27 | else 28 | content_box.remove (scrolled_window); 29 | }); 30 | 31 | header_bar = new Adw.HeaderBar (); 32 | header_bar.set_title_widget (window_title); 33 | header_bar.pack_start (log_button); 34 | 35 | progress_label = new Gtk.Label (""); 36 | 37 | closing_label = new Gtk.Label (""); 38 | 39 | container = new Gtk.Box (Gtk.Orientation.VERTICAL, 2); 40 | 41 | scrolled_window = new Gtk.ScrolledWindow (); 42 | scrolled_window.set_child (container); 43 | scrolled_window.set_size_request (200, 125); 44 | scrolled_window.set_policy (Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC); 45 | scrolled_window.add_css_class ("card"); 46 | scrolled_window.add_css_class ("dialog"); 47 | scrolled_window.get_vadjustment ().changed.connect (scroll_to_bottom); 48 | 49 | progress_bar = new Gtk.ProgressBar (); 50 | progress_bar.set_hexpand (true); 51 | progress_bar.set_vexpand (true); 52 | progress_bar.set_valign (Gtk.Align.CENTER); 53 | progress_bar.set_show_text (true); 54 | 55 | content_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 12); 56 | content_box.set_margin_bottom (12); 57 | content_box.set_margin_start (12); 58 | content_box.set_margin_end (12); 59 | content_box.append (progress_bar); 60 | 61 | toolbar_view = new Adw.ToolbarView (); 62 | toolbar_view.add_top_bar (header_bar); 63 | toolbar_view.set_content (content_box); 64 | 65 | closed.connect (dialog_on_close); 66 | 67 | set_child (toolbar_view); 68 | } 69 | 70 | protected void initialize (Models.Release release) { 71 | this.release = release; 72 | 73 | header_bar.set_show_end_title_buttons (false); 74 | 75 | window_title.set_subtitle (release.displayed_title); 76 | 77 | Timeout.add_full (Priority.DEFAULT, 25, () => { 78 | if (finished || stop) 79 | return false; 80 | 81 | if (pulse)progress_bar.pulse (); 82 | 83 | return true; 84 | }); 85 | } 86 | 87 | protected void dialog_on_close () { 88 | stop = true; 89 | } 90 | 91 | public virtual void done (bool success) { 92 | header_bar.set_show_end_title_buttons (true); 93 | 94 | finished = true; 95 | pulse = false; 96 | progress_bar.set_fraction (1); 97 | 98 | if (success) { 99 | count = 5; 100 | 101 | refresh_closing_label (); 102 | 103 | GLib.Timeout.add_seconds_full (Priority.DEFAULT, 1, () => { 104 | if (stop) 105 | return false; 106 | 107 | if (count == 0) { 108 | close (); 109 | return false; 110 | } 111 | 112 | refresh_closing_label (); 113 | 114 | return true; 115 | }); 116 | } 117 | } 118 | 119 | void refresh_closing_label () { 120 | closing_label.set_text ("%s %i".printf (_("Closing in"), count--)); 121 | if (closing_label.get_parent () == null) 122 | container.append (closing_label); 123 | } 124 | 125 | public void add_text (string text) { 126 | var label = new Gtk.Label (text); 127 | container.append (label); 128 | progress_bar.set_text (text); 129 | } 130 | 131 | void scroll_to_bottom () { 132 | var adjustment = scrolled_window.get_vadjustment (); 133 | adjustment.set_value (adjustment.get_upper () - adjustment.get_page_size ()); 134 | } 135 | } 136 | } -------------------------------------------------------------------------------- /src/widgets/busy-dialogs/install-dialog.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Widgets.Dialogs { 2 | public class InstallDialog : BusyDialog { 3 | public InstallDialog (Models.Release release) { 4 | initialize (release); 5 | 6 | window_title.set_title (_("Installing")); 7 | 8 | release.notify["progress"].connect (release_progress_changed); 9 | 10 | closed.connect (install_dialog_on_close); 11 | } 12 | 13 | void install_dialog_on_close () { 14 | dialog_on_close (); 15 | 16 | release.canceled = true; 17 | } 18 | 19 | void release_progress_changed () { 20 | progress_label.set_text (release.progress); 21 | if (progress_label.get_parent () == null) 22 | container.append (progress_label); 23 | 24 | progress_bar.set_fraction (double.parse (release.progress) / 100); 25 | 26 | pulse = progress_bar.get_fraction () == 1; 27 | 28 | header_bar.set_show_end_title_buttons (!pulse); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/widgets/busy-dialogs/remove-dialog.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Widgets.Dialogs { 2 | public class RemoveDialog : BusyDialog { 3 | public RemoveDialog (Models.Release release) { 4 | initialize (release); 5 | 6 | window_title.set_title (_("Removing")); 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /src/widgets/busy-dialogs/upgrade-dialog.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Widgets.Dialogs { 2 | public class UpgradeDialog : BusyDialog { 3 | public UpgradeDialog (Models.Release release) { 4 | initialize (release); 5 | 6 | window_title.set_title (_("Upgrading")); 7 | } 8 | } 9 | } -------------------------------------------------------------------------------- /src/widgets/description-dialog.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Widgets { 2 | public class DescriptionDialog : Adw.Dialog { 3 | Models.Release release { get; set; } 4 | 5 | Adw.ToolbarView toolbar_view { get; set; } 6 | Adw.WindowTitle window_title { get; set; } 7 | Adw.ButtonContent web_button_content { get; set; } 8 | Gtk.Button web_button { get; set; } 9 | Adw.HeaderBar header_bar { get; set; } 10 | Gtk.ScrolledWindow scrolled_window { get; set; } 11 | Gtk.Label description_label { get; set; } 12 | 13 | construct { 14 | window_title = new Adw.WindowTitle (_("More information"), ""); 15 | 16 | web_button_content = new Adw.ButtonContent (); 17 | web_button_content.set_icon_name ("world-www-symbolic"); 18 | web_button_content.set_label (_("Open")); 19 | 20 | web_button = new Gtk.Button (); 21 | web_button.set_child (web_button_content); 22 | web_button.set_tooltip_text (_("Open in your web browser")); 23 | web_button.clicked.connect (web_button_clicked); 24 | 25 | header_bar = new Adw.HeaderBar (); 26 | header_bar.pack_start (web_button); 27 | header_bar.set_title_widget (window_title); 28 | 29 | description_label = new Gtk.Label (null); 30 | description_label.set_halign (Gtk.Align.START); 31 | 32 | scrolled_window = new Gtk.ScrolledWindow (); 33 | scrolled_window.set_child (description_label); 34 | scrolled_window.set_policy (Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC); 35 | scrolled_window.set_size_request (375, 175); 36 | scrolled_window.add_css_class ("card"); 37 | scrolled_window.add_css_class ("dialog"); 38 | scrolled_window.set_margin_top (7); 39 | scrolled_window.set_margin_bottom (12); 40 | scrolled_window.set_margin_start (12); 41 | scrolled_window.set_margin_end (12); 42 | 43 | toolbar_view = new Adw.ToolbarView (); 44 | toolbar_view.add_top_bar (header_bar); 45 | toolbar_view.set_content (scrolled_window); 46 | 47 | set_child (toolbar_view); 48 | } 49 | 50 | public DescriptionDialog (Models.Release release) { 51 | this.release = release; 52 | 53 | window_title.set_subtitle (release.displayed_title); 54 | description_label.set_label (release.description); 55 | } 56 | 57 | void web_button_clicked () { 58 | Utils.System.open_url (release.page_url); 59 | } 60 | } 61 | } -------------------------------------------------------------------------------- /src/widgets/info-box.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Widgets { 2 | public class InfoBox : Gtk.Box { 3 | Menu menu { get; set; } 4 | Gtk.MenuButton menu_button { get; set; } 5 | Adw.WindowTitle window_title { get; set; } 6 | Adw.HeaderBar header { get; set; } 7 | Adw.ToolbarView toolbar_view { get; set; } 8 | 9 | public List launcher_boxes; 10 | 11 | construct { 12 | set_orientation (Gtk.Orientation.VERTICAL); 13 | 14 | window_title = new Adw.WindowTitle ("", ""); 15 | 16 | var item = new MenuItem (_("_Installed Only"), null); 17 | item.set_action_and_target ("win.set-installed-only", "b"); 18 | 19 | var filters_section = new Menu (); 20 | filters_section.append_item (item); 21 | 22 | var other_section = new Menu (); 23 | other_section.append (_("_Keyboard Shortcuts"), "win.show-help-overlay"); 24 | other_section.append (_("_About ProtonPlus"), "app.about"); 25 | 26 | menu = new Menu (); 27 | menu.append_section (null, filters_section); 28 | menu.append_section (null, other_section); 29 | 30 | menu_button = new Gtk.MenuButton (); 31 | menu_button.set_tooltip_text (_("Main Menu")); 32 | menu_button.set_icon_name ("open-menu-symbolic"); 33 | menu_button.set_menu_model (menu); 34 | 35 | header = new Adw.HeaderBar (); 36 | header.set_title_widget (window_title); 37 | header.pack_end (menu_button); 38 | 39 | toolbar_view = new Adw.ToolbarView (); 40 | toolbar_view.add_top_bar (header); 41 | 42 | launcher_boxes = new List (); 43 | 44 | append (toolbar_view); 45 | } 46 | 47 | public void switch_mode (bool mode) { 48 | foreach (var box in launcher_boxes) { 49 | box.switch_mode (mode); 50 | } 51 | } 52 | 53 | public void switch_launcher (string title, int position) { 54 | window_title.set_title (title); 55 | 56 | var current_launcher_box = launcher_boxes.nth_data (position); 57 | 58 | if (current_launcher_box.get_parent () == null) 59 | toolbar_view.set_content (current_launcher_box); 60 | } 61 | 62 | public void initialize (List launchers) { 63 | foreach (var launcher in launchers) { 64 | var launcher_box = new Widgets.LauncherBox (launcher); 65 | launcher_boxes.append (launcher_box); 66 | } 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /src/widgets/launcher-box.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Widgets { 2 | public class LauncherBox : Gtk.Widget { 3 | Models.Launcher launcher { get; set; } 4 | Gtk.BinLayout bin_layout { get; set; } 5 | Gtk.Box content { get; set; } 6 | Adw.Clamp clamp { get; set; } 7 | Gtk.ScrolledWindow scrolled_window { get; set; } 8 | List runner_groups; 9 | 10 | public LauncherBox (Models.Launcher launcher) { 11 | this.launcher = launcher; 12 | 13 | set_vexpand (true); 14 | set_margin_start (15); 15 | set_margin_end (15); 16 | set_margin_bottom (15); 17 | 18 | bin_layout = new Gtk.BinLayout (); 19 | set_layout_manager (bin_layout); 20 | 21 | content = new Gtk.Box (Gtk.Orientation.VERTICAL, 15); 22 | 23 | clamp = new Adw.Clamp (); 24 | clamp.set_maximum_size (700); 25 | clamp.set_child (content); 26 | 27 | scrolled_window = new Gtk.ScrolledWindow (); 28 | scrolled_window.set_child (clamp); 29 | scrolled_window.set_parent (this); 30 | 31 | runner_groups = new List (); 32 | 33 | foreach (var group in launcher.groups) { 34 | var runner_group = new RunnerGroup (group); 35 | runner_groups.append (runner_group); 36 | content.append (runner_group); 37 | } 38 | } 39 | 40 | public void switch_mode (bool mode) { 41 | foreach (var runner_group in runner_groups) { 42 | runner_group.load (mode); 43 | } 44 | } 45 | } 46 | } -------------------------------------------------------------------------------- /src/widgets/load-more-row.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Widgets { 2 | public class LoadMoreRow : Adw.ActionRow { 3 | Gtk.Button load_button { get; set; } 4 | Gtk.Box suffix_box { get; set; } 5 | 6 | construct { 7 | load_button = new Gtk.Button.from_icon_name ("dots-symbolic"); 8 | load_button.set_tooltip_text (_("Load more")); 9 | load_button.add_css_class ("flat"); 10 | load_button.clicked.connect (() => activated ()); 11 | 12 | suffix_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0); 13 | suffix_box.set_margin_end (10); 14 | suffix_box.set_valign (Gtk.Align.CENTER); 15 | suffix_box.append (load_button); 16 | 17 | set_title (_("Load more")); 18 | add_suffix (suffix_box); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /src/widgets/release-row.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Widgets { 2 | public abstract class ReleaseRow : Adw.ActionRow { 3 | protected Gtk.Button install_button { get; set; } 4 | protected Gtk.Button remove_button { get; set; } 5 | protected Gtk.Button info_button { get; set; } 6 | protected Gtk.Box input_box { get; set; } 7 | 8 | construct { 9 | remove_button = new Gtk.Button.from_icon_name ("trash-symbolic"); 10 | remove_button.set_tooltip_text (_("Delete %s").printf (title)); 11 | remove_button.add_css_class ("flat"); 12 | 13 | install_button = new Gtk.Button.from_icon_name ("download-symbolic"); 14 | install_button.set_tooltip_text (_("Install %s").printf (title)); 15 | install_button.add_css_class ("flat"); 16 | 17 | info_button = new Gtk.Button.from_icon_name ("info-circle-symbolic"); 18 | info_button.set_tooltip_text (_("Show more information")); 19 | info_button.add_css_class ("flat"); 20 | 21 | input_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 10); 22 | input_box.set_margin_end (10); 23 | input_box.set_valign (Gtk.Align.CENTER); 24 | input_box.append (info_button); 25 | input_box.append (remove_button); 26 | input_box.append (install_button); 27 | 28 | add_suffix (input_box); 29 | 30 | install_button.clicked.connect (install_button_clicked); 31 | remove_button.clicked.connect (remove_button_clicked); 32 | info_button.clicked.connect (info_button_clicked); 33 | } 34 | 35 | protected abstract void install_button_clicked (); 36 | 37 | protected abstract void remove_button_clicked (); 38 | 39 | protected abstract void info_button_clicked (); 40 | } 41 | } -------------------------------------------------------------------------------- /src/widgets/release-rows/basic.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Widgets.ReleaseRows { 2 | public class Basic : ReleaseRow { 3 | Models.Releases.Basic release { get; set; } 4 | 5 | public Basic (Models.Releases.Basic release) { 6 | this.release = release; 7 | 8 | if (release.description == null || release.page_url == null) 9 | input_box.remove (info_button); 10 | 11 | release.notify["displayed-title"].connect (release_displayed_title_changed); 12 | 13 | release_displayed_title_changed (); 14 | 15 | release.notify["state"].connect (release_state_changed); 16 | 17 | release_state_changed (); 18 | } 19 | 20 | protected override void install_button_clicked () { 21 | var install_dialog = new Dialogs.InstallDialog (release); 22 | install_dialog.present (Application.window); 23 | 24 | release.send_message.connect (install_dialog.add_text); 25 | 26 | release.install.begin ((obj, res) => { 27 | var success = release.install.end (res); 28 | 29 | install_dialog.done (success || release.canceled); 30 | }); 31 | } 32 | 33 | protected override void remove_button_clicked () { 34 | var alert_dialog = new Adw.AlertDialog (_("Delete %s").printf (release.title), "%s\n\n%s".printf (_("You're about to remove %s from your system.").printf (release.title), _("Are you sure you want this?"))); 35 | 36 | alert_dialog.add_response ("no", _("No")); 37 | alert_dialog.add_response ("yes", _("Yes")); 38 | 39 | alert_dialog.set_response_appearance ("no", Adw.ResponseAppearance.DEFAULT); 40 | alert_dialog.set_response_appearance ("yes", Adw.ResponseAppearance.DESTRUCTIVE); 41 | 42 | alert_dialog.choose.begin (Application.window, null, (obj, res) => { 43 | var response = alert_dialog.choose.end (res); 44 | 45 | if (response != "yes") 46 | return; 47 | 48 | var remove_dialog = new Dialogs.RemoveDialog (release); 49 | remove_dialog.present (Application.window); 50 | 51 | release.send_message.connect (remove_dialog.add_text); 52 | 53 | release.remove.begin (new Models.Parameters (), (obj, res) => { 54 | var success = release.remove.end (res); 55 | 56 | remove_dialog.done (success); 57 | }); 58 | }); 59 | } 60 | 61 | protected override void info_button_clicked () { 62 | var description_dialog = new DescriptionDialog (release); 63 | description_dialog.present (Application.window); 64 | } 65 | 66 | void release_displayed_title_changed () { 67 | set_title (release.displayed_title); 68 | } 69 | 70 | void release_state_changed () { 71 | var installed = release.state == Models.Release.State.UP_TO_DATE; 72 | 73 | install_button.set_visible (!installed); 74 | remove_button.set_visible (installed); 75 | } 76 | } 77 | } -------------------------------------------------------------------------------- /src/widgets/release-rows/steamtinkerlaunch.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Widgets.ReleaseRows { 2 | public class SteamTinkerLaunch : ReleaseRow { 3 | public Gtk.Button upgrade_button { get; set; } 4 | 5 | Models.Releases.SteamTinkerLaunch release; 6 | 7 | construct { 8 | upgrade_button = new Gtk.Button (); 9 | upgrade_button.add_css_class ("flat"); 10 | upgrade_button.clicked.connect (upgrade_button_clicked); 11 | 12 | input_box.append (upgrade_button); 13 | } 14 | 15 | public SteamTinkerLaunch (Models.Releases.SteamTinkerLaunch release) { 16 | this.release = release; 17 | 18 | if (release.runner.group.launcher.installation_type != Models.Launcher.InstallationTypes.SYSTEM) { 19 | input_box.remove (install_button); 20 | input_box.remove (remove_button); 21 | input_box.remove (upgrade_button); 22 | } else { 23 | input_box.remove (info_button); 24 | } 25 | 26 | release.notify["displayed-title"].connect (release_displayed_title_changed); 27 | 28 | release_displayed_title_changed (); 29 | 30 | release.notify["state"].connect (release_state_changed); 31 | 32 | release_state_changed (); 33 | } 34 | 35 | protected override void install_button_clicked () { 36 | // Steam Deck doesn't need any external dependencies. 37 | if (!Utils.System.IS_STEAM_OS) { 38 | var missing_dependencies = ""; 39 | 40 | var yad_installed = false; 41 | if (Utils.System.check_dependency ("yad")) { 42 | string stdout = Utils.System.run_command ("yad --version"); 43 | float version = float.parse (stdout.split (" ")[0]); 44 | yad_installed = version >= 7.2; 45 | } 46 | if (!yad_installed)missing_dependencies += "yad >= 7.2\n"; 47 | 48 | if (!Utils.System.check_dependency ("awk") && !Utils.System.check_dependency ("gawk"))missing_dependencies += "awk/gawk\n"; 49 | if (!Utils.System.check_dependency ("git"))missing_dependencies += "git\n"; 50 | if (!Utils.System.check_dependency ("pgrep"))missing_dependencies += "pgrep\n"; 51 | if (!Utils.System.check_dependency ("unzip"))missing_dependencies += "unzip\n"; 52 | if (!Utils.System.check_dependency ("wget"))missing_dependencies += "wget\n"; 53 | if (!Utils.System.check_dependency ("xdotool"))missing_dependencies += "xdotool\n"; 54 | if (!Utils.System.check_dependency ("xprop"))missing_dependencies += "xprop\n"; 55 | if (!Utils.System.check_dependency ("xrandr"))missing_dependencies += "xrandr\n"; 56 | if (!Utils.System.check_dependency ("xxd"))missing_dependencies += "xxd\n"; 57 | if (!Utils.System.check_dependency ("xwininfo"))missing_dependencies += "xwininfo\n"; 58 | 59 | if (missing_dependencies != "") { 60 | var alert_dialog = new Adw.AlertDialog (_("Missing dependencies!"), "%s\n\n%s\n%s".printf (_("You are missing the following dependencies for %s:").printf (title), missing_dependencies, _("Installation will be canceled."))); 61 | 62 | alert_dialog.add_response ("ok", _("OK")); 63 | 64 | alert_dialog.present (Widgets.Application.window); 65 | 66 | return; 67 | } 68 | } 69 | 70 | var has_external_install = release.detect_external_locations (); 71 | 72 | if (has_external_install) { 73 | var alert_dialog = new Adw.AlertDialog (_("Existing installation of %s").printf (title), "%s\n\n%s".printf (_("It looks like you currently have another version of %s which was not installed by ProtonPlus.").printf (title), _("Do you want to delete it and install %s with ProtonPlus?").printf (title))); 74 | 75 | alert_dialog.add_response ("cancel", _("Cancel")); 76 | alert_dialog.add_response ("ok", _("OK")); 77 | 78 | alert_dialog.set_response_appearance ("cancel", Adw.ResponseAppearance.DEFAULT); 79 | alert_dialog.set_response_appearance ("ok", Adw.ResponseAppearance.DESTRUCTIVE); 80 | 81 | alert_dialog.choose.begin (Widgets.Application.window, null, (obj, res) => { 82 | string response = alert_dialog.choose.end (res); 83 | 84 | if (response != "ok") 85 | return; 86 | 87 | start_install (); 88 | }); 89 | } else { 90 | start_install (); 91 | } 92 | } 93 | 94 | void start_install () { 95 | var install_dialog = new Dialogs.InstallDialog (release); 96 | install_dialog.present (Application.window); 97 | 98 | release.send_message.connect (install_dialog.add_text); 99 | 100 | release.install.begin ((obj, res) => { 101 | var success = release.install.end (res); 102 | 103 | install_dialog.done (success); 104 | }); 105 | } 106 | 107 | protected override void remove_button_clicked () { 108 | var remove_check = new Gtk.CheckButton.with_label (_("Check this to also remove your configuration files.")); 109 | 110 | var alert_dialog = new Adw.AlertDialog (_("Delete %s").printf (release.title), "%s\n\n%s".printf (_("You're about to remove %s from your system.").printf (release.title), _("Are you sure you want this?"))); 111 | 112 | alert_dialog.set_extra_child (remove_check); 113 | 114 | alert_dialog.add_response ("no", _("No")); 115 | alert_dialog.add_response ("yes", _("Yes")); 116 | 117 | alert_dialog.set_response_appearance ("no", Adw.ResponseAppearance.DEFAULT); 118 | alert_dialog.set_response_appearance ("yes", Adw.ResponseAppearance.DESTRUCTIVE); 119 | 120 | alert_dialog.response.connect ((response) => { 121 | if (response != "yes") 122 | return; 123 | 124 | var remove_dialog = new Dialogs.RemoveDialog (release); 125 | remove_dialog.present (Application.window); 126 | 127 | release.send_message.connect (remove_dialog.add_text); 128 | 129 | var parameters = new Models.Releases.SteamTinkerLaunch.STL_Remove_Parameters (); 130 | parameters.delete_config = remove_check.get_active (); 131 | parameters.user_request = true; 132 | 133 | release.remove.begin (parameters, (obj, res) => { 134 | var success = release.remove.end (res); 135 | 136 | remove_dialog.done (success); 137 | }); 138 | }); 139 | 140 | alert_dialog.present (Widgets.Application.window); 141 | } 142 | 143 | void upgrade_button_clicked () { 144 | if (release.state == Models.Release.State.UP_TO_DATE) 145 | return; 146 | 147 | var upgrade_dialog = new Dialogs.UpgradeDialog (release); 148 | upgrade_dialog.present (Application.window); 149 | 150 | release.send_message.connect (upgrade_dialog.add_text); 151 | 152 | release.upgrade.begin ((obj, res) => { 153 | var success = release.upgrade.end (res); 154 | 155 | upgrade_dialog.done (success); 156 | }); 157 | } 158 | 159 | protected override void info_button_clicked () { 160 | Adw.AlertDialog? alert_dialog = null; 161 | switch (release.runner.group.launcher.installation_type) { 162 | case Models.Launcher.InstallationTypes.FLATPAK : 163 | var command_label = new Gtk.Label ("flatpak install com.valvesoftware.Steam.Utility.steamtinkerlaunch"); 164 | command_label.set_selectable (true); 165 | alert_dialog = new Adw.AlertDialog (_("%s is not supported").printf ("Steam Flatpak"), _("To install %s for the %s, please run the following command:").printf (release.title, "Steam Flatpak")); 166 | alert_dialog.set_extra_child (command_label); 167 | break; 168 | case Models.Launcher.InstallationTypes.SNAP: 169 | alert_dialog = new Adw.AlertDialog (_("%s is not supported").printf ("Steam Snap"), _("There's currently no known way for us to install %s for the %s.").printf (release.title, "Steam Snap")); 170 | break; 171 | default: 172 | break; 173 | } 174 | if (alert_dialog != null) { 175 | alert_dialog.add_response ("ok", _("OK")); 176 | 177 | alert_dialog.present (Application.window); 178 | } 179 | } 180 | 181 | void release_displayed_title_changed () { 182 | set_title (release.displayed_title); 183 | } 184 | 185 | void release_state_changed () { 186 | var installed = release.state == Models.Release.State.UP_TO_DATE || release.state == Models.Release.State.UPDATE_AVAILABLE; 187 | var updated = release.state == Models.Release.State.UP_TO_DATE; 188 | 189 | install_button.set_visible (!installed); 190 | remove_button.set_visible (installed); 191 | upgrade_button.set_visible (installed); 192 | 193 | if (upgrade_button.get_visible ()) { 194 | upgrade_button.set_icon_name (updated ? "circle-check-symbolic" : "circle-chevron-up-symbolic"); 195 | upgrade_button.set_tooltip_text (updated ? _("%s is up-to-date").printf (release.title) : _("Update %s to the latest version").printf (release.title)); 196 | } 197 | } 198 | } 199 | } -------------------------------------------------------------------------------- /src/widgets/runner-group.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Widgets { 2 | public class RunnerGroup : Adw.PreferencesGroup { 3 | List rows; 4 | 5 | public Models.Group group; 6 | 7 | public RunnerGroup(Models.Group group) { 8 | this.group = group; 9 | 10 | set_title(group.title); 11 | set_description(group.description); 12 | 13 | load(false); 14 | } 15 | 16 | public void load(bool installed_only) { 17 | clear(); 18 | 19 | if (installed_only) 20 | load_installed_only(); 21 | else 22 | load_normal(); 23 | } 24 | 25 | void load_normal() { 26 | foreach (var runner in group.runners) { 27 | var runner_row = new RunnerRow(runner); 28 | add(runner_row); 29 | rows.append(runner_row); 30 | } 31 | } 32 | 33 | void load_installed_only() { 34 | try { 35 | var directory_path = group.launcher.directory + group.directory; 36 | File directory = File.new_for_path(directory_path); 37 | FileEnumerator? enumerator = directory.enumerate_children("standard::*", FileQueryInfoFlags.NONE, null); 38 | 39 | if (enumerator != null) { 40 | FileInfo? file_info; 41 | while ((file_info = enumerator.next_file()) != null) { 42 | if (file_info.get_file_type() != FileType.DIRECTORY) 43 | continue; 44 | 45 | var title = file_info.get_name(); 46 | var runner = new Models.Runners.Proton_GE(group); // FIXME This should be replaced by it's own model instead of using Proton_GE 47 | var release = new Models.Releases.Basic.simple(runner, title, directory_path + "/" + title); 48 | 49 | var remove_button = new Gtk.Button.from_icon_name("trash-symbolic"); 50 | remove_button.set_tooltip_text(_("Delete %s").printf(release.title)); 51 | remove_button.add_css_class("flat"); 52 | 53 | var input_box = new Gtk.Box(Gtk.Orientation.HORIZONTAL, 10); 54 | input_box.set_valign(Gtk.Align.CENTER); 55 | input_box.append(remove_button); 56 | 57 | var row = new Adw.ActionRow(); 58 | row.set_title(release.title); 59 | row.add_suffix(input_box); 60 | 61 | remove_button.clicked.connect(() => { 62 | var alert_dialog = new Adw.AlertDialog(_("Delete %s").printf(release.title), "%s\n\n%s".printf(_("You're about to remove %s from your system.").printf(release.title), _("Are you sure you want this?"))); 63 | 64 | alert_dialog.add_response("no", _("No")); 65 | alert_dialog.add_response("yes", _("Yes")); 66 | 67 | alert_dialog.set_response_appearance("no", Adw.ResponseAppearance.DEFAULT); 68 | alert_dialog.set_response_appearance("yes", Adw.ResponseAppearance.DESTRUCTIVE); 69 | 70 | alert_dialog.choose.begin(Application.window, null, (obj, res) => { 71 | var response = alert_dialog.choose.end(res); 72 | 73 | if (response != "yes") 74 | return; 75 | 76 | var remove_dialog = new Dialogs.RemoveDialog(release); 77 | remove_dialog.present(Application.window); 78 | 79 | release.send_message.connect((message) => { 80 | switch (release.state) { 81 | case Models.Release.State.BUSY_INSTALLING : 82 | remove_dialog.add_text(message); 83 | break; 84 | case Models.Release.State.BUSY_REMOVING : 85 | remove_dialog.add_text(message); 86 | break; 87 | default: 88 | break; 89 | } 90 | }); 91 | 92 | release.remove.begin(new Models.Parameters(), (obj, res) => { 93 | var success = release.remove.end(res); 94 | 95 | if (success) 96 | remove(row); 97 | 98 | remove_dialog.done(success); 99 | }); 100 | }); 101 | }); 102 | 103 | add(row); 104 | rows.append(row); 105 | } 106 | } 107 | } catch (Error e) { 108 | message(e.message); 109 | } 110 | } 111 | 112 | void clear() { 113 | if (rows == null)return; 114 | 115 | foreach (var row in rows) { 116 | remove(row); 117 | } 118 | 119 | rows = new List (); 120 | } 121 | } 122 | } -------------------------------------------------------------------------------- /src/widgets/runner-row.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Widgets { 2 | public class RunnerRow : Adw.ExpanderRow { 3 | Models.Runner runner { get; set; } 4 | Gtk.Spinner spinner { get; set; } 5 | LoadMoreRow load_more_row { get; set; } 6 | 7 | int count { get; set; } 8 | 9 | public RunnerRow (Models.Runner runner) { 10 | this.runner = runner; 11 | 12 | count = 0; 13 | 14 | load_more_row = new LoadMoreRow (); 15 | load_more_row.activated.connect (load_more_row_activated); 16 | 17 | spinner = new Gtk.Spinner (); 18 | spinner.set_visible (false); 19 | 20 | notify["expanded"].connect (() => expanded_changed (false)); 21 | 22 | set_title (runner.title); 23 | set_subtitle (runner.description); 24 | add_suffix (spinner); 25 | } 26 | 27 | void expanded_changed (bool force_load = false) { 28 | if (get_expanded ()) { 29 | var a = runner.releases.length () == 0 || force_load; 30 | var b = runner.releases.length () >= 0 && count == 0; 31 | if (a || b) { 32 | spinner.start (); 33 | spinner.set_visible (true); 34 | 35 | if (a) { 36 | runner.load.begin ((obj, res) => { 37 | insert_loaded_releases (runner.load.end (res)); 38 | }); 39 | } else if (b) { 40 | insert_loaded_releases (runner.releases); 41 | } 42 | } 43 | } 44 | } 45 | 46 | void insert_loaded_releases (List releases) { 47 | foreach (var release in releases) { 48 | if (release is Models.Releases.Basic || release is Models.Releases.GitHubAction) { 49 | var row = new Widgets.ReleaseRows.Basic ((Models.Releases.Basic) release); 50 | add_row (row); 51 | } else if (release is Models.Releases.SteamTinkerLaunch) { 52 | var row = new Widgets.ReleaseRows.SteamTinkerLaunch ((Models.Releases.SteamTinkerLaunch) release); 53 | add_row (row); 54 | } 55 | 56 | count++; 57 | } 58 | 59 | if (runner.has_more) 60 | add_row (load_more_row); 61 | 62 | spinner.stop (); 63 | spinner.set_visible (false); 64 | } 65 | 66 | void load_more_row_activated () { 67 | remove (load_more_row); 68 | expanded_changed (true); 69 | } 70 | } 71 | } -------------------------------------------------------------------------------- /src/widgets/sidebar-row.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Widgets { 2 | public class SidebarRow : Adw.ActionRow { 3 | Gtk.Image icon; 4 | 5 | public SidebarRow (string title, string type, string resource_path, string directory) { 6 | icon = new Gtk.Image.from_resource (resource_path); 7 | icon.set_pixel_size (48); 8 | 9 | add_prefix (icon); 10 | add_css_class ("sidebar-row"); 11 | set_title (title); 12 | set_subtitle (type); 13 | set_activatable (true); 14 | set_tooltip_text (directory); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /src/widgets/sidebar.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Widgets { 2 | public class Sidebar : Gtk.Box { 3 | Gtk.ListBox list_box { get; set; } 4 | unowned List launchers; 5 | Models.Launcher selected_launcher; 6 | 7 | construct { 8 | var window_title = new Adw.WindowTitle ("ProtonPlus", ""); 9 | 10 | var header_bar = new Adw.HeaderBar (); 11 | header_bar.set_title_widget (window_title); 12 | 13 | list_box = new Gtk.ListBox (); 14 | list_box.set_activate_on_single_click (true); 15 | list_box.set_selection_mode (Gtk.SelectionMode.SINGLE); 16 | list_box.add_css_class ("navigation-sidebar"); 17 | list_box.set_hexpand (true); 18 | list_box.set_vexpand (true); 19 | list_box.row_activated.connect (list_box_row_activated); 20 | list_box.row_selected.connect (list_box_row_selected); 21 | 22 | var toolbar_view = new Adw.ToolbarView (); 23 | toolbar_view.add_top_bar (header_bar); 24 | toolbar_view.set_content (list_box); 25 | 26 | append (toolbar_view); 27 | } 28 | 29 | public void initialize (List launchers) { 30 | list_box.remove_all (); 31 | 32 | this.launchers = launchers; 33 | 34 | foreach (var launcher in launchers) { 35 | var row = new SidebarRow (launcher.title, launcher.get_installation_type_title (), launcher.icon_path, launcher.directory); 36 | list_box.append (row); 37 | } 38 | } 39 | 40 | void list_box_row_activated (Gtk.ListBoxRow? row) { 41 | activate_action_variant ("win.set-nav-view-active", true); 42 | } 43 | 44 | void list_box_row_selected (Gtk.ListBoxRow? row) { 45 | selected_launcher = launchers.nth_data (row.get_index ()); 46 | activate_action_variant ("win.load-info-box", row.get_index ()); 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /src/widgets/status-box.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Widgets { 2 | public class StatusBox : Gtk.Box { 3 | Adw.ToolbarView toolbar_view { get; set; } 4 | Adw.HeaderBar header_bar { get; set; } 5 | Adw.StatusPage status_page { get; set; } 6 | 7 | construct { 8 | header_bar = new Adw.HeaderBar (); 9 | header_bar.add_css_class ("flat"); 10 | 11 | status_page = new Adw.StatusPage (); 12 | status_page.set_vexpand (true); 13 | status_page.set_hexpand (true); 14 | 15 | toolbar_view = new Adw.ToolbarView (); 16 | toolbar_view.add_top_bar (header_bar); 17 | toolbar_view.set_content (status_page); 18 | 19 | append (toolbar_view); 20 | } 21 | 22 | public void initialize (string? icon_name, string title, string description) { 23 | status_page.set_icon_name (icon_name); 24 | status_page.set_title (title); 25 | status_page.set_description (description); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/widgets/window.vala: -------------------------------------------------------------------------------- 1 | namespace ProtonPlus.Widgets { 2 | public class Window : Adw.ApplicationWindow { 3 | List launchers; 4 | 5 | Adw.NavigationSplitView navigation_split_view { get; set; } 6 | Adw.NavigationPage info_page { get; set; } 7 | Adw.NavigationPage sidebar_page { get; set; } 8 | Widgets.StatusBox status_box { get; set; } 9 | Widgets.InfoBox info_box { get; set; } 10 | Widgets.Sidebar sidebar { get; set; } 11 | Adw.Breakpoint breakpoint { get; set; } 12 | 13 | construct { 14 | set_application ((Adw.Application) GLib.Application.get_default ()); 15 | set_title (Config.APP_NAME); 16 | 17 | add_action (set_nav_view_active ()); 18 | add_action (load_info_box ()); 19 | add_action (set_installed_only ()); 20 | 21 | set_size_request (460, 600); 22 | 23 | status_box = new Widgets.StatusBox (); 24 | 25 | info_page = new Adw.NavigationPage.with_tag (info_box = new Widgets.InfoBox (), "InfoBox", "main"); 26 | 27 | sidebar_page = new Adw.NavigationPage.with_tag (sidebar = new Widgets.Sidebar (), "Sidebar", "sidebar"); 28 | 29 | navigation_split_view = new Adw.NavigationSplitView (); 30 | navigation_split_view.set_sidebar (sidebar_page); 31 | navigation_split_view.set_content (info_page); 32 | navigation_split_view.set_show_content (true); 33 | 34 | breakpoint = new Adw.Breakpoint (Adw.BreakpointCondition.parse ("max-width: 660px")); 35 | breakpoint.add_setter (navigation_split_view, "collapsed", true); 36 | 37 | add_breakpoint (breakpoint); 38 | } 39 | 40 | public void initialize () { 41 | launchers = Models.Launcher.get_all (); 42 | 43 | if (launchers.length () > 0) { 44 | info_box.initialize (launchers); 45 | sidebar.initialize (launchers); 46 | 47 | if (navigation_split_view.get_parent () == null) 48 | set_content (navigation_split_view); 49 | } else { 50 | status_box.initialize (null, _("Welcome to %s").printf (Config.APP_NAME), _("Install Steam, Lutris, Bottles or Heroic Games Launcher to get started.")); 51 | 52 | if (status_box.get_parent () == null) 53 | set_content (status_box); 54 | 55 | Timeout.add (10000, () => { 56 | initialize (); 57 | 58 | return false; 59 | }); 60 | } 61 | } 62 | 63 | SimpleAction set_installed_only () { 64 | SimpleAction action = new SimpleAction.stateful ("set-installed-only", VariantType.BOOLEAN, true); 65 | 66 | action.activate.connect ((variant) => { 67 | action.set_state (!action.get_state ().get_boolean ()); 68 | info_box.switch_mode (!action.get_state ().get_boolean ()); 69 | }); 70 | 71 | return action; 72 | } 73 | 74 | SimpleAction set_nav_view_active () { 75 | SimpleAction action = new SimpleAction ("set-nav-view-active", VariantType.BOOLEAN); 76 | 77 | action.activate.connect ((variant) => { 78 | navigation_split_view.set_show_content (variant.get_boolean ()); 79 | }); 80 | 81 | return action; 82 | } 83 | 84 | SimpleAction load_info_box () { 85 | SimpleAction action = new SimpleAction ("load-info-box", VariantType.INT32); 86 | 87 | action.activate.connect ((variant) => { 88 | info_box.switch_launcher (launchers.nth_data (variant.get_int32 ()).title, variant.get_int32 ()); 89 | }); 90 | 91 | return action; 92 | } 93 | } 94 | } --------------------------------------------------------------------------------