[
  {
    "path": ".gitattributes",
    "content": "# Ensure Windows-friendly line endings and encoding for batch and registry files\n*.bat text eol=crlf\n*.cmd text eol=crlf\n*.reg text eol=crlf working-tree-encoding=UTF-16LE-BOM\n"
  },
  {
    "path": ".github/CODE_OF_CONDUCT.md",
    "content": "\n# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, caste, color, religion, or sexual\nidentity and orientation.\n\nWe pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community.\n\n## Our Standards\n\nExamples of behavior that contributes to a positive environment for our\ncommunity include:\n\n* Demonstrating empathy and kindness toward other people\n* Being respectful of differing opinions, viewpoints, and experiences\n* Giving and gracefully accepting constructive feedback\n* Accepting responsibility and apologizing to those affected by our mistakes,\n  and learning from the experience\n* Focusing on what is best not just for us as individuals, but for the overall\n  community\n\nExamples of unacceptable behavior include:\n\n* The use of sexualized language or imagery, and sexual attention or advances of\n  any kind\n* Trolling, insulting or derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or email address,\n  without their explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Enforcement Responsibilities\n\nCommunity leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful.\n\nCommunity leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate.\n\n## Scope\n\nThis Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\nhttps://matrix.to/#/#winapps:matrix.org.\nAll complaints will be reviewed and investigated promptly and fairly.\n\nAll community leaders are obligated to respect the privacy and security of the\nreporter of any incident.\n\n## Enforcement Guidelines\n\nCommunity leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:\n\n### 1. Correction\n\n**Community Impact**: Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community.\n\n**Consequence**: A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested.\n\n### 2. Warning\n\n**Community Impact**: A violation through a single incident or series of\nactions.\n\n**Consequence**: A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or permanent\nban.\n\n### 3. Temporary Ban\n\n**Community Impact**: A serious violation of community standards, including\nsustained inappropriate behavior.\n\n**Consequence**: A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban.\n\n### 4. Permanent Ban\n\n**Community Impact**: Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior, harassment of an\nindividual, or aggression toward or disparagement of classes of individuals.\n\n**Consequence**: A permanent ban from any sort of public interaction within the\ncommunity.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage],\nversion 2.1, available at\n[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].\n\nCommunity Impact Guidelines were inspired by\n[Mozilla's code of conduct enforcement ladder][Mozilla CoC].\n\nFor answers to common questions about this code of conduct, see the FAQ at\n[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at\n[https://www.contributor-covenant.org/translations][translations].\n\n[homepage]: https://www.contributor-covenant.org\n[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html\n[Mozilla CoC]: https://github.com/mozilla/diversity\n[FAQ]: https://www.contributor-covenant.org/faq\n[translations]: https://www.contributor-covenant.org/translations\n"
  },
  {
    "path": ".github/CONTRIBUTING.md",
    "content": "# Contribution Guidelines\n\nThank you for contributing to winapps! Before you can contribute, we ask some things of you:\n\n- Please follow our Code of Conduct, the Contributor Covenant. You can find a copy in this repository or under https://www.contributor-covenant.org/\n- All Contributors have to sign a Developer Certificate of Origin, agreeing to license their contribution under the AGPLv3. Historically, we used to require a CLA because we had to relicense the codebase from ARR to AGPLv3; however, this is being phased out. You can find a copy of the DCO below or under https://developercertificate.org/.\n- Please follow code conventions enforced by `pre-commit`. To keep down CI usage, please run it locally before committing too.\n  See <https://pre-commit.com> for installation, then run `pre-commit install` inside the `winapps` repository you cloned.\n\n## About using Artificial Intelligence for pull requests\n\n> [!IMPORTANT]\n> If you are using any kind of AI assistance to contribute to WinApps, it must be disclosed in the pull request.\n\n### AI-generated code\n\nWhen using AI assistance, we expect contributors to understand the code that is produced and be able to answer critical questions about it. It isn't a maintainers job to review a PR so broken that it requires significant rework to be acceptable. In a perfect world, AI assistance would produce equal or higher quality work than any human. That isn't the world we live in today, and in most cases it's generating slop. A good rule of thumb is that if another person can easily tell a pull request is AI-generated, it needs some more work.\n\n### Other kinds of AI assistance\n\nCurrently, [CodeRabbit](https://coderabbit.ai) is configured to review pull requests *on demand* when `@coderabbitai review` is commented on pull requests.\nHowever, we ask of you to not use it for PRs of which you are the authors unless asked to. Additionally, please do not AI-generate descriptions for larger pull requests or reviews by hand. This does not include things like commit messages.\n\n### AI \"Art\"\n\nWe do not condone AI-generated \"art\", including AI-written and AI-produced tutorials, as well as AI-generated icons for contributed applications.\nAdditionally, please do not share these kinds of media on any official WinApps channel.\n\n## Guidelines for pre-defined applications\n\nSome pre-defined applications contain a header like:\n\n```\n# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n```\n\nThis is for historic reasons, see [LICENSE.md](../LICENSE.md) and [COPYRIGHT.md](../COPYRIGHT.md).\nWhen contributing new applications, please *do not* include such a header.\n\n## Developer Certificate of Origin\n\nDeveloper Certificate of Origin\nVersion 1.1\n\nCopyright (C) 2004, 2006 The Linux Foundation and its contributors.\n\nEveryone is permitted to copy and distribute verbatim copies of this\nlicense document, but changing it is not allowed.\n\n\nDeveloper's Certificate of Origin 1.1\n\nBy making a contribution to this project, I certify that:\n\n(a) The contribution was created in whole or in part by me and I\n    have the right to submit it under the open source license\n    indicated in the file; or\n\n(b) The contribution is based upon previous work that, to the best\n    of my knowledge, is covered under an appropriate open source\n    license and I have the right under that license to submit that\n    work with modifications, whether created in whole or in part\n    by me, under the same open source license (unless I am\n    permitted to submit under a different license), as indicated\n    in the file; or\n\n(c) The contribution was provided directly to me by some other\n    person who certified (a), (b) or (c) and I have not modified\n    it.\n\n(d) I understand and agree that this project and the contribution\n    are public and that a record of the contribution (including all\n    personal information I submit with it, including my sign-off) is\n    maintained indefinitely and may be redistributed consistent with\n    this project or the open source license(s) involved.\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/1-bug.yml",
    "content": "name: Bug Report\ndescription: File a bug report.\nlabels: [\"triage\"]\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        We cannot fix nor support all bugs caused by FreeRDP, especially on Wayland.\n        If you experience visual bugs, please open a discussion instead.\n\n  - type: textarea\n    id: what-happened\n    attributes:\n      label: What happened?\n      description: Also tell us, what did you expect to happen?\n      placeholder: Tell us what you see!\n      value: \"A bug happened!\"\n    validations:\n      required: true\n\n  - type: input\n    id: freerdp\n    attributes:\n      label: Your FreeRDP version and where you got it from\n      placeholder: \"FreeRDP 3.10 (Debian Backports)\"\n    validations:\n      required: true\n\n  - type: input\n    id: distro\n    attributes:\n      label: Your Linux distribution and version\n      placeholder: \"Debian Trixie\"\n    validations:\n      required: true\n\n  - type: textarea\n    id: config\n    attributes:\n      label: Your `winapps.conf`\n      description: Please copy and paste your `winapps.conf`. Make sure to not include any sensible data. This will be automatically formatted into code, so no need for backticks.\n      render: shell\n    validations:\n      required: true\n\n  - type: textarea\n    id: logs\n    attributes:\n      label: Logs\n      description: Give the output of WinApps, FreeRDP etc. where / if applicable.\n      render: shell\n\n  - type: checkboxes\n    id: terms\n    attributes:\n      label: Terms\n      options:\n        - label: I am running the latest version.\n          required: true\n        - label: To the best of my knowledge, this is a bug and not a setup nor a FreeRDP problem.\n          required: true\n        - label: I have checked for duplicate issues.\n          required: true\n        - label: I agree to follow this project's Code of Conduct.\n          required: true\n"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: Community Support\n    url: https://github.com/winapps-org/winapps/discussions\n    about: Get help with non-bug issues here. Please use this instead of filing bug reports.\n"
  },
  {
    "path": ".github/cla-signatures.csv",
    "content": "User Name;Repository Owner;Repository Name;CLA Title;Gist URL;Gist Version;Signed At;Revoked At;Signed for Organization\nsparky3387;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2025-08-31T12:31:32.643Z;2025-09-04T11:04:01.000Z;TRUE\nmatheusmelo18;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2025-08-30T15:57:01.072Z;2025-09-04T11:04:01.000Z;TRUE\nLibadoxon;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2025-08-29T09:53:59.385Z;2025-09-04T11:04:01.000Z;TRUE\nDreamail;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2025-08-29T06:43:07.752Z;2025-09-04T11:04:01.000Z;TRUE\nqueenkjuul;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2025-08-27T16:19:30.937Z;2025-09-04T11:04:01.000Z;TRUE\nDevZiaus;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2025-08-25T21:17:17.792Z;2025-09-04T11:04:01.000Z;TRUE\nnlogozzo;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2025-08-24T15:25:25.197Z;2025-09-04T11:04:01.000Z;TRUE\nosalbahr;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2025-08-24T15:09:01.053Z;2025-09-04T11:04:01.000Z;TRUE\n9Morello;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2025-08-19T12:14:39.707Z;2025-09-04T11:04:01.000Z;TRUE\nSunrongguo2008;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2025-08-06T15:56:33.681Z;2025-09-04T11:04:01.000Z;TRUE\nwovw;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2025-07-31T04:14:50.710Z;2025-09-04T11:04:01.000Z;TRUE\ndenisstrizhkin;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2025-07-07T14:43:28.482Z;2025-09-04T11:04:01.000Z;TRUE\njoeshachaf;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2025-06-29T21:17:46.041Z;2025-09-04T11:04:01.000Z;TRUE\nAldo-f;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2025-06-19T19:26:32.522Z;2025-09-04T11:04:01.000Z;TRUE\nMr-MyDooM;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2025-06-10T03:01:36.680Z;2025-09-04T11:04:01.000Z;TRUE\nTheowulf-dev;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2025-06-10T00:44:09.838Z;2025-09-04T11:04:01.000Z;TRUE\nJoAllg;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2025-06-04T21:04:46.931Z;2025-09-04T11:04:01.000Z;TRUE\nsears-s;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2025-05-24T02:56:00.518Z;2025-09-04T11:04:01.000Z;TRUE\negvrl;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2025-05-15T14:33:38.598Z;2025-09-04T11:04:01.000Z;TRUE\nthefiredragon;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2025-04-19T19:06:36.941Z;2025-09-04T11:04:01.000Z;TRUE\narwarw;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2025-04-19T16:12:55.873Z;2025-09-04T11:04:01.000Z;TRUE\nlinull24;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2025-04-11T17:32:09.319Z;2025-09-04T11:04:01.000Z;TRUE\nkroese;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2025-04-02T15:26:03.840Z;2025-09-04T11:04:01.000Z;TRUE\ndasinking;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2025-03-25T00:54:49.660Z;2025-09-04T11:04:01.000Z;TRUE\ntstormn3tw0rk;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2025-03-21T07:08:26.583Z;2025-09-04T11:04:01.000Z;TRUE\nlimemane;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2025-03-10T16:38:38.276Z;2025-09-04T11:04:01.000Z;TRUE\nborekon;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2025-02-24T11:40:24.101Z;2025-09-04T11:04:01.000Z;TRUE\nwhitewolf101;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2025-02-23T03:29:22.061Z;2025-09-04T11:04:01.000Z;TRUE\nraffaem;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2025-02-16T14:28:21.523Z;2025-09-04T11:04:01.000Z;TRUE\nmbekkomo;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2025-02-02T11:40:36.462Z;2025-09-04T11:04:01.000Z;TRUE\ntoastedcrumpets;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2025-01-16T13:53:21.167Z;2025-09-04T11:04:01.000Z;TRUE\nstarbr3aker;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2025-01-12T17:30:37.473Z;2025-09-04T11:04:01.000Z;TRUE\nDeluxe-7;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2025-01-11T01:48:33.638Z;2025-09-04T11:04:01.000Z;TRUE\nstceum;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2025-01-03T07:14:43.695Z;2025-09-04T11:04:01.000Z;TRUE\nmolostovvs;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2024-12-13T07:48:02.950Z;2025-09-04T11:04:01.000Z;TRUE\nmindset-tk;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2024-11-25T22:30:30.062Z;2025-09-04T11:04:01.000Z;TRUE\nlunatic-gh;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2024-11-18T20:39:24.413Z;2025-09-04T11:04:01.000Z;TRUE\nUsername404-59;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2024-10-08T21:06:39.926Z;2025-09-04T11:04:01.000Z;TRUE\nCHN-beta;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2024-10-05T06:12:56.133Z;2025-09-04T11:04:01.000Z;TRUE\neylenburg;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2024-10-02T13:49:45.131Z;2025-09-04T11:04:01.000Z;TRUE\ntristanRW;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2024-09-23T14:48:15.158Z;2025-09-04T11:04:01.000Z;TRUE\nMopigamesYT;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2024-09-19T14:54:16.014Z;2025-09-04T11:04:01.000Z;TRUE\nqueler;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2024-09-09T03:33:59.760Z;2025-09-04T11:04:01.000Z;TRUE\nC0rn3j;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2024-09-01T22:30:46.600Z;2025-09-04T11:04:01.000Z;TRUE\nFixeQyt;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2024-08-30T14:22:17.912Z;2025-09-04T11:04:01.000Z;TRUE\nRheaBarar;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2024-08-19T07:36:53.003Z;2025-09-04T11:04:01.000Z;TRUE\nCoruscant11;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2024-08-18T12:46:39.393Z;2025-09-04T11:04:01.000Z;TRUE\nescapefreeg;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2024-08-11T03:04:17.227Z;2025-09-04T11:04:01.000Z;TRUE\nitiligent;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2024-07-26T04:13:53.616Z;2025-09-04T11:04:01.000Z;TRUE\ngordoncheong;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2024-07-25T11:14:28.928Z;2025-09-04T11:04:01.000Z;TRUE\nbkanuka;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2024-07-08T19:31:49.299Z;2025-09-04T11:04:01.000Z;TRUE\nKazevic;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2024-07-02T06:46:56.451Z;2025-09-04T11:04:01.000Z;TRUE\nKernelGhost;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2024-07-01T06:44:06.681Z;2025-09-04T11:04:01.000Z;TRUE\nMrTumnis;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2024-06-13T00:24:25.715Z;2025-09-04T11:04:01.000Z;TRUE\nAlchemi1963;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2024-05-30T21:48:08.692Z;2025-09-04T11:04:01.000Z;TRUE\nGreatNovaDragon;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2024-04-20T20:34:17.381Z;2024-09-21T21:48:55.765Z;TRUE\nMatt-M-3;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2023-12-04T17:31:44.383Z;2025-09-04T11:04:01.000Z;TRUE\nHyperspeed1313;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2023-11-10T18:19:32.080Z;2025-09-04T11:04:01.000Z;TRUE\nnotPlancha;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2023-09-04T18:07:58.859Z;2025-09-04T11:04:01.000Z;TRUE\nfreechelmi;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2023-07-16T08:31:50.083Z;2025-09-04T11:04:01.000Z;TRUE\nLDprg;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2023-07-16T07:08:14.876Z;2025-09-04T11:04:01.000Z;TRUE\nfbartels;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2023-07-15T19:49:07.019Z;2025-09-04T11:04:01.000Z;TRUE\noskardotglobal;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2023-07-15T15:09:49.368Z;2025-09-04T11:04:01.000Z;TRUE\nLeonardo-DGS;winapps-org;;fcla.md;https://gist.github.com/oskardotglobal/35f0a72eb45fcc7087e535561383dbc5;1f2d08888e405a91582607bf95176a5755363929;2023-07-15T15:07:40.778Z;2025-09-04T11:04:01.000Z;TRUE\n"
  },
  {
    "path": ".gitignore",
    "content": "/.idea\n/.vscode\n/result\n.DS_Store\n**/.DS_Store\n"
  },
  {
    "path": ".pre-commit-config.yaml",
    "content": "exclude: ^(.+)\\.patch$\n\nrepos:\n  - repo: https://github.com/Lucas-C/pre-commit-hooks\n    rev: v1.5.5\n    hooks:\n      - id: chmod\n        args: [ \"775\" ]\n        files: (\\.sh|winapps)$\n      - id: forbid-crlf\n        exclude: '\\.(bat|cmd|reg)$'\n      - id: remove-crlf\n        exclude: '\\.(bat|cmd|reg)$'\n      - id: forbid-tabs\n      - id: remove-tabs\n        args: [ --whitespaces-count, \"4\" ]\n\n  - repo: https://github.com/pre-commit/pre-commit-hooks\n    rev: v6.0.0\n    hooks:\n      - id: check-added-large-files\n      - id: check-case-conflict\n      - id: check-executables-have-shebangs\n      - id: check-json\n      - id: check-merge-conflict\n      - id: check-shebang-scripts-are-executable\n      - id: check-symlinks\n      - id: check-toml\n      - id: check-vcs-permalinks\n      - id: check-xml\n      - id: check-yaml\n      - id: destroyed-symlinks\n      - id: detect-private-key\n      - id: end-of-file-fixer\n      - id: fix-byte-order-marker\n      - id: mixed-line-ending\n        exclude: '\\.(bat|cmd|reg)$'\n      - id: pretty-format-json\n        args: [ \"--autofix\", \"--no-sort-keys\" ]\n      - id: sort-simple-yaml\n      - id: trailing-whitespace\n\n  - repo: https://github.com/scop/pre-commit-shfmt\n    rev: v3.12.0-2\n    hooks:\n      - id: shfmt\n        args: [\"-i\", \"4\", \"-ci\", \"-s\"]\n\n  - repo: https://github.com/shellcheck-py/shellcheck-py\n    rev: v0.11.0.1\n    hooks:\n      - id: shellcheck\n"
  },
  {
    "path": "COPYRIGHT.md",
    "content": "Some of the files are\n\n    Copyright (c) 2024 fmstrat\n\nMany files also contain contributions from third parties.\nIn this case the original copyright of the contributions can be traced through the history of the source version control system.\n\nWhen that is not the case, the files contain a prominent notice stating the original copyright and applicable license, or come with their own dedicated COPYRIGHT and/or LICENSE file.\n"
  },
  {
    "path": "LICENSE.md",
    "content": "For copyright information, please see the [COPYRIGHT.md](./COPYRIGHT.md) file.\n\nThis project has files licensed under different licenses.\n\nThe original project by Fmstrat <https://github.com/Fmstrat/winapps/> is not free software.\nDue to lack of a license, it is All Rights Reserved by the original author.\n\nWe have tried contacting Fmstrat about this, but they abandoned the project and did not reply nor apply an open-source license to the project.\nHowever, almost all parts of the codebase have been rewritten and all new contributions require signing a Developer Certificate of Origin (or historically, a CLA; see [CONTRIBUTING.md](./.github/CONTRIBUTING.md)), making most parts of the codebase AGPLv3.\n\nRefer to a specific file for its respective license.\n\n# GNU AFFERO GENERAL PUBLIC LICENSE\n\nVersion 3, 19 November 2007\n\nCopyright (C) 2007 Free Software Foundation, Inc.\n<https://fsf.org/>\n\nEveryone is permitted to copy and distribute verbatim copies of this\nlicense document, but changing it is not allowed.\n\n## Preamble\n\nThe GNU Affero General Public License is a free, copyleft license for\nsoftware and other kinds of works, specifically designed to ensure\ncooperation with the community in the case of network server software.\n\nThe licenses for most software and other practical works are designed\nto take away your freedom to share and change the works. By contrast,\nour General Public Licenses are intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains\nfree software for all its users.\n\nWhen we speak of free software, we are referring to freedom, not\nprice. Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\nDevelopers that use our General Public Licenses protect your rights\nwith two steps: (1) assert copyright on the software, and (2) offer\nyou this License which gives you legal permission to copy, distribute\nand/or modify the software.\n\nA secondary benefit of defending all users' freedom is that\nimprovements made in alternate versions of the program, if they\nreceive widespread use, become available for other developers to\nincorporate. Many developers of free software are heartened and\nencouraged by the resulting cooperation. However, in the case of\nsoftware used on network servers, this result may fail to come about.\nThe GNU General Public License permits making a modified version and\nletting the public access it on a server without ever releasing its\nsource code to the public.\n\nThe GNU Affero General Public License is designed specifically to\nensure that, in such cases, the modified source code becomes available\nto the community. It requires the operator of a network server to\nprovide the source code of the modified version running there to the\nusers of that server. Therefore, public use of a modified version, on\na publicly accessible server, gives the public access to the source\ncode of the modified version.\n\nAn older license, called the Affero General Public License and\npublished by Affero, was designed to accomplish similar goals. This is\na different license, not a version of the Affero GPL, but Affero has\nreleased a new version of the Affero GPL which permits relicensing\nunder this license.\n\nThe precise terms and conditions for copying, distribution and\nmodification follow.\n\n## TERMS AND CONDITIONS\n\n### 0. Definitions.\n\n\"This License\" refers to version 3 of the GNU Affero General Public\nLicense.\n\n\"Copyright\" also means copyright-like laws that apply to other kinds\nof works, such as semiconductor masks.\n\n\"The Program\" refers to any copyrightable work licensed under this\nLicense. Each licensee is addressed as \"you\". \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\nTo \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of\nan exact copy. The resulting work is called a \"modified version\" of\nthe earlier work or a work \"based on\" the earlier work.\n\nA \"covered work\" means either the unmodified Program or a work based\non the Program.\n\nTo \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy. Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\nTo \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies. Mere interaction with a user\nthrough a computer network, with no transfer of a copy, is not\nconveying.\n\nAn interactive user interface displays \"Appropriate Legal Notices\" to\nthe extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License. If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n### 1. Source Code.\n\nThe \"source code\" for a work means the preferred form of the work for\nmaking modifications to it. \"Object code\" means any non-source form of\na work.\n\nA \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\nThe \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form. A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\nThe \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities. However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work. For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\nThe Corresponding Source need not include anything that users can\nregenerate automatically from other parts of the Corresponding Source.\n\nThe Corresponding Source for a work in source code form is that same\nwork.\n\n### 2. Basic Permissions.\n\nAll rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met. This License explicitly affirms your unlimited\npermission to run the unmodified Program. The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work. This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\nYou may make, run and propagate covered works that you do not convey,\nwithout conditions so long as your license otherwise remains in force.\nYou may convey covered works to others for the sole purpose of having\nthem make modifications exclusively for you, or provide you with\nfacilities for running those works, provided that you comply with the\nterms of this License in conveying all material for which you do not\ncontrol copyright. Those thus making or running the covered works for\nyou must do so exclusively on your behalf, under your direction and\ncontrol, on terms that prohibit them from making any copies of your\ncopyrighted material outside their relationship with you.\n\nConveying under any other circumstances is permitted solely under the\nconditions stated below. Sublicensing is not allowed; section 10 makes\nit unnecessary.\n\n### 3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\nNo covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\nWhen you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such\ncircumvention is effected by exercising rights under this License with\nrespect to the covered work, and you disclaim any intention to limit\noperation or modification of the work as a means of enforcing, against\nthe work's users, your or third parties' legal rights to forbid\ncircumvention of technological measures.\n\n### 4. Conveying Verbatim Copies.\n\nYou may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\nYou may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n### 5. Conveying Modified Source Versions.\n\nYou may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these\nconditions:\n\n-   a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n-   b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under\n    section 7. This requirement modifies the requirement in section 4\n    to \"keep intact all notices\".\n-   c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy. This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged. This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n-   d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\nA compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit. Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n### 6. Conveying Non-Source Forms.\n\nYou may convey a covered work in object code form under the terms of\nsections 4 and 5, provided that you also convey the machine-readable\nCorresponding Source under the terms of this License, in one of these\nways:\n\n-   a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n-   b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the Corresponding\n    Source from a network server at no charge.\n-   c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source. This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n-   d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge. You need not require recipients to copy the\n    Corresponding Source along with the object code. If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source. Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n-   e) Convey the object code using peer-to-peer transmission,\n    provided you inform other peers where the object code and\n    Corresponding Source of the work are being offered to the general\n    public at no charge under subsection 6d.\n\nA separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\nA \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal,\nfamily, or household purposes, or (2) anything designed or sold for\nincorporation into a dwelling. In determining whether a product is a\nconsumer product, doubtful cases shall be resolved in favor of\ncoverage. For a particular product received by a particular user,\n\"normally used\" refers to a typical or common use of that class of\nproduct, regardless of the status of the particular user or of the way\nin which the particular user actually uses, or expects or is expected\nto use, the product. A product is a consumer product regardless of\nwhether the product has substantial commercial, industrial or\nnon-consumer uses, unless such uses represent the only significant\nmode of use of the product.\n\n\"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to\ninstall and execute modified versions of a covered work in that User\nProduct from a modified version of its Corresponding Source. The\ninformation must suffice to ensure that the continued functioning of\nthe modified object code is in no case prevented or interfered with\nsolely because modification has been made.\n\nIf you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information. But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\nThe requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or\nupdates for a work that has been modified or installed by the\nrecipient, or for the User Product in which it has been modified or\ninstalled. Access to a network may be denied when the modification\nitself materially and adversely affects the operation of the network\nor violates the rules and protocols for communication across the\nnetwork.\n\nCorresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n### 7. Additional Terms.\n\n\"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law. If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\nWhen you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit. (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.) You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\nNotwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders\nof that material) supplement the terms of this License with terms:\n\n-   a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n-   b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n-   c) Prohibiting misrepresentation of the origin of that material,\n    or requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n-   d) Limiting the use for publicity purposes of names of licensors\n    or authors of the material; or\n-   e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n-   f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions\n    of it) with contractual assumptions of liability to the recipient,\n    for any liability that these contractual assumptions directly\n    impose on those licensors and authors.\n\nAll other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10. If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term. If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\nIf you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\nAdditional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions; the\nabove requirements apply either way.\n\n### 8. Termination.\n\nYou may not propagate or modify a covered work except as expressly\nprovided under this License. Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\nHowever, if you cease all violation of this License, then your license\nfrom a particular copyright holder is reinstated (a) provisionally,\nunless and until the copyright holder explicitly and finally\nterminates your license, and (b) permanently, if the copyright holder\nfails to notify you of the violation by some reasonable means prior to\n60 days after the cessation.\n\nMoreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\nTermination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License. If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n### 9. Acceptance Not Required for Having Copies.\n\nYou are not required to accept this License in order to receive or run\na copy of the Program. Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance. However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work. These actions infringe copyright if you do\nnot accept this License. Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n### 10. Automatic Licensing of Downstream Recipients.\n\nEach time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License. You are not responsible\nfor enforcing compliance by third parties with this License.\n\nAn \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations. If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\nYou may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License. For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n### 11. Patents.\n\nA \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based. The\nwork thus licensed is called the contributor's \"contributor version\".\n\nA contributor's \"essential patent claims\" are all patent claims owned\nor controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version. For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\nEach contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\nIn the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement). To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\nIf you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients. \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\nIf, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\nA patent license is \"discriminatory\" if it does not include within the\nscope of its coverage, prohibits the exercise of, or is conditioned on\nthe non-exercise of one or more of the rights that are specifically\ngranted under this License. You may not convey a covered work if you\nare a party to an arrangement with a third party that is in the\nbusiness of distributing software, under which you make payment to the\nthird party based on the extent of your activity of conveying the\nwork, and under which the third party grants, to any of the parties\nwho would receive the covered work from you, a discriminatory patent\nlicense (a) in connection with copies of the covered work conveyed by\nyou (or copies made from those copies), or (b) primarily for and in\nconnection with specific products or compilations that contain the\ncovered work, unless you entered into that arrangement, or that patent\nlicense was granted, prior to 28 March 2007.\n\nNothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n### 12. No Surrender of Others' Freedom.\n\nIf conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License. If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under\nthis License and any other pertinent obligations, then as a\nconsequence you may not convey it at all. For example, if you agree to\nterms that obligate you to collect a royalty for further conveying\nfrom those to whom you convey the Program, the only way you could\nsatisfy both those terms and this License would be to refrain entirely\nfrom conveying the Program.\n\n### 13. Remote Network Interaction; Use with the GNU General Public License.\n\nNotwithstanding any other provision of this License, if you modify the\nProgram, your modified version must prominently offer all users\ninteracting with it remotely through a computer network (if your\nversion supports such interaction) an opportunity to receive the\nCorresponding Source of your version by providing access to the\nCorresponding Source from a network server at no charge, through some\nstandard or customary means of facilitating copying of software. This\nCorresponding Source shall include the Corresponding Source for any\nwork covered by version 3 of the GNU General Public License that is\nincorporated pursuant to the following paragraph.\n\nNotwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU General Public License into a single\ncombined work, and to convey the resulting work. The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the work with which it is combined will remain governed by version\n3 of the GNU General Public License.\n\n### 14. Revised Versions of this License.\n\nThe Free Software Foundation may publish revised and/or new versions\nof the GNU Affero General Public License from time to time. Such new\nversions will be similar in spirit to the present version, but may\ndiffer in detail to address new problems or concerns.\n\nEach version is given a distinguishing version number. If the Program\nspecifies that a certain numbered version of the GNU Affero General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation. If the Program does not specify a version number of the\nGNU Affero General Public License, you may choose any version ever\npublished by the Free Software Foundation.\n\nIf the Program specifies that a proxy can decide which future versions\nof the GNU Affero General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\nLater license versions may give you additional or different\npermissions. However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n### 15. Disclaimer of Warranty.\n\nTHERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT\nWARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT\nLIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR\nA PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND\nPERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE\nDEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR\nCORRECTION.\n\n### 16. Limitation of Liability.\n\nIN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR\nCONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,\nINCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES\nARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT\nNOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR\nLOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM\nTO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER\nPARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.\n\n### 17. Interpretation of Sections 15 and 16.\n\nIf the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\nEND OF TERMS AND CONDITIONS\n"
  },
  {
    "path": "README.md",
    "content": "<p align=\"center\"><img align=\"center\" width=\"700\" src=\"./docs/readme/banner_dark.svg#gh-dark-mode-only\"/></p>\n<p align=\"center\"><img align=\"center\" width=\"700\" src=\"./docs/readme/banner_light.svg#gh-light-mode-only\"/></p>\n<hr>\n\nRun Windows applications (including [Microsoft 365](https://www.microsoft365.com/) and [Adobe Creative Cloud](https://www.adobe.com/creativecloud.html)) on GNU/Linux with `KDE Plasma`, `GNOME` or `XFCE`, integrated seamlessly as if they were native to the OS.\n\n<p align=\"center\"><img src=\"./docs/readme/demo.png\" width=1000 alt=\"WinApps Demonstration.\"></p>\n\n## Underlying Mechanism\nWinApps works by:\n1. Running Windows in a `Docker`, `Podman` or `libvirt` virtual machine.\n2. Querying Windows for all installed applications.\n3. Creating shortcuts to selected Windows applications on the host GNU/Linux OS.\n4. Using [`FreeRDP`](https://www.freerdp.com/) as a backend to seamlessly render Windows applications alongside GNU/Linux applications.\n\n## Additional Features\n- The GNU/Linux `/home` directory is accessible within Windows via the `\\\\tsclient\\home` mount.\n- Integration with `Nautilus`, allowing you to right-click files to open them with specific Windows applications based on the file MIME type.\n- The [official taskbar widget](https://github.com/winapps-org/WinApps-Launcher) enables seamless administration of the Windows subsystem and offers an easy way to launch Windows applications.\n- Microsoft Office links (e.g. ms-word://) from the host system are automatically opened in the Windows subsystem. (Note: You may need to use a [User Agent Switcher](https://github.com/ray-lothian/UserAgent-Switcher/) browser extension and set the User-Agent to Windows, as the Office webapps typically hide the \"Open in Desktop App\" option for Linux users.)\n\n## Supported Applications\n**WinApps supports <u>*ALL*</u> Windows applications.** Support does not, however, extend to kernel-level anti-cheat systems (e.g. Riot Vanguard).\n\nUniversal application support is achieved by:\n1. Scanning Windows for any community tested applications (list below).\n2. Scanning Windows for any other `.exe` files listed within the Windows Registry.\n\nCommunity tested applications benefit from high-resolution icons and pre-populated MIME types. This enables file managers to determine which Windows applications should open files based on file extensions. Icons for other detected applications are pulled from `.exe` files.\n\nContributing to the list of supported applications is encouraged through submission of pull requests! Please help us grow the WinApps community.\n\n*Please note that the provided list of community tested applications is community-driven. As such, some applications may not be tested and verified by the WinApps team.*\n\n### Community Tested Applications\n<table cellpadding=\"10\" cellspacing=\"0\" border=\"0\">\n    <tr>\n        <!-- Adobe Acrobat Pro -->\n        <td>\n            <img src=\"apps/acrobat-x-pro/icon.svg\" width=\"100\">\n        </td>\n        <td>\n            <b>Adobe Acrobat Pro</b><br>\n            (X)<br>\n            <i><a href=\"https://commons.wikimedia.org/wiki/File:Adobe_Acrobat_DC_logo_2020.svg\">Icon</a> in the Public Domain.</i>\n        </td>\n        <!-- Adobe After Effects -->\n        <td>\n            <img src=\"apps/aftereffects-cc/icon.svg\" width=\"100\">\n        </td>\n        <td>\n            <b>Adobe After Effects</b><br>\n            (CC)<br>\n            <i><a href=\"https://commons.wikimedia.org/wiki/File:Adobe_After_Effects_CC_icon.svg\">Icon</a> in the Public Domain.</i>\n        </td>\n    </tr>\n    <tr>\n        <!-- Adobe Audition -->\n        <td>\n            <img src=\"apps/audition-cc/icon.svg\" width=\"100\">\n        </td>\n        <td>\n            <b>Adobe Audition</b><br>\n            (CC)<br>\n            <i><a href=\"https://en.m.wikipedia.org/wiki/File:Adobe_Audition_CC_icon_%282020%29.svg\">Icon</a> in the Public Domain.</i>\n        </td>\n        <!-- Adobe Bridge -->\n        <td>\n            <img src=\"apps/bridge-cs6/icon.svg\" width=\"100\">\n        </td>\n        <td>\n            <b>Adobe Bridge</b><br>\n            (CS6, CC)<br>\n            <i><a href=\"https://en.m.wikipedia.org/wiki/File:Adobe_Bridge_CC_icon.svg\">Icon</a> in the Public Domain.</i>\n        </td>\n    </tr>\n    <tr>\n        <!-- Adobe Creative Cloud -->\n        <td>\n            <img src=\"apps/adobe-cc/icon.svg\" width=\"100\">\n        </td>\n        <td>\n            <b>Adobe Creative Cloud</b><br>\n            (CC)<br>\n            <i><a href=\"https://iconduck.com/icons/240218/adobe-creative-cloud\">Icon</a> under <a href=\"https://iconduck.com/licenses/mit\">MIT license</a>.</i>\n        </td>\n        <!-- Adobe Illustrator -->\n        <td>\n            <img src=\"apps/illustrator-cc/icon.svg\" width=\"100\">\n        </td>\n        <td>\n            <b>Adobe Illustrator</b><br>\n            (CC)<br>\n            <i><a href=\"https://commons.wikimedia.org/wiki/File:Adobe_Illustrator_CC_icon.svg\">Icon</a> in the Public Domain.</i>\n        </td>\n    </tr>\n    <tr>\n        <!-- Adobe InDesign -->\n        <td>\n            <img src=\"apps/indesign-cc/icon.svg\" width=\"100\">\n        </td>\n        <td>\n            <b>Adobe InDesign</b><br>\n            (CC)<br>\n            <i><a href=\"https://commons.wikimedia.org/wiki/File:Adobe_InDesign_CC_icon.svg\">Icon</a> in the Public Domain.</i>\n        </td>\n        <!-- Adobe Lightroom -->\n        <td>\n            <img src=\"apps/lightroom-cc/icon.svg\" width=\"100\">\n        </td>\n        <td>\n            <b>Adobe Lightroom</b><br>\n            (CC)<br>\n            <i><a href=\"https://commons.wikimedia.org/wiki/File:Adobe_Photoshop_Lightroom_CC_logo.svg\">Icon</a> in the Public Domain.</i>\n        </td>\n    </tr>\n    <tr>\n        <!-- Adobe Photoshop -->\n        <td>\n            <img src=\"apps/photoshop-cc/icon.svg\" width=\"100\">\n        </td>\n        <td>\n            <b>Adobe Photoshop</b><br>\n            (CS6, CC, 2022)<br>\n            <i><a href=\"https://commons.wikimedia.org/wiki/File:Adobe_Photoshop_CC_icon.svg\">Icon</a> in the Public Domain.</i>\n        </td>\n        <!-- Affinity Designer 2 -->\n        <td>\n            <img src=\"apps/afdesign/icon.svg\" width=\"100\">\n        </td>\n        <td>\n            <b>Affinity Designer 2</b><br>\n            <i><a href=\"https://commons.wikimedia.org/wiki/File:Affinity_Designer_V2_icon.svg\">Icon</a> under CC-BY-SA 4.0 via Serif Ltd.</i>\n        </td>\n    </tr>\n    <tr>\n        <!-- Affinity Photo 2 -->\n        <td>\n            <img src=\"apps/afphoto/icon.svg\" width=\"100\">\n        </td>\n        <td>\n            <b>Affinity Photo 2</b><br>\n            <i><a href=\"https://commons.wikimedia.org/wiki/File:Affinity_Photo_V2_icon.svg\">Icon</a> under CC-BY-SA 4.0 via Serif Ltd.</i>\n        </td>\n        <!-- Affinity Publisher 2 -->\n        <td>\n            <img src=\"apps/afpub/icon.svg\" width=\"100\">\n        </td>\n        <td>\n            <b>Affinity Publisher 2</b><br>\n            <i><a href=\"https://commons.wikimedia.org/wiki/File:Affinity_Publisher_V2_icon.svg\">Icon</a> under CC-BY-SA 4.0 via Serif Ltd.</i>\n        </td>\n        <tr>\n        <!-- Affinity by Canva (v3)-->\n        <td>\n            <img src=\"apps/affinity/icon.svg\" width=\"100\">\n        </td>\n            <td>\n            <b>Affinity by Canva (v3)</b><br>\n            <i><a href=\"https://commons.wikimedia.org/wiki/File:Affinity_(App)_Logo.svg\">Icon</a> in the Public Domain.</i>\n        </td>\n        <!-- Command Prompt -->\n        <td>\n            <img src=\"apps/cmd/icon.svg\" width=\"100\">\n        </td>\n        <td>\n            <b>Command Prompt</b><br>\n            (cmd.exe)<br>\n            <i><a href=\"https://github.com/microsoft/terminal/blob/main/res/terminal/Terminal.svg\">Icon</a> under <a href=\"https://github.com/microsoft/terminal/blob/main/LICENSE\">MIT license</a>.</i>\n        </td>\n    </tr>\n    <tr>\n        <!-- File Explorer -->\n        <td>\n            <img src=\"apps/explorer/icon.svg\" width=\"100\">\n        </td>\n        <td>\n            <b>File Explorer</b><br>\n            (Windows Explorer)<br>\n            <i><a href=\"https://en.wikipedia.org/wiki/File:Microsoft_PowerToys-Logo_File_Explorer_Preview_02.svg\">Icon</a> in the Public Domain.</i>\n        </td>\n        <!-- Internet Explorer -->\n        <td>\n            <img src=\"apps/iexplorer/icon.svg\" width=\"100\">\n        </td>\n        <td>\n            <b>Internet Explorer</b><br>\n            (11)<br>\n            <i><a href=\"https://commons.wikimedia.org/wiki/File:Internet_Explorer_10%2B11_logo.svg\">Icon</a> in the Public Domain.</i>\n        </td>\n    </tr>\n    <tr>\n         <!-- Microsoft Access -->\n        <td>\n            <img src=\"apps/access/icon.svg\" width=\"100\">\n        </td>\n        <td>\n            <b>Microsoft Access</b><br>\n            (2016, 2019, o365)<br>\n            <i><a href=\"https://en.wikipedia.org/wiki/File:Microsoft_Office_Access_(2025-present).svg\">Icon</a> in the Public Domain.</i>\n        </td>\n        <!-- Microsoft Excel -->\n        <td>\n            <img src=\"apps/excel/icon.svg\" width=\"100\">\n        </td>\n        <td>\n            <b>Microsoft Excel</b><br>\n            (2016, 2019, o365)<br>\n            <i><a href=\"https://en.wikipedia.org/wiki/File:Microsoft_Office_Excel_(2025–present).svg\">Icon</a> in the Public Domain.</i>\n        </td>\n    </tr>\n    <tr>\n        <!-- Microsoft Word -->\n        <td>\n            <img src=\"apps/word/icon.svg\" width=\"100\">\n        </td>\n        <td>\n            <b>Microsoft Word</b><br>\n            (2016, 2019, o365)<br>\n            <i><a href=\"https://en.wikipedia.org/wiki/File:Microsoft_Office_Word_(2025–present).svg\">Icon</a> in the Public Domain.</i>\n        </td>\n        <!-- Microsoft OneNote -->\n        <td>\n            <img src=\"apps/onenote/icon.svg\" width=\"100\">\n        </td>\n        <td>\n            <b>Microsoft OneNote</b><br>\n            (2016, 2019, o365)<br>\n            <i><a href=\"https://en.wikipedia.org/wiki/File:Microsoft_OneNote_Icon_(2025_-_present).svg\">Icon</a> in the Public Domain.</i>\n        </td>\n    </tr>\n    <tr>\n        <!-- Microsoft Outlook -->\n        <td>\n            <img src=\"apps/outlook/icon.svg\" width=\"100\">\n        </td>\n        <td>\n            <b>Microsoft Outlook</b><br>\n            (2016, 2019, o365)<br>\n            <i><a href=\"https://en.wikipedia.org/wiki/File:Microsoft_Outlook_Icon_(2025–present).svg\">Icon</a> in the Public Domain.</i>\n        </td>\n        <!-- Microsoft PowerPoint -->\n        <td>\n            <img src=\"apps/powerpoint/icon.svg\" width=\"100\">\n        </td>\n        <td>\n            <b>Microsoft PowerPoint</b><br>\n            (2016, 2019, o365)<br>\n            <i><a href=\"https://en.wikipedia.org/wiki/File:Microsoft_Office_PowerPoint_(2025–present).svg\">Icon</a> in the Public Domain.</i>\n            </td>\n    </tr>\n    <tr>\n        <!-- Microsoft Publisher -->\n        <td>\n            <img src=\"apps/publisher/icon.svg\" width=\"100\">\n        </td>\n        <td>\n            <b>Microsoft Publisher</b><br>\n            (2016, 2019, o365)<br>\n            <i><a href=\"https://en.m.wikipedia.org/wiki/File:Microsoft_Office_Publisher_(2019-present).svg\">Icon</a> in the Public Domain.</i>\n        </td>\n        <!-- Microsoft Visio -->\n        <td>\n            <img src=\"apps/visio/icon.svg\" width=\"100\">\n        </td>\n        <td>\n            <b>Microsoft Visio</b><br>\n            (Standard/Pro. 2021, Plan 2)<br>\n            <i><a href=\"https://en.m.wikipedia.org/wiki/File:Microsoft_Office_Visio_(2019).svg\">Icon</a> in the Public Domain.</i>\n        </td>\n    </tr>\n    <tr>\n        <!-- Microsoft Project -->\n        <td>\n            <img src=\"apps/project/icon.svg\" width=\"100\">\n        </td>\n        <td>\n            <b>Microsoft Project</b><br>\n            (Standard/Pro. 2021, Plan 3/5)<br>\n            <i><a href=\"https://en.m.wikipedia.org/wiki/File:Microsoft_Project_(2019–present).svg\">Icon</a> in the Public Domain.</i>\n        </td>\n        <!-- Microsoft Visual Studio -->\n        <td>\n            <img src=\"apps/visual-studio-pro/icon.svg\" width=\"100\">\n        </td>\n        <td>\n            <b>Microsoft Visual Studio</b><br>\n            (Comm./Pro./Ent. 2022)<br>\n            <i><a href=\"https://en.m.wikipedia.org/wiki/File:Visual_Studio_Icon_2022.svg\">Icon</a> in the Public Domain.</i>\n        </td>\n    </tr>\n    <tr>\n        <!-- Autodesk Fusion 360 -->\n        <td>\n            <img src=\"apps/fusion-360/icon.svg\" width=\"100\">\n        </td>\n        <td>\n            <b>Autodesk Fusion 360</b><br>\n            <i><a href=\"https://commons.wikimedia.org/wiki/File:Fusion360_Logo.svg\">Icon</a> in the Public Domain.</i>\n        </td>\n        <!-- mIRC -->\n        <td>\n            <img src=\"apps/mirc/icon.svg\" width=\"100\">\n        </td>\n        <td>\n            <b>mIRC</b><br>\n            <i><a href=\"https://en.wikipedia.org/wiki/MIRC#/media/File:Mircnewlogo.png\">Icon</a> in the Public Domain.</i>\n        </td>\n    </tr>\n    <tr>\n        <!-- PowerShell -->\n        <td>\n            <img src=\"apps/powershell/icon.svg\" width=\"100\">\n        </td>\n        <td>\n            <b>PowerShell</b><br>\n            <i><a href=\"https://iconduck.com/icons/102322/file-type-powershell\">Icon</a> under <a href=\"https://iconduck.com/licenses/mit\">MIT license</a>.</i>\n        </td>\n        <!-- Windows -->\n        <td>\n            <img src=\"install/windows.svg\" width=\"100\">\n        </td>\n        <td>\n            <b>Windows</b><br>\n            (Full RDP Session)<br>\n            <i><a href=\"url\">Icon</a> in the Public Domain.</i>\n        </td>\n    </tr>\n        </table>\n\n## Installation\n### Step 1: Configure a Windows VM\nBoth `Docker` and `Podman` are recommended backends for running the Windows virtual machine, as they facilitate an automated Windows installation process. WinApps is also compatible with `libvirt`. While this method requires considerably more manual configuration, it also provides greater virtual machine customisation options. All three methods leverage the `KVM` hypervisor, ensuring excellent virtual machine performance. Ultimately, the choice of backend depends on your specific use case.\n\nThe following guides are available:\n- [Creating a Windows VM with `Docker` or `Podman`](docs/docker.md)\n- [Creating a Windows VM with `libvirt`](docs/libvirt.md)\n\nIf you already have a Windows VM or server you wish to use with WinApps, you will still have to follow the [final steps described in the `libvirt` documentation](docs/libvirt.md#final-configuration-steps).\n\n### Step 2: Install Dependencies\nInstall the required dependencies.\n  - Debian/Ubuntu:\n      ```bash\n      sudo apt install -y curl dialog freerdp3-x11 git iproute2 libnotify-bin netcat-openbsd\n      ```\n\n> [!NOTE]\n> On Debian 12 (_\"bookworm\"_), you need to enable the `backports` repository for the `freerdp3-x11` package to become available.\n> For instructions, see https://backports.debian.org/Instructions.\n\n  - Fedora/RHEL:\n      ```bash\n      sudo dnf install -y curl dialog freerdp git iproute libnotify nmap-ncat\n      ```\n  - Arch Linux:\n      ```bash\n      sudo pacman -Syu --needed -y curl dialog freerdp git iproute2 libnotify openbsd-netcat\n      ```\n  - openSUSE:\n      ```bash\n      sudo zypper install -y curl dialog freerdp git iproute2 libnotify-tools netcat-openbsd\n      ```\n  - Gentoo Linux:\n      ```bash\n      sudo emerge --ask=n net-misc/curl dev-util/dialog net-misc/freerdp:3 dev-vcs/git sys-apps/iproute2 x11-libs/libnotify net-analyzer/openbsd-netcat\n      ```\n\n> [!NOTE]\n> WinApps requires `FreeRDP` version 3 or later. If not available for your distribution through your package manager, you can install the [Flatpak](https://flathub.org/apps/com.freerdp.FreeRDP):\n> ```bash\n> flatpak install flathub com.freerdp.FreeRDP\n> sudo flatpak override --filesystem=home com.freerdp.FreeRDP # To use `+home-drive`\n> ```\n> However, if you have weird issues like [#233](https://github.com/winapps-org/winapps/issues/233) when running Flatpak, please compile FreeRDP from source according to [this guide](https://github.com/FreeRDP/FreeRDP/wiki/Compilation).\n\n### Step 3: Create a WinApps Configuration File\nCreate a configuration file at `~/.config/winapps/winapps.conf` containing the following:\n```bash\n##################################\n#   WINAPPS CONFIGURATION FILE   #\n##################################\n\n# INSTRUCTIONS\n# - Leading and trailing whitespace are ignored.\n# - Empty lines are ignored.\n# - Lines starting with '#' are ignored.\n# - All characters following a '#' are ignored.\n\n# [WINDOWS USERNAME]\nRDP_USER=\"MyWindowsUser\"\n\n# [WINDOWS PASSWORD]\n# NOTES:\n# - If using FreeRDP v3.9.0 or greater, you *have* to set a password\n# - RDP_ASKPASS is provided as a more secure option to RDP_PASS:\n#   - Calls an external command and uses its stdout as the password\n#   - The password is not passed on the command line to freerdp, keeping it out of logs\n#   - If specified, takes precedence over RDP_PASS\n#   - Examples to use this:\n#     - RDP_ASKPASS=\"~/some-custom-command\"\n#     - RDP_ASKPASS=\"bash -c 'cat ~/.some-secret-file'\"\n#     - RDP_ASKPASS=\"bash -c 'kwallet-query --folder winapps --read-password rdp kdewallet'\"\n#\nRDP_PASS=\"MyWindowsPassword\"\nRDP_ASKPASS=\"\"\n\n# [WINDOWS DOMAIN]\n# DEFAULT VALUE: '' (BLANK)\nRDP_DOMAIN=\"\"\n\n# [WINDOWS IPV4 ADDRESS]\n# NOTES:\n# - If using 'libvirt', 'RDP_IP' will be determined by WinApps at runtime if left unspecified.\n# DEFAULT VALUE:\n# - 'docker': '127.0.0.1'\n# - 'podman': '127.0.0.1'\n# - 'libvirt': '' (BLANK)\nRDP_IP=\"127.0.0.1\"\n\n# [VM NAME]\n# NOTES:\n# - Only applicable when using 'libvirt'\n# - The libvirt VM name must match so that WinApps can determine VM IP, start the VM, etc.\n# DEFAULT VALUE: 'RDPWindows'\nVM_NAME=\"RDPWindows\"\n\n# [WINAPPS BACKEND]\n# DEFAULT VALUE: 'docker'\n# VALID VALUES:\n# - 'docker'\n# - 'podman'\n# - 'libvirt'\n# - 'manual'\nWAFLAVOR=\"docker\"\n\n# [DISPLAY SCALING FACTOR]\n# NOTES:\n# - If an unsupported value is specified, a warning will be displayed.\n# - If an unsupported value is specified, WinApps will use the closest supported value.\n# DEFAULT VALUE: '100'\n# VALID VALUES:\n# - '100'\n# - '140'\n# - '180'\nRDP_SCALE=\"100\"\n\n# [MOUNTING REMOVABLE PATHS FOR FILES]\n# NOTES:\n# - By default, `udisks` (which you most likely have installed) uses /run/media for mounting removable devices.\n#   This improves compatibility with most desktop environments (DEs).\n# ATTENTION: The Filesystem Hierarchy Standard (FHS) recommends /media instead. Verify your system's configuration.\n# - To manually mount devices, you may optionally use /mnt.\n# REFERENCE: https://wiki.archlinux.org/title/Udisks#Mount_to_/media\nREMOVABLE_MEDIA=\"/run/media\"\n\n# [ADDITIONAL FREERDP FLAGS & ARGUMENTS]\n# NOTES:\n# - You can try adding /network:lan to these flags in order to increase performance, however, some users have faced issues with this.\n#   If this does not work or if it does not work without the flag, you can try adding /nsc and /gfx.\n# DEFAULT VALUE: '/cert:tofu /sound /microphone +home-drive'\n# VALID VALUES: See https://github.com/awakecoding/FreeRDP-Manuals/blob/master/User/FreeRDP-User-Manual.markdown\nRDP_FLAGS=\"/cert:tofu /sound /microphone +home-drive\"\n\n# [NON FULL WINDOWS RDP FLAGS]\n# NOTES:\n# - Use these flags to pass specific flags to the freerdp command when you are starting a non-full RDP session (any other command than winapps windows)\n# DEFAULT_VALUES: ''\n# VALID_VALUES: See https://github.com/awakecoding/FreeRDP-Manuals/blob/master/User/FreeRDP-User-Manual.markdown\nRDP_FLAGS_NON_WINDOWS=\"\"\n\n# [FULL WINDOWS RDP FLAGS]\n# NOTES:\n# - Use these flags to pass specific flags to the freerdp command when you are starting a full RDP session (winapps windows)\n# DEFAULT_VALUES: ''\n# VALID_VALUES: See https://github.com/awakecoding/FreeRDP-Manuals/blob/master/User/FreeRDP-User-Manual.markdown\nRDP_FLAGS_WINDOWS=\"\"\n\n# [DEBUG WINAPPS]\n# NOTES:\n# - Creates and appends to ~/.local/share/winapps/winapps.log when running WinApps.\n# DEFAULT VALUE: 'true'\n# VALID VALUES:\n# - 'true'\n# - 'false'\nDEBUG=\"true\"\n\n# [AUTOMATICALLY PAUSE WINDOWS]\n# NOTES:\n# - This is currently INCOMPATIBLE with 'manual'.\n# DEFAULT VALUE: 'off'\n# VALID VALUES:\n# - 'on'\n# - 'off'\nAUTOPAUSE=\"off\"\n\n# [AUTOMATICALLY PAUSE WINDOWS TIMEOUT]\n# NOTES:\n# - This setting determines the duration of inactivity to tolerate before Windows is automatically paused.\n# - This setting is ignored if 'AUTOPAUSE' is set to 'off'.\n# - The value must be specified in seconds (to the nearest 10 seconds e.g., '30', '40', '50', etc.).\n# - For RemoteApp RDP sessions, there is a mandatory 20-second delay, so the minimum value that can be specified here is '20'.\n# - Source: https://techcommunity.microsoft.com/t5/security-compliance-and-identity/terminal-services-remoteapp-8482-session-termination-logic/ba-p/246566\n# DEFAULT VALUE: '300'\n# VALID VALUES: >=20\nAUTOPAUSE_TIME=\"300\"\n\n# [FREERDP COMMAND]\n# NOTES:\n# - WinApps will attempt to automatically detect the correct command to use for your system.\n# DEFAULT VALUE: '' (BLANK)\n# VALID VALUES: The command required to run FreeRDPv3 on your system (e.g., 'xfreerdp', 'xfreerdp3', etc.).\nFREERDP_COMMAND=\"\"\n\n# [TIMEOUTS]\n# NOTES:\n# - These settings control various timeout durations within the WinApps setup.\n# - Increasing the timeouts is only necessary if the corresponding errors occur.\n# - Ensure you have followed all the Troubleshooting Tips in the error message first.\n\n# PORT CHECK\n# - The maximum time (in seconds) to wait when checking if the RDP port on Windows is open.\n# - Corresponding error: \"NETWORK CONFIGURATION ERROR\" (exit status 13).\n# DEFAULT VALUE: '5'\nPORT_TIMEOUT=\"5\"\n\n# RDP CONNECTION TEST\n# - The maximum time (in seconds) to wait when testing the initial RDP connection to Windows.\n# - Corresponding error: \"REMOTE DESKTOP PROTOCOL FAILURE\" (exit status 14).\n# DEFAULT VALUE: '30'\nRDP_TIMEOUT=\"30\"\n\n# APPLICATION SCAN\n# - The maximum time (in seconds) to wait for the script that scans for installed applications on Windows to complete.\n# - Corresponding error: \"APPLICATION QUERY FAILURE\" (exit status 15).\n# DEFAULT VALUE: '60'\nAPP_SCAN_TIMEOUT=\"60\"\n\n# WINDOWS BOOT\n# - The maximum time (in seconds) to wait for the Windows VM to boot if it is not running, before attempting to launch an application.\n# DEFAULT VALUE: '120'\nBOOT_TIMEOUT=\"120\"\n\n# FREERDP RAIL HIDEF\n# - This option controls the value of the `hidef` option passed to the /app parameter of the FreeRDP command.\n# - Setting this option to 'off' may resolve window misalignment issues related to maximized windows.\n# DEFAULT VALUE: 'on'\nHIDEF=\"on\"\n```\n\n> [!IMPORTANT]\n> To safeguard your Windows password, ensure `~/.config/winapps/winapps.conf` is accessible only by your user account.\n> ```bash\n> chown $(whoami):$(whoami) ~/.config/winapps/winapps.conf\n> chmod 600 ~/.config/winapps/winapps.conf\n> ```\n\n> [!IMPORTANT]\n> `RDP_USER` and `RDP_PASS` must correspond to a complete Windows user account and password, such as those created during Windows setup or for a domain user. User/PIN combinations are not valid for RDP access.\n\n> [!IMPORTANT]\n> If you wish to use an alternative WinApps backend (other than `Docker`), uncomment and change `WAFLAVOR=\"docker\"` to `WAFLAVOR=\"podman\"` or `WAFLAVOR=\"libvirt\"`.\n\n#### Configuration Options Explained\n- If using a pre-existing Windows RDP server on your LAN, you must use `RDP_IP` to specify the location of the Windows server. You may also wish to configure a static IP address for this server.\n- If running a Windows VM using `libvirt` with NAT enabled, leave `RDP_IP` commented out and WinApps will auto-detect the local IP address for the VM.\n- For domain users, you can uncomment and change `RDP_DOMAIN`.\n- On high-resolution (UHD) displays, you can set `RDP_SCALE` to the scale you would like to use (100, 140 or 180).\n- To add additional flags to the FreeRDP call (e.g. `/prevent-session-lock 120`), uncomment and use the `RDP_FLAGS` configuration option.\n- For multi-monitor setups, you can try adding `/multimon` to `RDP_FLAGS`. A FreeRDP bug may result in a black screen however, in which case you should revert this change.\n- To enable non-English input and seamless language switching, you can try adding `/kbd:unicode` to `RDP_FLAGS`. This ensures client inputs are sent as Unicode sequences.\n- If you enable `DEBUG`, a log will be created on each application start in `~/.local/share/winapps/winapps.log`.\n- If using a system on which the FreeRDP command is not `xfreerdp` or `xfreerdp3`, the correct command can be specified using `FREERDP_COMMAND`.\n\n### Step 4: Test FreeRDP\n1. Test establishing an RDP session by running the following command, replacing the `/u:`, `/p:`, and `/v:` values with the correct values specified in `~/.config/winapps/winapps.conf`.\n\n    ```bash\n    xfreerdp3 /u:\"MyWindowsUser\" /p:\"MyWindowsPassword\" /v:127.0.0.1 /cert:tofu\n\n    # Or, if you are using Podman\n    podman unshare --rootless-netns xfreerdp3 /u:\"MyWindowsUser\" /p:\"MyWindowsPassword\" /v:127.0.0.1 /cert:tofu\n\n    # Or, if you installed FreeRDP using Flatpak\n    flatpak run --command=xfreerdp com.freerdp.FreeRDP /u:\"MyWindowsUser\" /p:\"MyWindowsPassword\" /v:127.0.0.1 /cert:tofu\n    ```\n\n    - Please note that the correct `FreeRDP` command may vary depending on your system (e.g. `xfreerdp`, `xfreerdp3`, etc.).\n    - Ensure you use the correct IP address for your Windows instance in the above command.\n    - If prompted within the terminal window, choose to accept the certificate permanently.\n\n    If the Windows desktop appears in a `FreeRDP` window, the configuration was successful and the correct RDP TLS certificate was enrolled on the Linux host. Disconnect from the RDP session and skip the following debugging step.\n\n2. [DEBUGGING STEP] If an outdated or expired certificate is detected, the `FreeRDP` command will display output resembling the following. In this case, the old certificate will need to be removed and a new RDP TLS certificate installed.\n\n    ```\n    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n    @           WARNING: CERTIFICATE NAME MISMATCH!           @\n    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n\n    The hostname used for this connection (192.168.122.2:3389)\n    does not match the name given in the certificate:\n    Common Name (CN):\n            RDPWindows\n    A valid certificate for the wrong name should NOT be trusted!\n\n    The host key for 192.168.122.2:3389 has changed\n\n    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n    @    WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED!     @\n    @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@\n\n    IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!\n    Someone could be eavesdropping on you right now (man-in-the-middle attack)!\n    It is also possible that a host key has just been changed.\n    The fingerprint for the host key sent by the remote host is 8e:b4:d2:8e:4e:14:e7:4e:82:9b:07:5b:e1:68:40:18:bc:db:5f:bc:29:0d:91:83:f9:17:f9:13:e6:51:dc:36\n    Please contact your system administrator.\n    Add correct host key in /home/rohanbarar/.config/freerdp/server/192.168.122.2_3389.pem to get rid of this message.\n    ```\n\n    If you experience the above error, delete any old or outdated RDP TLS certificates associated with Windows, as they can prevent `FreeRDP` from establishing a connection.\n\n    These certificates are located within `~/.config/freerdp/server/` and follow the naming format `<Windows-VM-IPv4-Address>_<RDP-Port>.pem` (e.g., `192.168.122.2_3389.pem`, `127.0.0.1_3389.pem`, etc.).\n\n    If you use FreeRDP for purposes other than WinApps, ensure you only remove certificates related to the relevant Windows VM. If no relevant certificates are found, no action is needed.\n\n    Following deletion, re-attempt establishing an RDP session.\n\n### Step 5: Run the WinApps Installer\nWith Windows still powered on, run the WinApps installer.\n\n```bash\nbash <(curl https://raw.githubusercontent.com/winapps-org/winapps/main/setup.sh)\n```\n\nOnce WinApps is installed, a list of additional arguments can be accessed by running `winapps-setup --help`.\n\n<img src=\"./docs/readme/installer.gif\" width=1000 alt=\"WinApps Installer Animation.\">\n\n## Adding Additional Pre-defined Applications\nAdding your own applications with custom icons and MIME types to the installer is easy. Simply copy one of the application configurations in the `apps` folder located within the WinApps repository, and:\n1. Modify the name and variables to reflect the appropriate/desired values for your application.\n2. Replace `icon.svg` with an SVG for your application (ensuring the icon is appropriately licensed).\n3. Remove and reinstall WinApps.\n4. Submit a pull request to add your application to WinApps as a community tested application once you have tested and verified your configuration (optional, but encouraged).\n\n## Running Applications Manually\nWinApps offers a manual mode for running applications that were not configured by the WinApps installer. This is completed with the `manual` flag. Executables that are in the Windows PATH do not require full path definition.\n\n```bash\nwinapps manual \"C:\\my\\directory\\executableNotInPath.exe\"\nwinapps manual executableInPath.exe\n```\n\n## Updating WinApps\nThe installer can be run multiple times. To update your installation of WinApps:\n1. Run the WinApps installer to remove WinApps from your system.\n2. Pull the latest changes from the WinApps GitHub repository.\n3. Re-install WinApps using the WinApps installer by running `winapps-setup`.\n\n## WinApps Launcher (Optional)\nThe [WinApps Launcher](https://github.com/winapps-org/winapps-launcher) provides a simple system tray menu that makes it easy to launch your installed Windows applications, open a full desktop RDP session, and control your Windows VM or container. You can start, stop, pause, reboot or hibernate Windows, as well as access your installed applications from a convenient list. This lightweight, optional tool helps streamline your overall WinApps experience.\n\n<img src=\"./docs/readme/launcher.gif\" width=1000 alt=\"WinApps Launcher Animation.\">\n\n## Installation using Nix\n\nFirst, follow Step 1 of the normal installation guide to create your VM.\nThen, install WinApps according to the following instructions.\n\nAfter installation, it will be available under `winapps`, with the installer being available under `winapps-setup`\nand the optional launcher being available under `winapps-launcher.`\n\n### Using standalone Nix\n\nFirst, make sure Flakes and the `nix` command are enabled.\nIn your `~/.config/nix/nix.conf`:\n```\nexperimental-features = nix-command flakes\n```\n\n```bash\nnix profile install github:winapps-org/winapps#winapps\nnix profile install github:winapps-org/winapps#winapps-launcher # optional\n```\n\n### On NixOS using Flakes\n\n```nix\n# flake.nix\n{\n  description = \"My configuration\";\n\n  inputs = {\n    nixpkgs.url = \"github:NixOS/nixpkgs/nixos-unstable\";\n\n    winapps = {\n      url = \"github:winapps-org/winapps\";\n      inputs.nixpkgs.follows = \"nixpkgs\";\n    };\n  };\n\n  outputs =\n    inputs@{\n      nixpkgs,\n      winapps,\n      ...\n    }:\n    {\n      nixosConfigurations.hostname = nixpkgs.lib.nixosSystem rec {\n        system = \"x86_64-linux\";\n\n        specialArgs = {\n          inherit inputs system;\n        };\n\n        modules = [\n          ./configuration.nix\n          (\n            {\n              pkgs,\n              system ? pkgs.system,\n              ...\n            }:\n            {\n              environment.systemPackages = [\n                winapps.packages.\"${system}\".winapps\n                winapps.packages.\"${system}\".winapps-launcher # optional\n              ];\n            }\n          )\n        ];\n      };\n    };\n}\n```\n\n### On NixOS without Flakes\n\n[Flakes aren't real and they can't hurt you.](https://jade.fyi/blog/flakes-arent-real/).\nHowever, if you still don't want to use flakes, you can use WinApps with flake-compat like:\n\n```nix\n# configuration.nix\n{\n  pkgs,\n  system ? pkgs.system,\n  ...\n}:\n{\n  # set up binary cache (optional)\n  nix.settings = {\n    substituters = [ \"https://winapps.cachix.org/\" ];\n    trusted-public-keys = [ \"winapps.cachix.org-1:HI82jWrXZsQRar/PChgIx1unmuEsiQMQq+zt05CD36g=\" ];\n    trusted-users = [ \"<your username>\" ]; # replace with your username\n  };\n\n  environment.systemPackages =\n    let\n      winapps =\n        (import (builtins.fetchTarball \"https://github.com/winapps-org/winapps/archive/main.tar.gz\"))\n        .packages.\"${system}\";\n    in\n    [\n      winapps.winapps\n      winapps.winapps-launcher # optional\n    ];\n}\n```\n\n## Star History\n<a href=\"https://star-history.com/#winapps-org/winapps&Date\">\n <picture>\n   <source media=\"(prefers-color-scheme: dark)\" srcset=\"https://api.star-history.com/svg?repos=winapps-org/winapps&type=Date&theme=dark\"/>\n   <source media=\"(prefers-color-scheme: light)\" srcset=\"https://api.star-history.com/svg?repos=winapps-org/winapps&type=Date\"/>\n   <img alt=\"Star History Chart\" src=\"https://api.star-history.com/svg?repos=winapps-org/winapps&type=Date\"/>\n </picture>\n</a>\n"
  },
  {
    "path": "apps/ChemDraw/info",
    "content": "# GNOME shortcut name\nNAME=\"ChemDraw\"\n\n# Used for descriptions and window class\nFULL_NAME=\"ChemDraw Prime\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\RevvitySignalsSoftware\\ChemDrawApplications_x64\\ChemDraw.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Science\"\n\n# GNOME mimetypes; file types: .cdxml .cdx .chm\nMIME_TYPES=\"chemical/x-cdx;application/vnd.chemdraw+xml;chemical/x-chemdraw;\"\n"
  },
  {
    "path": "apps/access/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"Access\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft Access\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Microsoft Office\\root\\Office16\\MSACCESS.EXE\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/x-msaccess;\"\n"
  },
  {
    "path": "apps/access-o365/info",
    "content": "# Copyright (c) 2024 Jon Champagne\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"Access\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft Access\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Microsoft Office\\Office16\\MSACCESS.EXE\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/x-msaccess;\"\n"
  },
  {
    "path": "apps/access-o365-x86/info",
    "content": "# GNOME shortcut name\nNAME=\"Access\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft Access\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files (x86)\\Microsoft Office\\Office16\\MSACCESS.EXE\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/x-msaccess;\"\n"
  },
  {
    "path": "apps/access-x86/info",
    "content": "# GNOME shortcut name\nNAME=\"Access\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft Access\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files (x86)\\Microsoft Office\\root\\Office16\\MSACCESS.EXE\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/x-msaccess;\"\n"
  },
  {
    "path": "apps/acrobat-dc/info",
    "content": "# GNOME shortcut name\nNAME=\"Adobe Acrobat DC\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Adobe Acrobat DC\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Adobe\\Acrobat DC\\Acrobat\\Acrobat.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Documents\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/pdf;\"\n"
  },
  {
    "path": "apps/acrobat-x-pro/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"Acrobat X Pro\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Acrobat X Pro\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files (x86)\\Adobe\\Acrobat 10.0\\Acrobat\\Acrobat.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Documents\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/pdf;\"\n"
  },
  {
    "path": "apps/acrobat9/info",
    "content": "# GNOME shortcut name\nNAME=\"Acrobat 9\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Acrobat 9\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files (x86)\\Adobe\\Acrobat 9.0\\Acrobat\\Acrobat.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Documents\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/pdf;\"\n"
  },
  {
    "path": "apps/adobe-cc/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"Creative Cloud\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Adobe Creative Cloud\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Adobe\\Adobe Creative Cloud\\ACC\\Creative Cloud.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Adobe\"\n\n# GNOME mimetypes\nMIME_TYPES=\"\"\n\n# System Icon\nICON=\"AdobeUpdate\"\n"
  },
  {
    "path": "apps/adobe-digital-editions-4.5/info",
    "content": "# GNOME shortcut name\nNAME=\"Adobe Digital Editions\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Adobe Digital Editions\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files (x86)\\Adobe\\Adobe Digital Editions 4.5\\DigitalEditions.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Adobe\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/epub+zip;application/vnd.adobe.adept+xml;\"\n\n# System Icon\nICON=\"AdobeDigitalEditions\"\n"
  },
  {
    "path": "apps/afdesign/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"Designer\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Affinity Designer\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Affinity\\Designer 2\\Designer.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Affinity\"\n\n# GNOME mimetypes\nMIME_TYPES=\"\"\n\n# System Icon\nICON=\"AffinityDesigner\"\n"
  },
  {
    "path": "apps/affinity/info",
    "content": "# GNOME shortcut name\nNAME=\"Affinity\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Affinity by Canva\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Affinity\\Affinity\\Affinity.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Affinity\"\n\n# GNOME mimetypes\nMIME_TYPES=\"\"\n\n# System Icon\nICON=\"Affinity\"\n"
  },
  {
    "path": "apps/afphoto/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"Photo\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Affinity Photo\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Affinity\\Photo 2\\Photo.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Affinity\"\n\n# GNOME mimetypes\nMIME_TYPES=\"\"\n\n# System Icon\nICON=\"AffinityPhoto\"\n"
  },
  {
    "path": "apps/afpub/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"Publisher\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Affinity Publisher\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Affinity\\Publisher 2\\Publisher.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Affinity\"\n\n# GNOME mimetypes\nMIME_TYPES=\"\"\n\n# System Icon\nICON=\"AffinityPublisher\"\n"
  },
  {
    "path": "apps/aftereffects-2024/info",
    "content": "# GNOME shortcut name\nNAME=\"After Effects 2024\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Adobe After Effects 2024\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Adobe\\Adobe After Effects 2024\\Support Files\\AfterFX.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Adobe\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/vnd.adobe.aftereffects.project;application/vnd.adobe.aftereffects.template;\"\n\n# System Icon\nICON=\"AdobeAfterEffects\"\n"
  },
  {
    "path": "apps/aftereffects-cc/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"After Effects CC\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Adobe After Effects CC\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Adobe\\Adobe After Effects 2020\\Support Files\\AfterFX.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Adobe\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/vnd.adobe.aftereffects.project;application/vnd.adobe.aftereffects.template;\"\n\n# System Icon\nICON=\"AdobeAfterEffect\"\n"
  },
  {
    "path": "apps/audition-2024/info",
    "content": "# GNOME shortcut name\nNAME=\"Audition 2024\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Adobe Audition 2024\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Adobe\\Adobe Audition 2024\\Adobe Audition.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Adobe\"\n\n# GNOME mimetypes\nMIME_TYPES=\"\"\n\n# System Icon\nICON=\"AdobeAudition\"\n"
  },
  {
    "path": "apps/audition-cc/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"Audition CC\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Adobe Audition CC\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Adobe\\Adobe Audition 2020\\Adobe Audition.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Adobe\"\n\n# GNOME mimetypes\nMIME_TYPES=\"\"\n\n# System Icon\nICON=\"AdobeAudition\"\n"
  },
  {
    "path": "apps/bridge-cc/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"Bridge CS6\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Adobe Bridge CS6\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Adobe\\Adobe Bridge CS6 (64 Bit)\\Bridge.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Adobe\"\n\n# GNOME mimetypes\nMIME_TYPES=\"image/vnd.adobe.photoshop;\"\n\n# System Icon\nICON=\"AdobeBridge\"\n"
  },
  {
    "path": "apps/bridge-cs6/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"Bridge CS6\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Adobe Bridge CS6\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Adobe\\Adobe Bridge 2020\\Bridge.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Adobe\"\n\n# GNOME mimetypes\nMIME_TYPES=\"image/vnd.adobe.photoshop;\"\n\n# System Icon\nICON=\"AdobeBridge\"\n"
  },
  {
    "path": "apps/bridge-cs6-x86/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"Bridge CS6\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Adobe Bridge CS6\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files (x86)\\Adobe\\Adobe Bridge CS6\\Bridge.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Adobe\"\n\n# GNOME mimetypes\nMIME_TYPES=\"image/vnd.adobe.photoshop;\"\n\n# System Icon\nICON=\"AdobeBridge\"\n"
  },
  {
    "path": "apps/cmd/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"Cmd\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Command Prompt\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Windows\\System32\\cmd.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Windows\"\n\n# GNOME mimetypes\nMIME_TYPES=\"\"\n\n# System Icon\nICON=\"Terminal\"\n"
  },
  {
    "path": "apps/dorico-6/info",
    "content": "# GNOME shortcut name\nNAME=\"Dorico 6\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Steinberg Dorico 6\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Steinberg\\Dorico6\\Dorico6.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Audio;Graphics;Publishing;Music;\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/vnd.steinberg.dorico-project;\"\n\n# System Icon\nICON=\"Dorico\"\n"
  },
  {
    "path": "apps/dymo-connect/info",
    "content": "# GNOME shortcut name\nNAME=\"DYMO Connect\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Software for DYMO label printers\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files (x86)\\DYMO\\DYMO Connect\\DYMOConnect.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"\"\n\n# System Icon\nICON=\"DYMOConnect\"\n"
  },
  {
    "path": "apps/emclient/info",
    "content": "# GNOME shortcut name\nNAME=\"eM Client\"\n\n# Used for descriptions and window class\nFULL_NAME=\"eM Client\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files (x86)\\eM Client\\mailclient.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Network;Office;\"\n\n# GNOME mimetypes\nMIME_TYPES=\"\"\n\n# System Icon\nICON=\"eM Client\"\n"
  },
  {
    "path": "apps/excel/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"Excel\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft Excel\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Microsoft Office\\Office16\\EXCEL.EXE\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/vnd.ms-excel;application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;application/vnd.openxmlformats-officedocument.spreadsheetml.template;application/vnd.ms-excel.sheet.macroEnabled.12;application/vnd.ms-excel.template.macroEnabled.12;application/vnd.ms-excel.addin.macroEnabled.12;application/vnd.ms-excel.sheet.binary.macroEnabled.12;\"\n\n# System Icon\nICON=\"ms-excel\"\n"
  },
  {
    "path": "apps/excel-o365/info",
    "content": "# Copyright (c) 2024 Jon Champagne\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"Excel\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft Excel\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Microsoft Office\\root\\Office16\\EXCEL.EXE\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/vnd.ms-excel;application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;application/vnd.openxmlformats-officedocument.spreadsheetml.template;application/vnd.ms-excel.sheet.macroEnabled.12;application/vnd.ms-excel.template.macroEnabled.12;application/vnd.ms-excel.addin.macroEnabled.12;application/vnd.ms-excel.sheet.binary.macroEnabled.12;\"\n\n# System Icon\nICON=\"ms-excel\"\n"
  },
  {
    "path": "apps/excel-o365-x86/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"Excel\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft Excel\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files (x86)\\Microsoft Office\\root\\Office16\\EXCEL.EXE\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/vnd.ms-excel;application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;application/vnd.openxmlformats-officedocument.spreadsheetml.template;application/vnd.ms-excel.sheet.macroEnabled.12;application/vnd.ms-excel.template.macroEnabled.12;application/vnd.ms-excel.addin.macroEnabled.12;application/vnd.ms-excel.sheet.binary.macroEnabled.12;\"\n\n# System Icon\nICON=\"ms-excel\"s\n"
  },
  {
    "path": "apps/excel-x86/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"Excel\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft Excel\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files (x86)\\Microsoft Office\\Office16\\EXCEL.EXE\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/vnd.ms-excel;application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;application/vnd.openxmlformats-officedocument.spreadsheetml.template;application/vnd.ms-excel.sheet.macroEnabled.12;application/vnd.ms-excel.template.macroEnabled.12;application/vnd.ms-excel.addin.macroEnabled.12;application/vnd.ms-excel.sheet.binary.macroEnabled.12;\"\n\n# System Icon\nICON=\"ms-excel\"\n"
  },
  {
    "path": "apps/excel-x86-2010/info",
    "content": "# GNOME shortcut name\nNAME=\"Excel\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft Excel\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files (x86)\\Microsoft Office\\Office14\\EXCEL.EXE\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/vnd.ms-excel;application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;application/vnd.openxmlformats-officedocument.spreadsheetml.template;application/vnd.ms-excel.sheet.macroEnabled.12;application/vnd.ms-excel.template.macroEnabled.12;application/vnd.ms-excel.addin.macroEnabled.12;application/vnd.ms-excel.sheet.binary.macroEnabled.12;\"\n"
  },
  {
    "path": "apps/explorer/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"Explorer\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft Explorer\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Windows\\explorer.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Windows\"\n\n# GNOME mimetypes\nMIME_TYPES=\"\"\n"
  },
  {
    "path": "apps/fusion-360/info",
    "content": "# GNOME shortcut name\nNAME=\"Fusion 360\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Autodesk Fusion 360\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Users\\%USERNAME%\\AppData\\Local\\Autodesk\\webdeploy\\production\\6a0c9611291d45bb9226980209917c3d\\FusionLauncher.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Graphics;3DGraphics\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/fusion\"\n"
  },
  {
    "path": "apps/iexplorer/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"Internet Explorer\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Internet Explorer\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Internet Explorer\\iexplore.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Network;WebBrowser;\"\n\n# GNOME mimetypes\nMIME_TYPES=\"text/html;text/xml;application/xhtml+xml;application/xml;application/rss+xml;application/rdf+xml;image/gif;image/jpeg;image/png;x-scheme-handler/http;x-scheme-handler/https;x-scheme-handler/ftp;x-scheme-handler/chrome;video/webm;application/x-xpinstall;\"\n"
  },
  {
    "path": "apps/illustrator-2024/info",
    "content": "# GNOME shortcut name\nNAME=\"Illustrator 2024\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Adobe Illustrator 2024\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Adobe\\Adobe Illustrator 2024\\Support Files\\Contents\\Windows\\Illustrator.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Adobe\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/illustrator;\"\n\n# System Icon\nICON=\"AdobeIllustrator\"\n"
  },
  {
    "path": "apps/illustrator-cc/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"Illustrator CC\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Adobe Illustrator CC\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Adobe\\Adobe Illustrator 2021\\Support Files\\Contents\\Windows\\Illustrator.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Adobe\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/illustrator;\"\n\n# System Icon\nICON=\"AdobeIllustrator\"\n"
  },
  {
    "path": "apps/indesign-2024/info",
    "content": "# GNOME shortcut name\nNAME=\"InDesign 2024\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Adobe InDesign 2024\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Adobe\\Adobe InDesign 2024\\InDesign.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Adobe\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/x-adobe-indesign-interchange;application/x-adobe-indesign;\"\n\n# System Icon\nICON=\"AdobeIndesign\"\n"
  },
  {
    "path": "apps/indesign-cc/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"InDesign CC\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Adobe InDesign CC\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Adobe\\Adobe InDesign 2021\\InDesign.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Adobe\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/x-adobe-indesign-interchange;application/x-adobe-indesign;\"\n\n# System Icon\nICON=\"AdobeIndesign\"\n"
  },
  {
    "path": "apps/lightroom-cc/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"Lightroom CC\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Adobe Lightroom CC\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Adobe\\Adobe Lightroom CC\\lightroom.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Adobe\"\n\n# GNOME mimetypes\nMIME_TYPES=\"\"\n\n# System Icon\nICON=\"AdobeLightroom\"\n"
  },
  {
    "path": "apps/lightroom-classic/info",
    "content": "# GNOME shortcut name\nNAME=\"Lightroom Classic\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Adobe Lightroom Classic\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Adobe\\Adobe Lightroom Classic\\Lightroom.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Adobe\"\n\n# GNOME mimetypes\nMIME_TYPES=\"\"\n\n# System Icon\nICON=\"AdobeLightroom\"\n"
  },
  {
    "path": "apps/linqpad8/info",
    "content": "# GNOME shortcut name\nNAME=\"LINQPad8\"\n\n# Used for descriptions and window class\nFULL_NAME=\"LINQPad 8\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\LINQPad8\\LINQPad8.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Development\"\n\n# GNOME mimetypes\nMIME_TYPES=\"text/cs\"\n"
  },
  {
    "path": "apps/mediaencoder-2024/info",
    "content": "# GNOME shortcut name\nNAME=\"Media Encoder 2024\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Adobe Media Encoder 2024\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Adobe\\Adobe Media Encoder 2024\\Adobe Media Encoder.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Adobe\"\n\n# GNOME mimetypes\nMIME_TYPES=\"\"\n\n# System Icon\nICON=\"AdobeMediaEncoder\"\n"
  },
  {
    "path": "apps/mirc/info",
    "content": "# GNOME shortcut name\nNAME=\"mIRC\"\n\n# Used for descriptions and window class\nFULL_NAME=\"mIRC\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files (x86)\\mIRC\\mirc.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Network;IRCclient;\"\n\n# GNOME mimetypes\nMIME_TYPES=\"\"\n\n# System Icon\nICON=\"mIRC\"\n"
  },
  {
    "path": "apps/ms-office-protocol-handler.desktop",
    "content": "[Desktop Entry]\nName=Microsoft Office Protocol Handler\nComment=Handle Microsoft Office URI schemes via WinApps\nExec=winapps manual %u\nTerminal=false\nType=Application\nMimeType=x-scheme-handler/ms-word;x-scheme-handler/ms-excel;x-scheme-handler/ms-powerpoint;x-scheme-handler/ms-outlook;x-scheme-handler/ms-access;x-scheme-handler/ms-visio;x-scheme-handler/ms-project;x-scheme-handler/ms-teams;x-scheme-handler/ms-whiteboard;x-scheme-handler/ms-officeapp;\nNoDisplay=true\nCategories=Office;Utility;\n"
  },
  {
    "path": "apps/mspaint/info",
    "content": "# GNOME shortcut name\nNAME=\"Paint\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft Paint\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\windows\\system32\\mspaint.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Graphics\"\n\n# GNOME mimetypes\nMIME_TYPES=\"image/png;image/bmp;image/jpeg;image/jpe;image/jpeg;image/tiff;image/tif;image/x-ms-bmp;image/dib;image/vnd.microsoft.icon;image/x-icon;image/ico;image/icon;text/ico;application/ico\"\n"
  },
  {
    "path": "apps/onenote/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"OneNote\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft OneNote\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Microsoft Office\\Office16\\ONENOTE.EXE\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/msonenote;\"\n\n# System Icon\nICON=\"ms-outlook\"\n"
  },
  {
    "path": "apps/onenote-o365/info",
    "content": "# Copyright (c) 2024 Jon Champagne\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"OneNote\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft OneNote\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Microsoft Office\\root\\Office16\\ONENOTE.EXE\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/msonenote;\"\n\n# System Icon\nICON=\"ms-outlook\"\n"
  },
  {
    "path": "apps/onenote-o365-x86/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"OneNote\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft OneNote\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files (x86)\\Microsoft Office\\root\\Office16\\ONENOTE.EXE\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/msonenote;\"\n\n# System Icon\nICON=\"ms-outlook\"\n"
  },
  {
    "path": "apps/onenote-x86/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"OneNote\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft OneNote\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files (x86)\\Microsoft Office\\Office16\\ONENOTE.EXE\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/msonenote;\"\n\n# System Icon\nICON=\"ms-onenote\"\n"
  },
  {
    "path": "apps/outlook/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"Outlook\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft Outlook\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Microsoft Office\\Office16\\OUTLOOK.EXE\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/vnd.ms-outlook;application/octet-stream;\"\n\n# System Icon\nICON=\"ms-outlook\"\n"
  },
  {
    "path": "apps/outlook-o365/info",
    "content": "# Copyright (c) 2024 Jon Champagne\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"Outlook\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft Outlook\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Microsoft Office\\root\\Office16\\OUTLOOK.EXE\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/vnd.ms-outlook;application/octet-stream;\"\n\n# System Icon\nICON=\"ms-outlook\"\n"
  },
  {
    "path": "apps/outlook-o365-x86/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"Outlook\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft Outlook\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files (x86)\\Microsoft Office\\root\\Office16\\OUTLOOK.EXE\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/vnd.ms-outlook;application/octet-stream;\"\n\n# System Icon\nICON=\"ms-outlook\"\n"
  },
  {
    "path": "apps/outlook-x86/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"Outlook\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft Outlook\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files (x86)\\Microsoft Office\\Office16\\OUTLOOK.EXE\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/vnd.ms-outlook;application/octet-stream;\"\n\n# System Icon\nICON=\"ms-outlook\"\n"
  },
  {
    "path": "apps/paint.net/info",
    "content": "# GNOME shortcut name\nNAME=\"Paint.NET\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Paint.NET\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Paint.NET\\paintdotnet.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Graphic;\"\n\n# GNOME mimetypes\nMIME_TYPES=\"\"\n\n# System Icon\nICON=\"Paint.Net\"\n"
  },
  {
    "path": "apps/pdfgear/info",
    "content": "# GNOME shortcut name\nNAME=\"pdfgear\"\n\n# Used for descriptions and window class\nFULL_NAME=\"PDF Gear\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\PDFgear\\PDFLauncher.exe\"\n\n# GNOME categories\nCATEGORIES=\"Office;Viewer;Graphics;pdf\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/pdf;\"\n"
  },
  {
    "path": "apps/photoshop-2022/info",
    "content": "# GNOME shortcut name\nNAME=\"Photoshop 2022\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Adobe Photoshop 2022\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Adobe\\Adobe Photoshop 2022\\Photoshop.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Adobe\"\n\n# GNOME mimetypes\nMIME_TYPES=\"image/vnd.adobe.photoshop;\"\n\n# System Icon\nICON=\"AdobePhotoshop\"\n"
  },
  {
    "path": "apps/photoshop-2024/info",
    "content": "# GNOME shortcut name\nNAME=\"Photoshop 2024\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Adobe Photoshop 2024\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Adobe\\Adobe Photoshop 2024\\Photoshop.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Adobe\"\n\n# GNOME mimetypes\nMIME_TYPES=\"image/vnd.adobe.photoshop;\"\n\n# System Icon\nICON=\"AdobePhotoshop\"\n"
  },
  {
    "path": "apps/photoshop-2025/info",
    "content": "# GNOME shortcut name\nNAME=\"Photoshop 2025\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Adobe Photoshop 2025\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Adobe\\Adobe Photoshop 2025\\Photoshop.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Adobe\"\n\n# GNOME mimetypes\nMIME_TYPES=\"image/vnd.adobe.photoshop;\"\n\n# System Icon\nICON=\"AdobePhotoshop\"\n"
  },
  {
    "path": "apps/photoshop-cc/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"Photoshop CC\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Adobe Photoshop CC\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Adobe\\Adobe Photoshop 2020\\Photoshop.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Adobe\"\n\n# GNOME mimetypes\nMIME_TYPES=\"image/vnd.adobe.photoshop;\"\n\n# System Icon\nICON=\"AdobePhotoshop\"\n"
  },
  {
    "path": "apps/photoshop-cs6/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"Photoshop CS6\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Adobe Photoshop CS6\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Adobe\\Adobe Photoshop CS6 (64 Bit)\\Photoshop.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Adobe\"\n\n# GNOME mimetypes\nMIME_TYPES=\"image/vnd.adobe.photoshop;\"\n\n# System Icon\nICON=\"AdobePhotoshop\"\n"
  },
  {
    "path": "apps/photoshop-cs6-x86/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"Photoshop CS6\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Adobe Photoshop CS6\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files (x86)\\Adobe\\Adobe Photoshop CS6\\Photoshop.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Adobe\"\n\n# GNOME mimetypes\nMIME_TYPES=\"image/vnd.adobe.photoshop;\"\n\n# System Icon\nICON=\"AdobePhotoshop\"\n"
  },
  {
    "path": "apps/powerbi/info",
    "content": "# GNOME shortcut name\nNAME=\"Power BI Desktop (Traditional)\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft Power BI Desktop\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Microsoft Power BI Desktop\\bin\\PBIDesktop.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/x-powerbi;application/vnd.ms-powerbi;application/powerbi;application/x-pbix;application/vnd.powerbi.pbix\"\n\n# System Icon\nICON=\"ms-powerbi\"\n"
  },
  {
    "path": "apps/powerbi-store/info",
    "content": "# GNOME shortcut name\nNAME=\"Power BI Desktop (Microsoft Store)\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft Power BI Desktop (Store Version)\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Users\\%USERNAME%\\AppData\\Local\\Microsoft\\WindowsApps\\PBIDesktop.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/x-powerbi;application/vnd.ms-powerbi;application/powerbi;application/x-pbix;application/vnd.powerbi.pbix\"\n\n# System Icon\nICON=\"ms-powerbi\"\n"
  },
  {
    "path": "apps/powerpoint/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"PowerPoint\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft PowerPoint\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Microsoft Office\\Office16\\POWERPNT.EXE\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/vnd.ms-powerpoint;application/vnd.openxmlformats-officedocument.presentationml.presentation;application/vnd.openxmlformats-officedocument.presentationml.template;application/vnd.openxmlformats-officedocument.presentationml.slideshow;application/vnd.ms-powerpoint.addin.macroEnabled.12;application/vnd.ms-powerpoint.presentation.macroEnabled.12;application/vnd.ms-powerpoint.template.macroEnabled.12;application/vnd.ms-powerpoint.slideshow.macroEnabled.12;\"\n\n# System Icon\nICON=\"ms-powerpoint\"\n"
  },
  {
    "path": "apps/powerpoint-o365/info",
    "content": "# Copyright (c) 2024 Jon Champagne\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"PowerPoint\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft PowerPoint\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Microsoft Office\\root\\Office16\\POWERPNT.EXE\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/vnd.ms-powerpoint;application/vnd.openxmlformats-officedocument.presentationml.presentation;application/vnd.openxmlformats-officedocument.presentationml.template;application/vnd.openxmlformats-officedocument.presentationml.slideshow;application/vnd.ms-powerpoint.addin.macroEnabled.12;application/vnd.ms-powerpoint.presentation.macroEnabled.12;application/vnd.ms-powerpoint.template.macroEnabled.12;application/vnd.ms-powerpoint.slideshow.macroEnabled.12;\"\n\n# System Icon\nICON=\"ms-powerpoint\"\n"
  },
  {
    "path": "apps/powerpoint-o365-x86/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"PowerPoint\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft PowerPoint\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files (x86)\\Microsoft Office\\root\\Office16\\POWERPNT.EXE\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/vnd.ms-powerpoint;application/vnd.openxmlformats-officedocument.presentationml.presentation;application/vnd.openxmlformats-officedocument.presentationml.template;application/vnd.openxmlformats-officedocument.presentationml.slideshow;application/vnd.ms-powerpoint.addin.macroEnabled.12;application/vnd.ms-powerpoint.presentation.macroEnabled.12;application/vnd.ms-powerpoint.template.macroEnabled.12;application/vnd.ms-powerpoint.slideshow.macroEnabled.12;\"\n\n# System Icon\nICON=\"ms-powerpoint\"\n"
  },
  {
    "path": "apps/powerpoint-x86/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"PowerPoint\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft PowerPoint\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files (x86)\\Microsoft Office\\Office16\\POWERPNT.EXE\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/vnd.ms-powerpoint;application/vnd.openxmlformats-officedocument.presentationml.presentation;application/vnd.openxmlformats-officedocument.presentationml.template;application/vnd.openxmlformats-officedocument.presentationml.slideshow;application/vnd.ms-powerpoint.addin.macroEnabled.12;application/vnd.ms-powerpoint.presentation.macroEnabled.12;application/vnd.ms-powerpoint.template.macroEnabled.12;application/vnd.ms-powerpoint.slideshow.macroEnabled.12;\"\n\n# System Icon\nICON=\"ms-powerpoint\"\n"
  },
  {
    "path": "apps/powershell/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"Powershell\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft Powershell\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Windows\\SysWOW64\\WindowsPowerShell\\v1.0\\powershell.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Windows\"\n\n# GNOME mimetypes\nMIME_TYPES=\"\"\n"
  },
  {
    "path": "apps/powershell-ide/info",
    "content": "# GNOME shortcut name\nNAME=\"Powershell\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft Powershell\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Windows\\SysWOW64\\WindowsPowerShell\\v1.0\\powershell.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Windows\"\n\n# GNOME mimetypes\nMIME_TYPES=\"\"\n"
  },
  {
    "path": "apps/premierepro-2024/info",
    "content": "# GNOME shortcut name\nNAME=\"Premiere Pro 2024\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Adobe Premiere Pro 2024\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Adobe\\Adobe Premiere Pro 2024\\Adobe Premiere Pro.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Adobe\"\n\n# GNOME mimetypes\nMIME_TYPES=\"image/vnd.adobe.premierepro.project;\"\n\n# System Icon\nICON=\"AdobePremierePro\"\n"
  },
  {
    "path": "apps/project/info",
    "content": "# GNOME shortcut name\nNAME=\"Project\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft Project\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Microsoft Office\\root\\Office16\\WINPROJ.EXE\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/vnd.ms-project;\"\n"
  },
  {
    "path": "apps/project-x86/info",
    "content": "NAME=\"Project\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft Project\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files (x86)\\Microsoft Office\\root\\Office16\\WINPROJ.EXE\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/vnd.ms-project;\"\n"
  },
  {
    "path": "apps/publisher/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"Publisher\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft Publisher\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Microsoft Office\\Office16\\MSPUB.EXE\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/vnd.ms-publisher;\"\n"
  },
  {
    "path": "apps/publisher-o365/info",
    "content": "# Copyright (c) 2024 Jon Champagne\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"Publisher\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft Publisher\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Microsoft Office\\root\\Office16\\MSPUB.EXE\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/vnd.ms-publisher;\"\n"
  },
  {
    "path": "apps/publisher-o365-x86/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"Publisher\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft Publisher\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files (x86)\\Microsoft Office\\root\\Office16\\MSPUB.EXE\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/vnd.ms-publisher;\"\n"
  },
  {
    "path": "apps/publisher-x86/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"Publisher\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft Publisher\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files (x86)\\Microsoft Office\\Office16\\MSPUB.EXE\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/vnd.ms-publisher;\"\n"
  },
  {
    "path": "apps/remarkable-desktop/info",
    "content": "# GNOME shortcut name\nNAME=\"reMarkable\"\n\n# Used for descriptions and window class\nFULL_NAME=\"reMarkable Desktop App\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\reMarkable\\reMarkable.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"\"\n\n# System Icon\nICON=\"reMarkable\"\n"
  },
  {
    "path": "apps/ssms20/info",
    "content": "# GNOME shortcut name\nNAME=\"SQL Server Management Studio\"\n\n# Used for descriptions and window class\nFULL_NAME=\"SQL Server Management Studio\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files (x86)\\Microsoft SQL Server Management Studio 20\\Common7\\IDE\\Ssms.exe\"\n\n# GNOME categories\nCATEGORIES=\"Development\"\n\n# GNOME mimetypes\nMIME_TYPES=\"text/sql\"\n"
  },
  {
    "path": "apps/turbotax-2024/info",
    "content": "# GNOME shortcut name\nNAME=\"TurboTax 2024\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Intuit TurboTax 2024\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\TurboTax\\Individual 2024\\64bit\\TurboTax.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office;Finance;\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/x-turbotax;\"\n\n# System Icon\nICON=\"TurboTax\"\n"
  },
  {
    "path": "apps/turbotax-2025/info",
    "content": "# GNOME shortcut name\nNAME=\"TurboTax 2025\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Intuit TurboTax 2025\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\TurboTax\\Individual 2025\\64bit\\TurboTax.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office;Finance;\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/x-turbotax;\"\n\n# System Icon\nICON=\"TurboTax\"\n"
  },
  {
    "path": "apps/visio/info",
    "content": "# GNOME shortcut name\nNAME=\"Visio\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft Visio\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Microsoft Office\\root\\Office16\\VISIO.EXE\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/vnd.ms-visio.template.main+xml;application/vnd.ms-visio.stencil.main+xml;application/vnd.ms-visio.drawing.main+xml;application/vnd.ms-visio.stencil.macroEnabled.main+xml;application/vnd.ms-visio.drawing.macroEnabled.main+xml;application/vnd.ms-visio.template.macroEnabled.main+xml;\"\n"
  },
  {
    "path": "apps/visio-x86/info",
    "content": "# GNOME shortcut name\nNAME=\"Visio\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft Visio\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files (x86)\\Microsoft Office\\root\\Office16\\VISIO.EXE\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/vnd.ms-visio.template.main+xml;application/vnd.ms-visio.stencil.main+xml;application/vnd.ms-visio.drawing.main+xml;application/vnd.ms-visio.stencil.macroEnabled.main+xml;application/vnd.ms-visio.drawing.macroEnabled.main+xml;application/vnd.ms-visio.template.macroEnabled.main+xml;\"\n"
  },
  {
    "path": "apps/visual-studio-comm/info",
    "content": "# GNOME shortcut name\nNAME=\"Visual Studio Community\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft Visual Studio - Community Version\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\Common7\\IDE\\devenv.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Development\"\n\n# GNOME mimetypes\nMIME_TYPES=\"\"\n"
  },
  {
    "path": "apps/visual-studio-ent/info",
    "content": "# GNOME shortcut name\nNAME=\"Visual Studio Enterprise\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft Visual Studio - Enterprise Version\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Microsoft Visual Studio\\2022\\Enterprise\\Common7\\IDE\\devenv.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Development\"\n\n# GNOME mimetypes\nMIME_TYPES=\"\"\n"
  },
  {
    "path": "apps/visual-studio-pro/info",
    "content": "# GNOME shortcut name\nNAME=\"Visual Studio Professional\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft Visual Studio - Professional Version\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Microsoft Visual Studio\\2022\\Professional\\Common7\\IDE\\devenv.exe\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Development\"\n\n# GNOME mimetypes\nMIME_TYPES=\"\"\n"
  },
  {
    "path": "apps/word/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"Word\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft Word\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Microsoft Office\\Office16\\WINWORD.EXE\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/msword;application/vnd.openxmlformats-officedocument.wordprocessingml.document;application/vnd.openxmlformats-officedocument.wordprocessingml.template;application/vnd.ms-word.document.macroEnabled.12;application/vnd.ms-word.template.macroEnabled.12;\"\n\n# System Icon\nICON=\"ms-word\"\n"
  },
  {
    "path": "apps/word-o365/info",
    "content": "# Copyright (c) 2024 Jon Champagne\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"Word\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft Word\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files\\Microsoft Office\\root\\Office16\\WINWORD.EXE\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/msword;application/vnd.openxmlformats-officedocument.wordprocessingml.document;application/vnd.openxmlformats-officedocument.wordprocessingml.template;application/vnd.ms-word.document.macroEnabled.12;application/vnd.ms-word.template.macroEnabled.12;\"\n\n# System Icon\nICON=\"ms-word\"\n"
  },
  {
    "path": "apps/word-o365-x86/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"Word\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft Word\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files (x86)\\Microsoft Office\\root\\Office16\\WINWORD.EXE\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/msword;application/vnd.openxmlformats-officedocument.wordprocessingml.document;application/vnd.openxmlformats-officedocument.wordprocessingml.template;application/vnd.ms-word.document.macroEnabled.12;application/vnd.ms-word.template.macroEnabled.12;\"\n\n# System Icon\nICON=\"ms-word\"\n"
  },
  {
    "path": "apps/word-x86/info",
    "content": "# Copyright (c) 2024 Fmstrat\n# All rights reserved.\n#\n# SPDX-License-Identifier: Proprietary\n\n# GNOME shortcut name\nNAME=\"Word\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft Word\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files (x86)\\Microsoft Office\\Office16\\WINWORD.EXE\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/msword;application/vnd.openxmlformats-officedocument.wordprocessingml.document;application/vnd.openxmlformats-officedocument.wordprocessingml.template;application/vnd.ms-word.document.macroEnabled.12;application/vnd.ms-word.template.macroEnabled.12;\"\n\n# System Icon\nICON=\"ms-word\"\n"
  },
  {
    "path": "apps/word-x86-2010/info",
    "content": "# GNOME shortcut name\nNAME=\"Word\"\n\n# Used for descriptions and window class\nFULL_NAME=\"Microsoft Word\"\n\n# The executable inside windows\nWIN_EXECUTABLE=\"C:\\Program Files (x86)\\Microsoft Office\\Office14\\WINWORD.EXE\"\n\n# GNOME categories\nCATEGORIES=\"WinApps;Office\"\n\n# GNOME mimetypes\nMIME_TYPES=\"application/msword;application/vnd.openxmlformats-officedocument.wordprocessingml.document;application/vnd.openxmlformats-officedocument.wordprocessingml.template;application/vnd.ms-word.document.macroEnabled.12;application/vnd.ms-word.template.macroEnabled.12;\"\n\n# System Icon\nICON=\"ms-word\"\n"
  },
  {
    "path": "bin/winapps",
    "content": "#!/usr/bin/env bash\n\n### GLOBAL CONSTANTS ###\n# ERROR CODES\nreadonly EC_MISSING_CONFIG=1\nreadonly EC_MISSING_FREERDP=2\nreadonly EC_NOT_IN_GROUP=3\nreadonly EC_FAIL_START=4\nreadonly EC_FAIL_RESUME=5\nreadonly EC_FAIL_DESTROY=6\nreadonly EC_SD_TIMEOUT=7\nreadonly EC_DIE_TIMEOUT=8\nreadonly EC_RESTART_TIMEOUT=9\nreadonly EC_NOT_EXIST=10\nreadonly EC_UNKNOWN=11\nreadonly EC_NO_IP=12\nreadonly EC_BAD_PORT=13\nreadonly EC_UNSUPPORTED_APP=14\nreadonly EC_INVALID_FLAVOR=15\n\n# PATHS\nreadonly APPDATA_PATH=\"${HOME}/.local/share/winapps\"\nreadonly SYS_APP_PATH=\"/usr/local/share/winapps\"\nreadonly LASTRUN_PATH=\"${APPDATA_PATH}/lastrun\"\nreadonly LOG_PATH=\"${APPDATA_PATH}/winapps.log\"\nreadonly CONFIG_PATH=\"${HOME}/.config/winapps/winapps.conf\"\nreadonly COMPOSE_PATH=\"${HOME}/.config/winapps/compose.yaml\"\n# shellcheck disable=SC2155 # Silence warnings regarding masking return values through simultaneous declaration and assignment.\nreadonly SCRIPT_DIR_PATH=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")\" &>/dev/null && pwd)\"\nreadonly SLEEP_DETECT_PATH=\"${APPDATA_PATH}/last_activity\"\nreadonly SLEEP_MARKER=\"${APPDATA_PATH}/sleep_marker\"\n\n# OTHER\nreadonly CONTAINER_NAME=\"WinApps\" # FOR 'docker' AND 'podman' ONLY\nreadonly RDP_PORT=3389\nreadonly DOCKER_IP=\"127.0.0.1\"\n# shellcheck disable=SC2155 # Silence warnings regarding masking return values through simultaneous declaration and assignment.\nreadonly RUNID=\"${RANDOM}\"\n\n### GLOBAL VARIABLES ###\n# WINAPPS CONFIGURATION FILE\nRDP_USER=\"\"\nRDP_PASS=\"\"\nRDP_ASKPASS=\"\"\nRDP_DOMAIN=\"\"\nRDP_IP=\"\"\nVM_NAME=\"RDPWindows\" # FOR 'libvirt' ONLY\nWAFLAVOR=\"docker\"\nRDP_FLAGS=\"\"\nFREERDP_COMMAND=\"\"\nREMOVABLE_MEDIA=\"\"\nRDP_SCALE=100\nAUTOPAUSE=\"off\"\nAUTOPAUSE_TIME=\"300\"\nDEBUG=\"true\"\nBOOT_TIMEOUT=120\nHIDEF=\"on\"\nRDP_FLATPAK=0\nRDP_FLAGS_WINDOWS=\"\"\nRDP_FLAGS_NON_WINDOWS=\"\"\n\n# OTHER\nFREERDP_PID=-1\nNEEDED_BOOT=false\n\n### TRAPS ###\n# Catch SIGINT (CTRL+C) and SIGTERM to call 'waKillCleanExit'.\ntrap waKillCleanExit SIGINT SIGTERM\n\n### FUNCTIONS ###\n# Name: 'waCleanFreeRDP'\n# Role: Clean up remains prior to exit.\nfunction waCleanFreeRDP() {\n    for proc_file in \"${APPDATA_PATH}\"/FreeRDP_Process_*.cproc; do\n        # Protect against glob non-expansion.\n        [[ -f \"$proc_file\" ]] || break\n\n        # Extract the file name from the path.\n        cproc=$(basename \"$proc_file\")\n\n        # Remove the 'FreeRDP_Process_' prefix.\n        cproc=\"${cproc#FreeRDP_Process_}\"\n\n        # Remove the '.cproc' file extension.\n        cproc=\"${cproc%.cproc}\"\n\n        if [[ \"$cproc\" =~ ^[0-9]+$ ]]; then\n            # Proceed if process dir missing OR comm unreadable OR name doesn’t match.\n            if [[ ! -d \"/proc/$cproc\" ]] || [[ ! -r \"/proc/$cproc/comm\" ]] || [[ $(<\"/proc/$cproc/comm\") != *freerdp* ]]; then\n                # Delete the file.\n                rm -- \"$proc_file\" &>/dev/null\n            fi\n        elif [[ \"$cproc\" == \"Flatpak\" ]]; then\n            if command -v flatpak > /dev/null 2>&1; then\n                # Proceed if the flatpak is not running.\n                if ! flatpak ps --columns=application | grep -q \"^com.freerdp.FreeRDP$\"; then\n                    # Delete the file.\n                    rm -- \"$proc_file\" &>/dev/null\n                fi\n            fi\n        fi\n    done\n}\n\n# Name: 'waKillFreeRDP'\n# Role: Kill WinApps FreeRDP sessions.\nfunction waKillFreeRDP() {\n    # Declare variables.\n    local TERMINATED_PROCESS_IDS=()\n\n    # Loop through each matching file and add to the array.\n    for FREERDP_PROCESS_FILE in \"${APPDATA_PATH}/FreeRDP_Process_\"*.cproc; do\n        # Ensure the pattern is not treated as a literal string if no files match.\n        [[ -f \"$FREERDP_PROCESS_FILE\" ]] || break\n\n        # Extract the file name from the path.\n        FREERDP_PROCESS_FILE=$(basename \"$FREERDP_PROCESS_FILE\")\n\n        # Remove the 'FreeRDP_Process_' prefix.\n        FREERDP_PROCESS_FILE=\"${FREERDP_PROCESS_FILE#FreeRDP_Process_}\"\n\n        # Remove the '.cproc' file extension.\n        FREERDP_PROCESS_FILE=\"${FREERDP_PROCESS_FILE%.cproc}\"\n\n        # Track termination action.\n        KILLED=false\n\n        # Terminate processes.\n        if [[ \"$FREERDP_PROCESS_FILE\" =~ ^[0-9]+$ ]] && \\\n           [[ -d \"/proc/$FREERDP_PROCESS_FILE\" ]] && \\\n           [[ -r \"/proc/$FREERDP_PROCESS_FILE/comm\" ]] && \\\n           [[ $(<\"/proc/$FREERDP_PROCESS_FILE/comm\") == *freerdp* ]]; then\n            # SIGTERM\n            kill -15 \"$FREERDP_PROCESS_FILE\" &>/dev/null\n\n            # Wait up to 5 seconds.\n            for _ in {1..5}; do\n                sleep 1\n                [[ ! -d \"/proc/$FREERDP_PROCESS_FILE\" ]] && break\n            done\n\n            # SIGKILL\n            if [[ -d \"/proc/$FREERDP_PROCESS_FILE\" ]] && \\\n               [[ -r \"/proc/$FREERDP_PROCESS_FILE/comm\" ]] && \\\n               [[ $(<\"/proc/$FREERDP_PROCESS_FILE/comm\") == *freerdp* ]]; then\n                kill -9 \"$FREERDP_PROCESS_FILE\" &>/dev/null\n            fi\n\n            # Track termination action.\n            KILLED=true\n        elif [[ \"$FREERDP_PROCESS_FILE\" == \"Flatpak\" ]] && \\\n             command -v flatpak >/dev/null 2>&1 && \\\n             flatpak ps --columns=application | grep -q \"^com.freerdp.FreeRDP$\"; then\n            # Terminate the process.\n            flatpak kill com.freerdp.FreeRDP &>/dev/null\n\n            # Track termination action.\n            KILLED=true\n        fi\n\n        # Delete FreeRDP process tracking file.\n        # NOTE: Better practice to call 'waCleanFreeRDP' to handle this in case FreeRDP process(es) still not killed.\n        #rm -- \"${APPDATA_PATH}/FreeRDP_Process_${FREERDP_PROCESS_FILE}.cproc\" &>/dev/null\n\n        if [[ \"$KILLED\" == true ]]; then\n            # Add the process ID to the list of terminated processes.\n            TERMINATED_PROCESS_IDS+=(\"$FREERDP_PROCESS_FILE\")\n        fi\n    done\n\n    # Display feedback if any processes were terminated.\n    if [ ${#TERMINATED_PROCESS_IDS[@]} -ne 0 ]; then\n        dprint \"KILLED FREERDP PROCESSES: $(\n            IFS=', '\n            printf '%s' \"${TERMINATED_PROCESS_IDS[*]}\"\n        ).\"\n        echo \"Killed FreeRDP process(es):\"\n        printf '%s\\n' \"${TERMINATED_PROCESS_IDS[@]}\"\n    fi\n}\n\n# Name: 'waKillCleanExit'\n# Role: Kill FreeRDP processes and clean process tracking files when WinApps is forcefully terminated.\nfunction waKillCleanExit() {\n    # Kill FreeRDP processes.\n    waKillFreeRDP\n\n    # Clean orphaned files.\n    waCleanFreeRDP\n\n    # Terminate script.\n    exit 1\n}\n\n# Name: 'waEarlyDispatch'\n# Role: Handle lightweight subcommands.\nfunction waEarlyDispatch() {\n    if [[ -z \"${1:-}\" ]] || [[ \"$1\" == \"help\" ]]; then\n        waHelp\n        exit 0\n    elif [[ \"$1\" == \"killrdp\" ]]; then\n        waKillFreeRDP\n        waCleanFreeRDP\n        exit 0\n    elif [[ \"$1\" == \"cleanrdp\" ]]; then\n        waCleanFreeRDP\n        exit 0\n    fi\n}\n\n# Name: 'waThrowExit'\n# Role: Throw an error message and exit the script.\nfunction waThrowExit() {\n    # Declare variables.\n    local ERR_CODE=\"$1\"\n\n    # Throw error.\n    case \"$ERR_CODE\" in\n    \"$EC_MISSING_CONFIG\")\n        # Missing WinApps configuration file.\n        dprint \"ERROR: MISSING WINAPPS CONFIGURATION FILE. EXITING.\"\n        notify-send --expire-time=8000 --icon=\"dialog-error\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"The WinApps configuration file is missing.\\nPlease create a WinApps configuration file at '${CONFIG_PATH}'.\"\n        ;;\n    \"$EC_MISSING_FREERDP\")\n        dprint \"ERROR: FREERDP VERSION 3 IS NOT INSTALLED. EXITING.\"\n        notify-send --expire-time=8000 --icon=\"dialog-error\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"FreeRDP version 3 is not installed.\"\n        ;;\n    \"$EC_NOT_IN_GROUP\")\n        dprint \"ERROR: USER NOT PART OF REQUIRED GROUPS. EXITING.\"\n        notify-send --expire-time=8000 --icon=\"dialog-error\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"The user $(whoami) is not part of the required groups.\nPlease run:\n    sudo usermod -a -G libvirt $(whoami)\n    sudo usermod -a -G kvm $(whoami)\"\n        ;;\n    \"$EC_FAIL_START\")\n        dprint \"ERROR: WINDOWS FAILED TO START. EXITING.\"\n        notify-send --expire-time=8000 --icon=\"dialog-error\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Windows failed to start.\"\n        ;;\n    \"$EC_FAIL_RESUME\")\n        dprint \"ERROR: WINDOWS FAILED TO RESUME. EXITING.\"\n        notify-send --expire-time=8000 --icon=\"dialog-error\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Windows failed to resume.\"\n        ;;\n    \"$EC_FAIL_DESTROY\")\n        dprint \"ERROR: FAILED TO FORCE STOP WINDOWS. EXITING.\"\n        notify-send --expire-time=8000 --icon=\"dialog-error\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Failed to forcibly stop Windows.\"\n        ;;\n    \"$EC_SD_TIMEOUT\")\n        dprint \"ERROR: WINDOWS TOOK TOO LONG TO SHUT DOWN. EXITING.\"\n        notify-send --expire-time=8000 --icon=\"dialog-error\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Windows took too long to shut down.\"\n        ;;\n    \"$EC_DIE_TIMEOUT\")\n        dprint \"ERROR: WINDOWS TOOK TOO LONG TO DIE. EXITING.\"\n        notify-send --expire-time=8000 --icon=\"dialog-error\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Windows took too long to die.\"\n        ;;\n    \"$EC_RESTART_TIMEOUT\")\n        dprint \"ERROR: WINDOWS TOOK TOO LONG TO RESTART. EXITING.\"\n        notify-send --expire-time=8000 --icon=\"dialog-error\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Windows took too long to restart.\"\n        ;;\n    \"$EC_NOT_EXIST\")\n        dprint \"ERROR: WINDOWS NONEXISTENT. EXITING.\"\n        notify-send --expire-time=8000 --icon=\"dialog-error\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Windows VM named '${VM_NAME}' does not exist.\"\n        ;;\n    \"$EC_UNKNOWN\")\n        dprint \"ERROR: UNKNOWN CONTAINER ERROR. EXITING.\"\n        notify-send --expire-time=8000 --icon=\"dialog-error\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Unknown Windows container error.\"\n        ;;\n    \"$EC_NO_IP\")\n        dprint \"ERROR: WINDOWS UNREACHABLE. EXITING.\"\n        notify-send --expire-time=8000 --icon=\"dialog-error\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Windows is unreachable.\\nPlease ensure Windows is assigned an IP address.\"\n        ;;\n    \"$EC_BAD_PORT\")\n        dprint \"ERROR: RDP PORT CLOSED. EXITING.\"\n        notify-send --expire-time=8000 --icon=\"dialog-error\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"The Windows RDP port '${RDP_PORT}' is closed.\\nPlease ensure Remote Desktop is correctly configured on Windows.\"\n        ;;\n    \"$EC_UNSUPPORTED_APP\")\n        dprint \"ERROR: APPLICATION NOT FOUND. EXITING.\"\n        notify-send --expire-time=8000 --icon=\"dialog-error\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Application not found.\\nPlease ensure the program is correctly configured as an officially supported application.\"\n        ;;\n    \"$EC_INVALID_FLAVOR\")\n        dprint \"ERROR: INVALID FLAVOR. EXITING.\"\n        notify-send --expire-time=8000 --icon=\"dialog-error\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Invalid WinApps flavor.\\nPlease ensure 'docker', 'podman' or 'libvirt' are specified as the flavor in the WinApps configuration file.\"\n        ;;\n    esac\n\n    # Terminate the script.\n    exit \"$ERR_CODE\"\n}\n\n# Name: 'dprint'\n# Role: Conditionally print debug messages to a log file, creating it if it does not exist.\nfunction dprint() {\n    [ \"$DEBUG\" = \"true\" ] && echo \"[$(date)-$RUNID] $1\" >>\"$LOG_PATH\"\n}\n# Name: 'waFixRemovableMedia'\n# Role: If REMOVABLE_MEDIA is empty, default to /run/media (udisks default) and show a warning.\nfunction waFixRemovableMedia() {\n    if [ -z \"$REMOVABLE_MEDIA\" ]; then\n        REMOVABLE_MEDIA=\"/run/media\"  # Default for udisks\n        dprint \"NOTICE: Using default REMOVABLE_MEDIA: $REMOVABLE_MEDIA\"\n        notify-send --expire-time=3000 --icon=\"drive-removable-media\" \\\n            \"WinApps Notice\" \"Using default removable media path: $REMOVABLE_MEDIA\"\n    fi\n}\n\n# Name: 'waHelp'\n# Role: Print usage information.\nfunction waHelp() {\n    local script_name\n    script_name=\"$(basename \"$0\")\"\n\n    echo \"Usage:\"\n    echo \"  ${script_name} help\"\n    echo \"  ${script_name} windows\"\n    echo \"  ${script_name} manual <remoteapp-executable>\"\n    echo \"  ${script_name} <app> [file]\"\n    echo \"  ${script_name} killrdp\"\n    echo \"  ${script_name} cleanrdp\"\n    echo\n\n    echo \"Commands:\"\n    echo \"  help                           --> Show this help message.\"\n    echo \"  windows                        --> Start a full Windows desktop RDP session.\"\n    echo \"  manual <remoteapp-executable>  --> Start a RemoteApp session for an arbitrary executable.\"\n    echo \"  <app> [file]                   --> Start a community-tested application using a preconfigured definition. Optionally open a file.\"\n    echo \"  killrdp                        --> Terminate any running WinApps FreeRDP sessions.\"\n    echo \"  cleanrdp                       --> Remove orphaned FreeRDP process tracking files.\"\n}\n\n# Name: 'waFixScale'\n# Role: Since FreeRDP only supports '/scale' values of 100, 140 or 180, find the closest supported argument to the user's configuration.\nfunction waFixScale() {\n    # Define variables.\n    local OLD_SCALE=100\n    local VALID_SCALE_1=100\n    local VALID_SCALE_2=140\n    local VALID_SCALE_3=180\n\n    # Check for an unsupported value.\n    if [ \"$RDP_SCALE\" != \"$VALID_SCALE_1\" ] && [ \"$RDP_SCALE\" != \"$VALID_SCALE_2\" ] && [ \"$RDP_SCALE\" != \"$VALID_SCALE_3\" ]; then\n        # Save the unsupported scale.\n        OLD_SCALE=\"$RDP_SCALE\"\n\n        # Calculate the absolute differences.\n        local DIFF_1=$(( RDP_SCALE > VALID_SCALE_1 ? RDP_SCALE - VALID_SCALE_1 : VALID_SCALE_1 - RDP_SCALE ))\n        local DIFF_2=$(( RDP_SCALE > VALID_SCALE_2 ? RDP_SCALE - VALID_SCALE_2 : VALID_SCALE_2 - RDP_SCALE ))\n        local DIFF_3=$(( RDP_SCALE > VALID_SCALE_3 ? RDP_SCALE - VALID_SCALE_3 : VALID_SCALE_3 - RDP_SCALE ))\n\n        # Set the final scale to the valid scale value with the smallest absolute difference.\n        if (( DIFF_1 <= DIFF_2 && DIFF_1 <= DIFF_3 )); then\n            RDP_SCALE=\"$VALID_SCALE_1\"\n        elif (( DIFF_2 <= DIFF_1 && DIFF_2 <= DIFF_3 )); then\n            RDP_SCALE=\"$VALID_SCALE_2\"\n        else\n            RDP_SCALE=\"$VALID_SCALE_3\"\n        fi\n\n        # Print feedback.\n        dprint \"WARNING: Unsupported RDP_SCALE value '${OLD_SCALE}'. Defaulting to '${RDP_SCALE}'.\"\n        notify-send --expire-time=4000 --icon=\"dialog-warning\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Unsupported RDP_SCALE value '${OLD_SCALE}'.\\nDefaulting to '${RDP_SCALE}'.\"\n    fi\n}\n\n# Name: 'waLoadConfig'\n# Role: Load the variables within the WinApps configuration file.\nfunction waLoadConfig() {\n    # Load WinApps configuration file.\n    if [ -f \"$CONFIG_PATH\" ]; then\n        # shellcheck source=/dev/null # Exclude WinApps configuration file from being checked by ShellCheck.\n        source \"$CONFIG_PATH\"\n    else\n        waThrowExit $EC_MISSING_CONFIG\n    fi\n\n    # Update $RDP_SCALE.\n    waFixScale\n    # Update when $REMOVABLE_MEDIA is null\n    waFixRemovableMedia\n    # Update $AUTOPAUSE_TIME.\n    # RemoteApp RDP sessions take, at minimum, 20 seconds to be terminated by the Windows server.\n    # Hence, subtract 20 from the timeout specified by the user, as a 'built in' timeout of 20 seconds will occur.\n    # Source: https://techcommunity.microsoft.com/t5/security-compliance-and-identity/terminal-services-remoteapp-8482-session-termination-logic/ba-p/246566\n    AUTOPAUSE_TIME=$((AUTOPAUSE_TIME - 20))\n    AUTOPAUSE_TIME=$((AUTOPAUSE_TIME < 0 ? 0 : AUTOPAUSE_TIME))\n}\n\n# Name: 'waLastRun'\n# Role: Determine the last time this script was run.\nfunction waLastRun() {\n    # Declare variables.\n    local LAST_RUN_UNIX_TIME=0\n    local CURR_RUN_UNIX_TIME=0\n\n    # Store the time this script was run last as a unix timestamp.\n    if [ -f \"$LASTRUN_PATH\" ]; then\n        LAST_RUN_UNIX_TIME=$(stat -t -c %Y \"$LASTRUN_PATH\")\n        dprint \"LAST_RUN: ${LAST_RUN_UNIX_TIME}\"\n    fi\n\n    # Update the file modification time with the current time.\n    touch -- \"$LASTRUN_PATH\"\n    CURR_RUN_UNIX_TIME=$(stat -t -c %Y \"$LASTRUN_PATH\")\n    dprint \"THIS_RUN: ${CURR_RUN_UNIX_TIME}\"\n}\n\n# Name: 'waGetFreeRDPCommand'\n# Role: Determine the correct FreeRDP command to use.\nfunction waGetFreeRDPCommand() {\n    # Declare variables.\n    local FREERDP_MAJOR_VERSION=\"\" # Stores the major version of the installed copy of FreeRDP.\n\n    # Attempt to set a FreeRDP command if the command variable is empty.\n    if [ -z \"$FREERDP_COMMAND\" ]; then\n        # Check for 'xfreerdp'.\n        if command -v xfreerdp &>/dev/null; then\n            # Check FreeRDP major version is 3 or greater.\n            FREERDP_MAJOR_VERSION=$(xfreerdp --version | head -n 1 | grep -o -m 1 '\\b[0-9]\\S*' | head -n 1 | cut -d'.' -f1)\n            if [[ $FREERDP_MAJOR_VERSION =~ ^[0-9]+$ ]] && ((FREERDP_MAJOR_VERSION >= 3)); then\n                FREERDP_COMMAND=\"xfreerdp\"\n            fi\n        fi\n\n        # Check for 'xfreerdp3' command as a fallback option.\n        if [ -z \"$FREERDP_COMMAND\" ]; then\n            if command -v xfreerdp3 &>/dev/null; then\n                # Check FreeRDP major version is 3 or greater.\n                FREERDP_MAJOR_VERSION=$(xfreerdp3 --version | head -n 1 | grep -o -m 1 '\\b[0-9]\\S*' | head -n 1 | cut -d'.' -f1)\n                if [[ $FREERDP_MAJOR_VERSION =~ ^[0-9]+$ ]] && ((FREERDP_MAJOR_VERSION >= 3)); then\n                    FREERDP_COMMAND=\"xfreerdp3\"\n                fi\n            fi\n        fi\n\n        # Check for FreeRDP Flatpak (fallback option).\n        if [ -z \"$FREERDP_COMMAND\" ]; then\n            if command -v flatpak &>/dev/null; then\n                if flatpak list --columns=application | grep -q \"^com.freerdp.FreeRDP$\"; then\n                    # Check FreeRDP major version is 3 or greater.\n                    FREERDP_MAJOR_VERSION=$(flatpak list --columns=application,version | grep \"^com.freerdp.FreeRDP\" | awk '{print $2}' | cut -d'.' -f1)\n                    if [[ $FREERDP_MAJOR_VERSION =~ ^[0-9]+$ ]] && ((FREERDP_MAJOR_VERSION >= 3)); then\n                        FREERDP_COMMAND=\"flatpak run --command=xfreerdp com.freerdp.FreeRDP\"\n                        RDP_FLATPAK=1\n                    fi\n                fi\n            fi\n        fi\n    fi\n\n    if command -v \"$FREERDP_COMMAND\" &>/dev/null || [ \"$FREERDP_COMMAND\" = \"flatpak run --command=xfreerdp com.freerdp.FreeRDP\" ]; then\n        dprint \"Using FreeRDP command '${FREERDP_COMMAND}'.\"\n\n        # Append additional flags or parameters to FreeRDP.\n        # These additional flags are loaded prior in 'waLoadConfig'.\n        [[ -n $RDP_FLAGS ]] && FREERDP_COMMAND=\"${FREERDP_COMMAND} ${RDP_FLAGS}\"\n    else\n        waThrowExit \"$EC_MISSING_FREERDP\"\n    fi\n}\n\n# Name: 'waCheckGroupMembership'\n# Role: Ensures the current user is part of the required groups.\nfunction waCheckGroupMembership() {\n    # Identify groups the current user belongs to.\n    # shellcheck disable=SC2155 # Silence warnings regarding masking return values through simultaneous declaration and assignment.\n    local USER_GROUPS=$(id -nG \"$(whoami)\")\n\n    if ! echo \"$USER_GROUPS\" | grep -qE '\\b(libvirt|libvirtd)\\b' || \\\n       ! echo \"$USER_GROUPS\" | grep -qE '\\bkvm\\b'; then\n        waThrowExit \"$EC_NOT_IN_GROUP\"\n    fi\n}\n\n# Name: 'waCheckVMRunning'\n# Role: Check if the Windows 'libvirt' VM is running, and attempt to start it if it is not.\nfunction waCheckVMRunning() {\n    # Declare exit status variable.\n    local EXIT_STATUS=0\n\n    # Declare timer variables.\n    local TIME_ELAPSED=0\n    local TIME_LIMIT=60\n    local TIME_INTERVAL=5\n\n    # Attempt to run the Windows virtual machine.\n    # Note: States 'running' and 'idle' do not require intervention, and are not checked for.\n    if (virsh list --all --name | grep -Fxq -- \"$VM_NAME\"); then\n        if (virsh list --state-shutoff --name | grep -Fxq -- \"$VM_NAME\"); then\n            dprint \"WINDOWS SHUT OFF. BOOTING WINDOWS.\"\n            notify-send --expire-time=4000 --icon=\"dialog-info\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Booting Windows.\"\n            NEEDED_BOOT=true\n            virsh start \"$VM_NAME\" &>/dev/null || EXIT_STATUS=$EC_FAIL_START\n            if (virsh list --state-paused --name | grep -Fxq -- \"$VM_NAME\"); then\n                dprint \"WINDOWS PAUSED. RESUMING WINDOWS.\"\n                notify-send --expire-time=4000 --icon=\"dialog-info\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Resuming Windows.\"\n                virsh resume \"$VM_NAME\" &>/dev/null || EXIT_STATUS=$EC_FAIL_RESUME\n            fi\n        elif (virsh list --state-paused --name | grep -Fxq -- \"$VM_NAME\"); then\n            dprint \"WINDOWS PAUSED. RESUMING WINDOWS.\"\n            notify-send --expire-time=4000 --icon=\"dialog-info\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Resuming Windows.\"\n            virsh resume \"$VM_NAME\" &>/dev/null || EXIT_STATUS=$EC_FAIL_RESUME\n        elif (virsh list --state-other --name | grep -Fxq -- \"$VM_NAME\"); then\n            if (virsh domstate \"$VM_NAME\" | grep -Fxq \"in shutdown\"); then\n                dprint \"WINDOWS SHUTTING DOWN. WAITING.\"\n                notify-send --expire-time=4000 --icon=\"dialog-info\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Windows is currently shutting down.\\nIt will automatically restart once the shutdown process is complete.\"\n                EXIT_STATUS=$EC_SD_TIMEOUT\n                while (( TIME_ELAPSED < TIME_LIMIT )); do\n                    if (virsh list --state-shutoff --name | grep -Fxq -- \"$VM_NAME\"); then\n                        EXIT_STATUS=0\n                        dprint \"WINDOWS SHUT OFF. BOOTING WINDOWS.\"\n                        notify-send --expire-time=4000 --icon=\"dialog-info\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Booting Windows.\"\n                        virsh start \"$VM_NAME\" &>/dev/null || EXIT_STATUS=$EC_FAIL_START\n                        NEEDED_BOOT=true\n                        break\n                    fi\n                    sleep $TIME_INTERVAL\n                    TIME_ELAPSED=$((TIME_ELAPSED + TIME_INTERVAL))\n                done\n            elif (virsh domstate \"$VM_NAME\" | grep -Fxq \"crashed\"); then\n                dprint \"WINDOWS CRASHED. DESTROYING WINDOWS.\"\n                notify-send --expire-time=4000 --icon=\"dialog-info\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Windows experienced an unexpected crash.\\nAttempting to restart Windows.\"\n                virsh destroy \"$VM_NAME\" &>/dev/null || EXIT_STATUS=$EC_FAIL_DESTROY\n                if [ \"$EXIT_STATUS\" -eq 0 ]; then\n                    dprint \"WINDOWS DESTROYED. BOOTING WINDOWS.\"\n                    notify-send --expire-time=4000 --icon=\"dialog-info\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Booting Windows.\"\n                    virsh start \"$VM_NAME\" &>/dev/null || EXIT_STATUS=$EC_FAIL_START\n                    NEEDED_BOOT=true\n                fi\n            elif (virsh domstate \"$VM_NAME\" | grep -Fxq \"dying\"); then\n                dprint \"WINDOWS DYING. WAITING.\"\n                notify-send --expire-time=4000 --icon=\"dialog-info\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Windows is currently shutting down unexpectedly.\\nIt will try to restart once the shutdown process finishes.\"\n                EXIT_STATUS=$EC_DIE_TIMEOUT\n                while (( TIME_ELAPSED < TIME_LIMIT )); do\n                    if (virsh domstate \"$VM_NAME\" | grep -Fxq \"crashed\"); then\n                        EXIT_STATUS=0\n                        dprint \"WINDOWS CRASHED. DESTROYING WINDOWS.\"\n                        notify-send --expire-time=4000 --icon=\"dialog-info\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Windows experienced an unexpected crash.\\nAttempting to restart Windows.\"\n                        virsh destroy \"$VM_NAME\" &>/dev/null || EXIT_STATUS=$EC_FAIL_DESTROY\n                        if [ \"$EXIT_STATUS\" -eq 0 ]; then\n                            dprint \"WINDOWS DESTROYED. BOOTING WINDOWS.\"\n                            notify-send --expire-time=4000 --icon=\"dialog-info\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Booting Windows.\"\n                            virsh start \"$VM_NAME\" &>/dev/null || EXIT_STATUS=$EC_FAIL_START\n                            NEEDED_BOOT=true\n                        fi\n                        break\n                    elif (virsh list --state-shutoff --name | grep -Fxq -- \"$VM_NAME\"); then\n                        EXIT_STATUS=0\n                        dprint \"WINDOWS SHUT OFF. BOOTING WINDOWS.\"\n                        notify-send --expire-time=4000 --icon=\"dialog-info\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Booting Windows.\"\n                        virsh start \"$VM_NAME\" &>/dev/null || EXIT_STATUS=$EC_FAIL_START\n                        NEEDED_BOOT=true\n                        break\n                    fi\n                    sleep $TIME_INTERVAL\n                    TIME_ELAPSED=$((TIME_ELAPSED + TIME_INTERVAL))\n                done\n            elif (virsh domstate \"$VM_NAME\" | grep -Fxq \"pmsuspended\" ); then\n                dprint \"WINDOWS SUSPENDED. RESUMING WINDOWS.\"\n                virsh resume \"$VM_NAME\" &>/dev/null || EXIT_STATUS=$EC_FAIL_RESUME\n            fi\n        fi\n    else\n        EXIT_STATUS=$EC_NOT_EXIST\n    fi\n\n    # Handle non-zero exit statuses.\n    [ \"$EXIT_STATUS\" -ne 0 ] && waThrowExit \"$EXIT_STATUS\"\n\n    # Wait for VM to be fully ready\n    if [[ \"$NEEDED_BOOT\" == \"true\" ]]; then\n        dprint \"WAITING FOR VM TO BE FULLY READY...\"\n        notify-send --expire-time=4000 --icon=\"dialog-info\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Waiting for Windows to be ready...\"\n\n        TIME_ELAPSED=0\n\n        while (( TIME_ELAPSED < BOOT_TIMEOUT )); do\n            # libvirt users need to find the IP address dynamically\n            if [[ -z \"$RDP_IP\" ]]; then\n                RDP_IP=$(waFindVMIP)\n            fi\n            # Check if VM is running\n            if (virsh list --state-running --name | grep -Fxq -- \"$VM_NAME\"); then\n                # Try to connect to RDP port to verify it's ready\n                if timeout 1 bash -c \">/dev/tcp/$RDP_IP/$RDP_PORT\" 2>/dev/null; then\n                    dprint \"VM IS READY\"\n                    notify-send --expire-time=4000 --icon=\"dialog-info\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Windows is ready.\"\n                    # Add a delay after Windows is ready\n                    if [ \"$NEEDED_BOOT\" = \"true\" ]; then\n                        sleep 10\n                    fi\n                    break\n                fi\n            fi\n\n            sleep 5\n            TIME_ELAPSED=$((TIME_ELAPSED + 5))\n\n            # Show progress every 30 seconds\n            if (( TIME_ELAPSED % 30 == 0 )); then\n                notify-send --expire-time=4000 --icon=\"dialog-info\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Still waiting for Windows to be ready... ($TIME_ELAPSED seconds elapsed)\"\n            fi\n        done\n\n        # If we timed out waiting for the VM\n        if (( TIME_ELAPSED >= BOOT_TIMEOUT )); then\n            dprint \"TIMEOUT WAITING FOR VM TO BE READY\"\n            notify-send --expire-time=4000 --icon=\"dialog-info\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Timeout waiting for Windows to be ready. Please try again.\"\n            waThrowExit $EC_FAIL_START\n        fi\n    fi\n}\n\n# Name: 'waCheckContainerRunning'\n# Role: Throw an error if the Docker container is not running.\nfunction waCheckContainerRunning() {\n    # Declare variables.\n    local EXIT_STATUS=0\n    local CONTAINER_STATE=\"\"\n    local COMPOSE_COMMAND=\"\"\n    local TIME_ELAPSED=0\n    local TIME_LIMIT=60\n    local TIME_INTERVAL=5\n\n    # Determine the state of the container.\n    CONTAINER_STATE=$(\"$WAFLAVOR\" inspect --format='{{.State.Status}}' \"$CONTAINER_NAME\")\n\n    # Determine the compose command.\n    case \"$WAFLAVOR\" in\n        \"docker\") COMPOSE_COMMAND=\"docker compose\" ;;\n        \"podman\") COMPOSE_COMMAND=\"podman-compose\" ;;\n    esac\n\n    # Check container state.\n    # Note: Errors DO NOT result in non-zero exit statuses.\n    # Docker: 'created', 'restarting', 'running', 'removing', 'paused', 'exited' or 'dead'.\n    # Podman: 'created', 'running', 'paused', 'exited' or 'unknown'.\n    case \"$CONTAINER_STATE\" in\n        \"created\")\n            dprint \"WINDOWS CREATED. BOOTING WINDOWS.\"\n            notify-send --expire-time=4000 --icon=\"dialog-info\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Booting Windows.\"\n            $COMPOSE_COMMAND --file \"$COMPOSE_PATH\" start &>/dev/null\n            NEEDED_BOOT=true\n            ;;\n        \"restarting\")\n            dprint \"WINDOWS RESTARTING. WAITING.\"\n            notify-send --expire-time=4000 --icon=\"dialog-info\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Windows is currently restarting. Please wait.\"\n            EXIT_STATUS=$EC_RESTART_TIMEOUT\n            while (( TIME_ELAPSED < TIME_LIMIT )); do\n                if [[ $(\"$WAFLAVOR\" inspect --format='{{.State.Status}}' \"$CONTAINER_NAME\") == \"running\" ]]; then\n                    EXIT_STATUS=0\n                    dprint \"WINDOWS RESTARTED.\"\n                    notify-send --expire-time=4000 --icon=\"dialog-info\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Restarted Windows.\"\n                    NEEDED_BOOT=true\n                    break\n                fi\n                sleep $TIME_INTERVAL\n                TIME_ELAPSED=$((TIME_ELAPSED + TIME_INTERVAL))\n            done\n            ;;\n        \"paused\")\n            dprint \"WINDOWS PAUSED. RESUMING WINDOWS.\"\n            notify-send --expire-time=4000 --icon=\"dialog-info\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Resuming Windows.\"\n            $COMPOSE_COMMAND --file \"$COMPOSE_PATH\" unpause &>/dev/null\n            ;;\n        \"exited\")\n            dprint \"WINDOWS SHUT OFF. BOOTING WINDOWS.\"\n            notify-send --expire-time=4000 --icon=\"dialog-info\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Booting Windows.\"\n            $COMPOSE_COMMAND --file \"$COMPOSE_PATH\" start &>/dev/null\n            NEEDED_BOOT=true\n            ;;\n        \"dead\")\n            dprint \"WINDOWS DEAD. RECREATING WINDOWS CONTAINER.\"\n            notify-send --expire-time=4000 --icon=\"dialog-info\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Re-creating and booting Windows.\"\n            $COMPOSE_COMMAND --file \"$COMPOSE_PATH\" down &>/dev/null && $COMPOSE_COMMAND --file \"$COMPOSE_PATH\" up -d &>/dev/null\n            NEEDED_BOOT=true\n            ;;\n        \"unknown\")\n            EXIT_STATUS=$EC_UNKNOWN\n            ;;\n    esac\n\n    # Handle non-zero exit statuses.\n    [ \"$EXIT_STATUS\" -ne 0 ] && waThrowExit \"$EXIT_STATUS\"\n\n    # Wait for container to be fully ready\n    if [[ \"$CONTAINER_STATE\" == \"created\" || \"$CONTAINER_STATE\" == \"exited\" || \"$CONTAINER_STATE\" == \"dead\" || \"$CONTAINER_STATE\" == \"restarting\" ]]; then\n        dprint \"WAITING FOR CONTAINER TO BE FULLY READY...\"\n        notify-send --expire-time=4000 --icon=\"dialog-info\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Waiting for Windows to be ready...\"\n\n        TIME_ELAPSED=0\n\n        while (( TIME_ELAPSED < BOOT_TIMEOUT )); do\n            # Check if container is running\n            if [[ $(\"$WAFLAVOR\" inspect --format='{{.State.Status}}' \"$CONTAINER_NAME\") == \"running\" ]]; then\n                # Try to connect to RDP port to verify it's ready\n                if timeout 1 bash -c \">/dev/tcp/$RDP_IP/$RDP_PORT\" 2>/dev/null; then\n                    dprint \"CONTAINER IS READY\"\n                    notify-send --expire-time=4000 --icon=\"dialog-info\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Windows is ready.\"\n                    # Add a delay after Windows is ready\n                    if [ \"$NEEDED_BOOT\" = \"true\" ]; then\n                        sleep 10\n                    fi\n                    break\n                fi\n            fi\n\n            sleep 5\n            TIME_ELAPSED=$((TIME_ELAPSED + 5))\n\n            # Show progress every 30 seconds\n            if (( TIME_ELAPSED % 30 == 0 )); then\n                notify-send --expire-time=4000 --icon=\"dialog-info\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Still waiting for Windows to be ready... ($TIME_ELAPSED seconds elapsed)\"\n            fi\n        done\n\n        # If we timed out waiting for the container\n        if (( TIME_ELAPSED >= BOOT_TIMEOUT )); then\n            dprint \"TIMEOUT WAITING FOR CONTAINER TO BE READY\"\n            notify-send --expire-time=4000 --icon=\"dialog-info\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Timeout waiting for Windows to be ready. Please try again.\"\n            waThrowExit $EC_FAIL_START\n        fi\n    fi\n}\n\n# Name: 'waCheckPortOpen'\n# Role: Assesses whether the RDP port on Windows is open.\nfunction waCheckPortOpen() {\n    # Declare variables.\n    local TIME_ELAPSED=0\n    local TIME_LIMIT=30\n    local TIME_INTERVAL=5\n\n    # Obtain Windows VM IP Address ('libvirt' ONLY)\n    # Note: 'RDP_IP' should not be empty if 'WAFLAVOR' is 'docker', since it is set to localhost before this function is called.\n    if [ -z \"$RDP_IP\" ] && [ \"$WAFLAVOR\" = \"libvirt\" ]; then\n\n        while (( TIME_ELAPSED < TIME_LIMIT )); do\n            if [ \"$TIME_ELAPSED\" -eq \"$TIME_INTERVAL\" ]; then\n                notify-send --expire-time=4000 --icon=\"dialog-info\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Requesting Windows IP address...\"\n            fi\n            RDP_IP=$(waFindVMIP)\n            [ -n \"$RDP_IP\" ] && break\n            sleep $TIME_INTERVAL\n            TIME_ELAPSED=$((TIME_ELAPSED + TIME_INTERVAL))\n        done\n\n        [ -z \"$RDP_IP\" ] && waThrowExit \"$EC_NO_IP\"\n    fi\n\n    # Check for an open RDP port.\n    timeout 10 nc -z \"$RDP_IP\" \"$RDP_PORT\" &>/dev/null || waThrowExit \"$EC_BAD_PORT\"\n}\n\n# Name: 'waRunCommand'\n# Role: Run the requested WinApps command.\nfunction waRunCommand() {\n    # Declare variables.\n    local ICON=\"\"\n    local FILE_PATH=\"\"\n\n    # Run option.\n    if [ \"$1\" = \"windows\" ]; then\n        # Update timeout (since there is no 'in-built' 20 second delay for full RDP sessions post-logout).\n        AUTOPAUSE_TIME=$((AUTOPAUSE_TIME + 20))\n\n        # Open Windows RDP session.\n        dprint \"WINDOWS\"\n        # shellcheck disable=SC2086 #Disable warning for globbing and word splitting\n        $FREERDP_COMMAND \\\n            $RDP_FLAGS_WINDOWS \\\n            /d:\"$RDP_DOMAIN\" \\\n            /u:\"$RDP_USER\" \\\n            ${RDP_PASSWORD_ARG:+\"$RDP_PASSWORD_ARG\"} \\\n            /scale:\"$RDP_SCALE\" \\\n            +auto-reconnect \\\n            +dynamic-resolution \\\n            /wm-class:\"Microsoft Windows\" \\\n            /t:\"Windows RDP Session [$RDP_IP]\" \\\n            /v:\"$RDP_IP\" &>/dev/null &\n\n        # Capture the process ID.\n        FREERDP_PID=$!\n    elif [ \"$1\" = \"manual\" ]; then\n        # Open specified application.\n        dprint \"MANUAL: ${2}\"\n        # shellcheck disable=SC2086 #Disable warning for globbing and word splitting\n        $FREERDP_COMMAND \\\n            $RDP_FLAGS_NON_WINDOWS \\\n            /d:\"$RDP_DOMAIN\" \\\n            /u:\"$RDP_USER\" \\\n            ${RDP_PASSWORD_ARG:+\"$RDP_PASSWORD_ARG\"} \\\n            /scale:\"$RDP_SCALE\" \\\n            +auto-reconnect \\\n            /app:program:\"$2\",hidef:\"$HIDEF\" \\\n            /v:\"$RDP_IP\" &>/dev/null &\n\n        # Capture the process ID.\n        FREERDP_PID=$!\n    else\n        # Script summoned from right-click menu or application icon (plus/minus a file path).\n        if [ -e \"${SCRIPT_DIR_PATH}/../apps/${1}/info\" ]; then\n            # shellcheck source=/dev/null # Exclude this file from being checked by ShellCheck.\n            source \"${SCRIPT_DIR_PATH}/../apps/${1}/info\"\n            ICON=\"${SCRIPT_DIR_PATH}/../apps/${1}/icon.svg\"\n        elif [ -e \"${APPDATA_PATH}/apps/${1}/info\" ]; then\n            # shellcheck source=/dev/null # Exclude this file from being checked by ShellCheck.\n            source \"${APPDATA_PATH}/apps/${1}/info\"\n            ICON=\"${APPDATA_PATH}/apps/${1}/icon.svg\"\n        elif [ -e \"${SYS_APP_PATH}/apps/${1}/info\" ]; then\n            # shellcheck source=/dev/null # Exclude this file from being checked by ShellCheck.\n            source \"${SYS_APP_PATH}/apps/${1}/info\"\n            ICON=\"${SYS_APP_PATH}/apps/${1}/icon.svg\"\n        else\n            waThrowExit \"$EC_UNSUPPORTED_APP\"\n        fi\n\n        # Check if a file path was specified, and pass this to the application.\n        if [ -z \"$2\" ]; then\n            # No file path specified.\n            # shellcheck disable=SC2086 #Disable warning for globbing and word splitting\n            $FREERDP_COMMAND \\\n               $RDP_FLAGS_NON_WINDOWS \\\n                /d:\"$RDP_DOMAIN\" \\\n                /u:\"$RDP_USER\" \\\n                ${RDP_PASSWORD_ARG:+\"$RDP_PASSWORD_ARG\"} \\\n                /scale:\"$RDP_SCALE\" \\\n                +auto-reconnect \\\n                /wm-class:\"$FULL_NAME\" \\\n                /app:program:\"$WIN_EXECUTABLE\",hidef:\"$HIDEF\",icon:\"$ICON\",name:\"$FULL_NAME\" \\\n                /v:\"$RDP_IP\" &>/dev/null &\n\n            # Capture the process ID.\n            FREERDP_PID=$!\n        else\n            # Convert path from UNIX to Windows style.\n            FILE_PATH=$(echo \"$2\" | sed \\\n                -e 's|^'\"${HOME}\"'|\\\\\\\\tsclient\\\\home|' \\\n                -e 's|^'\"${REMOVABLE_MEDIA}\"'|\\\\\\\\tsclient\\\\media|' \\\n                -e 's|/|\\\\|g')\n            dprint \"UNIX_FILE_PATH: ${2}\"\n            dprint \"WINDOWS_FILE_PATH: ${FILE_PATH}\"\n\n            # shellcheck disable=SC2086 #Disable warning for globbing and word splitting\n            $FREERDP_COMMAND \\\n               $RDP_FLAGS_NON_WINDOWS \\\n                /d:\"$RDP_DOMAIN\" \\\n                /u:\"$RDP_USER\" \\\n                ${RDP_PASSWORD_ARG:+\"$RDP_PASSWORD_ARG\"} \\\n                /scale:\"$RDP_SCALE\" \\\n                +auto-reconnect \\\n                /drive:media,\"$REMOVABLE_MEDIA\" \\\n                /wm-class:\"$FULL_NAME\" \\\n                /app:program:\"$WIN_EXECUTABLE\",hidef:\"$HIDEF\",icon:\"$ICON\",name:\"$FULL_NAME\",cmd:\\\"\"$FILE_PATH\"\\\" \\\n                /v:\"$RDP_IP\" &>/dev/null &\n\n            # Capture the process ID.\n            FREERDP_PID=$!\n        fi\n    fi\n\n    if [[ \"$RDP_FLATPAK\" == \"0\" ]]; then\n        if [ \"$FREERDP_PID\" -ne -1 ]; then\n            # Create a file with the process ID.\n            touch -- \"${APPDATA_PATH}/FreeRDP_Process_${FREERDP_PID}.cproc\"\n\n            # Wait for the process to terminate.\n            wait \"$FREERDP_PID\"\n\n            # Remove the file with the process ID.\n            rm -- \"${APPDATA_PATH}/FreeRDP_Process_${FREERDP_PID}.cproc\" &>/dev/null\n        fi\n    else\n        # Create a file.\n        touch -- \"${APPDATA_PATH}/FreeRDP_Process_Flatpak.cproc\"\n\n        # Wait for the process to terminate.\n        while flatpak ps --columns=application | grep -q \"^com.freerdp.FreeRDP$\"; do\n            sleep 5\n        done\n\n        # Remove the file.\n        rm -- \"${APPDATA_PATH}/FreeRDP_Process_Flatpak.cproc\" &>/dev/null\n    fi\n}\n\n# Name: 'waCheckIdle'\n# Role: Suspend Windows if idle.\nfunction waCheckIdle() {\n    # Declare variables\n    local TIME_INTERVAL=10\n    local TIME_ELAPSED=0\n    local SUSPEND_WINDOWS=0\n\n    # Prevent 'autopause' functionality with unsupported Windows backends.\n    if [ \"$WAFLAVOR\" != \"manual\" ]; then\n        # Check if there are no WinApps-related FreeRDP processes running.\n        if ! ls \"$APPDATA_PATH\"/FreeRDP_Process_*.cproc &>/dev/null; then\n            SUSPEND_WINDOWS=1\n            while (( TIME_ELAPSED < AUTOPAUSE_TIME )); do\n                if ls \"$APPDATA_PATH\"/FreeRDP_Process_*.cproc &>/dev/null; then\n                    SUSPEND_WINDOWS=0\n                    break\n                fi\n                sleep $TIME_INTERVAL\n                TIME_ELAPSED=$((TIME_ELAPSED + TIME_INTERVAL))\n            done\n        fi\n\n        # Hibernate/Pause Windows.\n        if [ \"$SUSPEND_WINDOWS\" -eq 1 ]; then\n            dprint \"IDLE FOR ${AUTOPAUSE_TIME} SECONDS. SUSPENDING WINDOWS.\"\n            notify-send --expire-time=8000 --icon=\"info\" --app-name=\"WinApps\" --urgency=\"low\" \"WinApps\" \"Pausing Windows due to inactivity.\"\n            if [ \"$WAFLAVOR\" = \"docker\" ]; then\n                docker compose --file \"$COMPOSE_PATH\" pause &>/dev/null\n            elif [ \"$WAFLAVOR\" = \"podman\" ]; then\n                podman-compose --file \"$COMPOSE_PATH\" pause &>/dev/null\n            elif [ \"$WAFLAVOR\" = \"libvirt\" ]; then\n                virsh suspend \"$VM_NAME\" &>/dev/null\n            fi\n        fi\n    fi\n}\n\n# Name: 'waTimeSync'\n# Role: Detect if system went to sleep by comparing uptime progression, then sync time in Windows VM\nfunction waTimeSync() {\n    local CURRENT_TIME\n    local CURRENT_UPTIME\n    local STORED_TIME=0\n    local STORED_UPTIME=0\n    local EXPECTED_UPTIME=0\n    local UPTIME_DIFF=0\n\n    CURRENT_TIME=$(date +%s)\n    CURRENT_UPTIME=$(awk '{print int($1)}' /proc/uptime)\n\n    # Read stored values if file exists\n    if [ -f \"$SLEEP_DETECT_PATH\" ]; then\n        STORED_TIME=$(head -n1 \"$SLEEP_DETECT_PATH\" 2>/dev/null || echo 0)\n        STORED_UPTIME=$(tail -n1 \"$SLEEP_DETECT_PATH\" 2>/dev/null || echo 0)\n    fi\n\n    if [ \"$STORED_TIME\" -gt 0 ] && [ \"$STORED_UPTIME\" -gt 0 ]; then\n        # Calculate what uptime should be now\n        EXPECTED_UPTIME=$((STORED_UPTIME + CURRENT_TIME - STORED_TIME))\n        UPTIME_DIFF=$((EXPECTED_UPTIME - CURRENT_UPTIME))\n\n        dprint \"UPTIME_DIFF: ${UPTIME_DIFF} seconds\"\n\n        # If uptime is significantly less than expected, system likely slept\n        if [[ \"$UPTIME_DIFF\" -gt 30 && ! -f \"$SLEEP_MARKER\" ]]; then\n            dprint \"DETECTED SLEEP/WAKE CYCLE (uptime gap: ${UPTIME_DIFF}s). CREATING SLEEP MARKER TO SYNC WINDOWS TIME.\"\n\n            # Create sleep marker which will be monitored by Windows VM to trigger time sync\n            touch -- \"$SLEEP_MARKER\"\n\n            dprint \"CREATED SLEEP MARKER\"\n        fi\n    fi\n\n    # Store current values\n    {\n        echo \"$CURRENT_TIME\"\n        echo \"$CURRENT_UPTIME\"\n    } > \"$SLEEP_DETECT_PATH\"\n}\n\nfunction waFindVMIP() {\n    local VM_MAC=\"\"\n\n    VM_MAC=$(virsh domiflist \"$VM_NAME\" | grep -oE \"([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})\") # VM MAC address.\n\n    # VM IP address\n    ip neigh show | grep -F -- \"$VM_MAC\" | grep -oE \"([0-9]{1,3}\\.){3}[0-9]{1,3}\"\n}\n\n### MAIN LOGIC ###\n#set -x # Enable for debugging.\nmkdir -p \"$APPDATA_PATH\"\ndprint \"START\"\ndprint \"SCRIPT_DIR: ${SCRIPT_DIR_PATH}\"\ndprint \"SCRIPT_ARGS: ${*}\"\ndprint \"HOME_DIR: ${HOME}\"\nwaEarlyDispatch \"$@\"\nwaLastRun\nwaLoadConfig\nwaGetFreeRDPCommand\nwaCleanFreeRDP\n\n# Send password on the command line if a command to retrieve the password from is not given\n# Otherwise, set FREERDP_ASKPASS which freerdp will read the stdout of to use as the password\nRDP_PASSWORD_ARG=\"/p:$RDP_PASS\"\n\nif [[ ! -z \"$RDP_ASKPASS\" ]]; then\n    export FREERDP_ASKPASS=\"$RDP_ASKPASS\"\n    unset RDP_PASSWORD_ARG\nfi\n\n# If using podman backend, modify the FreeRDP command to enter a new namespace.\nif [ \"$WAFLAVOR\" = \"podman\" ]; then\n    FREERDP_COMMAND=\"podman unshare --rootless-netns ${FREERDP_COMMAND}\"\nfi\n\nif [ \"$WAFLAVOR\" = \"docker\" ] || [ \"$WAFLAVOR\" = \"podman\" ]; then\n    RDP_IP=\"$DOCKER_IP\"\n    waCheckContainerRunning\nelif [ \"$WAFLAVOR\" = \"libvirt\" ]; then\n    waCheckGroupMembership\n    waCheckVMRunning\nelif [ \"$WAFLAVOR\" = \"manual\" ]; then\n    waCheckPortOpen\nelse\n    waThrowExit \"$EC_INVALID_FLAVOR\"\nfi\n\nwaCheckPortOpen\nwaTimeSync\nwaRunCommand \"$@\"\n\nif [[ \"$AUTOPAUSE\" == \"on\" ]]; then\n    waCheckIdle\nfi\n\ndprint \"END\"\n"
  },
  {
    "path": "compose.yaml",
    "content": "# For documentation, FAQ, additional configuration options and technical help, visit: https://github.com/dockur/windows\n\nname: \"winapps\" # Docker Compose Project Name.\nvolumes:\n  # Create Volume 'data'.\n  # Located @ '/var/lib/docker/volumes/winapps_data/_data' (Docker).\n  # Located @ '/var/lib/containers/storage/volumes/winapps_data/_data' or '~/.local/share/containers/storage/volumes/winapps_data/_data' (Podman).\n  data:\nservices:\n  windows:\n    image: ghcr.io/dockur/windows:latest\n    container_name: WinApps # Created Docker VM Name.\n    environment:\n      # Version of Windows to configure. For valid options, visit:\n      # https://github.com/dockur/windows?tab=readme-ov-file#how-do-i-select-the-windows-version\n      # https://github.com/dockur/windows?tab=readme-ov-file#how-do-i-install-a-custom-image\n      VERSION: \"11\"\n      RAM_SIZE: \"4G\" # RAM allocated to the Windows VM.\n      CPU_CORES: \"4\" # CPU cores allocated to the Windows VM.\n      DISK_SIZE: \"64G\" # Size of the primary hard disk.\n      # DISK2_SIZE: \"32G\" # Uncomment to add an additional hard disk to the Windows VM. Ensure it is mounted as a volume below.\n      USERNAME: \"MyWindowsUser\" # Edit here to set a custom Windows username. The default is 'MyWindowsUser'.\n      PASSWORD: \"MyWindowsPassword\" # Edit here to set a password for the Windows user. The default is 'MyWindowsPassword'.\n      HOME: \"${HOME}\" # Set path to Linux user home folder.\n    ports:\n      - 8006:8006 # Map '8006' on Linux host to '8006' on Windows VM --> For VNC Web Interface @ http://127.0.0.1:8006.\n      - 3389:3389/tcp # Map '3389' on Linux host to '3389' on Windows VM --> For Remote Desktop Protocol (RDP).\n      - 3389:3389/udp # Map '3389' on Linux host to '3389' on Windows VM --> For Remote Desktop Protocol (RDP).\n    cap_add:\n      - NET_ADMIN  # Add network permission\n    stop_grace_period: 120s # Wait 120 seconds before sending SIGTERM when attempting to shut down the Windows VM.\n    restart: on-failure # Restart the Windows VM if the exit code indicates an error.\n    volumes:\n      - data:/storage # Mount volume 'data' to use as Windows 'C:' drive.\n      - ${HOME}:/shared # Mount Linux user home directory @ '\\\\host.lan\\Data'.\n      #- /path/to/second/hard/disk:/storage2 # Uncomment to create a virtual second hard disk and mount it within the Windows VM. Ensure 'DISK2_SIZE' is specified above.\n      - ./oem:/oem # Enables automatic post-install execution of 'oem/install.bat', applying Windows registry modifications contained within 'oem/RDPApps.reg'.\n      #- /path/to/windows/install/media.iso:/custom.iso # Uncomment to use a custom Windows ISO. If specified, 'VERSION' (e.g. 'tiny11') will be ignored.\n    devices:\n      - /dev/kvm # Enable KVM.\n      - /dev/net/tun # Enable tuntap\n      # Uncomment to mount a disk directly within the Windows VM.\n      # WARNING: /dev/sdX paths may change after reboot. Use persistent identifiers!\n      # NOTE: 'disk1' will be mounted as the main drive. THIS DISK WILL BE FORMATTED BY DOCKER.\n      # All following disks (disk2, ...) WILL NOT BE FORMATTED.\n      # - /dev/disk/by-id/<id>:/disk1\n      # - /dev/disk/by-id/<id>:/disk2\n    # group_add:      # uncomment this line and the next one for using rootless podman containers\n    #   - keep-groups # to make /dev/kvm work with podman. needs \"crun\" installed, \"runc\" will not work! Add your user to the 'kvm' group or another that can access /dev/kvm.\n"
  },
  {
    "path": "default.nix",
    "content": "(import (\n  let\n    lock = builtins.fromJSON (builtins.readFile ./flake.lock);\n  in\n  fetchTarball {\n    url =\n      lock.nodes.flake-compat.locked.url\n        or \"https://github.com/edolstra/flake-compat/archive/${lock.nodes.flake-compat.locked.rev}.tar.gz\";\n    sha256 = lock.nodes.flake-compat.locked.narHash;\n  }\n) { src = ./.; }).defaultNix\n"
  },
  {
    "path": "docs/docker.md",
    "content": "# Creating a Windows VM in `Docker` or `Podman`\nAlthough WinApps supports using `QEMU+KVM+libvirt` as a backend for running Windows virtual machines, it is recommended to use `Docker` or `Podman`. These backends automate the setup process, eliminating the need for manual configuration and optimisation of the Windows virtual machine.\n\n> [!IMPORTANT]\n> Running a Windows virtual machine using `Docker` or `Podman` as a backend is only possible on GNU/Linux systems. This is due to the necessity of kernel interfaces, such as the KVM hypervisor, for achieving acceptable performance. The performance of the virtual machine can vary based on the version of the Linux kernel, with newer releases generally offering better performance.\n\n> [!IMPORTANT]\n> WinApps does __NOT__ officially support versions of Windows prior to Windows 10. Despite this, it may be possible to achieve a successful installation with some additional experimentation. If you find a way to achieve this, please share your solution through a pull request for the benefit of other users.\n> Possible setup instructions for Windows 10:\n> - 'Professional', 'Enterprise' or 'Server' editions of Windows are required to run RDP applications. Windows 'Home' will __NOT__ suffice.\n> - It is recommended to edit the initial `compose.yaml` file to keep your required username and password from the beginning.\n> - It is recommended to not use `sudo` to force commands to run. Add your user to the relevant permissions group wherever possible.\n\n> [!IMPORTANT]\n> The iptables kernel module must be loaded for folder sharing with the host to work.\n> Check that the output of `lsmod | grep ip_tables` and `lsmod | grep iptable_nat` is non-empty.\n> If the output of one of the previous commands is empty, run `echo -e \"ip_tables\\niptable_nat\" | sudo tee /etc/modules-load.d/iptables.conf` and reboot.\n\n## `Docker`\n### Installation\nYou can find a guide for installing `Docker Engine` [here](https://docs.docker.com/engine/install/).\n\n### Setup `Docker` Container\nWinApps utilises `docker compose` to configure Windows VMs. A template [`compose.yaml`](../compose.yaml) is provided.\n\nPrior to installing Windows, you can modify the RAM and number of CPU cores available to the Windows VM by changing `RAM_SIZE` and `CPU_CORES` within `compose.yaml`.\n\nIt is also possible to specify the version of Windows you wish to install within `compose.yaml` by modifying `VERSION`.\n\nPlease refer to the [original GitHub repository](https://github.com/dockur/windows) for more information on additional configuration options.\n\n> [!NOTE]\n> If you want to undo all your changes and start from scratch, run the following. For `podman`, replace `docker compose` with `podman-compose`.\n> ```bash\n> docker compose down --rmi=all --volumes\n> ```\n\n### Installing Windows\nYou can initiate the Windows installation using `docker compose`.\n```bash\ncd winapps\ndocker compose --file ./compose.yaml up\n```\n\nYou can then access the Windows virtual machine via a VNC connection to complete the Windows setup by navigating to http://127.0.0.1:8006 in your web browser.\n\n### Changing `compose.yaml`\nChanges to `compose.yaml` require the container to be removed and re-created. This should __NOT__ affect your data.\n\n```bash\n# Stop and remove the existing container.\ndocker compose --file ~/.config/winapps/compose.yaml down\n\n# Remove the existing FreeRDP certificate (if required).\n# Note: A new certificate will be created when connecting via RDP for the first time.\nrm ~/.config/freerdp/server/127.0.0.1_3389.pem\n\n# Re-create the container with the updated configuration.\n# Add the -d flag at the end to run the container in the background.\ndocker compose --file ~/.config/winapps/compose.yaml up\n```\n\n### Subsequent Use\n```bash\ndocker compose --file ~/.config/winapps/compose.yaml start # Power on the Windows VM\ndocker compose --file ~/.config/winapps/compose.yaml pause # Pause the Windows VM\ndocker compose --file ~/.config/winapps/compose.yaml unpause # Resume the Windows VM\ndocker compose --file ~/.config/winapps/compose.yaml restart # Restart the Windows VM\ndocker compose --file ~/.config/winapps/compose.yaml stop # Gracefully shut down the Windows VM\ndocker compose --file ~/.config/winapps/compose.yaml kill # Force shut down the Windows VM\n```\n\n## `Podman`\n### Installation\n1. Install `Podman` using [this guide](https://podman.io/docs/installation).\n2. Install `podman-compose` using [this guide](https://github.com/containers/podman-compose?tab=readme-ov-file#installation).\n\n### Setup `Podman` Container\nPlease follow the [`docker` instructions](#setup-docker-container).\n\n> [!NOTE]\n> #### Rootless `podman` containers\n> If you are invoking podman as a user, your container will be \"rootless\". This can be desirable as a security feature. However, you may encounter an error about missing permissions to /dev/kvm as a consequence.\n>\n> For rootless podman to work, you need to add your user to the `kvm` group (depending on your distribution) to be able to access `/dev/kvm`. Make sure that you are using `crun` as your container runtime, not `runc`. Usually this is done by stopping all containers and (de-)installing the corresponding packages. Then either invoke podman-compose as `podman-compose --file ./compose.yaml --podman-create-args '--group-add keep-groups' up`. Or edit `compose.yaml` and uncomment the `group_add:` section at the end, and add `[]`.\n\n> [!IMPORTANT]\n> Ensure `WAFLAVOR` is set to `\"podman\"` in `~/.config/winapps/winapps.conf`.\n\n### Installing Windows\nYou can initiate the Windows installation using `podman-compose`.\n```bash\ncd winapps\npodman-compose --file ./compose.yaml up\n```\n\nYou can then access the Windows virtual machine via a VNC connection to complete the Windows setup by navigating to http://127.0.0.1:8006 in your web browser.\n\n### Changing `compose.yaml`\nChanges to `compose.yaml` require the container to be removed and re-created. This should __NOT__ affect your data.\n\n```bash\n# Stop and remove the existing container.\npodman-compose --file ~/.config/winapps/compose.yaml down\n\n# Remove the existing FreeRDP certificate (if required).\n# Note: A new certificate will be created when connecting via RDP for the first time.\nrm ~/.config/freerdp/server/127.0.0.1_3389.pem\n\n# Re-create the container with the updated configuration.\npodman-compose --file ~/.config/winapps/compose.yaml up\n```\n\n### Subsequent Use\n```bash\npodman-compose --file ~/.config/winapps/compose.yaml start # Power on the Windows VM\npodman-compose --file ~/.config/winapps/compose.yaml pause # Pause the Windows VM\npodman-compose --file ~/.config/winapps/compose.yaml unpause # Resume the Windows VM\npodman-compose --file ~/.config/winapps/compose.yaml restart # Restart the Windows VM\npodman-compose --file ~/.config/winapps/compose.yaml stop # Gracefully shut down the Windows VM\npodman-compose --file ~/.config/winapps/compose.yaml kill # Force shut down the Windows VM\n```\n"
  },
  {
    "path": "docs/libvirt.md",
    "content": "# Creating a `libvirt` Windows VM\nThis method of configuring a Windows virtual machine for use with WinApps is significantly more involved than utilising `Docker` or `Podman`. Nevertheless, expert users may prefer this method due to its greater flexibility and wider range of customisation options (e.g. GPU passthrough).\n\n<details>\n<summary><strong>Understanding The Virtualisation Stack</strong></summary>\n\nBefore beginning, it is important to have a basic understanding of the various components involved in this particular method.\n\n1. `QEMU` is a FOSS emulator that performs hardware virtualisation, enabling operating systems and applications designed for one architecture (e.g., aarch64) to run on systems with differing architectures (e.g., amd64). When used in conjunction with `KVM`, it can run virtual machines at near-native speed (provided the guest virtual machine matches the host architecture) by utilising hardware extensions like Intel VT-x or AMD-V.\n2. `KVM` is a Linux kernel module that enables the kernel to function as a type-1 hypervisor. `KVM` runs directly on the underlying hardware (as opposed to on top of the GNU/Linux host OS). For many workloads, the performance overhead is minimal, often in the range of 2-5%. `KVM` requires a CPU with hardware virtualisation extensions.\n3. `libvirt` is an open-source API, daemon, and management tool for orchestrating platform virtualisation. It provides a consistent and stable interface for managing various virtualisation technologies, including `KVM` and `QEMU` (as well as others). `libvirt` offers a wide range of functionality to control the lifecycle of virtual machines, storage, networks, and interfaces, making it easier to interact with virtualisation capabilities programmatically or via command-line tools.\n4. `virt-manager` (Virtual Machine Manager) is a GUI desktop application that provides an easy-to-use interface for creating, configuring and controlling virtual machines. `virt-manager`  utilises `libvirt` as a backend.\n\nTogether, these components form a powerful and flexible virtualization stack, with `KVM` providing low-level kernel-based virtualisation capabilities, `QEMU` providing high-level userspace-based virtualisation functionality, `libvirt` managing the resources and `virt-manager` offering an intuitive graphical management interface.\n\n<p align=\"center\">\n    <img src=\"./libvirt_images/Virtualisation_Stack.svg\" width=\"500px\"/>\n</p>\n\n</details>\n\n## Prerequisites\n1. Ensure your CPU supports hardware virtualisation extensions by [reading this article](https://wiki.archlinux.org/title/KVM).\n\n2. Install all dependencies by installing `virt-manager`. This will ensure that your package manager automatically installs all the necessary components.\n    ```bash\n    sudo apt install virt-manager # Debian/Ubuntu\n    sudo dnf install virt-manager # Fedora/RHEL\n    sudo pacman -S virt-manager # Arch Linux\n    sudo emerge app-emulation/virt-manager # Gentoo Linux\n    ```\n\n3. Configure `libvirt` to use the 'system' URI by adding the line `LIBVIRT_DEFAULT_URI=\"qemu:///system\"` to your preferred shell profile file (e.g., `.bashrc`, `.zshrc`, etc.).\n    ```bash\n    echo 'export LIBVIRT_DEFAULT_URI=\"qemu:///system\"' >> ~/.bashrc\n    ```\n\n> [!NOTE]\n> WinApps may not read your shell's configuration. If you're having issues getting the installer to detect your VM, try adding\n> `LIBVIRT_DEFAULT_URI=\"qemu:///system\"` to your `/etc/environment` like:\n> ```bash\n> echo 'LIBVIRT_DEFAULT_URI=\"qemu:///system\"' | sudo tee -a /etc/environment\n> ```\n> Thanks to imoize for pointing this out: https://github.com/winapps-org/winapps/issues/310#issuecomment-2505348088\n\n4. Configure rootless `libvirt` and `kvm` by adding your user to groups of the same name.\n    ``` bash\n    sudo usermod -a -G kvm $(id -un) # Add the user to the 'kvm' group.\n    sudo usermod -a -G libvirt $(id -un) # Add the user to the 'libvirt' group.\n    sudo reboot # Reboot the system to ensure the user is added to the relevant groups.\n    ```\n\n    Note: On NixOS, the group name for libvirt is `libvirtd` and not `libvirt`. In addition, user and group management on NixOS is handled through the Nix configuration files and not via traditional tools like `usermod`. Please see \"Adding User to a group\" on [this NixOS Wiki page](https://wiki.nixos.org/wiki/User_management).\n\n    Note: Due to a known bug in `rpm-ostree`, which affects various distributions such as Silverblue, Bazzite, Bluefin, Kinoite, Aurora, UCore, and others, the commands provided earlier may not properly add your user to all required groups. If the `groups $USER` command does not show your user as being part of the necessary groups, you'll need to manually add these groups to `/etc/group` if they are present in `/usr/lib/group`.\n\n    To resolve this:\n    1. Identify which groups are missing from the output of `groups $USER`.\n    2. Use the following snippet to add each missing group to `/etc/group`. Ensure you replace \"kvm\" with the name of the missing group.\n\n        ```bash\n        grep -E '^kvm:' /usr/lib/group | sudo tee -a /etc/group\n        sudo usermod -aG kvm $USER\n        ```\n\n    3. Reboot your system to ensure that the user is correctly added to the relevant groups.\n\n5. If relevant to your distribution, disable `AppArmor` for the `libvirt` daemon.\n    ``` bash\n    sudo ln -s /etc/apparmor.d/usr.sbin.libvirtd /etc/apparmor.d/disable/ # Disable AppArmor for the libvirt daemon by creating a symbolic link.\n    ```\n\n> [!NOTE]\n> Systems with `SELinux` may also require security policy adjustments if virtual machine images are stored outside the default `/var/lib/libvirt/images` directory. Read [this guide](https://docs.redhat.com/en/documentation/red_hat_enterprise_linux/5/html/virtualization/sect-virtualization-security_for_virtualization-selinux_and_virtualization#sect-Virtualization-Security_for_virtualization-SELinux_and_virtualization) for more information.\n\n6. Download a [Windows 10](https://www.microsoft.com/software-download/windows10ISO) or [Windows 11](https://www.microsoft.com/software-download/windows11) installation `.ISO` image.\n\n> [!IMPORTANT]\n> 'Professional', 'Enterprise' or 'Server' editions of Windows are required to run RDP applications. Windows 'Home' will NOT suffice.\n\n7. Download [VirtIO drivers](https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/latest-virtio/virtio-win.iso) for the Windows virtual machine.\n\n> [!NOTE]\n> VirtIO drivers enhance system performance and minimize overhead by enabling the Windows virtual machine to use specialised network and disk device drivers. These drivers are aware that they are operating inside a virtual machine, and cooperate with the hypervisor. This approach eliminates the need for the hypervisor to emulate physical hardware devices, which is a computationally expensive process. This setup allows guests to achieve high-performance network and disk operations, leveraging the benefits of paravirtualisation.\n> The above link contains the latest release of the `VirtIO` drivers for Windows, compiled and signed by Red Hat. Older versions of the `VirtIO` drivers can be downloaded [here](https://fedorapeople.org/groups/virt/virtio-win/direct-downloads/archive-virtio/?C=M;O=D).\n> You can read more about `VirtIO` [here](https://wiki.libvirt.org/Virtio.html) and [here](https://developer.ibm.com/articles/l-virtio/).\n\n## Creating a Windows VM\n1. Open `virt-manager`.\n\n> [!NOTE]\n> The name given to the application can vary between GNU/Linux distributions (e.g., 'Virtual Machines', 'Virtual Machine Manager', etc.)\n\n<p align=\"center\">\n    <img src=\"./libvirt_images/00.png\" width=\"500px\"/>\n</p>\n\n2. Navigate to `Edit`&rarr;`Preferences`. Ensure `Enable XML editing` is enabled, then click the `Close` button.\n\n<p align=\"center\">\n    <img src=\"./libvirt_images/01.png\" width=\"500px\"/>\n</p>\n\n3. Create a new virtual machine by clicking the `+` button.\n\n<p align=\"center\">\n    <img src=\"./libvirt_images/02.png\" width=\"500px\" alt=\"Creating a new virtual machine in 'virt-manager'\"/>\n</p>\n\n4. Choose `Local install media` and click `Forward`.\n\n<p align=\"center\">\n    <img src=\"./libvirt_images/03.png\" width=\"500px\"/>\n</p>\n\n5. Select the location of your Windows 10 or 11 `.ISO` by clicking `Browse...` and `Browse Local`. Ensure `Automatically detect from the installation media / source` is enabled.\n\n<p align=\"center\">\n    <img src=\"./libvirt_images/04_1.png\" width=\"500px\"/>\n    <img src=\"./libvirt_images/04_2.png\" width=\"700px\"/>\n</p>\n\n6. Configure the RAM and CPU cores allocated to the Windows virtual machine. We recommend `2` CPUs and `4096MB` of RAM. We will use the `VirtIO` Memory Ballooning service, which means the virtual machine can use up to `4096MB` of memory, but it will only consume this amount if necessary.\n\n<p align=\"center\">\n    <img src=\"./libvirt_images/05.png\" width=\"500px\"/>\n</p>\n\n7. Configure the virtual disk by setting its maximum size. While this size represents the largest it can grow to, the disk will only use this space as needed.\n\n<p align=\"center\">\n    <img src=\"./libvirt_images/06.png\" width=\"500px\"/>\n</p>\n\n8. Name your virtual machine `RDPWindows` to ensure it is recognized by WinApps, and select the option to `Customize configuration before installation`.\n\n<p align=\"center\">\n    <img src=\"./libvirt_images/07.png\" width=\"500px\"/>\n</p>\n\n> [!NOTE]\n> A name other than `RDPWindows` can be used if `VM_NAME` is set in `~/.config/winapps/winapps.conf`.\n\n9. After clicking `Finish`, select `Copy host CPU configuration` under 'CPUs', and then click `Apply`.\n\n> [!NOTE]\n> Sometimes this feature gets disabled after installing Windows. Make sure to check and re-enable this option after the installation is complete.\n\n<p align=\"center\">\n    <img src=\"./libvirt_images/08.png\" width=\"700px\"/>\n</p>\n\n10. Navigate to the `XML` tab, and edit the `<clock>` section to disable all timers except for the hypervclock, thereby drastically reducing idle CPU usage. Once changed, click `Apply`.\n    ```xml\n    <clock offset='localtime'>\n      <timer name='rtc' present='no' tickpolicy='catchup'/>\n      <timer name='pit' present='no' tickpolicy='delay'/>\n      <timer name='hpet' present='no'/>\n      <timer name='kvmclock' present='no'/>\n      <timer name='hypervclock' present='yes'/>\n    </clock>\n    ```\n\n<p align=\"center\">\n    <img src=\"./libvirt_images/09.png\" width=\"700px\"/>\n</p>\n\n11. Enable Hyper-V enlightenments by adding the following to the `<hyperv>` section. Once changed, click `Apply`.\n\n    ```xml\n    <hyperv>\n      <relaxed state='on'/>\n      <vapic state='on'/>\n      <spinlocks state='on' retries='8191'/>\n      <vpindex state='on'/>\n      <synic state='on'/>\n      <stimer state='on'>\n        <direct state='on'/>\n      </stimer>\n      <reset state='on'/>\n      <frequencies state='on'/>\n      <reenlightenment state='on'/>\n      <tlbflush state='on'/>\n      <ipi state='on'/>\n    </hyperv>\n    ```\n\n> [!NOTE]\n> Hyper-V enlightenments make Windows (and other Hyper-V guests) think they are running on top of a Hyper-V compatible hypervisor. This enables use of Hyper-V specific features, allowing `KVM` to implement paravirtualised interfaces for improved virtual machine performance.\n\n12. Add the following XML snippet within the `<devices>` section to enable the GNU/Linux host to communicate with Windows using `QEMU Guest Agent`.\n\n    ```xml\n    <channel type='unix'>\n      <source mode='bind'/>\n      <target type='virtio' name='org.qemu.guest_agent.0'/>\n      <address type='virtio-serial' controller='0' bus='0' port='2'/>\n    </channel>\n    ```\n\n13. In the 'Memory' section, set the `Current allocation` to the minimum amount of memory you want the virtual machine to use, with a recommended value of `1024MB`.\n> [!NOTE]\n> Depending upon certain apps (like OneDrive), the balloon driver cannot keep up. If you are having segfaults (random reboots), turn off memory ballooning. `<memballoon model=\"none\"/>`\n\n<p align=\"center\">\n    <img src=\"./libvirt_images/10.png\" width=\"500px\"/>\n</p>\n\n14. (Optional) Under `Boot Options`, enable `Start virtual machine on host boot up`.\n\n<p align=\"center\">\n    <img src=\"./libvirt_images/11.png\" width=\"500px\"/>\n</p>\n\n15. Navigate to 'SATA Disk 1' and set the `Disk bus` type to `VirtIO`. This allows disk access to be paravirtualised, improving virtual machine performance.\n\n<p align=\"center\">\n    <img src=\"./libvirt_images/12.png\" width=\"500px\"/>\n</p>\n\n16. Navigate to 'NIC' and set the `Device model` type to `virtio` to enable paravirtualised networking.\n> [!NOTE]\n> `virtio` is the recommended device model and should be used for normal operation. If you specifically want internet access during the Windows installation (for example, to sign in with a Microsoft account), you may temporarily set the device model to `e1000e`. After installation and VirtIO driver setup are complete, switch the NIC back to `virtio` for best performance.\n\n<p align=\"center\">\n    <img src=\"./libvirt_images/13.png\" width=\"500px\"/>\n</p>\n\n17. Click the `Add Hardware` button in the lower left, and choose `Storage`. For `Device type`, select `CDROM device` and choose the VirtIO driver `.ISO` you downloaded earlier. Click `Finish` to add the new CD-ROM device.\n\n> [!IMPORTANT]\n> If you skip this step, the Windows installer will fail to recognise and list the virtual hard drive you created earlier.\n\n<p align=\"center\">\n    <img src=\"./libvirt_images/14.png\" width=\"500px\"/>\n</p>\n\n<details>\n<summary><strong>(Optional) Assign Specific Physical CPU Cores</strong></summary>\n\nAssigning specific physical CPU cores to the virtual machine can improve performance by reducing context switching and ensuring that the virtual machine's workload consistently uses the same cores, leading to better CPU cache utilisation. This is an optional step.\n\n1. Run `lscpu -e` to determine which L1, L2 and L3 caches are associated with which CPU cores.\n\n    Example 1 (Intel 11th Gen Core i7-1185G7):\n    ```\n    CPU NODE SOCKET CORE L1d:L1i:L2:L3 ONLINE    MAXMHZ   MINMHZ\n      0    0      0    0 0:0:0:0          yes 4800.0000 400.0000\n      1    0      0    1 1:1:1:0          yes 4800.0000 400.0000\n      2    0      0    2 2:2:2:0          yes 4800.0000 400.0000\n      3    0      0    3 3:3:3:0          yes 4800.0000 400.0000\n      4    0      0    0 0:0:0:0          yes 4800.0000 400.0000\n      5    0      0    1 1:1:1:0          yes 4800.0000 400.0000\n      6    0      0    2 2:2:2:0          yes 4800.0000 400.0000\n      7    0      0    3 3:3:3:0          yes 4800.0000 400.0000\n    ```\n\n    - C<sub>0</sub> = T<sub>0</sub>+T<sub>4</sub> &rarr; L1<sub>0</sub>+L2<sub>0</sub>+L3<sub>0</sub>\n    - C<sub>1</sub> = T<sub>1</sub>+T<sub>5</sub> &rarr; L1<sub>1</sub>+L2<sub>1</sub>+L3<sub>0</sub>\n    - C<sub>2</sub> = T<sub>2</sub>+T<sub>6</sub> &rarr; L1<sub>2</sub>+L2<sub>2</sub>+L3<sub>0</sub>\n    - C<sub>3</sub> = T<sub>3</sub>+T<sub>7</sub> &rarr; L1<sub>3</sub>+L2<sub>3</sub>+L3<sub>0</sub>\n\n    Example 2 (AMD Ryzen 5 1600):\n    ```\n    CPU NODE SOCKET CORE L1d:L1i:L2:L3 ONLINE MAXMHZ    MINMHZ\n    0   0    0      0    0:0:0:0       yes    3800.0000 1550.0000\n    1   0    0      0    0:0:0:0       yes    3800.0000 1550.0000\n    2   0    0      1    1:1:1:0       yes    3800.0000 1550.0000\n    3   0    0      1    1:1:1:0       yes    3800.0000 1550.0000\n    4   0    0      2    2:2:2:0       yes    3800.0000 1550.0000\n    5   0    0      2    2:2:2:0       yes    3800.0000 1550.0000\n    6   0    0      3    3:3:3:1       yes    3800.0000 1550.0000\n    7   0    0      3    3:3:3:1       yes    3800.0000 1550.0000\n    8   0    0      4    4:4:4:1       yes    3800.0000 1550.0000\n    9   0    0      4    4:4:4:1       yes    3800.0000 1550.0000\n    10  0    0      5    5:5:5:1       yes    3800.0000 1550.0000\n    11  0    0      5    5:5:5:1       yes    3800.0000 1550.0000\n    ```\n\n    - C<sub>0</sub> = T<sub>0</sub>+T<sub>1</sub> &rarr; L1<sub>0</sub>+L2<sub>0</sub>+L3<sub>0</sub>\n    - C<sub>1</sub> = T<sub>2</sub>+T<sub>3</sub> &rarr; L1<sub>1</sub>+L2<sub>1</sub>+L3<sub>0</sub>\n    - C<sub>2</sub> = T<sub>4</sub>+T<sub>5</sub> &rarr; L1<sub>2</sub>+L2<sub>2</sub>+L3<sub>0</sub>\n    - C<sub>3</sub> = T<sub>6</sub>+T<sub>7</sub> &rarr; L1<sub>3</sub>+L2<sub>3</sub>+L3<sub>1</sub>\n    - C<sub>4</sub> = T<sub>8</sub>+T<sub>9</sub> &rarr; L1<sub>4</sub>+L2<sub>4</sub>+L3<sub>1</sub>\n    - C<sub>5</sub> = T<sub>10</sub>+T<sub>11</sub> &rarr; L1<sub>5</sub>+L2<sub>5</sub>+L3<sub>1</sub>\n\n2. Select which CPU cores to 'pin'. You should aim to select a combination of CPU cores that minimises sharing of caches between Windows and GNU/Linux.\n\n    Example 1:\n    - CPU cores share the same singular L3 cache, so this cannot be optimised.\n    - CPU cores utilise different L1 and L2 caches, so isolating corresponding thread pairs will help improve performance.\n    - Thus, if limiting the virtual machine to a maximum of 4 threads, there are 10 possible optimal configurations:\n        - T<sub>0</sub>+T<sub>4</sub>\n        - T<sub>1</sub>+T<sub>5</sub>\n        - T<sub>2</sub>+T<sub>6</sub>\n        - T<sub>3</sub>+T<sub>7</sub>\n        - T<sub>0</sub>+T<sub>4</sub>+T<sub>1</sub>+T<sub>5</sub>\n        - T<sub>0</sub>+T<sub>4</sub>+T<sub>2</sub>+T<sub>6</sub>\n        - T<sub>0</sub>+T<sub>4</sub>+T<sub>3</sub>+T<sub>7</sub>\n        - T<sub>1</sub>+T<sub>5</sub>+T<sub>2</sub>+T<sub>6</sub>\n        - T<sub>1</sub>+T<sub>5</sub>+T<sub>3</sub>+T<sub>7</sub>\n        - T<sub>2</sub>+T<sub>6</sub>+T<sub>3</sub>+T<sub>7</sub>\n\n    Example 2:\n    - Threads 0-5 utilise one L3 cache whereas threads 6-11 utilise a different L3 cache. Thus, one of these two sets of threads should be pinned to the virtual machine.\n    - Pinning and isolating fewer than these (e.g. threads 8-11) would result in the host system making use of the L3 cache in threads 6 and 7, resulting in cache evictions and therefore bad performance.\n    - Thus, there are only two possible optimal configurations:\n        - T<sub>0</sub>+T<sub>1</sub>+T<sub>2</sub>+T<sub>3</sub>+T<sub>4</sub>+T<sub>5</sub>\n        - T<sub>6</sub>+T<sub>7</sub>+T<sub>8</sub>+T<sub>9</sub>+T<sub>10</sub>+T<sub>11</sub>\n\n3. Prepare and add/modify the following to the `<vcpu>`, `<cputune>` and `<cpu>` sections, adjusting the values to match your selected threads.\n\n    Example 1: The following selects 'T<sub>2</sub>+T<sub>6</sub>+T<sub>3</sub>+T<sub>7</sub>'.\n\n    ```xml\n    <vcpu placement=\"static\">4</vcpu>\n    <cputune>\n        <vcpupin vcpu=\"0\" cpuset=\"2\"/>\n        <vcpupin vcpu=\"1\" cpuset=\"6\"/>\n        <vcpupin vcpu=\"2\" cpuset=\"3\"/>\n        <vcpupin vcpu=\"3\" cpuset=\"7\"/>\n    </cputune>\n    <cpu mode=\"host-passthrough\" check=\"none\" migratable=\"on\">\n        <topology sockets=\"1\" dies=\"1\" clusters=\"1\" cores=\"2\" threads=\"2\"/>\n    </cpu>\n    ```\n\n    Example 2: The following selects 'T<sub>6</sub>+T<sub>7</sub>+T<sub>8</sub>+T<sub>9</sub>+T<sub>10</sub>+T<sub>11</sub>'.\n\n    ```xml\n    <vcpu placement=\"static\">6</vcpu>\n    <cputune>\n        <vcpupin vcpu=\"0\" cpuset=\"6\"/>\n        <vcpupin vcpu=\"1\" cpuset=\"7\"/>\n        <vcpupin vcpu=\"2\" cpuset=\"8\"/>\n        <vcpupin vcpu=\"3\" cpuset=\"9\"/>\n        <vcpupin vcpu=\"4\" cpuset=\"10\"/>\n        <vcpupin vcpu=\"5\" cpuset=\"11\"/>\n    </cputune>\n    <cpu mode=\"host-passthrough\" check=\"none\" migratable=\"on\">\n        <topology sockets=\"1\" dies=\"1\" clusters=\"1\" cores=\"3\" threads=\"2\"/>\n    </cpu>\n    ```\n\n> [!NOTE]\n> More information on configuring CPU pinning can be found in [this excellent guide](https://wiki.archlinux.org/title/PCI_passthrough_via_OVMF#CPU_pinning).\n\n</details>\n\nBelow is an example `.XML` file that describes a Windows 11 virtual machine.\n<details>\n<summary><strong>Example .XML File</strong></summary>\n\n```xml\n<domain type=\"kvm\">\n  <name>RDPWindows</name>\n  <uuid>4d76e36e-c632-43e0-83c0-dc9f36c2823a</uuid>\n  <metadata>\n    <libosinfo:libosinfo xmlns:libosinfo=\"http://libosinfo.org/xmlns/libvirt/domain/1.0\">\n      <libosinfo:os id=\"http://microsoft.com/win/11\"/>\n    </libosinfo:libosinfo>\n  </metadata>\n  <memory unit=\"KiB\">8388608</memory>\n  <currentMemory unit=\"KiB\">8388608</currentMemory>\n  <vcpu placement=\"static\">4</vcpu>\n  <cputune>\n    <vcpupin vcpu=\"0\" cpuset=\"2\"/>\n    <vcpupin vcpu=\"1\" cpuset=\"6\"/>\n    <vcpupin vcpu=\"2\" cpuset=\"3\"/>\n    <vcpupin vcpu=\"3\" cpuset=\"7\"/>\n  </cputune>\n  <os firmware=\"efi\">\n    <type arch=\"x86_64\" machine=\"pc-q35-8.1\">hvm</type>\n    <firmware>\n      <feature enabled=\"yes\" name=\"enrolled-keys\"/>\n      <feature enabled=\"yes\" name=\"secure-boot\"/>\n    </firmware>\n    <loader readonly=\"yes\" secure=\"yes\" type=\"pflash\" format=\"qcow2\">/usr/share/edk2/ovmf/OVMF_CODE_4M.secboot.qcow2</loader>\n    <nvram template=\"/usr/share/edk2/ovmf/OVMF_VARS_4M.secboot.qcow2\" format=\"qcow2\">/var/lib/libvirt/qemu/nvram/RDPWindows_VARS.qcow2</nvram>\n    <boot dev=\"hd\"/>\n  </os>\n  <features>\n    <acpi/>\n    <apic/>\n    <hyperv mode=\"custom\">\n      <relaxed state=\"on\"/>\n      <vapic state=\"on\"/>\n      <spinlocks state=\"on\" retries=\"8191\"/>\n      <vpindex state=\"on\"/>\n      <synic state=\"on\"/>\n      <stimer state=\"on\">\n        <direct state=\"on\"/>\n      </stimer>\n      <reset state=\"on\"/>\n      <frequencies state=\"on\"/>\n      <reenlightenment state=\"on\"/>\n      <tlbflush state=\"on\"/>\n      <ipi state=\"on\"/>\n    </hyperv>\n    <vmport state=\"off\"/>\n    <smm state=\"on\"/>\n  </features>\n  <cpu mode=\"host-passthrough\" check=\"none\" migratable=\"on\">\n    <topology sockets=\"1\" dies=\"1\" clusters=\"1\" cores=\"2\" threads=\"2\"/>\n  </cpu>\n  <clock offset=\"localtime\">\n    <timer name=\"rtc\" present=\"no\" tickpolicy=\"catchup\"/>\n    <timer name=\"pit\" present=\"no\" tickpolicy=\"delay\"/>\n    <timer name=\"hpet\" present=\"no\"/>\n    <timer name=\"kvmclock\" present=\"no\"/>\n    <timer name=\"hypervclock\" present=\"yes\"/>\n  </clock>\n  <on_poweroff>destroy</on_poweroff>\n  <on_reboot>restart</on_reboot>\n  <on_crash>destroy</on_crash>\n  <pm>\n    <suspend-to-mem enabled=\"no\"/>\n    <suspend-to-disk enabled=\"no\"/>\n  </pm>\n  <devices>\n    <emulator>/usr/bin/qemu-system-x86_64</emulator>\n    <disk type=\"file\" device=\"disk\">\n      <driver name=\"qemu\" type=\"qcow2\" discard=\"unmap\"/>\n      <source file=\"/var/lib/libvirt/images/RDPWindows.qcow2\"/>\n      <target dev=\"vda\" bus=\"virtio\"/>\n      <address type=\"pci\" domain=\"0x0000\" bus=\"0x04\" slot=\"0x00\" function=\"0x0\"/>\n    </disk>\n    <disk type=\"file\" device=\"cdrom\">\n      <driver name=\"qemu\" type=\"raw\"/>\n      <target dev=\"sdb\" bus=\"sata\"/>\n      <readonly/>\n      <address type=\"drive\" controller=\"0\" bus=\"0\" target=\"0\" unit=\"1\"/>\n    </disk>\n    <controller type=\"usb\" index=\"0\" model=\"qemu-xhci\" ports=\"15\">\n      <address type=\"pci\" domain=\"0x0000\" bus=\"0x02\" slot=\"0x00\" function=\"0x0\"/>\n    </controller>\n    <controller type=\"pci\" index=\"0\" model=\"pcie-root\"/>\n    <controller type=\"pci\" index=\"1\" model=\"pcie-root-port\">\n      <model name=\"pcie-root-port\"/>\n      <target chassis=\"1\" port=\"0x10\"/>\n      <address type=\"pci\" domain=\"0x0000\" bus=\"0x00\" slot=\"0x02\" function=\"0x0\" multifunction=\"on\"/>\n    </controller>\n    <controller type=\"pci\" index=\"2\" model=\"pcie-root-port\">\n      <model name=\"pcie-root-port\"/>\n      <target chassis=\"2\" port=\"0x11\"/>\n      <address type=\"pci\" domain=\"0x0000\" bus=\"0x00\" slot=\"0x02\" function=\"0x1\"/>\n    </controller>\n    <controller type=\"pci\" index=\"3\" model=\"pcie-root-port\">\n      <model name=\"pcie-root-port\"/>\n      <target chassis=\"3\" port=\"0x12\"/>\n      <address type=\"pci\" domain=\"0x0000\" bus=\"0x00\" slot=\"0x02\" function=\"0x2\"/>\n    </controller>\n    <controller type=\"pci\" index=\"4\" model=\"pcie-root-port\">\n      <model name=\"pcie-root-port\"/>\n      <target chassis=\"4\" port=\"0x13\"/>\n      <address type=\"pci\" domain=\"0x0000\" bus=\"0x00\" slot=\"0x02\" function=\"0x3\"/>\n    </controller>\n    <controller type=\"pci\" index=\"5\" model=\"pcie-root-port\">\n      <model name=\"pcie-root-port\"/>\n      <target chassis=\"5\" port=\"0x14\"/>\n      <address type=\"pci\" domain=\"0x0000\" bus=\"0x00\" slot=\"0x02\" function=\"0x4\"/>\n    </controller>\n    <controller type=\"pci\" index=\"6\" model=\"pcie-root-port\">\n      <model name=\"pcie-root-port\"/>\n      <target chassis=\"6\" port=\"0x15\"/>\n      <address type=\"pci\" domain=\"0x0000\" bus=\"0x00\" slot=\"0x02\" function=\"0x5\"/>\n    </controller>\n    <controller type=\"pci\" index=\"7\" model=\"pcie-root-port\">\n      <model name=\"pcie-root-port\"/>\n      <target chassis=\"7\" port=\"0x16\"/>\n      <address type=\"pci\" domain=\"0x0000\" bus=\"0x00\" slot=\"0x02\" function=\"0x6\"/>\n    </controller>\n    <controller type=\"pci\" index=\"8\" model=\"pcie-root-port\">\n      <model name=\"pcie-root-port\"/>\n      <target chassis=\"8\" port=\"0x17\"/>\n      <address type=\"pci\" domain=\"0x0000\" bus=\"0x00\" slot=\"0x02\" function=\"0x7\"/>\n    </controller>\n    <controller type=\"pci\" index=\"9\" model=\"pcie-root-port\">\n      <model name=\"pcie-root-port\"/>\n      <target chassis=\"9\" port=\"0x18\"/>\n      <address type=\"pci\" domain=\"0x0000\" bus=\"0x00\" slot=\"0x03\" function=\"0x0\" multifunction=\"on\"/>\n    </controller>\n    <controller type=\"pci\" index=\"10\" model=\"pcie-root-port\">\n      <model name=\"pcie-root-port\"/>\n      <target chassis=\"10\" port=\"0x19\"/>\n      <address type=\"pci\" domain=\"0x0000\" bus=\"0x00\" slot=\"0x03\" function=\"0x1\"/>\n    </controller>\n    <controller type=\"pci\" index=\"11\" model=\"pcie-root-port\">\n      <model name=\"pcie-root-port\"/>\n      <target chassis=\"11\" port=\"0x1a\"/>\n      <address type=\"pci\" domain=\"0x0000\" bus=\"0x00\" slot=\"0x03\" function=\"0x2\"/>\n    </controller>\n    <controller type=\"pci\" index=\"12\" model=\"pcie-root-port\">\n      <model name=\"pcie-root-port\"/>\n      <target chassis=\"12\" port=\"0x1b\"/>\n      <address type=\"pci\" domain=\"0x0000\" bus=\"0x00\" slot=\"0x03\" function=\"0x3\"/>\n    </controller>\n    <controller type=\"pci\" index=\"13\" model=\"pcie-root-port\">\n      <model name=\"pcie-root-port\"/>\n      <target chassis=\"13\" port=\"0x1c\"/>\n      <address type=\"pci\" domain=\"0x0000\" bus=\"0x00\" slot=\"0x03\" function=\"0x4\"/>\n    </controller>\n    <controller type=\"pci\" index=\"14\" model=\"pcie-root-port\">\n      <model name=\"pcie-root-port\"/>\n      <target chassis=\"14\" port=\"0x1d\"/>\n      <address type=\"pci\" domain=\"0x0000\" bus=\"0x00\" slot=\"0x03\" function=\"0x5\"/>\n    </controller>\n    <controller type=\"sata\" index=\"0\">\n      <address type=\"pci\" domain=\"0x0000\" bus=\"0x00\" slot=\"0x1f\" function=\"0x2\"/>\n    </controller>\n    <controller type=\"virtio-serial\" index=\"0\">\n      <address type=\"pci\" domain=\"0x0000\" bus=\"0x03\" slot=\"0x00\" function=\"0x0\"/>\n    </controller>\n    <interface type=\"network\">\n      <mac address=\"52:54:00:81:ff:44\"/>\n      <source network=\"default\"/>\n      <model type=\"virtio\"/>\n      <address type=\"pci\" domain=\"0x0000\" bus=\"0x01\" slot=\"0x00\" function=\"0x0\"/>\n    </interface>\n    <serial type=\"pty\">\n      <target type=\"isa-serial\" port=\"0\">\n        <model name=\"isa-serial\"/>\n      </target>\n    </serial>\n    <console type=\"pty\">\n      <target type=\"serial\" port=\"0\"/>\n    </console>\n    <channel type=\"spicevmc\">\n      <target type=\"virtio\" name=\"com.redhat.spice.0\"/>\n      <address type=\"virtio-serial\" controller=\"0\" bus=\"0\" port=\"1\"/>\n    </channel>\n    <channel type='unix'>\n      <source mode='bind'/>\n      <target type='virtio' name='org.qemu.guest_agent.0'/>\n      <address type='virtio-serial' controller='0' bus='0' port='2'/>\n    </channel>\n    <input type=\"tablet\" bus=\"usb\">\n      <address type=\"usb\" bus=\"0\" port=\"1\"/>\n    </input>\n    <input type=\"mouse\" bus=\"ps2\"/>\n    <input type=\"keyboard\" bus=\"ps2\"/>\n    <tpm model=\"tpm-crb\">\n      <backend type=\"emulator\" version=\"2.0\"/>\n    </tpm>\n    <graphics type=\"spice\" autoport=\"yes\">\n      <listen type=\"address\"/>\n      <image compression=\"off\"/>\n    </graphics>\n    <sound model=\"ich9\">\n      <address type=\"pci\" domain=\"0x0000\" bus=\"0x00\" slot=\"0x1b\" function=\"0x0\"/>\n    </sound>\n    <audio id=\"1\" type=\"spice\"/>\n    <video>\n      <model type=\"qxl\" ram=\"65536\" vram=\"65536\" vgamem=\"16384\" heads=\"1\" primary=\"yes\"/>\n      <address type=\"pci\" domain=\"0x0000\" bus=\"0x00\" slot=\"0x01\" function=\"0x0\"/>\n    </video>\n    <hostdev mode=\"subsystem\" type=\"usb\" managed=\"yes\">\n      <source>\n        <vendor id=\"0x0bda\"/>\n        <product id=\"0x554e\"/>\n      </source>\n      <address type=\"usb\" bus=\"0\" port=\"4\"/>\n    </hostdev>\n    <redirdev bus=\"usb\" type=\"spicevmc\">\n      <address type=\"usb\" bus=\"0\" port=\"2\"/>\n    </redirdev>\n    <watchdog model=\"itco\" action=\"reset\"/>\n    <memballoon model=\"virtio\">\n      <address type=\"pci\" domain=\"0x0000\" bus=\"0x05\" slot=\"0x00\" function=\"0x0\"/>\n    </memballoon>\n  </devices>\n</domain>\n```\n\n</details>\n\n## Install Windows\nClick `Begin Installation` in the top left.\n\n<p align=\"center\">\n    <img src=\"./libvirt_images/15.png\" width=\"700px\"/>\n</p>\n\n<p align=\"center\">\n    <img src=\"./libvirt_images/16.png\" width=\"700px\"/>\n</p>\n\nOnce you get to the point of selecting the location for installation, you will see there are no disks available. This is because the `VirtIO driver` needs to be specified manually.\n\n1. Select `Load driver`.\n\n<p align=\"center\">\n    <img src=\"./libvirt_images/17.png\" width=\"700px\"/>\n</p>\n\n2. The installer will then ask you to specify where the driver is located. Select the drive the `VirtIO` driver `.ISO` is mounted on.\n\n<p align=\"center\">\n    <img src=\"./libvirt_images/18.png\" width=\"700px\"/>\n</p>\n\n3. Choose the appropriate driver for the operating system you've selected, which is likely either the `w10` or `w11` drivers.\n\n<p align=\"center\">\n    <img src=\"./libvirt_images/19.png\" width=\"700px\"/>\n</p>\n\n4. The virtual hard disk should now be visible and available for selection.\n\n<p align=\"center\">\n    <img src=\"./libvirt_images/20.png\" width=\"700px\"/>\n</p>\n\nThe next hurdle will be bypassing the network selection screen. As the `VirtIO` drivers for networking have not yet been loaded, the virtual machine will not be able to be connected to the internet.\n- For Windows 11: When prompted to select your country or region, press \"Shift + F10\" to open the command prompt. Enter `OOBE\\BYPASSNRO` and press Enter. The system will restart, allowing you to select \"I don't have internet\" later on. It is crucial to run this command as soon as possible, as doing so later in the installation process will not work, and you may be required to create a Microsoft account despite not having an internet connection.\n\n<p align=\"center\">\n    <img src=\"./libvirt_images/21.png\" width=\"700px\"/>\n</p>\n\n- For Windows 10: Simply click \"I don't have internet\".\n\n<p align=\"center\">\n    <img src=\"./libvirt_images/22.png\" width=\"700px\"/>\n</p>\n\nFollowing the above, choose to \"Continue with limited setup\".\n\n<p align=\"center\">\n    <img src=\"./libvirt_images/23.png\" width=\"700px\"/>\n</p>\n\n## Final Configuration Steps\nOpen `File Explorer` and navigate to the drive where the \"virtio-win\" `.iso` is mounted. Run `virtio-win-guest-tools.exe` to install all necessary drivers as well as `QEMU Guest Agent`. Leave everything as default and click `Next` through the installer.\n\n<p align=\"center\">\n    <img src=\"./libvirt_images/24.png\" width=\"700px\"/>\n</p>\n\n<p align=\"center\">\n    <img src=\"./libvirt_images/25.png\" width=\"700px\"/>\n</p>\n\nConfirm `QEMU Guest Agent` was successfully installed by running `Get-Service QEMU-GA` within a PowerShell window. The output should resemble:\n\n```\nStatus   Name               DisplayName\n------   ----               -----------\nRunning  QEMU-GA            QEMU Guest Agent\n```\n\nYou can then test whether the host GNU/Linux system can communicate with Windows via `QEMU Guest Agent` by running `virsh qemu-agent-command RDPWindows '{\"execute\":\"guest-get-osinfo\"}' --pretty`. The output should resemble:\n\n```json\n{\n  \"return\": {\n    \"name\": \"Microsoft Windows\",\n    \"kernel-release\": \"26100\",\n    \"version\": \"Microsoft Windows 11\",\n    \"variant\": \"client\",\n    \"pretty-name\": \"Windows 10 Pro\",\n    \"version-id\": \"11\",\n    \"variant-id\": \"client\",\n    \"kernel-version\": \"10.0\",\n    \"machine\": \"x86_64\",\n    \"id\": \"mswindows\"\n  }\n}\n```\n\nNext, you will need to make some registry changes to enable RDP Applications to run on the system. Start by downloading the [RDPApps.reg](../oem/RDPApps.reg) file, right-clicking on the `Raw` button, and clicking on `Save target as`. Repeat the same thing for the [install.bat](../oem/install.bat), the [TimeSync.ps1](../oem/TimeSync.ps1) and the [NetProfileCleanup.ps1](../oem/NetProfileCleanup.ps1). **Do not download 'Container.reg'** - this file is only required for users using docker or podman.\n\n<p align=\"center\">\n    <img src=\"./libvirt_images/26.png\" width=\"700px\"/>\n</p>\n\nOnce you have downloaded all three files, right-click the install.bat and select \"Run as administrator\".\n\n<p align=\"center\">\n    <img src=\"./libvirt_images/27.png\" width=\"700px\"/>\n</p>\n\nOnce this is complete, restart the Windows virtual machine.\n\n<details>\n<summary><strong>(Optional) Configuring a Fallback Shared Folder</strong></summary>\n\nWhen connecting to Windows through FreeRDP, your home folder will be shared automatically. However, this sharing setup does not apply when using Windows via virt-manager. To configure a fallback shared folder, follow these steps:\n\n1. Navigate to \"Virtual Hardware Details\", then \"Memory\" and then check the box for \"Enable shared memory\".\n\n2. Add filesystem hardware by going to \"Virtual Hardware Details\" and selecting \"Add Hardware\" followed by \"Filesystem\". Choose `virtiofs` as the driver, enter the path to the shared folder, and provide a name for the shared folder in the target path (e.g., \"Windows Shared Folder\").\n\n3. Install [`WinFSP`](https://github.com/winfsp/winfsp/releases/) on Windows.\n\n4. Enable and start a 'VirtIO Filesystem' service within Windows by running the following commands within a PowerShell prompt.\n    ```PowerShell\n    sc.exe create VirtioFsSvc binpath= \"C:\\Program Files\\Virtio-Win\\VioFS\\virtiofs.exe\" start=auto depend=\"WinFsp.Launcher/VirtioFsDrv\" DisplayName=\"Virtio Filesystem Service\"\n    sc.exe start VirtioFsSvc\n    ```\n\n5. Reboot Windows.\n\n</details>\n\n<details>\n<summary><strong>(Optional) Configuring a Static IP Address</strong></summary>\n\n1. Identify the Windows MAC address.\n    ```bash\n    virsh dumpxml \"RDPWindows\" | grep \"mac address\"\n    ```\n\n2. Edit the virtual network configuration.\n    1. Identify the correct network name.\n        ```bash\n        virsh net-list # Will likely return \"default\"\n        ```\n\n    2. Edit the configuration file.\n        ```bash\n        virsh net-edit \"default\" # Replace \"default\" with the appropriate network name if different\n        ```\n\n    3. Update the `<dhcp>` section in the configuration file using the MAC address you obtained earlier. In the below example, \"RDPWindows\" has MAC address \"df:87:4c:75:e5:fb\" and is assigned the static IP address \"192.168.122.2\".\n        ```xml\n        <dhcp>\n          <range start=\"192.168.122.2\" end=\"192.168.122.254\"/>\n          <host mac=\"df:87:4c:75:e5:fb\" name=\"RDPWindows\" ip=\"192.168.122.2\"/>\n          <host mac=\"53:45:6b:de:a0:7b\" name=\"Debian\" ip=\"192.168.122.3\"/>\n          <host mac=\"7d:62:4f:59:ef:f5\" name=\"FreeBSD\" ip=\"192.168.122.4\"/>\n        </dhcp>\n        ```\n\n    4. Restart the virtual network.\n        ```bash\n        virsh net-destroy \"default\" # Replace with the correct name on your system\n        virsh net-start \"default\" # Replace with the correct name on your system\n        ```\n\n    5. Reboot Windows.\n\n</details>\n\n<details>\n<summary><strong>(Optional) Installing Spice Guest Tools</strong></summary>\n\nYou may also wish to install [Spice Guest Tools](https://www.spice-space.org/download/windows/spice-guest-tools/spice-guest-tools-latest.exe) inside the virtual machine, which enables features like auto-desktop resize and cut-and-paste when accessing the virtual machine through `virt-manager`. Since WinApps uses RDP, however, this is unnecessary if you don't plan to access the virtual machine via `virt-manager`.\n\n</details>\n\n## Installing Windows Software and Configuring WinApps\nYou may now proceed to install other applications like 'Microsoft 365', 'Adobe Creative Cloud' or any other applications you would like to use through WinApps.\n\n> [!IMPORTANT]\n> Ensure `WAFLAVOR` is set to `\"libvirt\"` in your `~/.config/winapps/winapps.conf` to prevent WinApps looking for a `Docker` installation instead.\n\nFinally, restart the virtual machine, but **DO NOT** log in. Close the virtual machine viewer and proceed to run the WinApps installation.\n\n```bash\nbash <(curl https://raw.githubusercontent.com/winapps-org/winapps/main/setup.sh)\n```\n"
  },
  {
    "path": "flake.nix",
    "content": "{\n  description = \"WinApps Nix packages\";\n\n  inputs = {\n    nixpkgs.url = \"github:NixOS/nixpkgs/nixos-unstable\";\n\n    flake-compat.url = \"https://flakehub.com/f/edolstra/flake-compat/1.tar.gz\";\n    flake-utils.url = \"github:numtide/flake-utils\";\n    nix-filter.url = \"github:numtide/nix-filter\";\n  };\n\n  nixConfig = {\n    extra-substituters = [\n      \"https://cache.garnix.io\"\n    ];\n\n    extra-trusted-public-keys = [\n      \"cache.garnix.io:CTFPyKSLcx5RMJKfLo5EEPUObbA78b0YQ2DTCJXqr9g=\"\n    ];\n  };\n\n  outputs =\n    {\n      nixpkgs,\n      flake-utils,\n      nix-filter,\n      ...\n    }:\n    flake-utils.lib.eachDefaultSystem (\n      system:\n      let\n        pkgs = import nixpkgs { inherit system; };\n      in\n      rec {\n        formatter = pkgs.nixfmt-rfc-style;\n\n        packages.winapps = pkgs.callPackage ./packages/winapps { inherit nix-filter; };\n        packages.winapps-launcher = pkgs.callPackage ./packages/winapps-launcher {\n          inherit (packages) winapps;\n        };\n      }\n    );\n}\n"
  },
  {
    "path": "install/ExtractPrograms.ps1",
    "content": "### FUNCTIONS ###\n# Name: 'GetApplicationIcon'\n# Role: Extract the icon from a given executable file as a base-64 string.\n# Args:\n#    - 'exePath': Provides the path to the executable file.\nFunction GetApplicationIcon {\n    param (\n        [Parameter(Mandatory = $true)]\n        [string]$exePath\n    )\n\n    try {\n        # Load the 'System.Drawing' assembly to access 'ExtractAssociatedIcon'.\n        Add-Type -AssemblyName System.Drawing\n\n        # Extract the icon from the executable.\n        $exeIcon = [System.Drawing.Icon]::ExtractAssociatedIcon($exePath)\n\n        # Create a bitmap from the icon.\n        $exeIconBitmap = New-Object System.Drawing.Bitmap $exeIcon.Width, $exeIcon.Height\n        $graphics = [System.Drawing.Graphics]::FromImage($exeIconBitmap)\n        $graphics.DrawIcon($exeIcon, 0, 0)\n\n        # Save the bitmap to a 'MemoryStream' as a '.PNG' to preserve the icon colour depth.\n        $memoryStream = New-Object System.IO.MemoryStream\n        $exeIconBitmap.Save($memoryStream, [System.Drawing.Imaging.ImageFormat]::Png)\n\n        # Convert the PNG 'MemoryStream' to a base-64 string.\n        $bytes = $memoryStream.ToArray()\n        $base64String = [Convert]::ToBase64String($bytes)\n\n        # Clean up.\n        $memoryStream.Flush()\n        $memoryStream.Dispose()\n        $graphics.Dispose()\n        $exeIconBitmap.Dispose()\n        $exeIcon.Dispose()\n    } catch {\n        # Use a generic 32x32 PNG.\n        $base64String = \"iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAAAXNSR0IB2cksfwAAAAlwSFlzAAALEwAACxMBAJqcGAAAASZQTFRFAAAA+vr65ubm4uLkhYmLvL7A7u7w+/r729vb4eHjFYPbFoTa5eXnGIbcG4jc+fn7Gofc7+/x7OzuF4Xb+fn54uLiC37Z5OTmEIHaIIjcEYHbDoDZFIPcJ43fHYjd9fX28PDy3d3fI4rd3d3dHojc19fXttTsJIve2dnZDX/YCn3Y09PTjL/p5+fnh7zo2traJYzfIYjdE4Pb6urrW6Tf9PT1Ioneir7otNPsCX3Zhbvn+Pj5YKfhJYfWMo7a39/gKIzeKo7eMI3ZNJDcXqbg4eHhuNTsB3zYIoncBXvZLIrXIYjbLJDgt7m6ubu+YqjiKYvYvr6+tba3rs/sz8/P1+byJonXv7/DiImLxsbGjo6Ra6reurq6io6QkJKVw8PD0tLSycnJq1DGywAAAGJ0Uk5TAP////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////+BVJDaAAABY0lEQVR4nM2RaVOCUBSGr1CBgFZimppgoGnKopZSaYGmRpravq///0904IqOM9j00WeGT+9ztgtCS8Dzyh98fL6i2+HqQoaj0RPSzQNgzZc4F4wgvUuoqkr1er094MjlIeBCwRdFua9CqURQ51cty7Lykj0YCIIibnlEkS4TgCuky3nbTmSFsCKSHuso96N/Ox1aacjrlYQQ3gjNCYV7UlUJ6szCeRZyXmlkNjEZEPSuLIMAuYTreVYROQ8Y8SLTNAhlCdfzLMsaIhfHgEAT7pLtvFTH9QxTNWrmLsaEDu8558y2ZOP5LLNTNUQyiCFnHaRZnjTmzryhnR36FSdnIU9up7RGxAOuKJjOFX2vHvKU5jPiepbvxzR3BIffwROc++AAJy9qjQxQwz9rIjyGeN6tj8VACEyZCqfQn3H7F48vTvwEdlIP+aWvMNkPcl8h8DYeN5vNTqdzCNz5CIv4h7AE/AKcwUFbShJywQAAAABJRU5ErkJggg==\"\n    }\n\n    # Return the base-64 string.\n    return $base64String\n}\n\n# Name: 'PrintArrayData'\n# Role: Print application names, executable paths and base-64 encoded icons in a format suitable for importing into bash arrays.\n# Args:\n#    - 'Names': An array of application names.\n#    - 'Paths': An array of executable paths.\n#    - 'Source': The source of the applications (e.g. Windows Registry, Package managers, Universal Windows Platform (UWP), etc.)\nfunction PrintArrayData {\n    param (\n        [string[]]$Names,\n        [string[]]$Paths,\n        [string]$Source\n    )\n\n    # Combine the arrays into an array of objects\n    $NamesandPaths = @()\n    for ($i = 0; $i -lt $Names.Length; $i++) {\n        $NamesandPaths += [PSCustomObject]@{\n            Name = $Names[$i]\n            Path = $Paths[$i]\n        }\n    }\n\n    # Sort the combined array based on the application names.\n    $NamesandPaths = $NamesandPaths | Sort-Object {$_.Name}\n\n    # Loop through the extracted executable file paths.\n    foreach ($Application in $NamesandPaths) {\n\n        # Remove undesirable suffix for chocolatey shims.\n        if ($Source -eq \"choco\") {\n            if ($Application.Name.EndsWith(\" - Chocolatey Shim\")) {\n                $Application.Name = $Application.Name.Substring(0, $Application.Name.Length - \" - Chocolatey Shim\".Length)\n            }\n        }\n\n        # Add the appropriate tag to the application name.\n        if ($Source -ne \"winreg\") {\n            $Application.Name = $Application.Name + \" [\" + $Source.ToUpper() + \"]\"\n        }\n\n        # Store the application icon as a base-64 string.\n        $Icon = GetApplicationIcon -exePath $Application.Path\n\n        # Output the results as bash commands that append the results to several bash arrays.\n        Write-Output ('NAMES+=(\"' + $Application.Name + '\")')\n        Write-Output ('EXES+=(\"' + $Application.Path + '\")')\n        Write-Output ('ICONS+=(\"' + $Icon + '\")')\n    }\n}\n\n# Name: 'GetApplicationName'\n# Role: Determine the application name for a given executable file.\n# Args:\n#    - 'exePath': The path to a given executable file.\nfunction GetApplicationName {\n    param (\n        [string]$exePath\n    )\n\n    try {\n        $productName = (Get-Item $exePath).VersionInfo.FileDescription.Trim() -replace '\\s+', ' '\n    } catch {\n        $productName = [System.IO.Path]::GetFileNameWithoutExtension($exePath)\n    }\n\n    return $productName\n}\n\n# Name: 'GetUWPApplicationName'\n# Role: Determine the application name for a given UWP application.\n# Args:\n#    - 'exePath': The path to a given executable file.\nfunction GetUWPApplicationName {\n    param (\n        [string]$exePath\n    )\n\n    # Query the application executable for the application name.\n    if (Test-Path $exePath) {\n        $productName = GetApplicationName -exePath $exePath\n    }\n\n    # Use the 'DisplayName' (if available) if the previous method failed.\n    if (-not $productName -and $app.DisplayName) {\n        $productName = $app.DisplayName\n    }\n\n    # Use the 'Name' (if available) as a final fallback.\n    if (-not $productName -and $app.Name) {\n        $productName = $app.Name\n    }\n\n    return $productName\n}\n\n# Name: 'GetUWPExecutablePath'\n# Role: Obtain the UWP application executable path from 'AppxManifest.xml'.\n# Args:\n#    - 'instLoc': UWP application folder path (C:\\Program Files\\WindowsApps\\*).\nfunction GetUWPExecutablePath {\n    param (\n        [string]$instLoc\n    )\n\n    # Determine the path to 'AppxManifest.xml' for the selected application.\n    $manifestPath = Join-Path -Path $instLoc -ChildPath \"AppxManifest.xml\"\n\n    if (Test-Path $manifestPath) {\n        # Parse the XML file.\n        [xml]$manifest = Get-Content $manifestPath\n        $applications = $manifest.Package.Applications.Application\n\n        # Return the path to the first executable specified within the XML.\n        foreach ($application in $applications) {\n            $executable = $application.Executable\n            if ($executable) {\n                return Join-Path -Path $instLoc -ChildPath $executable\n            }\n        }\n    }\n\n    # Return 'null' if nothing was found.\n    return $null\n}\n\n# Name: 'AppSearchWinReg'\n# Role: Search the Windows Registry for installed applications.\nfunction AppSearchWinReg {\n    # Initialise empty arrays.\n    $exeNames = @()\n    $exePaths = @()\n    $validPaths = @()\n\n    # Query windows registry for unique installed executable files.\n    $exePaths = Get-ItemProperty \"HKLM:\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\*\" |\n        ForEach-Object { $_.\"(default)\" } |  # Extract the value of the (default) property\n        Where-Object { $_ -ne $null } |  # Filter out null values\n        Sort-Object -Unique  # Ensure uniqueness\n\n    # Remove leading and trailing double quotes from all paths.\n    $exePaths = $exePaths -replace '^\"*|\"*$'\n\n    # Get corresponding application names for unique installed executable files.\n    foreach ($exePath in $exePaths) {\n        if (Test-Path -Path $exePath) {\n            $validPaths += $exePath\n            $exeNames += GetApplicationName -exePath $exePath\n        }\n    }\n\n    # Process extracted executable file paths.\n    PrintArrayData -Names $exeNames -Paths $validPaths -Source \"winreg\"\n}\n\n# Name: 'AppSearchUWP'\n# Role: Search for 'non-system' UWP applications.\nfunction AppSearchUWP {\n    # Initialise empty arrays.\n    $exeNames = @()\n    $exePaths = @()\n\n    # Obtain all 'non-system' UWP applications using 'Get-AppxPackage'.\n    $uwpApps = Get-AppxPackage | Where-Object {\n        $_.IsFramework -eq $false -and\n        $_.IsResourcePackage -eq $false -and\n        $_.SignatureKind -ne 'System'\n    }\n\n    # Create an array to store UWP application details.\n    $uwpAppDetails = @()\n\n    # Loop through each UWP application.\n    foreach ($app in $uwpApps) {\n        # Initialise the variable responsible for storing the UWP application name.\n        $productName = $null\n\n        # Obtain the path to the UWP application executable.\n        $exePath = GetUWPExecutablePath -instLoc $app.InstallLocation\n\n        # Proceed only if an executable path was identified.\n        if ($exePath) {\n            $productName = GetUWPApplicationName -exePath $exePath\n\n            # Ignore UWP applications with no name, or those named 'Microsoft® Windows® Operating System'.\n            if ($productName -ne \"Microsoft® Windows® Operating System\" -and [string]::IsNullOrEmpty($productName) -eq $false) {\n                # Store the UWP application name and executable path.\n                $exeNames += $productName\n                $exePaths += $exePath\n            }\n        }\n    }\n\n    # Process extracted executable file paths.\n    PrintArrayData -Names $exeNames -Paths $exePaths -Source \"uwp\"\n}\n\n# Name: 'AppSearchChocolatey'\n# Role: Search for chocolatey shims.\nfunction AppSearchChocolatey {\n    # Initialise empty arrays.\n    $exeNames = @()\n    $exePaths = @()\n\n    # Specify the 'chocolatey' shims directory.\n    $chocoDir = \"C:\\ProgramData\\chocolatey\\bin\"\n\n    # Check if the 'chocolatey' shims directory exists.\n    if (Test-Path -Path $chocoDir -PathType Container) {\n        # Get all shim '.exe' files.\n        $shimExeFiles = Get-ChildItem -Path $chocoDir -Filter *.exe\n\n        # Loop through each '.shim' file to extract the executable path.\n        foreach ($shimExeFile in $shimExeFiles) {\n            # Resolve the shim to the actual executable path.\n            $exePath = (Get-Command $shimExeFile).Source\n\n            # Proceed only if an executable path was identified.\n            if ($exePath) {\n                $exeNames += GetApplicationName -exePath $exePath\n                $exePaths += $exePath\n            }\n        }\n\n        # Process extracted executable file paths.\n        PrintArrayData -Names $exeNames -Paths $exePaths -Source \"choco\"\n    }\n}\n\n# Name: 'AppSearchScoop'\n# Role: Search for scoop shims.\nfunction AppSearchScoop {\n    # Initialise empty arrays.\n    $exeNames = @()\n    $exePaths = @()\n\n    # Specify the 'scoop' shims directory.\n    $scoopDir = \"$HOME\\scoop\\shims\"\n\n    # Check if the 'scoop' shims directory exists.\n    if (Test-Path -Path $scoopDir -PathType Container) {\n        # Get all '.shim' files.\n        $shimFiles = Get-ChildItem -Path $scoopDir -Filter *.shim\n\n        # Loop through each '.shim' file to extract the executable path.\n        foreach ($shimFile in $shimFiles) {\n            # Read the content of the '.shim' file.\n            $shimFileContent = Get-Content -Path $shimFile.FullName\n\n            # Extract the path using regex, exiting the loop after the first match is found.\n            $exePath = \"\"\n\n            foreach ($line in $shimFileContent) {\n                # '^\\s*path\\s*=\\s*\"([^\"]+)\"'\n                # ^       --> Asserts the start of the line.\n                # \\s*     --> Matches any whitespace characters (zero or more times).\n                # path    --> Matches the literal string \"path\".\n                # \\s*=\\s* --> Matches an equal sign = surrounded by optional whitespace characters.\n                # \"       --> Matches an initial double quote.\n                # ([^\"]+) --> Captures one or more characters that are not \", representing the path inside the double quotes.\n                # \"       --> Matches a final double quote.\n                if ($line -match '^\\s*path\\s*=\\s*\"([^\"]+)\"') {\n                    $exePath = $matches[1]\n                    break\n                }\n            }\n\n            if ($exePath -ne \"\") {\n                $exeNames += GetApplicationName -exePath $exePath\n                $exePaths += $exePath\n            }\n        }\n\n        # Process extracted executable file paths.\n        PrintArrayData -Names $exeNames -Paths $exePaths -Source \"scoop\"\n    }\n}\n\n### SEQUENTIAL LOGIC ###\n# Print bash commands to define three new arrays.\nWrite-Output 'NAMES=()'\nWrite-Output 'EXES=()'\nWrite-Output 'ICONS=()'\n\n# Search for installed applications.\nAppSearchWinReg     # Windows Registry\nif (Get-Command Get-AppxPackage -ErrorAction SilentlyContinue){\n    AppSearchUWP        # Universal Windows Platform\n}\nAppSearchChocolatey # Chocolatey Package Manager\nAppSearchScoop      # Scoop Package Manager\n"
  },
  {
    "path": "install/inquirer.sh",
    "content": "#!/usr/bin/env bash\n# Copyright (c) 2024 kahkhang\n# All rights reserved.\n#\n# SPDX-License-Identifier: MIT\n# For original source, see https://github.com/kahkhang/Inquirer.sh\n\n### GLOBAL CONSTANTS ###\ndeclare -r ANSI_LIGHT_BLUE=\"\\033[1;94m\" # Light blue text.\ndeclare -r ANSI_LIGHT_GREEN=\"\\033[92m\"  # Light green text.\ndeclare -r ANSI_CLEAR_TEXT=\"\\033[0m\"    # Default text.\ndeclare -r DIALOG_HEIGHT=14             # Height of dialog window.\ndeclare -r TEXT_WIDTH_OFFSET=4          # Offset for fitting title text.\ndeclare -r CHK_OPTION_WIDTH_OFFSET=10   # Offset for fitting options.\ndeclare -r MNU_OPTION_WIDTH_OFFSET=7    # Offset for fitting options.\n\n### FUNCTIONS ###\nfunction inqMenu() {\n    # DECLARE VARIABLES.\n    # Variables created from function arguments:\n    declare DIALOG_TEXT=\"$1\"                      # Dialog heading.\n    declare INPUT_OPTIONS_VAR=\"$2\"                # Input variable name.\n    declare RETURN_STRING_VAR=\"$3\"                # Output variable name.\n    declare -n INPUT_OPTIONS=\"$INPUT_OPTIONS_VAR\" # Input array nameref.\n    declare -n RETURN_STRING=\"$RETURN_STRING_VAR\" # Output string nameref.\n    # Note: namerefs allow changes made through the nameref to affect the\n    # referenced variable, even across different scopes like function calls.\n\n    # Other variables:\n    declare TRIMMED_OPTIONS=()         # Input array post-trimming.\n    declare PADDED_OPTIONS=()          # Input array with extra white space.\n    declare DIALOG_OPTIONS=()          # Input array for options dialog.\n    declare DIALOG_WIDTH=0             # Width of dialog window.\n    declare OPTION_NUMBER=0            # Number of options in dialog window.\n    declare SELECTED_OPTIONS_STRING=\"\" # Output value from dialog window.\n\n    # MAIN LOGIC.\n    # Trim leading and trailing white space for each option.\n    for OPTION in \"${INPUT_OPTIONS[@]}\"; do\n        TRIMMED_OPTIONS+=(\"$(echo \"$OPTION\" | sed 's/^[ \\t]*//;s/[ \\t]*$//')\")\n    done\n\n    # Find the length of the longest option to set the dialog width.\n    for OPTION in \"${TRIMMED_OPTIONS[@]}\"; do\n        if [ \"${#OPTION}\" -gt \"$DIALOG_WIDTH\" ]; then\n            DIALOG_WIDTH=${#OPTION}\n        fi\n    done\n\n    # Apply the offset value to the dialog width.\n    DIALOG_WIDTH=$((DIALOG_WIDTH + MNU_OPTION_WIDTH_OFFSET))\n\n    # Adjust the dialog width again if the dialog text is longer.\n    if [ \"$DIALOG_WIDTH\" -lt $((${#DIALOG_TEXT} + TEXT_WIDTH_OFFSET)) ]; then\n        DIALOG_WIDTH=\"$((${#DIALOG_TEXT} + TEXT_WIDTH_OFFSET))\"\n    fi\n\n    # Pad option text with trailing white space to left-align all options.\n    for OPTION in \"${TRIMMED_OPTIONS[@]}\"; do\n        local PAD_LENGTH=$((DIALOG_WIDTH - MNU_OPTION_WIDTH_OFFSET - ${#OPTION}))\n        # shellcheck disable=SC2155\n        local PADDED_OPTION=\"${OPTION}$(printf '%*s' $PAD_LENGTH)\"\n        PADDED_OPTIONS+=(\"$PADDED_OPTION\")\n    done\n\n    # Convert options into the appropriate format for a 'dialog' menu.\n    for PADDED_OPTION in \"${PADDED_OPTIONS[@]}\"; do\n        DIALOG_OPTIONS+=(\"$PADDED_OPTION\" \"\")\n    done\n\n    # Store the number of options.\n    OPTION_NUMBER=\"${#INPUT_OPTIONS[@]}\"\n\n    # Produce checkbox.\n    # The output string contains options delimited by spaces.\n    # Each option is enclosed in double quotes within the output string.\n    # For example: '\"Option 1  \" \"The  Second Option   \" \"    Option Number 3 \"'\n    SELECTED_OPTIONS_STRING=$(dialog \\\n        --keep-tite \\\n        --clear \\\n        --no-shadow \\\n        --menu \\\n        \"$DIALOG_TEXT\" \\\n        \"$DIALOG_HEIGHT\" \\\n        \"$DIALOG_WIDTH\" \\\n        \"$OPTION_NUMBER\" \\\n        \"${DIALOG_OPTIONS[@]}\" \\\n        2>&1 >/dev/tty) || exit 0\n\n    # Remove white space added previously.\n    RETURN_STRING=$(echo \"$SELECTED_OPTIONS_STRING\" |\n        sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')\n\n    # Remove escapes (introduced by 'dialog' if options have parentheses).\n    RETURN_STRING=\"${RETURN_STRING//\\\\/}\" # ${variable//search/replace}\n\n    # Display question and response.\n    echo -e \"${ANSI_LIGHT_GREEN}Q) ${ANSI_CLEAR_TEXT}${ANSI_LIGHT_BLUE}${DIALOG_TEXT}${ANSI_CLEAR_TEXT} --> ${ANSI_LIGHT_GREEN}${RETURN_STRING}${ANSI_CLEAR_TEXT}\"\n}\n\nfunction inqChkBx() {\n    # DECLARE VARIABLES.\n    # Variables created from function arguments:\n    declare DIALOG_TEXT=\"$1\"                      # Dialog heading.\n    declare INPUT_OPTIONS_VAR=\"$2\"                # Input variable name.\n    declare RETURN_ARRAY_VAR=\"$3\"                 # Output variable name.\n    declare -n INPUT_OPTIONS=\"$INPUT_OPTIONS_VAR\" # Input array nameref.\n    declare -n RETURN_ARRAY=\"$RETURN_ARRAY_VAR\"   # Output array nameref.\n    # Note: namerefs allow changes made through the nameref to affect the\n    # referenced variable, even across different scopes like function calls.\n\n    # Other variables:\n    declare TRIMMED_OPTIONS=()         # Input array post-trimming.\n    declare PADDED_OPTIONS=()          # Input array with extra white space.\n    declare DIALOG_OPTIONS=()          # Input array for options dialog.\n    declare DIALOG_WIDTH=0             # Width of dialog window.\n    declare OPTION_NUMBER=0            # Number of options in dialog window.\n    declare SELECTED_OPTIONS_STRING=\"\" # Output value from dialog window.\n\n    # MAIN LOGIC.\n    # Trim leading and trailing white space for each option.\n    for OPTION in \"${INPUT_OPTIONS[@]}\"; do\n        TRIMMED_OPTIONS+=(\"$(echo \"$OPTION\" | sed 's/^[ \\t]*//;s/[ \\t]*$//')\")\n    done\n\n    # Find the length of the longest option to set the dialog width.\n    for OPTION in \"${TRIMMED_OPTIONS[@]}\"; do\n        if [ \"${#OPTION}\" -gt \"$DIALOG_WIDTH\" ]; then\n            DIALOG_WIDTH=${#OPTION}\n        fi\n    done\n\n    # Apply the offset value to the dialog width.\n    DIALOG_WIDTH=$((DIALOG_WIDTH + CHK_OPTION_WIDTH_OFFSET))\n\n    # Adjust the dialog width again if the dialog text is longer.\n    if [ \"$DIALOG_WIDTH\" -lt $((${#DIALOG_TEXT} + TEXT_WIDTH_OFFSET)) ]; then\n        DIALOG_WIDTH=\"$((${#DIALOG_TEXT} + TEXT_WIDTH_OFFSET))\"\n    fi\n\n    # Pad option text with trailing white space to left-align all options.\n    for OPTION in \"${TRIMMED_OPTIONS[@]}\"; do\n        local PAD_LENGTH=$((DIALOG_WIDTH - CHK_OPTION_WIDTH_OFFSET - ${#OPTION}))\n        # shellcheck disable=SC2155\n        local PADDED_OPTION=\"${OPTION}$(printf '%*s' $PAD_LENGTH)\"\n        PADDED_OPTIONS+=(\"$PADDED_OPTION\")\n    done\n\n    # Convert options into the appropriate format for a 'dialog' checkbox.\n    for PADDED_OPTION in \"${PADDED_OPTIONS[@]}\"; do\n        DIALOG_OPTIONS+=(\"$PADDED_OPTION\" \"\" off)\n    done\n\n    # Store the number of options.\n    OPTION_NUMBER=\"${#INPUT_OPTIONS[@]}\"\n\n    # Produce checkbox.\n    # The output string contains options delimited by spaces.\n    # Each option is enclosed in double quotes within the output string.\n    # For example: '\"Option 1  \" \"The  Second Option   \" \"    Option Number 3 \"'\n    SELECTED_OPTIONS_STRING=$(dialog \\\n        --keep-tite \\\n        --clear \\\n        --no-shadow \\\n        --checklist \\\n        \"$DIALOG_TEXT\" \\\n        \"$DIALOG_HEIGHT\" \\\n        \"$DIALOG_WIDTH\" \\\n        \"$OPTION_NUMBER\" \\\n        \"${DIALOG_OPTIONS[@]}\" \\\n        2>&1 >/dev/tty) || exit 0\n\n    # Convert the output string into an array.\n    # shellcheck disable=SC2001\n    while IFS= read -r LINE; do\n        LINE=\"${LINE/#\\\"/}\"     # Remove leading double quote.\n        LINE=\"${LINE/%\\\"/}\"     # Remove trailing double quote.\n        RETURN_ARRAY+=(\"$LINE\") # Add to array.\n    done < <(echo \"$SELECTED_OPTIONS_STRING\" | sed 's/\\\" \\\"/\\\"\\n\\\"/g')\n\n    # Final modifications.\n    for ((i = 0; i < ${#RETURN_ARRAY[@]}; i++)); do\n        # Remove white space added previously.\n        # shellcheck disable=SC2001\n        RETURN_ARRAY[i]=$(echo \"${RETURN_ARRAY[i]}\" |\n            sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')\n\n        # Remove escapes (introduced by 'dialog' if options have parentheses).\n        RETURN_ARRAY[i]=${RETURN_ARRAY[i]//\\\\/} # ${variable//search/replace}\n    done\n}\n"
  },
  {
    "path": "oem/NetProfileCleanup.ps1",
    "content": "# Get the current network profile name\n$currentProfile = (Get-NetConnectionProfile).Name\n\n# Get all profiles from the registry\n$profilesKey = \"HKLM:\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\NetworkList\\Profiles\"\n$profiles = Get-ChildItem -Path $profilesKey\n\nforeach ($profile in $profiles) {\n    $profilePath = \"$profilesKey\\$($profile.PSChildName)\"\n    $profileName = (Get-ItemProperty -Path $profilePath).ProfileName\n\n    # Remove profiles that don't match the current one\n    if ($profileName -ne $currentProfile) {\n        Remove-Item -Path $profilePath -Recurse\n        Write-Host \"Deleted profile: $profileName\"\n    }\n}\n\n# Change the current profile name to \"WinApps\"\n$profiles = Get-ChildItem -Path $profilesKey\nforeach ($profile in $profiles) {\n    $profilePath = \"$profilesKey\\$($profile.PSChildName)\"\n    $profileName = (Get-ItemProperty -Path $profilePath).ProfileName\n\n    if ($profileName -eq $currentProfile) {\n        # Update the profile name\n        Set-ItemProperty -Path $profilePath -Name \"ProfileName\" -Value \"WinApps\"\n        Write-Host \"Renamed profile to: WinApps\"\n    }\n}\n"
  },
  {
    "path": "oem/TimeSync.ps1",
    "content": "# Script to monitor if there is a sleep_marker created by WinApps (indicating the Linux host was suspended) in order to trigger a time sync as the time in the Windows VM will otherwise drift while Linux is suspended.\n\n# Define the path to monitor. Make sure this matches the location for the sleep_marker in the Winapps script (need to match the APPDATA path).\n$filePath = \"\\\\tsclient\\home\\.local\\share\\winapps\\sleep_marker\"\n$networkPath = \"\\\\tsclient\\home\"\n\n# Function to check and handle file\nfunction Monitor-File {\n    while ($true) {\n        # Check if network location is available\n        try {\n            $null = Test-Path -Path $networkPath -ErrorAction Stop\n            # Check if file exists\n            if (Test-Path -Path $filePath) {\n                # Run time resync silently\n                w32tm /resync /quiet\n\n                # Remove the file\n                Remove-Item -Path $filePath -Force\n            }\n        }\n        catch {\n            # Network location not available, continue monitoring silently\n        }\n\n        # Wait 5 minutes before next check\n        Start-Sleep -Seconds 3000\n    }\n}\n\n# Start monitoring silently\nMonitor-File\n"
  },
  {
    "path": "oem/install.bat",
    "content": "@echo off\r\ntitle WinApps Setup Wizard\r\n\r\n:: Check for administrative privileges\r\nfltmc >nul 2>&1 || (\r\n    echo [INFO] Script not running as administrator. Attempting to relaunch with elevation...\r\n    powershell -Command \"Start-Process '%~f0' -Verb runAs\"\r\n    exit /b\r\n)\r\n\r\necho ============================================\r\necho             WinApps Setup Wizard\r\necho ============================================\r\necho.\r\necho [INFO] Starting setup...\r\n\r\n:: Apply RDP and system configuration tweaks\r\necho [INFO] Importing \"RDPApps.reg\"...\r\nif exist \"%~dp0RDPApps.reg\" (\r\n    reg import \"%~dp0RDPApps.reg\" >nul 2>&1\r\n    if %ERRORLEVEL% equ 0 (\r\n        echo [SUCCESS] Imported \"RDPApps.reg\".\r\n    ) else (\r\n        echo [ERROR] Failed to import \"RDPApps.reg\".\r\n    )\r\n) else (\r\n    echo [ERROR] \"RDPApps.reg\" not found. Skipping...\r\n)\r\n\r\n:: Allow Remote Desktop connections through the firewall\r\necho [INFO] Allowing Remote Desktop connections through the firewall...\r\npowershell -NoProfile -NonInteractive -ExecutionPolicy Bypass ^\r\n  -Command \"if (Get-Command Enable-NetFirewallRule -ErrorAction SilentlyContinue) { try { Enable-NetFirewallRule -DisplayGroup 'Remote Desktop' -ErrorAction Stop; exit 0 } catch { exit 1 } } else { exit 2 }\" >nul 2>&1\r\nif %ERRORLEVEL% equ 0 (\r\n    echo [SUCCESS] Firewall changes applied successfully.\r\n) else (\r\n    :: Fallback to using 'netsh' to make the firewall modification\r\n    netsh advfirewall firewall set rule group=\"remote desktop\" new enable=Yes >nul 2>&1\r\n    if %ERRORLEVEL% equ 0 (\r\n        echo [SUCCESS] Firewall changes applied successfully.\r\n    ) else (\r\n        echo [ERROR] Failed to apply firewall changes.\r\n        echo         Please manually enable Remote Desktop via 'Settings --> System --> Remote Desktop'.\r\n    )\r\n)\r\n\r\n:: Configure the system clock to use UTC instead of local time\r\nif exist \"%~dp0Container.reg\" (\r\n    echo [INFO] Importing \"Container.reg\"...\r\n    reg import \"%~dp0Container.reg\" >nul 2>&1\r\n    if %ERRORLEVEL% equ 0 (\r\n        echo [SUCCESS] Imported \"Container.reg\".\r\n    ) else (\r\n        echo [ERROR] Failed to import \"Container.reg\".\r\n    )\r\n) else (\r\n    echo [WARNING] \"Container.reg\" not found. Skipping...\r\n)\r\n\r\n:: Create a startup task to clean up stale network profiles\r\necho [INFO] Creating network profile cleanup task...\r\n\r\n:: Initialise values required to create the startup task\r\nset \"scriptpath=%windir%\\NetProfileCleanup.ps1\"\r\nset \"taskname=WinApps_NetworkProfileCleanup\"\r\nset \"command=powershell.exe -ExecutionPolicy Bypass -File \"\"%scriptpath%\"\"\"\r\n\r\n:: Copy the script to the Windows directory\r\ncopy /Y \"%~dp0NetProfileCleanup.ps1\" \"%scriptpath%\" >nul\r\nif %ERRORLEVEL% neq 0 (\r\n    echo [ERROR] Failed to copy \"NetProfileCleanup.ps1\" to \"%windir%\".\r\n) else (\r\n    schtasks /create /tn \"%taskname%\" /tr \"%command%\" /sc onstart /ru \"SYSTEM\" /rl HIGHEST /f >nul 2>&1\r\n    if %ERRORLEVEL% equ 0 (\r\n        echo [SUCCESS] Created scheduled task \"%taskname%\".\r\n    ) else (\r\n        echo [ERROR] Failed to create scheduled task \"%taskname%\".\r\n    )\r\n)\r\n\r\nREM Create time sync task to be run by the user at login\r\ncopy %~dp0\\TimeSync.ps1 %windir%\r\nset \"taskname2=TimeSync\"\r\nset \"command2=powershell.exe -WindowStyle Hidden -ExecutionPolicy Bypass -File \\\"%windir%\\TimeSync.ps1\\\"\"\r\n\r\nschtasks /query /tn \"%taskname2%\" >nul\r\nif %ERRORLEVEL% equ 0 (\r\n    echo %DATE% %TIME% Task \"%taskname2%\" already exists, skipping creation.\r\n) else (\r\n    schtasks /create /tn \"%taskname2%\" /tr \"%command2%\" /sc onlogon /rl HIGHEST /f\r\n    if %ERRORLEVEL% equ 0 (\r\n        echo %DATE% %TIME% Scheduled task \"%taskname2%\" created successfully.\r\n    ) else (\r\n        echo %DATE% %TIME% Failed to create scheduled task %taskname2%.\r\n    )\r\n)\r\n"
  },
  {
    "path": "packages/winapps/default.nix",
    "content": "{\n  stdenv,\n  lib,\n  makeWrapper,\n  freerdp,\n  dialog,\n  libnotify,\n  netcat,\n  iproute2,\n  nix-filter ? throw \"Pass github:numtide/nix-filter as an argument!\",\n  ...\n}:\nstdenv.mkDerivation rec {\n  pname = \"winapps\";\n  version = \"0-unstable-2025-07-02\";\n\n  src = nix-filter {\n    root = ./../..;\n    include = [\n      \"apps\"\n      \"install\"\n      \"bin\"\n      \"icons\"\n      \"LICENSE.md\"\n      \"COPYRIGHT.md\"\n      \"setup.sh\"\n    ];\n  };\n\n  nativeBuildInputs = [ makeWrapper ];\n  buildInputs = [\n    freerdp\n    libnotify\n    dialog\n    netcat\n    iproute2\n  ];\n\n  patches = [\n    ./setup.patch\n  ];\n\n  postPatch = ''\n    substituteAllInPlace bin/winapps\n    substituteAllInPlace setup.sh\n    patchShebangs install/inquirer.sh\n  '';\n\n  installPhase = ''\n    runHook preInstall\n\n    mkdir -p $out\n    mkdir -p $out/src\n\n    cp -r ./ $out/src/\n\n    install -m755 -D bin/winapps $out/bin/winapps\n    install -m755 -D setup.sh $out/bin/winapps-setup\n\n    for f in winapps-setup winapps; do\n      wrapProgram $out/bin/$f \\\n        --set LIBVIRT_DEFAULT_URI \"qemu:///system\" \\\n        --prefix PATH : \"${lib.makeBinPath buildInputs}\"\n    done\n\n    runHook postInstall\n  '';\n\n  meta = with lib; {\n    homepage = \"https://github.com/winapps-org/winapps\";\n    description = \"Run Windows applications (including Microsoft 365 and Adobe Creative Cloud) on GNU/Linux with KDE, GNOME or XFCE, integrated seamlessly as if they were native to the OS. Wayland is currently unsupported.\";\n    mainProgram = \"winapps\";\n    platforms = platforms.linux;\n    license = licenses.agpl3Plus;\n  };\n}\n"
  },
  {
    "path": "packages/winapps/setup.patch",
    "content": "diff --git a/setup.sh b/setup.sh\nindex 3a871c8..71a8fa0 100755\n--- a/setup.sh\n+++ b/setup.sh\n@@ -39,8 +39,8 @@ readonly SYS_BIN_PATH=\"/usr/local/bin\"                  # UNIX path to 'bin' dir\n readonly USER_BIN_PATH=\"${HOME}/.local/bin\"             # UNIX path to 'bin' directory for a '--user' WinApps installation.\n readonly USER_BIN_PATH_WIN='\\\\tsclient\\home\\.local\\bin' # WINDOWS path to 'bin' directory for a '--user' WinApps installation.\n # 'SOURCE'\n-readonly SYS_SOURCE_PATH=\"${SYS_BIN_PATH}/winapps-src\" # UNIX path to WinApps source directory for a '--system' WinApps installation.\n-readonly USER_SOURCE_PATH=\"${USER_BIN_PATH}/winapps-src\" # UNIX path to WinApps source directory for a '--user' WinApps installation.\n+readonly SYS_SOURCE_PATH=\"@out@/src\" # UNIX path to WinApps source directory for a '--system' WinApps installation.\n+readonly USER_SOURCE_PATH=\"@out@/src\" # UNIX path to WinApps source directory for a '--user' WinApps installation.\n # 'APP'\n readonly SYS_APP_PATH=\"/usr/share/applications\"                        # UNIX path to 'applications' directory for a '--system' WinApps installation.\n readonly USER_APP_PATH=\"${HOME}/.local/share/applications\"             # UNIX path to 'applications' directory for a '--user' WinApps installation.\n@@ -70,7 +70,7 @@ readonly TEST_PATH_WIN=\"${USER_APPDATA_PATH_WIN}\\\\FreeRDP_Connection_Test\" # WIN\n # 'WinApps Configuration File'\n readonly CONFIG_PATH=\"${HOME}/.config/winapps/winapps.conf\" # UNIX path to the WinApps configuration file.\n # 'Inquirer Bash Script'\n-readonly INQUIRER_PATH=\"./install/inquirer.sh\" # UNIX path to the 'inquirer' script, which is used to produce selection menus.\n+readonly INQUIRER_PATH=\"@out@/src/install/inquirer.sh\" # UNIX path to the 'inquirer' script, which is used to produce selection menus.\n \n # REMOTE DESKTOP CONFIGURATION\n readonly RDP_PORT=3389         # Port used for RDP on Windows.\n@@ -157,13 +157,6 @@ function waGetSourceCode() {\n         echo -e \"${WARNING_TEXT}[WARNING]${CLEAR_TEXT} You might want to remove your old installation on '${SCRIPT_DIR_PATH}'.\"\n     fi\n \n-    if [[ ! -d \"$SOURCE_PATH\" ]]; then\n-        $SUDO git clone --recurse-submodules --remote-submodules https://github.com/winapps-org/winapps.git \"$SOURCE_PATH\"\n-    else\n-        echo -e \"${INFO_TEXT}WinApps installation already present at ${CLEAR_TEXT}${COMMAND_TEXT}${SOURCE_PATH}${CLEAR_TEXT}${INFO_TEXT}. Updating...${CLEAR_TEXT}\"\n-        $SUDO git -C \"$SOURCE_PATH\" pull --no-rebase\n-    fi\n-\n     # Silently change the working directory.\n     if ! cd \"$SOURCE_PATH\" &>/dev/null; then\n         # Display the error type.\n@@ -188,21 +181,8 @@ function waGetSourceCode() {\n # Name: 'waGetInquirer'\n # Role: Loads the inquirer script, even if the source isn't cloned yet\n function waGetInquirer() {\n-    local INQUIRER=$INQUIRER_PATH\n-\n-    if [ -d \"$SYS_SOURCE_PATH\" ]; then\n-        INQUIRER=$SYS_SOURCE_PATH/$INQUIRER_PATH\n-    elif [ -d \"$USER_SOURCE_PATH\" ] ; then\n-        INQUIRER=$USER_SOURCE_PATH/$INQUIRER_PATH\n-    else\n-        INQUIRER=\"/tmp/waInquirer.sh\"\n-        rm -f \"$INQUIRER\"\n-\n-        curl -o \"$INQUIRER\" \"https://raw.githubusercontent.com/winapps-org/winapps/main/install/inquirer.sh\"\n-    fi\n-\n     # shellcheck source=/dev/null # Exclude this file from being checked by ShellCheck.\n-    source \"$INQUIRER\"\n+    source \"$INQUIRER_PATH\"\n }\n \n # Name: 'waCheckInput'\n@@ -807,7 +787,7 @@ function waCheckGroupMembership() {\n     # Identify groups the current user belongs to.\n     USER_GROUPS=$(groups \"$(whoami)\")\n \n-    if ! (echo \"$USER_GROUPS\" | grep -q -E \"\\blibvirt\\b\") || ! (echo \"$USER_GROUPS\" | grep -q -E \"\\bkvm\\b\"); then\n+    if ! (echo \"$USER_GROUPS\" | grep -q -E \"\\blibvirtd\\b\") || ! (echo \"$USER_GROUPS\" | grep -q -E \"\\bkvm\\b\"); then\n         # Complete the previous line.\n         echo -e \"${FAIL_TEXT}Failed!${CLEAR_TEXT}\\n\"\n \n@@ -1239,11 +1219,11 @@ function waConfigureWindows() {\n     # Populate variables.\n     WIN_BASH=\"\\\n #!/usr/bin/env bash\n-${BIN_PATH}/winapps windows\"\n+@out@/bin/winapps windows\"\n     WIN_DESKTOP=\"\\\n [Desktop Entry]\n Name=Windows\n-Exec=${BIN_PATH}/winapps windows %F\n+Exec=@out@/bin/winapps windows %F\n Terminal=false\n Type=Application\n Icon=${APPDATA_PATH}/icons/windows.svg\n@@ -1290,13 +1270,13 @@ function waConfigureApp() {\n     # Determine the content of the bash script for the application.\n     APP_BASH=\"\\\n #!/usr/bin/env bash\n-${BIN_PATH}/winapps ${1}\"\n+@out@/bin/winapps ${1}\"\n \n     # Determine the content of the '.desktop' file for the application.\n     APP_DESKTOP_FILE=\"\\\n [Desktop Entry]\n Name=${NAME}\n-Exec=${BIN_PATH}/winapps ${1} %F\n+Exec=@out@/bin/winapps ${1} %F\n Terminal=false\n Type=Application\n Icon=${APP_ICON}\n@@ -1364,7 +1344,9 @@ function waConfigureOfficiallySupported() {\n             fi\n \n             # Copy the protocol handler to the appropriate directory.\n+            # Fix permissions for nix.\n             $SUDO cp \"./apps/ms-office-protocol-handler.desktop\" \"$TARGET_DIR/ms-office-protocol-handler.desktop\"\n+            $SUDO chmod u+w \"$TARGET_DIR/ms-office-protocol-handler.desktop\"\n         fi\n \n         # Print feedback.\n@@ -1596,7 +1578,7 @@ function waInstall() {\n     echo -e \"${BOLD_TEXT}Installing WinApps.${CLEAR_TEXT}\"\n \n     # Check for existing conflicting WinApps installations.\n-    waCheckExistingInstall\n+    # waCheckExistingInstall\n \n     # Load the WinApps configuration file.\n     waLoadConfig\n@@ -1664,9 +1646,8 @@ function waInstall() {\n     # Check for installed applications.\n     waFindInstalled\n \n-    # Install the WinApps bash scripts.\n-    $SUDO ln -sf \"${SOURCE_PATH}/bin/winapps\" \"${BIN_PATH}/winapps\"\n-    $SUDO ln -sf \"${SOURCE_PATH}/setup.sh\" \"${BIN_PATH}/winapps-setup\"\n+    # Fix permissions for nix.\n+    $SUDO chmod u+w -R \"${APPDATA_PATH}\"\n \n     # Configure the Windows RDP session application launcher.\n     waConfigureWindows\n@@ -1727,18 +1708,15 @@ function waUninstall() {\n     local DESKTOP_FILE_NAME=\"\"        # Stores the name of the '.desktop' file for the application.\n     local BASH_SCRIPT_NAME=\"\"         # Stores the name of the application.\n \n-    # Remove the 'WinApps' bash scripts.\n-    $SUDO rm -f \"${BIN_PATH}/winapps\"\n-    $SUDO rm -f \"${BIN_PATH}/winapps-setup\"\n-\n     # Remove WinApps configuration data, temporary files and logs.\n+    chmod -R +rw \"$USER_APPDATA_PATH\"\n     rm -rf \"$USER_APPDATA_PATH\"\n \n     # Remove application icons and shortcuts.\n     $SUDO rm -rf \"$APPDATA_PATH\"\n \n     # Store '.desktop' files containing \"${BIN_PATH}/winapps\" in an array, returning an empty array if no such files exist.\n-    readarray -t WINAPPS_DESKTOP_FILES < <(grep -l -d skip \"${BIN_PATH}/winapps\" \"${APP_PATH}/\"* 2>/dev/null || true)\n+    readarray -t WINAPPS_DESKTOP_FILES < <(grep -l -d skip \"@out@/bin/winapps\" \"${APP_PATH}/\"* 2>/dev/null || true)\n \n     # Remove each '.desktop' file.\n     for DESKTOP_FILE_PATH in \"${WINAPPS_DESKTOP_FILES[@]}\"; do\n@@ -1759,7 +1737,7 @@ function waUninstall() {\n     done\n \n     # Store the paths of bash scripts calling 'WinApps' to launch specific applications in an array, returning an empty array if no such files exist.\n-    readarray -t WINAPPS_APP_BASH_SCRIPTS < <(grep -l -d skip \"${BIN_PATH}/winapps\" \"${BIN_PATH}/\"* 2>/dev/null || true)\n+    readarray -t WINAPPS_APP_BASH_SCRIPTS < <(grep -l -d skip \"@out@/bin/winapps\" \"${BIN_PATH}/\"* 2>/dev/null || true)\n \n     # Remove each bash script.\n     for BASH_SCRIPT_PATH in \"${WINAPPS_APP_BASH_SCRIPTS[@]}\"; do\n@@ -1780,10 +1758,9 @@ function waUninstall() {\n     done\n \n     # Print caveats.\n-    echo -e \"\\n${INFO_TEXT}Please note that your WinApps configuration and the WinApps source code were not removed.${CLEAR_TEXT}\"\n-    echo -e \"${INFO_TEXT}You can remove these manually by running:${CLEAR_TEXT}\"\n+    echo -e \"\\n${INFO_TEXT}Please note that your WinApps configuration and the WinApps package were not removed.${CLEAR_TEXT}\"\n+    echo -e \"${INFO_TEXT}You can remove your config manually by running:${CLEAR_TEXT}\"\n     echo -e \"${COMMAND_TEXT}rm -r $(dirname \"$CONFIG_PATH\")${CLEAR_TEXT}\"\n-    echo -e \"${COMMAND_TEXT}rm -r ${SOURCE_PATH}${CLEAR_TEXT}\\n\"\n \n     # Print feedback.\n     echo -e \"${SUCCESS_TEXT}UNINSTALLATION COMPLETE.${CLEAR_TEXT}\"\n"
  },
  {
    "path": "packages/winapps-launcher/WinApps-Launcher.patch",
    "content": "diff --git a/WinApps-Launcher.sh b/WinApps-Launcher.sh\nindex 1d3a929..a5d7d4c 100755\n--- a/WinApps-Launcher.sh\n+++ b/WinApps-Launcher.sh\n@@ -19,7 +19,7 @@ declare -rx EC_WIN_NOT_SPEC=6\n declare -rx EC_NO_WIN_FOUND=7\n \n # Paths\n-declare -rx ICONS_PATH=\"./Icons\"\n+declare -rx ICONS_PATH=\"@out@/Icons\"\n declare -rx APPDATA_PATH=\"${XDG_DATA_HOME:-$HOME/.local/share}/winapps\"\n declare -rx CONFIG_PATH=\"${XDG_CONFIG_HOME:-$HOME/.config}/winapps\"\n declare -rx CONFIG_FILE=\"${CONFIG_PATH}/winapps.conf\"\n"
  },
  {
    "path": "packages/winapps-launcher/default.nix",
    "content": "{\n  stdenv,\n  lib,\n  fetchFromGitHub,\n  makeWrapper,\n  makeDesktopItem,\n  yad,\n  winapps ? throw \"Pass in the winapps package\",\n  ...\n}:\nlet\n  rev = \"87f92a80c7e421ab7d1b8801e647dcbfaaa6ee34\";\n  hash = \"sha256-aZ8uusg5yQOD1xYfaX2IQCbcPdHuVA0tiy1NDkdGCCs=\";\nin\nstdenv.mkDerivation rec {\n  pname = \"winapps-launcher\";\n  version = \"0-unstable-2025-09-01\";\n\n  src = fetchFromGitHub {\n    owner = \"winapps-org\";\n    repo = \"WinApps-Launcher\";\n\n    inherit rev hash;\n  };\n\n  nativeBuildInputs = [ makeWrapper ];\n  buildInputs = [\n    yad\n    winapps\n  ];\n\n  patches = [ ./WinApps-Launcher.patch ];\n\n  postPatch = ''\n    substituteAllInPlace WinApps-Launcher.sh\n  '';\n\n  installPhase = ''\n    runHook preInstall\n\n    mkdir -p $out\n    cp -r ./Icons $out/Icons\n\n    install -m755 -D WinApps-Launcher.sh $out/bin/winapps-launcher\n    install -Dm444 -T Icons/AppIcon.svg $out/share/pixmaps/winapps.svg\n\n    wrapProgram $out/bin/winapps-launcher \\\n      --set LIBVIRT_DEFAULT_URI \"qemu:///system\" \\\n      --prefix PATH : \"${lib.makeBinPath buildInputs}\"\n\n    runHook postInstall\n  '';\n\n  desktopItems = [\n    (makeDesktopItem {\n      name = \"winapps\";\n      exec = \"winapps-launcher\";\n      icon = \"winapps\";\n      comment = meta.description;\n      desktopName = \"WinApps\";\n      categories = [ \"Utility\" ];\n    })\n  ];\n\n  meta = with lib; {\n    homepage = \"https://github.com/winapps-org/WinApps-Launcher\";\n    description = \"Graphical launcher for WinApps. Run Windows applications (including Microsoft 365 and Adobe Creative Cloud) on GNU/Linux with KDE, GNOME or XFCE, integrated seamlessly as if they were native to the OS. Wayland is currently unsupported.\";\n    mainProgram = \"winapps-launcher\";\n    platforms = platforms.linux;\n    license = licenses.gpl3;\n  };\n}\n"
  },
  {
    "path": "renovate.json",
    "content": "{\n  \"$schema\": \"https://docs.renovatebot.com/renovate-schema.json\",\n  \"extends\": [\n    \"config:best-practices\",\n    \"group:all\",\n    \"schedule:monthly\"\n  ]\n}\n"
  },
  {
    "path": "setup.sh",
    "content": "#!/usr/bin/env bash\n\n# shellcheck disable=SC2034           # Silence warnings regarding unused variables globally.\n\n### GLOBAL CONSTANTS ###\n# ANSI ESCAPE SEQUENCES\nreadonly BOLD_TEXT=\"\\033[1m\"          # Bold\nreadonly CLEAR_TEXT=\"\\033[0m\"         # Clear\nreadonly COMMAND_TEXT=\"\\033[0;37m\"    # Grey\nreadonly DONE_TEXT=\"\\033[0;32m\"       # Green\nreadonly ERROR_TEXT=\"\\033[1;31m\"      # Bold + Red\nreadonly EXIT_TEXT=\"\\033[1;41;37m\"    # Bold + White + Red Background\nreadonly FAIL_TEXT=\"\\033[0;91m\"       # Bright Red\nreadonly INFO_TEXT=\"\\033[0;33m\"       # Orange/Yellow\nreadonly SUCCESS_TEXT=\"\\033[1;42;37m\" # Bold + White + Green Background\nreadonly WARNING_TEXT=\"\\033[1;33m\"    # Bold + Orange/Yellow\n\n# ERROR CODES\nreadonly EC_FAILED_CD=\"1\"        # Failed to change directory to location of script.\nreadonly EC_BAD_ARGUMENT=\"2\"     # Unsupported argument passed to script.\nreadonly EC_EXISTING_INSTALL=\"3\" # Existing conflicting WinApps installation.\nreadonly EC_NO_CONFIG=\"4\"        # Absence of a valid WinApps configuration file.\nreadonly EC_MISSING_DEPS=\"5\"     # Missing dependencies.\nreadonly EC_NO_SUDO=\"6\"          # Insufficient privileges to invoke superuser access.\nreadonly EC_NOT_IN_GROUP=\"7\"     # Current user not in group 'libvirt' and/or 'kvm'.\nreadonly EC_VM_OFF=\"8\"           # Windows 'libvirt' VM powered off.\nreadonly EC_VM_PAUSED=\"9\"        # Windows 'libvirt' VM paused.\nreadonly EC_VM_ABSENT=\"10\"       # Windows 'libvirt' VM does not exist.\nreadonly EC_CONTAINER_OFF=\"11\"   # Windows Docker container is not running.\nreadonly EC_NO_IP=\"12\"           # Windows does not have an IP address.\nreadonly EC_BAD_PORT=\"13\"        # Windows is unreachable via RDP_PORT.\nreadonly EC_RDP_FAIL=\"14\"        # FreeRDP failed to establish a connection with Windows.\nreadonly EC_APPQUERY_FAIL=\"15\"   # Failed to query Windows for installed applications.\nreadonly EC_INVALID_FLAVOR=\"16\"  # Backend specified is not 'libvirt', 'docker' or 'podman'.\n\n# PATHS\n# 'BIN'\nreadonly SYS_BIN_PATH=\"/usr/local/bin\"                  # UNIX path to 'bin' directory for a '--system' WinApps installation.\nreadonly USER_BIN_PATH=\"${HOME}/.local/bin\"             # UNIX path to 'bin' directory for a '--user' WinApps installation.\nreadonly USER_BIN_PATH_WIN='\\\\tsclient\\home\\.local\\bin' # WINDOWS path to 'bin' directory for a '--user' WinApps installation.\n# 'SOURCE'\nreadonly SYS_SOURCE_PATH=\"${SYS_BIN_PATH}/winapps-src\" # UNIX path to WinApps source directory for a '--system' WinApps installation.\nreadonly USER_SOURCE_PATH=\"${USER_BIN_PATH}/winapps-src\" # UNIX path to WinApps source directory for a '--user' WinApps installation.\n# 'APP'\nreadonly SYS_APP_PATH=\"/usr/share/applications\"                        # UNIX path to 'applications' directory for a '--system' WinApps installation.\nreadonly USER_APP_PATH=\"${HOME}/.local/share/applications\"             # UNIX path to 'applications' directory for a '--user' WinApps installation.\nreadonly USER_APP_PATH_WIN='\\\\tsclient\\home\\.local\\share\\applications' # WINDOWS path to 'applications' directory for a '--user' WinApps installation.\n# 'APPDATA'\nreadonly SYS_APPDATA_PATH=\"/usr/local/share/winapps\"                  # UNIX path to 'application data' directory for a '--system' WinApps installation.\nreadonly USER_APPDATA_PATH=\"${HOME}/.local/share/winapps\"             # UNIX path to 'application data' directory for a '--user' WinApps installation.\nreadonly USER_APPDATA_PATH_WIN='\\\\tsclient\\home\\.local\\share\\winapps' # WINDOWS path to 'application data' directory for a '--user' WinApps installation.\n# 'Installed Batch Script'\nreadonly BATCH_SCRIPT_PATH=\"${USER_APPDATA_PATH}/installed.bat\"          # UNIX path to a batch script used to search Windows for applications.\nreadonly BATCH_SCRIPT_PATH_WIN=\"${USER_APPDATA_PATH_WIN}\\\\installed.bat\" # WINDOWS path to a batch script used to search Windows for applications.\n# 'Installed File'\nreadonly TMP_INST_FILE_PATH=\"${USER_APPDATA_PATH}/installed.tmp\"          # UNIX path to a temporary file containing the names of detected officially supported applications.\nreadonly TMP_INST_FILE_PATH_WIN=\"${USER_APPDATA_PATH_WIN}\\\\installed.tmp\" # WINDOWS path to a temporary file containing the names of detected officially supported applications.\nreadonly INST_FILE_PATH=\"${USER_APPDATA_PATH}/installed\"                  # UNIX path to a file containing the names of detected officially supported applications.\nreadonly INST_FILE_PATH_WIN=\"${USER_APPDATA_PATH_WIN}\\\\installed\"         # WINDOWS path to a file containing the names of detected officially supported applications.\n# 'PowerShell Script'\nreadonly PS_SCRIPT_PATH=\"./install/ExtractPrograms.ps1\"                          # UNIX path to a PowerShell script used to store the names, executable paths and icons (base64) of detected applications.\nreadonly PS_SCRIPT_HOME_PATH=\"${USER_APPDATA_PATH}/ExtractPrograms.ps1\"          # UNIX path to a copy of the PowerShell script within the user's home directory to enable access by Windows.\nreadonly PS_SCRIPT_HOME_PATH_WIN=\"${USER_APPDATA_PATH_WIN}\\\\ExtractPrograms.ps1\" # WINDOWS path to a copy of the PowerShell script within the user's home directory to enable access by Windows.\n# 'Detected File'\nreadonly DETECTED_FILE_PATH=\"${USER_APPDATA_PATH}/detected\"          # UNIX path to a file containing the output generated by the PowerShell script, formatted to define bash arrays.\nreadonly DETECTED_FILE_PATH_WIN=\"${USER_APPDATA_PATH_WIN}\\\\detected\" # WINDOWS path to a file containing the output generated by the PowerShell script, formatted to define bash arrays.\n# 'FreeRDP Connection Test File'\nreadonly TEST_PATH=\"${USER_APPDATA_PATH}/FreeRDP_Connection_Test\"          # UNIX path to temporary file whose existence is used to confirm a successful RDP connection was established.\nreadonly TEST_PATH_WIN=\"${USER_APPDATA_PATH_WIN}\\\\FreeRDP_Connection_Test\" # WINDOWS path to temporary file whose existence is used to confirm a successful RDP connection was established.\n# 'WinApps Configuration File'\nreadonly CONFIG_PATH=\"${HOME}/.config/winapps/winapps.conf\" # UNIX path to the WinApps configuration file.\n# 'Inquirer Bash Script'\nreadonly INQUIRER_PATH=\"./install/inquirer.sh\" # UNIX path to the 'inquirer' script, which is used to produce selection menus.\n\n# REMOTE DESKTOP CONFIGURATION\nreadonly RDP_PORT=3389         # Port used for RDP on Windows.\nreadonly DOCKER_IP=\"127.0.0.1\" # Localhost.\n\n### GLOBAL VARIABLES ###\n# USER INPUT\nOPT_SYSTEM=0    # Set to '1' if the user specifies '--system'.\nOPT_USER=0      # Set to '1' if the user specifies '--user'.\nOPT_UNINSTALL=0 # Set to '1' if the user specifies '--uninstall'.\nOPT_AOSA=0      # Set to '1' if the user specifies '--setupAllOfficiallySupportedApps'.\nOPT_ADD_APPS=0  # Set to '1' if the user specifies '--add-apps'.\n\n# WINAPPS CONFIGURATION FILE\nRDP_USER=\"\"          # Imported variable.\nRDP_PASS=\"\"          # Imported variable.\nRDP_ASKPASS=\"\"       # Imported variable.\nRDP_DOMAIN=\"\"        # Imported variable.\nRDP_IP=\"\"            # Imported variable.\nVM_NAME=\"RDPWindows\" # Name of the Windows VM (FOR 'libvirt' ONLY).\nWAFLAVOR=\"docker\"    # Imported variable.\nRDP_SCALE=100        # Imported variable.\nRDP_FLAGS=\"\"         # Imported variable.\nDEBUG=\"true\"         # Imported variable.\nFREERDP_COMMAND=\"\"   # Imported variable.\n\nPORT_TIMEOUT=5      # Default port check timeout.\nRDP_TIMEOUT=30      # Default RDP connection test timeout.\nAPP_SCAN_TIMEOUT=60 # Default application scan timeout.\n\n# PERMISSIONS AND DIRECTORIES\nSUDO=\"\"         # Set to \"sudo\" if the user specifies '--system', or \"\" if the user specifies '--user'.\nBIN_PATH=\"\"     # Set to $SYS_BIN_PATH if the user specifies '--system', or $USER_BIN_PATH if the user specifies '--user'.\nAPP_PATH=\"\"     # Set to $SYS_APP_PATH if the user specifies '--system', or $USER_APP_PATH if the user specifies '--user'.\nAPPDATA_PATH=\"\" # Set to $SYS_APPDATA_PATH if the user specifies '--system', or $USER_APPDATA_PATH if the user specifies '--user'.\nSOURCE_PATH=\"\"  # Set to $SYS_SOURCE_PATH if the user specifies '--system', or $USER_SOURCE_PATH if the user specifies '--user'.\n\n# INSTALLATION PROCESS\nINSTALLED_EXES=() # List of executable file names of officially supported applications that have already been configured during the current installation process.\n\n### TRAPS ###\nset -o errtrace              # Ensure traps are inherited by all shell functions and subshells.\ntrap \"waTerminateScript\" ERR # Catch non-zero return values.\n\n### FUNCTIONS ###\n# Name: 'waTerminateScript'\n# Role: Terminates the script when a non-zero return value is encountered.\n# shellcheck disable=SC2329 # Silence warning regarding this function never being invoked (shellCheck is currently bad at figuring out functions that are invoked via trap).\nfunction waTerminateScript() {\n    # Store the non-zero exit status received by the trap.\n    local EXIT_STATUS=$?\n\n    # Display the exit status.\n    echo -e \"${EXIT_TEXT}Exiting with status '${EXIT_STATUS}'.${CLEAR_TEXT}\"\n\n    # Terminate the script.\n    exit \"$EXIT_STATUS\"\n}\n# Name: 'waUsage'\n# Role: Displays usage information for the script.\nfunction waUsage() {\n    echo -e \"Usage:\n  ${COMMAND_TEXT}    --user${CLEAR_TEXT}                                        # Install WinApps and selected applications in ${HOME}\n  ${COMMAND_TEXT}    --system${CLEAR_TEXT}                                      # Install WinApps and selected applications in /usr\n  ${COMMAND_TEXT}    --user --setupAllOfficiallySupportedApps${CLEAR_TEXT}      # Install WinApps and all officially supported applications in ${HOME}\n  ${COMMAND_TEXT}    --system --setupAllOfficiallySupportedApps${CLEAR_TEXT}    # Install WinApps and all officially supported applications in /usr\n  ${COMMAND_TEXT}    --user --uninstall${CLEAR_TEXT}                            # Uninstall everything in ${HOME}\n  ${COMMAND_TEXT}    --system --uninstall${CLEAR_TEXT}                          # Uninstall everything in /usr\n  ${COMMAND_TEXT}    --user --add-apps${CLEAR_TEXT}                             # Add new applications to existing installation in ${HOME}\n  ${COMMAND_TEXT}    --system --add-apps${CLEAR_TEXT}                           # Add new applications to existing installation in /usr\n  ${COMMAND_TEXT}    --help${CLEAR_TEXT}                                        # Display this usage message.\"\n}\n\n\n# Name: 'waGetSourceCode'\n# Role: Grab the WinApps source code using Git.\nfunction waGetSourceCode() {\n    # Declare variables.\n    local SCRIPT_DIR_PATH=\"\" # Stores the absolute path of the directory containing the script.\n\n    # Determine the absolute path to the directory containing the script.\n    SCRIPT_DIR_PATH=$(readlink -f \"$(dirname \"${BASH_SOURCE[0]}\")\")\n\n    # Check if winapps is currently installed on $SOURCE_PATH\n    if [[ -f \"$SCRIPT_DIR_PATH/winapps\" && \"$SCRIPT_DIR_PATH\" != \"$SOURCE_PATH\" ]]; then\n        # Display a warning.\n        echo -e \"${WARNING_TEXT}[WARNING]${CLEAR_TEXT} You are running a WinApps installation located outside of default location '${SOURCE_PATH}'. A new installation will be created.\"\n        echo -e \"${WARNING_TEXT}[WARNING]${CLEAR_TEXT} You might want to remove your old installation on '${SCRIPT_DIR_PATH}'.\"\n    fi\n\n    if [[ ! -d \"$SOURCE_PATH\" ]]; then\n        $SUDO git clone --recurse-submodules --remote-submodules https://github.com/winapps-org/winapps.git \"$SOURCE_PATH\"\n    else\n        echo -e \"${INFO_TEXT}WinApps installation already present at ${CLEAR_TEXT}${COMMAND_TEXT}${SOURCE_PATH}${CLEAR_TEXT}${INFO_TEXT}. Updating...${CLEAR_TEXT}\"\n        $SUDO git -C \"$SOURCE_PATH\" pull --no-rebase\n    fi\n\n    # Silently change the working directory.\n    if ! cd \"$SOURCE_PATH\" &>/dev/null; then\n        # Display the error type.\n        echo -e \"${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}DIRECTORY CHANGE FAILURE.${CLEAR_TEXT}\"\n\n        # Display error details.\n        echo -e \"${INFO_TEXT}Failed to change the working directory to ${CLEAR_TEXT}${COMMAND_TEXT}${SOURCE_PATH}${CLEAR_TEXT}${INFO_TEXT}.${CLEAR_TEXT}\"\n\n        # Display the suggested action(s).\n        echo \"--------------------------------------------------------------------------------\"\n        echo \"Ensure:\"\n        echo -e \"  - ${COMMAND_TEXT}${SOURCE_PATH}${CLEAR_TEXT} exists.\"\n        echo -e \"  - ${COMMAND_TEXT}${SOURCE_PATH}${CLEAR_TEXT} has been cloned and checked out properly.\"\n        echo -e \"  - The current user has sufficient permissions to access and write to ${COMMAND_TEXT}${SOURCE_PATH}${CLEAR_TEXT}.\"\n        echo \"--------------------------------------------------------------------------------\"\n\n        # Terminate the script.\n        return \"$EC_FAILED_CD\"\n    fi\n}\n\n# Name: 'waGetInquirer'\n# Role: Loads the inquirer script, even if the source isn't cloned yet\nfunction waGetInquirer() {\n    local INQUIRER=$INQUIRER_PATH\n\n    if [ -d \"$SYS_SOURCE_PATH\" ]; then\n        INQUIRER=$SYS_SOURCE_PATH/$INQUIRER_PATH\n    elif [ -d \"$USER_SOURCE_PATH\" ] ; then\n        INQUIRER=$USER_SOURCE_PATH/$INQUIRER_PATH\n    else\n        INQUIRER=\"/tmp/waInquirer.sh\"\n        rm -f \"$INQUIRER\"\n\n        curl -o \"$INQUIRER\" \"https://raw.githubusercontent.com/winapps-org/winapps/main/install/inquirer.sh\"\n    fi\n\n    # shellcheck source=/dev/null # Exclude this file from being checked by ShellCheck.\n    source \"$INQUIRER\"\n}\n\n# Name: 'waCheckInput'\n# Role: Sanitises input and guides users through selecting appropriate options if no arguments are provided.\nfunction waCheckInput() {\n    # Declare variables.\n    local OPTIONS=()      # Stores the options.\n    local SELECTED_OPTION # Stores the option selected by the user.\n\n    if [[ $# -gt 0 ]]; then\n        # Parse arguments.\n        for argument in \"$@\"; do\n            case \"$argument\" in\n            \"--user\")\n                OPT_USER=1\n                ;;\n            \"--system\")\n                OPT_SYSTEM=1\n                ;;\n            \"--setupAllOfficiallySupportedApps\")\n                OPT_AOSA=1\n                ;;\n            \"--uninstall\")\n                OPT_UNINSTALL=1\n                ;;\n            \"--add-apps\")\n                OPT_ADD_APPS=1\n                ;;\n            \"--help\")\n                waUsage\n                exit 0\n                ;;\n            *)\n                # Display the error type.\n                echo -e \"${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}INVALID ARGUMENT.${CLEAR_TEXT}\"\n\n                # Display the error details.\n                echo -e \"${INFO_TEXT}Unsupported argument${CLEAR_TEXT} ${COMMAND_TEXT}${argument}${CLEAR_TEXT}${INFO_TEXT}.${CLEAR_TEXT}\"\n\n                # Display the suggested action(s).\n                echo \"--------------------------------------------------------------------------------\"\n                waUsage\n                echo \"--------------------------------------------------------------------------------\"\n\n                # Terminate the script.\n                return \"$EC_BAD_ARGUMENT\"\n                ;;\n            esac\n        done\n    else\n        # Install vs. uninstall?\n        OPTIONS=(\"Install\" \"Uninstall\")\n        inqMenu \"Install or uninstall WinApps?\" OPTIONS SELECTED_OPTION\n\n        # Set flags.\n        if [[ $SELECTED_OPTION == \"Uninstall\" ]]; then\n            OPT_UNINSTALL=1\n        fi\n\n        # User vs. system?\n        OPTIONS=(\"Current User\" \"System\")\n        inqMenu \"Configure WinApps for the current user '$(whoami)' or the whole system?\" OPTIONS SELECTED_OPTION\n\n        # Set flags.\n        if [[ $SELECTED_OPTION == \"Current User\" ]]; then\n            OPT_USER=1\n        elif [[ $SELECTED_OPTION == \"System\" ]]; then\n            OPT_SYSTEM=1\n        fi\n\n        # Automatic vs. manual?\n        if [ \"$OPT_UNINSTALL\" -eq 0 ]; then\n            OPTIONS=(\"Manual (Default)\" \"Automatic\")\n            inqMenu \"Automatically install supported applications or choose manually?\" OPTIONS SELECTED_OPTION\n\n            # Set flags.\n            if [[ $SELECTED_OPTION == \"Automatic\" ]]; then\n                OPT_AOSA=1\n            fi\n        fi\n\n        # Newline.\n        echo \"\"\n    fi\n\n    # Simultaneous 'User' and 'System'.\n    if [ \"$OPT_SYSTEM\" -eq 1 ] && [ \"$OPT_USER\" -eq 1 ]; then\n        # Display the error type.\n        echo -e \"${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}CONFLICTING ARGUMENTS.${CLEAR_TEXT}\"\n\n        # Display the error details.\n        echo -e \"${INFO_TEXT}You cannot specify both${CLEAR_TEXT} ${COMMAND_TEXT}--user${CLEAR_TEXT} ${INFO_TEXT}and${CLEAR_TEXT} ${COMMAND_TEXT}--system${CLEAR_TEXT} ${INFO_TEXT}simultaneously.${CLEAR_TEXT}\"\n\n        # Display the suggested action(s).\n        echo \"--------------------------------------------------------------------------------\"\n        waUsage\n        echo \"--------------------------------------------------------------------------------\"\n\n        # Terminate the script.\n        return \"$EC_BAD_ARGUMENT\"\n    fi\n\n    # Simultaneous 'Uninstall' and 'AOSA'.\n    if [ \"$OPT_UNINSTALL\" -eq 1 ] && [ \"$OPT_AOSA\" -eq 1 ]; then\n        # Display the error type.\n        echo -e \"${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}CONFLICTING ARGUMENTS.${CLEAR_TEXT}\"\n\n        # Display the error details.\n        echo -e \"${INFO_TEXT}You cannot specify both${CLEAR_TEXT} ${COMMAND_TEXT}--uninstall${CLEAR_TEXT} ${INFO_TEXT}and${CLEAR_TEXT} ${COMMAND_TEXT}--aosa${CLEAR_TEXT} ${INFO_TEXT}simultaneously.${CLEAR_TEXT}\"\n\n        # Display the suggested action(s).\n        echo \"--------------------------------------------------------------------------------\"\n        waUsage\n        echo \"--------------------------------------------------------------------------------\"\n\n        # Terminate the script.\n        return \"$EC_BAD_ARGUMENT\"\n    fi\n\n    # Simultaneous 'Uninstall' and 'Add Apps'.\n    if [ \"$OPT_UNINSTALL\" -eq 1 ] && [ \"$OPT_ADD_APPS\" -eq 1 ]; then\n        # Display the error type.\n        echo -e \"${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}CONFLICTING ARGUMENTS.${CLEAR_TEXT}\"\n\n        # Display the error details.\n        echo -e \"${INFO_TEXT}You cannot specify both${CLEAR_TEXT} ${COMMAND_TEXT}--uninstall${CLEAR_TEXT} ${INFO_TEXT}and${CLEAR_TEXT} ${COMMAND_TEXT}--add-apps${CLEAR_TEXT} ${INFO_TEXT}simultaneously.${CLEAR_TEXT}\"\n\n        # Display the suggested action(s).\n        echo \"--------------------------------------------------------------------------------\"\n        waUsage\n        echo \"--------------------------------------------------------------------------------\"\n\n        # Terminate the script.\n        return \"$EC_BAD_ARGUMENT\"\n    fi\n\n    # Simultaneous 'AOSA' and 'Add Apps'.\n    if [ \"$OPT_AOSA\" -eq 1 ] && [ \"$OPT_ADD_APPS\" -eq 1 ]; then\n        # Display the error type.\n        echo -e \"${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}CONFLICTING ARGUMENTS.${CLEAR_TEXT}\"\n\n        # Display the error details.\n        echo -e \"${INFO_TEXT}You cannot specify both${CLEAR_TEXT} ${COMMAND_TEXT}--setupAllOfficiallySupportedApps${CLEAR_TEXT} ${INFO_TEXT}and${CLEAR_TEXT} ${COMMAND_TEXT}--add-apps${CLEAR_TEXT} ${INFO_TEXT}simultaneously.${CLEAR_TEXT}\"\n\n        # Display the suggested action(s).\n        echo \"--------------------------------------------------------------------------------\"\n        waUsage\n        echo \"--------------------------------------------------------------------------------\"\n\n        # Terminate the script.\n        return \"$EC_BAD_ARGUMENT\"\n    fi\n\n    # No 'User' or 'System'.\n    if [ \"$OPT_SYSTEM\" -eq 0 ] && [ \"$OPT_USER\" -eq 0 ]; then\n        # Display the error type.\n        echo -e \"${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}INSUFFICIENT ARGUMENTS.${CLEAR_TEXT}\"\n\n        # Display the error details.\n        echo -e \"${INFO_TEXT}You must specify either${CLEAR_TEXT} ${COMMAND_TEXT}--user${CLEAR_TEXT} ${INFO_TEXT}or${CLEAR_TEXT} ${COMMAND_TEXT}--system${CLEAR_TEXT} ${INFO_TEXT}to proceed.${CLEAR_TEXT}\"\n\n        # Display the suggested action(s).\n        echo \"--------------------------------------------------------------------------------\"\n        waUsage\n        echo \"--------------------------------------------------------------------------------\"\n\n        # Terminate the script.\n        return \"$EC_BAD_ARGUMENT\"\n    fi\n}\n\n# Name: 'waConfigurePathsAndPermissions'\n# Role: Sets paths and adjusts permissions as specified.\nfunction waConfigurePathsAndPermissions() {\n    if [ \"$OPT_USER\" -eq 1 ]; then\n        SUDO=\"\"\n        SOURCE_PATH=\"$USER_SOURCE_PATH\"\n        BIN_PATH=\"$USER_BIN_PATH\"\n        APP_PATH=\"$USER_APP_PATH\"\n        APPDATA_PATH=\"$USER_APPDATA_PATH\"\n    elif [ \"$OPT_SYSTEM\" -eq 1 ]; then\n        SUDO=\"sudo\"\n        SOURCE_PATH=\"$SYS_SOURCE_PATH\"\n        BIN_PATH=\"$SYS_BIN_PATH\"\n        APP_PATH=\"$SYS_APP_PATH\"\n        APPDATA_PATH=\"$SYS_APPDATA_PATH\"\n\n        # Preemptively obtain superuser privileges.\n        sudo -v || {\n            # Display the error type.\n            echo -e \"${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}AUTHENTICATION FAILURE.${CLEAR_TEXT}\"\n\n            # Display the error details.\n            echo -e \"${INFO_TEXT}Failed to gain superuser privileges.${CLEAR_TEXT}\"\n\n            # Display the suggested action(s).\n            echo \"--------------------------------------------------------------------------------\"\n            echo \"Please check your password and try again.\"\n            echo \"If you continue to experience issues, contact your system administrator.\"\n            echo \"--------------------------------------------------------------------------------\"\n\n            # Terminate the script.\n            return \"$EC_NO_SUDO\"\n        }\n    fi\n}\n# Name: 'waCheckExistingInstall'\n# Role: Identifies any existing WinApps installations that may conflict with the new installation.\nfunction waCheckExistingInstall() {\n    # Print feedback.\n    echo -n \"Checking for existing conflicting WinApps installations... \"\n\n    # If --add-apps is specified, we don't want to fail if an installation exists\n    if [ \"$OPT_ADD_APPS\" -eq 1 ]; then\n        # Check for an existing 'user' installation.\n        if [[ -f \"${USER_BIN_PATH}/winapps\" && -d \"${USER_SOURCE_PATH}/winapps\" ]]; then\n            # Complete the previous line.\n            echo -e \"${DONE_TEXT}Found!${CLEAR_TEXT}\"\n            echo -e \"${INFO_TEXT}Adding new applications to existing user installation.${CLEAR_TEXT}\"\n            return 0\n        fi\n\n        # Check for an existing 'system' installation.\n        if [[ -f \"${SYS_BIN_PATH}/winapps\" && -d \"${SYS_SOURCE_PATH}/winapps\" ]]; then\n            # Complete the previous line.\n            echo -e \"${DONE_TEXT}Found!${CLEAR_TEXT}\"\n            echo -e \"${INFO_TEXT}Adding new applications to existing system installation.${CLEAR_TEXT}\"\n            return 0\n        fi\n\n        # If we're adding apps but no installation exists, that's an error\n        echo -e \"${FAIL_TEXT}Failed!${CLEAR_TEXT}\\n\"\n\n        # Display the error type.\n        echo -e \"${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}NO EXISTING WINAPPS INSTALLATION.${CLEAR_TEXT}\"\n\n        # Display the error details.\n        echo -e \"${INFO_TEXT}No existing WinApps installation was detected.${CLEAR_TEXT}\"\n\n        # Display the suggested action(s).\n        echo \"--------------------------------------------------------------------------------\"\n        echo -e \"Please install WinApps first using ${COMMAND_TEXT}winapps-setup --user${CLEAR_TEXT} or ${COMMAND_TEXT}winapps-setup --system${CLEAR_TEXT}.\"\n        echo \"--------------------------------------------------------------------------------\"\n\n        # Terminate the script.\n        return \"$EC_EXISTING_INSTALL\"\n    fi\n\n    # Check for an existing 'user' installation.\n    if [[ -f \"${USER_BIN_PATH}/winapps\" || -d \"${USER_SOURCE_PATH}/winapps\" ]]; then\n        # Complete the previous line.\n        echo -e \"${FAIL_TEXT}Failed!${CLEAR_TEXT}\\n\"\n\n        # Display the error type.\n        echo -e \"${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}EXISTING 'USER' WINAPPS INSTALLATION.${CLEAR_TEXT}\"\n\n        # Display the error details.\n        echo -e \"${INFO_TEXT}A previous WinApps installation was detected for the current user.${CLEAR_TEXT}\"\n\n        # Display the suggested action(s).\n        echo \"--------------------------------------------------------------------------------\"\n        echo -e \"Please remove the existing WinApps installation using ${COMMAND_TEXT}winapps-setup --user --uninstall${CLEAR_TEXT}.\"\n        echo \"--------------------------------------------------------------------------------\"\n\n        # Terminate the script.\n        return \"$EC_EXISTING_INSTALL\"\n    fi\n\n    # Check for an existing 'system' installation.\n    if [[ -f \"${SYS_BIN_PATH}/winapps\" || -d \"${SYS_SOURCE_PATH}/winapps\" ]]; then\n        # Complete the previous line.\n        echo -e \"${FAIL_TEXT}Failed!${CLEAR_TEXT}\\n\"\n\n        # Display the error type.\n        echo -e \"${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}EXISTING 'SYSTEM' WINAPPS INSTALLATION.${CLEAR_TEXT}\"\n\n        # Display the error details.\n        echo -e \"${INFO_TEXT}A previous system-wide WinApps installation was detected.${CLEAR_TEXT}\"\n\n        # Display the suggested action(s).\n        echo \"--------------------------------------------------------------------------------\"\n        echo -e \"Please remove the existing WinApps installation using ${COMMAND_TEXT}winapps-setup --system --uninstall${CLEAR_TEXT}.\"\n        echo \"--------------------------------------------------------------------------------\"\n\n        # Terminate the script.\n        return \"$EC_EXISTING_INSTALL\"\n    fi\n\n    # Print feedback.\n    echo -e \"${DONE_TEXT}Done!${CLEAR_TEXT}\"\n}\n\n\n# Name: 'waFixScale'\n# Role: Since FreeRDP only supports '/scale' values of 100, 140 or 180, find the closest supported argument to the user's configuration.\nfunction waFixScale() {\n    # Define variables.\n    local OLD_SCALE=100\n    local VALID_SCALE_1=100\n    local VALID_SCALE_2=140\n    local VALID_SCALE_3=180\n\n    # Check for an unsupported value.\n    if [ \"$RDP_SCALE\" != \"$VALID_SCALE_1\" ] && [ \"$RDP_SCALE\" != \"$VALID_SCALE_2\" ] && [ \"$RDP_SCALE\" != \"$VALID_SCALE_3\" ]; then\n        # Save the unsupported scale.\n        OLD_SCALE=\"$RDP_SCALE\"\n\n        # Calculate the absolute differences.\n        local DIFF_1=$(( RDP_SCALE > VALID_SCALE_1 ? RDP_SCALE - VALID_SCALE_1 : VALID_SCALE_1 - RDP_SCALE ))\n        local DIFF_2=$(( RDP_SCALE > VALID_SCALE_2 ? RDP_SCALE - VALID_SCALE_2 : VALID_SCALE_2 - RDP_SCALE ))\n        local DIFF_3=$(( RDP_SCALE > VALID_SCALE_3 ? RDP_SCALE - VALID_SCALE_3 : VALID_SCALE_3 - RDP_SCALE ))\n\n        # Set the final scale to the valid scale value with the smallest absolute difference.\n        if (( DIFF_1 <= DIFF_2 && DIFF_1 <= DIFF_3 )); then\n            RDP_SCALE=\"$VALID_SCALE_1\"\n        elif (( DIFF_2 <= DIFF_1 && DIFF_2 <= DIFF_3 )); then\n            RDP_SCALE=\"$VALID_SCALE_2\"\n        else\n            RDP_SCALE=\"$VALID_SCALE_3\"\n        fi\n\n        # Print feedback.\n        echo -e \"${WARNING_TEXT}[WARNING]${CLEAR_TEXT} Unsupported RDP_SCALE value '${OLD_SCALE}' detected. Defaulting to '${RDP_SCALE}'.\"\n    fi\n}\n\n# Name: 'waLoadConfig'\n# Role: Loads settings specified within the WinApps configuration file.\nfunction waLoadConfig() {\n    # Print feedback.\n    echo -n \"Attempting to load WinApps configuration file... \"\n\n    if [ ! -f \"$CONFIG_PATH\" ]; then\n        # Complete the previous line.\n        echo -e \"${FAIL_TEXT}Failed!${CLEAR_TEXT}\\n\"\n\n        # Display the error type.\n        echo -e \"${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}MISSING CONFIGURATION FILE.${CLEAR_TEXT}\"\n\n        # Display the error details.\n        echo -e \"${INFO_TEXT}A valid WinApps configuration file was not found.${CLEAR_TEXT}\"\n\n        # Display the suggested action(s).\n        echo \"--------------------------------------------------------------------------------\"\n        echo -e \"Please create a configuration file at ${COMMAND_TEXT}${CONFIG_PATH}${CLEAR_TEXT}.\"\n        echo -e \"See https://github.com/winapps-org/winapps?tab=readme-ov-file#step-3-create-a-winapps-configuration-file\"\n        echo \"--------------------------------------------------------------------------------\"\n\n        # Terminate the script.\n        return \"$EC_NO_CONFIG\"\n    else\n        # Load the WinApps configuration file.\n        # shellcheck source=/dev/null # Exclude this file from being checked by ShellCheck.\n        source \"$CONFIG_PATH\"\n\n        # Send password on the command line if a command to retrieve the password from is not given\n        # Otherwise, set FREERDP_ASKPASS which freerdp will read the stdout of to use as the password\n        RDP_PASSWORD_ARG=\"/p:$RDP_PASS\"\n\n        if [[ ! -z \"$RDP_ASKPASS\" ]]; then\n            export FREERDP_ASKPASS=\"$RDP_ASKPASS\"\n            unset RDP_PASSWORD_ARG\n        fi\n    fi\n\n    # Print feedback.\n    echo -e \"${DONE_TEXT}Done!${CLEAR_TEXT}\"\n}\n\n# Name: 'waCheckScriptDependencies'\n# Role: Terminate script if dependencies are missing.\nfunction waCheckScriptDependencies() {\n    # 'Git'\n    if ! command -v git &>/dev/null; then\n        # Display the error type.\n        echo -e \"${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}MISSING DEPENDENCIES.${CLEAR_TEXT}\"\n\n        # Display the error details.\n        echo -e \"${INFO_TEXT}Please install 'git' to proceed.${CLEAR_TEXT}\"\n\n        # Display the suggested action(s).\n        echo \"--------------------------------------------------------------------------------\"\n        echo \"Debian/Ubuntu-based systems:\"\n        echo -e \"  ${COMMAND_TEXT}sudo apt install git${CLEAR_TEXT}\"\n        echo \"Red Hat/Fedora-based systems:\"\n        echo -e \"  ${COMMAND_TEXT}sudo dnf install git${CLEAR_TEXT}\"\n        echo \"Arch Linux systems:\"\n        echo -e \"  ${COMMAND_TEXT}sudo pacman -S git${CLEAR_TEXT}\"\n        echo \"Gentoo Linux systems:\"\n        echo -e \"  ${COMMAND_TEXT}sudo emerge --ask dev-vcs/git${CLEAR_TEXT}\"\n        echo \"--------------------------------------------------------------------------------\"\n\n        # Terminate the script.\n        return \"$EC_MISSING_DEPS\"\n    fi\n\n    # 'curl'\n    if ! command -v curl &>/dev/null; then\n        # Display the error type.\n        echo -e \"${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}MISSING DEPENDENCIES.${CLEAR_TEXT}\"\n\n        # Display the error details.\n        echo -e \"${INFO_TEXT}Please install 'curl' to proceed.${CLEAR_TEXT}\"\n\n        # Display the suggested action(s).\n        echo \"--------------------------------------------------------------------------------\"\n        echo \"Debian/Ubuntu-based systems:\"\n        echo -e \"  ${COMMAND_TEXT}sudo apt install curl${CLEAR_TEXT}\"\n        echo \"Red Hat/Fedora-based systems:\"\n        echo -e \"  ${COMMAND_TEXT}sudo dnf install curl${CLEAR_TEXT}\"\n        echo \"Arch Linux systems:\"\n        echo -e \"  ${COMMAND_TEXT}sudo pacman -S curl${CLEAR_TEXT}\"\n        echo \"Gentoo Linux systems:\"\n        echo -e \"  ${COMMAND_TEXT}sudo emerge --ask net-misc/curl${CLEAR_TEXT}\"\n        echo \"--------------------------------------------------------------------------------\"\n\n        # Terminate the script.\n        return \"$EC_MISSING_DEPS\"\n    fi\n\n    # 'Dialog'.\n    if ! command -v dialog &>/dev/null; then\n        # Display the error type.\n        echo -e \"${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}MISSING DEPENDENCIES.${CLEAR_TEXT}\"\n\n        # Display the error details.\n        echo -e \"${INFO_TEXT}Please install 'dialog' to proceed.${CLEAR_TEXT}\"\n\n        # Display the suggested action(s).\n        echo \"--------------------------------------------------------------------------------\"\n        echo \"Debian/Ubuntu-based systems:\"\n        echo -e \"  ${COMMAND_TEXT}sudo apt install dialog${CLEAR_TEXT}\"\n        echo \"Red Hat/Fedora-based systems:\"\n        echo -e \"  ${COMMAND_TEXT}sudo dnf install dialog${CLEAR_TEXT}\"\n        echo \"Arch Linux systems:\"\n        echo -e \"  ${COMMAND_TEXT}sudo pacman -S dialog${CLEAR_TEXT}\"\n        echo \"Gentoo Linux systems:\"\n        echo -e \"  ${COMMAND_TEXT}sudo emerge --ask dialog${CLEAR_TEXT}\"\n        echo \"--------------------------------------------------------------------------------\"\n\n        # Terminate the script.\n        return \"$EC_MISSING_DEPS\"\n    fi\n}\n\n# Name: 'waCheckInstallDependencies'\n# Role: Terminate script if dependencies required to install WinApps are missing.\nfunction waCheckInstallDependencies() {\n    # Declare variables.\n    local FREERDP_MAJOR_VERSION=\"\" # Stores the major version of the installed copy of FreeRDP.\n\n    # Print feedback.\n    echo -n \"Checking whether dependencies are installed... \"\n\n    # 'libnotify'\n    if ! command -v notify-send &>/dev/null; then\n        # Complete the previous line.\n        echo -e \"${FAIL_TEXT}Failed!${CLEAR_TEXT}\\n\"\n\n        # Display the error type.\n        echo -e \"${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}MISSING DEPENDENCIES.${CLEAR_TEXT}\"\n\n        # Display the error details.\n        echo -e \"${INFO_TEXT}Please install 'libnotify' to proceed.${CLEAR_TEXT}\"\n\n        # Display the suggested action(s).\n        echo \"--------------------------------------------------------------------------------\"\n        echo \"Debian/Ubuntu-based systems:\"\n        echo -e \"  ${COMMAND_TEXT}sudo apt install libnotify-bin${CLEAR_TEXT}\"\n        echo \"Red Hat/Fedora-based systems:\"\n        echo -e \"  ${COMMAND_TEXT}sudo dnf install libnotify${CLEAR_TEXT}\"\n        echo \"Arch Linux systems:\"\n        echo -e \"  ${COMMAND_TEXT}sudo pacman -S libnotify${CLEAR_TEXT}\"\n        echo \"Gentoo Linux systems:\"\n        echo -e \"  ${COMMAND_TEXT}sudo emerge --ask x11-libs/libnotify${CLEAR_TEXT}\"\n        echo \"--------------------------------------------------------------------------------\"\n\n        # Terminate the script.\n        return \"$EC_MISSING_DEPS\"\n    fi\n\n    # 'Netcat'\n    if ! command -v nc &>/dev/null; then\n        # Complete the previous line.\n        echo -e \"${FAIL_TEXT}Failed!${CLEAR_TEXT}\\n\"\n\n        # Display the error type.\n        echo -e \"${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}MISSING DEPENDENCIES.${CLEAR_TEXT}\"\n\n        # Display the error details.\n        echo -e \"${INFO_TEXT}Please install 'netcat' to proceed.${CLEAR_TEXT}\"\n\n        # Display the suggested action(s).\n        echo \"--------------------------------------------------------------------------------\"\n        echo \"Debian/Ubuntu-based systems:\"\n        echo -e \"  ${COMMAND_TEXT}sudo apt install netcat${CLEAR_TEXT}\"\n        echo \"Red Hat/Fedora-based systems:\"\n        echo -e \"  ${COMMAND_TEXT}sudo dnf install nmap-ncat${CLEAR_TEXT}\"\n        echo \"Arch Linux systems:\"\n        echo -e \"  ${COMMAND_TEXT}sudo pacman -S openbsd-netcat${CLEAR_TEXT}\"\n        echo \"Gentoo Linux systems:\"\n        echo -e \"  ${COMMAND_TEXT}sudo emerge --ask net-analyzer/netcat${CLEAR_TEXT}\"\n        echo \"--------------------------------------------------------------------------------\"\n\n        # Terminate the script.\n        return \"$EC_MISSING_DEPS\"\n    fi\n\n    # 'FreeRDP' (Version 3).\n    # Attempt to set a FreeRDP command if the command variable is empty.\n    if [ -z \"$FREERDP_COMMAND\" ]; then\n        # Check common commands used to launch FreeRDP.\n        if command -v xfreerdp &>/dev/null; then\n            # Check FreeRDP major version is 3 or greater.\n            FREERDP_MAJOR_VERSION=$(xfreerdp --version | head -n 1 | grep -o -m 1 '\\b[0-9]\\S*' | head -n 1 | cut -d'.' -f1)\n            if [[ $FREERDP_MAJOR_VERSION =~ ^[0-9]+$ ]] && ((FREERDP_MAJOR_VERSION >= 3)); then\n                FREERDP_COMMAND=\"xfreerdp\"\n            fi\n        fi\n\n        # Check for xfreerdp3 command as a fallback option.\n        if [ -z \"$FREERDP_COMMAND\" ]; then\n            if command -v xfreerdp3 &>/dev/null; then\n                # Check FreeRDP major version is 3 or greater.\n                FREERDP_MAJOR_VERSION=$(xfreerdp3 --version | head -n 1 | grep -o -m 1 '\\b[0-9]\\S*' | head -n 1 | cut -d'.' -f1)\n                if [[ $FREERDP_MAJOR_VERSION =~ ^[0-9]+$ ]] && ((FREERDP_MAJOR_VERSION >= 3)); then\n                    FREERDP_COMMAND=\"xfreerdp3\"\n                fi\n            fi\n        fi\n\n        # Check for FreeRDP flatpak as a fallback option.\n        if [ -z \"$FREERDP_COMMAND\" ]; then\n            if command -v flatpak &>/dev/null; then\n                if flatpak list --columns=application | grep -q \"^com.freerdp.FreeRDP$\"; then\n                    # Check FreeRDP major version is 3 or greater.\n                    FREERDP_MAJOR_VERSION=$(flatpak list --columns=application,version | grep \"^com.freerdp.FreeRDP\" | awk '{print $2}' | cut -d'.' -f1)\n                    if [[ $FREERDP_MAJOR_VERSION =~ ^[0-9]+$ ]] && ((FREERDP_MAJOR_VERSION >= 3)); then\n                        FREERDP_COMMAND=\"flatpak run --command=xfreerdp com.freerdp.FreeRDP\"\n                    fi\n                fi\n            fi\n        fi\n    fi\n\n    if ! command -v \"$FREERDP_COMMAND\" &>/dev/null && [ \"$FREERDP_COMMAND\" != \"flatpak run --command=xfreerdp com.freerdp.FreeRDP\" ]; then\n        # Complete the previous line.\n        echo -e \"${FAIL_TEXT}Failed!${CLEAR_TEXT}\\n\"\n\n        # Display the error type.\n        echo -e \"${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}MISSING DEPENDENCIES.${CLEAR_TEXT}\"\n\n        # Display the error details.\n        echo -e \"${INFO_TEXT}Please install 'FreeRDP' version 3 to proceed.${CLEAR_TEXT}\"\n\n        # Display the suggested action(s).\n        echo \"--------------------------------------------------------------------------------\"\n        echo \"Debian/Ubuntu-based systems:\"\n        echo -e \"  ${COMMAND_TEXT}sudo apt install freerdp3-x11${CLEAR_TEXT}\"\n        echo \"Red Hat/Fedora-based systems:\"\n        echo -e \"  ${COMMAND_TEXT}sudo dnf install freerdp${CLEAR_TEXT}\"\n        echo \"Arch Linux systems:\"\n        echo -e \"  ${COMMAND_TEXT}sudo pacman -S freerdp${CLEAR_TEXT}\"\n        echo \"Gentoo Linux systems:\"\n        echo -e \"  ${COMMAND_TEXT}sudo emerge --ask net-misc/freerdp${CLEAR_TEXT}\"\n        echo \"\"\n        echo \"You can also install FreeRDP as a Flatpak.\"\n        echo \"Install Flatpak, add the Flathub repository and then install FreeRDP:\"\n        echo -e \"${COMMAND_TEXT}flatpak install flathub com.freerdp.FreeRDP${CLEAR_TEXT}\"\n        echo -e \"${COMMAND_TEXT}sudo flatpak override --filesystem=home com.freerdp.FreeRDP${CLEAR_TEXT}\"\n        echo \"--------------------------------------------------------------------------------\"\n\n        # Terminate the script.\n        return \"$EC_MISSING_DEPS\"\n    fi\n\n    # 'libvirt'/'virt-manager' + 'iproute2'.\n    if [ \"$WAFLAVOR\" = \"libvirt\" ]; then\n        if ! command -v virsh &>/dev/null; then\n            # Complete the previous line.\n            echo -e \"${FAIL_TEXT}Failed!${CLEAR_TEXT}\\n\"\n\n            # Display the error type.\n            echo -e \"${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}MISSING DEPENDENCIES.${CLEAR_TEXT}\"\n\n            # Display the error details.\n            echo -e \"${INFO_TEXT}Please install 'Virtual Machine Manager' to proceed.${CLEAR_TEXT}\"\n\n            # Display the suggested action(s).\n            echo \"--------------------------------------------------------------------------------\"\n            echo \"Debian/Ubuntu-based systems:\"\n            echo -e \"  ${COMMAND_TEXT}sudo apt install virt-manager${CLEAR_TEXT}\"\n            echo \"Red Hat/Fedora-based systems:\"\n            echo -e \"  ${COMMAND_TEXT}sudo dnf install virt-manager${CLEAR_TEXT}\"\n            echo \"Arch Linux systems:\"\n            echo -e \"  ${COMMAND_TEXT}sudo pacman -S virt-manager${CLEAR_TEXT}\"\n            echo \"Gentoo Linux systems:\"\n            echo -e \"  ${COMMAND_TEXT}sudo emerge --ask app-emulation/virt-manager${CLEAR_TEXT}\"\n            echo \"--------------------------------------------------------------------------------\"\n\n            # Terminate the script.\n            return \"$EC_MISSING_DEPS\"\n        fi\n\n        if ! command -v ip &>/dev/null; then\n            # Complete the previous line.\n            echo -e \"${FAIL_TEXT}Failed!${CLEAR_TEXT}\\n\"\n\n            # Display the error type.\n            echo -e \"${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}MISSING DEPENDENCIES.${CLEAR_TEXT}\"\n\n            # Display the error details.\n            echo -e \"${INFO_TEXT}Please install 'iproute2' to proceed.${CLEAR_TEXT}\"\n\n            # Display the suggested action(s).\n            echo \"--------------------------------------------------------------------------------\"\n            echo \"Debian/Ubuntu-based systems:\"\n            echo -e \"  ${COMMAND_TEXT}sudo apt install iproute2${CLEAR_TEXT}\"\n            echo \"Red Hat/Fedora-based systems:\"\n            echo -e \"  ${COMMAND_TEXT}sudo dnf install iproute${CLEAR_TEXT}\"\n            echo \"Arch Linux systems:\"\n            echo -e \"  ${COMMAND_TEXT}sudo pacman -S iproute2${CLEAR_TEXT}\"\n            echo \"Gentoo Linux systems:\"\n            echo -e \"  ${COMMAND_TEXT}sudo emerge --ask net-misc/iproute2${CLEAR_TEXT}\"\n            echo \"--------------------------------------------------------------------------------\"\n\n            # Terminate the script.\n            return \"$EC_MISSING_DEPS\"\n        fi\n    elif [ \"$WAFLAVOR\" = \"docker\" ]; then\n        if ! command -v docker &>/dev/null; then\n            # Complete the previous line.\n            echo -e \"${FAIL_TEXT}Failed!${CLEAR_TEXT}\\n\"\n\n            # Display the error type.\n            echo -e \"${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}MISSING DEPENDENCIES.${CLEAR_TEXT}\"\n\n            # Display the error details.\n            echo -e \"${INFO_TEXT}Please install 'Docker Engine' to proceed.${CLEAR_TEXT}\"\n\n            # Display the suggested action(s).\n            echo \"--------------------------------------------------------------------------------\"\n            echo \"Please visit https://docs.docker.com/engine/install/ for more information.\"\n            echo \"--------------------------------------------------------------------------------\"\n\n            # Terminate the script.\n            return \"$EC_MISSING_DEPS\"\n        fi\n    elif [ \"$WAFLAVOR\" = \"podman\" ]; then\n        if ! command -v podman-compose &>/dev/null || ! command -v podman &>/dev/null; then\n            # Complete the previous line.\n            echo -e \"${FAIL_TEXT}Failed!${CLEAR_TEXT}\\n\"\n\n            # Display the error type.\n            echo -e \"${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}MISSING DEPENDENCIES.${CLEAR_TEXT}\"\n\n            # Display the error details.\n            echo -e \"${INFO_TEXT}Please install 'podman' and 'podman-compose' to proceed.${CLEAR_TEXT}\"\n\n            # Display the suggested action(s).\n            echo \"--------------------------------------------------------------------------------\"\n            echo \"Please visit https://podman.io/docs/installation for more information.\"\n            echo \"Please visit https://github.com/containers/podman-compose for more information.\"\n            echo \"--------------------------------------------------------------------------------\"\n\n            # Terminate the script.\n            return \"$EC_MISSING_DEPS\"\n        fi\n    fi\n\n    # Print feedback.\n    echo -e \"${DONE_TEXT}Done!${CLEAR_TEXT}\"\n}\n\n# Name: 'waCheckGroupMembership'\n# Role: Ensures the current user is part of the required groups.\nfunction waCheckGroupMembership() {\n    # Print feedback.\n    echo -n \"Checking whether the user '$(whoami)' is part of the required groups... \"\n\n    # Declare variables.\n    local USER_GROUPS=\"\" # Stores groups the current user belongs to.\n\n    # Identify groups the current user belongs to.\n    USER_GROUPS=$(groups \"$(whoami)\")\n\n    if ! (echo \"$USER_GROUPS\" | grep -q -E \"\\blibvirt\\b\") || ! (echo \"$USER_GROUPS\" | grep -q -E \"\\bkvm\\b\"); then\n        # Complete the previous line.\n        echo -e \"${FAIL_TEXT}Failed!${CLEAR_TEXT}\\n\"\n\n        # Display the error type.\n        echo -e \"${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}GROUP MEMBERSHIP CHECK ERROR.${CLEAR_TEXT}\"\n\n        # Display the error details.\n        echo -e \"${INFO_TEXT}The current user '$(whoami)' is not part of group 'libvirt' and/or group 'kvm'.${CLEAR_TEXT}\"\n\n        # Display the suggested action(s).\n        echo \"--------------------------------------------------------------------------------\"\n        echo \"Please run the below commands, followed by a system reboot:\"\n        echo -e \"${COMMAND_TEXT}sudo usermod -a -G libvirt $(whoami)${CLEAR_TEXT}\"\n        echo -e \"${COMMAND_TEXT}sudo usermod -a -G kvm $(whoami)${CLEAR_TEXT}\"\n        echo \"--------------------------------------------------------------------------------\"\n\n        # Terminate the script.\n        return \"$EC_NOT_IN_GROUP\"\n    fi\n\n    # Print feedback.\n    echo -e \"${DONE_TEXT}Done!${CLEAR_TEXT}\"\n}\n\n# Name: 'waCheckVMRunning'\n# Role: Checks the state of the Windows 'libvirt' VM to ensure it is running.\nfunction waCheckVMRunning() {\n    # Print feedback.\n    echo -n \"Checking the status of the Windows VM... \"\n\n    # Obtain VM Status\n    VM_PAUSED=0\n    virsh list --state-paused --name | grep -Fxq -- \"$VM_NAME\" || VM_PAUSED=\"$?\"\n    VM_RUNNING=0\n    virsh list --state-running --name | grep -Fxq -- \"$VM_NAME\" || VM_RUNNING=\"$?\"\n    VM_SHUTOFF=0\n    virsh list --state-shutoff --name | grep -Fxq -- \"$VM_NAME\" || VM_SHUTOFF=\"$?\"\n\n    if [[ $VM_SHUTOFF == \"0\" ]]; then\n        # Complete the previous line.\n        echo -e \"${FAIL_TEXT}Failed!${CLEAR_TEXT}\\n\"\n\n        # Display the error type.\n        echo -e \"${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}WINDOWS VM NOT RUNNING.${CLEAR_TEXT}\"\n\n        # Display the error details.\n        echo -e \"${INFO_TEXT}The Windows VM '${VM_NAME}' is powered off.${CLEAR_TEXT}\"\n\n        # Display the suggested action(s).\n        echo \"--------------------------------------------------------------------------------\"\n        echo \"Please run the below command to start the Windows VM:\"\n        echo -e \"${COMMAND_TEXT}virsh start ${VM_NAME}${CLEAR_TEXT}\"\n        echo \"--------------------------------------------------------------------------------\"\n\n        # Terminate the script.\n        return \"$EC_VM_OFF\"\n    elif [[ $VM_PAUSED == \"0\" ]]; then\n        # Complete the previous line.\n        echo -e \"${FAIL_TEXT}Failed!${CLEAR_TEXT}\\n\"\n\n        # Display the error type.\n        echo -e \"${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}WINDOWS VM NOT RUNNING.${CLEAR_TEXT}\"\n\n        # Display the error details.\n        echo -e \"${INFO_TEXT}The Windows VM '${VM_NAME}' is paused.${CLEAR_TEXT}\"\n\n        # Display the suggested action(s).\n        echo \"--------------------------------------------------------------------------------\"\n        echo \"Please run the below command to resume the Windows VM:\"\n        echo -e \"${COMMAND_TEXT}virsh resume ${VM_NAME}${CLEAR_TEXT}\"\n        echo \"--------------------------------------------------------------------------------\"\n\n        # Terminate the script.\n        return \"$EC_VM_PAUSED\"\n    elif [[ $VM_RUNNING != \"0\" ]]; then\n        # Complete the previous line.\n        echo -e \"${FAIL_TEXT}Failed!${CLEAR_TEXT}\\n\"\n\n        # Display the error type.\n        echo -e \"${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}WINDOWS VM DOES NOT EXIST.${CLEAR_TEXT}\"\n\n        # Display the error details.\n        echo -e \"${INFO_TEXT}The Windows VM '${VM_NAME}' could not be found.${CLEAR_TEXT}\"\n\n        # Display the suggested action(s).\n        echo \"--------------------------------------------------------------------------------\"\n        echo \"Please ensure a Windows VM with the name '${VM_NAME}' exists.\"\n        echo \"--------------------------------------------------------------------------------\"\n\n        # Terminate the script.\n        return \"$EC_VM_ABSENT\"\n    fi\n\n    # Print feedback.\n    echo -e \"${DONE_TEXT}Done!${CLEAR_TEXT}\"\n}\n\n# Name: 'waCheckContainerRunning'\n# Role: Throw an error if the Docker/Podman container is not running.\nfunction waCheckContainerRunning() {\n    # Print feedback.\n    echo -n \"Checking container status... \"\n\n    # Declare variables.\n    local CONTAINER_STATE=\"\"\n    local COMPOSE_COMMAND=\"\"\n\n    # Determine the state of the container.\n    CONTAINER_STATE=$(\"$WAFLAVOR\" ps --all --filter name=\"WinApps\" --format '{{.Status}}')\n    CONTAINER_STATE=${CONTAINER_STATE,,} # Convert the string to lowercase.\n    CONTAINER_STATE=${CONTAINER_STATE%% *} # Extract the first word.\n\n    # Determine the compose command.\n    case \"$WAFLAVOR\" in\n        \"docker\") COMPOSE_COMMAND=\"docker compose\" ;;\n        \"podman\") COMPOSE_COMMAND=\"podman-compose\" ;;\n    esac\n\n    # Check container state.\n    if [[ \"$CONTAINER_STATE\" != \"up\" ]]; then\n        # Complete the previous line.\n        echo -e \"${FAIL_TEXT}Failed!${CLEAR_TEXT}\\n\"\n\n        # Display the error type.\n        echo -e \"${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}CONTAINER NOT RUNNING.${CLEAR_TEXT}\"\n\n        # Display the error details.\n        echo -e \"${INFO_TEXT}Windows is not running.${CLEAR_TEXT}\"\n\n        # Display the suggested action(s).\n        echo \"--------------------------------------------------------------------------------\"\n        echo \"Please ensure Windows is powered on:\"\n        echo -e \"${COMMAND_TEXT}${COMPOSE_COMMAND} --file ~/.config/winapps/compose.yaml start${CLEAR_TEXT}\"\n        echo \"--------------------------------------------------------------------------------\"\n\n        # Terminate the script.\n        return \"$EC_CONTAINER_OFF\"\n    fi\n\n    # Print feedback.\n    echo -e \"${DONE_TEXT}Done!${CLEAR_TEXT}\"\n}\n\n# Name: 'waCheckPortOpen'\n# Role: Assesses whether the RDP port on Windows is open.\nfunction waCheckPortOpen() {\n    # Print feedback.\n    echo -n \"Checking for an open RDP Port on Windows... \"\n\n    # Declare variables.\n    local VM_MAC=\"\" # Stores the MAC address of the Windows VM.\n\n    # Obtain Windows VM IP Address (FOR 'libvirt' ONLY)\n    # Note: 'RDP_IP' should not be empty if 'WAFLAVOR' is 'docker', since it is set to localhost before this function is called.\n    if [ -z \"$RDP_IP\" ] && [ \"$WAFLAVOR\" = \"libvirt\" ]; then\n        VM_MAC=$(virsh domiflist \"$VM_NAME\" | grep -oE \"([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})\") # VM MAC address.\n        RDP_IP=$(ip neigh show | grep \"$VM_MAC\" | grep -oE \"([0-9]{1,3}\\.){3}[0-9]{1,3}\")         # VM IP address.\n\n        if [ -z \"$RDP_IP\" ]; then\n            # Complete the previous line.\n            echo -e \"${FAIL_TEXT}Failed!${CLEAR_TEXT}\\n\"\n\n            # Display the error type.\n            echo -e \"${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}NETWORK CONFIGURATION ERROR.${CLEAR_TEXT}\"\n\n            # Display the error details.\n            echo -e \"${INFO_TEXT}The IP address of the Windows VM '${VM_NAME}' could not be found.${CLEAR_TEXT}\"\n\n            # Display the suggested action(s).\n            echo \"--------------------------------------------------------------------------------\"\n            echo \"Please ensure networking is properly configured for the Windows VM.\"\n            echo \"--------------------------------------------------------------------------------\"\n\n            # Terminate the script.\n            return \"$EC_NO_IP\"\n        fi\n    fi\n\n    # Check for an open RDP port.\n    if ! timeout \"$PORT_TIMEOUT\" nc -z \"$RDP_IP\" \"$RDP_PORT\" &>/dev/null; then\n        # Complete the previous line.\n        echo -e \"${FAIL_TEXT}Failed!${CLEAR_TEXT}\\n\"\n\n        # Display the error type.\n        echo -e \"${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}NETWORK CONFIGURATION ERROR.${CLEAR_TEXT}\"\n\n        # Display the error details.\n        echo -e \"${INFO_TEXT}Failed to establish a connection with Windows at '${RDP_IP}:${RDP_PORT}'.${CLEAR_TEXT}\"\n\n        # Display the suggested action(s).\n        echo \"--------------------------------------------------------------------------------\"\n        echo \"Please ensure Remote Desktop is configured on Windows as per the WinApps README.\"\n        echo -e \"Then you can try increasing the ${COMMAND_TEXT}PORT_TIMEOUT${CLEAR_TEXT} in ${COMMAND_TEXT}${CONFIG_PATH}${CLEAR_TEXT}.\"\n        echo \"--------------------------------------------------------------------------------\"\n\n        # Terminate the script.\n        return \"$EC_BAD_PORT\"\n    fi\n\n    # Print feedback.\n    echo -e \"${DONE_TEXT}Done!${CLEAR_TEXT}\"\n}\n\n# Name: 'waCheckRDPAccess'\n# Role: Tests if Windows is accessible via RDP.\nfunction waCheckRDPAccess() {\n    # Print feedback.\n    echo -n \"Attempting to establish a Remote Desktop connection with Windows... \"\n\n    # Declare variables.\n    local FREERDP_LOG=\"\"  # Stores the path of the FreeRDP log file.\n    local FREERDP_PROC=\"\" # Stores the FreeRDP process ID.\n    local ELAPSED_TIME=\"\" # Stores the time counter.\n\n    # Log file path.\n    FREERDP_LOG=\"${USER_APPDATA_PATH}/FreeRDP_Test_$(date +'%Y%m%d_%H%M_%N').log\"\n\n    # Ensure the output directory exists.\n    mkdir -p \"$USER_APPDATA_PATH\"\n\n    # Remove existing 'FreeRDP Connection Test' file.\n    rm -f \"$TEST_PATH\"\n\n    # This command should create a file on the host filesystem before terminating the RDP session. This command is silently executed as a background process.\n    # If the file is created, it means Windows received the command via FreeRDP successfully and can read and write to the Linux home folder.\n    # Note: The following final line is expected within the log, indicating successful execution of the 'tsdiscon' command and termination of the RDP session.\n    # [INFO][com.freerdp.core] - [rdp_print_errinfo]: ERRINFO_LOGOFF_BY_USER (0x0000000C):The disconnection was initiated by the user logging off their session on the server.\n    # shellcheck disable=SC2140,SC2027,SC2086 # Disable warnings regarding unquoted strings.\n    $FREERDP_COMMAND \\\n        $RDP_FLAGS_NON_WINDOWS \\\n        /cert:tofu \\\n        /d:\"$RDP_DOMAIN\" \\\n        /u:\"$RDP_USER\" \\\n        ${RDP_PASSWORD_ARG:+\"$RDP_PASSWORD_ARG\"} \\\n        /scale:\"$RDP_SCALE\" \\\n        +auto-reconnect \\\n        +home-drive \\\n        /app:program:\"C:\\Windows\\System32\\cmd.exe\",cmd:\"/C type NUL > $TEST_PATH_WIN && tsdiscon\" \\\n        /v:\"$RDP_IP\" &>\"$FREERDP_LOG\" &\n\n    # Store the FreeRDP process ID.\n    FREERDP_PROC=$!\n\n    # Initialise the time counter.\n    ELAPSED_TIME=0\n\n    # Wait a maximum of $RDP_TIMEOUT seconds for the background process to complete.\n    while [ \"$ELAPSED_TIME\" -lt \"$RDP_TIMEOUT\" ]; do\n        # Check if the FreeRDP process is complete or if the test file exists.\n        if ! ps -p \"$FREERDP_PROC\" &>/dev/null || [ -f \"$TEST_PATH\" ]; then\n            break\n        fi\n\n        # Wait for 5 seconds.\n        sleep 5\n        ELAPSED_TIME=$((ELAPSED_TIME + 5))\n    done\n\n    # Check if FreeRDP process is not complete.\n    if ps -p \"$FREERDP_PROC\" &>/dev/null; then\n        # SIGKILL FreeRDP.\n        kill -9 \"$FREERDP_PROC\" &>/dev/null\n    fi\n\n    # Check if test file does not exist.\n    if ! [ -f \"$TEST_PATH\" ]; then\n        # Complete the previous line.\n        echo -e \"${FAIL_TEXT}Failed!${CLEAR_TEXT}\\n\"\n\n        # Display the error type.\n        echo -e \"${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}REMOTE DESKTOP PROTOCOL FAILURE.${CLEAR_TEXT}\"\n\n        # Display the error details.\n        echo -e \"${INFO_TEXT}FreeRDP failed to establish a connection with Windows.${CLEAR_TEXT}\"\n\n        # Display the suggested action(s).\n        echo \"--------------------------------------------------------------------------------\"\n        echo -e \"Please view the log at ${COMMAND_TEXT}${FREERDP_LOG}${CLEAR_TEXT}.\"\n        echo \"Troubleshooting Tips:\"\n        echo \"  - Ensure the user is logged out of Windows prior to initiating the WinApps installation.\"\n        echo \"  - Ensure the credentials within the WinApps configuration file are correct.\"\n        echo -e \"  - Utilise a new certificate by removing relevant certificate(s) in ${COMMAND_TEXT}${HOME}/.config/freerdp/server${CLEAR_TEXT}.\"\n        echo -e \"  - Try increasing the ${COMMAND_TEXT}RDP_TIMEOUT${CLEAR_TEXT} in ${COMMAND_TEXT}${CONFIG_PATH}${CLEAR_TEXT}.\"\n        echo \"  - If using 'libvirt', ensure the Windows VM is correctly named as specified within the README.\"\n        echo \"  - If using 'libvirt', ensure 'Remote Desktop' is enabled within the Windows VM.\"\n        echo \"  - If using 'libvirt', ensure you have merged 'RDPApps.reg' into the Windows VM's registry.\"\n        echo \"  - If using 'libvirt', try logging into and back out of the Windows VM within 'virt-manager' prior to initiating the WinApps installation.\"\n        echo \"--------------------------------------------------------------------------------\"\n\n        # Terminate the script.\n        return \"$EC_RDP_FAIL\"\n    else\n        # Remove the temporary test file.\n        rm -f \"$TEST_PATH\"\n    fi\n\n    # Print feedback.\n    echo -e \"${DONE_TEXT}Done!${CLEAR_TEXT}\"\n}\n\n# Name: 'waFindInstalled'\n# Role: Identifies installed applications on Windows.\nfunction waFindInstalled() {\n    # Print feedback.\n    echo -n \"Checking for installed Windows applications... \"\n\n    # Declare variables.\n    local FREERDP_LOG=\"\"  # Stores the path of the FreeRDP log file.\n    local FREERDP_PROC=\"\" # Stores the FreeRDP process ID.\n    local ELAPSED_TIME=\"\" # Stores the time counter.\n\n    # Log file path.\n    FREERDP_LOG=\"${USER_APPDATA_PATH}/FreeRDP_Scan_$(date +'%Y%m%d_%H%M_%N').log\"\n\n    # Make the output directory if required.\n    mkdir -p \"$USER_APPDATA_PATH\"\n\n    # Remove temporary files from previous WinApps installations.\n    rm -f \"$BATCH_SCRIPT_PATH\" \"$TMP_INST_FILE_PATH\" \"$INST_FILE_PATH\" \"$PS_SCRIPT_HOME_PATH\" \"$DETECTED_FILE_PATH\"\n\n    # Copy PowerShell script to a directory within the user's home folder.\n    # This will enable the PowerShell script to be accessed and executed by Windows.\n    cp \"$PS_SCRIPT_PATH\" \"$PS_SCRIPT_HOME_PATH\"\n\n    # Enumerate over each officially supported application.\n    for APPLICATION in ./apps/*; do\n        # Extract the name of the application from the absolute path of the folder.\n        APPLICATION=\"$(basename \"$APPLICATION\")\"\n\n        if [[ \"$APPLICATION\" == \"ms-office-protocol-handler.desktop\" ]]; then\n            continue\n        fi\n\n        # Source 'Info' File Containing:\n        # - The Application Name          (FULL_NAME)\n        # - The Shortcut Name             (NAME)\n        # - Application Categories        (CATEGORIES)\n        # - Executable Path               (WIN_EXECUTABLE)\n        # - Supported MIME Types          (MIME_TYPES)\n        # - Application Icon              (ICON)\n        # shellcheck source=/dev/null # Exclude this file from being checked by ShellCheck.\n        source \"./apps/${APPLICATION}/info\"\n\n        # Append commands to batch file.\n        echo \"IF EXIST \\\"${WIN_EXECUTABLE}\\\" ECHO ${APPLICATION}^|^|^|${WIN_EXECUTABLE} >> ${TMP_INST_FILE_PATH_WIN}\" >>\"$BATCH_SCRIPT_PATH\"\n    done\n\n    # Append a command to the batch script to run the PowerShell script and store its output in the 'detected' file.\n    # shellcheck disable=SC2129 # Silence warning regarding repeated redirects.\n    echo \"powershell.exe -ExecutionPolicy Bypass -File ${PS_SCRIPT_HOME_PATH_WIN} > ${DETECTED_FILE_PATH_WIN}\" >>\"$BATCH_SCRIPT_PATH\"\n\n    # Append a command to the batch script to rename the temporary file containing the names of all detected officially supported applications.\n    echo \"RENAME ${TMP_INST_FILE_PATH_WIN} installed\" >>\"$BATCH_SCRIPT_PATH\"\n\n    # Append a command to the batch script to terminate the remote desktop session once all previous commands are complete.\n    echo \"tsdiscon\" >>\"$BATCH_SCRIPT_PATH\"\n\n    # Silently execute the batch script within Windows in the background (Log Output To File)\n    # Note: The following final line is expected within the log, indicating successful execution of the 'tsdiscon' command and termination of the RDP session.\n    # [INFO][com.freerdp.core] - [rdp_print_errinfo]: ERRINFO_LOGOFF_BY_USER (0x0000000C):The disconnection was initiated by the user logging off their session on the server.\n    # shellcheck disable=SC2140,SC2027,SC2086 # Disable warnings regarding unquoted strings.\n    $FREERDP_COMMAND \\\n        $RDP_FLAGS_NON_WINDOWS \\\n        /cert:tofu \\\n        /d:\"$RDP_DOMAIN\" \\\n        /u:\"$RDP_USER\" \\\n        ${RDP_PASSWORD_ARG:+\"$RDP_PASSWORD_ARG\"} \\\n        /scale:\"$RDP_SCALE\" \\\n        +auto-reconnect \\\n        +home-drive \\\n        /app:program:\"C:\\Windows\\System32\\cmd.exe\",cmd:\"/C \"$BATCH_SCRIPT_PATH_WIN\"\" \\\n        /v:\"$RDP_IP\" &>\"$FREERDP_LOG\" &\n\n    # Store the FreeRDP process ID.\n    FREERDP_PROC=$!\n\n    # Initialise the time counter.\n    ELAPSED_TIME=0\n\n    # Wait a maximum of $APP_SCAN_TIMEOUT seconds for the batch script to finish running.\n    while [ $ELAPSED_TIME -lt \"$APP_SCAN_TIMEOUT\" ]; do\n        # Check if the FreeRDP process is complete or if the 'installed' file exists.\n        if ! ps -p \"$FREERDP_PROC\" &>/dev/null || [ -f \"$INST_FILE_PATH\" ]; then\n            break\n        fi\n\n        # Wait for 5 seconds.\n        sleep 5\n        ELAPSED_TIME=$((ELAPSED_TIME + 5))\n    done\n\n    # Check if the FreeRDP process is not complete.\n    if ps -p \"$FREERDP_PROC\" &>/dev/null; then\n        # SIGKILL FreeRDP.\n        kill -9 \"$FREERDP_PROC\" &>/dev/null\n    fi\n\n    # Check if test file does not exist.\n    if ! [ -f \"$INST_FILE_PATH\" ]; then\n        # Complete the previous line.\n        echo -e \"${FAIL_TEXT}Failed!${CLEAR_TEXT}\\n\"\n\n        # Display the error type.\n        echo -e \"${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}APPLICATION QUERY FAILURE.${CLEAR_TEXT}\"\n\n        # Display the error details.\n        echo -e \"${INFO_TEXT}Failed to query Windows for installed applications.${CLEAR_TEXT}\"\n\n        # Display the suggested action(s).\n        echo \"--------------------------------------------------------------------------------\"\n        echo -e \"Please view the log at ${COMMAND_TEXT}${FREERDP_LOG}${CLEAR_TEXT}.\"\n        echo -e \"You can try increasing the ${COMMAND_TEXT}APP_SCAN_TIMEOUT${CLEAR_TEXT} in ${COMMAND_TEXT}${CONFIG_PATH}${CLEAR_TEXT}.\"\n        echo \"--------------------------------------------------------------------------------\"\n\n        # Terminate the script.\n        return \"$EC_APPQUERY_FAIL\"\n    fi\n\n    # Print feedback.\n    echo -e \"${DONE_TEXT}Done!${CLEAR_TEXT}\"\n}\n\n# Name: 'waConfigureWindows'\n# Role: Create an application entry for launching Windows via Remote Desktop.\nfunction waConfigureWindows() {\n    # Print feedback.\n    echo -n \"Creating an application entry for Windows... \"\n\n    # Declare variables.\n    local WIN_BASH=\"\"    # Stores the bash script to launch a Windows RDP session.\n    local WIN_DESKTOP=\"\" # Stores the '.desktop' file to launch a Windows RDP session.\n\n    # Populate variables.\n    WIN_BASH=\"\\\n#!/usr/bin/env bash\n${BIN_PATH}/winapps windows\"\n    WIN_DESKTOP=\"\\\n[Desktop Entry]\nName=Windows\nExec=${BIN_PATH}/winapps windows %F\nTerminal=false\nType=Application\nIcon=${APPDATA_PATH}/icons/windows.svg\nStartupWMClass=Microsoft Windows\nComment=Microsoft Windows RDP Session\"\n\n    # Copy the 'Windows' icon.\n    $SUDO cp \"./install/windows.svg\" \"${APPDATA_PATH}/icons/windows.svg\"\n\n    # Write the desktop entry content to a file.\n    echo \"$WIN_DESKTOP\" | $SUDO tee \"${APP_PATH}/windows.desktop\" &>/dev/null\n\n    # Write the bash script to a file.\n    echo \"$WIN_BASH\" | $SUDO tee \"${BIN_PATH}/windows\" &>/dev/null\n\n    # Mark the bash script as executable.\n    $SUDO chmod a+x \"${BIN_PATH}/windows\"\n\n    # Print feedback.\n    echo -e \"${DONE_TEXT}Done!${CLEAR_TEXT}\"\n}\n\n# Name: 'waConfigureApp'\n# Role: Create application entries for a given application installed on Windows.\nfunction waConfigureApp() {\n    # Declare variables.\n    local APP_ICON=\"\"         # Stores the path to the application icon.\n    local APP_BASH=\"\"         # Stores the bash script used to launch the application.\n    local APP_DESKTOP_FILE=\"\" # Stores the '.desktop' file used to launch the application.\n\n    # Source 'Info' File Containing:\n    # - The Application Name          (FULL_NAME)\n    # - The Shortcut Name             (NAME)\n    # - Application Categories        (CATEGORIES)\n    # - Executable Path               (WIN_EXECUTABLE)\n    # - Supported MIME Types          (MIME_TYPES)\n    # - Application Icon              (ICON)\n    # shellcheck source=/dev/null # Exclude this file from being checked by ShellCheck.\n    source \"${APPDATA_PATH}/apps/${1}/info\"\n\n    # Determine path to application icon using arguments passed to function.\n    APP_ICON=\"${APPDATA_PATH}/apps/${1}/icon.${2}\"\n\n    # Determine the content of the bash script for the application.\n    APP_BASH=\"\\\n#!/usr/bin/env bash\n${BIN_PATH}/winapps ${1}\"\n\n    # Determine the content of the '.desktop' file for the application.\n    APP_DESKTOP_FILE=\"\\\n[Desktop Entry]\nName=${NAME}\nExec=${BIN_PATH}/winapps ${1} %F\nTerminal=false\nType=Application\nIcon=${APP_ICON}\nStartupWMClass=${FULL_NAME}\nComment=${FULL_NAME}\nCategories=${CATEGORIES}\nMimeType=${MIME_TYPES}\"\n\n    # Store the '.desktop' file for the application.\n    echo \"$APP_DESKTOP_FILE\" | $SUDO tee \"${APP_PATH}/${1}.desktop\" &>/dev/null\n\n    # Store the bash script for the application.\n    echo \"$APP_BASH\" | $SUDO tee \"${BIN_PATH}/${1}\" &>/dev/null\n\n    # Mark bash script as executable.\n    $SUDO chmod a+x \"${BIN_PATH}/${1}\"\n}\n\n# Name: 'waConfigureOfficiallySupported'\n# Role: Create application entries for officially supported applications installed on Windows.\nfunction waConfigureOfficiallySupported() {\n    # Declare variables.\n    local OSA_LIST=() # Stores a list of all officially supported applications installed on Windows.\n    local OFFICE_APPS=(\"access\" \"access-o365\" \"access-o365-x86\" \"access-x86\" \"adobe-cc\" \"acrobat9\" \"acrobat-x-pro\" \"aftereffects-cc\" \"audition-cc\" \"bridge-cc\" \"bridge-cc-x86\" \"bridge-cs6\" \"bridge-cs6-x86\" \"cmd\" \"dymo-connect\" \"excel\" \"excel-o365\" \"excel-o365-x86\" \"excel-x86\" \"excel-x86-2010\" \"explorer\" \"iexplorer\" \"illustrator-cc\" \"lightroom-cc\" \"linqpad8\" \"mirc\" \"mspaint\" \"onenote\" \"onenote-o365\" \"onenote-o365-x86\" \"onenote-x86\" \"outlook\" \"outlook-o365\" \"outlook-o365-x86\" \"powerbi\" \"powerbi-store\" \"powerpoint\" \"powerpoint-o365\" \"powerpoint-o365-x86\" \"powerpoint-x86\" \"publisher\" \"publisher-o365\" \"publisher-o365-x86\" \"publisher-x86\" \"project\" \"project-x86\" \"remarkable-desktop\" \"ssms20\" \"visual-studio-comm\" \"visual-studio-ent\" \"visual-studio-pro\" \"visio\" \"visio-x86\" \"word\" \"word-o365\" \"word-o365-x86\" \"word-x86\" \"word-x86-2010\")\n\n    # Read the list of officially supported applications that are installed on Windows into an array, returning an empty array if no such files exist.\n    readarray -t OSA_LIST < <(grep -v '^[[:space:]]*$' \"$INST_FILE_PATH\" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' 2>/dev/null || true)\n\n    # Create application entries for each officially supported application.\n    for OSA in \"${OSA_LIST[@]}\"; do\n        # Split the line by the '|||' delimiter\n        local APP_NAME=\"${OSA%%|||*}\"\n        local ACTUAL_WIN_EXECUTABLE=\"${OSA##*|||}\"\n\n        # If splitting failed for some reason, skip this line to be safe.\n        if [[ -z \"$APP_NAME\" || -z \"$ACTUAL_WIN_EXECUTABLE\" ]]; then\n            continue\n        fi\n\n        # Print feedback using the clean application name.\n        echo -n \"Creating an application entry for ${APP_NAME}... \"\n\n        # Copy the original, unmodified application assets.\n        # --no-preserve=mode is needed to avoid missing write permissions when copying from Nix store.\n        $SUDO cp -r --no-preserve=mode \"./apps/${APP_NAME}\" \"${APPDATA_PATH}/apps\"\n\n        local DESTINATION_INFO_FILE=\"${APPDATA_PATH}/apps/${APP_NAME}/info\"\n\n        # Sanitize the string using pure Bash. This is fast and safe.\n        local SED_SAFE_PATH=\"${ACTUAL_WIN_EXECUTABLE//&/\\\\&}\"\n        SED_SAFE_PATH=\"${SED_SAFE_PATH//\\\\/\\\\\\\\}\"\n\n        # Use the sanitized string to safely edit the file.\n        $SUDO sed -i \"s|^WIN_EXECUTABLE=.*|WIN_EXECUTABLE=\\\"${SED_SAFE_PATH}\\\"|\" \"$DESTINATION_INFO_FILE\"\n\n        # Configure the application using the clean name.\n        waConfigureApp \"$APP_NAME\" svg\n\n        # Check if the application is an Office app and copy the protocol handler.\n        if [[ \" ${OFFICE_APPS[*]} \" == *\" $APP_NAME \"* ]]; then\n            # Determine the target directory based on whether the installation is for the system or user.\n            if [[ \"$OPT_SYSTEM\" -eq 1 ]]; then\n                TARGET_DIR=\"$SYS_APP_PATH\"\n            else\n                TARGET_DIR=\"$USER_APP_PATH\"\n            fi\n\n            # Copy the protocol handler to the appropriate directory.\n            $SUDO cp \"./apps/ms-office-protocol-handler.desktop\" \"$TARGET_DIR/ms-office-protocol-handler.desktop\"\n        fi\n\n        # Print feedback.\n        echo -e \"${DONE_TEXT}Done!${CLEAR_TEXT}\"\n    done\n\n    # Delete 'install' file.\n    rm -f \"$INST_FILE_PATH\"\n}\n\n# Name: 'waConfigureApps'\n# Role: Allow the user to select which officially supported applications to configure.\nfunction waConfigureApps() {\n    # Declare variables.\n    local OSA_LIST=()      # Stores a list of all officially supported applications installed on Windows.\n    local APPS=()          # Stores a list of both the simplified and full names of each installed officially supported application.\n    local OPTIONS=()       # Stores a list of options presented to the user.\n    local APP_INSTALL=\"\"   # Stores the option selected by the user.\n    local SELECTED_APPS=() # Stores the officially supported applications selected by the user.\n    local TEMP_ARRAY=()    # Temporary array used for sorting elements of an array.\n    declare -A APP_DATA_MAP # Associative array to map short names back to their full data line.\n\n    # Read the list of officially supported applications that are installed on Windows into an array, returning an empty array if no such files exist.\n    # This will remove leading and trailing whitespace characters as well as ignore empty lines.\n    readarray -t OSA_LIST < <(grep -v '^[[:space:]]*$' \"$INST_FILE_PATH\" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' 2>/dev/null || true)\n\n    # Loop over each officially supported application installed on Windows.\n    for OSA in \"${OSA_LIST[@]}\"; do\n        # Source 'Info' File Containing:\n        # - The Application Name          (FULL_NAME)\n        # - The Shortcut Name             (NAME)\n        # - Application Categories        (CATEGORIES)\n        # - Executable Path               (WIN_EXECUTABLE)\n        # - Supported MIME Types          (MIME_TYPES)\n        # - Application Icon              (ICON)\n\n        # Split the line to get the clean application name\n        local APP_NAME=\"${OSA%%|||*}\"\n        local ACTUAL_WIN_EXECUTABLE=\"${OSA##*|||*}\"\n\n        # If splitting failed, skip this entry.\n        if [[ -z \"$APP_NAME\" ]]; then\n            continue\n        fi\n\n        # Use the clean APP_NAME to source the info file\n        # shellcheck source=/dev/null # Exclude this file from being checked by ShellCheck.\n        source \"./apps/${APP_NAME}/info\"\n\n        # Add both the simplified and full name of the application to an array.\n        APPS+=(\"${FULL_NAME} (${APP_NAME})\")\n\n        # Store the original data line in our map so we can retrieve it later.\n        APP_DATA_MAP[\"$APP_NAME\"]=\"$OSA\"\n\n        # Extract the executable file name (e.g. 'MyApp.exe') from the absolute path.\n        WIN_EXECUTABLE=\"${ACTUAL_WIN_EXECUTABLE##*\\\\}\"\n\n        # Trim any leading or trailing whitespace characters from the executable file name.\n        read -r WIN_EXECUTABLE <<<\"$(echo \"$WIN_EXECUTABLE\" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')\"\n\n        # Add the executable file name (in lowercase) to the array.\n        INSTALLED_EXES+=(\"${WIN_EXECUTABLE,,}\")\n    done\n\n    # Sort the 'APPS' array in alphabetical order.\n    IFS=$'\\n'\n    # shellcheck disable=SC2207 # Silence warnings regarding preferred use of 'mapfile' or 'read -a'.\n    TEMP_ARRAY=($(sort <<<\"${APPS[*]}\"))\n    unset IFS\n    APPS=(\"${TEMP_ARRAY[@]}\")\n\n    # Prompt user to select which officially supported applications to configure.\n    OPTIONS=(\n        \"Set up all detected officially supported applications\"\n        \"Choose specific officially supported applications to set up\"\n        \"Skip setting up any officially supported applications\"\n    )\n    inqMenu \"How would you like to handle officially supported applications?\" OPTIONS APP_INSTALL\n\n    # Remove unselected officially supported applications from the 'install' file.\n    if [[ $APP_INSTALL == \"Choose specific officially supported applications to set up\" ]]; then\n        inqChkBx \"Which officially supported applications would you like to set up?\" APPS SELECTED_APPS\n\n        # Clear/create the 'install' file.\n        echo \"\" >\"$INST_FILE_PATH\"\n\n        # Add each selected officially supported application back to the 'install' file.\n        for SELECTED_APP in \"${SELECTED_APPS[@]}\"; do\n            # Capture the substring within (but not including) the parentheses.\n            # This substring represents the officially supported application name (see above loop).\n            local SHORT_NAME=\"${SELECTED_APP##*(}\"\n            SHORT_NAME=\"${SHORT_NAME%%)}\"\n\n            # Use the map to find the original data line (e.g., \"word|||C:\\...\") and write it back.\n            echo \"${APP_DATA_MAP[$SHORT_NAME]}\" >>\"$INST_FILE_PATH\"\n        done\n    fi\n\n    # Configure selected (or all) officially supported applications.\n    if [[ $APP_INSTALL != \"Skip setting up any officially supported applications\" ]]; then\n        waConfigureOfficiallySupported\n    fi\n}\n\n# Name: 'waConfigureDetectedApps'\n# Role: Allow the user to select which detected applications to configure.\nfunction waConfigureDetectedApps() {\n    # Declare variables.\n    local APPS=()                   # Stores a list of both the simplified and full names of each detected application.\n    local EXE_FILENAME=\"\"           # Stores the executable filename of a given detected application.\n    local EXE_FILENAME_NOEXT=\"\"     # Stores the executable filename without the file extension of a given detected application.\n    local EXE_FILENAME_LOWERCASE=\"\" # Stores the executable filename of a given detected application in lowercase letters only.\n    local OPTIONS=()                # Stores a list of options presented to the user.\n    local APP_INSTALL=\"\"            # Stores the option selected by the user.\n    local SELECTED_APPS=()          # Detected applications selected by the user.\n    local APP_DESKTOP_FILE=\"\"       # Stores the '.desktop' file used to launch the application.\n    local TEMP_ARRAY=()             # Temporary array used for sorting elements of an array.\n\n    if [ -f \"$DETECTED_FILE_PATH\" ]; then\n        # On UNIX systems, lines are terminated with a newline character (\\n).\n        # On WINDOWS systems, lines are terminated with both a carriage return (\\r) and a newline (\\n) character.\n        # Remove all carriage returns (\\r) within the 'detected' file, as the file was written by Windows.\n        sed -i 's/\\r//g' \"$DETECTED_FILE_PATH\"\n\n        # Import the detected application information:\n        # - Application Names               (NAMES)\n        # - Application Icons in base64     (ICONS)\n        # - Application Executable Paths    (EXES)\n        # shellcheck source=/dev/null # Exclude this file from being checked by ShellCheck.\n        source \"$DETECTED_FILE_PATH\"\n\n        # shellcheck disable=SC2153 # Silence warnings regarding possible misspellings.\n        for INDEX in \"${!NAMES[@]}\"; do\n            # Extract the executable file name (e.g. 'MyApp.exe').\n            EXE_FILENAME=${EXES[$INDEX]##*\\\\}\n\n            # Convert the executable file name to lower-case (e.g. 'myapp.exe').\n            EXE_FILENAME_LOWERCASE=\"${EXE_FILENAME,,}\"\n\n            # Remove the file extension (e.g. 'MyApp').\n            EXE_FILENAME_NOEXT=\"${EXE_FILENAME%.*}\"\n\n            # Check if the executable was previously configured as part of setting up officially supported applications.\n            if [[ \" ${INSTALLED_EXES[*]} \" != *\" ${EXE_FILENAME_LOWERCASE} \"* ]]; then\n                # If not previously configured, add the application to the list of detected applications.\n                APPS+=(\"${NAMES[$INDEX]} (${EXE_FILENAME_NOEXT})\")\n            fi\n        done\n\n        # Sort the 'APPS' array in alphabetical order.\n        IFS=$'\\n'\n        # shellcheck disable=SC2207 # Silence warnings regarding preferred use of 'mapfile' or 'read -a'.\n        TEMP_ARRAY=($(sort <<<\"${APPS[*]}\"))\n        unset IFS\n        APPS=(\"${TEMP_ARRAY[@]}\")\n\n        # Prompt user to select which other detected applications to configure.\n        OPTIONS=(\n            \"Set up all detected applications\"\n            \"Select which applications to set up\"\n            \"Do not set up any applications\"\n        )\n        inqMenu \"How would you like to handle other detected applications?\" OPTIONS APP_INSTALL\n\n        # Store selected detected applications.\n        if [[ $APP_INSTALL == \"Select which applications to set up\" ]]; then\n            inqChkBx \"Which other applications would you like to set up?\" APPS SELECTED_APPS\n        elif [[ $APP_INSTALL == \"Set up all detected applications\" ]]; then\n            for APP in \"${APPS[@]}\"; do\n                SELECTED_APPS+=(\"$APP\")\n            done\n        fi\n\n        for SELECTED_APP in \"${SELECTED_APPS[@]}\"; do\n            # Capture the substring within (but not including) the parentheses.\n            # This substring represents the executable filename without the file extension (see above loop).\n            EXE_FILENAME_NOEXT=\"${SELECTED_APP##*(}\"\n            EXE_FILENAME_NOEXT=\"${EXE_FILENAME_NOEXT%%)}\"\n\n            # Capture the substring prior to the space and parentheses.\n            # This substring represents the detected application name (see above loop).\n            PROGRAM_NAME=\"${SELECTED_APP% (*}\"\n\n            # Loop through all detected applications to find the detected application being processed.\n            for INDEX in \"${!NAMES[@]}\"; do\n                # Check for a matching detected application entry.\n                if [[ ${NAMES[$INDEX]} == \"$PROGRAM_NAME\" ]] && [[ ${EXES[$INDEX]} == *\"\\\\$EXE_FILENAME_NOEXT\"* ]]; then\n                    # Print feedback.\n                    echo -n \"Creating an application entry for ${PROGRAM_NAME}... \"\n\n                    # Create directory to store application icon and information.\n                    $SUDO mkdir -p \"${APPDATA_PATH}/apps/${EXE_FILENAME_NOEXT}\"\n\n                    # Determine the content of the '.desktop' file for the application.\n                    APP_DESKTOP_FILE=\"\\\n# GNOME Shortcut Name\nNAME=\\\"${PROGRAM_NAME}\\\"\n# Used for Descriptions and Window Class\nFULL_NAME=\\\"${PROGRAM_NAME}\\\"\n# Path to executable inside Windows\nWIN_EXECUTABLE=\\\"${EXES[$INDEX]}\\\"\n# GNOME Categories\nCATEGORIES=\\\"WinApps\\\"\n# GNOME MIME Types\nMIME_TYPES=\\\"\\\"\"\n\n                    # Store the '.desktop' file for the application.\n                    echo \"$APP_DESKTOP_FILE\" | $SUDO tee \"${APPDATA_PATH}/apps/${EXE_FILENAME_NOEXT}/info\" &>/dev/null\n\n                    # Write application icon to file.\n                    echo \"${ICONS[$INDEX]}\" | base64 -d | $SUDO tee \"${APPDATA_PATH}/apps/${EXE_FILENAME_NOEXT}/icon.png\" &>/dev/null\n\n                    # Configure the application.\n                    waConfigureApp \"$EXE_FILENAME_NOEXT\" png\n\n                    # Print feedback.\n                    echo -e \"${DONE_TEXT}Done!${CLEAR_TEXT}\"\n                fi\n            done\n        done\n    fi\n}\n\n# Name: 'waInstall'\n# Role: Installs WinApps.\nfunction waInstall() {\n    # Print feedback.\n    echo -e \"${BOLD_TEXT}Installing WinApps.${CLEAR_TEXT}\"\n\n    # Check for existing conflicting WinApps installations.\n    waCheckExistingInstall\n\n    # Load the WinApps configuration file.\n    waLoadConfig\n\n    # Check for missing dependencies.\n    waCheckInstallDependencies\n\n    # Update $RDP_SCALE.\n    waFixScale\n\n    # Append additional FreeRDP flags if required.\n    if [[ -n $RDP_FLAGS ]]; then\n        FREERDP_COMMAND=\"${FREERDP_COMMAND} ${RDP_FLAGS}\"\n    fi\n\n    # If using 'docker' or 'podman', set RDP_IP to localhost.\n    if [ \"$WAFLAVOR\" = \"docker\" ] || [ \"$WAFLAVOR\" = \"podman\" ]; then\n        RDP_IP=\"$DOCKER_IP\"\n    fi\n\n    # If using podman backend, modify the FreeRDP command to enter a new namespace.\n    if [ \"$WAFLAVOR\" = \"podman\" ]; then\n        FREERDP_COMMAND=\"podman unshare --rootless-netns ${FREERDP_COMMAND}\"\n    fi\n\n    if [ \"$WAFLAVOR\" = \"docker\" ] || [ \"$WAFLAVOR\" = \"podman\" ]; then\n        # Check if Windows is powered on.\n        waCheckContainerRunning\n    elif [ \"$WAFLAVOR\" = \"libvirt\" ]; then\n        # Verify the current user's group membership.\n        waCheckGroupMembership\n\n        # Check if the Windows VM is powered on.\n        waCheckVMRunning\n    elif [ \"$WAFLAVOR\" = \"manual\" ]; then\n        waCheckPortOpen\n    else\n        # Display the error type.\n        echo -e \"${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}INVALID WINAPPS BACKEND.${CLEAR_TEXT}\"\n\n        # Display the error details.\n        echo -e \"${INFO_TEXT}An invalid WinApps backend '${WAFLAVOR}' was specified.${CLEAR_TEXT}\"\n\n        # Display the suggested action(s).\n        echo \"--------------------------------------------------------------------------------\"\n        echo -e \"Please ensure 'WAFLAVOR' is set to 'docker', 'podman' or 'libvirt' in ${COMMAND_TEXT}${CONFIG_PATH}${CLEAR_TEXT}.\"\n        echo \"--------------------------------------------------------------------------------\"\n\n        # Terminate the script.\n        return \"$EC_INVALID_FLAVOR\"\n    fi\n\n    # Check if the RDP port on Windows is open.\n    waCheckPortOpen\n\n    # Test RDP access to Windows.\n    waCheckRDPAccess\n\n    # Create required directories.\n    $SUDO mkdir -p \"$BIN_PATH\"\n    $SUDO mkdir -p \"$APP_PATH\"\n    $SUDO mkdir -p \"$APPDATA_PATH/apps\"\n    $SUDO mkdir -p \"$APPDATA_PATH/icons\"\n\n    # Check for installed applications.\n    waFindInstalled\n\n    # Install the WinApps bash scripts.\n    $SUDO ln -sf \"${SOURCE_PATH}/bin/winapps\" \"${BIN_PATH}/winapps\"\n    $SUDO ln -sf \"${SOURCE_PATH}/setup.sh\" \"${BIN_PATH}/winapps-setup\"\n\n    # Configure the Windows RDP session application launcher.\n    waConfigureWindows\n\n    if [ \"$OPT_AOSA\" -eq 1 ]; then\n        # Automatically configure all officially supported applications.\n        waConfigureOfficiallySupported\n    else\n        # Configure officially supported applications.\n        waConfigureApps\n\n        # Configure other detected applications.\n        waConfigureDetectedApps\n    fi\n\n    # Ensure BIN_PATH is on PATH\n    waEnsureOnPath\n\n    # Print feedback.\n    echo -e \"${SUCCESS_TEXT}INSTALLATION COMPLETE.${CLEAR_TEXT}\"\n}\n\n# Name: 'waEnsureOnPath'\n# Role: Ensures that $BIN_PATH is on $PATH.\nfunction waEnsureOnPath() {\n    if [[ \":$PATH:\" != *\":$BIN_PATH:\"* ]]; then\n        echo -e \"${WARNING_TEXT}[WARNING]${CLEAR_TEXT} It seems like '${BIN_PATH}' is not on PATH.\"\n        echo -e \"${WARNING_TEXT}[WARNING]${CLEAR_TEXT} You can add it by running:\"\n        # shellcheck disable=SC2086\n        echo -e \"${WARNING_TEXT}[WARNING]${CLEAR_TEXT}   - For Bash: ${COMMAND_TEXT}echo 'export PATH=\"${BIN_PATH}:\\$PATH\"' >> ~/.bashrc && source ~/.bashrc${CLEAR_TEXT}\"\n        # shellcheck disable=SC2086\n        echo -e \"${WARNING_TEXT}[WARNING]${CLEAR_TEXT}   - For ZSH: ${COMMAND_TEXT}echo 'export PATH=\"${BIN_PATH}:\\$PATH\"' >> ~/.zshrc && source ~/.zshrc${CLEAR_TEXT}\"\n        echo -e \"${WARNING_TEXT}[WARNING]${CLEAR_TEXT} Make sure to restart your Terminal afterwards.\\n\"\n    fi\n}\n\n# Name: 'waUninstall'\n# Role: Uninstalls WinApps.\nfunction waUninstall() {\n\n    # Print feedback.\n    [ \"$OPT_SYSTEM\" -eq 1 ] && echo -e \"${BOLD_TEXT}REMOVING SYSTEM INSTALLATION.${CLEAR_TEXT}\"\n    [ \"$OPT_USER\" -eq 1 ] && echo -e \"${BOLD_TEXT}REMOVING USER INSTALLATION.${CLEAR_TEXT}\"\n\n    # Determine the target directory for the protocol handler based on the installation type.\n    if [[ \"$OPT_SYSTEM\" -eq 1 ]]; then\n        TARGET_DIR=\"$SYS_APP_PATH\"\n    else\n        TARGET_DIR=\"$USER_APP_PATH\"\n    fi\n\n    # Remove the 'ms-office-protocol-handler.desktop' file if it exists.\n    $SUDO rm -f \"$TARGET_DIR/ms-office-protocol-handler.desktop\"\n\n    # Declare variables.\n    local WINAPPS_DESKTOP_FILES=()    # Stores a list of '.desktop' file paths.\n    local WINAPPS_APP_BASH_SCRIPTS=() # Stores a list of bash script paths.\n    local DESKTOP_FILE_NAME=\"\"        # Stores the name of the '.desktop' file for the application.\n    local BASH_SCRIPT_NAME=\"\"         # Stores the name of the application.\n\n    # Remove the 'WinApps' bash scripts.\n    $SUDO rm -f \"${BIN_PATH}/winapps\"\n    $SUDO rm -f \"${BIN_PATH}/winapps-setup\"\n\n    # Remove WinApps configuration data, temporary files and logs.\n    rm -rf \"$USER_APPDATA_PATH\"\n\n    # Remove application icons and shortcuts.\n    $SUDO rm -rf \"$APPDATA_PATH\"\n\n    # Store '.desktop' files containing \"${BIN_PATH}/winapps\" in an array, returning an empty array if no such files exist.\n    readarray -t WINAPPS_DESKTOP_FILES < <(grep -l -d skip \"${BIN_PATH}/winapps\" \"${APP_PATH}/\"* 2>/dev/null || true)\n\n    # Remove each '.desktop' file.\n    for DESKTOP_FILE_PATH in \"${WINAPPS_DESKTOP_FILES[@]}\"; do\n        # Trim leading and trailing whitespace from '.desktop' file path.\n        DESKTOP_FILE_PATH=$(echo \"$DESKTOP_FILE_PATH\" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')\n\n        # Extract the file name.\n        DESKTOP_FILE_NAME=$(basename \"$DESKTOP_FILE_PATH\" | sed 's/\\.[^.]*$//')\n\n        # Print feedback.\n        echo -n \"Removing '.desktop' file for '${DESKTOP_FILE_NAME}'... \"\n\n        # Delete the file.\n        $SUDO rm \"$DESKTOP_FILE_PATH\"\n\n        # Print feedback.\n        echo -e \"${DONE_TEXT}Done!${CLEAR_TEXT}\"\n    done\n\n    # Store the paths of bash scripts calling 'WinApps' to launch specific applications in an array, returning an empty array if no such files exist.\n    readarray -t WINAPPS_APP_BASH_SCRIPTS < <(grep -l -d skip \"${BIN_PATH}/winapps\" \"${BIN_PATH}/\"* 2>/dev/null || true)\n\n    # Remove each bash script.\n    for BASH_SCRIPT_PATH in \"${WINAPPS_APP_BASH_SCRIPTS[@]}\"; do\n        # Trim leading and trailing whitespace from bash script path.\n        BASH_SCRIPT_PATH=$(echo \"$BASH_SCRIPT_PATH\" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')\n\n        # Extract the file name.\n        BASH_SCRIPT_NAME=$(basename \"$BASH_SCRIPT_PATH\" | sed 's/\\.[^.]*$//')\n\n        # Print feedback.\n        echo -n \"Removing bash script for '${BASH_SCRIPT_NAME}'... \"\n\n        # Delete the file.\n        $SUDO rm \"$BASH_SCRIPT_PATH\"\n\n        # Print feedback.\n        echo -e \"${DONE_TEXT}Done!${CLEAR_TEXT}\"\n    done\n\n    # Print caveats.\n    echo -e \"\\n${INFO_TEXT}Please note that your WinApps configuration and the WinApps source code were not removed.${CLEAR_TEXT}\"\n    echo -e \"${INFO_TEXT}You can remove these manually by running:${CLEAR_TEXT}\"\n    echo -e \"${COMMAND_TEXT}rm -r $(dirname \"$CONFIG_PATH\")${CLEAR_TEXT}\"\n    echo -e \"${COMMAND_TEXT}rm -r ${SOURCE_PATH}${CLEAR_TEXT}\\n\"\n\n    # Print feedback.\n    echo -e \"${SUCCESS_TEXT}UNINSTALLATION COMPLETE.${CLEAR_TEXT}\"\n}\n\n# Name: 'waAddApps'\n# Role: Adds new applications to an existing WinApps installation.\nfunction waAddApps() {\n    # Print feedback.\n    echo -e \"${BOLD_TEXT}Adding new applications to existing WinApps installation.${CLEAR_TEXT}\"\n\n    # Load the WinApps configuration file.\n    waLoadConfig\n\n    # Check for missing dependencies.\n    waCheckInstallDependencies\n\n    # Update $RDP_SCALE.\n    waFixScale\n\n    # Append additional FreeRDP flags if required.\n    if [[ -n $RDP_FLAGS ]]; then\n        FREERDP_COMMAND=\"${FREERDP_COMMAND} ${RDP_FLAGS}\"\n    fi\n\n    # If using 'docker' or 'podman', set RDP_IP to localhost.\n    if [ \"$WAFLAVOR\" = \"docker\" ] || [ \"$WAFLAVOR\" = \"podman\" ]; then\n        RDP_IP=\"$DOCKER_IP\"\n    fi\n\n    # If using podman backend, modify the FreeRDP command to enter a new namespace.\n    if [ \"$WAFLAVOR\" = \"podman\" ]; then\n        FREERDP_COMMAND=\"podman unshare --rootless-netns ${FREERDP_COMMAND}\"\n    fi\n\n    if [ \"$WAFLAVOR\" = \"docker\" ] || [ \"$WAFLAVOR\" = \"podman\" ]; then\n        # Check if Windows is powered on.\n        waCheckContainerRunning\n    elif [ \"$WAFLAVOR\" = \"libvirt\" ]; then\n        # Verify the current user's group membership.\n        waCheckGroupMembership\n\n        # Check if the Windows VM is powered on.\n        waCheckVMRunning\n    elif [ \"$WAFLAVOR\" = \"manual\" ]; then\n        waCheckPortOpen\n    else\n        # Display the error type.\n        echo -e \"${ERROR_TEXT}ERROR:${CLEAR_TEXT} ${BOLD_TEXT}INVALID WINAPPS BACKEND.${CLEAR_TEXT}\"\n\n        # Display the error details.\n        echo -e \"${INFO_TEXT}An invalid WinApps backend '${WAFLAVOR}' was specified.${CLEAR_TEXT}\"\n\n        # Display the suggested action(s).\n        echo \"--------------------------------------------------------------------------------\"\n        echo -e \"Please ensure 'WAFLAVOR' is set to 'docker', 'podman' or 'libvirt' in ${COMMAND_TEXT}${CONFIG_PATH}${CLEAR_TEXT}.\"\n        echo \"--------------------------------------------------------------------------------\"\n\n        # Terminate the script.\n        return \"$EC_INVALID_FLAVOR\"\n    fi\n\n    # Check if the RDP port on Windows is open.\n    waCheckPortOpen\n\n    # Test RDP access to Windows.\n    waCheckRDPAccess\n\n    # Check for installed applications.\n    waFindInstalled\n\n    # Configure officially supported applications.\n    waConfigureApps\n\n    # Configure other detected applications.\n    waConfigureDetectedApps\n    # Print feedback.\n    echo -e \"${SUCCESS_TEXT}ADDING NEW APPS COMPLETE.${CLEAR_TEXT}\"\n}\n\n\n\n### SEQUENTIAL LOGIC ###\n# Welcome the user.\necho -e \"${BOLD_TEXT}\\\n################################################################################\n#                                                                              #\n#                            WinApps Install Wizard                            #\n#                                                                              #\n################################################################################\n${CLEAR_TEXT}\"\n\n# Check dependencies for the script.\nwaCheckScriptDependencies\n\n# Source the contents of 'inquirer.sh'.\nwaGetInquirer\n\n# Sanitise and parse the user input.\nwaCheckInput \"$@\"\n\n# Configure paths and permissions.\nwaConfigurePathsAndPermissions\n\n# Get the source code\nwaGetSourceCode\n# Install or uninstall WinApps.\nif [ \"$OPT_UNINSTALL\" -eq 1 ]; then\n    waUninstall\nelif [ \"$OPT_ADD_APPS\" -eq 1 ]; then\n    waAddApps\nelse\n    waInstall\nfi\n\nexit 0\n"
  }
]