├── .editorconfig
├── .github
├── dependabot.yml
├── rustc-problem-matcher.json
├── test-problem-matcher.json
└── workflows
│ ├── ci.yml
│ └── dependency-review.yml
├── .gitignore
├── .markdownlint.yaml
├── .prettierrc.toml
├── .run
├── Run AquariWM (Wayland).run.xml
└── Run AquariWM (X11).run.xml
├── .rustfmt.toml
├── .trunk
├── .gitignore
└── trunk.yaml
├── CONTRIBUTING.md
├── Cargo.toml
├── LICENSE
├── README.md
├── derive-extras
├── Cargo.toml
└── src
│ ├── lib.rs
│ └── numbers_to_words.rs
├── publish-docs
├── rust-toolchain.toml
└── src
├── cli.rs
├── display_server.rs
├── display_server
├── wayland.rs
├── wayland
│ ├── grabs.rs
│ ├── grabs
│ │ ├── move_grab.rs
│ │ └── resize_grab.rs
│ ├── input.rs
│ └── state.rs
├── x11.rs
└── x11
│ ├── testing.rs
│ └── util.rs
├── layout.rs
├── layout
├── implementations.rs
├── implementations
│ ├── iter.rs
│ └── node_changes.rs
└── managers.rs
├── main.rs
└── state.rs
/.editorconfig:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at https://mozilla.org/MPL/2.0/.
4 |
5 | # What is this? EditorConfig is a file which many text editors natively support, and many others
6 | # have plugins available to achieve the same, which tells a text editor universal settings that it
7 | # should use when editing a particular project. This is useful so that a style guide can be
8 | # maintained throughout the project.
9 |
10 | # This declares that this is the .editorconfig file for the whole project, i.e. the root.
11 | root = true
12 |
13 | [*]
14 | indent_style = tab
15 | indent_size = 4
16 | end_of_line = lf
17 | charset = utf-8
18 | trim_trailing_whitespace = true
19 | insert_final_newline = true
20 | max_line_length = 120
21 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at https://mozilla.org/MPL/2.0/.
4 |
5 | # Please see the documentation for all Dependabot workflow configuration options:
6 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
7 |
8 | version: 2
9 | updates:
10 |
11 | # Maintain Cargo dependencies for security updates
12 | - package-ecosystem: "cargo"
13 | directory: "/"
14 | schedule:
15 | interval: "daily"
16 |
17 | # Maintain GitHub Actions dependencies for security updates
18 | - package-ecosystem: "github-actions"
19 | directory: "/"
20 | schedule:
21 | interval: "daily"
22 |
--------------------------------------------------------------------------------
/.github/rustc-problem-matcher.json:
--------------------------------------------------------------------------------
1 | {
2 | "problemMatcher": [
3 | {
4 | "owner": "rustc",
5 | "severity": "warning",
6 | "pattern": [
7 | {
8 | "regexp": "^(warning|warn|error|note)(?:\\[(\\w+)\\])?:\\s+(.*)$",
9 | "severity": 1,
10 | "code": 2,
11 | "message": 3
12 | },
13 | {
14 | "regexp": "^\\s+(?:-->\\s+((?:[\\w\\-.]+\\/)*)([\\w\\-.]+):(\\d+):(\\d+)$)?",
15 | "fromPath": 1,
16 | "file": 2,
17 | "line": 3,
18 | "column": 4
19 | }
20 | ]
21 | }
22 | ]
23 | }
24 |
--------------------------------------------------------------------------------
/.github/test-problem-matcher.json:
--------------------------------------------------------------------------------
1 | {
2 | "problemMatcher": [
3 | {
4 | "owner": "rust-tests",
5 | "severity": "error",
6 | "pattern": [
7 | {
8 | "regexp": "^----[\\w\\s:]+----$"
9 | },
10 | {
11 | "regexp": "^(.*)$",
12 | "message": 1
13 | },
14 | {
15 | "regexp": "^.*$"
16 | },
17 | {
18 | "regexp": "^(?:.*,\\s+((?:[\\w\\-.]+\\/)*)([\\w\\-.]+):(\\d+):(\\d+)$)?",
19 | "fromPath": 1,
20 | "file": 2,
21 | "line": 3,
22 | "column": 4
23 | }
24 | ]
25 | }
26 | ]
27 | }
28 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at https://mozilla.org/MPL/2.0/.
4 |
5 | # Runs checks, tests, code analysis, auto-formats code, applies recommended
6 | # fixes, and publishes documentation.
7 | name: Continuous integration
8 |
9 | on: [ push, pull_request ]
10 |
11 | permissions:
12 | contents: write
13 | pages: write
14 | id-token: write
15 |
16 | jobs:
17 | cancel-runs:
18 | runs-on: ubuntu-latest
19 |
20 | steps:
21 | # If this workflow is already in progress or queued, we cancel it; we are
22 | # about to do the exact same tests and documentation on potentially new
23 | # code, so it is pointless to continue them.
24 | - name: Cancel existing workflow runs
25 | uses: styfle/cancel-workflow-action@0.12.0
26 | with:
27 | access_token: ${{ github.token }}
28 |
29 | # Automatically applies suggested fixes from `clippy` and formats with rustfmt.
30 | fix-n-format:
31 | runs-on: ubuntu-latest
32 | continue-on-error: true
33 |
34 | outputs:
35 | # The ID of the commit made for the fixes, or, if no fixes were applied,
36 | # the commit that triggered the workflow.
37 | commit-id: ${{ steps.commit-id.outputs.COMMIT_ID }}
38 |
39 | steps:
40 | # Check out (a.k.a. clones) the AquariWM repository.
41 | - name: Checkout AquariWM
42 | uses: actions/checkout@v4
43 |
44 | # Install `libsystemd-dev`, `libudev-dev`, `libseat-dev`, `libinput-dev`, and
45 | # `libxkbcommon-dev` to satisfy dependencies.
46 | - name: Install system libraries required to build AquariWM
47 | run: |
48 | sudo apt-get update
49 | sudo apt-get install libsystemd-dev libudev-dev libseat-dev libinput-dev libxkbcommon-dev
50 |
51 | # Install the latest nightly release of the Rust toolchain.
52 | - name: Install latest nightly
53 | uses: actions-rs/toolchain@v1
54 | with:
55 | profile: minimal
56 | toolchain: nightly
57 | override: true
58 | components: clippy, rustfmt
59 |
60 | - name: Configure git credentials
61 | run: |
62 | git config user.name github-actions
63 | git config user.email github-actions@github.com
64 |
65 | # Apply fixes with `clippy`.
66 | - name: Apply recommended fixes
67 | uses: actions-rs/cargo@v1
68 | with:
69 | command: clippy
70 | args: --workspace --fix --color always
71 |
72 | - name: Commit clippy fix changes
73 | # Commit changes, or, if that fails, do nothing.
74 | run: |
75 | git pull origin ${{ github.ref }}
76 | git diff --quiet || echo "### Applied recommended clippy fixes." >> $GITHUB_STEP_SUMMARY
77 | git diff --quiet || (git commit -am "[CI${{ github.run_number}}] applied recommended clippy fixes" && echo "$(git log --format='%H' -n 1)" >> $GITHUB_STEP_SUMMARY && echo "$(git log --format='%H' -n 1)" >> .git-blame-ignore-revs)
78 |
79 | # Automatically format the code with `rustfmt`.
80 | - name: Format code with `rustfmt`
81 | uses: actions-rs/cargo@v1
82 | with:
83 | command: fmt
84 | args: --all
85 |
86 | - name: Commit rustfmt changes
87 | run: |
88 | git pull origin ${{ github.ref }}
89 | git diff --quiet || echo "### Formatted code with rustfmt." >> $GITHUB_STEP_SUMMARY
90 | git diff --quiet || (git commit -am "[CI${{ github.run_number}}] formatted code with rustfmt" && echo "$(git log --format='%H' -n 1)" >> $GITHUB_STEP_SUMMARY && echo "$(git log --format='%H' -n 1)" >> .git-blame-ignore-revs)
91 |
92 | # This sets the `commit-id` to the latest commit. If there were changes
93 | # made, that means it will be the commit for those changes, otherwise it
94 | # will be the commit that triggered the workflow.
95 | - name: Set the `commit-id` output
96 | id: commit-id
97 | run: echo "COMMIT_ID=$(git log --format='%H' -n 1)" >> $GITHUB_OUTPUT
98 |
99 | - name: Push changes
100 | run: |
101 | git push origin HEAD:${{ github.ref }} || :
102 |
103 | # Runs unit tests.
104 | run-tests:
105 | needs: fix-n-format
106 | if: success() || failure()
107 | runs-on: ubuntu-latest
108 |
109 | steps:
110 | # Check out (a.k.a. clones) the AquariWM repository.
111 | - name: Checkout AquariWM
112 | uses: actions/checkout@v4
113 | with:
114 | ref: ${{ needs.fix-n-format.outputs.commit-id }}
115 |
116 | # Install `libsystemd-dev`, `libudev-dev`, `libseat-dev`, `libinput-dev`, and
117 | # `libxkbcommon-dev` to satisfy dependencies.
118 | - name: Install system libraries required to build AquariWM
119 | run: |
120 | sudo apt-get update
121 | sudo apt-get install libsystemd-dev libudev-dev libseat-dev libinput-dev libxkbcommon-dev
122 |
123 | # Install the latest nightly release of the Rust toolchain.
124 | - name: Install latest nightly
125 | uses: actions-rs/toolchain@v1
126 | with:
127 | profile: minimal
128 | toolchain: nightly
129 | override: true
130 |
131 | # Matches test failures so annotations can be added and such.
132 | - name: Add test problem matching
133 | run: echo "::add-matcher::.github/test-problem-matcher.json"
134 |
135 | # Run unit tests with `cargo test`.
136 | - name: Run tests
137 | run: cargo test --workspace --color never
138 |
139 | - name: Remove test problem matching
140 | if: ${{ success() || failure() }}
141 | run: echo "::remove-matcher owner=rust-tests::"
142 |
143 | # Analyses the code with `clippy`.
144 | clippy-analysis:
145 | runs-on: ubuntu-latest
146 | # We run clippy analysis after any fixes that can be applied have been.
147 | needs: fix-n-format
148 | if: success() || failure()
149 |
150 | steps:
151 | # Check out (a.k.a. clones) the AquariWM repository with fixes made by `clippy`
152 | # in `clippy-fixes`, if any.
153 | - name: Checkout AquariWM
154 | uses: actions/checkout@v4
155 | with:
156 | ref: ${{ needs.fix-n-format.outputs.commit-id }}
157 |
158 | # Install `libsystemd-dev`, `libudev-dev`, `libseat-dev`, `libinput-dev`, and
159 | # `libxkbcommon-dev` to satisfy dependencies.
160 | - name: Install system libraries required to build AquariWM
161 | run: |
162 | sudo apt-get update
163 | sudo apt-get install libsystemd-dev libudev-dev libseat-dev libinput-dev libxkbcommon-dev
164 |
165 | # Install the latest nightly release of the Rust toolchain.
166 | - name: Install latest nightly
167 | uses: actions-rs/toolchain@v1
168 | with:
169 | profile: minimal
170 | toolchain: nightly
171 | override: true
172 | components: clippy
173 |
174 | # Matches errors, warnings, etc. so annotations can be added and such.
175 | - name: Add Rust problem matching
176 | run: echo "::add-matcher::.github/rustc-problem-matcher.json"
177 |
178 | # Analyse the code with `clippy`.
179 | - name: Clippy analysis
180 | run: cargo clippy --workspace --color never
181 |
182 | - name: Remove Rust problem matching
183 | if: ${{ success() || failure() }}
184 | run: echo "::remove-matcher owner=rustc::"
185 |
186 | # Generate the docs with rustdoc.
187 | build-docs:
188 | runs-on: ubuntu-latest
189 | # We only build the documentation after the code has been changed so that
190 | # the code sources linked in the documentation are up-to-date.
191 | needs: fix-n-format
192 | if: success() || failure()
193 |
194 | steps:
195 | # Check out (a.k.a. clones) the AquariWM repository with fixes made by `clippy` in
196 | # `clippy-fixes`, if any, and formatting made by `rustfmt` in `auto-format`, if any.
197 | - name: Checkout AquariWM
198 | uses: actions/checkout@v4
199 | with:
200 | ref: ${{ needs.fix-n-format.outputs.commit-id }}
201 | path: aquariwm
202 |
203 | # Install `libsystemd-dev`, `libudev-dev`, `libseat-dev`, `libinput-dev`, and
204 | # `libxkbcommon-dev` to satisfy dependencies.
205 | - name: Install system libraries required to build AquariWM
206 | run: |
207 | sudo apt-get update
208 | sudo apt-get install libsystemd-dev libudev-dev libseat-dev libinput-dev libxkbcommon-dev
209 |
210 | # Check out a template to put the generated docs in.
211 | - name: Checkout AquariWM docs template
212 | uses: actions/checkout@v4
213 | with:
214 | repository: AquariWM/aquariwm-docs-template
215 | path: template
216 |
217 | # Install the Rust toolchain so that docs can be generated.
218 | - name: Install latest nightly
219 | uses: actions-rs/toolchain@v1
220 | with:
221 | profile: minimal
222 | toolchain: nightly
223 | override: true
224 |
225 | # Matches errors, warnings, etc. so annotations can be added and such.
226 | - name: Add Rust problem matching
227 | run: echo "::add-matcher::aquariwm/.github/rustc-problem-matcher.json"
228 |
229 | # Setup GitHub Pages to easily deploy to it.
230 | - name: Setup GitHub Pages
231 | uses: actions/configure-pages@v4
232 |
233 | # Build documentation with `rustdoc`.
234 | - name: Build documentation
235 | working-directory: aquariwm
236 | run: cargo doc --no-deps --workspace --color never
237 |
238 | - name: Remove Rust problem matching
239 | if: ${{ success() || failure() }}
240 | run: echo "::remove-matcher owner=rustc::"
241 |
242 | # Place the built documentation into the template, ready to be deployed.
243 | - name: Move generated docs into docs template
244 | run: mv aquariwm/target/doc template/doc
245 |
246 | # Upload the template, now containing the built docs, as an artifact that
247 | # can be accessed by the `deploy-docs` job.
248 | - name: Upload GitHub Pages artifact
249 | uses: actions/upload-pages-artifact@v2
250 | with:
251 | path: template
252 |
253 | # Deploy the documentation with GitHub Pages.
254 | deploy-docs:
255 | if: github.event_name == 'push' && github.ref_type == 'branch' && github.ref_name == 'main'
256 | environment:
257 | name: github-pages
258 | url: ${{ steps.deployment.outputs.page_url }}
259 |
260 | runs-on: ubuntu-latest
261 | # Can't deploy the documentation until it exists!
262 | needs: build-docs
263 |
264 | steps:
265 | # Deploys the documentation to GitHub Pages using the artifact (stored
266 | # but not committed changes for Actions) saved earlier.
267 | - name: Deploy documentation to GitHub Pages
268 | uses: actions/deploy-pages@v3
269 |
--------------------------------------------------------------------------------
/.github/workflows/dependency-review.yml:
--------------------------------------------------------------------------------
1 | # Dependency Review Action
2 | #
3 | # This Action will scan dependency manifest files that change as part of a Pull Request, surfacing known-vulnerable versions of the packages declared or updated in the PR. Once installed, if the workflow run is marked as required, PRs introducing known-vulnerable packages will be blocked from merging.
4 | #
5 | # Source repository: https://github.com/actions/dependency-review-action
6 | # Public documentation: https://docs.github.com/en/code-security/supply-chain-security/understanding-your-software-supply-chain/about-dependency-review#dependency-review-enforcement
7 | name: 'Dependency Review'
8 | on: [pull_request]
9 |
10 | permissions:
11 | contents: read
12 |
13 | jobs:
14 | dependency-review:
15 | runs-on: ubuntu-latest
16 | steps:
17 | - name: 'Checkout Repository'
18 | uses: actions/checkout@v4
19 | - name: 'Dependency Review'
20 | uses: actions/dependency-review-action@v3
21 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at https://mozilla.org/MPL/2.0/.
4 |
5 | # Generated by Cargo
6 | # will have compiled files and executables
7 | /target/
8 |
9 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
10 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
11 | Cargo.lock
12 |
13 | # These are backup files generated by rustfmt
14 | **/*.rs.bk
15 |
16 | # Added by cargo
17 | /target
18 |
19 | .idea/
20 |
--------------------------------------------------------------------------------
/.markdownlint.yaml:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at https://mozilla.org/MPL/2.0/.
4 |
5 | # Autoformatter friendly markdownlint config (all formatting rules disabled)
6 | default: true
7 | blank_lines: false
8 | bullet: false
9 | html: false
10 | indentation: false
11 | line_length: false
12 | spaces: false
13 | url: false
14 | whitespace: false
15 |
--------------------------------------------------------------------------------
/.prettierrc.toml:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at https://mozilla.org/MPL/2.0/.
4 |
5 | useTabs = true
6 |
--------------------------------------------------------------------------------
/.run/Run AquariWM (Wayland).run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/.run/Run AquariWM (X11).run.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/.rustfmt.toml:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at https://mozilla.org/MPL/2.0/.
4 |
5 | hard_tabs = true
6 | combine_control_expr = false
7 | wrap_comments = true
8 | comment_width = 100
9 | max_width = 120
10 | condense_wildcard_suffixes = true
11 | format_strings = true
12 |
13 | imports_layout = "HorizontalVertical"
14 | imports_granularity = "Crate"
15 | merge_imports = true
16 | group_imports = "StdExternalCrate"
17 |
18 | match_block_trailing_comma = true
19 | normalize_comments = true
20 | normalize_doc_attributes = true
21 | unstable_features = true
22 | use_field_init_shorthand = true
23 | use_try_shorthand = true
24 |
--------------------------------------------------------------------------------
/.trunk/.gitignore:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at https://mozilla.org/MPL/2.0/.
4 |
5 | *out
6 | *logs
7 | external
8 |
--------------------------------------------------------------------------------
/.trunk/trunk.yaml:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at https://mozilla.org/MPL/2.0/.
4 |
5 | version: 0.1
6 | cli:
7 | version: 0.15.0-beta
8 | lint:
9 | enabled:
10 | - actionlint@1.6.15
11 | - clippy@1.58.1
12 | - gitleaks@8.8.11
13 | - markdownlint@0.31.1
14 | - prettier@2.7.1
15 | - rustfmt@1.58.1
16 | - taplo@release-taplo-cli-0.6.8
17 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 |
4 |
5 | # Please note...
6 | This document is only temporary, and should be replaced by a contributors' wiki. To summarise code
7 | style requirements (until the wiki contains such information):
8 | - AquariWM uses _hard tab characters_ throughout the project for indentation, rather than spaces.
9 | More information about this can be found in the pinned discussion in the Discussions tab of the
10 | GitHub repository (AquariWM/aquariwm). `EditorConfig` and `rustfmt` are configured to support
11 | this already, and we recommend that you install a plugin for your editor to recognise
12 | `.editorconfig` files automatically, as well as to format your Rust code automatically with
13 | `cargo fmt`.
14 | - The code style guidelines of the language you are using should be used _unless_ we specify
15 | otherwise; for instance, as we specify that hard tab characters shall be used, that takes
16 | preference over Rust's official code style guidelines.
17 |
18 | You can find code style guidelines for Rust
19 | [here](https://doc.rust-lang.org/1.0.0/style/README.html). For license header information, please
20 | read ahead.
21 |
22 | ## License headers
23 | AquariWM's core is licensed under the Mozilla Public License v2.0 (MPL-2.0) license. As stated by
24 | Mozilla:
25 |
26 | > This license must be used for all new code, unless the containing project, module or
27 | externally-imported codebase uses a different license. If you can't put a header in the file due to
28 | its structure, please put it in a LICENSE file in the same directory.
29 |
30 | All new code contributed to AquariWM's core is required to include the following license headers at
31 | the beginning of all relevant files. If the file format in question does not support comments,
32 | please include a `LICENSE` file in the same directory with the plain-text version of this header.
33 |
34 | ### Rust, and others with C-like comments
35 | ```rust
36 | // This Source Code Form is subject to the terms of the Mozilla Public
37 | // License, v. 2.0. If a copy of the MPL was not distributed with this
38 | // file, You can obtain one at https://mozilla.org/MPL/2.0/.
39 | ```
40 |
41 | ### Shell scripts, and others
42 | ```bash
43 | # This Source Code Form is subject to the terms of the Mozilla Public
44 | # License, v. 2.0. If a copy of the MPL was not distributed with this
45 | # file, You can obtain one at https://mozilla.org/MPL/2.0/.
46 | ```
47 |
48 | ### Markdown, HTML, and other markup languages
49 | ```markdown
50 |
53 | ```
54 |
55 | ### `LICENSE` files, and others
56 | ```
57 | This Source Code Form is subject to the terms of the Mozilla Public
58 | License, v. 2.0. If a copy of the MPL was not distributed with this
59 | file, You can obtain one at https://mozilla.org/MPL/2.0/.
--------------------------------------------------------------------------------
/Cargo.toml:
--------------------------------------------------------------------------------
1 | # This Source Code Form is subject to the terms of the Mozilla Public
2 | # License, v. 2.0. If a copy of the MPL was not distributed with this
3 | # file, You can obtain one at https://mozilla.org/MPL/2.0/.
4 |
5 | [package]
6 | name = "aquariwm"
7 | version = "0.1.0"
8 | authors = ["AquariWM", "Antikyth"]
9 | edition = "2021"
10 | readme = true
11 | repository = "https://github.com/AquariWM/aquariwm"
12 | license = "MPL-2.0"
13 |
14 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
15 |
16 | [workspace]
17 | members = ["derive-extras"]
18 |
19 | [features]
20 | # TODO: When compiling for release, maybe don't include the testing feature?
21 | default = ["wayland", "x11", "testing"]
22 |
23 | wayland = ["dep:smithay"]
24 | x11 = ["dep:x11rb-async", "async", "winit?/x11"]
25 |
26 | # NOTE: winit is provided even if x11 is not enabled, because there is no way to specify that two
27 | # features must be enabled for an optional dependency.
28 | testing = ["smithay?/backend_winit", "dep:winit"]
29 | # Features required for async AquariWM implementations (i.e. our X11 implementation).
30 | async = ["dep:futures", "dep:tokio"]
31 |
32 | [dependencies]
33 | bitflags = "2.2.1"
34 | thiserror = "1.0.50"
35 | truncate-integer = "0.5.0"
36 | derive-extras = { path = "./derive-extras" }
37 |
38 | # CLI
39 | clap = { version = "4.4.7", features = ["derive"] }
40 | clap_complete = "4.4.4"
41 |
42 | # Logging
43 | tracing = "0.1.40"
44 | tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
45 |
46 | ####################################################################################################
47 | # Display server specific
48 | ####################################################################################################
49 | # X11
50 | [dependencies.x11rb-async]
51 | version = "0.13.0"
52 | optional = true
53 |
54 | # Wayland
55 | [dependencies.smithay]
56 | git = "https://github.com/Smithay/smithay"
57 | features = ["wayland_frontend", "desktop"]
58 | optional = true
59 |
60 | ####################################################################################################
61 | # Async
62 | ####################################################################################################
63 |
64 |
65 |
66 | [dependencies.futures]
67 | version = "0.3.29"
68 | optional = true
69 |
70 | [dependencies.tokio]
71 | version = "1.33.0"
72 | features = ["full"]
73 | optional = true
74 |
75 | ####################################################################################################
76 | # Testing
77 | ####################################################################################################
78 |
79 | [dependencies.winit]
80 | version = "0.29.3"
81 | default-features = false
82 | optional = true
83 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Mozilla Public License Version 2.0
2 | ==================================
3 |
4 | 1. Definitions
5 | --------------
6 |
7 | 1.1. "Contributor"
8 | means each individual or legal entity that creates, contributes to
9 | the creation of, or owns Covered Software.
10 |
11 | 1.2. "Contributor Version"
12 | means the combination of the Contributions of others (if any) used
13 | by a Contributor and that particular Contributor's Contribution.
14 |
15 | 1.3. "Contribution"
16 | means Covered Software of a particular Contributor.
17 |
18 | 1.4. "Covered Software"
19 | means Source Code Form to which the initial Contributor has attached
20 | the notice in Exhibit A, the Executable Form of such Source Code
21 | Form, and Modifications of such Source Code Form, in each case
22 | including portions thereof.
23 |
24 | 1.5. "Incompatible With Secondary Licenses"
25 | means
26 |
27 | (a) that the initial Contributor has attached the notice described
28 | in Exhibit B to the Covered Software; or
29 |
30 | (b) that the Covered Software was made available under the terms of
31 | version 1.1 or earlier of the License, but not also under the
32 | terms of a Secondary License.
33 |
34 | 1.6. "Executable Form"
35 | means any form of the work other than Source Code Form.
36 |
37 | 1.7. "Larger Work"
38 | means a work that combines Covered Software with other material, in
39 | a separate file or files, that is not Covered Software.
40 |
41 | 1.8. "License"
42 | means this document.
43 |
44 | 1.9. "Licensable"
45 | means having the right to grant, to the maximum extent possible,
46 | whether at the time of the initial grant or subsequently, any and
47 | all of the rights conveyed by this License.
48 |
49 | 1.10. "Modifications"
50 | means any of the following:
51 |
52 | (a) any file in Source Code Form that results from an addition to,
53 | deletion from, or modification of the contents of Covered
54 | Software; or
55 |
56 | (b) any new file in Source Code Form that contains any Covered
57 | Software.
58 |
59 | 1.11. "Patent Claims" of a Contributor
60 | means any patent claim(s), including without limitation, method,
61 | process, and apparatus claims, in any patent Licensable by such
62 | Contributor that would be infringed, but for the grant of the
63 | License, by the making, using, selling, offering for sale, having
64 | made, import, or transfer of either its Contributions or its
65 | Contributor Version.
66 |
67 | 1.12. "Secondary License"
68 | means either the GNU General Public License, Version 2.0, the GNU
69 | Lesser General Public License, Version 2.1, the GNU Affero General
70 | Public License, Version 3.0, or any later versions of those
71 | licenses.
72 |
73 | 1.13. "Source Code Form"
74 | means the form of the work preferred for making modifications.
75 |
76 | 1.14. "You" (or "Your")
77 | means an individual or a legal entity exercising rights under this
78 | License. For legal entities, "You" includes any entity that
79 | controls, is controlled by, or is under common control with You. For
80 | purposes of this definition, "control" means (a) the power, direct
81 | or indirect, to cause the direction or management of such entity,
82 | whether by contract or otherwise, or (b) ownership of more than
83 | fifty percent (50%) of the outstanding shares or beneficial
84 | ownership of such entity.
85 |
86 | 2. License Grants and Conditions
87 | --------------------------------
88 |
89 | 2.1. Grants
90 |
91 | Each Contributor hereby grants You a world-wide, royalty-free,
92 | non-exclusive license:
93 |
94 | (a) under intellectual property rights (other than patent or trademark)
95 | Licensable by such Contributor to use, reproduce, make available,
96 | modify, display, perform, distribute, and otherwise exploit its
97 | Contributions, either on an unmodified basis, with Modifications, or
98 | as part of a Larger Work; and
99 |
100 | (b) under Patent Claims of such Contributor to make, use, sell, offer
101 | for sale, have made, import, and otherwise transfer either its
102 | Contributions or its Contributor Version.
103 |
104 | 2.2. Effective Date
105 |
106 | The licenses granted in Section 2.1 with respect to any Contribution
107 | become effective for each Contribution on the date the Contributor first
108 | distributes such Contribution.
109 |
110 | 2.3. Limitations on Grant Scope
111 |
112 | The licenses granted in this Section 2 are the only rights granted under
113 | this License. No additional rights or licenses will be implied from the
114 | distribution or licensing of Covered Software under this License.
115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a
116 | Contributor:
117 |
118 | (a) for any code that a Contributor has removed from Covered Software;
119 | or
120 |
121 | (b) for infringements caused by: (i) Your and any other third party's
122 | modifications of Covered Software, or (ii) the combination of its
123 | Contributions with other software (except as part of its Contributor
124 | Version); or
125 |
126 | (c) under Patent Claims infringed by Covered Software in the absence of
127 | its Contributions.
128 |
129 | This License does not grant any rights in the trademarks, service marks,
130 | or logos of any Contributor (except as may be necessary to comply with
131 | the notice requirements in Section 3.4).
132 |
133 | 2.4. Subsequent Licenses
134 |
135 | No Contributor makes additional grants as a result of Your choice to
136 | distribute the Covered Software under a subsequent version of this
137 | License (see Section 10.2) or under the terms of a Secondary License (if
138 | permitted under the terms of Section 3.3).
139 |
140 | 2.5. Representation
141 |
142 | Each Contributor represents that the Contributor believes its
143 | Contributions are its original creation(s) or it has sufficient rights
144 | to grant the rights to its Contributions conveyed by this License.
145 |
146 | 2.6. Fair Use
147 |
148 | This License is not intended to limit any rights You have under
149 | applicable copyright doctrines of fair use, fair dealing, or other
150 | equivalents.
151 |
152 | 2.7. Conditions
153 |
154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
155 | in Section 2.1.
156 |
157 | 3. Responsibilities
158 | -------------------
159 |
160 | 3.1. Distribution of Source Form
161 |
162 | All distribution of Covered Software in Source Code Form, including any
163 | Modifications that You create or to which You contribute, must be under
164 | the terms of this License. You must inform recipients that the Source
165 | Code Form of the Covered Software is governed by the terms of this
166 | License, and how they can obtain a copy of this License. You may not
167 | attempt to alter or restrict the recipients' rights in the Source Code
168 | Form.
169 |
170 | 3.2. Distribution of Executable Form
171 |
172 | If You distribute Covered Software in Executable Form then:
173 |
174 | (a) such Covered Software must also be made available in Source Code
175 | Form, as described in Section 3.1, and You must inform recipients of
176 | the Executable Form how they can obtain a copy of such Source Code
177 | Form by reasonable means in a timely manner, at a charge no more
178 | than the cost of distribution to the recipient; and
179 |
180 | (b) You may distribute such Executable Form under the terms of this
181 | License, or sublicense it under different terms, provided that the
182 | license for the Executable Form does not attempt to limit or alter
183 | the recipients' rights in the Source Code Form under this License.
184 |
185 | 3.3. Distribution of a Larger Work
186 |
187 | You may create and distribute a Larger Work under terms of Your choice,
188 | provided that You also comply with the requirements of this License for
189 | the Covered Software. If the Larger Work is a combination of Covered
190 | Software with a work governed by one or more Secondary Licenses, and the
191 | Covered Software is not Incompatible With Secondary Licenses, this
192 | License permits You to additionally distribute such Covered Software
193 | under the terms of such Secondary License(s), so that the recipient of
194 | the Larger Work may, at their option, further distribute the Covered
195 | Software under the terms of either this License or such Secondary
196 | License(s).
197 |
198 | 3.4. Notices
199 |
200 | You may not remove or alter the substance of any license notices
201 | (including copyright notices, patent notices, disclaimers of warranty,
202 | or limitations of liability) contained within the Source Code Form of
203 | the Covered Software, except that You may alter any license notices to
204 | the extent required to remedy known factual inaccuracies.
205 |
206 | 3.5. Application of Additional Terms
207 |
208 | You may choose to offer, and to charge a fee for, warranty, support,
209 | indemnity or liability obligations to one or more recipients of Covered
210 | Software. However, You may do so only on Your own behalf, and not on
211 | behalf of any Contributor. You must make it absolutely clear that any
212 | such warranty, support, indemnity, or liability obligation is offered by
213 | You alone, and You hereby agree to indemnify every Contributor for any
214 | liability incurred by such Contributor as a result of warranty, support,
215 | indemnity or liability terms You offer. You may include additional
216 | disclaimers of warranty and limitations of liability specific to any
217 | jurisdiction.
218 |
219 | 4. Inability to Comply Due to Statute or Regulation
220 | ---------------------------------------------------
221 |
222 | If it is impossible for You to comply with any of the terms of this
223 | License with respect to some or all of the Covered Software due to
224 | statute, judicial order, or regulation then You must: (a) comply with
225 | the terms of this License to the maximum extent possible; and (b)
226 | describe the limitations and the code they affect. Such description must
227 | be placed in a text file included with all distributions of the Covered
228 | Software under this License. Except to the extent prohibited by statute
229 | or regulation, such description must be sufficiently detailed for a
230 | recipient of ordinary skill to be able to understand it.
231 |
232 | 5. Termination
233 | --------------
234 |
235 | 5.1. The rights granted under this License will terminate automatically
236 | if You fail to comply with any of its terms. However, if You become
237 | compliant, then the rights granted under this License from a particular
238 | Contributor are reinstated (a) provisionally, unless and until such
239 | Contributor explicitly and finally terminates Your grants, and (b) on an
240 | ongoing basis, if such Contributor fails to notify You of the
241 | non-compliance by some reasonable means prior to 60 days after You have
242 | come back into compliance. Moreover, Your grants from a particular
243 | Contributor are reinstated on an ongoing basis if such Contributor
244 | notifies You of the non-compliance by some reasonable means, this is the
245 | first time You have received notice of non-compliance with this License
246 | from such Contributor, and You become compliant prior to 30 days after
247 | Your receipt of the notice.
248 |
249 | 5.2. If You initiate litigation against any entity by asserting a patent
250 | infringement claim (excluding declaratory judgment actions,
251 | counter-claims, and cross-claims) alleging that a Contributor Version
252 | directly or indirectly infringes any patent, then the rights granted to
253 | You by any and all Contributors for the Covered Software under Section
254 | 2.1 of this License shall terminate.
255 |
256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all
257 | end user license agreements (excluding distributors and resellers) which
258 | have been validly granted by You or Your distributors under this License
259 | prior to termination shall survive termination.
260 |
261 | ************************************************************************
262 | * *
263 | * 6. Disclaimer of Warranty *
264 | * ------------------------- *
265 | * *
266 | * Covered Software is provided under this License on an "as is" *
267 | * basis, without warranty of any kind, either expressed, implied, or *
268 | * statutory, including, without limitation, warranties that the *
269 | * Covered Software is free of defects, merchantable, fit for a *
270 | * particular purpose or non-infringing. The entire risk as to the *
271 | * quality and performance of the Covered Software is with You. *
272 | * Should any Covered Software prove defective in any respect, You *
273 | * (not any Contributor) assume the cost of any necessary servicing, *
274 | * repair, or correction. This disclaimer of warranty constitutes an *
275 | * essential part of this License. No use of any Covered Software is *
276 | * authorized under this License except under this disclaimer. *
277 | * *
278 | ************************************************************************
279 |
280 | ************************************************************************
281 | * *
282 | * 7. Limitation of Liability *
283 | * -------------------------- *
284 | * *
285 | * Under no circumstances and under no legal theory, whether tort *
286 | * (including negligence), contract, or otherwise, shall any *
287 | * Contributor, or anyone who distributes Covered Software as *
288 | * permitted above, be liable to You for any direct, indirect, *
289 | * special, incidental, or consequential damages of any character *
290 | * including, without limitation, damages for lost profits, loss of *
291 | * goodwill, work stoppage, computer failure or malfunction, or any *
292 | * and all other commercial damages or losses, even if such party *
293 | * shall have been informed of the possibility of such damages. This *
294 | * limitation of liability shall not apply to liability for death or *
295 | * personal injury resulting from such party's negligence to the *
296 | * extent applicable law prohibits such limitation. Some *
297 | * jurisdictions do not allow the exclusion or limitation of *
298 | * incidental or consequential damages, so this exclusion and *
299 | * limitation may not apply to You. *
300 | * *
301 | ************************************************************************
302 |
303 | 8. Litigation
304 | -------------
305 |
306 | Any litigation relating to this License may be brought only in the
307 | courts of a jurisdiction where the defendant maintains its principal
308 | place of business and such litigation shall be governed by laws of that
309 | jurisdiction, without reference to its conflict-of-law provisions.
310 | Nothing in this Section shall prevent a party's ability to bring
311 | cross-claims or counter-claims.
312 |
313 | 9. Miscellaneous
314 | ----------------
315 |
316 | This License represents the complete agreement concerning the subject
317 | matter hereof. If any provision of this License is held to be
318 | unenforceable, such provision shall be reformed only to the extent
319 | necessary to make it enforceable. Any law or regulation which provides
320 | that the language of a contract shall be construed against the drafter
321 | shall not be used to construe this License against a Contributor.
322 |
323 | 10. Versions of the License
324 | ---------------------------
325 |
326 | 10.1. New Versions
327 |
328 | Mozilla Foundation is the license steward. Except as provided in Section
329 | 10.3, no one other than the license steward has the right to modify or
330 | publish new versions of this License. Each version will be given a
331 | distinguishing version number.
332 |
333 | 10.2. Effect of New Versions
334 |
335 | You may distribute the Covered Software under the terms of the version
336 | of the License under which You originally received the Covered Software,
337 | or under the terms of any subsequent version published by the license
338 | steward.
339 |
340 | 10.3. Modified Versions
341 |
342 | If you create software not governed by this License, and you want to
343 | create a new license for such software, you may create and use a
344 | modified version of this License if you rename the license and remove
345 | any references to the name of the license steward (except to note that
346 | such modified license differs from this License).
347 |
348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary
349 | Licenses
350 |
351 | If You choose to distribute Source Code Form that is Incompatible With
352 | Secondary Licenses under the terms of this version of the License, the
353 | notice described in Exhibit B of this License must be attached.
354 |
355 | Exhibit A - Source Code Form License Notice
356 | -------------------------------------------
357 |
358 | This Source Code Form is subject to the terms of the Mozilla Public
359 | License, v. 2.0. If a copy of the MPL was not distributed with this
360 | file, You can obtain one at http://mozilla.org/MPL/2.0/.
361 |
362 | If it is not possible or desirable to put the notice in a particular
363 | file, then You may include the notice in a location (such as a LICENSE
364 | file in a relevant directory) where a recipient would be likely to look
365 | for such a notice.
366 |
367 | You may add additional accurate notices of copyright ownership.
368 |
369 | Exhibit B - "Incompatible With Secondary Licenses" Notice
370 | ---------------------------------------------------------
371 |
372 | This Source Code Form is "Incompatible With Secondary Licenses", as
373 | defined by the Mozilla Public License, v. 2.0.
374 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
4 |
5 | # AquariWM
6 |
7 | AquariWM will be an X11 window manager and Wayland compositor focused on a highly modular design. It will also focus on
8 | high-quality documentation and being accessible and welcoming to all.
9 |
10 | ## Modularity
11 |
12 | Tiling window layout managers will be able to be provided by external applications ('AquariWM clients'). AquariWM
13 | clients will also be able to provide decoration managers, providing the window decorations for windows.
14 |
15 | Other areas of modularity will exist too but ideas for them will become clear as AquariWM is developed - areas could
16 | include input methods, config/settings managers, or adding modularity that X11 already has to the AquariWM Wayland
17 | compositor (window managers and compositors being provided by external clients being one example).
18 |
19 | ## Current state
20 |
21 | At the time of writing (December 2023), AquariWM development is in early stages, though the layout manager system is
22 | implemented in Rust (with the goal of transitioning to use a custom protocol in the future, the specifics of which are
23 | yet to be decided). @Antikyth, the only author of AquariWM at the time of writing, is working on
24 | [`generational-arena-tree`], a tree implementation in Rust that gives the flexibility to implement more complex features
25 | for tiling layouts (e.g. taking windows' minimum and maximum sizes into account). Specifically, it allows:
26 | - nodes to be mutated directly,
27 | - nodes to be iterated over mutably,
28 | - nodes to be split by type into separate branches (nodes that may have children) and leaves (nodes that may not have
29 | children), which each have their own associated data type.
30 | - This is required because, in window layouts, every branch has an orientation, and every leaf has a window. No branch
31 | may have a window, and no leaf may have an orientation.
32 |
33 | Here is a screenshot of a working Main + Stack layout manager implemented in the current state of AquariWM:
34 | 
35 |
36 | [`generational-arena-tree`]: https://github.com/AquariWM/generational-arena-tree
37 |
--------------------------------------------------------------------------------
/derive-extras/Cargo.toml:
--------------------------------------------------------------------------------
1 | [package]
2 | name = "derive-extras"
3 | authors = ["Antikyth"]
4 | version = "0.1.0"
5 | license = "MPL-2.0"
6 | description = "Adds a number of useful extra `#[derive(...)]` macros."
7 | edition = "2021"
8 |
9 | [lib]
10 | proc-macro = true
11 |
12 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
13 |
14 | [dependencies]
15 | quote = "1.0.33"
16 | syn = "2.0.39"
17 | proc-macro2 = "1.0.69"
18 |
--------------------------------------------------------------------------------
/derive-extras/src/lib.rs:
--------------------------------------------------------------------------------
1 | // This Source Code Form is subject to the terms of the Mozilla Public
2 | // License, v. 2.0. If a copy of the MPL was not distributed with this
3 | // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4 |
5 | use proc_macro::TokenStream;
6 | use proc_macro2::{Ident, Span, TokenStream as TokenStream2};
7 | use quote::{quote, ToTokens};
8 | use syn::{
9 | parse_macro_input,
10 | parse_quote,
11 | spanned::Spanned,
12 | Attribute,
13 | Data,
14 | DataEnum,
15 | DataStruct,
16 | DeriveInput,
17 | Error,
18 | Fields,
19 | GenericParam,
20 | Index,
21 | Meta,
22 | Type,
23 | Variant,
24 | };
25 |
26 | use crate::numbers_to_words::encode_ordinal;
27 |
28 | mod numbers_to_words;
29 |
30 | /// Derives [`Default`] with more flexibility than Rust's built-in `#[derive(Default)]`.
31 | ///
32 | /// In addition to the functionality allowed by the built-in `#[derive(Default)]`, this version
33 | /// allows:
34 | /// - overriding the default values for particular fields using `#[default = ...]` or
35 | /// `#[default(...)]`; and
36 | /// - using `#[default]` on a non-unit enum variant
37 | ///
38 | /// # Examples
39 | ///
40 | /// ## Structs
41 | /// ```
42 | /// # #[allow(unused_qualifications)]
43 | /// #[derive(derive_extras::Default)]
44 | /// struct ExampleStruct {
45 | /// x: i32,
46 | /// #[default = 15]
47 | /// y: i32,
48 | /// z: i32,
49 | /// }
50 | /// ```
51 | /// For this struct, the following [`Default`] implementation is derived:
52 | /// ```
53 | /// # struct ExampleStruct {
54 | /// # x: i32,
55 | /// # y: i32,
56 | /// # z: i32,
57 | /// # }
58 | /// #
59 | /// impl Default for ExampleStruct {
60 | /// fn default() -> Self {
61 | /// Self {
62 | /// x: Default::default(),
63 | /// y: 15,
64 | /// z: Default::default(),
65 | /// }
66 | /// }
67 | /// }
68 | /// ```
69 | ///
70 | /// ## Enums
71 | /// ```
72 | /// # #[allow(unused_qualifications)]
73 | /// #[derive(derive_extras::Default)]
74 | /// enum ExampleEnum {
75 | /// Unit,
76 | /// Tuple(i32, i32, i32),
77 | ///
78 | /// #[default]
79 | /// Struct {
80 | /// x: i32,
81 | /// #[default = 15]
82 | /// y: i32,
83 | /// z: i32,
84 | /// },
85 | /// }
86 | /// ```
87 | /// For this enum, the following [`Default`] implementation is derived:
88 | /// ```
89 | /// # enum ExampleEnum {
90 | /// # Unit,
91 | /// # Tuple(i32, i32, i32),
92 | /// # Struct {
93 | /// # x: i32,
94 | /// # y: i32,
95 | /// # z: i32,
96 | /// # },
97 | /// # }
98 | /// #
99 | /// impl Default for ExampleEnum {
100 | /// fn default() -> Self {
101 | /// Self::Struct {
102 | /// x: Default::default(),
103 | /// y: 15,
104 | /// z: Default::default(),
105 | /// }
106 | /// }
107 | /// }
108 | /// ```
109 | ///
110 | /// [`Default`]: core::default::Default
111 | #[proc_macro_derive(Default, attributes(default))]
112 | pub fn default(input: TokenStream) -> TokenStream {
113 | let input = parse_macro_input!(input as DeriveInput);
114 |
115 | let DeriveInput {
116 | mut generics,
117 | ident,
118 | data,
119 | ..
120 | } = input;
121 |
122 | for param in &mut generics.params {
123 | if let GenericParam::Type(param) = param {
124 | param.bounds.push(parse_quote!(::core::default::Default))
125 | }
126 | }
127 |
128 | let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
129 |
130 | let body = match data {
131 | Data::Struct(DataStruct { fields, .. }) => {
132 | let init = expand_default_for_fields(fields);
133 |
134 | quote!(Self #init)
135 | },
136 |
137 | Data::Enum(r#enum) => expand_default_for_enum(r#enum).unwrap_or_else(|error| error.into_compile_error()),
138 |
139 | Data::Union(_) => unimplemented!("unions are not supported"),
140 | };
141 |
142 | let tokens = quote! {
143 | impl #impl_generics ::core::default::Default for #ident #ty_generics #where_clause {
144 | fn default() -> Self {
145 | #body
146 | }
147 | }
148 | };
149 |
150 | tokens.into()
151 | }
152 |
153 | fn expand_default_for_enum(r#enum: DataEnum) -> syn::Result {
154 | let variant = {
155 | let mut variant = None;
156 |
157 | for var in r#enum.variants {
158 | for attr in &var.attrs {
159 | if attr.meta.require_path_only().map(|path| path.is_ident("default"))? {
160 | match &variant {
161 | None => variant = Some(var),
162 | Some(_) => return Err(Error::new(attr.span(), "conflicting #[default] attribute")),
163 | }
164 |
165 | break;
166 | }
167 | }
168 | }
169 |
170 | variant
171 | };
172 |
173 | match variant {
174 | Some(Variant { ident, fields, .. }) => {
175 | let init = expand_default_for_fields(fields);
176 |
177 | Ok(quote!(Self::#ident #init))
178 | },
179 |
180 | None => Err(Error::new(
181 | Span::call_site(),
182 | "one enum variant must have a #[default] attribute",
183 | )),
184 | }
185 | }
186 |
187 | fn expand_default_for_fields(fields: Fields) -> Option {
188 | match fields {
189 | Fields::Unit => None,
190 |
191 | Fields::Named(fields) => {
192 | let field_init = fields.named.into_pairs().map(|pair| {
193 | let (field, comma) = pair.into_tuple();
194 |
195 | let (ident, ty, attrs) = (field.ident, field.ty, field.attrs);
196 |
197 | let value = field_value(ty, attrs);
198 |
199 | quote!(#ident: #value #comma)
200 | });
201 |
202 | Some(quote!({ #(#field_init)* }))
203 | },
204 |
205 | Fields::Unnamed(fields) => {
206 | let field_init = fields.unnamed.into_pairs().map(|pair| {
207 | let (field, comma) = pair.into_tuple();
208 |
209 | let value = field_value(field.ty, field.attrs);
210 |
211 | quote!(#value #comma)
212 | });
213 |
214 | Some(quote!((#(#field_init)*)))
215 | },
216 | }
217 | }
218 |
219 | fn field_value(ty: Type, attrs: Vec) -> TokenStream2 {
220 | let default_attr = attrs
221 | .into_iter()
222 | .find(|attribute| attribute.meta.path().is_ident("default"));
223 |
224 | match default_attr {
225 | // If there is a default attribute, use its value.
226 | Some(default_attr) => match default_attr.meta {
227 | Meta::Path(path) => Error::new(
228 | path.span(),
229 | format!(
230 | "expected a value for this attribute: `{}(...)` or `{} = ...`",
231 | "default", "default",
232 | ),
233 | )
234 | .into_compile_error(),
235 |
236 | Meta::List(meta) => {
237 | let tokens = meta.tokens;
238 |
239 | quote!({ #tokens })
240 | },
241 |
242 | Meta::NameValue(meta) => meta.value.into_token_stream(),
243 | },
244 |
245 | // If there is no default attribute, use `Default::default()`.
246 | None => {
247 | quote!(<#ty as ::core::default::Default>::default())
248 | },
249 | }
250 | }
251 |
252 | /// Generates appropriate builder methods for a struct.
253 | ///
254 | /// This assumes that the struct itself acts like a builder.
255 | ///
256 | /// This derive macro also adds a `#[new]` helper attribute. If this is added to the struct, a `new`
257 | /// function is also generated with a `where Self: Default` bound.
258 | ///
259 | /// The builder methods generated for each field will have that field's visibility: private fields
260 | /// will have private methods, etc.
261 | ///
262 | /// # Examples
263 | /// ```
264 | /// # use derive_extras::builder;
265 | /// #
266 | /// #[derive(Default, builder)]
267 | /// #[new]
268 | /// struct Example {
269 | /// pub x: i32,
270 | /// pub y: i32,
271 | /// }
272 | /// ```
273 | /// This will derive the following implementations:
274 | /// ```
275 | /// # #[derive(Default)]
276 | /// # struct Example {
277 | /// # pub x: i32,
278 | /// # pub y: i32,
279 | /// # }
280 | /// #
281 | /// impl Example {
282 | /// /// Creates a new `Example`.
283 | /// ///
284 | /// /// This is equivalent to Example::[default()].
285 | /// ///
286 | /// /// [default()]: Default::default()
287 | /// pub fn new() -> Self
288 | /// where
289 | /// Self: Default,
290 | /// {
291 | /// Self::default()
292 | /// }
293 | ///
294 | /// /// Sets `x` to the given value.
295 | /// pub fn x(mut self, x: i32) -> Self {
296 | /// self.x = x;
297 | ///
298 | /// self
299 | /// }
300 | ///
301 | /// /// Sets `y` to the given value.
302 | /// pub fn y(mut self, y: i32) -> Self {
303 | /// self.y = y;
304 | ///
305 | /// self
306 | /// }
307 | /// }
308 | /// ```
309 | ///
310 | ///
311 | /// `#[derive(builder)]` also works on tuple structs (with any number of fields):
312 | /// ```
313 | /// # use derive_extras::builder;
314 | /// #
315 | /// #[derive(Default, builder)]
316 | /// struct Example(pub i32, pub i32);
317 | /// ```
318 | /// This will derive the following implementations:
319 | /// ```
320 | /// # #[derive(Default)]
321 | /// # struct Example(pub i32, pub i32);
322 | /// #
323 | /// impl Example {
324 | /// /// Sets the first field to the given value.
325 | /// pub fn first(mut self, first: i32) -> Self {
326 | /// self.0 = first;
327 | ///
328 | /// self
329 | /// }
330 | ///
331 | /// /// Sets the second field to the given value.
332 | /// pub fn second(mut self, second: i32) -> Self {
333 | /// self.1 = second;
334 | ///
335 | /// self
336 | /// }
337 | /// }
338 | /// ```
339 | #[proc_macro_derive(builder, attributes(new))]
340 | pub fn builder(input: TokenStream) -> TokenStream {
341 | let input = parse_macro_input!(input as DeriveInput);
342 |
343 | let tokens = match input.data {
344 | Data::Struct(r#struct) => {
345 | let name = input.ident;
346 |
347 | let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
348 |
349 | let new = input.attrs.iter().any(|attr| attr.path().is_ident("new")).then(|| {
350 | quote! {
351 | #[doc = concat!("Creates a new `", stringify!(#name), "`.")]
352 | #[doc = ""]
353 | #[doc = concat!("This is equivalent to ", stringify!(#name), "::[default()].")]
354 | #[doc = ""]
355 | #[doc = "[default()]: ::core::default::Default::default()"]
356 | pub fn new() -> Self
357 | where
358 | Self: ::core::default::Default,
359 | {
360 | ::default()
361 | }
362 | }
363 | });
364 |
365 | let config_methods = match &r#struct.fields {
366 | Fields::Unit => None,
367 |
368 | // Named fields.
369 | Fields::Named(fields) => {
370 | let methods: TokenStream2 = fields
371 | .named
372 | .iter()
373 | .map(|field| {
374 | let vis = &field.vis;
375 | let ty = &field.ty;
376 |
377 | let ident = &field.ident;
378 |
379 | let docs = ident
380 | .as_ref()
381 | .map(|ident| format!("Sets `{ident}` to the given value."));
382 |
383 | quote! {
384 | #[doc = #docs]
385 | #vis fn #ident(mut self, #ident: #ty) -> Self {
386 | self.#ident = #ident;
387 |
388 | self
389 | }
390 | }
391 | })
392 | .collect();
393 |
394 | Some(methods)
395 | },
396 |
397 | // Unnamed fields.
398 | Fields::Unnamed(fields) => {
399 | let methods = fields
400 | .unnamed
401 | .iter()
402 | .enumerate()
403 | .map(|(i, field)| {
404 | let vis = &field.vis;
405 | let ty = &field.ty;
406 |
407 | let index = Index::from(i);
408 | let ident = Ident::new(&encode_ordinal(i + 1, '_'), Span::call_site());
409 |
410 | let ordinal = encode_ordinal(i + 1, ' ');
411 | let docs = format!("Sets the {ordinal} field to the given value.");
412 |
413 | quote! {
414 | #[doc = #docs]
415 | #vis fn #ident(mut self, #ident: #ty) -> Self {
416 | self.#index = #ident;
417 |
418 | self
419 | }
420 | }
421 | })
422 | .collect();
423 |
424 | Some(methods)
425 | },
426 | };
427 |
428 | // Final generated implementation.
429 | quote! {
430 | impl #impl_generics #name #ty_generics #where_clause {
431 | #new
432 |
433 | #config_methods
434 | }
435 | }
436 | },
437 |
438 | Data::Enum(_enum) => unimplemented!("enums are not supported"),
439 | Data::Union(_union) => unimplemented!("unions are not supported"),
440 | };
441 |
442 | tokens.into()
443 | }
444 |
--------------------------------------------------------------------------------
/derive-extras/src/numbers_to_words.rs:
--------------------------------------------------------------------------------
1 | // This Source Code Form is subject to the terms of the Mozilla Public
2 | // License, v. 2.0. If a copy of the MPL was not distributed with this
3 | // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4 |
5 | use std::iter;
6 |
7 | const DIGITS: [&str; 20] = [
8 | "zero",
9 | "one",
10 | "two",
11 | "three",
12 | "four",
13 | "five",
14 | "six",
15 | "seven",
16 | "eight",
17 | "nine",
18 | "ten",
19 | "eleven",
20 | "twelve",
21 | "thirteen",
22 | "fourteen",
23 | "fifteen",
24 | "sixteen",
25 | "seventeen",
26 | "eighteen",
27 | "nineteen",
28 | ];
29 | const TENS: [&str; 8] = [
30 | "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety",
31 | ];
32 | const ORDERS: [&str; 12] = [
33 | "thousand",
34 | "million",
35 | "billion",
36 | "trillion",
37 | "quadrillion",
38 | "quintillion",
39 | "sextillion",
40 | "septillion",
41 | "octillion",
42 | "nonillion",
43 | "decillion",
44 | "undecillion",
45 | ];
46 |
47 | const DIGITS_ORDINAL: [&str; 20] = [
48 | "zeroth",
49 | "first",
50 | "second",
51 | "third",
52 | "fourth",
53 | "fifth",
54 | "sixth",
55 | "seventh",
56 | "eighth",
57 | "ninth",
58 | "tenth",
59 | "eleventh",
60 | "twelfth",
61 | "thirteenth",
62 | "fourteenth",
63 | "fifteenth",
64 | "sixteenth",
65 | "seventeenth",
66 | "eighteenth",
67 | "nineteenth",
68 | ];
69 | const TENS_ORDINAL: [&str; 8] = [
70 | "twentieth",
71 | "thirtieth",
72 | "fortieth",
73 | "fiftieth",
74 | "sixtieth",
75 | "seventieth",
76 | "eightieth",
77 | "ninetieth",
78 | ];
79 | const ORDERS_ORDINAL: [&str; 12] = [
80 | "thousandth",
81 | "millionth",
82 | "billionth",
83 | "trillionth",
84 | "quadrillionth",
85 | "quintillionth",
86 | "sextillionth",
87 | "septillionth",
88 | "octillionth",
89 | "nonillionth",
90 | "decillionth",
91 | "undecillionth",
92 | ];
93 |
94 | /// Encodes the given `number` as a [string] using the given `separator` between words.
95 | ///
96 | /// # Examples
97 | /// ```no_run
98 | /// # fn encode(number: usize, separator: char) -> String { "".to_owned() }
99 | /// assert_eq!(encode(1_782, ' '), "one thousand seven hundred and eighty two");
100 | /// assert_eq!(encode(93, '_'), "ninety_three");
101 | /// ```
102 | ///
103 | /// [string]: String
104 | pub fn encode(number: usize, separator: char) -> String {
105 | let sep = separator;
106 |
107 | match number {
108 | // Digits and teens.
109 | 0..=19 => DIGITS[number].to_owned(),
110 | // Other tens.
111 | 20..=99 => {
112 | // Tens start at twenty, so subtract 2.
113 | let tens = (number / 10) - 2;
114 | let digits = number % 10;
115 |
116 | match digits {
117 | 0 => TENS[tens].to_owned(),
118 | _ => format!("{}{sep}{}", TENS[tens], DIGITS[digits]),
119 | }
120 | },
121 | // Hundreds.
122 | 100..=999 => {
123 | let hundreds = number / 100;
124 | let rest = number % 100;
125 |
126 | match rest {
127 | 0 => format!("{}{sep}hundred", DIGITS[hundreds]),
128 | rest => format!("{}{sep}hundred{sep}and{}", DIGITS[hundreds], encode(rest, sep)),
129 | }
130 | },
131 | // Other numbers.
132 | _ => {
133 | let (div, order) = iter::successors(Some(1usize), |n| n.checked_mul(1_000))
134 | .zip(ORDERS)
135 | .find(|&(n, _)| n > number / 1_000)
136 | .unwrap();
137 |
138 | let upper = number / div;
139 | let rest = number % div;
140 |
141 | match rest {
142 | 0 => format!("{}{sep}{}", encode(upper, sep), order),
143 | _ => format!("{}{sep}{}{sep}{}", encode(upper, sep), order, encode(rest, sep)),
144 | }
145 | },
146 | }
147 | }
148 |
149 | /// Encodes the given ordinal `number` as a [string] using the given `separator` between words.
150 | ///
151 | /// # Examples
152 | /// ```no_run
153 | /// # fn encode_ordinal(number: usize, separator: char) -> String { "".to_owned() }
154 | /// assert_eq!(encode_ordinal(1_782, ' '), "one thousand seven hundred and eighty second");
155 | /// assert_eq!(encode_ordinal(93, '_'), "ninety_third");
156 | /// ```
157 | ///
158 | /// [string]: String
159 | pub fn encode_ordinal(number: usize, separator: char) -> String {
160 | let sep = separator;
161 |
162 | match number {
163 | // Digits and teens.
164 | 0..=19 => DIGITS_ORDINAL[number].to_owned(),
165 | // Other tens.
166 | 20..=99 => {
167 | // Tens start at twenty, so subtract 2.
168 | let tens = (number / 10) - 2;
169 | let digits = number % 10;
170 |
171 | match digits {
172 | 0 => TENS_ORDINAL[tens].to_owned(),
173 | _ => format!("{}{sep}{}", TENS[tens], DIGITS_ORDINAL[digits]),
174 | }
175 | },
176 | // Hundreds.
177 | 100..=999 => {
178 | let hundreds = number / 100;
179 | let rest = number % 100;
180 |
181 | match rest {
182 | 0 => format!("{}{sep}hundredth", DIGITS[hundreds]),
183 | _ => format!(
184 | "{}{sep}hundred{sep}and{sep}{}",
185 | DIGITS[hundreds],
186 | encode_ordinal(rest, sep)
187 | ),
188 | }
189 | },
190 | // Other numbers.
191 | _ => {
192 | let (div, (order, order_ordinal)) = iter::successors(Some(1usize), |n| n.checked_mul(1_000))
193 | .zip(ORDERS.iter().zip(ORDERS_ORDINAL))
194 | .find(|&(n, _)| n > number / 1_000)
195 | .unwrap();
196 |
197 | let upper = number / div;
198 | let rest = number % div;
199 |
200 | match rest {
201 | 0 => format!("{}{sep}{}", encode(upper, sep), order_ordinal),
202 | _ => format!("{}{sep}{}{sep}{}", encode(upper, sep), order, encode_ordinal(rest, sep)),
203 | }
204 | },
205 | }
206 | }
207 |
--------------------------------------------------------------------------------
/publish-docs:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # This is a short script to build `aquariwm-server`'s documentation and publish it to GitHub Pages.
4 | # You can see the GitHub Pages site at https://docs.aquariwm.org/
5 |
6 | cargo clean
7 | cargo doc
8 | rm -rf ./docs
9 | echo "" > ./target/doc/index.html
10 | cp -r ./target/doc ./docs
11 |
--------------------------------------------------------------------------------
/rust-toolchain.toml:
--------------------------------------------------------------------------------
1 | [toolchain]
2 | channel = "nightly"
3 |
--------------------------------------------------------------------------------
/src/cli.rs:
--------------------------------------------------------------------------------
1 | // This Source Code Form is subject to the terms of the Mozilla Public
2 | // License, v. 2.0. If a copy of the MPL was not distributed with this
3 | // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4 |
5 | use clap::Parser;
6 |
7 | #[derive(Debug, Parser)]
8 | pub struct Cli {
9 | /// Whether AquariWM should be launched in a testing window.
10 | #[cfg(feature = "testing")]
11 | #[arg(long, alias = "test")]
12 | pub testing: bool,
13 |
14 | /// Disables `testing`.
15 | #[cfg(feature = "testing")]
16 | #[arg(long = "no-testing", alias = "no-test", overrides_with = "testing")]
17 | pub no_testing: bool,
18 |
19 | #[arg(long = "window-gap", alias = "gap")]
20 | /// The gap between windows in a tiling layout.
21 | pub window_gap: Option,
22 |
23 | #[command(subcommand)]
24 | pub subcommand: Subcommand,
25 | }
26 |
27 | impl Cli {
28 | /// Returns whether testing is enabled.
29 | #[inline]
30 | pub const fn testing(&self) -> bool {
31 | #[cfg(feature = "testing")]
32 | if cfg!(debug_assertions) {
33 | !self.no_testing
34 | } else {
35 | self.testing
36 | }
37 |
38 | #[cfg(not(feature = "testing"))]
39 | false
40 | }
41 | }
42 |
43 | #[derive(Debug, clap::Subcommand)]
44 | pub enum Subcommand {
45 | /// Launch AquariWM running in Wayland mode.
46 | #[cfg(feature = "wayland")]
47 | Wayland,
48 | /// Launch AquariWM running in X11 mode.
49 | #[cfg(feature = "x11")]
50 | X11,
51 | }
52 |
--------------------------------------------------------------------------------
/src/display_server.rs:
--------------------------------------------------------------------------------
1 | // This Source Code Form is subject to the terms of the Mozilla Public
2 | // License, v. 2.0. If a copy of the MPL was not distributed with this
3 | // file, You can obtain one at https://mozilla.org/MPL/2.0/.
4 |
5 | use std::{error::Error, future::Future};
6 |
7 | #[cfg(feature = "wayland")]
8 | pub use wayland::Wayland;
9 | #[cfg(feature = "x11")]
10 | pub use x11::X11;
11 |
12 | use crate::layout::LayoutSettings;
13 |
14 | #[cfg(feature = "wayland")]
15 | pub mod wayland;
16 | #[cfg(feature = "x11")]
17 | pub mod x11;
18 |
19 | /// An implementation of AquariWM for a particular display server (i.e. X11 or Wayland).
20 | pub trait DisplayServer {
21 | /// The return type used by the display server's [`run`] function.
22 | ///
23 | /// [`run`]: Self::run
24 | type Output;
25 | /// The name of the display server (e.g. `"X11"`).
26 | const NAME: &'static str;
27 |
28 | /// Runs the AquariWM implementation for this display server.
29 | fn run(testing: bool, settings: LayoutSettings) -> Self::Output;
30 |
31 | /// Returns AquariWM's title, formatted with the display server [`NAME`].
32 | ///
33 | /// This can be used for the title of the testing window, for example.
34 | fn title() -> String {
35 | format!("AquariWM ({})", Self::NAME)
36 | }
37 | }
38 |
39 | /// A [display server] whose [`run`] function does not return a [future].
40 | ///
41 | /// [display server]: DisplayServer
42 | /// [future]: Future
43 | /// [`run`]: Self::run
44 | #[cfg_attr(feature = "async", doc = "", doc = " # See also", doc = " - [AsyncDisplayServer]")]
45 | pub trait SyncDisplayServer: DisplayServer