Author SHA1 Message Date
outfoxxed da4ea5ff95 crash: switch to cpptrace from breakpad 2026-03-02 09:58:26 -08:00
141 changed files with 1601 additions and 5888 deletions

No files matched your search

-1
View File
@@ -20,7 +20,6 @@ Checks: >
-cppcoreguidelines-avoid-do-while, -cppcoreguidelines-avoid-do-while,
-cppcoreguidelines-pro-type-reinterpret-cast, -cppcoreguidelines-pro-type-reinterpret-cast,
-cppcoreguidelines-pro-type-vararg, -cppcoreguidelines-pro-type-vararg,
-cppcoreguidelines-pro-type-union-access,
-cppcoreguidelines-use-enum-class, -cppcoreguidelines-use-enum-class,
google-global-names-in-headers, google-global-names-in-headers,
google-readability-casting, google-readability-casting,
+78 -13
View File
@@ -1,17 +1,82 @@
name: Crash Report (v1) name: Crash Report (v1)
description: Quickshell has crashed (old) description: Quickshell has crashed
labels: ["unactionable"] labels: ["bug", "crash"]
body: body:
- type: markdown - type: textarea
id: crashinfo
attributes: attributes:
value: | label: General crash information
Thank you for taking the time to click the report button. description: |
At this point most of the worst issues in 0.2.1 and before have been fixed and we are Paste the contents of the `info.txt` file in your crash folder here.
preparing for a new release. Please do not submit this report. value: "<details> <summary>General information</summary>
- type: checkboxes
id: donotcheck
```
<Paste the contents of the file here inside of the triple backticks>
```
</details>"
validations:
required: true
- type: textarea
id: userinfo
attributes: attributes:
label: Read the text above. Do not submit the report. label: What caused the crash
options: description: |
- label: Yes I want this report to be deleted. Any information likely to help debug the crash. What were you doing when the crash occurred,
required: true what changes did you make, can you get it to happen again?
- type: textarea
id: dump
attributes:
label: Minidump
description: |
Attach `minidump.dmp.log` here. If it is too big to upload, compress it.
You may skip this step if quickshell crashed while processing a password
or other sensitive information. If you skipped it write why instead.
validations:
required: true
- type: textarea
id: logs
attributes:
label: Log file
description: |
Attach `log.qslog.log` here. If it is too big to upload, compress it.
You can preview the log if you'd like using `quickshell read-log <path-to-log>`.
validations:
required: true
- type: textarea
id: config
attributes:
label: Configuration
description: |
Attach your configuration here, preferrably in full (not just one file).
Compress it into a zip, tar, etc.
This will help us reproduce the crash ourselves.
- type: textarea
id: bt
attributes:
label: Backtrace
description: |
If you have gdb installed and use systemd, or otherwise know how to get a backtrace,
we would appreciate one. (You may have gdb installed without knowing it)
1. Run `coredumpctl debug <pid>` where `pid` is the number shown after "Crashed process ID"
in the crash reporter.
2. Once it loads, type `bt -full` (then enter)
3. Copy the output and attach it as a file or in a spoiler.
- type: textarea
id: exe
attributes:
label: Executable
description: |
If the crash folder contains a executable.txt file, upload it here. If not you can ignore this field.
If it is too big to upload, compress it.
Note: executable.txt is the quickshell binary. It has a .txt extension due to github's limitations on
filetypes.
+4 -4
View File
@@ -9,21 +9,21 @@ body:
description: | description: |
Any information likely to help debug the crash. What were you doing when the crash occurred, Any information likely to help debug the crash. What were you doing when the crash occurred,
what changes did you make, can you get it to happen again? what changes did you make, can you get it to happen again?
- type: upload - type: textarea
id: report id: report
attributes: attributes:
label: Report file label: Report file
description: Attach `report.txt` here. description: Attach `report.txt` here.
validations: validations:
required: true required: true
- type: upload - type: textarea
id: logs id: logs
attributes: attributes:
label: Log file label: Log file
description: | description: |
Attach `log.qslog.log` here. If it is too big to upload, compress it. Attach `log.qslog.log` here. If it is too big to upload, compress it.
You can preview the log if you'd like using `qs log <path-to-log> -r '*=true'`. You can preview the log if you'd like using `quickshell read-log <path-to-log>`.
validations: validations:
required: true required: true
- type: textarea - type: textarea
@@ -31,7 +31,7 @@ body:
attributes: attributes:
label: Configuration label: Configuration
description: | description: |
Attach or link your configuration here, preferrably in full (not just one file). Attach your configuration here, preferrably in full (not just one file).
Compress it into a zip, tar, etc. Compress it into a zip, tar, etc.
This will help us reproduce the crash ourselves. This will help us reproduce the crash ourselves.
+12 -11
View File
@@ -15,7 +15,15 @@ Please make this descriptive enough to identify your specific package, for examp
- `Nixpkgs` - `Nixpkgs`
- `Fedora COPR (errornointernet/quickshell)` - `Fedora COPR (errornointernet/quickshell)`
If you are forking quickshell, please change `CRASHREPORT_URL` to your own issue tracker. `-DDISTRIBUTOR_DEBUGINFO_AVAILABLE=YES/NO`
If we can retrieve binaries and debug information for the package without actually running your
distribution (e.g. from an website), and you would like to strip the binary, please set this to `YES`.
If we cannot retrieve debug information, please set this to `NO` and
**ensure you aren't distributing stripped (non debuggable) binaries**.
In both cases you should build with `-DCMAKE_BUILD_TYPE=RelWithDebInfo` (then split or keep the debuginfo).
### QML Module dir ### QML Module dir
Currently all QML modules are statically linked to quickshell, but this is where Currently all QML modules are statically linked to quickshell, but this is where
@@ -33,7 +41,6 @@ Quickshell has a set of base dependencies you will always need, names vary by di
- `cmake` - `cmake`
- `qt6base` - `qt6base`
- `qt6declarative` - `qt6declarative`
- `libdrm`
- `qtshadertools` (build-time) - `qtshadertools` (build-time)
- `spirv-tools` (build-time) - `spirv-tools` (build-time)
- `pkg-config` (build-time) - `pkg-config` (build-time)
@@ -67,14 +74,7 @@ To disable: `-DCRASH_HANDLER=OFF`
Dependencies: `cpptrace` Dependencies: `cpptrace`
Note: `-DVENDOR_CPPTRACE=ON` can be set to vendor cpptrace using FetchContent. Note: `-DVENDOR_CPPTRACE=ON` can be set to vendor cpptrace using FetchContent.
This still requires `libunwind` to build.
When using FetchContent, `libunwind` is required, and `libdwarf` can be provided by the
package manager or fetched with FetchContent.
*Please ensure binaries have usable symbols.* We do not necessarily need full debuginfo, but
leaving symbols in the binary is extremely helpful. You can check if symbols are useful
by sending a SIGSEGV to the process and ensuring symbols for the quickshell binary are present
in the trace.
### Jemalloc ### Jemalloc
We recommend leaving Jemalloc enabled as it will mask memory fragmentation caused We recommend leaving Jemalloc enabled as it will mask memory fragmentation caused
@@ -147,6 +147,7 @@ Enables streaming video from monitors and toplevel windows through various proto
To disable: `-DSCREENCOPY=OFF` To disable: `-DSCREENCOPY=OFF`
Dependencies: Dependencies:
- `libdrm`
- `libgbm` - `libgbm`
- `vulkan-headers` (build-time) - `vulkan-headers` (build-time)
@@ -242,7 +243,7 @@ Only `ninja` builds are tested, but makefiles may work.
#### Configuring the build #### Configuring the build
```sh ```sh
$ cmake -GNinja -B build -DCMAKE_BUILD_TYPE=Release [additional disable flags from above here] $ cmake -GNinja -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo [additional disable flags from above here]
``` ```
Note that features you do not supply dependencies for MUST be disabled with their associated flags Note that features you do not supply dependencies for MUST be disabled with their associated flags
+3 -10
View File
@@ -1,11 +1,7 @@
cmake_minimum_required(VERSION 3.20) cmake_minimum_required(VERSION 3.20)
project(quickshell VERSION "0.2.1" LANGUAGES CXX C) project(quickshell VERSION "0.2.1" LANGUAGES CXX C)
set(UNRELEASED_FEATURES set(UNRELEASED_FEATURES)
"network.2"
"colorquant-imagerect"
"window-parent"
)
set(QT_MIN_VERSION "6.6.0") set(QT_MIN_VERSION "6.6.0")
set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD 20)
@@ -13,9 +9,6 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(QS_BUILD_OPTIONS "") set(QS_BUILD_OPTIONS "")
# should be changed for forks
set(CRASHREPORT_URL "https://github.com/outfoxxed/quickshell/issues/new?template=crash2.yml" CACHE STRING "Bugreport URL")
function(boption VAR NAME DEFAULT) function(boption VAR NAME DEFAULT)
cmake_parse_arguments(PARSE_ARGV 3 arg "" "REQUIRES" "") cmake_parse_arguments(PARSE_ARGV 3 arg "" "REQUIRES" "")
@@ -47,6 +40,7 @@ string(APPEND QS_BUILD_OPTIONS " Distributor: ${DISTRIBUTOR}")
message(STATUS "Quickshell configuration") message(STATUS "Quickshell configuration")
message(STATUS " Distributor: ${DISTRIBUTOR}") message(STATUS " Distributor: ${DISTRIBUTOR}")
boption(DISTRIBUTOR_DEBUGINFO_AVAILABLE "Distributor provided debuginfo" NO)
boption(NO_PCH "Disable precompild headers (dev)" OFF) boption(NO_PCH "Disable precompild headers (dev)" OFF)
boption(BUILD_TESTING "Build tests (dev)" OFF) boption(BUILD_TESTING "Build tests (dev)" OFF)
boption(ASAN "ASAN (dev)" OFF) # note: better output with gcc than clang boption(ASAN "ASAN (dev)" OFF) # note: better output with gcc than clang
@@ -91,9 +85,8 @@ include(cmake/util.cmake)
add_compile_options(-Wall -Wextra -Wno-vla-cxx-extension) add_compile_options(-Wall -Wextra -Wno-vla-cxx-extension)
# pipewire defines these, breaking PCH # pipewire defines this, breaking PCH
add_compile_definitions(_REENTRANT) add_compile_definitions(_REENTRANT)
add_compile_options(-fno-strict-overflow)
if (FRAME_POINTERS) if (FRAME_POINTERS)
add_compile_options(-fno-omit-frame-pointer) add_compile_options(-fno-omit-frame-pointer)
+221 -26
View File
@@ -1,40 +1,235 @@
# Contributing # Contributing / Development
Instructions for development setup and upstreaming patches.
Thank you for taking the time to contribute. If you just want to build or package quickshell see [BUILD.md](BUILD.md).
To ensure nobody's time is wasted, please follow the rules below.
## Acceptable Code Contributions ## Development
- All changes submitted MUST be **fully understood by the submitter**. If you do not know why or how Install the dependencies listed in [BUILD.md](BUILD.md).
your change works, do not submit it to be merged. You must be able to explain your reasoning You probably want all of them even if you don't use all of them
for every change. to ensure tests work correctly and avoid passing a bunch of configure
flags when you need to wipe the build directory.
- Changes MUST be submitted by a human who will be responsible for them. Changes submitted without Quickshell also uses `just` for common development command aliases.
a human in the loop such as automated tooling and AI Agents are **strictly disallowed**. Accounts
responsible for such contribution attempts **will be banned**.
- Changes MUST respect Quickshell's license and the license of any source works. Changes including The dependencies are also available as a nix shell or nix flake which we recommend
code from any other works must disclose the source of the code, explain why it was used, and using with nix-direnv.
ensure the license is compatible.
- Changes must follow the guidelines outlined in [HACKING.md](HACKING.md) for style and substance. Common aliases:
- `just configure [<debug|release> [extra cmake args]]` (note that you must specify debug/release to specify extra args)
- `just build` - runs the build, configuring if not configured already.
- `just run [args]` - runs quickshell with the given arguments
- `just clean` - clean up build artifacts. `just clean build` is somewhat common.
- Changes must stand on their own as a unit. Do not make multiple unrelated changes in one PR. ### Formatting
Changes depending on prior merges should be marked as a draft. All contributions should be formatted similarly to what already exists.
Group related functionality together.
## Acceptable Non-code Contributions Run the formatter using `just fmt`.
If the results look stupid, fix the clang-format file if possible,
or disable clang-format in the affected area
using `// clang-format off` and `// clang-format on`.
- Bug and crash reports. You must follow the instructions in the issue templates and provide the #### Style preferences not caught by clang-format
information requested. These are flexible. You can ignore them if it looks or works better to
for one reason or another.
- Feature requests can be made via Issues. Please check to ensure nobody else has requested the same feature. Use `auto` if the type of a variable can be deduced automatically, instead of
redeclaring the returned value's type. Additionally, auto should be used when a
constructor takes arguments.
- Do not make insubstantial or pointless changes. ```cpp
auto x = <expr>; // ok
auto x = QString::number(3); // ok
QString x; // ok
QString x = "foo"; // ok
auto x = QString("foo"); // ok
- Changes to project rules / policy / governance will not be entertained, except from significant auto x = QString(); // avoid
long-term contributors. These changes should not be addressed through contribution channels. QString x(); // avoid
QString x("foo"); // avoid
```
## Merge timelines Put newlines around logical units of code, and after closing braces. If the
most reasonable logical unit of code takes only a single line, it should be
merged into the next single line logical unit if applicable.
```cpp
// multiple units
auto x = <expr>; // unit 1
auto y = <expr>; // unit 2
We handle work for the most part on a push basis. If your PR has been ignored for a while auto x = <expr>; // unit 1
and is still relevant please bump it. emit this->y(); // unit 2
auto x1 = <expr>; // unit 1
auto x2 = <expr>; // unit 1
auto x3 = <expr>; // unit 1
auto y1 = <expr>; // unit 2
auto y2 = <expr>; // unit 2
auto y3 = <expr>; // unit 2
// one unit
auto x = <expr>;
if (x...) {
// ...
}
// if more than one variable needs to be used then add a newline
auto x = <expr>;
auto y = <expr>;
if (x && y) {
// ...
}
```
Class formatting:
```cpp
//! Doc comment summary
/// Doc comment body
class Foo: public QObject {
// The Q_OBJECT macro comes first. Macros are ; terminated.
Q_OBJECT;
QML_ELEMENT;
QML_CLASSINFO(...);
// Properties must stay on a single line or the doc generator won't be able to pick them up
Q_PROPERTY(...);
/// Doc comment
Q_PROPERTY(...);
/// Doc comment
Q_PROPERTY(...);
public:
// Classes should have explicit constructors if they aren't intended to
// implicitly cast. The constructor can be inline in the header if it has no body.
explicit Foo(QObject* parent = nullptr): QObject(parent) {}
// Instance functions if applicable.
static Foo* instance();
// Member functions unrelated to properties come next
void function();
void function();
void function();
// Then Q_INVOKABLEs
Q_INVOKABLE function();
/// Doc comment
Q_INVOKABLE function();
/// Doc comment
Q_INVOKABLE function();
// Then property related functions, in the order (bindable, getter, setter).
// Related functions may be included here as well. Function bodies may be inline
// if they are a single expression. There should be a newline between each
// property's methods.
[[nodiscard]] QBindable<T> bindableFoo() { return &this->bFoo; }
[[nodiscard]] T foo() const { return this->foo; }
void setFoo();
[[nodiscard]] T bar() const { return this->foo; }
void setBar();
signals:
// Signals that are not property change related go first.
// Property change signals go in property definition order.
void asd();
void asd2();
void fooChanged();
void barChanged();
public slots:
// generally Q_INVOKABLEs are preferred to public slots.
void slot();
private slots:
// ...
private:
// statics, then functions, then fields
static const foo BAR;
static void foo();
void foo();
void bar();
// property related members are prefixed with `m`.
QString mFoo;
QString bar;
// Bindables go last and should be prefixed with `b`.
Q_OBJECT_BINDABLE_PROPERTY(Foo, QString, bFoo, &Foo::fooChanged);
};
```
### Linter
All contributions should pass the linter.
Note that running the linter requires disabling precompiled
headers and including the test codepaths:
```sh
$ just configure debug -DNO_PCH=ON -DBUILD_TESTING=ON
$ just lint-changed
```
If the linter is complaining about something that you think it should not,
please disable the lint in your MR and explain your reasoning if it isn't obvious.
### Tests
If you feel like the feature you are working on is very complex or likely to break,
please write some tests. We will ask you to directly if you send in an MR for an
overly complex or breakable feature.
At least all tests that passed before your changes should still be passing
by the time your contribution is ready.
You can run the tests using `just test` but you must enable them first
using `-DBUILD_TESTING=ON`.
### Documentation
Most of quickshell's documentation is automatically generated from the source code.
You should annotate `Q_PROPERTY`s and `Q_INVOKABLE`s with doc comments. Note that the parser
cannot handle random line breaks and will usually require you to disable clang-format if the
lines are too long.
Before submitting an MR, if adding new features please make sure the documentation is generated
reasonably using the `quickshell-docs` repo. We recommend checking it out at `/docs` in this repo.
Doc comments take the form `///` or `///!` (summary) and work with markdown.
You can reference other types using the `@@[Module.][Type.][member]` shorthand
where all parts are optional. If module or type are not specified they will
be inferred as the current module. Member can be a `property`, `function()` or `signal(s)`.
Look at existing code for how it works.
Quickshell modules additionally have a `module.md` file which contains a summary, description,
and list of headers to scan for documentation.
## Contributing
### Commits
Please structure your commit messages as `scope[!]: commit` where
the scope is something like `core` or `service/mpris`. (pick what has been
used historically or what makes sense if new). Add `!` for changes that break
existing APIs or functionality.
Commit descriptions should contain a summary of the changes if they are not
sufficiently addressed in the commit message.
Please squash/rebase additions or edits to previous changes and follow the
commit style to keep the history easily searchable at a glance.
Depending on the change, it is often reasonable to squash it into just
a single commit. (If you do not follow this we will squash your changes
for you.)
### Sending patches
You may contribute by submitting a pull request on github, asking for
an account on our git server, or emailing patches / git bundles
directly to `[email protected]`.
### Getting help
If you're getting stuck, you can come talk to us in the
[quickshell-development matrix room](https://matrix.to/#/#quickshell-development:outfoxxed.me)
for help on implementation, conventions, etc.
Feel free to ask for advice early in your implementation if you are
unsure.
-226
View File
@@ -1,226 +0,0 @@
## Development
Install the dependencies listed in [BUILD.md](BUILD.md).
You probably want all of them even if you don't use all of them
to ensure tests work correctly and avoid passing a bunch of configure
flags when you need to wipe the build directory.
The dependencies are also available as a nix shell or nix flake which we recommend
using with nix-direnv.
Quickshell uses `just` for common development command aliases.
Common aliases:
- `just configure [<debug|release> [extra cmake args]]` (note that you must specify debug/release to specify extra args)
- `just build` - runs the build, configuring if not configured already.
- `just run [args]` - runs quickshell with the given arguments
- `just clean` - clean up build artifacts. `just clean build` is somewhat common.
### Formatting
All contributions should be formatted similarly to what already exists.
Group related functionality together.
Run the formatter using `just fmt`.
If the results look stupid, fix the clang-format file if possible,
or disable clang-format in the affected area
using `// clang-format off` and `// clang-format on`.
#### Style preferences not caught by clang-format
These are flexible. You can ignore them if it looks or works better to
for one reason or another.
Use `auto` if the type of a variable can be deduced automatically, instead of
redeclaring the returned value's type. Additionally, auto should be used when a
constructor takes arguments.
```cpp
auto x = <expr>; // ok
auto x = QString::number(3); // ok
QString x; // ok
QString x = "foo"; // ok
auto x = QString("foo"); // ok
auto x = QString(); // avoid
QString x(); // avoid
QString x("foo"); // avoid
```
Put newlines around logical units of code, and after closing braces. If the
most reasonable logical unit of code takes only a single line, it should be
merged into the next single line logical unit if applicable.
```cpp
// multiple units
auto x = <expr>; // unit 1
auto y = <expr>; // unit 2
auto x = <expr>; // unit 1
emit this->y(); // unit 2
auto x1 = <expr>; // unit 1
auto x2 = <expr>; // unit 1
auto x3 = <expr>; // unit 1
auto y1 = <expr>; // unit 2
auto y2 = <expr>; // unit 2
auto y3 = <expr>; // unit 2
// one unit
auto x = <expr>;
if (x...) {
// ...
}
// if more than one variable needs to be used then add a newline
auto x = <expr>;
auto y = <expr>;
if (x && y) {
// ...
}
```
Class formatting:
```cpp
//! Doc comment summary
/// Doc comment body
class Foo: public QObject {
// The Q_OBJECT macro comes first. Macros are ; terminated.
Q_OBJECT;
QML_ELEMENT;
QML_CLASSINFO(...);
// Properties must stay on a single line or the doc generator won't be able to pick them up
Q_PROPERTY(...);
/// Doc comment
Q_PROPERTY(...);
/// Doc comment
Q_PROPERTY(...);
public:
// Classes should have explicit constructors if they aren't intended to
// implicitly cast. The constructor can be inline in the header if it has no body.
explicit Foo(QObject* parent = nullptr): QObject(parent) {}
// Instance functions if applicable.
static Foo* instance();
// Member functions unrelated to properties come next
void function();
void function();
void function();
// Then Q_INVOKABLEs
Q_INVOKABLE function();
/// Doc comment
Q_INVOKABLE function();
/// Doc comment
Q_INVOKABLE function();
// Then property related functions, in the order (bindable, getter, setter).
// Related functions may be included here as well. Function bodies may be inline
// if they are a single expression. There should be a newline between each
// property's methods.
[[nodiscard]] QBindable<T> bindableFoo() { return &this->bFoo; }
[[nodiscard]] T foo() const { return this->foo; }
void setFoo();
[[nodiscard]] T bar() const { return this->foo; }
void setBar();
signals:
// Signals that are not property change related go first.
// Property change signals go in property definition order.
void asd();
void asd2();
void fooChanged();
void barChanged();
public slots:
// generally Q_INVOKABLEs are preferred to public slots.
void slot();
private slots:
// ...
private:
// statics, then functions, then fields
static const foo BAR;
static void foo();
void foo();
void bar();
// property related members are prefixed with `m`.
QString mFoo;
QString bar;
// Bindables go last and should be prefixed with `b`.
Q_OBJECT_BINDABLE_PROPERTY(Foo, QString, bFoo, &Foo::fooChanged);
};
```
Use lowercase .h suffixed Qt headers, e.g. `<qwindow.h>` over `<QWindow>`.
### Linter
All contributions should pass the linter.
Note that running the linter requires disabling precompiled
headers and including the test codepaths:
```sh
$ just configure debug -DNO_PCH=ON -DBUILD_TESTING=ON
$ just lint-changed
```
If the linter is complaining about something that you think it should not,
please disable the lint in your MR and explain your reasoning if it isn't obvious.
### Tests
If you feel like the feature you are working on is very complex or likely to break,
please write some tests. We will ask you to directly if you send in an MR for an
overly complex or breakable feature.
At least all tests that passed before your changes should still be passing
by the time your contribution is ready.
You can run the tests using `just test` but you must enable them first
using `-DBUILD_TESTING=ON`.
### Documentation
Most of quickshell's documentation is automatically generated from the source code.
You should annotate `Q_PROPERTY`s and `Q_INVOKABLE`s with doc comments. Note that the parser
cannot handle random line breaks and will usually require you to disable clang-format if the
lines are too long.
Make sure new files containing doc comments are added to a `module.md` file.
See existing module files for reference.
Doc comments take the form `///` or `///!` (summary) and work with markdown.
You can reference other types using the `@@[Module.][Type.][member]` shorthand
where all parts are optional. If module or type are not specified they will
be inferred as the current module. Member can be a `property`, `function()` or `signal(s)`.
Look at existing code for how it works.
If you have made a user visible change since the last tagged release, describe it in
[changelog/next.md](changelog/next.md).
## Contributing
### Commits
Please structure your commit messages as `scope: commit` where
the scope is something like `core` or `service/mpris`. (pick what has been
used historically or what makes sense if new).
Commit descriptions should contain a summary of the changes if they are not
sufficiently addressed in the commit message.
Please squash/rebase additions or edits to previous changes and follow the
commit style to keep the history easily searchable at a glance.
Depending on the change, it is often reasonable to squash it into just
a single commit. (If you do not follow this we will squash your changes
for you.)
### Getting help
If you're getting stuck, you can come talk to us in the
[quickshell-development matrix room](https://matrix.to/#/#quickshell-development:outfoxxed.me)
for help on implementation, conventions, etc. There is also a bridged [discord server](https://discord.gg/UtZeT3xNyT).
Feel free to ask for advice early in your implementation if you are
unsure.
+1 -1
View File
@@ -13,7 +13,7 @@ lint-changed:
git diff --name-only HEAD | grep "^.*\.cpp\$" | parallel -j$(nproc) --no-notice --will-cite --tty --bar clang-tidy --load={{ env_var("TIDYFOX") }} git diff --name-only HEAD | grep "^.*\.cpp\$" | parallel -j$(nproc) --no-notice --will-cite --tty --bar clang-tidy --load={{ env_var("TIDYFOX") }}
lint-staged: lint-staged:
git diff --staged --name-only --diff-filter=d HEAD | grep "^.*\.cpp\$" | parallel -j$(nproc) --no-notice --will-cite --tty --bar clang-tidy --load={{ env_var("TIDYFOX") }} git diff --staged --name-only HEAD | grep "^.*\.cpp\$" | parallel -j$(nproc) --no-notice --will-cite --tty --bar clang-tidy --load={{ env_var("TIDYFOX") }}
configure target='debug' *FLAGS='': configure target='debug' *FLAGS='':
cmake -GNinja -B {{builddir}} \ cmake -GNinja -B {{builddir}} \
+1 -3
View File
@@ -7,9 +7,7 @@ This repo is hosted at:
- https://github.com/quickshell-mirror/quickshell - https://github.com/quickshell-mirror/quickshell
# Contributing / Development # Contributing / Development
- [HACKING.md](HACKING.md) - Development instructions and policy. See [CONTRIBUTING.md](CONTRIBUTING.md) for details.
- [CONTRIBUTING.md](CONTRIBUTING.md) - Contribution policy.
- [BUILD.md](BUILD.md) - Packaging and build instructions.
#### License #### License
+1 -26
View File
@@ -20,17 +20,12 @@ set shell id.
- Added the ability to handle move and resize events to FloatingWindow. - Added the ability to handle move and resize events to FloatingWindow.
- Pipewire service now reconnects if pipewire dies or a protocol error occurs. - Pipewire service now reconnects if pipewire dies or a protocol error occurs.
- Added pipewire audio peak detection. - Added pipewire audio peak detection.
- Added network management support. - Added initial support for network management.
- Added support for grabbing focus from popup windows. - Added support for grabbing focus from popup windows.
- Added support for IPC signal listeners. - Added support for IPC signal listeners.
- Added Quickshell version checking and version gated preprocessing. - Added Quickshell version checking and version gated preprocessing.
- Added a way to detect if an icon is from the system icon theme or not. - Added a way to detect if an icon is from the system icon theme or not.
- Added vulkan support to screencopy. - Added vulkan support to screencopy.
- Added generic WindowManager interface implementing ext-workspace.
- Added ext-background-effect window blur support.
- Added per-corner radius support to Region.
- Added ColorQuantizer region selection.
- Added dialog window support to FloatingWindow.
## Other Changes ## Other Changes
@@ -38,13 +33,6 @@ set shell id.
- IPC operations filter available instances to the current display connection by default. - IPC operations filter available instances to the current display connection by default.
- PwNodeLinkTracker ignores sound level monitoring programs. - PwNodeLinkTracker ignores sound level monitoring programs.
- Replaced breakpad with cpptrace. - Replaced breakpad with cpptrace.
- Reloads are prevented if no file content has changed.
- Added `QS_DISABLE_FILE_WATCHER` environment variable to disable file watching.
- Added `QS_DISABLE_CRASH_HANDLER` environment variable to disable crash handling.
- Added `QS_CRASHREPORT_URL` environment variable to allow overriding the crash reporter link.
- Added `AppId` pragma and `QS_APP_ID` environment variable to allow overriding the desktop application ID.
- Added `DropExpensiveFonts` pragma and `QS_DROP_EXPENSIVE_FONTS` environment variable which avoids loading fonts which may cause lag and excessive memory usage if many variants are used.
- Unrecognized pragmas are no longer a hard error for future backward compatibility.
## Bug Fixes ## Bug Fixes
@@ -55,7 +43,6 @@ set shell id.
- Fixed volumes not initializing if a pipewire device was already loaded before its node. - Fixed volumes not initializing if a pipewire device was already loaded before its node.
- Fixed hyprland active toplevel not resetting after window closes. - Fixed hyprland active toplevel not resetting after window closes.
- Fixed hyprland ipc window names and titles being reversed. - Fixed hyprland ipc window names and titles being reversed.
- Fixed a hyprland ipc crash when refreshing toplevels before workspaces.
- Fixed missing signals for system tray item title and description updates. - Fixed missing signals for system tray item title and description updates.
- Fixed asynchronous loaders not working after reload. - Fixed asynchronous loaders not working after reload.
- Fixed asynchronous loaders not working before window creation. - Fixed asynchronous loaders not working before window creation.
@@ -63,21 +50,9 @@ set shell id.
- Fixed ClippingRectangle related crashes. - Fixed ClippingRectangle related crashes.
- Fixed crashes when monitors are unplugged. - Fixed crashes when monitors are unplugged.
- Fixed crashes when default pipewire devices are lost. - Fixed crashes when default pipewire devices are lost.
- Fixed ToplevelManager not clearing activeToplevel on deactivation.
- Desktop action order is now preserved.
- Fixed partial socket reads in greetd and hyprland on slow machines.
- Worked around Qt bug causing crashes when plugging and unplugging monitors.
- Fixed HyprlandFocusGrab crashing if windows were destroyed after being passed to it.
- Fixed ScreencopyView pixelation when scaled.
- Fixed JsonAdapter crashing and providing bad data on read when using JsonObject.
- Fixed JsonAdapter sending unnecessary property changes for primitive values.
- Fixed JsonAdapter serialization for lists.
- Fixed pipewire crashes after hotplugging devices and changing default outputs.
## Packaging Changes ## Packaging Changes
- `glib` and `polkit` have been added as dependencies when compiling with polkit agent support. - `glib` and `polkit` have been added as dependencies when compiling with polkit agent support.
- `vulkan-headers` has been added as a build-time dependency for screencopy (Vulkan backend support). - `vulkan-headers` has been added as a build-time dependency for screencopy (Vulkan backend support).
- `breakpad` has been replaced by `cpptrace`, which is far easier to package, and the `CRASH_REPORTER` cmake variable has been replaced with `CRASH_HANDLER` to stop this from being easy to ignore. - `breakpad` has been replaced by `cpptrace`, which is far easier to package, and the `CRASH_REPORTER` cmake variable has been replaced with `CRASH_HANDLER` to stop this from being easy to ignore.
- `DISTRIBUTOR_DEBUGINFO_AVAILABLE` was removed as it is no longer important without breakpad.
- `libdrm` is now unconditionally required as a direct dependency.
+12 -10
View File
@@ -19,7 +19,6 @@
wayland-protocols, wayland-protocols,
wayland-scanner, wayland-scanner,
xorg, xorg,
libxcb ? xorg.libxcb,
libdrm, libdrm,
libgbm ? null, libgbm ? null,
vulkan-headers, vulkan-headers,
@@ -76,21 +75,24 @@
buildInputs = [ buildInputs = [
qt6.qtbase qt6.qtbase
qt6.qtdeclarative qt6.qtdeclarative
libdrm
cli11 cli11
] ]
++ lib.optional withQtSvg qt6.qtsvg ++ lib.optional withQtSvg qt6.qtsvg
++ lib.optional withCrashHandler (cpptrace.overrideAttrs (prev: { ++ lib.optionals withCrashHandler [
cmakeFlags = prev.cmakeFlags ++ [ (cpptrace.overrideAttrs (prev: {
"-DCPPTRACE_UNWIND_WITH_LIBUNWIND=TRUE" cmakeFlags = prev.cmakeFlags ++ [
]; "-DCPPTRACE_UNWIND_WITH_LIBUNWIND=TRUE"
buildInputs = prev.buildInputs ++ [ libunwind ]; ];
})) buildInputs = prev.buildInputs ++ [ libunwind ];
}))
libunwind
libdwarf
]
++ lib.optional withJemalloc jemalloc ++ lib.optional withJemalloc jemalloc
++ lib.optional (withWayland && lib.strings.compareVersions qt6.qtbase.version "6.10.0" == -1) qt6.qtwayland ++ lib.optional (withWayland && lib.strings.compareVersions qt6.qtbase.version "6.10.0" == -1) qt6.qtwayland
++ lib.optionals withWayland [ wayland wayland-protocols ] ++ lib.optionals withWayland [ wayland wayland-protocols ]
++ lib.optionals (withWayland && libgbm != null) [ libgbm vulkan-headers ] ++ lib.optionals (withWayland && libgbm != null) [ libdrm libgbm vulkan-headers ]
++ lib.optional withX11 libxcb ++ lib.optional withX11 xorg.libxcb
++ lib.optional withPam pam ++ lib.optional withPam pam
++ lib.optional withPipewire pipewire ++ lib.optional withPipewire pipewire
++ lib.optionals withPolkit [ polkit glib ]; ++ lib.optionals withPolkit [ polkit glib ];
-1
View File
@@ -11,7 +11,6 @@ add_subdirectory(window)
add_subdirectory(io) add_subdirectory(io)
add_subdirectory(widgets) add_subdirectory(widgets)
add_subdirectory(ui) add_subdirectory(ui)
add_subdirectory(windowmanager)
if (CRASH_HANDLER) if (CRASH_HANDLER)
add_subdirectory(crash) add_subdirectory(crash)
+6
View File
@@ -15,6 +15,12 @@ else()
set(CRASH_HANDLER_DEF 0) set(CRASH_HANDLER_DEF 0)
endif() endif()
if (DISTRIBUTOR_DEBUGINFO_AVAILABLE)
set(DEBUGINFO_AVAILABLE 1)
else()
set(DEBUGINFO_AVAILABLE 0)
endif()
configure_file(build.hpp.in build.hpp @ONLY ESCAPE_QUOTES) configure_file(build.hpp.in build.hpp @ONLY ESCAPE_QUOTES)
target_include_directories(quickshell-build INTERFACE ${CMAKE_CURRENT_BINARY_DIR}) target_include_directories(quickshell-build INTERFACE ${CMAKE_CURRENT_BINARY_DIR})
+1 -1
View File
@@ -8,10 +8,10 @@
#define QS_UNRELEASED_FEATURES "@UNRELEASED_FEATURES@" #define QS_UNRELEASED_FEATURES "@UNRELEASED_FEATURES@"
#define GIT_REVISION "@GIT_REVISION@" #define GIT_REVISION "@GIT_REVISION@"
#define DISTRIBUTOR "@DISTRIBUTOR@" #define DISTRIBUTOR "@DISTRIBUTOR@"
#define DISTRIBUTOR_DEBUGINFO_AVAILABLE @DEBUGINFO_AVAILABLE@
#define CRASH_HANDLER @CRASH_HANDLER_DEF@ #define CRASH_HANDLER @CRASH_HANDLER_DEF@
#define BUILD_TYPE "@CMAKE_BUILD_TYPE@" #define BUILD_TYPE "@CMAKE_BUILD_TYPE@"
#define COMPILER "@CMAKE_CXX_COMPILER_ID@ (@CMAKE_CXX_COMPILER_VERSION@)" #define COMPILER "@CMAKE_CXX_COMPILER_ID@ (@CMAKE_CXX_COMPILER_VERSION@)"
#define COMPILE_FLAGS "@CMAKE_CXX_FLAGS@" #define COMPILE_FLAGS "@CMAKE_CXX_FLAGS@"
#define BUILD_CONFIGURATION "@QS_BUILD_OPTIONS@" #define BUILD_CONFIGURATION "@QS_BUILD_OPTIONS@"
#define CRASHREPORT_URL "@CRASHREPORT_URL@"
// NOLINTEND // NOLINTEND
+1 -5
View File
@@ -1,4 +1,3 @@
pkg_check_modules(libdrm REQUIRED IMPORTED_TARGET libdrm)
qt_add_library(quickshell-core STATIC qt_add_library(quickshell-core STATIC
plugin.cpp plugin.cpp
shell.cpp shell.cpp
@@ -41,9 +40,6 @@ qt_add_library(quickshell-core STATIC
scriptmodel.cpp scriptmodel.cpp
colorquantizer.cpp colorquantizer.cpp
toolsupport.cpp toolsupport.cpp
streamreader.cpp
debuginfo.cpp
hook.cpp
) )
qt_add_qml_module(quickshell-core qt_add_qml_module(quickshell-core
@@ -56,7 +52,7 @@ qt_add_qml_module(quickshell-core
install_qml_module(quickshell-core) install_qml_module(quickshell-core)
target_link_libraries(quickshell-core PRIVATE Qt::Quick Qt::QuickPrivate Qt::Widgets quickshell-build PkgConfig::libdrm) target_link_libraries(quickshell-core PRIVATE Qt::Quick Qt::QuickPrivate Qt::Widgets quickshell-build)
qs_module_pch(quickshell-core SET large) qs_module_pch(quickshell-core SET large)
+5 -33
View File
@@ -13,7 +13,6 @@
#include <qnumeric.h> #include <qnumeric.h>
#include <qobject.h> #include <qobject.h>
#include <qqmllist.h> #include <qqmllist.h>
#include <qrect.h>
#include <qrgb.h> #include <qrgb.h>
#include <qthreadpool.h> #include <qthreadpool.h>
#include <qtmetamacros.h> #include <qtmetamacros.h>
@@ -25,15 +24,9 @@ namespace {
QS_LOGGING_CATEGORY(logColorQuantizer, "quickshell.colorquantizer", QtWarningMsg); QS_LOGGING_CATEGORY(logColorQuantizer, "quickshell.colorquantizer", QtWarningMsg);
} }
ColorQuantizerOperation::ColorQuantizerOperation( ColorQuantizerOperation::ColorQuantizerOperation(QUrl* source, qreal depth, qreal rescaleSize)
QUrl* source,
qreal depth,
QRect imageRect,
qreal rescaleSize
)
: source(source) : source(source)
, maxDepth(depth) , maxDepth(depth)
, imageRect(imageRect)
, rescaleSize(rescaleSize) { , rescaleSize(rescaleSize) {
this->setAutoDelete(false); this->setAutoDelete(false);
} }
@@ -44,11 +37,6 @@ void ColorQuantizerOperation::quantizeImage(const QAtomicInteger<bool>& shouldCa
this->colors.clear(); this->colors.clear();
auto image = QImage(this->source->toLocalFile()); auto image = QImage(this->source->toLocalFile());
if (this->imageRect.isValid()) {
image = image.copy(this->imageRect);
}
if ((image.width() > this->rescaleSize || image.height() > this->rescaleSize) if ((image.width() > this->rescaleSize || image.height() > this->rescaleSize)
&& this->rescaleSize > 0) && this->rescaleSize > 0)
{ {
@@ -210,27 +198,16 @@ void ColorQuantizer::setDepth(qreal depth) {
this->mDepth = depth; this->mDepth = depth;
emit this->depthChanged(); emit this->depthChanged();
if (this->componentCompleted && !this->mSource.isEmpty()) this->quantizeAsync(); if (this->componentCompleted) this->quantizeAsync();
} }
} }
void ColorQuantizer::setImageRect(QRect imageRect) {
if (this->mImageRect != imageRect) {
this->mImageRect = imageRect;
emit this->imageRectChanged();
if (this->componentCompleted && !this->mSource.isEmpty()) this->quantizeAsync();
}
}
void ColorQuantizer::resetImageRect() { this->setImageRect(QRect()); }
void ColorQuantizer::setRescaleSize(int rescaleSize) { void ColorQuantizer::setRescaleSize(int rescaleSize) {
if (this->mRescaleSize != rescaleSize) { if (this->mRescaleSize != rescaleSize) {
this->mRescaleSize = rescaleSize; this->mRescaleSize = rescaleSize;
emit this->rescaleSizeChanged(); emit this->rescaleSizeChanged();
if (this->componentCompleted && !this->mSource.isEmpty()) this->quantizeAsync(); if (this->componentCompleted) this->quantizeAsync();
} }
} }
@@ -244,13 +221,8 @@ void ColorQuantizer::quantizeAsync() {
if (this->liveOperation) this->cancelAsync(); if (this->liveOperation) this->cancelAsync();
qCDebug(logColorQuantizer) << "Starting color quantization asynchronously"; qCDebug(logColorQuantizer) << "Starting color quantization asynchronously";
this->liveOperation =
this->liveOperation = new ColorQuantizerOperation( new ColorQuantizerOperation(&this->mSource, this->mDepth, this->mRescaleSize);
&this->mSource,
this->mDepth,
this->mImageRect,
this->mRescaleSize
);
QObject::connect( QObject::connect(
this->liveOperation, this->liveOperation,
+1 -16
View File
@@ -5,7 +5,6 @@
#include <qproperty.h> #include <qproperty.h>
#include <qqmlintegration.h> #include <qqmlintegration.h>
#include <qqmlparserstatus.h> #include <qqmlparserstatus.h>
#include <qrect.h>
#include <qrunnable.h> #include <qrunnable.h>
#include <qtmetamacros.h> #include <qtmetamacros.h>
#include <qtypes.h> #include <qtypes.h>
@@ -17,7 +16,7 @@ class ColorQuantizerOperation
Q_OBJECT; Q_OBJECT;
public: public:
explicit ColorQuantizerOperation(QUrl* source, qreal depth, QRect imageRect, qreal rescaleSize); explicit ColorQuantizerOperation(QUrl* source, qreal depth, qreal rescaleSize);
void run() override; void run() override;
void tryCancel(); void tryCancel();
@@ -45,7 +44,6 @@ private:
QList<QColor> colors; QList<QColor> colors;
QUrl* source; QUrl* source;
qreal maxDepth; qreal maxDepth;
QRect imageRect;
qreal rescaleSize; qreal rescaleSize;
}; };
@@ -80,13 +78,6 @@ class ColorQuantizer
/// binary split of the color space /// binary split of the color space
Q_PROPERTY(qreal depth READ depth WRITE setDepth NOTIFY depthChanged); Q_PROPERTY(qreal depth READ depth WRITE setDepth NOTIFY depthChanged);
// clang-format off
/// Rectangle that the source image is cropped to.
///
/// Can be set to `undefined` to reset.
Q_PROPERTY(QRect imageRect READ imageRect WRITE setImageRect RESET resetImageRect NOTIFY imageRectChanged);
// clang-format on
/// The size to rescale the image to, when rescaleSize is 0 then no scaling will be done. /// The size to rescale the image to, when rescaleSize is 0 then no scaling will be done.
/// > [!NOTE] Results from color quantization doesn't suffer much when rescaling, it's /// > [!NOTE] Results from color quantization doesn't suffer much when rescaling, it's
/// > reccommended to rescale, otherwise the quantization process will take much longer. /// > reccommended to rescale, otherwise the quantization process will take much longer.
@@ -106,10 +97,6 @@ public:
[[nodiscard]] qreal depth() const { return this->mDepth; } [[nodiscard]] qreal depth() const { return this->mDepth; }
void setDepth(qreal depth); void setDepth(qreal depth);
[[nodiscard]] QRect imageRect() const { return this->mImageRect; }
void setImageRect(QRect imageRect);
void resetImageRect();
[[nodiscard]] qreal rescaleSize() const { return this->mRescaleSize; } [[nodiscard]] qreal rescaleSize() const { return this->mRescaleSize; }
void setRescaleSize(int rescaleSize); void setRescaleSize(int rescaleSize);
@@ -117,7 +104,6 @@ signals:
void colorsChanged(); void colorsChanged();
void sourceChanged(); void sourceChanged();
void depthChanged(); void depthChanged();
void imageRectChanged();
void rescaleSizeChanged(); void rescaleSizeChanged();
public slots: public slots:
@@ -131,7 +117,6 @@ private:
ColorQuantizerOperation* liveOperation = nullptr; ColorQuantizerOperation* liveOperation = nullptr;
QUrl mSource; QUrl mSource;
qreal mDepth = 0; qreal mDepth = 0;
QRect mImageRect;
qreal mRescaleSize = 0; qreal mRescaleSize = 0;
Q_OBJECT_BINDABLE_PROPERTY( Q_OBJECT_BINDABLE_PROPERTY(
-176
View File
@@ -1,176 +0,0 @@
#include "debuginfo.hpp"
#include <array>
#include <cstring>
#include <string_view>
#include <fcntl.h>
#include <qconfig.h>
#include <qcontainerfwd.h>
#include <qdebug.h>
#include <qfile.h>
#include <qfloat16.h>
#include <qhashfunctions.h>
#include <qscopeguard.h>
#include <qtversion.h>
#include <unistd.h>
#include <xf86drm.h>
#include "build.hpp"
extern char** environ; // NOLINT
namespace qs::debuginfo {
QString qsVersion() {
return QS_VERSION " (revision " GIT_REVISION ", distributed by " DISTRIBUTOR ")";
}
QString qtVersion() { return qVersion() % QStringLiteral(" (built against " QT_VERSION_STR ")"); }
QString gpuInfo() {
auto deviceCount = drmGetDevices2(0, nullptr, 0);
if (deviceCount < 0) return "Failed to get DRM device count: " % QString::number(deviceCount);
auto* devices = new drmDevicePtr[deviceCount];
auto devicesArrayGuard = qScopeGuard([&] { delete[] devices; });
auto r = drmGetDevices2(0, devices, deviceCount);
if (deviceCount < 0) return "Failed to get DRM devices: " % QString::number(r);
auto devicesGuard = qScopeGuard([&] {
for (auto i = 0; i != deviceCount; ++i) drmFreeDevice(&devices[i]); // NOLINT
});
QString info;
auto stream = QTextStream(&info);
for (auto i = 0; i != deviceCount; ++i) {
auto* device = devices[i]; // NOLINT
int deviceNodeType = -1;
if (device->available_nodes & (1 << DRM_NODE_RENDER)) deviceNodeType = DRM_NODE_RENDER;
else if (device->available_nodes & (1 << DRM_NODE_PRIMARY)) deviceNodeType = DRM_NODE_PRIMARY;
if (deviceNodeType == -1) continue;
auto* deviceNode = device->nodes[DRM_NODE_RENDER]; // NOLINT
auto driver = [&]() -> QString {
auto fd = open(deviceNode, O_RDWR | O_CLOEXEC);
if (fd == -1) return "<failed to open device node>";
auto fdGuard = qScopeGuard([&] { close(fd); });
auto* ver = drmGetVersion(fd);
if (!ver) return "<drmGetVersion failed>";
auto verGuard = qScopeGuard([&] { drmFreeVersion(ver); });
// clang-format off
return QString(ver->name)
% ' ' % QString::number(ver->version_major)
% '.' % QString::number(ver->version_minor)
% '.' % QString::number(ver->version_patchlevel)
% " (" % ver->desc % ')';
// clang-format on
}();
QString product = "unknown";
QString address = "unknown";
auto hex = [](int num, int pad) { return QString::number(num, 16).rightJustified(pad, '0'); };
switch (device->bustype) {
case DRM_BUS_PCI: {
auto* b = device->businfo.pci;
auto* d = device->deviceinfo.pci;
address = "PCI " % hex(b->bus, 2) % ':' % hex(b->dev, 2) % '.' % hex(b->func, 1);
product = hex(d->vendor_id, 4) % ':' % hex(d->device_id, 4);
} break;
case DRM_BUS_USB: {
auto* b = device->businfo.usb;
auto* d = device->deviceinfo.usb;
address = "USB " % QString::number(b->bus) % ':' % QString::number(b->dev);
product = hex(d->vendor, 4) % ':' % hex(d->product, 4);
} break;
default: break;
}
stream << "GPU " << deviceNode << "\n Driver: " << driver << "\n Model: " << product
<< "\n Address: " << address << '\n';
}
return info;
}
QString systemInfo() {
QString info;
auto stream = QTextStream(&info);
stream << gpuInfo() << '\n';
stream << "/etc/os-release:";
auto osReleaseFile = QFile("/etc/os-release");
if (osReleaseFile.open(QFile::ReadOnly)) {
stream << '\n' << osReleaseFile.readAll() << '\n';
osReleaseFile.close();
} else {
stream << "FAILED TO OPEN\n";
}
stream << "/etc/lsb-release:";
auto lsbReleaseFile = QFile("/etc/lsb-release");
if (lsbReleaseFile.open(QFile::ReadOnly)) {
stream << '\n' << lsbReleaseFile.readAll();
lsbReleaseFile.close();
} else {
stream << "FAILED TO OPEN\n";
}
return info;
}
QString envInfo() {
QString info;
auto stream = QTextStream(&info);
for (auto** envp = environ; *envp != nullptr; ++envp) { // NOLINT
auto prefixes = std::array<std::string_view, 6> {
"QS_",
"QT_",
"QML_",
"QML2_",
"QSG_",
"XDG_CURRENT_DESKTOP=",
};
for (const auto& prefix: prefixes) {
if (strncmp(prefix.data(), *envp, prefix.length()) == 0) goto print;
}
continue;
print:
stream << *envp << '\n';
}
return info;
}
QString combinedInfo() {
QString info;
auto stream = QTextStream(&info);
stream << "===== Version Information =====\n";
stream << "Quickshell: " << qsVersion() << '\n';
stream << "Qt: " << qtVersion() << '\n';
stream << "\n===== Build Information =====\n";
stream << "Build Type: " << BUILD_TYPE << '\n';
stream << "Compiler: " << COMPILER << '\n';
stream << "Compile Flags: " << COMPILE_FLAGS << '\n';
stream << "Configuration:\n" << BUILD_CONFIGURATION << '\n';
stream << "\n===== System Information =====\n";
stream << systemInfo();
stream << "\n===== Environment (trimmed) =====\n";
stream << envInfo();
return info;
}
} // namespace qs::debuginfo
-14
View File
@@ -1,14 +0,0 @@
#pragma once
#include <qcontainerfwd.h>
namespace qs::debuginfo {
QString qsVersion();
QString qtVersion();
QString gpuInfo();
QString systemInfo();
QString envInfo();
QString combinedInfo();
} // namespace qs::debuginfo
+9 -22
View File
@@ -107,10 +107,7 @@ ParsedDesktopEntryData DesktopEntry::parseText(const QString& id, const QString&
auto groupName = QString(); auto groupName = QString();
auto entries = QHash<QString, QPair<Locale, QString>>(); auto entries = QHash<QString, QPair<Locale, QString>>();
auto actionOrder = QStringList(); auto finishCategory = [&data, &groupName, &entries]() {
auto pendingActions = QHash<QString, DesktopActionData>();
auto finishCategory = [&data, &groupName, &entries, &actionOrder, &pendingActions]() {
if (groupName == "Desktop Entry") { if (groupName == "Desktop Entry") {
if (entries.value("Type").second != "Application") return; if (entries.value("Type").second != "Application") return;
@@ -132,10 +129,9 @@ ParsedDesktopEntryData DesktopEntry::parseText(const QString& id, const QString&
else if (key == "Terminal") data.terminal = value == "true"; else if (key == "Terminal") data.terminal = value == "true";
else if (key == "Categories") data.categories = value.split(u';', Qt::SkipEmptyParts); else if (key == "Categories") data.categories = value.split(u';', Qt::SkipEmptyParts);
else if (key == "Keywords") data.keywords = value.split(u';', Qt::SkipEmptyParts); else if (key == "Keywords") data.keywords = value.split(u';', Qt::SkipEmptyParts);
else if (key == "Actions") actionOrder = value.split(u';', Qt::SkipEmptyParts);
} }
} else if (groupName.startsWith("Desktop Action ")) { } else if (groupName.startsWith("Desktop Action ")) {
auto actionName = groupName.sliced(15); auto actionName = groupName.sliced(16);
DesktopActionData action; DesktopActionData action;
action.id = actionName; action.id = actionName;
@@ -151,7 +147,7 @@ ParsedDesktopEntryData DesktopEntry::parseText(const QString& id, const QString&
} }
} }
pendingActions.insert(actionName, action); data.actions.insert(actionName, action);
} }
entries.clear(); entries.clear();
@@ -197,13 +193,6 @@ ParsedDesktopEntryData DesktopEntry::parseText(const QString& id, const QString&
} }
finishCategory(); finishCategory();
for (const auto& actionId: actionOrder) {
if (pendingActions.contains(actionId)) {
data.actions.append(pendingActions.value(actionId));
}
}
return data; return data;
} }
@@ -227,18 +216,17 @@ void DesktopEntry::updateState(const ParsedDesktopEntryData& newState) {
this->updateActions(newState.actions); this->updateActions(newState.actions);
} }
void DesktopEntry::updateActions(const QVector<DesktopActionData>& newActions) { void DesktopEntry::updateActions(const QHash<QString, DesktopActionData>& newActions) {
auto old = this->mActions; auto old = this->mActions;
this->mActions.clear();
for (const auto& d: newActions) { for (const auto& [key, d]: newActions.asKeyValueRange()) {
DesktopAction* act = nullptr; DesktopAction* act = nullptr;
auto found = std::ranges::find(old, d.id, &DesktopAction::mId); if (auto found = old.find(key); found != old.end()) {
if (found != old.end()) { act = found.value();
act = *found;
old.erase(found); old.erase(found);
} else { } else {
act = new DesktopAction(d.id, this); act = new DesktopAction(d.id, this);
this->mActions.insert(key, act);
} }
Qt::beginPropertyUpdateGroup(); Qt::beginPropertyUpdateGroup();
@@ -249,7 +237,6 @@ void DesktopEntry::updateActions(const QVector<DesktopActionData>& newActions) {
Qt::endPropertyUpdateGroup(); Qt::endPropertyUpdateGroup();
act->mEntries = d.entries; act->mEntries = d.entries;
this->mActions.append(act);
} }
for (auto* leftover: old) { for (auto* leftover: old) {
@@ -263,7 +250,7 @@ void DesktopEntry::execute() const {
bool DesktopEntry::isValid() const { return !this->bName.value().isEmpty(); } bool DesktopEntry::isValid() const { return !this->bName.value().isEmpty(); }
QVector<DesktopAction*> DesktopEntry::actions() const { return this->mActions; } QVector<DesktopAction*> DesktopEntry::actions() const { return this->mActions.values(); }
QVector<QString> DesktopEntry::parseExecString(const QString& execString) { QVector<QString> DesktopEntry::parseExecString(const QString& execString) {
QVector<QString> arguments; QVector<QString> arguments;
+3 -3
View File
@@ -43,7 +43,7 @@ struct ParsedDesktopEntryData {
QVector<QString> categories; QVector<QString> categories;
QVector<QString> keywords; QVector<QString> keywords;
QHash<QString, QString> entries; QHash<QString, QString> entries;
QVector<DesktopActionData> actions; QHash<QString, DesktopActionData> actions;
}; };
/// A desktop entry. See @@DesktopEntries for details. /// A desktop entry. See @@DesktopEntries for details.
@@ -164,10 +164,10 @@ public:
// clang-format on // clang-format on
private: private:
void updateActions(const QVector<DesktopActionData>& newActions); void updateActions(const QHash<QString, DesktopActionData>& newActions);
ParsedDesktopEntryData state; ParsedDesktopEntryData state;
QVector<DesktopAction*> mActions; QHash<QString, DesktopAction*> mActions;
friend class DesktopAction; friend class DesktopAction;
}; };
-12
View File
@@ -209,8 +209,6 @@ bool EngineGeneration::setExtraWatchedFiles(const QVector<QString>& files) {
for (const auto& file: files) { for (const auto& file: files) {
if (!this->scanner.scannedFiles.contains(file)) { if (!this->scanner.scannedFiles.contains(file)) {
this->extraWatchedFiles.append(file); this->extraWatchedFiles.append(file);
QByteArray data;
this->scanner.readAndHashFile(file, data);
} }
} }
@@ -231,11 +229,6 @@ void EngineGeneration::onFileChanged(const QString& name) {
auto fileInfo = QFileInfo(name); auto fileInfo = QFileInfo(name);
if (fileInfo.isFile() && fileInfo.size() == 0) return; if (fileInfo.isFile() && fileInfo.size() == 0) return;
if (!this->scanner.hasFileContentChanged(name)) {
qCDebug(logQmlScanner) << "Ignoring file change with unchanged content:" << name;
return;
}
emit this->filesChanged(); emit this->filesChanged();
} }
} }
@@ -244,11 +237,6 @@ void EngineGeneration::onDirectoryChanged() {
// try to find any files that were just deleted from a replace operation // try to find any files that were just deleted from a replace operation
for (auto& file: this->deletedWatchedFiles) { for (auto& file: this->deletedWatchedFiles) {
if (QFileInfo(file).exists()) { if (QFileInfo(file).exists()) {
if (!this->scanner.hasFileContentChanged(file)) {
qCDebug(logQmlScanner) << "Ignoring restored file with unchanged content:" << file;
continue;
}
emit this->filesChanged(); emit this->filesChanged();
break; break;
} }
-86
View File
@@ -1,86 +0,0 @@
#include <dlfcn.h>
#include <array>
#include <cstdlib>
#include <string>
#include <cpptrace/basic.hpp>
#include <QCoreApplication>
#include <QEvent>
#include <QLoggingCategory>
#include <QMetaObject>
#include <QObject>
__attribute__((constructor)) static void initHook() {
unsetenv("LD_PRELOAD");
}
Q_LOGGING_CATEGORY(logDestructors, "quickshell.destructors");
Q_LOGGING_CATEGORY(logPostEvent, "quickshell.postEvent");
static std::string traceAddrs() {
std::array<cpptrace::frame_ptr, 64> frames{};
auto count = cpptrace::safe_generate_raw_trace(frames.data(), frames.size(), 1);
std::string result;
for (size_t i = 0; i < count; i++) {
if (i > 0) result += ',';
char buf[20];
snprintf(buf, sizeof(buf), "%p", reinterpret_cast<void*>(frames[i]));
result += buf;
}
return result;
}
using VoidMemberFn = void (*)(QObject*);
static VoidMemberFn realD2() {
static auto fn = reinterpret_cast<VoidMemberFn>(dlsym(RTLD_NEXT, "_ZN7QObjectD2Ev"));
return fn;
}
static VoidMemberFn realDeleteLater() {
static auto fn =
reinterpret_cast<VoidMemberFn>(dlsym(RTLD_NEXT, "_ZN7QObject11deleteLaterEv"));
return fn;
}
using PostEventFn = void (*)(QObject*, QEvent*, int);
static PostEventFn realPostEvent() {
static auto fn = reinterpret_cast<PostEventFn>(
dlsym(RTLD_NEXT, "_ZN16QCoreApplication9postEventEP7QObjectP6QEventi")
);
return fn;
}
extern "C" {
void _ZN7QObjectD2Ev(QObject* self) {
const auto* meta = self->metaObject();
auto trace = traceAddrs();
qCDebug(logDestructors, "~QObject %p %s\n%s", static_cast<void*>(self), meta->className(), trace.c_str());
realD2()(self);
}
void _ZN7QObject11deleteLaterEv(QObject* self) {
const auto* meta = self->metaObject();
auto trace = traceAddrs();
qCDebug(logDestructors, "deleteLater %p %s\n%s", static_cast<void*>(self), meta->className(), trace.c_str());
realDeleteLater()(self);
}
void _ZN16QCoreApplication9postEventEP7QObjectP6QEventi(
QObject* receiver,
QEvent* event,
int priority
) {
//if (event->type() == QEvent::User + 1) {
const auto* meta = receiver->metaObject();
auto trace = traceAddrs();
qCDebug(logPostEvent, "%p %d %s\n%s", static_cast<void*>(receiver), event->type(), meta->className(), trace.c_str());
//}
realPostEvent()(receiver, event, priority);
}
} // extern "C"
+4 -4
View File
@@ -3,14 +3,14 @@
#include <qdatastream.h> #include <qdatastream.h>
QDataStream& operator<<(QDataStream& stream, const InstanceInfo& info) { QDataStream& operator<<(QDataStream& stream, const InstanceInfo& info) {
stream << info.instanceId << info.configPath << info.shellId << info.appId << info.launchTime stream << info.instanceId << info.configPath << info.shellId << info.launchTime << info.pid
<< info.pid << info.display; << info.display;
return stream; return stream;
} }
QDataStream& operator>>(QDataStream& stream, InstanceInfo& info) { QDataStream& operator>>(QDataStream& stream, InstanceInfo& info) {
stream >> info.instanceId >> info.configPath >> info.shellId >> info.appId >> info.launchTime stream >> info.instanceId >> info.configPath >> info.shellId >> info.launchTime >> info.pid
>> info.pid >> info.display; >> info.display;
return stream; return stream;
} }
-1
View File
@@ -9,7 +9,6 @@ struct InstanceInfo {
QString instanceId; QString instanceId;
QString configPath; QString configPath;
QString shellId; QString shellId;
QString appId;
QDateTime launchTime; QDateTime launchTime;
pid_t pid = -1; pid_t pid = -1;
QString display; QString display;
+3 -13
View File
@@ -14,7 +14,6 @@
#include <qlist.h> #include <qlist.h>
#include <qlogging.h> #include <qlogging.h>
#include <qloggingcategory.h> #include <qloggingcategory.h>
#include <qmutex.h>
#include <qnamespace.h> #include <qnamespace.h>
#include <qobject.h> #include <qobject.h>
#include <qobjectdefs.h> #include <qobjectdefs.h>
@@ -32,9 +31,6 @@
#include <sys/sendfile.h> #include <sys/sendfile.h>
#include <sys/types.h> #include <sys/types.h>
#endif #endif
#ifdef __FreeBSD__
#include <unistd.h>
#endif
#include "instanceinfo.hpp" #include "instanceinfo.hpp"
#include "logcat.hpp" #include "logcat.hpp"
@@ -71,7 +67,7 @@ bool copyFileData(int sourceFd, int destFd, qint64 size) {
return true; return true;
#else #else
std::array<char, 64 * 1024> buffer = {}; std::array<char, 64 * 1024> buffer = {};
auto remaining = usize; auto remaining = totalTarget;
while (remaining > 0) { while (remaining > 0) {
auto chunk = std::min(remaining, buffer.size()); auto chunk = std::min(remaining, buffer.size());
@@ -221,7 +217,6 @@ void LogManager::messageHandler(
} }
if (display) { if (display) {
auto locker = QMutexLocker(&self->stdoutMutex);
LogMessage::formatMessage( LogMessage::formatMessage(
self->stdoutStream, self->stdoutStream,
message, message,
@@ -310,15 +305,10 @@ void LogManager::init(
instance->rules->append(parser.rules()); instance->rules->append(parser.rules());
} }
instance->lastCategoryFilter = QLoggingCategory::installFilter(&LogManager::filterCategory);
if (instance->lastCategoryFilter == &LogManager::filterCategory) {
qCFatal(logLogging) << "Quickshell's log filter has been installed twice. This is a bug.";
instance->lastCategoryFilter = nullptr;
}
qInstallMessageHandler(&LogManager::messageHandler); qInstallMessageHandler(&LogManager::messageHandler);
instance->lastCategoryFilter = QLoggingCategory::installFilter(&LogManager::filterCategory);
qCDebug(logLogging) << "Creating offthread logger..."; qCDebug(logLogging) << "Creating offthread logger...";
auto* thread = new QThread(); auto* thread = new QThread();
instance->threadProxy.moveToThread(thread); instance->threadProxy.moveToThread(thread);
-2
View File
@@ -10,7 +10,6 @@
#include <qlatin1stringview.h> #include <qlatin1stringview.h>
#include <qlogging.h> #include <qlogging.h>
#include <qloggingcategory.h> #include <qloggingcategory.h>
#include <qmutex.h>
#include <qobject.h> #include <qobject.h>
#include <qtmetamacros.h> #include <qtmetamacros.h>
@@ -136,7 +135,6 @@ private:
QHash<QLatin1StringView, CategoryFilter> allFilters; QHash<QLatin1StringView, CategoryFilter> allFilters;
QTextStream stdoutStream; QTextStream stdoutStream;
QMutex stdoutMutex;
LoggingThreadProxy threadProxy; LoggingThreadProxy threadProxy;
friend void initLogCategoryLevel(const char* name, QtMsgType defaultLevel); friend void initLogCategoryLevel(const char* name, QtMsgType defaultLevel);
+1 -1
View File
@@ -64,7 +64,7 @@ QDir* QsPaths::baseRunDir() {
if (this->baseRunState == DirState::Unknown) { if (this->baseRunState == DirState::Unknown) {
auto runtimeDir = qEnvironmentVariable("XDG_RUNTIME_DIR"); auto runtimeDir = qEnvironmentVariable("XDG_RUNTIME_DIR");
if (runtimeDir.isEmpty()) { if (runtimeDir.isEmpty()) {
runtimeDir = QString("/run/user/%1").arg(getuid()); runtimeDir = QString("/run/user/$1").arg(getuid());
qCInfo(logPaths) << "XDG_RUNTIME_DIR was not set, defaulting to" << runtimeDir; qCInfo(logPaths) << "XDG_RUNTIME_DIR was not set, defaulting to" << runtimeDir;
} }
+4 -1
View File
@@ -18,6 +18,7 @@
#include <qwindow.h> #include <qwindow.h>
#include "../window/proxywindow.hpp" #include "../window/proxywindow.hpp"
#include "../window/windowinterface.hpp"
#include "iconprovider.hpp" #include "iconprovider.hpp"
#include "model.hpp" #include "model.hpp"
#include "platformmenu_p.hpp" #include "platformmenu_p.hpp"
@@ -90,8 +91,10 @@ bool PlatformMenuEntry::display(QObject* parentWindow, int relativeX, int relati
} else if (parentWindow == nullptr) { } else if (parentWindow == nullptr) {
qCritical() << "Cannot display PlatformMenuEntry with null parent window."; qCritical() << "Cannot display PlatformMenuEntry with null parent window.";
return false; return false;
} else if (auto* proxy = ProxyWindowBase::forObject(parentWindow)) { } else if (auto* proxy = qobject_cast<ProxyWindowBase*>(parentWindow)) {
window = proxy->backingWindow(); window = proxy->backingWindow();
} else if (auto* interface = qobject_cast<WindowInterface*>(parentWindow)) {
window = interface->proxyWindow()->backingWindow();
} else { } else {
qCritical() << "PlatformMenuEntry.display() must be called with a window."; qCritical() << "PlatformMenuEntry.display() must be called with a window.";
return false; return false;
-16
View File
@@ -9,18 +9,6 @@ static QVector<QsEnginePlugin*> plugins; // NOLINT
void QsEnginePlugin::registerPlugin(QsEnginePlugin& plugin) { plugins.push_back(&plugin); } void QsEnginePlugin::registerPlugin(QsEnginePlugin& plugin) { plugins.push_back(&plugin); }
void QsEnginePlugin::preinitPluginsOnly() {
plugins.removeIf([](QsEnginePlugin* plugin) { return !plugin->applies(); });
std::ranges::sort(plugins, [](QsEnginePlugin* a, QsEnginePlugin* b) {
return b->dependencies().contains(a->name());
});
for (QsEnginePlugin* plugin: plugins) {
plugin->preinit();
}
}
void QsEnginePlugin::initPlugins() { void QsEnginePlugin::initPlugins() {
plugins.removeIf([](QsEnginePlugin* plugin) { return !plugin->applies(); }); plugins.removeIf([](QsEnginePlugin* plugin) { return !plugin->applies(); });
@@ -28,10 +16,6 @@ void QsEnginePlugin::initPlugins() {
return b->dependencies().contains(a->name()); return b->dependencies().contains(a->name());
}); });
for (QsEnginePlugin* plugin: plugins) {
plugin->preinit();
}
for (QsEnginePlugin* plugin: plugins) { for (QsEnginePlugin* plugin: plugins) {
plugin->init(); plugin->init();
} }
-2
View File
@@ -18,14 +18,12 @@ public:
virtual QString name() { return QString(); } virtual QString name() { return QString(); }
virtual QList<QString> dependencies() { return {}; } virtual QList<QString> dependencies() { return {}; }
virtual bool applies() { return true; } virtual bool applies() { return true; }
virtual void preinit() {}
virtual void init() {} virtual void init() {}
virtual void registerTypes() {} virtual void registerTypes() {}
virtual void constructGeneration(EngineGeneration& /*unused*/) {} // NOLINT virtual void constructGeneration(EngineGeneration& /*unused*/) {} // NOLINT
virtual void onReload() {} virtual void onReload() {}
static void registerPlugin(QsEnginePlugin& plugin); static void registerPlugin(QsEnginePlugin& plugin);
static void preinitPluginsOnly();
static void initPlugins(); static void initPlugins();
static void runConstructGeneration(EngineGeneration& generation); static void runConstructGeneration(EngineGeneration& generation);
static void runOnReload(); static void runOnReload();
+4 -1
View File
@@ -11,6 +11,7 @@
#include <qwindow.h> #include <qwindow.h>
#include "../window/proxywindow.hpp" #include "../window/proxywindow.hpp"
#include "../window/windowinterface.hpp"
#include "types.hpp" #include "types.hpp"
bool PopupAnchorState::operator==(const PopupAnchorState& other) const { bool PopupAnchorState::operator==(const PopupAnchorState& other) const {
@@ -39,8 +40,10 @@ void PopupAnchor::setWindowInternal(QObject* window) {
} }
if (window) { if (window) {
if (auto* proxy = ProxyWindowBase::forObject(window)) { if (auto* proxy = qobject_cast<ProxyWindowBase*>(window)) {
this->bProxyWindow = proxy; this->bProxyWindow = proxy;
} else if (auto* interface = qobject_cast<WindowInterface*>(window)) {
this->bProxyWindow = interface->proxyWindow();
} else { } else {
qWarning() << "Tried to set popup anchor window to" << window qWarning() << "Tried to set popup anchor window to" << window
<< "which is not a quickshell window."; << "which is not a quickshell window.";
+1 -20
View File
@@ -26,7 +26,6 @@
#include "../io/processcore.hpp" #include "../io/processcore.hpp"
#include "generation.hpp" #include "generation.hpp"
#include "iconimageprovider.hpp" #include "iconimageprovider.hpp"
#include "instanceinfo.hpp"
#include "paths.hpp" #include "paths.hpp"
#include "qmlscreen.hpp" #include "qmlscreen.hpp"
#include "rootwrapper.hpp" #include "rootwrapper.hpp"
@@ -61,9 +60,7 @@ void QuickshellSettings::setWorkingDirectory(QString workingDirectory) { // NOLI
emit this->workingDirectoryChanged(); emit this->workingDirectoryChanged();
} }
bool QuickshellSettings::watchFiles() const { bool QuickshellSettings::watchFiles() const { return this->mWatchFiles; }
return this->mWatchFiles && qEnvironmentVariableIsEmpty("QS_DISABLE_FILE_WATCHER");
}
void QuickshellSettings::setWatchFiles(bool watchFiles) { void QuickshellSettings::setWatchFiles(bool watchFiles) {
if (watchFiles == this->mWatchFiles) return; if (watchFiles == this->mWatchFiles) return;
@@ -154,22 +151,6 @@ qint32 QuickshellGlobal::processId() const { // NOLINT
return getpid(); return getpid();
} }
QString QuickshellGlobal::instanceId() const { // NOLINT
return InstanceInfo::CURRENT.instanceId;
}
QString QuickshellGlobal::shellId() const { // NOLINT
return InstanceInfo::CURRENT.shellId;
}
QString QuickshellGlobal::appId() const { // NOLINT
return InstanceInfo::CURRENT.appId;
}
QDateTime QuickshellGlobal::launchTime() const { // NOLINT
return InstanceInfo::CURRENT.launchTime;
}
qsizetype QuickshellGlobal::screensCount(QQmlListProperty<QuickshellScreenInfo>* /*unused*/) { qsizetype QuickshellGlobal::screensCount(QQmlListProperty<QuickshellScreenInfo>* /*unused*/) {
return QuickshellTracked::instance()->screens.size(); return QuickshellTracked::instance()->screens.size();
} }
-20
View File
@@ -17,7 +17,6 @@
#include "../io/processcore.hpp" #include "../io/processcore.hpp"
#include "doc.hpp" #include "doc.hpp"
#include "instanceinfo.hpp"
#include "qmlscreen.hpp" #include "qmlscreen.hpp"
///! Accessor for some options under the Quickshell type. ///! Accessor for some options under the Quickshell type.
@@ -84,21 +83,6 @@ class QuickshellGlobal: public QObject {
// clang-format off // clang-format off
/// Quickshell's process id. /// Quickshell's process id.
Q_PROPERTY(qint32 processId READ processId CONSTANT); Q_PROPERTY(qint32 processId READ processId CONSTANT);
/// A unique identifier for this Quickshell instance
Q_PROPERTY(QString instanceId READ instanceId CONSTANT)
/// The shell ID, used to differentiate between different shell configurations.
///
/// Defaults to a stable value derived from the config path.
/// Can be overridden with `//@ pragma ShellId <id>` in the root qml file.
Q_PROPERTY(QString shellId READ shellId CONSTANT)
/// The desktop application ID.
///
/// Defaults to `org.quickshell`.
/// Can be overridden with `//@ pragma AppId <id>` in the root qml file
/// or the `QS_APP_ID` environment variable.
Q_PROPERTY(QString appId READ appId CONSTANT)
/// The time at which this Quickshell instance was launched.
Q_PROPERTY(QDateTime launchTime READ launchTime CONSTANT)
/// All currently connected screens. /// All currently connected screens.
/// ///
/// This property updates as connected screens change. /// This property updates as connected screens change.
@@ -165,10 +149,6 @@ class QuickshellGlobal: public QObject {
public: public:
[[nodiscard]] qint32 processId() const; [[nodiscard]] qint32 processId() const;
[[nodiscard]] QString instanceId() const;
[[nodiscard]] QString shellId() const;
[[nodiscard]] QString appId() const;
[[nodiscard]] QDateTime launchTime() const;
QQmlListProperty<QuickshellScreenInfo> screens(); QQmlListProperty<QuickshellScreenInfo> screens();
-133
View File
@@ -1,5 +1,4 @@
#include "region.hpp" #include "region.hpp"
#include <algorithm>
#include <cmath> #include <cmath>
#include <qobject.h> #include <qobject.h>
@@ -19,11 +18,6 @@ PendingRegion::PendingRegion(QObject* parent): QObject(parent) {
QObject::connect(this, &PendingRegion::yChanged, this, &PendingRegion::changed); QObject::connect(this, &PendingRegion::yChanged, this, &PendingRegion::changed);
QObject::connect(this, &PendingRegion::widthChanged, this, &PendingRegion::changed); QObject::connect(this, &PendingRegion::widthChanged, this, &PendingRegion::changed);
QObject::connect(this, &PendingRegion::heightChanged, this, &PendingRegion::changed); QObject::connect(this, &PendingRegion::heightChanged, this, &PendingRegion::changed);
QObject::connect(this, &PendingRegion::radiusChanged, this, &PendingRegion::changed);
QObject::connect(this, &PendingRegion::topLeftRadiusChanged, this, &PendingRegion::changed);
QObject::connect(this, &PendingRegion::topRightRadiusChanged, this, &PendingRegion::changed);
QObject::connect(this, &PendingRegion::bottomLeftRadiusChanged, this, &PendingRegion::changed);
QObject::connect(this, &PendingRegion::bottomRightRadiusChanged, this, &PendingRegion::changed);
QObject::connect(this, &PendingRegion::childrenChanged, this, &PendingRegion::changed); QObject::connect(this, &PendingRegion::childrenChanged, this, &PendingRegion::changed);
} }
@@ -51,79 +45,6 @@ void PendingRegion::onItemDestroyed() { this->mItem = nullptr; }
void PendingRegion::onChildDestroyed() { this->mRegions.removeAll(this->sender()); } void PendingRegion::onChildDestroyed() { this->mRegions.removeAll(this->sender()); }
qint32 PendingRegion::radius() const { return this->mRadius; }
void PendingRegion::setRadius(qint32 radius) {
if (radius == this->mRadius) return;
this->mRadius = radius;
emit this->radiusChanged();
if (!(this->mCornerOverrides & TopLeft)) emit this->topLeftRadiusChanged();
if (!(this->mCornerOverrides & TopRight)) emit this->topRightRadiusChanged();
if (!(this->mCornerOverrides & BottomLeft)) emit this->bottomLeftRadiusChanged();
if (!(this->mCornerOverrides & BottomRight)) emit this->bottomRightRadiusChanged();
}
qint32 PendingRegion::topLeftRadius() const {
return (this->mCornerOverrides & TopLeft) ? this->mTopLeftRadius : this->mRadius;
}
void PendingRegion::setTopLeftRadius(qint32 radius) {
this->mTopLeftRadius = radius;
this->mCornerOverrides |= TopLeft;
emit this->topLeftRadiusChanged();
}
void PendingRegion::resetTopLeftRadius() {
this->mCornerOverrides &= ~TopLeft;
emit this->topLeftRadiusChanged();
}
qint32 PendingRegion::topRightRadius() const {
return (this->mCornerOverrides & TopRight) ? this->mTopRightRadius : this->mRadius;
}
void PendingRegion::setTopRightRadius(qint32 radius) {
this->mTopRightRadius = radius;
this->mCornerOverrides |= TopRight;
emit this->topRightRadiusChanged();
}
void PendingRegion::resetTopRightRadius() {
this->mCornerOverrides &= ~TopRight;
emit this->topRightRadiusChanged();
}
qint32 PendingRegion::bottomLeftRadius() const {
return (this->mCornerOverrides & BottomLeft) ? this->mBottomLeftRadius : this->mRadius;
}
void PendingRegion::setBottomLeftRadius(qint32 radius) {
this->mBottomLeftRadius = radius;
this->mCornerOverrides |= BottomLeft;
emit this->bottomLeftRadiusChanged();
}
void PendingRegion::resetBottomLeftRadius() {
this->mCornerOverrides &= ~BottomLeft;
emit this->bottomLeftRadiusChanged();
}
qint32 PendingRegion::bottomRightRadius() const {
return (this->mCornerOverrides & BottomRight) ? this->mBottomRightRadius : this->mRadius;
}
void PendingRegion::setBottomRightRadius(qint32 radius) {
this->mBottomRightRadius = radius;
this->mCornerOverrides |= BottomRight;
emit this->bottomRightRadiusChanged();
}
void PendingRegion::resetBottomRightRadius() {
this->mCornerOverrides &= ~BottomRight;
emit this->bottomRightRadiusChanged();
}
QQmlListProperty<PendingRegion> PendingRegion::regions() { QQmlListProperty<PendingRegion> PendingRegion::regions() {
return QQmlListProperty<PendingRegion>( return QQmlListProperty<PendingRegion>(
this, this,
@@ -169,60 +90,6 @@ QRegion PendingRegion::build() const {
region = QRegion(this->mX, this->mY, this->mWidth, this->mHeight, type); region = QRegion(this->mX, this->mY, this->mWidth, this->mHeight, type);
} }
if (this->mShape == RegionShape::Rect && !region.isEmpty()) {
auto tl = std::max(this->topLeftRadius(), 0);
auto tr = std::max(this->topRightRadius(), 0);
auto bl = std::max(this->bottomLeftRadius(), 0);
auto br = std::max(this->bottomRightRadius(), 0);
if (tl > 0 || tr > 0 || bl > 0 || br > 0) {
auto rect = region.boundingRect();
auto x = rect.x();
auto y = rect.y();
auto w = rect.width();
auto h = rect.height();
// Normalize so adjacent corners don't exceed their shared edge.
// Each corner is scaled by the tightest constraint of its two edges.
auto topScale = tl + tr > w ? static_cast<double>(w) / (tl + tr) : 1.0;
auto bottomScale = bl + br > w ? static_cast<double>(w) / (bl + br) : 1.0;
auto leftScale = tl + bl > h ? static_cast<double>(h) / (tl + bl) : 1.0;
auto rightScale = tr + br > h ? static_cast<double>(h) / (tr + br) : 1.0;
tl = static_cast<qint32>(tl * std::min(topScale, leftScale));
tr = static_cast<qint32>(tr * std::min(topScale, rightScale));
bl = static_cast<qint32>(bl * std::min(bottomScale, leftScale));
br = static_cast<qint32>(br * std::min(bottomScale, rightScale));
// Unlock each corner: subtract (cornerBox - quarterEllipse) from the
// full rect. Each corner only modifies pixels inside its own box,
// so no diagonal overlap is possible.
if (tl > 0) {
auto box = QRegion(x, y, tl, tl);
auto ellipse = QRegion(x, y, tl * 2, tl * 2, QRegion::Ellipse);
region -= box - (ellipse & box);
}
if (tr > 0) {
auto box = QRegion(x + w - tr, y, tr, tr);
auto ellipse = QRegion(x + w - tr * 2, y, tr * 2, tr * 2, QRegion::Ellipse);
region -= box - (ellipse & box);
}
if (bl > 0) {
auto box = QRegion(x, y + h - bl, bl, bl);
auto ellipse = QRegion(x, y + h - bl * 2, bl * 2, bl * 2, QRegion::Ellipse);
region -= box - (ellipse & box);
}
if (br > 0) {
auto box = QRegion(x + w - br, y + h - br, br, br);
auto ellipse = QRegion(x + w - br * 2, y + h - br * 2, br * 2, br * 2, QRegion::Ellipse);
region -= box - (ellipse & box);
}
}
}
for (const auto& childRegion: this->mRegions) { for (const auto& childRegion: this->mRegions) {
region = childRegion->applyTo(region); region = childRegion->applyTo(region);
} }
-60
View File
@@ -66,29 +66,6 @@ class PendingRegion: public QObject {
Q_PROPERTY(qint32 width MEMBER mWidth NOTIFY widthChanged); Q_PROPERTY(qint32 width MEMBER mWidth NOTIFY widthChanged);
/// Defaults to 0. Does nothing if @@item is set. /// Defaults to 0. Does nothing if @@item is set.
Q_PROPERTY(qint32 height MEMBER mHeight NOTIFY heightChanged); Q_PROPERTY(qint32 height MEMBER mHeight NOTIFY heightChanged);
// clang-format off
/// Corner radius for rounded rectangles. Only applies when @@shape is `Rect`. Defaults to 0.
///
/// Acts as the default for @@topLeftRadius, @@topRightRadius, @@bottomLeftRadius,
/// and @@bottomRightRadius.
Q_PROPERTY(qint32 radius READ radius WRITE setRadius NOTIFY radiusChanged);
/// Top-left corner radius. Only applies when @@shape is `Rect`.
///
/// Defaults to @@radius, and may be reset by assigning `undefined`.
Q_PROPERTY(qint32 topLeftRadius READ topLeftRadius WRITE setTopLeftRadius RESET resetTopLeftRadius NOTIFY topLeftRadiusChanged);
/// Top-right corner radius. Only applies when @@shape is `Rect`.
///
/// Defaults to @@radius, and may be reset by assigning `undefined`.
Q_PROPERTY(qint32 topRightRadius READ topRightRadius WRITE setTopRightRadius RESET resetTopRightRadius NOTIFY topRightRadiusChanged);
/// Bottom-left corner radius. Only applies when @@shape is `Rect`.
///
/// Defaults to @@radius, and may be reset by assigning `undefined`.
Q_PROPERTY(qint32 bottomLeftRadius READ bottomLeftRadius WRITE setBottomLeftRadius RESET resetBottomLeftRadius NOTIFY bottomLeftRadiusChanged);
/// Bottom-right corner radius. Only applies when @@shape is `Rect`.
///
/// Defaults to @@radius, and may be reset by assigning `undefined`.
Q_PROPERTY(qint32 bottomRightRadius READ bottomRightRadius WRITE setBottomRightRadius RESET resetBottomRightRadius NOTIFY bottomRightRadiusChanged);
// clang-format on
/// Regions to apply on top of this region. /// Regions to apply on top of this region.
/// ///
@@ -114,25 +91,6 @@ public:
void setItem(QQuickItem* item); void setItem(QQuickItem* item);
[[nodiscard]] qint32 radius() const;
void setRadius(qint32 radius);
[[nodiscard]] qint32 topLeftRadius() const;
void setTopLeftRadius(qint32 radius);
void resetTopLeftRadius();
[[nodiscard]] qint32 topRightRadius() const;
void setTopRightRadius(qint32 radius);
void resetTopRightRadius();
[[nodiscard]] qint32 bottomLeftRadius() const;
void setBottomLeftRadius(qint32 radius);
void resetBottomLeftRadius();
[[nodiscard]] qint32 bottomRightRadius() const;
void setBottomRightRadius(qint32 radius);
void resetBottomRightRadius();
QQmlListProperty<PendingRegion> regions(); QQmlListProperty<PendingRegion> regions();
[[nodiscard]] bool empty() const; [[nodiscard]] bool empty() const;
@@ -151,11 +109,6 @@ signals:
void yChanged(); void yChanged();
void widthChanged(); void widthChanged();
void heightChanged(); void heightChanged();
void radiusChanged();
void topLeftRadiusChanged();
void topRightRadiusChanged();
void bottomLeftRadiusChanged();
void bottomRightRadiusChanged();
void childrenChanged(); void childrenChanged();
/// Triggered when the region's geometry changes. /// Triggered when the region's geometry changes.
@@ -177,25 +130,12 @@ private:
static void static void
regionsReplace(QQmlListProperty<PendingRegion>* prop, qsizetype i, PendingRegion* region); regionsReplace(QQmlListProperty<PendingRegion>* prop, qsizetype i, PendingRegion* region);
enum CornerOverride : quint8 {
TopLeft = 0b1,
TopRight = 0b10,
BottomLeft = 0b100,
BottomRight = 0b1000,
};
QQuickItem* mItem = nullptr; QQuickItem* mItem = nullptr;
qint32 mX = 0; qint32 mX = 0;
qint32 mY = 0; qint32 mY = 0;
qint32 mWidth = 0; qint32 mWidth = 0;
qint32 mHeight = 0; qint32 mHeight = 0;
qint32 mRadius = 0;
qint32 mTopLeftRadius = 0;
qint32 mTopRightRadius = 0;
qint32 mBottomLeftRadius = 0;
qint32 mBottomRightRadius = 0;
quint8 mCornerOverrides = 0;
QList<PendingRegion*> mRegions; QList<PendingRegion*> mRegions;
}; };
+15 -41
View File
@@ -3,7 +3,6 @@
#include <utility> #include <utility>
#include <qcontainerfwd.h> #include <qcontainerfwd.h>
#include <qcryptographichash.h>
#include <qdir.h> #include <qdir.h>
#include <qfileinfo.h> #include <qfileinfo.h>
#include <qjsengine.h> #include <qjsengine.h>
@@ -22,25 +21,6 @@
QS_LOGGING_CATEGORY(logQmlScanner, "quickshell.qmlscanner", QtWarningMsg); QS_LOGGING_CATEGORY(logQmlScanner, "quickshell.qmlscanner", QtWarningMsg);
bool QmlScanner::readAndHashFile(const QString& path, QByteArray& data) {
auto file = QFile(path);
if (!file.open(QFile::ReadOnly)) return false;
data = file.readAll();
this->fileHashes.insert(path, QCryptographicHash::hash(data, QCryptographicHash::Md5));
return true;
}
bool QmlScanner::hasFileContentChanged(const QString& path) const {
auto it = this->fileHashes.constFind(path);
if (it == this->fileHashes.constEnd()) return true;
auto file = QFile(path);
if (!file.open(QFile::ReadOnly)) return true;
auto newHash = QCryptographicHash::hash(file.readAll(), QCryptographicHash::Md5);
return newHash != it.value();
}
void QmlScanner::scanDir(const QDir& dir) { void QmlScanner::scanDir(const QDir& dir) {
if (this->scannedDirs.contains(dir)) return; if (this->scannedDirs.contains(dir)) return;
this->scannedDirs.push_back(dir); this->scannedDirs.push_back(dir);
@@ -129,13 +109,13 @@ bool QmlScanner::scanQmlFile(const QString& path, bool& singleton, bool& interna
qCDebug(logQmlScanner) << "Scanning qml file" << path; qCDebug(logQmlScanner) << "Scanning qml file" << path;
QByteArray fileData; auto file = QFile(path);
if (!this->readAndHashFile(path, fileData)) { if (!file.open(QFile::ReadOnly | QFile::Text)) {
qCWarning(logQmlScanner) << "Failed to open file" << path; qCWarning(logQmlScanner) << "Failed to open file" << path;
return false; return false;
} }
auto stream = QTextStream(&fileData); auto stream = QTextStream(&file);
auto imports = QVector<QString>(); auto imports = QVector<QString>();
bool inHeader = true; bool inHeader = true;
@@ -145,7 +125,10 @@ bool QmlScanner::scanQmlFile(const QString& path, bool& singleton, bool& interna
QString overrideText; QString overrideText;
bool isOverridden = false; bool isOverridden = false;
auto& pragmaEngine = *QmlScanner::preprocEngine(); auto pragmaEngine = QJSEngine();
pragmaEngine.globalObject().setPrototype(
pragmaEngine.newQObject(new qs::scan::env::PreprocEnv())
);
auto postError = [&, this](QString error) { auto postError = [&, this](QString error) {
this->scanErrors.append({.file = path, .message = std::move(error), .line = lineNum}); this->scanErrors.append({.file = path, .message = std::move(error), .line = lineNum});
@@ -236,6 +219,8 @@ bool QmlScanner::scanQmlFile(const QString& path, bool& singleton, bool& interna
postError("unclosed preprocessor if block"); postError("unclosed preprocessor if block");
} }
file.close();
if (isOverridden) { if (isOverridden) {
this->fileIntercepts.insert(path, overrideText); this->fileIntercepts.insert(path, overrideText);
} }
@@ -272,11 +257,8 @@ bool QmlScanner::scanQmlFile(const QString& path, bool& singleton, bool& interna
continue; continue;
} }
if (import.endsWith(".js")) { if (import.endsWith(".js")) this->scannedFiles.push_back(cpath);
this->scannedFiles.push_back(cpath); else this->scanDir(cpath);
QByteArray jsData;
this->readAndHashFile(cpath, jsData);
} else this->scanDir(cpath);
} }
return true; return true;
@@ -291,12 +273,14 @@ void QmlScanner::scanQmlRoot(const QString& path) {
bool QmlScanner::scanQmlJson(const QString& path) { bool QmlScanner::scanQmlJson(const QString& path) {
qCDebug(logQmlScanner) << "Scanning qml.json file" << path; qCDebug(logQmlScanner) << "Scanning qml.json file" << path;
QByteArray data; auto file = QFile(path);
if (!this->readAndHashFile(path, data)) { if (!file.open(QFile::ReadOnly | QFile::Text)) {
qCWarning(logQmlScanner) << "Failed to open file" << path; qCWarning(logQmlScanner) << "Failed to open file" << path;
return false; return false;
} }
auto data = file.readAll();
// Importing this makes CI builds fail for some reason. // Importing this makes CI builds fail for some reason.
QJsonParseError error; // NOLINT (misc-include-cleaner) QJsonParseError error; // NOLINT (misc-include-cleaner)
auto json = QJsonDocument::fromJson(data, &error); auto json = QJsonDocument::fromJson(data, &error);
@@ -367,13 +351,3 @@ QPair<QString, QString> QmlScanner::jsonToQml(const QJsonValue& value, int inden
return qMakePair(QStringLiteral("var"), "null"); return qMakePair(QStringLiteral("var"), "null");
} }
} }
QJSEngine* QmlScanner::preprocEngine() {
static auto* engine = [] {
auto* engine = new QJSEngine();
engine->globalObject().setPrototype(engine->newQObject(new qs::scan::env::PreprocEnv()));
return engine;
}();
return engine;
}
-8
View File
@@ -1,10 +1,8 @@
#pragma once #pragma once
#include <qbytearray.h>
#include <qcontainerfwd.h> #include <qcontainerfwd.h>
#include <qdir.h> #include <qdir.h>
#include <qhash.h> #include <qhash.h>
#include <qjsengine.h>
#include <qloggingcategory.h> #include <qloggingcategory.h>
#include <qvector.h> #include <qvector.h>
@@ -23,7 +21,6 @@ public:
QVector<QDir> scannedDirs; QVector<QDir> scannedDirs;
QVector<QString> scannedFiles; QVector<QString> scannedFiles;
QHash<QString, QByteArray> fileHashes;
QHash<QString, QString> fileIntercepts; QHash<QString, QString> fileIntercepts;
struct ScanError { struct ScanError {
@@ -34,15 +31,10 @@ public:
QVector<ScanError> scanErrors; QVector<ScanError> scanErrors;
bool readAndHashFile(const QString& path, QByteArray& data);
[[nodiscard]] bool hasFileContentChanged(const QString& path) const;
private: private:
QDir rootPath; QDir rootPath;
bool scanQmlFile(const QString& path, bool& singleton, bool& internal); bool scanQmlFile(const QString& path, bool& singleton, bool& internal);
bool scanQmlJson(const QString& path); bool scanQmlJson(const QString& path);
[[nodiscard]] static QPair<QString, QString> jsonToQml(const QJsonValue& value, int indent = 0); [[nodiscard]] static QPair<QString, QString> jsonToQml(const QJsonValue& value, int indent = 0);
static QJSEngine* preprocEngine();
}; };
-98
View File
@@ -1,98 +0,0 @@
#include "streamreader.hpp"
#include <cstring>
#include <qbytearray.h>
#include <qiodevice.h>
#include <qtypes.h>
void StreamReader::setDevice(QIODevice* device) {
this->reset();
this->device = device;
}
void StreamReader::startTransaction() {
this->cursor = 0;
this->failed = false;
}
bool StreamReader::fill() {
auto available = this->device->bytesAvailable();
if (available <= 0) return false;
auto oldSize = this->buffer.size();
this->buffer.resize(oldSize + available);
auto bytesRead = this->device->read(this->buffer.data() + oldSize, available); // NOLINT
if (bytesRead <= 0) {
this->buffer.resize(oldSize);
return false;
}
this->buffer.resize(oldSize + bytesRead);
return true;
}
QByteArray StreamReader::readBytes(qsizetype count) {
if (this->failed) return {};
auto needed = this->cursor + count;
while (this->buffer.size() < needed) {
if (!this->fill()) {
this->failed = true;
return {};
}
}
auto result = this->buffer.mid(this->cursor, count);
this->cursor += count;
return result;
}
QByteArray StreamReader::readUntil(char terminator) {
if (this->failed) return {};
auto searchFrom = this->cursor;
auto idx = this->buffer.indexOf(terminator, searchFrom);
while (idx == -1) {
searchFrom = this->buffer.size();
if (!this->fill()) {
this->failed = true;
return {};
}
idx = this->buffer.indexOf(terminator, searchFrom);
}
auto length = idx - this->cursor + 1;
auto result = this->buffer.mid(this->cursor, length);
this->cursor += length;
return result;
}
void StreamReader::readInto(char* ptr, qsizetype count) {
auto data = this->readBytes(count);
if (!data.isEmpty()) memcpy(ptr, data.data(), count);
}
qint32 StreamReader::readI32() {
qint32 value = 0;
this->readInto(reinterpret_cast<char*>(&value), sizeof(qint32));
return value;
}
bool StreamReader::commitTransaction() {
if (this->failed) {
this->cursor = 0;
return false;
}
this->buffer.remove(0, this->cursor);
this->cursor = 0;
return true;
}
void StreamReader::reset() {
this->buffer.clear();
this->cursor = 0;
}
-26
View File
@@ -1,26 +0,0 @@
#pragma once
#include <qbytearray.h>
#include <qiodevice.h>
#include <qtypes.h>
class StreamReader {
public:
void setDevice(QIODevice* device);
void startTransaction();
QByteArray readBytes(qsizetype count);
QByteArray readUntil(char terminator);
void readInto(char* ptr, qsizetype count);
qint32 readI32();
bool commitTransaction();
void reset();
private:
bool fill();
QIODevice* device = nullptr;
QByteArray buffer;
qsizetype cursor = 0;
bool failed = false;
};
+1 -5
View File
@@ -177,8 +177,6 @@ void QmlToolingSupport::updateToolingFs(
auto fileInfo = QFileInfo(path); auto fileInfo = QFileInfo(path);
if (!fileInfo.isFile()) continue; if (!fileInfo.isFile()) continue;
if (scanner.fileIntercepts.contains(path)) continue;
auto spath = linkDir.filePath(name); auto spath = linkDir.filePath(name);
auto sFileInfo = QFileInfo(spath); auto sFileInfo = QFileInfo(spath);
@@ -207,10 +205,8 @@ void QmlToolingSupport::updateToolingFs(
} }
auto spath = linkDir.filePath(name); auto spath = linkDir.filePath(name);
QFile::remove(spath);
auto file = QFile(spath); auto file = QFile(spath);
if (!file.open(QFile::ReadWrite | QFile::Text | QFile::NewOnly)) { if (!file.open(QFile::ReadWrite | QFile::Text)) {
qCCritical(logTooling) << "Failed to open injected file" << spath; qCCritical(logTooling) << "Failed to open injected file" << spath;
continue; continue;
} }
+3 -38
View File
@@ -1,15 +1,12 @@
#include "handler.hpp" #include "handler.hpp"
#include <algorithm> #include <algorithm>
#include <array> #include <array>
#include <cerrno>
#include <csignal> #include <csignal>
#include <cstdio> #include <cstdio>
#include <cstring> #include <cstring>
#include <exception>
#include <cpptrace/basic.hpp> #include <cpptrace/basic.hpp>
#include <cpptrace/forward.hpp> #include <cpptrace/forward.hpp>
#include <cpptrace/utils.hpp>
#include <qdatastream.h> #include <qdatastream.h>
#include <qfile.h> #include <qfile.h>
#include <qlogging.h> #include <qlogging.h>
@@ -60,12 +57,6 @@ void signalHandler(
siginfo_t* /*info*/, // NOLINT (misc-include-cleaner) siginfo_t* /*info*/, // NOLINT (misc-include-cleaner)
void* /*context*/ void* /*context*/
) { ) {
// NOLINTBEGIN (misc-include-cleaner)
sigset_t set;
sigfillset(&set);
sigprocmask(SIG_UNBLOCK, &set, nullptr);
// NOLINTEND
if (CrashInfo::INSTANCE.traceFd != -1) { if (CrashInfo::INSTANCE.traceFd != -1) {
auto traceBuffer = std::array<cpptrace::frame_ptr, 1024>(); auto traceBuffer = std::array<cpptrace::frame_ptr, 1024>();
auto frameCount = cpptrace::safe_generate_raw_trace(traceBuffer.data(), traceBuffer.size(), 1); auto frameCount = cpptrace::safe_generate_raw_trace(traceBuffer.data(), traceBuffer.size(), 1);
@@ -73,21 +64,10 @@ void signalHandler(
for (size_t i = 0; i < static_cast<size_t>(frameCount); i++) { for (size_t i = 0; i < static_cast<size_t>(frameCount); i++) {
auto frame = cpptrace::safe_object_frame(); auto frame = cpptrace::safe_object_frame();
cpptrace::get_safe_object_frame(traceBuffer[i], &frame); cpptrace::get_safe_object_frame(traceBuffer[i], &frame);
write(CrashInfo::INSTANCE.traceFd, &frame, sizeof(cpptrace::safe_object_frame));
auto* wptr = reinterpret_cast<char*>(&frame);
auto* end = wptr + sizeof(cpptrace::safe_object_frame); // NOLINT
while (wptr != end) {
auto r = write(CrashInfo::INSTANCE.traceFd, &frame, sizeof(cpptrace::safe_object_frame));
if (r < 0 && errno == EINTR) continue;
if (r <= 0) goto fail;
wptr += r; // NOLINT
}
} }
fail:;
} }
// TODO: coredump fork and crash reporter remain as zombies, fix
auto coredumpPid = fork(); auto coredumpPid = fork();
if (coredumpPid == 0) { if (coredumpPid == 0) {
raise(sig); raise(sig);
@@ -135,6 +115,7 @@ void signalHandler(
perror("Failed to fork and launch crash reporter.\n"); perror("Failed to fork and launch crash reporter.\n");
_exit(-1); _exit(-1);
} else if (pid == 0) { } else if (pid == 0) {
// dup to remove CLOEXEC // dup to remove CLOEXEC
auto dumpFdStr = std::array<char, 48>(); auto dumpFdStr = std::array<char, 48>();
auto logFdStr = std::array<char, 48>(); auto logFdStr = std::array<char, 48>();
@@ -158,21 +139,6 @@ void signalHandler(
} }
} }
void handleCppTerminate() {
if (auto ptr = std::current_exception()) {
try {
std::rethrow_exception(ptr);
} catch (std::exception& e) {
qFatal().nospace() << "Terminate called with C++ exception ("
<< cpptrace::demangle(typeid(e).name()).data() << "): " << e.what();
} catch (...) {
qFatal() << "Terminate called with non exception object";
}
}
qFatal() << "Terminate called without active C++ exception";
}
} // namespace } // namespace
void CrashHandler::init() { void CrashHandler::init() {
@@ -202,6 +168,7 @@ void CrashHandler::init() {
// Set up alternate signal stack for stack overflow handling // Set up alternate signal stack for stack overflow handling
auto ss = stack_t(); auto ss = stack_t();
ss.ss_sp = new char[SIGSTKSZ]; ss.ss_sp = new char[SIGSTKSZ];
;
ss.ss_size = SIGSTKSZ; ss.ss_size = SIGSTKSZ;
ss.ss_flags = 0; ss.ss_flags = 0;
sigaltstack(&ss, nullptr); sigaltstack(&ss, nullptr);
@@ -221,8 +188,6 @@ void CrashHandler::init() {
// NOLINTEND (misc-include-cleaner) // NOLINTEND (misc-include-cleaner)
std::set_terminate(&handleCppTerminate);
qCInfo(logCrashHandler) << "Crash handler initialized."; qCInfo(logCrashHandler) << "Crash handler initialized.";
} }
+15 -16
View File
@@ -5,7 +5,6 @@
#include <qapplication.h> #include <qapplication.h>
#include <qboxlayout.h> #include <qboxlayout.h>
#include <qconfig.h> #include <qconfig.h>
#include <qcontainerfwd.h>
#include <qdesktopservices.h> #include <qdesktopservices.h>
#include <qfont.h> #include <qfont.h>
#include <qfontinfo.h> #include <qfontinfo.h>
@@ -13,22 +12,11 @@
#include <qnamespace.h> #include <qnamespace.h>
#include <qobject.h> #include <qobject.h>
#include <qpushbutton.h> #include <qpushbutton.h>
#include <qtenvironmentvariables.h>
#include <qtversion.h> #include <qtversion.h>
#include <qwidget.h> #include <qwidget.h>
#include "build.hpp" #include "build.hpp"
namespace {
QString crashreportUrl() {
if (auto url = qEnvironmentVariable("QS_CRASHREPORT_URL"); !url.isEmpty()) {
return url;
}
return CRASHREPORT_URL;
}
} // namespace
class ReportLabel: public QWidget { class ReportLabel: public QWidget {
public: public:
ReportLabel(const QString& label, const QString& content, QWidget* parent): QWidget(parent) { ReportLabel(const QString& label, const QString& content, QWidget* parent): QWidget(parent) {
@@ -79,16 +67,22 @@ CrashReporterGui::CrashReporterGui(QString reportFolder, int pid)
if (qtVersionMatches) { if (qtVersionMatches) {
mainLayout->addWidget( mainLayout->addWidget(
new QLabel("Please open a bug report for this issue on the issue tracker.") new QLabel("Please open a bug report for this issue via github or email.")
); );
} else { } else {
mainLayout->addWidget(new QLabel( mainLayout->addWidget(new QLabel(
"Please rebuild Quickshell against the current Qt version.\n" "Please rebuild Quickshell against the current Qt version.\n"
"If this does not solve the problem, please open a bug report on the issue tracker." "If this does not solve the problem, please open a bug report via github or email."
)); ));
} }
mainLayout->addWidget(new ReportLabel("Tracker:", crashreportUrl(), this)); mainLayout->addWidget(new ReportLabel(
"Github:",
"https://github.com/quickshell-mirror/quickshell/issues/new?template=crash2.yml",
this
));
mainLayout->addWidget(new ReportLabel("Email:", "[email protected]", this));
auto* buttons = new QWidget(this); auto* buttons = new QWidget(this);
buttons->setMinimumWidth(900); buttons->setMinimumWidth(900);
@@ -118,5 +112,10 @@ void CrashReporterGui::openFolder() {
QDesktopServices::openUrl(QUrl::fromLocalFile(this->reportFolder)); QDesktopServices::openUrl(QUrl::fromLocalFile(this->reportFolder));
} }
void CrashReporterGui::openReportUrl() { QDesktopServices::openUrl(QUrl(crashreportUrl())); } void CrashReporterGui::openReportUrl() {
QDesktopServices::openUrl(
QUrl("https://github.com/outfoxxed/quickshell/issues/new?template=crash2.yml")
);
}
void CrashReporterGui::cancel() { QApplication::quit(); } void CrashReporterGui::cancel() { QApplication::quit(); }
+32 -10
View File
@@ -6,6 +6,7 @@
#include <cpptrace/basic.hpp> #include <cpptrace/basic.hpp>
#include <cpptrace/formatting.hpp> #include <cpptrace/formatting.hpp>
#include <qapplication.h> #include <qapplication.h>
#include <qconfig.h>
#include <qcoreapplication.h> #include <qcoreapplication.h>
#include <qdatastream.h> #include <qdatastream.h>
#include <qdir.h> #include <qdir.h>
@@ -14,19 +15,19 @@
#include <qloggingcategory.h> #include <qloggingcategory.h>
#include <qtenvironmentvariables.h> #include <qtenvironmentvariables.h>
#include <qtextstream.h> #include <qtextstream.h>
#include <qtversion.h>
#include <qtypes.h> #include <qtypes.h>
#include <sys/sendfile.h> #include <sys/sendfile.h>
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#include "../core/debuginfo.hpp"
#include "../core/instanceinfo.hpp" #include "../core/instanceinfo.hpp"
#include "../core/logcat.hpp" #include "../core/logcat.hpp"
#include "../core/logging.hpp" #include "../core/logging.hpp"
#include "../core/logging_p.hpp" #include "../core/logging_p.hpp"
#include "../core/paths.hpp" #include "../core/paths.hpp"
#include "../core/plugin.hpp"
#include "../core/ringbuf.hpp" #include "../core/ringbuf.hpp"
#include "build.hpp"
#include "interface.hpp" #include "interface.hpp"
namespace { namespace {
@@ -170,15 +171,41 @@ void recordCrashInfo(const QDir& crashDir, const InstanceInfo& instance) {
qCCritical(logCrashReporter) << "Failed to open crash info file for writing."; qCCritical(logCrashReporter) << "Failed to open crash info file for writing.";
} else { } else {
auto stream = QTextStream(&extraInfoFile); auto stream = QTextStream(&extraInfoFile);
stream << qs::debuginfo::combinedInfo(); stream << "===== Build Information =====\n";
stream << "Git Revision: " << GIT_REVISION << '\n';
stream << "Buildtime Qt Version: " << QT_VERSION_STR << "\n";
stream << "Build Type: " << BUILD_TYPE << '\n';
stream << "Compiler: " << COMPILER << '\n';
stream << "Complie Flags: " << COMPILE_FLAGS << "\n\n";
stream << "Build configuration:\n" << BUILD_CONFIGURATION << "\n";
stream << "\n===== Instance Information =====\n"; stream << "\n===== Runtime Information =====\n";
stream << "Runtime Qt Version: " << qVersion() << '\n';
stream << "Signal: " << strsignal(crashSignal) << " (" << crashSignal << ")\n"; // NOLINT stream << "Signal: " << strsignal(crashSignal) << " (" << crashSignal << ")\n"; // NOLINT
stream << "Crashed process ID: " << crashProc << '\n'; stream << "Crashed process ID: " << crashProc << '\n';
stream << "Run ID: " << instance.instanceId << '\n'; stream << "Run ID: " << instance.instanceId << '\n';
stream << "Shell ID: " << instance.shellId << '\n'; stream << "Shell ID: " << instance.shellId << '\n';
stream << "Config Path: " << instance.configPath << '\n'; stream << "Config Path: " << instance.configPath << '\n';
stream << "\n===== System Information =====\n\n";
stream << "/etc/os-release:";
auto osReleaseFile = QFile("/etc/os-release");
if (osReleaseFile.open(QFile::ReadOnly)) {
stream << '\n' << osReleaseFile.readAll() << '\n';
osReleaseFile.close();
} else {
stream << "FAILED TO OPEN\n";
}
stream << "/etc/lsb-release:";
auto lsbReleaseFile = QFile("/etc/lsb-release");
if (lsbReleaseFile.open(QFile::ReadOnly)) {
stream << '\n' << lsbReleaseFile.readAll();
lsbReleaseFile.close();
} else {
stream << "FAILED TO OPEN\n";
}
stream << "\n===== Stacktrace =====\n"; stream << "\n===== Stacktrace =====\n";
if (stacktrace.empty()) { if (stacktrace.empty()) {
stream << "(no trace available)\n"; stream << "(no trace available)\n";
@@ -231,17 +258,12 @@ void qsCheckCrash(int argc, char** argv) {
); );
auto app = QApplication(argc, argv); auto app = QApplication(argc, argv);
auto desktopId = QApplication::setDesktopFileName("org.quickshell");
info.instance.appId.isEmpty() ? QStringLiteral("org.quickshell") : info.instance.appId;
QApplication::setDesktopFileName(desktopId);
auto crashDir = QsPaths::crashDir(info.instance.instanceId); auto crashDir = QsPaths::crashDir(info.instance.instanceId);
qCInfo(logCrashReporter) << "Starting crash reporter..."; qCInfo(logCrashReporter) << "Starting crash reporter...";
// Required platform compatibility hooks
QsEnginePlugin::preinitPluginsOnly();
recordCrashInfo(crashDir, info.instance); recordCrashInfo(crashDir, info.instance);
auto gui = CrashReporterGui(crashDir.path(), crashProc); auto gui = CrashReporterGui(crashDir.path(), crashProc);
+16 -53
View File
@@ -1,13 +1,11 @@
#include "jsonadapter.hpp" #include "jsonadapter.hpp"
#include <qassociativeiterable.h>
#include <qcontainerfwd.h> #include <qcontainerfwd.h>
#include <qjsonarray.h> #include <qjsonarray.h>
#include <qjsondocument.h> #include <qjsondocument.h>
#include <qjsonobject.h> #include <qjsonobject.h>
#include <qjsonvalue.h> #include <qjsonvalue.h>
#include <qjsvalue.h> #include <qjsvalue.h>
#include <qmetacontainer.h>
#include <qmetaobject.h> #include <qmetaobject.h>
#include <qnamespace.h> #include <qnamespace.h>
#include <qobject.h> #include <qobject.h>
@@ -16,7 +14,6 @@
#include <qqmlengine.h> #include <qqmlengine.h>
#include <qqmlinfo.h> #include <qqmlinfo.h>
#include <qqmllist.h> #include <qqmllist.h>
#include <qsequentialiterable.h>
#include <qstringview.h> #include <qstringview.h>
#include <qvariant.h> #include <qvariant.h>
@@ -134,22 +131,13 @@ QJsonObject JsonAdapter::serializeRec(const QObject* obj, const QMetaObject* bas
} }
json.insert(prop.name(), array); json.insert(prop.name(), array);
} else if (val.canConvert<QJSValue>()) {
auto variant = val.value<QJSValue>().toVariant();
auto jv = QJsonValue::fromVariant(variant);
json.insert(prop.name(), jv);
} else { } else {
if (val.canConvert<QJSValue>()) val = val.value<QJSValue>().toVariant(); auto jv = QJsonValue::fromVariant(val);
json.insert(prop.name(), jv);
auto jsonVal = QJsonValue::fromVariant(val);
if (jsonVal.isNull() && !val.isNull() && val.isValid()) {
if (val.canConvert<QAssociativeIterable>()) {
val.convert(QMetaType::fromType<QVariantMap>());
} else if (val.canConvert<QSequentialIterable>()) {
val.convert(QMetaType::fromType<QVariantList>());
}
jsonVal = QJsonValue::fromVariant(val);
}
json.insert(prop.name(), jsonVal);
} }
} }
} }
@@ -166,16 +154,14 @@ void JsonAdapter::deserializeRec(const QJsonObject& json, QObject* obj, const QM
auto jval = json.value(prop.name()); auto jval = json.value(prop.name());
if (prop.metaType() == QMetaType::fromType<QVariant>()) { if (prop.metaType() == QMetaType::fromType<QVariant>()) {
auto newVariant = jval.toVariant(); auto variant = jval.toVariant();
auto oldValue = prop.read(obj); auto oldValue = prop.read(this).value<QJSValue>();
auto oldVariant =
oldValue.canConvert<QJSValue>() ? oldValue.value<QJSValue>().toVariant() : oldValue;
// Calling prop.write with a new QJSValue will cause a property update // Calling prop.write with a new QJSValue will cause a property update
// even if content is identical. // even if content is identical.
if (newVariant != oldVariant) { if (jval.toVariant() != oldValue.toVariant()) {
auto jsValue = qmlEngine(this)->fromVariant<QJSValue>(newVariant); auto jsValue = qmlEngine(this)->fromVariant<QJSValue>(jval.toVariant());
prop.write(obj, QVariant::fromValue(jsValue)); prop.write(this, QVariant::fromValue(jsValue));
} }
} else if (QMetaType::canView(prop.metaType(), QMetaType::fromType<JsonObject*>())) { } else if (QMetaType::canView(prop.metaType(), QMetaType::fromType<JsonObject*>())) {
// FIXME: This doesn't support creating descendants of JsonObject, as QMetaType.metaObject() // FIXME: This doesn't support creating descendants of JsonObject, as QMetaType.metaObject()
@@ -210,7 +196,7 @@ void JsonAdapter::deserializeRec(const QJsonObject& json, QObject* obj, const QM
QMetaType::fromType<QQmlListProperty<JsonObject>>() QMetaType::fromType<QQmlListProperty<JsonObject>>()
)) ))
{ {
auto pval = prop.read(obj); auto pval = prop.read(this);
if (pval.canConvert<QQmlListProperty<JsonObject>>()) { if (pval.canConvert<QQmlListProperty<JsonObject>>()) {
auto lp = pval.value<QQmlListProperty<JsonObject>>(); auto lp = pval.value<QQmlListProperty<JsonObject>>();
@@ -261,35 +247,12 @@ void JsonAdapter::deserializeRec(const QJsonObject& json, QObject* obj, const QM
} }
} else { } else {
auto variant = jval.toVariant(); auto variant = jval.toVariant();
auto convVariant = variant;
if (convVariant.convert(prop.metaType())) { if (variant.convert(prop.metaType())) {
prop.write(obj, convVariant); prop.write(obj, variant);
} else { } else {
auto pval = prop.read(obj); qmlWarning(this) << "Failed to deserialize property " << prop.name() << ": expected "
if (variant.canConvert<QSequentialIterable>() && pval.canView<QSequentialIterable>()) { << prop.metaType().name() << " but got " << jval.toVariant().typeName();
auto targetv = QVariant(pval.metaType());
auto target = targetv.view<QSequentialIterable>().metaContainer();
auto valueType = target.valueMetaType();
auto i = 0;
for (QVariant item: variant.value<QSequentialIterable>()) {
if (item.convert(valueType)) {
target.addValueAtEnd(targetv.data(), item.constData());
} else {
qmlWarning(this) << "Failed to deserialize list member " << i << " of property "
<< prop.name() << ": expected " << valueType.name() << " but got "
<< item.typeName();
}
++i;
}
prop.write(obj, targetv);
} else {
qmlWarning(this) << "Failed to deserialize property " << prop.name() << ": expected "
<< prop.metaType().name() << " but got "
<< jval.toVariant().typeName();
}
} }
} }
} }
+1 -4
View File
@@ -3,7 +3,6 @@
#include <variant> #include <variant>
#include <qbuffer.h> #include <qbuffer.h>
#include <qcoreapplication.h>
#include <qlocalserver.h> #include <qlocalserver.h>
#include <qlocalsocket.h> #include <qlocalsocket.h>
#include <qlogging.h> #include <qlogging.h>
@@ -128,9 +127,7 @@ int IpcClient::connect(const QString& id, const std::function<void(IpcClient& cl
void IpcKillCommand::exec(IpcServerConnection* /*unused*/) { void IpcKillCommand::exec(IpcServerConnection* /*unused*/) {
qInfo() << "Exiting due to IPC request."; qInfo() << "Exiting due to IPC request.";
auto* generation = EngineGeneration::currentGeneration(); EngineGeneration::currentGeneration()->quit();
if (generation) generation->quit();
else QCoreApplication::exit(0);
} }
} // namespace qs::ipc } // namespace qs::ipc
+15 -5
View File
@@ -25,12 +25,12 @@
#include <qtversion.h> #include <qtversion.h>
#include <unistd.h> #include <unistd.h>
#include "../core/debuginfo.hpp"
#include "../core/instanceinfo.hpp" #include "../core/instanceinfo.hpp"
#include "../core/logging.hpp" #include "../core/logging.hpp"
#include "../core/paths.hpp" #include "../core/paths.hpp"
#include "../io/ipccomm.hpp" #include "../io/ipccomm.hpp"
#include "../ipc/ipc.hpp" #include "../ipc/ipc.hpp"
#include "build.hpp"
#include "launch_p.hpp" #include "launch_p.hpp"
namespace qs::launch { namespace qs::launch {
@@ -519,10 +519,20 @@ int runCommand(int argc, char** argv, QCoreApplication* coreApplication) {
} }
if (state.misc.printVersion) { if (state.misc.printVersion) {
if (state.log.verbosity == 0) { qCInfo(logBare).noquote().nospace() << "quickshell " << QS_VERSION << ", revision "
qCInfo(logBare).noquote() << "Quickshell" << qs::debuginfo::qsVersion(); << GIT_REVISION << ", distributed by: " << DISTRIBUTOR;
} else {
qCInfo(logBare).noquote() << qs::debuginfo::combinedInfo(); if (state.log.verbosity > 1) {
qCInfo(logBare).noquote() << "\nBuildtime Qt Version:" << QT_VERSION_STR;
qCInfo(logBare).noquote() << "Runtime Qt Version:" << qVersion();
qCInfo(logBare).noquote() << "Compiler:" << COMPILER;
qCInfo(logBare).noquote() << "Compile Flags:" << COMPILE_FLAGS;
}
if (state.log.verbosity > 0) {
qCInfo(logBare).noquote() << "\nBuild Type:" << BUILD_TYPE;
qCInfo(logBare).noquote() << "Build configuration:";
qCInfo(logBare).noquote().nospace() << BUILD_CONFIGURATION;
} }
} else if (*state.subcommand.log) { } else if (*state.subcommand.log) {
return readLogFile(state); return readLogFile(state);
+5 -56
View File
@@ -76,8 +76,6 @@ int launch(const LaunchArgs& args, char** argv, QCoreApplication* coreApplicatio
bool useSystemStyle = false; bool useSystemStyle = false;
QString iconTheme = qEnvironmentVariable("QS_ICON_THEME"); QString iconTheme = qEnvironmentVariable("QS_ICON_THEME");
QHash<QString, QString> envOverrides; QHash<QString, QString> envOverrides;
QString appId = qEnvironmentVariable("QS_APP_ID");
bool dropExpensiveFonts = false;
QString dataDir; QString dataDir;
QString stateDir; QString stateDir;
QString cacheDir; QString cacheDir;
@@ -93,7 +91,6 @@ int launch(const LaunchArgs& args, char** argv, QCoreApplication* coreApplicatio
else if (pragma == "NativeTextRendering") pragmas.nativeTextRendering = true; else if (pragma == "NativeTextRendering") pragmas.nativeTextRendering = true;
else if (pragma == "IgnoreSystemSettings") pragmas.desktopSettingsAware = false; else if (pragma == "IgnoreSystemSettings") pragmas.desktopSettingsAware = false;
else if (pragma == "RespectSystemStyle") pragmas.useSystemStyle = true; else if (pragma == "RespectSystemStyle") pragmas.useSystemStyle = true;
else if (pragma == "DropExpensiveFonts") pragmas.dropExpensiveFonts = true;
else if (pragma.startsWith("IconTheme ")) pragmas.iconTheme = pragma.sliced(10); else if (pragma.startsWith("IconTheme ")) pragmas.iconTheme = pragma.sliced(10);
else if (pragma.startsWith("Env ")) { else if (pragma.startsWith("Env ")) {
auto envPragma = pragma.sliced(4); auto envPragma = pragma.sliced(4);
@@ -107,8 +104,6 @@ int launch(const LaunchArgs& args, char** argv, QCoreApplication* coreApplicatio
auto var = envPragma.sliced(0, splitIdx).trimmed(); auto var = envPragma.sliced(0, splitIdx).trimmed();
auto val = envPragma.sliced(splitIdx + 1).trimmed(); auto val = envPragma.sliced(splitIdx + 1).trimmed();
pragmas.envOverrides.insert(var, val); pragmas.envOverrides.insert(var, val);
} else if (pragma.startsWith("AppId ")) {
pragmas.appId = pragma.sliced(6).trimmed();
} else if (pragma.startsWith("ShellId ")) { } else if (pragma.startsWith("ShellId ")) {
shellId = pragma.sliced(8).trimmed(); shellId = pragma.sliced(8).trimmed();
} else if (pragma.startsWith("DataDir ")) { } else if (pragma.startsWith("DataDir ")) {
@@ -118,7 +113,8 @@ int launch(const LaunchArgs& args, char** argv, QCoreApplication* coreApplicatio
} else if (pragma.startsWith("CacheDir ")) { } else if (pragma.startsWith("CacheDir ")) {
pragmas.cacheDir = pragma.sliced(9).trimmed(); pragmas.cacheDir = pragma.sliced(9).trimmed();
} else { } else {
qWarning() << "Unrecognized pragma" << pragma; qCritical() << "Unrecognized pragma" << pragma;
return -1;
} }
} else if (line.startsWith("import")) break; } else if (line.startsWith("import")) break;
} }
@@ -132,24 +128,19 @@ int launch(const LaunchArgs& args, char** argv, QCoreApplication* coreApplicatio
qInfo() << "Shell ID:" << shellId << "Path ID" << pathId; qInfo() << "Shell ID:" << shellId << "Path ID" << pathId;
auto launchTime = qs::Common::LAUNCH_TIME.toSecsSinceEpoch(); auto launchTime = qs::Common::LAUNCH_TIME.toSecsSinceEpoch();
auto appId = pragmas.appId.isEmpty() ? QStringLiteral("org.quickshell") : pragmas.appId;
InstanceInfo::CURRENT = InstanceInfo { InstanceInfo::CURRENT = InstanceInfo {
.instanceId = base36Encode(getpid()) + base36Encode(launchTime), .instanceId = base36Encode(getpid()) + base36Encode(launchTime),
.configPath = args.configPath, .configPath = args.configPath,
.shellId = shellId, .shellId = shellId,
.appId = appId,
.launchTime = qs::Common::LAUNCH_TIME, .launchTime = qs::Common::LAUNCH_TIME,
.pid = getpid(), .pid = getpid(),
.display = getDisplayConnection(), .display = getDisplayConnection(),
}; };
#if CRASH_HANDLER #if CRASH_HANDLER
if (qEnvironmentVariableIsSet("QS_DISABLE_CRASH_HANDLER")) { crash::CrashHandler::init();
qInfo() << "Crash handling disabled.";
} else {
crash::CrashHandler::init();
{
auto* log = LogManager::instance(); auto* log = LogManager::instance();
crash::CrashHandler::setRelaunchInfo({ crash::CrashHandler::setRelaunchInfo({
.instance = InstanceInfo::CURRENT, .instance = InstanceInfo::CURRENT,
@@ -178,48 +169,6 @@ int launch(const LaunchArgs& args, char** argv, QCoreApplication* coreApplicatio
qputenv(var.toUtf8(), val.toUtf8()); qputenv(var.toUtf8(), val.toUtf8());
} }
pragmas.dropExpensiveFonts |= qEnvironmentVariableIntValue("QS_DROP_EXPENSIVE_FONTS") == 1;
if (pragmas.dropExpensiveFonts) {
if (auto* runDir = QsPaths::instance()->instanceRunDir()) {
auto baseConfigPath = qEnvironmentVariable("FONTCONFIG_FILE");
if (baseConfigPath.isEmpty()) baseConfigPath = "/etc/fonts/fonts.conf";
auto filterPath = runDir->filePath("fonts-override.conf");
auto filterFile = QFile(filterPath);
if (filterFile.open(QFile::WriteOnly | QFile::Truncate | QFile::Text)) {
auto filterTemplate = QStringLiteral(R"(<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "urn:fontconfig:fonts.dtd">
<fontconfig>
<include ignore_missing="no">%1</include>
<selectfont>
<rejectfont>
<pattern>
<patelt name="fontwrapper">
<string>woff</string>
</patelt>
</pattern>
<pattern>
<patelt name="fontwrapper">
<string>woff2</string>
</patelt>
</pattern>
</rejectfont>
</selectfont>
</fontconfig>
)");
QTextStream(&filterFile) << filterTemplate.arg(baseConfigPath);
filterFile.close();
qputenv("FONTCONFIG_FILE", filterPath.toUtf8());
} else {
qCritical() << "Could not write fontconfig filter to" << filterPath;
}
} else {
qCritical() << "Could not create fontconfig filter: instance run directory unavailable";
}
}
// The qml engine currently refuses to cache non file (qsintercept) paths. // The qml engine currently refuses to cache non file (qsintercept) paths.
// if (auto* cacheDir = QsPaths::instance()->cacheDir()) { // if (auto* cacheDir = QsPaths::instance()->cacheDir()) {
@@ -280,7 +229,7 @@ int launch(const LaunchArgs& args, char** argv, QCoreApplication* coreApplicatio
app = new QGuiApplication(qArgC, argv); app = new QGuiApplication(qArgC, argv);
} }
QGuiApplication::setDesktopFileName(appId); QGuiApplication::setDesktopFileName("org.quickshell");
if (args.debugPort != -1) { if (args.debugPort != -1) {
QQmlDebuggingEnabler::enableDebugging(true); QQmlDebuggingEnabler::enableDebugging(true);
-1
View File
@@ -4,7 +4,6 @@ qt_add_library(quickshell-network STATIC
network.cpp network.cpp
device.cpp device.cpp
wifi.cpp wifi.cpp
enums.cpp
) )
target_include_directories(quickshell-network PRIVATE target_include_directories(quickshell-network PRIVATE
+43 -9
View File
@@ -8,7 +8,6 @@
#include <qtmetamacros.h> #include <qtmetamacros.h>
#include "../core/logcat.hpp" #include "../core/logcat.hpp"
#include "enums.hpp"
namespace qs::network { namespace qs::network {
@@ -16,9 +15,49 @@ namespace {
QS_LOGGING_CATEGORY(logNetworkDevice, "quickshell.network.device", QtWarningMsg); QS_LOGGING_CATEGORY(logNetworkDevice, "quickshell.network.device", QtWarningMsg);
} // namespace } // namespace
QString DeviceConnectionState::toString(DeviceConnectionState::Enum state) {
switch (state) {
case Unknown: return QStringLiteral("Unknown");
case Connecting: return QStringLiteral("Connecting");
case Connected: return QStringLiteral("Connected");
case Disconnecting: return QStringLiteral("Disconnecting");
case Disconnected: return QStringLiteral("Disconnected");
default: return QStringLiteral("Unknown");
}
}
QString DeviceType::toString(DeviceType::Enum type) {
switch (type) {
case None: return QStringLiteral("None");
case Wifi: return QStringLiteral("Wifi");
default: return QStringLiteral("Unknown");
}
}
QString NMDeviceState::toString(NMDeviceState::Enum state) {
switch (state) {
case Unknown: return QStringLiteral("Unknown");
case Unmanaged: return QStringLiteral("Not managed by NetworkManager");
case Unavailable: return QStringLiteral("Unavailable");
case Disconnected: return QStringLiteral("Disconnected");
case Prepare: return QStringLiteral("Preparing to connect");
case Config: return QStringLiteral("Connecting to a network");
case NeedAuth: return QStringLiteral("Waiting for authentication");
case IPConfig: return QStringLiteral("Requesting IPv4 and/or IPv6 addresses from the network");
case IPCheck:
return QStringLiteral("Checking if further action is required for the requested connection");
case Secondaries:
return QStringLiteral("Waiting for a required secondary connection to activate");
case Activated: return QStringLiteral("Connected");
case Deactivating: return QStringLiteral("Disconnecting");
case Failed: return QStringLiteral("Failed to connect");
default: return QStringLiteral("Unknown");
};
}
NetworkDevice::NetworkDevice(DeviceType::Enum type, QObject* parent): QObject(parent), mType(type) { NetworkDevice::NetworkDevice(DeviceType::Enum type, QObject* parent): QObject(parent), mType(type) {
this->bindableConnected().setBinding([this]() { this->bindableConnected().setBinding([this]() {
return this->bState == ConnectionState::Connected; return this->bState == DeviceConnectionState::Connected;
}); });
}; };
@@ -27,17 +66,12 @@ void NetworkDevice::setAutoconnect(bool autoconnect) {
emit this->requestSetAutoconnect(autoconnect); emit this->requestSetAutoconnect(autoconnect);
} }
void NetworkDevice::setNmManaged(bool managed) {
if (this->bNmManaged == managed) return;
emit this->requestSetNmManaged(managed);
}
void NetworkDevice::disconnect() { void NetworkDevice::disconnect() {
if (this->bState == ConnectionState::Disconnected) { if (this->bState == DeviceConnectionState::Disconnected) {
qCCritical(logNetworkDevice) << "Device" << this << "is already disconnected"; qCCritical(logNetworkDevice) << "Device" << this << "is already disconnected";
return; return;
} }
if (this->bState == ConnectionState::Disconnecting) { if (this->bState == DeviceConnectionState::Disconnecting) {
qCCritical(logNetworkDevice) << "Device" << this << "is already disconnecting"; qCCritical(logNetworkDevice) << "Device" << this << "is already disconnecting";
return; return;
} }
+79 -30
View File
@@ -6,22 +6,76 @@
#include <qtmetamacros.h> #include <qtmetamacros.h>
#include <qtypes.h> #include <qtypes.h>
#include "../core/doc.hpp"
#include "enums.hpp"
namespace qs::network { namespace qs::network {
///! Connection state of a NetworkDevice.
class DeviceConnectionState: public QObject {
Q_OBJECT;
QML_ELEMENT;
QML_SINGLETON;
public:
enum Enum : quint8 {
Unknown = 0,
Connecting = 1,
Connected = 2,
Disconnecting = 3,
Disconnected = 4,
};
Q_ENUM(Enum);
Q_INVOKABLE static QString toString(DeviceConnectionState::Enum state);
};
///! Type of network device.
class DeviceType: public QObject {
Q_OBJECT;
QML_ELEMENT;
QML_SINGLETON;
public:
enum Enum : quint8 {
None = 0,
Wifi = 1,
};
Q_ENUM(Enum);
Q_INVOKABLE static QString toString(DeviceType::Enum type);
};
///! NetworkManager-specific device state.
/// In sync with https://networkmanager.dev/docs/api/latest/nm-dbus-types.html#NMDeviceState.
class NMDeviceState: public QObject {
Q_OBJECT;
QML_ELEMENT;
QML_SINGLETON;
public:
enum Enum : quint8 {
Unknown = 0,
Unmanaged = 10,
Unavailable = 20,
Disconnected = 30,
Prepare = 40,
Config = 50,
NeedAuth = 60,
IPConfig = 70,
IPCheck = 80,
Secondaries = 90,
Activated = 100,
Deactivating = 110,
Failed = 120,
};
Q_ENUM(Enum);
Q_INVOKABLE static QString toString(NMDeviceState::Enum state);
};
///! A network device. ///! A network device.
/// The @@type property may be used to determine if this device is a @@WifiDevice. /// When @@type is `Wifi`, the device is a @@WifiDevice, which can be used to scan for and connect to access points.
class NetworkDevice: public QObject { class NetworkDevice: public QObject {
Q_OBJECT; Q_OBJECT;
QML_ELEMENT; QML_ELEMENT;
QML_UNCREATABLE("Devices can only be acquired through Network"); QML_UNCREATABLE("Devices can only be acquired through Network");
// clang-format off // clang-format off
/// The device type. /// The device type.
///
/// When the device type is `Wifi`, the device object is a @@WifiDevice which exposes wifi network
/// connection and scanning.
Q_PROPERTY(DeviceType::Enum type READ type CONSTANT); Q_PROPERTY(DeviceType::Enum type READ type CONSTANT);
/// The name of the device's control interface. /// The name of the device's control interface.
Q_PROPERTY(QString name READ name NOTIFY nameChanged BINDABLE bindableName); Q_PROPERTY(QString name READ name NOTIFY nameChanged BINDABLE bindableName);
@@ -30,12 +84,10 @@ class NetworkDevice: public QObject {
/// True if the device is connected. /// True if the device is connected.
Q_PROPERTY(bool connected READ default NOTIFY connectedChanged BINDABLE bindableConnected); Q_PROPERTY(bool connected READ default NOTIFY connectedChanged BINDABLE bindableConnected);
/// Connection state of the device. /// Connection state of the device.
Q_PROPERTY(qs::network::ConnectionState::Enum state READ default NOTIFY stateChanged BINDABLE bindableState); Q_PROPERTY(qs::network::DeviceConnectionState::Enum state READ default NOTIFY stateChanged BINDABLE bindableState);
/// True if the device is managed by NetworkManager. /// A more specific device state when the backend is NetworkManager.
/// Q_PROPERTY(qs::network::NMDeviceState::Enum nmState READ default NOTIFY nmStateChanged BINDABLE bindableNmState);
/// > [!WARNING] Only valid for the NetworkManager backend. /// True if the device is allowed to autoconnect.
Q_PROPERTY(bool nmManaged READ nmManaged WRITE setNmManaged NOTIFY nmManagedChanged)
/// True if the device is allowed to autoconnect to a network.
Q_PROPERTY(bool autoconnect READ autoconnect WRITE setAutoconnect NOTIFY autoconnectChanged); Q_PROPERTY(bool autoconnect READ autoconnect WRITE setAutoconnect NOTIFY autoconnectChanged);
// clang-format on // clang-format on
@@ -45,28 +97,25 @@ public:
/// Disconnects the device and prevents it from automatically activating further connections. /// Disconnects the device and prevents it from automatically activating further connections.
Q_INVOKABLE void disconnect(); Q_INVOKABLE void disconnect();
[[nodiscard]] DeviceType::Enum type() const { return this->mType; } [[nodiscard]] DeviceType::Enum type() const { return this->mType; };
QBindable<QString> bindableName() { return &this->bName; } QBindable<QString> bindableName() { return &this->bName; };
[[nodiscard]] QString name() const { return this->bName; } [[nodiscard]] QString name() const { return this->bName; };
QBindable<QString> bindableAddress() { return &this->bAddress; } QBindable<QString> bindableAddress() { return &this->bAddress; };
QBindable<bool> bindableConnected() { return &this->bConnected; } QBindable<bool> bindableConnected() { return &this->bConnected; };
QBindable<ConnectionState::Enum> bindableState() { return &this->bState; } QBindable<DeviceConnectionState::Enum> bindableState() { return &this->bState; };
QBindable<bool> bindableNmManaged() { return &this->bNmManaged; } QBindable<NMDeviceState::Enum> bindableNmState() { return &this->bNmState; };
[[nodiscard]] bool nmManaged() { return this->bNmManaged; } [[nodiscard]] bool autoconnect() const { return this->bAutoconnect; };
void setNmManaged(bool managed); QBindable<bool> bindableAutoconnect() { return &this->bAutoconnect; };
QBindable<bool> bindableAutoconnect() { return &this->bAutoconnect; }
[[nodiscard]] bool autoconnect() { return this->bAutoconnect; }
void setAutoconnect(bool autoconnect); void setAutoconnect(bool autoconnect);
signals: signals:
QSDOC_HIDE void requestDisconnect(); void requestDisconnect();
QSDOC_HIDE void requestSetAutoconnect(bool autoconnect); void requestSetAutoconnect(bool autoconnect);
QSDOC_HIDE void requestSetNmManaged(bool managed);
void nameChanged(); void nameChanged();
void addressChanged(); void addressChanged();
void connectedChanged(); void connectedChanged();
void stateChanged(); void stateChanged();
void nmManagedChanged(); void nmStateChanged();
void autoconnectChanged(); void autoconnectChanged();
private: private:
@@ -75,8 +124,8 @@ private:
Q_OBJECT_BINDABLE_PROPERTY(NetworkDevice, QString, bName, &NetworkDevice::nameChanged); Q_OBJECT_BINDABLE_PROPERTY(NetworkDevice, QString, bName, &NetworkDevice::nameChanged);
Q_OBJECT_BINDABLE_PROPERTY(NetworkDevice, QString, bAddress, &NetworkDevice::addressChanged); Q_OBJECT_BINDABLE_PROPERTY(NetworkDevice, QString, bAddress, &NetworkDevice::addressChanged);
Q_OBJECT_BINDABLE_PROPERTY(NetworkDevice, bool, bConnected, &NetworkDevice::connectedChanged); Q_OBJECT_BINDABLE_PROPERTY(NetworkDevice, bool, bConnected, &NetworkDevice::connectedChanged);
Q_OBJECT_BINDABLE_PROPERTY(NetworkDevice, ConnectionState::Enum, bState, &NetworkDevice::stateChanged); Q_OBJECT_BINDABLE_PROPERTY(NetworkDevice, DeviceConnectionState::Enum, bState, &NetworkDevice::stateChanged);
Q_OBJECT_BINDABLE_PROPERTY(NetworkDevice, bool, bNmManaged, &NetworkDevice::nmManagedChanged); Q_OBJECT_BINDABLE_PROPERTY(NetworkDevice, NMDeviceState::Enum, bNmState, &NetworkDevice::nmStateChanged);
Q_OBJECT_BINDABLE_PROPERTY(NetworkDevice, bool, bAutoconnect, &NetworkDevice::autoconnectChanged); Q_OBJECT_BINDABLE_PROPERTY(NetworkDevice, bool, bAutoconnect, &NetworkDevice::autoconnectChanged);
// clang-format on // clang-format on
}; };
-86
View File
@@ -1,86 +0,0 @@
#include "enums.hpp"
#include <qstring.h>
namespace qs::network {
QString NetworkConnectivity::toString(NetworkConnectivity::Enum conn) {
switch (conn) {
case Unknown: return QStringLiteral("Unknown");
case None: return QStringLiteral("Not connected to a network");
case Portal: return QStringLiteral("Connection intercepted by a captive portal");
case Limited: return QStringLiteral("Partial internet connectivity");
case Full: return QStringLiteral("Full internet connectivity");
default: return QStringLiteral("Unknown");
}
}
QString NetworkBackendType::toString(NetworkBackendType::Enum type) {
switch (type) {
case NetworkBackendType::None: return "None";
case NetworkBackendType::NetworkManager: return "NetworkManager";
default: return "Unknown";
}
}
QString ConnectionState::toString(ConnectionState::Enum state) {
switch (state) {
case Unknown: return QStringLiteral("Unknown");
case Connecting: return QStringLiteral("Connecting");
case Connected: return QStringLiteral("Connected");
case Disconnecting: return QStringLiteral("Disconnecting");
case Disconnected: return QStringLiteral("Disconnected");
default: return QStringLiteral("Unknown");
}
}
QString ConnectionFailReason::toString(ConnectionFailReason::Enum reason) {
switch (reason) {
case Unknown: return QStringLiteral("Unknown");
case NoSecrets: return QStringLiteral("Secrets were required but not provided");
case WifiClientDisconnected: return QStringLiteral("Wi-Fi supplicant diconnected");
case WifiClientFailed: return QStringLiteral("Wi-Fi supplicant failed");
case WifiAuthTimeout: return QStringLiteral("Wi-Fi connection took too long to authenticate");
case WifiNetworkLost: return QStringLiteral("Wi-Fi network could not be found");
default: return QStringLiteral("Unknown");
}
}
QString DeviceType::toString(DeviceType::Enum type) {
switch (type) {
case None: return QStringLiteral("None");
case Wifi: return QStringLiteral("Wifi");
default: return QStringLiteral("Unknown");
}
}
QString WifiSecurityType::toString(WifiSecurityType::Enum type) {
switch (type) {
case Unknown: return QStringLiteral("Unknown");
case Wpa3SuiteB192: return QStringLiteral("WPA3 Suite B 192-bit");
case Sae: return QStringLiteral("WPA3");
case Wpa2Eap: return QStringLiteral("WPA2 Enterprise");
case Wpa2Psk: return QStringLiteral("WPA2");
case WpaEap: return QStringLiteral("WPA Enterprise");
case WpaPsk: return QStringLiteral("WPA");
case StaticWep: return QStringLiteral("WEP");
case DynamicWep: return QStringLiteral("Dynamic WEP");
case Leap: return QStringLiteral("LEAP");
case Owe: return QStringLiteral("OWE");
case Open: return QStringLiteral("Open");
default: return QStringLiteral("Unknown");
}
}
QString WifiDeviceMode::toString(WifiDeviceMode::Enum mode) {
switch (mode) {
case Unknown: return QStringLiteral("Unknown");
case AdHoc: return QStringLiteral("Ad-Hoc");
case Station: return QStringLiteral("Station");
case AccessPoint: return QStringLiteral("Access Point");
case Mesh: return QStringLiteral("Mesh");
default: return QStringLiteral("Unknown");
};
}
} // namespace qs::network
-154
View File
@@ -1,154 +0,0 @@
#pragma once
#include <qobject.h>
#include <qqmlintegration.h>
#include <qtypes.h>
namespace qs::network {
///! The degree to which the host can reach the internet.
class NetworkConnectivity: public QObject {
Q_OBJECT;
QML_ELEMENT;
QML_SINGLETON;
public:
enum Enum : quint8 {
/// Network connectivity is unknown. This means the connectivity checks are disabled or have not run yet.
Unknown = 0,
/// The host is not connected to any network.
None = 1,
/// The internet connection is hijacked by a captive portal gateway.
/// This indicates the shell should open a sandboxed web browser window for the purpose of authenticating to a gateway.
Portal = 2,
/// The host is connected to a network but does not appear to be able to reach the full internet.
Limited = 3,
/// The host is connected to a network and appears to be able to reach the full internet.
Full = 4,
};
Q_ENUM(Enum);
Q_INVOKABLE static QString toString(NetworkConnectivity::Enum conn);
};
///! The backend supplying the Network service.
class NetworkBackendType: public QObject {
Q_OBJECT;
QML_ELEMENT;
QML_SINGLETON;
public:
enum Enum : quint8 {
None = 0,
NetworkManager = 1,
};
Q_ENUM(Enum);
Q_INVOKABLE static QString toString(NetworkBackendType::Enum type);
};
///! The connection state of a device or network.
class ConnectionState: public QObject {
Q_OBJECT;
QML_ELEMENT;
QML_SINGLETON;
public:
enum Enum : quint8 {
Unknown = 0,
Connecting = 1,
Connected = 2,
Disconnecting = 3,
Disconnected = 4,
};
Q_ENUM(Enum);
Q_INVOKABLE static QString toString(ConnectionState::Enum state);
};
///! The reason a connection failed.
class ConnectionFailReason: public QObject {
Q_OBJECT;
QML_ELEMENT;
QML_SINGLETON;
public:
enum Enum : quint8 {
/// The connection failed for an unknown reason.
Unknown = 0,
/// Secrets were required, but not provided.
NoSecrets = 1,
/// The Wi-Fi supplicant disconnected.
WifiClientDisconnected = 2,
/// The Wi-Fi supplicant failed.
WifiClientFailed = 3,
/// The Wi-Fi connection took too long to authenticate.
WifiAuthTimeout = 4,
/// The Wi-Fi network could not be found.
WifiNetworkLost = 5,
};
Q_ENUM(Enum);
Q_INVOKABLE static QString toString(ConnectionFailReason::Enum reason);
};
///! Type of a @@NetworkDevice.
class DeviceType: public QObject {
Q_OBJECT;
QML_ELEMENT;
QML_SINGLETON;
public:
enum Enum : quint8 {
None = 0,
Wifi = 1,
};
Q_ENUM(Enum);
Q_INVOKABLE static QString toString(DeviceType::Enum type);
};
///! The security type of a @@WifiNetwork.
class WifiSecurityType: public QObject {
Q_OBJECT;
QML_ELEMENT;
QML_SINGLETON;
public:
enum Enum : quint8 {
Wpa3SuiteB192 = 0,
Sae = 1,
Wpa2Eap = 2,
Wpa2Psk = 3,
WpaEap = 4,
WpaPsk = 5,
StaticWep = 6,
DynamicWep = 7,
Leap = 8,
Owe = 9,
Open = 10,
Unknown = 11,
};
Q_ENUM(Enum);
Q_INVOKABLE static QString toString(WifiSecurityType::Enum type);
};
///! The 802.11 mode of a @@WifiDevice.
class WifiDeviceMode: public QObject {
Q_OBJECT;
QML_ELEMENT;
QML_SINGLETON;
public:
enum Enum : quint8 {
/// The device is part of an Ad-Hoc network without a central access point.
AdHoc = 0,
/// The device is a station that can connect to networks.
Station = 1,
/// The device is a local hotspot/access point.
AccessPoint = 2,
/// The device is an 802.11s mesh point.
Mesh = 3,
/// The device mode is unknown.
Unknown = 4,
};
Q_ENUM(Enum);
Q_INVOKABLE static QString toString(WifiDeviceMode::Enum mode);
};
} // namespace qs::network
-2
View File
@@ -4,8 +4,6 @@ headers = [
"network.hpp", "network.hpp",
"device.hpp", "device.hpp",
"wifi.hpp", "wifi.hpp",
"enums.hpp",
"nm/settings.hpp",
] ]
----- -----
This module exposes Network management APIs provided by a supported network backend. This module exposes Network management APIs provided by a supported network backend.
+12 -78
View File
@@ -1,7 +1,6 @@
#include "network.hpp" #include "network.hpp"
#include <utility> #include <utility>
#include <qdebug.h>
#include <qlogging.h> #include <qlogging.h>
#include <qloggingcategory.h> #include <qloggingcategory.h>
#include <qobject.h> #include <qobject.h>
@@ -10,9 +9,7 @@
#include "../core/logcat.hpp" #include "../core/logcat.hpp"
#include "device.hpp" #include "device.hpp"
#include "enums.hpp"
#include "nm/backend.hpp" #include "nm/backend.hpp"
#include "nm/settings.hpp"
namespace qs::network { namespace qs::network {
@@ -20,22 +17,25 @@ namespace {
QS_LOGGING_CATEGORY(logNetwork, "quickshell.network", QtWarningMsg); QS_LOGGING_CATEGORY(logNetwork, "quickshell.network", QtWarningMsg);
} // namespace } // namespace
QString NetworkState::toString(NetworkState::Enum state) {
switch (state) {
case NetworkState::Connecting: return QStringLiteral("Connecting");
case NetworkState::Connected: return QStringLiteral("Connected");
case NetworkState::Disconnecting: return QStringLiteral("Disconnecting");
case NetworkState::Disconnected: return QStringLiteral("Disconnected");
default: return QStringLiteral("Unknown");
}
}
Networking::Networking(QObject* parent): QObject(parent) { Networking::Networking(QObject* parent): QObject(parent) {
// Try to create the NetworkManager backend and bind to it. // Try to create the NetworkManager backend and bind to it.
auto* nm = new NetworkManager(this); auto* nm = new NetworkManager(this);
if (nm->isAvailable()) { if (nm->isAvailable()) {
// clang-format off
QObject::connect(nm, &NetworkManager::deviceAdded, this, &Networking::deviceAdded); QObject::connect(nm, &NetworkManager::deviceAdded, this, &Networking::deviceAdded);
QObject::connect(nm, &NetworkManager::deviceRemoved, this, &Networking::deviceRemoved); QObject::connect(nm, &NetworkManager::deviceRemoved, this, &Networking::deviceRemoved);
QObject::connect(this, &Networking::requestSetWifiEnabled, nm, &NetworkManager::setWifiEnabled); QObject::connect(this, &Networking::requestSetWifiEnabled, nm, &NetworkManager::setWifiEnabled);
QObject::connect(this, &Networking::requestSetConnectivityCheckEnabled, nm, &NetworkManager::setConnectivityCheckEnabled);
QObject::connect(this, &Networking::requestCheckConnectivity, nm, &NetworkManager::checkConnectivity);
this->bindableWifiEnabled().setBinding([nm]() { return nm->wifiEnabled(); }); this->bindableWifiEnabled().setBinding([nm]() { return nm->wifiEnabled(); });
this->bindableWifiHardwareEnabled().setBinding([nm]() { return nm->wifiHardwareEnabled(); }); this->bindableWifiHardwareEnabled().setBinding([nm]() { return nm->wifiHardwareEnabled(); });
this->bindableCanCheckConnectivity().setBinding([nm]() { return nm->connectivityCheckAvailable(); });
this->bindableConnectivityCheckEnabled().setBinding([nm]() { return nm->connectivityCheckEnabled(); });
this->bindableConnectivity().setBinding([nm]() { return static_cast<NetworkConnectivity::Enum>(nm->connectivity()); });
// clang-format on
this->mBackend = nm; this->mBackend = nm;
this->mBackendType = NetworkBackendType::NetworkManager; this->mBackendType = NetworkBackendType::NetworkManager;
@@ -43,89 +43,23 @@ Networking::Networking(QObject* parent): QObject(parent) {
} else { } else {
delete nm; delete nm;
} }
qCCritical(logNetwork) << "Network will not work. Could not find an available backend.";
}
Networking* Networking::instance() { qCCritical(logNetwork) << "Network will not work. Could not find an available backend.";
static Networking* instance = new Networking(); // NOLINT
return instance;
} }
void Networking::deviceAdded(NetworkDevice* dev) { this->mDevices.insertObject(dev); } void Networking::deviceAdded(NetworkDevice* dev) { this->mDevices.insertObject(dev); }
void Networking::deviceRemoved(NetworkDevice* dev) { this->mDevices.removeObject(dev); } void Networking::deviceRemoved(NetworkDevice* dev) { this->mDevices.removeObject(dev); }
void Networking::checkConnectivity() {
if (!this->bConnectivityCheckEnabled || !this->bCanCheckConnectivity) return;
emit this->requestCheckConnectivity();
}
void Networking::setWifiEnabled(bool enabled) { void Networking::setWifiEnabled(bool enabled) {
if (this->bWifiEnabled == enabled) return; if (this->bWifiEnabled == enabled) return;
emit this->requestSetWifiEnabled(enabled); emit this->requestSetWifiEnabled(enabled);
} }
void Networking::setConnectivityCheckEnabled(bool enabled) {
if (this->bConnectivityCheckEnabled == enabled) return;
emit this->requestSetConnectivityCheckEnabled(enabled);
}
NetworkingQml::NetworkingQml(QObject* parent): QObject(parent) {
// clang-format off
QObject::connect(Networking::instance(), &Networking::wifiEnabledChanged, this, &NetworkingQml::wifiEnabledChanged);
QObject::connect(Networking::instance(), &Networking::wifiHardwareEnabledChanged, this, &NetworkingQml::wifiHardwareEnabledChanged);
QObject::connect(Networking::instance(), &Networking::canCheckConnectivityChanged, this, &NetworkingQml::canCheckConnectivityChanged);
QObject::connect(Networking::instance(), &Networking::connectivityCheckEnabledChanged, this, &NetworkingQml::connectivityCheckEnabledChanged);
QObject::connect(Networking::instance(), &Networking::connectivityChanged, this, &NetworkingQml::connectivityChanged);
// clang-format on
}
void NetworkingQml::checkConnectivity() { Networking::instance()->checkConnectivity(); }
Network::Network(QString name, QObject* parent): QObject(parent), mName(std::move(name)) { Network::Network(QString name, QObject* parent): QObject(parent), mName(std::move(name)) {
this->bStateChanging.setBinding([this] { this->bStateChanging.setBinding([this] {
auto state = this->bState.value(); auto state = this->bState.value();
return state == ConnectionState::Connecting || state == ConnectionState::Disconnecting; return state == NetworkState::Connecting || state == NetworkState::Disconnecting;
}); });
}; };
void Network::connect() {
if (this->bConnected) {
qCCritical(logNetwork) << this << "is already connected.";
return;
}
this->requestConnect();
}
void Network::connectWithSettings(NMSettings* settings) {
if (this->bConnected) {
qCCritical(logNetwork) << this << "is already connected.";
return;
}
if (this->bNmSettings.value().indexOf(settings) == -1) return;
this->requestConnectWithSettings(settings);
}
void Network::disconnect() {
if (!this->bConnected) {
qCCritical(logNetwork) << this << "is not currently connected";
return;
}
this->requestDisconnect();
}
void Network::forget() { this->requestForget(); }
void Network::settingsAdded(NMSettings* settings) {
auto list = this->bNmSettings.value();
if (list.contains(settings)) return;
list.append(settings);
this->bNmSettings = list;
}
void Network::settingsRemoved(NMSettings* settings) {
auto list = this->bNmSettings.value();
list.removeOne(settings);
this->bNmSettings = list;
}
} // namespace qs::network } // namespace qs::network
+61 -152
View File
@@ -6,14 +6,43 @@
#include <qtmetamacros.h> #include <qtmetamacros.h>
#include <qtypes.h> #include <qtypes.h>
#include "../core/doc.hpp"
#include "../core/model.hpp" #include "../core/model.hpp"
#include "device.hpp" #include "device.hpp"
#include "enums.hpp"
#include "nm/settings.hpp"
namespace qs::network { namespace qs::network {
///! The connection state of a Network.
class NetworkState: public QObject {
Q_OBJECT;
QML_ELEMENT;
QML_SINGLETON;
public:
enum Enum : quint8 {
Unknown = 0,
Connecting = 1,
Connected = 2,
Disconnecting = 3,
Disconnected = 4,
};
Q_ENUM(Enum);
Q_INVOKABLE static QString toString(NetworkState::Enum state);
};
///! The backend supplying the Network service.
class NetworkBackendType: public QObject {
Q_OBJECT;
QML_ELEMENT;
QML_SINGLETON;
public:
enum Enum : quint8 {
None = 0,
NetworkManager = 1,
};
Q_ENUM(Enum);
};
class NetworkBackend: public QObject { class NetworkBackend: public QObject {
Q_OBJECT; Q_OBJECT;
@@ -24,65 +53,15 @@ protected:
explicit NetworkBackend(QObject* parent = nullptr): QObject(parent) {}; explicit NetworkBackend(QObject* parent = nullptr): QObject(parent) {};
}; };
class Networking: public QObject {
Q_OBJECT;
public:
static Networking* instance();
void checkConnectivity();
[[nodiscard]] ObjectModel<NetworkDevice>* devices() { return &this->mDevices; }
[[nodiscard]] NetworkBackendType::Enum backend() const { return this->mBackendType; }
QBindable<bool> bindableWifiEnabled() { return &this->bWifiEnabled; }
[[nodiscard]] bool wifiEnabled() const { return this->bWifiEnabled; }
void setWifiEnabled(bool enabled);
QBindable<bool> bindableWifiHardwareEnabled() { return &this->bWifiHardwareEnabled; }
QBindable<bool> bindableCanCheckConnectivity() { return &this->bCanCheckConnectivity; }
QBindable<bool> bindableConnectivityCheckEnabled() { return &this->bConnectivityCheckEnabled; }
[[nodiscard]] bool connectivityCheckEnabled() const { return this->bConnectivityCheckEnabled; }
void setConnectivityCheckEnabled(bool enabled);
QBindable<NetworkConnectivity::Enum> bindableConnectivity() { return &this->bConnectivity; }
signals:
void requestSetWifiEnabled(bool enabled);
void requestSetConnectivityCheckEnabled(bool enabled);
void requestCheckConnectivity();
void wifiEnabledChanged();
void wifiHardwareEnabledChanged();
void canCheckConnectivityChanged();
void connectivityCheckEnabledChanged();
void connectivityChanged();
private slots:
void deviceAdded(NetworkDevice* dev);
void deviceRemoved(NetworkDevice* dev);
private:
explicit Networking(QObject* parent = nullptr);
ObjectModel<NetworkDevice> mDevices {this};
NetworkBackend* mBackend = nullptr;
NetworkBackendType::Enum mBackendType = NetworkBackendType::None;
// clang-format off
Q_OBJECT_BINDABLE_PROPERTY(Networking, bool, bWifiEnabled, &Networking::wifiEnabledChanged);
Q_OBJECT_BINDABLE_PROPERTY(Networking, bool, bWifiHardwareEnabled, &Networking::wifiHardwareEnabledChanged);
Q_OBJECT_BINDABLE_PROPERTY(Networking, bool, bCanCheckConnectivity, &Networking::canCheckConnectivityChanged);
Q_OBJECT_BINDABLE_PROPERTY(Networking, bool, bConnectivityCheckEnabled, &Networking::connectivityCheckEnabledChanged);
Q_OBJECT_BINDABLE_PROPERTY(Networking, NetworkConnectivity::Enum, bConnectivity, &Networking::connectivityChanged);
// clang-format on
};
///! The Network service. ///! The Network service.
/// An interface to a network backend (currently only NetworkManager), /// An interface to a network backend (currently only NetworkManager),
/// which can be used to view, configure, and connect to various networks. /// which can be used to view, configure, and connect to various networks.
class NetworkingQml: public QObject { class Networking: public QObject {
Q_OBJECT; Q_OBJECT;
QML_NAMED_ELEMENT(Networking);
QML_SINGLETON; QML_SINGLETON;
QML_ELEMENT;
// clang-format off // clang-format off
/// A list of all network devices. Networks are exposed through their respective devices. /// A list of all network devices.
QSDOC_TYPE_OVERRIDE(ObjectModel<qs::network::NetworkDevice>*); QSDOC_TYPE_OVERRIDE(ObjectModel<qs::network::NetworkDevice>*);
Q_PROPERTY(UntypedObjectModel* devices READ devices CONSTANT); Q_PROPERTY(UntypedObjectModel* devices READ devices CONSTANT);
/// The backend being used to power the Network service. /// The backend being used to power the Network service.
@@ -91,143 +70,73 @@ class NetworkingQml: public QObject {
Q_PROPERTY(bool wifiEnabled READ wifiEnabled WRITE setWifiEnabled NOTIFY wifiEnabledChanged); Q_PROPERTY(bool wifiEnabled READ wifiEnabled WRITE setWifiEnabled NOTIFY wifiEnabledChanged);
/// State of the rfkill hardware block of all wireless devices. /// State of the rfkill hardware block of all wireless devices.
Q_PROPERTY(bool wifiHardwareEnabled READ default NOTIFY wifiHardwareEnabledChanged BINDABLE bindableWifiHardwareEnabled); Q_PROPERTY(bool wifiHardwareEnabled READ default NOTIFY wifiHardwareEnabledChanged BINDABLE bindableWifiHardwareEnabled);
/// True if the @@backend supports connectivity checks.
Q_PROPERTY(bool canCheckConnectivity READ default NOTIFY canCheckConnectivityChanged BINDABLE bindableCanCheckConnectivity);
/// True if connectivity checking is enabled.
Q_PROPERTY(bool connectivityCheckEnabled READ connectivityCheckEnabled WRITE setConnectivityCheckEnabled NOTIFY connectivityCheckEnabledChanged);
/// The result of the last connectivity check.
///
/// Connectivity checks may require additional configuration depending on your distro.
///
/// > [!NOTE] This property can be used to determine if network access is restricted
/// > or gated behind a captive portal.
/// >
/// > If checking for captive portals, @@checkConnectivity() should be called after
/// > the portal is dismissed to update this property.
Q_PROPERTY(qs::network::NetworkConnectivity::Enum connectivity READ default NOTIFY connectivityChanged BINDABLE bindableConnectivity);
// clang-format on // clang-format on
public: public:
explicit NetworkingQml(QObject* parent = nullptr); explicit Networking(QObject* parent = nullptr);
/// Re-check the network connectivity state immediately. [[nodiscard]] ObjectModel<NetworkDevice>* devices() { return &this->mDevices; };
/// > [!NOTE] This should be invoked after a user dismisses a web browser that was opened to authenticate via a captive portal. [[nodiscard]] NetworkBackendType::Enum backend() const { return this->mBackendType; };
Q_INVOKABLE static void checkConnectivity(); QBindable<bool> bindableWifiEnabled() { return &this->bWifiEnabled; };
[[nodiscard]] bool wifiEnabled() const { return this->bWifiEnabled; };
[[nodiscard]] static ObjectModel<NetworkDevice>* devices() { void setWifiEnabled(bool enabled);
return Networking::instance()->devices(); QBindable<bool> bindableWifiHardwareEnabled() { return &this->bWifiHardwareEnabled; };
}
[[nodiscard]] static NetworkBackendType::Enum backend() {
return Networking::instance()->backend();
}
[[nodiscard]] static bool wifiEnabled() { return Networking::instance()->wifiEnabled(); }
static void setWifiEnabled(bool enabled) { Networking::instance()->setWifiEnabled(enabled); }
[[nodiscard]] static QBindable<bool> bindableWifiHardwareEnabled() {
return Networking::instance()->bindableWifiHardwareEnabled();
}
[[nodiscard]] static QBindable<bool> bindableWifiEnabled() {
return Networking::instance()->bindableWifiEnabled();
}
[[nodiscard]] static QBindable<bool> bindableCanCheckConnectivity() {
return Networking::instance()->bindableCanCheckConnectivity();
}
[[nodiscard]] static bool connectivityCheckEnabled() {
return Networking::instance()->connectivityCheckEnabled();
}
static void setConnectivityCheckEnabled(bool enabled) {
Networking::instance()->setConnectivityCheckEnabled(enabled);
}
[[nodiscard]] static QBindable<NetworkConnectivity::Enum> bindableConnectivity() {
return Networking::instance()->bindableConnectivity();
}
signals: signals:
void requestSetWifiEnabled(bool enabled);
void wifiEnabledChanged(); void wifiEnabledChanged();
void wifiHardwareEnabledChanged(); void wifiHardwareEnabledChanged();
void canCheckConnectivityChanged();
void connectivityCheckEnabledChanged(); private slots:
void connectivityChanged(); void deviceAdded(NetworkDevice* dev);
void deviceRemoved(NetworkDevice* dev);
private:
ObjectModel<NetworkDevice> mDevices {this};
NetworkBackend* mBackend = nullptr;
NetworkBackendType::Enum mBackendType = NetworkBackendType::None;
// clang-format off
Q_OBJECT_BINDABLE_PROPERTY(Networking, bool, bWifiEnabled, &Networking::wifiEnabledChanged);
Q_OBJECT_BINDABLE_PROPERTY(Networking, bool, bWifiHardwareEnabled, &Networking::wifiHardwareEnabledChanged);
// clang-format on
}; };
///! A network. ///! A network.
/// A network. Networks derived from a @@WifiDevice are @@WifiNetwork instances.
class Network: public QObject { class Network: public QObject {
Q_OBJECT; Q_OBJECT;
QML_ELEMENT; QML_ELEMENT;
QML_UNCREATABLE("Network can only be aqcuired through networking devices"); QML_UNCREATABLE("BaseNetwork can only be aqcuired through network devices");
// clang-format off // clang-format off
/// The name of the network. /// The name of the network.
Q_PROPERTY(QString name READ name CONSTANT); Q_PROPERTY(QString name READ name CONSTANT);
/// A list of NetworkManager connnection settings profiles for this network.
///
/// > [!WARNING] Only valid for the NetworkManager backend.
Q_PROPERTY(QList<NMSettings*> nmSettings READ nmSettings NOTIFY nmSettingsChanged BINDABLE bindableNmSettings);
/// True if the network is connected. /// True if the network is connected.
Q_PROPERTY(bool connected READ default NOTIFY connectedChanged BINDABLE bindableConnected); Q_PROPERTY(bool connected READ default NOTIFY connectedChanged BINDABLE bindableConnected);
/// True if the wifi network has known connection settings saved.
Q_PROPERTY(bool known READ default NOTIFY knownChanged BINDABLE bindableKnown);
/// The connectivity state of the network. /// The connectivity state of the network.
Q_PROPERTY(ConnectionState::Enum state READ default NOTIFY stateChanged BINDABLE bindableState); Q_PROPERTY(NetworkState::Enum state READ default NOTIFY stateChanged BINDABLE bindableState);
/// If the network is currently connecting or disconnecting. Shorthand for checking @@state. /// If the network is currently connecting or disconnecting. Shorthand for checking @@state.
Q_PROPERTY(bool stateChanging READ default NOTIFY stateChangingChanged BINDABLE bindableStateChanging); Q_PROPERTY(bool stateChanging READ default NOTIFY stateChangingChanged BINDABLE bindableStateChanging);
// clang-format on // clang-format on
public: public:
explicit Network(QString name, QObject* parent = nullptr); explicit Network(QString name, QObject* parent = nullptr);
/// Attempt to connect to the network.
///
/// > [!NOTE] If the network is a @@WifiNetwork and requires secrets, a @@connectionFailed(s)
/// > signal will be emitted with `NoSecrets`.
/// > @@WifiNetwork.connectWithPsk() can be used to provide secrets.
Q_INVOKABLE void connect();
/// Attempt to connect to the network with a specific @@nmSettings entry.
///
/// > [!WARNING] Only valid for the NetworkManager backend.
Q_INVOKABLE void connectWithSettings(NMSettings* settings);
/// Disconnect from the network.
Q_INVOKABLE void disconnect();
/// Forget all connection settings for this network.
Q_INVOKABLE void forget();
void settingsAdded(NMSettings* settings); [[nodiscard]] QString name() const { return this->mName; };
void settingsRemoved(NMSettings* settings);
// clang-format off
[[nodiscard]] QString name() const { return this->mName; }
[[nodiscard]] const QList<NMSettings*>& nmSettings() const { return this->bNmSettings; }
QBindable<QList<NMSettings*>> bindableNmSettings() const { return &this->bNmSettings; }
QBindable<bool> bindableConnected() { return &this->bConnected; } QBindable<bool> bindableConnected() { return &this->bConnected; }
QBindable<bool> bindableKnown() { return &this->bKnown; } QBindable<NetworkState::Enum> bindableState() { return &this->bState; }
[[nodiscard]] ConnectionState::Enum state() const { return this->bState; }
QBindable<ConnectionState::Enum> bindableState() { return &this->bState; }
QBindable<bool> bindableStateChanging() { return &this->bStateChanging; } QBindable<bool> bindableStateChanging() { return &this->bStateChanging; }
// clang-format on
signals: signals:
/// Signals that a connection to the network has failed because of the given @@ConnectionFailReason.
void connectionFailed(ConnectionFailReason::Enum reason);
void connectedChanged(); void connectedChanged();
void knownChanged();
void stateChanged(); void stateChanged();
void stateChangingChanged(); void stateChangingChanged();
void nmSettingsChanged();
QSDOC_HIDE void requestConnect();
QSDOC_HIDE void requestConnectWithSettings(NMSettings* settings);
QSDOC_HIDE void requestDisconnect();
QSDOC_HIDE void requestForget();
protected: protected:
QString mName; QString mName;
// clang-format off
Q_OBJECT_BINDABLE_PROPERTY(Network, bool, bConnected, &Network::connectedChanged); Q_OBJECT_BINDABLE_PROPERTY(Network, bool, bConnected, &Network::connectedChanged);
Q_OBJECT_BINDABLE_PROPERTY(Network, bool, bKnown, &Network::knownChanged); Q_OBJECT_BINDABLE_PROPERTY(Network, NetworkState::Enum, bState, &Network::stateChanged);
Q_OBJECT_BINDABLE_PROPERTY(Network, ConnectionState::Enum, bState, &Network::stateChanged);
Q_OBJECT_BINDABLE_PROPERTY(Network, bool, bStateChanging, &Network::stateChangingChanged); Q_OBJECT_BINDABLE_PROPERTY(Network, bool, bStateChanging, &Network::stateChangingChanged);
Q_OBJECT_BINDABLE_PROPERTY(Network, QList<NMSettings*>, bNmSettings, &Network::nmSettingsChanged);
// clang-format on
}; };
} // namespace qs::network } // namespace qs::network
+1 -3
View File
@@ -63,12 +63,10 @@ qt_add_dbus_interface(NM_DBUS_INTERFACES
qt_add_library(quickshell-network-nm STATIC qt_add_library(quickshell-network-nm STATIC
backend.cpp backend.cpp
device.cpp device.cpp
active_connection.cpp connection.cpp
settings.cpp
accesspoint.cpp accesspoint.cpp
wireless.cpp wireless.cpp
utils.cpp utils.cpp
dbus_types.cpp
enums.hpp enums.hpp
${NM_DBUS_INTERFACES} ${NM_DBUS_INTERFACES}
) )
+8 -8
View File
@@ -48,14 +48,14 @@ public:
[[nodiscard]] bool isValid() const; [[nodiscard]] bool isValid() const;
[[nodiscard]] QString path() const; [[nodiscard]] QString path() const;
[[nodiscard]] QString address() const; [[nodiscard]] QString address() const;
[[nodiscard]] QByteArray ssid() const { return this->bSsid; } [[nodiscard]] QByteArray ssid() const { return this->bSsid; };
[[nodiscard]] quint8 signalStrength() const { return this->bSignalStrength; } [[nodiscard]] quint8 signalStrength() const { return this->bSignalStrength; };
[[nodiscard]] NM80211ApFlags::Enum flags() const { return this->bFlags; } [[nodiscard]] NM80211ApFlags::Enum flags() const { return this->bFlags; };
[[nodiscard]] NM80211ApSecurityFlags::Enum wpaFlags() const { return this->bWpaFlags; } [[nodiscard]] NM80211ApSecurityFlags::Enum wpaFlags() const { return this->bWpaFlags; };
[[nodiscard]] NM80211ApSecurityFlags::Enum rsnFlags() const { return this->bRsnFlags; } [[nodiscard]] NM80211ApSecurityFlags::Enum rsnFlags() const { return this->bRsnFlags; };
[[nodiscard]] NM80211Mode::Enum mode() const { return this->bMode; } [[nodiscard]] NM80211Mode::Enum mode() const { return this->bMode; };
[[nodiscard]] QBindable<WifiSecurityType::Enum> bindableSecurity() { return &this->bSecurity; } [[nodiscard]] QBindable<WifiSecurityType::Enum> bindableSecurity() { return &this->bSecurity; };
[[nodiscard]] WifiSecurityType::Enum security() const { return this->bSecurity; } [[nodiscard]] WifiSecurityType::Enum security() const { return this->bSecurity; };
signals: signals:
void loaded(); void loaded();
-68
View File
@@ -1,68 +0,0 @@
#include "active_connection.hpp"
#include <qcontainerfwd.h>
#include <qdbusconnection.h>
#include <qdbuspendingcall.h>
#include <qlogging.h>
#include <qloggingcategory.h>
#include <qnamespace.h>
#include <qobject.h>
#include <qstring.h>
#include <qtypes.h>
#include "../../core/logcat.hpp"
#include "../../dbus/properties.hpp"
#include "dbus_nm_active_connection.h"
#include "enums.hpp"
namespace qs::network {
using namespace qs::dbus;
namespace {
QS_LOGGING_CATEGORY(logNetworkManager, "quickshell.network.networkmanager", QtWarningMsg);
}
NMActiveConnection::NMActiveConnection(const QString& path, QObject* parent): QObject(parent) {
this->proxy = new DBusNMActiveConnectionProxy(
"org.freedesktop.NetworkManager",
path,
QDBusConnection::systemBus(),
this
);
if (!this->proxy->isValid()) {
qCWarning(logNetworkManager) << "Cannot create DBus interface for connection at" << path;
return;
}
// clang-format off
QObject::connect(&this->activeConnectionProperties, &DBusPropertyGroup::getAllFinished, this, &NMActiveConnection::loaded, Qt::SingleShotConnection);
QObject::connect(this->proxy, &DBusNMActiveConnectionProxy::StateChanged, this, &NMActiveConnection::onStateChanged);
// clang-format on
this->activeConnectionProperties.setInterface(this->proxy);
this->activeConnectionProperties.updateAllViaGetAll();
}
void NMActiveConnection::onStateChanged(quint32 /*state*/, quint32 reason) {
auto enumReason = static_cast<NMConnectionStateReason::Enum>(reason);
if (this->bStateReason == enumReason) return;
this->bStateReason = enumReason;
}
bool NMActiveConnection::isValid() const { return this->proxy && this->proxy->isValid(); }
QString NMActiveConnection::address() const {
return this->proxy ? this->proxy->service() : QString();
}
QString NMActiveConnection::path() const { return this->proxy ? this->proxy->path() : QString(); }
} // namespace qs::network
namespace qs::dbus {
DBusResult<qs::network::NMConnectionState::Enum>
DBusDataTransform<qs::network::NMConnectionState::Enum>::fromWire(quint32 wire) {
return DBusResult(static_cast<qs::network::NMConnectionState::Enum>(wire));
}
} // namespace qs::dbus
+14 -49
View File
@@ -1,6 +1,5 @@
#include "backend.hpp" #include "backend.hpp"
#include <qbytearray.h>
#include <qdbusconnection.h> #include <qdbusconnection.h>
#include <qdbusextratypes.h> #include <qdbusextratypes.h>
#include <qdbusmetatype.h> #include <qdbusmetatype.h>
@@ -16,7 +15,6 @@
#include "../../core/logcat.hpp" #include "../../core/logcat.hpp"
#include "../../dbus/properties.hpp" #include "../../dbus/properties.hpp"
#include "../device.hpp" #include "../device.hpp"
#include "../enums.hpp"
#include "../network.hpp" #include "../network.hpp"
#include "../wifi.hpp" #include "../wifi.hpp"
#include "dbus_nm_backend.h" #include "dbus_nm_backend.h"
@@ -33,8 +31,7 @@ QS_LOGGING_CATEGORY(logNetworkManager, "quickshell.network.networkmanager", QtWa
} }
NetworkManager::NetworkManager(QObject* parent): NetworkBackend(parent) { NetworkManager::NetworkManager(QObject* parent): NetworkBackend(parent) {
qCDebug(logNetworkManager) << "Connecting to NetworkManager"; qDBusRegisterMetaType<ConnectionSettingsMap>();
qDBusRegisterMetaType<NMSettingsMap>();
auto bus = QDBusConnection::systemBus(); auto bus = QDBusConnection::systemBus();
if (!bus.isConnected()) { if (!bus.isConnected()) {
@@ -72,23 +69,6 @@ void NetworkManager::init() {
this->registerDevices(); this->registerDevices();
} }
void NetworkManager::checkConnectivity() {
auto pending = this->proxy->CheckConnectivity();
auto* call = new QDBusPendingCallWatcher(pending, this);
auto responseCallback = [](QDBusPendingCallWatcher* call) {
const QDBusPendingReply<quint32> reply = *call;
if (reply.isError()) {
qCInfo(logNetworkManager) << "Failed to check connectivity: " << reply.error().message();
}
delete call;
};
QObject::connect(call, &QDBusPendingCallWatcher::finished, this, responseCallback);
}
void NetworkManager::registerDevices() { void NetworkManager::registerDevices() {
auto pending = this->proxy->GetAllDevices(); auto pending = this->proxy->GetAllDevices();
auto* call = new QDBusPendingCallWatcher(pending, this); auto* call = new QDBusPendingCallWatcher(pending, this);
@@ -137,21 +117,23 @@ void NetworkManager::registerDevice(const QString& path) {
} }
if (dev) { if (dev) {
qCDebug(logNetworkManager) << "Device added:" << path;
if (!dev->isValid()) { if (!dev->isValid()) {
qCWarning(logNetworkManager) << "Ignoring invalid registration of" << path; qCWarning(logNetworkManager) << "Ignoring invalid registration of" << path;
delete dev; delete dev;
} else { } else {
this->mDevices[path] = dev; this->mDevices[path] = dev;
// Only register a frontend device while it's managed by NM.
auto onManagedChanged = [this, dev, type](bool managed) {
managed ? this->registerFrontendDevice(type, dev) : this->removeFrontendDevice(dev);
};
// clang-format off // clang-format off
QObject::connect(dev, &NMDevice::addAndActivateConnection, this, &NetworkManager::addAndActivateConnection); QObject::connect(dev, &NMDevice::addAndActivateConnection, this, &NetworkManager::addAndActivateConnection);
QObject::connect(dev, &NMDevice::activateConnection, this, &NetworkManager::activateConnection); QObject::connect(dev, &NMDevice::activateConnection, this, &NetworkManager::activateConnection);
QObject::connect(dev, &NMDevice::managedChanged, this, onManagedChanged);
// clang-format on // clang-format on
this->registerFrontendDevice(type, dev); if (dev->managed()) this->registerFrontendDevice(type, dev);
} }
} else {
qCDebug(logNetworkManager) << "Ignoring registration of unsupported device:" << path;
} }
temp->deleteLater(); temp->deleteLater();
} }
@@ -191,22 +173,21 @@ void NetworkManager::registerFrontendDevice(NMDeviceType::Enum type, NMDevice* d
// Bind generic NetworkDevice properties // Bind generic NetworkDevice properties
auto translateState = [dev]() { auto translateState = [dev]() {
switch (dev->state()) { switch (dev->state()) {
case 0 ... 20: return ConnectionState::Unknown; case 0 ... 20: return DeviceConnectionState::Unknown;
case 30: return ConnectionState::Disconnected; case 30: return DeviceConnectionState::Disconnected;
case 40 ... 90: return ConnectionState::Connecting; case 40 ... 90: return DeviceConnectionState::Connecting;
case 100: return ConnectionState::Connected; case 100: return DeviceConnectionState::Connected;
case 110 ... 120: return ConnectionState::Disconnecting; case 110 ... 120: return DeviceConnectionState::Disconnecting;
} }
}; };
// clang-format off // clang-format off
frontendDev->bindableName().setBinding([dev]() { return dev->interface(); }); frontendDev->bindableName().setBinding([dev]() { return dev->interface(); });
frontendDev->bindableAddress().setBinding([dev]() { return dev->hwAddress(); }); frontendDev->bindableAddress().setBinding([dev]() { return dev->hwAddress(); });
frontendDev->bindableNmState().setBinding([dev]() { return dev->state(); });
frontendDev->bindableState().setBinding(translateState); frontendDev->bindableState().setBinding(translateState);
frontendDev->bindableAutoconnect().setBinding([dev]() { return dev->autoconnect(); }); frontendDev->bindableAutoconnect().setBinding([dev]() { return dev->autoconnect(); });
frontendDev->bindableNmManaged().setBinding([dev]() { return dev->managed(); });
QObject::connect(frontendDev, &WifiDevice::requestDisconnect, dev, &NMDevice::disconnect); QObject::connect(frontendDev, &WifiDevice::requestDisconnect, dev, &NMDevice::disconnect);
QObject::connect(frontendDev, &NetworkDevice::requestSetAutoconnect, dev, &NMDevice::setAutoconnect); QObject::connect(frontendDev, &NetworkDevice::requestSetAutoconnect, dev, &NMDevice::setAutoconnect);
QObject::connect(frontendDev, &NetworkDevice::requestSetNmManaged, dev, &NMDevice::setManaged);
// clang-format on // clang-format on
this->mFrontendDevices.insert(dev->path(), frontendDev); this->mFrontendDevices.insert(dev->path(), frontendDev);
@@ -234,7 +215,6 @@ void NetworkManager::onDevicePathRemoved(const QDBusObjectPath& path) {
auto* dev = iter.value(); auto* dev = iter.value();
this->mDevices.erase(iter); this->mDevices.erase(iter);
if (dev) { if (dev) {
qCDebug(logNetworkManager) << "Device removed:" << path.path();
this->removeFrontendDevice(dev); this->removeFrontendDevice(dev);
delete dev; delete dev;
} }
@@ -260,7 +240,7 @@ void NetworkManager::activateConnection(
} }
void NetworkManager::addAndActivateConnection( void NetworkManager::addAndActivateConnection(
const NMSettingsMap& settings, const ConnectionSettingsMap& settings,
const QDBusObjectPath& devPath, const QDBusObjectPath& devPath,
const QDBusObjectPath& specificObjectPath const QDBusObjectPath& specificObjectPath
) { ) {
@@ -279,12 +259,6 @@ void NetworkManager::addAndActivateConnection(
QObject::connect(call, &QDBusPendingCallWatcher::finished, this, responseCallback); QObject::connect(call, &QDBusPendingCallWatcher::finished, this, responseCallback);
} }
void NetworkManager::setConnectivityCheckEnabled(bool enabled) {
if (enabled == this->bConnectivityCheckEnabled) return;
this->bConnectivityCheckEnabled = enabled;
this->pConnectivityCheckEnabled.write();
}
void NetworkManager::setWifiEnabled(bool enabled) { void NetworkManager::setWifiEnabled(bool enabled) {
if (enabled == this->bWifiEnabled) return; if (enabled == this->bWifiEnabled) return;
this->bWifiEnabled = enabled; this->bWifiEnabled = enabled;
@@ -294,12 +268,3 @@ void NetworkManager::setWifiEnabled(bool enabled) {
bool NetworkManager::isAvailable() const { return this->proxy && this->proxy->isValid(); }; bool NetworkManager::isAvailable() const { return this->proxy && this->proxy->isValid(); };
} // namespace qs::network } // namespace qs::network
namespace qs::dbus {
DBusResult<qs::network::NMConnectivityState::Enum>
DBusDataTransform<qs::network::NMConnectivityState::Enum>::fromWire(quint32 wire) {
return DBusResult(static_cast<qs::network::NMConnectivityState::Enum>(wire));
}
} // namespace qs::dbus
+3 -32
View File
@@ -10,20 +10,7 @@
#include "../../dbus/properties.hpp" #include "../../dbus/properties.hpp"
#include "../network.hpp" #include "../network.hpp"
#include "dbus_nm_backend.h" #include "dbus_nm_backend.h"
#include "dbus_types.hpp"
#include "device.hpp" #include "device.hpp"
#include "enums.hpp"
namespace qs::dbus {
template <>
struct DBusDataTransform<qs::network::NMConnectivityState::Enum> {
using Wire = quint32;
using Data = qs::network::NMConnectivityState::Enum;
static DBusResult<Data> fromWire(Wire wire);
};
} // namespace qs::dbus
namespace qs::network { namespace qs::network {
@@ -34,34 +21,24 @@ public:
explicit NetworkManager(QObject* parent = nullptr); explicit NetworkManager(QObject* parent = nullptr);
[[nodiscard]] bool isAvailable() const override; [[nodiscard]] bool isAvailable() const override;
[[nodiscard]] bool wifiEnabled() const { return this->bWifiEnabled; } [[nodiscard]] bool wifiEnabled() const { return this->bWifiEnabled; };
[[nodiscard]] bool wifiHardwareEnabled() const { return this->bWifiHardwareEnabled; } [[nodiscard]] bool wifiHardwareEnabled() const { return this->bWifiHardwareEnabled; };
[[nodiscard]] bool connectivityCheckAvailable() const {
return this->bConnectivityCheckAvailable;
};
[[nodiscard]] bool connectivityCheckEnabled() const { return this->bConnectivityCheckEnabled; }
[[nodiscard]] NMConnectivityState::Enum connectivity() const { return this->bConnectivity; }
signals: signals:
void deviceAdded(NetworkDevice* device); void deviceAdded(NetworkDevice* device);
void deviceRemoved(NetworkDevice* device); void deviceRemoved(NetworkDevice* device);
void wifiEnabledChanged(bool enabled); void wifiEnabledChanged(bool enabled);
void wifiHardwareEnabledChanged(bool enabled); void wifiHardwareEnabledChanged(bool enabled);
void connectivityStateChanged(NMConnectivityState::Enum state);
void connectivityCheckAvailableChanged(bool available);
void connectivityCheckEnabledChanged(bool enabled);
public slots: public slots:
void setWifiEnabled(bool enabled); void setWifiEnabled(bool enabled);
void setConnectivityCheckEnabled(bool enabled);
void checkConnectivity();
private slots: private slots:
void onDevicePathAdded(const QDBusObjectPath& path); void onDevicePathAdded(const QDBusObjectPath& path);
void onDevicePathRemoved(const QDBusObjectPath& path); void onDevicePathRemoved(const QDBusObjectPath& path);
void activateConnection(const QDBusObjectPath& connPath, const QDBusObjectPath& devPath); void activateConnection(const QDBusObjectPath& connPath, const QDBusObjectPath& devPath);
void addAndActivateConnection( void addAndActivateConnection(
const NMSettingsMap& settings, const ConnectionSettingsMap& settings,
const QDBusObjectPath& devPath, const QDBusObjectPath& devPath,
const QDBusObjectPath& specificObjectPath const QDBusObjectPath& specificObjectPath
); );
@@ -79,16 +56,10 @@ private:
// clang-format off // clang-format off
Q_OBJECT_BINDABLE_PROPERTY(NetworkManager, bool, bWifiEnabled, &NetworkManager::wifiEnabledChanged); Q_OBJECT_BINDABLE_PROPERTY(NetworkManager, bool, bWifiEnabled, &NetworkManager::wifiEnabledChanged);
Q_OBJECT_BINDABLE_PROPERTY(NetworkManager, bool, bWifiHardwareEnabled, &NetworkManager::wifiHardwareEnabledChanged); Q_OBJECT_BINDABLE_PROPERTY(NetworkManager, bool, bWifiHardwareEnabled, &NetworkManager::wifiHardwareEnabledChanged);
Q_OBJECT_BINDABLE_PROPERTY(NetworkManager, NMConnectivityState::Enum, bConnectivity, &NetworkManager::connectivityStateChanged);
Q_OBJECT_BINDABLE_PROPERTY(NetworkManager, bool, bConnectivityCheckAvailable, &NetworkManager::connectivityCheckAvailableChanged);
Q_OBJECT_BINDABLE_PROPERTY(NetworkManager, bool, bConnectivityCheckEnabled, &NetworkManager::connectivityCheckEnabledChanged);
QS_DBUS_BINDABLE_PROPERTY_GROUP(NetworkManager, dbusProperties); QS_DBUS_BINDABLE_PROPERTY_GROUP(NetworkManager, dbusProperties);
QS_DBUS_PROPERTY_BINDING(NetworkManager, pWifiEnabled, bWifiEnabled, dbusProperties, "WirelessEnabled"); QS_DBUS_PROPERTY_BINDING(NetworkManager, pWifiEnabled, bWifiEnabled, dbusProperties, "WirelessEnabled");
QS_DBUS_PROPERTY_BINDING(NetworkManager, pWifiHardwareEnabled, bWifiHardwareEnabled, dbusProperties, "WirelessHardwareEnabled"); QS_DBUS_PROPERTY_BINDING(NetworkManager, pWifiHardwareEnabled, bWifiHardwareEnabled, dbusProperties, "WirelessHardwareEnabled");
QS_DBUS_PROPERTY_BINDING(NetworkManager, pConnectivity, bConnectivity, dbusProperties, "Connectivity");
QS_DBUS_PROPERTY_BINDING(NetworkManager, pConnectivityCheckAvailable, bConnectivityCheckAvailable, dbusProperties, "ConnectivityCheckAvailable");
QS_DBUS_PROPERTY_BINDING(NetworkManager, pConnectivityCheckEnabled, bConnectivityCheckEnabled, dbusProperties, "ConnectivityCheckEnabled");
// clang-format on // clang-format on
DBusNetworkManagerProxy* proxy = nullptr; DBusNetworkManagerProxy* proxy = nullptr;
}; };
+151
View File
@@ -0,0 +1,151 @@
#include "connection.hpp"
#include <qdbusconnection.h>
#include <qdbusmetatype.h>
#include <qdbuspendingcall.h>
#include <qdbuspendingreply.h>
#include <qlogging.h>
#include <qloggingcategory.h>
#include <qnamespace.h>
#include <qobject.h>
#include <qstring.h>
#include <qtmetamacros.h>
#include <qtypes.h>
#include "../../core/logcat.hpp"
#include "../../dbus/properties.hpp"
#include "../wifi.hpp"
#include "dbus_nm_active_connection.h"
#include "dbus_nm_connection_settings.h"
#include "dbus_types.hpp"
#include "enums.hpp"
#include "utils.hpp"
namespace qs::network {
using namespace qs::dbus;
namespace {
QS_LOGGING_CATEGORY(logNetworkManager, "quickshell.network.networkmanager", QtWarningMsg);
}
NMConnectionSettings::NMConnectionSettings(const QString& path, QObject* parent): QObject(parent) {
qDBusRegisterMetaType<ConnectionSettingsMap>();
this->proxy = new DBusNMConnectionSettingsProxy(
"org.freedesktop.NetworkManager",
path,
QDBusConnection::systemBus(),
this
);
if (!this->proxy->isValid()) {
qCWarning(logNetworkManager) << "Cannot create DBus interface for connection at" << path;
return;
}
QObject::connect(
this->proxy,
&DBusNMConnectionSettingsProxy::Updated,
this,
&NMConnectionSettings::updateSettings
);
this->bSecurity.setBinding([this]() { return securityFromConnectionSettings(this->bSettings); });
this->connectionSettingsProperties.setInterface(this->proxy);
this->connectionSettingsProperties.updateAllViaGetAll();
this->updateSettings();
}
void NMConnectionSettings::updateSettings() {
auto pending = this->proxy->GetSettings();
auto* call = new QDBusPendingCallWatcher(pending, this);
auto responseCallback = [this](QDBusPendingCallWatcher* call) {
const QDBusPendingReply<ConnectionSettingsMap> reply = *call;
if (reply.isError()) {
qCWarning(logNetworkManager)
<< "Failed to get" << this->path() << "settings:" << reply.error().message();
} else {
this->bSettings = reply.value();
}
if (!this->mLoaded) {
emit this->loaded();
this->mLoaded = true;
}
delete call;
};
QObject::connect(call, &QDBusPendingCallWatcher::finished, this, responseCallback);
}
void NMConnectionSettings::forget() {
auto pending = this->proxy->Delete();
auto* call = new QDBusPendingCallWatcher(pending, this);
auto responseCallback = [this](QDBusPendingCallWatcher* call) {
const QDBusPendingReply<> reply = *call;
if (reply.isError()) {
qCWarning(logNetworkManager)
<< "Failed to forget" << this->path() << ":" << reply.error().message();
}
delete call;
};
QObject::connect(call, &QDBusPendingCallWatcher::finished, this, responseCallback);
}
bool NMConnectionSettings::isValid() const { return this->proxy && this->proxy->isValid(); }
QString NMConnectionSettings::address() const {
return this->proxy ? this->proxy->service() : QString();
}
QString NMConnectionSettings::path() const { return this->proxy ? this->proxy->path() : QString(); }
NMActiveConnection::NMActiveConnection(const QString& path, QObject* parent): QObject(parent) {
this->proxy = new DBusNMActiveConnectionProxy(
"org.freedesktop.NetworkManager",
path,
QDBusConnection::systemBus(),
this
);
if (!this->proxy->isValid()) {
qCWarning(logNetworkManager) << "Cannot create DBus interface for connection at" << path;
return;
}
// clang-format off
QObject::connect(&this->activeConnectionProperties, &DBusPropertyGroup::getAllFinished, this, &NMActiveConnection::loaded, Qt::SingleShotConnection);
QObject::connect(this->proxy, &DBusNMActiveConnectionProxy::StateChanged, this, &NMActiveConnection::onStateChanged);
// clang-format on
this->activeConnectionProperties.setInterface(this->proxy);
this->activeConnectionProperties.updateAllViaGetAll();
}
void NMActiveConnection::onStateChanged(quint32 /*state*/, quint32 reason) {
auto enumReason = static_cast<NMConnectionStateReason::Enum>(reason);
if (this->mStateReason == enumReason) return;
this->mStateReason = enumReason;
emit this->stateReasonChanged(enumReason);
}
bool NMActiveConnection::isValid() const { return this->proxy && this->proxy->isValid(); }
QString NMActiveConnection::address() const {
return this->proxy ? this->proxy->service() : QString();
}
QString NMActiveConnection::path() const { return this->proxy ? this->proxy->path() : QString(); }
} // namespace qs::network
namespace qs::dbus {
DBusResult<qs::network::NMConnectionState::Enum>
DBusDataTransform<qs::network::NMConnectionState::Enum>::fromWire(quint32 wire) {
return DBusResult(static_cast<qs::network::NMConnectionState::Enum>(wire));
}
} // namespace qs::dbus
@@ -1,16 +1,18 @@
#pragma once #pragma once
#include <qbytearray.h>
#include <qdbusextratypes.h> #include <qdbusextratypes.h>
#include <qdbuspendingcall.h> #include <qdbuspendingcall.h>
#include <qdbuspendingreply.h>
#include <qobject.h> #include <qobject.h>
#include <qproperty.h> #include <qproperty.h>
#include <qqmlintegration.h> #include <qtmetamacros.h>
#include <qstringlist.h>
#include <qtypes.h> #include <qtypes.h>
#include "../../dbus/properties.hpp" #include "../../dbus/properties.hpp"
#include "../wifi.hpp"
#include "dbus_nm_active_connection.h" #include "dbus_nm_active_connection.h"
#include "dbus_nm_connection_settings.h"
#include "dbus_types.hpp"
#include "enums.hpp" #include "enums.hpp"
namespace qs::dbus { namespace qs::dbus {
@@ -26,6 +28,40 @@ struct DBusDataTransform<qs::network::NMConnectionState::Enum> {
namespace qs::network { namespace qs::network {
// Proxy of a /org/freedesktop/NetworkManager/Settings/Connection/* object.
class NMConnectionSettings: public QObject {
Q_OBJECT;
public:
explicit NMConnectionSettings(const QString& path, QObject* parent = nullptr);
void forget();
[[nodiscard]] bool isValid() const;
[[nodiscard]] QString path() const;
[[nodiscard]] QString address() const;
[[nodiscard]] ConnectionSettingsMap settings() const { return this->bSettings; };
[[nodiscard]] WifiSecurityType::Enum security() const { return this->bSecurity; };
[[nodiscard]] QBindable<WifiSecurityType::Enum> bindableSecurity() { return &this->bSecurity; };
signals:
void loaded();
void settingsChanged(ConnectionSettingsMap settings);
void securityChanged(WifiSecurityType::Enum security);
void ssidChanged(QString ssid);
private:
bool mLoaded = false;
void updateSettings();
// clang-format off
Q_OBJECT_BINDABLE_PROPERTY(NMConnectionSettings, ConnectionSettingsMap, bSettings, &NMConnectionSettings::settingsChanged);
Q_OBJECT_BINDABLE_PROPERTY(NMConnectionSettings, WifiSecurityType::Enum, bSecurity, &NMConnectionSettings::securityChanged);
QS_DBUS_BINDABLE_PROPERTY_GROUP(NMConnectionSettings, connectionSettingsProperties);
// clang-format on
DBusNMConnectionSettingsProxy* proxy = nullptr;
};
// Proxy of a /org/freedesktop/NetworkManager/ActiveConnection/* object. // Proxy of a /org/freedesktop/NetworkManager/ActiveConnection/* object.
class NMActiveConnection: public QObject { class NMActiveConnection: public QObject {
Q_OBJECT; Q_OBJECT;
@@ -36,27 +72,31 @@ public:
[[nodiscard]] bool isValid() const; [[nodiscard]] bool isValid() const;
[[nodiscard]] QString path() const; [[nodiscard]] QString path() const;
[[nodiscard]] QString address() const; [[nodiscard]] QString address() const;
[[nodiscard]] QDBusObjectPath connection() const { return this->bConnection; } [[nodiscard]] QDBusObjectPath connection() const { return this->bConnection; };
[[nodiscard]] NMConnectionState::Enum state() const { return this->bState; } [[nodiscard]] NMConnectionState::Enum state() const { return this->bState; };
[[nodiscard]] NMConnectionStateReason::Enum stateReason() const { return this->bStateReason; } [[nodiscard]] NMConnectionStateReason::Enum stateReason() const { return this->mStateReason; };
signals: signals:
void loaded(); void loaded();
void connectionChanged(QDBusObjectPath path); void connectionChanged(QDBusObjectPath path);
void stateChanged(NMConnectionState::Enum state); void stateChanged(NMConnectionState::Enum state);
void stateReasonChanged(NMConnectionStateReason::Enum reason); void stateReasonChanged(NMConnectionStateReason::Enum reason);
void uuidChanged(const QString& uuid);
private slots: private slots:
void onStateChanged(quint32 state, quint32 reason); void onStateChanged(quint32 state, quint32 reason);
private: private:
NMConnectionStateReason::Enum mStateReason = NMConnectionStateReason::Unknown;
// clang-format off // clang-format off
Q_OBJECT_BINDABLE_PROPERTY(NMActiveConnection, QDBusObjectPath, bConnection, &NMActiveConnection::connectionChanged); Q_OBJECT_BINDABLE_PROPERTY(NMActiveConnection, QDBusObjectPath, bConnection, &NMActiveConnection::connectionChanged);
Q_OBJECT_BINDABLE_PROPERTY(NMActiveConnection, QString, bUuid, &NMActiveConnection::uuidChanged);
Q_OBJECT_BINDABLE_PROPERTY(NMActiveConnection, NMConnectionState::Enum, bState, &NMActiveConnection::stateChanged); Q_OBJECT_BINDABLE_PROPERTY(NMActiveConnection, NMConnectionState::Enum, bState, &NMActiveConnection::stateChanged);
Q_OBJECT_BINDABLE_PROPERTY(NMActiveConnection, NMConnectionStateReason::Enum, bStateReason, &NMActiveConnection::stateReasonChanged);
QS_DBUS_BINDABLE_PROPERTY_GROUP(NMActiveConnection, activeConnectionProperties); QS_DBUS_BINDABLE_PROPERTY_GROUP(NMActiveConnection, activeConnectionProperties);
QS_DBUS_PROPERTY_BINDING(NMActiveConnection, pConnection, bConnection, activeConnectionProperties, "Connection"); QS_DBUS_PROPERTY_BINDING(NMActiveConnection, pConnection, bConnection, activeConnectionProperties, "Connection");
QS_DBUS_PROPERTY_BINDING(NMActiveConnection, pUuid, bUuid, activeConnectionProperties, "Uuid");
QS_DBUS_PROPERTY_BINDING(NMActiveConnection, pState, bState, activeConnectionProperties, "State"); QS_DBUS_PROPERTY_BINDING(NMActiveConnection, pState, bState, activeConnectionProperties, "State");
// clang-format on // clang-format on
DBusNMActiveConnectionProxy* proxy = nullptr; DBusNMActiveConnectionProxy* proxy = nullptr;
-69
View File
@@ -1,69 +0,0 @@
#include "dbus_types.hpp"
#include <qbytearray.h>
#include <qcontainerfwd.h>
#include <qdbusargument.h>
#include <qmap.h>
#include <qmetatype.h>
#include <qstring.h>
namespace qs::network {
const QDBusArgument& operator>>(const QDBusArgument& argument, NMSettingsMap& map) {
argument.beginMap();
while (!argument.atEnd()) {
argument.beginMapEntry();
QString groupName;
argument >> groupName;
QVariantMap group;
argument >> group;
map.insert(groupName, group);
argument.endMapEntry();
}
argument.endMap();
return argument;
}
const QDBusArgument& operator<<(QDBusArgument& argument, const NMSettingsMap& map) {
argument.beginMap(qMetaTypeId<QString>(), qMetaTypeId<QVariantMap>());
for (auto it = map.constBegin(); it != map.constEnd(); ++it) {
argument.beginMapEntry();
argument << it.key();
argument << it.value();
argument.endMapEntry();
}
argument.endMap();
return argument;
}
const QDBusArgument& operator>>(const QDBusArgument& argument, NMIPv6Address& addr) {
argument.beginStructure();
argument >> addr.address >> addr.prefix >> addr.gateway;
argument.endStructure();
return argument;
}
const QDBusArgument& operator<<(QDBusArgument& argument, const NMIPv6Address& addr) {
argument.beginStructure();
argument << addr.address << addr.prefix << addr.gateway;
argument.endStructure();
return argument;
}
const QDBusArgument& operator>>(const QDBusArgument& argument, NMIPv6Route& route) {
argument.beginStructure();
argument >> route.destination >> route.prefix >> route.nexthop >> route.metric;
argument.endStructure();
return argument;
}
const QDBusArgument& operator<<(QDBusArgument& argument, const NMIPv6Route& route) {
argument.beginStructure();
argument << route.destination << route.prefix << route.nexthop << route.metric;
argument.endStructure();
return argument;
}
} // namespace qs::network
+3 -34
View File
@@ -1,40 +1,9 @@
#pragma once #pragma once
#include <qbytearray.h> #include <qdbusextratypes.h>
#include <qdbusargument.h>
#include <qmap.h> #include <qmap.h>
#include <qstring.h> #include <qstring.h>
#include <qtypes.h>
#include <qvariant.h> #include <qvariant.h>
namespace qs::network { using ConnectionSettingsMap = QMap<QString, QVariantMap>;
Q_DECLARE_METATYPE(ConnectionSettingsMap);
using NMSettingsMap = QMap<QString, QVariantMap>;
const QDBusArgument& operator>>(const QDBusArgument& argument, NMSettingsMap& map);
const QDBusArgument& operator<<(QDBusArgument& argument, const NMSettingsMap& map);
struct NMIPv6Address {
QByteArray address;
quint32 prefix = 0;
QByteArray gateway;
};
const QDBusArgument& operator>>(const QDBusArgument& argument, qs::network::NMIPv6Address& addr);
const QDBusArgument& operator<<(QDBusArgument& argument, const qs::network::NMIPv6Address& addr);
struct NMIPv6Route {
QByteArray destination;
quint32 prefix = 0;
QByteArray nexthop;
quint32 metric = 0;
};
const QDBusArgument& operator>>(const QDBusArgument& argument, qs::network::NMIPv6Route& route);
const QDBusArgument& operator<<(QDBusArgument& argument, const qs::network::NMIPv6Route& route);
} // namespace qs::network
Q_DECLARE_METATYPE(qs::network::NMSettingsMap);
Q_DECLARE_METATYPE(qs::network::NMIPv6Address);
Q_DECLARE_METATYPE(qs::network::NMIPv6Route);
+19 -39
View File
@@ -14,10 +14,9 @@
#include "../../core/logcat.hpp" #include "../../core/logcat.hpp"
#include "../../dbus/properties.hpp" #include "../../dbus/properties.hpp"
#include "active_connection.hpp" #include "../device.hpp"
#include "connection.hpp"
#include "dbus_nm_device.h" #include "dbus_nm_device.h"
#include "enums.hpp"
#include "settings.hpp"
namespace qs::network { namespace qs::network {
using namespace qs::dbus; using namespace qs::dbus;
@@ -40,29 +39,19 @@ NMDevice::NMDevice(const QString& path, QObject* parent): QObject(parent) {
} }
// clang-format off // clang-format off
QObject::connect(this, &NMDevice::availableSettingsPathsChanged, this, &NMDevice::onAvailableSettingsPathsChanged); QObject::connect(this, &NMDevice::availableConnectionPathsChanged, this, &NMDevice::onAvailableConnectionPathsChanged);
QObject::connect(this, &NMDevice::activeConnectionPathChanged, this, &NMDevice::onActiveConnectionPathChanged); QObject::connect(this, &NMDevice::activeConnectionPathChanged, this, &NMDevice::onActiveConnectionPathChanged);
QObject::connect(this->deviceProxy, &DBusNMDeviceProxy::StateChanged, this, &NMDevice::onStateChanged);
// clang-format on // clang-format on
this->deviceProperties.setInterface(this->deviceProxy); this->deviceProperties.setInterface(this->deviceProxy);
this->deviceProperties.updateAllViaGetAll(); this->deviceProperties.updateAllViaGetAll();
} }
void NMDevice::onStateChanged(quint32 newState, quint32 /*oldState*/, quint32 reason) {
auto enumReason = static_cast<NMDeviceStateReason::Enum>(reason);
auto enumNewState = static_cast<NMDeviceState::Enum>(newState);
if (enumNewState == NMDeviceState::Failed) this->bLastFailReason = enumReason;
if (this->bStateReason == enumReason) return;
this->bStateReason = enumReason;
}
void NMDevice::onActiveConnectionPathChanged(const QDBusObjectPath& path) { void NMDevice::onActiveConnectionPathChanged(const QDBusObjectPath& path) {
const QString stringPath = path.path(); const QString stringPath = path.path();
// Remove old active connection // Remove old active connection
if (this->mActiveConnection) { if (this->mActiveConnection) {
qCDebug(logNetworkManager) << "Active connection removed:" << this->mActiveConnection->path();
QObject::disconnect(this->mActiveConnection, nullptr, this, nullptr); QObject::disconnect(this->mActiveConnection, nullptr, this, nullptr);
delete this->mActiveConnection; delete this->mActiveConnection;
this->mActiveConnection = nullptr; this->mActiveConnection = nullptr;
@@ -75,7 +64,6 @@ void NMDevice::onActiveConnectionPathChanged(const QDBusObjectPath& path) {
qCWarning(logNetworkManager) << "Ignoring invalid registration of" << stringPath; qCWarning(logNetworkManager) << "Ignoring invalid registration of" << stringPath;
delete active; delete active;
} else { } else {
qCDebug(logNetworkManager) << "Active connection added:" << stringPath;
this->mActiveConnection = active; this->mActiveConnection = active;
QObject::connect( QObject::connect(
active, active,
@@ -88,44 +76,42 @@ void NMDevice::onActiveConnectionPathChanged(const QDBusObjectPath& path) {
} }
} }
void NMDevice::onAvailableSettingsPathsChanged(const QList<QDBusObjectPath>& paths) { void NMDevice::onAvailableConnectionPathsChanged(const QList<QDBusObjectPath>& paths) {
QSet<QString> newPathSet; QSet<QString> newPathSet;
for (const QDBusObjectPath& path: paths) { for (const QDBusObjectPath& path: paths) {
newPathSet.insert(path.path()); newPathSet.insert(path.path());
} }
const auto existingPaths = this->mSettings.keys(); const auto existingPaths = this->mConnections.keys();
const QSet<QString> existingPathSet(existingPaths.begin(), existingPaths.end()); const QSet<QString> existingPathSet(existingPaths.begin(), existingPaths.end());
const auto addedSettings = newPathSet - existingPathSet; const auto addedConnections = newPathSet - existingPathSet;
const auto removedSettings = existingPathSet - newPathSet; const auto removedConnections = existingPathSet - newPathSet;
for (const QString& path: addedSettings) { for (const QString& path: addedConnections) {
this->registerSettings(path); this->registerConnection(path);
} }
for (const QString& path: removedSettings) { for (const QString& path: removedConnections) {
auto* connection = this->mSettings.take(path); auto* connection = this->mConnections.take(path);
if (!connection) { if (!connection) {
qCDebug(logNetworkManager) << "Sent removal signal for" << path << "which is not registered."; qCDebug(logNetworkManager) << "Sent removal signal for" << path << "which is not registered.";
} else { } else {
qCDebug(logNetworkManager) << "Connection settings removed:" << path;
delete connection; delete connection;
} }
}; };
} }
void NMDevice::registerSettings(const QString& path) { void NMDevice::registerConnection(const QString& path) {
auto* settings = new NMSettings(path, this); auto* connection = new NMConnectionSettings(path, this);
if (!settings->isValid()) { if (!connection->isValid()) {
qCWarning(logNetworkManager) << "Ignoring invalid registration of" << path; qCWarning(logNetworkManager) << "Ignoring invalid registration of" << path;
delete settings; delete connection;
} else { } else {
qCDebug(logNetworkManager) << "Connection settings added:" << path; this->mConnections.insert(path, connection);
this->mSettings.insert(path, settings);
QObject::connect( QObject::connect(
settings, connection,
&NMSettings::loaded, &NMConnectionSettings::loaded,
this, this,
[this, settings]() { emit this->settingsLoaded(settings); }, [this, connection]() { emit this->connectionLoaded(connection); },
Qt::SingleShotConnection Qt::SingleShotConnection
); );
} }
@@ -139,12 +125,6 @@ void NMDevice::setAutoconnect(bool autoconnect) {
this->pAutoconnect.write(); this->pAutoconnect.write();
} }
void NMDevice::setManaged(bool managed) {
if (managed == this->bManaged) return;
this->bManaged = managed;
this->pManaged.write();
}
bool NMDevice::isValid() const { return this->deviceProxy && this->deviceProxy->isValid(); } bool NMDevice::isValid() const { return this->deviceProxy && this->deviceProxy->isValid(); }
QString NMDevice::address() const { QString NMDevice::address() const {
return this->deviceProxy ? this->deviceProxy->service() : QString(); return this->deviceProxy ? this->deviceProxy->service() : QString();
+15 -25
View File
@@ -8,10 +8,8 @@
#include <qtypes.h> #include <qtypes.h>
#include "../../dbus/properties.hpp" #include "../../dbus/properties.hpp"
#include "../enums.hpp" #include "connection.hpp"
#include "active_connection.hpp"
#include "dbus_nm_device.h" #include "dbus_nm_device.h"
#include "settings.hpp"
namespace qs::dbus { namespace qs::dbus {
@@ -38,49 +36,43 @@ public:
[[nodiscard]] virtual bool isValid() const; [[nodiscard]] virtual bool isValid() const;
[[nodiscard]] QString path() const; [[nodiscard]] QString path() const;
[[nodiscard]] QString address() const; [[nodiscard]] QString address() const;
[[nodiscard]] QString interface() const { return this->bInterface; } [[nodiscard]] QString interface() const { return this->bInterface; };
[[nodiscard]] QString hwAddress() const { return this->bHwAddress; } [[nodiscard]] QString hwAddress() const { return this->bHwAddress; };
[[nodiscard]] bool managed() const { return this->bManaged; } [[nodiscard]] bool managed() const { return this->bManaged; };
[[nodiscard]] NMDeviceState::Enum state() const { return this->bState; } [[nodiscard]] NMDeviceState::Enum state() const { return this->bState; };
[[nodiscard]] NMDeviceStateReason::Enum stateReason() const { return this->bStateReason; } [[nodiscard]] bool autoconnect() const { return this->bAutoconnect; };
[[nodiscard]] NMDeviceStateReason::Enum lastFailReason() const { return this->bLastFailReason; } [[nodiscard]] NMActiveConnection* activeConnection() const { return this->mActiveConnection; };
[[nodiscard]] bool autoconnect() const { return this->bAutoconnect; }
[[nodiscard]] NMActiveConnection* activeConnection() const { return this->mActiveConnection; }
signals: signals:
void activateConnection(const QDBusObjectPath& connPath, const QDBusObjectPath& devPath); void activateConnection(const QDBusObjectPath& connPath, const QDBusObjectPath& devPath);
void addAndActivateConnection( void addAndActivateConnection(
const NMSettingsMap& settings, const ConnectionSettingsMap& settings,
const QDBusObjectPath& devPath, const QDBusObjectPath& devPath,
const QDBusObjectPath& apPath const QDBusObjectPath& apPath
); );
void settingsLoaded(NMSettings* settings); void connectionLoaded(NMConnectionSettings* connection);
void settingsRemoved(NMSettings* settings); void connectionRemoved(NMConnectionSettings* connection);
void availableSettingsPathsChanged(QList<QDBusObjectPath> paths); void availableConnectionPathsChanged(QList<QDBusObjectPath> paths);
void activeConnectionPathChanged(const QDBusObjectPath& connection); void activeConnectionPathChanged(const QDBusObjectPath& connection);
void activeConnectionLoaded(NMActiveConnection* active); void activeConnectionLoaded(NMActiveConnection* active);
void interfaceChanged(const QString& interface); void interfaceChanged(const QString& interface);
void hwAddressChanged(const QString& hwAddress); void hwAddressChanged(const QString& hwAddress);
void managedChanged(bool managed); void managedChanged(bool managed);
void stateChanged(NMDeviceState::Enum state); void stateChanged(NMDeviceState::Enum state);
void stateReasonChanged(NMDeviceStateReason::Enum reason);
void lastFailReasonChanged(NMDeviceStateReason::Enum reason);
void autoconnectChanged(bool autoconnect); void autoconnectChanged(bool autoconnect);
public slots: public slots:
void disconnect(); void disconnect();
void setAutoconnect(bool autoconnect); void setAutoconnect(bool autoconnect);
void setManaged(bool managed);
private slots: private slots:
void onStateChanged(quint32 newState, quint32 oldState, quint32 reason); void onAvailableConnectionPathsChanged(const QList<QDBusObjectPath>& paths);
void onAvailableSettingsPathsChanged(const QList<QDBusObjectPath>& paths);
void onActiveConnectionPathChanged(const QDBusObjectPath& path); void onActiveConnectionPathChanged(const QDBusObjectPath& path);
private: private:
void registerSettings(const QString& path); void registerConnection(const QString& path);
QHash<QString, NMSettings*> mSettings; QHash<QString, NMConnectionSettings*> mConnections;
NMActiveConnection* mActiveConnection = nullptr; NMActiveConnection* mActiveConnection = nullptr;
// clang-format off // clang-format off
@@ -88,10 +80,8 @@ private:
Q_OBJECT_BINDABLE_PROPERTY(NMDevice, QString, bHwAddress, &NMDevice::hwAddressChanged); Q_OBJECT_BINDABLE_PROPERTY(NMDevice, QString, bHwAddress, &NMDevice::hwAddressChanged);
Q_OBJECT_BINDABLE_PROPERTY(NMDevice, bool, bManaged, &NMDevice::managedChanged); Q_OBJECT_BINDABLE_PROPERTY(NMDevice, bool, bManaged, &NMDevice::managedChanged);
Q_OBJECT_BINDABLE_PROPERTY(NMDevice, NMDeviceState::Enum, bState, &NMDevice::stateChanged); Q_OBJECT_BINDABLE_PROPERTY(NMDevice, NMDeviceState::Enum, bState, &NMDevice::stateChanged);
Q_OBJECT_BINDABLE_PROPERTY(NMDevice, NMDeviceStateReason::Enum, bStateReason, &NMDevice::stateReasonChanged);
Q_OBJECT_BINDABLE_PROPERTY(NMDevice, NMDeviceStateReason::Enum, bLastFailReason, &NMDevice::lastFailReasonChanged);
Q_OBJECT_BINDABLE_PROPERTY(NMDevice, bool, bAutoconnect, &NMDevice::autoconnectChanged); Q_OBJECT_BINDABLE_PROPERTY(NMDevice, bool, bAutoconnect, &NMDevice::autoconnectChanged);
Q_OBJECT_BINDABLE_PROPERTY(NMDevice, QList<QDBusObjectPath>, bAvailableConnections, &NMDevice::availableSettingsPathsChanged); Q_OBJECT_BINDABLE_PROPERTY(NMDevice, QList<QDBusObjectPath>, bAvailableConnections, &NMDevice::availableConnectionPathsChanged);
Q_OBJECT_BINDABLE_PROPERTY(NMDevice, QDBusObjectPath, bActiveConnection, &NMDevice::activeConnectionPathChanged); Q_OBJECT_BINDABLE_PROPERTY(NMDevice, QDBusObjectPath, bActiveConnection, &NMDevice::activeConnectionPathChanged);
QS_DBUS_BINDABLE_PROPERTY_GROUP(NMDeviceAdapter, deviceProperties); QS_DBUS_BINDABLE_PROPERTY_GROUP(NMDeviceAdapter, deviceProperties);
-158
View File
@@ -7,20 +7,6 @@
namespace qs::network { namespace qs::network {
// In sync with https://networkmanager.dev/docs/api/latest/nm-dbus-types.html#NMConnectivityState
class NMConnectivityState: public QObject {
Q_OBJECT;
public:
enum Enum : quint8 {
Unknown = 0,
None = 1,
Portal = 2,
Limited = 3,
Full = 4,
};
};
// Indicates the type of hardware represented by a device object. // Indicates the type of hardware represented by a device object.
class NMDeviceType: public QObject { class NMDeviceType: public QObject {
Q_OBJECT; Q_OBJECT;
@@ -66,123 +52,6 @@ public:
Q_ENUM(Enum); Q_ENUM(Enum);
}; };
// In sync with https://networkmanager.dev/docs/api/latest/nm-dbus-types.html#NMDeviceState.
class NMDeviceState: public QObject {
Q_OBJECT;
QML_ELEMENT;
QML_SINGLETON;
public:
enum Enum : quint8 {
Unknown = 0,
Unmanaged = 10,
Unavailable = 20,
Disconnected = 30,
Prepare = 40,
Config = 50,
NeedAuth = 60,
IPConfig = 70,
IPCheck = 80,
Secondaries = 90,
Activated = 100,
Deactivating = 110,
Failed = 120,
};
Q_ENUM(Enum);
};
// Device state change reason codes.
// In sync with https://networkmanager.dev/docs/api/latest/nm-dbus-types.html#NMDeviceStateReason.
class NMDeviceStateReason: public QObject {
Q_OBJECT;
QML_ELEMENT;
QML_SINGLETON;
public:
enum Enum : quint8 {
None = 0,
Unknown = 1,
NowManaged = 2,
NowUnmanaged = 3,
ConfigFailed = 4,
IpConfigUnavailable = 5,
IpConfigExpired = 6,
NoSecrets = 7,
SupplicantDisconnect = 8,
SupplicantConfigFailed = 9,
SupplicantFailed = 10,
SupplicantTimeout = 11,
PppStartFailed = 12,
PppDisconnect = 13,
PppFailed = 14,
DhcpStartFailed = 15,
DhcpError = 16,
DhcpFailed = 17,
SharedStartFailed = 18,
SharedFailed = 19,
AutoIpStartFailed = 20,
AutoIpError = 21,
AutoIpFailed = 22,
ModemBusy = 23,
ModemNoDialTone = 24,
ModemNoCarrier = 25,
ModemDialTimeout = 26,
ModemDialFailed = 27,
ModemInitFailed = 28,
GsmApnFailed = 29,
GsmRegistrationNotSearching = 30,
GsmRegistrationDenied = 31,
GsmRegistrationTimeout = 32,
GsmRegistrationFailed = 33,
GsmPinCheckFailed = 34,
FirmwareMissing = 35,
Removed = 36,
Sleeping = 37,
ConnectionRemoved = 38,
UserRequested = 39,
Carrier = 40,
ConnectionAssumed = 41,
SupplicantAvailable = 42,
ModemNotFound = 43,
BtFailed = 44,
GsmSimNotInserted = 45,
GsmSimPinRequired = 46,
GsmSimPukRequired = 47,
GsmSimWrong = 48,
InfinibandMode = 49,
DependencyFailed = 50,
Br2684Failed = 51,
ModemManagerUnavailable = 52,
SsidNotFound = 53,
SecondaryConnectionFailed = 54,
DcbFcoeFailed = 55,
TeamdControlFailed = 56,
ModemFailed = 57,
ModemAvailable = 58,
SimPinIncorrect = 59,
NewActivation = 60,
ParentChanged = 61,
ParentManagedChanged = 62,
OvsdbFailed = 63,
IpAddressDuplicate = 64,
IpMethodUnsupported = 65,
SriovConfigurationFailed = 66,
PeerNotFound = 67,
DeviceHandlerFailed = 68,
UnmanagedByDefault = 69,
UnmanagedExternalDown = 70,
UnmanagedLinkNotInit = 71,
UnmanagedQuitting = 72,
UnmanagedManagerDisabled = 73,
UnmanagedUserConf = 74,
UnmanagedUserExplicit = 75,
UnmanagedUserSettings = 76,
UnmanagedUserUdev = 77,
NetworkingOff = 78,
};
Q_ENUM(Enum);
};
// 802.11 specific device encryption and authentication capabilities. // 802.11 specific device encryption and authentication capabilities.
// In sync with https://networkmanager.dev/docs/api/latest/nm-dbus-types.html#NMDeviceWifiCapabilities. // In sync with https://networkmanager.dev/docs/api/latest/nm-dbus-types.html#NMDeviceWifiCapabilities.
class NMWirelessCapabilities: public QObject { class NMWirelessCapabilities: public QObject {
@@ -284,31 +153,4 @@ public:
Q_ENUM(Enum); Q_ENUM(Enum);
}; };
/// In sync with https://networkmanager.dev/docs/api/latest/nm-dbus-types.html#NMActiveConnectionStateReason.
class NMConnectionStateReason: public QObject {
Q_OBJECT;
QML_ELEMENT;
QML_SINGLETON;
public:
enum Enum : quint8 {
Unknown = 0,
None = 1,
UserDisconnected = 2,
DeviceDisconnected = 3,
ServiceStopped = 4,
IpConfigInvalid = 5,
ConnectTimeout = 6,
ServiceStartTimeout = 7,
ServiceStartFailed = 8,
NoSecrets = 9,
LoginFailed = 10,
ConnectionRemoved = 11,
DependencyFailed = 12,
DeviceRealizeFailed = 13,
DeviceRemoved = 14
};
Q_ENUM(Enum);
};
} // namespace qs::network } // namespace qs::network
@@ -1,10 +1,5 @@
<node> <node>
<interface name="org.freedesktop.NetworkManager.Device"> <interface name="org.freedesktop.NetworkManager.Device">
<method name="Disconnect"/> <method name="Disconnect"/>
<signal name="StateChanged">
<arg name="new_state" type="u"/>
<arg name="old_state" type="u"/>
<arg name="reason" type="u"/>
</signal>
</interface> </interface>
</node> </node>
@@ -2,18 +2,8 @@
<interface name="org.freedesktop.NetworkManager.Settings.Connection"> <interface name="org.freedesktop.NetworkManager.Settings.Connection">
<method name="GetSettings"> <method name="GetSettings">
<arg name="settings" type="a{sa{sv}}" direction="out"/> <arg name="settings" type="a{sa{sv}}" direction="out"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="qs::network::NMSettingsMap"/> <annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="ConnectionSettingsMap"/>
</method> </method>
<method name="GetSecrets">
<arg name="setting_name" type="s" direction="in"/>
<arg name="secrets" type="a{sa{sv}}" direction="out"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="qs::network::NMSettingsMap"/>
</method>
<method name="Update">
<arg name="properties" type="a{sa{sv}}" direction="in"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="qs::network::NMSettingsMap"/>
</method>
<method name="ClearSecrets"/>
<method name="Delete"/> <method name="Delete"/>
<signal name="Updated"/> <signal name="Updated"/>
<signal name="Removed"/> <signal name="Removed"/>
@@ -1,8 +1,5 @@
<node> <node>
<interface name="org.freedesktop.NetworkManager"> <interface name="org.freedesktop.NetworkManager">
<method name="CheckConnectivity">
<arg direction="out" type="u" name="connectivity"/>
</method>
<method name="GetAllDevices"> <method name="GetAllDevices">
<arg direction="out" type="ao" name="devices"/> <arg direction="out" type="ao" name="devices"/>
</method> </method>
@@ -14,7 +11,7 @@
</method> </method>
<method name="AddAndActivateConnection"> <method name="AddAndActivateConnection">
<arg direction="in" type="a{sa{sv}}" name="connection"/> <arg direction="in" type="a{sa{sv}}" name="connection"/>
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="qs::network::NMSettingsMap"/> <annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="ConnectionSettingsMap"/>
<arg direction="in" type="o" name="device"/> <arg direction="in" type="o" name="device"/>
<arg direction="in" type="o" name="specific_object"/> <arg direction="in" type="o" name="specific_object"/>
<arg direction="out" type="o" name="path"/> <arg direction="out" type="o" name="path"/>
-227
View File
@@ -1,227 +0,0 @@
#include "settings.hpp"
#include <qcontainerfwd.h>
#include <qdbusconnection.h>
#include <qdbusmetatype.h>
#include <qdbuspendingcall.h>
#include <qdbuspendingreply.h>
#include <qdebug.h>
#include <qlogging.h>
#include <qloggingcategory.h>
#include <qmetatype.h>
#include <qobject.h>
#include <qstring.h>
#include <qtmetamacros.h>
#include <qtypes.h>
#include "../../core/logcat.hpp"
#include "../../dbus/properties.hpp"
#include "dbus_nm_connection_settings.h"
#include "dbus_types.hpp"
#include "utils.hpp"
namespace qs::network {
using namespace qs::dbus;
namespace {
QS_LOGGING_CATEGORY(logNetworkManager, "quickshell.network", QtWarningMsg);
QS_LOGGING_CATEGORY(logNMSettings, "quickshell.network.nm_settings", QtWarningMsg);
} // namespace
NMSettings::NMSettings(const QString& path, QObject* parent): QObject(parent) {
qDBusRegisterMetaType<QList<QVariantMap>>();
qDBusRegisterMetaType<QList<quint32>>();
qDBusRegisterMetaType<QList<QList<quint32>>>();
qDBusRegisterMetaType<QList<QByteArray>>();
qDBusRegisterMetaType<NMIPv6Address>();
qDBusRegisterMetaType<QList<NMIPv6Address>>();
qDBusRegisterMetaType<NMIPv6Route>();
qDBusRegisterMetaType<QList<NMIPv6Route>>();
this->proxy = new DBusNMConnectionSettingsProxy(
"org.freedesktop.NetworkManager",
path,
QDBusConnection::systemBus(),
this
);
if (!this->proxy->isValid()) {
qCWarning(logNetworkManager) << "Cannot create DBus interface for connection settings at"
<< path;
return;
}
QObject::connect(
this->proxy,
&DBusNMConnectionSettingsProxy::Updated,
this,
&NMSettings::getSettings
);
this->bId.setBinding([this]() { return this->bSettings.value()["connection"]["id"].toString(); });
this->bUuid.setBinding([this]() {
return this->bSettings.value()["connection"]["uuid"].toString();
});
this->settingsProperties.setInterface(this->proxy);
this->settingsProperties.updateAllViaGetAll();
this->getSettings();
}
void NMSettings::getSettings() {
auto pending = this->proxy->GetSettings();
auto* call = new QDBusPendingCallWatcher(pending, this);
auto responseCallback = [this](QDBusPendingCallWatcher* call) {
const QDBusPendingReply<NMSettingsMap> reply = *call;
if (reply.isError()) {
qCWarning(logNetworkManager)
<< "Failed to get settings for" << this->path() << ":" << reply.error().message();
} else {
auto settings = reply.value();
manualSettingDemarshall(settings);
this->bSettings = settings;
qCDebug(logNetworkManager) << "Settings map updated for" << this->path();
if (!this->mLoaded) {
emit this->loaded();
this->mLoaded = true;
}
};
delete call;
};
QObject::connect(call, &QDBusPendingCallWatcher::finished, this, responseCallback);
}
QDBusPendingCallWatcher* NMSettings::updateSettings(
const NMSettingsMap& settingsToChange,
const NMSettingsMap& settingsToRemove
) {
auto settings = removeSettingsInMap(this->bSettings, settingsToRemove);
settings = mergeSettingsMaps(settings, settingsToChange);
auto pending = this->proxy->Update(settings);
auto* call = new QDBusPendingCallWatcher(pending, this);
return call;
}
void NMSettings::clearSecrets() {
auto pending = this->proxy->ClearSecrets();
auto* call = new QDBusPendingCallWatcher(pending, this);
auto responseCallback = [this](QDBusPendingCallWatcher* call) {
const QDBusPendingReply<> reply = *call;
if (reply.isError()) {
qCWarning(logNetworkManager)
<< "Failed to clear secrets for" << this->path() << ":" << reply.error().message();
} else {
qCDebug(logNetworkManager) << "Cleared secrets for" << this->path();
}
delete call;
};
QObject::connect(call, &QDBusPendingCallWatcher::finished, this, responseCallback);
}
void NMSettings::forget() {
auto pending = this->proxy->Delete();
auto* call = new QDBusPendingCallWatcher(pending, this);
auto responseCallback = [this](QDBusPendingCallWatcher* call) {
const QDBusPendingReply<> reply = *call;
if (reply.isError()) {
qCWarning(logNetworkManager)
<< "Failed to forget" << this->path() << ":" << reply.error().message();
} else {
qCDebug(logNetworkManager) << "Successfully deletion of" << this->path();
}
delete call;
};
QObject::connect(call, &QDBusPendingCallWatcher::finished, this, responseCallback);
}
QVariantMap NMSettings::read() {
QVariantMap result;
const auto& settings = this->bSettings.value();
for (auto it = settings.constBegin(); it != settings.constEnd(); ++it) {
QVariantMap group;
for (auto jt = it.value().constBegin(); jt != it.value().constEnd(); ++jt) {
group.insert(jt.key(), settingTypeToQml(jt.value()));
}
result.insert(it.key(), group);
}
return result;
}
void NMSettings::write(const QVariantMap& settings) {
NMSettingsMap changedSettings;
NMSettingsMap removedSettings;
QStringList failedSettings;
for (auto it = settings.constBegin(); it != settings.constEnd(); ++it) {
if (!it.value().canConvert<QVariantMap>()) continue;
auto group = it.value().toMap();
QVariantMap toChange;
QVariantMap toRemove;
for (auto jt = group.constBegin(); jt != group.constEnd(); ++jt) {
if (jt.value().isNull()) {
toRemove.insert(jt.key(), QVariant());
} else {
auto converted = settingTypeFromQml(it.key(), jt.key(), jt.value());
if (!converted.isValid()) failedSettings.append(it.key() + "." + jt.key());
else toChange.insert(jt.key(), converted);
}
}
if (!toChange.isEmpty()) changedSettings.insert(it.key(), toChange);
if (!toRemove.isEmpty()) removedSettings.insert(it.key(), toRemove);
}
if (!failedSettings.isEmpty()) {
qCWarning(logNMSettings) << "A write to" << this
<< "has received bad types for the following settings:"
<< failedSettings.join(", ");
}
auto* call = this->updateSettings(changedSettings, removedSettings);
auto responseCallback = [this](QDBusPendingCallWatcher* call) {
const QDBusPendingReply<> reply = *call;
if (reply.isError()) {
qCWarning(logNetworkManager)
<< "Failed to update settings for" << this->path() << ":" << reply.error().message();
} else {
qCDebug(logNMSettings) << "Successful write to" << this;
}
delete call;
};
QObject::connect(call, &QDBusPendingCallWatcher::finished, this, responseCallback);
}
bool NMSettings::isValid() const { return this->proxy && this->proxy->isValid(); }
QString NMSettings::address() const { return this->proxy ? this->proxy->service() : QString(); }
QString NMSettings::path() const { return this->proxy ? this->proxy->path() : QString(); }
} // namespace qs::network
QDebug operator<<(QDebug debug, const qs::network::NMSettings* settings) {
auto saver = QDebugStateSaver(debug);
if (settings) {
debug.nospace() << "NMSettings(" << static_cast<const void*>(settings)
<< ", uuid=" << settings->uuid() << ")";
} else {
debug << "WifiNetwork(nullptr)";
}
return debug;
}
-82
View File
@@ -1,82 +0,0 @@
#pragma once
#include <qbytearray.h>
#include <qdbusextratypes.h>
#include <qdbuspendingcall.h>
#include <qdbuspendingreply.h>
#include <qobject.h>
#include <qproperty.h>
#include <qqmlintegration.h>
#include <qstringlist.h>
#include <qtmetamacros.h>
#include <qtypes.h>
#include "../../dbus/properties.hpp"
#include "dbus_nm_connection_settings.h"
#include "dbus_types.hpp"
namespace qs::network {
// Proxy of a /org/freedesktop/NetworkManager/Settings/Connection/* object.
///! A NetworkManager connection settings profile.
class NMSettings: public QObject {
Q_OBJECT;
QML_ELEMENT;
QML_UNCREATABLE("");
/// The human-readable unique identifier for the connection.
Q_PROPERTY(QString id READ default NOTIFY idChanged BINDABLE bindableId);
/// A universally unique identifier for the connection.
Q_PROPERTY(QString uuid READ uuid NOTIFY uuidChanged BINDABLE bindableUuid);
public:
explicit NMSettings(const QString& path, QObject* parent = nullptr);
/// Clear all of the secrets belonging to the settings.
Q_INVOKABLE void clearSecrets();
/// Delete the settings.
Q_INVOKABLE void forget();
/// Update the connection with new settings and save the connection to disk.
/// Only changed fields need to be included.
/// Writing a setting to `null` will remove the setting or reset it to its default.
///
/// > [!NOTE] Secrets may be part of the update request,
/// > and will be either stored in persistent storage or sent to a Secret Agent for storage,
/// > depending on the flags associated with each secret.
Q_INVOKABLE void write(const QVariantMap& settings);
/// Get the settings map describing this network configuration.
///
/// > [!NOTE] This will never include any secrets required for connection to the network, as those are often protected.
Q_INVOKABLE QVariantMap read();
[[nodiscard]] bool isValid() const;
[[nodiscard]] QString path() const;
[[nodiscard]] QString address() const;
[[nodiscard]] NMSettingsMap map() { return this->bSettings; }
QDBusPendingCallWatcher*
updateSettings(const NMSettingsMap& settingsToChange, const NMSettingsMap& settingsToRemove = {});
QBindable<QString> bindableId() { return &this->bId; }
[[nodiscard]] QString uuid() const { return this->bUuid; }
QBindable<QString> bindableUuid() { return &this->bUuid; }
signals:
void loaded();
void settingsChanged(NMSettingsMap settings);
void idChanged(QString id);
void uuidChanged(QString uuid);
private:
bool mLoaded = false;
void getSettings();
Q_OBJECT_BINDABLE_PROPERTY(NMSettings, NMSettingsMap, bSettings, &NMSettings::settingsChanged);
Q_OBJECT_BINDABLE_PROPERTY(NMSettings, QString, bId, &NMSettings::idChanged);
Q_OBJECT_BINDABLE_PROPERTY(NMSettings, QString, bUuid, &NMSettings::uuidChanged);
QS_DBUS_BINDABLE_PROPERTY_GROUP(NMSettings, settingsProperties);
DBusNMConnectionSettingsProxy* proxy = nullptr;
};
} // namespace qs::network
QDebug operator<<(QDebug debug, const qs::network::NMSettings* settings);
+7 -284
View File
@@ -1,28 +1,27 @@
#include "utils.hpp" #include "utils.hpp"
#include <cmath>
// We depend on non-std Linux extensions that ctime doesn't put in the global namespace // We depend on non-std Linux extensions that ctime doesn't put in the global namespace
// NOLINTNEXTLINE(modernize-deprecated-headers) // NOLINTNEXTLINE(modernize-deprecated-headers)
#include <time.h> #include <time.h>
#include <qcontainerfwd.h> #include <qcontainerfwd.h>
#include <qdatetime.h> #include <qdatetime.h>
#include <qdbusargument.h>
#include <qdbusservicewatcher.h> #include <qdbusservicewatcher.h>
#include <qobject.h> #include <qobject.h>
#include <qqmlintegration.h> #include <qqmlintegration.h>
#include <qtypes.h> #include <qtypes.h>
#include "../enums.hpp" #include "../wifi.hpp"
#include "dbus_types.hpp" #include "dbus_types.hpp"
#include "enums.hpp" #include "enums.hpp"
namespace qs::network { namespace qs::network {
WifiSecurityType::Enum securityFromSettingsMap(const NMSettingsMap& settings) { WifiSecurityType::Enum securityFromConnectionSettings(const ConnectionSettingsMap& settings) {
const QString mapName = "802-11-wireless-security"; const QVariantMap& security = settings.value("802-11-wireless-security");
if (!settings.contains(mapName)) return WifiSecurityType::Unknown; if (security.isEmpty()) {
const QVariantMap& security = settings.value(mapName); return WifiSecurityType::Open;
if (security.isEmpty()) return WifiSecurityType::Open; };
const QString keyMgmt = security["key-mgmt"].toString(); const QString keyMgmt = security["key-mgmt"].toString();
const QString authAlg = security["auth-alg"].toString(); const QString authAlg = security["auth-alg"].toString();
@@ -46,8 +45,6 @@ WifiSecurityType::Enum securityFromSettingsMap(const NMSettingsMap& settings) {
return WifiSecurityType::Sae; return WifiSecurityType::Sae;
} else if (keyMgmt == "wpa-eap-suite-b-192") { } else if (keyMgmt == "wpa-eap-suite-b-192") {
return WifiSecurityType::Wpa3SuiteB192; return WifiSecurityType::Wpa3SuiteB192;
} else if (keyMgmt == "owe") {
return WifiSecurityType::Owe;
} }
return WifiSecurityType::Open; return WifiSecurityType::Open;
} }
@@ -227,280 +224,6 @@ WifiSecurityType::Enum findBestWirelessSecurity(
return WifiSecurityType::Unknown; return WifiSecurityType::Unknown;
} }
NMSettingsMap mergeSettingsMaps(const NMSettingsMap& target, const NMSettingsMap& source) {
NMSettingsMap result = target;
for (auto iter = source.constBegin(); iter != source.constEnd(); ++iter) {
result[iter.key()].insert(iter.value());
}
return result;
}
NMSettingsMap removeSettingsInMap(const NMSettingsMap& target, const NMSettingsMap& toRemove) {
NMSettingsMap result = target;
for (auto iter = toRemove.constBegin(); iter != toRemove.constEnd(); ++iter) {
const QString& group = iter.key();
const QVariantMap& keysToRemove = iter.value();
if (!result.contains(group)) continue;
for (auto jt = keysToRemove.constBegin(); jt != keysToRemove.constEnd(); ++jt) {
result[group].remove(jt.key());
}
// Remove the group entirely if it's now empty
if (result[group].isEmpty()) {
result.remove(group);
}
}
return result;
}
// Some NMSettingsMap settings remain QDBusArguments after autodemarshalling.
// Manually demarshall these for any complex signature we have registered.
void manualSettingDemarshall(NMSettingsMap& map) {
auto demarshallValue = [](const QVariant& value) -> QVariant {
if (value.userType() != qMetaTypeId<QDBusArgument>()) {
return value;
}
auto arg = value.value<QDBusArgument>();
auto signature = arg.currentSignature();
if (signature == "ay") return QVariant::fromValue(qdbus_cast<QByteArray>(arg));
if (signature == "aay") return QVariant::fromValue(qdbus_cast<QList<QByteArray>>(arg));
if (signature == "au") return QVariant::fromValue(qdbus_cast<QList<quint32>>(arg));
if (signature == "aau") return QVariant::fromValue(qdbus_cast<QList<QList<quint32>>>(arg));
if (signature == "aa{sv}") return QVariant::fromValue(qdbus_cast<QList<QVariantMap>>(arg));
if (signature == "a(ayuay)") return QVariant::fromValue(qdbus_cast<QList<NMIPv6Address>>(arg));
if (signature == "a(ayuayu)") return QVariant::fromValue(qdbus_cast<QList<NMIPv6Route>>(arg));
return value;
};
for (auto it = map.begin(); it != map.end(); ++it)
for (auto jt = it.value().begin(); jt != it.value().end(); ++jt)
jt.value() = demarshallValue(jt.value());
}
// Some NMSettingsMap setting types can't be expressed in QML.
// Convert these settings to their correct type or return an invalid QVariant.
QVariant settingTypeFromQml(const QString& group, const QString& key, const QVariant& value) {
auto s = group + "." + key;
// QString -> QByteArray
if (s == "802-1x.ca-cert" || s == "802-1x.client-cert" || s == "802-1x.private-key"
|| s == "802-1x.password-raw" || s == "802-1x.phase2-ca-cert"
|| s == "802-1x.phase2-client-cert" || s == "802-1x.phase2-private-key"
|| s == "802-11-wireless.ssid")
{
if (value.typeId() == QMetaType::QString) {
return value.toString().toUtf8();
}
if (value.typeId() == QMetaType::QByteArray) {
return value;
}
return QVariant();
}
// QVariantList -> QList<QByteArray>
if (s == "ipv6.dns") {
if (value.typeId() == QMetaType::QVariantList) {
QList<QByteArray> r;
for (const auto& v: value.toList()) {
if (v.typeId() == QMetaType::QString) {
r.append(v.toString().toUtf8());
} else {
r.append(v.toByteArray());
}
}
return QVariant::fromValue(r);
}
return QVariant();
}
// QVariantList -> QList<quint32>
if (s == "ipv4.dns") {
if (value.typeId() == QMetaType::QVariantList) {
QList<quint32> r;
for (const auto& v: value.toList()) {
r.append(v.value<quint32>());
}
return QVariant::fromValue(r);
}
return QVariant();
}
// QVariantList -> QList<QList<quint32>>
if (s == "ipv4.addresses" || s == "ipv4.routes") {
if (value.typeId() == QMetaType::QVariantList) {
QList<QList<quint32>> r;
for (const auto& v: value.toList()) {
if (v.typeId() != QMetaType::QVariantList) {
continue;
}
QList<quint32> inner;
for (const auto& u: v.toList()) {
inner.append(u.value<quint32>());
}
r.append(inner);
}
return QVariant::fromValue(r);
}
return QVariant();
}
// QVariantList -> QList<QVariantMap>
if (s == "ipv4.address-data" || s == "ipv4.route-data" || s == "ipv4.routing-rules"
|| s == "ipv6.address-data" || s == "ipv6.route-data" || s == "ipv6.routing-rules")
{
if (value.typeId() == QMetaType::QVariantList) {
QList<QVariantMap> r;
for (const auto& v: value.toList()) {
if (!v.canConvert<QVariantMap>()) {
continue;
}
r.append(v.toMap());
}
return QVariant::fromValue(r);
}
return QVariant();
}
// QVariantList -> QList<NMIPv6Address>
if (s == "ipv6.addresses") {
if (value.typeId() == QMetaType::QVariantList) {
QList<NMIPv6Address> r;
for (const auto& v: value.toList()) {
if (v.typeId() != QMetaType::QVariantList) {
continue;
}
auto fields = v.toList();
if (fields.size() != 3) {
continue;
}
const QByteArray address = fields[0].typeId() == QMetaType::QString
? fields[0].toString().toUtf8()
: fields[0].toByteArray();
const QByteArray gateway = fields[2].typeId() == QMetaType::QString
? fields[2].toString().toUtf8()
: fields[2].toByteArray();
r.append({.address = address, .prefix = fields[1].value<quint32>(), .gateway = gateway});
}
return QVariant::fromValue(r);
}
return QVariant();
}
// QVariantList -> QList<NMIPv6Route>
if (s == "ipv6.routes") {
if (value.typeId() == QMetaType::QVariantList) {
QList<NMIPv6Route> r;
for (const auto& v: value.toList()) {
if (v.typeId() != QMetaType::QVariantList) {
continue;
}
auto fields = v.toList();
if (fields.size() != 4) {
continue;
}
const QByteArray destination = fields[0].typeId() == QMetaType::QString
? fields[0].toString().toUtf8()
: fields[0].toByteArray();
const QByteArray nexthop = fields[2].typeId() == QMetaType::QString
? fields[2].toString().toUtf8()
: fields[2].toByteArray();
r.append(
{.destination = destination,
.prefix = fields[1].value<quint32>(),
.nexthop = nexthop,
.metric = fields[3].value<quint32>()}
);
}
return QVariant::fromValue(r);
}
return QVariant();
}
// QVariantList -> QStringList
if (s == "connection.permissions" || s == "ipv4.dns-search" || s == "ipv6.dns-search"
|| s == "802-11-wireless.seen-bssids")
{
if (value.typeId() == QMetaType::QVariantList) {
QStringList stringList;
for (const auto& item: value.toList()) {
stringList.append(item.toString());
}
return stringList;
}
return QVariant();
}
// double (whole number) -> qint32
if (value.typeId() == QMetaType::Double) {
auto num = value.toDouble();
if (std::isfinite(num) && num == std::trunc(num)) {
return QVariant::fromValue(static_cast<qint32>(num));
}
}
return value;
}
// Some NMSettingsMap setting types must be converted to a type that is supported by QML.
// Although QByteArrays can be represented in QML, we convert them to strings for convenience.
QVariant settingTypeToQml(const QVariant& value) {
// QByteArray -> QString
if (value.typeId() == QMetaType::QByteArray) {
return QString::fromUtf8(value.toByteArray());
}
// QList<QByteArray> -> QVariantList
if (value.userType() == qMetaTypeId<QList<QByteArray>>()) {
QVariantList out;
for (const auto& ba: value.value<QList<QByteArray>>()) {
out.append(QString::fromUtf8(ba));
}
return out;
}
// QList<NMIPv6Address> -> QVariantList
if (value.userType() == qMetaTypeId<QList<NMIPv6Address>>()) {
QVariantList out;
for (const auto& addr: value.value<QList<NMIPv6Address>>()) {
out.append(
QVariant::fromValue(
QVariantList {
QString::fromUtf8(addr.address),
addr.prefix,
QString::fromUtf8(addr.gateway),
}
)
);
}
return out;
}
// QList<NMIPv6Route> -> QVariantList
if (value.userType() == qMetaTypeId<QList<NMIPv6Route>>()) {
QVariantList out;
for (const auto& route: value.value<QList<NMIPv6Route>>()) {
out.append(
QVariant::fromValue(
QVariantList {
QString::fromUtf8(route.destination),
route.prefix,
QString::fromUtf8(route.nexthop),
route.metric,
}
)
);
}
return out;
}
return value;
}
// NOLINTBEGIN // NOLINTBEGIN
QDateTime clockBootTimeToDateTime(qint64 clockBootTime) { QDateTime clockBootTimeToDateTime(qint64 clockBootTime) {
clockid_t clkId = CLOCK_BOOTTIME; clockid_t clkId = CLOCK_BOOTTIME;
+3 -12
View File
@@ -3,14 +3,15 @@
#include <qcontainerfwd.h> #include <qcontainerfwd.h>
#include <qdbusservicewatcher.h> #include <qdbusservicewatcher.h>
#include <qobject.h> #include <qobject.h>
#include <qqmlintegration.h>
#include "../enums.hpp" #include "../wifi.hpp"
#include "dbus_types.hpp" #include "dbus_types.hpp"
#include "enums.hpp" #include "enums.hpp"
namespace qs::network { namespace qs::network {
WifiSecurityType::Enum securityFromSettingsMap(const NMSettingsMap& settings); WifiSecurityType::Enum securityFromConnectionSettings(const ConnectionSettingsMap& settings);
bool deviceSupportsApCiphers( bool deviceSupportsApCiphers(
NMWirelessCapabilities::Enum caps, NMWirelessCapabilities::Enum caps,
@@ -39,16 +40,6 @@ WifiSecurityType::Enum findBestWirelessSecurity(
NM80211ApSecurityFlags::Enum apRsn NM80211ApSecurityFlags::Enum apRsn
); );
NMSettingsMap mergeSettingsMaps(const NMSettingsMap& target, const NMSettingsMap& source);
NMSettingsMap removeSettingsInMap(const NMSettingsMap& target, const NMSettingsMap& toRemove);
void manualSettingDemarshall(NMSettingsMap& map);
QVariant settingTypeFromQml(const QString& group, const QString& key, const QVariant& value);
QVariant settingTypeToQml(const QVariant& value);
QDateTime clockBootTimeToDateTime(qint64 clockBootTime); QDateTime clockBootTimeToDateTime(qint64 clockBootTime);
} // namespace qs::network } // namespace qs::network
+67 -164
View File
@@ -1,7 +1,6 @@
#include "wireless.hpp" #include "wireless.hpp"
#include <utility> #include <utility>
#include <qcontainerfwd.h>
#include <qdatetime.h> #include <qdatetime.h>
#include <qdbusconnection.h> #include <qdbusconnection.h>
#include <qdbusextratypes.h> #include <qdbusextratypes.h>
@@ -12,23 +11,20 @@
#include <qloggingcategory.h> #include <qloggingcategory.h>
#include <qnamespace.h> #include <qnamespace.h>
#include <qobject.h> #include <qobject.h>
#include <qpointer.h>
#include <qstring.h> #include <qstring.h>
#include <qtmetamacros.h> #include <qtmetamacros.h>
#include <qtypes.h> #include <qtypes.h>
#include <qvariant.h>
#include "../../core/logcat.hpp" #include "../../core/logcat.hpp"
#include "../../dbus/properties.hpp" #include "../../dbus/properties.hpp"
#include "../enums.hpp" #include "../network.hpp"
#include "../wifi.hpp" #include "../wifi.hpp"
#include "accesspoint.hpp" #include "accesspoint.hpp"
#include "active_connection.hpp" #include "connection.hpp"
#include "dbus_nm_wireless.h" #include "dbus_nm_wireless.h"
#include "dbus_types.hpp" #include "dbus_types.hpp"
#include "device.hpp" #include "device.hpp"
#include "enums.hpp" #include "enums.hpp"
#include "settings.hpp"
#include "utils.hpp" #include "utils.hpp"
namespace qs::network { namespace qs::network {
@@ -46,43 +42,38 @@ NMWirelessNetwork::NMWirelessNetwork(QString ssid, QObject* parent)
, bReason(NMConnectionStateReason::None) , bReason(NMConnectionStateReason::None)
, bState(NMConnectionState::Deactivated) {} , bState(NMConnectionState::Deactivated) {}
void NMWirelessNetwork::updateReferenceSettings() { void NMWirelessNetwork::updateReferenceConnection() {
// If the network has no connections, the reference is nullptr. // If the network has no connections, the reference is nullptr.
if (this->mSettings.isEmpty()) { if (this->mConnections.isEmpty()) {
this->mReferenceSettings = nullptr; this->mReferenceConn = nullptr;
this->bSecurity = WifiSecurityType::Unknown; this->bSecurity = WifiSecurityType::Unknown;
// Set security back to reference AP.
if (this->mReferenceAp) { if (this->mReferenceAp) {
this->bSecurity.setBinding([this]() { return this->mReferenceAp->security(); }); this->bSecurity.setBinding([this]() { return this->mReferenceAp->security(); });
} }
return; return;
}; };
// If the network has an active connection, use its settings as the reference. // If the network has an active connection, use it as the reference.
if (this->mActiveConnection) { if (this->mActiveConnection) {
auto* settings = this->mSettings.value(this->mActiveConnection->connection().path()); auto* conn = this->mConnections.value(this->mActiveConnection->connection().path());
if (settings && settings != this->mReferenceSettings) { if (conn && conn != this->mReferenceConn) {
this->mReferenceSettings = settings; this->mReferenceConn = conn;
this->bSecurity.setBinding([settings]() { return securityFromSettingsMap(settings->map()); }); this->bSecurity.setBinding([conn]() { return conn->security(); });
} }
return; return;
} }
// Otherwise, choose the settings responsible for the last successful connection. // Otherwise, choose the connection with the strongest security settings.
NMSettings* selectedSettings = nullptr; NMConnectionSettings* selectedConn = nullptr;
quint64 selectedTimestamp = 0; for (auto* conn: this->mConnections.values()) {
for (auto* settings: this->mSettings.values()) { if (!selectedConn || conn->security() > selectedConn->security()) {
const quint64 timestamp = settings->map()["connection"]["timestamp"].toULongLong(); selectedConn = conn;
if (!selectedSettings || timestamp > selectedTimestamp) {
selectedSettings = settings;
selectedTimestamp = timestamp;
} }
} }
if (this->mReferenceConn != selectedConn) {
if (this->mReferenceSettings != selectedSettings) { this->mReferenceConn = selectedConn;
this->mReferenceSettings = selectedSettings; this->bSecurity.setBinding([selectedConn]() { return selectedConn->security(); });
this->bSecurity.setBinding([selectedSettings]() {
return securityFromSettingsMap(selectedSettings->map());
});
} }
} }
@@ -110,7 +101,7 @@ void NMWirelessNetwork::updateReferenceAp() {
this->mReferenceAp = selectedAp; this->mReferenceAp = selectedAp;
this->bSignalStrength.setBinding([selectedAp]() { return selectedAp->signalStrength(); }); this->bSignalStrength.setBinding([selectedAp]() { return selectedAp->signalStrength(); });
// Reference AP is used for security when there's no connection settings. // Reference AP is used for security when there's no connection settings.
if (!this->mReferenceSettings) { if (!this->mReferenceConn) {
this->bSecurity.setBinding([selectedAp]() { return selectedAp->security(); }); this->bSecurity.setBinding([selectedAp]() { return selectedAp->security(); });
} }
} }
@@ -122,7 +113,7 @@ void NMWirelessNetwork::addAccessPoint(NMAccessPoint* ap) {
auto onDestroyed = [this, ap]() { auto onDestroyed = [this, ap]() {
if (this->mAccessPoints.take(ap->path())) { if (this->mAccessPoints.take(ap->path())) {
this->updateReferenceAp(); this->updateReferenceAp();
if (this->mAccessPoints.isEmpty() && this->mSettings.isEmpty()) emit this->disappeared(); if (this->mAccessPoints.isEmpty() && this->mConnections.isEmpty()) emit this->disappeared();
} }
}; };
// clang-format off // clang-format off
@@ -132,45 +123,44 @@ void NMWirelessNetwork::addAccessPoint(NMAccessPoint* ap) {
this->updateReferenceAp(); this->updateReferenceAp();
}; };
void NMWirelessNetwork::addSettings(NMSettings* settings) { void NMWirelessNetwork::addConnection(NMConnectionSettings* conn) {
if (this->mSettings.contains(settings->path())) return; if (this->mConnections.contains(conn->path())) return;
this->mSettings.insert(settings->path(), settings); this->mConnections.insert(conn->path(), conn);
auto onDestroyed = [this, conn]() {
auto onDestroyed = [this, settings]() { if (this->mConnections.take(conn->path())) {
if (this->mSettings.take(settings->path())) { this->updateReferenceConnection();
emit this->settingsRemoved(settings); if (this->mConnections.isEmpty()) this->bKnown = false;
this->updateReferenceSettings(); if (this->mAccessPoints.isEmpty() && this->mConnections.isEmpty()) emit this->disappeared();
if (this->mSettings.isEmpty()) this->bKnown = false;
if (this->mAccessPoints.isEmpty() && this->mSettings.isEmpty()) emit this->disappeared();
} }
}; };
QObject::connect(settings, &NMSettings::destroyed, this, onDestroyed); // clang-format off
QObject::connect(conn, &NMConnectionSettings::securityChanged, this, &NMWirelessNetwork::updateReferenceConnection);
QObject::connect(conn, &NMConnectionSettings::destroyed, this, onDestroyed);
// clang-format on
this->bKnown = true; this->bKnown = true;
this->updateReferenceSettings(); this->updateReferenceConnection();
emit this->settingsAdded(settings);
}; };
void NMWirelessNetwork::addActiveConnection(NMActiveConnection* active) { void NMWirelessNetwork::addActiveConnection(NMActiveConnection* active) {
if (this->mActiveConnection) return; if (this->mActiveConnection) return;
this->mActiveConnection = active; this->mActiveConnection = active;
this->bState.setBinding([active]() { return active->state(); }); this->bState.setBinding([active]() { return active->state(); });
this->bReason.setBinding([active]() { return active->stateReason(); }); this->bReason.setBinding([active]() { return active->stateReason(); });
auto onDestroyed = [this, active]() { auto onDestroyed = [this, active]() {
if (this->mActiveConnection && this->mActiveConnection == active) { if (this->mActiveConnection && this->mActiveConnection == active) {
this->mActiveConnection = nullptr; this->mActiveConnection = nullptr;
this->updateReferenceSettings(); this->updateReferenceConnection();
this->bState = NMConnectionState::Deactivated; this->bState = NMConnectionState::Deactivated;
this->bReason = NMConnectionStateReason::None; this->bReason = NMConnectionStateReason::None;
} }
}; };
QObject::connect(active, &NMActiveConnection::destroyed, this, onDestroyed); QObject::connect(active, &NMActiveConnection::destroyed, this, onDestroyed);
this->updateReferenceSettings(); this->updateReferenceConnection();
}; };
void NMWirelessNetwork::forget() { void NMWirelessNetwork::forget() {
if (this->mSettings.isEmpty()) return; if (this->mConnections.isEmpty()) return;
for (auto* conn: this->mSettings.values()) { for (auto* conn: this->mConnections.values()) {
conn->forget(); conn->forget();
} }
} }
@@ -210,7 +200,7 @@ void NMWirelessDevice::initWireless() {
QObject::connect(this->wirelessProxy, &DBusNMWirelessProxy::AccessPointAdded, this, &NMWirelessDevice::onAccessPointAdded); QObject::connect(this->wirelessProxy, &DBusNMWirelessProxy::AccessPointAdded, this, &NMWirelessDevice::onAccessPointAdded);
QObject::connect(this->wirelessProxy, &DBusNMWirelessProxy::AccessPointRemoved, this, &NMWirelessDevice::onAccessPointRemoved); QObject::connect(this->wirelessProxy, &DBusNMWirelessProxy::AccessPointRemoved, this, &NMWirelessDevice::onAccessPointRemoved);
QObject::connect(this, &NMWirelessDevice::accessPointLoaded, this, &NMWirelessDevice::onAccessPointLoaded); QObject::connect(this, &NMWirelessDevice::accessPointLoaded, this, &NMWirelessDevice::onAccessPointLoaded);
QObject::connect(this, &NMWirelessDevice::settingsLoaded, this, &NMWirelessDevice::onSettingsLoaded); QObject::connect(this, &NMWirelessDevice::connectionLoaded, this, &NMWirelessDevice::onConnectionLoaded);
QObject::connect(this, &NMWirelessDevice::activeConnectionLoaded, this, &NMWirelessDevice::onActiveConnectionLoaded); QObject::connect(this, &NMWirelessDevice::activeConnectionLoaded, this, &NMWirelessDevice::onActiveConnectionLoaded);
QObject::connect(this, &NMWirelessDevice::scanningChanged, this, &NMWirelessDevice::onScanningChanged); QObject::connect(this, &NMWirelessDevice::scanningChanged, this, &NMWirelessDevice::onScanningChanged);
// clang-format on // clang-format on
@@ -228,7 +218,6 @@ void NMWirelessDevice::onAccessPointRemoved(const QDBusObjectPath& path) {
<< "which is not registered."; << "which is not registered.";
return; return;
} }
qCDebug(logNetworkManager) << "Access point removed:" << path.path();
delete ap; delete ap;
} }
@@ -244,26 +233,28 @@ void NMWirelessDevice::onAccessPointLoaded(NMAccessPoint* ap) {
} }
} }
void NMWirelessDevice::onSettingsLoaded(NMSettings* settings) { void NMWirelessDevice::onConnectionLoaded(NMConnectionSettings* conn) {
const NMSettingsMap& map = settings->map(); const ConnectionSettingsMap& settings = conn->settings();
// Filter connections that aren't wireless or have missing settings // Filter connections that aren't wireless or have missing settings
if (map["connection"]["id"].toString().isEmpty() || map["connection"]["uuid"].toString().isEmpty() if (settings["connection"]["id"].toString().isEmpty()
|| !map.contains("802-11-wireless") || map["802-11-wireless"]["ssid"].toString().isEmpty()) || settings["connection"]["uuid"].toString().isEmpty()
|| !settings.contains("802-11-wireless")
|| settings["802-11-wireless"]["ssid"].toString().isEmpty())
{ {
return; return;
} }
const auto ssid = map["802-11-wireless"]["ssid"].toString(); const auto ssid = settings["802-11-wireless"]["ssid"].toString();
const auto mode = map["802-11-wireless"]["mode"].toString(); const auto mode = settings["802-11-wireless"]["mode"].toString();
if (mode == "infrastructure") { if (mode == "infrastructure") {
auto* net = this->mNetworks.value(ssid); auto* net = this->mNetworks.value(ssid);
if (!net) net = this->registerNetwork(ssid); if (!net) net = this->registerNetwork(ssid);
net->addSettings(settings); net->addConnection(conn);
// Check for active connections that loaded before their respective connection settings // Check for active connections that loaded before their respective connection settings
auto* active = this->activeConnection(); auto* active = this->activeConnection();
if (active && settings->path() == active->connection().path()) { if (active && conn->path() == active->connection().path()) {
net->addActiveConnection(active); net->addActiveConnection(active);
} }
} }
@@ -274,8 +265,8 @@ void NMWirelessDevice::onActiveConnectionLoaded(NMActiveConnection* active) {
// Find an exisiting network with connection settings that matches the active // Find an exisiting network with connection settings that matches the active
const QString activeConnPath = active->connection().path(); const QString activeConnPath = active->connection().path();
for (const auto& net: this->mNetworks.values()) { for (const auto& net: this->mNetworks.values()) {
for (auto* settings: net->settings()) { for (auto* conn: net->connections()) {
if (activeConnPath == settings->path()) { if (activeConnPath == conn->path()) {
net->addActiveConnection(active); net->addActiveConnection(active);
return; return;
} }
@@ -343,7 +334,6 @@ void NMWirelessDevice::registerAccessPoint(const QString& path) {
return; return;
} }
qCDebug(logNetworkManager) << "Access point added:" << path;
this->mAccessPoints.insert(path, ap); this->mAccessPoints.insert(path, ap);
QObject::connect( QObject::connect(
ap, ap,
@@ -366,18 +356,22 @@ void NMWirelessDevice::registerAccessPoint(const QString& path) {
NMWirelessNetwork* NMWirelessDevice::registerNetwork(const QString& ssid) { NMWirelessNetwork* NMWirelessDevice::registerNetwork(const QString& ssid) {
auto* net = new NMWirelessNetwork(ssid, this); auto* net = new NMWirelessNetwork(ssid, this);
// To avoid exposing outdated state to the frontend, filter the backend networks to only show
// the known or currently connected networks when the scanner is off.
auto visible = [this, net]() { auto visible = [this, net]() {
return this->bScanning || net->state() == NMConnectionState::Activated || net->known(); return this->bScanning || net->state() == NMConnectionState::Activated || net->known();
}; };
auto onVisibilityChanged = [this, net](bool visible) {
visible ? this->registerFrontendNetwork(net) : this->removeFrontendNetwork(net);
};
net->bindableVisible().setBinding(visible); net->bindableVisible().setBinding(visible);
net->bindableActiveApPath().setBinding([this]() { return this->activeApPath().path(); }); net->bindableActiveApPath().setBinding([this]() { return this->activeApPath().path(); });
net->bindableDeviceFailReason().setBinding([this]() { return this->lastFailReason(); });
QObject::connect(net, &NMWirelessNetwork::disappeared, this, &NMWirelessDevice::removeNetwork); QObject::connect(net, &NMWirelessNetwork::disappeared, this, &NMWirelessDevice::removeNetwork);
QObject::connect(net, &NMWirelessNetwork::visibilityChanged, this, onVisibilityChanged);
qCDebug(logNetworkManager) << "Registered network for SSID" << ssid;
this->mNetworks.insert(ssid, net); this->mNetworks.insert(ssid, net);
this->registerFrontendNetwork(net); if (net->visible()) this->registerFrontendNetwork(net);
return net; return net;
} }
@@ -391,137 +385,46 @@ void NMWirelessDevice::registerFrontendNetwork(NMWirelessNetwork* net) {
frontendNet->bindableSignalStrength().setBinding(translateSignal); frontendNet->bindableSignalStrength().setBinding(translateSignal);
frontendNet->bindableConnected().setBinding(translateState); frontendNet->bindableConnected().setBinding(translateState);
frontendNet->bindableKnown().setBinding([net]() { return net->known(); }); frontendNet->bindableKnown().setBinding([net]() { return net->known(); });
frontendNet->bindableNmReason().setBinding([net]() { return net->reason(); });
frontendNet->bindableSecurity().setBinding([net]() { return net->security(); }); frontendNet->bindableSecurity().setBinding([net]() { return net->security(); });
frontendNet->bindableState().setBinding([net]() { frontendNet->bindableState().setBinding([net]() {
return static_cast<ConnectionState::Enum>(net->state()); return static_cast<NetworkState::Enum>(net->state());
});
QObject::connect(net, &NMWirelessNetwork::reasonChanged, this, [net, frontendNet]() {
if (net->reason() == NMConnectionStateReason::DeviceDisconnected) {
auto deviceReason = net->deviceFailReason();
if (deviceReason == NMDeviceStateReason::NoSecrets)
emit frontendNet->connectionFailed(ConnectionFailReason::NoSecrets);
if (deviceReason == NMDeviceStateReason::SupplicantDisconnect)
emit frontendNet->connectionFailed(ConnectionFailReason::WifiClientDisconnected);
if (deviceReason == NMDeviceStateReason::SupplicantFailed)
emit frontendNet->connectionFailed(ConnectionFailReason::WifiClientFailed);
if (deviceReason == NMDeviceStateReason::SupplicantTimeout)
emit frontendNet->connectionFailed(ConnectionFailReason::WifiAuthTimeout);
if (deviceReason == NMDeviceStateReason::SsidNotFound)
emit frontendNet->connectionFailed(ConnectionFailReason::WifiNetworkLost);
}
}); });
QObject::connect(frontendNet, &WifiNetwork::requestConnect, this, [this, net]() { QObject::connect(frontendNet, &WifiNetwork::requestConnect, this, [this, net]() {
if (net->referenceSettings()) { if (net->referenceConnection()) {
emit this->activateConnection( emit this->activateConnection(
QDBusObjectPath(net->referenceSettings()->path()), QDBusObjectPath(net->referenceConnection()->path()),
QDBusObjectPath(this->path()) QDBusObjectPath(this->path())
); );
return; return;
} }
if (net->referenceAp()) { if (net->referenceAp()) {
emit this->addAndActivateConnection( emit this->addAndActivateConnection(
NMSettingsMap(), ConnectionSettingsMap(),
QDBusObjectPath(this->path()), QDBusObjectPath(this->path()),
QDBusObjectPath(net->referenceAp()->path()) QDBusObjectPath(net->referenceAp()->path())
); );
return;
} }
qCInfo(logNetworkManager) << "Failed to connect to"
<< this->path() + ": The network disappeared.";
}); });
QObject::connect( QObject::connect(
frontendNet, frontendNet,
&WifiNetwork::requestConnectWithPsk, &WifiNetwork::requestDisconnect,
this, this,
[this, net](const QString& psk) { &NMWirelessDevice::disconnect
NMSettingsMap settings;
settings["802-11-wireless-security"]["psk"] = psk;
if (const QPointer<NMSettings> ref = net->referenceSettings()) {
auto* call = ref->updateSettings(settings);
QObject::connect(
call,
&QDBusPendingCallWatcher::finished,
this,
[this, ref](QDBusPendingCallWatcher* call) {
const QDBusPendingReply<> reply = *call;
if (reply.isError()) {
qCInfo(logNetworkManager)
<< "Failed to write PSK for" << this->path() + ":" << reply.error().message();
} else {
if (!ref) {
qCInfo(logNetworkManager) << "Failed to connectWithPsk to"
<< this->path() + ": The settings disappeared.";
} else {
emit this->activateConnection(
QDBusObjectPath(ref->path()),
QDBusObjectPath(this->path())
);
}
}
delete call;
}
);
return;
}
if (net->referenceAp()) {
emit this->addAndActivateConnection(
settings,
QDBusObjectPath(this->path()),
QDBusObjectPath(net->referenceAp()->path())
);
return;
}
qCInfo(logNetworkManager) << "Failed to connectWithPsk to"
<< this->path() + ": The network disappeared.";
}
); );
QObject::connect(
frontendNet,
&WifiNetwork::requestConnectWithSettings,
this,
[this](NMSettings* settings) {
if (settings) {
emit this->activateConnection(
QDBusObjectPath(settings->path()),
QDBusObjectPath(this->path())
);
return;
}
qCInfo(logNetworkManager) << "Failed to connectWithSettings to"
<< this->path() + ": The provided settings no longer exist.";
}
);
QObject::connect(
net,
&NMWirelessNetwork::visibilityChanged,
this,
[this, frontendNet](bool visible) {
if (visible) this->networkAdded(frontendNet);
else this->networkRemoved(frontendNet);
}
);
// clang-format off
QObject::connect(frontendNet, &WifiNetwork::requestDisconnect, this, &NMWirelessDevice::disconnect);
QObject::connect(frontendNet, &WifiNetwork::requestForget, net, &NMWirelessNetwork::forget); QObject::connect(frontendNet, &WifiNetwork::requestForget, net, &NMWirelessNetwork::forget);
QObject::connect(net, &NMWirelessNetwork::settingsAdded, frontendNet, &WifiNetwork::settingsAdded);
QObject::connect(net, &NMWirelessNetwork::settingsRemoved, frontendNet, &WifiNetwork::settingsRemoved);
// clang-format on
this->mFrontendNetworks.insert(ssid, frontendNet); this->mFrontendNetworks.insert(ssid, frontendNet);
if (net->visible()) emit this->networkAdded(frontendNet); emit this->networkAdded(frontendNet);
} }
void NMWirelessDevice::removeFrontendNetwork(NMWirelessNetwork* net) { void NMWirelessDevice::removeFrontendNetwork(NMWirelessNetwork* net) {
auto* frontendNet = this->mFrontendNetworks.take(net->ssid()); auto* frontendNet = this->mFrontendNetworks.take(net->ssid());
if (frontendNet) { if (frontendNet) {
if (net->visible()) emit this->networkRemoved(frontendNet); emit this->networkRemoved(frontendNet);
frontendNet->deleteLater(); frontendNet->deleteLater();
} }
} }
+26 -33
View File
@@ -9,11 +9,10 @@
#include "../wifi.hpp" #include "../wifi.hpp"
#include "accesspoint.hpp" #include "accesspoint.hpp"
#include "active_connection.hpp" #include "connection.hpp"
#include "dbus_nm_wireless.h" #include "dbus_nm_wireless.h"
#include "device.hpp" #include "device.hpp"
#include "enums.hpp" #include "enums.hpp"
#include "settings.hpp"
namespace qs::dbus { namespace qs::dbus {
template <> template <>
@@ -33,7 +32,7 @@ struct DBusDataTransform<QDateTime> {
} // namespace qs::dbus } // namespace qs::dbus
namespace qs::network { namespace qs::network {
// NMWirelessNetwork aggregates all related NMActiveConnection, NMAccessPoint, and NMSettings objects. // NMWirelessNetwork aggregates all related NMActiveConnection, NMAccessPoint, and NMConnectionSetting objects.
class NMWirelessNetwork: public QObject { class NMWirelessNetwork: public QObject {
Q_OBJECT; Q_OBJECT;
@@ -41,51 +40,46 @@ public:
explicit NMWirelessNetwork(QString ssid, QObject* parent = nullptr); explicit NMWirelessNetwork(QString ssid, QObject* parent = nullptr);
void addAccessPoint(NMAccessPoint* ap); void addAccessPoint(NMAccessPoint* ap);
void addSettings(NMSettings* settings); void addConnection(NMConnectionSettings* conn);
void addActiveConnection(NMActiveConnection* active); void addActiveConnection(NMActiveConnection* active);
void forget(); void forget();
// clang-format off [[nodiscard]] QString ssid() const { return this->mSsid; };
[[nodiscard]] QString ssid() const { return this->mSsid; } [[nodiscard]] quint8 signalStrength() const { return this->bSignalStrength; };
[[nodiscard]] quint8 signalStrength() const { return this->bSignalStrength; } [[nodiscard]] WifiSecurityType::Enum security() const { return this->bSecurity; };
[[nodiscard]] WifiSecurityType::Enum security() const { return this->bSecurity; } [[nodiscard]] NMConnectionState::Enum state() const { return this->bState; };
[[nodiscard]] NMConnectionState::Enum state() const { return this->bState; } [[nodiscard]] bool known() const { return this->bKnown; };
[[nodiscard]] bool known() const { return this->bKnown; } [[nodiscard]] NMConnectionStateReason::Enum reason() const { return this->bReason; };
[[nodiscard]] NMConnectionStateReason::Enum reason() const { return this->bReason; } [[nodiscard]] NMAccessPoint* referenceAp() const { return this->mReferenceAp; };
QBindable<NMDeviceStateReason::Enum> bindableDeviceFailReason() { return &this->bDeviceFailReason; } [[nodiscard]] NMConnectionSettings* referenceConnection() const { return this->mReferenceConn; };
[[nodiscard]] NMDeviceStateReason::Enum deviceFailReason() const { return this->bDeviceFailReason; } [[nodiscard]] QList<NMAccessPoint*> accessPoints() const { return this->mAccessPoints.values(); };
[[nodiscard]] NMAccessPoint* referenceAp() const { return this->mReferenceAp; } [[nodiscard]] QList<NMConnectionSettings*> connections() const {
[[nodiscard]] QList<NMAccessPoint*> accessPoints() const { return this->mAccessPoints.values(); } return this->mConnections.values();
[[nodiscard]] QList<NMSettings*> settings() const { return this->mSettings.values(); } }
[[nodiscard]] NMSettings* referenceSettings() const { return this->mReferenceSettings; } [[nodiscard]] QBindable<QString> bindableActiveApPath() { return &this->bActiveApPath; };
QBindable<QString> bindableActiveApPath() { return &this->bActiveApPath; } [[nodiscard]] QBindable<bool> bindableVisible() { return &this->bVisible; };
QBindable<bool> bindableVisible() { return &this->bVisible; } [[nodiscard]] bool visible() const { return this->bVisible; };
bool visible() const { return this->bVisible; }
// clang-format on
signals: signals:
void disappeared(); void disappeared();
void settingsAdded(NMSettings* settings);
void settingsRemoved(NMSettings* settings);
void visibilityChanged(bool visible); void visibilityChanged(bool visible);
void signalStrengthChanged(quint8 signal); void signalStrengthChanged(quint8 signal);
void stateChanged(NMConnectionState::Enum state); void stateChanged(NMConnectionState::Enum state);
void knownChanged(bool known); void knownChanged(bool known);
void securityChanged(WifiSecurityType::Enum security); void securityChanged(WifiSecurityType::Enum security);
void reasonChanged(NMConnectionStateReason::Enum reason); void reasonChanged(NMConnectionStateReason::Enum reason);
void deviceFailReasonChanged(NMDeviceStateReason::Enum reason);
void capabilitiesChanged(NMWirelessCapabilities::Enum caps); void capabilitiesChanged(NMWirelessCapabilities::Enum caps);
void activeApPathChanged(QString path); void activeApPathChanged(QString path);
private: private:
void updateReferenceAp(); void updateReferenceAp();
void updateReferenceSettings(); void updateReferenceConnection();
QString mSsid; QString mSsid;
QHash<QString, NMAccessPoint*> mAccessPoints; QHash<QString, NMAccessPoint*> mAccessPoints;
QHash<QString, NMSettings*> mSettings; QHash<QString, NMConnectionSettings*> mConnections;
NMAccessPoint* mReferenceAp = nullptr; NMAccessPoint* mReferenceAp = nullptr;
NMSettings* mReferenceSettings = nullptr; NMConnectionSettings* mReferenceConn = nullptr;
NMActiveConnection* mActiveConnection = nullptr; NMActiveConnection* mActiveConnection = nullptr;
// clang-format off // clang-format off
@@ -94,7 +88,6 @@ private:
Q_OBJECT_BINDABLE_PROPERTY(NMWirelessNetwork, WifiSecurityType::Enum, bSecurity, &NMWirelessNetwork::securityChanged); Q_OBJECT_BINDABLE_PROPERTY(NMWirelessNetwork, WifiSecurityType::Enum, bSecurity, &NMWirelessNetwork::securityChanged);
Q_OBJECT_BINDABLE_PROPERTY(NMWirelessNetwork, NMConnectionStateReason::Enum, bReason, &NMWirelessNetwork::reasonChanged); Q_OBJECT_BINDABLE_PROPERTY(NMWirelessNetwork, NMConnectionStateReason::Enum, bReason, &NMWirelessNetwork::reasonChanged);
Q_OBJECT_BINDABLE_PROPERTY(NMWirelessNetwork, NMConnectionState::Enum, bState, &NMWirelessNetwork::stateChanged); Q_OBJECT_BINDABLE_PROPERTY(NMWirelessNetwork, NMConnectionState::Enum, bState, &NMWirelessNetwork::stateChanged);
Q_OBJECT_BINDABLE_PROPERTY(NMWirelessNetwork, NMDeviceStateReason::Enum, bDeviceFailReason, &NMWirelessNetwork::deviceFailReasonChanged);
Q_OBJECT_BINDABLE_PROPERTY(NMWirelessNetwork, quint8, bSignalStrength, &NMWirelessNetwork::signalStrengthChanged); Q_OBJECT_BINDABLE_PROPERTY(NMWirelessNetwork, quint8, bSignalStrength, &NMWirelessNetwork::signalStrengthChanged);
Q_OBJECT_BINDABLE_PROPERTY(NMWirelessNetwork, QString, bActiveApPath, &NMWirelessNetwork::activeApPathChanged); Q_OBJECT_BINDABLE_PROPERTY(NMWirelessNetwork, QString, bActiveApPath, &NMWirelessNetwork::activeApPathChanged);
// clang-format on // clang-format on
@@ -110,10 +103,10 @@ public:
explicit NMWirelessDevice(const QString& path, QObject* parent = nullptr); explicit NMWirelessDevice(const QString& path, QObject* parent = nullptr);
[[nodiscard]] bool isValid() const override; [[nodiscard]] bool isValid() const override;
[[nodiscard]] NMWirelessCapabilities::Enum capabilities() { return this->bCapabilities; } [[nodiscard]] NMWirelessCapabilities::Enum capabilities() { return this->bCapabilities; };
[[nodiscard]] const QDBusObjectPath& activeApPath() { return this->bActiveAccessPoint; } [[nodiscard]] const QDBusObjectPath& activeApPath() { return this->bActiveAccessPoint; };
[[nodiscard]] NM80211Mode::Enum mode() { return this->bMode; } [[nodiscard]] NM80211Mode::Enum mode() { return this->bMode; };
[[nodiscard]] QBindable<bool> bindableScanning() { return &this->bScanning; } [[nodiscard]] QBindable<bool> bindableScanning() { return &this->bScanning; };
signals: signals:
void accessPointLoaded(NMAccessPoint* ap); void accessPointLoaded(NMAccessPoint* ap);
@@ -130,7 +123,7 @@ private slots:
void onAccessPointAdded(const QDBusObjectPath& path); void onAccessPointAdded(const QDBusObjectPath& path);
void onAccessPointRemoved(const QDBusObjectPath& path); void onAccessPointRemoved(const QDBusObjectPath& path);
void onAccessPointLoaded(NMAccessPoint* ap); void onAccessPointLoaded(NMAccessPoint* ap);
void onSettingsLoaded(NMSettings* settings); void onConnectionLoaded(NMConnectionSettings* conn);
void onActiveConnectionLoaded(NMActiveConnection* active); void onActiveConnectionLoaded(NMActiveConnection* active);
void onScanTimeout(); void onScanTimeout();
void onScanningChanged(bool scanning); void onScanningChanged(bool scanning);
+139 -344
View File
@@ -5,356 +5,151 @@ import Quickshell
import Quickshell.Widgets import Quickshell.Widgets
import Quickshell.Networking import Quickshell.Networking
Scope { FloatingWindow {
Component { color: contentItem.palette.window
id: editorComponent
FloatingWindow {
id: editorWindow
required property var nmSettings
color: contentItem.palette.window
Component.onCompleted: editorArea.text = JSON.stringify(nmSettings.read(), null, 2) ColumnLayout {
anchors.fill: parent
anchors.margins: 5
ColumnLayout { Column {
anchors.fill: parent Layout.fillWidth: true
anchors.margins: 10 RowLayout {
Label {
text: "WiFi"
font.bold: true
font.pointSize: 12
}
CheckBox {
text: "Software"
checked: Networking.wifiEnabled
onClicked: Networking.wifiEnabled = !Networking.wifiEnabled
}
CheckBox {
enabled: false
text: "Hardware"
checked: Networking.wifiHardwareEnabled
}
}
}
Label { ListView {
text: "Editing " + nmSettings?.id + " (" + nmSettings?.uuid + ")" clip: true
font.bold: true Layout.fillWidth: true
font.pointSize: 12 Layout.fillHeight: true
} model: Networking.devices
ScrollView { delegate: WrapperRectangle {
Layout.fillWidth: true width: parent.width
Layout.fillHeight: true color: "transparent"
TextArea { border.color: palette.button
id: editorArea border.width: 1
wrapMode: TextEdit.Wrap margin: 5
selectByMouse: true
}
}
RowLayout { ColumnLayout {
Layout.fillWidth: true RowLayout {
Label { Label { text: modelData.name; font.bold: true }
id: statusLabel Label { text: modelData.address }
Layout.fillWidth: true Label { text: `(Type: ${DeviceType.toString(modelData.type)})` }
color: palette.placeholderText }
} RowLayout {
Button { Label {
text: "Reload" text: DeviceConnectionState.toString(modelData.state)
onClicked: { color: modelData.connected ? palette.link : palette.placeholderText
editorArea.text = JSON.stringify(editorWindow.nmSettings.read(), null, 2); }
statusLabel.text = "Reloaded"; Label {
} visible: Networking.backend == NetworkBackendType.NetworkManager && (modelData.state == DeviceConnectionState.Connecting || modelData.state == DeviceConnectionState.Disconnecting)
} text: `(${NMDeviceState.toString(modelData.nmState)})`
Button { }
text: "Save" Button {
onClicked: { visible: modelData.state == DeviceConnectionState.Connected
try { text: "Disconnect"
const parsed = JSON.parse(editorArea.text); onClicked: modelData.disconnect()
nmSettings.write(parsed); }
statusLabel.text = "Saved"; CheckBox {
} catch (e) { text: "Autoconnect"
statusLabel.text = "Parse error: " + e.message; checked: modelData.autoconnect
} onClicked: modelData.autoconnect = !modelData.autoconnect
} }
} Label {
Button { text: `Mode: ${WifiDeviceMode.toString(modelData.mode)}`
text: "Close" visible: modelData.type == DeviceType.Wifi
onClicked: { }
editorArea.focus = false; CheckBox {
editorWindow.destroy(); text: "Scanner"
} checked: modelData.scannerEnabled
} onClicked: modelData.scannerEnabled = !modelData.scannerEnabled
} visible: modelData.type === DeviceType.Wifi
} }
} }
}
FloatingWindow { Repeater {
color: contentItem.palette.window Layout.fillWidth: true
model: {
if (modelData.type !== DeviceType.Wifi) return []
return [...modelData.networks.values].sort((a, b) => {
if (a.connected !== b.connected) {
return b.connected - a.connected
}
return b.signalStrength - a.signalStrength
})
}
ColumnLayout { WrapperRectangle {
anchors.fill: parent Layout.fillWidth: true
anchors.margins: 5 color: modelData.connected ? palette.highlight : palette.button
border.color: palette.mid
border.width: 1
margin: 5
ColumnLayout { RowLayout {
Label { ColumnLayout {
text: `Networking (${NetworkBackendType.toString(Networking.backend)} backend)` Layout.fillWidth: true
font.bold: true RowLayout {
font.pointSize: 12 Label { text: modelData.name; font.bold: true }
} Label {
RowLayout { text: modelData.known ? "Known" : ""
Label { color: palette.placeholderText
text: `Connectivity` }
font.bold: true }
} RowLayout {
Label { Label {
text: `${NetworkConnectivity.toString(Networking.connectivity)}` text: `Security: ${WifiSecurityType.toString(modelData.security)}`
visible: Networking.canCheckConnectivity color: palette.placeholderText
} }
Button { Label {
text: "Re-check" text: `| Signal strength: ${Math.round(modelData.signalStrength*100)}%`
visible: Networking.canCheckConnectivity && Networking.connectivityCheckEnabled color: palette.placeholderText
onClicked: Networking.checkConnectivity() }
} }
CheckBox { Label {
text: "Checking enabled" visible: Networking.backend == NetworkBackendType.NetworkManager && (modelData.nmReason != NMConnectionStateReason.Unknown && modelData.nmReason != NMConnectionStateReason.None)
checked: Networking.connectivityCheckEnabled text: `Connection change reason: ${NMConnectionStateReason.toString(modelData.nmReason)}`
onClicked: Networking.connectivityCheckEnabled = !Networking.connectivityCheckEnabled }
visible: Networking.canCheckConnectivity }
} RowLayout {
CheckBox { Layout.alignment: Qt.AlignRight
enabled: false Button {
text: "Supported" text: "Connect"
checked: Networking.canCheckConnectivity onClicked: modelData.connect()
} visible: !modelData.connected
} }
} Button {
text: "Disconnect"
Column { onClicked: modelData.disconnect()
Layout.fillWidth: true visible: modelData.connected
RowLayout { }
Label { Button {
text: "WiFi" text: "Forget"
font.bold: true onClicked: modelData.forget()
} visible: modelData.known
CheckBox { }
text: "Software" }
checked: Networking.wifiEnabled }
onClicked: Networking.wifiEnabled = !Networking.wifiEnabled }
} }
CheckBox { }
enabled: false }
text: "Hardware" }
checked: Networking.wifiHardwareEnabled }
}
}
}
ListView {
clip: true
Layout.fillWidth: true
Layout.fillHeight: true
model: Networking.devices
delegate: WrapperRectangle {
width: parent.width
color: "transparent"
border.color: palette.button
border.width: 1
margin: 5
ColumnLayout {
RowLayout {
Label {
text: modelData.name
font.bold: true
}
Label {
text: modelData.address
}
Label {
text: `(Type: ${DeviceType.toString(modelData.type)})`
}
CheckBox {
text: `Managed`
checked: modelData.nmManaged
onClicked: modelData.nmManaged = !modelData.nmManaged
}
}
RowLayout {
Label {
text: ConnectionState.toString(modelData.state)
color: modelData.connected ? palette.link : palette.placeholderText
}
Button {
visible: modelData.state == ConnectionState.Connected
text: "Disconnect"
onClicked: modelData.disconnect()
}
CheckBox {
text: "Autoconnect"
checked: modelData.autoconnect
onClicked: modelData.autoconnect = !modelData.autoconnect
}
Label {
text: `Mode: ${WifiDeviceMode.toString(modelData.mode)}`
visible: modelData.type == DeviceType.Wifi
}
CheckBox {
text: "Scanner"
checked: modelData.scannerEnabled
onClicked: modelData.scannerEnabled = !modelData.scannerEnabled
visible: modelData.type === DeviceType.Wifi
}
}
Repeater {
Layout.fillWidth: true
model: ScriptModel {
values: [...modelData.networks.values].sort((a, b) => {
if (a.connected !== b.connected) {
return b.connected - a.connected;
}
return b.signalStrength - a.signalStrength;
})
}
WrapperRectangle {
property var chosenSettings: {
const settings = modelData.nmSettings;
if (!settings || settings.length === 0) {
return null;
}
if (settings.length === 1) {
return settings[0];
}
return settings[settingsComboBox.currentIndex];
}
Connections {
target: modelData
function onConnectionFailed(reason) {
failLoader.sourceComponent = failComponent;
failLoader.item.failReason = reason;
}
function onStateChanged() {
if (modelData.state == ConnectionState.Connecting) {
failLoader.sourceComponent = null;
}
}
}
Component {
id: failComponent
RowLayout {
property var failReason
Label {
text: ConnectionFailReason.toString(failReason)
}
RowLayout {
TextField {
id: pskField
placeholderText: "PSK"
}
Button {
text: "Set"
visible: pskField.visible
onClicked: {
modelData.connectWithPsk(pskField.text);
failLoader.sourceComponent = null;
}
}
visible: modelData.security === WifiSecurityType.WpaPsk || modelData.security === WifiSecurityType.Wpa2Psk || modelData.security === WifiSecurityType.Sae
}
Button {
text: "Close"
onClicked: failLoader.sourceComponent = null
}
}
}
Layout.fillWidth: true
color: modelData.connected ? palette.highlight : palette.button
border.color: palette.mid
border.width: 1
margin: 5
RowLayout {
ColumnLayout {
Layout.fillWidth: true
RowLayout {
Label {
text: modelData.name
font.bold: true
}
Label {
text: modelData.known ? "Known" : ""
color: palette.placeholderText
}
}
RowLayout {
Label {
text: `Security: ${WifiSecurityType.toString(modelData.security)}`
color: palette.placeholderText
}
Label {
text: `| Signal strength: ${Math.round(modelData.signalStrength * 100)}%`
color: palette.placeholderText
}
}
}
ColumnLayout {
Layout.alignment: Qt.AlignRight
RowLayout {
Layout.alignment: Qt.AlignRight
BusyIndicator {
implicitHeight: 30
implicitWidth: 30
running: modelData.stateChanging
visible: modelData.stateChanging
}
Label {
text: ConnectionState.toString(modelData.state)
color: modelData.connected ? palette.link : palette.placeholderText
}
RowLayout {
Label {
text: "Choose settings:"
}
ComboBox {
id: settingsComboBox
model: modelData.nmSettings.map(s => s?.read()?.connection?.id)
currentIndex: 0
}
visible: modelData.nmSettings.length > 1
}
Button {
text: "Connect"
onClicked: {
if (chosenSettings)
modelData.connectWithSettings(chosenSettings);
else
modelData.connect();
}
visible: !modelData.connected
}
Button {
text: "Disconnect"
onClicked: modelData.disconnect()
visible: modelData.connected
}
Button {
text: "Forget"
onClicked: modelData.forget()
visible: modelData.known
}
Button {
text: "Edit"
visible: modelData.known
onClicked: {
if (chosenSettings)
editorComponent.createObject(null, {
nmSettings: chosenSettings
});
}
}
}
Loader {
id: failLoader
Layout.alignment: Qt.AlignRight
visible: sourceComponent !== null
}
}
}
}
}
}
}
}
}
}
} }
+76 -12
View File
@@ -6,35 +6,99 @@
#include <qloggingcategory.h> #include <qloggingcategory.h>
#include <qobject.h> #include <qobject.h>
#include <qstring.h> #include <qstring.h>
#include <qtmetamacros.h>
#include "../core/logcat.hpp" #include "../core/logcat.hpp"
#include "device.hpp" #include "device.hpp"
#include "enums.hpp"
#include "network.hpp" #include "network.hpp"
namespace qs::network { namespace qs::network {
namespace { namespace {
QS_LOGGING_CATEGORY(logWifiNetwork, "quickshell.wifinetwork", QtWarningMsg); QS_LOGGING_CATEGORY(logWifi, "quickshell.network.wifi", QtWarningMsg);
} // namespace
QString WifiSecurityType::toString(WifiSecurityType::Enum type) {
switch (type) {
case Unknown: return QStringLiteral("Unknown");
case Wpa3SuiteB192: return QStringLiteral("WPA3 Suite B 192-bit");
case Sae: return QStringLiteral("WPA3");
case Wpa2Eap: return QStringLiteral("WPA2 Enterprise");
case Wpa2Psk: return QStringLiteral("WPA2");
case WpaEap: return QStringLiteral("WPA Enterprise");
case WpaPsk: return QStringLiteral("WPA");
case StaticWep: return QStringLiteral("WEP");
case DynamicWep: return QStringLiteral("Dynamic WEP");
case Leap: return QStringLiteral("LEAP");
case Owe: return QStringLiteral("OWE");
case Open: return QStringLiteral("Open");
default: return QStringLiteral("Unknown");
}
} }
QString WifiDeviceMode::toString(WifiDeviceMode::Enum mode) {
switch (mode) {
case Unknown: return QStringLiteral("Unknown");
case AdHoc: return QStringLiteral("Ad-Hoc");
case Station: return QStringLiteral("Station");
case AccessPoint: return QStringLiteral("Access Point");
case Mesh: return QStringLiteral("Mesh");
default: return QStringLiteral("Unknown");
};
}
QString NMConnectionStateReason::toString(NMConnectionStateReason::Enum reason) {
switch (reason) {
case Unknown: return QStringLiteral("Unknown");
case None: return QStringLiteral("No reason");
case UserDisconnected: return QStringLiteral("User disconnection");
case DeviceDisconnected:
return QStringLiteral("The device the connection was using was disconnected.");
case ServiceStopped:
return QStringLiteral("The service providing the VPN connection was stopped.");
case IpConfigInvalid:
return QStringLiteral("The IP config of the active connection was invalid.");
case ConnectTimeout:
return QStringLiteral("The connection attempt to the VPN service timed out.");
case ServiceStartTimeout:
return QStringLiteral(
"A timeout occurred while starting the service providing the VPN connection."
);
case ServiceStartFailed:
return QStringLiteral("Starting the service providing the VPN connection failed.");
case NoSecrets: return QStringLiteral("Necessary secrets for the connection were not provided.");
case LoginFailed: return QStringLiteral("Authentication to the server failed.");
case ConnectionRemoved:
return QStringLiteral("Necessary secrets for the connection were not provided.");
case DependencyFailed:
return QStringLiteral("Master connection of this connection failed to activate.");
case DeviceRealizeFailed: return QStringLiteral("Could not create the software device link.");
case DeviceRemoved: return QStringLiteral("The device this connection depended on disappeared.");
default: return QStringLiteral("Unknown");
};
};
WifiNetwork::WifiNetwork(QString ssid, QObject* parent): Network(std::move(ssid), parent) {}; WifiNetwork::WifiNetwork(QString ssid, QObject* parent): Network(std::move(ssid), parent) {};
void WifiNetwork::connectWithPsk(const QString& psk) { void WifiNetwork::connect() {
if (this->bConnected) { if (this->bConnected) {
qCCritical(logWifiNetwork) << this << "is already connected."; qCCritical(logWifi) << this << "is already connected.";
return; return;
} }
if (this->bSecurity != WifiSecurityType::WpaPsk && this->bSecurity != WifiSecurityType::Wpa2Psk
&& this->bSecurity != WifiSecurityType::Sae) this->requestConnect();
{
qCCritical(logWifiNetwork) << this << "has the wrong security type for a PSK.";
return;
}
emit this->requestConnectWithPsk(psk);
} }
void WifiNetwork::disconnect() {
if (!this->bConnected) {
qCCritical(logWifi) << this << "is not currently connected";
return;
}
this->requestDisconnect();
}
void WifiNetwork::forget() { this->requestForget(); }
WifiDevice::WifiDevice(QObject* parent): NetworkDevice(DeviceType::Wifi, parent) {}; WifiDevice::WifiDevice(QObject* parent): NetworkDevice(DeviceType::Wifi, parent) {};
void WifiDevice::setScannerEnabled(bool enabled) { void WifiDevice::setScannerEnabled(bool enabled) {
+105 -18
View File
@@ -6,15 +6,90 @@
#include <qtmetamacros.h> #include <qtmetamacros.h>
#include <qtypes.h> #include <qtypes.h>
#include "../core/doc.hpp"
#include "../core/model.hpp" #include "../core/model.hpp"
#include "device.hpp" #include "device.hpp"
#include "enums.hpp"
#include "network.hpp" #include "network.hpp"
namespace qs::network { namespace qs::network {
///! WiFi subtype of @@Network. ///! The security type of a wifi network.
class WifiSecurityType: public QObject {
Q_OBJECT;
QML_ELEMENT;
QML_SINGLETON;
public:
enum Enum : quint8 {
Wpa3SuiteB192 = 0,
Sae = 1,
Wpa2Eap = 2,
Wpa2Psk = 3,
WpaEap = 4,
WpaPsk = 5,
StaticWep = 6,
DynamicWep = 7,
Leap = 8,
Owe = 9,
Open = 10,
Unknown = 11,
};
Q_ENUM(Enum);
Q_INVOKABLE static QString toString(WifiSecurityType::Enum type);
};
///! The 802.11 mode of a wifi device.
class WifiDeviceMode: public QObject {
Q_OBJECT;
QML_ELEMENT;
QML_SINGLETON;
public:
enum Enum : quint8 {
/// The device is part of an Ad-Hoc network without a central access point.
AdHoc = 0,
/// The device is a station that can connect to networks.
Station = 1,
/// The device is a local hotspot/access point.
AccessPoint = 2,
/// The device is an 802.11s mesh point.
Mesh = 3,
/// The device mode is unknown.
Unknown = 4,
};
Q_ENUM(Enum);
Q_INVOKABLE static QString toString(WifiDeviceMode::Enum mode);
};
///! NetworkManager-specific reason for a WifiNetworks connection state.
/// In sync with https://networkmanager.dev/docs/api/latest/nm-dbus-types.html#NMActiveConnectionStateReason.
class NMConnectionStateReason: public QObject {
Q_OBJECT;
QML_ELEMENT;
QML_SINGLETON;
public:
enum Enum : quint8 {
Unknown = 0,
None = 1,
UserDisconnected = 2,
DeviceDisconnected = 3,
ServiceStopped = 4,
IpConfigInvalid = 5,
ConnectTimeout = 6,
ServiceStartTimeout = 7,
ServiceStartFailed = 8,
NoSecrets = 9,
LoginFailed = 10,
ConnectionRemoved = 11,
DependencyFailed = 12,
DeviceRealizeFailed = 13,
DeviceRemoved = 14
};
Q_ENUM(Enum);
Q_INVOKABLE static QString toString(NMConnectionStateReason::Enum reason);
};
///! An available wifi network.
class WifiNetwork: public Network { class WifiNetwork: public Network {
Q_OBJECT; Q_OBJECT;
QML_ELEMENT; QML_ELEMENT;
@@ -22,46 +97,58 @@ class WifiNetwork: public Network {
// clang-format off // clang-format off
/// The current signal strength of the network, from 0.0 to 1.0. /// The current signal strength of the network, from 0.0 to 1.0.
Q_PROPERTY(qreal signalStrength READ default NOTIFY signalStrengthChanged BINDABLE bindableSignalStrength); Q_PROPERTY(qreal signalStrength READ default NOTIFY signalStrengthChanged BINDABLE bindableSignalStrength);
/// True if the wifi network has known connection settings saved.
Q_PROPERTY(bool known READ default NOTIFY knownChanged BINDABLE bindableKnown);
/// The security type of the wifi network. /// The security type of the wifi network.
Q_PROPERTY(WifiSecurityType::Enum security READ default NOTIFY securityChanged BINDABLE bindableSecurity); Q_PROPERTY(WifiSecurityType::Enum security READ default NOTIFY securityChanged BINDABLE bindableSecurity);
/// A specific reason for the connection state when the backend is NetworkManager.
Q_PROPERTY(NMConnectionStateReason::Enum nmReason READ default NOTIFY nmReasonChanged BINDABLE bindableNmReason);
// clang-format on // clang-format on
public: public:
explicit WifiNetwork(QString ssid, QObject* parent = nullptr); explicit WifiNetwork(QString ssid, QObject* parent = nullptr);
/// Attempt to connect to the network with the given PSK. If the PSK is wrong,
/// a @@Network.connectionFailed(s) signal will be emitted with `NoSecrets`. /// Attempt to connect to the wifi network.
/// ///
/// The networking backend may store the PSK for future use with @@Network.connect(). /// > [!WARNING] Quickshell does not yet provide a NetworkManager authentication agent,
/// As such, calling that function first is recommended to avoid having to show a /// > meaning another agent will need to be active to enter passwords for unsaved networks.
/// prompt if not required. Q_INVOKABLE void connect();
/// /// Disconnect from the wifi network.
/// > [!NOTE] PSKs should only be provided when the @@security is one of Q_INVOKABLE void disconnect();
/// > `WpaPsk`, `Wpa2Psk`, or `Sae`. /// Forget all connection settings for this wifi network.
Q_INVOKABLE void connectWithPsk(const QString& psk); Q_INVOKABLE void forget();
QBindable<qreal> bindableSignalStrength() { return &this->bSignalStrength; } QBindable<qreal> bindableSignalStrength() { return &this->bSignalStrength; }
QBindable<bool> bindableKnown() { return &this->bKnown; }
QBindable<NMConnectionStateReason::Enum> bindableNmReason() { return &this->bNmReason; }
QBindable<WifiSecurityType::Enum> bindableSecurity() { return &this->bSecurity; } QBindable<WifiSecurityType::Enum> bindableSecurity() { return &this->bSecurity; }
signals: signals:
QSDOC_HIDE void requestConnectWithPsk(QString psk); void requestConnect();
void requestDisconnect();
void requestForget();
void signalStrengthChanged(); void signalStrengthChanged();
void knownChanged();
void securityChanged(); void securityChanged();
void nmReasonChanged();
private: private:
// clang-format off // clang-format off
Q_OBJECT_BINDABLE_PROPERTY(WifiNetwork, qreal, bSignalStrength, &WifiNetwork::signalStrengthChanged); Q_OBJECT_BINDABLE_PROPERTY(WifiNetwork, qreal, bSignalStrength, &WifiNetwork::signalStrengthChanged);
Q_OBJECT_BINDABLE_PROPERTY(WifiNetwork, bool, bKnown, &WifiNetwork::knownChanged);
Q_OBJECT_BINDABLE_PROPERTY(WifiNetwork, NMConnectionStateReason::Enum, bNmReason, &WifiNetwork::nmReasonChanged);
Q_OBJECT_BINDABLE_PROPERTY(WifiNetwork, WifiSecurityType::Enum, bSecurity, &WifiNetwork::securityChanged); Q_OBJECT_BINDABLE_PROPERTY(WifiNetwork, WifiSecurityType::Enum, bSecurity, &WifiNetwork::securityChanged);
// clang-format on // clang-format on
}; };
///! WiFi variant of a @@NetworkDevice. ///! Wireless variant of a NetworkDevice.
class WifiDevice: public NetworkDevice { class WifiDevice: public NetworkDevice {
Q_OBJECT; Q_OBJECT;
QML_ELEMENT; QML_ELEMENT;
QML_UNCREATABLE(""); QML_UNCREATABLE("");
// clang-format off // clang-format off
/// A list of this available or connected wifi networks. /// A list of this available and connected wifi networks.
QSDOC_TYPE_OVERRIDE(ObjectModel<WifiNetwork>*); QSDOC_TYPE_OVERRIDE(ObjectModel<WifiNetwork>*);
Q_PROPERTY(UntypedObjectModel* networks READ networks CONSTANT); Q_PROPERTY(UntypedObjectModel* networks READ networks CONSTANT);
/// True when currently scanning for networks. /// True when currently scanning for networks.
@@ -77,9 +164,9 @@ public:
void networkAdded(WifiNetwork* net); void networkAdded(WifiNetwork* net);
void networkRemoved(WifiNetwork* net); void networkRemoved(WifiNetwork* net);
[[nodiscard]] ObjectModel<WifiNetwork>* networks() { return &this->mNetworks; } [[nodiscard]] ObjectModel<WifiNetwork>* networks() { return &this->mNetworks; };
QBindable<bool> bindableScannerEnabled() { return &this->bScannerEnabled; } QBindable<bool> bindableScannerEnabled() { return &this->bScannerEnabled; };
[[nodiscard]] bool scannerEnabled() const { return this->bScannerEnabled; } [[nodiscard]] bool scannerEnabled() const { return this->bScannerEnabled; };
void setScannerEnabled(bool enabled); void setScannerEnabled(bool enabled);
QBindable<WifiDeviceMode::Enum> bindableMode() { return &this->bMode; } QBindable<WifiDeviceMode::Enum> bindableMode() { return &this->bMode; }
+1 -1
View File
@@ -12,7 +12,7 @@ qt_add_qml_module(quickshell-service-greetd
install_qml_module(quickshell-service-greetd) install_qml_module(quickshell-service-greetd)
# can't be Qt::Qml because generation.hpp pulls in gui types # can't be Qt::Qml because generation.hpp pulls in gui types
target_link_libraries(quickshell-service-greetd PRIVATE Qt::Quick quickshell-core) target_link_libraries(quickshell-service-greetd PRIVATE Qt::Quick)
qs_module_pch(quickshell-service-greetd) qs_module_pch(quickshell-service-greetd)
+65 -68
View File
@@ -145,7 +145,6 @@ void GreetdConnection::setInactive() {
QString GreetdConnection::user() const { return this->mUser; } QString GreetdConnection::user() const { return this->mUser; }
void GreetdConnection::onSocketConnected() { void GreetdConnection::onSocketConnected() {
this->reader.setDevice(&this->socket);
qCDebug(logGreetd) << "Connected to greetd socket."; qCDebug(logGreetd) << "Connected to greetd socket.";
if (this->mTargetActive) { if (this->mTargetActive) {
@@ -161,84 +160,82 @@ void GreetdConnection::onSocketError(QLocalSocket::LocalSocketError error) {
} }
void GreetdConnection::onSocketReady() { void GreetdConnection::onSocketReady() {
while (true) { qint32 length = 0;
this->reader.startTransaction();
auto length = this->reader.readI32();
auto text = this->reader.readBytes(length);
if (!this->reader.commitTransaction()) return;
auto json = QJsonDocument::fromJson(text).object(); this->socket.read(reinterpret_cast<char*>(&length), sizeof(qint32));
auto type = json.value("type").toString();
qCDebug(logGreetd).noquote() << "Received greetd response:" << text; auto text = this->socket.read(length);
auto json = QJsonDocument::fromJson(text).object();
auto type = json.value("type").toString();
if (type == "success") { qCDebug(logGreetd).noquote() << "Received greetd response:" << text;
switch (this->mState) {
case GreetdState::Authenticating:
qCDebug(logGreetd) << "Authentication complete.";
this->mState = GreetdState::ReadyToLaunch;
emit this->stateChanged();
emit this->readyToLaunch();
break;
case GreetdState::Launching:
qCDebug(logGreetd) << "Target session set successfully.";
this->mState = GreetdState::Launched;
emit this->stateChanged();
emit this->launched();
if (this->mExitAfterLaunch) { if (type == "success") {
qCDebug(logGreetd) << "Quitting."; switch (this->mState) {
EngineGeneration::currentGeneration()->quit(); case GreetdState::Authenticating:
} qCDebug(logGreetd) << "Authentication complete.";
this->mState = GreetdState::ReadyToLaunch;
emit this->stateChanged();
emit this->readyToLaunch();
break;
case GreetdState::Launching:
qCDebug(logGreetd) << "Target session set successfully.";
this->mState = GreetdState::Launched;
emit this->stateChanged();
emit this->launched();
break; if (this->mExitAfterLaunch) {
default: goto unexpected; qCDebug(logGreetd) << "Quitting.";
} EngineGeneration::currentGeneration()->quit();
} else if (type == "error") {
auto errorType = json.value("error_type").toString();
auto desc = json.value("description").toString();
// Special case this error in case a session was already running.
// This cancels and restarts the session.
if (errorType == "error" && desc == "a session is already being configured") {
qCDebug(
logGreetd
) << "A session was already in progress, cancelling it and starting a new one.";
this->setActive(false);
this->setActive(true);
return;
} }
if (errorType == "auth_error") { break;
emit this->authFailure(desc); default: goto unexpected;
this->setActive(false); }
} else if (errorType == "error") { } else if (type == "error") {
qCWarning(logGreetd) << "Greetd error occurred" << desc; auto errorType = json.value("error_type").toString();
emit this->error(desc); auto desc = json.value("description").toString();
} else goto unexpected;
// errors terminate the session // Special case this error in case a session was already running.
this->setInactive(); // This cancels and restarts the session.
} else if (type == "auth_message") { if (errorType == "error" && desc == "a session is already being configured") {
auto message = json.value("auth_message").toString(); qCDebug(
auto type = json.value("auth_message_type").toString(); logGreetd
auto error = type == "error"; ) << "A session was already in progress, cancelling it and starting a new one.";
auto responseRequired = type == "visible" || type == "secret"; this->setActive(false);
auto echoResponse = type != "secret"; this->setActive(true);
return;
}
this->mResponseRequired = responseRequired; if (errorType == "auth_error") {
emit this->authMessage(message, error, responseRequired, echoResponse); emit this->authFailure(desc);
this->setActive(false);
if (!responseRequired) { } else if (errorType == "error") {
this->sendRequest({{"type", "post_auth_message_response"}}); qCWarning(logGreetd) << "Greetd error occurred" << desc;
} emit this->error(desc);
} else goto unexpected; } else goto unexpected;
continue; // errors terminate the session
unexpected: this->setInactive();
qCCritical(logGreetd) << "Received unexpected greetd response" << text; } else if (type == "auth_message") {
this->setActive(false); auto message = json.value("auth_message").toString();
} auto type = json.value("auth_message_type").toString();
auto error = type == "error";
auto responseRequired = type == "visible" || type == "secret";
auto echoResponse = type != "secret";
this->mResponseRequired = responseRequired;
emit this->authMessage(message, error, responseRequired, echoResponse);
if (!responseRequired) {
this->sendRequest({{"type", "post_auth_message_response"}});
}
} else goto unexpected;
return;
unexpected:
qCCritical(logGreetd) << "Received unexpected greetd response" << text;
this->setActive(false);
} }
void GreetdConnection::sendRequest(const QJsonObject& json) { void GreetdConnection::sendRequest(const QJsonObject& json) {
-3
View File
@@ -8,8 +8,6 @@
#include <qtmetamacros.h> #include <qtmetamacros.h>
#include <qtypes.h> #include <qtypes.h>
#include "../../core/streamreader.hpp"
///! State of the Greetd connection. ///! State of the Greetd connection.
/// See @@Greetd.state. /// See @@Greetd.state.
class GreetdState: public QObject { class GreetdState: public QObject {
@@ -76,5 +74,4 @@ private:
bool mResponseRequired = false; bool mResponseRequired = false;
QString mUser; QString mUser;
QLocalSocket socket; QLocalSocket socket;
StreamReader reader;
}; };
-3
View File
@@ -8,9 +8,6 @@
#include <qtmetamacros.h> #include <qtmetamacros.h>
#include <sys/signal.h> #include <sys/signal.h>
#include <sys/wait.h> #include <sys/wait.h>
#ifdef __FreeBSD__
#include <signal.h>
#endif
#include "../../core/logcat.hpp" #include "../../core/logcat.hpp"
#include "ipc.hpp" #include "ipc.hpp"
+4 -9
View File
@@ -143,17 +143,12 @@ void PwCore::onSync(void* data, quint32 id, qint32 seq) {
void PwCore::onError(void* data, quint32 id, qint32 /*seq*/, qint32 res, const char* message) { void PwCore::onError(void* data, quint32 id, qint32 /*seq*/, qint32 res, const char* message) {
auto* self = static_cast<PwCore*>(data); auto* self = static_cast<PwCore*>(data);
// Pipewire's documentation describes the error event as being fatal, however it isn't. if (message != nullptr) {
// We're not sure what causes these ENOENTs on device removal, presumably something in qCWarning(logLoop) << "Fatal pipewire error on object" << id << "with code" << res << message;
// the teardown sequence, but they're harmless. Attempting to handle them as a fatal } else {
// error causes unnecessary triggers for shells. qCWarning(logLoop) << "Fatal pipewire error on object" << id << "with code" << res;
if (res == -ENOENT) {
qCDebug(logLoop) << "Pipewire ENOENT on object" << id << "with code" << res << message;
return;
} }
qCWarning(logLoop) << "Pipewire error on object" << id << "with code" << res << message;
emit self->fatalError(); emit self->fatalError();
} }
+8 -42
View File
@@ -214,24 +214,13 @@ void PwDefaultTracker::setDefaultSink(PwNode* node) {
qCInfo(logDefaults) << "Default sink changed to" << node; qCInfo(logDefaults) << "Default sink changed to" << node;
if (this->mDefaultSink != nullptr) { if (this->mDefaultSink != nullptr) {
// Targeted disconnect is used because this can also be the default configured sink. QObject::disconnect(this->mDefaultSink, nullptr, this, nullptr);
QObject::disconnect(
this->mDefaultSink,
&PwBindableObject::destroying,
this,
&PwDefaultTracker::onDefaultSinkDestroyed
);
} }
this->mDefaultSink = node; this->mDefaultSink = node;
if (node != nullptr) { if (node != nullptr) {
QObject::connect( QObject::connect(node, &QObject::destroyed, this, &PwDefaultTracker::onDefaultSinkDestroyed);
node,
&PwBindableObject::destroying,
this,
&PwDefaultTracker::onDefaultSinkDestroyed
);
} }
emit this->defaultSinkChanged(); emit this->defaultSinkChanged();
@@ -255,24 +244,13 @@ void PwDefaultTracker::setDefaultSource(PwNode* node) {
qCInfo(logDefaults) << "Default source changed to" << node; qCInfo(logDefaults) << "Default source changed to" << node;
if (this->mDefaultSource != nullptr) { if (this->mDefaultSource != nullptr) {
// Targeted disconnect is used because this can also be the default configured source. QObject::disconnect(this->mDefaultSource, nullptr, this, nullptr);
QObject::disconnect(
this->mDefaultSource,
&PwBindableObject::destroying,
this,
&PwDefaultTracker::onDefaultSourceDestroyed
);
} }
this->mDefaultSource = node; this->mDefaultSource = node;
if (node != nullptr) { if (node != nullptr) {
QObject::connect( QObject::connect(node, &QObject::destroyed, this, &PwDefaultTracker::onDefaultSourceDestroyed);
node,
&PwBindableObject::destroying,
this,
&PwDefaultTracker::onDefaultSourceDestroyed
);
} }
emit this->defaultSourceChanged(); emit this->defaultSourceChanged();
@@ -296,13 +274,7 @@ void PwDefaultTracker::setDefaultConfiguredSink(PwNode* node) {
qCInfo(logDefaults) << "Default configured sink changed to" << node; qCInfo(logDefaults) << "Default configured sink changed to" << node;
if (this->mDefaultConfiguredSink != nullptr) { if (this->mDefaultConfiguredSink != nullptr) {
// Targeted disconnect is used because this can also be the default sink. QObject::disconnect(this->mDefaultConfiguredSink, nullptr, this, nullptr);
QObject::disconnect(
this->mDefaultConfiguredSink,
&PwBindableObject::destroying,
this,
&PwDefaultTracker::onDefaultConfiguredSinkDestroyed
);
} }
this->mDefaultConfiguredSink = node; this->mDefaultConfiguredSink = node;
@@ -310,7 +282,7 @@ void PwDefaultTracker::setDefaultConfiguredSink(PwNode* node) {
if (node != nullptr) { if (node != nullptr) {
QObject::connect( QObject::connect(
node, node,
&PwBindableObject::destroying, &QObject::destroyed,
this, this,
&PwDefaultTracker::onDefaultConfiguredSinkDestroyed &PwDefaultTracker::onDefaultConfiguredSinkDestroyed
); );
@@ -337,13 +309,7 @@ void PwDefaultTracker::setDefaultConfiguredSource(PwNode* node) {
qCInfo(logDefaults) << "Default configured source changed to" << node; qCInfo(logDefaults) << "Default configured source changed to" << node;
if (this->mDefaultConfiguredSource != nullptr) { if (this->mDefaultConfiguredSource != nullptr) {
// Targeted disconnect is used because this can also be the default source. QObject::disconnect(this->mDefaultConfiguredSource, nullptr, this, nullptr);
QObject::disconnect(
this->mDefaultConfiguredSource,
&PwBindableObject::destroying,
this,
&PwDefaultTracker::onDefaultConfiguredSourceDestroyed
);
} }
this->mDefaultConfiguredSource = node; this->mDefaultConfiguredSource = node;
@@ -351,7 +317,7 @@ void PwDefaultTracker::setDefaultConfiguredSource(PwNode* node) {
if (node != nullptr) { if (node != nullptr) {
QObject::connect( QObject::connect(
node, node,
&PwBindableObject::destroying, &QObject::destroyed,
this, this,
&PwDefaultTracker::onDefaultConfiguredSourceDestroyed &PwDefaultTracker::onDefaultConfiguredSourceDestroyed
); );
-11
View File
@@ -68,13 +68,11 @@ function (wl_proto target name dir)
target_include_directories(${target} INTERFACE ${PROTO_BUILD_PATH}) target_include_directories(${target} INTERFACE ${PROTO_BUILD_PATH})
target_link_libraries(${target} wl-proto-${name}-wl Qt6::WaylandClient Qt6::WaylandClientPrivate) target_link_libraries(${target} wl-proto-${name}-wl Qt6::WaylandClient Qt6::WaylandClientPrivate)
qs_pch(${target} SET wayland-protocol) qs_pch(${target} SET wayland-protocol)
target_compile_options(wl-proto-${name}-wl PRIVATE ${wayland_CFLAGS})
endfunction() endfunction()
# ----- # -----
qt_add_library(quickshell-wayland STATIC qt_add_library(quickshell-wayland STATIC
wl_proxy_safe_deref.cpp
platformmenu.cpp platformmenu.cpp
popupanchor.cpp popupanchor.cpp
xdgshell.cpp xdgshell.cpp
@@ -82,10 +80,6 @@ qt_add_library(quickshell-wayland STATIC
output_tracking.cpp output_tracking.cpp
) )
# required for wl_proxy_safe_deref
target_link_libraries(quickshell-wayland PRIVATE ${CMAKE_DL_LIBS})
target_link_options(quickshell PRIVATE "LINKER:--export-dynamic-symbol=wl_proxy_get_listener")
# required to make sure the constructor is linked # required to make sure the constructor is linked
add_library(quickshell-wayland-init OBJECT init.cpp) add_library(quickshell-wayland-init OBJECT init.cpp)
@@ -120,9 +114,6 @@ if (HYPRLAND)
add_subdirectory(hyprland) add_subdirectory(hyprland)
endif() endif()
add_subdirectory(background_effect)
list(APPEND WAYLAND_MODULES Quickshell.Wayland._BackgroundEffect)
add_subdirectory(idle_inhibit) add_subdirectory(idle_inhibit)
list(APPEND WAYLAND_MODULES Quickshell.Wayland._IdleInhibitor) list(APPEND WAYLAND_MODULES Quickshell.Wayland._IdleInhibitor)
@@ -132,8 +123,6 @@ list(APPEND WAYLAND_MODULES Quickshell.Wayland._IdleNotify)
add_subdirectory(shortcuts_inhibit) add_subdirectory(shortcuts_inhibit)
list(APPEND WAYLAND_MODULES Quickshell.Wayland._ShortcutsInhibitor) list(APPEND WAYLAND_MODULES Quickshell.Wayland._ShortcutsInhibitor)
add_subdirectory(windowmanager)
# widgets for qmenu # widgets for qmenu
target_link_libraries(quickshell-wayland PRIVATE target_link_libraries(quickshell-wayland PRIVATE
Qt::Quick Qt::Widgets Qt::WaylandClient Qt::WaylandClientPrivate Qt::Quick Qt::Widgets Qt::WaylandClient Qt::WaylandClientPrivate
@@ -1,24 +0,0 @@
qt_add_library(quickshell-wayland-background-effect STATIC
manager.cpp
surface.cpp
qml.cpp
)
qt_add_qml_module(quickshell-wayland-background-effect
URI Quickshell.Wayland._BackgroundEffect
VERSION 0.1
DEPENDENCIES QtQml
)
install_qml_module(quickshell-wayland-background-effect)
wl_proto(wlp-background-effect ext-background-effect-v1 "${WAYLAND_PROTOCOLS}/staging/ext-background-effect")
target_link_libraries(quickshell-wayland-background-effect PRIVATE
Qt::Quick Qt::WaylandClient Qt::WaylandClientPrivate wayland-client
wlp-background-effect
)
qs_module_pch(quickshell-wayland-background-effect)
target_link_libraries(quickshell PRIVATE quickshell-wayland-background-effectplugin)
-38
View File
@@ -1,38 +0,0 @@
#include "manager.hpp"
#include <cstdint>
#include <private/qwaylandwindow_p.h>
#include <qtmetamacros.h>
#include <qwayland-ext-background-effect-v1.h>
#include <qwaylandclientextension.h>
#include "surface.hpp"
namespace qs::wayland::background_effect::impl {
BackgroundEffectManager::BackgroundEffectManager(): QWaylandClientExtensionTemplate(1) {
this->initialize();
}
BackgroundEffectSurface*
BackgroundEffectManager::createEffectSurface(QtWaylandClient::QWaylandWindow* window) {
return new BackgroundEffectSurface(this->get_background_effect(window->surface()));
}
bool BackgroundEffectManager::blurAvailable() const {
return this->isActive() && this->mBlurAvailable;
}
void BackgroundEffectManager::ext_background_effect_manager_v1_capabilities(uint32_t flags) {
auto available = static_cast<bool>(flags & capability_blur);
if (available == this->mBlurAvailable) return;
this->mBlurAvailable = available;
emit this->blurAvailableChanged();
}
BackgroundEffectManager* BackgroundEffectManager::instance() {
static auto* instance = new BackgroundEffectManager(); // NOLINT
return instance->isInitialized() ? instance : nullptr;
}
} // namespace qs::wayland::background_effect::impl
-37
View File
@@ -1,37 +0,0 @@
#pragma once
#include <private/qwaylandwindow_p.h>
#include <qobject.h>
#include <qtmetamacros.h>
#include <qwayland-ext-background-effect-v1.h>
#include <qwaylandclientextension.h>
#include "surface.hpp"
namespace qs::wayland::background_effect::impl {
class BackgroundEffectManager
: public QWaylandClientExtensionTemplate<BackgroundEffectManager>
, public QtWayland::ext_background_effect_manager_v1 {
Q_OBJECT;
public:
explicit BackgroundEffectManager();
BackgroundEffectSurface* createEffectSurface(QtWaylandClient::QWaylandWindow* window);
[[nodiscard]] bool blurAvailable() const;
static BackgroundEffectManager* instance();
signals:
void blurAvailableChanged();
protected:
void ext_background_effect_manager_v1_capabilities(uint32_t flags) override;
private:
bool mBlurAvailable = false;
};
} // namespace qs::wayland::background_effect::impl
-246
View File
@@ -1,246 +0,0 @@
#include "qml.hpp"
#include <memory>
#include <private/qhighdpiscaling_p.h>
#include <private/qwaylandwindow_p.h>
#include <qcoreevent.h>
#include <qevent.h>
#include <qlogging.h>
#include <qnumeric.h>
#include <qobject.h>
#include <qregion.h>
#include <qtmetamacros.h>
#include <qvariant.h>
#include <qwindow.h>
#include "../../core/region.hpp"
#include "../../window/proxywindow.hpp"
#include "../../window/windowinterface.hpp"
#include "manager.hpp"
#include "surface.hpp"
using QtWaylandClient::QWaylandWindow;
namespace qs::wayland::background_effect {
BackgroundEffect* BackgroundEffect::qmlAttachedProperties(QObject* object) {
auto* proxyWindow = qobject_cast<ProxyWindowBase*>(object);
if (!proxyWindow) {
if (auto* iface = qobject_cast<WindowInterface*>(object)) {
proxyWindow = iface->proxyWindow();
}
}
if (!proxyWindow) return nullptr;
return new BackgroundEffect(proxyWindow);
}
BackgroundEffect::BackgroundEffect(ProxyWindowBase* window): QObject(nullptr), proxyWindow(window) {
QObject::connect(
window,
&ProxyWindowBase::windowConnected,
this,
&BackgroundEffect::onWindowConnected
);
QObject::connect(window, &ProxyWindowBase::polished, this, &BackgroundEffect::onWindowPolished);
QObject::connect(
window,
&ProxyWindowBase::devicePixelRatioChanged,
this,
&BackgroundEffect::updateBlurRegion
);
QObject::connect(window, &QObject::destroyed, this, &BackgroundEffect::onProxyWindowDestroyed);
if (window->backingWindow()) {
this->onWindowConnected();
}
}
PendingRegion* BackgroundEffect::blurRegion() const { return this->mBlurRegion; }
void BackgroundEffect::setBlurRegion(PendingRegion* region) {
if (region == this->mBlurRegion) return;
if (this->mBlurRegion) {
QObject::disconnect(this->mBlurRegion, nullptr, this, nullptr);
}
this->mBlurRegion = region;
if (region) {
QObject::connect(region, &QObject::destroyed, this, &BackgroundEffect::onBlurRegionDestroyed);
QObject::connect(region, &PendingRegion::changed, this, &BackgroundEffect::updateBlurRegion);
}
this->updateBlurRegion();
emit this->blurRegionChanged();
}
void BackgroundEffect::onBlurRegionDestroyed() {
this->mBlurRegion = nullptr;
this->updateBlurRegion();
emit this->blurRegionChanged();
}
void BackgroundEffect::updateBlurRegion() {
if (!this->surface || !this->proxyWindow) return;
this->pendingBlurRegion = true;
this->proxyWindow->schedulePolish();
}
void BackgroundEffect::onWindowPolished() {
if (!this->surface || !this->pendingBlurRegion) return;
if (!this->mWaylandWindow || !this->mWaylandWindow->surface()) {
this->pendingBlurRegion = false;
return;
}
QRegion region;
if (this->mBlurRegion) {
region =
this->mBlurRegion->applyTo(QRect(0, 0, this->mWindow->width(), this->mWindow->height()));
auto scale = QHighDpiScaling::factor(this->mWindow);
if (!qFuzzyCompare(scale, 1.0)) {
region = QHighDpi::scale(region, scale);
}
auto margins = this->mWaylandWindow->clientSideMargins();
region.translate(margins.left(), margins.top());
}
this->surface->setBlurRegion(region);
this->pendingBlurRegion = false;
}
bool BackgroundEffect::eventFilter(QObject* object, QEvent* event) {
if (event->type() == QEvent::PlatformSurface) {
auto* surfaceEvent = dynamic_cast<QPlatformSurfaceEvent*>(event);
if (surfaceEvent->surfaceEventType() == QPlatformSurfaceEvent::SurfaceAboutToBeDestroyed) {
this->surface = nullptr;
this->pendingBlurRegion = false;
}
}
return this->QObject::eventFilter(object, event);
}
void BackgroundEffect::onWindowConnected() {
this->mWindow = this->proxyWindow->backingWindow();
this->mWindow->installEventFilter(this);
QObject::connect(
this->mWindow,
&QWindow::visibleChanged,
this,
&BackgroundEffect::onWindowVisibleChanged
);
this->onWindowVisibleChanged();
}
void BackgroundEffect::onWindowVisibleChanged() {
if (this->mWindow->isVisible()) {
if (!this->mWindow->handle()) {
this->mWindow->create();
}
}
auto* window = dynamic_cast<QWaylandWindow*>(this->mWindow->handle());
if (window == this->mWaylandWindow) return;
if (this->mWaylandWindow) {
QObject::disconnect(this->mWaylandWindow, nullptr, this, nullptr);
}
this->mWaylandWindow = window;
if (!window) return;
QObject::connect(
this->mWaylandWindow,
&QObject::destroyed,
this,
&BackgroundEffect::onWaylandWindowDestroyed
);
QObject::connect(
this->mWaylandWindow,
&QWaylandWindow::surfaceCreated,
this,
&BackgroundEffect::onWaylandSurfaceCreated
);
QObject::connect(
this->mWaylandWindow,
&QWaylandWindow::surfaceDestroyed,
this,
&BackgroundEffect::onWaylandSurfaceDestroyed
);
if (this->mWaylandWindow->surface()) {
this->onWaylandSurfaceCreated();
}
}
void BackgroundEffect::onWaylandWindowDestroyed() { this->mWaylandWindow = nullptr; }
void BackgroundEffect::onWaylandSurfaceCreated() {
auto* manager = impl::BackgroundEffectManager::instance();
if (!manager) {
qWarning() << "Cannot enable background effect as ext-background-effect-v1 is not supported "
"by the current compositor.";
return;
}
// Steal protocol surface from previous BackgroundEffect to avoid duplicate-attachment on reload.
auto v = this->mWaylandWindow->property("qs_background_effect");
if (v.canConvert<BackgroundEffect*>()) {
auto* prev = v.value<BackgroundEffect*>();
if (prev != this && prev->surface) {
this->surface.swap(prev->surface);
}
}
if (!this->surface) {
this->surface = std::unique_ptr<impl::BackgroundEffectSurface>(
manager->createEffectSurface(this->mWaylandWindow)
);
}
this->mWaylandWindow->setProperty("qs_background_effect", QVariant::fromValue(this));
this->pendingBlurRegion = this->mBlurRegion != nullptr;
if (this->pendingBlurRegion) {
this->proxyWindow->schedulePolish();
}
}
void BackgroundEffect::onWaylandSurfaceDestroyed() {
this->surface = nullptr;
this->pendingBlurRegion = false;
if (!this->proxyWindow) {
this->deleteLater();
}
}
void BackgroundEffect::onProxyWindowDestroyed() {
// Don't delete the BackgroundEffect, and therefore the impl::BackgroundEffectSurface
// until the wl_surface is destroyed. Deleting it when the proxy window is deleted would
// cause a frame without blur between the destruction of the ext_background_effect_surface_v1
// and wl_surface objects.
this->proxyWindow = nullptr;
if (this->surface == nullptr) {
this->deleteLater();
}
}
} // namespace qs::wayland::background_effect
-80
View File
@@ -1,80 +0,0 @@
#pragma once
#include <memory>
#include <private/qwaylandwindow_p.h>
#include <qcoreevent.h>
#include <qobject.h>
#include <qqmlintegration.h>
#include <qtmetamacros.h>
#include <qwindow.h>
#include "../../core/region.hpp"
#include "../../window/proxywindow.hpp"
#include "surface.hpp"
namespace qs::wayland::background_effect {
///! Background blur effect for Wayland surfaces.
/// Applies background blur behind a @@Quickshell.QsWindow or subclass,
/// as an attached object, using the [ext-background-effect-v1] Wayland protocol.
///
/// > [!NOTE] Using a background effect requires the compositor support the
/// > [ext-background-effect-v1] protocol.
///
/// [ext-background-effect-v1]: https://wayland.app/protocols/ext-background-effect-v1
///
/// #### Example
/// ```qml
/// @@Quickshell.PanelWindow {
/// id: root
/// color: "#80000000"
///
/// BackgroundEffect.blurRegion: Region { item: root.contentItem }
/// }
/// ```
class BackgroundEffect: public QObject {
Q_OBJECT;
// clang-format off
/// Region to blur behind the surface. Set to null to remove blur.
Q_PROPERTY(PendingRegion* blurRegion READ blurRegion WRITE setBlurRegion NOTIFY blurRegionChanged);
// clang-format on
QML_ELEMENT;
QML_UNCREATABLE("BackgroundEffect can only be used as an attached object.");
QML_ATTACHED(BackgroundEffect);
public:
explicit BackgroundEffect(ProxyWindowBase* window);
[[nodiscard]] PendingRegion* blurRegion() const;
void setBlurRegion(PendingRegion* region);
static BackgroundEffect* qmlAttachedProperties(QObject* object);
bool eventFilter(QObject* object, QEvent* event) override;
signals:
void blurRegionChanged();
private slots:
void onWindowConnected();
void onWindowVisibleChanged();
void onWaylandWindowDestroyed();
void onWaylandSurfaceCreated();
void onWaylandSurfaceDestroyed();
void onProxyWindowDestroyed();
void onBlurRegionDestroyed();
void onWindowPolished();
void updateBlurRegion();
private:
ProxyWindowBase* proxyWindow = nullptr;
QWindow* mWindow = nullptr;
QtWaylandClient::QWaylandWindow* mWaylandWindow = nullptr;
bool pendingBlurRegion = false;
PendingRegion* mBlurRegion = nullptr;
std::unique_ptr<impl::BackgroundEffectSurface> surface;
};
} // namespace qs::wayland::background_effect
-37
View File
@@ -1,37 +0,0 @@
#include "surface.hpp"
#include <private/qwaylanddisplay_p.h>
#include <private/qwaylandintegration_p.h>
#include <qregion.h>
#include <qwayland-ext-background-effect-v1.h>
#include <wayland-client-protocol.h>
namespace qs::wayland::background_effect::impl {
BackgroundEffectSurface::BackgroundEffectSurface(
::ext_background_effect_surface_v1* surface // NOLINT(misc-include-cleaner)
)
: QtWayland::ext_background_effect_surface_v1(surface) {}
BackgroundEffectSurface::~BackgroundEffectSurface() {
if (!this->isInitialized()) return;
this->destroy();
}
void BackgroundEffectSurface::setBlurRegion(const QRegion& region) {
if (!this->isInitialized()) return;
if (region.isEmpty()) {
this->set_blur_region(nullptr);
return;
}
static const auto* waylandIntegration = QtWaylandClient::QWaylandIntegration::instance();
auto* display = waylandIntegration->display();
auto* wlRegion = display->createRegion(region);
this->set_blur_region(wlRegion);
wl_region_destroy(wlRegion); // NOLINT(misc-include-cleaner)
}
} // namespace qs::wayland::background_effect::impl
-18
View File
@@ -1,18 +0,0 @@
#pragma once
#include <qregion.h>
#include <qtclasshelpermacros.h>
#include <qwayland-ext-background-effect-v1.h>
namespace qs::wayland::background_effect::impl {
class BackgroundEffectSurface: public QtWayland::ext_background_effect_surface_v1 {
public:
explicit BackgroundEffectSurface(::ext_background_effect_surface_v1* surface);
~BackgroundEffectSurface() override;
Q_DISABLE_COPY_MOVE(BackgroundEffectSurface);
void setBlurRegion(const QRegion& region);
};
} // namespace qs::wayland::background_effect::impl
@@ -1,62 +0,0 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import Quickshell
import Quickshell.Wayland
FloatingWindow {
id: root
color: "transparent"
contentItem.palette.windowText: "white"
ColumnLayout {
anchors.centerIn: parent
CheckBox {
id: enableBox
checked: true
text: "Enable Blur"
}
Button {
text: "Hide->Show"
onClicked: {
root.visible = false
showTimer.start()
}
}
Timer {
id: showTimer
interval: 200
onTriggered: root.visible = true
}
Slider {
id: radiusSlider
from: 0
to: 1000
value: 100
}
component EdgeSlider: Slider {
from: -1
to: 1000
value: -1
}
EdgeSlider { id: topLeftSlider }
EdgeSlider { id: topRightSlider }
EdgeSlider { id: bottomLeftSlider }
EdgeSlider { id: bottomRightSlider }
}
BackgroundEffect.blurRegion: Region {
item: enableBox.checked ? root.contentItem : null
radius: radiusSlider.value == -1 ? undefined : radiusSlider.value
topLeftRadius: topLeftSlider.value == -1 ? undefined : topLeftSlider.value
topRightRadius: topRightSlider.value == -1 ? undefined : topRightSlider.value
bottomLeftRadius: bottomLeftSlider.value == -1 ? undefined : bottomLeftSlider.value
bottomRightRadius: bottomRightSlider.value == -1 ? undefined : bottomRightSlider.value
}
}
+18 -10
View File
@@ -10,6 +10,7 @@
#include <EGL/egl.h> #include <EGL/egl.h>
#include <EGL/eglext.h> #include <EGL/eglext.h>
#include <GL/gl.h> #include <GL/gl.h>
#include <GLES3/gl32.h>
#include <fcntl.h> #include <fcntl.h>
#include <gbm.h> #include <gbm.h>
#include <libdrm/drm_fourcc.h> #include <libdrm/drm_fourcc.h>
@@ -27,7 +28,6 @@
#include <qscopedpointer.h> #include <qscopedpointer.h>
#include <qsgrendererinterface.h> #include <qsgrendererinterface.h>
#include <qsgtexture_platform.h> #include <qsgtexture_platform.h>
#include <qtypes.h>
#include <qvulkanfunctions.h> #include <qvulkanfunctions.h>
#include <qvulkaninstance.h> #include <qvulkaninstance.h>
#include <qwayland-linux-dmabuf-v1.h> #include <qwayland-linux-dmabuf-v1.h>
@@ -35,6 +35,7 @@
#include <sys/mman.h> #include <sys/mman.h>
#include <sys/types.h> #include <sys/types.h>
#include <unistd.h> #include <unistd.h>
#include <qtypes.h>
#include <vulkan/vulkan_core.h> #include <vulkan/vulkan_core.h>
#include <wayland-client-protocol.h> #include <wayland-client-protocol.h>
#include <wayland-linux-dmabuf-v1-client-protocol.h> #include <wayland-linux-dmabuf-v1-client-protocol.h>
@@ -79,8 +80,10 @@ bool drmFormatHasAlpha(uint32_t drmFormat) {
case DRM_FORMAT_ABGR8888: case DRM_FORMAT_ABGR8888:
case DRM_FORMAT_ARGB2101010: case DRM_FORMAT_ARGB2101010:
case DRM_FORMAT_ABGR2101010: case DRM_FORMAT_ABGR2101010:
case DRM_FORMAT_ABGR16161616F: return true; case DRM_FORMAT_ABGR16161616F:
default: return false; return true;
default:
return false;
} }
} }
@@ -815,8 +818,7 @@ WlBufferQSGTexture* WlDmaBuffer::createQsgTextureVulkan(QQuickWindow* window) co
// dup() is required because vkAllocateMemory with VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT // dup() is required because vkAllocateMemory with VK_EXTERNAL_MEMORY_HANDLE_TYPE_DMA_BUF_BIT_EXT
// takes ownership of the fd on succcess. Without dup, WlDmaBuffer would double-close. // takes ownership of the fd on succcess. Without dup, WlDmaBuffer would double-close.
const int dupFd = const int dupFd = dup(this->planes[0].fd); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
dup(this->planes[0].fd); // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
if (dupFd < 0) { if (dupFd < 0) {
qCWarning(logDmabuf) << "Failed to dup() fd for DMA-BUF import"; qCWarning(logDmabuf) << "Failed to dup() fd for DMA-BUF import";
goto cleanup_fail; // NOLINT goto cleanup_fail; // NOLINT
@@ -907,12 +909,12 @@ WlBufferQSGTexture* WlDmaBuffer::createQsgTextureVulkan(QQuickWindow* window) co
// find the graphics queue family index for the ownrship transfer. // find the graphics queue family index for the ownrship transfer.
uint32_t graphicsQueueFamily = 0; uint32_t graphicsQueueFamily = 0;
uint32_t queueFamilyCount = 0; uint32_t queueFamilyCount = 0;
instFuncs->vkGetPhysicalDeviceQueueFamilyProperties(physDevice, &queueFamilyCount, nullptr); instFuncs->vkGetPhysicalDeviceQueueFamilyProperties(
physDevice, &queueFamilyCount, nullptr
);
std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount); std::vector<VkQueueFamilyProperties> queueFamilies(queueFamilyCount);
instFuncs->vkGetPhysicalDeviceQueueFamilyProperties( instFuncs->vkGetPhysicalDeviceQueueFamilyProperties(
physDevice, physDevice, &queueFamilyCount, queueFamilies.data()
&queueFamilyCount,
queueFamilies.data()
); );
for (uint32_t i = 0; i < queueFamilyCount; ++i) { for (uint32_t i = 0; i < queueFamilyCount; ++i) {
if (queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { if (queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {
@@ -987,7 +989,13 @@ WlBufferQSGTexture* WlDmaBuffer::createQsgTextureVulkan(QQuickWindow* window) co
} }
} }
auto* tex = new WlDmaBufferVulkanQSGTexture(devFuncs, device, image, memory, qsgTexture); auto* tex = new WlDmaBufferVulkanQSGTexture(
devFuncs,
device,
image,
memory,
qsgTexture
);
qCDebug(logDmabuf) << "Created WlDmaBufferVulkanQSGTexture" << tex << "from" << this; qCDebug(logDmabuf) << "Created WlDmaBufferVulkanQSGTexture" << tex << "from" << this;
return tex; return tex;
} }
-4
View File
@@ -123,10 +123,6 @@ void WlBufferQSGDisplayNode::setRect(const QRectF& rect) {
this->setMatrix(matrix); this->setMatrix(matrix);
} }
void WlBufferQSGDisplayNode::setFiltering(QSGTexture::Filtering filtering) {
this->imageNode->setFiltering(filtering);
}
void WlBufferQSGDisplayNode::syncSwapchain(const WlBufferSwapchain& swapchain) { void WlBufferQSGDisplayNode::syncSwapchain(const WlBufferSwapchain& swapchain) {
auto* buffer = swapchain.frontbuffer(); auto* buffer = swapchain.frontbuffer();
auto& texture = swapchain.presentSecondBuffer ? this->buffer2 : this->buffer1; auto& texture = swapchain.presentSecondBuffer ? this->buffer2 : this->buffer1;
-1
View File
@@ -33,7 +33,6 @@ public:
void syncSwapchain(const WlBufferSwapchain& swapchain); void syncSwapchain(const WlBufferSwapchain& swapchain);
void setRect(const QRectF& rect); void setRect(const QRectF& rect);
void setFiltering(QSGTexture::Filtering filtering);
private: private:
QQuickWindow* window; QQuickWindow* window;
+49 -50
View File
@@ -9,6 +9,7 @@
#include <qwindow.h> #include <qwindow.h>
#include "../../../window/proxywindow.hpp" #include "../../../window/proxywindow.hpp"
#include "../../../window/windowinterface.hpp"
#include "grab.hpp" #include "grab.hpp"
#include "manager.hpp" #include "manager.hpp"
@@ -37,51 +38,8 @@ QObjectList HyprlandFocusGrab::windows() const { return this->windowObjects; }
void HyprlandFocusGrab::setWindows(QObjectList windows) { void HyprlandFocusGrab::setWindows(QObjectList windows) {
if (windows == this->windowObjects) return; if (windows == this->windowObjects) return;
if (this->grab) this->grab->startTransaction();
for (auto* obj: this->windowObjects) {
if (windows.contains(obj)) continue;
QObject::disconnect(obj, nullptr, this, nullptr);
auto* proxy = ProxyWindowBase::forObject(obj);
if (!proxy) continue;
QObject::disconnect(proxy, nullptr, this, nullptr);
if (this->grab && proxy->backingWindow()) {
this->grab->removeWindow(proxy->backingWindow());
}
}
for (auto it = windows.begin(); it != windows.end();) {
auto* proxy = ProxyWindowBase::forObject(*it);
if (!proxy) {
it = windows.erase(it);
continue;
}
if (this->windowObjects.contains(*it)) {
++it;
continue;
}
QObject::connect(*it, &QObject::destroyed, this, &HyprlandFocusGrab::onObjectDestroyed);
QObject::connect(
proxy,
&ProxyWindowBase::windowConnected,
this,
&HyprlandFocusGrab::onProxyConnected
);
if (this->grab && proxy->backingWindow()) {
this->grab->addWindow(proxy->backingWindow());
}
++it;
}
if (this->grab) this->grab->completeTransaction();
this->windowObjects = std::move(windows); this->windowObjects = std::move(windows);
this->syncWindows();
emit this->windowsChanged(); emit this->windowsChanged();
} }
@@ -117,18 +75,59 @@ void HyprlandFocusGrab::tryActivate() {
QObject::connect(this->grab, &FocusGrab::cleared, this, &HyprlandFocusGrab::onGrabCleared); QObject::connect(this->grab, &FocusGrab::cleared, this, &HyprlandFocusGrab::onGrabCleared);
this->grab->startTransaction(); this->grab->startTransaction();
for (auto* obj: this->windowObjects) { for (auto* proxy: this->trackedProxies) {
auto* proxy = ProxyWindowBase::forObject(obj); if (proxy->backingWindow() != nullptr) {
if (proxy && proxy->backingWindow()) {
this->grab->addWindow(proxy->backingWindow()); this->grab->addWindow(proxy->backingWindow());
} }
} }
this->grab->completeTransaction(); this->grab->completeTransaction();
} }
void HyprlandFocusGrab::onObjectDestroyed(QObject* object) { void HyprlandFocusGrab::syncWindows() {
this->windowObjects.removeOne(object); auto newProxy = QList<ProxyWindowBase*>();
emit this->windowsChanged(); for (auto* windowObject: this->windowObjects) {
auto* proxyWindow = qobject_cast<ProxyWindowBase*>(windowObject);
if (proxyWindow == nullptr) {
if (auto* iface = qobject_cast<WindowInterface*>(windowObject)) {
proxyWindow = iface->proxyWindow();
}
}
if (proxyWindow != nullptr) {
newProxy.push_back(proxyWindow);
}
}
if (this->grab) this->grab->startTransaction();
for (auto* oldWindow: this->trackedProxies) {
if (!newProxy.contains(oldWindow)) {
QObject::disconnect(oldWindow, nullptr, this, nullptr);
if (this->grab != nullptr && oldWindow->backingWindow() != nullptr) {
this->grab->removeWindow(oldWindow->backingWindow());
}
}
}
for (auto* newProxy: newProxy) {
if (!this->trackedProxies.contains(newProxy)) {
QObject::connect(
newProxy,
&ProxyWindowBase::windowConnected,
this,
&HyprlandFocusGrab::onProxyConnected
);
if (this->grab != nullptr && newProxy->backingWindow() != nullptr) {
this->grab->addWindow(newProxy->backingWindow());
}
}
}
this->trackedProxies = newProxy;
if (this->grab) this->grab->completeTransaction();
} }
} // namespace qs::hyprland } // namespace qs::hyprland
+3 -1
View File
@@ -96,13 +96,15 @@ private slots:
void onGrabActivated(); void onGrabActivated();
void onGrabCleared(); void onGrabCleared();
void onProxyConnected(); void onProxyConnected();
void onObjectDestroyed(QObject* object);
private: private:
void tryActivate(); void tryActivate();
void syncWindows();
bool targetActive = false; bool targetActive = false;
QObjectList windowObjects; QObjectList windowObjects;
QList<ProxyWindowBase*> trackedProxies;
QList<QWindow*> trackedWindows;
focus_grab::FocusGrab* grab = nullptr; focus_grab::FocusGrab* grab = nullptr;
}; };
Loaded 100 of 141 files, more files were not shown because too many files have changed in this diff. Show more